diff options
Diffstat (limited to 'src/plugins')
707 files changed, 26042 insertions, 17022 deletions
diff --git a/src/plugins/analyzerbase/analyzerrunconfigwidget.cpp b/src/plugins/analyzerbase/analyzerrunconfigwidget.cpp index 47510bd5f7..35ace48a98 100644 --- a/src/plugins/analyzerbase/analyzerrunconfigwidget.cpp +++ b/src/plugins/analyzerbase/analyzerrunconfigwidget.cpp @@ -98,8 +98,8 @@ void AnalyzerRunConfigWidget::chooseSettings(int setting) m_configWidget->setEnabled(isCustom); m_restoreButton->setEnabled(isCustom); m_details->setSummaryText(isCustom - ? tr("Use <strong>Customized Settings<strong>") - : tr("Use <strong>Global Settings<strong>")); + ? tr("Use <strong>Customized Settings</strong>") + : tr("Use <strong>Global Settings</strong>")); } void AnalyzerRunConfigWidget::restoreGlobal() diff --git a/src/plugins/android/androidconfigurations.cpp b/src/plugins/android/androidconfigurations.cpp index ec27cc34d5..5d2263d6bd 100644 --- a/src/plugins/android/androidconfigurations.cpp +++ b/src/plugins/android/androidconfigurations.cpp @@ -37,6 +37,7 @@ #include "androiddevicedialog.h" #include <coreplugin/icore.h> +#include <coreplugin/messagemanager.h> #include <utils/hostosinfo.h> #include <utils/persistentsettings.h> #include <projectexplorer/kitmanager.h> @@ -248,7 +249,7 @@ void AndroidConfigurations::updateAvailableSdkPlatforms() QProcess proc; proc.setProcessEnvironment(androidToolEnvironment().toProcessEnvironment()); proc.start(androidToolPath().toString(), QStringList() << QLatin1String("list") << QLatin1String("target")); // list avaialbe AVDs - if (!proc.waitForFinished(-1)) { + if (!proc.waitForFinished(5000)) { proc.terminate(); return; } @@ -458,7 +459,7 @@ QVector<AndroidDeviceInfo> AndroidConfigurations::connectedDevices(QString *erro QVector<AndroidDeviceInfo> devices; QProcess adbProc; adbProc.start(adbToolPath().toString(), QStringList() << QLatin1String("devices")); - if (!adbProc.waitForFinished(-1)) { + if (!adbProc.waitForFinished(5000)) { adbProc.kill(); if (error) *error = tr("Could not run: %1").arg(adbToolPath().toString() + QLatin1String(" devices")); @@ -563,6 +564,8 @@ QString AndroidConfigurations::createAVD(const QString &target, const QString &n break; } + Core::MessageManager::write(QString::fromLocal8Bit(question), Core::MessageManager::Flash); + proc.waitForFinished(); if (proc.exitCode()) // error! @@ -577,7 +580,7 @@ bool AndroidConfigurations::removeAVD(const QString &name) const proc.start(androidToolPath().toString(), QStringList() << QLatin1String("delete") << QLatin1String("avd") << QLatin1String("-n") << name); - if (!proc.waitForFinished(-1)) { + if (!proc.waitForFinished(5000)) { proc.terminate(); return false; } @@ -591,7 +594,7 @@ QVector<AndroidDeviceInfo> AndroidConfigurations::androidVirtualDevices() const proc.setProcessEnvironment(androidToolEnvironment().toProcessEnvironment()); proc.start(androidToolPath().toString(), QStringList() << QLatin1String("list") << QLatin1String("avd")); // list available AVDs - if (!proc.waitForFinished(-1)) { + if (!proc.waitForFinished(5000)) { proc.terminate(); return devices; } @@ -634,7 +637,7 @@ QVector<AndroidDeviceInfo> AndroidConfigurations::androidVirtualDevices() const QString AndroidConfigurations::startAVD(const QString &name, int apiLevel, QString cpuAbi) const { - if (findAvd(apiLevel, cpuAbi) || startAVDAsync(name)) + if (!findAvd(apiLevel, cpuAbi).isEmpty() || startAVDAsync(name)) return waitForAvd(apiLevel, cpuAbi); return QString(); } @@ -656,7 +659,7 @@ bool AndroidConfigurations::startAVDAsync(const QString &avdName) const return true; } -bool AndroidConfigurations::findAvd(int apiLevel, const QString &cpuAbi) const +QString AndroidConfigurations::findAvd(int apiLevel, const QString &cpuAbi) const { QVector<AndroidDeviceInfo> devices = connectedDevices(); foreach (AndroidDeviceInfo device, devices) { @@ -666,37 +669,50 @@ bool AndroidConfigurations::findAvd(int apiLevel, const QString &cpuAbi) const continue; if (device.sdk != apiLevel) continue; - return true; + return device.serialNumber; + } + return QString(); +} + +bool AndroidConfigurations::isConnected(const QString &serialNumber) const +{ + QVector<AndroidDeviceInfo> devices = connectedDevices(); + foreach (AndroidDeviceInfo device, devices) { + if (device.serialNumber == serialNumber) + return true; } return false; } -QString AndroidConfigurations::waitForAvd(int apiLevel, const QString &cpuAbi) const +bool AndroidConfigurations::waitForBooted(const QString &serialNumber, const QFutureInterface<bool> &fi) const { - // we cannot use adb -e wait-for-device, since that doesn't work if a emulator is already running + // found a serial number, now wait until it's done booting... + for (int i = 0; i < 60; ++i) { + if (fi.isCanceled()) + return false; + if (hasFinishedBooting(serialNumber)) { + return true; + } else { + Utils::sleep(2000); + if (!isConnected(serialNumber)) // device was disconnected + return false; + } + } + return false; +} - // 15 rounds of 8s sleeping, a minute for the avd to start +QString AndroidConfigurations::waitForAvd(int apiLevel, const QString &cpuAbi, const QFutureInterface<bool> &fi) const +{ + // we cannot use adb -e wait-for-device, since that doesn't work if a emulator is already running + // 60 rounds of 2s sleeping, two minutes for the avd to start QString serialNumber; - for (int i = 0; i < 15; ++i) { - QVector<AndroidDeviceInfo> devices = connectedDevices(); - foreach (AndroidDeviceInfo device, devices) { - if (!device.serialNumber.startsWith(QLatin1String("emulator"))) - continue; - if (!device.cpuAbi.contains(cpuAbi)) - continue; - if (device.sdk != apiLevel) - continue; - serialNumber = device.serialNumber; - // found a serial number, now wait until it's done booting... - for (int i = 0; i < 15; ++i) { - if (hasFinishedBooting(serialNumber)) - return serialNumber; - else - Utils::sleep(8000); - } + for (int i = 0; i < 60; ++i) { + if (fi.isCanceled()) return QString(); - } - Utils::sleep(8000); + serialNumber = findAvd(apiLevel, cpuAbi); + if (!serialNumber.isEmpty()) + return waitForBooted(serialNumber, fi) ? serialNumber : QString(); + Utils::sleep(2000); } return QString(); } @@ -710,7 +726,7 @@ bool AndroidConfigurations::isBootToQt(const QString &device) const QProcess adbProc; adbProc.start(adbToolPath().toString(), arguments); - if (!adbProc.waitForFinished(-1)) { + if (!adbProc.waitForFinished(5000)) { adbProc.kill(); return false; } @@ -726,7 +742,7 @@ int AndroidConfigurations::getSDKVersion(const QString &device) const QProcess adbProc; adbProc.start(adbToolPath().toString(), arguments); - if (!adbProc.waitForFinished(-1)) { + if (!adbProc.waitForFinished(5000)) { adbProc.kill(); return -1; } @@ -749,7 +765,7 @@ QString AndroidConfigurations::getProductModel(const QString &device) const QProcess adbProc; adbProc.start(adbToolPath().toString(), arguments); - if (!adbProc.waitForFinished(-1)) { + if (!adbProc.waitForFinished(5000)) { adbProc.kill(); return device; } @@ -769,7 +785,7 @@ bool AndroidConfigurations::hasFinishedBooting(const QString &device) const QProcess adbProc; adbProc.start(adbToolPath().toString(), arguments); - if (!adbProc.waitForFinished(-1)) { + if (!adbProc.waitForFinished(5000)) { adbProc.kill(); return false; } @@ -792,7 +808,7 @@ QStringList AndroidConfigurations::getAbis(const QString &device) const QProcess adbProc; adbProc.start(adbToolPath().toString(), arguments); - if (!adbProc.waitForFinished(-1)) { + if (!adbProc.waitForFinished(5000)) { adbProc.kill(); return result; } diff --git a/src/plugins/android/androidconfigurations.h b/src/plugins/android/androidconfigurations.h index 41ba97dbc4..bf7feb298e 100644 --- a/src/plugins/android/androidconfigurations.h +++ b/src/plugins/android/androidconfigurations.h @@ -36,6 +36,7 @@ #include <QVector> #include <QHash> #include <QMap> +#include <QFutureInterface> #include <projectexplorer/abi.h> #include <utils/fileutils.h> #include <utils/environment.h> @@ -108,8 +109,9 @@ public: QVector<AndroidDeviceInfo> androidVirtualDevices() const; QString startAVD(const QString &name, int apiLevel, QString cpuAbi) const; bool startAVDAsync(const QString &avdName) const; - bool findAvd(int apiLevel, const QString &cpuAbi) const; - QString waitForAvd(int apiLevel, const QString &cpuAbi) const; + QString findAvd(int apiLevel, const QString &cpuAbi) const; + QString waitForAvd(int apiLevel, const QString &cpuAbi, const QFutureInterface<bool> &fi = QFutureInterface<bool>()) const; + // special version for AndroidDeployQt::run QString bestNdkPlatformMatch(const QString &targetAPI) const; QStringList makeExtraSearchDirectories() const; @@ -123,6 +125,8 @@ public: QString getProductModel(const QString &device) const; bool hasFinishedBooting(const QString &device) const; + bool waitForBooted(const QString &serialNumber, const QFutureInterface<bool> &fi) const; + bool isConnected(const QString &serialNumber) const; AndroidDeviceInfo showDeviceDialog(ProjectExplorer::Project *project, int apiLevel, const QString &abi); void setDefaultDevice(ProjectExplorer::Project *project, const QString &abi, const QString &serialNumber); // serial number or avd name diff --git a/src/plugins/android/androiddeployqtstep.cpp b/src/plugins/android/androiddeployqtstep.cpp index 7e87a7bfb5..15038df111 100644 --- a/src/plugins/android/androiddeployqtstep.cpp +++ b/src/plugins/android/androiddeployqtstep.cpp @@ -285,7 +285,7 @@ bool AndroidDeployQtStep::init() if (!result) return false; - if (!AndroidConfigurations::instance().findAvd(m_deviceAPILevel, m_targetArch)) + if (AndroidConfigurations::instance().findAvd(m_deviceAPILevel, m_targetArch).isEmpty()) AndroidConfigurations::instance().startAVDAsync(m_avdName); return true; } @@ -293,7 +293,7 @@ bool AndroidDeployQtStep::init() void AndroidDeployQtStep::run(QFutureInterface<bool> &fi) { if (!m_avdName.isEmpty()) { - QString serialNumber = AndroidConfigurations::instance().waitForAvd(m_deviceAPILevel, m_targetArch); + QString serialNumber = AndroidConfigurations::instance().waitForAvd(m_deviceAPILevel, m_targetArch, fi); if (serialNumber.isEmpty()) { fi.reportResult(false); emit finished(); diff --git a/src/plugins/android/androiddeployqtwidget.ui b/src/plugins/android/androiddeployqtwidget.ui index a726f9134f..180bccc3b7 100644 --- a/src/plugins/android/androiddeployqtwidget.ui +++ b/src/plugins/android/androiddeployqtwidget.ui @@ -200,7 +200,7 @@ <item row="1" column="0"> <widget class="QLabel" name="targetSDKLabel"> <property name="text"> - <string>Android target SDK:</string> + <string>Android build SDK:</string> </property> </widget> </item> @@ -241,7 +241,7 @@ <item> <widget class="QLabel" name="oldFilesWarningLabel"> <property name="text"> - <string>Qt no longer uses the folder "android" in the project's source directory.</string> + <string>Qt no longer uses the folder "android" in the project's source directory.</string> </property> <property name="wordWrap"> <bool>true</bool> diff --git a/src/plugins/android/androiddeploystep.cpp b/src/plugins/android/androiddeploystep.cpp index 957df22b41..9d8695cb5a 100644 --- a/src/plugins/android/androiddeploystep.cpp +++ b/src/plugins/android/androiddeploystep.cpp @@ -281,8 +281,7 @@ unsigned int AndroidDeployStep::remoteModificationTime(const QString &fullDestin QStringList arguments = AndroidDeviceInfo::adbSelector(m_deviceSerialNumber); arguments << QLatin1String("ls") << destination; process.start(AndroidConfigurations::instance().adbToolPath().toString(), arguments); - process.waitForFinished(-1); - if (process.error() != QProcess::UnknownError + if (!process.waitForFinished(5000) || process.exitCode() != 0) return -1; QByteArray output = process.readAll(); @@ -389,7 +388,7 @@ void AndroidDeployStep::deployFiles(QProcess *process, const QList<DeployItem> & bool AndroidDeployStep::deployPackage() { if (!m_avdName.isEmpty()) { - if (!AndroidConfigurations::instance().findAvd(m_deviceAPILevel, m_targetArch) + if (AndroidConfigurations::instance().findAvd(m_deviceAPILevel, m_targetArch).isEmpty() && !AndroidConfigurations::instance().startAVDAsync(m_avdName)) return false; m_deviceSerialNumber = AndroidConfigurations::instance().waitForAvd(m_deviceAPILevel, m_targetArch); diff --git a/src/plugins/android/androidmanager.cpp b/src/plugins/android/androidmanager.cpp index 3e83c3a282..aed0ebe0f1 100644 --- a/src/plugins/android/androidmanager.cpp +++ b/src/plugins/android/androidmanager.cpp @@ -791,7 +791,7 @@ QString AndroidManager::androidNameForApiLevel(int x) case 19: return QLatin1String("Android 4.4"); default: - return QLatin1String("Unknown Android version."); + return tr("Unknown Android version."); } } diff --git a/src/plugins/android/androidmanifesteditorfactory.cpp b/src/plugins/android/androidmanifesteditorfactory.cpp index e6e5b0ed0b..a60342cda4 100644 --- a/src/plugins/android/androidmanifesteditorfactory.cpp +++ b/src/plugins/android/androidmanifesteditorfactory.cpp @@ -41,17 +41,17 @@ using namespace Android::Internal; AndroidManifestEditorFactory::AndroidManifestEditorFactory(QObject *parent) - : Core::IEditorFactory(parent), - m_actionHandler(new TextEditor::TextEditorActionHandler(Constants::ANDROID_MANIFEST_EDITOR_CONTEXT)) + : Core::IEditorFactory(parent) { setId(Constants::ANDROID_MANIFEST_EDITOR_ID); setDisplayName(tr("Android Manifest editor")); addMimeType(Constants::ANDROID_MANIFEST_MIME_TYPE); + new TextEditor::TextEditorActionHandler(this, Constants::ANDROID_MANIFEST_EDITOR_CONTEXT); } Core::IEditor *AndroidManifestEditorFactory::createEditor(QWidget *parent) { - AndroidManifestEditorWidget *editor = new AndroidManifestEditorWidget(parent, m_actionHandler); + AndroidManifestEditorWidget *editor = new AndroidManifestEditorWidget(parent); TextEditor::TextEditorSettings::initializeEditor(editor); return editor->editor(); } diff --git a/src/plugins/android/androidmanifesteditorfactory.h b/src/plugins/android/androidmanifesteditorfactory.h index 47f1b44762..e9bd307ea2 100644 --- a/src/plugins/android/androidmanifesteditorfactory.h +++ b/src/plugins/android/androidmanifesteditorfactory.h @@ -32,8 +32,6 @@ #include <coreplugin/editormanager/ieditorfactory.h> -namespace TextEditor { class TextEditorActionHandler; } - namespace Android { namespace Internal { @@ -45,9 +43,6 @@ public: explicit AndroidManifestEditorFactory(QObject *parent = 0); Core::IEditor *createEditor(QWidget *parent); - -private: - TextEditor::TextEditorActionHandler *m_actionHandler; }; } // namespace Internal diff --git a/src/plugins/android/androidmanifesteditorwidget.cpp b/src/plugins/android/androidmanifesteditorwidget.cpp index 1e50a4987e..07e9e6223f 100644 --- a/src/plugins/android/androidmanifesteditorwidget.cpp +++ b/src/plugins/android/androidmanifesteditorwidget.cpp @@ -95,7 +95,7 @@ Project *androidProject(const QString &file) } // anonymous namespace -AndroidManifestEditorWidget::AndroidManifestEditorWidget(QWidget *parent, TextEditor::TextEditorActionHandler *ah) +AndroidManifestEditorWidget::AndroidManifestEditorWidget(QWidget *parent) : TextEditor::PlainTextEditorWidget(parent), m_dirty(false), m_stayClean(false), @@ -106,7 +106,6 @@ AndroidManifestEditorWidget::AndroidManifestEditorWidget(QWidget *parent, TextEd doc->setMimeType(QLatin1String(Constants::ANDROID_MANIFEST_MIME_TYPE)); setBaseTextDocument(doc); - ah->setupActions(this); configure(QLatin1String(Constants::ANDROID_MANIFEST_MIME_TYPE)); initializePage(); @@ -203,7 +202,7 @@ void AndroidManifestEditorWidget::initializePage() m_androidTargetSdkVersion = new QComboBox(packageGroupBox); m_androidTargetSdkVersion->setToolTip( - tr("Sets the target SDK. Set this to the highest tested version." + tr("Sets the target SDK. Set this to the highest tested version. " "This disables compatibility behavior of the system for your application.")); m_androidTargetSdkVersion->addItem(tr("Not set"), 0); @@ -471,7 +470,7 @@ bool AndroidManifestEditorWidget::eventFilter(QObject *obj, QEvent *event) void AndroidManifestEditorWidget::updateTargetComboBox() { - const QString docPath(static_cast<AndroidManifestDocument *>(editor()->document())->filePath()); + const QString docPath(baseTextDocument()->filePath()); ProjectExplorer::Project *project = androidProject(docPath); QStringList items; if (project) { @@ -588,7 +587,7 @@ void AndroidManifestEditorWidget::preSave() syncToEditor(); if (m_setAppName && m_appNameInStringsXml) { - QString baseDir = QFileInfo(static_cast<AndroidManifestDocument *>(editor()->document())->filePath()).absolutePath(); + QString baseDir = QFileInfo(baseTextDocument()->filePath()).absolutePath(); QString fileName = baseDir + QLatin1String("/res/values/strings.xml"); QFile f(fileName); if (f.open(QIODevice::ReadOnly)) { @@ -612,7 +611,7 @@ void AndroidManifestEditorWidget::preSave() m_setAppName = false; } - QString baseDir = QFileInfo(static_cast<AndroidManifestDocument *>(editor()->document())->filePath()).absolutePath(); + QString baseDir = QFileInfo(baseTextDocument()->filePath()).absolutePath(); if (!m_lIconPath.isEmpty()) { copyIcon(LowDPI, baseDir, m_lIconPath); m_lIconPath.clear(); @@ -696,7 +695,7 @@ void AndroidManifestEditorWidget::updateInfoBar() void AndroidManifestEditorWidget::updateSdkVersions() { - const QString docPath(static_cast<AndroidManifestDocument *>(editor()->document())->filePath()); + const QString docPath(baseTextDocument()->filePath()); Project *project = androidProject(docPath); QPair<int, int> apiLevels = AndroidManager::apiLevelRange(project ? project->activeTarget() : 0); for (int i = apiLevels.first; i < apiLevels.second + 1; ++i) @@ -714,7 +713,7 @@ void AndroidManifestEditorWidget::updateSdkVersions() void AndroidManifestEditorWidget::updateInfoBar(const QString &errorMessage, int line, int column) { - Core::InfoBar *infoBar = editorDocument()->infoBar(); + Core::InfoBar *infoBar = baseTextDocument()->infoBar(); QString text; if (line < 0) text = tr("Could not parse file: '%1'.").arg(errorMessage); @@ -732,7 +731,7 @@ void AndroidManifestEditorWidget::updateInfoBar(const QString &errorMessage, int void AndroidManifestEditorWidget::hideInfoBar() { - Core::InfoBar *infoBar = editorDocument()->infoBar(); + Core::InfoBar *infoBar = baseTextDocument()->infoBar(); infoBar->removeInfo(infoBarId); m_timerParseCheck.stop(); } @@ -771,7 +770,7 @@ void AndroidManifestEditorWidget::syncToWidgets(const QDomDocument &doc) setApiLevel(m_androidMinSdkVersion, usesSdkElement, QLatin1String("android:minSdkVersion")); setApiLevel(m_androidTargetSdkVersion, usesSdkElement, QLatin1String("android:targetSdkVersion")); - QString baseDir = QFileInfo(static_cast<AndroidManifestDocument *>(editor()->document())->filePath()).absolutePath(); + QString baseDir = QFileInfo(baseTextDocument()->filePath()).absolutePath(); QString fileName = baseDir + QLatin1String("/res/values/strings.xml"); QDomElement applicationElement = manifest.firstChildElement(QLatin1String("application")); diff --git a/src/plugins/android/androidmanifesteditorwidget.h b/src/plugins/android/androidmanifesteditorwidget.h index 74b9862cc7..eada4a006c 100644 --- a/src/plugins/android/androidmanifesteditorwidget.h +++ b/src/plugins/android/androidmanifesteditorwidget.h @@ -51,8 +51,6 @@ QT_END_NAMESPACE namespace Core { class IEditor; } -namespace TextEditor { class TextEditorActionHandler; } - namespace Android { namespace Internal { class AndroidManifestEditor; @@ -86,7 +84,7 @@ public: Source }; - explicit AndroidManifestEditorWidget(QWidget *parent, TextEditor::TextEditorActionHandler *ah); + explicit AndroidManifestEditorWidget(QWidget *parent); bool open(QString *errorString, const QString &fileName, const QString &realFileName); diff --git a/src/plugins/android/androidpotentialkit.cpp b/src/plugins/android/androidpotentialkit.cpp index 6eb93201c4..06aea43f10 100644 --- a/src/plugins/android/androidpotentialkit.cpp +++ b/src/plugins/android/androidpotentialkit.cpp @@ -76,6 +76,7 @@ AndroidPotentialKitWidget::AndroidPotentialKitWidget(QWidget *parent) : Utils::DetailsWidget(parent) { setSummaryText(QLatin1String("<b>Create Android Kits</b>")); + setIcon(QIcon(QLatin1String(Constants::ANDROID_SETTINGS_CATEGORY_ICON))); //detailsWidget->setState(Utils::DetailsWidget::NoSummary); QWidget *mainWidget = new QWidget(this); setWidget(mainWidget); @@ -84,7 +85,7 @@ AndroidPotentialKitWidget::AndroidPotentialKitWidget(QWidget *parent) layout->setMargin(0); QLabel *label = new QLabel; label->setText(tr("Qt Creator needs additional settings to enable Android support." - "You can configure those settings in the Options dialog.")); + " You can configure those settings in the Options dialog.")); label->setWordWrap(true); layout->addWidget(label, 0, 0, 1, 2); diff --git a/src/plugins/android/androidrunconfiguration.cpp b/src/plugins/android/androidrunconfiguration.cpp index 523981646a..5e45f39f5c 100644 --- a/src/plugins/android/androidrunconfiguration.cpp +++ b/src/plugins/android/androidrunconfiguration.cpp @@ -150,14 +150,6 @@ const QString AndroidRunConfiguration::remoteChannel() const return QLatin1String(":5039"); } -const QString AndroidRunConfiguration::dumperLib() const -{ - QtSupport::BaseQtVersion *version = QtSupport::QtKitInformation::qtVersion(target()->kit()); - if (!version) - return QString(); - return version->gdbDebuggingHelperLibrary(); -} - QString AndroidRunConfiguration::proFilePath() const { return m_proFilePath; diff --git a/src/plugins/android/androidrunconfiguration.h b/src/plugins/android/androidrunconfiguration.h index 6b8faa3e6a..0263386567 100644 --- a/src/plugins/android/androidrunconfiguration.h +++ b/src/plugins/android/androidrunconfiguration.h @@ -59,7 +59,6 @@ public: QString proFilePath() const; const QString remoteChannel() const; - const QString dumperLib() const; bool isEnabled() const; QString disabledReason() const; diff --git a/src/plugins/android/androidruncontrol.cpp b/src/plugins/android/androidruncontrol.cpp index 4134507d2a..a3563c9125 100644 --- a/src/plugins/android/androidruncontrol.cpp +++ b/src/plugins/android/androidruncontrol.cpp @@ -107,7 +107,7 @@ QString AndroidRunControl::displayName() const QIcon AndroidRunControl::icon() const { - return QIcon(QLatin1String(ProjectExplorer::Constants::ICON_DEBUG_SMALL)); + return QIcon(QLatin1String(ProjectExplorer::Constants::ICON_RUN_SMALL)); } } // namespace Internal diff --git a/src/plugins/android/androidrunner.cpp b/src/plugins/android/androidrunner.cpp index 7370d89c5c..8045dd6dbe 100644 --- a/src/plugins/android/androidrunner.cpp +++ b/src/plugins/android/androidrunner.cpp @@ -217,7 +217,7 @@ void AndroidRunner::asyncStart() emit remoteProcessFinished(tr("Failed to forward C++ debugging ports. Reason: %1.").arg(adb.errorString())); return; } - if (!adb.waitForFinished(-1)) { + if (!adb.waitForFinished(5000)) { emit remoteProcessFinished(tr("Failed to forward C++ debugging ports.")); return; } @@ -260,7 +260,7 @@ void AndroidRunner::asyncStart() emit remoteProcessFinished(tr("Failed to start the activity. Reason: %1.").arg(adb.errorString())); return; } - if (!adb.waitForFinished(-1)) { + if (!adb.waitForFinished(5000)) { adb.terminate(); emit remoteProcessFinished(tr("Unable to start '%1'.").arg(m_packageName)); return; diff --git a/src/plugins/android/androidsettingspage.cpp b/src/plugins/android/androidsettingspage.cpp index a38d81877f..09053b532a 100644 --- a/src/plugins/android/androidsettingspage.cpp +++ b/src/plugins/android/androidsettingspage.cpp @@ -52,16 +52,10 @@ AndroidSettingsPage::AndroidSettingsPage(QObject *parent) setCategoryIcon(QLatin1String(Constants::ANDROID_SETTINGS_CATEGORY_ICON)); } -bool AndroidSettingsPage::matches(const QString &searchKeyWord) const +QWidget *AndroidSettingsPage::widget() { - return m_keywords.contains(searchKeyWord, Qt::CaseInsensitive); -} - -QWidget *AndroidSettingsPage::createPage(QWidget *parent) -{ - m_widget = new AndroidSettingsWidget(parent); - if (m_keywords.isEmpty()) - m_keywords = m_widget->searchKeywords(); + if (!m_widget) + m_widget = new AndroidSettingsWidget; return m_widget; } @@ -97,6 +91,7 @@ void AndroidSettingsPage::apply() void AndroidSettingsPage::finish() { + delete m_widget; } } // namespace Internal diff --git a/src/plugins/android/androidsettingspage.h b/src/plugins/android/androidsettingspage.h index 1cecf78e36..05ac1be110 100644 --- a/src/plugins/android/androidsettingspage.h +++ b/src/plugins/android/androidsettingspage.h @@ -32,6 +32,8 @@ #include <coreplugin/dialogs/ioptionspage.h> +#include <QPointer> + namespace Android { namespace Internal { @@ -44,14 +46,12 @@ class AndroidSettingsPage : public Core::IOptionsPage public: explicit AndroidSettingsPage(QObject *parent = 0); - bool matches(const QString &searchKeyWord) const; - QWidget *createPage(QWidget *parent); + QWidget *widget(); void apply(); void finish(); private: - QString m_keywords; - AndroidSettingsWidget *m_widget; + QPointer<AndroidSettingsWidget> m_widget; }; } // namespace Internal diff --git a/src/plugins/android/androidsettingswidget.cpp b/src/plugins/android/androidsettingswidget.cpp index 1c16ccda17..3b8a16844f 100644 --- a/src/plugins/android/androidsettingswidget.cpp +++ b/src/plugins/android/androidsettingswidget.cpp @@ -129,24 +129,6 @@ AndroidSettingsWidget::~AndroidSettingsWidget() delete m_ui; } -QString AndroidSettingsWidget::searchKeywords() const -{ - QString rc; - QTextStream(&rc) << m_ui->SDKLocationLabel->text() - << ' ' << m_ui->SDKLocationLineEdit->text() - << ' ' << m_ui->NDKLocationLabel->text() - << ' ' << m_ui->NDKLocationLineEdit->text() - << ' ' << m_ui->AntLocationLabel->text() - << ' ' << m_ui->AntLocationLineEdit->text() - << ' ' << m_ui->OpenJDKLocationLabel->text() - << ' ' << m_ui->OpenJDKLocationLineEdit->text() - << ' ' << m_ui->AVDManagerLabel->text() - << ' ' << m_ui->DataPartitionSizeLable->text() - << ' ' << m_ui->DataPartitionSizeSpinBox->text(); - rc.remove(QLatin1Char('&')); - return rc; -} - void AndroidSettingsWidget::initGui() { m_ui->setupUi(this); diff --git a/src/plugins/android/androidsettingswidget.h b/src/plugins/android/androidsettingswidget.h index 145785ab40..fd2aa9dafb 100644 --- a/src/plugins/android/androidsettingswidget.h +++ b/src/plugins/android/androidsettingswidget.h @@ -66,11 +66,10 @@ class AndroidSettingsWidget : public QWidget Q_OBJECT public: // Todo: This would be so much simpler if it just used Utils::PathChooser!!! - AndroidSettingsWidget(QWidget *parent); + AndroidSettingsWidget(QWidget *parent = 0); ~AndroidSettingsWidget(); void saveSettings(bool saveNow = false); - QString searchKeywords() const; private slots: void sdkLocationEditingFinished(); diff --git a/src/plugins/android/androidsettingswidget.ui b/src/plugins/android/androidsettingswidget.ui index 4c4c1d8bd0..985ccde98c 100644 --- a/src/plugins/android/androidsettingswidget.ui +++ b/src/plugins/android/androidsettingswidget.ui @@ -214,7 +214,7 @@ </sizepolicy> </property> <property name="text"> - <string>Ant location:</string> + <string>Ant executable:</string> </property> <property name="alignment"> <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> @@ -323,7 +323,7 @@ <item> <widget class="QPushButton" name="manageAVDPushButton"> <property name="text"> - <string>Start Android AVD Manager</string> + <string>Start AVD Manager</string> </property> </widget> </item> diff --git a/src/plugins/autotoolsprojectmanager/autotoolsproject.cpp b/src/plugins/autotoolsprojectmanager/autotoolsproject.cpp index 8877a661f6..9f86d71425 100644 --- a/src/plugins/autotoolsprojectmanager/autotoolsproject.cpp +++ b/src/plugins/autotoolsprojectmanager/autotoolsproject.cpp @@ -78,6 +78,7 @@ AutotoolsProject::AutotoolsProject(AutotoolsManager *manager, const QString &fil m_watchedFiles(), m_makefileParserThread(0) { + setId(Constants::AUTOTOOLS_PROJECT_ID); setProjectContext(Core::Context(Constants::PROJECT_CONTEXT)); setProjectLanguages(Core::Context(ProjectExplorer::Constants::LANG_CXX)); @@ -108,11 +109,6 @@ QString AutotoolsProject::displayName() const return m_projectName; } -Core::Id AutotoolsProject::id() const -{ - return Core::Id(Constants::AUTOTOOLS_PROJECT_ID); -} - Core::IDocument *AutotoolsProject::document() const { return m_file; @@ -437,7 +433,7 @@ void AutotoolsProject::updateCppCodeModel() part->files << CppTools::ProjectFile(file, CppTools::ProjectFile::CXXSource); part->includePaths += m_makefileParserThread->includePaths(); - part->defines += m_makefileParserThread->defines(); + part->projectDefines += m_makefileParserThread->defines(); pinfo.appendProjectPart(part); modelManager->updateProjectInfo(pinfo); diff --git a/src/plugins/autotoolsprojectmanager/autotoolsproject.h b/src/plugins/autotoolsprojectmanager/autotoolsproject.h index 862e1204d7..2ba726f6ef 100644 --- a/src/plugins/autotoolsprojectmanager/autotoolsproject.h +++ b/src/plugins/autotoolsprojectmanager/autotoolsproject.h @@ -73,7 +73,6 @@ public: ~AutotoolsProject(); QString displayName() const; - Core::Id id() const; Core::IDocument *document() const; ProjectExplorer::IProjectManager *projectManager() const; ProjectExplorer::ProjectNode *rootProjectNode() const; diff --git a/src/plugins/bazaar/bazaar.pro b/src/plugins/bazaar/bazaar.pro index 06259346df..f3a266221b 100644 --- a/src/plugins/bazaar/bazaar.pro +++ b/src/plugins/bazaar/bazaar.pro @@ -13,7 +13,8 @@ SOURCES += \ branchinfo.cpp \ clonewizardpage.cpp \ clonewizard.cpp \ - cloneoptionspanel.cpp + cloneoptionspanel.cpp \ + uncommitdialog.cpp HEADERS += \ bazaarclient.h \ constants.h \ @@ -29,11 +30,13 @@ HEADERS += \ branchinfo.h \ clonewizard.h \ clonewizardpage.h \ - cloneoptionspanel.h + cloneoptionspanel.h \ + uncommitdialog.h FORMS += \ optionspage.ui \ revertdialog.ui \ bazaarcommitpanel.ui \ pullorpushdialog.ui \ - cloneoptionspanel.ui + cloneoptionspanel.ui \ + uncommitdialog.ui RESOURCES += bazaar.qrc diff --git a/src/plugins/bazaar/bazaar.qbs b/src/plugins/bazaar/bazaar.qbs index 8a5791e6be..54cbc804f0 100644 --- a/src/plugins/bazaar/bazaar.qbs +++ b/src/plugins/bazaar/bazaar.qbs @@ -49,6 +49,9 @@ QtcPlugin { "pullorpushdialog.h", "pullorpushdialog.ui", "revertdialog.ui", + "uncommitdialog.cpp", + "uncommitdialog.h", + "uncommitdialog.ui", "images/bazaar.png", ] } diff --git a/src/plugins/bazaar/bazaarclient.cpp b/src/plugins/bazaar/bazaarclient.cpp index d7af1e4e73..943da0fdb8 100644 --- a/src/plugins/bazaar/bazaarclient.cpp +++ b/src/plugins/bazaar/bazaarclient.cpp @@ -30,6 +30,7 @@ #include "constants.h" #include <vcsbase/vcsbaseplugin.h> +#include <vcsbase/vcsbaseoutputwindow.h> #include <vcsbase/vcsbaseeditorparameterwidget.h> #include <utils/synchronousprocess.h> @@ -102,6 +103,24 @@ BranchInfo BazaarClient::synchronousBranchQuery(const QString &repositoryRoot) c return BranchInfo(repositoryRoot, false); } +//! Removes the last committed revision(s) +bool BazaarClient::synchronousUncommit(const QString &workingDir, + const QString &revision, + const QStringList &extraOptions) +{ + QStringList args; + args << QLatin1String("uncommit") + << QLatin1String("--force") // Say yes to all questions + << QLatin1String("--verbose") // Will print out what is being removed + << revisionSpec(revision) + << extraOptions; + QByteArray stdOut; + const bool success = vcsFullySynchronousExec(workingDir, args, &stdOut); + if (!stdOut.isEmpty()) + VcsBase::VcsBaseOutputWindow::instance()->append(QString::fromUtf8(stdOut)); + return success; +} + void BazaarClient::commit(const QString &repositoryRoot, const QStringList &files, const QString &commitMessageFile, const QStringList &extraOptions) { diff --git a/src/plugins/bazaar/bazaarclient.h b/src/plugins/bazaar/bazaarclient.h index 1612034989..b0dc2d5bae 100644 --- a/src/plugins/bazaar/bazaarclient.h +++ b/src/plugins/bazaar/bazaarclient.h @@ -49,6 +49,9 @@ public: bool synchronousSetUserId(); BranchInfo synchronousBranchQuery(const QString &repositoryRoot) const; + bool synchronousUncommit(const QString &workingDir, + const QString& revision = QString(), + const QStringList &extraOptions = QStringList()); void commit(const QString &repositoryRoot, const QStringList &files, const QString &commitMessageFile, const QStringList &extraOptions = QStringList()); void annotate(const QString &workingDir, const QString &file, diff --git a/src/plugins/bazaar/bazaarplugin.cpp b/src/plugins/bazaar/bazaarplugin.cpp index 3e7f238ee1..cf79874a2f 100644 --- a/src/plugins/bazaar/bazaarplugin.cpp +++ b/src/plugins/bazaar/bazaarplugin.cpp @@ -34,6 +34,7 @@ #include "bazaarcommitwidget.h" #include "bazaareditor.h" #include "pullorpushdialog.h" +#include "uncommitdialog.h" #include "commiteditor.h" #include "clonewizard.h" @@ -421,6 +422,13 @@ void BazaarPlugin::createRepositoryActions(const Core::Context &context) m_bazaarContainer->addAction(command); m_commandLocator->appendCommand(command); + action = new QAction(tr("Uncommit..."), this); + m_repositoryActionList.append(action); + command = Core::ActionManager::registerAction(action, Core::Id(Constants::UNCOMMIT), context); + connect(action, SIGNAL(triggered()), this, SLOT(uncommit())); + m_bazaarContainer->addAction(command); + m_commandLocator->appendCommand(command); + QAction *createRepositoryAction = new QAction(tr("Create Repository..."), this); command = Core::ActionManager::registerAction(createRepositoryAction, Core::Id(Constants::CREATE_REPOSITORY), context); connect(createRepositoryAction, SIGNAL(triggered()), this, SLOT(createRepository())); @@ -640,6 +648,16 @@ void BazaarPlugin::commitFromEditor() Core::EditorManager::closeEditor(); } +void BazaarPlugin::uncommit() +{ + const VcsBase::VcsBasePluginState state = currentState(); + QTC_ASSERT(state.hasTopLevel(), return); + + UnCommitDialog dialog; + if (dialog.exec() == QDialog::Accepted) + m_client->synchronousUncommit(state.topLevel(), dialog.revision(), dialog.extraOptions()); +} + bool BazaarPlugin::submitEditorAboutToClose() { CommitEditor *commitEditor = qobject_cast<CommitEditor *>(submitEditor()); diff --git a/src/plugins/bazaar/bazaarplugin.h b/src/plugins/bazaar/bazaarplugin.h index 955795dd3d..382ea438e4 100644 --- a/src/plugins/bazaar/bazaarplugin.h +++ b/src/plugins/bazaar/bazaarplugin.h @@ -106,6 +106,7 @@ private slots: void commit(); void showCommitWidget(const QList<VcsBase::VcsBaseClient::StatusItem> &status); void commitFromEditor(); + void uncommit(); void diffFromEditorSelected(const QStringList &files); #ifdef WITH_TESTS void testDiffFileResolving_data(); diff --git a/src/plugins/bazaar/constants.h b/src/plugins/bazaar/constants.h index 5848733672..54e9505004 100644 --- a/src/plugins/bazaar/constants.h +++ b/src/plugins/bazaar/constants.h @@ -87,6 +87,7 @@ const char PULL[] = "Bazaar.Action.Pull"; const char PUSH[] = "Bazaar.Action.Push"; const char UPDATE[] = "Bazaar.Action.Update"; const char COMMIT[] = "Bazaar.Action.Commit"; +const char UNCOMMIT[] = "Bazaar.Action.UnCommit"; const char CREATE_REPOSITORY[] = "Bazaar.Action.CreateRepository"; // Submit editor actions diff --git a/src/plugins/bazaar/optionspage.cpp b/src/plugins/bazaar/optionspage.cpp index 376a7b9871..e58e30b810 100644 --- a/src/plugins/bazaar/optionspage.cpp +++ b/src/plugins/bazaar/optionspage.cpp @@ -44,6 +44,7 @@ OptionsPageWidget::OptionsPageWidget(QWidget *parent) m_ui.setupUi(this); m_ui.commandChooser->setExpectedKind(Utils::PathChooser::ExistingCommand); m_ui.commandChooser->setPromptDialogTitle(tr("Bazaar Command")); + m_ui.commandChooser->setHistoryCompleter(QLatin1String("Bazaar.Command.History")); } BazaarSettings OptionsPageWidget::settings() const @@ -66,37 +67,17 @@ void OptionsPageWidget::setSettings(const BazaarSettings &s) m_ui.timeout->setValue(s.intValue(BazaarSettings::timeoutKey)); } -QString OptionsPageWidget::searchKeywords() const -{ - QString rc; - QLatin1Char sep(' '); - QTextStream(&rc) - << sep << m_ui.configGroupBox->title() - << sep << m_ui.commandLabel->text() - << sep << m_ui.userGroupBox->title() - << sep << m_ui.defaultUsernameLabel->text() - << sep << m_ui.defaultEmailLabel->text() - << sep << m_ui.miscGroupBox->title() - << sep << m_ui.showLogEntriesLabel->text() - << sep << m_ui.timeoutSecondsLabel->text() - ; - rc.remove(QLatin1Char('&')); - return rc; -} - OptionsPage::OptionsPage() { setId(VcsBase::Constants::VCS_ID_BAZAAR); setDisplayName(tr("Bazaar")); } -QWidget *OptionsPage::createPage(QWidget *parent) +QWidget *OptionsPage::widget() { if (!m_optionsPageWidget) - m_optionsPageWidget = new OptionsPageWidget(parent); + m_optionsPageWidget = new OptionsPageWidget; m_optionsPageWidget->setSettings(BazaarPlugin::instance()->settings()); - if (m_searchKeywords.isEmpty()) - m_searchKeywords = m_optionsPageWidget->searchKeywords(); return m_optionsPageWidget; } @@ -113,8 +94,3 @@ void OptionsPage::apply() emit settingsChanged(); } } - -bool OptionsPage::matches(const QString &s) const -{ - return m_searchKeywords.contains(s, Qt::CaseInsensitive); -} diff --git a/src/plugins/bazaar/optionspage.h b/src/plugins/bazaar/optionspage.h index fe87c38150..c0493480d8 100644 --- a/src/plugins/bazaar/optionspage.h +++ b/src/plugins/bazaar/optionspage.h @@ -50,7 +50,6 @@ public: BazaarSettings settings() const; void setSettings(const BazaarSettings &s); - QString searchKeywords() const; private: Ui::OptionsPage m_ui; @@ -64,16 +63,14 @@ class OptionsPage : public VcsBase::VcsBaseOptionsPage public: OptionsPage(); - QWidget *createPage(QWidget *parent); + QWidget *widget(); void apply(); void finish() { } - bool matches(const QString &s) const; signals: void settingsChanged(); private: - QString m_searchKeywords; QPointer<OptionsPageWidget> m_optionsPageWidget; }; diff --git a/src/plugins/bazaar/uncommitdialog.cpp b/src/plugins/bazaar/uncommitdialog.cpp new file mode 100644 index 0000000000..30c7c75451 --- /dev/null +++ b/src/plugins/bazaar/uncommitdialog.cpp @@ -0,0 +1,83 @@ +/************************************************************************** +** +** Copyright (c) 2013 Hugues Delorme +** 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 "uncommitdialog.h" + +#include "ui_uncommitdialog.h" +#include "bazaarclient.h" +#include "bazaarplugin.h" +#include <utils/qtcassert.h> + +#include <QPushButton> + +namespace Bazaar { +namespace Internal { + +UnCommitDialog::UnCommitDialog(QWidget *parent) + : QDialog(parent), + m_ui(new Ui::UnCommitDialog) +{ + m_ui->setupUi(this); + + QPushButton* dryRunBtn = new QPushButton(tr("Dry Run")); + dryRunBtn->setToolTip(tr("Test the outcome of removing the last committed revision, without actually removing anything.")); + m_ui->buttonBox->addButton(dryRunBtn, QDialogButtonBox::ApplyRole); + connect(dryRunBtn, SIGNAL(clicked()), this, SLOT(dryRun())); +} + +UnCommitDialog::~UnCommitDialog() +{ + delete m_ui; +} + +QStringList UnCommitDialog::extraOptions() const +{ + QStringList opts; + if (m_ui->keepTagsCheckBox->isChecked()) + opts += QLatin1String("--keep-tags"); + if (m_ui->localCheckBox->isChecked()) + opts += QLatin1String("--local"); + return opts; +} + +QString UnCommitDialog::revision() const +{ + return m_ui->revisionLineEdit->text().trimmed(); +} + +void UnCommitDialog::dryRun() +{ + BazaarPlugin* bzrPlugin = BazaarPlugin::instance(); + QTC_ASSERT(bzrPlugin->currentState().hasTopLevel(), return); + bzrPlugin->client()->synchronousUncommit(bzrPlugin->currentState().topLevel(), + revision(), + extraOptions() << QLatin1String("--dry-run")); +} + +} // namespace Internal +} // namespace Bazaar diff --git a/src/plugins/bazaar/uncommitdialog.h b/src/plugins/bazaar/uncommitdialog.h new file mode 100644 index 0000000000..9a59db18c1 --- /dev/null +++ b/src/plugins/bazaar/uncommitdialog.h @@ -0,0 +1,62 @@ +/************************************************************************** +** +** Copyright (c) 2013 Hugues Delorme +** 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. +** +****************************************************************************/ +#ifndef UNCOMMITDIALOG_H +#define UNCOMMITDIALOG_H + +#include <QDialog> + +namespace Bazaar { +namespace Internal { + +namespace Ui { +class UnCommitDialog; +} + +class UnCommitDialog : public QDialog +{ + Q_OBJECT + +public: + explicit UnCommitDialog(QWidget *parent = 0); + ~UnCommitDialog(); + + QStringList extraOptions() const; + QString revision() const; + +private slots: + void dryRun(); + +private: + Ui::UnCommitDialog *m_ui; +}; + +} // namespace Internal +} // namespace Bazaar + +#endif // UNCOMMITDIALOG_H diff --git a/src/plugins/bazaar/uncommitdialog.ui b/src/plugins/bazaar/uncommitdialog.ui new file mode 100644 index 0000000000..f08a8c8a7a --- /dev/null +++ b/src/plugins/bazaar/uncommitdialog.ui @@ -0,0 +1,104 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>Bazaar::Internal::UnCommitDialog</class> + <widget class="QDialog" name="Bazaar::Internal::UnCommitDialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>412</width> + <height>124</height> + </rect> + </property> + <property name="windowTitle"> + <string>Uncommit</string> + </property> + <layout class="QFormLayout" name="formLayout"> + <item row="0" column="0" colspan="2"> + <widget class="QCheckBox" name="keepTagsCheckBox"> + <property name="toolTip"> + <string/> + </property> + <property name="text"> + <string>Keep tags that point to removed revisions</string> + </property> + </widget> + </item> + <item row="1" column="0" colspan="2"> + <widget class="QCheckBox" name="localCheckBox"> + <property name="text"> + <string>Only remove the commits from the local branch when in a checkout</string> + </property> + </widget> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="revisionLabel"> + <property name="text"> + <string>Revision:</string> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="QLineEdit" name="revisionLineEdit"> + <property name="toolTip"> + <string>If a revision is specified, uncommits revisions to leave the branch at the specified revision. +For example, "Revision: 15" will leave the branch at revision 15.</string> + </property> + <property name="placeholderText"> + <string>Last committed</string> + </property> + </widget> + </item> + <item row="3" column="1"> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + </layout> + <zorder>buttonBox</zorder> + <zorder>revisionLabel</zorder> + <zorder>revisionLineEdit</zorder> + <zorder>keepTagsCheckBox</zorder> + <zorder>localCheckBox</zorder> + </widget> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>Bazaar::Internal::UnCommitDialog</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel"> + <x>248</x> + <y>254</y> + </hint> + <hint type="destinationlabel"> + <x>157</x> + <y>274</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>Bazaar::Internal::UnCommitDialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel"> + <x>316</x> + <y>260</y> + </hint> + <hint type="destinationlabel"> + <x>286</x> + <y>274</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/src/plugins/clangcodemodel/ClangCodeModel.pluginspec.in b/src/plugins/clangcodemodel/ClangCodeModel.pluginspec.in new file mode 100644 index 0000000000..6ec7131af0 --- /dev/null +++ b/src/plugins/clangcodemodel/ClangCodeModel.pluginspec.in @@ -0,0 +1,21 @@ +<plugin name=\"ClangCodeModel\" version=\"$$QTCREATOR_VERSION\" compatVersion=\"$$QTCREATOR_VERSION\" experimental=\"true\"> + <vendor>Digia Plc</vendor> + <copyright>(C) 2013 Digia Plc</copyright> + <license> +Commercial Usage + +Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt Commercial License Agreement provided with the Software or, alternatively, in accordance with the terms contained in a written agreement between you and Nokia. + +GNU Lesser General Public License Usage + +Alternatively, this plugin may be used under the terms of the GNU Lesser General Public License version 2.1 as published by the Free Software Foundation. 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. + </license> + <category>C++</category> + <description>Clang Code Model plugin.</description> + <url>http://www.qt-project.org</url> + <dependencyList> + <dependency name=\"Core\" version=\"$$QTCREATOR_VERSION\"/> + <dependency name=\"CppTools\" version=\"$$QTCREATOR_VERSION\"/> + <dependency name=\"TextEditor\" version=\"$$QTCREATOR_VERSION\"/> + </dependencyList> +</plugin> diff --git a/src/plugins/clangcodemodel/README b/src/plugins/clangcodemodel/README new file mode 100644 index 0000000000..8612113270 --- /dev/null +++ b/src/plugins/clangcodemodel/README @@ -0,0 +1,65 @@ +The ClangCodeModel plugin +========================= + +The ClangCodeModel plugin integrates the clang frontend into Qt Creator. Clang +is "a C language family frontend for LLVM". You can find more information at +http://clang.llvm.org/. + +At the time of writing the plugin can replace the following functionality of +the built-in code model: + * Highlighting + * Completion + +All other functionality relies on the built-in code model (indexing, quick +fixes, follow symbol, find usages, ...). + +Setup +===== + +Compile the plugin +------------------ + +1. Get libclang + +You need to have libclang (and thus llvm) installed on your system. Either +build llvm/clang yourself [1], install some ready-to-use package [2] or use the +package manager of your system. + + [1] http://clang.llvm.org/get_started.html + See http://llvm.org/docs/GettingStarted.html#git-mirror for git mirrors. + [2] http://llvm.org/releases/ or http://llvm.org/builds/ + +If you are building llvm/clang yourself, make sure to build it in release mode. + +2. Set LLVM_INSTALL_DIR and (re)build Qt Creator + +Point the LLVM_INSTALL_DIR variable to the build/installation directory of +llvm, e.g.: + + Installed via package manager on GNU/Linux: + LLVM_INSTALL_DIR=/usr/lib/llvm-3.4 + Manually build on Unix in release mode: + LLVM_INSTALL_DIR=$HOME/llvm-build/Release+Asserts + Installed a snapshot on Windows: + LLVM_INSTALL_DIR=C:\llvm + +Set the variable either as part of the build environment or pass it directly to +qmake and rebuild Qt Creator. Watch out for a message like + + Project MESSAGE: Building ClangCodeModel plugin with Clang from /usr/lib/llvm-3.4 + Project MESSAGE: INCLUDEPATH += /usr/lib/llvm-3.4/include + Project MESSAGE: LIBS += -L/usr/lib/llvm-3.4/lib -lclang + +This indicates that the ClangCodeModel plugin will be build. + +Enable the plugin +----------------- + +Enable the "ClangCodeModel" plugin in the dialog "Menu: Help -> About Plugins" +and restart Qt Creator. + +Select the file types you want to use the ClangCodeModel for in "Menu: Tools -> +Options -> C++ -> Tab: Code Model". For the next opened file matching the +selected file types the ClangCodeModel will be used (see limitations at the +start of this README). + diff --git a/src/plugins/qmlprofiler/canvas/qdeclarativecanvastimer_p.h b/src/plugins/clangcodemodel/clang_global.h index 852431d718..199b62aaf5 100644 --- a/src/plugins/qmlprofiler/canvas/qdeclarativecanvastimer_p.h +++ b/src/plugins/clangcodemodel/clang_global.h @@ -27,36 +27,16 @@ ** ****************************************************************************/ -#ifndef QDECLARATIVECANVASTIMER_P_H -#define QDECLARATIVECANVASTIMER_P_H +#ifndef CLANG_GLOBAL_H +#define CLANG_GLOBAL_H -#include <QJSValue> -#include <qtimer.h> -#include <qlist.h> +#include <qglobal.h> -QT_BEGIN_NAMESPACE +#if defined(CLANGCODEMODEL_LIBRARY) +# define CLANG_EXPORT Q_DECL_EXPORT +#else +# define CLANG_EXPORT Q_DECL_IMPORT +#endif -class CanvasTimer : public QTimer -{ - Q_OBJECT -public: - CanvasTimer(QObject *parent, const QJSValue &data); - -public Q_SLOTS: - void handleTimeout(); - bool equals(const QJSValue &value){return m_value.equals(value);} - -public: - static void createTimer(QObject *parent, const QJSValue &val, long timeout, bool singleshot); - static void removeTimer(CanvasTimer *timer); - static void removeTimer(const QJSValue &); - -private: - QJSValue m_value; - -}; - -QT_END_NAMESPACE - -#endif // QDECLARATIVECANVASTIMER_P_H +#endif // CLANG_GLOBAL_H diff --git a/src/plugins/clangcodemodel/clang_installation.pri b/src/plugins/clangcodemodel/clang_installation.pri new file mode 100644 index 0000000000..5cc4638738 --- /dev/null +++ b/src/plugins/clangcodemodel/clang_installation.pri @@ -0,0 +1,89 @@ +isEmpty(LLVM_INSTALL_DIR):LLVM_INSTALL_DIR=$$(LLVM_INSTALL_DIR) + +DEFINES += CLANG_COMPLETION +DEFINES += CLANG_HIGHLIGHTING +#DEFINES += CLANG_INDEXING + +defineReplace(findLLVMConfig) { + LLVM_CONFIG_VARIANTS = \ + llvm-config llvm-config-3.2 llvm-config-3.3 llvm-config-3.4 \ + llvm-config-3.5 llvm-config-3.6 llvm-config-4.0 llvm-config-4.1 + + # Prefer llvm-config* from LLVM_INSTALL_DIR + !isEmpty(LLVM_INSTALL_DIR) { + for (variant, LLVM_CONFIG_VARIANTS) { + variant=$$LLVM_INSTALL_DIR/bin/$$variant + exists($$variant) { + return($$variant) + } + } + } + + # Find llvm-config* in PATH + ENV_PATH = $$(PATH) + win32 { + ENV_PATH = $$split($$ENV_PATH, ;) + } else { + ENV_PATH = $$split($$ENV_PATH, :) + } + for (variant, LLVM_CONFIG_VARIANTS) { + for (path, ENV_PATH) { + subvariant = $$path/$$variant + exists($$subvariant) { + return($$subvariant) + } + } + } + + # Fallback + return(llvm-config) +} + +win32 { + LLVM_INCLUDEPATH = $$LLVM_INSTALL_DIR/include + exists ($${LLVM_INSTALL_DIR}/lib/clang.*) { + CLANG_LIB = clang + } else { + exists ($${LLVM_INSTALL_DIR}/lib/libclang.*) { + CLANG_LIB = libclang + } else { + error("Cannot find Clang shared library!") + } + } + LLVM_LIBS = \ + -L$$LLVM_INSTALL_DIR/bin \ + -L$$LLVM_INSTALL_DIR/lib \ + -l$${CLANG_LIB} + LLVM_LIBS += -ladvapi32 -lshell32 +} + +unix { + LLVM_CONFIG = $$findLLVMConfig() + + LLVM_INCLUDEPATH = $$system($$LLVM_CONFIG --includedir) + isEmpty(LLVM_INCLUDEPATH):LLVM_INCLUDEPATH=$$LLVM_INSTALL_DIR/include + LLVM_LIBDIR = $$system($$LLVM_CONFIG --libdir) + isEmpty(LLVM_LIBDIR):LLVM_LIBDIR=$$LLVM_INSTALL_DIR/lib + + exists ($${LLVM_LIBDIR}/libclang.*) { + #message("LLVM was build with autotools") + CLANG_LIB = clang + } else { + exists ($${LLVM_LIBDIR}/liblibclang.*) { + #message("LLVM was build with CMake") + CLANG_LIB = libclang + } else { + exists ($${LLVM_INSTALL_DIR}/lib/libclang.*) { + #message("libclang placed separately from LLVM") + CLANG_LIB = clang + LLVM_LIBDIR = $${LLVM_INSTALL_DIR}/lib + LLVM_INCLUDEPATH=$${LLVM_INSTALL_DIR}/include + } else { + error("Cannot find Clang shared library!") + } + } + } + + LLVM_LIBS = -L$${LLVM_LIBDIR} + LLVM_LIBS += -l$${CLANG_LIB} +} diff --git a/src/plugins/clangcodemodel/clangcodemodel.pro b/src/plugins/clangcodemodel/clangcodemodel.pro new file mode 100644 index 0000000000..9267be19c1 --- /dev/null +++ b/src/plugins/clangcodemodel/clangcodemodel.pro @@ -0,0 +1,126 @@ +include(../../qtcreatorplugin.pri) +include(clang_installation.pri) + +message("Building ClangCodeModel plugin with Clang from $$LLVM_INSTALL_DIR") +message(" INCLUDEPATH += $$LLVM_INCLUDEPATH") +message(" LIBS += $$LLVM_LIBS") + +LIBS += $$LLVM_LIBS +INCLUDEPATH += $$LLVM_INCLUDEPATH +DEFINES += CLANGCODEMODEL_LIBRARY + +unix:QMAKE_LFLAGS += -Wl,-rpath,\'$$LLVM_LIBDIR\' + +contains(DEFINES, CLANG_COMPLETION) { + HEADERS += clangcompletion.h clangcompleter.h completionproposalsbuilder.h + SOURCES += clangcompletion.cpp clangcompleter.cpp completionproposalsbuilder.cpp +} + +contains(DEFINES, CLANG_HIGHLIGHTING) { + HEADERS += cppcreatemarkers.h clanghighlightingsupport.h + SOURCES += cppcreatemarkers.cpp clanghighlightingsupport.cpp +} + +HEADERS += clangutils.h \ + cxprettyprinter.h + +SOURCES += clangutils.cpp \ + cxprettyprinter.cpp + +SOURCES += \ + $$PWD/clangcodemodelplugin.cpp \ + $$PWD/sourcemarker.cpp \ + $$PWD/symbol.cpp \ + $$PWD/sourcelocation.cpp \ + $$PWD/unit.cpp \ + $$PWD/utils.cpp \ + $$PWD/utils_p.cpp \ + $$PWD/liveunitsmanager.cpp \ + $$PWD/semanticmarker.cpp \ + $$PWD/diagnostic.cpp \ + $$PWD/unsavedfiledata.cpp \ + $$PWD/fastindexer.cpp \ + $$PWD/pchinfo.cpp \ + $$PWD/pchmanager.cpp \ + $$PWD/clangprojectsettings.cpp \ + $$PWD/clangprojectsettingspropertiespage.cpp \ + $$PWD/raii/scopedclangoptions.cpp \ + $$PWD/clangmodelmanagersupport.cpp + +HEADERS += \ + $$PWD/clangcodemodelplugin.h \ + $$PWD/clang_global.h \ + $$PWD/sourcemarker.h \ + $$PWD/constants.h \ + $$PWD/symbol.h \ + $$PWD/cxraii.h \ + $$PWD/sourcelocation.h \ + $$PWD/unit.h \ + $$PWD/utils.h \ + $$PWD/utils_p.h \ + $$PWD/liveunitsmanager.h \ + $$PWD/semanticmarker.h \ + $$PWD/diagnostic.h \ + $$PWD/unsavedfiledata.h \ + $$PWD/fastindexer.h \ + $$PWD/pchinfo.h \ + $$PWD/pchmanager.h \ + $$PWD/clangprojectsettings.h \ + $$PWD/clangprojectsettingspropertiespage.h \ + $$PWD/raii/scopedclangoptions.h \ + $$PWD/clangmodelmanagersupport.h + +contains(DEFINES, CLANG_INDEXING) { + HEADERS += \ + $$PWD/clangindexer.h \ + $$PWD/clangsymbolsearcher.h \ + $$PWD/index.h \ + $$PWD/indexer.h +# $$PWD/dependencygraph.h \ + + SOURCES += \ + $$PWD/clangindexer.cpp \ + $$PWD/clangsymbolsearcher.cpp \ + $$PWD/index.cpp \ + $$PWD/indexer.cpp +# $$PWD/dependencygraph.cpp \ +} + +equals(TEST, 1) { + RESOURCES += \ + $$PWD/test/clang_tests_database.qrc + + HEADERS += \ + $$PWD/test/completiontesthelper.h + + SOURCES += \ + $$PWD/test/completiontesthelper.cpp \ + $$PWD/test/clangcompletion_test.cpp + + OTHER_FILES += \ + $$PWD/test/cxx_regression_1.cpp \ + $$PWD/test/cxx_regression_2.cpp \ + $$PWD/test/cxx_regression_3.cpp \ + $$PWD/test/cxx_regression_4.cpp \ + $$PWD/test/cxx_regression_5.cpp \ + $$PWD/test/cxx_regression_6.cpp \ + $$PWD/test/cxx_regression_7.cpp \ + $$PWD/test/cxx_regression_8.cpp \ + $$PWD/test/cxx_regression_9.cpp \ + $$PWD/test/cxx_snippets_1.cpp \ + $$PWD/test/cxx_snippets_2.cpp \ + $$PWD/test/cxx_snippets_3.cpp \ + test/cxx_snippets_4.cpp \ + test/objc_messages_1.mm \ + test/objc_messages_2.mm \ + test/objc_messages_3.mm +} + +FORMS += $$PWD/clangprojectsettingspropertiespage.ui + +macx { + LIBCLANG_VERSION=3.3 + POSTL = install_name_tool -change "@executable_path/../lib/libclang.$${LIBCLANG_VERSION}.dylib" "$$LLVM_INSTALL_DIR/lib/libclang.$${LIBCLANG_VERSION}.dylib" "\"$${DESTDIR}/lib$${TARGET}.dylib\"" $$escape_expand(\\n\\t) + !isEmpty(QMAKE_POST_LINK):QMAKE_POST_LINK = $$escape_expand(\\n\\t)$$QMAKE_POST_LINK + QMAKE_POST_LINK = $$POSTL $$QMAKE_POST_LINK +} diff --git a/src/plugins/clangcodemodel/clangcodemodel.qbs b/src/plugins/clangcodemodel/clangcodemodel.qbs new file mode 100644 index 0000000000..1044984bdc --- /dev/null +++ b/src/plugins/clangcodemodel/clangcodemodel.qbs @@ -0,0 +1,191 @@ +import qbs +import qbs.File + +QtcPlugin { + name: "ClangCodeModel" + + Depends { name: "Qt"; submodules: ["concurrent", "widgets"] } + Depends { name: "Core" } + Depends { name: "CppTools" } + Depends { name: "ProjectExplorer" } + Depends { name: "TextEditor" } + Depends { name: "Utils" } + + property string llvmInstallDir: qbs.getenv("LLVM_INSTALL_DIR") + condition: llvmInstallDir && !llvmInstallDir.isEmpty + + property bool clangCompletion: true + property bool clangHighlighting: true + property bool clangIndexing: false + + // Not used atm; we just rely on the LLVM_INSTALL_DIR environment variable. + property string llvmConfig: { + var llvmConfigVariants = [ + "llvm-config", "llvm-config-3.2", "llvm-config-3.3", "llvm-config-3.4", + "llvm-config-3.5", "llvm-config-3.6", "llvm-config-4.0", "llvm-config-4.1" + ]; + + // Prefer llvm-config* from LLVM_INSTALL_DIR + for (var i = 0; i < llvmConfigVariants.length; ++i) { + var variant = llvmInstallDir + "/bin/" + llvmConfigVariants[i]; + if (File.exists(variant)) + return variant; + } + + // Find llvm-config* in PATH + var pathListString = qbs.getenv("PATH"); + var separator = qbs.hostOS.contains("windows") ? ";" : ":"; + var pathList = pathListString.split(separator); + for (var i = 0; i < llvmConfigVariants.length; ++i) { + for (var j = 0; j < pathList.length; ++j) { + var variant = pathList[j] + "/" + llvmConfigVariants[i]; + if (File.exists(variant)) + return variant; + } + } + + // Fallback + return "llvm-config"; + } + + property string llvmIncludeDir: llvmInstallDir + "/include" + cpp.includePaths: base.concat(llvmIncludeDir) + + property stringList llvmLibDirs: { + var list = [llvmInstallDir + "/lib"]; + if (qbs.targetOS.contains("windows")) + list.push(llvmInstallDir + "/bin"); + return list; + } + cpp.libraryPaths: base.concat(llvmLibDirs) + cpp.rpaths: cpp.libraryPaths + + property string llvmLib: "clang" + property stringList additionalLibraries: qbs.targetOS.contains("windows") + ? ["advapi32", "shell32"] : [] + cpp.dynamicLibraries: base.concat(llvmLib).concat(additionalLibraries) + + Group { + name: "Completion support" + condition: product.clangCompletion + files: [ + "clangcompleter.cpp", + "clangcompleter.h", + "clangcompletion.cpp", + "clangcompletion.h", + "completionproposalsbuilder.cpp", + "completionproposalsbuilder.h", + ] + } + + Group { + name: "Highlighting support" + condition: product.clangHighlighting + files: [ + "clanghighlightingsupport.cpp", + "clanghighlightingsupport.h", + "cppcreatemarkers.cpp", + "cppcreatemarkers.h", + ] + } + + Group { + name: "Indexing support" + condition: product.clangIndexing + files: [ + "clangindexer.cpp", + "clangindexer.h", + "clangsymbolsearcher.cpp", + "clangsymbolsearcher.h", + "index.cpp", + "index.h", + "indexer.cpp", + "indexer.h", + // "dependencygraph.h", + // "dependencygraph.cpp" + ] + } + + Group { + name: "Tests" + condition: project.testsEnabled + prefix: "test/" + files: [ + "clang_tests_database.qrc", + "clangcompletion_test.cpp", + "completiontesthelper.cpp", + "completiontesthelper.h", + ] + } + + Group { + name: "Test resources" + prefix: "test/" + fileTags: "none" + files: [ + "cxx_regression_1.cpp", + "cxx_regression_2.cpp", + "cxx_regression_3.cpp", + "cxx_regression_4.cpp", + "cxx_regression_5.cpp", + "cxx_regression_6.cpp", + "cxx_regression_7.cpp", + "cxx_regression_8.cpp", + "cxx_regression_9.cpp", + "cxx_snippets_1.cpp", + "cxx_snippets_2.cpp", + "cxx_snippets_3.cpp", + "cxx_snippets_4.cpp", + "objc_messages_1.mm", + "objc_messages_2.mm", + "objc_messages_3.mm", + ] + } + + files: [ + "clang_global.h", + "clangmodelmanagersupport.cpp", + "clangmodelmanagersupport.h", + "clangcodemodelplugin.cpp", + "clangcodemodelplugin.h", + "clangprojectsettings.cpp", + "clangprojectsettings.h", + "clangprojectsettingspropertiespage.cpp", + "clangprojectsettingspropertiespage.h", + "clangprojectsettingspropertiespage.ui", + "clangutils.cpp", + "clangutils.h", + "constants.h", + "cxprettyprinter.cpp", + "cxprettyprinter.h", + "cxraii.h", + "diagnostic.cpp", + "diagnostic.h", + "fastindexer.cpp", + "fastindexer.h", + "liveunitsmanager.cpp", + "liveunitsmanager.h", + "pchinfo.cpp", + "pchinfo.h", + "pchmanager.cpp", + "pchmanager.h", + "semanticmarker.cpp", + "semanticmarker.h", + "sourcelocation.cpp", + "sourcelocation.h", + "sourcemarker.cpp", + "sourcemarker.h", + "symbol.cpp", + "symbol.h", + "unit.cpp", + "unit.h", + "unsavedfiledata.cpp", + "unsavedfiledata.h", + "utils.cpp", + "utils.h", + "utils_p.cpp", + "utils_p.h", + "raii/scopedclangoptions.cpp", + "raii/scopedclangoptions.h", + ] +} diff --git a/src/plugins/clangcodemodel/clangcodemodel_dependencies.pri b/src/plugins/clangcodemodel/clangcodemodel_dependencies.pri new file mode 100644 index 0000000000..dea57152d0 --- /dev/null +++ b/src/plugins/clangcodemodel/clangcodemodel_dependencies.pri @@ -0,0 +1,7 @@ +QTC_PLUGIN_NAME = ClangCodeModel +QTC_LIB_DEPENDS += \ + utils +QTC_PLUGIN_DEPENDS += \ + coreplugin \ + cpptools \ + texteditor diff --git a/src/plugins/clangcodemodel/clangcodemodelplugin.cpp b/src/plugins/clangcodemodel/clangcodemodelplugin.cpp new file mode 100644 index 0000000000..6f21da3850 --- /dev/null +++ b/src/plugins/clangcodemodel/clangcodemodelplugin.cpp @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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 "clangcodemodelplugin.h" +#include "clangprojectsettingspropertiespage.h" +#include "fastindexer.h" +#include "pchmanager.h" +#include "utils.h" + +#include <coreplugin/coreconstants.h> +#include <coreplugin/icore.h> +#include <coreplugin/imode.h> +#include <coreplugin/modemanager.h> +#include <coreplugin/id.h> + +#include <cpptools/cppmodelmanager.h> + +#include <projectexplorer/projectexplorer.h> +#include <projectexplorer/session.h> + +#include <QtPlugin> + +namespace ClangCodeModel { +namespace Internal { + +bool ClangCodeModelPlugin::initialize(const QStringList &arguments, QString *errorMessage) +{ + Q_UNUSED(arguments) + Q_UNUSED(errorMessage) + + addAutoReleasedObject(new ClangProjectSettingsPanelFactory); + + ClangCodeModel::Internal::initializeClang(); + + connect(Core::EditorManager::instance(), SIGNAL(editorAboutToClose(Core::IEditor*)), + &m_liveUnitsManager, SLOT(editorAboutToClose(Core::IEditor*))); + connect(Core::EditorManager::instance(), SIGNAL(editorOpened(Core::IEditor*)), + &m_liveUnitsManager, SLOT(editorOpened(Core::IEditor*))); + + PCHManager *pchManager = new PCHManager(this); + FastIndexer *fastIndexer = 0; + +#ifdef CLANG_INDEXING + m_indexer.reset(new ClangIndexer); + fastIndexer = m_indexer.data(); + CppTools::CppModelManagerInterface::instance()->setIndexingSupport(m_indexer->indexingSupport()); +#endif // CLANG_INDEXING + + // wire up the pch manager + QObject *session = ProjectExplorer::SessionManager::instance(); + connect(session, SIGNAL(aboutToRemoveProject(ProjectExplorer::Project*)), + pchManager, SLOT(onAboutToRemoveProject(ProjectExplorer::Project*))); + connect(CppTools::CppModelManagerInterface::instance(), SIGNAL(projectPartsUpdated(ProjectExplorer::Project*)), + pchManager, SLOT(onProjectPartsUpdated(ProjectExplorer::Project*))); + + m_modelManagerSupport.reset(new ModelManagerSupport(fastIndexer)); + CppTools::CppModelManagerInterface::instance()->addModelManagerSupport( + m_modelManagerSupport.data()); + + return true; +} + +void ClangCodeModelPlugin::extensionsInitialized() +{ +} + +} // namespace Internal +} // namespace Clang + +Q_EXPORT_PLUGIN(ClangCodeModel::Internal::ClangCodeModelPlugin) diff --git a/src/plugins/clangcodemodel/clangcodemodelplugin.h b/src/plugins/clangcodemodel/clangcodemodelplugin.h new file mode 100644 index 0000000000..53603d2ed7 --- /dev/null +++ b/src/plugins/clangcodemodel/clangcodemodelplugin.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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. +** +****************************************************************************/ + +#ifndef CLANGPLUGIN_H +#define CLANGPLUGIN_H + +#include "clangmodelmanagersupport.h" +#include "liveunitsmanager.h" + +#ifdef CLANG_INDEXING +# include "clangindexer.h" +#endif // CLANG_INDEXING + +#include <extensionsystem/iplugin.h> + +namespace ClangCodeModel { +namespace Internal { + +class ClangCodeModelPlugin: public ExtensionSystem::IPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "ClangCodeModel.json") + +public: + bool initialize(const QStringList &arguments, QString *errorMessage); + + void extensionsInitialized(); + +private: + LiveUnitsManager m_liveUnitsManager; + QScopedPointer<ModelManagerSupport> m_modelManagerSupport; +#ifdef CLANG_INDEXING + QScopedPointer<ClangIndexer> m_indexer; +#endif // CLANG_INDEXING + +#ifdef WITH_TESTS +private slots: + void test_CXX_regressions(); + void test_CXX_regressions_data(); + void test_CXX_snippets(); + void test_CXX_snippets_data(); + void test_ObjC_hints(); + void test_ObjC_hints_data(); +#endif +}; + +} // namespace Internal +} // namespace Clang + +#endif // CLANGPLUGIN_H diff --git a/src/plugins/clangcodemodel/clangcompleter.cpp b/src/plugins/clangcodemodel/clangcompleter.cpp new file mode 100644 index 0000000000..31dcf7546b --- /dev/null +++ b/src/plugins/clangcodemodel/clangcompleter.cpp @@ -0,0 +1,208 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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 "clangcompleter.h" +#include "sourcemarker.h" +#include "unsavedfiledata.h" +#include "utils_p.h" +#include "completionproposalsbuilder.h" +#include "raii/scopedclangoptions.h" +#include "unit.h" + +#include <QDebug> +#include <QFile> +#include <QMutex> +#include <QMutexLocker> +#include <QTime> + +#include <clang-c/Index.h> + +//#define TIME_COMPLETION + +class ClangCodeModel::ClangCompleter::PrivateData +{ +public: + PrivateData() + : m_mutex(QMutex::Recursive) + , m_isSignalSlotCompletion(false) + { + } + + ~PrivateData() + { + } + + bool parseFromFile(const Internal::UnsavedFiles &unsavedFiles) + { + Q_ASSERT(!m_unit.isLoaded()); + if (m_unit.fileName().isEmpty()) + return false; + + unsigned opts = clang_defaultEditingTranslationUnitOptions(); +#if defined(CINDEX_VERSION) && (CINDEX_VERSION > 5) + opts |= CXTranslationUnit_CacheCompletionResults; + opts |= CXTranslationUnit_IncludeBriefCommentsInCodeCompletion; +#endif + m_unit.setManagementOptions(opts); + + m_unit.setUnsavedFiles(unsavedFiles); + m_unit.parse(); + return m_unit.isLoaded(); + } + +public: + QMutex m_mutex; + Internal::Unit m_unit; + bool m_isSignalSlotCompletion; +}; + +using namespace ClangCodeModel; +using namespace ClangCodeModel::Internal; + +/** + * @brief Constructs with highest possible priority + */ +CodeCompletionResult::CodeCompletionResult() + : m_priority(SHRT_MAX) + , m_completionKind(Other) + , m_availability(Available) + , m_hasParameters(false) +{} + +/** + * @brief Constructs with given priority + * @param priority Will be reversed, because clang's highest priority is 0, + * but inside QtCreator it is the lowest priority + */ +CodeCompletionResult::CodeCompletionResult(unsigned priority) + : m_priority(SHRT_MAX - priority) + , m_completionKind(Other) + , m_availability(Available) + , m_hasParameters(false) +{ +} + +ClangCompleter::ClangCompleter() + : d(new PrivateData) +{ +} + +ClangCompleter::~ClangCompleter() +{ +} + +QString ClangCompleter::fileName() const +{ + return d->m_unit.fileName(); +} + +void ClangCompleter::setFileName(const QString &fileName) +{ + if (d->m_unit.fileName() != fileName) { + d->m_unit = Internal::Unit(fileName); + } +} + +QStringList ClangCompleter::options() const +{ + return d->m_unit.compilationOptions(); +} + +void ClangCompleter::setOptions(const QStringList &options) const +{ + if (d->m_unit.compilationOptions() != options) { + d->m_unit.setCompilationOptions(options); + d->m_unit.unload(); + } +} + +bool ClangCompleter::isSignalSlotCompletion() const +{ + return d->m_isSignalSlotCompletion; +} + +void ClangCompleter::setSignalSlotCompletion(bool isSignalSlot) +{ + d->m_isSignalSlotCompletion = isSignalSlot; +} + +bool ClangCompleter::reparse(const UnsavedFiles &unsavedFiles) +{ + if (!d->m_unit.isLoaded()) + return d->parseFromFile(unsavedFiles); + + d->m_unit.setUnsavedFiles(unsavedFiles); + d->m_unit.reparse(); + return d->m_unit.isLoaded(); +} + +QList<CodeCompletionResult> ClangCompleter::codeCompleteAt(unsigned line, + unsigned column, + const UnsavedFiles &unsavedFiles) +{ +#ifdef TIME_COMPLETION + QTime t;t.start(); +#endif // TIME_COMPLETION + + if (!d->m_unit.isLoaded()) + if (!d->parseFromFile(unsavedFiles)) + return QList<CodeCompletionResult>(); + + ScopedCXCodeCompleteResults results; + d->m_unit.setUnsavedFiles(unsavedFiles); + d->m_unit.codeCompleteAt(line, column, results); + + QList<CodeCompletionResult> completions; + if (results) { + const quint64 contexts = clang_codeCompleteGetContexts(results); + CompletionProposalsBuilder builder(completions, contexts, d->m_isSignalSlotCompletion); + for (unsigned i = 0; i < results.size(); ++i) + builder(results.completionAt(i)); + } + +#ifdef TIME_COMPLETION + qDebug() << "Completion timing:" << completions.size() << "results in" << t.elapsed() << "ms."; +#endif // TIME_COMPLETION + + return completions; +} + +bool ClangCompleter::objcEnabled() const +{ + static const QString objcppOption = QLatin1String("-ObjC++"); + static const QString objcOption = QLatin1String("-ObjC"); + + QStringList options = d->m_unit.compilationOptions(); + return options.contains(objcOption) || options.contains(objcppOption); +} + +QMutex *ClangCompleter::mutex() const +{ + return &d->m_mutex; +} diff --git a/src/plugins/clangcodemodel/clangcompleter.h b/src/plugins/clangcodemodel/clangcompleter.h new file mode 100644 index 0000000000..49e95a7c70 --- /dev/null +++ b/src/plugins/clangcodemodel/clangcompleter.h @@ -0,0 +1,223 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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. +** +****************************************************************************/ + +#ifndef CLANGCOMPLETER_H +#define CLANGCOMPLETER_H + +#include "clang_global.h" +#include "diagnostic.h" +#include "sourcelocation.h" +#include "utils.h" + +#include <QList> +#include <QMap> +#include <QMutex> +#include <QPair> +#include <QSharedPointer> +#include <QString> +#include <QStringList> +#include <QVariant> + +namespace ClangCodeModel { + +class SourceMarker; + +class CLANG_EXPORT CodeCompletionResult +{ +public: + enum Kind { + Other = 0, + FunctionCompletionKind, + ConstructorCompletionKind, + DestructorCompletionKind, + VariableCompletionKind, + ClassCompletionKind, + EnumCompletionKind, + EnumeratorCompletionKind, + NamespaceCompletionKind, + PreProcessorCompletionKind, + SignalCompletionKind, + SlotCompletionKind, + ObjCMessageCompletionKind, + KeywordCompletionKind, + ClangSnippetKind + }; + + enum Availability { + Available, + Deprecated, + NotAvailable, + NotAccessible + }; + +public: + CodeCompletionResult(); + CodeCompletionResult(unsigned priority); + + unsigned priority() const + { return m_priority; } + + bool isValid() const + { return !m_text.isEmpty(); } + + QString text() const + { return m_text; } + void setText(const QString &text) + { m_text = text; } + + QString hint() const + { return m_hint; } + void setHint(const QString &hint) + { m_hint = hint; } + + QString snippet() const + { return m_snippet; } + void setSnippet(const QString &snippet) + { m_snippet = snippet; } + + Kind completionKind() const + { return m_completionKind; } + void setCompletionKind(Kind type) + { m_completionKind = type; } + + int compare(const CodeCompletionResult &other) const + { + if (m_priority < other.m_priority) + return -1; + else if (m_priority > other.m_priority) + return 1; + + if (m_completionKind < other.m_completionKind) + return -1; + else if (m_completionKind > other.m_completionKind) + return 1; + + if (m_text < other.m_text) + return -1; + else if (m_text > other.m_text) + return 1; + + if (m_hint < other.m_hint) + return -1; + else if (m_hint > other.m_hint) + return 1; + + if (!m_hasParameters && other.m_hasParameters) + return -1; + else if (m_hasParameters && !other.m_hasParameters) + return 1; + + if (m_availability < other.m_availability) + return -1; + else if (m_availability > other.m_availability) + return 1; + + return 0; + } + + bool hasParameters() const + { return m_hasParameters; } + void setHasParameters(bool hasParameters) + { m_hasParameters = hasParameters; } + + Availability availability() const + { return m_availability; } + void setAvailability(Availability availability) + { m_availability = availability; } + +private: + unsigned m_priority; + Kind m_completionKind; + QString m_text; + QString m_hint; + QString m_snippet; + Availability m_availability; + bool m_hasParameters; +}; + +inline CLANG_EXPORT uint qHash(const CodeCompletionResult &ccr) +{ return ccr.completionKind() ^ qHash(ccr.text()); } + +inline CLANG_EXPORT bool operator==(const CodeCompletionResult &ccr1, const CodeCompletionResult &ccr2) +{ return ccr1.compare(ccr2) == 0; } + +inline CLANG_EXPORT bool operator<(const CodeCompletionResult &ccr1, const CodeCompletionResult &ccr2) +{ + return ccr1.compare(ccr2) < 0; +} + +class CLANG_EXPORT ClangCompleter +{ + Q_DISABLE_COPY(ClangCompleter) + + class PrivateData; + +public: // data structures + typedef QSharedPointer<ClangCompleter> Ptr; + +public: // methods + ClangCompleter(); + ~ClangCompleter(); + + QString fileName() const; + void setFileName(const QString &fileName); + + QStringList options() const; + void setOptions(const QStringList &options) const; + + bool isSignalSlotCompletion() const; + void setSignalSlotCompletion(bool isSignalSlot); + + bool reparse(const Internal::UnsavedFiles &unsavedFiles); + + /** + * Do code-completion at the specified position. + * + * \param line The line number on which to do code-completion. The first + * line of a file has line number 1. + * \param column The column number where to do code-completion. Column + * numbers start with 1. + */ + QList<CodeCompletionResult> codeCompleteAt(unsigned line, + unsigned column, + const Internal::UnsavedFiles &unsavedFiles); + + bool objcEnabled() const; + + QMutex *mutex() const; + +private: // instance fields + QScopedPointer<PrivateData> d; +}; + +} // namespace Clang + +Q_DECLARE_METATYPE(ClangCodeModel::CodeCompletionResult) + +#endif // CLANGCOMPLETER_H diff --git a/src/plugins/clangcodemodel/clangcompletion.cpp b/src/plugins/clangcodemodel/clangcompletion.cpp new file mode 100644 index 0000000000..0a1a2ad578 --- /dev/null +++ b/src/plugins/clangcodemodel/clangcompletion.cpp @@ -0,0 +1,1221 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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 "clangcompletion.h" +#include "clangutils.h" +#include "pchmanager.h" + +#include <coreplugin/icore.h> +#include <coreplugin/idocument.h> +#include <coreplugin/mimedatabase.h> + +#include <cplusplus/BackwardsScanner.h> +#include <cplusplus/ExpressionUnderCursor.h> +#include <cplusplus/Token.h> +#include <cplusplus/MatchingText.h> + +#include <cppeditor/cppeditorconstants.h> + +#include <cpptools/cppdoxygen.h> +#include <cpptools/cppmodelmanagerinterface.h> + +#include <texteditor/basetexteditor.h> +#include <texteditor/convenience.h> +#include <texteditor/codeassist/basicproposalitemlistmodel.h> +#include <texteditor/codeassist/basicproposalitem.h> +#include <texteditor/codeassist/functionhintproposal.h> +#include <texteditor/codeassist/genericproposal.h> +#include <texteditor/codeassist/ifunctionhintproposalmodel.h> +#include <texteditor/texteditorsettings.h> +#include <texteditor/completionsettings.h> + +#include <QCoreApplication> +#include <QDirIterator> +#include <QTextCursor> +#include <QTextDocument> + +static const bool DebugTiming = !qgetenv("QTC_CLANG_VERBOSE").isEmpty(); + +using namespace ClangCodeModel; +using namespace ClangCodeModel::Internal; +using namespace CPlusPlus; +using namespace CppTools; +using namespace TextEditor; + +static const char SNIPPET_ICON_PATH[] = ":/texteditor/images/snippet.png"; + +namespace { + +int activationSequenceChar(const QChar &ch, + const QChar &ch2, + const QChar &ch3, + unsigned *kind, + bool wantFunctionCall) +{ + int referencePosition = 0; + int completionKind = T_EOF_SYMBOL; + switch (ch.toLatin1()) { + case '.': + if (ch2 != QLatin1Char('.')) { + completionKind = T_DOT; + referencePosition = 1; + } + break; + case ',': + completionKind = T_COMMA; + referencePosition = 1; + break; + case '(': + if (wantFunctionCall) { + completionKind = T_LPAREN; + referencePosition = 1; + } + break; + case ':': + if (ch3 != QLatin1Char(':') && ch2 == QLatin1Char(':')) { + completionKind = T_COLON_COLON; + referencePosition = 2; + } + break; + case '>': + if (ch2 == QLatin1Char('-')) { + completionKind = T_ARROW; + referencePosition = 2; + } + break; + case '*': + if (ch2 == QLatin1Char('.')) { + completionKind = T_DOT_STAR; + referencePosition = 2; + } else if (ch3 == QLatin1Char('-') && ch2 == QLatin1Char('>')) { + completionKind = T_ARROW_STAR; + referencePosition = 3; + } + break; + case '\\': + case '@': + if (ch2.isNull() || ch2.isSpace()) { + completionKind = T_DOXY_COMMENT; + referencePosition = 1; + } + break; + case '<': + completionKind = T_ANGLE_STRING_LITERAL; + referencePosition = 1; + break; + case '"': + completionKind = T_STRING_LITERAL; + referencePosition = 1; + break; + case '/': + completionKind = T_SLASH; + referencePosition = 1; + break; + case '#': + completionKind = T_POUND; + referencePosition = 1; + break; + } + + if (kind) + *kind = completionKind; + + return referencePosition; +} + +static QList<CodeCompletionResult> unfilteredCompletion(const ClangCompletionAssistInterface* interface, + const QString &fileName, + unsigned line, unsigned column, + QByteArray modifiedInput = QByteArray(), + bool isSignalSlotCompletion = false) +{ + ClangCompleter::Ptr wrapper = interface->clangWrapper(); + QMutexLocker lock(wrapper->mutex()); + //### TODO: check if we're cancelled after we managed to acquire the mutex + + wrapper->setFileName(fileName); + wrapper->setOptions(interface->options()); + wrapper->setSignalSlotCompletion(isSignalSlotCompletion); + UnsavedFiles unsavedFiles = interface->unsavedFiles(); + if (!modifiedInput.isEmpty()) + unsavedFiles.insert(fileName, modifiedInput); + + QTime t; + if (DebugTiming) { + qDebug() << "Here we go with ClangCompletionAssistProcessor...."; + t.start(); + } + + QList<CodeCompletionResult> result = wrapper->codeCompleteAt(line, column + 1, unsavedFiles); + qSort(result); + + if (DebugTiming) + qDebug() << "... Completion done in" << t.elapsed() << "ms, with" << result.count() << "items."; + + return result; +} + +} // Anonymous + +namespace ClangCodeModel { +namespace Internal { + +// ----------------------------- +// ClangCompletionAssistProvider +// ----------------------------- +ClangCompletionAssistProvider::ClangCompletionAssistProvider() + : m_clangCompletionWrapper(new ClangCodeModel::ClangCompleter) +{ +} + +IAssistProcessor *ClangCompletionAssistProvider::createProcessor() const +{ + return new ClangCompletionAssistProcessor; +} + +IAssistInterface *ClangCompletionAssistProvider::createAssistInterface( + ProjectExplorer::Project *project, TextEditor::BaseTextEditor *editor, + QTextDocument *document, int position, AssistReason reason) const +{ + Q_UNUSED(project); + + QString fileName = editor->document()->filePath(); + CppModelManagerInterface *modelManager = CppModelManagerInterface::instance(); + QList<ProjectPart::Ptr> parts = modelManager->projectPart(fileName); + if (parts.isEmpty()) + parts += modelManager->fallbackProjectPart(); + QStringList includePaths, frameworkPaths, options; + PchInfo::Ptr pchInfo; + foreach (ProjectPart::Ptr part, parts) { + if (part.isNull()) + continue; + options = ClangCodeModel::Utils::createClangOptions(part, fileName); + pchInfo = PCHManager::instance()->pchInfo(part); + if (!pchInfo.isNull()) + options.append(ClangCodeModel::Utils::createPCHInclusionOptions(pchInfo->fileName())); + includePaths = part->includePaths; + frameworkPaths = part->frameworkPaths; + break; + } + + return new ClangCodeModel::ClangCompletionAssistInterface( + m_clangCompletionWrapper, + document, position, fileName, reason, + options, includePaths, frameworkPaths, pchInfo); +} + +// ------------------------ +// ClangAssistProposalModel +// ------------------------ +class ClangAssistProposalModel : public TextEditor::BasicProposalItemListModel +{ +public: + ClangAssistProposalModel() + : TextEditor::BasicProposalItemListModel() + , m_sortable(false) + , m_completionOperator(T_EOF_SYMBOL) + , m_replaceDotForArrow(false) + {} + + virtual bool isSortable(const QString &prefix) const; + bool m_sortable; + unsigned m_completionOperator; + bool m_replaceDotForArrow; +}; + +// ------------------- +// ClangAssistProposal +// ------------------- +class ClangAssistProposal : public TextEditor::GenericProposal +{ +public: + ClangAssistProposal(int cursorPos, TextEditor::IGenericProposalModel *model) + : TextEditor::GenericProposal(cursorPos, model) + , m_replaceDotForArrow(static_cast<ClangAssistProposalModel *>(model)->m_replaceDotForArrow) + {} + + virtual bool isCorrective() const { return m_replaceDotForArrow; } + virtual void makeCorrection(BaseTextEditor *editor) + { + editor->setCursorPosition(basePosition() - 1); + editor->replace(1, QLatin1String("->")); + moveBasePosition(1); + } + +private: + bool m_replaceDotForArrow; +}; + +// ---------------------- +// ClangFunctionHintModel +// ---------------------- +class ClangFunctionHintModel : public TextEditor::IFunctionHintProposalModel +{ +public: + ClangFunctionHintModel(const QList<CodeCompletionResult> functionSymbols) + : m_functionSymbols(functionSymbols) + , m_currentArg(-1) + {} + + virtual void reset() {} + virtual int size() const { return m_functionSymbols.size(); } + virtual QString text(int index) const; + virtual int activeArgument(const QString &prefix) const; + +private: + QList<ClangCodeModel::CodeCompletionResult> m_functionSymbols; + mutable int m_currentArg; +}; + +QString ClangFunctionHintModel::text(int index) const +{ +#if 0 + // TODO: add the boldening to the result + Overview overview; + overview.setShowReturnTypes(true); + overview.setShowArgumentNames(true); + overview.setMarkedArgument(m_currentArg + 1); + Function *f = m_functionSymbols.at(index); + + const QString prettyMethod = overview(f->type(), f->name()); + const int begin = overview.markedArgumentBegin(); + const int end = overview.markedArgumentEnd(); + + QString hintText; + hintText += Qt::escape(prettyMethod.left(begin)); + hintText += "<b>"; + hintText += Qt::escape(prettyMethod.mid(begin, end - begin)); + hintText += "</b>"; + hintText += Qt::escape(prettyMethod.mid(end)); + return hintText; +#endif + return m_functionSymbols.at(index).hint(); +} + +int ClangFunctionHintModel::activeArgument(const QString &prefix) const +{ + int argnr = 0; + int parcount = 0; + SimpleLexer tokenize; + QList<CPlusPlus::Token> tokens = tokenize(prefix); + for (int i = 0; i < tokens.count(); ++i) { + const CPlusPlus::Token &tk = tokens.at(i); + if (tk.is(T_LPAREN)) + ++parcount; + else if (tk.is(T_RPAREN)) + --parcount; + else if (! parcount && tk.is(T_COMMA)) + ++argnr; + } + + if (parcount < 0) + return -1; + + if (argnr != m_currentArg) + m_currentArg = argnr; + + return argnr; +} + +class ClangAssistProposalItem : public TextEditor::BasicProposalItem +{ +public: + ClangAssistProposalItem() {} + + virtual bool prematurelyApplies(const QChar &c) const; + virtual void applyContextualContent(TextEditor::BaseTextEditor *editor, + int basePosition) const; + + void keepCompletionOperator(unsigned compOp) { m_completionOperator = compOp; } + + bool isOverloaded() const + { return !m_overloads.isEmpty(); } + void addOverload(const CodeCompletionResult &ccr) + { m_overloads.append(ccr); } + + CodeCompletionResult originalItem() const + { + const QVariant &v = data(); + if (v.canConvert<CodeCompletionResult>()) + return v.value<CodeCompletionResult>(); + else + return CodeCompletionResult(); + } + + bool isCodeCompletionResult() const + { return data().canConvert<CodeCompletionResult>(); } + +private: + unsigned m_completionOperator; + mutable QChar m_typedChar; + QList<CodeCompletionResult> m_overloads; +}; + +/// @return True, because clang always returns priorities for sorting +bool ClangAssistProposalModel::isSortable(const QString &prefix) const +{ + Q_UNUSED(prefix) + return true; +} + +} // namespace Internal +} // namespace ClangCodeModel + +bool ClangAssistProposalItem::prematurelyApplies(const QChar &typedChar) const +{ + bool ok = false; + + if (m_completionOperator == T_SIGNAL || m_completionOperator == T_SLOT) + ok = QString::fromLatin1("(,").contains(typedChar); + else if (m_completionOperator == T_STRING_LITERAL || m_completionOperator == T_ANGLE_STRING_LITERAL) + ok = (typedChar == QLatin1Char('/')) && text().endsWith(QLatin1Char('/')); + else if (!isCodeCompletionResult()) + ok = (typedChar == QLatin1Char('(')); /* && data().canConvert<CompleteFunctionDeclaration>()*/ //### + else if (originalItem().completionKind() == CodeCompletionResult::ObjCMessageCompletionKind) + ok = QString::fromLatin1(";.,").contains(typedChar); + else + ok = QString::fromLatin1(";.,:(").contains(typedChar); + + if (ok) + m_typedChar = typedChar; + + return ok; +} + +void ClangAssistProposalItem::applyContextualContent(TextEditor::BaseTextEditor *editor, + int basePosition) const +{ + const CodeCompletionResult ccr = originalItem(); + + QString toInsert = text(); + QString extraChars; + int extraLength = 0; + int cursorOffset = 0; + + bool autoParenthesesEnabled = true; + if (m_completionOperator == T_SIGNAL || m_completionOperator == T_SLOT) { + extraChars += QLatin1Char(')'); + if (m_typedChar == QLatin1Char('(')) // Eat the opening parenthesis + m_typedChar = QChar(); + } else if (m_completionOperator == T_STRING_LITERAL || m_completionOperator == T_ANGLE_STRING_LITERAL) { + if (!toInsert.endsWith(QLatin1Char('/'))) { + extraChars += QLatin1Char((m_completionOperator == T_ANGLE_STRING_LITERAL) ? '>' : '"'); + } else { + if (m_typedChar == QLatin1Char('/')) // Eat the slash + m_typedChar = QChar(); + } + } else if (ccr.isValid()) { + const CompletionSettings &completionSettings = + TextEditorSettings::instance()->completionSettings(); + const bool autoInsertBrackets = completionSettings.m_autoInsertBrackets; + + if (autoInsertBrackets && + (ccr.completionKind() == CodeCompletionResult::FunctionCompletionKind + || ccr.completionKind() == CodeCompletionResult::DestructorCompletionKind + || ccr.completionKind() == CodeCompletionResult::SignalCompletionKind + || ccr.completionKind() == CodeCompletionResult::SlotCompletionKind)) { + // When the user typed the opening parenthesis, he'll likely also type the closing one, + // in which case it would be annoying if we put the cursor after the already automatically + // inserted closing parenthesis. + const bool skipClosingParenthesis = m_typedChar != QLatin1Char('('); + + if (completionSettings.m_spaceAfterFunctionName) + extraChars += QLatin1Char(' '); + extraChars += QLatin1Char('('); + if (m_typedChar == QLatin1Char('(')) + m_typedChar = QChar(); + + // If the function doesn't return anything, automatically place the semicolon, + // unless we're doing a scope completion (then it might be function definition). + const QChar characterAtCursor = editor->textDocument()->characterAt(editor->position()); + bool endWithSemicolon = m_typedChar == QLatin1Char(';')/* + || (function->returnType()->isVoidType() && m_completionOperator != T_COLON_COLON)*/; //### + const QChar semicolon = m_typedChar.isNull() ? QLatin1Char(';') : m_typedChar; + + if (endWithSemicolon && characterAtCursor == semicolon) { + endWithSemicolon = false; + m_typedChar = QChar(); + } + + // If the function takes no arguments, automatically place the closing parenthesis + if (!isOverloaded() && !ccr.hasParameters() && skipClosingParenthesis) { + extraChars += QLatin1Char(')'); + if (endWithSemicolon) { + extraChars += semicolon; + m_typedChar = QChar(); + } + } else if (autoParenthesesEnabled) { + const QChar lookAhead = editor->textDocument()->characterAt(editor->position() + 1); + if (MatchingText::shouldInsertMatchingText(lookAhead)) { + extraChars += QLatin1Char(')'); + --cursorOffset; + if (endWithSemicolon) { + extraChars += semicolon; + --cursorOffset; + m_typedChar = QChar(); + } + } + } + } + +#if 0 + if (autoInsertBrackets && data().canConvert<CompleteFunctionDeclaration>()) { + if (m_typedChar == QLatin1Char('(')) + m_typedChar = QChar(); + + // everything from the closing parenthesis on are extra chars, to + // make sure an auto-inserted ")" gets replaced by ") const" if necessary + int closingParen = toInsert.lastIndexOf(QLatin1Char(')')); + extraChars = toInsert.mid(closingParen); + toInsert.truncate(closingParen); + } +#endif + } + + // Append an unhandled typed character, adjusting cursor offset when it had been adjusted before + if (!m_typedChar.isNull()) { + extraChars += m_typedChar; + if (cursorOffset != 0) + --cursorOffset; + } + + // Avoid inserting characters that are already there + const int endsPosition = editor->position(TextEditor::ITextEditor::EndOfLine); + const QString existingText = editor->textDocument()->textAt(editor->position(), + endsPosition - editor->position()); + int existLength = 0; + if (!existingText.isEmpty()) { + // Calculate the exist length in front of the extra chars + existLength = toInsert.length() - (editor->position() - basePosition); + while (!existingText.startsWith(toInsert.right(existLength))) { + if (--existLength == 0) + break; + } + } + for (int i = 0; i < extraChars.length(); ++i) { + const QChar a = extraChars.at(i); + const QChar b = editor->textDocument()->characterAt(editor->position() + i + existLength); + if (a == b) + ++extraLength; + else + break; + } + toInsert += extraChars; + + // Insert the remainder of the name + const int length = editor->position() - basePosition + existLength + extraLength; + editor->setCursorPosition(basePosition); + editor->replace(length, toInsert); + if (cursorOffset) + editor->setCursorPosition(editor->position() + cursorOffset); +} + +bool ClangCompletionAssistInterface::objcEnabled() const +{ + return m_clangWrapper->objcEnabled(); +} + +ClangCompletionAssistInterface::ClangCompletionAssistInterface(ClangCompleter::Ptr clangWrapper, + QTextDocument *document, + int position, + const QString &fileName, + AssistReason reason, + const QStringList &options, + const QStringList &includePaths, + const QStringList &frameworkPaths, + const PchInfo::Ptr &pchInfo) + : DefaultAssistInterface(document, position, fileName, reason) + , m_clangWrapper(clangWrapper) + , m_options(options) + , m_includePaths(includePaths) + , m_frameworkPaths(frameworkPaths) + , m_savedPchPointer(pchInfo) +{ + Q_ASSERT(!clangWrapper.isNull()); + + CppModelManagerInterface *mmi = CppModelManagerInterface::instance(); + Q_ASSERT(mmi); + m_unsavedFiles = Utils::createUnsavedFiles(mmi->workingCopy()); +} + +ClangCompletionAssistProcessor::ClangCompletionAssistProcessor() + : m_preprocessorCompletions(QStringList() + << QLatin1String("define") + << QLatin1String("error") + << QLatin1String("include") + << QLatin1String("line") + << QLatin1String("pragma") + << QLatin1String("pragma once") + << QLatin1String("pragma omp atomic") + << QLatin1String("pragma omp parallel") + << QLatin1String("pragma omp for") + << QLatin1String("pragma omp ordered") + << QLatin1String("pragma omp parallel for") + << QLatin1String("pragma omp section") + << QLatin1String("pragma omp sections") + << QLatin1String("pragma omp parallel sections") + << QLatin1String("pragma omp single") + << QLatin1String("pragma omp master") + << QLatin1String("pragma omp critical") + << QLatin1String("pragma omp barrier") + << QLatin1String("pragma omp flush") + << QLatin1String("pragma omp threadprivate") + << QLatin1String("undef") + << QLatin1String("if") + << QLatin1String("ifdef") + << QLatin1String("ifndef") + << QLatin1String("elif") + << QLatin1String("else") + << QLatin1String("endif")) + , m_model(new ClangAssistProposalModel) + , m_hintProposal(0) + +{ +} + +ClangCompletionAssistProcessor::~ClangCompletionAssistProcessor() +{ +} + +IAssistProposal *ClangCompletionAssistProcessor::perform(const IAssistInterface *interface) +{ + m_interface.reset(static_cast<const ClangCompletionAssistInterface *>(interface)); + + if (interface->reason() != ExplicitlyInvoked && !accepts()) + return 0; + + int index = startCompletionHelper(); + if (index != -1) { + if (m_hintProposal) + return m_hintProposal; + + m_model->m_sortable = (m_model->m_completionOperator != T_EOF_SYMBOL); + return createContentProposal(); + } + + return 0; +} + +int ClangCompletionAssistProcessor::startCompletionHelper() +{ + //### TODO: clean-up this method, some calculated values might not be used anymore. + + Q_ASSERT(m_model); + + const int startOfName = findStartOfName(); + m_startPosition = startOfName; + m_model->m_completionOperator = T_EOF_SYMBOL; + + int endOfOperator = m_startPosition; + + // Skip whitespace preceding this position + while (m_interface->characterAt(endOfOperator - 1).isSpace()) + --endOfOperator; + + const QString fileName = m_interface->fileName(); + + int endOfExpression = startOfOperator(endOfOperator, + &m_model->m_completionOperator, + /*want function call =*/ true); + + if (m_model->m_completionOperator == T_EOF_SYMBOL) { + endOfOperator = m_startPosition; + } else if (m_model->m_completionOperator == T_DOXY_COMMENT) { + for (int i = 1; i < T_DOXY_LAST_TAG; ++i) + addCompletionItem(QString::fromLatin1(doxygenTagSpell(i)), + m_icons.keywordIcon()); + return m_startPosition; + } + + // Pre-processor completion + //### TODO: check if clang can do pp completion + if (m_model->m_completionOperator == T_POUND) { + completePreprocessor(); + m_startPosition = startOfName; + return m_startPosition; + } + + // Include completion + if (m_model->m_completionOperator == T_STRING_LITERAL + || m_model->m_completionOperator == T_ANGLE_STRING_LITERAL + || m_model->m_completionOperator == T_SLASH) { + + QTextCursor c(m_interface->textDocument()); + c.setPosition(endOfExpression); + if (completeInclude(c)) + m_startPosition = startOfName; + return m_startPosition; + } + + ExpressionUnderCursor expressionUnderCursor; + QTextCursor tc(m_interface->textDocument()); + + if (m_model->m_completionOperator == T_COMMA) { + tc.setPosition(endOfExpression); + const int start = expressionUnderCursor.startOfFunctionCall(tc); + if (start == -1) { + m_model->m_completionOperator = T_EOF_SYMBOL; + return -1; + } + + endOfExpression = start; + m_startPosition = start + 1; + m_model->m_completionOperator = T_LPAREN; + } + + QString expression; + int startOfExpression = m_interface->position(); + tc.setPosition(endOfExpression); + + if (m_model->m_completionOperator) { + expression = expressionUnderCursor(tc); + startOfExpression = endOfExpression - expression.length(); + + if (m_model->m_completionOperator == T_LPAREN) { + if (expression.endsWith(QLatin1String("SIGNAL"))) + m_model->m_completionOperator = T_SIGNAL; + + else if (expression.endsWith(QLatin1String("SLOT"))) + m_model->m_completionOperator = T_SLOT; + + else if (m_interface->position() != endOfOperator) { + // We don't want a function completion when the cursor isn't at the opening brace + expression.clear(); + m_model->m_completionOperator = T_EOF_SYMBOL; + m_startPosition = startOfName; + startOfExpression = m_interface->position(); + } + } + } else if (expression.isEmpty()) { + while (startOfExpression > 0 && m_interface->characterAt(startOfExpression).isSpace()) + --startOfExpression; + } + + int line = 0, column = 0; +// Convenience::convertPosition(m_interface->document(), startOfExpression, &line, &column); + Convenience::convertPosition(m_interface->textDocument(), endOfOperator, &line, &column); + return startCompletionInternal(fileName, line, column, endOfOperator); +} + +int ClangCompletionAssistProcessor::startOfOperator(int pos, + unsigned *kind, + bool wantFunctionCall) const +{ + const QChar ch = pos > -1 ? m_interface->characterAt(pos - 1) : QChar(); + const QChar ch2 = pos > 0 ? m_interface->characterAt(pos - 2) : QChar(); + const QChar ch3 = pos > 1 ? m_interface->characterAt(pos - 3) : QChar(); + + int start = pos - activationSequenceChar(ch, ch2, ch3, kind, wantFunctionCall); + if (start != pos) { + QTextCursor tc(m_interface->textDocument()); + tc.setPosition(pos); + + // Include completion: make sure the quote character is the first one on the line + if (*kind == T_STRING_LITERAL) { + QTextCursor s = tc; + s.movePosition(QTextCursor::StartOfLine, QTextCursor::KeepAnchor); + QString sel = s.selectedText(); + if (sel.indexOf(QLatin1Char('"')) < sel.length() - 1) { + *kind = T_EOF_SYMBOL; + start = pos; + } + } + + if (*kind == T_COMMA) { + ExpressionUnderCursor expressionUnderCursor; + if (expressionUnderCursor.startOfFunctionCall(tc) == -1) { + *kind = T_EOF_SYMBOL; + start = pos; + } + } + + SimpleLexer tokenize; + LanguageFeatures lf = tokenize.languageFeatures(); + lf.qtMocRunEnabled = true; + lf.objCEnabled = true; + tokenize.setLanguageFeatures(lf); + tokenize.setSkipComments(false); + const QList<CPlusPlus::Token> &tokens = tokenize(tc.block().text(), BackwardsScanner::previousBlockState(tc.block())); + const int tokenIdx = SimpleLexer::tokenBefore(tokens, qMax(0, tc.positionInBlock() - 1)); // get the token at the left of the cursor + const CPlusPlus::Token tk = (tokenIdx == -1) ? CPlusPlus::Token() : tokens.at(tokenIdx); + + if (*kind == T_DOXY_COMMENT && !(tk.is(T_DOXY_COMMENT) || tk.is(T_CPP_DOXY_COMMENT))) { + *kind = T_EOF_SYMBOL; + start = pos; + } + // Don't complete in comments or strings, but still check for include completion + else if (tk.is(T_COMMENT) || tk.is(T_CPP_COMMENT) || + (tk.isLiteral() && (*kind != T_STRING_LITERAL + && *kind != T_ANGLE_STRING_LITERAL + && *kind != T_SLASH))) { + *kind = T_EOF_SYMBOL; + start = pos; + } + // Include completion: can be triggered by slash, but only in a string + else if (*kind == T_SLASH && (tk.isNot(T_STRING_LITERAL) && tk.isNot(T_ANGLE_STRING_LITERAL))) { + *kind = T_EOF_SYMBOL; + start = pos; + } + else if (*kind == T_LPAREN) { + if (tokenIdx > 0) { + const CPlusPlus::Token &previousToken = tokens.at(tokenIdx - 1); // look at the token at the left of T_LPAREN + switch (previousToken.kind()) { + case T_IDENTIFIER: + case T_GREATER: + case T_SIGNAL: + case T_SLOT: + break; // good + + default: + // that's a bad token :) + *kind = T_EOF_SYMBOL; + start = pos; + } + } + } + // Check for include preprocessor directive + else if (*kind == T_STRING_LITERAL || *kind == T_ANGLE_STRING_LITERAL || *kind == T_SLASH) { + bool include = false; + if (tokens.size() >= 3) { + if (tokens.at(0).is(T_POUND) && tokens.at(1).is(T_IDENTIFIER) && (tokens.at(2).is(T_STRING_LITERAL) || + tokens.at(2).is(T_ANGLE_STRING_LITERAL))) { + const CPlusPlus::Token &directiveToken = tokens.at(1); + QString directive = tc.block().text().mid(directiveToken.begin(), + directiveToken.length()); + if (directive == QLatin1String("include") || + directive == QLatin1String("include_next") || + directive == QLatin1String("import")) { + include = true; + } + } + } + + if (!include) { + *kind = T_EOF_SYMBOL; + start = pos; + } + } + } + + return start; +} + +int ClangCompletionAssistProcessor::findStartOfName(int pos) const +{ + if (pos == -1) + pos = m_interface->position(); + QChar chr; + + // Skip to the start of a name + do { + chr = m_interface->characterAt(--pos); + } while (chr.isLetterOrNumber() || chr == QLatin1Char('_')); + + return pos + 1; +} + +bool ClangCompletionAssistProcessor::accepts() const +{ + const int pos = m_interface->position(); + unsigned token = T_EOF_SYMBOL; + + const int start = startOfOperator(pos, &token, /*want function call=*/ true); + if (start != pos) { + if (token == T_POUND) { + const int column = pos - m_interface->textDocument()->findBlock(start).position(); + if (column != 1) + return false; + } + + return true; + } else { + // Trigger completion after three characters of a name have been typed, when not editing an existing name + QChar characterUnderCursor = m_interface->characterAt(pos); + if (!characterUnderCursor.isLetterOrNumber() && characterUnderCursor != QLatin1Char('_')) { + const int startOfName = findStartOfName(pos); + if (pos - startOfName >= 3) { + const QChar firstCharacter = m_interface->characterAt(startOfName); + if (firstCharacter.isLetter() || firstCharacter == QLatin1Char('_')) { + // Finally check that we're not inside a comment or string (code copied from startOfOperator) + QTextCursor tc(m_interface->textDocument()); + tc.setPosition(pos); + + SimpleLexer tokenize; + LanguageFeatures lf = tokenize.languageFeatures(); + lf.qtMocRunEnabled = true; + lf.objCEnabled = true; + tokenize.setLanguageFeatures(lf); + tokenize.setSkipComments(false); + const QList<CPlusPlus::Token> &tokens = tokenize(tc.block().text(), BackwardsScanner::previousBlockState(tc.block())); + const int tokenIdx = SimpleLexer::tokenBefore(tokens, qMax(0, tc.positionInBlock() - 1)); + const CPlusPlus::Token tk = (tokenIdx == -1) ? CPlusPlus::Token() : tokens.at(tokenIdx); + + if (!tk.isComment() && !tk.isLiteral()) { + return true; + } else if (tk.isLiteral() + && tokens.size() == 3 + && tokens.at(0).kind() == T_POUND + && tokens.at(1).kind() == T_IDENTIFIER) { + const QString &line = tc.block().text(); + const CPlusPlus::Token &idToken = tokens.at(1); + const QStringRef &identifier = + line.midRef(idToken.begin(), idToken.end() - idToken.begin()); + if (identifier == QLatin1String("include") + || identifier == QLatin1String("include_next") + || (m_interface->objcEnabled() && identifier == QLatin1String("import"))) { + return true; + } + } + } + } + } + } + + return false; +} + +IAssistProposal *ClangCompletionAssistProcessor::createContentProposal() +{ + m_model->loadContent(m_completions); + return new ClangAssistProposal(m_startPosition, m_model.take()); +} + +/// Seach backwards in the document starting from pos to find the first opening +/// parenthesis. Nested parenthesis are skipped. +static int findOpenParen(QTextDocument *doc, int start) +{ + unsigned parenCount = 1; + for (int pos = start; pos >= 0; --pos) { + const QChar ch = doc->characterAt(pos); + if (ch == QLatin1Char('(')) { + --parenCount; + if (parenCount == 0) + return pos; + } else if (ch == QLatin1Char(')')) { + ++parenCount; + } + } + return -1; +} + +static QByteArray modifyInput(QTextDocument *doc, int endOfExpression) { + int comma = endOfExpression; + while (comma > 0) { + const QChar ch = doc->characterAt(comma); + if (ch == QLatin1Char(',')) + break; + if (ch == QLatin1Char(';') || ch == QLatin1Char('{') || ch == QLatin1Char('}')) { + // Safety net: we don't seem to have "connect(pointer, SIGNAL(" as + // input, so stop searching. + comma = -1; + break; + } + --comma; + } + if (comma < 0) + return QByteArray(); + const int openBrace = findOpenParen(doc, comma); + if (openBrace < 0) + return QByteArray(); + + QByteArray modifiedInput = doc->toPlainText().toUtf8(); + const int len = endOfExpression - comma; + QByteArray replacement(len - 4, ' '); + replacement.append(")->"); + modifiedInput.replace(comma, len, replacement); + modifiedInput.insert(openBrace, '('); + return modifiedInput; +} + +int ClangCompletionAssistProcessor::startCompletionInternal(const QString fileName, + unsigned line, + unsigned column, + int endOfExpression) +{ + bool signalCompletion = false; + bool slotCompletion = false; + QByteArray modifiedInput; + + if (m_model->m_completionOperator == T_SIGNAL) { + signalCompletion = true; + modifiedInput = modifyInput(m_interface->textDocument(), endOfExpression); + } else if (m_model->m_completionOperator == T_SLOT) { + slotCompletion = true; + modifiedInput = modifyInput(m_interface->textDocument(), endOfExpression); + } else if (m_model->m_completionOperator == T_LPAREN) { + // Find the expression that precedes the current name + int index = endOfExpression; + while (m_interface->characterAt(index - 1).isSpace()) + --index; + + QTextCursor tc(m_interface->textDocument()); + tc.setPosition(index); + ExpressionUnderCursor euc; + index = euc.startOfFunctionCall(tc); + int nameStart = findStartOfName(index); + QTextCursor tc2(m_interface->textDocument()); + tc2.setPosition(nameStart); + tc2.setPosition(index, QTextCursor::KeepAnchor); + const QString functionName = tc2.selectedText().trimmed(); + int l = line, c = column; + Convenience::convertPosition(m_interface->textDocument(), nameStart, &l, &c); + + if (DebugTiming) + qDebug()<<"complete constructor or function @" << line<<":"<<column << "->"<<l<<":"<<c; + + const QList<CodeCompletionResult> completions = unfilteredCompletion( + m_interface.data(), fileName, l, c, QByteArray(), signalCompletion || slotCompletion); + QList<CodeCompletionResult> functionCompletions; + foreach (const CodeCompletionResult &ccr, completions) { + if (ccr.completionKind() == CodeCompletionResult::FunctionCompletionKind + || ccr.completionKind() == CodeCompletionResult::ConstructorCompletionKind + || ccr.completionKind() == CodeCompletionResult::DestructorCompletionKind + || ccr.completionKind() == CodeCompletionResult::SignalCompletionKind + || ccr.completionKind() == CodeCompletionResult::SlotCompletionKind) + if (ccr.text() == functionName) + functionCompletions.append(ccr); + } + + if (!functionCompletions.isEmpty()) { + IFunctionHintProposalModel *model = new ClangFunctionHintModel(functionCompletions); + m_hintProposal = new FunctionHintProposal(m_startPosition, model); + return m_startPosition; + } + } + + const QIcon snippetIcon = QIcon(QLatin1String(SNIPPET_ICON_PATH)); + QList<CodeCompletionResult> completions = unfilteredCompletion( + m_interface.data(), fileName, line, column, modifiedInput, signalCompletion || slotCompletion); + QHash<QString, ClangAssistProposalItem *> items; + foreach (const CodeCompletionResult &ccr, completions) { + if (!ccr.isValid()) + continue; + if (signalCompletion && ccr.completionKind() != CodeCompletionResult::SignalCompletionKind) + continue; + if (slotCompletion && ccr.completionKind() != CodeCompletionResult::SlotCompletionKind) + continue; + + const QString txt(ccr.text()); + ClangAssistProposalItem *item = items.value(txt, 0); + if (item) { + item->addOverload(ccr); + } else { + item = new ClangAssistProposalItem; + items.insert(txt, item); + item->setText(txt); + item->setDetail(ccr.hint()); + item->setOrder(ccr.priority()); + + const QString snippet = ccr.snippet(); + if (!snippet.isEmpty()) + item->setData(snippet); + else + item->setData(qVariantFromValue(ccr)); + } + + // FIXME: show the effective accessebility instead of availability + switch (ccr.completionKind()) { + case CodeCompletionResult::ClassCompletionKind: item->setIcon(m_icons.iconForType(Icons::ClassIconType)); break; + case CodeCompletionResult::EnumCompletionKind: item->setIcon(m_icons.iconForType(Icons::EnumIconType)); break; + case CodeCompletionResult::EnumeratorCompletionKind: item->setIcon(m_icons.iconForType(Icons::EnumeratorIconType)); break; + + case CodeCompletionResult::ConstructorCompletionKind: // fall through + case CodeCompletionResult::DestructorCompletionKind: // fall through + case CodeCompletionResult::FunctionCompletionKind: + case CodeCompletionResult::ObjCMessageCompletionKind: + switch (ccr.availability()) { + case CodeCompletionResult::Available: + case CodeCompletionResult::Deprecated: + item->setIcon(m_icons.iconForType(Icons::FuncPublicIconType)); + break; + default: + item->setIcon(m_icons.iconForType(Icons::FuncPrivateIconType)); + break; + } + break; + + case CodeCompletionResult::SignalCompletionKind: + item->setIcon(m_icons.iconForType(Icons::SignalIconType)); + break; + + case CodeCompletionResult::SlotCompletionKind: + switch (ccr.availability()) { + case CodeCompletionResult::Available: + case CodeCompletionResult::Deprecated: + item->setIcon(m_icons.iconForType(Icons::SlotPublicIconType)); + break; + case CodeCompletionResult::NotAccessible: + case CodeCompletionResult::NotAvailable: + item->setIcon(m_icons.iconForType(Icons::SlotPrivateIconType)); + break; + } + break; + + case CodeCompletionResult::NamespaceCompletionKind: item->setIcon(m_icons.iconForType(Icons::NamespaceIconType)); break; + case CodeCompletionResult::PreProcessorCompletionKind: item->setIcon(m_icons.iconForType(Icons::MacroIconType)); break; + case CodeCompletionResult::VariableCompletionKind: + switch (ccr.availability()) { + case CodeCompletionResult::Available: + case CodeCompletionResult::Deprecated: + item->setIcon(m_icons.iconForType(Icons::VarPublicIconType)); + break; + default: + item->setIcon(m_icons.iconForType(Icons::VarPrivateIconType)); + break; + } + break; + + case CodeCompletionResult::KeywordCompletionKind: + item->setIcon(m_icons.iconForType(Icons::KeywordIconType)); + break; + + case CodeCompletionResult::ClangSnippetKind: + item->setIcon(snippetIcon); + break; + + default: + break; + } + } + + foreach (ClangAssistProposalItem *item, items.values()) + m_completions.append(item); + + return m_startPosition; +} + +/** + * @brief Creates completion proposals for #include and given cursor + * @param cursor - cursor placed after opening bracked or quote + * @return false if completions list is empty + */ +bool ClangCompletionAssistProcessor::completeInclude(const QTextCursor &cursor) +{ + QString directoryPrefix; + if (m_model->m_completionOperator == T_SLASH) { + QTextCursor c = cursor; + c.movePosition(QTextCursor::StartOfLine, QTextCursor::KeepAnchor); + QString sel = c.selectedText(); + int startCharPos = sel.indexOf(QLatin1Char('"')); + if (startCharPos == -1) { + startCharPos = sel.indexOf(QLatin1Char('<')); + m_model->m_completionOperator = T_ANGLE_STRING_LITERAL; + } else { + m_model->m_completionOperator = T_STRING_LITERAL; + } + if (startCharPos != -1) + directoryPrefix = sel.mid(startCharPos + 1, sel.length() - 1); + } + + // Make completion for all relevant includes + QStringList includePaths = m_interface->includePaths(); + const QString ¤tFilePath = QFileInfo(m_interface->fileName()).path(); + if (!includePaths.contains(currentFilePath)) + includePaths.append(currentFilePath); + + const Core::MimeType mimeType = Core::MimeDatabase::findByType(QLatin1String("text/x-c++hdr")); + const QStringList suffixes = mimeType.suffixes(); + + foreach (const QString &includePath, includePaths) { + QString realPath = includePath; + if (!directoryPrefix.isEmpty()) { + realPath += QLatin1Char('/'); + realPath += directoryPrefix; + } + completeIncludePath(realPath, suffixes); + } + + foreach (const QString &frameworkPath, m_interface->frameworkPaths()) { + QString realPath = frameworkPath; + if (!directoryPrefix.isEmpty()) { + realPath += QLatin1Char('/'); + realPath += directoryPrefix; + realPath += QLatin1String(".framework/Headers"); + } + completeIncludePath(realPath, suffixes); + } + + return !m_completions.isEmpty(); +} + +/** + * @brief Adds #include completion proposals using given include path + * @param realPath - one of directories where compiler searches includes + * @param suffixes - file suffixes for C/C++ header files + */ +void ClangCompletionAssistProcessor::completeIncludePath(const QString &realPath, + const QStringList &suffixes) +{ + QDirIterator i(realPath, QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot); + const QString hint = + QObject::tr("Location: ", "Parent folder for proposed #include completion") + + QDir::cleanPath(realPath); + while (i.hasNext()) { + const QString fileName = i.next(); + const QFileInfo fileInfo = i.fileInfo(); + const QString suffix = fileInfo.suffix(); + if (suffix.isEmpty() || suffixes.contains(suffix)) { + QString text = fileName.mid(realPath.length() + 1); + if (fileInfo.isDir()) + text += QLatin1Char('/'); + + ClangAssistProposalItem *item = new ClangAssistProposalItem; + item->setText(text); + item->setDetail(hint); + item->setIcon(m_icons.keywordIcon()); + item->keepCompletionOperator(m_model->m_completionOperator); + m_completions.append(item); + } + } +} + +void ClangCompletionAssistProcessor::completePreprocessor() +{ + foreach (const QString &preprocessorCompletion, m_preprocessorCompletions) + addCompletionItem(preprocessorCompletion, + m_icons.iconForType(Icons::MacroIconType)); + + if (m_interface->objcEnabled()) + addCompletionItem(QLatin1String("import"), + m_icons.iconForType(Icons::MacroIconType)); +} + +void ClangCompletionAssistProcessor::addCompletionItem(const QString &text, + const QIcon &icon, + int order, + const QVariant &data) +{ + ClangAssistProposalItem *item = new ClangAssistProposalItem; + item->setText(text); + item->setIcon(icon); + item->setOrder(order); + item->setData(data); + item->keepCompletionOperator(m_model->m_completionOperator); + m_completions.append(item); +} diff --git a/src/plugins/clangcodemodel/clangcompletion.h b/src/plugins/clangcodemodel/clangcompletion.h new file mode 100644 index 0000000000..7e9f027fb7 --- /dev/null +++ b/src/plugins/clangcodemodel/clangcompletion.h @@ -0,0 +1,144 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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. +** +****************************************************************************/ + +#ifndef CPPEDITOR_INTERNAL_CLANGCOMPLETION_H +#define CPPEDITOR_INTERNAL_CLANGCOMPLETION_H + +#include "clangcompleter.h" + +#include <cplusplus/Icons.h> + +#include <cpptools/cppcompletionassistprovider.h> + +#include <texteditor/codeassist/basicproposalitem.h> +#include <texteditor/codeassist/completionassistprovider.h> +#include <texteditor/codeassist/defaultassistinterface.h> +#include <texteditor/codeassist/iassistprocessor.h> + +#include <QStringList> +#include <QTextCursor> + +namespace ClangCodeModel { + +namespace Internal { +class ClangAssistProposalModel; + +class ClangCompletionAssistProvider : public CppTools::CppCompletionAssistProvider +{ +public: + ClangCompletionAssistProvider(); + + virtual TextEditor::IAssistProcessor *createProcessor() const; + virtual TextEditor::IAssistInterface *createAssistInterface( + ProjectExplorer::Project *project, TextEditor::BaseTextEditor *editor, + QTextDocument *document, int position, TextEditor::AssistReason reason) const; + +private: + ClangCodeModel::ClangCompleter::Ptr m_clangCompletionWrapper; +}; + +} // namespace Internal + +class CLANG_EXPORT ClangCompletionAssistInterface: public TextEditor::DefaultAssistInterface +{ +public: + ClangCompletionAssistInterface(ClangCodeModel::ClangCompleter::Ptr clangWrapper, + QTextDocument *document, + int position, + const QString &fileName, + TextEditor::AssistReason reason, + const QStringList &options, + const QStringList &includePaths, + const QStringList &frameworkPaths, + const Internal::PchInfo::Ptr &pchInfo); + + ClangCodeModel::ClangCompleter::Ptr clangWrapper() const + { return m_clangWrapper; } + + const ClangCodeModel::Internal::UnsavedFiles &unsavedFiles() const + { return m_unsavedFiles; } + + bool objcEnabled() const; + + const QStringList &options() const + { return m_options; } + + const QStringList &includePaths() const + { return m_includePaths; } + + const QStringList &frameworkPaths() const + { return m_frameworkPaths; } + +private: + ClangCodeModel::ClangCompleter::Ptr m_clangWrapper; + ClangCodeModel::Internal::UnsavedFiles m_unsavedFiles; + QStringList m_options, m_includePaths, m_frameworkPaths; + Internal::PchInfo::Ptr m_savedPchPointer; +}; + +class CLANG_EXPORT ClangCompletionAssistProcessor : public TextEditor::IAssistProcessor +{ +public: + ClangCompletionAssistProcessor(); + virtual ~ClangCompletionAssistProcessor(); + + virtual TextEditor::IAssistProposal *perform(const TextEditor::IAssistInterface *interface); + +private: + int startCompletionHelper(); + int startOfOperator(int pos, unsigned *kind, bool wantFunctionCall) const; + int findStartOfName(int pos = -1) const; + bool accepts() const; + TextEditor::IAssistProposal *createContentProposal(); + + int startCompletionInternal(const QString fileName, + unsigned line, unsigned column, + int endOfExpression); + + bool completeInclude(const QTextCursor &cursor); + void completeIncludePath(const QString &realPath, const QStringList &suffixes); + void completePreprocessor(); + void addCompletionItem(const QString &text, + const QIcon &icon = QIcon(), + int order = 0, + const QVariant &data = QVariant()); + +private: + int m_startPosition; + QScopedPointer<const ClangCompletionAssistInterface> m_interface; + QList<TextEditor::BasicProposalItem *> m_completions; + CPlusPlus::Icons m_icons; + QStringList m_preprocessorCompletions; + QScopedPointer<Internal::ClangAssistProposalModel> m_model; + TextEditor::IAssistProposal *m_hintProposal; +}; + +} // namespace Clang + +#endif // CPPEDITOR_INTERNAL_CLANGCOMPLETION_H diff --git a/src/plugins/clangcodemodel/clanghighlightingsupport.cpp b/src/plugins/clangcodemodel/clanghighlightingsupport.cpp new file mode 100644 index 0000000000..57ca1435c5 --- /dev/null +++ b/src/plugins/clangcodemodel/clanghighlightingsupport.cpp @@ -0,0 +1,99 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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 "clanghighlightingsupport.h" + +#include <coreplugin/idocument.h> +#include <texteditor/basetexteditor.h> +#include <texteditor/itexteditor.h> + +#include <QTextBlock> +#include <QTextEdit> +#include "pchmanager.h" + + +using namespace ClangCodeModel; +using namespace ClangCodeModel::Internal; +using namespace CppTools; + +ClangHighlightingSupport::ClangHighlightingSupport(TextEditor::ITextEditor *textEditor, FastIndexer *fastIndexer) + : CppHighlightingSupport(textEditor) + , m_fastIndexer(fastIndexer) + , m_semanticMarker(new ClangCodeModel::SemanticMarker) +{ +} + +ClangHighlightingSupport::~ClangHighlightingSupport() +{ +} + +bool ClangHighlightingSupport::hightlighterHandlesIfdefedOutBlocks() const +{ +#if CINDEX_VERSION_MINOR >= 21 + return true; +#else + return false; +#endif +} + +QFuture<TextEditor::HighlightingResult> ClangHighlightingSupport::highlightingFuture( + const CPlusPlus::Document::Ptr &doc, + const CPlusPlus::Snapshot &snapshot) const +{ + Q_UNUSED(doc); + Q_UNUSED(snapshot); + + TextEditor::BaseTextEditorWidget *ed = qobject_cast<TextEditor::BaseTextEditorWidget *>(editor()->widget()); + int firstLine = 1; + int lastLine = ed->document()->blockCount(); + + const QString fileName = editor()->document()->filePath(); + CppModelManagerInterface *modelManager = CppModelManagerInterface::instance(); + QList<ProjectPart::Ptr> parts = modelManager->projectPart(fileName); + if (parts.isEmpty()) + parts += modelManager->fallbackProjectPart(); + QStringList options; + PchInfo::Ptr pchInfo; + foreach (const ProjectPart::Ptr &part, parts) { + if (part.isNull()) + continue; + options = Utils::createClangOptions(part, fileName); + pchInfo = PCHManager::instance()->pchInfo(part); + if (!pchInfo.isNull()) + options.append(ClangCodeModel::Utils::createPCHInclusionOptions(pchInfo->fileName())); + if (!options.isEmpty()) + break; + } + + CreateMarkers *createMarkers = CreateMarkers::create(m_semanticMarker, + fileName, options, + firstLine, lastLine, + m_fastIndexer, pchInfo); + return createMarkers->start(); +} diff --git a/src/plugins/clangcodemodel/clanghighlightingsupport.h b/src/plugins/clangcodemodel/clanghighlightingsupport.h new file mode 100644 index 0000000000..7c795c6962 --- /dev/null +++ b/src/plugins/clangcodemodel/clanghighlightingsupport.h @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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. +** +****************************************************************************/ + +#ifndef CLANG_CLANGHIGHLIGHTINGSUPPORT_H +#define CLANG_CLANGHIGHLIGHTINGSUPPORT_H + +#include "clangutils.h" +#include "cppcreatemarkers.h" +#include "fastindexer.h" + +#include <cpptools/cpphighlightingsupport.h> + +#include <QObject> +#include <QScopedPointer> + +namespace ClangCodeModel { + +class ClangHighlightingSupport: public CppTools::CppHighlightingSupport +{ +public: + ClangHighlightingSupport(TextEditor::ITextEditor *textEditor, + Internal::FastIndexer *fastIndexer); + ~ClangHighlightingSupport(); + + virtual bool requiresSemanticInfo() const + { return false; } + + virtual bool hightlighterHandlesDiagnostics() const + { return true; } + + virtual bool hightlighterHandlesIfdefedOutBlocks() const; + + virtual QFuture<TextEditor::HighlightingResult> highlightingFuture( + const CPlusPlus::Document::Ptr &doc, const CPlusPlus::Snapshot &snapshot) const; + +private: + Internal::FastIndexer *m_fastIndexer; + ClangCodeModel::SemanticMarker::Ptr m_semanticMarker; +}; + +} // namespace ClangCodeModel + +#endif // CLANG_CLANGHIGHLIGHTINGSUPPORT_H diff --git a/src/plugins/clangcodemodel/clangindexer.cpp b/src/plugins/clangcodemodel/clangindexer.cpp new file mode 100644 index 0000000000..9c9e6ca4cf --- /dev/null +++ b/src/plugins/clangcodemodel/clangindexer.cpp @@ -0,0 +1,169 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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 "clangindexer.h" +#include "clangsymbolsearcher.h" +#include "clangutils.h" +#include "indexer.h" +#include "liveunitsmanager.h" + +#include <coreplugin/icore.h> +#include <coreplugin/progressmanager/progressmanager.h> +#include <cpptools/cppmodelmanagerinterface.h> +#include <projectexplorer/projectexplorer.h> +#include <projectexplorer/session.h> + +#include <QDir> + +using namespace ClangCodeModel; +using namespace ClangCodeModel::Internal; + +ClangIndexingSupport::ClangIndexingSupport(ClangIndexer *indexer) + : m_indexer(indexer) +{ +} + +ClangIndexingSupport::~ClangIndexingSupport() +{ +} + +QFuture<void> ClangIndexingSupport::refreshSourceFiles(const QStringList &sourceFiles) +{ + return m_indexer->refreshSourceFiles(sourceFiles); +} + +CppTools::SymbolSearcher *ClangIndexingSupport::createSymbolSearcher(CppTools::SymbolSearcher::Parameters parameters, QSet<QString> fileNames) +{ + return new ClangSymbolSearcher(m_indexer, parameters, fileNames); +} + +ClangIndexer::ClangIndexer() + : QObject(0) + , m_indexingSupport(new ClangIndexingSupport(this)) + , m_isLoadingSession(false) + , m_clangIndexer(new Indexer(this)) +{ + connect(m_clangIndexer, SIGNAL(indexingStarted(QFuture<void>)), + this, SLOT(onIndexingStarted(QFuture<void>))); + + ProjectExplorer::ProjectExplorerPlugin *pe = + ProjectExplorer::ProjectExplorerPlugin::instance(); + + ProjectExplorer::SessionManager *session = pe->session(); + connect(session, SIGNAL(aboutToLoadSession(QString)), + this, SLOT(onAboutToLoadSession(QString))); + connect(session, SIGNAL(sessionLoaded(QString)), + this, SLOT(onSessionLoaded(QString))); + connect(session, SIGNAL(aboutToSaveSession()), + this, SLOT(onAboutToSaveSession())); +} + +ClangIndexer::~ClangIndexer() +{ + m_clangIndexer->cancel(true); +} + +CppTools::CppIndexingSupport *ClangIndexer::indexingSupport() +{ + return m_indexingSupport.data(); +} + +QFuture<void> ClangIndexer::refreshSourceFiles(const QStringList &sourceFiles) +{ + typedef CppTools::ProjectPart ProjectPart; + CppTools::CppModelManagerInterface *mmi = CppTools::CppModelManagerInterface::instance(); + LiveUnitsManager *lum = LiveUnitsManager::instance(); + + if (m_clangIndexer->isBusy()) + m_clangIndexer->cancel(true); + + foreach (const QString &file, sourceFiles) { + if (lum->isTracking(file)) + continue; // we get notified separately about open files. + const QList<ProjectPart::Ptr> &parts = mmi->projectPart(file); + if (!parts.isEmpty()) + m_clangIndexer->addFile(file, parts.at(0)); + else + m_clangIndexer->addFile(file, ProjectPart::Ptr()); + } + + if (!m_isLoadingSession) + m_clangIndexer->regenerate(); + + return QFuture<void>(); +} + +void ClangIndexer::match(ClangSymbolSearcher *searcher) const +{ + m_clangIndexer->match(searcher); +} + +void ClangIndexer::onAboutToLoadSession(const QString &sessionName) +{ + m_isLoadingSession = true; + + if (sessionName == QLatin1String("default")) + return; + + QString path = Core::ICore::instance()->userResourcePath() + QLatin1String("/codemodel/"); + if (QFile::exists(path) || QDir().mkpath(path)) + m_clangIndexer->initialize(path + sessionName + QLatin1String(".qci")); +} + +void ClangIndexer::onSessionLoaded(QString) +{ + m_isLoadingSession = false; + m_clangIndexer->regenerate(); +} + +void ClangIndexer::onAboutToSaveSession() +{ + m_clangIndexer->finalize(); +} + +void ClangIndexer::indexNow(const ClangCodeModel::Internal::Unit &unit) +{ + typedef CppTools::ProjectPart ProjectPart; + + QString file = unit.fileName(); + CppTools::CppModelManagerInterface *mmi = CppTools::CppModelManagerInterface::instance(); + const QList<ProjectPart::Ptr> &parts = mmi->projectPart(file); + ProjectPart::Ptr part; + if (!parts.isEmpty()) + part = parts.at(0); + if (!m_isLoadingSession) + m_clangIndexer->runQuickIndexing(unit, part); +} + +void ClangIndexer::onIndexingStarted(QFuture<void> indexingFuture) +{ + Core::ICore::instance()->progressManager()->addTask(indexingFuture, + tr("C++ Indexing"), + QLatin1String("Key.Temp.Indexing")); +} diff --git a/src/plugins/debugger/lldblib/lldbenginehost.h b/src/plugins/clangcodemodel/clangindexer.h index d39f6748a9..cd0351de02 100644 --- a/src/plugins/debugger/lldblib/lldbenginehost.h +++ b/src/plugins/clangcodemodel/clangindexer.h @@ -27,62 +27,68 @@ ** ****************************************************************************/ -#ifndef DEBUGGER_LLDBENGINE_HOST_H -#define DEBUGGER_LLDBENGINE_HOST_H +#ifndef CLANGINDEXER_H +#define CLANGINDEXER_H -#include "ipcenginehost.h" -#include <ssh/ssherrors.h> -#include <ssh/sshconnection.h> -#include <ssh/sshremoteprocess.h> -#include <ssh/sshremoteprocessrunner.h> +#include "fastindexer.h" -#include <QProcess> -#include <QQueue> +#include <cpptools/cppindexingsupport.h> + +#include <QObject> + +namespace ClangCodeModel { + +class Indexer; -namespace Debugger { namespace Internal { -class SshIODevice : public QIODevice +class ClangIndexer; +class ClangSymbolSearcher; + +class ClangIndexingSupport: public CppTools::CppIndexingSupport { -Q_OBJECT public: - SshIODevice(QSsh::SshRemoteProcessRunner *r); - ~SshIODevice(); - virtual qint64 bytesAvailable () const; - virtual qint64 writeData (const char * data, qint64 maxSize); - virtual qint64 readData (char * data, qint64 maxSize); -private slots: - void processStarted(); - void outputAvailable(); - void errorOutputAvailable(); + ClangIndexingSupport(ClangIndexer *indexer); + virtual ~ClangIndexingSupport(); + + virtual QFuture<void> refreshSourceFiles(const QStringList &sourceFiles); + virtual CppTools::SymbolSearcher *createSymbolSearcher(CppTools::SymbolSearcher::Parameters parameters, QSet<QString> fileNames); + private: - QSsh::SshRemoteProcessRunner *runner; - QSsh::SshRemoteProcess::Ptr proc; - int buckethead; - QQueue<QByteArray> buckets; - QByteArray startupbuffer; + ClangIndexer *m_indexer; }; -class LldbEngineHost : public IPCEngineHost +class ClangIndexer: public QObject, public FastIndexer { Q_OBJECT public: - explicit LldbEngineHost(const DebuggerStartParameters &startParameters); - ~LldbEngineHost(); + ClangIndexer(); + ~ClangIndexer(); + + CppTools::CppIndexingSupport *indexingSupport(); + + QFuture<void> refreshSourceFiles(const QStringList &sourceFiles); + + void match(ClangSymbolSearcher *searcher) const; + + void indexNow(const Unit &unit); + +public slots: + void onAboutToLoadSession(const QString &sessionName); + void onSessionLoaded(QString); + void onAboutToSaveSession(); -private: - QProcess *m_guestProcess; - QSsh::SshRemoteProcessRunner *m_ssh; -protected: - void nuke(); private slots: - void sshConnectionError(QSsh::SshError); - void finished(int, QProcess::ExitStatus); - void stderrReady(); + void onIndexingStarted(QFuture<void> indexingFuture); + +private: + QScopedPointer<ClangIndexingSupport> m_indexingSupport; + bool m_isLoadingSession; + Indexer *m_clangIndexer; }; } // namespace Internal -} // namespace Debugger +} // namespace ClangCodeModel -#endif // DEBUGGER_LLDBENGINE_HOST_H +#endif // CLANGINDEXER_H diff --git a/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp b/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp new file mode 100644 index 0000000000..db90ebad8f --- /dev/null +++ b/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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 "clangcompletion.h" +#include "clanghighlightingsupport.h" +#include "clangmodelmanagersupport.h" + +#include <QCoreApplication> + +using namespace ClangCodeModel; +using namespace ClangCodeModel::Internal; + +ModelManagerSupport::ModelManagerSupport(FastIndexer *fastIndexer) + : m_completionAssistProvider(new ClangCompletionAssistProvider) + , m_fastIndexer(fastIndexer) +{ +} + +ModelManagerSupport::~ModelManagerSupport() +{ +} + +QString ModelManagerSupport::id() const +{ + return QLatin1String("ClangCodeMode.ClangCodeMode"); +} + +QString ModelManagerSupport::displayName() const +{ + return QCoreApplication::translate("ModelManagerSupport::displayName", + "Clang"); +} + +CppTools::CppCompletionAssistProvider *ModelManagerSupport::completionAssistProvider() +{ + return m_completionAssistProvider.data(); +} + +CppTools::CppHighlightingSupport *ModelManagerSupport::highlightingSupport( + TextEditor::ITextEditor *editor) +{ + return new ClangHighlightingSupport(editor, m_fastIndexer); +} diff --git a/src/plugins/clangcodemodel/clangmodelmanagersupport.h b/src/plugins/clangcodemodel/clangmodelmanagersupport.h new file mode 100644 index 0000000000..833c5f6672 --- /dev/null +++ b/src/plugins/clangcodemodel/clangmodelmanagersupport.h @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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. +** +****************************************************************************/ + +#ifndef CLANGCODEMODEL_INTERNAL_CLANGMODELMANAGERSUPPORT_H +#define CLANGCODEMODEL_INTERNAL_CLANGMODELMANAGERSUPPORT_H + +#include <cpptools/cppmodelmanagersupport.h> + +#include <QScopedPointer> + +namespace ClangCodeModel { +namespace Internal { + +class FastIndexer; + +class ModelManagerSupport: public CppTools::ModelManagerSupport +{ + Q_DISABLE_COPY(ModelManagerSupport) + +public: + ModelManagerSupport(FastIndexer *fastIndexer); + virtual ~ModelManagerSupport(); + + virtual QString id() const; + virtual QString displayName() const; + + virtual CppTools::CppCompletionAssistProvider *completionAssistProvider(); + virtual CppTools::CppHighlightingSupport *highlightingSupport(TextEditor::ITextEditor *editor); + +private: + QScopedPointer<CppTools::CppCompletionAssistProvider> m_completionAssistProvider; + FastIndexer *m_fastIndexer; +}; + +} // namespace Internal +} // namespace ClangCodeModel + +#endif // CLANGCODEMODEL_INTERNAL_CLANGMODELMANAGERSUPPORT_H diff --git a/src/plugins/clangcodemodel/clangprojectsettings.cpp b/src/plugins/clangcodemodel/clangprojectsettings.cpp new file mode 100644 index 0000000000..a0712324e1 --- /dev/null +++ b/src/plugins/clangcodemodel/clangprojectsettings.cpp @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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 "clangprojectsettings.h" + +using namespace ClangCodeModel; + +ClangProjectSettings::ClangProjectSettings(ProjectExplorer::Project *project) + : m_project(project) + , m_pchUsage(PchUse_None) +{ + Q_ASSERT(project); + + connect(project, SIGNAL(settingsLoaded()), + this, SLOT(pullSettings())); + connect(project, SIGNAL(aboutToSaveSettings()), + this, SLOT(pushSettings())); +} + +ClangProjectSettings::~ClangProjectSettings() +{ +} + +ProjectExplorer::Project *ClangProjectSettings::project() const +{ + return m_project; +} + +ClangProjectSettings::PchUsage ClangProjectSettings::pchUsage() const +{ + return m_pchUsage; +} + +void ClangProjectSettings::setPchUsage(ClangProjectSettings::PchUsage pchUsage) +{ + if (pchUsage < PchUse_None || pchUsage > PchUse_Custom) + return; + + if (m_pchUsage != pchUsage) { + m_pchUsage = pchUsage; + emit pchSettingsChanged(); + } +} + +QString ClangProjectSettings::customPchFile() const +{ + return m_customPchFile; +} + +void ClangProjectSettings::setCustomPchFile(const QString &customPchFile) +{ + if (m_customPchFile != customPchFile) { + m_customPchFile = customPchFile; + emit pchSettingsChanged(); + } +} + +static QLatin1String PchUsageKey("PchUsage"); +static QLatin1String CustomPchFileKey("CustomPchFile"); +static QLatin1String SettingsNameKey("ClangProjectSettings"); + +void ClangProjectSettings::pushSettings() +{ + QVariantMap settings; + settings[PchUsageKey] = m_pchUsage; + settings[CustomPchFileKey] = m_customPchFile; + + QVariant s(settings); + m_project->setNamedSettings(SettingsNameKey, s); +} + +void ClangProjectSettings::pullSettings() +{ + QVariant s = m_project->namedSettings(SettingsNameKey); + QVariantMap settings = s.toMap(); + + const PchUsage storedPchUsage = static_cast<PchUsage>( + settings.value(PchUsageKey, PchUse_Unknown).toInt()); + if (storedPchUsage != PchUse_Unknown) + setPchUsage(storedPchUsage); + setCustomPchFile(settings.value(CustomPchFileKey).toString()); +} diff --git a/src/plugins/clangcodemodel/clangprojectsettings.h b/src/plugins/clangcodemodel/clangprojectsettings.h new file mode 100644 index 0000000000..aa87553aa8 --- /dev/null +++ b/src/plugins/clangcodemodel/clangprojectsettings.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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. +** +****************************************************************************/ + +#ifndef CLANGPROJECTSETTINGS_H +#define CLANGPROJECTSETTINGS_H + +#include "clang_global.h" + +#include <projectexplorer/project.h> + +#include <QObject> +#include <QString> + +namespace ClangCodeModel { + +class CLANG_EXPORT ClangProjectSettings: public QObject +{ + Q_OBJECT + +public: + enum PchUsage { + PchUse_Unknown = 0, + PchUse_None = 1, + PchUse_BuildSystem_Exact = 2, + PchUse_BuildSystem_Fuzzy = 3, + PchUse_Custom = 4 + }; + +public: + ClangProjectSettings(ProjectExplorer::Project *project); + virtual ~ClangProjectSettings(); + + ProjectExplorer::Project *project() const; + + PchUsage pchUsage() const; + void setPchUsage(PchUsage pchUsage); + + QString customPchFile() const; + void setCustomPchFile(const QString &customPchFile); + +signals: + void pchSettingsChanged(); + +public slots: + void pullSettings(); + void pushSettings(); + +private: + ProjectExplorer::Project *m_project; + PchUsage m_pchUsage; + QString m_customPchFile; +}; + +} // ClangCodeModel namespace + +#endif // CLANGPROJECTSETTINGS_H diff --git a/src/plugins/clangcodemodel/clangprojectsettingspropertiespage.cpp b/src/plugins/clangcodemodel/clangprojectsettingspropertiespage.cpp new file mode 100644 index 0000000000..f8f021c4b5 --- /dev/null +++ b/src/plugins/clangcodemodel/clangprojectsettingspropertiespage.cpp @@ -0,0 +1,165 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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 "clangprojectsettings.h" +#include "clangprojectsettingspropertiespage.h" +#include "pchmanager.h" + +#include <QButtonGroup> +#include <QCoreApplication> +#include <QFileDialog> + +using namespace ProjectExplorer; +using namespace ClangCodeModel::Internal; + +static const char CLANGPROJECTSETTINGS_PANEL_ID[] = "ClangCodeModel.ProjectPanel"; + + +QString ClangProjectSettingsPanelFactory::id() const +{ + return QLatin1String(CLANGPROJECTSETTINGS_PANEL_ID); +} + +QString ClangProjectSettingsPanelFactory::displayName() const +{ + return QCoreApplication::translate("ClangProjectSettingsPropertiesPage", + "Clang Settings"); +} + +int ClangProjectSettingsPanelFactory::priority() const +{ + return 60; +} + +bool ClangProjectSettingsPanelFactory::supports(Project *project) +{ + Q_UNUSED(project); + + return true; +} + +PropertiesPanel *ClangProjectSettingsPanelFactory::createPanel(Project *project) +{ + PropertiesPanel *panel = new PropertiesPanel; + panel->setDisplayName(QCoreApplication::translate( + "ClangProjectSettingsPropertiesPage", + "Clang Settings")); + panel->setWidget(new ClangProjectSettingsWidget(project)); + return panel; +} + +ClangProjectSettingsWidget::ClangProjectSettingsWidget(Project *project) + : m_project(project) +{ + m_ui.setupUi(this); + + ClangProjectSettings *cps = PCHManager::instance()->settingsForProject(project); + Q_ASSERT(cps); + + QButtonGroup *pchGroup = new QButtonGroup(this); + pchGroup->addButton(m_ui.noneButton, ClangProjectSettings::PchUse_None); + pchGroup->addButton(m_ui.exactButton, ClangProjectSettings::PchUse_BuildSystem_Exact); + pchGroup->addButton(m_ui.fuzzyButton, ClangProjectSettings::PchUse_BuildSystem_Fuzzy); + pchGroup->addButton(m_ui.customButton, ClangProjectSettings::PchUse_Custom); + switch (cps->pchUsage()) { + case ClangProjectSettings::PchUse_None: + case ClangProjectSettings::PchUse_BuildSystem_Exact: + case ClangProjectSettings::PchUse_BuildSystem_Fuzzy: + case ClangProjectSettings::PchUse_Custom: + pchGroup->button(cps->pchUsage())->setChecked(true); + break; + default: break; + } + pchUsageChanged(cps->pchUsage()); + connect(pchGroup, SIGNAL(buttonClicked(int)), + this, SLOT(pchUsageChanged(int))); + + m_ui.customField->setText(cps->customPchFile()); + connect(m_ui.customField, SIGNAL(editingFinished()), + this, SLOT(customPchFileChanged())); + connect(m_ui.customButton, SIGNAL(clicked()), + this, SLOT(customPchButtonClicked())); +} + +void ClangProjectSettingsWidget::pchUsageChanged(int id) +{ + ClangProjectSettings *cps = PCHManager::instance()->settingsForProject(m_project); + Q_ASSERT(cps); + cps->setPchUsage(static_cast<ClangProjectSettings::PchUsage>(id)); + + switch (id) { + case ClangProjectSettings::PchUse_None: + case ClangProjectSettings::PchUse_BuildSystem_Fuzzy: + case ClangProjectSettings::PchUse_BuildSystem_Exact: + m_ui.customField->setEnabled(false); + m_ui.chooseButton->setEnabled(false); + break; + + case ClangProjectSettings::PchUse_Custom: + m_ui.customField->setEnabled(true); + m_ui.chooseButton->setEnabled(true); + break; + + default: + break; + } +} + +void ClangProjectSettingsWidget::customPchFileChanged() +{ + ClangProjectSettings *cps = PCHManager::instance()->settingsForProject(m_project); + Q_ASSERT(cps); + if (cps->pchUsage() != ClangProjectSettings::PchUse_Custom) + return; + QString fileName = m_ui.customField->text(); + if (!QFile(fileName).exists()) + return; + + cps->setCustomPchFile(fileName); +} + +void ClangProjectSettingsWidget::customPchButtonClicked() +{ + ClangProjectSettings *cps = PCHManager::instance()->settingsForProject(m_project); + Q_ASSERT(cps); + + QFileDialog d(this); + d.setNameFilters(QStringList() << tr("Header Files (*.h)") + << tr("All Files (*)")); + d.setFileMode(QFileDialog::ExistingFile); + d.setDirectory(m_project->projectDirectory()); + if (!d.exec()) + return; + const QStringList fileNames = d.selectedFiles(); + if (fileNames.isEmpty() || fileNames.first().isEmpty()) + return; + + m_ui.customField->setText(fileNames.first()); + cps->setCustomPchFile(fileNames.first()); +} diff --git a/src/plugins/clangcodemodel/clangprojectsettingspropertiespage.h b/src/plugins/clangcodemodel/clangprojectsettingspropertiespage.h new file mode 100644 index 0000000000..19f8136833 --- /dev/null +++ b/src/plugins/clangcodemodel/clangprojectsettingspropertiespage.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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. +** +****************************************************************************/ + +#ifndef CLANGPROJECTSETTINGSPROPERTIESPAGE_H +#define CLANGPROJECTSETTINGSPROPERTIESPAGE_H + +#include "ui_clangprojectsettingspropertiespage.h" + +#include <projectexplorer/iprojectproperties.h> + +#include <QString> + +namespace ClangCodeModel { +namespace Internal { + +class ClangProjectSettingsPanelFactory: public ProjectExplorer::IProjectPanelFactory +{ +public: + QString id() const; + QString displayName() const; + int priority() const; + bool supports(ProjectExplorer::Project *project); + ProjectExplorer::PropertiesPanel *createPanel(ProjectExplorer::Project *project); +}; + +class ClangProjectSettingsWidget: public QWidget +{ + Q_OBJECT + +public: + ClangProjectSettingsWidget(ProjectExplorer::Project *project); + +protected slots: + void pchUsageChanged(int id); + void customPchFileChanged(); + void customPchButtonClicked(); + +private: + Ui::ClangProjectSettingsPropertiesPage m_ui; + ProjectExplorer::Project *m_project; +}; + +} // ClangCodeModel namespace +} // Internal namespace + +#endif // CLANGPROJECTSETTINGSPROPERTIESPAGE_H diff --git a/src/plugins/clangcodemodel/clangprojectsettingspropertiespage.ui b/src/plugins/clangcodemodel/clangprojectsettingspropertiespage.ui new file mode 100644 index 0000000000..66dcf45c5b --- /dev/null +++ b/src/plugins/clangcodemodel/clangprojectsettingspropertiespage.ui @@ -0,0 +1,87 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>ClangCodeModel::Internal::ClangProjectSettingsPropertiesPage</class> + <widget class="QWidget" name="ClangCodeModel::Internal::ClangProjectSettingsPropertiesPage"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>814</width> + <height>330</height> + </rect> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <layout class="QGridLayout" name="gridLayout"> + <item row="0" column="0"> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Pre-compiled headers:</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QRadioButton" name="noneButton"> + <property name="text"> + <string>None</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QRadioButton" name="exactButton"> + <property name="text"> + <string>Build system (exact)</string> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="QRadioButton" name="fuzzyButton"> + <property name="text"> + <string>Build system (fuzzy)</string> + </property> + </widget> + </item> + <item row="3" column="1"> + <widget class="QRadioButton" name="customButton"> + <property name="text"> + <string>Custom</string> + </property> + </widget> + </item> + <item row="3" column="2"> + <widget class="QLineEdit" name="customField"> + <property name="enabled"> + <bool>false</bool> + </property> + </widget> + </item> + <item row="3" column="3"> + <widget class="QPushButton" name="chooseButton"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Choose...</string> + </property> + </widget> + </item> + </layout> + </item> + <item> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>12</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/src/plugins/clangcodemodel/clangsymbolsearcher.cpp b/src/plugins/clangcodemodel/clangsymbolsearcher.cpp new file mode 100644 index 0000000000..f1ab12db48 --- /dev/null +++ b/src/plugins/clangcodemodel/clangsymbolsearcher.cpp @@ -0,0 +1,155 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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 "clangsymbolsearcher.h" +#include "symbol.h" + +#include <cpptools/searchsymbols.h> + +#include <cassert> + +using namespace ClangCodeModel; +using namespace ClangCodeModel::Internal; + +ClangSymbolSearcher::ClangSymbolSearcher(ClangIndexer *indexer, const Parameters ¶meters, QSet<QString> fileNames, QObject *parent) + : CppTools::SymbolSearcher(parent) + , m_indexer(indexer) + , m_parameters(parameters) + , m_fileNames(fileNames) + , m_future(0) +{ + assert(indexer); +} + +ClangSymbolSearcher::~ClangSymbolSearcher() +{ +} + +void ClangSymbolSearcher::runSearch(QFutureInterface<SearchResultItem> &future) +{ + m_future = &future; + m_indexer->match(this); + m_future = 0; +} + +void ClangSymbolSearcher::search(const QLinkedList<Symbol> &allSymbols) +{ + QString findString = (m_parameters.flags & Find::FindRegularExpression + ? m_parameters.text : QRegExp::escape(m_parameters.text)); + if (m_parameters.flags & Find::FindWholeWords) + findString = QString::fromLatin1("\\b%1\\b").arg(findString); + QRegExp matcher(findString, (m_parameters.flags & Find::FindCaseSensitively + ? Qt::CaseSensitive : Qt::CaseInsensitive)); + + const int chunkSize = 10; + QVector<Find::SearchResultItem> resultItems; + resultItems.reserve(100); + + m_future->setProgressRange(0, allSymbols.size() % chunkSize); + m_future->setProgressValue(0); + + int symbolNr = 0; + foreach (const Symbol &s, allSymbols) { + if (symbolNr % chunkSize == 0) { + m_future->setProgressValue(symbolNr / chunkSize); + m_future->reportResults(resultItems); + resultItems.clear(); + + if (m_future->isPaused()) + m_future->waitForResume(); + if (m_future->isCanceled()) + return; + } + ++symbolNr; + + CppTools::ModelItemInfo info; + + switch (s.m_kind) { + case Symbol::Enum: + if (m_parameters.types & SymbolSearcher::Enums) { + info.type = CppTools::ModelItemInfo::Enum; + info.symbolType = QLatin1String("enum"); + break; + } else { + continue; + } + case Symbol::Class: + if (m_parameters.types & SymbolSearcher::Classes) { + info.type = CppTools::ModelItemInfo::Class; + info.symbolType = QLatin1String("class"); + break; + } else { + continue; + } + case Symbol::Method: + case Symbol::Function: + case Symbol::Constructor: + case Symbol::Destructor: + if (m_parameters.types & SymbolSearcher::Functions) { + info.type = CppTools::ModelItemInfo::Method; + break; + } else { + continue; + } + case Symbol::Declaration: + if (m_parameters.types & SymbolSearcher::Declarations) { + info.type = CppTools::ModelItemInfo::Declaration; + break; + } else { + continue; + } + + default: continue; + } + + if (matcher.indexIn(s.m_name) == -1) + continue; + + info.symbolName = s.m_name; + info.fullyQualifiedName = s.m_qualification.split(QLatin1String("::")) << s.m_name; + info.fileName = s.m_location.fileName(); + info.icon = s.iconForSymbol(); + info.line = s.m_location.line(); + info.column = s.m_location.column() - 1; + + Find::SearchResultItem item; + item.path << s.m_qualification; + item.text = s.m_name; + item.icon = info.icon; + item.textMarkPos = -1; + item.textMarkLength = 0; + item.lineNumber = -1; + item.userData = qVariantFromValue(info); + + resultItems << item; + } + + if (!resultItems.isEmpty()) + m_future->reportResults(resultItems); +} diff --git a/src/plugins/clangcodemodel/clangsymbolsearcher.h b/src/plugins/clangcodemodel/clangsymbolsearcher.h new file mode 100644 index 0000000000..70615308de --- /dev/null +++ b/src/plugins/clangcodemodel/clangsymbolsearcher.h @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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. +** +****************************************************************************/ + +#ifndef CLANGSYMBOLSEARCHER_H +#define CLANGSYMBOLSEARCHER_H + +#include "clangindexer.h" + +#include <cpptools/cppindexingsupport.h> + +#include <QLinkedList> + +namespace ClangCodeModel { + +class Symbol; + +namespace Internal { + +class ClangSymbolSearcher: public CppTools::SymbolSearcher +{ + Q_OBJECT + + typedef CppTools::SymbolSearcher::Parameters Parameters; + typedef Find::SearchResultItem SearchResultItem; + +public: + ClangSymbolSearcher(ClangIndexer *indexer, const Parameters ¶meters, QSet<QString> fileNames, QObject *parent = 0); + virtual ~ClangSymbolSearcher(); + virtual void runSearch(QFutureInterface<SearchResultItem> &future); + + void search(const QLinkedList<Symbol> &allSymbols); + +private: + ClangIndexer *m_indexer; + const Parameters m_parameters; + const QSet<QString> m_fileNames; + QFutureInterface<SearchResultItem> *m_future; +}; + +} // namespace Internal +} // namespace ClangCodeModel + +#endif // CLANGSYMBOLSEARCHER_H diff --git a/src/plugins/clangcodemodel/clangutils.cpp b/src/plugins/clangcodemodel/clangutils.cpp new file mode 100644 index 0000000000..24e7d459d5 --- /dev/null +++ b/src/plugins/clangcodemodel/clangutils.cpp @@ -0,0 +1,322 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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 "clangutils.h" + +#include <clang-c/Index.h> + +#include <coreplugin/documentmanager.h> +#include <coreplugin/icore.h> +#include <coreplugin/idocument.h> + +#include <QFile> +#include <QSet> +#include <QString> + +using namespace ClangCodeModel; +using namespace ClangCodeModel::Internal; +using namespace Core; +using namespace CppTools; + +namespace ClangCodeModel { +namespace Utils { + +namespace { +bool isBlacklisted(const QString &path) +{ + static QStringList blacklistedPaths = QStringList() + << QLatin1String("lib/gcc/i686-apple-darwin"); + + foreach (const QString &blacklisted, blacklistedPaths) + if (path.contains(blacklisted)) + return true; + + return false; +} +} // anonymous namespace + +UnsavedFiles createUnsavedFiles(CppModelManagerInterface::WorkingCopy workingCopy) +{ + // TODO: change the modelmanager to hold one working copy, and amend it every time we ask for one. + // TODO: Reason: the UnsavedFile needs a QByteArray. + + QSet<QString> modifiedFiles; + foreach (IDocument *doc, Core::DocumentManager::modifiedDocuments()) + modifiedFiles.insert(doc->filePath()); + + UnsavedFiles result; + QHashIterator<QString, QPair<QByteArray, unsigned> > wcIter = workingCopy.iterator(); + while (wcIter.hasNext()) { + wcIter.next(); + const QString &fileName = wcIter.key(); + if (modifiedFiles.contains(fileName) && QFile(fileName).exists()) + result.insert(fileName, wcIter.value().first); + } + + return result; +} + +/** + * @brief Creates list of command-line arguments required for correct parsing + * @param pPart Null if file isn't part of any project + * @param fileName Path to file, non-empty + */ +QStringList createClangOptions(const ProjectPart::Ptr &pPart, const QString &fileName) +{ + ProjectFile::Kind fileKind = ProjectFile::Unclassified; + if (!pPart.isNull()) + foreach (const ProjectFile &file, pPart->files) + if (file.path == fileName) { + fileKind = file.kind; + break; + } + if (fileKind == ProjectFile::Unclassified) + fileKind = ProjectFile::classify(fileName); + + return createClangOptions(pPart, fileKind); +} + +static QStringList buildDefines(const QByteArray &defines, bool toolchainDefines) +{ + QStringList result; + + foreach (QByteArray def, defines.split('\n')) { + if (def.isEmpty()) + continue; + + if (toolchainDefines) { + //### FIXME: the next 3 check shouldn't be needed: we probably don't want to get the compiler-defined defines in. + if (!def.startsWith("#define ")) + continue; + if (def.startsWith("#define _")) + continue; + if (def.startsWith("#define OBJC_NEW_PROPERTIES")) + continue; + } + + QByteArray str = def.mid(8); + int spaceIdx = str.indexOf(' '); + QString arg; + if (spaceIdx != -1) { + arg = QLatin1String("-D" + str.left(spaceIdx) + "=" + str.mid(spaceIdx + 1)); + } else { + arg = QLatin1String("-D" + str); + } + arg = arg.replace(QLatin1String("\\\""), QLatin1String("\"")); + arg = arg.replace(QLatin1String("\""), QLatin1String("")); + if (!result.contains(arg)) + result.append(arg); + } + + return result; +} + +/** + * @brief Creates list of command-line arguments required for correct parsing + * @param pPart Null if file isn't part of any project + * @param fileKind Determines language and source/header state + */ +QStringList createClangOptions(const ProjectPart::Ptr &pPart, ProjectFile::Kind fileKind) +{ + QStringList result = clangLanguageOption(fileKind); + switch (fileKind) { + default: + case CppTools::ProjectFile::CXXHeader: + case CppTools::ProjectFile::CXXSource: + case CppTools::ProjectFile::ObjCXXHeader: + case CppTools::ProjectFile::ObjCXXSource: + case CppTools::ProjectFile::CudaSource: + case CppTools::ProjectFile::OpenCLSource: + result << clangOptionsForCxx(pPart->qtVersion, + pPart->cxxVersion, + pPart->cxxExtensions); + break; + case CppTools::ProjectFile::CHeader: + case CppTools::ProjectFile::CSource: + case CppTools::ProjectFile::ObjCHeader: + case CppTools::ProjectFile::ObjCSource: + result << clangOptionsForC(pPart->cVersion, + pPart->cxxExtensions); + break; + } + + if (pPart.isNull()) + return result; + + result << QLatin1String("-nostdinc"); + + result << buildDefines(pPart->toolchainDefines, true); + result << buildDefines(pPart->projectDefines, false); + + foreach (const QString &frameworkPath, pPart->frameworkPaths) + result.append(QLatin1String("-F") + frameworkPath); + foreach (const QString &inc, pPart->includePaths) + if (!inc.isEmpty() && !isBlacklisted(inc)) + result << (QLatin1String("-I") + inc); + +#if 0 + qDebug() << "--- m_args:"; + foreach (const QString &arg, result) + qDebug() << "\t" << qPrintable(arg); + qDebug() << "---"; +#endif + + return result; +} + +/// @return Option to speed up parsing with precompiled header +QStringList createPCHInclusionOptions(const QStringList &pchFiles) +{ + QStringList opts; + + foreach (const QString &pchFile, pchFiles) { + if (QFile(pchFile).exists()) { + opts += QLatin1String("-include-pch"); + opts += pchFile; + } + } + + return opts; +} + +/// @return Option to speed up parsing with precompiled header +QStringList createPCHInclusionOptions(const QString &pchFile) +{ + return createPCHInclusionOptions(QStringList() << pchFile); +} + +/// @return "-std" flag to select standard, flags for C extensions +QStringList clangOptionsForC(ProjectPart::CVersion cVersion, ProjectPart::CXXExtensions cxxExtensions) +{ + QStringList opts; + bool gnuExpensions = cxxExtensions & ProjectPart::GnuExtensions; + switch (cVersion) { + case ProjectPart::C89: + opts << (gnuExpensions ? QLatin1String("-std=gnu89") : QLatin1String("-std=c89")); + break; + case ProjectPart::C99: + opts << (gnuExpensions ? QLatin1String("-std=gnu99") : QLatin1String("-std=c99")); + break; + case ProjectPart::C11: + opts << (gnuExpensions ? QLatin1String("-std=gnu11") : QLatin1String("-std=c11")); + break; + } + + if (cxxExtensions & ProjectPart::MicrosoftExtensions) { + opts << QLatin1String("-fms-extensions"); + } + +#if defined(CINDEX_VERSION) // clang 3.2 or higher + if (cxxExtensions & ProjectPart::BorlandExtensions) + opts << QLatin1String("-fborland-extensions"); +#endif + + return opts; +} + +/// @return "-std" flag to select standard, flags for C++ extensions, Qt injections +QStringList clangOptionsForCxx(ProjectPart::QtVersion qtVersion, + ProjectPart::CXXVersion cxxVersion, + ProjectPart::CXXExtensions cxxExtensions) +{ + QStringList opts; + bool gnuExpensions = cxxExtensions & ProjectPart::GnuExtensions; + switch (cxxVersion) { + case ProjectPart::CXX11: + opts << (gnuExpensions ? QLatin1String("-std=gnu++11") : QLatin1String("-std=c++11")); + break; + case ProjectPart::CXX98: + opts << (gnuExpensions ? QLatin1String("-std=gnu++98") : QLatin1String("-std=c++98")); + break; + } + + if (cxxExtensions & ProjectPart::MicrosoftExtensions) { + opts << QLatin1String("-fms-extensions") + << QLatin1String("-fdelayed-template-parsing"); + } + +#if defined(CINDEX_VERSION) // clang 3.2 or higher + if (cxxExtensions & ProjectPart::BorlandExtensions) + opts << QLatin1String("-fborland-extensions"); +#endif + + static const QString injectedHeader(Core::ICore::instance()->resourcePath() + QLatin1String("/cplusplus/qt%1-qobjectdefs-injected.h")); +// if (qtVersion == ProjectPart::Qt4) +// opts << QLatin1String("-include") << injectedHeader.arg(QLatin1Char('4')); + if (qtVersion == ProjectPart::Qt5) + opts << QLatin1String("-include") << injectedHeader.arg(QLatin1Char('5')); + + return opts; +} + +/// @return "-x language-code" +QStringList clangLanguageOption(ProjectFile::Kind fileKind) +{ + QStringList opts; + opts += QLatin1String("-x"); + + switch (fileKind) { + case ProjectFile::CHeader: +// opts += QLatin1String("c-header"); +// break; + case ProjectFile::CXXHeader: + default: + opts += QLatin1String("c++-header"); + break; + case ProjectFile::CXXSource: + opts += QLatin1String("c++"); + break; + case ProjectFile::CSource: + opts += QLatin1String("c"); + break; + case ProjectFile::ObjCHeader: +// opts += QLatin1String("objective-c-header"); +// break; + case ProjectFile::ObjCXXHeader: + opts += QLatin1String("objective-c++-header"); + break; + case ProjectFile::ObjCSource: + opts += QLatin1String("objective-c"); + break; + case ProjectFile::ObjCXXSource: + opts += QLatin1String("objective-c++"); + break; + case ProjectFile::OpenCLSource: + opts += QLatin1String("cl"); + break; + case ProjectFile::CudaSource: + opts += QLatin1String("cuda"); + break; + } + + return opts; +} + +} // namespace Utils +} // namespace Clang diff --git a/src/plugins/clangcodemodel/clangutils.h b/src/plugins/clangcodemodel/clangutils.h new file mode 100644 index 0000000000..bca42657f9 --- /dev/null +++ b/src/plugins/clangcodemodel/clangutils.h @@ -0,0 +1,58 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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. +** +****************************************************************************/ + +#ifndef CPPTOOLS_CLANGUTILS_H +#define CPPTOOLS_CLANGUTILS_H + +#include "utils.h" + +#include <cpptools/cppmodelmanagerinterface.h> + +namespace ClangCodeModel { +namespace Utils { + +ClangCodeModel::Internal::UnsavedFiles createUnsavedFiles(CppTools::CppModelManagerInterface::WorkingCopy workingCopy); + +QStringList createClangOptions(const CppTools::ProjectPart::Ptr &pPart, CppTools::ProjectFile::Kind fileKind); +QStringList createClangOptions(const CppTools::ProjectPart::Ptr &pPart, const QString &fileName = QString()); +QStringList clangNonProjectFileOptions(CppTools::ProjectFile::Kind kind); +QStringList createPCHInclusionOptions(const QStringList &pchFiles); +QStringList createPCHInclusionOptions(const QString &pchFile); + +QStringList clangLanguageOption(CppTools::ProjectFile::Kind fileKind); +QStringList clangOptionsForC(CppTools::ProjectPart::CVersion cVersion, + CppTools::ProjectPart::CXXExtensions cxxExtensions); +QStringList clangOptionsForCxx(CppTools::ProjectPart::QtVersion qtVersion, + CppTools::ProjectPart::CXXVersion cxxVersion, + CppTools::ProjectPart::CXXExtensions cxxExtensions); + +} // namespace Utils +} // namespace Clang + +#endif // CPPTOOLS_CLANGUTILS_H diff --git a/src/plugins/clangcodemodel/completionproposalsbuilder.cpp b/src/plugins/clangcodemodel/completionproposalsbuilder.cpp new file mode 100644 index 0000000000..235346e117 --- /dev/null +++ b/src/plugins/clangcodemodel/completionproposalsbuilder.cpp @@ -0,0 +1,748 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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 "completionproposalsbuilder.h" +#include "utils_p.h" + +#include <QTextDocument> +#include <QCoreApplication> + +enum PriorityFixes { + PriorityFix_ExplicitDestructorCall = 10 +}; + +namespace ClangCodeModel { + +namespace { +struct ObjCMessagePart { + QString text; + int signatureLen; // length of "setScale:" in "setScale: 13" + + ObjCMessagePart() : signatureLen(0) {} + + ObjCMessagePart(const QString &signature, int &indentBonus) + : text(signature) + , signatureLen(signature.length() + indentBonus) + { + indentBonus = 0; + } + + void addToSignature(const QString &signature) + { + text += signature; + signatureLen += signature.length(); + } +}; +} // anonymous namespace + +/** + * @class ClangCodeModel::CompletionProposalsBuilder + * @brief Captures completion lists and than processes sequence of completion chunks + * + * Can produce several completion proposals for one CXCompletionResult, if and + * only if result contains chunks with kind 'Optional' + * Different proposals can have the same text, it's normal behavior. + * + * @note Unit tests are in \a clangcompletion_test.cpp + * + * @note Unresolved problems: + * Function hint not appear after space: "foo(1, "; + * Slot can have optional arguments, that produces 2 slots. + * + */ + +CompletionProposalsBuilder::CompletionProposalsBuilder(QList<CodeCompletionResult> &results, quint64 contexts, bool isSignalSlotCompletion) + : m_results(results) + , m_contexts(contexts) + , m_isSignalSlotCompletion(isSignalSlotCompletion) +{ +} + +void CompletionProposalsBuilder::operator ()(const CXCompletionResult &cxResult) +{ + resetWithResult(cxResult); + +#if defined(CINDEX_VERSION) && (CINDEX_VERSION > 5) + const QString brief = Internal::getQString(clang_getCompletionBriefComment(cxResult.CompletionString)); + if (!brief.isEmpty()) + m_comment += QLatin1String("<b>Brief:</b> ") + Qt::escape(brief); +#endif + + if (m_resultAvailability == CodeCompletionResult::Deprecated) { + m_comment += QLatin1String("<b>@note</b> "); + m_comment += QCoreApplication::translate("deprecated C++ symbol", "Is deprecated"); + } + + m_hint = QLatin1String("<p>"); + switch (m_resultKind) { + case CodeCompletionResult::ObjCMessageCompletionKind: + concatChunksForObjectiveCMessage(cxResult); + break; + case CodeCompletionResult::ClassCompletionKind: + case CodeCompletionResult::NamespaceCompletionKind: + case CodeCompletionResult::EnumeratorCompletionKind: + concatChunksForNestedName(cxResult.CompletionString); + break; + case CodeCompletionResult::ClangSnippetKind: + concatChunksAsSnippet(cxResult.CompletionString); + break; + case CodeCompletionResult::SlotCompletionKind: + case CodeCompletionResult::SignalCompletionKind: + if (m_isSignalSlotCompletion) + concatSlotSignalSignature(cxResult.CompletionString); + else + concatChunksOnlyTypedText(cxResult.CompletionString); + break; + default: + concatChunksOnlyTypedText(cxResult.CompletionString); + break; + } + m_hint += QLatin1String("</p>"); + m_hint += m_comment; + + finalize(); + foreach (const OptionalChunk &chunk, m_optionalChunks) { + m_hint.insert(chunk.positionInHint, chunk.hint); + finalize(); + } +} + +/** + * @return Internal ClangCodeModel's completion kind, that affects further postprocessing + */ +CodeCompletionResult::Kind CompletionProposalsBuilder::getKind(const CXCompletionResult &cxResult) +{ + CXCompletionString complStr = cxResult.CompletionString; + CXCursorKind cursorKind = cxResult.CursorKind; + + switch (cursorKind) { + case CXCursor_Constructor: + return CodeCompletionResult::ConstructorCompletionKind; + + case CXCursor_Destructor: + return CodeCompletionResult::DestructorCompletionKind; + + case CXCursor_CXXMethod: { + const unsigned numAnnotations = clang_getCompletionNumAnnotations(complStr); + bool isSignal = false, isSlot = false; + for (unsigned i = 0; i < numAnnotations && !isSignal && !isSlot; ++i) { + CXString cxAnn = clang_getCompletionAnnotation(complStr, i); + QString ann = Internal::getQString(cxAnn); + isSignal = ann == QLatin1String("qt_signal"); + isSlot = ann == QLatin1String("qt_slot"); + } + if (isSignal) + return CodeCompletionResult::SignalCompletionKind; + if (isSlot) + return CodeCompletionResult::SlotCompletionKind; + } // intentional fall-through! + case CXCursor_ConversionFunction: + case CXCursor_FunctionDecl: + case CXCursor_FunctionTemplate: + case CXCursor_MemberRef: + case CXCursor_MemberRefExpr: + return CodeCompletionResult::FunctionCompletionKind; + break; + + case CXCursor_FieldDecl: + case CXCursor_VarDecl: + case CXCursor_ParmDecl: + case CXCursor_ObjCIvarDecl: + case CXCursor_ObjCPropertyDecl: + case CXCursor_ObjCSynthesizeDecl: + case CXCursor_NonTypeTemplateParameter: + return CodeCompletionResult::VariableCompletionKind; + + case CXCursor_Namespace: + case CXCursor_NamespaceAlias: + case CXCursor_NamespaceRef: + return CodeCompletionResult::NamespaceCompletionKind; + + case CXCursor_StructDecl: + case CXCursor_UnionDecl: + case CXCursor_ClassDecl: + case CXCursor_TypeRef: + case CXCursor_TemplateRef: + case CXCursor_TypedefDecl: + case CXCursor_ClassTemplate: + case CXCursor_ClassTemplatePartialSpecialization: + case CXCursor_ObjCClassRef: + case CXCursor_ObjCInterfaceDecl: + case CXCursor_ObjCImplementationDecl: + case CXCursor_ObjCCategoryDecl: + case CXCursor_ObjCCategoryImplDecl: + case CXCursor_ObjCProtocolDecl: + case CXCursor_ObjCProtocolRef: + case CXCursor_TemplateTypeParameter: + case CXCursor_TemplateTemplateParameter: + return CodeCompletionResult::ClassCompletionKind; + + case CXCursor_EnumConstantDecl: + return CodeCompletionResult::EnumeratorCompletionKind; + + case CXCursor_EnumDecl: + return CodeCompletionResult::EnumCompletionKind; + + case CXCursor_MacroDefinition: { + const unsigned numChunks = clang_getNumCompletionChunks(complStr); + for (unsigned i = 0; i < numChunks; ++i) { + CXCompletionChunkKind kind = clang_getCompletionChunkKind(complStr, i); + if (kind == CXCompletionChunk_Placeholder) { + return CodeCompletionResult::FunctionCompletionKind; + } + } + return CodeCompletionResult::PreProcessorCompletionKind; + } + + case CXCursor_PreprocessingDirective: + case CXCursor_MacroExpansion: + case CXCursor_InclusionDirective: + return CodeCompletionResult::PreProcessorCompletionKind; + + case CXCursor_ObjCClassMethodDecl: + case CXCursor_ObjCInstanceMethodDecl: + return CodeCompletionResult::ObjCMessageCompletionKind; + + case CXCursor_NotImplemented: { + const unsigned numChunks = clang_getNumCompletionChunks(complStr); + for (unsigned i = 0; i < numChunks; ++i) { + CXCompletionChunkKind kind = clang_getCompletionChunkKind(complStr, i); + if (kind == CXCompletionChunk_Placeholder) { + return CodeCompletionResult::ClangSnippetKind; + } + } + return CodeCompletionResult::KeywordCompletionKind; + } + + default: + break; + } + + return CodeCompletionResult::Other; +} + +/** + * @return Symbol availability, which is almost unused + */ +CodeCompletionResult::Availability CompletionProposalsBuilder::getAvailability(const CXCompletionResult &cxResult) +{ + CXCompletionString complStr = cxResult.CompletionString; + switch (clang_getCompletionAvailability(complStr)) { + case CXAvailability_Deprecated: + return CodeCompletionResult::Deprecated; + + case CXAvailability_NotAvailable: + return CodeCompletionResult::NotAvailable; + + case CXAvailability_NotAccessible: + return CodeCompletionResult::NotAccessible; + + default: + return CodeCompletionResult::Available; + } +} + +/** + * @return Start index of name, which is unused in Qt signal/slot signature + * @param text Text of Placeholder completion string chunk + */ +int CompletionProposalsBuilder::findNameInPlaceholder(const QString &text) +{ + bool firstIdPassed = false; + bool isInIdentifier = false; + int bracesCounter = 0; + int idStart = 0; + + for (int i = 0, n = text.size(); i < n; ++i) { + const QChar ch = text[i]; + + if (ch == QLatin1Char(':')) { + firstIdPassed = false; + isInIdentifier = false; + } + + if (ch == QLatin1Char('<') || ch == QLatin1Char('(')) { + if (isInIdentifier && text.mid(idStart, i - idStart) == QLatin1String("const")) + firstIdPassed = false; + ++bracesCounter; + isInIdentifier = false; + } else if (ch == QLatin1Char('>') || ch == QLatin1Char(')')) { + if (isInIdentifier && text.mid(idStart, i - idStart) == QLatin1String("const")) + firstIdPassed = false; + --bracesCounter; + isInIdentifier = false; + } else if (bracesCounter == 0) { + if (isInIdentifier) { + isInIdentifier = ch.isLetterOrNumber() || (ch == QLatin1Char('_')); + if (!isInIdentifier && text.mid(idStart, i - idStart) == QLatin1String("const")) + firstIdPassed = false; + } else if (ch.isLetter() || (ch == QLatin1Char('_'))) { + if (firstIdPassed) + return i; + isInIdentifier = true; + idStart = i; + firstIdPassed = true; + } + } + } + return text.size(); +} + +void CompletionProposalsBuilder::resetWithResult(const CXCompletionResult &cxResult) +{ + m_priority = clang_getCompletionPriority(cxResult.CompletionString); + m_resultKind = getKind(cxResult); + m_resultAvailability = getAvailability(cxResult); + m_hasParameters = false; + m_hint.clear(); + m_text.clear(); + m_snippet.clear(); + m_comment.clear(); + m_optionalChunks.clear(); +} + +/** + * @brief Appends completion proposal initialized with collected data + */ +void CompletionProposalsBuilder::finalize() +{ + // Fixes code completion: operator and destructor cases + if ((m_contexts & CXCompletionContext_DotMemberAccess) + || (m_contexts & CXCompletionContext_ArrowMemberAccess) + || (m_contexts & CXCompletionContext_AnyValue)) { + if (m_resultKind == CodeCompletionResult::DestructorCompletionKind) + m_priority *= PriorityFix_ExplicitDestructorCall; + else if (m_resultKind == CodeCompletionResult::FunctionCompletionKind + && m_text.startsWith(QLatin1String("operator"))) + return; + } + + CodeCompletionResult ccr(m_priority); + ccr.setCompletionKind(m_resultKind); + ccr.setAvailability(m_resultAvailability); + ccr.setHasParameters(m_hasParameters); + ccr.setHint(m_hint); + ccr.setText(m_text); + ccr.setSnippet(m_snippet); + m_results.append(ccr); +} + +/** + * @brief Creates text, hint and snippet + * + * Text is just signature, e.g. 'length' for [NSString length] or 'respondsToSelector:' + * for [id respondsToSelector:(SEL)sel]. + * Snippet is actual text, where any message parameter becames snippet part: + * 'respondsToSelector:$(SEL)sel$'. + * Hint consists of snippet preview and doxygen 'return' entry with returned type. + */ +void CompletionProposalsBuilder::concatChunksForObjectiveCMessage(const CXCompletionResult &cxResult) +{ + CXCompletionString cxString = cxResult.CompletionString; + const unsigned count = clang_getNumCompletionChunks(cxString); + unsigned index = 0; + QString hintPrefix; + if (cxResult.CursorKind == CXCursor_ObjCClassMethodDecl) + hintPrefix += QLatin1Char('+'); + else + hintPrefix += QLatin1Char('-'); + int indentBonus = 1; + + bool addSpaceAtPrefixEnd = true; + for (; index < count; ++index) { + CXCompletionChunkKind chunkKind = clang_getCompletionChunkKind(cxString, index); + if (chunkKind == CXCompletionChunk_TypedText || chunkKind == CXCompletionChunk_Informative) { + break; + } + const QString text = Internal::getQString(clang_getCompletionChunkText(cxString, index), false); + if (chunkKind == CXCompletionChunk_ResultType) { + hintPrefix += QLatin1String("("); + hintPrefix += Qt::escape(text); + hintPrefix += QLatin1String(") "); + indentBonus += 3 + text.length(); + addSpaceAtPrefixEnd = false; + } else { + hintPrefix += Qt::escape(text); + indentBonus += text.length(); + m_snippet += text; + } + } + if (addSpaceAtPrefixEnd) { + m_snippet += QLatin1Char(' '); + hintPrefix += QLatin1Char(' '); + indentBonus += 1; + } + + m_hint += hintPrefix; + + QList<ObjCMessagePart> parts; + bool previousWasTypedText = false; + for (; index < count; ++index) { + CXCompletionChunkKind chunkKind = clang_getCompletionChunkKind(cxString, index); + const QString text = Internal::getQString(clang_getCompletionChunkText(cxString, index), false); + + switch (chunkKind) { + case CXCompletionChunk_TypedText: + if (previousWasTypedText) + parts.back().addToSignature(text); + else + parts.append(ObjCMessagePart(text, indentBonus)); + m_snippet += text; + m_text += text; + break; + case CXCompletionChunk_Informative: + parts.append(ObjCMessagePart(text, indentBonus)); + break; + case CXCompletionChunk_Text: + case CXCompletionChunk_LeftParen: + case CXCompletionChunk_RightParen: + case CXCompletionChunk_Comma: + case CXCompletionChunk_HorizontalSpace: + m_snippet += text; + parts.back().text += Qt::escape(text); + break; + case CXCompletionChunk_Placeholder: + appendSnippet(text); + parts.back().text += QLatin1String("<b>"); + parts.back().text += Qt::escape(text); + parts.back().text += QLatin1String("</b>"); + break; + case CXCompletionChunk_LeftAngle: + m_snippet += text; + parts.back().text += QLatin1String("<"); + break; + case CXCompletionChunk_RightAngle: + m_snippet += text; + parts.back().text += QLatin1String(">"); + break; + default: + break; + } + + previousWasTypedText = (chunkKind == CXCompletionChunk_TypedText); + } + + int indent = 0; + foreach (const ObjCMessagePart &part, parts) + indent = qMax(indent, part.signatureLen); + bool isFirstPart = true; + foreach (const ObjCMessagePart &part, parts) { + if (!isFirstPart) + m_hint += QLatin1String("<br/>"); + isFirstPart = false; + for (int i = 0; i < indent - part.signatureLen; ++i) + m_hint += QLatin1String(" "); + m_hint += part.text; + } +} + +/** + * @brief Creates entries like 'MyClass', 'MyNamespace::', 'MyEnumClass::Value1' + */ +void CompletionProposalsBuilder::concatChunksForNestedName(const CXCompletionString &cxString) +{ + bool hasPlaceholder = false; + unsigned count = clang_getNumCompletionChunks(cxString); + for (unsigned i = 0; i < count; ++i) { + CXCompletionChunkKind chunkKind = clang_getCompletionChunkKind(cxString, i); + QString text = Internal::getQString(clang_getCompletionChunkText(cxString, i), false); + + switch (chunkKind) { + case CXCompletionChunk_TypedText: + case CXCompletionChunk_Text: + m_text += text; + m_snippet += text; + m_hint += text; + break; + + case CXCompletionChunk_LeftAngle: + case CXCompletionChunk_RightAngle: + case CXCompletionChunk_Comma: + case CXCompletionChunk_HorizontalSpace: + m_snippet += text; + m_hint += Qt::escape(text); + break; + + case CXCompletionChunk_Placeholder: + hasPlaceholder = true; + appendSnippet(text); + appendHintBold(text); + break; + + default: + break; + } + } + if (!hasPlaceholder) + m_snippet.clear(); +} + +/** + * @brief Creates text, snippet and hint for snippet preview + * + * Text is copy of snippet without '$' marks. + * Hint also have 'return' doxygen entry if applicable (e.g. 'typeid...') + */ +void CompletionProposalsBuilder::concatChunksAsSnippet(const CXCompletionString &cxString) +{ + unsigned count = clang_getNumCompletionChunks(cxString); + for (unsigned i = 0; i < count; ++i) { + CXCompletionChunkKind chunkKind = clang_getCompletionChunkKind(cxString, i); + const QString text = Internal::getQString(clang_getCompletionChunkText(cxString, i), false); + + switch (chunkKind) { + case CXCompletionChunk_ResultType: + attachResultTypeToComment(text); + break; + + case CXCompletionChunk_Placeholder: + m_text += text; + appendSnippet(text); + appendHintBold(text); + break; + case CXCompletionChunk_LeftAngle: + m_snippet += text; + m_text += text; + m_hint += QLatin1String("<"); + break; + case CXCompletionChunk_RightAngle: + m_snippet += text; + m_text += text; + m_hint += QLatin1String(">"); + break; + + case CXCompletionChunk_VerticalSpace: + m_snippet += QLatin1Char('\n'); + m_text += QLatin1Char(' '); + m_hint += QLatin1String("<br/>"); + break; + + default: + m_snippet += text; + m_text += text; + m_hint += text; + break; + } + } +} + +/** + * @brief Creates short text and hint with details + * + * Text is just function or variable name. Hint also contains function signature + * or variable type. + */ +void CompletionProposalsBuilder::concatChunksOnlyTypedText(const CXCompletionString &cxString) +{ + bool previousChunkWasLParen = false; + bool isInsideTemplateSpec = false; + + unsigned count = clang_getNumCompletionChunks(cxString); + for (unsigned i = 0; i < count; ++i) { + CXCompletionChunkKind chunkKind = clang_getCompletionChunkKind(cxString, i); + QString text = Internal::getQString(clang_getCompletionChunkText(cxString, i), false); + + switch (chunkKind) { + case CXCompletionChunk_LeftParen: + case CXCompletionChunk_RightParen: + case CXCompletionChunk_Text: + case CXCompletionChunk_LeftAngle: + case CXCompletionChunk_RightAngle: + m_hint += Qt::escape(text); + break; + + case CXCompletionChunk_HorizontalSpace: + case CXCompletionChunk_Comma: + if (isInsideTemplateSpec) { + m_snippet += text; + } + m_hint += Qt::escape(text); + break; + + case CXCompletionChunk_Placeholder: + if (isInsideTemplateSpec) { + appendSnippet(text); + } + m_hint += Qt::escape(text); + break; + + case CXCompletionChunk_TypedText: + m_text = text; + appendHintBold(text); + break; + + case CXCompletionChunk_ResultType: { + m_hint += Qt::escape(text); + QChar last = text[text.size() - 1]; + if (last != QLatin1Char('*') && last != QLatin1Char('&')) + m_hint += QLatin1Char(' '); + } + break; + + case CXCompletionChunk_Informative: + if (text == QLatin1String(" const")) + m_hint += text; + break; + + case CXCompletionChunk_Optional: + appendOptionalChunks(clang_getCompletionChunkCompletionString(cxString, i), + m_hint.size()); + break; + + default: + break; + } + + if (chunkKind == CXCompletionChunk_RightParen && previousChunkWasLParen) + m_hasParameters = false; + + if (chunkKind == CXCompletionChunk_LeftParen) { + previousChunkWasLParen = true; + m_hasParameters = true; + } else { + previousChunkWasLParen = false; + } + + if (chunkKind == CXCompletionChunk_LeftAngle) { + m_snippet = m_text; + m_snippet += text; + isInsideTemplateSpec = true; + } else if (chunkKind == CXCompletionChunk_RightAngle) { + isInsideTemplateSpec = false; + m_snippet += text; + } + } +} + +/** + * @brief Produces signal/slot signatures for 'connect' methods family + */ +void CompletionProposalsBuilder::concatSlotSignalSignature(const CXCompletionString &cxString) +{ + QString resultType; + + unsigned count = clang_getNumCompletionChunks(cxString); + for (unsigned i = 0; i < count; ++i) { + CXCompletionChunkKind chunkKind = clang_getCompletionChunkKind(cxString, i); + QString text = Internal::getQString(clang_getCompletionChunkText(cxString, i), false); + + switch (chunkKind) { + case CXCompletionChunk_Placeholder: + text.truncate(findNameInPlaceholder(text)); + // fall-through + case CXCompletionChunk_TypedText: + case CXCompletionChunk_LeftParen: + case CXCompletionChunk_RightParen: + case CXCompletionChunk_Comma: + case CXCompletionChunk_Text: + m_text += text; + break; + case CXCompletionChunk_ResultType: + resultType += text; + resultType += QLatin1Char(' '); + break; + + default: + break; + } + } + + const QString parent = Internal::getQString(clang_getCompletionParent(cxString, NULL)); + if (m_resultKind == CodeCompletionResult::SlotCompletionKind) + m_hint += QObject::tr("Slot of %1, returns %2").arg(parent).arg(resultType); + else + m_hint += QObject::tr("Signal of %1, returns %2").arg(parent).arg(resultType); +} + +/** + * @brief Stores optional part for further postprocessing in \a finalize() + * @param insertionIndex Index where to insert optional chunk into hint + */ +void CompletionProposalsBuilder::appendOptionalChunks(const CXCompletionString &cxString, + int insertionIndex) +{ + OptionalChunk chunk; + chunk.positionInHint = insertionIndex; + + unsigned count = clang_getNumCompletionChunks(cxString); + for (unsigned i = 0; i < count; ++i) { + CXCompletionChunkKind chunkKind = clang_getCompletionChunkKind(cxString, i); + QString text = Internal::getQString(clang_getCompletionChunkText(cxString, i), false); + + switch (chunkKind) { + case CXCompletionChunk_Placeholder: + chunk.hint += Qt::escape(text); + break; + + case CXCompletionChunk_Comma: + chunk.hint += text; + chunk.hint += QLatin1Char(' '); + break; + + case CXCompletionChunk_Optional: + m_optionalChunks.append(chunk); + appendOptionalChunks(clang_getCompletionChunkCompletionString(cxString, i), + insertionIndex + chunk.hint.size()); + return; + + default: + break; + } + } + + m_optionalChunks.append(chunk); +} + +void CompletionProposalsBuilder::attachResultTypeToComment(const QString &resultType) +{ + if (resultType.isEmpty()) + return; + + if (!m_comment.isEmpty()) + m_comment += QLatin1String("<br/>"); + + m_comment += QLatin1String("<b>@return</b> "); + m_comment += resultType; +} + +void CompletionProposalsBuilder::appendSnippet(const QString &text) +{ + m_snippet += QLatin1Char('$'); + m_snippet += text; + m_snippet += QLatin1Char('$'); +} + +void CompletionProposalsBuilder::appendHintBold(const QString &text) +{ + m_hint += QLatin1String("<b>"); + m_hint += Qt::escape(text); + m_hint += QLatin1String("</b>"); +} + +} // namespace ClangCodeModel diff --git a/src/plugins/clangcodemodel/completionproposalsbuilder.h b/src/plugins/clangcodemodel/completionproposalsbuilder.h new file mode 100644 index 0000000000..919fbd9c89 --- /dev/null +++ b/src/plugins/clangcodemodel/completionproposalsbuilder.h @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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. +** +****************************************************************************/ + +#ifndef CLANGCODEMODEL_COMPLETIONPROPOSALSBUILDER_H +#define CLANGCODEMODEL_COMPLETIONPROPOSALSBUILDER_H + +#include "clangcompleter.h" +#include "clang_global.h" +#include <clang-c/Index.h> + +namespace ClangCodeModel { + +class CLANG_EXPORT CompletionProposalsBuilder +{ +public: + CompletionProposalsBuilder(QList<CodeCompletionResult> &results, quint64 contexts, bool isSignalSlotCompletion); + void operator ()(const CXCompletionResult &cxResult); + +private: + struct OptionalChunk { + int positionInHint; + QString hint; + + OptionalChunk() : positionInHint(0) {} + }; + + static CodeCompletionResult::Kind getKind(const CXCompletionResult &cxResult); + static CodeCompletionResult::Availability getAvailability(const CXCompletionResult &cxResult); + static int findNameInPlaceholder(const QString &text); + void resetWithResult(const CXCompletionResult &cxResult); + void finalize(); + + void concatChunksForObjectiveCMessage(const CXCompletionResult &cxResult); + void concatChunksForNestedName(const CXCompletionString &cxString); + void concatChunksAsSnippet(const CXCompletionString &cxString); + void concatChunksOnlyTypedText(const CXCompletionString &cxString); + void concatSlotSignalSignature(const CXCompletionString &cxString); + void appendOptionalChunks(const CXCompletionString &cxString, + int insertionIndex); + void attachResultTypeToComment(const QString &text); + + void appendSnippet(const QString &text); + void appendHintBold(const QString &text); + + QList<CodeCompletionResult> &m_results; + const quint64 m_contexts; + const bool m_isSignalSlotCompletion; + + unsigned m_priority; + CodeCompletionResult::Kind m_resultKind; + CodeCompletionResult::Availability m_resultAvailability; + bool m_hasParameters; + QString m_hint; + QString m_text; + QString m_snippet; + QString m_comment; + QList<OptionalChunk> m_optionalChunks; +}; + +} // namespace ClangCodeModel + +#endif // CLANGCODEMODEL_COMPLETIONPROPOSALSBUILDER_H diff --git a/src/plugins/clangcodemodel/constants.h b/src/plugins/clangcodemodel/constants.h new file mode 100644 index 0000000000..2699cbeca8 --- /dev/null +++ b/src/plugins/clangcodemodel/constants.h @@ -0,0 +1,61 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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. +** +****************************************************************************/ + +#ifndef CONSTANTS_H +#define CONSTANTS_H + +#include <QtCore/QLatin1Char> +#include <QtCore/QLatin1Char> + +namespace ClangCodeModel { +namespace Constants { + +static const QLatin1Char kLParen('('); +static const QLatin1Char kRParen(')'); +static const QLatin1Char kLBrace('{'); +static const QLatin1Char kRBrace('}'); +static const QLatin1Char kLBracket('['); +static const QLatin1Char kRBracket(']'); +static const QLatin1Char kLABracket('<'); +static const QLatin1Char kRABracket('>'); +static const QLatin1Char kSemiColon(';'); +static const QLatin1Char kPound('#'); +static const QLatin1Char kColon(':'); +static const QLatin1Char kExclamation('!'); +static const QLatin1Char kSpace(' '); +static const QLatin1Char kSlash('/'); +static const QLatin1Char kStar('*'); +static const QLatin1Char kDoubleQuote('"'); +static const QLatin1Char kNewLine('\n'); +static const QLatin1Char kHorizontalTab('\t'); + +} +} + +#endif // CONSTANTS_H diff --git a/src/plugins/clangcodemodel/cppcreatemarkers.cpp b/src/plugins/clangcodemodel/cppcreatemarkers.cpp new file mode 100644 index 0000000000..ebd162e730 --- /dev/null +++ b/src/plugins/clangcodemodel/cppcreatemarkers.cpp @@ -0,0 +1,201 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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 "clangutils.h" +#include "cppcreatemarkers.h" + +#include <cplusplus/CppDocument.h> +#include <cpptools/cppmodelmanagerinterface.h> +#include <utils/runextensions.h> + +#include <QCoreApplication> +#include <QMutexLocker> +#include <QThreadPool> + +#include <QDebug> + +static const bool DebugTiming = !qgetenv("QTC_CLANG_VERBOSE").isEmpty(); + +using namespace ClangCodeModel; +using namespace ClangCodeModel::Internal; +using namespace CppTools; + +CreateMarkers *CreateMarkers::create(SemanticMarker::Ptr semanticMarker, + const QString &fileName, + const QStringList &options, + unsigned firstLine, unsigned lastLine, + FastIndexer *fastIndexer, + const Internal::PchInfo::Ptr &pchInfo) +{ + if (semanticMarker.isNull()) + return 0; + else + return new CreateMarkers(semanticMarker, fileName, options, firstLine, lastLine, fastIndexer, pchInfo); +} + +CreateMarkers::CreateMarkers(SemanticMarker::Ptr semanticMarker, + const QString &fileName, + const QStringList &options, + unsigned firstLine, unsigned lastLine, + FastIndexer *fastIndexer, + const Internal::PchInfo::Ptr &pchInfo) + : m_marker(semanticMarker) + , m_pchInfo(pchInfo) + , m_fileName(fileName) + , m_options(options) + , m_firstLine(firstLine) + , m_lastLine(lastLine) + , m_fastIndexer(fastIndexer) +{ + Q_ASSERT(!semanticMarker.isNull()); + + m_flushRequested = false; + m_flushLine = 0; + + m_unsavedFiles = Utils::createUnsavedFiles(CppModelManagerInterface::instance()->workingCopy()); +} + +CreateMarkers::~CreateMarkers() +{ } + +void CreateMarkers::run() +{ + QMutexLocker lock(m_marker->mutex()); + if (isCanceled()) + return; + + m_options += QLatin1String("-fspell-checking"); + + QTime t; + if (DebugTiming) { + qDebug() << "*** Highlighting from" << m_firstLine << "to" << m_lastLine << "of" << m_fileName; + qDebug() << "***** Options: " << m_options.join(QLatin1String(" ")); + t.start(); + } + + m_usages.clear(); + m_marker->setFileName(m_fileName); + m_marker->setCompilationOptions(m_options); + + m_marker->reparse(m_unsavedFiles); + + if (DebugTiming) + qDebug() << "*** Reparse for highlighting took" << t.elapsed() << "ms."; + + m_pchInfo.clear(); + + typedef CPlusPlus::Document::DiagnosticMessage OtherDiagnostic; + QList<OtherDiagnostic> msgs; + foreach (const ClangCodeModel::Diagnostic &d, m_marker->diagnostics()) { + if (DebugTiming) + qDebug() << d.severityAsString() << d.location() << d.spelling(); + + if (d.location().fileName() != m_marker->fileName()) + continue; + + // TODO: retrieve fix-its for this diagnostic + + int level; + switch (d.severity()) { + case Diagnostic::Fatal: level = OtherDiagnostic::Fatal; break; + case Diagnostic::Error: level = OtherDiagnostic::Error; break; + case Diagnostic::Warning: level = OtherDiagnostic::Warning; break; + default: continue; + } + msgs.append(OtherDiagnostic(level, d.location().fileName(), d.location().line(), + d.location().column(), d.spelling(), d.length())); + } + if (isCanceled()) { + reportFinished(); + return; + } + + CppModelManagerInterface *mmi = CppModelManagerInterface::instance(); + static const QString key = QLatin1String("ClangCodeModel.Diagnostics"); + mmi->setExtraDiagnostics(m_marker->fileName(), key, msgs); +#if CINDEX_VERSION_MINOR >= 21 + mmi->setIfdefedOutBlocks(m_marker->fileName(), m_marker->ifdefedOutBlocks()); +#endif + + if (isCanceled()) { + reportFinished(); + return; + } + + QList<ClangCodeModel::SourceMarker> markers = m_marker->sourceMarkersInRange(m_firstLine, m_lastLine); + foreach (const ClangCodeModel::SourceMarker &m, markers) + addUse(SourceMarker(m.location().line(), m.location().column(), m.length(), m.kind())); + + if (isCanceled()) { + reportFinished(); + return; + } + + flush(); + reportFinished(); + + if (DebugTiming) { + qDebug() << "*** Highlighting took" << t.elapsed() << "ms in total."; + t.restart(); + } + + if (m_fastIndexer) + m_fastIndexer->indexNow(m_marker->unit()); + + if (DebugTiming) + qDebug() << "*** Fast re-indexing took" << t.elapsed() << "ms in total."; +} + +void CreateMarkers::addUse(const SourceMarker &marker) +{ +// if (! enclosingFunctionDefinition()) { + if (m_usages.size() >= 100) { + if (m_flushRequested && marker.line != m_flushLine) + flush(); + else if (! m_flushRequested) { + m_flushRequested = true; + m_flushLine = marker.line; + } + } +// } + + m_usages.append(marker); +} + +void CreateMarkers::flush() +{ + m_flushRequested = false; + m_flushLine = 0; + + if (m_usages.isEmpty()) + return; + + reportResults(m_usages); + m_usages.clear(); +} diff --git a/src/plugins/clangcodemodel/cppcreatemarkers.h b/src/plugins/clangcodemodel/cppcreatemarkers.h new file mode 100644 index 0000000000..9f42f47c32 --- /dev/null +++ b/src/plugins/clangcodemodel/cppcreatemarkers.h @@ -0,0 +1,106 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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. +** +****************************************************************************/ + +#ifndef CPPCREATEMARKERS_H +#define CPPCREATEMARKERS_H + +#include "fastindexer.h" +#include "sourcemarker.h" +#include "semanticmarker.h" +#include "pchinfo.h" + +#include <texteditor/semantichighlighter.h> + +#include <QSet> +#include <QFuture> +#include <QtConcurrentRun> + +namespace ClangCodeModel { + +class CreateMarkers: + public QObject, + public QRunnable, + public QFutureInterface<TextEditor::HighlightingResult> +{ + Q_OBJECT + Q_DISABLE_COPY(CreateMarkers) + +public: + virtual ~CreateMarkers(); + + virtual void run(); + + typedef TextEditor::HighlightingResult SourceMarker; + + typedef QFuture<SourceMarker> Future; + + Future start() + { + this->setRunnable(this); + this->reportStarted(); + Future future = this->future(); + QThreadPool::globalInstance()->start(this, QThread::LowestPriority); + return future; + } + + static CreateMarkers *create(ClangCodeModel::SemanticMarker::Ptr semanticMarker, + const QString &fileName, + const QStringList &options, + unsigned firstLine, unsigned lastLine, + Internal::FastIndexer *fastIndexer, + const Internal::PchInfo::Ptr &pchInfo); + + void addUse(const SourceMarker &marker); + void flush(); + +protected: + CreateMarkers(ClangCodeModel::SemanticMarker::Ptr semanticMarker, + const QString &fileName, const QStringList &options, + unsigned firstLine, unsigned lastLine, + Internal::FastIndexer *fastIndexer, + const Internal::PchInfo::Ptr &pchInfo); + +private: + ClangCodeModel::SemanticMarker::Ptr m_marker; + Internal::PchInfo::Ptr m_pchInfo; + QString m_fileName; + QStringList m_options; + unsigned m_firstLine; + unsigned m_lastLine; + Internal::FastIndexer *m_fastIndexer; + QVector<SourceMarker> m_usages; + bool m_flushRequested; + unsigned m_flushLine; + + ClangCodeModel::Internal::UnsavedFiles m_unsavedFiles; +}; + +} // namespace ClangCodeModel + +#endif // CPPCREATEMARKERS_H diff --git a/src/plugins/clangcodemodel/cxprettyprinter.cpp b/src/plugins/clangcodemodel/cxprettyprinter.cpp new file mode 100644 index 0000000000..1b954462cd --- /dev/null +++ b/src/plugins/clangcodemodel/cxprettyprinter.cpp @@ -0,0 +1,550 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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 "cxprettyprinter.h" +#include "utils_p.h" +#include "cxraii.h" +#include <QStringList> + +using namespace ClangCodeModel; +using namespace ClangCodeModel::Internal; + +CXPrettyPrinter::CXPrettyPrinter() + : m_indent(0) +{ +} + +QString CXPrettyPrinter::toString(CXCompletionChunkKind kind) const +{ + switch (kind) { + case CXCompletionChunk_Optional: + return QLatin1String("Optional"); + case CXCompletionChunk_TypedText: + return QLatin1String("TypedText"); + case CXCompletionChunk_Text: + return QLatin1String("Text"); + case CXCompletionChunk_Placeholder: + return QLatin1String("Placeholder"); + case CXCompletionChunk_Informative: + return QLatin1String("Informative"); + case CXCompletionChunk_CurrentParameter: + return QLatin1String("CurrentParameter"); + case CXCompletionChunk_LeftParen: + return QLatin1String("LeftParen"); + case CXCompletionChunk_RightParen: + return QLatin1String("RightParen"); + case CXCompletionChunk_LeftBracket: + return QLatin1String("LeftBracket"); + case CXCompletionChunk_RightBracket: + return QLatin1String("RightBracket"); + case CXCompletionChunk_LeftBrace: + return QLatin1String("LeftBrace"); + case CXCompletionChunk_RightBrace: + return QLatin1String("RightBrace"); + case CXCompletionChunk_LeftAngle: + return QLatin1String("LeftAngle"); + case CXCompletionChunk_RightAngle: + return QLatin1String("RightAngle"); + case CXCompletionChunk_Comma: + return QLatin1String("Comma"); + case CXCompletionChunk_ResultType: + return QLatin1String("ResultType"); + case CXCompletionChunk_Colon: + return QLatin1String("Colon"); + case CXCompletionChunk_SemiColon: + return QLatin1String("SemiColon"); + case CXCompletionChunk_Equal: + return QLatin1String("Equal"); + case CXCompletionChunk_HorizontalSpace: + return QLatin1String("HorizontalSpace"); + case CXCompletionChunk_VerticalSpace: + return QLatin1String("VerticalSpace"); + default: + return QLatin1String("<UNKNOWN>"); + } +} + +QString CXPrettyPrinter::toString(CXAvailabilityKind kind) const +{ + switch (kind) { + case CXAvailability_Available: + return QLatin1String("Available"); + case CXAvailability_Deprecated: + return QLatin1String("Deprecated"); + case CXAvailability_NotAccessible: + return QLatin1String("NotAccessible"); + case CXAvailability_NotAvailable: + return QLatin1String("NotAvailable"); + default: + return QLatin1String("<UNKNOWN>"); + } +} + +QString CXPrettyPrinter::toString(CXCursorKind kind) const +{ + return getQString(clang_getCursorKindSpelling(kind)); +} + +QString CXPrettyPrinter::toString(CXDiagnosticSeverity severity) const +{ + switch (severity) + { + case CXDiagnostic_Ignored: + return QLatin1String("Ignored"); + case CXDiagnostic_Note: + return QLatin1String("Note"); + case CXDiagnostic_Warning: + return QLatin1String("Warning"); + case CXDiagnostic_Error: + return QLatin1String("Error"); + case CXDiagnostic_Fatal: + return QLatin1String("Fatal"); + default: + return QLatin1String("<UNKNOWN>"); + } +} + +QString CXPrettyPrinter::jsonForCompletionMeta(CXCodeCompleteResults *results) +{ + QString json; + m_printed.swap(json); + m_indent = 0; + + m_printed += QLatin1String("CXCodeCompleteResults {"); + m_indent += 4; + + CXCursorKind containerKind = clang_codeCompleteGetContainerKind(results, NULL); + writeLineEnd(); + m_printed += QLatin1String("'container CursorKind': "); + m_printed += toString(containerKind); + m_printed += QLatin1Char(','); + + QString containerUSR(Internal::getQString(clang_codeCompleteGetContainerUSR(results))); + if (!containerUSR.isEmpty()) { + writeLineEnd(); + m_printed += QLatin1String("'container USR': "); + m_printed += containerUSR; + m_printed += QLatin1Char(','); + } + + QString objCSelector(Internal::getQString(clang_codeCompleteGetObjCSelector(results))); + if (!objCSelector.isEmpty()) { + writeLineEnd(); + m_printed += QLatin1String("'Objective-C selector': "); + m_printed += objCSelector; + m_printed += QLatin1Char(','); + } + + writeLineEnd(); + m_printed += QLatin1String("'contexts': ["); + m_indent += 4; + writeCompletionContexts(results); + m_indent -= 4; + writeLineEnd(); + m_printed += QLatin1Char(']'); + + m_indent -= 4; + writeLineEnd(); + m_printed += QLatin1Char('}'); + + m_printed.swap(json); + return json; +} + +QString CXPrettyPrinter::jsonForCompletionString(const CXCompletionString &string) +{ + QString json; + m_printed.swap(json); + m_indent = 0; + + m_printed += QLatin1String("CXCompletionString: "); + writeCompletionStringJson(string); + + m_printed.swap(json); + return json; +} + +QString CXPrettyPrinter::jsonForCompletion(const CXCompletionResult &result) +{ + QString json; + m_printed.swap(json); + m_indent = 4; + + m_printed += QLatin1String("CXCompletionResult: {\n" + " CompletionString: "); + writeCompletionStringJson(result.CompletionString); + m_printed += QLatin1Char('\n'); + + m_printed += QLatin1String(" CursorKind: "); + m_printed += toString(result.CursorKind); + m_printed += QLatin1String(";\n}"); + + m_printed.swap(json); + return json; +} + +/** + * @brief CXPrettyPrinter::jsonForDiagnsotic + * @param diagnostic + * @return + * + * List of used clang-c API calls: + * CXDiagnosticSet clang_getChildDiagnostics(CXDiagnostic D); + * CXSourceLocation clang_getDiagnosticLocation(CXDiagnostic); + * CXString clang_getDiagnosticOption(CXDiagnostic Diag, + * CXString *Disable); + * unsigned clang_getDiagnosticCategory(CXDiagnostic); + * CXString clang_getDiagnosticCategoryText(CXDiagnostic); + * unsigned clang_getDiagnosticNumRanges(CXDiagnostic); + * CXSourceRange clang_getDiagnosticRange(CXDiagnostic Diagnostic, + * unsigned Range); + * unsigned clang_getDiagnosticNumFixIts(CXDiagnostic Diagnostic); + * CXString clang_getDiagnosticFixIt(CXDiagnostic Diagnostic, + * unsigned FixIt, + * CXSourceRange *ReplacementRange); + */ +QString CXPrettyPrinter::jsonForDiagnsotic(const CXDiagnostic &diagnostic) +{ + QString json; + m_printed.swap(json); + m_indent = 0; + + m_printed += QLatin1String("CXDiagnostic: "); + writeDiagnosticJson(diagnostic); + + m_printed.swap(json); + return json; +} + +void CXPrettyPrinter::writeCompletionContexts(CXCodeCompleteResults *results) +{ + quint64 contexts = clang_codeCompleteGetContexts(results); + QStringList lines; + + if (contexts & CXCompletionContext_AnyType) + lines << QLatin1String("'any type'"); + if (contexts & CXCompletionContext_AnyValue) + lines << QLatin1String("'any value'"); + if (contexts & CXCompletionContext_ObjCObjectValue) + lines << QLatin1String("'Objective-C object'"); + if (contexts & CXCompletionContext_ObjCSelectorValue) + lines << QLatin1String("'Objective-C selector'"); + if (contexts & CXCompletionContext_CXXClassTypeValue) + lines << QLatin1String("'C++ class'"); + if (contexts & CXCompletionContext_DotMemberAccess) + lines << QLatin1String("'. member access'"); + if (contexts & CXCompletionContext_ArrowMemberAccess) + lines << QLatin1String("'-> member access'"); + if (contexts & CXCompletionContext_ObjCPropertyAccess) + lines << QLatin1String("'. Objective-C property access'"); + if (contexts & CXCompletionContext_EnumTag) + lines << QLatin1String("'enum tag'"); + if (contexts & CXCompletionContext_UnionTag) + lines << QLatin1String("'union tag'"); + if (contexts & CXCompletionContext_StructTag) + lines << QLatin1String("'struct tag'"); + if (contexts & CXCompletionContext_ClassTag) + lines << QLatin1String("'C++ class tag'"); + if (contexts & CXCompletionContext_Namespace) + lines << QLatin1String("'namespace tag'"); + if (contexts & CXCompletionContext_NestedNameSpecifier) + lines << QLatin1String("'C++ nested name specifier'"); + if (contexts & CXCompletionContext_ObjCInterface) + lines << QLatin1String("'Objective-C interface'"); + if (contexts & CXCompletionContext_ObjCProtocol) + lines << QLatin1String("'Objective-C protocol'"); + if (contexts & CXCompletionContext_ObjCCategory) + lines << QLatin1String("'Objective-C category'"); + if (contexts & CXCompletionContext_ObjCInstanceMessage) + lines << QLatin1String("'Objective-C instance message'"); + if (contexts & CXCompletionContext_ObjCClassMessage) + lines << QLatin1String("'Objective-C class message'"); + if (contexts & CXCompletionContext_ObjCSelectorName) + lines << QLatin1String("'Objective-C selector name'"); + if (contexts & CXCompletionContext_MacroName) + lines << QLatin1String("'macro name'"); + if (contexts & CXCompletionContext_NaturalLanguage) + lines << QLatin1String("'natural language'"); + + foreach (const QString &line, lines) { + writeLineEnd(); + m_printed += line + QLatin1String(","); + } +} + +void CXPrettyPrinter::writeCompletionStringJson(const CXCompletionString &string) +{ + m_printed += QLatin1Char('{'); + writeLineEnd(); + + // availability + m_printed += QLatin1String("availability: "); + m_printed += toString(clang_getCompletionAvailability(string)); + m_printed += QLatin1Char(';'); + writeLineEnd(); + + // priority + m_printed += QLatin1String("priority: "); + m_printed += QString::number(clang_getCompletionPriority(string)); + m_printed += QLatin1Char(';'); + writeLineEnd(); + + // parent + m_printed += QLatin1String("parent: \'"); + m_printed += getQString(clang_getCompletionParent(string, NULL)); + m_printed += QLatin1String("\';"); + writeLineEnd(); + + // chunks + m_printed += QLatin1String("chunks: ["); + m_indent += 4; + unsigned numChunks = clang_getNumCompletionChunks(string); + for (unsigned i = 0; i < numChunks; ++i) { + writeLineEnd(); + writeCompletionChunkJson(string, i); + } + m_indent -= 4; + writeLineEnd(); + m_printed += QLatin1Char(']'); + writeLineEnd(); + + // annotation + m_printed += QLatin1String("annotations: ["); + m_indent += 4; + unsigned numAnns = clang_getCompletionNumAnnotations(string); + for (unsigned i = 0; i < numAnns; ++i) { + writeLineEnd(); + writeCompletionAnnotationJson(string, i); + } + m_indent -= 4; + writeLineEnd(); + m_printed += QLatin1Char(']'); + writeLineEnd(); + + m_printed += QLatin1Char('}'); +} + +void CXPrettyPrinter::writeCompletionChunkJson(const CXCompletionString &string, unsigned i) +{ + QString text = getQString(clang_getCompletionChunkText(string, i)); + QString kind = toString(clang_getCompletionChunkKind(string, i)); + CXCompletionString optional = clang_getCompletionChunkCompletionString(string, i); + + m_printed += kind; + m_printed += QLatin1String(": "); + if (!text.isEmpty()) { + m_printed += QLatin1Char('\''); + m_printed += text; + m_printed += QLatin1Char('\''); + } + if (optional != NULL) { + if (!text.isEmpty()) + m_printed += QLatin1String(", "); + m_indent += 4; + writeCompletionStringJson(optional); + m_indent -= 4; + } +} + +void CXPrettyPrinter::writeCompletionAnnotationJson(const CXCompletionString &string, unsigned i) +{ + m_printed += QLatin1Char('\''); + m_printed += getQString(clang_getCompletionAnnotation(string, i)); + m_printed += QLatin1Char('\''); +} + +void CXPrettyPrinter::writeDiagnosticJson(const CXDiagnostic &diag) +{ + m_printed += QLatin1String("{"); + m_indent += 4; + writeLineEnd(); + + // message + m_printed += QLatin1Char('\''); + m_printed += getQString(clang_formatDiagnostic(diag, /*options*/ 0)); + m_printed += QLatin1Char('\''); + writeLineEnd(); + + // severity + m_printed += QLatin1String("severity: "); + m_printed += toString(clang_getDiagnosticSeverity(diag)); + writeLineEnd(); + + // location + m_printed += QLatin1String("location: "); + writeLocationJson(clang_getDiagnosticLocation(diag)); + writeLineEnd(); + + // fix-its + unsigned numFixIts = clang_getDiagnosticNumFixIts(diag); + if (numFixIts > 0) { + m_printed += QLatin1String("FixIts: ["); + writeLineEnd(); + for (unsigned i = 0; i < numFixIts; ++i) { + writeFixItJson(diag, i); + writeLineEnd(); + } + m_printed += QLatin1String("]"); + writeLineEnd(); + } + + // clang CLI options + CXString cxDisabler; + QString enabler = getQString(clang_getDiagnosticOption(diag, &cxDisabler)); + QString disabler = getQString(cxDisabler); + if (!enabler.isEmpty()) { + m_printed += QLatin1String("enabledBy: \'"); + m_printed += enabler; + m_printed += QLatin1String("';"); + writeLineEnd(); + } + if (!disabler.isEmpty()) { + m_printed += QLatin1String("disabledBy: \'"); + m_printed += disabler; + m_printed += QLatin1String("';"); + writeLineEnd(); + } + + // diagnostic category + m_printed += QLatin1String("category: \'"); + m_printed += getQString(clang_getDiagnosticCategoryText(diag)); + m_printed += QLatin1String("';"); + + // ranges + unsigned numRanges = clang_getDiagnosticNumRanges(diag); + if (numRanges > 0) { + writeLineEnd(); + m_printed += QLatin1String("ranges: ["); + m_indent += 4; + for (unsigned i = 0; i < numRanges; ++i) { + writeLineEnd(); + writeRangeJson(clang_getDiagnosticRange(diag, i)); + } + m_indent -= 4; + writeLineEnd(); + m_printed += QLatin1String("]"); + } + + // children + CXDiagnosticSet set(clang_getChildDiagnostics(diag)); + unsigned numChildren = clang_getNumDiagnosticsInSet(set); + if (numChildren > 0) { + writeLineEnd(); + m_printed += QLatin1String("children: ["); + m_indent += 4; + for (unsigned i = 0; i < numChildren; ++i) { + writeLineEnd(); + ScopedCXDiagnostic child(clang_getDiagnosticInSet(set, i)); + writeDiagnosticJson(child); + } + m_indent -= 4; + writeLineEnd(); + m_printed += QLatin1String("]"); + } + + m_indent -= 4; + writeLineEnd(); + m_printed += QLatin1String("}"); +} + +void CXPrettyPrinter::writeFixItJson(const CXDiagnostic &diag, unsigned i) +{ + CXSourceRange range; // half-open range [a, b) + QString text = getQString(clang_getDiagnosticFixIt(diag, i, &range)); + + m_printed += QLatin1String("{ newText: "); + m_printed += QLatin1String("\'"); + m_printed += text; + m_printed += QLatin1String("\', range: "); + writeRangeJson(range); + m_printed += QLatin1String("}"); +} + +void CXPrettyPrinter::writeRangeJson(const CXSourceRange &range) +{ + SourceLocation start = getSpellingLocation(clang_getRangeStart(range)); + SourceLocation end = getSpellingLocation(clang_getRangeEnd(range)); + + m_printed += QLatin1Char('{'); + m_indent += 4; + writeLineEnd(); + + m_printed += QLatin1String("file: \'"); + m_printed += start.fileName(); + m_printed += QLatin1String("\',"); + writeLineEnd(); + + m_printed += QLatin1String("from: {"); + m_printed += QString::number(start.line()); + m_printed += QLatin1String(", "); + m_printed += QString::number(start.column()); + m_printed += QLatin1String("},"); + + m_printed += QLatin1String("to: {"); + m_printed += QString::number(end.line()); + m_printed += QLatin1String(", "); + m_printed += QString::number(end.column()); + m_printed += QLatin1Char('}'); + + m_indent -= 4; + writeLineEnd(); + m_printed += QLatin1Char('}'); +} + +void CXPrettyPrinter::writeLocationJson(const CXSourceLocation &location) +{ + SourceLocation loc = getSpellingLocation(location); + m_printed += QLatin1Char('{'); + m_indent += 4; + writeLineEnd(); + + m_printed += QLatin1String("file: \'"); + m_printed += loc.fileName(); + m_printed += QLatin1String("\',"); + writeLineEnd(); + + m_printed += QLatin1String("line: "); + m_printed += QString::number(loc.line()); + m_printed += QLatin1String(","); + writeLineEnd(); + + m_printed += QLatin1String("column: "); + m_printed += QString::number(loc.column()); + + m_indent -= 4; + writeLineEnd(); + m_printed += QLatin1String("}"); +} + +void CXPrettyPrinter::writeLineEnd() +{ + m_printed += QLatin1Char('\n'); + for (int i = 0; i < m_indent; ++i) + m_printed += QLatin1Char(' '); +} diff --git a/src/plugins/clangcodemodel/cxprettyprinter.h b/src/plugins/clangcodemodel/cxprettyprinter.h new file mode 100644 index 0000000000..2143181dac --- /dev/null +++ b/src/plugins/clangcodemodel/cxprettyprinter.h @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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. +** +****************************************************************************/ + +#ifndef CXPRETTYPRINTER_H +#define CXPRETTYPRINTER_H + +#include <clang-c/Index.h> +#include <QString> + +namespace ClangCodeModel { +namespace Internal { + +class CXPrettyPrinter +{ +public: + CXPrettyPrinter(); + + QString toString(CXCompletionChunkKind kind) const; + QString toString(CXAvailabilityKind kind) const; + QString toString(CXCursorKind kind) const; + QString toString(CXDiagnosticSeverity severity) const; + + QString jsonForCompletionMeta(CXCodeCompleteResults *results); + QString jsonForCompletionString(const CXCompletionString &string); + QString jsonForCompletion(const CXCompletionResult &result); + QString jsonForDiagnsotic(const CXDiagnostic &diagnostic); + +private: + int m_indent; + QString m_printed; + + void writeCompletionContexts(CXCodeCompleteResults *results); + void writeCompletionStringJson(const CXCompletionString &string); + void writeCompletionChunkJson(const CXCompletionString &string, unsigned i); + void writeCompletionAnnotationJson(const CXCompletionString &string, unsigned i); + + void writeDiagnosticJson(const CXDiagnostic &diag); + void writeFixItJson(const CXDiagnostic &diag, unsigned i); + + void writeRangeJson(const CXSourceRange &range); + void writeLocationJson(const CXSourceLocation &location); + + void writeLineEnd(); +}; + +} // namespace Internal +} // namespace ClangCodeModel + +#endif // CXPRETTYPRINTER_H diff --git a/src/plugins/clangcodemodel/cxraii.h b/src/plugins/clangcodemodel/cxraii.h new file mode 100644 index 0000000000..9461bb8dc1 --- /dev/null +++ b/src/plugins/clangcodemodel/cxraii.h @@ -0,0 +1,145 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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. +** +****************************************************************************/ + +#ifndef CXRAII_H +#define CXRAII_H + +#include <clang-c/Index.h> + +// Simple RAII types for their CX correspondings + +namespace ClangCodeModel { +namespace Internal { + +template <class CXType_T> +struct ScopedCXType +{ +protected: + typedef void (*DisposeFunction)(CXType_T); + + ScopedCXType(DisposeFunction f) + : m_cx(0), m_disposeFunction(f) + {} + ScopedCXType(const CXType_T &cx, DisposeFunction f) + : m_cx(cx) , m_disposeFunction(f) + {} + +public: + ~ScopedCXType() + { + dispose(); + } + + operator CXType_T() const { return m_cx; } + bool operator!() const { return !m_cx; } + bool isNull() const { return !m_cx; } + void reset(const CXType_T &cx) + { + dispose(); + m_cx = cx; + } + +private: + ScopedCXType(const ScopedCXType &); + const ScopedCXType &operator=(const ScopedCXType &); + + void dispose() + { + if (m_cx) + m_disposeFunction(m_cx); + } + + CXType_T m_cx; + DisposeFunction m_disposeFunction; +}; + +struct ScopedCXIndex : ScopedCXType<CXIndex> +{ + ScopedCXIndex() + : ScopedCXType<CXIndex>(&clang_disposeIndex) + {} + ScopedCXIndex(const CXIndex &index) + : ScopedCXType<CXIndex>(index, &clang_disposeIndex) + {} +}; + +struct ScopedCXTranslationUnit : ScopedCXType<CXTranslationUnit> +{ + ScopedCXTranslationUnit() + : ScopedCXType<CXTranslationUnit>(&clang_disposeTranslationUnit) + {} + ScopedCXTranslationUnit(const CXTranslationUnit &unit) + : ScopedCXType<CXTranslationUnit>(unit, &clang_disposeTranslationUnit) + {} +}; + +struct ScopedCXDiagnostic : ScopedCXType<CXDiagnostic> +{ + ScopedCXDiagnostic() + : ScopedCXType<CXDiagnostic>(&clang_disposeDiagnostic) + {} + ScopedCXDiagnostic(const CXDiagnostic &diagnostic) + : ScopedCXType<CXDiagnostic>(diagnostic, &clang_disposeDiagnostic) + {} +}; + +struct ScopedCXDiagnosticSet : ScopedCXType<CXDiagnostic> +{ + ScopedCXDiagnosticSet() + : ScopedCXType<CXDiagnosticSet>(&clang_disposeDiagnosticSet) + {} + ScopedCXDiagnosticSet(const CXDiagnostic &diagnostic) + : ScopedCXType<CXDiagnosticSet>(diagnostic, &clang_disposeDiagnosticSet) + {} +}; + +struct ScopedCXCodeCompleteResults : ScopedCXType<CXCodeCompleteResults*> +{ + ScopedCXCodeCompleteResults() + : ScopedCXType<CXCodeCompleteResults*>(&clang_disposeCodeCompleteResults) + {} + ScopedCXCodeCompleteResults(CXCodeCompleteResults *results) + : ScopedCXType<CXCodeCompleteResults*>(results, &clang_disposeCodeCompleteResults) + {} + + unsigned size() const + { + return static_cast<CXCodeCompleteResults *>(*this)->NumResults; + } + + const CXCompletionResult &completionAt(unsigned i) + { + return static_cast<CXCodeCompleteResults *>(*this)->Results[i]; + } +}; + +} // Internal +} // ClangCodeModel + +#endif // CXRAII_H diff --git a/src/plugins/clangcodemodel/dependencygraph.cpp b/src/plugins/clangcodemodel/dependencygraph.cpp new file mode 100644 index 0000000000..f0aadd6692 --- /dev/null +++ b/src/plugins/clangcodemodel/dependencygraph.cpp @@ -0,0 +1,208 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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 "dependencygraph.h" + +#include <QtCore/QtConcurrentRun> + +using namespace ClangCodeModel; +using namespace Internal; + +DependencyGraph::DependencyGraph() +{ + m_includeTracker.setResolutionMode(IncludeTracker::EveryMatchResolution); +} + +DependencyGraph::~DependencyGraph() +{ + discard(); +} + +void DependencyGraph::cancel() +{ + if (m_computeWatcher.isRunning()) { + m_computeWatcher.cancel(); + m_computeWatcher.waitForFinished(); + } +} + +void DependencyGraph::addFile(const QString &fileName, const QStringList &compilationOptions) +{ + cancel(); + + m_files.append(qMakePair(fileName, compilationOptions)); +} + +QFuture<void> DependencyGraph::compute() +{ + QFuture<void> future = QtConcurrent::run(this, &DependencyGraph::computeCore); + m_computeWatcher.setFuture(future); + return future; +} + +void DependencyGraph::computeCore() +{ + for (int i = 0; i < m_files.size(); ++i) { + if (m_computeWatcher.isCanceled()) + break; + + const QPair<QString, QStringList> &p = m_files.at(i); + const QString ¤tFile = p.first; + const QStringList &options = p.second; + const QPair<bool, NodeRefSetIt> &v = findVertex(currentFile); + if (!v.first) + processIncludes(insertVertex(currentFile), options); + } + + emit dependencyGraphAvailable(); +} + +void DependencyGraph::processIncludes(NodeRefSetIt currentIt, + const QStringList &compilationOptions) +{ + const QString ¤tFile = currentIt.key(); + const QStringList &includes = m_includeTracker.directIncludes(currentFile, compilationOptions); + foreach (const QString &include, includes) { + if (m_computeWatcher.isCanceled()) + return; + + QPair<bool, NodeRefSetIt> v = findVertex(include); + if (!v.first) { + v.second = insertVertex(include); + processIncludes(v.second, compilationOptions); + } + insertEdge(currentIt, v.second); + } +} + +namespace { + +struct SimpleVisitor +{ + bool acceptFile(const QString &fileName) + { + m_allFiles.append(fileName); + return false; + } + + QStringList m_allFiles; +}; + +} + +QStringList DependencyGraph::collectDependencies(const QString &referenceFile, + DependencyRole role) const +{ + SimpleVisitor visitor; + collectDependencies(referenceFile, role, &visitor); + + return visitor.m_allFiles; +} + +bool DependencyGraph::hasDependency(const QString &referenceFile, DependencyRole role) const +{ + QPair<bool, NodeRefSetIt> v = findVertex(referenceFile); + if (!v.first) + return false; + + NodeListIt nodeIt = v.second.value(); + + if (role == FilesDirectlyIncludedBy || role == FilesIncludedBy) + return nodeIt->m_out != 0; + + return nodeIt->m_in != 0; +} + +void DependencyGraph::discard() +{ + cancel(); + + for (NodeListIt it = m_nodes.begin(); it != m_nodes.end(); ++it) { + deleteAdjacencies(it->m_out); + deleteAdjacencies(it->m_in); + } + m_nodes.clear(); + m_nodesRefs.clear(); + m_files.clear(); +} + + +DependencyGraph::Node::Node(const QString &fileName) + : m_fileName(fileName) + , m_out(0) + , m_in(0) +{} + +DependencyGraph::AdjacencyNode::AdjacencyNode(NodeListIt it) + : m_next(0) + , m_nodeIt(it) +{} + +QPair<bool, DependencyGraph::NodeRefSetIt> DependencyGraph::findVertex(const QString &s) const +{ + bool found = false; + NodeRefSetIt it = const_cast<NodeRefSet &>(m_nodesRefs).find(s); + if (it != m_nodesRefs.end()) + found = true; + return qMakePair(found, it); +} + +DependencyGraph::NodeRefSetIt DependencyGraph::insertVertex(const QString &s) +{ + Q_ASSERT(m_nodesRefs.find(s) == m_nodesRefs.end()); + + m_nodes.append(Node(s)); + return m_nodesRefs.insert(s, m_nodes.end() - 1); +} + +void DependencyGraph::insertEdge(DependencyGraph::NodeRefSetIt fromIt, + DependencyGraph::NodeRefSetIt toIt) +{ + NodeListIt nodeFromIt = fromIt.value(); + NodeListIt nodeToIt = toIt.value(); + + createAdjacency(&nodeFromIt->m_out, new AdjacencyNode(nodeToIt)); + createAdjacency(&nodeToIt->m_in, new AdjacencyNode(nodeFromIt)); +} + +void DependencyGraph::deleteAdjacencies(AdjacencyNode *node) +{ + while (node) { + AdjacencyNode *next = node->m_next; + delete node; + node = next; + } +} + +void DependencyGraph::createAdjacency(AdjacencyNode **node, AdjacencyNode *newNode) +{ + if (*node) + newNode->m_next = *node; + *node = newNode; +} diff --git a/src/plugins/clangcodemodel/dependencygraph.h b/src/plugins/clangcodemodel/dependencygraph.h new file mode 100644 index 0000000000..9dccc37e04 --- /dev/null +++ b/src/plugins/clangcodemodel/dependencygraph.h @@ -0,0 +1,235 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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. +** +****************************************************************************/ + +#ifndef DEPENDENCYGRAPH_H +#define DEPENDENCYGRAPH_H + +#include "includetracker.h" + +#include <QtCore/QObject> +#include <QtCore/QStringList> +#include <QtCore/QLinkedList> +#include <QtCore/QHash> +#include <QtCore/QPair> +#include <QtCore/QQueue> +#include <QtCore/QFuture> +#include <QtCore/QFutureWatcher> +#include <QtCore/QDebug> + +namespace ClangCodeModel { +namespace Internal { + +class DependencyGraph : public QObject +{ + Q_OBJECT + Q_DISABLE_COPY(DependencyGraph) + +public: + DependencyGraph(); + ~DependencyGraph(); + + void addFile(const QString &fileName, const QStringList &compilationOptions); + + QFuture<void> compute(); + + enum DependencyRole + { + FilesDirectlyIncludedBy, // Only direct inclusions + FilesIncludedBy, // Both direct and indirect inclusions + FilesWhichDirectlyInclude, // This one is directly included from... + FilesWhichInclude // This one is directly or indirectly included from... + }; + + /* + * You should use this version if you simply want all the dependencies, no matter what. + */ + QStringList collectDependencies(const QString &referenceFile, DependencyRole role) const; + + /* + * You should use this version if you might be interested on a particular dependency + * and don't want to continue the search once you have found it. In this case you need + * supply a visitor. Currently the visitor concept simply requires that a type Visitor_T + * models a function that will receive a file string s and indicate whether or not to + * continue: + * + * Visitor_T().acceptFile(s) must be a valid expression. + * + */ + template <class Visitor_T> + void collectDependencies(const QString &referenceFile, + DependencyRole role, + Visitor_T *visitor) const; + + bool hasDependency(const QString &referenceFile, DependencyRole role) const; + + void discard(); + +signals: + void dependencyGraphAvailable(); + +private: + QList<QPair<QString, QStringList> > m_files; + IncludeTracker m_includeTracker; + QFutureWatcher<void> m_computeWatcher; + + void cancel(); + void computeCore(); + + // The dependency graph is represent as an adjacency list. The vertices contains + // a list of *out* edges and a list of *in* edges. Each vertex corresponds to a file. + // Its out edges correspond to the files which get directly included by this one, while + // its in edges correspond to files that directly include this one. + // + // For better space efficiency, the adjacency nodes doen't explicitly store the file + // names themselves, but rather an iterator to the corresponding vertex. In addition, + // for speed efficiency we keep track of a hash table that contains iterators to the + // actual vertex storage container, which actually contains the strings for the file + // names. The vertex container itself is a linked list, it has the semantics we need, + // in particular regarding iterator invalidation. + + struct AdjacencyNode; + struct Node + { + Node(const QString &fileName); + + QString m_fileName; + AdjacencyNode *m_out; + AdjacencyNode *m_in; + }; + + typedef QLinkedList<Node> NodeList; + typedef NodeList::iterator NodeListIt; + typedef QHash<QString, NodeListIt> NodeRefSet; + typedef NodeRefSet::iterator NodeRefSetIt; + + struct AdjacencyNode + { + AdjacencyNode(NodeListIt it); + + AdjacencyNode *m_next; + NodeListIt m_nodeIt; + }; + + + void processIncludes(NodeRefSetIt currentFileIt, + const QStringList &compilationOptions); + + template <class Visitor_T> + void collectFilesBFS(NodeListIt nodeIt, DependencyRole role, Visitor_T *visitor) const; + + + // Core graph operations and data + + QPair<bool, NodeRefSetIt> findVertex(const QString &s) const; + NodeRefSetIt insertVertex(const QString &s); + void insertEdge(NodeRefSetIt fromIt, NodeRefSetIt toIt); + + void deleteAdjacencies(AdjacencyNode *node); + void createAdjacency(AdjacencyNode **node, AdjacencyNode *newNode); + + NodeList m_nodes; + NodeRefSet m_nodesRefs; +}; + +template <class Visitor_T> +void DependencyGraph::collectDependencies(const QString &referenceFile, + DependencyRole role, + Visitor_T *visitor) const +{ + if (m_computeWatcher.isRunning()) + return; + + QPair<bool, NodeRefSetIt> v = findVertex(referenceFile); + if (!v.first) + return; + + NodeListIt nodeIt = v.second.value(); + + if (role == FilesDirectlyIncludedBy || role == FilesWhichDirectlyInclude) { + AdjacencyNode *adj; + if (role == FilesDirectlyIncludedBy) + adj = nodeIt->m_out; + else + adj = nodeIt->m_in; + + for (; adj; adj = adj->m_next) { + NodeListIt dependentIt = adj->m_nodeIt; + if (visitor->acceptFile(dependentIt->m_fileName)) + return; + } + } else { + collectFilesBFS(nodeIt, role, visitor); + } +} + +template <class Visitor_T> +void DependencyGraph::collectFilesBFS(NodeListIt nodeIt, + DependencyRole role, + Visitor_T *visitor) const +{ + Q_ASSERT(role == FilesIncludedBy || role == FilesWhichInclude); + + if (m_computeWatcher.isRunning()) + return; + + QQueue<NodeListIt> q; + q.enqueue(nodeIt); + + QSet<QString> visited; + visited.insert(nodeIt->m_fileName); + + while (!q.isEmpty()) { + NodeListIt currentIt = q.dequeue(); + AdjacencyNode *adj; + if (role == FilesIncludedBy) + adj = currentIt->m_out; + else + adj = currentIt->m_in; + while (adj) { + NodeListIt adjNodeIt = adj->m_nodeIt; + adj = adj->m_next; + + const QString &adjFileName = adjNodeIt->m_fileName; + if (visited.contains(adjFileName)) + continue; + + if (visitor->acceptFile(adjFileName)) + return; + + visited.insert(adjFileName); + q.enqueue(adjNodeIt); + } + } +} + + +} // Internal +} // ClangCodeModel + +#endif // DEPENDENCYGRAPH_H diff --git a/src/plugins/clangcodemodel/diagnostic.cpp b/src/plugins/clangcodemodel/diagnostic.cpp new file mode 100644 index 0000000000..f0aaef70fc --- /dev/null +++ b/src/plugins/clangcodemodel/diagnostic.cpp @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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 "diagnostic.h" + +#include <QtCore/QCoreApplication> +#include <QtCore/QStringList> + +using namespace ClangCodeModel; + +Diagnostic::Diagnostic() + : m_severity(Unknown) + , m_length(0) +{} + +Diagnostic::Diagnostic(Severity severity, const SourceLocation &location, unsigned length, const QString &spelling) + : m_severity(severity) + , m_loc(location) + , m_length(length) + , m_spelling(spelling) +{} + +const QString Diagnostic::severityAsString() const +{ + if (m_severity == Unknown) + return QString(); + + static QStringList strs = QStringList() + << QCoreApplication::translate("Diagnostic", "ignored") + << QCoreApplication::translate("Diagnostic", "note") + << QCoreApplication::translate("Diagnostic", "warning") + << QCoreApplication::translate("Diagnostic", "error") + << QCoreApplication::translate("Diagnostic", "fatal") + ; + + return strs.at(m_severity); +} diff --git a/src/plugins/clangcodemodel/diagnostic.h b/src/plugins/clangcodemodel/diagnostic.h new file mode 100644 index 0000000000..8ecbcb09e7 --- /dev/null +++ b/src/plugins/clangcodemodel/diagnostic.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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. +** +****************************************************************************/ + +#ifndef CLANG_DIAGNOSTIC_H +#define CLANG_DIAGNOSTIC_H + +#include "clang_global.h" +#include "sourcelocation.h" + +#include <QMetaType> + +namespace ClangCodeModel { + +class CLANG_EXPORT Diagnostic +{ +public: + enum Severity { + Unknown = -1, + Ignored = 0, + Note = 1, + Warning = 2, + Error = 3, + Fatal = 4 + }; + +public: + Diagnostic(); + Diagnostic(Severity severity, const SourceLocation &location, unsigned length, const QString &spelling); + + Severity severity() const + { return m_severity; } + + const QString severityAsString() const; + + const SourceLocation &location() const + { return m_loc; } + + unsigned length() const + { return m_length; } + + const QString &spelling() const + { return m_spelling; } + +private: + Severity m_severity; + SourceLocation m_loc; + unsigned m_length; + QString m_spelling; +}; + +} // namespace ClangCodeModel + +Q_DECLARE_METATYPE(ClangCodeModel::Diagnostic) +Q_DECLARE_METATYPE(QList<ClangCodeModel::Diagnostic>) + +#endif // CLANG_DIAGNOSTIC_H diff --git a/src/plugins/clangcodemodel/fastindexer.cpp b/src/plugins/clangcodemodel/fastindexer.cpp new file mode 100644 index 0000000000..fcf369dfec --- /dev/null +++ b/src/plugins/clangcodemodel/fastindexer.cpp @@ -0,0 +1,36 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** 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 "fastindexer.h" + +using namespace ClangCodeModel::Internal; + +FastIndexer::~FastIndexer() +{ +} diff --git a/src/plugins/clangcodemodel/fastindexer.h b/src/plugins/clangcodemodel/fastindexer.h new file mode 100644 index 0000000000..024d3d7b9e --- /dev/null +++ b/src/plugins/clangcodemodel/fastindexer.h @@ -0,0 +1,49 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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. +** +****************************************************************************/ + +#ifndef FASTINDEXER_H +#define FASTINDEXER_H + +#include "unit.h" + +namespace ClangCodeModel { +namespace Internal { + +class FastIndexer +{ +public: + virtual ~FastIndexer() = 0; + + virtual void indexNow(const Unit &unit) = 0; +}; + +} // Internal namespace +} // ClangCodeModel namespace + +#endif // FASTINDEXER_H diff --git a/src/plugins/clangcodemodel/index.cpp b/src/plugins/clangcodemodel/index.cpp new file mode 100644 index 0000000000..529ad48066 --- /dev/null +++ b/src/plugins/clangcodemodel/index.cpp @@ -0,0 +1,491 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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 "clangsymbolsearcher.h" +#include "index.h" + +#include <QStringList> +#include <QLinkedList> +#include <QHash> +#include <QDataStream> +#include <QPair> +#include <QFileInfo> +#include <QMutex> +#include <QMutexLocker> + +namespace ClangCodeModel { +namespace Internal { + +class ClangSymbolSearcher; + +class IndexPrivate +{ +public: + IndexPrivate(); + + void insertSymbol(const Symbol &symbol, const QDateTime &timeStamp); + QList<Symbol> symbols(const QString &fileName) const; + QList<Symbol> symbols(const QString &fileName, Symbol::Kind kind) const; + QList<Symbol> symbols(const QString &fileName, Symbol::Kind kind, const QString &uqName) const; + QList<Symbol> symbols(const QString &fileName, const QString &uqName) const; + QList<Symbol> symbols(Symbol::Kind kind) const; + + void match(ClangSymbolSearcher *searcher) const; + + void insertFile(const QString &fileName, const QDateTime &timeStamp); + void removeFile(const QString &fileName); + void removeFiles(const QStringList &fileNames); + bool containsFile(const QString &fileName) const; + QStringList files() const; + + void clear(); + + bool isEmpty() const; + + void trackTimeStamp(const Symbol &symbol, const QDateTime &timeStamp); + void trackTimeStamp(const QString &fileName, const QDateTime &timeStamp); + + bool validate(const QString &fileName) const; + + QByteArray serialize() const; + void deserialize(const QByteArray &data); + +private: + typedef QLinkedList<Symbol> SymbolCont; + typedef SymbolCont::iterator SymbolIt; + + typedef QHash<QString, QList<SymbolIt> > NameIndex; + typedef QHash<Symbol::Kind, NameIndex> KindIndex; + typedef QHash<QString, KindIndex> FileIndex; + + typedef QList<SymbolIt>::iterator SymbolIndexIt; + typedef NameIndex::iterator NameIndexIt; + typedef KindIndex::iterator KindIndexIt; + typedef FileIndex::iterator FileIndexIt; + typedef FileIndex::const_iterator FileIndexCIt; + + void insertSymbol(const Symbol &symbol); + void removeSymbol(SymbolIndexIt it); + + QPair<bool, SymbolIndexIt> findEquivalentSymbol(const Symbol &symbol); + void updateEquivalentSymbol(SymbolIndexIt it, const Symbol &symbol); + + void createIndexes(SymbolIt it); + QList<SymbolIt> removeIndexes(const QString &fileName); + + static QList<Symbol> symbolsFromIterators(const QList<SymbolIt> &symbolList); + + // @TODO: Sharing of compilation options... + + mutable QMutex m_mutex; + SymbolCont m_container; + FileIndex m_files; + QHash<QString, QDateTime> m_timeStamps; +}; + +} // namespace Internal +} // namespace ClangCodeModel + +using namespace ClangCodeModel; +using namespace Internal; + +IndexPrivate::IndexPrivate() + : m_mutex(QMutex::Recursive) +{ +} + +void IndexPrivate::createIndexes(SymbolIt it) +{ + m_files[it->m_location.fileName()][it->m_kind][it->m_name].append(it); +} + +QList<QLinkedList<Symbol>::iterator> IndexPrivate::removeIndexes(const QString &fileName) +{ + QList<SymbolIt> iterators; + KindIndex kindIndex = m_files.take(fileName); + KindIndexIt it = kindIndex.begin(); + KindIndexIt eit = kindIndex.end(); + for (; it != eit; ++it) { + NameIndex nameIndex = *it; + NameIndexIt nit = nameIndex.begin(); + NameIndexIt neit = nameIndex.end(); + for (; nit != neit; ++nit) + iterators.append(*nit); + } + return iterators; +} + +void IndexPrivate::insertSymbol(const Symbol &symbol) +{ + QMutexLocker locker(&m_mutex); + + SymbolIt it = m_container.insert(m_container.begin(), symbol); + createIndexes(it); +} + +void IndexPrivate::insertSymbol(const Symbol &symbol, const QDateTime &timeStamp) +{ + const QPair<bool, SymbolIndexIt> &find = findEquivalentSymbol(symbol); + if (find.first) + updateEquivalentSymbol(find.second, symbol); + else + insertSymbol(symbol); + + trackTimeStamp(symbol, timeStamp); +} + +QPair<bool, IndexPrivate::SymbolIndexIt> IndexPrivate::findEquivalentSymbol(const Symbol &symbol) +{ + // Despite the loop below finding a symbol should be efficient, since we already filter + // the file name, the kind, and the qualified name through the indexing mechanism. In many + // cases it will iterate only once. + QList<SymbolIt> &byName = m_files[symbol.m_location.fileName()][symbol.m_kind][symbol.m_name]; + for (SymbolIndexIt it = byName.begin(); it != byName.end(); ++it) { + const Symbol &candidateSymbol = *(*it); + // @TODO: Overloads, template specializations + if (candidateSymbol.m_qualification == symbol.m_qualification) + return qMakePair(true, it); + } + + return qMakePair(false, QList<SymbolIt>::iterator()); +} + +void IndexPrivate::updateEquivalentSymbol(SymbolIndexIt it, const Symbol &symbol) +{ + SymbolIt symbolIt = *it; + + Q_ASSERT(symbolIt->m_kind == symbol.m_kind); + Q_ASSERT(symbolIt->m_qualification == symbol.m_qualification); + Q_ASSERT(symbolIt->m_name == symbol.m_name); + Q_ASSERT(symbolIt->m_location.fileName() == symbol.m_location.fileName()); + + symbolIt->m_location = symbol.m_location; +} + +void IndexPrivate::removeSymbol(SymbolIndexIt it) +{ + SymbolIt symbolIt = *it; + + m_container.erase(symbolIt); + + KindIndex &kindIndex = m_files[symbolIt->m_location.fileName()]; + NameIndex &nameIndex = kindIndex[symbolIt->m_kind]; + QList<SymbolIt> &byName = nameIndex[symbolIt->m_name]; + byName.erase(it); + if (byName.isEmpty()) { + nameIndex.remove(symbolIt->m_name); + if (nameIndex.isEmpty()) { + kindIndex.remove(symbolIt->m_kind); + if (kindIndex.isEmpty()) + m_files.remove(symbolIt->m_location.fileName()); + } + } +} + +QList<Symbol> IndexPrivate::symbols(const QString &fileName) const +{ + QMutexLocker locker(&m_mutex); + + QList<Symbol> all; + const QList<NameIndex> &byKind = m_files.value(fileName).values(); + foreach (const NameIndex &nameIndex, byKind) { + const QList<QList<SymbolIt> > &byName = nameIndex.values(); + foreach (const QList<SymbolIt> &symbols, byName) + all.append(symbolsFromIterators(symbols)); + } + return all; +} + +QList<Symbol> IndexPrivate::symbols(const QString &fileName, Symbol::Kind kind) const +{ + QMutexLocker locker(&m_mutex); + + QList<Symbol> all; + const QList<QList<SymbolIt> > &byName = m_files.value(fileName).value(kind).values(); + foreach (const QList<SymbolIt> &symbols, byName) + all.append(symbolsFromIterators(symbols)); + return all; +} + +QList<Symbol> IndexPrivate::symbols(const QString &fileName, + Symbol::Kind kind, + const QString &uqName) const +{ + QMutexLocker locker(&m_mutex); + + return symbolsFromIterators(m_files.value(fileName).value(kind).value(uqName)); +} + +QList<Symbol> IndexPrivate::symbols(Symbol::Kind kind) const +{ + QMutexLocker locker(&m_mutex); + + QList<Symbol> all; + FileIndexCIt it = m_files.begin(); + FileIndexCIt eit = m_files.end(); + for (; it != eit; ++it) + all.append(symbols(it.key(), kind)); + return all; +} + +void IndexPrivate::match(ClangSymbolSearcher *searcher) const +{ + QMutexLocker locker(&m_mutex); + + searcher->search(m_container); +} + +QList<Symbol> IndexPrivate::symbolsFromIterators(const QList<SymbolIt> &symbolList) +{ + QList<Symbol> all; + foreach (SymbolIt symbolIt, symbolList) + all.append(*symbolIt); + return all; +} + +void IndexPrivate::trackTimeStamp(const Symbol &symbol, const QDateTime &timeStamp) +{ + QMutexLocker locker(&m_mutex); + + trackTimeStamp(symbol.m_location.fileName(), timeStamp); +} + +void IndexPrivate::trackTimeStamp(const QString &fileName, const QDateTime &timeStamp) +{ + QMutexLocker locker(&m_mutex); + + // We keep track of time stamps on a per file basis (most recent one). + m_timeStamps[fileName] = timeStamp; +} + +bool IndexPrivate::validate(const QString &fileName) const +{ + QMutexLocker locker(&m_mutex); + + const QDateTime &timeStamp = m_timeStamps.value(fileName); + if (!timeStamp.isValid()) + return false; + + QFileInfo fileInfo(fileName); + if (fileInfo.lastModified() > timeStamp) + return false; + + return true; +} + +void IndexPrivate::insertFile(const QString &fileName, const QDateTime &timeStamp) +{ + QMutexLocker locker(&m_mutex); + + trackTimeStamp(fileName, timeStamp); +} + +QStringList IndexPrivate::files() const +{ + QMutexLocker locker(&m_mutex); + + return m_timeStamps.keys(); +} + +bool IndexPrivate::containsFile(const QString &fileName) const +{ + QMutexLocker locker(&m_mutex); + + return m_timeStamps.contains(fileName); +} + +void IndexPrivate::removeFile(const QString &fileName) +{ + QMutexLocker locker(&m_mutex); + + const QList<SymbolIt> &iterators = removeIndexes(fileName); + foreach (SymbolIt it, iterators) + m_container.erase(it); + + m_timeStamps.remove(fileName); +} + +void IndexPrivate::removeFiles(const QStringList &fileNames) +{ + QMutexLocker locker(&m_mutex); + + foreach (const QString &fileName, fileNames) + removeFile(fileName); +} + +void IndexPrivate::clear() +{ + QMutexLocker locker(&m_mutex); + + m_container.clear(); + m_files.clear(); + m_timeStamps.clear(); +} + +bool IndexPrivate::isEmpty() const +{ + QMutexLocker locker(&m_mutex); + + return m_timeStamps.isEmpty(); +} + +QByteArray IndexPrivate::serialize() const +{ + QMutexLocker locker(&m_mutex); + + QByteArray data; + QDataStream stream(&data, QIODevice::WriteOnly); + + stream << (quint32)0x0A0BFFEE; + stream << (quint16)1; + stream.setVersion(QDataStream::Qt_4_7); + stream << m_container; + stream << m_timeStamps; + + return data; +} + +void IndexPrivate::deserialize(const QByteArray &data) +{ + QMutexLocker locker(&m_mutex); + + clear(); + + // @TODO: Version compatibility handling. + + QDataStream stream(data); + + quint32 header; + stream >> header; + if (header != 0x0A0BFFEE) + return; + + quint16 indexVersion; + stream >> indexVersion; + if (indexVersion != 1) + return; + + stream.setVersion(QDataStream::Qt_4_7); + + SymbolCont symbols; + stream >> symbols; + stream >> m_timeStamps; + + // @TODO: Overload the related functions with batch versions. + foreach (const Symbol &symbol, symbols) + insertSymbol(symbol); +} + + +Index::Index() + : d(new IndexPrivate) +{} + +Index::~Index() +{} + +void Index::insertSymbol(const Symbol &symbol, const QDateTime &timeStamp) +{ + d->insertSymbol(symbol, timeStamp); +} + +QList<Symbol> Index::symbols(const QString &fileName) const +{ + return d->symbols(fileName); +} + +QList<Symbol> Index::symbols(const QString &fileName, Symbol::Kind kind) const +{ + return d->symbols(fileName, kind); +} + +QList<Symbol> Index::symbols(const QString &fileName, Symbol::Kind kind, const QString &uqName) const +{ + return d->symbols(fileName, kind, uqName); +} + +QList<Symbol> Index::symbols(Symbol::Kind kind) const +{ + return d->symbols(kind); +} + +void Index::match(ClangSymbolSearcher *searcher) const +{ + d->match(searcher); +} + +void Index::insertFile(const QString &fileName, const QDateTime &timeStamp) +{ + d->insertFile(fileName, timeStamp); +} + +QStringList Index::files() const +{ + return d->files(); +} + +bool Index::containsFile(const QString &fileName) const +{ + return d->containsFile(fileName); +} + +void Index::removeFile(const QString &fileName) +{ + d->removeFile(fileName); +} + +void Index::removeFiles(const QStringList &fileNames) +{ + d->removeFiles(fileNames); +} + +void Index::clear() +{ + d->clear(); +} + +bool Index::isEmpty() const +{ + return d->isEmpty(); +} + +bool Index::validate(const QString &fileName) const +{ + return d->validate(fileName); +} + +QByteArray Index::serialize() const +{ + return d->serialize(); +} + +void Index::deserialize(const QByteArray &data) +{ + d->deserialize(data); +} diff --git a/src/plugins/clangcodemodel/index.h b/src/plugins/clangcodemodel/index.h new file mode 100644 index 0000000000..20d4f5fe00 --- /dev/null +++ b/src/plugins/clangcodemodel/index.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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. +** +****************************************************************************/ + +#ifndef INDEX_H +#define INDEX_H + +#include "symbol.h" + +#include <QtCore/QByteArray> +#include <QtCore/QString> +#include <QtCore/QList> +#include <QtCore/QScopedPointer> +#include <QtCore/QDateTime> +#include <QStringList> + +namespace ClangCodeModel { + +class Symbol; + +namespace Internal { + +class ClangSymbolSearcher; +class IndexPrivate; + +class Index +{ +public: + Index(); + ~Index(); + + void insertSymbol(const Symbol &symbol, const QDateTime &timeStamp); + QList<Symbol> symbols(const QString &fileName) const; + QList<Symbol> symbols(const QString &fileName, Symbol::Kind kind) const; + QList<Symbol> symbols(const QString &fileName, Symbol::Kind kind, const QString &uqName) const; + QList<Symbol> symbols(Symbol::Kind kind) const; + + void match(ClangSymbolSearcher *searcher) const; + + void insertFile(const QString &fileName, const QDateTime &timeStamp); + void removeFile(const QString &fileName); + void removeFiles(const QStringList &fileNames); + bool containsFile(const QString &fileName) const; + QStringList files() const; + + bool validate(const QString &fileName) const; + + void clear(); + + bool isEmpty() const; + + QByteArray serialize() const; + void deserialize(const QByteArray &data); + +private: + QScopedPointer<IndexPrivate> d; +}; + +} // Internal +} // ClangCodeModel + +#endif // INDEX_H diff --git a/src/plugins/clangcodemodel/indexer.cpp b/src/plugins/clangcodemodel/indexer.cpp new file mode 100644 index 0000000000..6769ec2729 --- /dev/null +++ b/src/plugins/clangcodemodel/indexer.cpp @@ -0,0 +1,1286 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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 "clangutils.h" +#include "indexer.h" +#include "index.h" +#include "cxraii.h" +#include "sourcelocation.h" +#include "liveunitsmanager.h" +#include "utils_p.h" +#include "clangsymbolsearcher.h" +#include "pchmanager.h" +#include "raii/scopedclangoptions.h" + +#include <clang-c/Index.h> + +#include <coreplugin/editormanager/editormanager.h> +#include <coreplugin/icore.h> +#include <coreplugin/progressmanager/progressmanager.h> +#include <utils/fileutils.h> +#include <utils/QtConcurrentTools> + +#include <QDebug> +#include <QVector> +#include <QHash> +#include <QSet> +#include <QFile> +#include <QFileInfo> +#include <QFutureWatcher> +#include <QDir> +#include <QFuture> +#include <QTime> +#include <QRunnable> +#include <QThreadPool> +#include <QDateTime> +#include <QStringBuilder> + +#include <cassert> + +//#define DEBUG +//#define DEBUG_DIAGNOSTICS + +#ifdef DEBUG + #define BEGIN_PROFILE_SCOPE(ID) { ScopepTimer t(ID); + #define END_PROFILE_SCOPE } +#else + #define BEGIN_PROFILE_SCOPE(ID) + #define END_PROFILE_SCOPE +#endif + +using namespace Utils; +using namespace ClangCodeModel; +using namespace Internal; + +namespace ClangCodeModel { + +// The indexing result, containing the symbols found, reported by the indexer processor. +struct IndexingResult +{ + typedef CppTools::ProjectPart ProjectPart; + + IndexingResult() + {} + + IndexingResult(const QVector<Symbol> &symbol, + const QSet<QString> &processedFiles, + const Unit &unit, + const ProjectPart::Ptr &projectPart) + : m_symbolsInfo(symbol) + , m_processedFiles(processedFiles) + , m_unit(unit) + , m_projectPart(projectPart) + {} + + QVector<Symbol> m_symbolsInfo; + QSet<QString> m_processedFiles; + Unit m_unit; + ProjectPart::Ptr m_projectPart; +}; + +class LibClangIndexer; + +class IndexerPrivate : public QObject +{ + Q_OBJECT + +public: + typedef CppTools::ProjectPart ProjectPart; + +public: + IndexerPrivate(Indexer *indexer); + ~IndexerPrivate() + { cancel(true); } + + // This enumeration is used to index a vector. So be careful when changing. + enum FileType { + ImplementationFile = 0, + HeaderFile, + TotalFileTypes + }; + + struct FileData + { + FileData() : m_upToDate(false) {} + FileData(const QString &fileName, + const ProjectPart::Ptr &projectPart, + bool upToDate = false) + : m_fileName(fileName) + , m_projectPart(projectPart) + , m_upToDate(upToDate) + , m_managementOptions(CXTranslationUnit_DetailedPreprocessingRecord + | CXTranslationUnit_Incomplete) + {} + + QString m_fileName; + ProjectPart::Ptr m_projectPart; + bool m_upToDate; + unsigned m_managementOptions; + }; + + void synchronize(const QVector<IndexingResult> &results); + void finished(LibClangIndexer *indexer); + bool noIndexersRunning() const; + +private: + mutable QMutex m_mutex; + + void indexingFinished(); + void cancelIndexing(); + int runningIndexerCount() const; + +public slots: + void dependencyGraphComputed(); + void restoredSymbolsAnalysed(); + +public: + enum IndexingMode { + RelaxedIndexing, // Index symbols from any file. + ConstrainedIndexing // Index symbols only from the requested files. + }; + + void startLoading(); + void concludeLoading(); + + void computeDependencyGraph(); + void analyzeRestoredSymbols(); + + void runQuickIndexing(const Unit &unit, const ProjectPart::Ptr &part); + void run(); + void run(const QStringList &fileNames); + void runCore(const QHash<QString, FileData> &headers, + const QHash<QString, FileData> &impls, + IndexingMode mode); + void watchIndexingThreads(QFutureInterface<void> &future); + bool isBusy() const; + void cancel(bool wait); + void reset(); + + bool addFile(const QString &fileName, + ProjectPart::Ptr projectPart); + void addOrUpdateFileData(const QString &fileName, + ProjectPart::Ptr projectPart, + bool upToDate); + QStringList allFiles() const; + bool isTrackingFile(const QString &fileName, FileType type) const; + static FileType identifyFileType(const QString &fileName); + static void populateFileNames(QStringList *all, const QList<FileData> &data); + QStringList compilationOptions(const QString &fileName) const; + + bool deserealizeSymbols(); + void serializeSymbols() const; + + QList<Symbol> symbols(Symbol::Kind kind) const; + QList<Symbol> symbols(const QString &fileName, const Symbol::Kind kind) const; + void match(ClangSymbolSearcher *searcher) const; + + Indexer *m_q; + QVector<QHash<QString, FileData> > m_files; + Index m_index; + bool m_hasQueuedFullRun; + QSet<QString> m_queuedFilesRun; + QString m_storagePath; + bool m_isLoaded; +// DependencyGraph m_dependencyGraph; + QScopedPointer<QFutureWatcher<void> >m_loadingWatcher; + QScopedPointer<QFutureWatcher<void> >m_indexingWatcher; + QThreadPool m_indexingPool; + QSet<LibClangIndexer *> m_runningIndexers; +}; + +} // ClangCodeModel + +Q_DECLARE_METATYPE(IndexingResult) + +namespace { + +struct ScopepTimer +{ + ScopepTimer(int id = 0) : m_id(id) { m_t.start(); } + ~ScopepTimer() { qDebug() << "\t#Timer" << m_id << ":" << m_t.elapsed() << "ms"; } + int m_id; + QTime m_t; +}; + +} // Anonymous + +namespace ClangCodeModel { + +class LibClangIndexer: public QRunnable +{ +protected: + typedef CppTools::ProjectPart ProjectPart; + +public: + LibClangIndexer(IndexerPrivate *indexer) + : m_indexer(indexer) + , m_isCanceled(false) + {} + + virtual ~LibClangIndexer() + {} + + void cancel() + { m_isCanceled = true; } + +protected: + void propagateResults(const ProjectPart::Ptr &projectPart) + { + if (!isCanceled()) { + QVector<IndexingResult> indexingResults; + indexingResults.reserve(m_allFiles.size()); + + foreach (const QString &fn, m_allFiles.keys()) { + QVector<ClangCodeModel::Symbol> symbols; unfoldSymbols(symbols, fn); + QSet<QString> processedFiles = QSet<QString>::fromList(m_allFiles.keys()); + Unit unit(fn); + IndexingResult indexingResult(symbols, processedFiles, unit, projectPart); + indexingResults.append(indexingResult); + + // TODO: includes need to be propagated to the dependency table. + } + m_indexer->synchronize(indexingResults); + } + + qDeleteAll(m_allFiles.values()); + m_allFiles.clear(); + qDeleteAll(m_allSymbols); + m_allSymbols.clear(); + } + +protected: + static inline LibClangIndexer *indexer(CXClientData d) + { return static_cast<LibClangIndexer *>(d); } + + static int abortQuery(CXClientData client_data, void *reserved) { + Q_UNUSED(reserved); + + return indexer(client_data)->isCanceled(); + } + + static void diagnostic(CXClientData client_data, CXDiagnosticSet diagSet, void *reserved) { + Q_UNUSED(client_data); + Q_UNUSED(diagSet); + Q_UNUSED(reserved); + } + + static CXIdxClientFile enteredMainFile(CXClientData client_data, CXFile file, void *reserved) { + Q_UNUSED(client_data); + Q_UNUSED(reserved); + + const QString fileName = getQString(clang_getFileName(file)); +// qDebug() << "enteredMainFile:" << fileName; + LibClangIndexer *lci = indexer(client_data); + File *f = lci->file(fileName); + f->setMainFile(); + + return f; + } + + static CXIdxClientFile includedFile(CXClientData client_data, const CXIdxIncludedFileInfo *info) { + Q_UNUSED(client_data); + + File *includingFile = 0; + clang_indexLoc_getFileLocation(info->hashLoc, reinterpret_cast<CXIdxClientFile*>(&includingFile), 0, 0, 0, 0); + + const QString fileName = getQString(clang_getFileName(info->file)); + File *f = indexer(client_data)->file(fileName); + + if (includingFile) + includingFile->addInclude(f); + + return f; + } + + static CXIdxClientFile importedASTFile(CXClientData client_data, const CXIdxImportedASTFileInfo *info) { + const QString fileName = getQString(clang_getFileName(info->file)); + +// qDebug() << "importedASTFile:" << fileName; + + indexer(client_data)->m_importedASTs.insert(fileName, false); + + return info->file; + } + + static CXIdxClientContainer startedTranslationUnit(CXClientData client_data, void *reserved) { + Q_UNUSED(client_data); + Q_UNUSED(reserved); + +// qDebug() << "startedTranslationUnit"; + return 0; + } + + static void indexDeclaration(CXClientData client_data, const CXIdxDeclInfo *info) { + LibClangIndexer *lci = indexer(client_data); + + File *includingFile = 0; + unsigned line = 0, column = 0, offset = 0; + clang_indexLoc_getFileLocation(info->loc, reinterpret_cast<CXIdxClientFile*>(&includingFile), 0, &line, &column, &offset); + + QString kind = getQString(clang_getCursorKindSpelling(info->cursor.kind)); + QString displayName = getQString(clang_getCursorDisplayName(info->cursor)); + QString spellingName = getQString(clang_getCursorSpelling(info->cursor)); +// qDebug() << (includingFile ? includingFile->name() : QLatin1String("<UNKNOWN FILE>")) << ":"<<line<<":"<<column<<": display name ="<<displayName<<"spelling name ="<<spellingName<<"of kind"<<kind; + + Symbol *sym = lci->newSymbol(info->cursor.kind, displayName, spellingName, includingFile, line, column, offset); + + // TODO: add to decl container... + if (includingFile) // TODO: check why includingFile can be null... + includingFile->addSymbol(sym); + + if (const CXIdxContainerInfo *semanticContainer = info->semanticContainer) { + if (Symbol *container = static_cast<Symbol *>(clang_index_getClientContainer(semanticContainer))) { + sym->semanticContainer = container; + container->addSymbol(sym); + } + } + + // TODO: ObjC containers + // TODO: index forward decls too? + + if (info->declAsContainer) + clang_index_setClientContainer(info->declAsContainer, sym); + } + + static void indexEntityReference(CXClientData client_data, const CXIdxEntityRefInfo *info) { + Q_UNUSED(client_data); + Q_UNUSED(info); + + // TODO: well, we do get the info, so why not (optionally?) remember all references? + } + +protected: + struct File; + struct Symbol; + + typedef QHash<QString, File *> FilesByName; + struct File + { + File(const QString &fileName) + : m_fileName(fileName) + {} + + void addInclude(File *f) + { + assert(f); + m_includes.insert(f->name(), f); + } + + QList<File *> includes() const + { return m_includes.values(); } + + QString name() const + { return m_fileName; } + + void setMainFile(bool isMainFile = true) + { m_isMainFile = isMainFile; } + + bool isMainFile() const + { return m_isMainFile; } + + void addSymbol(Symbol *symbol) + { + assert(symbol); + m_symbols.append(symbol); + } + + QVector<Symbol *> symbols() const + { return m_symbols; } + + private: + QString m_fileName; + FilesByName m_includes; + bool m_isMainFile; + QVector<Symbol *> m_symbols; + }; + + struct Symbol + { + Symbol(enum CXCursorKind kind, const QString &displayName, const QString &spellingName, File *file, unsigned line, unsigned column, unsigned offset) + : kind(kind) + , displayName(displayName) + , spellingName(spellingName) + , file(file) + , line(line) + , column(column) + , offset(offset) + , semanticContainer(0) + {} + + QString spellKind() const + { return getQString(clang_getCursorKindSpelling(kind)); } + + void addSymbol(Symbol *symbol) + { symbols.append(symbol); } + + enum CXCursorKind kind; + QString displayName, spellingName; + File *file; + unsigned line, column, offset; + Symbol *semanticContainer; + QVector<Symbol *> symbols; + }; + +protected: + bool isCanceled() const + { return m_isCanceled; } + + void finish() + { m_indexer->finished(this); } + + File *file(const QString &fileName) + { + File *f = m_allFiles[fileName]; + if (!f) { + f = new File(fileName); + m_allFiles.insert(fileName, f); + } + return f; + } + + Symbol *newSymbol(enum CXCursorKind kind, const QString &displayName, const QString &spellingName, File *file, unsigned line, unsigned column, unsigned offset) + { + Symbol *s = new Symbol(kind, displayName, spellingName, file, line, column, offset); + m_allSymbols.append(s); + return s; + } + + void dumpInfo() + { + qDebug() << "=== indexing info dump ==="; + qDebug() << "indexed" << m_allFiles.size() << "files. Main files:"; + foreach (const File *f, m_allFiles) { + if (!f->isMainFile()) + continue; + qDebug() << f->name() << ":"; + foreach (const File *inc, f->includes()) + qDebug() << " includes" << inc->name(); + dumpSymbols(f->symbols(), QByteArray(" ")); + } + + qDebug() << "=== end of dump ==="; + } + + void dumpSymbols(const QVector<Symbol *> &symbols, const QByteArray &indent) + { + if (symbols.isEmpty()) + return; + + qDebug("%scontained symbols:", indent.constData()); + QByteArray newIndent = indent + " "; + foreach (const Symbol *s, symbols) { + qDebug("%s%s (%s)", newIndent.constData(), s->spellingName.toUtf8().constData(), s->spellKind().toUtf8().constData()); + dumpSymbols(s->symbols, newIndent); + } + } + + void unfoldSymbols(QVector<ClangCodeModel::Symbol> &result, const QString &fileName) { + const QVector<Symbol *> symbolsForFile = file(fileName)->symbols(); + foreach (const Symbol *s, symbolsForFile) { + unfoldSymbols(s, result); + } + } + + void unfoldSymbols(const Symbol *s, QVector<ClangCodeModel::Symbol> &result) { + if (!s->file) + return; + + ClangCodeModel::Symbol sym; + sym.m_name = s->spellingName; + sym.m_qualification = s->spellingName; + + static QLatin1String sep("::"); + for (Symbol *parent = s->semanticContainer; parent; parent = parent->semanticContainer) + sym.m_qualification = parent->spellingName + sep + sym.m_qualification; + + sym.m_location = SourceLocation(s->file->name(), s->line, s->column, s->offset); + + switch (s->kind) { + case CXCursor_EnumDecl: sym.m_kind = ClangCodeModel::Symbol::Enum; break; + case CXCursor_StructDecl: + case CXCursor_ClassDecl: sym.m_kind = ClangCodeModel::Symbol::Class; break; + case CXCursor_CXXMethod: sym.m_kind = ClangCodeModel::Symbol::Method; break; + case CXCursor_FunctionTemplate: + case CXCursor_FunctionDecl: sym.m_kind = ClangCodeModel::Symbol::Function; break; + case CXCursor_DeclStmt: sym.m_kind = ClangCodeModel::Symbol::Declaration; break; + case CXCursor_Constructor: sym.m_kind = ClangCodeModel::Symbol::Constructor; break; + case CXCursor_Destructor: sym.m_kind = ClangCodeModel::Symbol::Destructor; break; + default: sym.m_kind = ClangCodeModel::Symbol::Unknown; break; + } + + result.append(sym); + } + +protected: + static IndexerCallbacks IndexCB; + +protected: + IndexerPrivate *m_indexer; + bool m_isCanceled; + QHash<QString, bool> m_importedASTs; + FilesByName m_allFiles; + QVector<Symbol *> m_allSymbols; +}; + +IndexerCallbacks LibClangIndexer::IndexCB = { + abortQuery, + diagnostic, + enteredMainFile, + includedFile, + importedASTFile, + startedTranslationUnit, + indexDeclaration, + indexEntityReference +}; + +class ProjectPartIndexer: public LibClangIndexer +{ +public: + ProjectPartIndexer(IndexerPrivate *indexer, const QList<IndexerPrivate::FileData> &todo) + : LibClangIndexer(indexer) + , m_todo(todo) + {} + + void run() + { + if (isCanceled() || m_todo.isEmpty()) { + finish(); + return; + } + + const ProjectPart::Ptr &pPart = m_todo[0].m_projectPart; + +restart: + CXIndex idx; + if (!(idx = clang_createIndex(/* excludeDeclsFromPCH */ 1, + /* displayDiagnosics=*/1))) { + qDebug() << "Could not create Index"; + return; + } + + CXIndexAction idxAction = clang_IndexAction_create(idx); + const unsigned index_opts = CXIndexOpt_SuppressWarnings; + + PCHManager *pchManager = PCHManager::instance(); + PchInfo::Ptr pchInfo = pchManager->pchInfo(pPart); + + for (int i = 0, ei = m_todo.size(); i < ei; ++i) { + const IndexerPrivate::FileData &fd = m_todo.at(i); + if (fd.m_upToDate) + continue; + + if (pchManager->pchInfo(pPart) != pchInfo) { + clang_IndexAction_dispose(idxAction); + clang_disposeIndex(idx); + goto restart; + } + + QStringList opts = ClangCodeModel::Utils::createClangOptions(pPart, fd.m_fileName); + if (!pchInfo.isNull()) + opts.append(Utils::createPCHInclusionOptions(pchInfo->fileName())); + + ScopedClangOptions scopedOpts(opts); + QByteArray fileName = fd.m_fileName.toUtf8(); + +// qDebug() << "Indexing file" << fd.m_fileName << "with options" << opts; + unsigned parsingOptions = fd.m_managementOptions; + parsingOptions |= CXTranslationUnit_SkipFunctionBodies; + + /*int result =*/ clang_indexSourceFile(idxAction, this, + &IndexCB, sizeof(IndexCB), + index_opts, fileName.constData(), + scopedOpts.data(), scopedOpts.size(), 0, 0, 0, + parsingOptions); + + // index imported ASTs: + foreach (const QString &astFile, m_importedASTs.keys()) { + if (m_importedASTs.value(astFile)) + continue; + + if (CXTranslationUnit TU = clang_createTranslationUnit( + idx, astFile.toUtf8().constData())) { + /*result =*/ clang_indexTranslationUnit(idxAction, this, + &IndexCB, + sizeof(IndexCB), + index_opts, TU); + clang_disposeTranslationUnit(TU); + } + + m_importedASTs[astFile] = true; + } + + propagateResults(fd.m_projectPart); + if (isCanceled()) + break; + } + +// dumpInfo(); + + clang_IndexAction_dispose(idxAction); + clang_disposeIndex(idx); + + finish(); + } + +private: + QList<IndexerPrivate::FileData> m_todo; +}; + +class QuickIndexer: public LibClangIndexer +{ +public: + QuickIndexer(IndexerPrivate *indexer, const Unit &unit, const ProjectPart::Ptr &projectPart) + : LibClangIndexer(indexer) + , m_unit(unit) + , m_projectPart(projectPart) + {} + + void run() + { + if (isCanceled() || !m_unit.isLoaded()) { + finish(); + return; + } + + CXIndexAction idxAction = clang_IndexAction_create(m_unit.clangIndex()); + const unsigned index_opts = CXIndexOpt_SuppressWarnings; + +// qDebug() << "Indexing TU" << m_unit.fileName() << "..."; + /*int result =*/ clang_indexTranslationUnit(idxAction, this, + &IndexCB, sizeof(IndexCB), + index_opts, + m_unit.clangTranslationUnit()); + + propagateResults(m_projectPart); + + clang_IndexAction_dispose(idxAction); + finish(); + } + +private: + Unit m_unit; + ProjectPart::Ptr m_projectPart; +}; + +} // ClangCodeModel + +IndexerPrivate::IndexerPrivate(Indexer *indexer) + : m_mutex(QMutex::Recursive) + , m_q(indexer) + , m_files(TotalFileTypes) + , m_hasQueuedFullRun(false) + , m_isLoaded(false) + , m_loadingWatcher(new QFutureWatcher<void>) + , m_indexingWatcher(new QFutureWatcher<void>) +{ +// const int magicThreadCount = QThread::idealThreadCount() * 4 / 3; + const int magicThreadCount = QThread::idealThreadCount() - 1; + m_indexingPool.setMaxThreadCount(std::max(magicThreadCount, 1)); + m_indexingPool.setExpiryTimeout(1000); +} + +void IndexerPrivate::runCore(const QHash<QString, FileData> & /*headers*/, + const QHash<QString, FileData> &impls, + IndexingMode /*mode*/) +{ + QMutexLocker locker(&m_mutex); + + typedef QHash<QString, FileData>::const_iterator FileContIt; + QHash<ProjectPart::Ptr, QList<IndexerPrivate::FileData> > parts; + typedef QHash<ProjectPart::Ptr, QList<IndexerPrivate::FileData> >::Iterator PartIter; + LiveUnitsManager *lum = LiveUnitsManager::instance(); + + for (FileContIt tit = impls.begin(), eit = impls.end(); tit != eit; ++tit) { + if (!tit->m_upToDate && !lum->isTracking(tit.key())) { + const IndexerPrivate::FileData &fd = tit.value(); + parts[fd.m_projectPart].append(fd); + } + } + + if (parts.isEmpty()) + return; + + for (PartIter i = parts.begin(), ei = parts.end(); i != ei; ++i) { + ProjectPartIndexer *ppi = new ProjectPartIndexer(this, i.value()); + m_runningIndexers.insert(ppi); + m_indexingPool.start(ppi); + } + + QFuture<void> task = QtConcurrent::run(&IndexerPrivate::watchIndexingThreads, this); + m_indexingWatcher->setFuture(task); + emit m_q->indexingStarted(task); +} + +void IndexerPrivate::watchIndexingThreads(QFutureInterface<void> &future) +{ + int maxTodo = runningIndexerCount(); + future.setProgressRange(0, maxTodo); + + int todo = -1; + while (todo) { + int newTodo = runningIndexerCount(); + if (todo != newTodo) + future.setProgressValue(maxTodo - newTodo); + todo = newTodo; + if (future.isCanceled()) { + cancelIndexing(); + return; + } + m_indexingPool.waitForDone(500); + } +} + +void IndexerPrivate::run() +{ + Q_ASSERT(m_isLoaded); + + QMutexLocker locker(&m_mutex); + + if (m_runningIndexers.isEmpty()) { + runCore(m_files.value(HeaderFile), + m_files.value(ImplementationFile), + RelaxedIndexing); + } else { + m_hasQueuedFullRun = true; + cancelIndexing(); + } +} + +void IndexerPrivate::run(const QStringList &fileNames) +{ + Q_ASSERT(m_isLoaded); + QMutexLocker locker(&m_mutex); + + if (noIndexersRunning()) { + QVector<QHash<QString, FileData> > files(TotalFileTypes); + foreach (const QString &fileName, fileNames) { + FileType type = identifyFileType(fileName); + if (!isTrackingFile(fileName, type)) { + // @TODO + continue; + } + + FileData *data = &m_files[type][fileName]; + data->m_upToDate = false; + files[type].insert(fileName, *data); + m_index.removeFile(fileName); + } + runCore(files.value(HeaderFile), + files.value(ImplementationFile), + ConstrainedIndexing); + } else { + m_queuedFilesRun.unite(fileNames.toSet()); + } +} + +bool IndexerPrivate::isBusy() const +{ + return !noIndexersRunning() || m_loadingWatcher->isRunning(); +} + +void IndexerPrivate::cancel(bool wait) +{ +// m_dependencyGraph.discard(); + + m_loadingWatcher->cancel(); + cancelIndexing(); + if (wait) { + m_loadingWatcher->waitForFinished(); + m_indexingWatcher->waitForFinished(); + while (!noIndexersRunning()) + m_indexingPool.waitForDone(100); + } +} + +void IndexerPrivate::reset() +{ + cancel(true); + serializeSymbols(); + + for (int i = 0; i < TotalFileTypes; ++i) + m_files[i].clear(); + m_hasQueuedFullRun = false; + m_queuedFilesRun.clear(); + m_storagePath.clear(); + m_index.clear(); + m_isLoaded = false; +} + +void IndexerPrivate::synchronize(const QVector<IndexingResult> &results) +{ + foreach (IndexingResult result, results) { + QMutexLocker locker(&m_mutex); + + result.m_unit.makeUnique(); + + foreach (const Symbol &symbol, result.m_symbolsInfo) { + addOrUpdateFileData(symbol.m_location.fileName(), + result.m_projectPart, + true); + + // Make the symbol available in the database. + m_index.insertSymbol(symbol, result.m_unit.timeStamp()); + } + + // There might be files which were processed but did not "generate" any indexable symbol, + // but we still need to make the index aware of them. + result.m_processedFiles.insert(result.m_unit.fileName()); + foreach (const QString &fileName, result.m_processedFiles) { + if (!m_index.containsFile(fileName)) + m_index.insertFile(fileName, result.m_unit.timeStamp()); + } + + // If this unit is being kept alive, update in the manager. + if (LiveUnitsManager::instance()->isTracking(result.m_unit.fileName())) + LiveUnitsManager::instance()->updateUnit(result.m_unit.fileName(), result.m_unit); + } +} + +void IndexerPrivate::finished(LibClangIndexer *indexer) +{ + QMutexLocker locker(&m_mutex); + + m_runningIndexers.remove(indexer); + if (noIndexersRunning()) + indexingFinished(); +} + +bool IndexerPrivate::noIndexersRunning() const +{ + QMutexLocker locker(&m_mutex); + + return m_runningIndexers.isEmpty(); +} + +void IndexerPrivate::indexingFinished() +{ + if (m_hasQueuedFullRun) { + m_hasQueuedFullRun = false; + run(); + } else if (!m_queuedFilesRun.isEmpty()) { + const QStringList &files = m_queuedFilesRun.toList(); + m_queuedFilesRun.clear(); + run(files); + } + + emit m_q->indexingFinished(); +} + +void IndexerPrivate::cancelIndexing() +{ + QMutexLocker locker(&m_mutex); + + foreach (LibClangIndexer* partIndexer, m_runningIndexers) { + partIndexer->cancel(); + } +} + +int IndexerPrivate::runningIndexerCount() const +{ + QMutexLocker locker(&m_mutex); + return m_runningIndexers.size(); +} + +void IndexerPrivate::addOrUpdateFileData(const QString &fileName, + ProjectPart::Ptr projectPart, + bool upToDate) +{ + Q_ASSERT(QDir::isAbsolutePath(fileName)); + + QString cleanFileName(normalizeFileName(fileName)); + + FileType fileType = identifyFileType(cleanFileName); + if (isTrackingFile(cleanFileName, fileType)) { + m_files[fileType][cleanFileName].m_projectPart = projectPart; + m_files[fileType][cleanFileName].m_upToDate = upToDate; + } else { + m_files[fileType].insert(cleanFileName, + FileData(cleanFileName, projectPart, upToDate)); + } + + if (!upToDate) + m_index.removeFile(cleanFileName); +} + +bool IndexerPrivate::addFile(const QString &fileName, + ProjectPart::Ptr projectPart) +{ + if (isBusy() + || fileName.trimmed().isEmpty() + || !QFileInfo(fileName).isFile()) + return false; + + addOrUpdateFileData(fileName, projectPart, false); + + return true; +} + +QStringList IndexerPrivate::allFiles() const +{ + QStringList all; + populateFileNames(&all, m_files.at(ImplementationFile).values()); + populateFileNames(&all, m_files.at(HeaderFile).values()); + return all; +} + +bool IndexerPrivate::isTrackingFile(const QString &fileName, FileType type) const +{ + return m_files.value(type).contains(normalizeFileName(fileName)); +} + +QStringList IndexerPrivate::compilationOptions(const QString &fileName) const +{ + FileType type = identifyFileType(fileName); + return Utils::createClangOptions(m_files.value(type).value(normalizeFileName(fileName)).m_projectPart); +} + +IndexerPrivate::FileType IndexerPrivate::identifyFileType(const QString &fileName) +{ + const QString fn = fileName.toLower(); + if (fn.endsWith(QLatin1String(".cpp")) + || fn.endsWith(QLatin1String(".cxx")) + || fn.endsWith(QLatin1String(".cc")) + || fn.endsWith(QLatin1String(".c")) + || fn.endsWith(QLatin1String(".m")) + || fn.endsWith(QLatin1String(".mm"))) { + return ImplementationFile; + } + + // Everything that is not an implementation file is treated as a header. This makes things + // easier when handling standard library files and any other file that does not use + // conventional suffixes. + return HeaderFile; +} + +void IndexerPrivate::populateFileNames(QStringList *all, const QList<FileData> &data) +{ + foreach (const FileData &fileData, data) + all->append(fileData.m_fileName); +} + + +namespace { + +struct DepedencyVisitor +{ + DepedencyVisitor(IndexerPrivate *indexer) : m_indexer(indexer) {} + + bool acceptFile(const QString &includer) + { + IndexerPrivate::FileType fileType = IndexerPrivate::identifyFileType(includer); + if (m_indexer->isTrackingFile(includer, fileType)) { + m_match = m_indexer->m_files.at(fileType).value(includer); + return true; + } + return false; + } + + IndexerPrivate *m_indexer; + IndexerPrivate::FileData m_match; +}; + +} // Anonymous + +void IndexerPrivate::startLoading() +{ + // In the case of existent persisted symbols, we restore them and make them visible + // to the indexer. However, we need a dependency graph in order to identify the proper + // options. + + if (deserealizeSymbols() && !m_index.isEmpty()) + computeDependencyGraph(); + else + concludeLoading(); +} + +void IndexerPrivate::concludeLoading() +{ + m_isLoaded = true; + run(); +} + +void IndexerPrivate::computeDependencyGraph() +{ + // FIXME +// for (int fileType = ImplementationFile; fileType < TotalFileTypes; ++fileType) { +// QHash<QString, FileData>::iterator it = m_files[fileType].begin(); +// for (; it != m_files[fileType].end(); ++it) +// m_dependencyGraph.addFile(it.value().m_fileName, it.value().m_compilationOptions); +// } + + m_loadingWatcher.reset(new QFutureWatcher<void>); + connect(m_loadingWatcher.data(), SIGNAL(finished()), this, SLOT(dependencyGraphComputed())); +// m_loadingWatcher->setFuture(m_dependencyGraph.compute()); +} + +void IndexerPrivate::dependencyGraphComputed() +{ + if (m_loadingWatcher->isCanceled()) + return; + + m_loadingWatcher.reset(new QFutureWatcher<void>); + connect(m_loadingWatcher.data(), SIGNAL(finished()), this, SLOT(restoredSymbolsAnalysed())); + m_loadingWatcher->setFuture(QtConcurrent::run(this, &IndexerPrivate::analyzeRestoredSymbols)); +} + +void IndexerPrivate::analyzeRestoredSymbols() +{ + // @TODO: We only check for time stamps, so we still need to handle somehow the case in + // which the project options (for example a .pro file) changed while "outside" a Creator + // session. + + foreach (const QString &fileName, m_index.files()) { + bool upToDate = m_index.validate(fileName); + + FileType fileType = identifyFileType(fileName); + if (isTrackingFile(fileName, fileType)) { + // When the file is already being tracked we simply need to update its state. + if (upToDate) + m_files[fileType][fileName].m_upToDate = true; + } else { + // If it's not being tracked we need to find at least one tracked dependency + // so we can use its options. + DepedencyVisitor visitor(this); +// m_dependencyGraph.collectDependencies(fileName, +// DependencyGraph::FilesWhichInclude, +// &visitor); + if (!visitor.m_match.m_fileName.isEmpty()) { + addOrUpdateFileData(fileName, + visitor.m_match.m_projectPart, + upToDate); + } else { + m_index.removeFile(fileName); + } + } + + if (!upToDate && m_index.containsFile(fileName)) + m_index.removeFile(fileName); + } +} + +void IndexerPrivate::runQuickIndexing(const Unit &unit, const CppTools::ProjectPart::Ptr &part) +{ + QMutexLocker locker(&m_mutex); + + addOrUpdateFileData(unit.fileName(), part, false); + + QuickIndexer indexer(this, unit, part); + indexer.run(); +} + +void IndexerPrivate::restoredSymbolsAnalysed() +{ + if (m_loadingWatcher->isCanceled()) + return; + + concludeLoading(); +} + +bool IndexerPrivate::deserealizeSymbols() +{ + if (m_storagePath.isEmpty()) + return false; + + ::Utils::FileReader reader; + if (reader.fetch(m_storagePath)) { + m_index.deserialize(reader.data()); + return true; + } + + return false; +} + +void IndexerPrivate::serializeSymbols() const +{ + if (m_storagePath.isEmpty()) + return; + + ::Utils::FileSaver saver(m_storagePath); + saver.write(m_index.serialize()); + if (!saver.finalize()) + qWarning("Failed to serialize index"); +} + +QList<Symbol> IndexerPrivate::symbols(Symbol::Kind kind) const +{ + if (m_loadingWatcher->isRunning()) + return QList<Symbol>(); + + return m_index.symbols(kind); +} + +QList<Symbol> IndexerPrivate::symbols(const QString &fileName, const Symbol::Kind kind) const +{ + if (m_loadingWatcher->isRunning()) + return QList<Symbol>(); + + if (kind == Symbol::Unknown) + return m_index.symbols(fileName); + + return m_index.symbols(fileName, kind); +} + +void IndexerPrivate::match(ClangSymbolSearcher *searcher) const +{ + if (m_loadingWatcher->isRunning()) + return; + + m_index.match(searcher); +} + +Indexer::Indexer(QObject *parent) + : QObject(parent) + , m_d(new IndexerPrivate(this)) +{} + +Indexer::~Indexer() +{} + +void Indexer::regenerate() +{ + if (!m_d->m_isLoaded) { + if (m_d->m_loadingWatcher->isRunning()) + return; + m_d->startLoading(); + } else { + m_d->run(); + } +} + +void Indexer::initialize(const QString &storagePath) +{ + Q_ASSERT(!m_d->m_isLoaded); + + m_d->m_storagePath = storagePath; +} + +void Indexer::evaluateFile(const QString &fileName) +{ + if (!m_d->m_isLoaded) + return; + + m_d->run(QStringList(normalizeFileName(fileName))); +} + +bool Indexer::isBusy() const +{ + return m_d->isBusy(); +} + +void Indexer::cancel(bool waitForFinished) +{ + return m_d->cancel(waitForFinished); +} + +void Indexer::finalize() +{ + m_d->reset(); +} + +bool Indexer::addFile(const QString &fileName, ProjectPart::Ptr projectPart) +{ + return m_d->addFile(fileName, projectPart); +} + +QStringList Indexer::allFiles() const +{ + return m_d->allFiles(); +} + +QStringList Indexer::compilationOptions(const QString &fileName) const +{ + return m_d->compilationOptions(fileName); +} + +QList<Symbol> Indexer::allFunctions() const +{ + return m_d->symbols(Symbol::Function); +} + +QList<Symbol> Indexer::allClasses() const +{ + return m_d->symbols(Symbol::Class); +} + +QList<Symbol> Indexer::allMethods() const +{ + return m_d->symbols(Symbol::Method); +} + +QList<Symbol> Indexer::allConstructors() const +{ + return m_d->symbols(Symbol::Constructor); +} + +QList<Symbol> Indexer::allDestructors() const +{ + return m_d->symbols(Symbol::Destructor); +} + +QList<Symbol> Indexer::functionsFromFile(const QString &fileName) const +{ + return m_d->symbols(fileName, Symbol::Function); +} + +QList<Symbol> Indexer::classesFromFile(const QString &fileName) const +{ + return m_d->symbols(fileName, Symbol::Class); +} + +QList<Symbol> Indexer::methodsFromFile(const QString &fileName) const +{ + return m_d->symbols(fileName, Symbol::Method); +} + +QList<Symbol> Indexer::constructorsFromFile(const QString &fileName) const +{ + return m_d->symbols(fileName, Symbol::Constructor); +} + +QList<Symbol> Indexer::destructorsFromFile(const QString &fileName) const +{ + return m_d->symbols(fileName, Symbol::Destructor); +} + +QList<Symbol> Indexer::allFromFile(const QString &fileName) const +{ + return m_d->symbols(fileName, Symbol::Unknown); +} + +void Indexer::match(ClangSymbolSearcher *searcher) const +{ + m_d->match(searcher); +} + +void Indexer::runQuickIndexing(const Unit &unit, const CppTools::ProjectPart::Ptr &part) +{ + m_d->runQuickIndexing(unit, part); +} + +#include "indexer.moc" diff --git a/src/plugins/clangcodemodel/indexer.h b/src/plugins/clangcodemodel/indexer.h new file mode 100644 index 0000000000..fc5079ebfd --- /dev/null +++ b/src/plugins/clangcodemodel/indexer.h @@ -0,0 +1,103 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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. +** +****************************************************************************/ + +#ifndef INDEXER_H +#define INDEXER_H + +#include "clang_global.h" +#include "symbol.h" +#include "unit.h" + +#include <cpptools/cppmodelmanagerinterface.h> + +#include <QtCore/QObject> +#include <QtCore/QString> +#include <QtCore/QStringList> +#include <QtCore/QScopedPointer> +#include <QtCore/QFuture> + +namespace ClangCodeModel { + +namespace Internal { +class ClangSymbolSearcher; +} // namespace Internal + +class IndexerPrivate; + +class CLANG_EXPORT Indexer : public QObject +{ + Q_OBJECT + +public: + typedef CppTools::ProjectPart ProjectPart; + +public: + Indexer(QObject *parent = 0); + ~Indexer(); + + void initialize(const QString &storagePath); + void finalize(); + + void regenerate(); + void evaluateFile(const QString &fileName); + bool isBusy() const; + void cancel(bool waitForFinished); + + bool addFile(const QString &fileName, ProjectPart::Ptr projectPart); + QStringList allFiles() const; + QStringList compilationOptions(const QString &fileName) const; + + QList<Symbol> allFunctions() const; + QList<Symbol> allClasses() const; + QList<Symbol> allMethods() const; + QList<Symbol> allConstructors() const; + QList<Symbol> allDestructors() const; + QList<Symbol> functionsFromFile(const QString &fileName) const; + QList<Symbol> classesFromFile(const QString &fileName) const; + QList<Symbol> methodsFromFile(const QString &fileName) const; + QList<Symbol> constructorsFromFile(const QString &fileName) const; + QList<Symbol> destructorsFromFile(const QString &fileName) const; + QList<Symbol> allFromFile(const QString &fileName) const; + + void match(Internal::ClangSymbolSearcher *searcher) const; + + void runQuickIndexing(const Internal::Unit &unit, const ProjectPart::Ptr &part); + +signals: + void indexingStarted(QFuture<void> future); + void indexingFinished(); + +private: + friend class IndexerPrivate; + QScopedPointer<IndexerPrivate> m_d; +}; + +} // ClangCodeModel + +#endif // INDEXER_H diff --git a/src/plugins/clangcodemodel/liveunitsmanager.cpp b/src/plugins/clangcodemodel/liveunitsmanager.cpp new file mode 100644 index 0000000000..5cf653fbb8 --- /dev/null +++ b/src/plugins/clangcodemodel/liveunitsmanager.cpp @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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 "liveunitsmanager.h" + +#include <coreplugin/idocument.h> + +using namespace ClangCodeModel; +using namespace Internal; + +LiveUnitsManager *LiveUnitsManager::m_instance = 0; + +LiveUnitsManager::LiveUnitsManager() +{ + Q_ASSERT(!m_instance); + m_instance = this; + + qRegisterMetaType<ClangCodeModel::Internal::Unit>(); +} + +LiveUnitsManager::~LiveUnitsManager() +{ + m_instance = 0; +} + +void LiveUnitsManager::requestTracking(const QString &fileName) +{ + if (!fileName.isEmpty() && !isTracking(fileName)) + m_units.insert(fileName, Unit(fileName)); +} + +void LiveUnitsManager::cancelTrackingRequest(const QString &fileName) +{ + if (!isTracking(fileName)) + return; + + // If no one else is tracking this particular unit, we remove it. + if (m_units[fileName].isUnique()) + m_units.remove(fileName); +} + +void LiveUnitsManager::updateUnit(const QString &fileName, const Unit &unit) +{ + if (!isTracking(fileName)) + return; + + m_units[fileName] = unit; + + emit unitAvailable(unit); +} + +Unit LiveUnitsManager::unit(const QString &fileName) +{ + return m_units.value(fileName); +} + +void LiveUnitsManager::editorOpened(Core::IEditor *editor) +{ + requestTracking(editor->document()->filePath()); +} + +void LiveUnitsManager::editorAboutToClose(Core::IEditor *editor) +{ + cancelTrackingRequest(editor->document()->filePath()); +} diff --git a/src/plugins/clangcodemodel/liveunitsmanager.h b/src/plugins/clangcodemodel/liveunitsmanager.h new file mode 100644 index 0000000000..ea74bf8044 --- /dev/null +++ b/src/plugins/clangcodemodel/liveunitsmanager.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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. +** +****************************************************************************/ + +#ifndef LIVEUNITSMANAGER_H +#define LIVEUNITSMANAGER_H + +#include "unit.h" + +#include <coreplugin/editormanager/ieditor.h> + +#include <QtCore/QObject> +#include <QtCore/QString> +#include <QtCore/QHash> + +namespace ClangCodeModel { +namespace Internal { + +class LiveUnitsManager : public QObject +{ + Q_OBJECT + +public: + LiveUnitsManager(); + ~LiveUnitsManager(); + static LiveUnitsManager *instance() + { return m_instance; } + + void requestTracking(const QString &fileName); + bool isTracking(const QString &fileName) const + { return m_units.contains(fileName); } + + void cancelTrackingRequest(const QString &fileName); + + void updateUnit(const QString &fileName, const Unit &unit); + Unit unit(const QString &fileName); + +public slots: + void editorOpened(Core::IEditor *editor); + void editorAboutToClose(Core::IEditor *editor); + +signals: + void unitAvailable(const ClangCodeModel::Internal::Unit &unit); + +private: + static LiveUnitsManager *m_instance; + QHash<QString, Unit> m_units; +}; + +} // Internal +} // ClangCodeModel + +#endif // LIVEUNITSMANAGER_H diff --git a/src/plugins/clangcodemodel/pchinfo.cpp b/src/plugins/clangcodemodel/pchinfo.cpp new file mode 100644 index 0000000000..e4a482a475 --- /dev/null +++ b/src/plugins/clangcodemodel/pchinfo.cpp @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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 "pchinfo.h" + +#include <QDir> + +using namespace ClangCodeModel::Internal; + +PchInfo::PchInfo() +{ +} + +PchInfo::~PchInfo() +{ +} + +PchInfo::Ptr PchInfo::createEmpty() +{ + return Ptr(new PchInfo); +} + +PchInfo::Ptr PchInfo::createWithFileName(const QString &inputFileName, + const QStringList &options, + bool objcEnabled) +{ + Ptr result(new PchInfo); + result->m_inputFileName = inputFileName; + result->m_options = options; + result->m_objcEnabled = objcEnabled; + + // The next 2 lines are just here to generate the file name.... + result->m_file.open(); + result->m_file.close(); + return result; +} diff --git a/src/plugins/clangcodemodel/pchinfo.h b/src/plugins/clangcodemodel/pchinfo.h new file mode 100644 index 0000000000..44a4353720 --- /dev/null +++ b/src/plugins/clangcodemodel/pchinfo.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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. +** +****************************************************************************/ + +#ifndef PCHINFO_H +#define PCHINFO_H + +#include <QString> +#include <QStringList> +#include <QSharedPointer> +#include <QTemporaryFile> + +namespace ClangCodeModel { +namespace Internal { + +class PchInfo +{ + PchInfo(); + +public: + typedef QSharedPointer<PchInfo> Ptr; + +public: + ~PchInfo(); + + static Ptr createEmpty(); + static Ptr createWithFileName(const QString &inputFileName, + const QStringList &options, bool objcEnabled); + + /// \return the (temporary) file name for the PCH file. + QString fileName() const + { return m_file.fileName(); } + + /// \return the input file for the PCH compilation. + QString inputFileName() const + { return m_inputFileName; } + + /// \return the options used to generate this PCH file. + QStringList options() const + { return m_options; } + + bool objcWasEnabled() const + { return m_objcEnabled; } + +private: + QString m_inputFileName; + QStringList m_options; + bool m_objcEnabled; + QTemporaryFile m_file; +}; + +} // Internal namespace +} // ClangCodeModel namespace + +#endif // PCHINFO_H diff --git a/src/plugins/clangcodemodel/pchmanager.cpp b/src/plugins/clangcodemodel/pchmanager.cpp new file mode 100644 index 0000000000..2a551f5c66 --- /dev/null +++ b/src/plugins/clangcodemodel/pchmanager.cpp @@ -0,0 +1,433 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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 "pchmanager.h" +#include "utils.h" +#include "clangutils.h" + +#include <coreplugin/icore.h> +#include <coreplugin/progressmanager/progressmanager.h> + +#include <utils/runextensions.h> + +#include <QFile> + +using namespace ClangCodeModel; +using namespace ClangCodeModel::Internal; +using namespace CPlusPlus; + +PCHManager *PCHManager::m_instance = 0; + +PCHManager::PCHManager(QObject *parent) + : QObject(parent) +{ + Q_ASSERT(!m_instance); + m_instance = this; + + QObject *msgMgr = Core::MessageManager::instance(); + connect(this, SIGNAL(pchMessage(QString, Core::MessageManager::PrintToOutputPaneFlags)), + msgMgr, SLOT(write(QString, Core::MessageManager::PrintToOutputPaneFlags))); + + connect(&m_pchGenerationWatcher, SIGNAL(finished()), + this, SLOT(updateActivePCHFiles())); +} + +PCHManager::~PCHManager() +{ + Q_ASSERT(m_instance); + m_instance = 0; + qDeleteAll(m_projectSettings.values()); + m_projectSettings.clear(); +} + +PCHManager *PCHManager::instance() +{ + return m_instance; +} + +PchInfo::Ptr PCHManager::pchInfo(const ProjectPart::Ptr &projectPart) const +{ + QMutexLocker locker(&m_mutex); + + return m_activePCHFiles[projectPart]; +} + +ClangProjectSettings *PCHManager::settingsForProject(ProjectExplorer::Project *project) +{ + QMutexLocker locker(&m_mutex); + + ClangProjectSettings *cps = m_projectSettings.value(project); + if (!cps) { + cps = new ClangProjectSettings(project); + m_projectSettings.insert(project, cps); + cps->pullSettings(); + connect(cps, SIGNAL(pchSettingsChanged()), + this, SLOT(clangProjectSettingsChanged())); + } + return cps; +} + +void PCHManager::setPCHInfo(const QList<ProjectPart::Ptr> &projectParts, + const PchInfo::Ptr &pchInfo, + const QPair<bool, QStringList> &msgs) +{ + QMutexLocker locker(&m_mutex); + + foreach (ProjectPart::Ptr pPart, projectParts) + m_activePCHFiles[pPart] = pchInfo; + + if (pchInfo) { + if (msgs.first) { + if (!pchInfo->fileName().isEmpty()) + emit pchMessage(tr("Successfully generated PCH file \"%1\".").arg( + pchInfo->fileName()), Core::MessageManager::Silent); + } else { + emit pchMessage(tr("Failed to generate PCH file \"%1\".").arg( + pchInfo->fileName()), Core::MessageManager::Silent); + } + if (!msgs.second.isEmpty()) + emit pchMessage(msgs.second.join(QLatin1String("\n")), Core::MessageManager::Flash); + } +} + +void PCHManager::clangProjectSettingsChanged() +{ + ClangProjectSettings *cps = qobject_cast<ClangProjectSettings *>(sender()); + if (!cps) + return; + + onProjectPartsUpdated(cps->project()); +} + +void PCHManager::onAboutToRemoveProject(ProjectExplorer::Project *project) +{ + Q_UNUSED(project); + + // we cannot ask the ModelManager for the parts, because, depending on + // the order of signal delivery, it might already have wiped any information + // about the project. + + updateActivePCHFiles(); +} + +void PCHManager::onProjectPartsUpdated(ProjectExplorer::Project *project) +{ + ClangProjectSettings *cps = settingsForProject(project); + Q_ASSERT(cps); + + CppTools::CppModelManagerInterface *mmi = CppTools::CppModelManagerInterface::instance(); + const QList<ProjectPart::Ptr> projectParts = mmi->projectInfo( + cps->project()).projectParts(); + updatePchInfo(cps, projectParts); + + emit pchInfoUpdated(); +} + +void PCHManager::updatePchInfo(ClangProjectSettings *cps, + const QList<ProjectPart::Ptr> &projectParts) +{ + if (m_pchGenerationWatcher.isRunning()) { +// m_pchGenerationWatcher.cancel(); + m_pchGenerationWatcher.waitForFinished(); + } + + QFuture<void> future = QtConcurrent::run(&PCHManager::doPchInfoUpdate, + cps->pchUsage(), + cps->customPchFile(), + projectParts); + m_pchGenerationWatcher.setFuture(future); + Core::ProgressManager::addTask(future, tr("Precompiling..."), "Key.Tmp.Precompiling"); +} + +namespace { + +bool hasObjCFiles(const CppTools::ProjectPart::Ptr &projectPart) +{ + foreach (const CppTools::ProjectFile &file, projectPart->files) { + switch (file.kind) { + case CppTools::ProjectFile::ObjCHeader: + case CppTools::ProjectFile::ObjCSource: + case CppTools::ProjectFile::ObjCXXHeader: + case CppTools::ProjectFile::ObjCXXSource: + return true; + default: + break; + } + } + return false; +} + +bool hasCppFiles(const CppTools::ProjectPart::Ptr &projectPart) +{ + foreach (const CppTools::ProjectFile &file, projectPart->files) { + switch (file.kind) { + case CppTools::ProjectFile::CudaSource: + case CppTools::ProjectFile::CXXHeader: + case CppTools::ProjectFile::CXXSource: + case CppTools::ProjectFile::OpenCLSource: + case CppTools::ProjectFile::ObjCXXHeader: + case CppTools::ProjectFile::ObjCXXSource: + return true; + default: + break; + } + } + return false; +} + +CppTools::ProjectFile::Kind getPrefixFileKind(bool hasObjectiveC, bool hasCPlusPlus) +{ + if (hasObjectiveC && hasCPlusPlus) + return CppTools::ProjectFile::ObjCXXHeader; + else if (hasObjectiveC) + return CppTools::ProjectFile::ObjCHeader; + else if (hasCPlusPlus) + return CppTools::ProjectFile::CXXHeader; + return CppTools::ProjectFile::CHeader; +} + +} + +void PCHManager::doPchInfoUpdate(QFutureInterface<void> &future, + ClangProjectSettings::PchUsage pchUsage, + const QString customPchFile, + const QList<ProjectPart::Ptr> projectParts) +{ + PCHManager *pchManager = PCHManager::instance(); + +// qDebug() << "switching to" << pchUsage; + + if (pchUsage == ClangProjectSettings::PchUse_None + || (pchUsage == ClangProjectSettings::PchUse_Custom && customPchFile.isEmpty())) { + future.setProgressRange(0, 2); + Core::MessageManager::write(QLatin1String("updatePchInfo: switching to none"), + Core::MessageManager::Silent); + PchInfo::Ptr emptyPch = PchInfo::createEmpty(); + pchManager->setPCHInfo(projectParts, emptyPch, qMakePair(true, QStringList())); + future.setProgressValue(1); + } else if (pchUsage == ClangProjectSettings::PchUse_BuildSystem_Fuzzy) { + Core::MessageManager::write( + QLatin1String("updatePchInfo: switching to build system (fuzzy)"), + Core::MessageManager::Silent); + QHash<QString, QSet<QString> > includes, frameworks; + QHash<QString, QSet<QByteArray> > definesPerPCH; + QHash<QString, bool> objc; + QHash<QString, bool> cplusplus; + QHash<QString, ProjectPart::QtVersion> qtVersions; + QHash<QString, ProjectPart::CVersion> cVersions; + QHash<QString, ProjectPart::CXXVersion> cxxVersions; + QHash<QString, ProjectPart::CXXExtensions> cxxExtensionsMap; + QHash<QString, QList<ProjectPart::Ptr> > inputToParts; + foreach (const ProjectPart::Ptr &projectPart, projectParts) { + if (projectPart->precompiledHeaders.isEmpty()) + continue; + const QString &pch = projectPart->precompiledHeaders.first(); // TODO: support more than 1 PCH file. + if (!QFile(pch).exists()) + continue; + inputToParts[pch].append(projectPart); + + includes[pch].unite(QSet<QString>::fromList(projectPart->includePaths)); + frameworks[pch].unite(QSet<QString>::fromList(projectPart->frameworkPaths)); + cVersions[pch] = std::max(cVersions.value(pch, ProjectPart::C89), projectPart->cVersion); + cxxVersions[pch] = std::max(cxxVersions.value(pch, ProjectPart::CXX98), projectPart->cxxVersion); + cxxExtensionsMap[pch] = cxxExtensionsMap[pch] | projectPart->cxxExtensions; + + if (hasObjCFiles(projectPart)) + objc[pch] = true; + if (hasCppFiles(projectPart)) + cplusplus[pch] = true; + + QSet<QByteArray> projectDefines = QSet<QByteArray>::fromList(projectPart->toolchainDefines.split('\n')); + QMutableSetIterator<QByteArray> iter(projectDefines); + while (iter.hasNext()){ + QByteArray v = iter.next(); + if (v.startsWith("#define _") || v.isEmpty()) // TODO: see ProjectPart::createClangOptions + iter.remove(); + } + projectDefines.unite(QSet<QByteArray>::fromList(projectPart->projectDefines.split('\n'))); + + if (definesPerPCH.contains(pch)) { + definesPerPCH[pch].intersect(projectDefines); + } else { + definesPerPCH[pch] = projectDefines; + } + + qtVersions[pch] = projectPart->qtVersion; + } + + future.setProgressRange(0, definesPerPCH.size() + 1); + future.setProgressValue(0); + + foreach (const QString &pch, inputToParts.keys()) { + if (future.isCanceled()) + return; + ProjectPart::Ptr projectPart(new ProjectPart); + projectPart->qtVersion = qtVersions[pch]; + projectPart->cVersion = cVersions[pch]; + projectPart->cxxVersion = cxxVersions[pch]; + projectPart->cxxExtensions = cxxExtensionsMap[pch]; + projectPart->includePaths = includes[pch].toList(); + projectPart->frameworkPaths = frameworks[pch].toList(); + + QList<QByteArray> defines = definesPerPCH[pch].toList(); + if (!defines.isEmpty()) { + projectPart->projectDefines = defines[0]; + for (int i = 1; i < defines.size(); ++i) { + projectPart->projectDefines += '\n'; + projectPart->projectDefines += defines[i]; + } + } + + CppTools::ProjectFile::Kind prefixFileKind = + getPrefixFileKind(objc.value(pch, false), cplusplus.value(pch, false)); + + QStringList options = Utils::createClangOptions(projectPart, prefixFileKind); + projectPart.reset(); + + PchInfo::Ptr pchInfo = pchManager->findMatchingPCH(pch, options, true); + QPair<bool, QStringList> msgs = qMakePair(true, QStringList()); + if (pchInfo.isNull()) { + + pchInfo = PchInfo::createWithFileName(pch, options, objc[pch]); + msgs = precompile(pchInfo); + } + pchManager->setPCHInfo(inputToParts[pch], pchInfo, msgs); + future.setProgressValue(future.progressValue() + 1); + } + } else if (pchUsage == ClangProjectSettings::PchUse_BuildSystem_Exact) { + future.setProgressRange(0, projectParts.size() + 1); + future.setProgressValue(0); + Core::MessageManager::write( + QLatin1String("updatePchInfo: switching to build system (exact)"), + Core::MessageManager::Silent); + foreach (const ProjectPart::Ptr &projectPart, projectParts) { + if (future.isCanceled()) + return; + if (projectPart->precompiledHeaders.isEmpty()) + continue; + const QString &pch = projectPart->precompiledHeaders.first(); // TODO: support more than 1 PCH file. + if (!QFile(pch).exists()) + continue; + + const bool hasObjC = hasObjCFiles(projectPart); + QStringList options = Utils::createClangOptions( + projectPart, getPrefixFileKind(hasObjC, hasCppFiles(projectPart))); + + PchInfo::Ptr pchInfo = pchManager->findMatchingPCH(pch, options, false); + QPair<bool, QStringList> msgs = qMakePair(true, QStringList()); + if (pchInfo.isNull()) { + pchInfo = PchInfo::createWithFileName(pch, options, hasObjC); + msgs = precompile(pchInfo); + } + pchManager->setPCHInfo(QList<ProjectPart::Ptr>() << projectPart, + pchInfo, msgs); + future.setProgressValue(future.progressValue() + 1); + } + } else if (pchUsage == ClangProjectSettings::PchUse_Custom) { + future.setProgressRange(0, 2); + future.setProgressValue(0); + Core::MessageManager::write( + QLatin1String("updatePchInfo: switching to custom") + customPchFile, + Core::MessageManager::Silent); + + QSet<QString> includes, frameworks; + bool objc = false; + bool cplusplus = false; + ProjectPart::Ptr united(new ProjectPart()); + united->cxxVersion = ProjectPart::CXX98; + foreach (const ProjectPart::Ptr &projectPart, projectParts) { + includes.unite(QSet<QString>::fromList(projectPart->includePaths)); + frameworks.unite(QSet<QString>::fromList(projectPart->frameworkPaths)); + united->cVersion = std::max(united->cVersion, projectPart->cVersion); + united->cxxVersion = std::max(united->cxxVersion, projectPart->cxxVersion); + united->qtVersion = std::max(united->qtVersion, projectPart->qtVersion); + objc |= hasObjCFiles(projectPart); + cplusplus |= hasCppFiles(projectPart); + } + united->frameworkPaths = frameworks.toList(); + united->includePaths = includes.toList(); + QStringList opts = Utils::createClangOptions( + united, getPrefixFileKind(objc, cplusplus)); + united.clear(); + + PchInfo::Ptr pchInfo = pchManager->findMatchingPCH(customPchFile, opts, true); + QPair<bool, QStringList> msgs = qMakePair(true, QStringList());; + if (future.isCanceled()) + return; + if (pchInfo.isNull()) { + pchInfo = PchInfo::createWithFileName(customPchFile, opts, objc); + msgs = precompile(pchInfo); + } + pchManager->setPCHInfo(projectParts, pchInfo, msgs); + future.setProgressValue(1); + } + + future.setProgressValue(future.progressValue() + 1); +} + +PchInfo::Ptr PCHManager::findMatchingPCH(const QString &inputFileName, + const QStringList &options, + bool fuzzyMatching) const +{ + QMutexLocker locker(&m_mutex); + + if (fuzzyMatching) { + QStringList opts = options; + opts.sort(); + foreach (PchInfo::Ptr pchInfo, m_activePCHFiles.values()) { + if (pchInfo->inputFileName() != inputFileName) + continue; + QStringList pchOpts = pchInfo->options(); + pchOpts.sort(); + if (pchOpts == opts) + return pchInfo; + } + } else { + foreach (PchInfo::Ptr pchInfo, m_activePCHFiles.values()) + if (pchInfo->inputFileName() == inputFileName + && pchInfo->options() == options) + return pchInfo; + } + + return PchInfo::Ptr(); +} + +void PCHManager::updateActivePCHFiles() +{ + QMutexLocker locker(&m_mutex); + + QSet<ProjectPart::Ptr> activeParts; + CppTools::CppModelManagerInterface *mmi = CppTools::CppModelManagerInterface::instance(); + foreach (const CppTools::CppModelManagerInterface::ProjectInfo &pi, mmi->projectInfos()) + activeParts.unite(QSet<ProjectPart::Ptr>::fromList(pi.projectParts())); + QList<ProjectPart::Ptr> partsWithPCHFiles = m_activePCHFiles.keys(); + foreach (ProjectPart::Ptr pPart, partsWithPCHFiles) + if (!activeParts.contains(pPart)) + m_activePCHFiles.remove(pPart); +} diff --git a/src/plugins/clangcodemodel/pchmanager.h b/src/plugins/clangcodemodel/pchmanager.h new file mode 100644 index 0000000000..638d7147ca --- /dev/null +++ b/src/plugins/clangcodemodel/pchmanager.h @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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. +** +****************************************************************************/ + +#ifndef PCHMANAGER_H +#define PCHMANAGER_H + +#include "clangprojectsettings.h" +#include "pchinfo.h" + +#include <cpptools/cppmodelmanagerinterface.h> +#include <projectexplorer/project.h> +#include <coreplugin/messagemanager.h> + +#include <QFutureWatcher> +#include <QHash> +#include <QMutex> +#include <QObject> + +namespace ClangCodeModel { +namespace Internal { + +class PCHManager: public QObject +{ + Q_OBJECT + + typedef CppTools::ProjectPart ProjectPart; + + static PCHManager *m_instance; + +public: + PCHManager(QObject *parent = 0); + virtual ~PCHManager(); + + static PCHManager *instance(); + + PchInfo::Ptr pchInfo(const ProjectPart::Ptr &projectPart) const; + ClangProjectSettings *settingsForProject(ProjectExplorer::Project *project); + +signals: + void pchInfoUpdated(); // TODO: check if this is used + void pchMessage(const QString &message, Core::MessageManager::PrintToOutputPaneFlags flags); + +public slots: + void clangProjectSettingsChanged(); + void onAboutToRemoveProject(ProjectExplorer::Project *project); + void onProjectPartsUpdated(ProjectExplorer::Project *project); + +private slots: + void updateActivePCHFiles(); + +private: + void updatePchInfo(ClangProjectSettings *cps, + const QList<ProjectPart::Ptr> &projectParts); + static void doPchInfoUpdate(QFutureInterface<void> &future, + ClangProjectSettings::PchUsage pchUsage, + const QString customPchFile, + const QList<ProjectPart::Ptr> projectParts); + void setPCHInfo(const QList<ProjectPart::Ptr> &projectParts, + const PchInfo::Ptr &pchInfo, + const QPair<bool, QStringList> &msgs); + PchInfo::Ptr findMatchingPCH(const QString &inputFileName, const QStringList &options, + bool fuzzyMatching) const; + +private: + mutable QMutex m_mutex; + QHash<ProjectPart::Ptr, PchInfo::Ptr> m_activePCHFiles; + QHash<ProjectExplorer::Project *, ClangProjectSettings *> m_projectSettings; + QFutureWatcher<void> m_pchGenerationWatcher; +}; + +} // namespace Internal +} // namespace ClangCodeModel + +#endif // PCHMANAGER_H diff --git a/src/plugins/clangcodemodel/raii/scopedclangoptions.cpp b/src/plugins/clangcodemodel/raii/scopedclangoptions.cpp new file mode 100644 index 0000000000..24f8b7cefb --- /dev/null +++ b/src/plugins/clangcodemodel/raii/scopedclangoptions.cpp @@ -0,0 +1,103 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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 "scopedclangoptions.h" + +namespace ClangCodeModel { + +/** + * @class ClangCodeModel::ScopedClangOptions + * @brief Converts QStringList to raw options, acceptable by clang-c parsing and indexing API + */ + +ScopedClangOptions::ScopedClangOptions(const QStringList &options) + : m_size(options.size()) + , m_rawOptions(new const char*[options.size()]) +{ + for (int i = 0 ; i < m_size; ++i) + m_rawOptions[i] = qstrdup(options[i].toUtf8()); +} + +ScopedClangOptions::~ScopedClangOptions() +{ + for (int i = 0; i < m_size; ++i) + delete[] m_rawOptions[i]; + delete[] m_rawOptions; +} + +const char **ScopedClangOptions::data() const +{ + return m_rawOptions; +} + +int ScopedClangOptions::size() const +{ + return m_size; +} + +/** + * @class ClangCodeModel::SharedClangOptions + * @brief Shared wrapper around \a {ClangCodeModel::ScopedClangOptions} ScopedClangOptions + */ + +SharedClangOptions::SharedClangOptions() + : d(0) +{ +} + +SharedClangOptions::SharedClangOptions(const QStringList &options) + : d(new ScopedClangOptions(options)) +{ +} + +/** + * @return Replaces options with new options list + */ +void SharedClangOptions::reloadOptions(const QStringList &options) +{ + d = QSharedPointer<ScopedClangOptions>(new ScopedClangOptions(options)); +} + +/** + * @return Pointer to clang raw options or NULL if uninitialized + */ +const char **SharedClangOptions::data() const +{ + return d ? d->data() : 0; +} + +/** + * @return Options count or 0 if uninitialized + */ +int SharedClangOptions::size() const +{ + return d ? d->size() : 0; +} + +} // namespace ClangCodeModel diff --git a/src/plugins/debugger/lldblib/lldboptionspage.h b/src/plugins/clangcodemodel/raii/scopedclangoptions.h index 71d5d05088..0f1020dead 100644 --- a/src/plugins/debugger/lldblib/lldboptionspage.h +++ b/src/plugins/clangcodemodel/raii/scopedclangoptions.h @@ -27,54 +27,46 @@ ** ****************************************************************************/ -#ifndef LLDBOPTIONSPAGE_H -#define LLDBOPTIONSPAGE_H +#ifndef CLANGCODEMODEL_SCOPEDCLANGOPTIONS_H +#define CLANGCODEMODEL_SCOPEDCLANGOPTIONS_H -#include <coreplugin/dialogs/ioptionspage.h> -#include "ui_lldboptionspagewidget.h" - -#include <QWidget> -#include <QPointer> +#include "../clang_global.h" +#include <QStringList> #include <QSharedPointer> -#include <QSettings> -namespace Debugger { -namespace Internal { +namespace ClangCodeModel { -class LldbOptionsPageWidget : public QWidget +class CLANG_EXPORT ScopedClangOptions { - Q_OBJECT - public: - explicit LldbOptionsPageWidget(QWidget *parent, QSettings *s); + ScopedClangOptions(const QStringList &options); + ~ScopedClangOptions(); -public slots: - void save(); - void load(); + const char **data() const; + int size() const; private: - Ui::LldbOptionsPageWidget m_ui; - QSettings *s; + void release(); + + int m_size; + const char **m_rawOptions; }; -class LldbOptionsPage : public Core::IOptionsPage +class CLANG_EXPORT SharedClangOptions { - Q_OBJECT - public: - LldbOptionsPage(); + SharedClangOptions(); + SharedClangOptions(const QStringList &options); + + void reloadOptions(const QStringList &options); - // IOptionsPage - QWidget *createPage(QWidget *parent); - void apply(); - void finish(); - bool matches(const QString &) const; + const char **data() const; + int size() const; private: - QPointer<LldbOptionsPageWidget> m_widget; + QSharedPointer<ScopedClangOptions> d; }; -} // namespace Internal -} // namespace Debugger +} // namespace ClangCodeModel -#endif // LLDBOPTIONSPAGE_H +#endif // CLANGCODEMODEL_SCOPEDCLANGOPTIONS_H diff --git a/src/plugins/clangcodemodel/semanticmarker.cpp b/src/plugins/clangcodemodel/semanticmarker.cpp new file mode 100644 index 0000000000..34c7acf934 --- /dev/null +++ b/src/plugins/clangcodemodel/semanticmarker.cpp @@ -0,0 +1,506 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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 "semanticmarker.h" +#include "unit.h" +#include "utils_p.h" +#include "cxraii.h" + +using namespace ClangCodeModel; +using namespace ClangCodeModel::Internal; + +static const unsigned ATTACHED_NOTES_LIMIT = 10; + +SemanticMarker::SemanticMarker() +{ +} + +SemanticMarker::~SemanticMarker() +{ +} + +QString SemanticMarker::fileName() const +{ + if (!m_unit) + return QString(); + + return m_unit->fileName(); +} + +void SemanticMarker::setFileName(const QString &fileName) +{ + if (this->fileName() == fileName) + return; + + QStringList oldOptions; + if (m_unit) + oldOptions = m_unit->compilationOptions(); + m_unit.reset(new Unit(fileName)); + if (!oldOptions.isEmpty()) + m_unit->setCompilationOptions(oldOptions); + + unsigned clangOpts = clang_defaultEditingTranslationUnitOptions(); + clangOpts |= CXTranslationUnit_Incomplete; + clangOpts |= CXTranslationUnit_DetailedPreprocessingRecord; + clangOpts &= ~CXTranslationUnit_CacheCompletionResults; + m_unit->setManagementOptions(clangOpts); +} + +void SemanticMarker::setCompilationOptions(const QStringList &options) +{ + Q_ASSERT(m_unit); + + if (m_unit->compilationOptions() == options) + return; + + m_unit->setCompilationOptions(options); +} + +void SemanticMarker::reparse(const UnsavedFiles &unsavedFiles) +{ + Q_ASSERT(m_unit); + + m_unit->setUnsavedFiles(unsavedFiles); + if (m_unit->isLoaded()) + m_unit->reparse(); + else + m_unit->parse(); +} + +/** + * \brief Calculate one or several ranges and append diagnostic for each range + * Extracted from SemanticMarker::diagnostics() to reuse code + */ +static void appendDiagnostic(const CXDiagnostic &diag, + const CXSourceLocation &cxLocation, + Diagnostic::Severity severity, + const QString &spelling, + QList<Diagnostic> &diagnostics) +{ + const unsigned rangeCount = clang_getDiagnosticNumRanges(diag); + bool expandLocation = true; + + for (unsigned i = 0; i < rangeCount; ++i) { + CXSourceRange r = clang_getDiagnosticRange(diag, i); + const SourceLocation &spellBegin = Internal::getSpellingLocation(clang_getRangeStart(r)); + const SourceLocation &spellEnd = Internal::getSpellingLocation(clang_getRangeEnd(r)); + unsigned length = spellEnd.offset() - spellBegin.offset(); + + // File name can be empty due clang bug + if (!spellBegin.fileName().isEmpty()) { + Diagnostic d(severity, spellBegin, length, spelling); + diagnostics.append(d); + expandLocation = false; + } + } + + if (expandLocation) { + const SourceLocation &location = Internal::getExpansionLocation(cxLocation); + Diagnostic d(severity, location, 0, spelling); + diagnostics.append(d); + } +} + +QList<Diagnostic> SemanticMarker::diagnostics() const +{ + QList<Diagnostic> diagnostics; + if (!m_unit || !m_unit->isLoaded()) + return diagnostics; + + const unsigned diagCount = m_unit->getNumDiagnostics(); + for (unsigned i = 0; i < diagCount; ++i) { + ScopedCXDiagnostic diag(m_unit->getDiagnostic(i)); + + Diagnostic::Severity severity = static_cast<Diagnostic::Severity>(clang_getDiagnosticSeverity(diag)); + if (severity == Diagnostic::Ignored || severity == Diagnostic::Note) + continue; + + CXSourceLocation cxLocation = clang_getDiagnosticLocation(diag); + QString spelling = Internal::getQString(clang_getDiagnosticSpelling(diag)); + + // Attach messages with Diagnostic::Note severity + ScopedCXDiagnosticSet cxChildren(clang_getChildDiagnostics(diag)); + const unsigned numChildren = clang_getNumDiagnosticsInSet(cxChildren); + const unsigned size = qMin(ATTACHED_NOTES_LIMIT, numChildren); + for (unsigned di = 0; di < size; ++di) { + ScopedCXDiagnostic child(clang_getDiagnosticInSet(cxChildren, di)); + spelling.append(QLatin1String("\n ")); + spelling.append(Internal::getQString(clang_getDiagnosticSpelling(child))); + } + + // Fatal error may occur in another file, but it breaks whole parsing + // Typical fatal error is unresolved #include + if (severity == Diagnostic::Fatal) { + for (unsigned di = 0; di < numChildren; ++di) { + ScopedCXDiagnostic child(clang_getDiagnosticInSet(cxChildren, di)); + appendDiagnostic(child, clang_getDiagnosticLocation(child), Diagnostic::Warning, spelling, diagnostics); + } + } + + appendDiagnostic(diag, cxLocation, severity, spelling, diagnostics); + } + + return diagnostics; +} + +QList<TextEditor::BlockRange> SemanticMarker::ifdefedOutBlocks() const +{ + QList<TextEditor::BlockRange> blocks; + + if (!m_unit || !m_unit->isLoaded()) + return blocks; + +#if CINDEX_VERSION_MINOR >= 21 + CXSourceRangeList *skippedRanges = clang_getSkippedRanges(m_unit->clangTranslationUnit(), + m_unit->getFile()); + blocks.reserve(skippedRanges->count); + for (unsigned i = 0; i < skippedRanges->count; ++i) { + const CXSourceRange &r = skippedRanges->ranges[i]; + const SourceLocation &spellBegin = Internal::getSpellingLocation(clang_getRangeStart(r)); + if (spellBegin.fileName() != fileName()) + continue; + const SourceLocation &spellEnd = Internal::getSpellingLocation(clang_getRangeEnd(r)); + const int begin = spellBegin.offset() + 1; + const int end = spellEnd.offset() - spellEnd.column(); + blocks.append(TextEditor::BlockRange(begin, end)); + } + clang_disposeSourceRangeList(skippedRanges); +#endif + + return blocks; +} + +namespace { +static void add(QList<SourceMarker> &markers, + const CXSourceRange &extent, + SourceMarker::Kind kind) +{ + CXSourceLocation start = clang_getRangeStart(extent); + CXSourceLocation end = clang_getRangeEnd(extent); + const SourceLocation &location = Internal::getExpansionLocation(start); + const SourceLocation &locationEnd = Internal::getExpansionLocation(end); + + if (location.offset() < locationEnd.offset()) { + const unsigned length = locationEnd.offset() - location.offset(); + markers.append(SourceMarker(location, length, kind)); + } +} + +/** + * @brief Selects correct highlighting for cursor that is reference + * @return SourceMarker::Unknown if cannot select highlighting + */ +static SourceMarker::Kind getKindByReferencedCursor(const CXCursor &cursor) +{ + const CXCursor referenced = clang_getCursorReferenced(cursor); + switch (clang_getCursorKind(referenced)) { + case CXCursor_EnumConstantDecl: + return SourceMarker::Enumeration; + + case CXCursor_FieldDecl: + case CXCursor_ObjCIvarDecl: + case CXCursor_ObjCPropertyDecl: + return SourceMarker::Field; + + case CXCursor_FunctionDecl: + case CXCursor_FunctionTemplate: + case CXCursor_Constructor: + return SourceMarker::Function; + + case CXCursor_VarDecl: + case CXCursor_ParmDecl: + case CXCursor_NonTypeTemplateParameter: + return SourceMarker::Local; + + case CXCursor_CXXMethod: + if (clang_CXXMethod_isVirtual(referenced)) + return SourceMarker::VirtualMethod; + else + return SourceMarker::Function; + + case CXCursor_ObjCClassMethodDecl: + case CXCursor_ObjCInstanceMethodDecl: + // calling method as property, e.h. "layer.shouldRasterize = YES" + return SourceMarker::Field; + + case CXCursor_UnexposedDecl: + // NSObject "self" method which is a pseudo keyword + if (clang_getCursorLanguage(referenced) == CXLanguage_ObjC) + return SourceMarker::PseudoKeyword; + break; + + default: + break; + } + return SourceMarker::Unknown; +} + +static const QSet<QString> ObjcPseudoKeywords = QSet<QString>() + << QLatin1String("end") + << QLatin1String("try") + << QLatin1String("defs") + << QLatin1String("throw") + << QLatin1String("class") + << QLatin1String("catch") + << QLatin1String("encode") + << QLatin1String("public") + << QLatin1String("dynamic") + << QLatin1String("finally") + << QLatin1String("package") + << QLatin1String("private") + << QLatin1String("optional") + << QLatin1String("property") + << QLatin1String("protocol") + << QLatin1String("required") + << QLatin1String("selector") + << QLatin1String("interface") + << QLatin1String("protected") + << QLatin1String("synthesize") + << QLatin1String("not_keyword") + << QLatin1String("synchronized") + << QLatin1String("implementation") + << QLatin1String("compatibility_alias") + ; + +} // Anonymous namespace + +/** + * @brief SemanticMarker::sourceMarkersInRange + * @param firstLine - first line where to generate highlighting markers + * @param lastLine - last line where to generate highlighting markers + * + * There still two kinds of problems: + * - clang_annotateTokens() can return wrong cursor, and it's normal behavior + * - some cases no handled + * + * Problems caused by wrong cursors: + * - range-based for from C++ 2011 + * - identifiers in some compound statements have type DeclStmt + * or CompoundStmt which refers to top-level construction. + * - CXCursor_ObjCIvarDecl mapped to field, but instance variable have + * incorrect cursor kind if it declared in private interface + * @interface MyApplication() { + * NSArray* _items; + * } + * + * Missed cases: + * - global variables highlighted as locals + * - appropriate marker had not been selected for listed cursors: + * CXCursor_ObjCProtocolExpr, CXCursor_ObjCEncodeExpr, + * CXCursor_ObjCDynamicDecl, CXCursor_ObjCBridgedCastExpr, + * CXCursor_ObjCSuperClassRef + * - template members of template classes&functions always highlighted + * as members, even if they are functions - no way to differ found. + * - @1, @{}, @[] + */ +QList<SourceMarker> SemanticMarker::sourceMarkersInRange(unsigned firstLine, + unsigned lastLine) +{ + Q_ASSERT(m_unit); + + QList<SourceMarker> result; + + if (!m_unit->isLoaded()) + return result; + + // Highlighting called asynchronously, and a few lines at the end can be deleted for this time. + CXSourceRange unitRange = clang_getCursorExtent(m_unit->getTranslationUnitCursor()); + SourceLocation unitEnd = getExpansionLocation(clang_getRangeEnd(unitRange)); + if (lastLine > unitEnd.line()) + lastLine = unitEnd.line(); + + if (firstLine > lastLine) + return result; + + IdentifierTokens idTokens(*m_unit, firstLine, lastLine); + + const CXSourceRange *atTokenExtent = 0; + for (unsigned i = 0; i < idTokens.count(); ++i) { + const CXToken &tok = idTokens.token(i); + CXTokenKind kind = clang_getTokenKind(tok); + if (atTokenExtent) { + if (CXToken_Literal == kind) { + if (m_unit->getTokenSpelling(tok).startsWith(QLatin1Char('"'))) + add(result, *atTokenExtent, SourceMarker::ObjCString); + atTokenExtent = 0; + continue; + } else { + add(result, *atTokenExtent, SourceMarker::PseudoKeyword); + atTokenExtent = 0; + } + } + + const CXSourceRange &tokenExtent = idTokens.extent(i); + + if (CXToken_Keyword == kind) { + QString spell = m_unit->getTokenSpelling(tok); + if (ObjcPseudoKeywords.contains(spell)) + add(result, tokenExtent, SourceMarker::PseudoKeyword); + continue; + } + + if (CXToken_Punctuation == kind) { + static const QLatin1String at("@"); + if (m_unit->getTokenSpelling(tok) == at) + atTokenExtent = &tokenExtent; + continue; + } + + if (CXToken_Identifier != kind) + continue; + + const CXCursor &cursor = idTokens.cursor(i); + const CXCursorKind cursorKind = clang_getCursorKind(cursor); + if (clang_isInvalid(cursorKind)) + continue; + + switch (cursorKind) { + case CXCursor_EnumConstantDecl: + add(result, tokenExtent, SourceMarker::Enumeration); + break; + + case CXCursor_ClassDecl: + case CXCursor_UnionDecl: + case CXCursor_ClassTemplate: + case CXCursor_ClassTemplatePartialSpecialization: + case CXCursor_EnumDecl: + case CXCursor_Namespace: + case CXCursor_NamespaceRef: + case CXCursor_NamespaceAlias: + case CXCursor_StructDecl: + case CXCursor_TemplateRef: + case CXCursor_TypeRef: + case CXCursor_TypedefDecl: + case CXCursor_Constructor: + case CXCursor_TemplateTypeParameter: + case CXCursor_TemplateTemplateParameter: + case CXCursor_UnexposedDecl: /* friend class MyClass; */ + add(result, tokenExtent, SourceMarker::Type); + break; + + case CXCursor_ParmDecl: + case CXCursor_VariableRef: + case CXCursor_VarDecl: + case CXCursor_NonTypeTemplateParameter: + add(result, tokenExtent, SourceMarker::Local); + break; + + case CXCursor_MemberRefExpr: + case CXCursor_MemberRef: + case CXCursor_DeclRefExpr: + case CXCursor_CallExpr: { + SourceMarker::Kind kind = getKindByReferencedCursor(cursor); + if (kind == SourceMarker::Unknown && cursorKind == CXCursor_MemberRefExpr) { + /* template class member in template function */ + kind = SourceMarker::Field; + } + if (kind != SourceMarker::Unknown) + add(result, tokenExtent, kind); + } break; + + case CXCursor_FieldDecl: + add(result, tokenExtent, SourceMarker::Field); + break; + + case CXCursor_Destructor: + case CXCursor_CXXMethod: { + if (clang_CXXMethod_isVirtual(cursor)) + add(result, tokenExtent, SourceMarker::VirtualMethod); + else + add(result, tokenExtent, SourceMarker::Function); + } break; + + case CXCursor_CXXOverrideAttr: + case CXCursor_CXXFinalAttr: + case CXCursor_AnnotateAttr: // 'annotate' in '__attribute__((annotate("AnyComment")))' + case CXCursor_UnexposedAttr: // 'align' in '__declspec(align(8))' + add(result, tokenExtent, SourceMarker::PseudoKeyword); + break; + + case CXCursor_FunctionDecl: + case CXCursor_FunctionTemplate: + case CXCursor_OverloadedDeclRef: + add(result, tokenExtent, SourceMarker::Function); + break; + + case CXCursor_ObjCInstanceMethodDecl: + case CXCursor_ObjCClassMethodDecl: + case CXCursor_ObjCSelectorExpr: + add(result, tokenExtent, SourceMarker::ObjectiveCMessage); + break; + + case CXCursor_ObjCMessageExpr: { + static const QLatin1String super("super"); + if (m_unit->getTokenSpelling(tok) == super) + add(result, tokenExtent, SourceMarker::PseudoKeyword); + else + add(result, tokenExtent, SourceMarker::ObjectiveCMessage); + } break; + + case CXCursor_ObjCCategoryDecl: + case CXCursor_ObjCCategoryImplDecl: + case CXCursor_ObjCImplementationDecl: + case CXCursor_ObjCInterfaceDecl: + case CXCursor_ObjCProtocolDecl: + case CXCursor_ObjCProtocolRef: + case CXCursor_ObjCClassRef: + case CXCursor_ObjCSuperClassRef: + case CXCursor_TypeAliasDecl: // C++11 type alias: 'using value_t = T' + add(result, tokenExtent, SourceMarker::Type); + break; + + case CXCursor_ObjCSynthesizeDecl: + case CXCursor_ObjCDynamicDecl: + case CXCursor_ObjCPropertyDecl: + case CXCursor_ObjCIvarDecl: + add(result, tokenExtent, SourceMarker::Field); + break; + + case CXCursor_MacroDefinition: + case CXCursor_MacroExpansion: + add(result, tokenExtent, SourceMarker::Macro); + break; + + case CXCursor_LabelRef: + case CXCursor_LabelStmt: + add(result, tokenExtent, SourceMarker::Label); + break; + + default: + break; + } + } + + return result; +} + +Unit SemanticMarker::unit() const +{ + return *m_unit; +} diff --git a/src/plugins/clangcodemodel/semanticmarker.h b/src/plugins/clangcodemodel/semanticmarker.h new file mode 100644 index 0000000000..4874bb5b22 --- /dev/null +++ b/src/plugins/clangcodemodel/semanticmarker.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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. +** +****************************************************************************/ + +#ifndef CLANG_SEMANTICMARKER_H +#define CLANG_SEMANTICMARKER_H + +#include "clang_global.h" +#include "diagnostic.h" +#include "fastindexer.h" +#include "sourcemarker.h" +#include "utils.h" + +#include <texteditor/itexteditor.h> + +#include <QMutex> +#include <QScopedPointer> +#include <QSharedPointer> +#include <QString> +#include <QStringList> + +namespace ClangCodeModel { + +namespace Internal { +class Unit; +} + +class CLANG_EXPORT SemanticMarker +{ + Q_DISABLE_COPY(SemanticMarker) + +public: + typedef QSharedPointer<SemanticMarker> Ptr; + +public: + SemanticMarker(); + ~SemanticMarker(); + + QMutex *mutex() const + { return &m_mutex; } + + QString fileName() const; + void setFileName(const QString &fileName); + + void setCompilationOptions(const QStringList &options); + + void reparse(const Internal::UnsavedFiles &unsavedFiles); + + QList<Diagnostic> diagnostics() const; + + QList<TextEditor::BlockRange> ifdefedOutBlocks() const; + + QList<SourceMarker> sourceMarkersInRange(unsigned firstLine, + unsigned lastLine); + + Internal::Unit unit() const; + +private: + mutable QMutex m_mutex; + QScopedPointer<Internal::Unit> m_unit; +}; + +} // namespace ClangCodeModel + +#endif // CLANG_SEMANTICMARKER_H diff --git a/src/plugins/clangcodemodel/sourcelocation.cpp b/src/plugins/clangcodemodel/sourcelocation.cpp new file mode 100644 index 0000000000..be5ace24bf --- /dev/null +++ b/src/plugins/clangcodemodel/sourcelocation.cpp @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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 "sourcelocation.h" + +using namespace ClangCodeModel; + +SourceLocation::SourceLocation() + : m_line(0) + , m_column(0) + , m_offset(0) +{} + +SourceLocation::SourceLocation(const QString &fileName, + unsigned line, + unsigned column, + unsigned offset) + : m_fileName(fileName) + , m_line(line) + , m_column(column) + , m_offset(offset) +{} + +namespace ClangCodeModel { + +bool operator==(const SourceLocation &a, const SourceLocation &b) +{ + return a.line() == b.line() + && a.column() == b.column() + && a.offset() == b.offset() + && a.fileName() == b.fileName() + ; +} + +bool operator!=(const SourceLocation &a, const SourceLocation &b) +{ + return !(a == b); +} + +QDebug operator<<(QDebug dbg, const SourceLocation &location) +{ + dbg.nospace() << location.fileName() + << " [" + << location.line() + << ":" + << location.column() + << "(" + << location.offset() + << ")]"; + return dbg.space(); +} + +} // ClangCodeModel diff --git a/src/plugins/clangcodemodel/sourcelocation.h b/src/plugins/clangcodemodel/sourcelocation.h new file mode 100644 index 0000000000..757e9e7c2d --- /dev/null +++ b/src/plugins/clangcodemodel/sourcelocation.h @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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. +** +****************************************************************************/ + +#ifndef SOURCELOCATION_H +#define SOURCELOCATION_H + +#include "clang_global.h" + +#include <QtCore/QString> +#include <QtCore/QDebug> + +namespace ClangCodeModel { + +class CLANG_EXPORT SourceLocation +{ +public: + SourceLocation(); + SourceLocation(const QString &fileName, + unsigned line = 0, + unsigned column = 0, + unsigned offset = 0); + + bool isNull() const { return m_fileName.isEmpty(); } + const QString &fileName() const { return m_fileName; } + unsigned line() const { return m_line; } + unsigned column() const { return m_column; } + unsigned offset() const { return m_offset; } + +private: + QString m_fileName; + unsigned m_line; + unsigned m_column; + unsigned m_offset; +}; + +bool operator==(const SourceLocation &a, const SourceLocation &b); +bool operator!=(const SourceLocation &a, const SourceLocation &b); + +QDebug operator<<(QDebug dbg, const SourceLocation &location); + +} // ClangCodeModel + +#endif // SOURCELOCATION_H diff --git a/src/plugins/clangcodemodel/sourcemarker.cpp b/src/plugins/clangcodemodel/sourcemarker.cpp new file mode 100644 index 0000000000..ff412e259d --- /dev/null +++ b/src/plugins/clangcodemodel/sourcemarker.cpp @@ -0,0 +1,41 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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 "sourcemarker.h" + +using namespace ClangCodeModel; + +SourceMarker::SourceMarker() + : m_length(0), m_kind(Unknown) +{} + +SourceMarker::SourceMarker(const SourceLocation &location, unsigned length, Kind kind) + : m_loc(location), m_length(length), m_kind(kind) +{ +} diff --git a/src/plugins/clangcodemodel/sourcemarker.h b/src/plugins/clangcodemodel/sourcemarker.h new file mode 100644 index 0000000000..ce1e10ecd6 --- /dev/null +++ b/src/plugins/clangcodemodel/sourcemarker.h @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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. +** +****************************************************************************/ + +#ifndef CLANG_SOURCEMARKER_H +#define CLANG_SOURCEMARKER_H + +#include "clang_global.h" +#include "sourcelocation.h" + +namespace ClangCodeModel { + +class CLANG_EXPORT SourceMarker +{ +public: // TODO: remove this, it's about the same as the TextEditor::SemanticHighlighter::Result + enum Kind { + Unknown = 0, + Type = 1, + Local, + Field, + Enumeration, + VirtualMethod, + Label, + Macro, + Function, + PseudoKeyword, + ObjCString, + + ObjectiveCMessage = VirtualMethod + }; + + SourceMarker(); + SourceMarker(const SourceLocation &location, + unsigned length, + Kind kind); + + bool isValid() const + { return m_loc.line() != 0; } + + bool isInvalid() const + { return m_loc.line() == 0; } + + const SourceLocation &location() const + { return m_loc; } + + unsigned length() const + { return m_length; } + + Kind kind() const + { return m_kind; } + + bool lessThan(const SourceMarker &other) const + { + if (m_loc.line() != other.m_loc.line()) + return m_loc.line() < other.m_loc.line(); + if (m_loc.column() != other.m_loc.column()) + return m_loc.column() < other.m_loc.column(); + return m_length < other.m_length; + } + +private: + SourceLocation m_loc; + unsigned m_length; + Kind m_kind; +}; + +CLANG_EXPORT inline bool operator<(const SourceMarker &one, const SourceMarker &two) +{ return one.lessThan(two); } + +} // namespace Clang + +#endif // CLANG_SOURCEMARKER_H diff --git a/src/plugins/clangcodemodel/symbol.cpp b/src/plugins/clangcodemodel/symbol.cpp new file mode 100644 index 0000000000..235a49e5ec --- /dev/null +++ b/src/plugins/clangcodemodel/symbol.cpp @@ -0,0 +1,117 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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 "symbol.h" + +#include <cplusplus/Icons.h> + +using namespace ClangCodeModel; + +Symbol::Symbol() + : m_kind(Unknown) +{} + +Symbol::Symbol(const QString &name, + const QString &qualification, + Kind type, + const SourceLocation &location) + : m_name(name) + , m_qualification(qualification) + , m_location(location) + , m_kind(type) +{} + +QIcon Symbol::iconForSymbol() const +{ + CPlusPlus::Icons icons; + switch (m_kind) { + case Enum: + return icons.iconForType(CPlusPlus::Icons::EnumIconType); + case Class: + return icons.iconForType(CPlusPlus::Icons::ClassIconType); + case Method: + case Function: + case Declaration: + case Constructor: + case Destructor: + return icons.iconForType(CPlusPlus::Icons::FuncPublicIconType); + default: + return icons.iconForType(CPlusPlus::Icons::UnknownIconType); + } +} + +namespace ClangCodeModel { + +QDataStream &operator<<(QDataStream &stream, const Symbol &symbol) +{ + stream << symbol.m_name + << symbol.m_qualification + << symbol.m_location.fileName() + << (quint32)symbol.m_location.line() + << (quint16)symbol.m_location.column() + << (quint32)symbol.m_location.offset() + << (qint8)symbol.m_kind; + + return stream; +} + +QDataStream &operator>>(QDataStream &stream, Symbol &symbol) +{ + QString fileName; + quint32 line; + quint16 column; + quint32 offset; + quint8 kind; + stream >> symbol.m_name + >> symbol.m_qualification + >> fileName + >> line + >> column + >> offset + >> kind; + symbol.m_location = SourceLocation(fileName, line, column, offset); + symbol.m_kind = Symbol::Kind(kind); + + return stream; +} + +bool operator==(const Symbol &a, const Symbol &b) +{ + return a.m_name == b.m_name + && a.m_qualification == b.m_qualification + && a.m_location == b.m_location + && a.m_kind == b.m_kind; +} + +bool operator!=(const Symbol &a, const Symbol &b) +{ + return !(a == b); +} + +} // ClangCodeModel diff --git a/src/plugins/clangcodemodel/symbol.h b/src/plugins/clangcodemodel/symbol.h new file mode 100644 index 0000000000..b25ee4d598 --- /dev/null +++ b/src/plugins/clangcodemodel/symbol.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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. +** +****************************************************************************/ + +#ifndef INDEXEDSYMBOLINFO_H +#define INDEXEDSYMBOLINFO_H + +#include "sourcelocation.h" + +#include <QString> +#include <QDataStream> +#include <QIcon> + +namespace ClangCodeModel { + +class Symbol +{ +public: + enum Kind { + Enum, + Class, + Method, // A member-function. + Function, // A free-function (global or within a namespace). + Declaration, + Constructor, + Destructor, + Unknown + }; + + Symbol(); + Symbol(const QString &name, + const QString &qualification, + Kind type, + const SourceLocation &location); + + QString m_name; + QString m_qualification; + SourceLocation m_location; + Kind m_kind; + + QIcon iconForSymbol() const; +}; + +QDataStream &operator<<(QDataStream &stream, const Symbol &symbol); +QDataStream &operator>>(QDataStream &stream, Symbol &symbol); + +bool operator==(const Symbol &a, const Symbol &b); +bool operator!=(const Symbol &a, const Symbol &b); + +} // Clang + +#endif // INDEXEDSYMBOLINFO_H diff --git a/src/plugins/clangcodemodel/test/clang_tests_database.qrc b/src/plugins/clangcodemodel/test/clang_tests_database.qrc new file mode 100644 index 0000000000..0014d36087 --- /dev/null +++ b/src/plugins/clangcodemodel/test/clang_tests_database.qrc @@ -0,0 +1,20 @@ +<RCC> + <qresource prefix="/unittests/ClangCodeModel"> + <file>cxx_regression_1.cpp</file> + <file>cxx_regression_2.cpp</file> + <file>cxx_regression_3.cpp</file> + <file>cxx_regression_4.cpp</file> + <file>cxx_regression_5.cpp</file> + <file>cxx_regression_6.cpp</file> + <file>cxx_regression_7.cpp</file> + <file>cxx_regression_8.cpp</file> + <file>cxx_regression_9.cpp</file> + <file>cxx_snippets_1.cpp</file> + <file>cxx_snippets_2.cpp</file> + <file>cxx_snippets_3.cpp</file> + <file>cxx_snippets_4.cpp</file> + <file>objc_messages_1.mm</file> + <file>objc_messages_2.mm</file> + <file>objc_messages_3.mm</file> + </qresource> +</RCC> diff --git a/src/plugins/clangcodemodel/test/clangcompletion_test.cpp b/src/plugins/clangcodemodel/test/clangcompletion_test.cpp new file mode 100644 index 0000000000..0214051705 --- /dev/null +++ b/src/plugins/clangcodemodel/test/clangcompletion_test.cpp @@ -0,0 +1,392 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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. +** +****************************************************************************/ + +/** + * @file clangcompletion_test.cpp + * @brief Performs test for C/C++ code completion + * + * All test cases given as strings with @ character that points to completion + * location. + */ + +#ifdef WITH_TESTS + +// Disabled because there still no tool to detect system Objective-C headers +#define ENABLE_OBJC_TESTS 0 + +#include <QtTest> +#include <QDebug> +#undef interface // Canceling "#DEFINE interface struct" on Windows + +#include "completiontesthelper.h" +#include "../clangcodemodelplugin.h" + +using namespace ClangCodeModel; +using namespace ClangCodeModel::Internal; + +//////////////////////////////////////////////////////////////////////////////// +// Test cases + +/** + * \defgroup Regression tests + * + * This group tests possible regressions in non-standard completion chunks + * handling: for example, macro arguments and clang's code snippets. + * + * @{ + */ + +void ClangCodeModelPlugin::test_CXX_regressions() +{ + QFETCH(QString, file); + QFETCH(QStringList, unexpected); + QFETCH(QStringList, mustHave); + + CompletionTestHelper helper; + helper << file; + + QStringList proposals = helper.codeCompleteTexts(); + + foreach (const QString &p, unexpected) + QTEST_ASSERT(false == proposals.contains(p)); + + foreach (const QString &p, mustHave) + QTEST_ASSERT(true == proposals.contains(p)); +} + +void ClangCodeModelPlugin::test_CXX_regressions_data() +{ + QTest::addColumn<QString>("file"); + QTest::addColumn<QStringList>("unexpected"); + QTest::addColumn<QStringList>("mustHave"); + + QString file; + QStringList unexpected; + QStringList mustHave; + + file = QLatin1String("cxx_regression_1.cpp"); + mustHave << QLatin1String("sqr"); + mustHave << QLatin1String("~Math"); + unexpected << QLatin1String("operator="); + QTest::newRow("case 1: method call completion") << file << unexpected << mustHave; + mustHave.clear(); + unexpected.clear(); + + file = QLatin1String("cxx_regression_2.cpp"); + unexpected << QLatin1String("i_second"); + unexpected << QLatin1String("c_second"); + unexpected << QLatin1String("f_second"); + mustHave << QLatin1String("i_first"); + mustHave << QLatin1String("c_first"); + QTest::newRow("case 2: multiple anonymous structs") << file << unexpected << mustHave; + mustHave.clear(); + unexpected.clear(); + + file = QLatin1String("cxx_regression_3.cpp"); + mustHave << QLatin1String("i8"); + mustHave << QLatin1String("i64"); + mustHave << QLatin1String("~Priv"); + unexpected << QLatin1String("operator="); + QTest::newRow("case 3: nested class resolution") << file << unexpected << mustHave; + mustHave.clear(); + unexpected.clear(); + + file = QLatin1String("cxx_regression_4.cpp"); + mustHave << QLatin1String("action"); + QTest::newRow("case 4: local (in function) class resolution") << file << unexpected << mustHave; + mustHave.clear(); + unexpected.clear(); + + file = QLatin1String("cxx_regression_5.cpp"); + mustHave << QLatin1String("doB"); + unexpected << QLatin1String("doA"); + QTest::newRow("case 5: nested template class resolution") << file << unexpected << mustHave; + mustHave.clear(); + unexpected.clear(); + + file = QLatin1String("cxx_regression_6.cpp"); + mustHave << QLatin1String("OwningPtr"); + QTest::newRow("case 6: using particular symbol from namespace") << file << unexpected << mustHave; + mustHave.clear(); + unexpected.clear(); + + file = QLatin1String("cxx_regression_7.cpp"); + mustHave << QLatin1String("dataMember"); + mustHave << QLatin1String("anotherMember"); + QTest::newRow("case 7: template class inherited from template parameter") << file << unexpected << mustHave; + mustHave.clear(); + unexpected.clear(); + + file = QLatin1String("cxx_regression_8.cpp"); + mustHave << QLatin1String("utils::"); + unexpected << QLatin1String("utils"); + QTest::newRow("case 8: namespace completion in function body") << file << unexpected << mustHave; + mustHave.clear(); + unexpected.clear(); + + file = QLatin1String("cxx_regression_9.cpp"); + mustHave << QLatin1String("EnumScoped::Value1"); + mustHave << QLatin1String("EnumScoped::Value2"); + mustHave << QLatin1String("EnumScoped::Value3"); + unexpected << QLatin1String("Value1"); + unexpected << QLatin1String("EnumScoped"); + QTest::newRow("case 9: c++11 enum class, value used in switch and 'case' completed") + << file << unexpected << mustHave; + mustHave.clear(); + unexpected.clear(); +} + +void ClangCodeModelPlugin::test_CXX_snippets() +{ + QFETCH(QString, file); + QFETCH(QStringList, texts); + QFETCH(QStringList, snippets); + Q_ASSERT(texts.size() == snippets.size()); + + CompletionTestHelper helper; + helper << file; + + QList<CodeCompletionResult> proposals = helper.codeComplete(); + + for (int i = 0, n = texts.size(); i < n; ++i) { + const QString &text = texts[i]; + const QString &snippet = snippets[i]; + const QString snippetError = + QLatin1String("Text and snippet mismatch: text '") + text + + QLatin1String("', snippet '") + snippet + + QLatin1String("', got snippet '%1'"); + + bool hasText = false; + foreach (const CodeCompletionResult &ccr, proposals) { + if (ccr.text() != text) + continue; + hasText = true; + QVERIFY2(snippet == ccr.snippet(), snippetError.arg(ccr.snippet()).toAscii()); + } + const QString textError(QLatin1String("Text not found:") + text); + QVERIFY2(hasText, textError.toAscii()); + } +} + +void ClangCodeModelPlugin::test_CXX_snippets_data() +{ + QTest::addColumn<QString>("file"); + QTest::addColumn<QStringList>("texts"); + QTest::addColumn<QStringList>("snippets"); + + QString file; + QStringList texts; + QStringList snippets; + + file = QLatin1String("cxx_snippets_1.cpp"); + texts << QLatin1String("reinterpret_cast<type>(expression)"); + snippets << QLatin1String("reinterpret_cast<$type$>($expression$)"); + + texts << QLatin1String("static_cast<type>(expression)"); + snippets << QLatin1String("static_cast<$type$>($expression$)"); + + texts << QLatin1String("new type(expressions)"); + snippets << QLatin1String("new $type$($expressions$)"); + + QTest::newRow("case: snippets for var declaration") << file << texts << snippets; + texts.clear(); + snippets.clear(); + + file = QLatin1String("cxx_snippets_2.cpp"); + texts << QLatin1String("private"); + snippets << QLatin1String(""); + + texts << QLatin1String("protected"); + snippets << QLatin1String(""); + + texts << QLatin1String("public"); + snippets << QLatin1String(""); + + texts << QLatin1String("friend"); + snippets << QLatin1String(""); + + texts << QLatin1String("virtual"); + snippets << QLatin1String(""); + + texts << QLatin1String("typedef type name"); + snippets << QLatin1String("typedef $type$ $name$"); + + QTest::newRow("case: snippets inside class declaration") << file << texts << snippets; + texts.clear(); + snippets.clear(); + + file = QLatin1String("cxx_snippets_3.cpp"); + texts << QLatin1String("List"); + snippets << QLatin1String("List<$class Item$>"); + + texts << QLatin1String("Tuple"); + snippets << QLatin1String("Tuple<$class First$, $class Second$, $typename Third$>"); + + QTest::newRow("case: template class insertion as snippet") << file << texts << snippets; + texts.clear(); + snippets.clear(); + + file = QLatin1String("cxx_snippets_4.cpp"); + texts << QLatin1String("clamp"); + snippets << QLatin1String(""); + + texts << QLatin1String("perform"); + snippets << QLatin1String("perform<$class T$>"); + + QTest::newRow("case: template function insertion as snippet") << file << texts << snippets; + texts.clear(); + snippets.clear(); +} + +void ClangCodeModelPlugin::test_ObjC_hints() +{ + QFETCH(QString, file); + QFETCH(QStringList, texts); + QFETCH(QStringList, snippets); + QFETCH(QStringList, hints); + Q_ASSERT(texts.size() == snippets.size()); + Q_ASSERT(texts.size() == hints.size()); + + CompletionTestHelper helper; + helper << file; + + QList<CodeCompletionResult> proposals = helper.codeComplete(); + + for (int i = 0, n = texts.size(); i < n; ++i) { + const QString &text = texts[i]; + const QString &snippet = snippets[i]; + const QString &hint = hints[i]; + const QString snippetError = + QLatin1String("Text and snippet mismatch: text '") + text + + QLatin1String("', snippet '") + snippet + + QLatin1String("', got snippet '%1'"); + const QString hintError = + QLatin1String("Text and hint mismatch: text '") + text + + QLatin1String("', hint\n'") + hint + + QLatin1String(", got hint\n'%1'"); + + bool hasText = false; + QStringList texts; + foreach (const CodeCompletionResult &ccr, proposals) { + texts << ccr.text(); + if (ccr.text() != text) + continue; + hasText = true; + QVERIFY2(snippet == ccr.snippet(), snippetError.arg(ccr.snippet()).toAscii()); + QVERIFY2(hint == ccr.hint(), hintError.arg(ccr.hint()).toAscii()); + } + const QString textError(QString::fromLatin1("Text '%1' not found in set %2") + .arg(text).arg(texts.join(QLatin1Char(',')))); + QVERIFY2(hasText, textError.toAscii()); + } +} + +static QString makeObjCHint(const char *cHintPattern) +{ + QString hintPattern(QString::fromUtf8(cHintPattern)); + QStringList lines = hintPattern.split(QLatin1Char('\n')); + QString hint = QLatin1String("<p>"); + bool prependNewline = false; + foreach (const QString &line, lines) { + if (prependNewline) + hint += QLatin1String("<br/>"); + prependNewline = true; + int i = 0; + while (i < line.size() && line[i] == QLatin1Char(' ')) { + ++i; + hint += QLatin1String(" "); + } + hint += line.mid(i); + } + hint += QLatin1String("</p>"); + return hint; +} + +void ClangCodeModelPlugin::test_ObjC_hints_data() +{ + QTest::addColumn<QString>("file"); + QTest::addColumn<QStringList>("texts"); + QTest::addColumn<QStringList>("snippets"); + QTest::addColumn<QStringList>("hints"); + + QString file; + QStringList texts; + QStringList snippets; + QStringList hints; + + file = QLatin1String("objc_messages_1.mm"); + texts << QLatin1String("spectacleQuality:"); + snippets << QLatin1String("spectacleQuality:$(bool)$"); + hints << makeObjCHint("-(int) spectacleQuality:<b>(bool)</b>"); + texts << QLatin1String("desiredAmountForDramaDose:andPersonsCount:"); + snippets << QLatin1String("desiredAmountForDramaDose:$(int)$ andPersonsCount:$(int)$"); + hints << makeObjCHint("-(int) desiredAmountForDramaDose:<b>(int)</b> \n" + " andPersonsCount:<b>(int)</b>"); + + QTest::newRow("case: objective-c instance messages call") << file << texts << snippets << hints; + texts.clear(); + snippets.clear(); + hints.clear(); + + file = QLatin1String("objc_messages_2.mm"); + texts << QLatin1String("eatenAmount"); + snippets << QLatin1String("(int) eatenAmount"); + hints << makeObjCHint("+(int) eatenAmount"); + texts << QLatin1String("desiredAmountForDramaDose:andPersonsCount:"); + snippets << QLatin1String("(int) desiredAmountForDramaDose:(int)dose andPersonsCount:(int)count"); + hints << makeObjCHint("+(int) desiredAmountForDramaDose:(int)dose \n" + " andPersonsCount:(int)count"); + + QTest::newRow("case: objective-c class messages in @implementation") << file << texts << snippets << hints; + texts.clear(); + snippets.clear(); + hints.clear(); + + file = QLatin1String("objc_messages_3.mm"); + texts << QLatin1String("eatenAmount"); + snippets << QLatin1String("(int) eatenAmount"); + hints << makeObjCHint("-(int) eatenAmount"); + texts << QLatin1String("spectacleQuality"); + snippets << QLatin1String("(int) spectacleQuality"); + hints << makeObjCHint("-(int) spectacleQuality"); + texts << QLatin1String("desiredAmountForDramaDose:andPersonsCount:"); + snippets << QLatin1String("(int) desiredAmountForDramaDose:(int)dose andPersonsCount:(int)count"); + hints << makeObjCHint("-(int) desiredAmountForDramaDose:(int)dose \n" + " andPersonsCount:(int)count"); + texts << QLatin1String("initWithOldTracker:"); + snippets << QLatin1String("(id) initWithOldTracker:(Bbbb<Aaaa> *)aabb"); + hints << makeObjCHint("-(id) initWithOldTracker:(Bbbb<Aaaa> *)aabb"); + + QTest::newRow("case: objective-c class messages from base class") << file << texts << snippets << hints; + texts.clear(); + snippets.clear(); + hints.clear(); +} + +#endif diff --git a/src/plugins/clangcodemodel/test/completiontesthelper.cpp b/src/plugins/clangcodemodel/test/completiontesthelper.cpp new file mode 100644 index 0000000000..f3b4c5c871 --- /dev/null +++ b/src/plugins/clangcodemodel/test/completiontesthelper.cpp @@ -0,0 +1,148 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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. +** +****************************************************************************/ + +#ifdef WITH_TESTS + +#include "completiontesthelper.h" +#include "../clangcompletion.h" +#include "../clangcompleter.h" +#include "../clangcodemodelplugin.h" + +#include <cpptools/cppcompletionassist.h> + +#include <texteditor/basetextdocument.h> +#include <texteditor/plaintexteditor.h> +#include <texteditor/codeassist/iassistproposal.h> +#include <texteditor/codeassist/iassistproposalmodel.h> +#include <texteditor/codeassist/basicproposalitemlistmodel.h> + +#include <utils/fileutils.h> +#include <utils/changeset.h> + +#include <QDir> +#include <QtTest> + +using namespace ClangCodeModel; +using namespace ClangCodeModel::Internal; +using namespace TextEditor; +using namespace CPlusPlus; +using namespace CppTools::Internal; + +namespace ClangCodeModel { +namespace Internal { + +CompletionTestHelper::CompletionTestHelper(QObject *parent) : + QObject(parent), + m_completer(new ClangCompleter()), + m_position(m_line), + m_line(0), + m_column(0) +{ + m_clangOptions << QLatin1String("-std=c++0x") + << QLatin1String("-ObjC++"); +} + +CompletionTestHelper::~CompletionTestHelper() +{ +} + +void CompletionTestHelper::operator <<(const QString &fileName) +{ + QResource res(QLatin1String(":/unittests/ClangCodeModel/") + fileName); + m_sourceCode = QByteArray(reinterpret_cast<const char*>(res.data()), res.size()); + findCompletionPos(); + + QString path = QDir::tempPath() + QLatin1String("/file.h"); + ::Utils::FileSaver srcSaver(path); + srcSaver.write(m_sourceCode); + srcSaver.finalize(); + + m_completer->setFileName(path); + m_completer->setOptions(m_clangOptions); +} + +QStringList CompletionTestHelper::codeCompleteTexts() +{ + QList<CodeCompletionResult> results = + m_completer->codeCompleteAt(m_line, m_column, m_unsavedFiles); + + QStringList completions; + foreach (const CodeCompletionResult& ccr, results) + completions << ccr.text(); + return completions; +} + +QList<CodeCompletionResult> CompletionTestHelper::codeComplete() +{ + return m_completer->codeCompleteAt(m_line, m_column, m_unsavedFiles); +} + +int CompletionTestHelper::position() const +{ + return m_position; +} + +const QByteArray &CompletionTestHelper::source() const +{ + return m_sourceCode; +} + +void CompletionTestHelper::addOption(const QString &option) +{ + m_clangOptions << option; +} + +void CompletionTestHelper::findCompletionPos() +{ + m_position = m_sourceCode.indexOf("<<<<"); + QVERIFY(m_position != -1); + m_sourceCode[m_position] = ' '; + m_sourceCode[m_position + 1] = ' '; + m_sourceCode[m_position + 2] = ' '; + m_sourceCode[m_position + 3] = ' '; + + // substring from 0 to '@' position + QByteArray substr(m_sourceCode.data(), m_position); + + m_line = 1; + m_column = 1; + for (int i = 0; i < substr.size(); ++i) { + if (substr[i] == '\n') { + ++m_line; + m_column = 1; + } else { + ++m_column; + } + } +} + +} // namespace Internal +} // namespace ClangCodeModel + +#endif diff --git a/src/plugins/clangcodemodel/test/completiontesthelper.h b/src/plugins/clangcodemodel/test/completiontesthelper.h new file mode 100644 index 0000000000..54a7c82038 --- /dev/null +++ b/src/plugins/clangcodemodel/test/completiontesthelper.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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. +** +****************************************************************************/ + +#ifndef CLANGCODEMODEL_TESTS_COMPLETIONTESTHELPER_H +#define CLANGCODEMODEL_TESTS_COMPLETIONTESTHELPER_H + +#ifdef WITH_TESTS + +#include "../clangcompleter.h" + +#include <QObject> +#include <QTextDocument> +#include <texteditor/basetexteditor.h> +#include <cplusplus/CppDocument.h> + +namespace TextEditor { class IAssistProposal; } + +namespace ClangCodeModel { +namespace Internal { + +class CompletionTestHelper : public QObject +{ + Q_OBJECT +public: + explicit CompletionTestHelper(QObject *parent = 0); + ~CompletionTestHelper(); + + void operator <<(const QString &fileName); + QStringList codeCompleteTexts(); + QList<CodeCompletionResult> codeComplete(); + + int position() const; + const QByteArray &source() const; + + void addOption(const QString &option); + +private: + void findCompletionPos(); + + UnsavedFiles m_unsavedFiles; + ClangCompleter::Ptr m_completer; + QStringList m_clangOptions; + + QByteArray m_sourceCode; + int m_position; + int m_line; + int m_column; +}; + +} // namespace Internal +} // namespace ClangCodeModel + +#endif + +#endif // CLANGCODEMODEL_TESTS_COMPLETIONTESTHELPER_H diff --git a/src/plugins/clangcodemodel/test/cxx_regression_1.cpp b/src/plugins/clangcodemodel/test/cxx_regression_1.cpp new file mode 100644 index 0000000000..15ae3540e9 --- /dev/null +++ b/src/plugins/clangcodemodel/test/cxx_regression_1.cpp @@ -0,0 +1,44 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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. +** +****************************************************************************/ + +/* + * Expected: 'sqr' + * Not expected: '~Math', 'operator='s + */ + +class Math +{ + int sqr(int a); +}; + +void foo() +{ + Math math; + int sqr = math.<<<<; +} diff --git a/src/plugins/clangcodemodel/test/cxx_regression_2.cpp b/src/plugins/clangcodemodel/test/cxx_regression_2.cpp new file mode 100644 index 0000000000..e342bde00a --- /dev/null +++ b/src/plugins/clangcodemodel/test/cxx_regression_2.cpp @@ -0,0 +1,50 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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. +** +****************************************************************************/ + +/* + * Expected: 'i_first' 'c_first' + * Not expected: 'i_second' 'c_second' 'f_second' + */ + +typedef struct { + int i_first; + char c_first; +} S1; + +typedef struct { + int i_second; + char c_second; + float f_second; +} S2; + +void foo() +{ + S1 s; + s.<<<<; +} diff --git a/src/plugins/clangcodemodel/test/cxx_regression_3.cpp b/src/plugins/clangcodemodel/test/cxx_regression_3.cpp new file mode 100644 index 0000000000..a1fd800998 --- /dev/null +++ b/src/plugins/clangcodemodel/test/cxx_regression_3.cpp @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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. +** +****************************************************************************/ + +/* + * Expected: 'i8' 'i64' + * Unexpected: 'Priv' 'operator=' + */ + +class Example +{ +public: + Example(); + ~Example(); + +private: + class Priv; + Priv *d; +}; + +class Example::Priv +{ +public: + int i8; + int i64; + + Priv() : i8(8), i64(64) {} +}; + +Example::Example() + : d(new Example::Priv()) +{ + d-><<<<; +} + +Example::~Example() +{ +} + +void f() +{ + Example w; +} diff --git a/src/plugins/clangcodemodel/test/cxx_regression_4.cpp b/src/plugins/clangcodemodel/test/cxx_regression_4.cpp new file mode 100644 index 0000000000..ac18460891 --- /dev/null +++ b/src/plugins/clangcodemodel/test/cxx_regression_4.cpp @@ -0,0 +1,41 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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. +** +****************************************************************************/ + +/* + * Expected: 'action' + */ + +void func() +{ + struct impl + { + static void action() {} + }; + impl::<<<<; +} diff --git a/src/plugins/clangcodemodel/test/cxx_regression_5.cpp b/src/plugins/clangcodemodel/test/cxx_regression_5.cpp new file mode 100644 index 0000000000..b5ab3e362f --- /dev/null +++ b/src/plugins/clangcodemodel/test/cxx_regression_5.cpp @@ -0,0 +1,61 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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. +** +****************************************************************************/ + +/* + * Expected: 'doB' + * Not expected: 'doA' + */ + +struct A { + struct Inside { + void doA() {} + }; +}; + +struct B { + struct Inside { + void doB() {} + }; +}; + +template<class T> class C { +public: + typename T::Inside inner; +}; + +int main() +{ + C<A> ca; + C<B> cb; + ca.inner.doA(); + cb.inner.<<<<; + + return 0; +} + diff --git a/src/plugins/clangcodemodel/test/cxx_regression_6.cpp b/src/plugins/clangcodemodel/test/cxx_regression_6.cpp new file mode 100644 index 0000000000..81f5ce78a5 --- /dev/null +++ b/src/plugins/clangcodemodel/test/cxx_regression_6.cpp @@ -0,0 +1,50 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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. +** +****************************************************************************/ + +/* + * Expected: 'OwningPtr' + */ + +namespace llvm { +class OwningPtr; +} + +namespace clang { +using llvm::OwningPtr; +} + +class llvm::OwningPtr +{ +}; + +void foo() +{ + clang::<<<< ptr; + (void)ptr; +} diff --git a/src/plugins/clangcodemodel/test/cxx_regression_7.cpp b/src/plugins/clangcodemodel/test/cxx_regression_7.cpp new file mode 100644 index 0000000000..5e9b9095d5 --- /dev/null +++ b/src/plugins/clangcodemodel/test/cxx_regression_7.cpp @@ -0,0 +1,47 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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. +** +****************************************************************************/ + +/* + * Expected: 'dataMember', 'anotherMember' + */ + +class Data { + int dataMember; +}; + +template <class T> class Other : public T +{ + int anotherMember; +}; + +void func() +{ + Other<Data> c; + c.<<<<; +} diff --git a/src/plugins/clangcodemodel/test/cxx_regression_8.cpp b/src/plugins/clangcodemodel/test/cxx_regression_8.cpp new file mode 100644 index 0000000000..6305c5c76c --- /dev/null +++ b/src/plugins/clangcodemodel/test/cxx_regression_8.cpp @@ -0,0 +1,46 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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. +** +****************************************************************************/ + +/* + * Expected: 'utils::' + * Not expected: 'utils' + */ + +namespace utils +{ +int sqr(int a) +{ + return a * a; +} +} + +void foo() +{ + <<<< +} diff --git a/src/plugins/clangcodemodel/test/cxx_regression_9.cpp b/src/plugins/clangcodemodel/test/cxx_regression_9.cpp new file mode 100644 index 0000000000..59a77646ec --- /dev/null +++ b/src/plugins/clangcodemodel/test/cxx_regression_9.cpp @@ -0,0 +1,54 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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. +** +****************************************************************************/ + +/* + * Expected: 'EnumScoped::Value1', 'EnumScoped::Value2', 'EnumScoped::Value3' + * Unexpected: 'Value1' + */ + +enum class EnumScoped +{ + Value1, + Value2, + Value3 +}; + +class ClassOwnsEnum +{ +}; + +int main() +{ + EnumScoped scoped = ; + switch (scoped) { + default: + break; + case <<<< + } +} diff --git a/src/plugins/clangcodemodel/test/cxx_snippets_1.cpp b/src/plugins/clangcodemodel/test/cxx_snippets_1.cpp new file mode 100644 index 0000000000..2d0bfc390b --- /dev/null +++ b/src/plugins/clangcodemodel/test/cxx_snippets_1.cpp @@ -0,0 +1,48 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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. +** +****************************************************************************/ + +/* + Expected: + text 'reinterpret_cast<type>(expression)' + snippet 'reinterpret_cast<$type$>($expression$)' + + text 'static_cast<type>(expression)' + snippet 'static_cast<$type$>($expression$)' + + text 'new type(expressions)' + snippet 'new $type$($expressions$)' + */ + +void foo() +{ + int data[] = { + 1, 2, 3 + }; + char *cdata = <<<<; +} diff --git a/src/plugins/clangcodemodel/test/cxx_snippets_2.cpp b/src/plugins/clangcodemodel/test/cxx_snippets_2.cpp new file mode 100644 index 0000000000..1d4853df94 --- /dev/null +++ b/src/plugins/clangcodemodel/test/cxx_snippets_2.cpp @@ -0,0 +1,44 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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. +** +****************************************************************************/ + +/* + Expected: + text 'private', + text 'protected', + text 'public', + text 'friend', + text 'virtual' + + text 'typedef type name', snippet 'typedef $type$ $name$' + */ + +class A +{ + <<<< +}; diff --git a/src/plugins/clangcodemodel/test/cxx_snippets_3.cpp b/src/plugins/clangcodemodel/test/cxx_snippets_3.cpp new file mode 100644 index 0000000000..6258c49c65 --- /dev/null +++ b/src/plugins/clangcodemodel/test/cxx_snippets_3.cpp @@ -0,0 +1,52 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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. +** +****************************************************************************/ + +// Expected: +// (List, List<$class Item$>), +// (Tuple, Tuple<$class First$, $class Second$, $typename Third$>) + +template <class Item> +class List +{ + Item *data; +}; + +template <class First, class Second, typename Third> +class Tuple +{ + First *data; + Second *data2; + Third *data3; +}; + +void check() +{ + <<<< +} + diff --git a/src/plugins/clangcodemodel/test/cxx_snippets_4.cpp b/src/plugins/clangcodemodel/test/cxx_snippets_4.cpp new file mode 100644 index 0000000000..e4bd99fe74 --- /dev/null +++ b/src/plugins/clangcodemodel/test/cxx_snippets_4.cpp @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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. +** +****************************************************************************/ + +// Expected: +// (clamp, ), +// (perform, perform<$class T$>) +// (perform3, perform3<$class T$, $int E$, $class D$>) + +// note: clang understands if parameter is redundant + +template<class T> +T clamp(T value, T a = 0.0, T b = 1.0) +{ + if (value < a) + return a; + if (value > b) + return b; + return value; +} + +template<class T> +void perform() +{ +} + +template<class T, int E, class D> +void perform3() +{ +} + +void check() +{ + <<<< +} diff --git a/src/plugins/clangcodemodel/test/objc_messages_1.mm b/src/plugins/clangcodemodel/test/objc_messages_1.mm new file mode 100644 index 0000000000..f64f4e985f --- /dev/null +++ b/src/plugins/clangcodemodel/test/objc_messages_1.mm @@ -0,0 +1,57 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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. +** +****************************************************************************/ + +/* + * Expected texts: + * eatenAmount + * spectacleQuality: + * desiredAmountForDramaDose:andPersonsCount: + * + * Expected hints: + * -(int) eatenAmount + * + * -(int) spectacleQuality:(bool)unused + */ + +@interface PopCornTracker { + int _quality; + int _eatenAmount; + int _remainedAmount; +} ++ (int) eatenAmount; +- (int) spectacleQuality : (bool)unused; +- (int) desiredAmountForDramaDose: (int)dose andPersonsCount: (int) count; +@end + +@implementation PopCornTracker +- (int) desiredAmountForDramaDose: (int)dose andPersonsCount: (int) count +{ + [self <<<<]; +} +@end diff --git a/src/plugins/clangcodemodel/test/objc_messages_2.mm b/src/plugins/clangcodemodel/test/objc_messages_2.mm new file mode 100644 index 0000000000..f534e0aa88 --- /dev/null +++ b/src/plugins/clangcodemodel/test/objc_messages_2.mm @@ -0,0 +1,42 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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. +** +****************************************************************************/ + +@interface PopCornTracker { + int _quality; + int _eatenAmount; + int _remainedAmount; +} ++ (int) eatenAmount; +- (int) spectacleQuality; ++ (int) desiredAmountForDramaDose: (int)dose andPersonsCount: (int) count; +@end + +@implementation PopCornTracker ++ <<<< +@end diff --git a/src/plugins/clangcodemodel/test/objc_messages_3.mm b/src/plugins/clangcodemodel/test/objc_messages_3.mm new file mode 100644 index 0000000000..8231b2fe8b --- /dev/null +++ b/src/plugins/clangcodemodel/test/objc_messages_3.mm @@ -0,0 +1,54 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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. +** +****************************************************************************/ + +@protocol Aaaa +@end + +@interface Bbbb +@end + +@interface PopCornTracker { + int _quality; + int _eatenAmount; + int _remainedAmount; +} +- (int) eatenAmount; +- (int) spectacleQuality; +- (int) desiredAmountForDramaDose: (int)dose andPersonsCount: (int) count; ++ (id) createNewTracker; ++ (id) createOldTracker:(Bbbb<Aaaa> *) aabb; +- (id) initWithOldTracker:(Bbbb<Aaaa> *) aabb; +@end + +@interface AdvancedPopCornTracker : PopCornTracker { +} + +- <<<< + +@end diff --git a/src/plugins/clangcodemodel/unit.cpp b/src/plugins/clangcodemodel/unit.cpp new file mode 100644 index 0000000000..553d1f45ea --- /dev/null +++ b/src/plugins/clangcodemodel/unit.cpp @@ -0,0 +1,455 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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 "unit.h" +#include "unsavedfiledata.h" +#include "utils_p.h" +#include "raii/scopedclangoptions.h" + +#include <clang-c/Index.h> + +#include <QtCore/QByteArray> +#include <QtCore/QVector> +#include <QtCore/QSharedData> +#include <QtCore/QDateTime> +#include <QtAlgorithms> + +#ifdef DEBUG_UNIT_COUNT +# include <QAtomicInt> +# include <QDebug> +static QBasicAtomicInt unitDataCount = Q_BASIC_ATOMIC_INITIALIZER(0); +#endif // DEBUG_UNIT_COUNT + +namespace ClangCodeModel { +namespace Internal { + +class UnitData : public QSharedData +{ +public: + UnitData(); + UnitData(const QString &fileName); + ~UnitData(); + + void swap(UnitData *unitData); + + void unload(); + bool isLoaded() const; + + void updateTimeStamp(); + + CXIndex m_index; + CXTranslationUnit m_tu; + QByteArray m_fileName; + QStringList m_compOptions; + SharedClangOptions m_sharedCompOptions; + unsigned m_managementOptions; + UnsavedFiles m_unsaved; + QDateTime m_timeStamp; +}; + +} // Internal +} // Clang + +using namespace ClangCodeModel; +using namespace ClangCodeModel::Internal; + +UnitData::UnitData() + : m_index(0) + , m_tu(0) + , m_managementOptions(0) +{ +} + +static const int DisplayDiagnostics = qgetenv("QTC_CLANG_VERBOSE").isEmpty() ? 0 : 1; + +UnitData::UnitData(const QString &fileName) + : m_index(clang_createIndex(/*excludeDeclsFromPCH*/ 1, DisplayDiagnostics)) + , m_tu(0) + , m_fileName(fileName.toUtf8()) + , m_managementOptions(0) +{ +} + +UnitData::~UnitData() +{ + unload(); + clang_disposeIndex(m_index); + m_index = 0; +} + +void UnitData::swap(UnitData *other) +{ + qSwap(m_index, other->m_index); + qSwap(m_tu, other->m_tu); + qSwap(m_fileName, other->m_fileName); + qSwap(m_compOptions, other->m_compOptions); + qSwap(m_sharedCompOptions, other->m_sharedCompOptions); + qSwap(m_managementOptions, other->m_managementOptions); + qSwap(m_unsaved, other->m_unsaved); + qSwap(m_timeStamp, other->m_timeStamp); +} + +void UnitData::unload() +{ + if (m_tu) { + clang_disposeTranslationUnit(m_tu); + m_tu = 0; + +#ifdef DEBUG_UNIT_COUNT + qDebug() << "# translation units:" << (unitDataCount.fetchAndAddOrdered(-1) - 1); +#endif // DEBUG_UNIT_COUNT + } +} + +bool UnitData::isLoaded() const +{ + return m_tu && m_index; +} + +void UnitData::updateTimeStamp() +{ + m_timeStamp = QDateTime::currentDateTime(); +} + +Unit::Unit() + : m_data(new UnitData) +{} + +Unit::Unit(const QString &fileName) + : m_data(new UnitData(fileName)) +{} + +Unit::Unit(const Unit &unit) + : m_data(unit.m_data) +{} + +Unit &Unit::operator =(const Unit &unit) +{ + if (this != &unit) + m_data = unit.m_data; + return *this; +} + +Unit::~Unit() +{} + +const QString Unit::fileName() const +{ + const QByteArray &name = m_data->m_fileName; + return QString::fromUtf8(name.data(), name.size()); +} + +bool Unit::isLoaded() const +{ + return m_data->isLoaded(); +} + +const QDateTime &Unit::timeStamp() const +{ + return m_data->m_timeStamp; +} + +QStringList Unit::compilationOptions() const +{ + return m_data->m_compOptions; +} + +void Unit::setCompilationOptions(const QStringList &compOptions) +{ + m_data->m_compOptions = compOptions; + m_data->m_sharedCompOptions.reloadOptions(compOptions); +} + +UnsavedFiles Unit::unsavedFiles() const +{ + return m_data->m_unsaved; +} + +void Unit::setUnsavedFiles(const UnsavedFiles &unsavedFiles) +{ + m_data->m_unsaved = unsavedFiles; +} + +unsigned Unit::managementOptions() const +{ + return m_data->m_managementOptions; +} + +void Unit::setManagementOptions(unsigned managementOptions) +{ + m_data->m_managementOptions = managementOptions; +} + +bool Unit::isUnique() const +{ +#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) + return m_data->ref.load() == 1; +#else + return m_data->ref == 1; +#endif +} + +void Unit::makeUnique() +{ + UnitData *uniqueData = new UnitData; + m_data->swap(uniqueData); // Notice we swap the data itself and not the shared pointer. + m_data = QExplicitlySharedDataPointer<UnitData>(uniqueData); +} + +void Unit::parse() +{ + m_data->unload(); + + m_data->updateTimeStamp(); + + UnsavedFileData unsaved(m_data->m_unsaved); + m_data->m_tu = clang_parseTranslationUnit(m_data->m_index, + m_data->m_fileName.constData(), + m_data->m_sharedCompOptions.data(), + m_data->m_sharedCompOptions.size(), + unsaved.files(), + unsaved.count(), + m_data->m_managementOptions); +} + +void Unit::reparse() +{ + Q_ASSERT(isLoaded()); + + UnsavedFileData unsaved(m_data->m_unsaved); + const unsigned opts = clang_defaultReparseOptions(m_data->m_tu); + if (clang_reparseTranslationUnit(m_data->m_tu, unsaved.count(), unsaved.files(), opts) != 0) + m_data->unload(); +} + +void Unit::create() +{ + // @TODO +} + +void Unit::createFromSourceFile() +{ + // @TODO +} + +int Unit::save(const QString &unitFileName) +{ + Q_ASSERT(isLoaded()); + + return clang_saveTranslationUnit(m_data->m_tu, + unitFileName.toUtf8().constData(), + clang_defaultSaveOptions(m_data->m_tu)); +} + +void Unit::unload() +{ + m_data->unload(); +} + +CXFile Unit::getFile() const +{ + Q_ASSERT(isLoaded()); + + return clang_getFile(m_data->m_tu, m_data->m_fileName.constData()); +} + +CXCursor Unit::getCursor(const CXSourceLocation &location) const +{ + Q_ASSERT(isLoaded()); + + return clang_getCursor(m_data->m_tu, location); +} + +CXSourceLocation Unit::getLocation(const CXFile &file, unsigned line, unsigned column) const +{ + Q_ASSERT(isLoaded()); + + return clang_getLocation(m_data->m_tu, file, line, column); +} + +void Unit::codeCompleteAt(unsigned line, unsigned column, ScopedCXCodeCompleteResults &results) +{ + unsigned flags = clang_defaultCodeCompleteOptions(); +#if defined(CINDEX_VERSION) && (CINDEX_VERSION > 5) + flags |= CXCodeComplete_IncludeBriefComments; +#endif + + UnsavedFileData unsaved(m_data->m_unsaved); + results.reset(clang_codeCompleteAt(m_data->m_tu, m_data->m_fileName.constData(), + line, column, + unsaved.files(), unsaved.count(), flags)); +} + +void Unit::tokenize(CXSourceRange range, CXToken **tokens, unsigned *tokenCount) const +{ + Q_ASSERT(isLoaded()); + Q_ASSERT(tokens); + Q_ASSERT(tokenCount); + Q_ASSERT(!clang_Range_isNull(range)); + + clang_tokenize(m_data->m_tu, range, tokens, tokenCount); +} + +void Unit::disposeTokens(CXToken *tokens, unsigned tokenCount) const +{ + Q_ASSERT(isLoaded()); + + clang_disposeTokens(m_data->m_tu, tokens, tokenCount); +} + +CXSourceRange Unit::getTokenExtent(const CXToken &token) const +{ + Q_ASSERT(isLoaded()); + + return clang_getTokenExtent(m_data->m_tu, token); +} + +void Unit::annotateTokens(CXToken *tokens, unsigned tokenCount, CXCursor *cursors) const +{ + Q_ASSERT(isLoaded()); + Q_ASSERT(tokens); + Q_ASSERT(cursors); + + clang_annotateTokens(m_data->m_tu, tokens, tokenCount, cursors); +} + +CXTranslationUnit Unit::clangTranslationUnit() const +{ + Q_ASSERT(isLoaded()); + + return m_data->m_tu; +} + +CXIndex Unit::clangIndex() const +{ + Q_ASSERT(isLoaded()); + + return m_data->m_index; +} + +QString Unit::getTokenSpelling(const CXToken &tok) const +{ + Q_ASSERT(isLoaded()); + + return getQString(clang_getTokenSpelling(m_data->m_tu, tok)); +} + +CXCursor Unit::getTranslationUnitCursor() const +{ + Q_ASSERT(isLoaded()); + + return clang_getTranslationUnitCursor(m_data->m_tu); +} + +CXString Unit::getTranslationUnitSpelling() const +{ + Q_ASSERT(isLoaded()); + + return clang_getTranslationUnitSpelling(m_data->m_tu); +} + +void Unit::getInclusions(CXInclusionVisitor visitor, CXClientData clientData) const +{ + Q_ASSERT(isLoaded()); + + clang_getInclusions(m_data->m_tu, visitor, clientData); +} + +unsigned Unit::getNumDiagnostics() const +{ + Q_ASSERT(isLoaded()); + + return clang_getNumDiagnostics(m_data->m_tu); +} + +CXDiagnostic Unit::getDiagnostic(unsigned index) const +{ + Q_ASSERT(isLoaded()); + + return clang_getDiagnostic(m_data->m_tu, index); +} + +IdentifierTokens::IdentifierTokens(const Unit &unit, unsigned firstLine, unsigned lastLine) + : m_unit(unit) + , m_tokenCount(0) + , m_tokens(0) + , m_cursors(0) + , m_extents(0) +{ + Q_ASSERT(unit.isLoaded()); + + // Calculate the range: + CXFile file = unit.getFile(); + CXSourceLocation startLocation = unit.getLocation(file, firstLine, 1); + CXSourceLocation endLocation = unit.getLocation(file, lastLine, 1); + CXSourceRange range = clang_getRange(startLocation, endLocation); + + // Retrieve all identifier tokens: + unit.tokenize(range, &m_tokens, &m_tokenCount); + if (m_tokenCount == 0) + return; + + // Get the cursors for the tokens: + m_cursors = new CXCursor[m_tokenCount]; + unit.annotateTokens(m_tokens, + m_tokenCount, + m_cursors); + + m_extents = new CXSourceRange[m_tokenCount]; + // Create the markers using the cursor to check the types: + for (unsigned i = 0; i < m_tokenCount; ++i) + m_extents[i] = unit.getTokenExtent(m_tokens[i]); +} + +IdentifierTokens::~IdentifierTokens() +{ + dispose(); +} + +void IdentifierTokens::dispose() +{ + if (!m_unit.isLoaded()) + return; + + if (m_tokenCount && m_tokens) { + m_unit.disposeTokens(m_tokens, m_tokenCount); + m_tokens = 0; + m_tokenCount = 0; + } + + if (m_cursors) { + delete[] m_cursors; + m_cursors = 0; + } + + if (m_extents) { + delete[] m_extents; + m_extents = 0; + } +} diff --git a/src/plugins/clangcodemodel/unit.h b/src/plugins/clangcodemodel/unit.h new file mode 100644 index 0000000000..98145d06d0 --- /dev/null +++ b/src/plugins/clangcodemodel/unit.h @@ -0,0 +1,195 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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. +** +****************************************************************************/ + +#ifndef UNIT_H +#define UNIT_H + +#include "utils.h" + +#include "cxraii.h" + +#include <QExplicitlySharedDataPointer> +#include <QMetaType> +#include <QString> +#include <QStringList> +#include <QVarLengthArray> + +QT_BEGIN_NAMESPACE +class QDateTime; +QT_END_NAMESPACE + +namespace ClangCodeModel { +namespace Internal { + +class UnitData; + +/* + * This is a minimal wrapper around clang's translation unit functionality. + * It should should contain only the very basic primitives which allow other + * components such as code completion, code navigation, and others to access + * data which directly depends on the translation unit. + * + * In other words, what's wrapped here is only the functions that receive a + * CXTranslationUnit as a parameter. And this class itself is then the corresponding + * abstraction of the CXTranslationUnit. + * + * Notes: + * - This class is not thread-safe. + * - It's responsibility of the client to make sure that the wrapped translation + * unit is consistent with the other data such as cursor and locations being used. + * - The data of the TU is shared. + * + * + * @TODO: This is similar but not exactly the same as the current ClangWrapper class. + * That class is now tuned to specific features, so it's not generic enough to be used + * an underlying component and aslo don't provide the data in a fine granularity as + * needed here. At some point we should split ClangWrapper into its different logical + * components and use this is the underlying structure. + */ +class Unit +{ +public: + Unit(); + explicit Unit(const QString &fileName); + Unit(const Unit &unit); + Unit &operator=(const Unit &unit); + ~Unit(); + + bool isLoaded() const; + + const QString fileName() const; + + const QDateTime &timeStamp() const; + + QStringList compilationOptions() const; + void setCompilationOptions(const QStringList &compOptions); + + UnsavedFiles unsavedFiles() const; + void setUnsavedFiles(const UnsavedFiles &unsavedFiles); + + unsigned managementOptions() const; + void setManagementOptions(unsigned managementOptions); + + // Sharing control facilities + // Method isUnique is just like an "isDetached", however makeUnique is mostly some kind + // of "detach" but which actually moves the data to this particular instance, invalidating + // then all the other shares. + bool isUnique() const; + void makeUnique(); + + // Methods for generating the TU. Name mappings are direct, for example: + // - parse corresponds to clang_parseTranslationUnit + // - createFromSourceFile corresponds to clang_createTranslationUnitFromSourceFile + void parse(); + void reparse(); + void create(); + void createFromSourceFile(); + int save(const QString &unitFileName); + void unload(); + + // Simple forwarding methods, separated by clang categories for convenience. + // As above, the names are directly mapped. Separated by categories as clang for convenience. + // Note that only methods that take the TU as a parameter should be wrapped. + + // - Diagnostic reporting + unsigned getNumDiagnostics() const; + CXDiagnostic getDiagnostic(unsigned index) const; + + // - Translation unit manipulation + CXString getTranslationUnitSpelling() const; + + // - File manipulation routines + CXFile getFile() const; + + // - Mapping between cursors and source code + CXCursor getCursor(const CXSourceLocation &location) const; + + // - Miscellaneous utility functions + void getInclusions(CXInclusionVisitor visitor, CXClientData clientData) const; + + // - Cursor manipulations + CXCursor getTranslationUnitCursor() const; + + // - Physical source locations + CXSourceLocation getLocation(const CXFile &file, unsigned line, unsigned column) const; + + void codeCompleteAt(unsigned line, unsigned column, ScopedCXCodeCompleteResults &results); + + void tokenize(CXSourceRange range, CXToken **tokens, unsigned *tokenCount) const; + void disposeTokens(CXToken *tokens, unsigned tokenCount) const; + CXSourceRange getTokenExtent(const CXToken &token) const; + void annotateTokens(CXToken *tokens, unsigned tokenCount, CXCursor *cursors) const; + + CXTranslationUnit clangTranslationUnit() const; + CXIndex clangIndex() const; + + QString getTokenSpelling(const CXToken &tok) const; + +private: + QExplicitlySharedDataPointer<UnitData> m_data; +}; + +class IdentifierTokens +{ + Q_DISABLE_COPY(IdentifierTokens) + +public: + IdentifierTokens(const Unit &m_unit, unsigned firstLine, unsigned lastLine); + ~IdentifierTokens(); + + unsigned count() const + { return m_tokenCount; } + + const CXToken &token(unsigned nr) const + { Q_ASSERT(nr < count()); return m_tokens[nr]; } + + const CXCursor &cursor(unsigned nr) const + { Q_ASSERT(nr < count()); return m_cursors[nr]; } + + const CXSourceRange &extent(unsigned nr) const + { Q_ASSERT(nr < count()); return m_extents[nr]; } + +private: + void dispose(); + +private: + const Unit &m_unit; + unsigned m_tokenCount; + CXToken *m_tokens; + + CXCursor *m_cursors; + CXSourceRange *m_extents; +}; + +} // Internal +} // Clang + +Q_DECLARE_METATYPE(ClangCodeModel::Internal::Unit) + +#endif // UNIT_H diff --git a/src/plugins/clangcodemodel/unsavedfiledata.cpp b/src/plugins/clangcodemodel/unsavedfiledata.cpp new file mode 100644 index 0000000000..73c93c9de8 --- /dev/null +++ b/src/plugins/clangcodemodel/unsavedfiledata.cpp @@ -0,0 +1,61 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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 "unsavedfiledata.h" + +using namespace ClangCodeModel::Internal; + +UnsavedFileData::UnsavedFileData(const UnsavedFiles &unsavedFiles) + : m_count(unsavedFiles.count()) + , m_files(0) +{ + if (m_count) { + m_files = new CXUnsavedFile[m_count]; + unsigned idx = 0; + for (UnsavedFiles::const_iterator it = unsavedFiles.begin(); it != unsavedFiles.end(); ++it, ++idx) { + QByteArray contents = it.value(); + const char *contentChars = qstrdup(contents.constData()); + m_files[idx].Contents = contentChars; + m_files[idx].Length = contents.size(); + + const char *fileName = qstrdup(it.key().toUtf8().constData()); + m_files[idx].Filename = fileName; + } + } +} + +UnsavedFileData::~UnsavedFileData() +{ + for (unsigned i = 0; i < m_count; ++i) { + delete[] m_files[i].Contents; + delete[] m_files[i].Filename; + } + + delete[] m_files; +} diff --git a/src/plugins/qmlprofiler/canvas/qmlprofilercanvas.h b/src/plugins/clangcodemodel/unsavedfiledata.h index ac7f863220..52b38bf031 100644 --- a/src/plugins/qmlprofiler/canvas/qmlprofilercanvas.h +++ b/src/plugins/clangcodemodel/unsavedfiledata.h @@ -27,50 +27,39 @@ ** ****************************************************************************/ -#ifndef QMLPROFILERCANVAS_H -#define QMLPROFILERCANVAS_H +#ifndef CLANG_INTERNAL_UNSAVEDFILEDATA_H +#define CLANG_INTERNAL_UNSAVEDFILEDATA_H -#include <QQuickPaintedItem> -#include <QTimer> -#include <QMutex> +#include "utils.h" -QT_BEGIN_NAMESPACE -class Context2D; -QT_END_NAMESPACE +#include <clang-c/Index.h> -namespace QmlProfiler { +namespace ClangCodeModel { namespace Internal { -class QmlProfilerCanvas : public QQuickPaintedItem +class UnsavedFileData { - Q_OBJECT + UnsavedFileData(const UnsavedFileData &); + UnsavedFileData &operator=(const UnsavedFileData &); -public: - QmlProfilerCanvas(); - -signals: - void drawRegion(Context2D *ctxt, const QRect ®ion); + typedef ClangCodeModel::Internal::UnsavedFiles UnsavedFiles; -public slots: - void requestPaint(); - void requestRedraw(); +public: + UnsavedFileData(const UnsavedFiles &unsavedFiles); + ~UnsavedFileData(); -private slots: - void draw(); + unsigned count() const + { return m_count; } -protected: - virtual void paint(QPainter *); - virtual void componentComplete(); - virtual void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry); + CXUnsavedFile *files() const + { return m_files; } private: - Context2D *m_context2d; - - QTimer m_drawTimer; - QMutex m_pixmapMutex; + unsigned m_count; + CXUnsavedFile *m_files; }; -} -} +} // namespace Internal +} // namespace ClangCodeModel -#endif // QMLPROFILERCANVAS_H +#endif // CLANG_INTERNAL_UNSAVEDFILEDATA_H diff --git a/src/plugins/clangcodemodel/utils.cpp b/src/plugins/clangcodemodel/utils.cpp new file mode 100644 index 0000000000..a147594d53 --- /dev/null +++ b/src/plugins/clangcodemodel/utils.cpp @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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 "diagnostic.h" +#include "unit.h" +#include "utils.h" +#include "utils_p.h" + +#include <clang-c/Index.h> + +#include <QMutex> +#include <QMutexLocker> + +namespace ClangCodeModel { +namespace Internal { + +QPair<bool, QStringList> precompile(const PchInfo::Ptr &pchInfo) +{ +// qDebug() << "*** Precompiling" << pchInfo->inputFileName() +// << "into" << pchInfo->fileName() +// << "with options" << pchInfo->options(); + + bool ok = false; + + Internal::Unit unit(pchInfo->inputFileName()); + unit.setCompilationOptions(pchInfo->options()); + + unsigned parseOpts = CXTranslationUnit_ForSerialization + | CXTranslationUnit_Incomplete; + unit.setManagementOptions(parseOpts); + + unit.parse(); + if (unit.isLoaded()) + ok = CXSaveError_None == unit.save(pchInfo->fileName()); + + return qMakePair(ok, Internal::formattedDiagnostics(unit)); +} + +namespace { +static bool clangInitialised = false; +static QMutex initialisationMutex; +} // anonymous namespace + +void initializeClang() +{ + if (clangInitialised) + return; + + QMutexLocker locker(&initialisationMutex); + if (clangInitialised) + return; + + clang_toggleCrashRecovery(1); + clang_enableStackTraces(); + clangInitialised = true; + + qRegisterMetaType<ClangCodeModel::Diagnostic>(); + qRegisterMetaType<QList<ClangCodeModel::Diagnostic> >(); +} + +} // Internal namespace +} // ClangCodeModel namespace + diff --git a/src/plugins/qtsupport/debugginghelper.h b/src/plugins/clangcodemodel/utils.h index 2786622811..d1a8abb4a0 100644 --- a/src/plugins/qtsupport/debugginghelper.h +++ b/src/plugins/clangcodemodel/utils.h @@ -27,32 +27,39 @@ ** ****************************************************************************/ -#ifndef DEBUGGINGHELPER_H -#define DEBUGGINGHELPER_H +#ifndef UTILS_H +#define UTILS_H -#include "qtsupport_global.h" - -#include <utils/buildablehelperlibrary.h> +#include "pchinfo.h" #include <QString> +#include <QStringList> +#include <QByteArray> +#include <QMap> +#include <QPair> -QT_FORWARD_DECLARE_CLASS(QStringList) - -namespace QtSupport { +/* + * A header for globally visible typedefs. This is particularly useful + * so we don't have to #include files simply because of a typedef. Still, + * not every typedef should go in here, only the minimal subset of the + * ones which are needed quite often. + */ +namespace ClangCodeModel { +namespace Internal { -class QTSUPPORT_EXPORT DebuggingHelperLibrary : public Utils::BuildableHelperLibrary -{ -public: - static QString debuggingHelperLibraryByInstallData(const QString &qtInstallData); - static QStringList debuggingHelperLibraryDirectories(const QString &qtInstallData); +typedef QMap<QString, QByteArray> UnsavedFiles; - // Build the helpers and return the output log/errormessage. - static bool build(BuildHelperArguments arguments, QString *log, QString *errorMessage); +/** + * Utility method to create a PCH file from a header file. + * + * \returns a boolean indicating success (true) or failure (false), and a + * list of diagnostic messages. + */ +QPair<bool, QStringList> precompile(const PchInfo::Ptr &pchInfo); - // Copy the source files to a target location and return the chosen target location. - static QString copy(const QString &qtInstallData, QString *errorMessage); +void initializeClang(); -}; -} // namespace QtSupport +} // Internal namespace +} // ClangCodeModel namespace -#endif // DEBUGGINGHELPER_H +#endif // UTILS_H diff --git a/src/plugins/clangcodemodel/utils_p.cpp b/src/plugins/clangcodemodel/utils_p.cpp new file mode 100644 index 0000000000..dbc764d004 --- /dev/null +++ b/src/plugins/clangcodemodel/utils_p.cpp @@ -0,0 +1,124 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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 "unit.h" +#include "utils_p.h" + +#include <QtCore/QDir> +#include <QtCore/QFileInfo> + +namespace ClangCodeModel { +namespace Internal { + +QString getQString(const CXString &cxString, bool disposeCXString) +{ + QString s = QString::fromUtf8(clang_getCString(cxString)); + if (disposeCXString) + clang_disposeString(cxString); + return s; +} + +namespace { + +SourceLocation getLocation(const CXSourceLocation &loc, + void (*clangFunction)(CXSourceLocation, + CXFile *, + unsigned *, + unsigned *, + unsigned *)) +{ + CXFile file; + unsigned line, column, offset; + (*clangFunction)(loc, &file, &line, &column, &offset); + return SourceLocation(normalizeFileName(getQString(clang_getFileName(file))), + line, + column, + offset); +} + +} // Anonymous + +SourceLocation getInstantiationLocation(const CXSourceLocation &loc) +{ + return getLocation(loc, &clang_getInstantiationLocation); +} + +SourceLocation getSpellingLocation(const CXSourceLocation &loc) +{ + return getLocation(loc, &clang_getSpellingLocation); +} + +SourceLocation getExpansionLocation(const CXSourceLocation &loc) +{ +// return getLocation(loc, &clang_getExpansionLocation); + return getLocation(loc, &clang_getInstantiationLocation); +} + +QString normalizeFileName(const QString &fileName) +{ + if (fileName.isEmpty()) + return fileName; + + return normalizeFileName(QFileInfo(fileName)); +} + +QString normalizeFileName(const QFileInfo &fileInfo) +{ + if (!fileInfo.isFile()) + return QString(); + + return QDir::cleanPath(fileInfo.absoluteFilePath()); +} + +QStringList formattedDiagnostics(const Unit &unit) +{ + QStringList diags; + if (!unit.isLoaded()) + return diags; + + const unsigned count = unit.getNumDiagnostics(); + for (unsigned i = 0; i < count; ++i) { + CXDiagnostic diag = unit.getDiagnostic(i); + + unsigned opt = CXDiagnostic_DisplaySourceLocation + | CXDiagnostic_DisplayColumn + | CXDiagnostic_DisplaySourceRanges + | CXDiagnostic_DisplayOption + | CXDiagnostic_DisplayCategoryId + | CXDiagnostic_DisplayCategoryName + ; + diags << getQString(clang_formatDiagnostic(diag, opt)); + clang_disposeDiagnostic(diag); + } + + return diags; +} + +} // Internal +} // ClangCodeModel diff --git a/src/plugins/clangcodemodel/utils_p.h b/src/plugins/clangcodemodel/utils_p.h new file mode 100644 index 0000000000..f88c10641e --- /dev/null +++ b/src/plugins/clangcodemodel/utils_p.h @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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. +** +****************************************************************************/ + +#ifndef CLANG_REUSE_H +#define CLANG_REUSE_H + +#include "sourcelocation.h" + +#include <clang-c/Index.h> + +#include <QtCore/QString> + +QT_BEGIN_NAMESPACE +class QFileInfo; +QT_END_NAMESPACE + +namespace ClangCodeModel { +namespace Internal { + +class Unit; + +QString getQString(const CXString &cxString, bool disposeCXString = true); + +SourceLocation getInstantiatonLocation(const CXSourceLocation &loc); // Deprecated +SourceLocation getSpellingLocation(const CXSourceLocation &loc); +SourceLocation getExpansionLocation(const CXSourceLocation &loc); + +// There are slight differences of behavior from apparently similar Qt file processing functions. +// For instance, QFileInfo::absoluteFilePath will uppercase driver letters, while the corresponding +// QDir function will not do so. Besides, we need to keep paths clean. So in order to avoid +// inconsistencies the functions below should be used for any indexing related task. +QString normalizeFileName(const QString &fileName); +QString normalizeFileName(const QFileInfo &fileInfo); + +QStringList formattedDiagnostics(const Unit &unit); + +} // Internal +} // ClangCodeModel + +#endif // CLANG_REUSE_H diff --git a/src/plugins/classview/classview.pro b/src/plugins/classview/classview.pro index 86352ed98f..a2c22df521 100644 --- a/src/plugins/classview/classview.pro +++ b/src/plugins/classview/classview.pro @@ -1,29 +1,29 @@ include(../../qtcreatorplugin.pri) HEADERS += \ - classviewplugin.h \ - classviewnavigationwidgetfactory.h \ classviewconstants.h \ + classviewmanager.h \ classviewnavigationwidget.h \ + classviewnavigationwidgetfactory.h \ classviewparser.h \ - classviewmanager.h \ - classviewsymbollocation.h \ - classviewsymbolinformation.h \ classviewparsertreeitem.h \ - classviewutils.h \ - classviewtreeitemmodel.h + classviewplugin.h \ + classviewsymbolinformation.h \ + classviewsymbollocation.h \ + classviewtreeitemmodel.h \ + classviewutils.h SOURCES += \ - classviewplugin.cpp \ - classviewnavigationwidgetfactory.cpp \ + classviewmanager.cpp \ classviewnavigationwidget.cpp \ + classviewnavigationwidgetfactory.cpp \ classviewparser.cpp \ - classviewmanager.cpp \ - classviewsymbollocation.cpp \ - classviewsymbolinformation.cpp \ classviewparsertreeitem.cpp \ - classviewutils.cpp \ - classviewtreeitemmodel.cpp + classviewplugin.cpp \ + classviewsymbolinformation.cpp \ + classviewsymbollocation.cpp \ + classviewtreeitemmodel.cpp \ + classviewutils.cpp FORMS += \ classviewnavigationwidget.ui diff --git a/src/plugins/classview/classview.qbs b/src/plugins/classview/classview.qbs index a3699245d0..a80e8837ce 100644 --- a/src/plugins/classview/classview.qbs +++ b/src/plugins/classview/classview.qbs @@ -12,31 +12,19 @@ QtcPlugin { Depends { name: "ProjectExplorer" } Depends { name: "TextEditor" } - files: [ "classview.qrc", "classviewconstants.h", - "classviewmanager.cpp", - "classviewmanager.h", - "classviewnavigationwidget.cpp", - "classviewnavigationwidget.h", - "classviewnavigationwidget.ui", - "classviewnavigationwidgetfactory.cpp", - "classviewnavigationwidgetfactory.h", - "classviewparser.cpp", - "classviewparser.h", - "classviewparsertreeitem.cpp", - "classviewparsertreeitem.h", - "classviewplugin.cpp", - "classviewplugin.h", - "classviewsymbolinformation.cpp", - "classviewsymbolinformation.h", - "classviewsymbollocation.cpp", - "classviewsymbollocation.h", - "classviewtreeitemmodel.cpp", - "classviewtreeitemmodel.h", - "classviewutils.cpp", - "classviewutils.h", + "classviewmanager.cpp", "classviewmanager.h", + "classviewnavigationwidget.cpp", "classviewnavigationwidget.h", "classviewnavigationwidget.ui", + "classviewnavigationwidgetfactory.cpp", "classviewnavigationwidgetfactory.h", + "classviewparser.cpp", "classviewparser.h", + "classviewparsertreeitem.cpp", "classviewparsertreeitem.h", + "classviewplugin.cpp", "classviewplugin.h", + "classviewsymbolinformation.cpp", "classviewsymbolinformation.h", + "classviewsymbollocation.cpp", "classviewsymbollocation.h", + "classviewtreeitemmodel.cpp", "classviewtreeitemmodel.h", + "classviewutils.cpp", "classviewutils.h", ] } diff --git a/src/plugins/clearcase/clearcasecontrol.cpp b/src/plugins/clearcase/clearcasecontrol.cpp index 8036c95743..93910c9a52 100644 --- a/src/plugins/clearcase/clearcasecontrol.cpp +++ b/src/plugins/clearcase/clearcasecontrol.cpp @@ -57,6 +57,10 @@ Core::Id ClearCaseControl::id() const bool ClearCaseControl::isConfigured() const { +#ifdef WITH_TESTS + if (m_plugin->isFakeCleartool()) + return true; +#endif const QString binary = m_plugin->settings().ccBinaryPath; if (binary.isEmpty()) return false; @@ -83,12 +87,23 @@ bool ClearCaseControl::supportsOperation(Operation operation) const return rc; } -Core::IVersionControl::OpenSupportMode ClearCaseControl::openSupportMode() const +Core::IVersionControl::OpenSupportMode ClearCaseControl::openSupportMode(const QString &fileName) const { - if (m_plugin->isDynamic()) - return IVersionControl::OpenMandatory; // Checkout is the only option for dynamic views - else + if (m_plugin->isDynamic()) { + // NB! Has to use managesFile() and not vcsStatus() since the index can only be guaranteed + // to be up to date if the file has been explicitly opened, which is not the case when + // doing a search and replace as a part of a refactoring. + if (m_plugin->managesFile(QFileInfo(fileName).absolutePath(), fileName)) { + // Checkout is the only option for managed files in dynamic views + return IVersionControl::OpenMandatory; + } else { + // Not managed files can be edited without noticing the VCS + return IVersionControl::NoOpen; + } + + } else { return IVersionControl::OpenOptional; // Snapshot views supports Hijack and check out + } } bool ClearCaseControl::vcsOpen(const QString &fileName) @@ -153,6 +168,8 @@ QString ClearCaseControl::vcsOpenText() const QString ClearCaseControl::vcsMakeWritableText() const { + if (m_plugin->isDynamic()) + return QString(); return tr("&Hijack"); } diff --git a/src/plugins/clearcase/clearcasecontrol.h b/src/plugins/clearcase/clearcasecontrol.h index b5d673ce38..258c3a7929 100644 --- a/src/plugins/clearcase/clearcasecontrol.h +++ b/src/plugins/clearcase/clearcasecontrol.h @@ -53,7 +53,7 @@ public: bool isConfigured() const; bool supportsOperation(Operation operation) const; - OpenSupportMode openSupportMode() const; + OpenSupportMode openSupportMode(const QString &fileName) const; bool vcsOpen(const QString &fileName); SettingsFlags settingsFlags() const; bool vcsAdd(const QString &fileName); diff --git a/src/plugins/clearcase/clearcaseplugin.cpp b/src/plugins/clearcase/clearcaseplugin.cpp index c971374207..a625609e15 100644 --- a/src/plugins/clearcase/clearcaseplugin.cpp +++ b/src/plugins/clearcase/clearcaseplugin.cpp @@ -48,6 +48,7 @@ #include <coreplugin/documentmanager.h> #include <coreplugin/editormanager/editormanager.h> #include <coreplugin/icore.h> +#include <coreplugin/infobar.h> #include <coreplugin/messagemanager.h> #include <coreplugin/mimedatabase.h> #include <coreplugin/progressmanager/progressmanager.h> @@ -186,6 +187,9 @@ ClearCasePlugin::ClearCasePlugin() : m_submitActionTriggered(false), m_activityMutex(new QMutex), m_statusMap(new StatusMap) + #ifdef WITH_TESTS + ,m_fakeClearTool(false) + #endif { qRegisterMetaType<ClearCase::Internal::FileStatus::Status>("ClearCase::Internal::FileStatus::Status"); } @@ -236,6 +240,76 @@ QString ClearCasePlugin::getDriveLetterOfPath(const QString &directory) return dir.path(); } +void ClearCasePlugin::updateStatusForFile(const QString &absFile) +{ + setStatus(absFile, getFileStatus(absFile), false); +} + +/// Give warning if a derived object is edited +void ClearCasePlugin::updateEditDerivedObjectWarning(const QString &fileName, + const FileStatus::Status status) +{ + if (!isDynamic()) + return; + + Core::IDocument *curDocument = Core::EditorManager::currentDocument(); + if (!curDocument) + return; + + Core::InfoBar *infoBar = curDocument->infoBar(); + const Core::Id derivedObjectWarning("ClearCase.DerivedObjectWarning"); + + if (status == FileStatus::Derived) { + if (!infoBar->canInfoBeAdded(derivedObjectWarning)) + return; + + infoBar->addInfo(Core::InfoBarEntry(derivedObjectWarning, + tr("Editing Derived Object: %1") + .arg(fileName))); + } else { + infoBar->removeInfo(derivedObjectWarning); + } +} + +FileStatus::Status ClearCasePlugin::getFileStatus(const QString &fileName) const +{ + QTC_CHECK(!fileName.isEmpty()); + + const QDir viewRootDir = QFileInfo(fileName).dir(); + const QString viewRoot = viewRootDir.path(); + + QStringList args(QLatin1String("ls")); + args << fileName; + QString buffer = runCleartoolSync(viewRoot, args); + + const int atatpos = buffer.indexOf(QLatin1String("@@")); + if (atatpos != -1) { // probably a managed file + const QString absFile = + viewRootDir.absoluteFilePath( + QDir::fromNativeSeparators(buffer.left(atatpos))); + QTC_CHECK(QFile(absFile).exists()); + QTC_CHECK(!absFile.isEmpty()); + + // "cleartool ls" of a derived object looks like this: + // /path/to/file/export/MyFile.h@@--11-13T19:52.266580 + const QChar c = buffer.at(atatpos + 2); + const bool isDerivedObject = c != QLatin1Char('/') && c != QLatin1Char('\\'); + if (isDerivedObject) + return FileStatus::Derived; + + // find first whitespace. anything before that is not interesting + const int wspos = buffer.indexOf(QRegExp(QLatin1String("\\s"))); + if (buffer.lastIndexOf(QLatin1String("CHECKEDOUT"), wspos) != -1) + return FileStatus::CheckedOut; + else + return FileStatus::CheckedIn; + } else { + QTC_CHECK(QFile(fileName).exists()); + QTC_CHECK(!fileName.isEmpty()); + return FileStatus::NotManaged; + } +} + /// /// Check if the directory is managed by ClearCase. /// @@ -483,12 +557,14 @@ bool ClearCasePlugin::initialize(const QStringList & /*arguments */, QString *er clearcaseMenu->addSeparator(globalcontext); m_diffActivityAction = new QAction(tr("Diff A&ctivity..."), this); + m_diffActivityAction->setEnabled(false); command = ActionManager::registerAction(m_diffActivityAction, CMD_ID_DIFF_ACTIVITY, globalcontext); connect(m_diffActivityAction, SIGNAL(triggered()), this, SLOT(diffActivity())); clearcaseMenu->addAction(command); m_commandLocator->appendCommand(command); m_checkInActivityAction = new Utils::ParameterAction(tr("Ch&eck In Activity"), tr("Chec&k In Activity \"%1\"..."), Utils::ParameterAction::EnabledWithParameter, this); + m_checkInActivityAction->setEnabled(false); command = ActionManager::registerAction(m_checkInActivityAction, CMD_ID_CHECKIN_ACTIVITY, globalcontext); connect(m_checkInActivityAction, SIGNAL(triggered()), this, SLOT(startCheckInActivity())); command->setAttribute(Command::CA_UpdateText); @@ -671,7 +747,16 @@ QStringList ClearCasePlugin::ccGetActiveVobs() const return res; } -// file must be relative to topLevel, and using '/' path separator +void ClearCasePlugin::checkAndReIndexUnknownFile(const QString &file) +{ + if (isDynamic()) { + // reindex unknown files + if (m_statusMap->value(file, FileStatus(FileStatus::Unknown)).status == FileStatus::Unknown) + updateStatusForFile(file); + } +} + +// file must be absolute, and using '/' path separator FileStatus ClearCasePlugin::vcsStatus(const QString &file) const { return m_statusMap->value(file, FileStatus(FileStatus::Unknown)); @@ -700,16 +785,41 @@ ClearCaseSubmitEditor *ClearCasePlugin::openClearCaseSubmitEditor(const QString return submitEditor; } +QString fileStatusToText(FileStatus fileStatus) +{ + switch (fileStatus.status) + { + case FileStatus::CheckedIn: + return QLatin1String("CheckedIn"); + case FileStatus::CheckedOut: + return QLatin1String("CheckedOut"); + case FileStatus::Hijacked: + return QLatin1String("Hijacked"); + case FileStatus::Missing: + return QLatin1String("Missing"); + case FileStatus::NotManaged: + return QLatin1String("ViewPrivate"); + case FileStatus::Unknown: + return QLatin1String("Unknown"); + default: + return QLatin1String("default"); + } +} + void ClearCasePlugin::updateStatusActions() { FileStatus fileStatus = FileStatus::Unknown; bool hasFile = currentState().hasFile(); if (hasFile) { QString absoluteFileName = currentState().currentFile(); - fileStatus = m_statusMap->value(absoluteFileName, FileStatus(FileStatus::Unknown)); + checkAndReIndexUnknownFile(absoluteFileName); + fileStatus = vcsStatus(absoluteFileName); + + updateEditDerivedObjectWarning(absoluteFileName, fileStatus.status); if (Constants::debug) - qDebug() << Q_FUNC_INFO << absoluteFileName << ", status = " << fileStatus.status; + qDebug() << Q_FUNC_INFO << absoluteFileName << ", status = " + << fileStatusToText(fileStatus.status) << "(" << fileStatus.status << ")"; } m_checkOutAction->setEnabled(hasFile && (fileStatus.status & (FileStatus::CheckedIn | FileStatus::Hijacked))); @@ -717,6 +827,9 @@ void ClearCasePlugin::updateStatusActions() m_undoHijackAction->setEnabled(!m_viewData.isDynamic && hasFile && (fileStatus.status & FileStatus::Hijacked)); m_checkInCurrentAction->setEnabled(hasFile && (fileStatus.status & FileStatus::CheckedOut)); m_addFileAction->setEnabled(hasFile && (fileStatus.status & FileStatus::NotManaged)); + m_diffCurrentAction->setEnabled(hasFile && (fileStatus.status != FileStatus::NotManaged)); + m_historyCurrentAction->setEnabled(hasFile && (fileStatus.status != FileStatus::NotManaged)); + m_annotateCurrentAction->setEnabled(hasFile && (fileStatus.status != FileStatus::NotManaged)); m_checkInActivityAction->setEnabled(m_viewData.isUcm); m_diffActivityAction->setEnabled(m_viewData.isUcm); @@ -746,6 +859,7 @@ void ClearCasePlugin::updateActions(VcsBase::VcsBasePlugin::ActionState as) m_annotateCurrentAction->setParameter(fileName); m_addFileAction->setParameter(fileName); m_updateIndexAction->setEnabled(!m_settings.disableIndexer); + updateStatusActions(); } @@ -766,6 +880,7 @@ void ClearCasePlugin::addCurrentFile() // Set the FileStatus of file given in absolute path void ClearCasePlugin::setStatus(const QString &file, FileStatus::Status status, bool update) { + QTC_CHECK(!file.isEmpty()); m_statusMap->insert(file, FileStatus(status, QFileInfo(file).permissions())); if (update && currentState().currentFile() == file) @@ -904,7 +1019,7 @@ void ClearCasePlugin::ccDiffWithPred(const QString &workingDir, const QStringLis if ((m_settings.diffType == GraphicalDiff) && (files.count() == 1)) { const QString file = files.first(); const QString absFilePath = workingDir + QLatin1Char('/') + file; - if (m_statusMap->value(absFilePath).status == FileStatus::Hijacked) + if (vcsStatus(absFilePath).status == FileStatus::Hijacked) diffGraphical(ccGetFileVersion(workingDir, file), file); else diffGraphical(file); @@ -918,7 +1033,7 @@ void ClearCasePlugin::ccDiffWithPred(const QString &workingDir, const QStringLis QString result; foreach (const QString &file, files) { const QString absFilePath = workingDir + QLatin1Char('/') + file; - if (m_statusMap->value(QDir::fromNativeSeparators(absFilePath)).status == FileStatus::Hijacked) + if (vcsStatus(QDir::fromNativeSeparators(absFilePath)).status == FileStatus::Hijacked) result += diffExternal(ccGetFileVersion(workingDir, file), file); else result += diffExternal(file); @@ -1210,7 +1325,8 @@ void ClearCasePlugin::viewStatus() m_viewData = ccGetView(m_topLevel); QTC_ASSERT(!m_viewData.name.isEmpty() && !m_settings.disableIndexer, return); VcsBase::VcsBaseOutputWindow *outputwindow = VcsBase::VcsBaseOutputWindow::instance(); - outputwindow->appendCommand(QLatin1String("Indexed files status (C=Checked Out, H=Hijacked, ?=Missing)")); + outputwindow->appendCommand(QLatin1String("Indexed files status (C=Checked Out, " + "H=Hijacked, ?=Missing)")); bool anymod = false; for (StatusMap::ConstIterator it = m_statusMap->constBegin(); it != m_statusMap->constEnd(); @@ -1414,9 +1530,8 @@ IEditor *ClearCasePlugin::showOutputInEditor(const QString& title, const QString e->setSource(source); if (codec) e->setCodec(codec); - IEditor *ie = e->editor(); - EditorManager::activateEditor(ie); - return ie; + EditorManager::activateEditor(editor); + return editor; } const ClearCaseSettings &ClearCasePlugin::settings() const @@ -1456,14 +1571,14 @@ bool ClearCasePlugin::vcsOpen(const QString &workingDir, const QString &fileName CheckOutDialog coDialog(title, m_viewData.isUcm); if (!m_settings.disableIndexer && - (fi.isWritable() || m_statusMap->value(absPath).status == FileStatus::Unknown)) + (fi.isWritable() || vcsStatus(absPath).status == FileStatus::Unknown)) QtConcurrent::run(&sync, QStringList(absPath)).waitForFinished(); - if (m_statusMap->value(absPath).status == FileStatus::CheckedOut) { + if (vcsStatus(absPath).status == FileStatus::CheckedOut) { QMessageBox::information(0, tr("ClearCase Checkout"), tr("File is already checked out.")); return true; } // Only snapshot views can have hijacked files - bool isHijacked = (!m_viewData.isDynamic && (m_statusMap->value(absPath).status & FileStatus::Hijacked)); + bool isHijacked = (!m_viewData.isDynamic && (vcsStatus(absPath).status & FileStatus::Hijacked)); if (!isHijacked) coDialog.hideHijack(); if (coDialog.exec() == QDialog::Accepted) { @@ -1735,7 +1850,13 @@ QString ClearCasePlugin::vcsGetRepositoryURL(const QString & /*directory*/) /// bool ClearCasePlugin::managesDirectory(const QString &directory, QString *topLevel /* = 0 */) const { +#ifdef WITH_TESTS + // If running with tests and fake ClearTool is enabled, then pretend we manage every directory + QString topLevelFound = m_fakeClearTool ? directory : findTopLevel(directory); +#else QString topLevelFound = findTopLevel(directory); +#endif + if (topLevel) *topLevel = topLevelFound; return !topLevelFound.isEmpty(); @@ -1852,9 +1973,9 @@ bool ClearCasePlugin::ccCheckUcm(const QString &viewname, const QString &working bool ClearCasePlugin::managesFile(const QString &workingDirectory, const QString &fileName) const { - QStringList args; - args << QLatin1String("ls") << fileName; - return runCleartoolSync(workingDirectory, args).contains(QLatin1String("@@")); + QString absFile = QFileInfo(QDir(workingDirectory), fileName).absoluteFilePath(); + const FileStatus::Status status = getFileStatus(absFile); + return status != FileStatus::NotManaged && status != FileStatus::Derived; } ViewData ClearCasePlugin::ccGetView(const QString &workingDir) const @@ -2117,6 +2238,233 @@ void ClearCasePlugin::testLogResolving() "src/plugins/clearcase/clearcaseeditor.h@@/main/branch1/branch2/9", "src/plugins/clearcase/clearcaseeditor.h@@/main/branch1/branch2/8"); } + +void ClearCasePlugin::initTestCase() +{ + m_tempFile = QDir::currentPath() + QLatin1String("/cc_file.cpp"); + Utils::FileSaver srcSaver(m_tempFile); + srcSaver.write(QByteArray()); + srcSaver.finalize(); +} + +void ClearCasePlugin::cleanupTestCase() +{ + QVERIFY(QFile::remove(m_tempFile)); +} + +void ClearCasePlugin::testFileStatusParsing_data() +{ + QTest::addColumn<QString>("filename"); + QTest::addColumn<QString>("cleartoolLsLine"); + QTest::addColumn<int>("status"); + + QTest::newRow("CheckedOut") + << m_tempFile + << QString(m_tempFile + QLatin1String("@@/main/branch1/CHECKEDOUT from /main/branch1/0 Rule: CHECKEDOUT")) + << static_cast<int>(FileStatus::CheckedOut); + + QTest::newRow("CheckedIn") + << m_tempFile + << QString(m_tempFile + QLatin1String("@@/main/9 Rule: MY_LABEL_1.6.4 [-mkbranch branch1]")) + << static_cast<int>(FileStatus::CheckedIn); + + QTest::newRow("Hijacked") + << m_tempFile + << QString(m_tempFile + QLatin1String("@@/main/9 [hijacked] Rule: MY_LABEL_1.5.33 [-mkbranch myview1]")) + << static_cast<int>(FileStatus::Hijacked); + + + QTest::newRow("Missing") + << m_tempFile + << QString(m_tempFile + QLatin1String("@@/main/9 [loaded but missing] Rule: MY_LABEL_1.5.33 [-mkbranch myview1]")) + << static_cast<int>(FileStatus::Missing); +} + +void ClearCasePlugin::testFileStatusParsing() +{ + ClearCasePlugin *plugin = ClearCasePlugin::instance(); + plugin->m_statusMap = QSharedPointer<StatusMap>(new StatusMap); + + QFETCH(QString, filename); + QFETCH(QString, cleartoolLsLine); + QFETCH(int, status); + + ClearCaseSync ccSync(plugin, plugin->m_statusMap); + ccSync.verifyParseStatus(filename, cleartoolLsLine, static_cast<FileStatus::Status>(status)); +} + +void ClearCasePlugin::testFileNotManaged() +{ + ClearCasePlugin *plugin = ClearCasePlugin::instance(); + plugin->m_statusMap = QSharedPointer<StatusMap>(new StatusMap); + ClearCaseSync ccSync(plugin, plugin->m_statusMap); + ccSync.verifyFileNotManaged(); +} + +void ClearCasePlugin::testFileCheckedOutDynamicView() +{ + ClearCasePlugin *plugin = ClearCasePlugin::instance(); + plugin->m_statusMap = QSharedPointer<StatusMap>(new StatusMap); + + ClearCaseSync ccSync(plugin, plugin->m_statusMap); + ccSync.verifyFileCheckedOutDynamicView(); +} + +void ClearCasePlugin::testFileCheckedInDynamicView() +{ + ClearCasePlugin *plugin = ClearCasePlugin::instance(); + plugin->m_statusMap = QSharedPointer<StatusMap>(new StatusMap); + ClearCaseSync ccSync(plugin, plugin->m_statusMap); + ccSync.verifyFileCheckedInDynamicView(); +} + +void ClearCasePlugin::testFileNotManagedDynamicView() +{ + ClearCasePlugin *plugin = ClearCasePlugin::instance(); + plugin->m_statusMap = QSharedPointer<StatusMap>(new StatusMap); + ClearCaseSync ccSync(plugin, plugin->m_statusMap); + ccSync.verifyFileNotManagedDynamicView(); +} + +namespace { +/** + * @brief Convenience class which also properly cleans up editors and temp files + */ +class TestCase +{ +public: + TestCase(const QString &fileName) : + m_fileName(fileName) , + m_editor(0) + { + ClearCasePlugin::instance()->setFakeCleartool(true); + Utils::FileSaver srcSaver(fileName); + srcSaver.write(QByteArray()); + srcSaver.finalize(); + + m_editor = Core::EditorManager::openEditor(fileName); + + QCoreApplication::processEvents(); // process any pending events + } + + ViewData dummyViewData() const + { + ViewData viewData; + viewData.name = QLatin1String("fake_view"); + viewData.root = QDir::currentPath(); + viewData.isUcm = false; + return viewData; + } + + ~TestCase() + { + Core::EditorManager::closeEditor(m_editor, false); + QCoreApplication::processEvents(); // process any pending events + + QFile file(m_fileName); + if (!file.isWritable()) // Windows can't delete read only files + file.setPermissions(file.permissions() | QFile::WriteUser); + QVERIFY(file.remove()); + ClearCasePlugin::instance()->setFakeCleartool(false); + } + +private: + QString m_fileName; + Core::IEditor *m_editor; +}; +} + +void ClearCasePlugin::testStatusActions_data() +{ + QTest::addColumn<int>("status"); + QTest::addColumn<bool>("checkOutAction"); + QTest::addColumn<bool>("undoCheckOutAction"); + QTest::addColumn<bool>("undoHijackAction"); + QTest::addColumn<bool>("checkInCurrentAction"); + QTest::addColumn<bool>("addFileAction"); + QTest::addColumn<bool>("checkInActivityAction"); + QTest::addColumn<bool>("diffActivityAction"); + + QTest::newRow("Unknown") << static_cast<int>(FileStatus::Unknown) + << true << true << true << true << true << false << false; + QTest::newRow("CheckedOut") << static_cast<int>(FileStatus::CheckedOut) + << false << true << false << true << false << false << false; + QTest::newRow("CheckedIn") << static_cast<int>(FileStatus::CheckedIn) + << true << false << false << false << false << false << false; + QTest::newRow("NotManaged") << static_cast<int>(FileStatus::NotManaged) + << false << false << false << false << true << false << false; +} + +void ClearCasePlugin::testStatusActions() +{ + const QString fileName = QDir::currentPath() + QLatin1String("/clearcase_file.cpp"); + TestCase testCase(fileName); + + m_viewData = testCase.dummyViewData(); + + QFETCH(int, status); + FileStatus::Status tempStatus = static_cast<FileStatus::Status>(status); + + // special case: file should appear as "Unknown" since there is no entry in the index + // and we don't want to explicitly set the status for this test case + if (tempStatus != FileStatus::Unknown) + setStatus(fileName, tempStatus, true); + + QFETCH(bool, checkOutAction); + QFETCH(bool, undoCheckOutAction); + QFETCH(bool, undoHijackAction); + QFETCH(bool, checkInCurrentAction); + QFETCH(bool, addFileAction); + QFETCH(bool, checkInActivityAction); + QFETCH(bool, diffActivityAction); + + QCOMPARE(m_checkOutAction->isEnabled(), checkOutAction); + QCOMPARE(m_undoCheckOutAction->isEnabled(), undoCheckOutAction); + QCOMPARE(m_undoHijackAction->isEnabled(), undoHijackAction); + QCOMPARE(m_checkInCurrentAction->isEnabled(), checkInCurrentAction); + QCOMPARE(m_addFileAction->isEnabled(), addFileAction); + QCOMPARE(m_checkInActivityAction->isEnabled(), checkInActivityAction); + QCOMPARE(m_diffActivityAction->isEnabled(), diffActivityAction); +} + +void ClearCasePlugin::testVcsStatusDynamicReadonlyNotManaged() +{ + // File is not in map, and is read-only + ClearCasePlugin::instance(); + m_statusMap = QSharedPointer<StatusMap>(new StatusMap); + + const QString fileName = QDir::currentPath() + QLatin1String("/readonly_notmanaged_file.cpp"); + + m_viewData.isDynamic = true; + TestCase testCase(fileName); + + QFile::setPermissions(fileName, QFile::ReadOwner | + QFile::ReadUser | + QFile::ReadGroup | + QFile::ReadOther); + + m_viewData = testCase.dummyViewData(); + m_viewData.isDynamic = true; + + QCOMPARE(vcsStatus(fileName).status, FileStatus::NotManaged); + +} + +void ClearCasePlugin::testVcsStatusDynamicNotManaged() +{ + ClearCasePlugin::instance(); + m_statusMap = QSharedPointer<StatusMap>(new StatusMap); + + const QString fileName = QDir::currentPath() + QLatin1String("/notmanaged_file.cpp"); + + m_viewData.isDynamic = true; + TestCase testCase(fileName); + + m_viewData = testCase.dummyViewData(); + m_viewData.isDynamic = true; + + QCOMPARE(vcsStatus(fileName).status, FileStatus::NotManaged); +} #endif } // namespace Internal diff --git a/src/plugins/clearcase/clearcaseplugin.h b/src/plugins/clearcase/clearcaseplugin.h index c6588db05e..3dcd4be5ea 100644 --- a/src/plugins/clearcase/clearcaseplugin.h +++ b/src/plugins/clearcase/clearcaseplugin.h @@ -89,7 +89,8 @@ public: CheckedOut = 0x02, Hijacked = 0x04, NotManaged = 0x08, - Missing = 0x10 + Missing = 0x10, + Derived = 0x20 } status; QFile::Permissions permissions; @@ -158,6 +159,7 @@ public: bool ccFileOp(const QString &workingDir, const QString &title, const QStringList &args, const QString &fileName, const QString &file2 = QString()); FileStatus vcsStatus(const QString &file) const; + void checkAndReIndexUnknownFile(const QString &file); QString currentView() const { return m_viewData.name; } QString viewRoot() const { return m_viewData.root; } void refreshActivities(); @@ -167,6 +169,10 @@ public: bool ccCheckUcm(const QString &viewname, const QString &workingDir) const; bool managesFile(const QString &workingDirectory, const QString &fileName) const; +#ifdef WITH_TESTS + inline void setFakeCleartool(const bool b = true) { m_fakeClearTool = b; } + inline bool isFakeCleartool() const { return m_fakeClearTool; } +#endif public slots: void vcsAnnotate(const QString &workingDir, const QString &file, @@ -199,9 +205,21 @@ private slots: void closing(); void updateStatusActions(); #ifdef WITH_TESTS + void initTestCase(); + void cleanupTestCase(); void testDiffFileResolving_data(); void testDiffFileResolving(); void testLogResolving(); + void testFileStatusParsing_data(); + void testFileStatusParsing(); + void testFileNotManaged(); + void testFileCheckedOutDynamicView(); + void testFileCheckedInDynamicView(); + void testFileNotManagedDynamicView(); + void testStatusActions_data(); + void testStatusActions(); + void testVcsStatusDynamicReadonlyNotManaged(); + void testVcsStatusDynamicNotManaged(); #endif protected: @@ -244,6 +262,10 @@ private: int timeOut, QTextCodec *outputCodec = 0); static QString getDriveLetterOfPath(const QString &directory); + FileStatus::Status getFileStatus(const QString &fileName) const; + void updateStatusForFile(const QString &absFile); + void updateEditDerivedObjectWarning(const QString &fileName, const FileStatus::Status status); + ClearCaseSettings m_settings; QString m_checkInMessageFileName; @@ -282,6 +304,10 @@ private: QSharedPointer<StatusMap> m_statusMap; static ClearCasePlugin *m_clearcasePluginInstance; +#ifdef WITH_TESTS + bool m_fakeClearTool; + QString m_tempFile; +#endif }; } // namespace Internal diff --git a/src/plugins/clearcase/clearcasesync.cpp b/src/plugins/clearcase/clearcasesync.cpp index 2560672ea9..d34f20e19b 100644 --- a/src/plugins/clearcase/clearcasesync.cpp +++ b/src/plugins/clearcase/clearcasesync.cpp @@ -34,6 +34,12 @@ #include <QFutureInterface> #include <QProcess> #include <QStringList> +#include <utils/qtcassert.h> + +#ifdef WITH_TESTS +#include <QTest> +#include <utils/fileutils.h> +#endif namespace ClearCase { namespace Internal { @@ -44,60 +50,108 @@ ClearCaseSync::ClearCaseSync(ClearCasePlugin *plugin, QSharedPointer<StatusMap> { } -void ClearCaseSync::run(QFutureInterface<void> &future, QStringList &files) +QStringList ClearCaseSync::updateStatusHotFiles(const QString &viewRoot, int &total) { - ClearCaseSettings settings = m_plugin->settings(); - const QString program = settings.ccBinaryPath; - if (program.isEmpty()) - return; - int total = files.size(); - const bool hot = (total < 10); - int processed = 0; - QString view = m_plugin->currentView(); - if (view.isEmpty()) - emit updateStreamAndView(); - if (!hot) - total = settings.totalFiles.value(view, total); + QStringList hotFiles; + // find all files whose permissions changed OR hijacked files + // (might have become checked out) + const StatusMap::Iterator send = m_statusMap->end(); + for (StatusMap::Iterator it = m_statusMap->begin(); it != send; ++it) { + const QFileInfo fi(viewRoot, it.key()); + const bool permChanged = it.value().permissions != fi.permissions(); + if (permChanged || it.value().status == FileStatus::Hijacked) { + hotFiles.append(it.key()); + it.value().status = FileStatus::Unknown; + ++total; + } + } + return hotFiles; +} - // refresh activities list - if (m_plugin->isUcm()) - m_plugin->refreshActivities(); +// Set status for all files to unknown until we're done indexing +void ClearCaseSync::invalidateStatus(const QDir &viewRootDir, + const QStringList &files) +{ + foreach (const QString &file, files) { + m_plugin->setStatus(viewRootDir.absoluteFilePath(file), FileStatus::Unknown, false); + } +} - if (settings.disableIndexer) +void ClearCaseSync::invalidateStatusAllFiles() +{ + const StatusMap::ConstIterator send = m_statusMap->end(); + for (StatusMap::ConstIterator it = m_statusMap->begin(); it != send; ++it) + m_plugin->setStatus(it.key(), FileStatus::Unknown, false); +} + +void ClearCaseSync::processCleartoolLsLine(const QDir &viewRootDir, const QString &buffer) +{ + const int atatpos = buffer.indexOf(QLatin1String("@@")); + + if (atatpos == -1) return; - const bool isDynamic = m_plugin->isDynamic(); + // find first whitespace. anything before that is not interesting + const int wspos = buffer.indexOf(QRegExp(QLatin1String("\\s"))); + const QString absFile = + viewRootDir.absoluteFilePath( + QDir::fromNativeSeparators(buffer.left(atatpos))); + QTC_CHECK(QFile(absFile).exists()); + QTC_CHECK(!absFile.isEmpty()); + + QString ccState; + const QRegExp reState(QLatin1String("^\\s*\\[[^\\]]*\\]")); // [hijacked]; [loaded but missing] + if (reState.indexIn(buffer, wspos + 1, QRegExp::CaretAtOffset) != -1) { + ccState = reState.cap(); + if (ccState.indexOf(QLatin1String("hijacked")) != -1) + m_plugin->setStatus(absFile, FileStatus::Hijacked, true); + else if (ccState.indexOf(QLatin1String("loaded but missing")) != -1) + m_plugin->setStatus(absFile, FileStatus::Missing, false); + } + else if (buffer.lastIndexOf(QLatin1String("CHECKEDOUT"), wspos) != -1) + m_plugin->setStatus(absFile, FileStatus::CheckedOut, true); + // don't care about checked-in files not listed in project + else if (m_statusMap->contains(absFile)) + m_plugin->setStatus(absFile, FileStatus::CheckedIn, true); +} + +void ClearCaseSync::updateTotalFilesCount(const QString view, ClearCaseSettings settings, + const int processed) +{ + settings = m_plugin->settings(); // Might have changed while task was running + settings.totalFiles[view] = processed; + m_plugin->setSettings(settings); +} + +void ClearCaseSync::updateStatusForNotManagedFiles(const QStringList &files) +{ + foreach (const QString &file, files) { + QString absFile = QFileInfo(file).absoluteFilePath(); + if (!m_statusMap->contains(absFile)) + m_plugin->setStatus(absFile, FileStatus::NotManaged, false); + } +} + +void ClearCaseSync::syncSnapshotView(QFutureInterface<void> &future, QStringList &files, + const ClearCaseSettings &settings) +{ + QString view = m_plugin->currentView(); + + int totalFileCount = files.size(); + const bool hot = (totalFileCount < 10); + int processed = 0; + if (!hot) + totalFileCount = settings.totalFiles.value(view, totalFileCount); + const QString viewRoot = m_plugin->viewRoot(); const QDir viewRootDir(viewRoot); QStringList args(QLatin1String("ls")); if (hot) { - // find all files whose permissions changed OR hijacked files - // (might have become checked out) - const StatusMap::Iterator send = m_statusMap->end(); - for (StatusMap::Iterator it = m_statusMap->begin(); it != send; ++it) { - const QFileInfo fi(viewRoot, it.key()); - const bool permChanged = it.value().permissions != fi.permissions(); - if (permChanged || it.value().status == FileStatus::Hijacked) { - files.append(it.key()); - it.value().status = FileStatus::Unknown; - ++total; - } else if (isDynamic && !fi.isWritable()) { // assume a read only file is checked in - it.value().status = FileStatus::CheckedIn; - ++total; - } - } + files << updateStatusHotFiles(viewRoot, totalFileCount); args << files; } else { - foreach (const QString &file, files) { - if (isDynamic) { // assume a read only file is checked in - const QFileInfo fi(viewRootDir, file); - if (!fi.isWritable()) - m_plugin->setStatus(fi.absoluteFilePath(), FileStatus::CheckedIn, false); - } else { - m_plugin->setStatus(viewRootDir.absoluteFilePath(file), FileStatus::Unknown, false); - } - } + invalidateStatus(viewRootDir, files); args << QLatin1String("-recurse"); QStringList vobs; @@ -111,10 +165,12 @@ void ClearCaseSync::run(QFutureInterface<void> &future, QStringList &files) // adding 1 for initial sync in which total is not accurate, to prevent finishing // (we don't want it to become green) - future.setProgressRange(0, total + 1); + future.setProgressRange(0, totalFileCount + 1); QProcess process; process.setWorkingDirectory(viewRoot); + const QString program = settings.ccBinaryPath; + process.start(program, args); if (!process.waitForStarted()) return; @@ -124,55 +180,208 @@ void ClearCaseSync::run(QFutureInterface<void> &future, QStringList &files) process.bytesAvailable() && !future.isCanceled()) { const QString line = QString::fromLocal8Bit(process.readLine().constData()); - buffer += line; if (buffer.endsWith(QLatin1Char('\n')) || process.atEnd()) { - const int atatpos = buffer.indexOf(QLatin1String("@@")); - if (atatpos != -1) { // probably managed file - // find first whitespace. anything before that is not interesting - const int wspos = buffer.indexOf(QRegExp(QLatin1String("\\s"))); - const QString absFile = - viewRootDir.absoluteFilePath( - QDir::fromNativeSeparators(buffer.left(atatpos))); - - QString ccState; - const QRegExp reState(QLatin1String("^\\s*\\[[^\\]]*\\]")); // [hijacked]; [loaded but missing] - if (reState.indexIn(buffer, wspos + 1, QRegExp::CaretAtOffset) != -1) { - ccState = reState.cap(); - if (ccState.indexOf(QLatin1String("hijacked")) != -1) - m_plugin->setStatus(absFile, FileStatus::Hijacked, true); - else if (ccState.indexOf(QLatin1String("loaded but missing")) != -1) - m_plugin->setStatus(absFile, FileStatus::Missing, false); - } - else if (buffer.lastIndexOf(QLatin1String("CHECKEDOUT"), wspos) != -1) - m_plugin->setStatus(absFile, FileStatus::CheckedOut, true); - // don't care about checked-in files not listed in project - else if (m_statusMap->contains(absFile)) - m_plugin->setStatus(absFile, FileStatus::CheckedIn, true); - } + processCleartoolLsLine(viewRootDir, buffer); buffer.clear(); - future.setProgressValue(qMin(total, ++processed)); + future.setProgressValue(qMin(totalFileCount, ++processed)); } } } if (!future.isCanceled()) { - foreach (const QString &file, files) { - QString absFile = QFileInfo(file).absoluteFilePath(); - if (!m_statusMap->contains(absFile)) - m_plugin->setStatus(absFile, FileStatus::NotManaged, false); - } - future.setProgressValue(total + 1); - if (!hot) { - settings = m_plugin->settings(); // Might have changed while task was running - settings.totalFiles[view] = processed; - m_plugin->setSettings(settings); + updateStatusForNotManagedFiles(files); + future.setProgressValue(totalFileCount + 1); + if (!hot) + updateTotalFilesCount(view, settings, processed); + } + + if (process.state() == QProcess::Running) + process.kill(); + + process.waitForFinished(); +} + +void ClearCaseSync::processCleartoolLscheckoutLine(const QString &buffer) +{ + QString absFile = buffer.trimmed(); + m_plugin->setStatus(absFile, FileStatus::CheckedOut, true); +} + +/// +/// Update the file status for dynamic views. +/// +void ClearCaseSync::syncDynamicView(QFutureInterface<void> &future, + const ClearCaseSettings& settings) +{ + // Always invalidate status for all files + invalidateStatusAllFiles(); + + QStringList args(QLatin1String("lscheckout")); + args << QLatin1String("-avobs") + << QLatin1String("-me") + << QLatin1String("-cview") + << QLatin1String("-s"); + + const QString viewRoot = m_plugin->viewRoot(); + + QProcess process; + process.setWorkingDirectory(viewRoot); + + const QString program = settings.ccBinaryPath; + process.start(program, args); + if (!process.waitForStarted()) + return; + + QString buffer; + int processed = 0; + while (process.waitForReadyRead() && !future.isCanceled()) { + while (process.state() == QProcess::Running && + process.bytesAvailable() && !future.isCanceled()) { + const QString line = QString::fromLocal8Bit(process.readLine().constData()); + buffer += line; + if (buffer.endsWith(QLatin1Char('\n')) || process.atEnd()) { + processCleartoolLscheckoutLine(buffer); + buffer.clear(); + future.setProgressValue(++processed); + } } } + if (process.state() == QProcess::Running) process.kill(); + process.waitForFinished(); } +void ClearCaseSync::run(QFutureInterface<void> &future, QStringList &files) +{ + ClearCaseSettings settings = m_plugin->settings(); + if (settings.disableIndexer) + return; + + const QString program = settings.ccBinaryPath; + if (program.isEmpty()) + return; + + // refresh activities list + if (m_plugin->isUcm()) + m_plugin->refreshActivities(); + + QString view = m_plugin->currentView(); + if (view.isEmpty()) + emit updateStreamAndView(); + + if (m_plugin->isDynamic()) + syncDynamicView(future, settings); + else + syncSnapshotView(future, files, settings); +} + +#ifdef WITH_TESTS +namespace { +class TempFile +{ +public: + TempFile(const QString &fileName) + : m_fileName(fileName) + { + Utils::FileSaver srcSaver(fileName); + srcSaver.write(QByteArray()); + srcSaver.finalize(); + + } + + QString fileName() const { return m_fileName; } + + ~TempFile() + { + QVERIFY(QFile::remove(m_fileName)); + } + +private: + const QString m_fileName; +}; +} + +void ClearCaseSync::verifyParseStatus(const QString &fileName, + const QString &cleartoolLsLine, + const FileStatus::Status status) +{ + QCOMPARE(m_statusMap->count(), 0); + processCleartoolLsLine(QDir(QLatin1String("/")), cleartoolLsLine); + + if (status == FileStatus::CheckedIn) { + // The algorithm doesn't store checked in files in the index, unless it was there already + QCOMPARE(m_statusMap->count(), 0); + QCOMPARE(m_statusMap->contains(fileName), false); + m_plugin->setStatus(fileName, FileStatus::Unknown, false); + processCleartoolLsLine(QDir(QLatin1String("/")), cleartoolLsLine); + } + + QCOMPARE(m_statusMap->count(), 1); + QCOMPARE(m_statusMap->contains(fileName), true); + QCOMPARE(m_statusMap->value(fileName).status, status); + + QCOMPARE(m_statusMap->contains(QLatin1String(("notexisting"))), false); +} + +void ClearCaseSync::verifyFileNotManaged() +{ + QCOMPARE(m_statusMap->count(), 0); + TempFile temp(QDir::currentPath() + QLatin1String("/notmanaged.cpp")); + const QString fileName = temp.fileName(); + + updateStatusForNotManagedFiles(QStringList(fileName)); + + QCOMPARE(m_statusMap->count(), 1); + + QCOMPARE(m_statusMap->contains(fileName), true); + QCOMPARE(m_statusMap->value(fileName).status, FileStatus::NotManaged); +} + +void ClearCaseSync::verifyFileCheckedOutDynamicView() +{ + QCOMPARE(m_statusMap->count(), 0); + + QString fileName(QLatin1String("/hello.C")); + processCleartoolLscheckoutLine(fileName); + + QCOMPARE(m_statusMap->count(), 1); + + QVERIFY(m_statusMap->contains(fileName)); + QCOMPARE(m_statusMap->value(fileName).status, FileStatus::CheckedOut); + + QVERIFY(!m_statusMap->contains(QLatin1String(("notexisting")))); +} + +void ClearCaseSync::verifyFileCheckedInDynamicView() +{ + QCOMPARE(m_statusMap->count(), 0); + + QString fileName(QLatin1String("/hello.C")); + + // checked in files are not kept in the index + QCOMPARE(m_statusMap->count(), 0); + QCOMPARE(m_statusMap->contains(fileName), false); +} + +void ClearCaseSync::verifyFileNotManagedDynamicView() +{ + QCOMPARE(m_statusMap->count(), 0); + TempFile temp(QDir::currentPath() + QLatin1String("/notmanaged.cpp")); + const QString fileName = temp.fileName(); + + updateStatusForNotManagedFiles(QStringList(fileName)); + + QCOMPARE(m_statusMap->count(), 1); + + QVERIFY(m_statusMap->contains(fileName)); + QCOMPARE(m_statusMap->value(fileName).status, FileStatus::NotManaged); +} + +#endif + + } // namespace Internal } // namespace ClearCase diff --git a/src/plugins/clearcase/clearcasesync.h b/src/plugins/clearcase/clearcasesync.h index b2e0250b92..6b0279029e 100644 --- a/src/plugins/clearcase/clearcasesync.h +++ b/src/plugins/clearcase/clearcasesync.h @@ -42,12 +42,38 @@ public: explicit ClearCaseSync(ClearCasePlugin *plugin, QSharedPointer<StatusMap> statusMap); void run(QFutureInterface<void> &future, QStringList &files); + QStringList updateStatusHotFiles(const QString &viewRoot, int &total); + void invalidateStatus(const QDir &viewRootDir, const QStringList &files); + void invalidateStatusAllFiles(); + void processCleartoolLsLine(const QDir &viewRootDir, const QString &buffer); + void updateTotalFilesCount(const QString view, ClearCaseSettings settings, const int processed); + void updateStatusForNotManagedFiles(const QStringList &files); + + void syncDynamicView(QFutureInterface<void> &future, + const ClearCaseSettings &settings); + void syncSnapshotView(QFutureInterface<void> &future, QStringList &files, + const ClearCaseSettings &settings); + + void processCleartoolLscheckoutLine(const QString &buffer); signals: void updateStreamAndView(); private: ClearCasePlugin *m_plugin; QSharedPointer<StatusMap> m_statusMap; + +public slots: +#ifdef WITH_TESTS + void verifyParseStatus(const QString &fileName, const QString &cleartoolLsLine, + const FileStatus::Status); + void verifyFileNotManaged(); + + void verifyFileCheckedOutDynamicView(); + void verifyFileCheckedInDynamicView(); + void verifyFileNotManagedDynamicView(); + +#endif + }; } // namespace Internal diff --git a/src/plugins/clearcase/settingspage.cpp b/src/plugins/clearcase/settingspage.cpp index cf646ad6dc..9433648f0e 100644 --- a/src/plugins/clearcase/settingspage.cpp +++ b/src/plugins/clearcase/settingspage.cpp @@ -52,6 +52,7 @@ SettingsPageWidget::SettingsPageWidget(QWidget *parent) : m_ui.setupUi(this); m_ui.commandPathChooser->setPromptDialogTitle(tr("ClearCase Command")); m_ui.commandPathChooser->setExpectedKind(PathChooser::ExistingCommand); + m_ui.commandPathChooser->setHistoryCompleter(QLatin1String("ClearCase.Command.History")); } ClearCaseSettings SettingsPageWidget::settings() const @@ -106,25 +107,6 @@ void SettingsPageWidget::setSettings(const ClearCaseSettings &s) m_ui.indexOnlyVOBsEdit->setText(s.indexOnlyVOBs); } -QString SettingsPageWidget::searchKeywords() const -{ - QString rc; - QLatin1Char sep(' '); - QTextStream(&rc) << m_ui.commandLabel->text() - << sep << m_ui.autoCheckOutCheckBox->text() - << sep << m_ui.externalDiffRadioButton->text() - << sep << m_ui.graphicalDiffRadioButton->text() - << sep << m_ui.diffArgsLabel->text() - << sep << m_ui.historyCountLabel->text() - << sep << m_ui.promptCheckBox->text() - << sep << m_ui.disableIndexerCheckBox->text() - << sep << m_ui.timeOutLabel->text() - << sep << m_ui.indexOnlyVOBsLabel->text() - ; - rc.remove(QLatin1Char('&')); - return rc; -} - SettingsPage::SettingsPage() : m_widget(0) { @@ -132,12 +114,11 @@ SettingsPage::SettingsPage() : setDisplayName(tr("ClearCase")); } -QWidget *SettingsPage::createPage(QWidget *parent) +QWidget *SettingsPage::widget() { - m_widget = new SettingsPageWidget(parent); + if (!m_widget) + m_widget = new SettingsPageWidget; m_widget->setSettings(ClearCasePlugin::instance()->settings()); - if (m_searchKeywords.isEmpty()) - m_searchKeywords = m_widget->searchKeywords(); return m_widget; } @@ -145,8 +126,3 @@ void SettingsPage::apply() { ClearCasePlugin::instance()->setSettings(m_widget->settings()); } - -bool SettingsPage::matches(const QString &s) const -{ - return m_searchKeywords.contains(s, Qt::CaseInsensitive); -} diff --git a/src/plugins/clearcase/settingspage.h b/src/plugins/clearcase/settingspage.h index 95258ca84d..ba5ea95594 100644 --- a/src/plugins/clearcase/settingspage.h +++ b/src/plugins/clearcase/settingspage.h @@ -35,6 +35,8 @@ #include "ui_settingspage.h" +#include <QPointer> + namespace ClearCase { namespace Internal { @@ -50,8 +52,6 @@ public: ClearCaseSettings settings() const; void setSettings(const ClearCaseSettings &); - QString searchKeywords() const; - private: Ui::SettingsPage m_ui; }; @@ -64,14 +64,12 @@ class SettingsPage : public VcsBase::VcsBaseOptionsPage public: SettingsPage(); - QWidget *createPage(QWidget *parent); + QWidget *widget(); void apply(); void finish() { } - bool matches(const QString &) const; private: - QString m_searchKeywords; - SettingsPageWidget* m_widget; + QPointer<SettingsPageWidget> m_widget; }; } // namespace ClearCase diff --git a/src/plugins/cmakeprojectmanager/cmakeeditor.cpp b/src/plugins/cmakeprojectmanager/cmakeeditor.cpp index 4d63e422d5..52200b679b 100644 --- a/src/plugins/cmakeprojectmanager/cmakeeditor.cpp +++ b/src/plugins/cmakeprojectmanager/cmakeeditor.cpp @@ -68,7 +68,7 @@ CMakeEditor::CMakeEditor(CMakeEditorWidget *editor) Core::IEditor *CMakeEditor::duplicate(QWidget *parent) { CMakeEditorWidget *w = qobject_cast<CMakeEditorWidget*>(widget()); - CMakeEditorWidget *ret = new CMakeEditorWidget(parent, w->factory(), w->actionHandler()); + CMakeEditorWidget *ret = new CMakeEditorWidget(parent, w->factory()); ret->duplicateFrom(w); TextEditor::TextEditorSettings::initializeEditor(ret); return ret->editor(); @@ -116,8 +116,8 @@ void CMakeEditor::build() // CMakeEditor // -CMakeEditorWidget::CMakeEditorWidget(QWidget *parent, CMakeEditorFactory *factory, TextEditor::TextEditorActionHandler *ah) - : BaseTextEditorWidget(parent), m_factory(factory), m_ah(ah) +CMakeEditorWidget::CMakeEditorWidget(QWidget *parent, CMakeEditorFactory *factory) + : BaseTextEditorWidget(parent), m_factory(factory) { QSharedPointer<CMakeDocument> doc(new CMakeDocument); doc->setMimeType(QLatin1String(CMakeProjectManager::Constants::CMAKEMIMETYPE)); @@ -127,8 +127,6 @@ CMakeEditorWidget::CMakeEditorWidget(QWidget *parent, CMakeEditorFactory *factor m_commentDefinition.clearCommentStyles(); m_commentDefinition.singleLine = QLatin1Char('#'); - - ah->setupActions(this); } TextEditor::BaseTextEditor *CMakeEditorWidget::createEditor() @@ -203,7 +201,7 @@ CMakeEditorWidget::Link CMakeEditorWidget::findLinkAt(const QTextCursor &cursor, // TODO: Resolve variables - QDir dir(QFileInfo(editorDocument()->filePath()).absolutePath()); + QDir dir(QFileInfo(baseTextDocument()->filePath()).absolutePath()); QString fileName = dir.filePath(buffer); QFileInfo fi(fileName); if (fi.exists()) { diff --git a/src/plugins/cmakeprojectmanager/cmakeeditor.h b/src/plugins/cmakeprojectmanager/cmakeeditor.h index a508c7b391..00f1b6f461 100644 --- a/src/plugins/cmakeprojectmanager/cmakeeditor.h +++ b/src/plugins/cmakeprojectmanager/cmakeeditor.h @@ -71,12 +71,11 @@ class CMakeEditorWidget : public TextEditor::BaseTextEditorWidget Q_OBJECT public: - CMakeEditorWidget(QWidget *parent, CMakeEditorFactory *factory, TextEditor::TextEditorActionHandler *ah); + CMakeEditorWidget(QWidget *parent, CMakeEditorFactory *factory); bool save(const QString &fileName = QString()); CMakeEditorFactory *factory() { return m_factory; } - TextEditor::TextEditorActionHandler *actionHandler() const { return m_ah; } Link findLinkAt(const QTextCursor &cursor, bool resolveTarget = true, bool inNextSplit = false); @@ -89,7 +88,6 @@ public slots: private: CMakeEditorFactory *m_factory; - TextEditor::TextEditorActionHandler *m_ah; Utils::CommentDefinition m_commentDefinition; }; diff --git a/src/plugins/cmakeprojectmanager/cmakeeditorfactory.cpp b/src/plugins/cmakeprojectmanager/cmakeeditorfactory.cpp index f3035d3204..015a208cd6 100644 --- a/src/plugins/cmakeprojectmanager/cmakeeditorfactory.cpp +++ b/src/plugins/cmakeprojectmanager/cmakeeditorfactory.cpp @@ -52,8 +52,7 @@ CMakeEditorFactory::CMakeEditorFactory(CMakeManager *manager) setDisplayName(tr(CMakeProjectManager::Constants::CMAKE_EDITOR_DISPLAY_NAME)); addMimeType(CMakeProjectManager::Constants::CMAKEMIMETYPE); - m_actionHandler = - new TextEditorActionHandler(Constants::C_CMAKEEDITOR, + new TextEditorActionHandler(this, Constants::C_CMAKEEDITOR, TextEditorActionHandler::UnCommentSelection | TextEditorActionHandler::JumpToFileUnderCursor); @@ -72,7 +71,7 @@ CMakeEditorFactory::CMakeEditorFactory(CMakeManager *manager) Core::IEditor *CMakeEditorFactory::createEditor(QWidget *parent) { - CMakeEditorWidget *rc = new CMakeEditorWidget(parent, this, m_actionHandler); + CMakeEditorWidget *rc = new CMakeEditorWidget(parent, this); TextEditor::TextEditorSettings::initializeEditor(rc); return rc->editor(); } diff --git a/src/plugins/cmakeprojectmanager/cmakeeditorfactory.h b/src/plugins/cmakeprojectmanager/cmakeeditorfactory.h index a73f127f6f..c942438509 100644 --- a/src/plugins/cmakeprojectmanager/cmakeeditorfactory.h +++ b/src/plugins/cmakeprojectmanager/cmakeeditorfactory.h @@ -34,8 +34,6 @@ #include <coreplugin/editormanager/ieditorfactory.h> -namespace TextEditor { class TextEditorActionHandler; } - namespace CMakeProjectManager { namespace Internal { @@ -50,7 +48,6 @@ public: private: const QStringList m_mimeTypes; CMakeManager *m_manager; - TextEditor::TextEditorActionHandler *m_actionHandler; }; } // namespace Internal diff --git a/src/plugins/cmakeprojectmanager/cmakeproject.cpp b/src/plugins/cmakeprojectmanager/cmakeproject.cpp index c86f3d47cc..7c75de5ac2 100644 --- a/src/plugins/cmakeprojectmanager/cmakeproject.cpp +++ b/src/plugins/cmakeprojectmanager/cmakeproject.cpp @@ -92,6 +92,7 @@ CMakeProject::CMakeProject(CMakeManager *manager, const QString &fileName) m_rootNode(new CMakeProjectNode(fileName)), m_watcher(new QFileSystemWatcher(this)) { + setId(Constants::CMAKEPROJECT_ID); setProjectContext(Core::Context(CMakeProjectManager::Constants::PROJECTCONTEXT)); setProjectLanguages(Core::Context(ProjectExplorer::Constants::LANG_CXX)); @@ -349,7 +350,7 @@ bool CMakeProject::parseCMakeLists() // This explicitly adds -I. to the include paths part->includePaths += projectDirectory(); part->includePaths += cbpparser.includeFiles(); - part->defines += cbpparser.defines(); + part->projectDefines += cbpparser.defines(); CppTools::ProjectFileAdder adder(part->files); foreach (const QString &file, m_files) @@ -512,11 +513,6 @@ QString CMakeProject::displayName() const return m_projectName; } -Core::Id CMakeProject::id() const -{ - return Core::Id(Constants::CMAKEPROJECT_ID); -} - Core::IDocument *CMakeProject::document() const { return m_file; diff --git a/src/plugins/cmakeprojectmanager/cmakeproject.h b/src/plugins/cmakeprojectmanager/cmakeproject.h index 67d420fa2d..1ba92dd82b 100644 --- a/src/plugins/cmakeprojectmanager/cmakeproject.h +++ b/src/plugins/cmakeprojectmanager/cmakeproject.h @@ -80,7 +80,6 @@ public: ~CMakeProject(); QString displayName() const; - Core::Id id() const; Core::IDocument *document() const; CMakeManager *projectManager() const; diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectmanager.cpp b/src/plugins/cmakeprojectmanager/cmakeprojectmanager.cpp index 42d35eba6f..c587850c8a 100644 --- a/src/plugins/cmakeprojectmanager/cmakeprojectmanager.cpp +++ b/src/plugins/cmakeprojectmanager/cmakeprojectmanager.cpp @@ -291,22 +291,23 @@ QString CMakeSettingsPage::findCmakeExecutable() const return Utils::Environment::systemEnvironment().searchInPath(QLatin1String("cmake")); } -QWidget *CMakeSettingsPage::createPage(QWidget *parent) +QWidget *CMakeSettingsPage::widget() { - QWidget *outerWidget = new QWidget(parent); - QFormLayout *formLayout = new QFormLayout(outerWidget); - formLayout->setFieldGrowthPolicy(QFormLayout::ExpandingFieldsGrow); - m_pathchooser = new Utils::PathChooser; - m_pathchooser->setExpectedKind(Utils::PathChooser::ExistingCommand); - formLayout->addRow(tr("Executable:"), m_pathchooser); - formLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Ignored, QSizePolicy::MinimumExpanding)); + if (!m_widget) { + m_widget = new QWidget; + QFormLayout *formLayout = new QFormLayout(m_widget); + formLayout->setFieldGrowthPolicy(QFormLayout::ExpandingFieldsGrow); + m_pathchooser = new Utils::PathChooser; + m_pathchooser->setExpectedKind(Utils::PathChooser::ExistingCommand); + formLayout->addRow(tr("Executable:"), m_pathchooser); + formLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Ignored, QSizePolicy::MinimumExpanding)); + + m_preferNinja = new QCheckBox(tr("Prefer Ninja generator (CMake 2.8.9 or higher required)")); + formLayout->addRow(m_preferNinja); + } m_pathchooser->setPath(m_cmakeValidatorForUser.cmakeExecutable()); - - m_preferNinja = new QCheckBox(tr("Prefer Ninja generator (CMake 2.8.9 or higher required)")); m_preferNinja->setChecked(preferNinja()); - formLayout->addRow(m_preferNinja); - - return outerWidget; + return m_widget; } void CMakeSettingsPage::saveSettings() const @@ -329,7 +330,7 @@ void CMakeSettingsPage::apply() void CMakeSettingsPage::finish() { - + delete m_widget; } QString CMakeSettingsPage::cmakeExecutable() const diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectmanager.h b/src/plugins/cmakeprojectmanager/cmakeprojectmanager.h index 930e60bd4a..ae7e59b5d3 100644 --- a/src/plugins/cmakeprojectmanager/cmakeprojectmanager.h +++ b/src/plugins/cmakeprojectmanager/cmakeprojectmanager.h @@ -40,12 +40,13 @@ #include <utils/environment.h> #include <utils/pathchooser.h> -#include <QFuture> -#include <QStringList> +#include <QAction> #include <QCheckBox> #include <QDir> +#include <QFuture> +#include <QPointer> +#include <QStringList> #include <QVector> -#include <QAction> #include "cmakevalidator.h" @@ -108,7 +109,7 @@ public: CMakeSettingsPage(); ~CMakeSettingsPage(); - QWidget *createPage(QWidget *parent); + QWidget *widget(); void apply(); void finish(); @@ -125,6 +126,7 @@ private: void saveSettings() const; QString findCmakeExecutable() const; + QPointer<QWidget> m_widget; Utils::PathChooser *m_pathchooser; QCheckBox *m_preferNinja; CMakeValidator m_cmakeValidatorForUser; diff --git a/src/plugins/cmakeprojectmanager/cmakerunconfiguration.cpp b/src/plugins/cmakeprojectmanager/cmakerunconfiguration.cpp index 4178d718bb..d2de5a46ba 100644 --- a/src/plugins/cmakeprojectmanager/cmakerunconfiguration.cpp +++ b/src/plugins/cmakeprojectmanager/cmakerunconfiguration.cpp @@ -212,16 +212,6 @@ void CMakeRunConfiguration::setCommandLineArguments(const QString &newText) m_arguments = newText; } -QString CMakeRunConfiguration::dumperLibrary() const -{ - return QtSupport::QtKitInformation::dumperLibrary(target()->kit()); -} - -QStringList CMakeRunConfiguration::dumperLibraryLocations() const -{ - return QtSupport::QtKitInformation::dumperLibraryLocations(target()->kit()); -} - void CMakeRunConfiguration::setEnabled(bool b) { if (m_enabled == b) diff --git a/src/plugins/cmakeprojectmanager/cmakerunconfiguration.h b/src/plugins/cmakeprojectmanager/cmakerunconfiguration.h index 9963057392..32669debf2 100644 --- a/src/plugins/cmakeprojectmanager/cmakerunconfiguration.h +++ b/src/plugins/cmakeprojectmanager/cmakerunconfiguration.h @@ -70,9 +70,6 @@ public: QString title() const; - QString dumperLibrary() const; - QStringList dumperLibraryLocations() const; - QVariantMap toMap() const; void setEnabled(bool b); diff --git a/src/plugins/coreplugin/actionmanager/actionmanager.cpp b/src/plugins/coreplugin/actionmanager/actionmanager.cpp index b219546ace..2b87746c85 100644 --- a/src/plugins/coreplugin/actionmanager/actionmanager.cpp +++ b/src/plugins/coreplugin/actionmanager/actionmanager.cpp @@ -336,7 +336,7 @@ QList<Command *> ActionManager::commands() { // transform list of CommandPrivate into list of Command QList<Command *> result; - foreach (Command *cmd, d->m_idCmdMap.values()) + foreach (Command *cmd, d->m_idCmdMap) result << cmd; return result; } diff --git a/src/plugins/coreplugin/actionmanager/commandmappings.cpp b/src/plugins/coreplugin/actionmanager/commandmappings.cpp index 85099285a0..53a96cbab4 100644 --- a/src/plugins/coreplugin/actionmanager/commandmappings.cpp +++ b/src/plugins/coreplugin/actionmanager/commandmappings.cpp @@ -50,42 +50,43 @@ CommandMappings::CommandMappings(QObject *parent) // IOptionsPage -QWidget *CommandMappings::createPage(QWidget *parent) +QWidget *CommandMappings::widget() { - m_page = new Ui::CommandMappings(); - QWidget *w = new QWidget(parent); - m_page->setupUi(w); - m_page->targetEdit->setAutoHideButton(Utils::FancyLineEdit::Right, true); - m_page->targetEdit->setPlaceholderText(QString()); - m_page->targetEdit->installEventFilter(this); - - connect(m_page->targetEdit, SIGNAL(buttonClicked(Utils::FancyLineEdit::Side)), - this, SLOT(removeTargetIdentifier())); - connect(m_page->resetButton, SIGNAL(clicked()), - this, SLOT(resetTargetIdentifier())); - connect(m_page->exportButton, SIGNAL(clicked()), - this, SLOT(exportAction())); - connect(m_page->importButton, SIGNAL(clicked()), - this, SLOT(importAction())); - connect(m_page->defaultButton, SIGNAL(clicked()), - this, SLOT(defaultAction())); - - initialize(); - - m_page->commandList->sortByColumn(0, Qt::AscendingOrder); - - connect(m_page->filterEdit, SIGNAL(textChanged(QString)), - this, SLOT(filterChanged(QString))); - connect(m_page->commandList, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)), - this, SLOT(commandChanged(QTreeWidgetItem*))); - connect(m_page->targetEdit, SIGNAL(textChanged(QString)), - this, SLOT(targetIdentifierChanged())); - - new Utils::HeaderViewStretcher(m_page->commandList->header(), 1); - - commandChanged(0); - - return w; + if (!m_widget) { + m_page = new Ui::CommandMappings(); + m_widget = new QWidget; + m_page->setupUi(m_widget); + m_page->targetEdit->setAutoHideButton(Utils::FancyLineEdit::Right, true); + m_page->targetEdit->setPlaceholderText(QString()); + m_page->targetEdit->installEventFilter(this); + + connect(m_page->targetEdit, SIGNAL(buttonClicked(Utils::FancyLineEdit::Side)), + this, SLOT(removeTargetIdentifier())); + connect(m_page->resetButton, SIGNAL(clicked()), + this, SLOT(resetTargetIdentifier())); + connect(m_page->exportButton, SIGNAL(clicked()), + this, SLOT(exportAction())); + connect(m_page->importButton, SIGNAL(clicked()), + this, SLOT(importAction())); + connect(m_page->defaultButton, SIGNAL(clicked()), + this, SLOT(defaultAction())); + + initialize(); + + m_page->commandList->sortByColumn(0, Qt::AscendingOrder); + + connect(m_page->filterEdit, SIGNAL(textChanged(QString)), + this, SLOT(filterChanged(QString))); + connect(m_page->commandList, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)), + this, SLOT(commandChanged(QTreeWidgetItem*))); + connect(m_page->targetEdit, SIGNAL(textChanged(QString)), + this, SLOT(targetIdentifierChanged())); + + new Utils::HeaderViewStretcher(m_page->commandList->header(), 1); + + commandChanged(0); + } + return m_widget; } void CommandMappings::setImportExportEnabled(bool enabled) @@ -126,6 +127,7 @@ void CommandMappings::setTargetHeader(const QString &s) void CommandMappings::finish() { + delete m_widget; if (!m_page) // page was never shown return; delete m_page; diff --git a/src/plugins/coreplugin/actionmanager/commandmappings.h b/src/plugins/coreplugin/actionmanager/commandmappings.h index db1296dfb6..df3ce639c9 100644 --- a/src/plugins/coreplugin/actionmanager/commandmappings.h +++ b/src/plugins/coreplugin/actionmanager/commandmappings.h @@ -33,6 +33,7 @@ #include <coreplugin/dialogs/ioptionspage.h> #include <QObject> +#include <QPointer> QT_BEGIN_NAMESPACE class QLineEdit; @@ -60,7 +61,7 @@ protected slots: protected: // IOptionsPage - QWidget *createPage(QWidget *parent); + QWidget *widget(); virtual void apply() {} virtual void finish(); @@ -80,6 +81,7 @@ protected: virtual void markPossibleCollisions(QTreeWidgetItem *) {} virtual void resetCollisionMarkers() {} private: + QPointer<QWidget> m_widget; Internal::Ui::CommandMappings *m_page; }; diff --git a/src/plugins/coreplugin/coreplugin.h b/src/plugins/coreplugin/coreplugin.h index 6a50bc0960..9855c39eba 100644 --- a/src/plugins/coreplugin/coreplugin.h +++ b/src/plugins/coreplugin/coreplugin.h @@ -57,6 +57,12 @@ public: public slots: void fileOpenRequest(const QString&); +private slots: +#if defined(WITH_TESTS) + void testVcsManager_data(); + void testVcsManager(); +#endif + private: void parseArguments(const QStringList & arguments); diff --git a/src/plugins/coreplugin/dialogs/ioptionspage.cpp b/src/plugins/coreplugin/dialogs/ioptionspage.cpp index 73ab549563..276fc86646 100644 --- a/src/plugins/coreplugin/dialogs/ioptionspage.cpp +++ b/src/plugins/coreplugin/dialogs/ioptionspage.cpp @@ -29,6 +29,11 @@ #include "ioptionspage.h" +#include <QCheckBox> +#include <QGroupBox> +#include <QLabel> +#include <QPushButton> + /*! \class Core::IOptionsPage \mainclass @@ -43,12 +48,58 @@ \li \c displayName() is the (translated) name for display \li \c category() is the unique id for the category that the page should be displayed in \li \c displayCategory() is the translated name of the category - \li \c createPage() is called to retrieve the widget to show in the - \gui Options dialog - The widget will be destroyed by the widget hierarchy when the dialog closes + \li \c widget() is called to retrieve the widget to show in the + \gui Options dialog. You should create a widget lazily here, and delete it again in the + finish() method. This method can be called multiple times, so you should only create a new + widget if the old one was deleted. \li \c apply() is called to store the settings. It should detect if any changes have been made and store those - \li \c finish() is called directly before the \gui Options dialog closes - \li \c matches() is used for the \gui Options dialog search filter + \li \c finish() is called directly before the \gui Options dialog closes. Here you should delete + the widget that was created in widget() to free resources. + \li \c matches() is used for the \gui Options dialog search filter. The default implementation + takes the widget() and searches for all labels, buttons, checkboxes and group boxes, + and matches on their texts/titles. You can implement your own matching algorithm, but + usually the default implementation will work fine. \endlist */ + + +Core::IOptionsPage::IOptionsPage(QObject *parent) + : QObject(parent), + m_keywordsInitialized(false) +{ + +} + +Core::IOptionsPage::~IOptionsPage() +{ +} + +bool Core::IOptionsPage::matches(const QString &searchKeyWord) const +{ + if (!m_keywordsInitialized) { + IOptionsPage *that = const_cast<IOptionsPage *>(this); + QWidget *widget = that->widget(); + if (!widget) + return false; + // find common subwidgets + foreach (const QLabel *label, widget->findChildren<QLabel *>()) + m_keywords << label->text(); + foreach (const QCheckBox *checkbox, widget->findChildren<QCheckBox *>()) + m_keywords << checkbox->text(); + foreach (const QPushButton *pushButton, widget->findChildren<QPushButton *>()) + m_keywords << pushButton->text(); + foreach (const QGroupBox *groupBox, widget->findChildren<QGroupBox *>()) + m_keywords << groupBox->title(); + + // clean up accelerators + QMutableStringListIterator it(m_keywords); + while (it.hasNext()) + it.next().remove(QLatin1Char('&')); + m_keywordsInitialized = true; + } + foreach (const QString &keyword, m_keywords) + if (keyword.contains(searchKeyWord, Qt::CaseInsensitive)) + return true; + return false; +} diff --git a/src/plugins/coreplugin/dialogs/ioptionspage.h b/src/plugins/coreplugin/dialogs/ioptionspage.h index aa468ef828..915b8b0462 100644 --- a/src/plugins/coreplugin/dialogs/ioptionspage.h +++ b/src/plugins/coreplugin/dialogs/ioptionspage.h @@ -34,6 +34,7 @@ #include <QIcon> #include <QObject> +#include <QStringList> namespace Core { @@ -42,7 +43,8 @@ class CORE_EXPORT IOptionsPage : public QObject Q_OBJECT public: - IOptionsPage(QObject *parent = 0) : QObject(parent) {} + IOptionsPage(QObject *parent = 0); + virtual ~IOptionsPage(); Id id() const { return m_id; } QString displayName() const { return m_displayName; } @@ -50,8 +52,8 @@ public: QString displayCategory() const { return m_displayCategory; } QIcon categoryIcon() const { return QIcon(m_categoryIcon); } - virtual bool matches(const QString & /* searchKeyWord*/) const { return false; } - virtual QWidget *createPage(QWidget *parent) = 0; + virtual bool matches(const QString &searchKeyWord) const; + virtual QWidget *widget() = 0; virtual void apply() = 0; virtual void finish() = 0; @@ -67,6 +69,9 @@ protected: QString m_displayName; QString m_displayCategory; QString m_categoryIcon; + + mutable bool m_keywordsInitialized; + mutable QStringList m_keywords; }; /* @@ -89,6 +94,7 @@ public: QIcon categoryIcon() const { return QIcon(m_categoryIcon); } virtual QList<IOptionsPage *> pages() const = 0; + virtual bool matches(const QString & /* searchKeyWord*/) const = 0; protected: void setCategory(Core::Id category) { m_category = category; } diff --git a/src/plugins/coreplugin/dialogs/newdialog.cpp b/src/plugins/coreplugin/dialogs/newdialog.cpp index 363b9195cb..5ea4bfe624 100644 --- a/src/plugins/coreplugin/dialogs/newdialog.cpp +++ b/src/plugins/coreplugin/dialogs/newdialog.cpp @@ -194,7 +194,7 @@ NewDialog::NewDialog(QWidget *parent) : m_ui->frame->setPalette(p); m_okButton = m_ui->buttonBox->button(QDialogButtonBox::Ok); m_okButton->setDefault(true); - m_okButton->setText(tr("&Choose...")); + m_okButton->setText(tr("Choose...")); m_model = new QStandardItemModel(this); m_twoLevelProxyModel = new TwoLevelProxyModel(this); diff --git a/src/plugins/coreplugin/dialogs/readonlyfilesdialog.cpp b/src/plugins/coreplugin/dialogs/readonlyfilesdialog.cpp index 33ebb692f1..78b50547f1 100644 --- a/src/plugins/coreplugin/dialogs/readonlyfilesdialog.cpp +++ b/src/plugins/coreplugin/dialogs/readonlyfilesdialog.cpp @@ -257,33 +257,33 @@ int ReadOnlyFilesDialog::exec() ReadOnlyResult result = RO_Cancel; QStringList failedToMakeWritable; - foreach (ReadOnlyFilesDialogPrivate::ButtonGroupForFile buttengroup, d->buttonGroups) { - result = static_cast<ReadOnlyResult>(buttengroup.group->checkedId()); + foreach (ReadOnlyFilesDialogPrivate::ButtonGroupForFile buttongroup, d->buttonGroups) { + result = static_cast<ReadOnlyResult>(buttongroup.group->checkedId()); switch (result) { case RO_MakeWritable: - if (!Utils::FileUtils::makeWritable(Utils::FileName(QFileInfo(buttengroup.fileName)))) { - failedToMakeWritable << buttengroup.fileName; + if (!Utils::FileUtils::makeWritable(Utils::FileName(QFileInfo(buttongroup.fileName)))) { + failedToMakeWritable << buttongroup.fileName; continue; } break; case RO_OpenVCS: - if (!d->versionControls[buttengroup.fileName]->vcsOpen(buttengroup.fileName)) { - failedToMakeWritable << buttengroup.fileName; + if (!d->versionControls[buttongroup.fileName]->vcsOpen(buttongroup.fileName)) { + failedToMakeWritable << buttongroup.fileName; continue; } break; case RO_SaveAs: if (!EditorManager::saveDocumentAs(d->document)) { - failedToMakeWritable << buttengroup.fileName; + failedToMakeWritable << buttongroup.fileName; continue; } break; default: - failedToMakeWritable << buttengroup.fileName; + failedToMakeWritable << buttongroup.fileName; continue; } - if (!QFileInfo(buttengroup.fileName).isWritable()) - failedToMakeWritable << buttengroup.fileName; + if (!QFileInfo(buttongroup.fileName).isWritable()) + failedToMakeWritable << buttongroup.fileName; } if (!failedToMakeWritable.isEmpty()) { if (d->showWarnings) @@ -366,7 +366,7 @@ void ReadOnlyFilesDialog::updateSelectAll() void ReadOnlyFilesDialog::initDialog(const QStringList &fileNames) { ui->setupUi(this); - ui->buttonBox->addButton(tr("&Change Permission"), QDialogButtonBox::AcceptRole); + ui->buttonBox->addButton(tr("Change &Permission"), QDialogButtonBox::AcceptRole); ui->buttonBox->addButton(QDialogButtonBox::Cancel); QString vcsOpenTextForAll; @@ -389,7 +389,7 @@ void ReadOnlyFilesDialog::initDialog(const QStringList &fileNames) IVersionControl *versionControlForFile = VcsManager::findVersionControlForDirectory(directory); const bool fileManagedByVCS = versionControlForFile - && versionControlForFile->openSupportMode() != IVersionControl::NoOpen; + && versionControlForFile->openSupportMode(fileName) != IVersionControl::NoOpen; if (fileManagedByVCS) { const QString vcsOpenTextForFile = versionControlForFile->vcsOpenText().remove(QLatin1Char('&')); @@ -407,7 +407,7 @@ void ReadOnlyFilesDialog::initDialog(const QStringList &fileNames) vcsMakeWritableTextForAll.clear(); } // Add make writable if it is supported by the reposetory. - if (versionControlForFile->openSupportMode() == IVersionControl::OpenOptional) { + if (versionControlForFile->openSupportMode(fileName) == IVersionControl::OpenOptional) { useMakeWritable = true; createRadioButtonForItem(item, radioButtonGroup, MakeWritable); } diff --git a/src/plugins/coreplugin/dialogs/settingsdialog.cpp b/src/plugins/coreplugin/dialogs/settingsdialog.cpp index ae3c8c47c3..e2de47cf98 100644 --- a/src/plugins/coreplugin/dialogs/settingsdialog.cpp +++ b/src/plugins/coreplugin/dialogs/settingsdialog.cpp @@ -66,12 +66,15 @@ static QPointer<SettingsDialog> m_instance = 0; class Category { public: + Category() : index(-1), providerPagesCreated(false) { } + Id id; int index; QString displayName; QIcon icon; QList<IOptionsPage *> pages; QList<IOptionsPageProvider *> providers; + bool providerPagesCreated; QTabWidget *tabWidget; }; @@ -210,15 +213,23 @@ bool CategoryFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &sou if (QSortFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent)) return true; + const QString pattern = filterRegExp().pattern(); const CategoryModel *cm = static_cast<CategoryModel*>(sourceModel()); - foreach (const IOptionsPage *page, cm->categories().at(sourceRow)->pages) { - const QString pattern = filterRegExp().pattern(); + const Category *category = cm->categories().at(sourceRow); + foreach (const IOptionsPage *page, category->pages) { if (page->displayCategory().contains(pattern, Qt::CaseInsensitive) - || page->displayName().contains(pattern, Qt::CaseInsensitive) - || page->matches(pattern)) + || page->displayName().contains(pattern, Qt::CaseInsensitive) + || page->matches(pattern)) return true; } + if (!category->providerPagesCreated) { + foreach (const IOptionsPageProvider *provider, category->providers) { + if (provider->matches(pattern)) + return true; + } + } + return false; } @@ -325,8 +336,6 @@ SettingsDialog::SettingsDialog(QWidget *parent) : // The order of the slot connection matters here, the filter slot // opens the matching page after the model has filtered. connect(m_filterLineEdit, SIGNAL(filterChanged(QString)), - this, SLOT(ensureAllCategoryWidgets())); - connect(m_filterLineEdit, SIGNAL(filterChanged(QString)), m_proxyModel, SLOT(setFilterFixedString(QString))); connect(m_filterLineEdit, SIGNAL(filterChanged(QString)), this, SLOT(filter(QString))); m_categoryList->setFocus(); @@ -432,15 +441,18 @@ void SettingsDialog::ensureCategoryWidget(Category *category) { if (category->tabWidget != 0) return; - foreach (const IOptionsPageProvider *provider, category->providers) { - category->pages += provider->pages(); + if (!category->providerPagesCreated) { + foreach (const IOptionsPageProvider *provider, category->providers) + category->pages += provider->pages(); + category->providerPagesCreated = true; } + qStableSort(category->pages.begin(), category->pages.end(), optionsPageLessThan); QTabWidget *tabWidget = new QTabWidget; for (int j = 0; j < category->pages.size(); ++j) { IOptionsPage *page = category->pages.at(j); - QWidget *widget = page->createPage(0); + QWidget *widget = page->widget(); tabWidget->addTab(widget, page->displayName()); } @@ -451,12 +463,6 @@ void SettingsDialog::ensureCategoryWidget(Category *category) category->index = m_stackedLayout->addWidget(tabWidget); } -void SettingsDialog::ensureAllCategoryWidgets() -{ - foreach (Category *category, m_model->categories()) - ensureCategoryWidget(category); -} - void SettingsDialog::disconnectTabWidgets() { foreach (Category *category, m_model->categories()) { @@ -514,7 +520,6 @@ void SettingsDialog::currentTabChanged(int index) void SettingsDialog::filter(const QString &text) { - ensureAllCategoryWidgets(); // When there is no current index, select the first one when possible if (!m_categoryList->currentIndex().isValid() && m_model->rowCount() > 0) m_categoryList->setCurrentIndex(m_proxyModel->index(0, 0)); diff --git a/src/plugins/coreplugin/dialogs/settingsdialog.h b/src/plugins/coreplugin/dialogs/settingsdialog.h index 240f9fe279..ce0d62de40 100644 --- a/src/plugins/coreplugin/dialogs/settingsdialog.h +++ b/src/plugins/coreplugin/dialogs/settingsdialog.h @@ -79,7 +79,6 @@ private slots: void currentChanged(const QModelIndex ¤t); void currentTabChanged(int); void filter(const QString &text); - void ensureAllCategoryWidgets(); private: SettingsDialog(QWidget *parent); diff --git a/src/plugins/coreplugin/dialogs/shortcutsettings.cpp b/src/plugins/coreplugin/dialogs/shortcutsettings.cpp index 696a661889..6db5e843b1 100644 --- a/src/plugins/coreplugin/dialogs/shortcutsettings.cpp +++ b/src/plugins/coreplugin/dialogs/shortcutsettings.cpp @@ -61,12 +61,12 @@ ShortcutSettings::ShortcutSettings(QObject *parent) setCategoryIcon(QLatin1String(Core::Constants::SETTINGS_CATEGORY_CORE_ICON)); } -QWidget *ShortcutSettings::createPage(QWidget *parent) +QWidget *ShortcutSettings::widget() { m_initialized = true; m_keyNum = m_key[0] = m_key[1] = m_key[2] = m_key[3] = 0; - QWidget *w = CommandMappings::createPage(parent); + QWidget *w = CommandMappings::widget(); const QString pageTitle = tr("Keyboard Shortcuts"); const QString targetLabelText = tr("Key sequence:"); @@ -78,12 +78,6 @@ QWidget *ShortcutSettings::createPage(QWidget *parent) setTargetHeader(editTitle); targetEdit()->setPlaceholderText(tr("Type to set shortcut")); - if (m_searchKeywords.isEmpty()) { - QTextStream(&m_searchKeywords) << ' ' << pageTitle - << ' ' << targetLabelText - << ' ' << editTitle; - } - return w; } @@ -102,11 +96,6 @@ void ShortcutSettings::finish() m_initialized = false; } -bool ShortcutSettings::matches(const QString &s) const -{ - return m_searchKeywords.contains(s, Qt::CaseInsensitive); -} - bool ShortcutSettings::eventFilter(QObject *o, QEvent *e) { Q_UNUSED(o) diff --git a/src/plugins/coreplugin/dialogs/shortcutsettings.h b/src/plugins/coreplugin/dialogs/shortcutsettings.h index 23c7a21bfc..26681bd250 100644 --- a/src/plugins/coreplugin/dialogs/shortcutsettings.h +++ b/src/plugins/coreplugin/dialogs/shortcutsettings.h @@ -62,10 +62,9 @@ class ShortcutSettings : public Core::CommandMappings public: ShortcutSettings(QObject *parent = 0); - QWidget *createPage(QWidget *parent); + QWidget *widget(); void apply(); void finish(); - bool matches(const QString &s) const; protected: bool eventFilter(QObject *o, QEvent *e); @@ -94,7 +93,6 @@ private: QList<ShortcutItem *> m_scitems; int m_key[4], m_keyNum; - QString m_searchKeywords; bool m_initialized; }; diff --git a/src/plugins/coreplugin/documentmanager.cpp b/src/plugins/coreplugin/documentmanager.cpp index ec6348263e..4df60a9d12 100644 --- a/src/plugins/coreplugin/documentmanager.cpp +++ b/src/plugins/coreplugin/documentmanager.cpp @@ -1367,11 +1367,6 @@ void DocumentManager::executeOpenWithMenuAction(QAction *action) EditorManager::openExternalEditor(entry.fileName, entry.externalEditor->id()); } -void DocumentManager::slotExecuteOpenWithMenuAction(QAction *action) -{ - executeOpenWithMenuAction(action); -} - bool DocumentManager::eventFilter(QObject *obj, QEvent *e) { if (obj == qApp && e->type() == QEvent::ApplicationActivate) { diff --git a/src/plugins/coreplugin/documentmanager.h b/src/plugins/coreplugin/documentmanager.h index adf3e7e7dc..2bd7439da0 100644 --- a/src/plugins/coreplugin/documentmanager.h +++ b/src/plugins/coreplugin/documentmanager.h @@ -126,10 +126,8 @@ public: lead to any editors to reload or any other editor manager actions. */ static void notifyFilesChangedInternally(const QStringList &files); - static void executeOpenWithMenuAction(QAction *action); - public slots: - void slotExecuteOpenWithMenuAction(QAction *action); + static void executeOpenWithMenuAction(QAction *action); signals: void currentFileChanged(const QString &filePath); diff --git a/src/plugins/coreplugin/editormanager/editormanager.cpp b/src/plugins/coreplugin/editormanager/editormanager.cpp index 60bd9e27db..18a6feb049 100644 --- a/src/plugins/coreplugin/editormanager/editormanager.cpp +++ b/src/plugins/coreplugin/editormanager/editormanager.cpp @@ -1894,7 +1894,7 @@ void EditorManager::vcsOpenCurrentEditor() const QString directory = QFileInfo(document->filePath()).absolutePath(); IVersionControl *versionControl = VcsManager::findVersionControlForDirectory(directory); - if (!versionControl || versionControl->openSupportMode() == IVersionControl::NoOpen) + if (!versionControl || versionControl->openSupportMode(document->filePath()) == IVersionControl::NoOpen) return; if (!versionControl->vcsOpen(document->filePath())) { @@ -1956,7 +1956,7 @@ void EditorManager::updateMakeWritableWarning() bool promptVCS = false; const QString directory = QFileInfo(document->filePath()).absolutePath(); IVersionControl *versionControl = VcsManager::findVersionControlForDirectory(directory); - if (versionControl && versionControl->openSupportMode() != IVersionControl::NoOpen) { + if (versionControl && versionControl->openSupportMode(document->filePath()) != IVersionControl::NoOpen) { if (versionControl->settingsFlags() & IVersionControl::AutoOpen) { vcsOpenCurrentEditor(); ww = false; @@ -1989,8 +1989,7 @@ void EditorManager::setupSaveActions(IDocument *document, QAction *saveAction, Q { saveAction->setEnabled(document != 0 && document->isModified()); saveAsAction->setEnabled(document != 0 && document->isSaveAsAllowed()); - revertToSavedAction->setEnabled(document != 0 - && !document->filePath().isEmpty() && document->isModified()); + revertToSavedAction->setEnabled(document != 0 && !document->filePath().isEmpty()); const QString documentName = document ? document->displayName() : QString(); QString quotedName; @@ -1999,7 +1998,9 @@ void EditorManager::setupSaveActions(IDocument *document, QAction *saveAction, Q quotedName = QLatin1Char('"') + documentName + QLatin1Char('"'); saveAction->setText(tr("&Save %1").arg(quotedName)); saveAsAction->setText(tr("Save %1 &As...").arg(quotedName)); - revertToSavedAction->setText(tr("Revert %1 to Saved").arg(quotedName)); + revertToSavedAction->setText(document->isModified() + ? tr("Revert %1 to Saved").arg(quotedName) + : tr("Reload %1").arg(quotedName)); } } diff --git a/src/plugins/coreplugin/editormanager/systemeditor.cpp b/src/plugins/coreplugin/editormanager/systemeditor.cpp index ec4b78c2c4..d8cac7b423 100644 --- a/src/plugins/coreplugin/editormanager/systemeditor.cpp +++ b/src/plugins/coreplugin/editormanager/systemeditor.cpp @@ -54,7 +54,7 @@ Id SystemEditor::id() const QString SystemEditor::displayName() const { - return QLatin1String("System Editor"); + return tr("System Editor"); } bool SystemEditor::startEditor(const QString &fileName, QString *errorMessage) diff --git a/src/plugins/coreplugin/editortoolbar.cpp b/src/plugins/coreplugin/editortoolbar.cpp index 4bdbbfd5ca..3d647a9f89 100644 --- a/src/plugins/coreplugin/editortoolbar.cpp +++ b/src/plugins/coreplugin/editortoolbar.cpp @@ -317,10 +317,12 @@ void EditorToolBar::listContextMenu(QPoint pos) DocumentModel::Entry *entry = EditorManager::documentModel()->documentAtRow( d->m_editorList->currentIndex()); QString fileName = entry ? entry->fileName() : QString(); - if (fileName.isEmpty()) + QString shortFileName = entry ? QFileInfo(fileName).fileName() : QString(); + if (fileName.isEmpty() || shortFileName.isEmpty()) return; QMenu menu; QAction *copyPath = menu.addAction(tr("Copy Full Path to Clipboard")); + QAction *copyFileName = menu.addAction(tr("Copy File Name to Clipboard")); menu.addSeparator(); EditorManager::addSaveAndCloseEditorActions(&menu, entry); menu.addSeparator(); @@ -328,6 +330,8 @@ void EditorToolBar::listContextMenu(QPoint pos) QAction *result = menu.exec(d->m_editorList->mapToGlobal(pos)); if (result == copyPath) QApplication::clipboard()->setText(QDir::toNativeSeparators(fileName)); + if (result == copyFileName) + QApplication::clipboard()->setText(shortFileName); } void EditorToolBar::makeEditorWritable() diff --git a/src/plugins/coreplugin/generalsettings.cpp b/src/plugins/coreplugin/generalsettings.cpp index eb6da5d961..f1f3fe4625 100644 --- a/src/plugins/coreplugin/generalsettings.cpp +++ b/src/plugins/coreplugin/generalsettings.cpp @@ -102,76 +102,61 @@ void GeneralSettings::fillLanguageBox() const } } -QWidget *GeneralSettings::createPage(QWidget *parent) +QWidget *GeneralSettings::widget() { - m_page = new Ui::GeneralSettings(); - m_widget = new QWidget(parent); - m_page->setupUi(m_widget); - - fillLanguageBox(); - - m_page->colorButton->setColor(StyleHelper::requestedBaseColor()); - m_page->reloadBehavior->setCurrentIndex(EditorManager::reloadSetting()); - if (HostOsInfo::isAnyUnixHost()) { - QSettings *settings = Core::ICore::settings(); - const QStringList availableTerminals = ConsoleProcess::availableTerminalEmulators(); - const QString currentTerminal = ConsoleProcess::terminalEmulator(settings, false); - m_page->terminalComboBox->addItems(availableTerminals); - m_page->terminalComboBox->lineEdit()->setText(currentTerminal); - m_page->terminalComboBox->lineEdit()->setPlaceholderText(ConsoleProcess::defaultTerminalEmulator()); - } else { - m_page->terminalLabel->hide(); - m_page->terminalComboBox->hide(); - m_page->resetTerminalButton->hide(); - } - - if (HostOsInfo::isAnyUnixHost() && !HostOsInfo::isMacHost()) { - QSettings *settings = Core::ICore::settings(); - m_page->externalFileBrowserEdit->setText(UnixUtils::fileBrowser(settings)); - } else { - m_page->externalFileBrowserLabel->hide(); - m_page->externalFileBrowserEdit->hide(); - m_page->resetFileBrowserButton->hide(); - m_page->helpExternalFileBrowserButton->hide(); - } - - m_page->autoSaveCheckBox->setChecked(EditorManager::autoSaveEnabled()); - m_page->autoSaveInterval->setValue(EditorManager::autoSaveInterval()); - m_page->resetWarningsButton->setEnabled(Core::InfoBar::anyGloballySuppressed() - || Utils::CheckableMessageBox::hasSuppressedQuestions(ICore::settings())); + if (!m_widget) { + m_page = new Ui::GeneralSettings(); + m_widget = new QWidget; + m_page->setupUi(m_widget); + + fillLanguageBox(); + + m_page->colorButton->setColor(StyleHelper::requestedBaseColor()); + m_page->reloadBehavior->setCurrentIndex(EditorManager::reloadSetting()); + if (HostOsInfo::isAnyUnixHost()) { + QSettings *settings = Core::ICore::settings(); + const QStringList availableTerminals = ConsoleProcess::availableTerminalEmulators(); + const QString currentTerminal = ConsoleProcess::terminalEmulator(settings, false); + m_page->terminalComboBox->addItems(availableTerminals); + m_page->terminalComboBox->lineEdit()->setText(currentTerminal); + m_page->terminalComboBox->lineEdit()->setPlaceholderText(ConsoleProcess::defaultTerminalEmulator()); + } else { + m_page->terminalLabel->hide(); + m_page->terminalComboBox->hide(); + m_page->resetTerminalButton->hide(); + } - connect(m_page->resetColorButton, SIGNAL(clicked()), - this, SLOT(resetInterfaceColor())); - connect(m_page->resetWarningsButton, SIGNAL(clicked()), - this, SLOT(resetWarnings())); - if (HostOsInfo::isAnyUnixHost()) { - connect(m_page->resetTerminalButton, SIGNAL(clicked()), this, SLOT(resetTerminal())); - if (!HostOsInfo::isMacHost()) { - connect(m_page->resetFileBrowserButton, SIGNAL(clicked()), this, SLOT(resetFileBrowser())); - connect(m_page->helpExternalFileBrowserButton, SIGNAL(clicked()), - this, SLOT(showHelpForFileBrowser())); + if (HostOsInfo::isAnyUnixHost() && !HostOsInfo::isMacHost()) { + QSettings *settings = Core::ICore::settings(); + m_page->externalFileBrowserEdit->setText(UnixUtils::fileBrowser(settings)); + } else { + m_page->externalFileBrowserLabel->hide(); + m_page->externalFileBrowserEdit->hide(); + m_page->resetFileBrowserButton->hide(); + m_page->helpExternalFileBrowserButton->hide(); } - } - if (m_searchKeywords.isEmpty()) { - QLatin1Char sep(' '); - QTextStream(&m_searchKeywords) - << m_page->interfaceBox->title() << sep - << m_page->colorLabel->text() << sep - << m_page->languageLabel->text() << sep - << m_page->systemBox->title() << sep - << m_page->terminalLabel->text() << sep - << m_page->modifiedLabel->text(); - m_searchKeywords.remove(QLatin1Char('&')); + m_page->autoSaveCheckBox->setChecked(EditorManager::autoSaveEnabled()); + m_page->autoSaveInterval->setValue(EditorManager::autoSaveInterval()); + m_page->resetWarningsButton->setEnabled(Core::InfoBar::anyGloballySuppressed() + || Utils::CheckableMessageBox::hasSuppressedQuestions(ICore::settings())); + + connect(m_page->resetColorButton, SIGNAL(clicked()), + this, SLOT(resetInterfaceColor())); + connect(m_page->resetWarningsButton, SIGNAL(clicked()), + this, SLOT(resetWarnings())); + if (HostOsInfo::isAnyUnixHost()) { + connect(m_page->resetTerminalButton, SIGNAL(clicked()), this, SLOT(resetTerminal())); + if (!HostOsInfo::isMacHost()) { + connect(m_page->resetFileBrowserButton, SIGNAL(clicked()), this, SLOT(resetFileBrowser())); + connect(m_page->helpExternalFileBrowserButton, SIGNAL(clicked()), + this, SLOT(showHelpForFileBrowser())); + } + } } return m_widget; } -bool GeneralSettings::matches(const QString &s) const -{ - return m_searchKeywords.contains(s, Qt::CaseInsensitive); -} - void GeneralSettings::apply() { if (!m_page) // wasn't shown, can't be changed @@ -195,6 +180,7 @@ void GeneralSettings::apply() void GeneralSettings::finish() { + delete m_widget; if (!m_page) // page was never shown return; delete m_page; diff --git a/src/plugins/coreplugin/generalsettings.h b/src/plugins/coreplugin/generalsettings.h index c6b868ea0a..d51a7a8ecd 100644 --- a/src/plugins/coreplugin/generalsettings.h +++ b/src/plugins/coreplugin/generalsettings.h @@ -51,10 +51,9 @@ class GeneralSettings : public IOptionsPage public: GeneralSettings(); - QWidget* createPage(QWidget *parent); + QWidget* widget(); void apply(); void finish(); - virtual bool matches(const QString &) const; private slots: void resetInterfaceColor(); @@ -70,7 +69,6 @@ private: QString language() const; void setLanguage(const QString&); Ui::GeneralSettings *m_page; - QString m_searchKeywords; QPointer<QMessageBox> m_dialog; QPointer<QWidget> m_widget; }; diff --git a/src/plugins/coreplugin/idocument.h b/src/plugins/coreplugin/idocument.h index e37a2c7fae..483c7a8daa 100644 --- a/src/plugins/coreplugin/idocument.h +++ b/src/plugins/coreplugin/idocument.h @@ -51,12 +51,6 @@ public: IgnoreAll = 2 }; - enum Utf8BomSetting { - AlwaysAdd = 0, - OnlyKeep = 1, - AlwaysDelete = 2 - }; - enum ChangeTrigger { TriggerInternal, TriggerExternal diff --git a/src/plugins/coreplugin/iversioncontrol.cpp b/src/plugins/coreplugin/iversioncontrol.cpp index 9559695c14..a798a78291 100644 --- a/src/plugins/coreplugin/iversioncontrol.cpp +++ b/src/plugins/coreplugin/iversioncontrol.cpp @@ -46,9 +46,64 @@ QString IVersionControl::vcsTopic(const QString &) return QString(); } -IVersionControl::OpenSupportMode IVersionControl::openSupportMode() const +IVersionControl::OpenSupportMode IVersionControl::openSupportMode(const QString &fileName) const { + Q_UNUSED(fileName); return NoOpen; } +} // namespace Core + +#if defined(WITH_TESTS) + +#include "vcsmanager.h" + +#include <QFileInfo> + +namespace Core { + +TestVersionControl::~TestVersionControl() +{ + VcsManager::instance()->clearVersionControlCache(); +} + +void TestVersionControl::setManagedDirectories(const QHash<QString, QString> &dirs) +{ + m_managedDirs = dirs; + m_dirCount = 0; + VcsManager::instance()->clearVersionControlCache(); +} + +void TestVersionControl::setManagedFiles(const QSet<QString> &files) +{ + m_managedFiles = files; + m_fileCount = 0; + VcsManager::instance()->clearVersionControlCache(); +} + +bool TestVersionControl::managesDirectory(const QString &filename, QString *topLevel) const +{ + ++m_dirCount; + + if (m_managedDirs.contains(filename)) { + if (topLevel) + *topLevel = m_managedDirs.value(filename); + return true; + } + return false; } + +bool TestVersionControl::managesFile(const QString &workingDirectory, const QString &fileName) const +{ + ++m_fileCount; + + QFileInfo fi(workingDirectory + QLatin1Char('/') + fileName); + QString dir = fi.absolutePath(); + if (!managesDirectory(dir, 0)) + return false; + QString file = fi.absoluteFilePath(); + return m_managedFiles.contains(file); +} + +} // namespace Core +#endif diff --git a/src/plugins/coreplugin/iversioncontrol.h b/src/plugins/coreplugin/iversioncontrol.h index 86114e7992..1d7ed25360 100644 --- a/src/plugins/coreplugin/iversioncontrol.h +++ b/src/plugins/coreplugin/iversioncontrol.h @@ -100,9 +100,9 @@ public: virtual bool supportsOperation(Operation operation) const = 0; /*! - * Returns the open support mode. + * Returns the open support mode for \a fileName. */ - virtual OpenSupportMode openSupportMode() const; + virtual OpenSupportMode openSupportMode(const QString &fileName) const; /*! * Called prior to save, if the file is read only. Should be implemented if @@ -153,7 +153,7 @@ public: /*! * Called to get the version control repository root. */ - virtual QString vcsGetRepositoryURL(const QString &director) = 0; + virtual QString vcsGetRepositoryURL(const QString &directory) = 0; /*! * Topic (e.g. name of the current branch) @@ -188,4 +188,54 @@ Q_DECLARE_OPERATORS_FOR_FLAGS(Core::IVersionControl::SettingsFlags) } // namespace Core +#if defined(WITH_TESTS) + +#include <QSet> + +namespace Core { + +class CORE_EXPORT TestVersionControl : public IVersionControl +{ + Q_OBJECT +public: + TestVersionControl(Core::Id id, const QString &name) : + m_id(id), m_displayName(name), m_dirCount(0), m_fileCount(0) + { } + ~TestVersionControl(); + + void setManagedDirectories(const QHash<QString, QString> &dirs); + void setManagedFiles(const QSet<QString> &files); + + int dirCount() const { return m_dirCount; } + int fileCount() const { return m_fileCount; } + + // IVersionControl interface + QString displayName() const { return m_displayName; } + Id id() const { return m_id; } + bool managesDirectory(const QString &filename, QString *topLevel) const; + bool managesFile(const QString &workingDirectory, const QString &fileName) const; + bool isConfigured() const { return true; } + bool supportsOperation(Operation) const { return false; } + bool vcsOpen(const QString &) { return false; } + bool vcsAdd(const QString &) { return false; } + bool vcsDelete(const QString &) { return false; } + bool vcsMove(const QString &, const QString &) { return false; } + bool vcsCreateRepository(const QString &) { return false; } + bool vcsCheckout(const QString &, const QByteArray &) { return false; } + QString vcsGetRepositoryURL(const QString &) { return QString(); } + bool vcsAnnotate(const QString &, int) { return false; } + +private: + Id m_id; + QString m_displayName; + QHash<QString, QString> m_managedDirs; + QSet<QString> m_managedFiles; + mutable int m_dirCount; + mutable int m_fileCount; +}; + +} // namespace Core +#endif + + #endif // IVERSIONCONTROL_H diff --git a/src/plugins/coreplugin/mimetypesettings.cpp b/src/plugins/coreplugin/mimetypesettings.cpp index 3d466dc437..0275e99816 100644 --- a/src/plugins/coreplugin/mimetypesettings.cpp +++ b/src/plugins/coreplugin/mimetypesettings.cpp @@ -41,6 +41,7 @@ #include <QCoreApplication> #include <QHash> #include <QMessageBox> +#include <QPointer> #include <QScopedPointer> #include <QSet> #include <QStringList> @@ -239,7 +240,6 @@ private slots: public: static const QChar kSemiColon; - QString m_keywords; MimeTypeSettingsModel *m_model; QSortFilterProxyModel *m_filterModel; int m_mimeForPatternSync; @@ -249,6 +249,7 @@ public: QList<int> m_modifiedMimeTypes; QString m_filterPattern; Ui::MimeTypeSettingsPage m_ui; + QPointer<QWidget> m_widget; }; const QChar MimeTypeSettingsPrivate::kSemiColon(QLatin1Char(';')); @@ -586,16 +587,13 @@ MimeTypeSettings::~MimeTypeSettings() delete d; } -bool MimeTypeSettings::matches(const QString &s) const +QWidget *MimeTypeSettings::widget() { - return d->m_keywords.contains(s, Qt::CaseInsensitive); -} - -QWidget *MimeTypeSettings::createPage(QWidget *parent) -{ - QWidget *w = new QWidget(parent); - d->configureUi(w); - return w; + if (!d->m_widget) { + d->m_widget = new QWidget; + d->configureUi(d->m_widget); + } + return d->m_widget; } void MimeTypeSettings::apply() @@ -625,6 +623,7 @@ void MimeTypeSettings::finish() d->updateMimeDatabase(); } d->resetState(); + delete d->m_widget; } } // Internal diff --git a/src/plugins/coreplugin/mimetypesettings.h b/src/plugins/coreplugin/mimetypesettings.h index d65ecf8cdb..244c188396 100644 --- a/src/plugins/coreplugin/mimetypesettings.h +++ b/src/plugins/coreplugin/mimetypesettings.h @@ -45,8 +45,7 @@ public: MimeTypeSettings(QObject *parent = 0); virtual ~MimeTypeSettings(); - virtual bool matches(const QString &s) const; - virtual QWidget *createPage(QWidget *parent); + virtual QWidget *widget(); virtual void apply(); virtual void finish(); diff --git a/src/plugins/coreplugin/testdatadir.cpp b/src/plugins/coreplugin/testdatadir.cpp index e6f08f26a6..5bbc946f88 100644 --- a/src/plugins/coreplugin/testdatadir.cpp +++ b/src/plugins/coreplugin/testdatadir.cpp @@ -35,7 +35,7 @@ #include <QString> #include <QTest> -using namespace Core::Internal::Tests; +using namespace Core::Tests; static void maybeAppendSlash(QString *string) { diff --git a/src/plugins/coreplugin/testdatadir.h b/src/plugins/coreplugin/testdatadir.h index fdd3bc6128..2b77cf2646 100644 --- a/src/plugins/coreplugin/testdatadir.h +++ b/src/plugins/coreplugin/testdatadir.h @@ -35,8 +35,15 @@ #include <QString> +#define QTC_DECLARE_MYTESTDATADIR(PATH) \ + class MyTestDataDir : public Core::Tests::TestDataDir \ + { \ + public: \ + MyTestDataDir(const QString &testDataDirectory = QString()) \ + : TestDataDir(QLatin1String(SRCDIR "/" PATH) + testDataDirectory) {} \ + }; + namespace Core { -namespace Internal { namespace Tests { class CORE_EXPORT TestDataDir @@ -53,7 +60,6 @@ private: }; } // namespace Tests -} // namespace Internal } // namespace Core #endif // TESTDATADIR_H diff --git a/src/plugins/coreplugin/toolsettings.cpp b/src/plugins/coreplugin/toolsettings.cpp index 24ecd95fcc..3da1264020 100644 --- a/src/plugins/coreplugin/toolsettings.cpp +++ b/src/plugins/coreplugin/toolsettings.cpp @@ -56,17 +56,12 @@ ToolSettings::ToolSettings(QObject *parent) : } -bool ToolSettings::matches(const QString & searchKeyWord) const +QWidget *ToolSettings::widget() { - return m_searchKeywords.contains(searchKeyWord, Qt::CaseInsensitive); -} - -QWidget *ToolSettings::createPage(QWidget *parent) -{ - m_widget = new ExternalToolConfig(parent); - m_widget->setTools(ExternalToolManager::toolsByCategory()); - if (m_searchKeywords.isEmpty()) - m_searchKeywords = m_widget->searchKeywords(); + if (!m_widget) { + m_widget = new ExternalToolConfig; + m_widget->setTools(ExternalToolManager::toolsByCategory()); + } return m_widget; } @@ -206,4 +201,5 @@ void ToolSettings::apply() void ToolSettings::finish() { + delete m_widget; } diff --git a/src/plugins/coreplugin/toolsettings.h b/src/plugins/coreplugin/toolsettings.h index cf59f08a14..3d6934cdef 100644 --- a/src/plugins/coreplugin/toolsettings.h +++ b/src/plugins/coreplugin/toolsettings.h @@ -46,13 +46,11 @@ class ToolSettings : public IOptionsPage public: explicit ToolSettings(QObject *parent = 0); - bool matches(const QString & searchKeyWord) const; - QWidget *createPage(QWidget *parent); + QWidget *widget(); void apply(); void finish(); private: - QString m_searchKeywords; QPointer<ExternalToolConfig> m_widget; }; diff --git a/src/plugins/coreplugin/vcsmanager.cpp b/src/plugins/coreplugin/vcsmanager.cpp index 080922d734..2293e034a8 100644 --- a/src/plugins/coreplugin/vcsmanager.cpp +++ b/src/plugins/coreplugin/vcsmanager.cpp @@ -59,6 +59,10 @@ static inline VersionControlList allVersionControls() return ExtensionSystem::PluginManager::getObjects<IVersionControl>(); } +#if defined(WITH_TESTS) +const char TEST_PREFIX[] = "/8E3A9BA0-0B97-40DF-AEC1-2BDF9FC9EDBE/"; +#endif + // ---- VCSManagerPrivate: // Maintains a cache of top-level directory->version control. @@ -196,7 +200,7 @@ VcsManager::~VcsManager() delete d; } -QObject *VcsManager::instance() +VcsManager *VcsManager::instance() { return m_instance; } @@ -233,8 +237,11 @@ IVersionControl* VcsManager::findVersionControlForDirectory(const QString &input { typedef QPair<QString, IVersionControl *> StringVersionControlPair; typedef QList<StringVersionControlPair> StringVersionControlPairs; - if (inputDirectory.isEmpty()) + if (inputDirectory.isEmpty()) { + if (topLevelDirectory) + topLevelDirectory->clear(); return 0; + } // Make sure we an absolute path: const QString directory = QDir(inputDirectory).absolutePath(); @@ -271,6 +278,11 @@ IVersionControl* VcsManager::findVersionControlForDirectory(const QString &input // Register Vcs(s) with the cache QString tmpDir = QFileInfo(directory).canonicalFilePath(); +#if defined WITH_TESTS + // Force caching of test directories (even though they do not exist): + if (directory.startsWith(QLatin1String(TEST_PREFIX))) + tmpDir = directory; +#endif // directory might refer to a historical directory which doesn't exist. // In this case, don't cache it. if (!tmpDir.isEmpty()) { @@ -352,24 +364,6 @@ IVersionControl *VcsManager::checkout(const QString &versionControlType, return 0; } -bool VcsManager::findVersionControl(const QString &versionControlType) -{ - foreach (IVersionControl * versionControl, allVersionControls()) { - if (versionControl->displayName() == versionControlType) - return true; - } - return false; -} - -QString VcsManager::repositoryUrl(const QString &directory) -{ - IVersionControl *vc = findVersionControlForDirectory(directory); - - if (vc && vc->supportsOperation(Core::IVersionControl::GetRepositoryRootOperation)) - return vc->vcsGetRepositoryURL(directory); - return QString(); -} - bool VcsManager::promptToDelete(IVersionControl *vc, const QString &fileName) { QTC_ASSERT(vc, return true); @@ -465,3 +459,181 @@ void VcsManager::configureVcs() } } // namespace Core + +#if defined(WITH_TESTS) + +#include <QtTest> + +#include "coreplugin.h" +#include "iversioncontrol.h" + +#include <extensionsystem/pluginmanager.h> + +namespace Core { +namespace Internal { + +const char ID_VCS_A[] = "A"; +const char ID_VCS_B[] = "B"; + +typedef QHash<QString, QString> FileHash; + +template<class T> +class ObjectPoolGuard +{ +public: + ObjectPoolGuard(T *watch) : m_watched(watch) + { + ExtensionSystem::PluginManager::addObject(watch); + } + + operator bool() { return m_watched; } + bool operator !() { return !m_watched; } + T &operator*() { return *m_watched; } + T *operator->() { return m_watched; } + T *value() { return m_watched; } + + ~ObjectPoolGuard() + { + ExtensionSystem::PluginManager::removeObject(m_watched); + delete m_watched; + } + +private: + T *m_watched; +}; + +static FileHash makeHash(const QStringList &list) +{ + FileHash result; + foreach (const QString &i, list) { + QStringList parts = i.split(QLatin1Char(':')); + QTC_ASSERT(parts.count() == 2, continue); + result.insert(QString::fromLatin1(TEST_PREFIX) + parts.at(0), + QString::fromLatin1(TEST_PREFIX) + parts.at(1)); + } + return result; +} + +static QString makeString(const QString &s) +{ + if (s.isEmpty()) + return QString(); + return QString::fromLatin1(TEST_PREFIX) + s; +} + +void CorePlugin::testVcsManager_data() +{ + // avoid conflicts with real files and directories: + + QTest::addColumn<QStringList>("dirsVcsA"); // <directory>:<toplevel> + QTest::addColumn<QStringList>("dirsVcsB"); // <directory>:<toplevel> + // <directory>:<toplevel>:<vcsid>:<- from cache, * from VCS> + QTest::addColumn<QStringList>("results"); + + QTest::newRow("A and B next to each other") + << (QStringList() + << QLatin1String("a:a") << QLatin1String("a/1:a") << QLatin1String("a/2:a") + << QLatin1String("a/2/5:a") << QLatin1String("a/2/5/6:a")) + << (QStringList() + << QLatin1String("b:b") << QLatin1String("b/3:b") << QLatin1String("b/4:b")) + << (QStringList() + << QLatin1String(":::-") // empty directory to look up + << QLatin1String("c:::*") // Neither in A nor B + << QLatin1String("a:a:A:*") // in A + << QLatin1String("b:b:B:*") // in B + << QLatin1String("b/3:b:B:*") // in B + << QLatin1String("b/4:b:B:*") // in B + << QLatin1String("a/1:a:A:*") // in A + << QLatin1String("a/2:a:A:*") // in A + << QLatin1String(":::-") // empty directory to look up + << QLatin1String("a/2/5/6:a:A:*") // in A + << QLatin1String("a/2/5:a:A:-") // in A (cached from before!) + // repeat: These need to come from the cache now: + << QLatin1String("c:::-") // Neither in A nor B + << QLatin1String("a:a:A:-") // in A + << QLatin1String("b:b:B:-") // in B + << QLatin1String("b/3:b:B:-") // in B + << QLatin1String("b/4:b:B:-") // in B + << QLatin1String("a/1:a:A:-") // in A + << QLatin1String("a/2:a:A:-") // in A + << QLatin1String("a/2/5/6:a:A:-") // in A + << QLatin1String("a/2/5:a:A:-") // in A + ); + QTest::newRow("B in A") + << (QStringList() + << QLatin1String("a:a") << QLatin1String("a/1:a") << QLatin1String("a/2:a") + << QLatin1String("a/2/5:a") << QLatin1String("a/2/5/6:a")) + << (QStringList() + << QLatin1String("a/1/b:a/1/b") << QLatin1String("a/1/b/3:a/1/b") + << QLatin1String("a/1/b/4:a/1/b") << QLatin1String("a/1/b/3/5:a/1/b") + << QLatin1String("a/1/b/3/5/6:a/1/b")) + << (QStringList() + << QLatin1String("a:a:A:*") // in A + << QLatin1String("c:::*") // Neither in A nor B + << QLatin1String("a/3:::*") // Neither in A nor B + << QLatin1String("a/1/b/x:::*") // Neither in A nor B + << QLatin1String("a/1/b:a/1/b:B:*") // in B + << QLatin1String("a/1:a:A:*") // in A + << QLatin1String("a/1/b/../../2:a:A:*") // in A + ); + QTest::newRow("A and B") // first one wins... + << (QStringList() << QLatin1String("a:a") << QLatin1String("a/1:a") << QLatin1String("a/2:a")) + << (QStringList() << QLatin1String("a:a") << QLatin1String("a/1:a") << QLatin1String("a/2:a")) + << (QStringList() << QLatin1String("a/2:a:A:*")); +} + +void CorePlugin::testVcsManager() +{ + // setup: + ObjectPoolGuard<TestVersionControl> vcsA(new TestVersionControl(ID_VCS_A, QLatin1String("A"))); + ObjectPoolGuard<TestVersionControl> vcsB(new TestVersionControl(ID_VCS_B, QLatin1String("B"))); + + // test: + QFETCH(QStringList, dirsVcsA); + QFETCH(QStringList, dirsVcsB); + QFETCH(QStringList, results); + + vcsA->setManagedDirectories(makeHash(dirsVcsA)); + vcsB->setManagedDirectories(makeHash(dirsVcsB)); + + QString realTopLevel = QLatin1String("ABC"); // Make sure this gets cleared if needed. + + // From VCSes: + int expectedCount = 0; + foreach (const QString &result, results) { + // qDebug() << "Expecting:" << result; + + QStringList split = result.split(QLatin1String(":")); + QCOMPARE(split.count(), 4); + QVERIFY(split.at(3) == QLatin1String("*") || split.at(3) == QLatin1String("-")); + + + const QString directory = split.at(0); + const QString topLevel = split.at(1); + const QString vcsId = split.at(2); + bool fromCache = split.at(3) == QLatin1String("-"); + + if (!fromCache && !directory.isEmpty()) + ++expectedCount; + + IVersionControl *vcs; + vcs = VcsManager::findVersionControlForDirectory(makeString(directory), &realTopLevel); + QCOMPARE(realTopLevel, makeString(topLevel)); + if (vcs) + QCOMPARE(vcs->id().toString(), vcsId); + else + QCOMPARE(QString(), vcsId); + QCOMPARE(vcsA->dirCount(), expectedCount); + QCOMPARE(vcsA->fileCount(), 0); + QCOMPARE(vcsB->dirCount(), expectedCount); + QCOMPARE(vcsB->fileCount(), 0); + } + + // teardown: + // handled by guards +} + +} // namespace Internal +} // namespace Core + +#endif diff --git a/src/plugins/coreplugin/vcsmanager.h b/src/plugins/coreplugin/vcsmanager.h index 8bc78de714..cb27af193c 100644 --- a/src/plugins/coreplugin/vcsmanager.h +++ b/src/plugins/coreplugin/vcsmanager.h @@ -58,23 +58,19 @@ class CORE_EXPORT VcsManager : public QObject Q_OBJECT public: - static QObject *instance(); + static VcsManager *instance(); static void extensionsInitialized(); static void resetVersionControlForDirectory(const QString &inputDirectory); static IVersionControl *findVersionControlForDirectory(const QString &directory, - QString *topLevelDirectory = 0); + QString *topLevelDirectory = 0); static QStringList repositories(const IVersionControl *); static IVersionControl *checkout(const QString &versionControlType, const QString &directory, const QByteArray &url); - // Used only by Trac plugin. - bool findVersionControl(const QString &versionControl); - // Used only by Trac plugin. - static QString repositoryUrl(const QString &directory); // Shows a confirmation dialog, whether the file should also be deleted // from revision control. Calls vcsDelete on the file. Returns false diff --git a/src/plugins/cpaster/cpasterplugin.cpp b/src/plugins/cpaster/cpasterplugin.cpp index b5d544e9a7..2916193889 100644 --- a/src/plugins/cpaster/cpasterplugin.cpp +++ b/src/plugins/cpaster/cpasterplugin.cpp @@ -211,7 +211,7 @@ void CodepasterPlugin::postEditor() if (ITextEditor *textEditor = qobject_cast<ITextEditor *>(editor)) { data = textEditor->selectedText(); if (data.isEmpty()) - data = textEditor->textDocument()->contents(); + data = textEditor->textDocument()->plainText(); mimeType = textEditor->document()->mimeType(); } } diff --git a/src/plugins/cpaster/fileshareprotocolsettingspage.cpp b/src/plugins/cpaster/fileshareprotocolsettingspage.cpp index 66c94ace1f..168afef539 100644 --- a/src/plugins/cpaster/fileshareprotocolsettingspage.cpp +++ b/src/plugins/cpaster/fileshareprotocolsettingspage.cpp @@ -104,10 +104,12 @@ FileShareProtocolSettingsPage::FileShareProtocolSettingsPage(const QSharedPointe setDisplayCategory(QCoreApplication::translate("CodePaster", Constants::CPASTER_SETTINGS_TR_CATEGORY)); } -QWidget *FileShareProtocolSettingsPage::createPage(QWidget *parent) +QWidget *FileShareProtocolSettingsPage::widget() { - m_widget = new FileShareProtocolSettingsWidget(parent); - m_widget->setSettings(*m_settings); + if (!m_widget) { + m_widget = new FileShareProtocolSettingsWidget; + m_widget->setSettings(*m_settings); + } return m_widget; } diff --git a/src/plugins/cpaster/fileshareprotocolsettingspage.h b/src/plugins/cpaster/fileshareprotocolsettingspage.h index 33c66736f8..feb6befea7 100644 --- a/src/plugins/cpaster/fileshareprotocolsettingspage.h +++ b/src/plugins/cpaster/fileshareprotocolsettingspage.h @@ -78,7 +78,7 @@ public: explicit FileShareProtocolSettingsPage(const QSharedPointer<FileShareProtocolSettings> &s, QObject *parent = 0); - QWidget *createPage(QWidget *parent); + QWidget *widget(); void apply(); void finish() { } diff --git a/src/plugins/cpaster/settingspage.cpp b/src/plugins/cpaster/settingspage.cpp index c9ae150add..f1df7d4bf9 100644 --- a/src/plugins/cpaster/settingspage.cpp +++ b/src/plugins/cpaster/settingspage.cpp @@ -90,13 +90,12 @@ SettingsPage::~SettingsPage() { } -QWidget *SettingsPage::createPage(QWidget *parent) +QWidget *SettingsPage::widget() { - m_widget = new SettingsWidget(m_protocols, parent); - m_widget->setSettings(*m_settings); - - if (m_searchKeywords.isEmpty()) - m_searchKeywords = m_widget->searchKeywords(); + if (!m_widget) { + m_widget = new SettingsWidget(m_protocols); + m_widget->setSettings(*m_settings); + } return m_widget; } @@ -111,11 +110,6 @@ void SettingsPage::apply() } } -bool SettingsPage::matches(const QString &s) const -{ - return m_searchKeywords.contains(s, Qt::CaseInsensitive); -} - void SettingsPage::addProtocol(const QString &name) { m_protocols.append(name); diff --git a/src/plugins/cpaster/settingspage.h b/src/plugins/cpaster/settingspage.h index ecb66f335c..98f19a19db 100644 --- a/src/plugins/cpaster/settingspage.h +++ b/src/plugins/cpaster/settingspage.h @@ -66,10 +66,9 @@ public: explicit SettingsPage(const QSharedPointer<Settings> &settings); ~SettingsPage(); - QWidget *createPage(QWidget *parent); + QWidget *widget(); void apply(); void finish() { } - bool matches(const QString &) const; void addProtocol(const QString& name); @@ -78,7 +77,6 @@ private: QPointer<SettingsWidget> m_widget; QStringList m_protocols; - QString m_searchKeywords; }; } // namespace CodePaster diff --git a/src/plugins/cppeditor/cppcodemodelinspectordialog.cpp b/src/plugins/cppeditor/cppcodemodelinspectordialog.cpp new file mode 100644 index 0000000000..812ef3eb80 --- /dev/null +++ b/src/plugins/cppeditor/cppcodemodelinspectordialog.cpp @@ -0,0 +1,2302 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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 "cppcodemodelinspectordialog.h" +#include "cppeditor.h" +#include "ui_cppcodemodelinspectordialog.h" + +#include <app/app_version.h> +#include <coreplugin/editormanager/editormanager.h> +#include <coreplugin/icore.h> +#include <cpptools/cppmodelmanagerinterface.h> +#include <cpptools/cppprojectfile.h> +#include <cpptools/cpptoolseditorsupport.h> +#include <projectexplorer/project.h> + +#include <cplusplus/CppDocument.h> +#include <cplusplus/Overview.h> +#include <cplusplus/Token.h> +#include <utils/qtcassert.h> + +#include <QAbstractTableModel> +#include <QLabel> +#include <QLineEdit> +#include <QPushButton> +#include <QSortFilterProxyModel> + +using namespace CppTools; + +namespace { + +// --- Utils -------------------------------------------------------------------------------------- + +QString toString(bool value) +{ + return value ? QLatin1String("Yes") : QLatin1String("No"); +} + +QString toString(unsigned value) +{ + return QString::number(value); +} + +QString toString(const QDateTime &dateTime) +{ + return dateTime.toString(QLatin1String("hh:mm:ss dd.MM.yy")); +} + +QString toString(CPlusPlus::Document::CheckMode checkMode) +{ +#define CASE_CHECKMODE(x) case CPlusPlus::Document::x: return QLatin1String(#x) + switch (checkMode) { + CASE_CHECKMODE(Unchecked); + CASE_CHECKMODE(FullCheck); + CASE_CHECKMODE(FastCheck); + // no default to get a compiler warning if anything is added + } +#undef CASE_CHECKMODE + return QString(); +} + +QString toString(CPlusPlus::Document::DiagnosticMessage::Level level) +{ +#define CASE_LEVEL(x) case CPlusPlus::Document::DiagnosticMessage::x: return QLatin1String(#x) + switch (level) { + CASE_LEVEL(Warning); + CASE_LEVEL(Error); + CASE_LEVEL(Fatal); + // no default to get a compiler warning if anything is added + } +#undef CASE_LEVEL + return QString(); +} + +QString toString(ProjectPart::CVersion cVersion) +{ +#define CASE_CVERSION(x) case ProjectPart::x: return QLatin1String(#x) + switch (cVersion) { + CASE_CVERSION(C89); + CASE_CVERSION(C99); + CASE_CVERSION(C11); + // no default to get a compiler warning if anything is added + } +#undef CASE_CVERSION + return QString(); +} + +QString toString(ProjectPart::CXXVersion cxxVersion) +{ +#define CASE_CXXVERSION(x) case ProjectPart::x: return QLatin1String(#x) + switch (cxxVersion) { + CASE_CXXVERSION(CXX98); + CASE_CXXVERSION(CXX11); + // no default to get a compiler warning if anything is added + } +#undef CASE_CXXVERSION + return QString(); +} + +QString toString(ProjectPart::CXXExtensions cxxExtension) +{ + QString result; + +#define CASE_CXXEXTENSION(ext) if (cxxExtension & ProjectPart::ext) \ + result += QLatin1String(#ext ", "); + + CASE_CXXEXTENSION(NoExtensions); + CASE_CXXEXTENSION(GnuExtensions); + CASE_CXXEXTENSION(MicrosoftExtensions); + CASE_CXXEXTENSION(BorlandExtensions); + CASE_CXXEXTENSION(OpenMPExtensions); +#undef CASE_CXXEXTENSION + if (result.endsWith(QLatin1String(", "))) + result.chop(2); + return result; +} + +QString toString(ProjectPart::QtVersion qtVersion) +{ +#define CASE_QTVERSION(x) case ProjectPart::x: return QLatin1String(#x) + switch (qtVersion) { + CASE_QTVERSION(UnknownQt); + CASE_QTVERSION(NoQt); + CASE_QTVERSION(Qt4); + CASE_QTVERSION(Qt5); + // no default to get a compiler warning if anything is added + } +#undef CASE_QTVERSION + return QString(); +} + +QString toString(const QList<ProjectFile> &projectFiles) +{ + QStringList filesList; + foreach (const ProjectFile &projectFile, projectFiles) + filesList << QDir::toNativeSeparators(projectFile.path); + qSort(filesList); + return filesList.join(QLatin1String("\n")); +} + +QString toString(ProjectFile::Kind kind) +{ +#define CASE_PROFECTFILEKIND(x) case ProjectFile::x: return QLatin1String(#x) + switch (kind) { + CASE_PROFECTFILEKIND(Unclassified); + CASE_PROFECTFILEKIND(CHeader); + CASE_PROFECTFILEKIND(CSource); + CASE_PROFECTFILEKIND(CXXHeader); + CASE_PROFECTFILEKIND(CXXSource); + CASE_PROFECTFILEKIND(ObjCHeader); + CASE_PROFECTFILEKIND(ObjCSource); + CASE_PROFECTFILEKIND(ObjCXXHeader); + CASE_PROFECTFILEKIND(ObjCXXSource); + CASE_PROFECTFILEKIND(CudaSource); + CASE_PROFECTFILEKIND(OpenCLSource); + // no default to get a compiler warning if anything is added + } +#undef CASE_PROFECTFILEKIND + return QString(); +} + +QString toString(CPlusPlus::Kind kind) +{ + using namespace CPlusPlus; +#define TOKEN(x) case x: return QLatin1String(#x) +#define TOKEN_AND_ALIASES(x,y) case x: return QLatin1String(#x "/" #y) + switch (kind) { + TOKEN(T_EOF_SYMBOL); + TOKEN(T_ERROR); + TOKEN(T_CPP_COMMENT); + TOKEN(T_CPP_DOXY_COMMENT); + TOKEN(T_COMMENT); + TOKEN(T_DOXY_COMMENT); + TOKEN(T_IDENTIFIER); + TOKEN(T_NUMERIC_LITERAL); + TOKEN(T_CHAR_LITERAL); + TOKEN(T_WIDE_CHAR_LITERAL); + TOKEN(T_UTF16_CHAR_LITERAL); + TOKEN(T_UTF32_CHAR_LITERAL); + TOKEN(T_STRING_LITERAL); + TOKEN(T_WIDE_STRING_LITERAL); + TOKEN(T_UTF8_STRING_LITERAL); + TOKEN(T_UTF16_STRING_LITERAL); + TOKEN(T_UTF32_STRING_LITERAL); + TOKEN(T_RAW_STRING_LITERAL); + TOKEN(T_RAW_WIDE_STRING_LITERAL); + TOKEN(T_RAW_UTF8_STRING_LITERAL); + TOKEN(T_RAW_UTF16_STRING_LITERAL); + TOKEN(T_RAW_UTF32_STRING_LITERAL); + TOKEN(T_AT_STRING_LITERAL); + TOKEN(T_ANGLE_STRING_LITERAL); + TOKEN_AND_ALIASES(T_AMPER, T_BITAND); + TOKEN_AND_ALIASES(T_AMPER_AMPER, T_AND); + TOKEN_AND_ALIASES(T_AMPER_EQUAL, T_AND_EQ); + TOKEN(T_ARROW); + TOKEN(T_ARROW_STAR); + TOKEN_AND_ALIASES(T_CARET, T_XOR); + TOKEN_AND_ALIASES(T_CARET_EQUAL, T_XOR_EQ); + TOKEN(T_COLON); + TOKEN(T_COLON_COLON); + TOKEN(T_COMMA); + TOKEN(T_SLASH); + TOKEN(T_SLASH_EQUAL); + TOKEN(T_DOT); + TOKEN(T_DOT_DOT_DOT); + TOKEN(T_DOT_STAR); + TOKEN(T_EQUAL); + TOKEN(T_EQUAL_EQUAL); + TOKEN_AND_ALIASES(T_EXCLAIM, T_NOT); + TOKEN_AND_ALIASES(T_EXCLAIM_EQUAL, T_NOT_EQ); + TOKEN(T_GREATER); + TOKEN(T_GREATER_EQUAL); + TOKEN(T_GREATER_GREATER); + TOKEN(T_GREATER_GREATER_EQUAL); + TOKEN(T_LBRACE); + TOKEN(T_LBRACKET); + TOKEN(T_LESS); + TOKEN(T_LESS_EQUAL); + TOKEN(T_LESS_LESS); + TOKEN(T_LESS_LESS_EQUAL); + TOKEN(T_LPAREN); + TOKEN(T_MINUS); + TOKEN(T_MINUS_EQUAL); + TOKEN(T_MINUS_MINUS); + TOKEN(T_PERCENT); + TOKEN(T_PERCENT_EQUAL); + TOKEN_AND_ALIASES(T_PIPE, T_BITOR); + TOKEN_AND_ALIASES(T_PIPE_EQUAL, T_OR_EQ); + TOKEN_AND_ALIASES(T_PIPE_PIPE, T_OR); + TOKEN(T_PLUS); + TOKEN(T_PLUS_EQUAL); + TOKEN(T_PLUS_PLUS); + TOKEN(T_POUND); + TOKEN(T_POUND_POUND); + TOKEN(T_QUESTION); + TOKEN(T_RBRACE); + TOKEN(T_RBRACKET); + TOKEN(T_RPAREN); + TOKEN(T_SEMICOLON); + TOKEN(T_STAR); + TOKEN(T_STAR_EQUAL); + TOKEN_AND_ALIASES(T_TILDE, T_COMPL); + TOKEN(T_TILDE_EQUAL); + TOKEN(T_ALIGNAS); + TOKEN(T_ALIGNOF); + TOKEN_AND_ALIASES(T_ASM, T___ASM/T___ASM__); + TOKEN(T_AUTO); + TOKEN(T_BOOL); + TOKEN(T_BREAK); + TOKEN(T_CASE); + TOKEN(T_CATCH); + TOKEN(T_CHAR); + TOKEN(T_CHAR16_T); + TOKEN(T_CHAR32_T); + TOKEN(T_CLASS); + TOKEN_AND_ALIASES(T_CONST, T___CONST/T___CONST__); + TOKEN(T_CONST_CAST); + TOKEN(T_CONSTEXPR); + TOKEN(T_CONTINUE); + TOKEN_AND_ALIASES(T_DECLTYPE, T___DECLTYPE); + TOKEN(T_DEFAULT); + TOKEN(T_DELETE); + TOKEN(T_DO); + TOKEN(T_DOUBLE); + TOKEN(T_DYNAMIC_CAST); + TOKEN(T_ELSE); + TOKEN(T_ENUM); + TOKEN(T_EXPLICIT); + TOKEN(T_EXPORT); + TOKEN(T_EXTERN); + TOKEN(T_FALSE); + TOKEN(T_FLOAT); + TOKEN(T_FOR); + TOKEN(T_FRIEND); + TOKEN(T_GOTO); + TOKEN(T_IF); + TOKEN_AND_ALIASES(T_INLINE, T___INLINE/T___INLINE__); + TOKEN(T_INT); + TOKEN(T_LONG); + TOKEN(T_MUTABLE); + TOKEN(T_NAMESPACE); + TOKEN(T_NEW); + TOKEN(T_NOEXCEPT); + TOKEN(T_NULLPTR); + TOKEN(T_OPERATOR); + TOKEN(T_PRIVATE); + TOKEN(T_PROTECTED); + TOKEN(T_PUBLIC); + TOKEN(T_REGISTER); + TOKEN(T_REINTERPRET_CAST); + TOKEN(T_RETURN); + TOKEN(T_SHORT); + TOKEN(T_SIGNED); + TOKEN(T_SIZEOF); + TOKEN(T_STATIC); + TOKEN(T_STATIC_ASSERT); + TOKEN(T_STATIC_CAST); + TOKEN(T_STRUCT); + TOKEN(T_SWITCH); + TOKEN(T_TEMPLATE); + TOKEN(T_THIS); + TOKEN(T_THREAD_LOCAL); + TOKEN(T_THROW); + TOKEN(T_TRUE); + TOKEN(T_TRY); + TOKEN(T_TYPEDEF); + TOKEN(T_TYPEID); + TOKEN(T_TYPENAME); + TOKEN(T_UNION); + TOKEN(T_UNSIGNED); + TOKEN(T_USING); + TOKEN(T_VIRTUAL); + TOKEN(T_VOID); + TOKEN_AND_ALIASES(T_VOLATILE, T___VOLATILE/T___VOLATILE__); + TOKEN(T_WCHAR_T); + TOKEN(T_WHILE); + TOKEN_AND_ALIASES(T___ATTRIBUTE__, T___ATTRIBUTE); + TOKEN(T___THREAD); + TOKEN_AND_ALIASES(T___TYPEOF__, T_TYPEOF/T___TYPEOF); + TOKEN(T_AT_CATCH); + TOKEN(T_AT_CLASS); + TOKEN(T_AT_COMPATIBILITY_ALIAS); + TOKEN(T_AT_DEFS); + TOKEN(T_AT_DYNAMIC); + TOKEN(T_AT_ENCODE); + TOKEN(T_AT_END); + TOKEN(T_AT_FINALLY); + TOKEN(T_AT_IMPLEMENTATION); + TOKEN(T_AT_INTERFACE); + TOKEN(T_AT_NOT_KEYWORD); + TOKEN(T_AT_OPTIONAL); + TOKEN(T_AT_PACKAGE); + TOKEN(T_AT_PRIVATE); + TOKEN(T_AT_PROPERTY); + TOKEN(T_AT_PROTECTED); + TOKEN(T_AT_PROTOCOL); + TOKEN(T_AT_PUBLIC); + TOKEN(T_AT_REQUIRED); + TOKEN(T_AT_SELECTOR); + TOKEN(T_AT_SYNCHRONIZED); + TOKEN(T_AT_SYNTHESIZE); + TOKEN(T_AT_THROW); + TOKEN(T_AT_TRY); + TOKEN(T_EMIT); + TOKEN(T_SIGNAL); + TOKEN(T_SLOT); + TOKEN(T_Q_SIGNAL); + TOKEN(T_Q_SLOT); + TOKEN(T_Q_SIGNALS); + TOKEN(T_Q_SLOTS); + TOKEN(T_Q_FOREACH); + TOKEN(T_Q_D); + TOKEN(T_Q_Q); + TOKEN(T_Q_INVOKABLE); + TOKEN(T_Q_PROPERTY); + TOKEN(T_Q_PRIVATE_PROPERTY); + TOKEN(T_Q_INTERFACES); + TOKEN(T_Q_EMIT); + TOKEN(T_Q_ENUMS); + TOKEN(T_Q_FLAGS); + TOKEN(T_Q_PRIVATE_SLOT); + TOKEN(T_Q_DECLARE_INTERFACE); + TOKEN(T_Q_OBJECT); + TOKEN(T_Q_GADGET); + // no default to get a compiler warning if anything is added + } +#undef TOKEN +#undef TOKEN_AND_ALIASES + return QString(); +} + +QString partsForFile(const QString &fileName) +{ + const QList<ProjectPart::Ptr> parts + = CppModelManagerInterface::instance()->projectPart(fileName); + QString result; + foreach (const ProjectPart::Ptr &part, parts) + result += part->displayName + QLatin1Char(','); + if (result.endsWith(QLatin1Char(','))) + result.chop(1); + return result; +} + +QString unresolvedFileNameWithDelimiters(const CPlusPlus::Document::Include &include) +{ + const QString unresolvedFileName = include.unresolvedFileName(); + if (include.type() == CPlusPlus::Client::IncludeLocal) + return QLatin1Char('"') + unresolvedFileName + QLatin1Char('"'); + return QLatin1Char('<') + unresolvedFileName + QLatin1Char('>'); +} + +QString pathListToString(const QStringList &pathList) +{ + QStringList result; + foreach (const QString &path, pathList) + result << QDir::toNativeSeparators(path); + return result.join(QLatin1String("\n")); +} + +QList<CPlusPlus::Document::Ptr> snapshotToList(const CPlusPlus::Snapshot &snapshot) +{ + QList<CPlusPlus::Document::Ptr> documents; + CPlusPlus::Snapshot::const_iterator it = snapshot.begin(), end = snapshot.end(); + for (; it != end; ++it) + documents.append(it.value()); + return documents; +} + +template <class T> void resizeColumns(QTreeView *view) +{ + for (int column = 0; column < T::ColumnCount - 1; ++column) + view->resizeColumnToContents(column); +} + +TextEditor::BaseTextEditor *currentEditor() +{ + return qobject_cast<TextEditor::BaseTextEditor*>(Core::EditorManager::currentEditor()); +} + +QString fileInCurrentEditor() +{ + if (TextEditor::BaseTextEditor *editor = currentEditor()) + return editor->document()->filePath(); + return QString(); +} + +class DepthFinder : public CPlusPlus::SymbolVisitor { +public: + DepthFinder() : m_symbol(0), m_depth(-1), m_foundDepth(-1), m_stop(false) {} + + int operator()(const CPlusPlus::Document::Ptr &document, CPlusPlus::Symbol *symbol) + { + m_symbol = symbol; + accept(document->globalNamespace()); + return m_foundDepth; + } + + bool preVisit(CPlusPlus::Symbol *symbol) + { + if (m_stop) + return false; + + if (symbol->asScope()) { + ++m_depth; + if (symbol == m_symbol) { + m_foundDepth = m_depth; + m_stop = true; + } + return true; + } + + return false; + } + + void postVisit(CPlusPlus::Symbol *symbol) + { + if (symbol->asScope()) + --m_depth; + } + +private: + CPlusPlus::Symbol *m_symbol; + int m_depth; + int m_foundDepth; + bool m_stop; +}; + +class CppCodeModelInspectorDumper +{ +public: + explicit CppCodeModelInspectorDumper(const CPlusPlus::Snapshot &globalSnapshot); + ~CppCodeModelInspectorDumper(); + + void dumpProjectInfos(const QList<CppModelManagerInterface::ProjectInfo> &projectInfos); + void dumpSnapshot(const CPlusPlus::Snapshot &snapshot, const QString &title, + bool isGlobalSnapshot = false); + void dumpWorkingCopy(const CppModelManagerInterface::WorkingCopy &workingCopy); + +private: + void dumpDocuments(const QList<CPlusPlus::Document::Ptr> &documents, + bool skipDetails = false); + static QByteArray indent(int level); + + CPlusPlus::Snapshot m_globalSnapshot; + QFile m_logFile; + QTextStream m_out; +}; + +CppCodeModelInspectorDumper::CppCodeModelInspectorDumper(const CPlusPlus::Snapshot &globalSnapshot) + : m_globalSnapshot(globalSnapshot), m_out(stderr) +{ + const QString logFileName = QDir::tempPath() + + QString::fromLatin1("/qtc-codemodelinspection.txt"); + m_logFile.setFileName(logFileName); + if (m_logFile.open(QIODevice::WriteOnly | QIODevice::Text)) { + m_out << "Code model inspection log file is \"" << QDir::toNativeSeparators(logFileName) + << "\".\n"; + m_out.setDevice(&m_logFile); + } + m_out << "*** START Code Model Inspection Report for "; + QString ideRevision; +#ifdef IDE_REVISION + ideRevision = QLatin1String(" from revision ") + + QString::fromLatin1(Core::Constants::IDE_REVISION_STR).left(10); +#endif + m_out << Core::ICore::versionString() << ideRevision << "\n"; + m_out << "Note: This file contains vim fold markers (\"{{{n\"). " + "Make use of them via \":set foldmethod=marker\".\n"; +} + +CppCodeModelInspectorDumper::~CppCodeModelInspectorDumper() +{ + m_out << "*** END Code Model Inspection Report\n"; +} + +void CppCodeModelInspectorDumper::dumpProjectInfos( + const QList<CppModelManagerInterface::ProjectInfo> &projectInfos) +{ + const QByteArray i1 = indent(1); + const QByteArray i2 = indent(2); + const QByteArray i3 = indent(3); + const QByteArray i4 = indent(4); + + m_out << "Projects loaded: " << projectInfos.size() << "{{{1\n"; + foreach (const CppModelManagerInterface::ProjectInfo &info, projectInfos) { + const QPointer<ProjectExplorer::Project> project = info.project(); + m_out << i1 << "Project " << project->displayName() << " (" << project->projectFilePath() + << "){{{2\n"; + + const QList<ProjectPart::Ptr> projectParts = info.projectParts(); + foreach (const ProjectPart::Ptr &part, projectParts) { + QString projectName = QLatin1String("<None>"); + QString projectFilePath = QLatin1String("<None>"); + if (ProjectExplorer::Project *project = part->project) { + projectName = project->displayName(); + projectFilePath = project->projectFilePath(); + } + m_out << i2 << "Project Part \"" << part->projectFile << "\"{{{3\n"; + m_out << i3 << "Project Part Name: " << part->displayName << "\n"; + m_out << i3 << "Project Name : " << projectName << "\n"; + m_out << i3 << "Project File : " << projectFilePath << "\n"; + m_out << i3 << "C Version : " << toString(part->cVersion) << "\n"; + m_out << i3 << "CXX Version : " << toString(part->cxxVersion) << "\n"; + m_out << i3 << "CXX Extensions : " << toString(part->cxxExtensions) << "\n"; + m_out << i3 << "Qt Version : " << toString(part->qtVersion) << "\n"; + + if (!part->files.isEmpty()) { + m_out << i3 << "Files:{{{4\n"; + foreach (const ProjectFile &projectFile, part->files) { + m_out << i4 << toString(projectFile.kind) << ": " << projectFile.path + << "\n"; + } + } + + if (!part->toolchainDefines.isEmpty()) { + m_out << i3 << "Toolchain Defines:{{{4\n"; + const QList<QByteArray> defineLines = part->toolchainDefines.split('\n'); + foreach (const QByteArray &defineLine, defineLines) + m_out << i4 << defineLine << "\n"; + } + if (!part->projectDefines.isEmpty()) { + m_out << i3 << "Project Defines:{{{4\n"; + const QList<QByteArray> defineLines = part->projectDefines.split('\n'); + foreach (const QByteArray &defineLine, defineLines) + m_out << i4 << defineLine << "\n"; + } + + if (!part->includePaths.isEmpty()) { + m_out << i3 << "Include Paths:{{{4\n"; + foreach (const QString &includePath, part->includePaths) + m_out << i4 << includePath << "\n"; + } + + if (!part->frameworkPaths.isEmpty()) { + m_out << i3 << "Framework Paths:{{{4\n"; + foreach (const QString &frameworkPath, part->frameworkPaths) + m_out << i4 << frameworkPath << "\n"; + } + + if (!part->precompiledHeaders.isEmpty()) { + m_out << i3 << "Precompiled Headers:{{{4\n"; + foreach (const QString &precompiledHeader, part->precompiledHeaders) + m_out << i4 << precompiledHeader << "\n"; + } + } // for part + } // for project Info +} + +void CppCodeModelInspectorDumper::dumpSnapshot(const CPlusPlus::Snapshot &snapshot, + const QString &title, bool isGlobalSnapshot) +{ + m_out << "Snapshot \"" << title << "\"{{{1\n"; + + const QByteArray i1 = indent(1); + const QList<CPlusPlus::Document::Ptr> documents = snapshotToList(snapshot); + + if (isGlobalSnapshot) { + if (!documents.isEmpty()) { + m_out << i1 << "Globally-Shared documents{{{2\n"; + dumpDocuments(documents, false); + } + } else { + // Divide into shared and not shared + QList<CPlusPlus::Document::Ptr> globallyShared; + QList<CPlusPlus::Document::Ptr> notGloballyShared; + foreach (const CPlusPlus::Document::Ptr &document, documents) { + CPlusPlus::Document::Ptr globalDocument = m_globalSnapshot.document(document->fileName()); + if (globalDocument && globalDocument->fingerprint() == document->fingerprint()) + globallyShared.append(document); + else + notGloballyShared.append(document); + } + + if (!notGloballyShared.isEmpty()) { + m_out << i1 << "Not-Globally-Shared documents:{{{2\n"; + dumpDocuments(notGloballyShared); + } + if (!globallyShared.isEmpty()) { + m_out << i1 << "Globally-Shared documents{{{2\n"; + dumpDocuments(globallyShared, true); + } + } +} + +void CppCodeModelInspectorDumper::dumpWorkingCopy( + const CppModelManagerInterface::WorkingCopy &workingCopy) +{ + m_out << "Working Copy contains " << workingCopy.size() << " entries{{{1\n"; + + const QByteArray i1 = indent(1); + QHashIterator<QString, QPair<QByteArray, unsigned> > it = workingCopy.iterator(); + while (it.hasNext()) { + it.next(); + const QString filePath = it.key(); + unsigned sourcRevision = it.value().second; + m_out << i1 << "rev=" << sourcRevision << ", " << filePath << "\n"; + } +} + +void CppCodeModelInspectorDumper::dumpDocuments(const QList<CPlusPlus::Document::Ptr> &documents, + bool skipDetails) +{ + const QByteArray i2 = indent(2); + const QByteArray i3 = indent(3); + const QByteArray i4 = indent(4); + foreach (const CPlusPlus::Document::Ptr &document, documents) { + if (skipDetails) { + m_out << i2 << "\"" << document->fileName() << "\"\n"; + continue; + } + + m_out << i2 << "Document \"" << document->fileName() << "\"{{{3\n"; + m_out << i3 << "Last Modified : " << toString(document->lastModified()) << "\n"; + m_out << i3 << "Revision : " << toString(document->revision()) << "\n"; + m_out << i3 << "Editor Revision: " << toString(document->editorRevision()) << "\n"; + m_out << i3 << "Check Mode : " << toString(document->checkMode()) << "\n"; + m_out << i3 << "Tokenized : " << toString(document->isTokenized()) << "\n"; + m_out << i3 << "Parsed : " << toString(document->isParsed()) << "\n"; + m_out << i3 << "Project Parts : " << partsForFile(document->fileName()) << "\n"; + + const QList<CPlusPlus::Document::Include> includes = document->unresolvedIncludes() + + document->resolvedIncludes(); + if (!includes.isEmpty()) { + m_out << i3 << "Includes:{{{4\n"; + foreach (const CPlusPlus::Document::Include &include, includes) { + m_out << i4 << "at line " << include.line() << ": " + << unresolvedFileNameWithDelimiters(include) << " ==> " + << include.resolvedFileName() << "\n"; + } + } + + const QList<CPlusPlus::Document::DiagnosticMessage> diagnosticMessages + = document->diagnosticMessages(); + if (!diagnosticMessages.isEmpty()) { + m_out << i3 << "Diagnostic Messages:{{{4\n"; + foreach (const CPlusPlus::Document::DiagnosticMessage &msg, diagnosticMessages) { + const CPlusPlus::Document::DiagnosticMessage::Level level + = static_cast<CPlusPlus::Document::DiagnosticMessage::Level>(msg.level()); + m_out << i4 << "at " << msg.line() << ":" << msg.column() << ", " << toString(level) + << ": " << msg.text() << "\n"; + } + } + + const QList<CPlusPlus::Macro> macroDefinitions = document->definedMacros(); + if (!macroDefinitions.isEmpty()) { + m_out << i3 << "(Un)Defined Macros:{{{4\n"; + foreach (const CPlusPlus::Macro ¯o, macroDefinitions) + m_out << i4 << "at line " << macro.line() << ": " << macro.toString() << "\n"; + } + + const QList<CPlusPlus::Document::MacroUse> macroUses = document->macroUses(); + if (!macroUses.isEmpty()) { + m_out << i3 << "Macro Uses:{{{4\n"; + foreach (const CPlusPlus::Document::MacroUse &use, macroUses) { + const QString type = use.isFunctionLike() + ? QLatin1String("function-like") : QLatin1String("object-like"); + m_out << i4 << "at line " << use.beginLine() << ", " + << QString::fromUtf8(use.macro().name()) << ", begin=" << use.begin() + << ", end=" << use.end() << ", " << type << ", args=" + << use.arguments().size() << "\n"; + } + } + + const QString source = QString::fromUtf8(document->utf8Source()); + if (!source.isEmpty()) { + m_out << i4 << "Source:{{{4\n"; + m_out << source; + m_out << "\n<<<EOF\n"; + } + } +} + +QByteArray CppCodeModelInspectorDumper::indent(int level) +{ + const QByteArray basicIndent(" "); + QByteArray indent = basicIndent; + while (level-- > 1) + indent += basicIndent; + return indent; +} + +} // anonymous namespace + +namespace CppEditor { +namespace Internal { + +// --- FilterableView ----------------------------------------------------------------------------- + +class FilterableView : public QWidget +{ + Q_OBJECT +public: + FilterableView(QWidget *parent); + + void setModel(QAbstractItemModel *model); + QItemSelectionModel *selectionModel() const; + void selectIndex(const QModelIndex &index); + void resizeColumns(int columnCount); + +signals: + void filterChanged(const QString &filterText); + +public slots: + void clearFilter(); + +private: + QTreeView *view; + QLineEdit *lineEdit; +}; + +FilterableView::FilterableView(QWidget *parent) + : QWidget(parent) +{ + view = new QTreeView(this); + view->setAlternatingRowColors(true); + view->setTextElideMode(Qt::ElideMiddle); + view->setSortingEnabled(true); + + lineEdit = new QLineEdit(this); + lineEdit->setPlaceholderText(QLatin1String("File Path")); + QObject::connect(lineEdit, SIGNAL(textChanged(QString)), SIGNAL(filterChanged(QString))); + + QLabel *label = new QLabel(QLatin1String("&Filter:"), this); + label->setBuddy(lineEdit); + + QPushButton *clearButton = new QPushButton(QLatin1String("&Clear"), this); + QObject::connect(clearButton, SIGNAL(clicked()), SLOT(clearFilter())); + + QHBoxLayout *filterBarLayout = new QHBoxLayout(); + filterBarLayout->addWidget(label); + filterBarLayout->addWidget(lineEdit); + filterBarLayout->addWidget(clearButton); + + QVBoxLayout *mainLayout = new QVBoxLayout(); + mainLayout->addWidget(view); + mainLayout->addLayout(filterBarLayout); + + setLayout(mainLayout); +} + +void FilterableView::setModel(QAbstractItemModel *model) +{ + view->setModel(model); +} + +QItemSelectionModel *FilterableView::selectionModel() const +{ + return view->selectionModel(); +} + +void FilterableView::selectIndex(const QModelIndex &index) +{ + if (index.isValid()) { + view->selectionModel()->setCurrentIndex(index, + QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); + } +} + +void FilterableView::resizeColumns(int columnCount) +{ + for (int column = 0; column < columnCount - 1; ++column) + view->resizeColumnToContents(column); +} + +void FilterableView::clearFilter() +{ + lineEdit->clear(); +} + +// --- KeyValueModel ------------------------------------------------------------------------------ + +class KeyValueModel : public QAbstractListModel +{ + Q_OBJECT +public: + typedef QList<QPair<QString, QString> > Table; + + KeyValueModel(QObject *parent); + void configure(const Table &table); + void clear(); + + enum Columns { KeyColumn, ValueColumn, ColumnCount }; + + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + QVariant headerData(int section, Qt::Orientation orientation, int role) const; + +private: + Table m_table; +}; + +KeyValueModel::KeyValueModel(QObject *parent) : QAbstractListModel(parent) +{ +} + +void KeyValueModel::configure(const Table &table) +{ + emit layoutAboutToBeChanged(); + m_table = table; + emit layoutChanged(); +} + +void KeyValueModel::clear() +{ + emit layoutAboutToBeChanged(); + m_table.clear(); + emit layoutChanged(); +} + +int KeyValueModel::rowCount(const QModelIndex &/*parent*/) const +{ + return m_table.size(); +} + +int KeyValueModel::columnCount(const QModelIndex &/*parent*/) const +{ + return KeyValueModel::ColumnCount; +} + +QVariant KeyValueModel::data(const QModelIndex &index, int role) const +{ + if (role == Qt::DisplayRole) { + const int row = index.row(); + const int column = index.column(); + if (column == KeyColumn) { + return m_table.at(row).first; + } else if (column == ValueColumn) { + return m_table.at(row).second; + } + } + return QVariant(); +} + +QVariant KeyValueModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { + switch (section) { + case KeyColumn: + return QLatin1String("Key"); + case ValueColumn: + return QLatin1String("Value"); + default: + return QVariant(); + } + } + return QVariant(); +} + +// --- SnapshotModel ------------------------------------------------------------------------------ + +class SnapshotModel : public QAbstractListModel +{ + Q_OBJECT +public: + SnapshotModel(QObject *parent); + void configure(const CPlusPlus::Snapshot &snapshot); + void setGlobalSnapshot(const CPlusPlus::Snapshot &snapshot); + + QModelIndex indexForDocument(const QString &filePath); + + enum Columns { SymbolCountColumn, SharedColumn, FilePathColumn, ColumnCount }; + + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + QVariant headerData(int section, Qt::Orientation orientation, int role) const; + +private: + QList<CPlusPlus::Document::Ptr> m_documents; + CPlusPlus::Snapshot m_globalSnapshot; +}; + +SnapshotModel::SnapshotModel(QObject *parent) : QAbstractListModel(parent) +{ +} + +void SnapshotModel::configure(const CPlusPlus::Snapshot &snapshot) +{ + emit layoutAboutToBeChanged(); + m_documents = snapshotToList(snapshot); + emit layoutChanged(); +} + +void SnapshotModel::setGlobalSnapshot(const CPlusPlus::Snapshot &snapshot) +{ + m_globalSnapshot = snapshot; +} + +QModelIndex SnapshotModel::indexForDocument(const QString &filePath) +{ + for (int i = 0, total = m_documents.size(); i < total; ++i) { + const CPlusPlus::Document::Ptr document = m_documents.at(i); + if (document->fileName() == filePath) + return index(i, FilePathColumn); + } + return QModelIndex(); +} + +int SnapshotModel::rowCount(const QModelIndex &/*parent*/) const +{ + return m_documents.size(); +} + +int SnapshotModel::columnCount(const QModelIndex &/*parent*/) const +{ + return SnapshotModel::ColumnCount; +} + +QVariant SnapshotModel::data(const QModelIndex &index, int role) const +{ + if (role == Qt::DisplayRole) { + const int column = index.column(); + CPlusPlus::Document::Ptr document = m_documents.at(index.row()); + if (column == SymbolCountColumn) { + return document->control()->symbolCount(); + } else if (column == SharedColumn) { + CPlusPlus::Document::Ptr globalDocument = m_globalSnapshot.document(document->fileName()); + const bool isShared + = globalDocument && globalDocument->fingerprint() == document->fingerprint(); + return toString(isShared); + } else if (column == FilePathColumn) { + return QDir::toNativeSeparators(document->fileName()); + } + } + return QVariant(); +} + +QVariant SnapshotModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { + switch (section) { + case SymbolCountColumn: + return QLatin1String("Symbols"); + case SharedColumn: + return QLatin1String("Shared"); + case FilePathColumn: + return QLatin1String("File Path"); + default: + return QVariant(); + } + } + return QVariant(); +} + +// --- IncludesModel ------------------------------------------------------------------------------ + +static bool includesSorter(const CPlusPlus::Document::Include &i1, + const CPlusPlus::Document::Include &i2) +{ + return i1.line() < i2.line(); +} + +class IncludesModel : public QAbstractListModel +{ + Q_OBJECT +public: + IncludesModel(QObject *parent); + void configure(const QList<CPlusPlus::Document::Include> &includes); + void clear(); + + enum Columns { ResolvedOrNotColumn, LineNumberColumn, FilePathsColumn, ColumnCount }; + + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + QVariant headerData(int section, Qt::Orientation orientation, int role) const; + +private: + QList<CPlusPlus::Document::Include> m_includes; +}; + +IncludesModel::IncludesModel(QObject *parent) : QAbstractListModel(parent) +{ +} + +void IncludesModel::configure(const QList<CPlusPlus::Document::Include> &includes) +{ + emit layoutAboutToBeChanged(); + m_includes = includes; + qStableSort(m_includes.begin(), m_includes.end(), includesSorter); + emit layoutChanged(); +} + +void IncludesModel::clear() +{ + emit layoutAboutToBeChanged(); + m_includes.clear(); + emit layoutChanged(); +} + +int IncludesModel::rowCount(const QModelIndex &/*parent*/) const +{ + return m_includes.size(); +} + +int IncludesModel::columnCount(const QModelIndex &/*parent*/) const +{ + return IncludesModel::ColumnCount; +} + +QVariant IncludesModel::data(const QModelIndex &index, int role) const +{ + if (role != Qt::DisplayRole && role != Qt::ForegroundRole) + return QVariant(); + + static const QBrush greenBrush(QColor(0, 139, 69)); + static const QBrush redBrush(QColor(205, 38, 38)); + + const CPlusPlus::Document::Include include = m_includes.at(index.row()); + const QString resolvedFileName = QDir::toNativeSeparators(include.resolvedFileName()); + const bool isResolved = !resolvedFileName.isEmpty(); + + if (role == Qt::DisplayRole) { + const int column = index.column(); + if (column == ResolvedOrNotColumn) { + return toString(isResolved); + } else if (column == LineNumberColumn) { + return include.line(); + } else if (column == FilePathsColumn) { + return QVariant(unresolvedFileNameWithDelimiters(include) + QLatin1String(" --> ") + + resolvedFileName); + } + } else if (role == Qt::ForegroundRole) { + return isResolved ? greenBrush : redBrush; + } + + return QVariant(); +} + +QVariant IncludesModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { + switch (section) { + case ResolvedOrNotColumn: + return QLatin1String("Resolved"); + case LineNumberColumn: + return QLatin1String("Line"); + case FilePathsColumn: + return QLatin1String("File Paths"); + default: + return QVariant(); + } + } + return QVariant(); +} + +// --- DiagnosticMessagesModel -------------------------------------------------------------------- + +static bool diagnosticMessagesModelSorter(const CPlusPlus::Document::DiagnosticMessage &m1, + const CPlusPlus::Document::DiagnosticMessage &m2) +{ + return m1.line() < m2.line(); +} + +class DiagnosticMessagesModel : public QAbstractListModel +{ + Q_OBJECT +public: + DiagnosticMessagesModel(QObject *parent); + void configure(const QList<CPlusPlus::Document::DiagnosticMessage> &messages); + void clear(); + + enum Columns { LevelColumn, LineColumnNumberColumn, MessageColumn, ColumnCount }; + + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + QVariant headerData(int section, Qt::Orientation orientation, int role) const; + +private: + QList<CPlusPlus::Document::DiagnosticMessage> m_messages; +}; + +DiagnosticMessagesModel::DiagnosticMessagesModel(QObject *parent) : QAbstractListModel(parent) +{ +} + +void DiagnosticMessagesModel::configure( + const QList<CPlusPlus::Document::DiagnosticMessage> &messages) +{ + emit layoutAboutToBeChanged(); + m_messages = messages; + qStableSort(m_messages.begin(), m_messages.end(), diagnosticMessagesModelSorter); + emit layoutChanged(); +} + +void DiagnosticMessagesModel::clear() +{ + emit layoutAboutToBeChanged(); + m_messages.clear(); + emit layoutChanged(); +} + +int DiagnosticMessagesModel::rowCount(const QModelIndex &/*parent*/) const +{ + return m_messages.size(); +} + +int DiagnosticMessagesModel::columnCount(const QModelIndex &/*parent*/) const +{ + return DiagnosticMessagesModel::ColumnCount; +} + +QVariant DiagnosticMessagesModel::data(const QModelIndex &index, int role) const +{ + if (role != Qt::DisplayRole && role != Qt::ForegroundRole) + return QVariant(); + + static const QBrush yellowOrangeBrush(QColor(237, 145, 33)); + static const QBrush redBrush(QColor(205, 38, 38)); + static const QBrush darkRedBrushQColor(QColor(139, 0, 0)); + + const CPlusPlus::Document::DiagnosticMessage message = m_messages.at(index.row()); + const CPlusPlus::Document::DiagnosticMessage::Level level + = static_cast<CPlusPlus::Document::DiagnosticMessage::Level>(message.level()); + + if (role == Qt::DisplayRole) { + const int column = index.column(); + if (column == LevelColumn) { + return toString(level); + } else if (column == LineColumnNumberColumn) { + return QVariant(QString::number(message.line()) + QLatin1Char(':') + + QString::number(message.column())); + } else if (column == MessageColumn) { + return message.text(); + } + } else if (role == Qt::ForegroundRole) { + switch (level) { + case CPlusPlus::Document::DiagnosticMessage::Warning: + return yellowOrangeBrush; + case CPlusPlus::Document::DiagnosticMessage::Error: + return redBrush; + case CPlusPlus::Document::DiagnosticMessage::Fatal: + return darkRedBrushQColor; + default: + return QVariant(); + } + } + + return QVariant(); +} + +QVariant DiagnosticMessagesModel::headerData(int section, Qt::Orientation orientation, int role) + const +{ + if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { + switch (section) { + case LevelColumn: + return QLatin1String("Level"); + case LineColumnNumberColumn: + return QLatin1String("Line:Column"); + case MessageColumn: + return QLatin1String("Message"); + default: + return QVariant(); + } + } + return QVariant(); +} + +// --- MacrosModel -------------------------------------------------------------------------------- + +class MacrosModel : public QAbstractListModel +{ + Q_OBJECT +public: + MacrosModel(QObject *parent); + void configure(const QList<CPlusPlus::Macro> ¯os); + void clear(); + + enum Columns { LineNumberColumn, MacroColumn, ColumnCount }; + + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + QVariant headerData(int section, Qt::Orientation orientation, int role) const; + +private: + QList<CPlusPlus::Macro> m_macros; +}; + +MacrosModel::MacrosModel(QObject *parent) : QAbstractListModel(parent) +{ +} + +void MacrosModel::configure(const QList<CPlusPlus::Macro> ¯os) +{ + emit layoutAboutToBeChanged(); + m_macros = macros; + emit layoutChanged(); +} + +void MacrosModel::clear() +{ + emit layoutAboutToBeChanged(); + m_macros.clear(); + emit layoutChanged(); +} + +int MacrosModel::rowCount(const QModelIndex &/*parent*/) const +{ + return m_macros.size(); +} + +int MacrosModel::columnCount(const QModelIndex &/*parent*/) const +{ + return MacrosModel::ColumnCount; +} + +QVariant MacrosModel::data(const QModelIndex &index, int role) const +{ + const int column = index.column(); + if (role == Qt::DisplayRole || (role == Qt::ToolTipRole && column == MacroColumn)) { + const CPlusPlus::Macro macro = m_macros.at(index.row()); + if (column == LineNumberColumn) + return macro.line(); + else if (column == MacroColumn) + return macro.toString(); + } else if (role == Qt::TextAlignmentRole) { + return Qt::AlignTop + Qt::AlignLeft; + } + return QVariant(); +} + +QVariant MacrosModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { + switch (section) { + case LineNumberColumn: + return QLatin1String("Line"); + case MacroColumn: + return QLatin1String("Macro"); + default: + return QVariant(); + } + } + return QVariant(); +} + +// --- SymbolsModel ------------------------------------------------------------------------------- + +class SymbolsModel : public QAbstractItemModel +{ + Q_OBJECT +public: + SymbolsModel(QObject *parent); + void configure(const CPlusPlus::Document::Ptr &document); + void clear(); + + enum Columns { SymbolColumn, LineNumberColumn, ColumnCount }; + + QModelIndex index(int row, int column, const QModelIndex &parent) const; + QModelIndex parent(const QModelIndex &child) const; + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + QVariant headerData(int section, Qt::Orientation orientation, int role) const; + +private: + CPlusPlus::Document::Ptr m_document; +}; + +SymbolsModel::SymbolsModel(QObject *parent) : QAbstractItemModel(parent) +{ +} + +void SymbolsModel::configure(const CPlusPlus::Document::Ptr &document) +{ + QTC_CHECK(document); + emit layoutAboutToBeChanged(); + m_document = document; + emit layoutChanged(); +} + +void SymbolsModel::clear() +{ + emit layoutAboutToBeChanged(); + m_document.clear(); + emit layoutChanged(); +} + +static CPlusPlus::Symbol *indexToSymbol(const QModelIndex &index) +{ + if (CPlusPlus::Symbol *symbol = static_cast<CPlusPlus::Symbol*>(index.internalPointer())) + return symbol; + return 0; +} + +static CPlusPlus::Scope *indexToScope(const QModelIndex &index) +{ + if (CPlusPlus::Symbol *symbol = indexToSymbol(index)) + return symbol->asScope(); + return 0; +} + +QModelIndex SymbolsModel::index(int row, int column, const QModelIndex &parent) const +{ + CPlusPlus::Scope *scope = 0; + if (parent.isValid()) + scope = indexToScope(parent); + else if (m_document) + scope = m_document->globalNamespace(); + + if (scope) { + if ((unsigned)row < scope->memberCount()) + return createIndex(row, column, scope->memberAt(row)); + } + + return QModelIndex(); +} + +QModelIndex SymbolsModel::parent(const QModelIndex &child) const +{ + if (!child.isValid()) + return QModelIndex(); + + if (CPlusPlus::Symbol *symbol = indexToSymbol(child)) { + if (CPlusPlus::Scope *scope = symbol->enclosingScope()) { + const int row = DepthFinder()(m_document, scope); + return createIndex(row, 0, scope); + } + } + + return QModelIndex(); +} + +int SymbolsModel::rowCount(const QModelIndex &parent) const +{ + if (parent.isValid()) { + if (CPlusPlus::Scope *scope = indexToScope(parent)) + return scope->memberCount(); + } else { + if (m_document) + return m_document->globalNamespace()->memberCount(); + } + return 0; +} + +int SymbolsModel::columnCount(const QModelIndex &) const +{ + return ColumnCount; +} + +QVariant SymbolsModel::data(const QModelIndex &index, int role) const +{ + const int column = index.column(); + if (role == Qt::DisplayRole) { + CPlusPlus::Symbol *symbol = indexToSymbol(index); + if (!symbol) + return QVariant(); + if (column == LineNumberColumn) { + return symbol->line(); + } else if (column == SymbolColumn) { + QString name = CPlusPlus::Overview().prettyName(symbol->name()); + if (name.isEmpty()) + name = QLatin1String("<no name>"); + return name; + } + } + return QVariant(); +} + +QVariant SymbolsModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { + switch (section) { + case SymbolColumn: + return QLatin1String("Symbol"); + case LineNumberColumn: + return QLatin1String("Line"); + default: + return QVariant(); + } + } + return QVariant(); +} + +// --- TokensModel -------------------------------------------------------------------------------- + +class TokensModel : public QAbstractListModel +{ + Q_OBJECT +public: + TokensModel(QObject *parent); + void configure(CPlusPlus::TranslationUnit *translationUnit); + void clear(); + + enum Columns { SpelledColumn, KindColumn, IndexColumn, OffsetColumn, LineColumnNumberColumn, + LengthColumn, GeneratedColumn, ExpandedColumn, WhiteSpaceColumn, NewlineColumn, + ColumnCount }; + + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + QVariant headerData(int section, Qt::Orientation orientation, int role) const; + +private: + struct TokenInfo { + CPlusPlus::Token token; + unsigned line; + unsigned column; + }; + QList<TokenInfo> m_tokenInfos; +}; + +TokensModel::TokensModel(QObject *parent) : QAbstractListModel(parent) +{ +} + +void TokensModel::configure(CPlusPlus::TranslationUnit *translationUnit) +{ + if (!translationUnit) + return; + + emit layoutAboutToBeChanged(); + m_tokenInfos.clear(); + for (int i = 0, total = translationUnit->tokenCount(); i < total; ++i) { + TokenInfo info; + info.token = translationUnit->tokenAt(i); + translationUnit->getPosition(info.token.offset, &info.line, &info.column); + m_tokenInfos.append(info); + } + emit layoutChanged(); +} + +void TokensModel::clear() +{ + emit layoutAboutToBeChanged(); + m_tokenInfos.clear(); + emit layoutChanged(); +} + +int TokensModel::rowCount(const QModelIndex &/*parent*/) const +{ + return m_tokenInfos.size(); +} + +int TokensModel::columnCount(const QModelIndex &/*parent*/) const +{ + return TokensModel::ColumnCount; +} + +QVariant TokensModel::data(const QModelIndex &index, int role) const +{ + const int column = index.column(); + if (role == Qt::DisplayRole) { + const TokenInfo info = m_tokenInfos.at(index.row()); + const CPlusPlus::Token token = info.token; + if (column == SpelledColumn) + return QString::fromUtf8(token.spell()); + else if (column == KindColumn) + return toString(static_cast<CPlusPlus::Kind>(token.kind())); + else if (column == IndexColumn) + return index.row(); + else if (column == OffsetColumn) + return token.offset; + else if (column == LineColumnNumberColumn) + return QString::fromLatin1("%1:%2").arg(toString(info.line), toString(info.column)); + else if (column == LengthColumn) + return toString(token.length()); + else if (column == GeneratedColumn) + return toString(token.generated()); + else if (column == ExpandedColumn) + return toString(token.expanded()); + else if (column == WhiteSpaceColumn) + return toString(token.whitespace()); + else if (column == NewlineColumn) + return toString(token.newline()); + } else if (role == Qt::TextAlignmentRole) { + return Qt::AlignTop + Qt::AlignLeft; + } + return QVariant(); +} + +QVariant TokensModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { + switch (section) { + case SpelledColumn: + return QLatin1String("Spelled"); + case KindColumn: + return QLatin1String("Kind"); + case IndexColumn: + return QLatin1String("Index"); + case OffsetColumn: + return QLatin1String("Offset"); + case LineColumnNumberColumn: + return QLatin1String("Line:Column"); + case LengthColumn: + return QLatin1String("Length"); + case GeneratedColumn: + return QLatin1String("Generated"); + case ExpandedColumn: + return QLatin1String("Expanded"); + case WhiteSpaceColumn: + return QLatin1String("Whitespace"); + case NewlineColumn: + return QLatin1String("Newline"); + default: + return QVariant(); + } + } + return QVariant(); +} + +// --- ProjectPartsModel -------------------------------------------------------------------------- + +class ProjectPartsModel : public QAbstractListModel +{ + Q_OBJECT +public: + ProjectPartsModel(QObject *parent); + + void configure(const QList<CppModelManagerInterface::ProjectInfo> &projectInfos, + const ProjectPart::Ptr ¤tEditorsProjectPart); + + QModelIndex indexForCurrentEditorsProjectPart() const; + ProjectPart::Ptr projectPartForProjectFile(const QString &projectFilePath) const; + + enum Columns { PartNameColumn, PartFilePathColumn, ColumnCount }; + + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + QVariant headerData(int section, Qt::Orientation orientation, int role) const; + +private: + QList<ProjectPart::Ptr> m_projectPartsList; + int m_currentEditorsProjectPartIndex; +}; + +ProjectPartsModel::ProjectPartsModel(QObject *parent) + : QAbstractListModel(parent), m_currentEditorsProjectPartIndex(-1) +{ +} + +void ProjectPartsModel::configure(const QList<CppModelManagerInterface::ProjectInfo> &projectInfos, + const ProjectPart::Ptr ¤tEditorsProjectPart) +{ + emit layoutAboutToBeChanged(); + m_projectPartsList.clear(); + foreach (const CppModelManagerInterface::ProjectInfo &info, projectInfos) { + foreach (const ProjectPart::Ptr &projectPart, info.projectParts()) { + if (!m_projectPartsList.contains(projectPart)) { + m_projectPartsList << projectPart; + if (projectPart == currentEditorsProjectPart) + m_currentEditorsProjectPartIndex = m_projectPartsList.size() - 1; + } + } + } + emit layoutChanged(); +} + +QModelIndex ProjectPartsModel::indexForCurrentEditorsProjectPart() const +{ + if (m_currentEditorsProjectPartIndex == -1) + return QModelIndex(); + return createIndex(m_currentEditorsProjectPartIndex, PartFilePathColumn); +} + +ProjectPart::Ptr ProjectPartsModel::projectPartForProjectFile(const QString &projectFilePath) const +{ + foreach (const ProjectPart::Ptr &part, m_projectPartsList) { + if (part->projectFile == projectFilePath) + return part; + } + return ProjectPart::Ptr(); +} + +int ProjectPartsModel::rowCount(const QModelIndex &/*parent*/) const +{ + return m_projectPartsList.size(); +} + +int ProjectPartsModel::columnCount(const QModelIndex &/*parent*/) const +{ + return ProjectPartsModel::ColumnCount; +} + +QVariant ProjectPartsModel::data(const QModelIndex &index, int role) const +{ + const int row = index.row(); + if (role == Qt::DisplayRole) { + const int column = index.column(); + if (column == PartNameColumn) + return m_projectPartsList.at(row)->displayName; + else if (column == PartFilePathColumn) + return QDir::toNativeSeparators(m_projectPartsList.at(row)->projectFile); + } + return QVariant(); +} + +QVariant ProjectPartsModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { + switch (section) { + case PartNameColumn: + return QLatin1String("Name"); + case PartFilePathColumn: + return QLatin1String("Project File Path"); + default: + return QVariant(); + } + } + return QVariant(); +} + +// --- WorkingCopyModel --------------------------------------------------------------------------- + +class WorkingCopyModel : public QAbstractListModel +{ + Q_OBJECT +public: + WorkingCopyModel(QObject *parent); + + void configure(const CppModelManagerInterface::WorkingCopy &workingCopy); + QModelIndex indexForFile(const QString &filePath); + + enum Columns { RevisionColumn, FilePathColumn, ColumnCount }; + + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + QVariant headerData(int section, Qt::Orientation orientation, int role) const; + +private: + struct WorkingCopyEntry { + WorkingCopyEntry(const QString &filePath, const QByteArray &source, unsigned revision) + : filePath(filePath), source(source), revision(revision) + {} + + QString filePath; + QByteArray source; + unsigned revision; + }; + + QList<WorkingCopyEntry> m_workingCopyList; +}; + +WorkingCopyModel::WorkingCopyModel(QObject *parent) : QAbstractListModel(parent) +{ +} + +void WorkingCopyModel::configure(const CppModelManagerInterface::WorkingCopy &workingCopy) +{ + emit layoutAboutToBeChanged(); + m_workingCopyList.clear(); + QHashIterator<QString, QPair<QByteArray, unsigned> > it = workingCopy.iterator(); + while (it.hasNext()) { + it.next(); + m_workingCopyList << WorkingCopyEntry(it.key(), it.value().first, it.value().second); + } + emit layoutChanged(); +} + +QModelIndex WorkingCopyModel::indexForFile(const QString &filePath) +{ + for (int i = 0, total = m_workingCopyList.size(); i < total; ++i) { + const WorkingCopyEntry entry = m_workingCopyList.at(i); + if (entry.filePath == filePath) + return index(i, FilePathColumn); + } + return QModelIndex(); +} + +int WorkingCopyModel::rowCount(const QModelIndex &/*parent*/) const +{ + return m_workingCopyList.size(); +} + +int WorkingCopyModel::columnCount(const QModelIndex &/*parent*/) const +{ + return WorkingCopyModel::ColumnCount; +} + +QVariant WorkingCopyModel::data(const QModelIndex &index, int role) const +{ + const int row = index.row(); + if (role == Qt::DisplayRole) { + const int column = index.column(); + if (column == RevisionColumn) + return m_workingCopyList.at(row).revision; + else if (column == FilePathColumn) + return m_workingCopyList.at(row).filePath; + } else if (role == Qt::UserRole) { + return m_workingCopyList.at(row).source; + } + return QVariant(); +} + +QVariant WorkingCopyModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { + switch (section) { + case RevisionColumn: + return QLatin1String("Revision"); + case FilePathColumn: + return QLatin1String("File Path"); + default: + return QVariant(); + } + } + return QVariant(); +} + +// --- SnapshotInfo ------------------------------------------------------------------------------- + +class SnapshotInfo +{ +public: + enum Type { GlobalSnapshot, EditorSnapshot }; + SnapshotInfo(const CPlusPlus::Snapshot &snapshot, Type type) + : snapshot(snapshot), type(type) {} + + CPlusPlus::Snapshot snapshot; + Type type; +}; + +// --- CppCodeModelInspectorDialog ---------------------------------------------------------------- + +CppCodeModelInspectorDialog::CppCodeModelInspectorDialog(QWidget *parent) + : QDialog(parent) + , m_ui(new Ui::CppCodeModelInspectorDialog) + , m_snapshotInfos(new QList<SnapshotInfo>()) + , m_snapshotView(new FilterableView(this)) + , m_snapshotModel(new SnapshotModel(this)) + , m_proxySnapshotModel(new QSortFilterProxyModel(this)) + , m_docGenericInfoModel(new KeyValueModel(this)) + , m_docIncludesModel(new IncludesModel(this)) + , m_docDiagnosticMessagesModel(new DiagnosticMessagesModel(this)) + , m_docMacrosModel(new MacrosModel(this)) + , m_docSymbolsModel(new SymbolsModel(this)) + , m_docTokensModel(new TokensModel(this)) + , m_projectPartsView(new FilterableView(this)) + , m_projectPartsModel(new ProjectPartsModel(this)) + , m_proxyProjectPartsModel(new QSortFilterProxyModel(this)) + , m_partGenericInfoModel(new KeyValueModel(this)) + , m_workingCopyView(new FilterableView(this)) + , m_workingCopyModel(new WorkingCopyModel(this)) + , m_proxyWorkingCopyModel(new QSortFilterProxyModel(this)) +{ + m_ui->setupUi(this); + m_ui->snapshotSelectorAndViewLayout->addWidget(m_snapshotView); + m_ui->projectPartsSplitter->insertWidget(0, m_projectPartsView); + m_ui->workingCopySplitter->insertWidget(0, m_workingCopyView); + + setAttribute(Qt::WA_DeleteOnClose); + connect(Core::ICore::instance(), SIGNAL(coreAboutToClose()), SLOT(close())); + + m_proxySnapshotModel->setSourceModel(m_snapshotModel); + m_proxySnapshotModel->setFilterKeyColumn(SnapshotModel::FilePathColumn); + m_snapshotView->setModel(m_proxySnapshotModel); + m_ui->docGeneralView->setModel(m_docGenericInfoModel); + m_ui->docIncludesView->setModel(m_docIncludesModel); + m_ui->docDiagnosticMessagesView->setModel(m_docDiagnosticMessagesModel); + m_ui->docDefinedMacrosView->setModel(m_docMacrosModel); + m_ui->docSymbolsView->setModel(m_docSymbolsModel); + m_ui->docTokensView->setModel(m_docTokensModel); + + m_proxyProjectPartsModel->setSourceModel(m_projectPartsModel); + m_proxyProjectPartsModel->setFilterKeyColumn(ProjectPartsModel::PartFilePathColumn); + m_projectPartsView->setModel(m_proxyProjectPartsModel); + m_ui->partGeneralView->setModel(m_partGenericInfoModel); + + m_proxyWorkingCopyModel->setSourceModel(m_workingCopyModel); + m_proxyWorkingCopyModel->setFilterKeyColumn(WorkingCopyModel::FilePathColumn); + m_workingCopyView->setModel(m_proxyWorkingCopyModel); + + connect(m_snapshotView->selectionModel(), + SIGNAL(currentRowChanged(QModelIndex ,QModelIndex)), + SLOT(onDocumentSelected(QModelIndex, QModelIndex))); + connect(m_snapshotView, SIGNAL(filterChanged(QString)), + SLOT(onSnapshotFilterChanged(QString))); + connect(m_ui->snapshotSelector, SIGNAL(currentIndexChanged(int)), + SLOT(onSnapshotSelected(int))); + connect(m_ui->docSymbolsView, SIGNAL(expanded(QModelIndex)), + SLOT(onSymbolsViewExpandedOrCollapsed(QModelIndex))); + connect(m_ui->docSymbolsView, SIGNAL(collapsed(QModelIndex)), + SLOT(onSymbolsViewExpandedOrCollapsed(QModelIndex))); + + connect(m_projectPartsView->selectionModel(), + SIGNAL(currentRowChanged(QModelIndex ,QModelIndex)), + SLOT(onProjectPartSelected(QModelIndex, QModelIndex))); + connect(m_projectPartsView, SIGNAL(filterChanged(QString)), + SLOT(onProjectPartFilterChanged(QString))); + + connect(m_workingCopyView->selectionModel(), + SIGNAL(currentRowChanged(QModelIndex ,QModelIndex)), + SLOT(onWorkingCopyDocumentSelected(QModelIndex, QModelIndex))); + connect(m_workingCopyView, SIGNAL(filterChanged(QString)), + SLOT(onWorkingCopyFilterChanged(QString))); + + connect(m_ui->refreshButton, SIGNAL(clicked()), SLOT(onRefreshRequested())); + connect(m_ui->closeButton, SIGNAL(clicked()), SLOT(close())); + + refresh(); +} + +CppCodeModelInspectorDialog::~CppCodeModelInspectorDialog() +{ + delete m_snapshotInfos; + delete m_ui; +} + +void CppCodeModelInspectorDialog::onRefreshRequested() +{ + refresh(); +} + +void CppCodeModelInspectorDialog::onSnapshotFilterChanged(const QString &pattern) +{ + m_proxySnapshotModel->setFilterWildcard(pattern); +} + +void CppCodeModelInspectorDialog::onSnapshotSelected(int row) +{ + if (row < 0 || row >= m_snapshotInfos->size()) + return; + + m_snapshotView->clearFilter(); + const SnapshotInfo info = m_snapshotInfos->at(row); + m_snapshotModel->configure(info.snapshot); + m_snapshotView->resizeColumns(SnapshotModel::ColumnCount); + + if (info.type == SnapshotInfo::GlobalSnapshot) { + // Select first document + const QModelIndex index = m_proxySnapshotModel->index(0, SnapshotModel::FilePathColumn); + m_snapshotView->selectIndex(index); + } else if (info.type == SnapshotInfo::EditorSnapshot) { + // Select first document, unless we can find the editor document + QModelIndex index = m_snapshotModel->indexForDocument(fileInCurrentEditor()); + index = m_proxySnapshotModel->mapFromSource(index); + if (!index.isValid()) + index = m_proxySnapshotModel->index(0, SnapshotModel::FilePathColumn); + m_snapshotView->selectIndex(index); + } +} + +void CppCodeModelInspectorDialog::onDocumentSelected(const QModelIndex ¤t, + const QModelIndex &) +{ + if (current.isValid()) { + const QModelIndex index = m_proxySnapshotModel->index(current.row(), + SnapshotModel::FilePathColumn); + const QString filePath = QDir::fromNativeSeparators( + m_proxySnapshotModel->data(index, Qt::DisplayRole).toString()); + const SnapshotInfo info = m_snapshotInfos->at(m_ui->snapshotSelector->currentIndex()); + updateDocumentData(info.snapshot.document(filePath)); + } else { + clearDocumentData(); + } +} + +void CppCodeModelInspectorDialog::onSymbolsViewExpandedOrCollapsed(const QModelIndex &) +{ + resizeColumns<SymbolsModel>(m_ui->docSymbolsView); +} + +void CppCodeModelInspectorDialog::onProjectPartFilterChanged(const QString &pattern) +{ + m_proxyProjectPartsModel->setFilterWildcard(pattern); +} + +void CppCodeModelInspectorDialog::onProjectPartSelected(const QModelIndex ¤t, + const QModelIndex &) +{ + if (current.isValid()) { + QModelIndex index = m_proxyProjectPartsModel->mapToSource(current); + if (index.isValid()) { + index = m_projectPartsModel->index(index.row(), ProjectPartsModel::PartFilePathColumn); + const QString projectFilePath = QDir::fromNativeSeparators( + m_projectPartsModel->data(index, Qt::DisplayRole).toString()); + updateProjectPartData(m_projectPartsModel->projectPartForProjectFile(projectFilePath)); + } + } else { + clearProjectPartData(); + } +} + +void CppCodeModelInspectorDialog::onWorkingCopyFilterChanged(const QString &pattern) +{ + m_proxyWorkingCopyModel->setFilterWildcard(pattern); +} + +void CppCodeModelInspectorDialog::onWorkingCopyDocumentSelected(const QModelIndex ¤t, + const QModelIndex &) +{ + if (current.isValid()) { + const QModelIndex index = m_proxyWorkingCopyModel->mapToSource(current); + if (index.isValid()) { + const QString source + = QString::fromUtf8(m_workingCopyModel->data(index, Qt::UserRole).toByteArray()); + m_ui->workingCopySourceEdit->setPlainText(source); + } + } else { + m_ui->workingCopySourceEdit->setPlainText(QString()); + } +} + +void CppCodeModelInspectorDialog::refresh() +{ + CppModelManagerInterface *cmm = CppModelManagerInterface::instance(); + + const int oldSnapshotIndex = m_ui->snapshotSelector->currentIndex(); + const bool selectEditorRelevant + = m_ui->selectEditorRelevantEntriesAfterRefreshCheckBox->isChecked(); + + // Snapshots and Documents + m_snapshotInfos->clear(); + m_ui->snapshotSelector->clear(); + + const CPlusPlus::Snapshot globalSnapshot = cmm->snapshot(); + CppCodeModelInspectorDumper dumper(globalSnapshot); + m_snapshotModel->setGlobalSnapshot(globalSnapshot); + + m_snapshotInfos->append(SnapshotInfo(globalSnapshot, SnapshotInfo::GlobalSnapshot)); + const QString globalSnapshotTitle + = QString::fromLatin1("Global/Indexing Snapshot (%1 Documents)").arg(globalSnapshot.size()); + m_ui->snapshotSelector->addItem(globalSnapshotTitle); + dumper.dumpSnapshot(globalSnapshot, globalSnapshotTitle, /*isGlobalSnapshot=*/ true); + + TextEditor::BaseTextEditor *editor = currentEditor(); + CppEditorSupport *editorSupport = 0; + if (editor) { + editorSupport = cmm->cppEditorSupport(editor); + if (editorSupport) { + const CPlusPlus::Snapshot editorSnapshot = editorSupport->snapshotUpdater()->snapshot(); + m_snapshotInfos->append(SnapshotInfo(editorSnapshot, SnapshotInfo::EditorSnapshot)); + const QString editorSnapshotTitle + = QString::fromLatin1("Current Editor's Snapshot (%1 Documents)") + .arg(editorSnapshot.size()); + dumper.dumpSnapshot(editorSnapshot, editorSnapshotTitle); + m_ui->snapshotSelector->addItem(editorSnapshotTitle); + } + CppEditor::Internal::CPPEditorWidget *cppEditorWidget + = qobject_cast<CppEditor::Internal::CPPEditorWidget *>(editor->editorWidget()); + if (cppEditorWidget) { + SemanticInfo semanticInfo = cppEditorWidget->semanticInfo(); + CPlusPlus::Snapshot snapshot; + + // Add semantic info snapshot + snapshot = semanticInfo.snapshot; + m_snapshotInfos->append(SnapshotInfo(snapshot, SnapshotInfo::EditorSnapshot)); + m_ui->snapshotSelector->addItem( + QString::fromLatin1("Current Editor's Semantic Info Snapshot (%1 Documents)") + .arg(snapshot.size())); + + // Add a pseudo snapshot containing only the semantic info document since this document + // is not part of the semantic snapshot. + snapshot = CPlusPlus::Snapshot(); + snapshot.insert(cppEditorWidget->semanticInfo().doc); + m_snapshotInfos->append(SnapshotInfo(snapshot, SnapshotInfo::EditorSnapshot)); + const QString snapshotTitle + = QString::fromLatin1("Current Editor's Pseudo Snapshot with Semantic Info Document (%1 Documents)") + .arg(snapshot.size()); + dumper.dumpSnapshot(snapshot, snapshotTitle); + m_ui->snapshotSelector->addItem(snapshotTitle); + } + } + + int snapshotIndex = 0; + if (selectEditorRelevant) { + for (int i = 0, total = m_snapshotInfos->size(); i < total; ++i) { + const SnapshotInfo info = m_snapshotInfos->at(i); + if (info.type == SnapshotInfo::EditorSnapshot) { + snapshotIndex = i; + break; + } + } + } else if (oldSnapshotIndex < m_snapshotInfos->size()) { + snapshotIndex = oldSnapshotIndex; + } + m_ui->snapshotSelector->setCurrentIndex(snapshotIndex); + onSnapshotSelected(snapshotIndex); + + // Project Parts + const ProjectPart::Ptr editorsProjectPart = editorSupport + ? editorSupport->snapshotUpdater()->currentProjectPart() + : ProjectPart::Ptr(); + + const QList<CppModelManagerInterface::ProjectInfo> projectInfos = cmm->projectInfos(); + dumper.dumpProjectInfos(projectInfos); + m_projectPartsModel->configure(projectInfos, editorsProjectPart); + m_projectPartsView->resizeColumns(ProjectPartsModel::ColumnCount); + QModelIndex index = m_proxyProjectPartsModel->index(0, ProjectPartsModel::PartFilePathColumn); + if (index.isValid()) { + if (selectEditorRelevant && editorsProjectPart) { + QModelIndex editorPartIndex = m_projectPartsModel->indexForCurrentEditorsProjectPart(); + editorPartIndex = m_proxyProjectPartsModel->mapFromSource(editorPartIndex); + if (editorPartIndex.isValid()) + index = editorPartIndex; + } + m_projectPartsView->selectIndex(index); + } + + // Working Copy + const CppModelManagerInterface::WorkingCopy workingCopy = cmm->workingCopy(); + dumper.dumpWorkingCopy(workingCopy); + m_workingCopyModel->configure(workingCopy); + m_workingCopyView->resizeColumns(WorkingCopyModel::ColumnCount); + if (workingCopy.size() > 0) { + QModelIndex index = m_proxyWorkingCopyModel->index(0, WorkingCopyModel::FilePathColumn); + if (selectEditorRelevant) { + const QModelIndex eindex = m_workingCopyModel->indexForFile(fileInCurrentEditor()); + if (eindex.isValid()) + index = m_proxyWorkingCopyModel->mapFromSource(eindex); + } + m_workingCopyView->selectIndex(index); + } +} + +enum DocumentTabs { + DocumentGeneralTab, + DocumentIncludesTab, + DocumentDiagnosticsTab, + DocumentDefinedMacrosTab, + DocumentPreprocessedSourceTab, + DocumentSymbolsTab, + DocumentTokensTab +}; + +static QString docTabName(int tabIndex, int numberOfEntries = -1) +{ + const char *names[] = { + "&General", + "&Includes", + "&Diagnostic Messages", + "(Un)Defined &Macros", + "P&reprocessed Source", + "&Symbols", + "&Tokens" + }; + QString result = QLatin1String(names[tabIndex]); + if (numberOfEntries != -1) + result += QString::fromLatin1(" (%1)").arg(numberOfEntries); + return result; +} + +void CppCodeModelInspectorDialog::clearDocumentData() +{ + m_docGenericInfoModel->clear(); + + m_ui->docTab->setTabText(DocumentIncludesTab, docTabName(DocumentIncludesTab)); + m_docIncludesModel->clear(); + + m_ui->docTab->setTabText(DocumentDiagnosticsTab, docTabName(DocumentDiagnosticsTab)); + m_docDiagnosticMessagesModel->clear(); + + m_ui->docTab->setTabText(DocumentDefinedMacrosTab, docTabName(DocumentDefinedMacrosTab)); + m_docMacrosModel->clear(); + + m_ui->docPreprocessedSourceEdit->setPlainText(QString()); + + m_docSymbolsModel->clear(); + + m_ui->docTab->setTabText(DocumentTokensTab, docTabName(DocumentTokensTab)); + m_docTokensModel->clear(); +} + +void CppCodeModelInspectorDialog::updateDocumentData(const CPlusPlus::Document::Ptr &document) +{ + QTC_ASSERT(document, return); + + // General + KeyValueModel::Table table = KeyValueModel::Table() + << qMakePair(QString::fromLatin1("File Path"), + QDir::toNativeSeparators(document->fileName())) + << qMakePair(QString::fromLatin1("Last Modified"), toString(document->lastModified())) + << qMakePair(QString::fromLatin1("Revision"), toString(document->revision())) + << qMakePair(QString::fromLatin1("Editor Revision"), toString(document->editorRevision())) + << qMakePair(QString::fromLatin1("Check Mode"), toString(document->checkMode())) + << qMakePair(QString::fromLatin1("Tokenized"), toString(document->isTokenized())) + << qMakePair(QString::fromLatin1("Parsed"), toString(document->isParsed())) + << qMakePair(QString::fromLatin1("Project Parts"), partsForFile(document->fileName())) + ; + m_docGenericInfoModel->configure(table); + resizeColumns<KeyValueModel>(m_ui->docGeneralView); + + // Includes + m_docIncludesModel->configure(document->resolvedIncludes() + document->unresolvedIncludes()); + resizeColumns<IncludesModel>(m_ui->docIncludesView); + m_ui->docTab->setTabText(DocumentIncludesTab, + docTabName(DocumentIncludesTab, m_docIncludesModel->rowCount())); + + // Diagnostic Messages + m_docDiagnosticMessagesModel->configure(document->diagnosticMessages()); + resizeColumns<DiagnosticMessagesModel>(m_ui->docDiagnosticMessagesView); + m_ui->docTab->setTabText(DocumentDiagnosticsTab, + docTabName(DocumentDiagnosticsTab, m_docDiagnosticMessagesModel->rowCount())); + + // Macros + m_docMacrosModel->configure(document->definedMacros()); + resizeColumns<MacrosModel>(m_ui->docDefinedMacrosView); + m_ui->docTab->setTabText(DocumentDefinedMacrosTab, + docTabName(DocumentDefinedMacrosTab, m_docMacrosModel->rowCount())); + + // Source + m_ui->docPreprocessedSourceEdit->setPlainText(QString::fromUtf8(document->utf8Source())); + + // Symbols + m_docSymbolsModel->configure(document); + resizeColumns<SymbolsModel>(m_ui->docSymbolsView); + + // Tokens + m_docTokensModel->configure(document->translationUnit()); + resizeColumns<TokensModel>(m_ui->docTokensView); + m_ui->docTab->setTabText(DocumentTokensTab, + docTabName(DocumentTokensTab, m_docTokensModel->rowCount())); +} + +enum ProjectPartTabs { + ProjectPartGeneralTab, + ProjectPartFilesTab, + ProjectPartDefinesTab, + ProjectPartIncludePathsTab, + ProjectPartFrameworkPathsTab, + ProjectPartPrecompiledHeadersTab +}; + +static QString partTabName(int tabIndex, int numberOfEntries = -1) +{ + const char *names[] = { + "&General", + "Project &Files", + "&Defines", + "&Include Paths", + "F&ramework Paths", + "Pre&compiled Headers" + }; + QString result = QLatin1String(names[tabIndex]); + if (numberOfEntries != -1) + result += QString::fromLatin1(" (%1)").arg(numberOfEntries); + return result; +} + +void CppCodeModelInspectorDialog::clearProjectPartData() +{ + m_partGenericInfoModel->clear(); + + m_ui->partProjectFilesEdit->setPlainText(QString()); + m_ui->projectPartTab->setTabText(ProjectPartFilesTab, partTabName(ProjectPartFilesTab)); + + m_ui->partToolchainDefinesEdit->setPlainText(QString()); + m_ui->partProjectDefinesEdit->setPlainText(QString()); + m_ui->projectPartTab->setTabText(ProjectPartDefinesTab, partTabName(ProjectPartDefinesTab)); + + m_ui->partIncludePathsEdit->setPlainText(QString()); + m_ui->projectPartTab->setTabText(ProjectPartIncludePathsTab, + partTabName(ProjectPartIncludePathsTab)); + + m_ui->partFrameworkPathsEdit->setPlainText(QString()); + m_ui->projectPartTab->setTabText(ProjectPartFrameworkPathsTab, + partTabName(ProjectPartFrameworkPathsTab)); + + m_ui->partPrecompiledHeadersEdit->setPlainText(QString()); + m_ui->projectPartTab->setTabText(ProjectPartPrecompiledHeadersTab, + partTabName(ProjectPartPrecompiledHeadersTab)); +} + +void CppCodeModelInspectorDialog::updateProjectPartData(const ProjectPart::Ptr &part) +{ + QTC_ASSERT(part, return); + + // General + QString projectName = QLatin1String("<None>"); + QString projectFilePath = QLatin1String("<None>"); + if (ProjectExplorer::Project *project = part->project) { + projectName = project->displayName(); + projectFilePath = project->projectFilePath(); + } + KeyValueModel::Table table = KeyValueModel::Table() + << qMakePair(QString::fromLatin1("Project Part Name"), part->displayName) + << qMakePair(QString::fromLatin1("Project Part File"), + QDir::toNativeSeparators(part->projectFile)) + << qMakePair(QString::fromLatin1("Project Name"), projectName) + << qMakePair(QString::fromLatin1("Project File"), + QDir::toNativeSeparators(projectFilePath)) + << qMakePair(QString::fromLatin1("C Version"), toString(part->cVersion)) + << qMakePair(QString::fromLatin1("CXX Version"), toString(part->cxxVersion)) + << qMakePair(QString::fromLatin1("CXX Extensions"), toString(part->cxxExtensions)) + << qMakePair(QString::fromLatin1("Qt Version"), toString(part->qtVersion)) + ; + m_partGenericInfoModel->configure(table); + resizeColumns<KeyValueModel>(m_ui->partGeneralView); + + // Project Files + m_ui->partProjectFilesEdit->setPlainText(toString(part->files)); + m_ui->projectPartTab->setTabText(ProjectPartFilesTab, + partTabName(ProjectPartFilesTab, part->files.size())); + + // Defines + const QList<QByteArray> defineLines = part->toolchainDefines.split('\n') + + part->projectDefines.split('\n'); + int numberOfDefines = 0; + foreach (const QByteArray &line, defineLines) { + if (line.startsWith("#define ")) + ++numberOfDefines; + } + m_ui->partToolchainDefinesEdit->setPlainText(QString::fromUtf8(part->toolchainDefines)); + m_ui->partProjectDefinesEdit->setPlainText(QString::fromUtf8(part->projectDefines)); + m_ui->projectPartTab->setTabText(ProjectPartDefinesTab, + partTabName(ProjectPartDefinesTab, numberOfDefines)); + + // Include Paths + m_ui->partIncludePathsEdit->setPlainText(pathListToString(part->includePaths)); + m_ui->projectPartTab->setTabText(ProjectPartIncludePathsTab, + partTabName(ProjectPartIncludePathsTab, part->includePaths.size())); + + // Framework Paths + m_ui->partFrameworkPathsEdit->setPlainText(pathListToString(part->frameworkPaths)); + m_ui->projectPartTab->setTabText(ProjectPartFrameworkPathsTab, + partTabName(ProjectPartFrameworkPathsTab, part->frameworkPaths.size())); + + // Precompiled Headers + m_ui->partPrecompiledHeadersEdit->setPlainText(pathListToString(part->precompiledHeaders)); + m_ui->projectPartTab->setTabText(ProjectPartPrecompiledHeadersTab, + partTabName(ProjectPartPrecompiledHeadersTab, part->precompiledHeaders.size())); +} + +bool CppCodeModelInspectorDialog::event(QEvent *e) +{ + if (e->type() == QEvent::ShortcutOverride) { + QKeyEvent *ke = static_cast<QKeyEvent *>(e); + if (ke->key() == Qt::Key_Escape && !ke->modifiers()) { + ke->accept(); + close(); + return false; + } + } + return QDialog::event(e); +} + +} // namespace Internal +} // namespace CppEditor + +#include "cppcodemodelinspectordialog.moc" diff --git a/src/plugins/cppeditor/cppcodemodelinspectordialog.h b/src/plugins/cppeditor/cppcodemodelinspectordialog.h new file mode 100644 index 0000000000..3348e03922 --- /dev/null +++ b/src/plugins/cppeditor/cppcodemodelinspectordialog.h @@ -0,0 +1,129 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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. +** +****************************************************************************/ + +#ifndef CPPCODEMODELINSPECTORDIALOG_H +#define CPPCODEMODELINSPECTORDIALOG_H + +#include <cpptools/cppmodelmanagerinterface.h> + +#include <cplusplus/CppDocument.h> + +#include <QDialog> +#include <QList> + +QT_BEGIN_NAMESPACE +class QSortFilterProxyModel; +class QModelIndex; +namespace Ui { class CppCodeModelInspectorDialog; } +QT_END_NAMESPACE + +namespace CppEditor { +namespace Internal { + +class FilterableView; +class SnapshotInfo; + +class DiagnosticMessagesModel; +class IncludesModel; +class KeyValueModel; +class MacrosModel; +class ProjectPartsModel; +class SnapshotModel; +class SymbolsModel; +class TokensModel; +class WorkingCopyModel; + +// +// This dialog is for DEBUGGING PURPOSES and thus NOT TRANSLATED. +// + +class CppCodeModelInspectorDialog : public QDialog +{ + Q_OBJECT + +public: + explicit CppCodeModelInspectorDialog(QWidget *parent = 0); + ~CppCodeModelInspectorDialog(); + +private slots: + void onRefreshRequested(); + + void onSnapshotFilterChanged(const QString &pattern); + void onSnapshotSelected(int row); + void onDocumentSelected(const QModelIndex ¤t, const QModelIndex &); + void onSymbolsViewExpandedOrCollapsed(const QModelIndex &); + + void onProjectPartFilterChanged(const QString &pattern); + void onProjectPartSelected(const QModelIndex ¤t, const QModelIndex &); + + void onWorkingCopyFilterChanged(const QString &pattern); + void onWorkingCopyDocumentSelected(const QModelIndex ¤t, const QModelIndex &); + +private: + void refresh(); + + void clearDocumentData(); + void updateDocumentData(const CPlusPlus::Document::Ptr &document); + + void clearProjectPartData(); + void updateProjectPartData(const CppTools::ProjectPart::Ptr &part); + + bool event(QEvent *e); + +private: + Ui::CppCodeModelInspectorDialog *m_ui; + + // Snapshots and Documents + QList<SnapshotInfo> *m_snapshotInfos; + FilterableView *m_snapshotView; + SnapshotModel *m_snapshotModel; + QSortFilterProxyModel *m_proxySnapshotModel; + KeyValueModel *m_docGenericInfoModel; + IncludesModel *m_docIncludesModel; + DiagnosticMessagesModel *m_docDiagnosticMessagesModel; + MacrosModel *m_docMacrosModel; + SymbolsModel *m_docSymbolsModel; + TokensModel *m_docTokensModel; + + // Project Parts + FilterableView *m_projectPartsView; + ProjectPartsModel *m_projectPartsModel; + QSortFilterProxyModel *m_proxyProjectPartsModel; + KeyValueModel *m_partGenericInfoModel; + + // Working Copy + FilterableView *m_workingCopyView; + WorkingCopyModel *m_workingCopyModel; + QSortFilterProxyModel *m_proxyWorkingCopyModel; +}; + +} // namespace Internal +} // namespace CppEditor + +#endif // CPPCODEMODELINSPECTORDIALOG_H diff --git a/src/plugins/cppeditor/cppcodemodelinspectordialog.ui b/src/plugins/cppeditor/cppcodemodelinspectordialog.ui new file mode 100644 index 0000000000..fbe95c08a7 --- /dev/null +++ b/src/plugins/cppeditor/cppcodemodelinspectordialog.ui @@ -0,0 +1,381 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>CppCodeModelInspectorDialog</class> + <widget class="QDialog" name="CppCodeModelInspectorDialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>818</width> + <height>756</height> + </rect> + </property> + <property name="windowTitle"> + <string notr="true">C++ Code Model Inspector</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QTabWidget" name="tabWidget"> + <property name="currentIndex"> + <number>0</number> + </property> + <widget class="QWidget" name="tab"> + <attribute name="title"> + <string notr="true">&Snapshots and Documents</string> + </attribute> + <layout class="QVBoxLayout" name="verticalLayout_5"> + <item> + <widget class="QSplitter" name="splitter"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <widget class="QWidget" name="layoutWidget"> + <layout class="QVBoxLayout" name="snapshotSelectorAndViewLayout"> + <item> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QLabel" name="snapshotSelectorLabel"> + <property name="text"> + <string notr="true">Sn&apshot:</string> + </property> + <property name="buddy"> + <cstring>snapshotSelector</cstring> + </property> + </widget> + </item> + <item> + <widget class="QComboBox" name="snapshotSelector"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> + <horstretch>100</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + <widget class="QTabWidget" name="docTab"> + <property name="currentIndex"> + <number>0</number> + </property> + <widget class="QWidget" name="tab_8"> + <attribute name="title"> + <string notr="true">&General</string> + </attribute> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <item> + <widget class="QTreeView" name="docGeneralView"> + <property name="alternatingRowColors"> + <bool>true</bool> + </property> + <property name="textElideMode"> + <enum>Qt::ElideMiddle</enum> + </property> + </widget> + </item> + </layout> + </widget> + <widget class="QWidget" name="tab_7"> + <attribute name="title"> + <string notr="true">&Includes</string> + </attribute> + <layout class="QVBoxLayout" name="verticalLayout_3"> + <item> + <widget class="QTreeView" name="docIncludesView"> + <property name="alternatingRowColors"> + <bool>true</bool> + </property> + <property name="textElideMode"> + <enum>Qt::ElideMiddle</enum> + </property> + </widget> + </item> + </layout> + </widget> + <widget class="QWidget" name="tab_6"> + <attribute name="title"> + <string notr="true">&Diagnostic Messages</string> + </attribute> + <layout class="QVBoxLayout" name="verticalLayout_6"> + <item> + <widget class="QTreeView" name="docDiagnosticMessagesView"> + <property name="alternatingRowColors"> + <bool>true</bool> + </property> + <property name="textElideMode"> + <enum>Qt::ElideMiddle</enum> + </property> + </widget> + </item> + </layout> + </widget> + <widget class="QWidget" name="tab_4"> + <attribute name="title"> + <string notr="true">(Un)Defined &Macros</string> + </attribute> + <layout class="QVBoxLayout" name="verticalLayout_7"> + <item> + <widget class="QTreeView" name="docDefinedMacrosView"> + <property name="alternatingRowColors"> + <bool>true</bool> + </property> + <property name="textElideMode"> + <enum>Qt::ElideMiddle</enum> + </property> + </widget> + </item> + </layout> + </widget> + <widget class="QWidget" name="tab_5"> + <attribute name="title"> + <string notr="true">P&reprocessed Source</string> + </attribute> + <layout class="QVBoxLayout" name="verticalLayout_4"> + <item> + <widget class="QPlainTextEdit" name="docPreprocessedSourceEdit"> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </widget> + <widget class="QWidget" name="tab_9"> + <attribute name="title"> + <string notr="true">&Symbols</string> + </attribute> + <layout class="QVBoxLayout" name="verticalLayout_8"> + <item> + <widget class="QTreeView" name="docSymbolsView"> + <property name="alternatingRowColors"> + <bool>true</bool> + </property> + <property name="textElideMode"> + <enum>Qt::ElideMiddle</enum> + </property> + </widget> + </item> + </layout> + </widget> + <widget class="QWidget" name="tab_10"> + <attribute name="title"> + <string notr="true">&Tokens</string> + </attribute> + <layout class="QVBoxLayout" name="verticalLayout_10"> + <item> + <widget class="QTreeView" name="docTokensView"> + <property name="alternatingRowColors"> + <bool>true</bool> + </property> + <property name="textElideMode"> + <enum>Qt::ElideMiddle</enum> + </property> + </widget> + </item> + </layout> + </widget> + </widget> + </widget> + </item> + </layout> + </widget> + <widget class="QWidget" name="tab_2"> + <attribute name="title"> + <string notr="true">&Project Parts</string> + </attribute> + <layout class="QVBoxLayout" name="verticalLayout_12"> + <item> + <widget class="QSplitter" name="projectPartsSplitter"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <widget class="QTabWidget" name="projectPartTab"> + <property name="currentIndex"> + <number>0</number> + </property> + <widget class="QWidget" name="tab_17"> + <attribute name="title"> + <string notr="true">&General</string> + </attribute> + <layout class="QVBoxLayout" name="verticalLayout_9"> + <item> + <widget class="QTreeView" name="partGeneralView"/> + </item> + </layout> + </widget> + <widget class="QWidget" name="tab_13"> + <attribute name="title"> + <string notr="true">Project &Files</string> + </attribute> + <layout class="QVBoxLayout" name="verticalLayout_14"> + <item> + <widget class="QPlainTextEdit" name="partProjectFilesEdit"> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </widget> + <widget class="QWidget" name="tab_14"> + <attribute name="title"> + <string notr="true">&Defines</string> + </attribute> + <layout class="QVBoxLayout" name="verticalLayout_19"> + <item> + <widget class="QGroupBox" name="groupBox"> + <property name="title"> + <string notr="true">Toolchain Defines</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_13"> + <item> + <widget class="QPlainTextEdit" name="partToolchainDefinesEdit"> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="groupBox_2"> + <property name="title"> + <string notr="true">Project Defines</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_15"> + <item> + <widget class="QPlainTextEdit" name="partProjectDefinesEdit"> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + <widget class="QWidget" name="tab_15"> + <attribute name="title"> + <string notr="true">&Include Paths</string> + </attribute> + <layout class="QVBoxLayout" name="verticalLayout_16"> + <item> + <widget class="QPlainTextEdit" name="partIncludePathsEdit"> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </widget> + <widget class="QWidget" name="definesTab_2"> + <attribute name="title"> + <string notr="true">F&ramework Paths</string> + </attribute> + <layout class="QVBoxLayout" name="verticalLayout_17"> + <item> + <widget class="QPlainTextEdit" name="partFrameworkPathsEdit"> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </widget> + <widget class="QWidget" name="tab_16"> + <attribute name="title"> + <string notr="true">Pre&compiled Headers</string> + </attribute> + <layout class="QVBoxLayout" name="verticalLayout_18"> + <item> + <widget class="QPlainTextEdit" name="partPrecompiledHeadersEdit"> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </widget> + </widget> + </widget> + </item> + </layout> + </widget> + <widget class="QWidget" name="tab_3"> + <attribute name="title"> + <string notr="true">&Working Copy</string> + </attribute> + <layout class="QVBoxLayout" name="verticalLayout_11"> + <item> + <widget class="QSplitter" name="workingCopySplitter"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="childrenCollapsible"> + <bool>true</bool> + </property> + <widget class="QPlainTextEdit" name="workingCopySourceEdit"> + <property name="readOnly"> + <bool>true</bool> + </property> + <property name="textInteractionFlags"> + <set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> + </property> + </widget> + </widget> + </item> + </layout> + </widget> + </widget> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_3"> + <item> + <widget class="QPushButton" name="refreshButton"> + <property name="text"> + <string notr="true">&Refresh</string> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="selectEditorRelevantEntriesAfterRefreshCheckBox"> + <property name="text"> + <string notr="true">Select &editor relevant entries after refresh</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QPushButton" name="closeButton"> + <property name="text"> + <string notr="true">Close</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/src/plugins/cppeditor/cppdoxygen_test.cpp b/src/plugins/cppeditor/cppdoxygen_test.cpp index 94749370e5..d4a2ab9a94 100644 --- a/src/plugins/cppeditor/cppdoxygen_test.cpp +++ b/src/plugins/cppeditor/cppdoxygen_test.cpp @@ -28,12 +28,13 @@ ****************************************************************************/ #include "cppeditor.h" +#include "cppeditorplugin.h" +#include "cppeditortestcase.h" #include <coreplugin/editormanager/editormanager.h> -#include <cplusplus/CppDocument.h> -#include <cppeditor/cppeditor.h> -#include <cppeditor/cppeditorplugin.h> #include <cpptools/cppmodelmanagerinterface.h> + +#include <cplusplus/CppDocument.h> #include <utils/fileutils.h> #include <QCoreApplication> @@ -58,89 +59,52 @@ typedef QByteArray _; * Encapsulates the whole process of setting up an editor, * pressing ENTER and checking the result. */ -struct TestCase -{ - QByteArray originalText; - int pos; - CPPEditor *editor; - CPPEditorWidget *editorWidget; - - TestCase(const QByteArray &input); - ~TestCase(); - - void run(const QByteArray &expected, int undoCount = 1); - -private: - TestCase(const TestCase &); - TestCase &operator=(const TestCase &); -}; - -/// The '|' in the input denotes the cursor position. -TestCase::TestCase(const QByteArray &input) - : originalText(input) +class DoxygenTestCase : public CppEditor::Internal::Tests::TestCase { - pos = originalText.indexOf('|'); - QVERIFY(pos != -1); - originalText.remove(pos, 1); - QString fileName(QDir::tempPath() + QLatin1String("/file.cpp")); - Utils::FileSaver srcSaver(fileName); - srcSaver.write(originalText); - srcSaver.finalize(); - CppTools::CppModelManagerInterface::instance()->updateSourceFiles(QStringList()<<fileName); - - // Wait for the parser in the future to give us the document - while (true) { - Snapshot s = CppTools::CppModelManagerInterface::instance()->snapshot(); - if (s.contains(fileName)) - break; - QCoreApplication::processEvents(); +public: + /// The '|' in the input denotes the cursor position. + DoxygenTestCase(const QByteArray &original, const QByteArray &expected) + { + QVERIFY(succeededSoFar()); + + CppEditor::Internal::Tests::TestDocument testDocument("file.cpp", original, '|'); + QVERIFY(testDocument.hasCursorMarker()); + testDocument.m_source.remove(testDocument.m_cursorPosition, 1); + QVERIFY(testDocument.writeToDisk()); + + // Update Code Model + QVERIFY(parseFiles(testDocument.filePath())); + + // Open Editor + QVERIFY(openCppEditor(testDocument.filePath(), &testDocument.m_editor, + &testDocument.m_editorWidget)); + closeEditorAtEndOfTestCase(testDocument.m_editor); + + // We want to test documents that start with a comment. By default, the + // editor will fold the very first comment it encounters, assuming + // it is a license header. Currently unfoldAll() does not work as + // expected (some blocks are still hidden in some test cases, so the + // cursor movements are not as expected). For the time being, we just + // prepend a declaration before the initial test comment. + // testDocument.m_editorWidget->unfoldAll(); + testDocument.m_editor->setCursorPosition(testDocument.m_cursorPosition); + + waitForRehighlightedSemanticDocument(testDocument.m_editorWidget); + + // Send 'ENTER' key press + QKeyEvent event(QEvent::KeyPress, Qt::Key_Enter, Qt::NoModifier); + QCoreApplication::sendEvent(testDocument.m_editorWidget, &event); + const QByteArray result = testDocument.m_editorWidget->document()->toPlainText().toUtf8(); + + QCOMPARE(QLatin1String(result), QLatin1String(expected)); + + testDocument.m_editorWidget->undo(); + const QByteArray contentsAfterUndo + = testDocument.m_editorWidget->document()->toPlainText().toUtf8(); + QCOMPARE(contentsAfterUndo, testDocument.m_source); } +}; - editor = dynamic_cast<CPPEditor *>(EditorManager::openEditor(fileName)); - QVERIFY(editor); - editorWidget = dynamic_cast<CPPEditorWidget *>(editor->editorWidget()); - QVERIFY(editorWidget); - - // We want to test documents that start with a comment. By default, the - // editor will fold the very first comment it encounters, assuming - // it is a license header. Currently unfoldAll() does not work as - // expected (some blocks are still hidden in some test cases, so the - // cursor movements are not as expected). For the time being, we just - // prepend a declaration before the initial test comment. -// editorWidget->unfoldAll(); - editor->setCursorPosition(pos); - - editorWidget->semanticRehighlight(true); - // Wait for the semantic info from the future: - while (editorWidget->semanticInfo().doc.isNull()) - QCoreApplication::processEvents(); -} - -TestCase::~TestCase() -{ - EditorManager::closeEditor(editor, false); - QCoreApplication::processEvents(); // process any pending events - - // Remove the test file from the code-model - CppTools::CppModelManagerInterface *mmi = CppTools::CppModelManagerInterface::instance(); - mmi->GC(); - QCOMPARE(mmi->snapshot().size(), 0); -} - -void TestCase::run(const QByteArray &expected, int undoCount) -{ - // Send 'ENTER' key press - QKeyEvent event(QEvent::KeyPress, Qt::Key_Enter, Qt::NoModifier); - QCoreApplication::sendEvent(editorWidget, &event); - const QByteArray result = editorWidget->document()->toPlainText().toUtf8(); - - QCOMPARE(QLatin1String(result), QLatin1String(expected)); - - for (int i = 0; i < undoCount; ++i) - editorWidget->undo(); - const QByteArray contentsAfterUndo = editorWidget->document()->toPlainText().toUtf8(); - QCOMPARE(contentsAfterUndo, originalText); -} } // anonymous namespace void CppEditorPlugin::test_doxygen_comments_data() @@ -286,6 +250,5 @@ void CppEditorPlugin::test_doxygen_comments() { QFETCH(QByteArray, given); QFETCH(QByteArray, expected); - TestCase data(given); - data.run(expected); + DoxygenTestCase(given, expected); } diff --git a/src/plugins/cppeditor/cppeditor.cpp b/src/plugins/cppeditor/cppeditor.cpp index f8264260ec..6b44b15670 100644 --- a/src/plugins/cppeditor/cppeditor.cpp +++ b/src/plugins/cppeditor/cppeditor.cpp @@ -1077,7 +1077,7 @@ void CPPEditorWidget::updateOutlineNow() return; const Snapshot snapshot = m_modelManager->snapshot(); - Document::Ptr document = snapshot.document(editorDocument()->filePath()); + Document::Ptr document = snapshot.document(baseTextDocument()->filePath()); if (!document) return; @@ -1203,6 +1203,9 @@ void CPPEditorWidget::finishHighlightSymbolUsages() if (m_highlighter.isCanceled()) return; // aborted + else if (m_lastSemanticInfo.doc.isNull()) + return; + TextEditor::SyntaxHighlighter *highlighter = baseTextDocument()->syntaxHighlighter(); QTC_ASSERT(highlighter, return); @@ -1562,6 +1565,8 @@ void CPPEditorWidget::setFontSettings(const TextEditor::FontSettings &fs) fs.toTextCharFormat(TextEditor::C_FUNCTION); m_semanticHighlightFormatMap[CppHighlightingSupport::PseudoKeywordUse] = fs.toTextCharFormat(TextEditor::C_KEYWORD); + m_semanticHighlightFormatMap[CppHighlightingSupport::StringUse] = + fs.toTextCharFormat(TextEditor::C_STRING); m_keywordFormat = fs.toTextCharFormat(TextEditor::C_KEYWORD); // only set the background, we do not want to modify foreground properties @@ -1669,7 +1674,7 @@ void CPPEditorWidget::updateSemanticInfo(const SemanticInfo &semanticInfo) // We can use the semanticInfo's snapshot (and avoid locking), but not its // document, since it doesn't contain expanded macros. - LookupContext context(semanticInfo.snapshot.document(editorDocument()->filePath()), + LookupContext context(semanticInfo.snapshot.document(baseTextDocument()->filePath()), semanticInfo.snapshot); SemanticInfo::LocalUseIterator it(semanticInfo.localUses); @@ -1838,7 +1843,7 @@ void CPPEditorWidget::onFunctionDeclDefLinkFound(QSharedPointer<FunctionDeclDefL m_declDefLink = link; Core::IDocument *targetDocument = Core::EditorManager::documentModel()->documentForFilePath( m_declDefLink->targetFile->fileName()); - if (editorDocument() != targetDocument) { + if (baseTextDocument() != targetDocument) { if (TextEditor::BaseTextDocument *baseTextDocument = qobject_cast<TextEditor::BaseTextDocument *>(targetDocument)) connect(baseTextDocument->document(), SIGNAL(contentsChanged()), this, SLOT(abortDeclDefLink())); @@ -1873,7 +1878,7 @@ void CPPEditorWidget::abortDeclDefLink() Core::IDocument *targetDocument = Core::EditorManager::documentModel()->documentForFilePath( m_declDefLink->targetFile->fileName()); - if (editorDocument() != targetDocument) { + if (baseTextDocument() != targetDocument) { if (TextEditor::BaseTextDocument *baseTextDocument = qobject_cast<TextEditor::BaseTextDocument *>(targetDocument)) disconnect(baseTextDocument->document(), SIGNAL(contentsChanged()), this, SLOT(abortDeclDefLink())); @@ -1990,7 +1995,7 @@ void CPPEditorWidget::showPreProcessorWidget() if (projectParts.isEmpty()) projectParts << m_modelManager->fallbackProjectPart(); - CppPreProcessorDialog preProcessorDialog(this, projectParts); + CppPreProcessorDialog preProcessorDialog(this, baseTextDocument()->filePath(), projectParts); if (preProcessorDialog.exec() == QDialog::Accepted) { QSharedPointer<SnapshotUpdater> updater = m_modelManager->cppEditorSupport(editor())->snapshotUpdater(); diff --git a/src/plugins/cppeditor/cppeditor.pro b/src/plugins/cppeditor/cppeditor.pro index 6257e2c165..3ab35cdf4c 100644 --- a/src/plugins/cppeditor/cppeditor.pro +++ b/src/plugins/cppeditor/cppeditor.pro @@ -1,37 +1,42 @@ DEFINES += CPPEDITOR_LIBRARY include(../../qtcreatorplugin.pri) -HEADERS += cppeditorplugin.h \ + +HEADERS += \ cppautocompleter.h \ cppclasswizard.h \ + cppcodemodelinspectordialog.h \ + cppeditor.h \ + cppeditor_global.h \ cppeditorconstants.h \ cppeditorenums.h \ - cppeditor_global.h \ - cppeditor.h \ + cppeditorplugin.h \ cppelementevaluator.h \ cppfilewizard.h \ cppfollowsymbolundercursor.h \ cppfunctiondecldeflink.h \ - cpphighlighterfactory.h \ cpphighlighter.h \ + cpphighlighterfactory.h \ cpphoverhandler.h \ + cppincludehierarchy.h \ + cppincludehierarchyitem.h \ + cppincludehierarchymodel.h \ + cppincludehierarchytreeview.h \ cppoutline.h \ + cpppreprocessordialog.h \ + cppquickfix.h \ cppquickfixassistant.h \ cppquickfixes.h \ - cppquickfix.h \ cppsnippetprovider.h \ cpptypehierarchy.h \ - cppincludehierarchy.h \ - cppincludehierarchymodel.h \ - cppincludehierarchyitem.h \ - cppincludehierarchytreeview.h \ cppvirtualfunctionassistprovider.h \ - cppvirtualfunctionproposalitem.h \ - cpppreprocessordialog.h + cppvirtualfunctionproposalitem.h -SOURCES += cppeditorplugin.cpp \ +SOURCES += \ cppautocompleter.cpp \ cppclasswizard.cpp \ + cppcodemodelinspectordialog.cpp \ cppeditor.cpp \ + cppeditorplugin.cpp \ cppelementevaluator.cpp \ cppfilewizard.cpp \ cppfollowsymbolundercursor.cpp \ @@ -39,35 +44,38 @@ SOURCES += cppeditorplugin.cpp \ cpphighlighter.cpp \ cpphighlighterfactory.cpp \ cpphoverhandler.cpp \ + cppincludehierarchy.cpp \ + cppincludehierarchyitem.cpp \ + cppincludehierarchymodel.cpp \ + cppincludehierarchytreeview.cpp \ cppoutline.cpp \ - cppquickfixassistant.cpp \ + cpppreprocessordialog.cpp \ cppquickfix.cpp \ + cppquickfixassistant.cpp \ cppquickfixes.cpp \ cppsnippetprovider.cpp \ cpptypehierarchy.cpp \ - cppincludehierarchy.cpp \ - cppincludehierarchymodel.cpp \ - cppincludehierarchyitem.cpp \ - cppincludehierarchytreeview.cpp \ cppvirtualfunctionassistprovider.cpp \ - cppvirtualfunctionproposalitem.cpp \ - cpppreprocessordialog.cpp + cppvirtualfunctionproposalitem.cpp -RESOURCES += cppeditor.qrc +FORMS += \ + cpppreprocessordialog.ui \ + cppcodemodelinspectordialog.ui -equals(TEST, 1) { - HEADERS += cppquickfix_test_utils.h +RESOURCES += \ + cppeditor.qrc +equals(TEST, 1) { + HEADERS += \ + cppeditortestcase.h \ + cppquickfix_test_utils.h SOURCES += \ + cppdoxygen_test.cpp \ + cppeditortestcase.cpp \ + cppincludehierarchy_test.cpp \ cppquickfix_test.cpp \ cppquickfix_test_utils.cpp \ - cppdoxygen_test.cpp \ fileandtokenactions_test.cpp \ - followsymbol_switchmethoddecldef_test.cpp \ - cppincludehierarchy_test.cpp - + followsymbol_switchmethoddecldef_test.cpp DEFINES += SRCDIR=\\\"$$PWD\\\" } - -FORMS += \ - cpppreprocessordialog.ui diff --git a/src/plugins/cppeditor/cppeditor.qbs b/src/plugins/cppeditor/cppeditor.qbs index a92808d279..8a2f724306 100644 --- a/src/plugins/cppeditor/cppeditor.qbs +++ b/src/plugins/cppeditor/cppeditor.qbs @@ -12,61 +12,38 @@ QtcPlugin { Depends { name: "CPlusPlus" } Depends { name: "TextEditor" } Depends { name: "ProjectExplorer" } + Depends { name: "app_version_header" } files: [ - "cppautocompleter.cpp", - "cppautocompleter.h", - "cppclasswizard.cpp", - "cppclasswizard.h", + "cppautocompleter.cpp", "cppautocompleter.h", + "cppclasswizard.cpp", "cppclasswizard.h", + "cppcodemodelinspectordialog.cpp", "cppcodemodelinspectordialog.h", "cppcodemodelinspectordialog.ui", + "cppeditor.cpp", "cppeditor.h", + "cppeditor.qrc", + "cppeditor_global.h", "cppeditorconstants.h", - "cppeditor.cpp", "cppeditorenums.h", - "cppeditor_global.h", - "cppeditor.h", - "cppeditorplugin.cpp", - "cppeditorplugin.h", - "cppeditor.qrc", - "cppelementevaluator.cpp", - "cppelementevaluator.h", - "cppfilewizard.cpp", - "cppfilewizard.h", - "cppfollowsymbolundercursor.cpp", - "cppfollowsymbolundercursor.h", - "cppfunctiondecldeflink.cpp", - "cppfunctiondecldeflink.h", - "cpphighlighter.cpp", - "cpphighlighterfactory.cpp", - "cpphighlighterfactory.h", - "cpphighlighter.h", - "cpphoverhandler.cpp", - "cpphoverhandler.h", - "cppincludehierarchy.cpp", - "cppincludehierarchy.h", - "cppincludehierarchyitem.cpp", - "cppincludehierarchyitem.h", - "cppincludehierarchymodel.cpp", - "cppincludehierarchymodel.h", - "cppincludehierarchytreeview.cpp", - "cppincludehierarchytreeview.h", - "cppoutline.cpp", - "cppoutline.h", - "cpppreprocessordialog.cpp", - "cpppreprocessordialog.h", - "cpppreprocessordialog.ui", - "cppquickfixassistant.cpp", - "cppquickfixassistant.h", - "cppquickfix.cpp", - "cppquickfixes.cpp", - "cppquickfixes.h", - "cppquickfix.h", - "cppsnippetprovider.cpp", - "cppsnippetprovider.h", - "cpptypehierarchy.cpp", - "cpptypehierarchy.h", - "cppvirtualfunctionassistprovider.cpp", - "cppvirtualfunctionassistprovider.h", - "cppvirtualfunctionproposalitem.cpp", - "cppvirtualfunctionproposalitem.h", + "cppeditorplugin.cpp", "cppeditorplugin.h", + "cppelementevaluator.cpp", "cppelementevaluator.h", + "cppfilewizard.cpp", "cppfilewizard.h", + "cppfollowsymbolundercursor.cpp", "cppfollowsymbolundercursor.h", + "cppfunctiondecldeflink.cpp", "cppfunctiondecldeflink.h", + "cpphighlighter.cpp", "cpphighlighter.h", + "cpphighlighterfactory.cpp", "cpphighlighterfactory.h", + "cpphoverhandler.cpp", "cpphoverhandler.h", + "cppincludehierarchy.cpp", "cppincludehierarchy.h", + "cppincludehierarchyitem.cpp", "cppincludehierarchyitem.h", + "cppincludehierarchymodel.cpp", "cppincludehierarchymodel.h", + "cppincludehierarchytreeview.cpp", "cppincludehierarchytreeview.h", + "cppoutline.cpp", "cppoutline.h", + "cpppreprocessordialog.cpp", "cpppreprocessordialog.h", "cpppreprocessordialog.ui", + "cppquickfix.cpp", "cppquickfix.h", + "cppquickfixassistant.cpp", "cppquickfixassistant.h", + "cppquickfixes.cpp", "cppquickfixes.h", + "cppsnippetprovider.cpp", "cppsnippetprovider.h", + "cpptypehierarchy.cpp", "cpptypehierarchy.h", + "cppvirtualfunctionassistprovider.cpp", "cppvirtualfunctionassistprovider.h", + "cppvirtualfunctionproposalitem.cpp", "cppvirtualfunctionproposalitem.h", ] Group { @@ -74,12 +51,13 @@ QtcPlugin { condition: project.testsEnabled files: [ "cppdoxygen_test.cpp", + "cppeditortestcase.cpp", "cppeditortestcase.h", + "cppincludehierarchy_test.cpp", "cppquickfix_test.cpp", "cppquickfix_test_utils.cpp", "cppquickfix_test_utils.h", "fileandtokenactions_test.cpp", "followsymbol_switchmethoddecldef_test.cpp", - "cppincludehierarchy_test.cpp", ] cpp.defines: outer.concat(['SRCDIR="' + FileInfo.path(filePath) + '"']) diff --git a/src/plugins/cppeditor/cppeditorconstants.h b/src/plugins/cppeditor/cppeditorconstants.h index 18b10ec10d..324d614456 100644 --- a/src/plugins/cppeditor/cppeditorconstants.h +++ b/src/plugins/cppeditor/cppeditorconstants.h @@ -44,6 +44,7 @@ const char FIND_USAGES[] = "CppEditor.FindUsages"; const char OPEN_PREPROCESSOR_DIALOG[] = "CppEditor.OpenPreprocessorDialog"; const char M_REFACTORING_MENU_INSERTION_POINT[] = "CppEditor.RefactorGroup"; const char UPDATE_CODEMODEL[] = "CppEditor.UpdateCodeModel"; +const char INSPECT_CPP_CODEMODEL[] = "CppEditor.InspectCppCodeModel"; const int TYPE_HIERARCHY_PRIORITY = 700; const char TYPE_HIERARCHY_ID[] = "CppEditor.TypeHierarchy"; diff --git a/src/plugins/cppeditor/cppeditorplugin.cpp b/src/plugins/cppeditor/cppeditorplugin.cpp index 2cfcaccf8b..fe02ac3d5c 100644 --- a/src/plugins/cppeditor/cppeditorplugin.cpp +++ b/src/plugins/cppeditor/cppeditorplugin.cpp @@ -42,6 +42,8 @@ #include "cppquickfixes.h" #include "cpphighlighterfactory.h" +#include "cppcodemodelinspectordialog.h" + #include <coreplugin/actionmanager/actioncontainer.h> #include <coreplugin/actionmanager/actionmanager.h> #include <coreplugin/coreconstants.h> @@ -78,6 +80,12 @@ CppEditorFactory::CppEditorFactory(CppEditorPlugin *owner) : addMimeType(CppEditor::Constants::CPP_SOURCE_MIMETYPE); addMimeType(CppEditor::Constants::CPP_HEADER_MIMETYPE); + new TextEditor::TextEditorActionHandler(this, CppEditor::Constants::C_CPPEDITOR, + TextEditor::TextEditorActionHandler::Format + | TextEditor::TextEditorActionHandler::UnCommentSelection + | TextEditor::TextEditorActionHandler::UnCollapseAll + | TextEditor::TextEditorActionHandler::FollowSymbolUnderCursor); + if (!Utils::HostOsInfo::isMacHost() && !Utils::HostOsInfo::isWindowsHost()) { FileIconProvider::registerIconOverlayForMimeType(":/cppeditor/images/qt_cpp.png", CppEditor::Constants::CPP_SOURCE_MIMETYPE); FileIconProvider::registerIconOverlayForMimeType(":/cppeditor/images/qt_c.png", CppEditor::Constants::C_SOURCE_MIMETYPE); @@ -98,8 +106,7 @@ IEditor *CppEditorFactory::createEditor(QWidget *parent) CppEditorPlugin *CppEditorPlugin::m_instance = 0; CppEditorPlugin::CppEditorPlugin() : - m_actionHandler(0), - m_sortedOutline(false), + m_sortedOutline(true), m_renameSymbolUnderCursorAction(0), m_findUsagesAction(0), m_reparseExternallyChangedFiles(0), @@ -112,7 +119,6 @@ CppEditorPlugin::CppEditorPlugin() : CppEditorPlugin::~CppEditorPlugin() { - delete m_actionHandler; m_instance = 0; } @@ -123,8 +129,6 @@ CppEditorPlugin *CppEditorPlugin::instance() void CppEditorPlugin::initializeEditor(CPPEditorWidget *editor) { - m_actionHandler->setupActions(editor); - editor->setLanguageSettingsId(CppTools::Constants::CPP_SETTINGS_ID); TextEditor::TextEditorSettings::initializeEditor(editor); @@ -285,13 +289,10 @@ bool CppEditorPlugin::initialize(const QStringList & /*arguments*/, QString *err connect(m_reparseExternallyChangedFiles, SIGNAL(triggered()), cppModelManager, SLOT(updateModifiedSourceFiles())); cppToolsMenu->addAction(cmd); - m_actionHandler = new TextEditor::TextEditorActionHandler(CppEditor::Constants::C_CPPEDITOR, - TextEditor::TextEditorActionHandler::Format - | TextEditor::TextEditorActionHandler::UnCommentSelection - | TextEditor::TextEditorActionHandler::UnCollapseAll - | TextEditor::TextEditorActionHandler::FollowSymbolUnderCursor); - - m_actionHandler->initializeActions(); + QAction *inspectCppCodeModel = new QAction(tr("Debug: Inspect C++ Code Model"), this); + cmd = ActionManager::registerAction(inspectCppCodeModel, Constants::INSPECT_CPP_CODEMODEL, globalContext); + cmd->setDefaultKeySequence(QKeySequence(Core::UseMacShortcuts ? tr("Meta+Shift+F12") : tr("Ctrl+Shift+F12"))); + connect(inspectCppCodeModel, SIGNAL(triggered()), this, SLOT(inspectCppCodeModel())); contextMenu->addSeparator(context); @@ -306,16 +307,13 @@ bool CppEditorPlugin::initialize(const QStringList & /*arguments*/, QString *err connect(ProgressManager::instance(), SIGNAL(allTasksFinished(Core::Id)), this, SLOT(onAllTasksFinished(Core::Id))); - connect(EditorManager::instance(), SIGNAL(currentEditorChanged(Core::IEditor*)), - SLOT(currentEditorChanged(Core::IEditor*))); - readSettings(); return true; } void CppEditorPlugin::readSettings() { - m_sortedOutline = ICore::settings()->value(QLatin1String("CppTools/SortedMethodOverview"), false).toBool(); + m_sortedOutline = ICore::settings()->value(QLatin1String("CppTools/SortedMethodOverview"), true).toBool(); } void CppEditorPlugin::writeSettings() @@ -390,13 +388,14 @@ void CppEditorPlugin::onAllTasksFinished(Core::Id type) } } -void CppEditorPlugin::currentEditorChanged(IEditor *editor) +void CppEditorPlugin::inspectCppCodeModel() { - if (!editor) - return; - - if (CPPEditorWidget *editorWidget = currentCppEditorWidget()) - editorWidget->semanticRehighlight(/*force = */ true); + if (m_cppCodeModelInspectorDialog) { + ICore::raiseWindow(m_cppCodeModelInspectorDialog); + } else { + m_cppCodeModelInspectorDialog = new CppCodeModelInspectorDialog(ICore::mainWindow()); + m_cppCodeModelInspectorDialog->show(); + } } void CppEditorPlugin::openTypeHierarchy() diff --git a/src/plugins/cppeditor/cppeditorplugin.h b/src/plugins/cppeditor/cppeditorplugin.h index b460436955..3d51932e13 100644 --- a/src/plugins/cppeditor/cppeditorplugin.h +++ b/src/plugins/cppeditor/cppeditorplugin.h @@ -38,7 +38,6 @@ #include <QAction> namespace TextEditor { -class TextEditorActionHandler; class ITextEditor; } // namespace TextEditor @@ -46,6 +45,7 @@ namespace CppEditor { namespace Internal { class CPPEditorWidget; +class CppCodeModelInspectorDialog; class CppQuickFixCollector; class CppQuickFixAssistProvider; @@ -90,7 +90,7 @@ public slots: private slots: void onTaskStarted(Core::Id type); void onAllTasksFinished(Core::Id type); - void currentEditorChanged(Core::IEditor *editor); + void inspectCppCodeModel(); #ifdef WITH_TESTS private slots: @@ -143,6 +143,7 @@ private slots: void test_quickfix_InsertDefFromDecl_macroUsesAtEndOfFile1(); void test_quickfix_InsertDefFromDecl_macroUsesAtEndOfFile2(); void test_quickfix_InsertDefFromDecl_erroneousStatementAtEndOfFile(); + void test_quickfix_InsertDefFromDecl_rvalueReference(); void test_quickfix_InsertDeclFromDef(); @@ -207,9 +208,6 @@ private slots: void test_quickfix_InsertVirtualMethods_implementationFile(); void test_quickfix_InsertVirtualMethods_BaseClassInNamespace(); - void test_functionhelper_virtualFunctions(); - void test_functionhelper_virtualFunctions_data(); - // tests for "Include Hiererchy" void test_includeHierarchyModel_simpleIncludes(); void test_includeHierarchyModel_simpleIncludedBy(); @@ -236,7 +234,6 @@ private: static CppEditorPlugin *m_instance; - TextEditor::TextEditorActionHandler *m_actionHandler; bool m_sortedOutline; QAction *m_renameSymbolUnderCursorAction; QAction *m_findUsagesAction; @@ -246,6 +243,8 @@ private: CppQuickFixAssistProvider *m_quickFixProvider; + QPointer<CppCodeModelInspectorDialog> m_cppCodeModelInspectorDialog; + QPointer<TextEditor::ITextEditor> m_currentEditor; }; diff --git a/src/plugins/cppeditor/cppeditortestcase.cpp b/src/plugins/cppeditor/cppeditortestcase.cpp new file mode 100644 index 0000000000..403f1b82c7 --- /dev/null +++ b/src/plugins/cppeditor/cppeditortestcase.cpp @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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 "cppeditortestcase.h" + +#include "cppeditor.h" + +#include <coreplugin/editormanager/editormanager.h> +#include <cplusplus/CppDocument.h> + +#include <QDir> + +namespace CppEditor { +namespace Internal { +namespace Tests { + +TestDocument::TestDocument(const QByteArray &fileName, const QByteArray &source, char cursorMarker) + : CppTools::Tests::TestDocument(fileName, source, cursorMarker) + , m_cursorPosition(source.indexOf(m_cursorMarker)) + , m_editor(0) + , m_editorWidget(0) +{ +} + +bool TestDocument::hasCursorMarker() const { return m_cursorPosition != -1; } + +TestCase::TestCase(bool runGarbageCollector) + : CppTools::Tests::TestCase(runGarbageCollector) +{ +} + +TestCase::~TestCase() +{ +} + +bool TestCase::openCppEditor(const QString &fileName, + Internal::CPPEditor **editor, + Internal::CPPEditorWidget **editorWidget) +{ + using namespace CppEditor::Internal; + if (CPPEditor *e = dynamic_cast<CPPEditor *>(Core::EditorManager::openEditor(fileName))) { + if (editor) + *editor = e; + if (editorWidget) { + if (CPPEditorWidget *w = dynamic_cast<CPPEditorWidget *>(e->editorWidget())) { + *editorWidget = w; + return true; + } else { + return false; // no or wrong widget + } + } else { + return true; // ok since no widget requested + } + } else { + return false; // no or wrong editor + } +} + +CPlusPlus::Document::Ptr TestCase::waitForRehighlightedSemanticDocument( + Internal::CPPEditorWidget *editorWidget) +{ + editorWidget->semanticRehighlight(true); + while (editorWidget->semanticInfo().doc.isNull()) + QCoreApplication::processEvents(); + return editorWidget->semanticInfo().doc; +} + +} // namespace Tests +} // namespace Internal +} // namespace CppEditor diff --git a/src/plugins/cppeditor/cppeditortestcase.h b/src/plugins/cppeditor/cppeditortestcase.h new file mode 100644 index 0000000000..dc0b4514ec --- /dev/null +++ b/src/plugins/cppeditor/cppeditortestcase.h @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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. +** +****************************************************************************/ + + +#ifndef CPPEDITORTESTCASE_H +#define CPPEDITORTESTCASE_H + +#include <cpptools/cpptoolstestcase.h> + +namespace CppEditor { + +namespace Internal { +class CPPEditor; +class CPPEditorWidget; + +namespace Tests { + +class TestDocument : public CppTools::Tests::TestDocument +{ +public: + TestDocument(const QByteArray &fileName, const QByteArray &source, char cursorMarker = '@'); + + bool hasCursorMarker() const; + +public: + int m_cursorPosition; + Internal::CPPEditor *m_editor; + Internal::CPPEditorWidget *m_editorWidget; +}; + +class TestCase : public CppTools::Tests::TestCase +{ +public: + TestCase(bool runGarbageCollector = true); + ~TestCase(); + + static bool openCppEditor(const QString &fileName, + Internal::CPPEditor **editor, + Internal::CPPEditorWidget **editorWidget = 0); + + static CPlusPlus::Document::Ptr waitForRehighlightedSemanticDocument( + Internal::CPPEditorWidget *editorWidget); +}; + +} // namespace Tests +} // namespace Internal +} // namespace CppEditor + +#endif // CPPEDITORTESTCASE_H diff --git a/src/plugins/cppeditor/cppelementevaluator.cpp b/src/plugins/cppeditor/cppelementevaluator.cpp index 50bc7cc7d5..0da38d6958 100644 --- a/src/plugins/cppeditor/cppelementevaluator.cpp +++ b/src/plugins/cppeditor/cppelementevaluator.cpp @@ -85,7 +85,7 @@ void CppElementEvaluator::execute() return; const Snapshot &snapshot = m_modelManager->snapshot(); - Document::Ptr doc = snapshot.document(m_editor->editorDocument()->filePath()); + Document::Ptr doc = snapshot.document(m_editor->baseTextDocument()->filePath()); if (!doc) return; diff --git a/src/plugins/cppeditor/cppfollowsymbolundercursor.cpp b/src/plugins/cppeditor/cppfollowsymbolundercursor.cpp index d16e7917b2..f1ad1f8bf4 100644 --- a/src/plugins/cppeditor/cppfollowsymbolundercursor.cpp +++ b/src/plugins/cppeditor/cppfollowsymbolundercursor.cpp @@ -39,6 +39,7 @@ #include <cplusplus/SimpleLexer.h> #include <cplusplus/TypeOfExpression.h> #include <cpptools/cppmodelmanagerinterface.h> +#include <cpptools/functionutils.h> #include <cpptools/symbolfinder.h> #include <texteditor/basetextdocumentlayout.h> #include <utils/qtcassert.h> @@ -78,7 +79,6 @@ private: private: // Provided - const QSharedPointer<TypeOfExpression> m_typeOfExpression; const Document::Ptr m_expressionDocument; Scope *m_scope; const Document::Ptr &m_document; @@ -128,11 +128,13 @@ bool VirtualFunctionHelper::canLookupVirtualFunctionOverrides(Function *function if (IdExpressionAST *idExpressionAST = m_baseExpressionAST->asIdExpression()) { NameAST *name = idExpressionAST->name; const bool nameIsQualified = name && name->asQualifiedName(); - result = !nameIsQualified && FunctionHelper::isVirtualFunction(function, m_snapshot); + result = !nameIsQualified && FunctionUtils::isVirtualFunction( + function, LookupContext(m_document, m_snapshot)); } else if (MemberAccessAST *memberAccessAST = m_baseExpressionAST->asMemberAccess()) { NameAST *name = memberAccessAST->member_name; const bool nameIsQualified = name && name->asQualifiedName(); - if (!nameIsQualified && FunctionHelper::isVirtualFunction(function, m_snapshot)) { + if (!nameIsQualified && FunctionUtils::isVirtualFunction( + function, LookupContext(m_document, m_snapshot))) { TranslationUnit *unit = m_expressionDocument->translationUnit(); QTC_ASSERT(unit, return false); m_accessTokenKind = unit->tokenKind(memberAccessAST->access_token); @@ -541,7 +543,7 @@ BaseTextEditorWidget::Link FollowSymbolUnderCursor::findLink(const QTextCursor & } // Now we prefer the doc from the snapshot with macros expanded. - Document::Ptr doc = snapshot.document(m_widget->editorDocument()->filePath()); + Document::Ptr doc = snapshot.document(m_widget->baseTextDocument()->filePath()); if (!doc) { doc = documentFromSemanticInfo; if (!doc) @@ -636,7 +638,7 @@ BaseTextEditorWidget::Link FollowSymbolUnderCursor::findLink(const QTextCursor & if (Symbol *d = r.declaration()) { if (d->isDeclaration() || d->isFunction()) { const QString fileName = QString::fromUtf8(d->fileName(), d->fileNameLength()); - if (m_widget->editorDocument()->filePath() == fileName) { + if (m_widget->baseTextDocument()->filePath() == fileName) { if (unsigned(lineNumber) == d->line() && unsigned(positionInBlock) >= d->column()) { // TODO: check the end result = r; // take the symbol under cursor. diff --git a/src/plugins/cppeditor/cppincludehierarchy.cpp b/src/plugins/cppeditor/cppincludehierarchy.cpp index ba755a157d..2806b0c011 100644 --- a/src/plugins/cppeditor/cppincludehierarchy.cpp +++ b/src/plugins/cppeditor/cppincludehierarchy.cpp @@ -142,12 +142,12 @@ void CppIncludeHierarchyWidget::perform() return; m_model->clear(); - m_model->buildHierarchy(m_editor, widget->editorDocument()->filePath()); + m_model->buildHierarchy(m_editor, widget->baseTextDocument()->filePath()); if (m_model->isEmpty()) return; - m_inspectedFile->setup(widget->editorDocument()->displayName(), - widget->editorDocument()->filePath()); + m_inspectedFile->setup(widget->baseTextDocument()->displayName(), + widget->baseTextDocument()->filePath()); //expand "Includes" m_treeView->expand(m_model->index(0, 0)); diff --git a/src/plugins/cppeditor/cppincludehierarchy_test.cpp b/src/plugins/cppeditor/cppincludehierarchy_test.cpp index f10cd3ba5d..bd95012692 100644 --- a/src/plugins/cppeditor/cppincludehierarchy_test.cpp +++ b/src/plugins/cppeditor/cppincludehierarchy_test.cpp @@ -28,6 +28,7 @@ ****************************************************************************/ #include "cppeditorplugin.h" +#include "cppeditortestcase.h" #include "cppincludehierarchymodel.h" #include <coreplugin/editormanager/editormanager.h> @@ -44,13 +45,16 @@ using namespace CppEditor::Internal; using namespace CppTools; namespace { -class TestCase + +class IncludeHierarchyTestCase: public CppEditor::Internal::Tests::TestCase { public: - TestCase(const QList<QByteArray> &sourceList) - : m_cmm(CppModelManagerInterface::instance()) - , m_editor(0) + IncludeHierarchyTestCase(const QList<QByteArray> &sourceList, + int includesCount, + int includedByCount) { + QVERIFY(succeededSoFar()); + QStringList filePaths; const int sourceListSize = sourceList.size(); for (int i = 0; i < sourceListSize; ++i) { @@ -59,58 +63,29 @@ public: // Write source to file const QString fileName = QString::fromLatin1("%1/file%2.h").arg(QDir::tempPath()) .arg(i+1); - Utils::FileSaver srcSaver(fileName); - srcSaver.write(source); - srcSaver.finalize(); + QVERIFY(writeFile(fileName, source)); filePaths << fileName; } // Update Code Model - m_cmm->updateSourceFiles(filePaths); - - // Wait for the parser in the future to give us the document - QStringList filePathsNotYetInSnapshot(filePaths); - forever { - const Snapshot snapshot = m_cmm->snapshot(); - foreach (const QString &filePath, filePathsNotYetInSnapshot) { - if (snapshot.contains(filePath)) - filePathsNotYetInSnapshot.removeOne(filePath); - } - if (filePathsNotYetInSnapshot.isEmpty()) - break; - QCoreApplication::processEvents(); - } - } + QVERIFY(parseFiles(filePaths)); - ~TestCase() - { - // Close editor - if (m_editor) - Core::EditorManager::closeEditor(m_editor, false); - - m_cmm->GC(); - QVERIFY(m_cmm->snapshot().isEmpty()); - } - - void run(int includesCount, int includedByCount) - { + // Open Editor const QString fileName = QDir::tempPath() + QLatin1String("/file1.h"); + CPPEditor *editor; + QVERIFY(openCppEditor(fileName, &editor)); + closeEditorAtEndOfTestCase(editor); - m_editor = qobject_cast<CPPEditor *>(Core::EditorManager::openEditor(fileName)); - QVERIFY(m_editor); - + // Test model CppIncludeHierarchyModel model(0); - model.buildHierarchy(m_editor, fileName); + model.buildHierarchy(editor, fileName); QCOMPARE(model.rowCount(model.index(0, 0)), includesCount); QCOMPARE(model.rowCount(model.index(1, 0)), includedByCount); } - -private: - CppModelManagerInterface *m_cmm; - CPPEditor *m_editor; }; -} + +} // anonymous namespace void CppEditorPlugin::test_includeHierarchyModel_simpleIncludes() { @@ -118,8 +93,7 @@ void CppEditorPlugin::test_includeHierarchyModel_simpleIncludes() sourceList.append(QByteArray("#include \"file2.h\"\n")); sourceList.append(QByteArray()); - TestCase testCase(sourceList); - testCase.run(1, 0); + IncludeHierarchyTestCase(sourceList, 1, 0); } void CppEditorPlugin::test_includeHierarchyModel_simpleIncludedBy() @@ -128,18 +102,15 @@ void CppEditorPlugin::test_includeHierarchyModel_simpleIncludedBy() sourceList.append(QByteArray()); sourceList.append(QByteArray("#include \"file1.h\"\n")); - TestCase testCase(sourceList); - testCase.run(0, 1); + IncludeHierarchyTestCase(sourceList, 0, 1); } void CppEditorPlugin::test_includeHierarchyModel_simpleIncludesAndIncludedBy() { QList<QByteArray> sourceList; - QByteArray source; sourceList.append(QByteArray("#include \"file2.h\"\n")); sourceList.append(QByteArray()); sourceList.append(QByteArray("#include \"file1.h\"\n")); - TestCase testCase(sourceList); - testCase.run(1, 1); + IncludeHierarchyTestCase(sourceList, 1, 1); } diff --git a/src/plugins/cppeditor/cpppreprocessordialog.cpp b/src/plugins/cppeditor/cpppreprocessordialog.cpp index 3a53f30231..0366069ae6 100644 --- a/src/plugins/cppeditor/cpppreprocessordialog.cpp +++ b/src/plugins/cppeditor/cpppreprocessordialog.cpp @@ -30,7 +30,6 @@ #include "cpppreprocessordialog.h" #include "ui_cpppreprocessordialog.h" -#include "cppeditor.h" #include "cppeditorconstants.h" #include "cppsnippetprovider.h" @@ -44,11 +43,11 @@ static bool projectPartLessThan(const CppTools::ProjectPart::Ptr &projectPart1, return projectPart1->displayName < projectPart2->displayName; } -CppPreProcessorDialog::CppPreProcessorDialog(CPPEditorWidget *editorWidget, +CppPreProcessorDialog::CppPreProcessorDialog(QWidget *parent, const QString &filePath, const QList<CppTools::ProjectPart::Ptr> &projectParts) - : QDialog(editorWidget) + : QDialog(parent) , m_ui(new Ui::CppPreProcessorDialog()) - , m_filePath(editorWidget->editor()->document()->filePath()) + , m_filePath(filePath) { setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); diff --git a/src/plugins/cppeditor/cpppreprocessordialog.h b/src/plugins/cppeditor/cpppreprocessordialog.h index 25c701fe62..07fd30281b 100644 --- a/src/plugins/cppeditor/cpppreprocessordialog.h +++ b/src/plugins/cppeditor/cpppreprocessordialog.h @@ -38,14 +38,12 @@ namespace CppEditor { namespace Internal { namespace Ui { class CppPreProcessorDialog; } -class CPPEditorWidget; - class CppPreProcessorDialog : public QDialog { Q_OBJECT public: - explicit CppPreProcessorDialog(CPPEditorWidget *editorWidget, + explicit CppPreProcessorDialog(QWidget *parent, const QString &filePath, const QList<CppTools::ProjectPart::Ptr> &projectParts); ~CppPreProcessorDialog(); diff --git a/src/plugins/cppeditor/cppquickfix_test.cpp b/src/plugins/cppeditor/cppquickfix_test.cpp index 7b6bb4e0e7..9f3975843c 100644 --- a/src/plugins/cppeditor/cppquickfix_test.cpp +++ b/src/plugins/cppeditor/cppquickfix_test.cpp @@ -31,13 +31,14 @@ #include "cppeditor.h" #include "cppeditorplugin.h" +#include "cppeditortestcase.h" #include "cppquickfixassistant.h" #include "cppquickfixes.h" #include <cpptools/cppcodestylepreferences.h> #include <cpptools/cppmodelmanager.h> -#include <cpptools/cpppreprocessor.h> #include <cpptools/cpppreprocessertesthelper.h> +#include <cpptools/cpppreprocessor.h> #include <cpptools/cpptoolssettings.h> #include <cpptools/includeutils.h> @@ -58,6 +59,8 @@ using namespace CppTools; using namespace IncludeUtils; using namespace TextEditor; +using CppTools::Tests::TestIncludePaths; + namespace { typedef QByteArray _; @@ -68,97 +71,68 @@ typedef QSharedPointer<TestDocument> TestDocumentPtr; /** * Represents a test document before and after applying the quick fix. * - * A TestDocument's originalSource may contain an '@' character to denote + * A TestDocument's source may contain an '@' character to denote * the cursor position. This marker is removed before the Editor reads * the document. */ -class TestDocument +class TestDocument : public CppEditor::Internal::Tests::TestDocument { public: - TestDocument(const QByteArray &theOriginalSource, const QByteArray &theExpectedSource, - const QString &fileName) - : originalSource(theOriginalSource) - , expectedSource(theExpectedSource) - , fileName(fileName) - , cursorMarkerPosition(theOriginalSource.indexOf('@')) - , editor(0) - , editorWidget(0) - { - originalSource.remove(cursorMarkerPosition, 1); - expectedSource.remove(theExpectedSource.indexOf('@'), 1); - } - - static TestDocumentPtr create(const QByteArray &theOriginalSource, - const QByteArray &theExpectedSource,const QString &fileName) - { - return TestDocumentPtr(new TestDocument(theOriginalSource, theExpectedSource, fileName)); - } - - bool hasCursorMarkerPosition() const { return cursorMarkerPosition != -1; } - bool changesExpected() const { return originalSource != expectedSource; } - - QString filePath() const + TestDocument(const QByteArray &fileName, const QByteArray &source, + const QByteArray &expectedSource) + : CppEditor::Internal::Tests::TestDocument(fileName, source) + , m_expectedSource(expectedSource) { - if (!QFileInfo(fileName).isAbsolute()) - return QDir::tempPath() + QLatin1Char('/') + fileName; - return fileName; + m_source.remove(m_cursorPosition, 1); + m_expectedSource.remove(expectedSource.indexOf(m_cursorMarker), 1); } - void writeToDisk() const + static TestDocumentPtr create(const QByteArray &fileName, const QByteArray &source, + const QByteArray &expectedSource) { - Utils::FileSaver srcSaver(filePath()); - srcSaver.write(originalSource); - srcSaver.finalize(); + return TestDocumentPtr(new TestDocument(fileName, source, expectedSource)); } - QByteArray originalSource; - QByteArray expectedSource; - - const QString fileName; - const int cursorMarkerPosition; - - CPPEditor *editor; - CPPEditorWidget *editorWidget; +public: + QByteArray m_expectedSource; }; +QList<TestDocumentPtr> singleDocument(const QByteArray &original, const QByteArray &expected) +{ + return QList<TestDocumentPtr>() << TestDocument::create("file.cpp", original, expected); +} + /** * Encapsulates the whole process of setting up an editor, getting the * quick-fix, applying it, and checking the result. */ -class TestCase +class QuickFixTestCase : public CppEditor::Internal::Tests::TestCase { public: - TestCase(const QByteArray &originalSource, const QByteArray &expectedSource, - const QStringList &includePaths = QStringList()); - TestCase(const QList<TestDocumentPtr> theTestFiles, - const QStringList &includePaths = QStringList()); - ~TestCase(); + QuickFixTestCase(const QList<TestDocumentPtr> theTestFiles, + CppQuickFixFactory *factory, + const QStringList &includePaths = QStringList(), + int resultIndex = 0); + ~QuickFixTestCase(); +private: QuickFixOperation::Ptr getFix(CppQuickFixFactory *factory, CPPEditorWidget *editorWidget, int resultIndex = 0); - TestDocumentPtr testFileWithCursorMarker() const; - - void run(CppQuickFixFactory *factory, int resultIndex = 0); - -private: - TestCase(const TestCase &); - TestCase &operator=(const TestCase &); - - void init(const QStringList &includePaths); private: - QList<TestDocumentPtr> testFiles; + QList<TestDocumentPtr> m_testFiles; - CppCodeStylePreferences *cppCodeStylePreferences; - QByteArray cppCodeStylePreferencesOriginalDelegateId; + CppCodeStylePreferences *m_cppCodeStylePreferences; + QByteArray m_cppCodeStylePreferencesOriginalDelegateId; - QStringList includePathsToRestore; - bool restoreIncludePaths; + QStringList m_includePathsToRestore; + bool m_restoreIncludePaths; }; /// Apply the factory on the source and get back the resultIndex'th result or a null pointer. -QuickFixOperation::Ptr TestCase::getFix(CppQuickFixFactory *factory, CPPEditorWidget *editorWidget, - int resultIndex) +QuickFixOperation::Ptr QuickFixTestCase::getFix(CppQuickFixFactory *factory, + CPPEditorWidget *editorWidget, + int resultIndex) { CppQuickFixInterface qfi(new CppQuickFixAssistInterface(editorWidget, ExplicitlyInvoked)); TextEditor::QuickFixOperations results; @@ -166,172 +140,128 @@ QuickFixOperation::Ptr TestCase::getFix(CppQuickFixFactory *factory, CPPEditorWi return results.isEmpty() ? QuickFixOperation::Ptr() : results.at(resultIndex); } -/// The '@' in the originalSource is the position from where the quick-fix discovery is triggered. -TestCase::TestCase(const QByteArray &originalSource, const QByteArray &expectedSource, - const QStringList &includePaths) - : cppCodeStylePreferences(0), restoreIncludePaths(false) +/// Leading whitespace is not removed, so we can check if the indetation ranges +/// have been set correctly by the quick-fix. +QByteArray &removeTrailingWhitespace(QByteArray &input) { - testFiles << TestDocument::create(originalSource, expectedSource, QLatin1String("file.cpp")); - init(includePaths); + QList<QByteArray> lines = input.split('\n'); + input.resize(0); + foreach (QByteArray line, lines) { + while (line.length() > 0) { + char lastChar = line[line.length() - 1]; + if (lastChar == ' ' || lastChar == '\t') + line = line.left(line.length() - 1); + else + break; + } + input.append(line); + input.append('\n'); + } + return input; } +/// The '@' in the originalSource is the position from where the quick-fix discovery is triggered. /// Exactly one TestFile must contain the cursor position marker '@' in the originalSource. -TestCase::TestCase(const QList<TestDocumentPtr> theTestFiles, const QStringList &includePaths) - : testFiles(theTestFiles), cppCodeStylePreferences(0), restoreIncludePaths(false) +QuickFixTestCase::QuickFixTestCase(const QList<TestDocumentPtr> theTestFiles, + CppQuickFixFactory *factory, + const QStringList &includePaths, + int resultIndex) + : m_testFiles(theTestFiles) + , m_cppCodeStylePreferences(0) + , m_restoreIncludePaths(false) { - init(includePaths); -} + QVERIFY(succeededSoFar()); -void TestCase::init(const QStringList &includePaths) -{ // Check if there is exactly one cursor marker unsigned cursorMarkersCount = 0; - foreach (const TestDocumentPtr testFile, testFiles) { - if (testFile->hasCursorMarkerPosition()) + foreach (const TestDocumentPtr testFile, m_testFiles) { + if (testFile->hasCursorMarker()) ++cursorMarkersCount; } QVERIFY2(cursorMarkersCount == 1, "Exactly one cursor marker is allowed."); // Write files to disk - foreach (TestDocumentPtr testFile, testFiles) + foreach (TestDocumentPtr testFile, m_testFiles) testFile->writeToDisk(); - CppTools::Internal::CppModelManager *cmm = CppTools::Internal::CppModelManager::instance(); - // Set appropriate include paths if (!includePaths.isEmpty()) { - restoreIncludePaths = true; - includePathsToRestore = cmm->includePaths(); - cmm->setIncludePaths(includePaths); + m_restoreIncludePaths = true; + m_includePathsToRestore = m_modelManager->includePaths(); + m_modelManager->setIncludePaths(includePaths); } // Update Code Model QStringList filePaths; - foreach (const TestDocumentPtr &testFile, testFiles) + foreach (const TestDocumentPtr &testFile, m_testFiles) filePaths << testFile->filePath(); - cmm->updateSourceFiles(filePaths); - - // Wait for the parser in the future to give us the document - QStringList filePathsNotYetInSnapshot(filePaths); - forever { - Snapshot snapshot = cmm->snapshot(); - foreach (const QString &filePath, filePathsNotYetInSnapshot) { - if (snapshot.contains(filePath)) - filePathsNotYetInSnapshot.removeOne(filePath); - } - if (filePathsNotYetInSnapshot.isEmpty()) - break; - QCoreApplication::processEvents(); - } + QVERIFY(parseFiles(filePaths)); // Open Files - foreach (TestDocumentPtr testFile, testFiles) { - testFile->editor - = dynamic_cast<CPPEditor *>(EditorManager::openEditor(testFile->filePath())); - QVERIFY(testFile->editor); + foreach (TestDocumentPtr testFile, m_testFiles) { + QVERIFY(openCppEditor(testFile->filePath(), &testFile->m_editor, + &testFile->m_editorWidget)); + closeEditorAtEndOfTestCase(testFile->m_editor); // Set cursor position - const int cursorPosition = testFile->hasCursorMarkerPosition() - ? testFile->cursorMarkerPosition : 0; - testFile->editor->setCursorPosition(cursorPosition); - - testFile->editorWidget = dynamic_cast<CPPEditorWidget *>(testFile->editor->editorWidget()); - QVERIFY(testFile->editorWidget); + const int cursorPosition = testFile->hasCursorMarker() + ? testFile->m_cursorPosition : 0; + testFile->m_editor->setCursorPosition(cursorPosition); // Rehighlight - testFile->editorWidget->semanticRehighlight(true); - // Wait for the semantic info from the future - while (testFile->editorWidget->semanticInfo().doc.isNull()) - QCoreApplication::processEvents(); + waitForRehighlightedSemanticDocument(testFile->m_editorWidget); } // Enforce the default cpp code style, so we are independent of config file settings. // This is needed by e.g. the GenerateGetterSetter quick fix. - cppCodeStylePreferences = CppToolsSettings::instance()->cppCodeStyle(); - QVERIFY(cppCodeStylePreferences); - cppCodeStylePreferencesOriginalDelegateId = cppCodeStylePreferences->currentDelegateId(); - cppCodeStylePreferences->setCurrentDelegate("qt"); -} - -TestCase::~TestCase() -{ - // Restore default cpp code style - if (cppCodeStylePreferences) - cppCodeStylePreferences->setCurrentDelegate(cppCodeStylePreferencesOriginalDelegateId); - - // Close editors - QList<Core::IEditor *> editorsToClose; - foreach (const TestDocumentPtr testFile, testFiles) { - if (testFile->editor) - editorsToClose << testFile->editor; - } - EditorManager::closeEditors(editorsToClose, false); - QCoreApplication::processEvents(); // process any pending events - - // Remove the test files from the code-model - CppModelManagerInterface *mmi = CppModelManagerInterface::instance(); - mmi->GC(); - QCOMPARE(mmi->snapshot().size(), 0); - - // Restore include paths - if (restoreIncludePaths) - CppTools::Internal::CppModelManager::instance()->setIncludePaths(includePathsToRestore); + m_cppCodeStylePreferences = CppToolsSettings::instance()->cppCodeStyle(); + QVERIFY(m_cppCodeStylePreferences); + m_cppCodeStylePreferencesOriginalDelegateId = m_cppCodeStylePreferences->currentDelegateId(); + m_cppCodeStylePreferences->setCurrentDelegate("qt"); - // Remove created files from file system - foreach (const TestDocumentPtr &testDocument, testFiles) - QVERIFY(QFile::remove(testDocument->filePath())); -} - -/// Leading whitespace is not removed, so we can check if the indetation ranges -/// have been set correctly by the quick-fix. -QByteArray &removeTrailingWhitespace(QByteArray &input) -{ - QList<QByteArray> lines = input.split('\n'); - input.resize(0); - foreach (QByteArray line, lines) { - while (line.length() > 0) { - char lastChar = line[line.length() - 1]; - if (lastChar == ' ' || lastChar == '\t') - line = line.left(line.length() - 1); - else - break; - } - input.append(line); - input.append('\n'); - } - return input; -} - -void TestCase::run(CppQuickFixFactory *factory, int resultIndex) -{ // Run the fix in the file having the cursor marker TestDocumentPtr testFile; - foreach (const TestDocumentPtr file, testFiles) { - if (file->hasCursorMarkerPosition()) + foreach (const TestDocumentPtr file, m_testFiles) { + if (file->hasCursorMarker()) testFile = file; } QVERIFY2(testFile, "No test file with cursor marker found"); - if (QuickFixOperation::Ptr fix = getFix(factory, testFile->editorWidget, resultIndex)) + if (QuickFixOperation::Ptr fix = getFix(factory, testFile->m_editorWidget, resultIndex)) fix->perform(); else qDebug() << "Quickfix was not triggered"; // Compare all files - foreach (const TestDocumentPtr testFile, testFiles) { + foreach (const TestDocumentPtr testFile, m_testFiles) { // Check - QByteArray result = testFile->editorWidget->document()->toPlainText().toUtf8(); + QByteArray result = testFile->m_editorWidget->document()->toPlainText().toUtf8(); removeTrailingWhitespace(result); - QCOMPARE(QLatin1String(result), QLatin1String(testFile->expectedSource)); + QCOMPARE(QLatin1String(result), QLatin1String(testFile->m_expectedSource)); // Undo the change for (int i = 0; i < 100; ++i) - testFile->editorWidget->undo(); - result = testFile->editorWidget->document()->toPlainText().toUtf8(); - QCOMPARE(result, testFile->originalSource); + testFile->m_editorWidget->undo(); + result = testFile->m_editorWidget->document()->toPlainText().toUtf8(); + QCOMPARE(result, testFile->m_source); } } +QuickFixTestCase::~QuickFixTestCase() +{ + // Restore default cpp code style + if (m_cppCodeStylePreferences) + m_cppCodeStylePreferences->setCurrentDelegate(m_cppCodeStylePreferencesOriginalDelegateId); + + // Restore include paths + if (m_restoreIncludePaths) + m_modelManager->setIncludePaths(m_includePathsToRestore); + + // Remove created files from file system + foreach (const TestDocumentPtr &testDocument, m_testFiles) + QVERIFY(QFile::remove(testDocument->filePath())); +} + /// Delegates directly to AddIncludeForUndefinedIdentifierOp for easier testing. class AddIncludeForUndefinedIdentifierTestFactory : public CppQuickFixFactory { @@ -1257,8 +1187,7 @@ void CppEditorPlugin::test_quickfix() if (expected.isEmpty()) expected = original + '\n'; - TestCase data(original, expected); - data.run(factory.data()); + QuickFixTestCase(singleDocument(original, expected), factory.data()); } /// Checks: In addition to test_quickfix_GenerateGetterSetter_basicGetterWithPrefix @@ -1288,7 +1217,7 @@ void CppEditorPlugin::test_quickfix_GenerateGetterSetter_basicGetterWithPrefixAn " void setIt(int value);\n" "};\n" "}\n\n"; - testFiles << TestDocument::create(original, expected, QLatin1String("file.h")); + testFiles << TestDocument::create("file.h", original, expected); // Source File original = @@ -1308,11 +1237,10 @@ void CppEditorPlugin::test_quickfix_GenerateGetterSetter_basicGetterWithPrefixAn " it = value;\n" "}\n\n" "}\n\n"; - testFiles << TestDocument::create(original, expected, QLatin1String("file.cpp")); + testFiles << TestDocument::create("file.cpp", original, expected); GenerateGetterSetter factory; - TestCase data(testFiles); - data.run(&factory); + QuickFixTestCase(testFiles, &factory); } /// Check if definition is inserted right after class for insert definition outside @@ -1342,7 +1270,7 @@ void CppEditorPlugin::test_quickfix_InsertDefFromDecl_afterClass() "{\n\n}\n" "\n" "class Bar {};\n\n"; - testFiles << TestDocument::create(original, expected, QLatin1String("file.h")); + testFiles << TestDocument::create("file.h", original, expected); // Source File original = @@ -1352,11 +1280,10 @@ void CppEditorPlugin::test_quickfix_InsertDefFromDecl_afterClass() "{\n\n" "}\n"; expected = original + "\n"; - testFiles << TestDocument::create(original, expected, QLatin1String("file.cpp")); + testFiles << TestDocument::create("file.cpp", original, expected); InsertDefFromDecl factory; - TestCase data(testFiles); - data.run(&factory, 1); + QuickFixTestCase(testFiles, &factory, QStringList(), 1); } /// Check from header file: If there is a source file, insert the definition in the source file. @@ -1375,7 +1302,7 @@ void CppEditorPlugin::test_quickfix_InsertDefFromDecl_headerSource_basic1() " Foo()@;\n" "};\n"; expected = original + "\n"; - testFiles << TestDocument::create(original, expected, QLatin1String("file.h")); + testFiles << TestDocument::create("file.h", original, expected); // Source File original.resize(0); @@ -1386,11 +1313,10 @@ void CppEditorPlugin::test_quickfix_InsertDefFromDecl_headerSource_basic1() "}\n" "\n" ; - testFiles << TestDocument::create(original, expected, QLatin1String("file.cpp")); + testFiles << TestDocument::create("file.cpp", original, expected); InsertDefFromDecl factory; - TestCase data(testFiles); - data.run(&factory); + QuickFixTestCase(testFiles, &factory); } /// Check from header file: If there is a source file, insert the definition in the source file. @@ -1405,7 +1331,7 @@ void CppEditorPlugin::test_quickfix_InsertDefFromDecl_headerSource_basic2() // Header File original = "void f()@;\n"; expected = original + "\n"; - testFiles << TestDocument::create(original, expected, QLatin1String("file.h")); + testFiles << TestDocument::create("file.h", original, expected); // Source File original = @@ -1425,11 +1351,10 @@ void CppEditorPlugin::test_quickfix_InsertDefFromDecl_headerSource_basic2() "}\n" "\n" ; - testFiles << TestDocument::create(original, expected, QLatin1String("file.cpp")); + testFiles << TestDocument::create("file.cpp", original, expected); InsertDefFromDecl factory; - TestCase data(testFiles); - data.run(&factory); + QuickFixTestCase(testFiles, &factory); } /// Check from source file: Insert in source file, not header file. @@ -1441,7 +1366,7 @@ void CppEditorPlugin::test_quickfix_InsertDefFromDecl_headerSource_basic3() QByteArray expected; // Empty Header File - testFiles << TestDocument::create("", "\n", QLatin1String("file.h")); + testFiles << TestDocument::create("file.h", "", "\n"); // Source File original = @@ -1457,11 +1382,10 @@ void CppEditorPlugin::test_quickfix_InsertDefFromDecl_headerSource_basic3() "}\n" "\n" ; - testFiles << TestDocument::create(original, expected, QLatin1String("file.cpp")); + testFiles << TestDocument::create("file.cpp", original, expected); InsertDefFromDecl factory; - TestCase data(testFiles); - data.run(&factory); + QuickFixTestCase(testFiles, &factory); } /// Check from header file: If the class is in a namespace, the added function definition @@ -1482,7 +1406,7 @@ void CppEditorPlugin::test_quickfix_InsertDefFromDecl_headerSource_namespace1() "};\n" "}\n"; expected = original + "\n"; - testFiles << TestDocument::create(original, expected, QLatin1String("file.h")); + testFiles << TestDocument::create("file.h", original, expected); // Source File original.resize(0); @@ -1493,11 +1417,10 @@ void CppEditorPlugin::test_quickfix_InsertDefFromDecl_headerSource_namespace1() "}\n" "\n" ; - testFiles << TestDocument::create(original, expected, QLatin1String("file.cpp")); + testFiles << TestDocument::create("file.cpp", original, expected); InsertDefFromDecl factory; - TestCase data(testFiles); - data.run(&factory); + QuickFixTestCase(testFiles, &factory); } /// Check from header file: If the class is in namespace N and the source file has a @@ -1518,7 +1441,7 @@ void CppEditorPlugin::test_quickfix_InsertDefFromDecl_headerSource_namespace2() "};\n" "}\n"; expected = original + "\n"; - testFiles << TestDocument::create(original, expected, QLatin1String("file.h")); + testFiles << TestDocument::create("file.h", original, expected); // Source File original = @@ -1533,11 +1456,10 @@ void CppEditorPlugin::test_quickfix_InsertDefFromDecl_headerSource_namespace2() "}\n" "\n" ; - testFiles << TestDocument::create(original, expected, QLatin1String("file.cpp")); + testFiles << TestDocument::create("file.cpp", original, expected); InsertDefFromDecl factory; - TestCase data(testFiles); - data.run(&factory); + QuickFixTestCase(testFiles, &factory); } /// Check definition insert inside class @@ -1555,8 +1477,7 @@ void CppEditorPlugin::test_quickfix_InsertDefFromDecl_insideClass() "};\n"; InsertDefFromDecl factory; - TestCase data(original, expected); - data.run(&factory, 1); + QuickFixTestCase(singleDocument(original, expected), &factory, QStringList(), 1); } /// Check not triggering when definition exists @@ -1570,8 +1491,7 @@ void CppEditorPlugin::test_quickfix_InsertDefFromDecl_notTriggeringWhenDefinitio const QByteArray expected = original + "\n"; InsertDefFromDecl factory; - TestCase data(original, expected); - data.run(&factory, 1); + QuickFixTestCase test(singleDocument(original, expected), &factory, QStringList(), 1); } /// Find right implementation file. @@ -1592,7 +1512,7 @@ void CppEditorPlugin::test_quickfix_InsertDefFromDecl_findRightImplementationFil "};\n" "}\n"; expected = original + "\n"; - testFiles << TestDocument::create(original, expected, QLatin1String("file.h")); + testFiles << TestDocument::create("file.h", original, expected); // Source File #1 original = @@ -1603,7 +1523,7 @@ void CppEditorPlugin::test_quickfix_InsertDefFromDecl_findRightImplementationFil "}\n" "\n"; expected = original + "\n"; - testFiles << TestDocument::create(original, expected, QLatin1String("file.cpp")); + testFiles << TestDocument::create("file.cpp", original, expected); // Source File #2 @@ -1619,11 +1539,10 @@ void CppEditorPlugin::test_quickfix_InsertDefFromDecl_findRightImplementationFil "{\n\n" "}\n" "\n"; - testFiles << TestDocument::create(original, expected, QLatin1String("file2.cpp")); + testFiles << TestDocument::create("file2.cpp", original, expected); InsertDefFromDecl factory; - TestCase data(testFiles); - data.run(&factory); + QuickFixTestCase(testFiles, &factory); } /// Ignore generated functions declarations when looking at the surrounding @@ -1646,7 +1565,7 @@ void CppEditorPlugin::test_quickfix_InsertDefFromDecl_ignoreSurroundingGenerated "};\n" "}\n"; expected = original + '\n'; - testFiles << TestDocument::create(original, expected, QLatin1String("file.h")); + testFiles << TestDocument::create("file.h", original, expected); // Source File #1 original = @@ -1666,7 +1585,7 @@ void CppEditorPlugin::test_quickfix_InsertDefFromDecl_ignoreSurroundingGenerated "{\n\n" "}\n" "\n"; - testFiles << TestDocument::create(original, expected, QLatin1String("file.cpp")); + testFiles << TestDocument::create("file.cpp", original, expected); // Source File #2 original = @@ -1676,11 +1595,10 @@ void CppEditorPlugin::test_quickfix_InsertDefFromDecl_ignoreSurroundingGenerated "{\n\n" "}\n"; expected = original + '\n'; - testFiles << TestDocument::create(original, expected, QLatin1String("file2.cpp")); + testFiles << TestDocument::create("file2.cpp", original, expected); InsertDefFromDecl factory; - TestCase data(testFiles); - data.run(&factory); + QuickFixTestCase(testFiles, &factory); } /// Check if whitespace is respected for operator functions @@ -1705,8 +1623,7 @@ void CppEditorPlugin::test_quickfix_InsertDefFromDecl_respectWsInOperatorNames1( "\n"; InsertDefFromDecl factory; - TestCase data(original, expected); - data.run(&factory); + QuickFixTestCase(singleDocument(original, expected), &factory); } /// Check if whitespace is respected for operator functions @@ -1731,8 +1648,7 @@ void CppEditorPlugin::test_quickfix_InsertDefFromDecl_respectWsInOperatorNames2( "\n"; InsertDefFromDecl factory; - TestCase data(original, expected); - data.run(&factory); + QuickFixTestCase(singleDocument(original, expected), &factory); } /// Check if a function like macro use is not separated by the function to insert @@ -1747,7 +1663,7 @@ void CppEditorPlugin::test_quickfix_InsertDefFromDecl_macroUsesAtEndOfFile1() // Header File original = "void f()@;\n"; expected = original + "\n"; - testFiles << TestDocument::create(original, expected, QLatin1String("file.h")); + testFiles << TestDocument::create("file.h", original, expected); // Source File original = @@ -1772,11 +1688,10 @@ void CppEditorPlugin::test_quickfix_InsertDefFromDecl_macroUsesAtEndOfFile1() "MACRO(int)\n" "\n" ; - testFiles << TestDocument::create(original, expected, QLatin1String("file.cpp")); + testFiles << TestDocument::create("file.cpp", original, expected); InsertDefFromDecl factory; - TestCase data(testFiles); - data.run(&factory); + QuickFixTestCase(testFiles, &factory); } /// Check if a function like macro use is not separated by the function to insert @@ -1791,7 +1706,7 @@ void CppEditorPlugin::test_quickfix_InsertDefFromDecl_macroUsesAtEndOfFile2() // Header File original = "void f()@;\n"; expected = original + "\n"; - testFiles << TestDocument::create(original, expected, QLatin1String("file.h")); + testFiles << TestDocument::create("file.h", original, expected); // Source File original = @@ -1814,11 +1729,10 @@ void CppEditorPlugin::test_quickfix_InsertDefFromDecl_macroUsesAtEndOfFile2() "MACRO(int)\n" "\n" ; - testFiles << TestDocument::create(original, expected, QLatin1String("file.cpp")); + testFiles << TestDocument::create("file.cpp", original, expected); InsertDefFromDecl factory; - TestCase data(testFiles); - data.run(&factory); + QuickFixTestCase(testFiles, &factory); } /// Check if insertion happens before syntactically erroneous statements at end of file. @@ -1832,7 +1746,7 @@ void CppEditorPlugin::test_quickfix_InsertDefFromDecl_erroneousStatementAtEndOfF // Header File original = "void f()@;\n"; expected = original + "\n"; - testFiles << TestDocument::create(original, expected, QLatin1String("file.h")); + testFiles << TestDocument::create("file.h", original, expected); // Source File original = @@ -1853,11 +1767,39 @@ void CppEditorPlugin::test_quickfix_InsertDefFromDecl_erroneousStatementAtEndOfF "MissingSemicolon(int)\n" "\n" ; - testFiles << TestDocument::create(original, expected, QLatin1String("file.cpp")); + testFiles << TestDocument::create("file.cpp", original, expected); + + InsertDefFromDecl factory; + QuickFixTestCase(testFiles, &factory); +} + +/// Check: Respect rvalue references +void CppEditorPlugin::test_quickfix_InsertDefFromDecl_rvalueReference() +{ + QList<TestDocumentPtr> testFiles; + + QByteArray original; + QByteArray expected; + + // Header File + original = "void f(Foo &&)@;\n"; + expected = original + "\n"; + testFiles << TestDocument::create("file.h", original, expected); + + // Source File + original = ""; + expected = + "\n" + "void f(Foo &&)\n" + "{\n" + "\n" + "}\n" + "\n" + ; + testFiles << TestDocument::create("file.cpp", original, expected); InsertDefFromDecl factory; - TestCase data(testFiles); - data.run(&factory); + QuickFixTestCase(testFiles, &factory); } // Function for one of InsertDeclDef section cases @@ -1879,7 +1821,7 @@ void insertToSectionDeclFromDef(const QByteArray §ion, int sectionIndex) + section + ":\n" + " Foo();\n" "@};\n\n"; - testFiles << TestDocument::create(original, expected, QLatin1String("file.h")); + testFiles << TestDocument::create("file.h", original, expected); // Source File original = @@ -1891,11 +1833,10 @@ void insertToSectionDeclFromDef(const QByteArray §ion, int sectionIndex) "\n" ; expected = original + "\n"; - testFiles << TestDocument::create(original, expected, QLatin1String("file.cpp")); + testFiles << TestDocument::create("file.cpp", original, expected); InsertDeclFromDef factory; - TestCase data(testFiles); - data.run(&factory, sectionIndex); + QuickFixTestCase(testFiles, &factory, QStringList(), sectionIndex); } /// Check from source file: Insert in header file. @@ -1911,10 +1852,8 @@ void CppEditorPlugin::test_quickfix_InsertDeclFromDef() QList<Include> includesForSource(const QByteArray &source) { - const QString fileName = TestIncludePaths::directoryOfTestFile() + QLatin1String("/file.cpp"); - Utils::FileSaver srcSaver(fileName); - srcSaver.write(source); - srcSaver.finalize(); + const QString fileName = TestIncludePaths::testFilePath(); + CppTools::Tests::TestCase::writeFile(fileName, source); using namespace CppTools::Internal; @@ -2078,8 +2017,8 @@ void CppEditorPlugin::test_quickfix_AddIncludeForUndefinedIdentifier_normal() // Header File original = "class Foo {};\n"; expected = original + "\n"; - testFiles << TestDocument::create(original, expected, TestIncludePaths::directoryOfTestFile() + QLatin1Char('/') - + QLatin1String("afile.h")); + testFiles << TestDocument::create(TestIncludePaths::directoryOfTestFile().toUtf8() + "/afile.h", + original, expected); // Source File original = @@ -2100,13 +2039,12 @@ void CppEditorPlugin::test_quickfix_AddIncludeForUndefinedIdentifier_normal() "}\n" "\n" ; - testFiles << TestDocument::create(original, expected, TestIncludePaths::directoryOfTestFile() + QLatin1Char('/') - + QLatin1String("afile.cpp")); + testFiles << TestDocument::create(TestIncludePaths::directoryOfTestFile().toUtf8() + + "/afile.cpp", original, expected); // Do not use the test factory, at least once we want to go through the "full stack". AddIncludeForUndefinedIdentifier factory; - TestCase data(testFiles, QStringList(TestIncludePaths::globalIncludePath())); - data.run(&factory); + QuickFixTestCase(testFiles, &factory, QStringList(TestIncludePaths::globalIncludePath())); } /// Check: Ignore *.moc includes @@ -2128,12 +2066,11 @@ void CppEditorPlugin::test_quickfix_AddIncludeForUndefinedIdentifier_ignoremoc() "#include \"file.moc\";\n" "\n" ; - testFiles << TestDocument::create(original, expected, TestIncludePaths::directoryOfTestFile() + QLatin1Char('/') - + QLatin1String("file.cpp")); + testFiles << TestDocument::create(TestIncludePaths::directoryOfTestFile().toUtf8() + + "/file.cpp", original, expected); AddIncludeForUndefinedIdentifierTestFactory factory(QLatin1String("\"file.h\"")); - TestCase data(testFiles, QStringList(TestIncludePaths::globalIncludePath())); - data.run(&factory); + QuickFixTestCase(testFiles, &factory, QStringList(TestIncludePaths::globalIncludePath())); } /// Check: Insert include at top for a sorted group @@ -2155,12 +2092,11 @@ void CppEditorPlugin::test_quickfix_AddIncludeForUndefinedIdentifier_sortingTop( "#include \"z.h\"\n" "\n\n" ; - testFiles << TestDocument::create(original, expected, TestIncludePaths::directoryOfTestFile() + QLatin1Char('/') - + QLatin1String("file.cpp")); + testFiles << TestDocument::create(TestIncludePaths::directoryOfTestFile().toUtf8() + + "/file.cpp", original, expected); AddIncludeForUndefinedIdentifierTestFactory factory(QLatin1String("\"file.h\"")); - TestCase data(testFiles, QStringList(TestIncludePaths::globalIncludePath())); - data.run(&factory); + QuickFixTestCase(testFiles, &factory, QStringList(TestIncludePaths::globalIncludePath())); } /// Check: Insert include in the middle for a sorted group @@ -2182,12 +2118,11 @@ void CppEditorPlugin::test_quickfix_AddIncludeForUndefinedIdentifier_sortingMidd "#include \"z.h\"\n" "\n\n" ; - testFiles << TestDocument::create(original, expected, TestIncludePaths::directoryOfTestFile() + QLatin1Char('/') - + QLatin1String("file.cpp")); + testFiles << TestDocument::create(TestIncludePaths::directoryOfTestFile().toUtf8() + + "/file.cpp", original, expected); AddIncludeForUndefinedIdentifierTestFactory factory(QLatin1String("\"file.h\"")); - TestCase data(testFiles, QStringList(TestIncludePaths::globalIncludePath())); - data.run(&factory); + QuickFixTestCase(testFiles, &factory, QStringList(TestIncludePaths::globalIncludePath())); } /// Check: Insert include at bottom for a sorted group @@ -2209,12 +2144,11 @@ void CppEditorPlugin::test_quickfix_AddIncludeForUndefinedIdentifier_sortingBott "#include \"file.h\"\n" "\n\n" ; - testFiles << TestDocument::create(original, expected, TestIncludePaths::directoryOfTestFile() + QLatin1Char('/') - + QLatin1String("file.cpp")); + testFiles << TestDocument::create(TestIncludePaths::directoryOfTestFile().toUtf8() + + "/file.cpp", original, expected); AddIncludeForUndefinedIdentifierTestFactory factory(QLatin1String("\"file.h\"")); - TestCase data(testFiles, QStringList(TestIncludePaths::globalIncludePath())); - data.run(&factory); + QuickFixTestCase(testFiles, &factory, QStringList(TestIncludePaths::globalIncludePath())); } /// Check: For an unsorted group the new include is appended @@ -2236,12 +2170,11 @@ void CppEditorPlugin::test_quickfix_AddIncludeForUndefinedIdentifier_appendToUns "#include \"file.h\"\n" "\n\n" ; - testFiles << TestDocument::create(original, expected, TestIncludePaths::directoryOfTestFile() + QLatin1Char('/') - + QLatin1String("file.cpp")); + testFiles << TestDocument::create(TestIncludePaths::directoryOfTestFile().toUtf8() + + + "/file.cpp", original, expected); AddIncludeForUndefinedIdentifierTestFactory factory(QLatin1String("\"file.h\"")); - TestCase data(testFiles, QStringList(TestIncludePaths::globalIncludePath())); - data.run(&factory); + QuickFixTestCase(testFiles, &factory, QStringList(TestIncludePaths::globalIncludePath())); } /// Check: Insert a local include at front if there are only global includes @@ -2264,12 +2197,11 @@ void CppEditorPlugin::test_quickfix_AddIncludeForUndefinedIdentifier_firstLocalI "#include <b.h>\n" "\n\n" ; - testFiles << TestDocument::create(original, expected, TestIncludePaths::directoryOfTestFile() + QLatin1Char('/') - + QLatin1String("file.cpp")); + testFiles << TestDocument::create(TestIncludePaths::directoryOfTestFile().toUtf8() + + "/file.cpp", original, expected); AddIncludeForUndefinedIdentifierTestFactory factory(QLatin1String("\"file.h\"")); - TestCase data(testFiles, QStringList(TestIncludePaths::globalIncludePath())); - data.run(&factory); + QuickFixTestCase(testFiles, &factory, QStringList(TestIncludePaths::globalIncludePath())); } /// Check: Insert a global include at back if there are only local includes @@ -2295,12 +2227,11 @@ void CppEditorPlugin::test_quickfix_AddIncludeForUndefinedIdentifier_firstGlobal "void f();\n" "\n" ; - testFiles << TestDocument::create(original, expected, TestIncludePaths::directoryOfTestFile() + QLatin1Char('/') - + QLatin1String("file.cpp")); + testFiles << TestDocument::create(TestIncludePaths::directoryOfTestFile().toUtf8() + + "/file.cpp", original, expected); AddIncludeForUndefinedIdentifierTestFactory factory(QLatin1String("<file.h>")); - TestCase data(testFiles, QStringList(TestIncludePaths::globalIncludePath())); - data.run(&factory); + QuickFixTestCase(testFiles, &factory, QStringList(TestIncludePaths::globalIncludePath())); } /// Check: Prefer group with longest matching prefix @@ -2326,12 +2257,11 @@ void CppEditorPlugin::test_quickfix_AddIncludeForUndefinedIdentifier_preferGroup "#include \"foo.h\"\n" "\n\n" ; - testFiles << TestDocument::create(original, expected, TestIncludePaths::directoryOfTestFile() + QLatin1Char('/') - + QLatin1String("file.cpp")); + testFiles << TestDocument::create(TestIncludePaths::directoryOfTestFile().toUtf8() + + "/file.cpp", original, expected); AddIncludeForUndefinedIdentifierTestFactory factory(QLatin1String("\"prefixc.h\"")); - TestCase data(testFiles, QStringList(TestIncludePaths::globalIncludePath())); - data.run(&factory); + QuickFixTestCase(testFiles, &factory, QStringList(TestIncludePaths::globalIncludePath())); } /// Check: Create a new include group if there are only include groups with a different include dir @@ -2354,12 +2284,11 @@ void CppEditorPlugin::test_quickfix_AddIncludeForUndefinedIdentifier_newGroupIfO "#include \"file.h\"\n" "\n\n" ; - testFiles << TestDocument::create(original, expected, TestIncludePaths::directoryOfTestFile() + QLatin1Char('/') - + QLatin1String("file.cpp")); + testFiles << TestDocument::create(TestIncludePaths::directoryOfTestFile().toUtf8() + + "/file.cpp", original, expected); AddIncludeForUndefinedIdentifierTestFactory factory(QLatin1String("\"file.h\"")); - TestCase data(testFiles, QStringList(TestIncludePaths::globalIncludePath())); - data.run(&factory); + QuickFixTestCase(testFiles, &factory, QStringList(TestIncludePaths::globalIncludePath())); } /// Check: Include group with mixed include dirs, sorted --> insert properly @@ -2383,12 +2312,11 @@ void CppEditorPlugin::test_quickfix_AddIncludeForUndefinedIdentifier_mixedDirsSo "#include <utils/file.h>\n" "\n\n" ; - testFiles << TestDocument::create(original, expected, TestIncludePaths::directoryOfTestFile() + QLatin1Char('/') - + QLatin1String("file.cpp")); + testFiles << TestDocument::create(TestIncludePaths::directoryOfTestFile().toUtf8() + + "/file.cpp", original, expected); AddIncludeForUndefinedIdentifierTestFactory factory(QLatin1String("<firstlib/file.h>")); - TestCase data(testFiles, QStringList(TestIncludePaths::globalIncludePath())); - data.run(&factory); + QuickFixTestCase(testFiles, &factory, QStringList(TestIncludePaths::globalIncludePath())); } /// Check: Include group with mixed include dirs, unsorted --> append @@ -2412,12 +2340,11 @@ void CppEditorPlugin::test_quickfix_AddIncludeForUndefinedIdentifier_mixedDirsUn "#include <lastlib/file.h>\n" "\n\n" ; - testFiles << TestDocument::create(original, expected, TestIncludePaths::directoryOfTestFile() + QLatin1Char('/') - + QLatin1String("file.cpp")); + testFiles << TestDocument::create(TestIncludePaths::directoryOfTestFile().toUtf8() + + "/file.cpp", original, expected); AddIncludeForUndefinedIdentifierTestFactory factory(QLatin1String("<lastlib/file.h>")); - TestCase data(testFiles, QStringList(TestIncludePaths::globalIncludePath())); - data.run(&factory); + QuickFixTestCase(testFiles, &factory, QStringList(TestIncludePaths::globalIncludePath())); } /// Check: Include group with mixed include types @@ -2439,12 +2366,11 @@ void CppEditorPlugin::test_quickfix_AddIncludeForUndefinedIdentifier_mixedInclud "#include <global.h>\n" "\n\n" ; - testFiles << TestDocument::create(original, expected, TestIncludePaths::directoryOfTestFile() + QLatin1Char('/') - + QLatin1String("file.cpp")); + testFiles << TestDocument::create(TestIncludePaths::directoryOfTestFile().toUtf8() + + "/file.cpp", original, expected); AddIncludeForUndefinedIdentifierTestFactory factory(QLatin1String("\"z.h\"")); - TestCase data(testFiles, QStringList(TestIncludePaths::globalIncludePath())); - data.run(&factory); + QuickFixTestCase(testFiles, &factory, QStringList(TestIncludePaths::globalIncludePath())); } /// Check: Include group with mixed include types @@ -2466,12 +2392,11 @@ void CppEditorPlugin::test_quickfix_AddIncludeForUndefinedIdentifier_mixedInclud "#include <global.h>\n" "\n\n" ; - testFiles << TestDocument::create(original, expected, TestIncludePaths::directoryOfTestFile() + QLatin1Char('/') - + QLatin1String("file.cpp")); + testFiles << TestDocument::create(TestIncludePaths::directoryOfTestFile().toUtf8() + + "/file.cpp", original, expected); AddIncludeForUndefinedIdentifierTestFactory factory(QLatin1String("\"a.h\"")); - TestCase data(testFiles, QStringList(TestIncludePaths::globalIncludePath())); - data.run(&factory); + QuickFixTestCase(testFiles, &factory, QStringList(TestIncludePaths::globalIncludePath())); } /// Check: Include group with mixed include types @@ -2493,12 +2418,11 @@ void CppEditorPlugin::test_quickfix_AddIncludeForUndefinedIdentifier_mixedInclud "#include <global.h>\n" "\n\n" ; - testFiles << TestDocument::create(original, expected, TestIncludePaths::directoryOfTestFile() + QLatin1Char('/') - + QLatin1String("file.cpp")); + testFiles << TestDocument::create(TestIncludePaths::directoryOfTestFile().toUtf8() + + "/file.cpp", original, expected); AddIncludeForUndefinedIdentifierTestFactory factory(QLatin1String("\"lib/file.h\"")); - TestCase data(testFiles, QStringList(TestIncludePaths::globalIncludePath())); - data.run(&factory); + QuickFixTestCase(testFiles, &factory, QStringList(TestIncludePaths::globalIncludePath())); } /// Check: Include group with mixed include types @@ -2520,12 +2444,11 @@ void CppEditorPlugin::test_quickfix_AddIncludeForUndefinedIdentifier_mixedInclud "#include <lib/file.h>\n" "\n\n" ; - testFiles << TestDocument::create(original, expected, TestIncludePaths::directoryOfTestFile() + QLatin1Char('/') - + QLatin1String("file.cpp")); + testFiles << TestDocument::create(TestIncludePaths::directoryOfTestFile().toUtf8() + + "/file.cpp", original, expected); AddIncludeForUndefinedIdentifierTestFactory factory(QLatin1String("<lib/file.h>")); - TestCase data(testFiles, QStringList(TestIncludePaths::globalIncludePath())); - data.run(&factory); + QuickFixTestCase(testFiles, &factory, QStringList(TestIncludePaths::globalIncludePath())); } /// Check: Insert very first include @@ -2545,12 +2468,11 @@ void CppEditorPlugin::test_quickfix_AddIncludeForUndefinedIdentifier_noinclude() "void f();\n" "\n" ; - testFiles << TestDocument::create(original, expected, TestIncludePaths::directoryOfTestFile() + QLatin1Char('/') - + QLatin1String("file.cpp")); + testFiles << TestDocument::create(TestIncludePaths::directoryOfTestFile().toUtf8() + + "/file.cpp", original, expected); AddIncludeForUndefinedIdentifierTestFactory factory(QLatin1String("\"file.h\"")); - TestCase data(testFiles, QStringList(TestIncludePaths::globalIncludePath())); - data.run(&factory); + QuickFixTestCase(testFiles, &factory, QStringList(TestIncludePaths::globalIncludePath())); } /// Check: Insert very first include if there is a c++ style comment on top @@ -2576,12 +2498,11 @@ void CppEditorPlugin::test_quickfix_AddIncludeForUndefinedIdentifier_veryFirstIn "void @f();\n" "\n" ; - testFiles << TestDocument::create(original, expected, TestIncludePaths::directoryOfTestFile() + QLatin1Char('/') - + QLatin1String("file.cpp")); + testFiles << TestDocument::create(TestIncludePaths::directoryOfTestFile().toUtf8() + + "/file.cpp", original, expected); AddIncludeForUndefinedIdentifierTestFactory factory(QLatin1String("\"file.h\"")); - TestCase data(testFiles, QStringList(TestIncludePaths::globalIncludePath())); - data.run(&factory); + QuickFixTestCase(testFiles, &factory, QStringList(TestIncludePaths::globalIncludePath())); } /// Check: Insert very first include if there is a c style comment on top @@ -2611,12 +2532,11 @@ void CppEditorPlugin::test_quickfix_AddIncludeForUndefinedIdentifier_veryFirstIn "void @f();\n" "\n" ; - testFiles << TestDocument::create(original, expected, TestIncludePaths::directoryOfTestFile() + QLatin1Char('/') - + QLatin1String("file.cpp")); + testFiles << TestDocument::create(TestIncludePaths::directoryOfTestFile().toUtf8() + + "/file.cpp", original, expected); AddIncludeForUndefinedIdentifierTestFactory factory(QLatin1String("\"file.h\"")); - TestCase data(testFiles, QStringList(TestIncludePaths::globalIncludePath())); - data.run(&factory); + QuickFixTestCase(testFiles, &factory, QStringList(TestIncludePaths::globalIncludePath())); } /// Check: If a "Qt Class" was not found by the locator, check the header files in the Qt @@ -2637,12 +2557,12 @@ void CppEditorPlugin::test_quickfix_AddIncludeForUndefinedIdentifier_checkQSomet "QDir dir;\n" "\n" ; - testFiles << TestDocument::create(original, expected, TestIncludePaths::directoryOfTestFile() + QLatin1Char('/') - + QLatin1String("file.cpp")); + testFiles << TestDocument::create(TestIncludePaths::directoryOfTestFile().toUtf8() + + "/file.cpp", original, expected); AddIncludeForUndefinedIdentifier factory; - TestCase data(testFiles, QStringList(TestIncludePaths::globalQtCoreIncludePath())); - data.run(&factory); + QuickFixTestCase(testFiles,&factory, + QStringList(CppTools::Tests::TestIncludePaths::globalQtCoreIncludePath())); } /// Check: Move definition from header to cpp. @@ -2668,7 +2588,7 @@ void CppEditorPlugin::test_quickfix_MoveFuncDefOutside_MemberFuncToCpp() "\n" " void bar();\n" "};\n"; - testFiles << TestDocument::create(original, expected, QLatin1String("file.h")); + testFiles << TestDocument::create("file.h", original, expected); // Source File original = @@ -2682,11 +2602,10 @@ void CppEditorPlugin::test_quickfix_MoveFuncDefOutside_MemberFuncToCpp() " return 5;\n" "}\n" "\n"; - testFiles << TestDocument::create(original, expected, QLatin1String("file.cpp")); + testFiles << TestDocument::create("file.cpp", original, expected); MoveFuncDefOutside factory; - TestCase data(testFiles); - data.run(&factory); + QuickFixTestCase(testFiles, &factory); } void CppEditorPlugin::test_quickfix_MoveFuncDefOutside_MemberFuncToCppInsideNS() @@ -2711,7 +2630,7 @@ void CppEditorPlugin::test_quickfix_MoveFuncDefOutside_MemberFuncToCppInsideNS() " int ba@r();\n" "};\n" "}\n\n"; - testFiles << TestDocument::create(original, expected, QLatin1String("file.h")); + testFiles << TestDocument::create("file.h", original, expected); // Source File original = @@ -2729,11 +2648,10 @@ void CppEditorPlugin::test_quickfix_MoveFuncDefOutside_MemberFuncToCppInsideNS() "}\n" "\n" "}\n\n"; - testFiles << TestDocument::create(original, expected, QLatin1String("file.cpp")); + testFiles << TestDocument::create("file.cpp", original, expected); MoveFuncDefOutside factory; - TestCase data(testFiles); - data.run(&factory); + QuickFixTestCase(testFiles, &factory); } /// Check: Move definition outside class @@ -2767,8 +2685,7 @@ void CppEditorPlugin::test_quickfix_MoveFuncDefOutside_MemberFuncOutside1() "void Foo::f4() {}\n\n"; MoveFuncDefOutside factory; - TestCase data(original, expected); - data.run(&factory); + QuickFixTestCase(singleDocument(original, expected), &factory); } /// Check: Move definition outside class @@ -2799,7 +2716,7 @@ void CppEditorPlugin::test_quickfix_MoveFuncDefOutside_MemberFuncOutside2() "{\n" " return 1;\n" "}\n\n"; - testFiles << TestDocument::create(original, expected, QLatin1String("file.h")); + testFiles << TestDocument::create("file.h", original, expected); // Source File original = @@ -2807,11 +2724,10 @@ void CppEditorPlugin::test_quickfix_MoveFuncDefOutside_MemberFuncOutside2() "void Foo::f1() {}\n" "void Foo::f3() {}\n"; expected = original + "\n"; - testFiles << TestDocument::create(original, expected, QLatin1String("file.cpp")); + testFiles << TestDocument::create("file.cpp", original, expected); MoveFuncDefOutside factory; - TestCase data(testFiles); - data.run(&factory, 1); + QuickFixTestCase(testFiles, &factory, QStringList(), 1); } /// Check: Move definition from header to cpp (with namespace). @@ -2837,7 +2753,7 @@ void CppEditorPlugin::test_quickfix_MoveFuncDefOutside_MemberFuncToCppNS() " inline int number() const;\n" "};\n" "}\n"; - testFiles << TestDocument::create(original, expected, QLatin1String("file.h")); + testFiles << TestDocument::create("file.h", original, expected); // Source File original = @@ -2851,11 +2767,10 @@ void CppEditorPlugin::test_quickfix_MoveFuncDefOutside_MemberFuncToCppNS() " return 5;\n" "}\n" "\n"; - testFiles << TestDocument::create(original, expected, QLatin1String("file.cpp")); + testFiles << TestDocument::create("file.cpp", original, expected); MoveFuncDefOutside factory; - TestCase data(testFiles); - data.run(&factory); + QuickFixTestCase(testFiles, &factory); } /// Check: Move definition from header to cpp (with namespace + using). @@ -2881,7 +2796,7 @@ void CppEditorPlugin::test_quickfix_MoveFuncDefOutside_MemberFuncToCppNSUsing() " inline int number() const;\n" "};\n" "}\n"; - testFiles << TestDocument::create(original, expected, QLatin1String("file.h")); + testFiles << TestDocument::create("file.h", original, expected); // Source File original = @@ -2897,11 +2812,10 @@ void CppEditorPlugin::test_quickfix_MoveFuncDefOutside_MemberFuncToCppNSUsing() " return 5;\n" "}\n" "\n"; - testFiles << TestDocument::create(original, expected, QLatin1String("file.cpp")); + testFiles << TestDocument::create("file.cpp", original, expected); MoveFuncDefOutside factory; - TestCase data(testFiles); - data.run(&factory); + QuickFixTestCase(testFiles, &factory); } /// Check: Move definition outside class with Namespace @@ -2928,8 +2842,7 @@ void CppEditorPlugin::test_quickfix_MoveFuncDefOutside_MemberFuncOutsideWithNs() "\n}\n"; MoveFuncDefOutside factory; - TestCase data(original, expected); - data.run(&factory); + QuickFixTestCase(singleDocument(original, expected), &factory); } /// Check: Move free function from header to cpp. @@ -2948,7 +2861,7 @@ void CppEditorPlugin::test_quickfix_MoveFuncDefOutside_FreeFuncToCpp() expected = "int number() const;\n" "\n"; - testFiles << TestDocument::create(original, expected, QLatin1String("file.h")); + testFiles << TestDocument::create("file.h", original, expected); // Source File original = @@ -2962,11 +2875,10 @@ void CppEditorPlugin::test_quickfix_MoveFuncDefOutside_FreeFuncToCpp() " return 5;\n" "}\n" "\n"; - testFiles << TestDocument::create(original, expected, QLatin1String("file.cpp")); + testFiles << TestDocument::create("file.cpp", original, expected); MoveFuncDefOutside factory; - TestCase data(testFiles); - data.run(&factory); + QuickFixTestCase(testFiles, &factory); } /// Check: Move free function from header to cpp (with namespace). @@ -2989,7 +2901,7 @@ void CppEditorPlugin::test_quickfix_MoveFuncDefOutside_FreeFuncToCppNS() "int number() const;\n" "}\n" "\n"; - testFiles << TestDocument::create(original, expected, QLatin1String("file.h")); + testFiles << TestDocument::create("file.h", original, expected); // Source File original = @@ -3003,11 +2915,10 @@ void CppEditorPlugin::test_quickfix_MoveFuncDefOutside_FreeFuncToCppNS() " return 5;\n" "}\n" "\n"; - testFiles << TestDocument::create(original, expected, QLatin1String("file.cpp")); + testFiles << TestDocument::create("file.cpp", original, expected); MoveFuncDefOutside factory; - TestCase data(testFiles); - data.run(&factory); + QuickFixTestCase(testFiles, &factory); } /// Check: Move Ctor with member initialization list (QTCREATORBUG-9157). @@ -3034,7 +2945,7 @@ void CppEditorPlugin::test_quickfix_MoveFuncDefOutside_CtorWithInitialization1() " int a;\n" " float b;\n" "};\n"; - testFiles << TestDocument::create(original, expected, QLatin1String("file.h")); + testFiles << TestDocument::create("file.h", original, expected); // Source File original ="#include \"file.h\"\n"; @@ -3044,11 +2955,10 @@ void CppEditorPlugin::test_quickfix_MoveFuncDefOutside_CtorWithInitialization1() "\n" "Foo::Foo() : a(42), b(3.141) {}\n" "\n"; - testFiles << TestDocument::create(original, expected, QLatin1String("file.cpp")); + testFiles << TestDocument::create("file.cpp", original, expected); MoveFuncDefOutside factory; - TestCase data(testFiles); - data.run(&factory); + QuickFixTestCase(testFiles, &factory); } /// Check: Move Ctor with member initialization list (QTCREATORBUG-9462). @@ -3078,7 +2988,7 @@ void CppEditorPlugin::test_quickfix_MoveFuncDefOutside_CtorWithInitialization2() "\n" " int member;\n" "};\n"; - testFiles << TestDocument::create(original, expected, QLatin1String("file.h")); + testFiles << TestDocument::create("file.h", original, expected); // Source File original ="#include \"file.h\"\n"; @@ -3090,11 +3000,10 @@ void CppEditorPlugin::test_quickfix_MoveFuncDefOutside_CtorWithInitialization2() "{\n" "}\n" "\n"; - testFiles << TestDocument::create(original, expected, QLatin1String("file.cpp")); + testFiles << TestDocument::create("file.cpp", original, expected); MoveFuncDefOutside factory; - TestCase data(testFiles); - data.run(&factory); + QuickFixTestCase(testFiles, &factory); } /// Check if definition is inserted right after class for move definition outside @@ -3123,7 +3032,7 @@ void CppEditorPlugin::test_quickfix_MoveFuncDefOutside_afterClass() "void Foo::a() {}\n" "\n" "class Bar {};\n\n"; - testFiles << TestDocument::create(original, expected, QLatin1String("file.h")); + testFiles << TestDocument::create("file.h", original, expected); // Source File original = @@ -3133,11 +3042,10 @@ void CppEditorPlugin::test_quickfix_MoveFuncDefOutside_afterClass() "{\n\n" "}\n"; expected = original + "\n"; - testFiles << TestDocument::create(original, expected, QLatin1String("file.cpp")); + testFiles << TestDocument::create("file.cpp", original, expected); MoveFuncDefOutside factory; - TestCase data(testFiles); - data.run(&factory, 1); + QuickFixTestCase(testFiles, &factory, QStringList(), 1); } /// Check if whitespace is respected for operator functions @@ -3159,8 +3067,7 @@ void CppEditorPlugin::test_quickfix_MoveFuncDefOutside_respectWsInOperatorNames1 "\n"; MoveFuncDefOutside factory; - TestCase data(original, expected); - data.run(&factory); + QuickFixTestCase(singleDocument(original, expected), &factory); } /// Check if whitespace is respected for operator functions @@ -3182,8 +3089,7 @@ void CppEditorPlugin::test_quickfix_MoveFuncDefOutside_respectWsInOperatorNames2 "\n"; MoveFuncDefOutside factory; - TestCase data(original, expected); - data.run(&factory); + QuickFixTestCase(singleDocument(original, expected), &factory); } /// Check: revert test_quickfix_MoveFuncDefOutside_MemberFuncToCpp() @@ -3202,7 +3108,7 @@ void CppEditorPlugin::test_quickfix_MoveFuncDefToDecl_MemberFunc() "class Foo {\n" " inline int number() const {return 5;}\n" "};\n\n"; - testFiles << TestDocument::create(original, expected, QLatin1String("file.h")); + testFiles << TestDocument::create("file.h", original, expected); // Source File original = @@ -3212,11 +3118,10 @@ void CppEditorPlugin::test_quickfix_MoveFuncDefToDecl_MemberFunc() expected = "#include \"file.h\"\n" "\n\n\n"; - testFiles << TestDocument::create(original, expected, QLatin1String("file.cpp")); + testFiles << TestDocument::create("file.cpp", original, expected); MoveFuncDefToDecl factory; - TestCase data(testFiles); - data.run(&factory); + QuickFixTestCase(testFiles, &factory); } /// Check: revert test_quickfix_MoveFuncDefOutside_MemberFuncOutside() @@ -3242,8 +3147,7 @@ void CppEditorPlugin::test_quickfix_MoveFuncDefToDecl_MemberFuncOutside() "\n\n\n"; MoveFuncDefToDecl factory; - TestCase data(original, expected); - data.run(&factory); + QuickFixTestCase(singleDocument(original, expected), &factory); } /// Check: revert test_quickfix_MoveFuncDefOutside_MemberFuncToCppNS() @@ -3269,7 +3173,7 @@ void CppEditorPlugin::test_quickfix_MoveFuncDefToDecl_MemberFuncToCppNS() " }\n" "};\n" "}\n\n"; - testFiles << TestDocument::create(original, expected, QLatin1String("file.h")); + testFiles << TestDocument::create("file.h", original, expected); // Source File original = @@ -3280,11 +3184,10 @@ void CppEditorPlugin::test_quickfix_MoveFuncDefToDecl_MemberFuncToCppNS() " return 5;\n" "}\n"; expected = "#include \"file.h\"\n\n\n\n"; - testFiles << TestDocument::create(original, expected, QLatin1String("file.cpp")); + testFiles << TestDocument::create("file.cpp", original, expected); MoveFuncDefToDecl factory; - TestCase data(testFiles); - data.run(&factory); + QuickFixTestCase(testFiles, &factory); } /// Check: revert test_quickfix_MoveFuncDefOutside_MemberFuncToCppNSUsing() @@ -3310,7 +3213,7 @@ void CppEditorPlugin::test_quickfix_MoveFuncDefToDecl_MemberFuncToCppNSUsing() " }\n" "};\n" "}\n\n"; - testFiles << TestDocument::create(original, expected, QLatin1String("file.h")); + testFiles << TestDocument::create("file.h", original, expected); // Source File original = @@ -3325,11 +3228,10 @@ void CppEditorPlugin::test_quickfix_MoveFuncDefToDecl_MemberFuncToCppNSUsing() "#include \"file.h\"\n" "using namespace MyNs;\n" "\n\n\n"; - testFiles << TestDocument::create(original, expected, QLatin1String("file.cpp")); + testFiles << TestDocument::create("file.cpp", original, expected); MoveFuncDefToDecl factory; - TestCase data(testFiles); - data.run(&factory); + QuickFixTestCase(testFiles, &factory); } /// Check: revert test_quickfix_MoveFuncDefOutside_MemberFuncOutsideWithNs() @@ -3356,8 +3258,7 @@ void CppEditorPlugin::test_quickfix_MoveFuncDefToDecl_MemberFuncOutsideWithNs() "};\n\n\n}\n\n"; MoveFuncDefToDecl factory; - TestCase data(original, expected); - data.run(&factory); + QuickFixTestCase(singleDocument(original, expected), &factory); } /// Check: revert test_quickfix_MoveFuncDefOutside_FreeFuncToCpp() @@ -3374,7 +3275,7 @@ void CppEditorPlugin::test_quickfix_MoveFuncDefToDecl_FreeFuncToCpp() "{\n" " return 5;\n" "}\n\n"; - testFiles << TestDocument::create(original, expected, QLatin1String("file.h")); + testFiles << TestDocument::create("file.h", original, expected); // Source File original = @@ -3386,11 +3287,10 @@ void CppEditorPlugin::test_quickfix_MoveFuncDefToDecl_FreeFuncToCpp() " return 5;\n" "}\n"; expected = "#include \"file.h\"\n\n\n\n\n"; - testFiles << TestDocument::create(original, expected, QLatin1String("file.cpp")); + testFiles << TestDocument::create("file.cpp", original, expected); MoveFuncDefToDecl factory; - TestCase data(testFiles); - data.run(&factory); + QuickFixTestCase(testFiles, &factory); } /// Check: revert test_quickfix_MoveFuncDefOutside_FreeFuncToCppNS() @@ -3412,7 +3312,7 @@ void CppEditorPlugin::test_quickfix_MoveFuncDefToDecl_FreeFuncToCppNS() " return 5;\n" "}\n" "}\n\n"; - testFiles << TestDocument::create(original, expected, QLatin1String("file.h")); + testFiles << TestDocument::create("file.h", original, expected); // Source File original = @@ -3425,11 +3325,10 @@ void CppEditorPlugin::test_quickfix_MoveFuncDefToDecl_FreeFuncToCppNS() expected = "#include \"file.h\"\n" "\n\n\n"; - testFiles << TestDocument::create(original, expected, QLatin1String("file.cpp")); + testFiles << TestDocument::create("file.cpp", original, expected); MoveFuncDefToDecl factory; - TestCase data(testFiles); - data.run(&factory); + QuickFixTestCase(testFiles, &factory); } /// Check: revert test_quickfix_MoveFuncDefOutside_CtorWithInitialization() @@ -3456,7 +3355,7 @@ void CppEditorPlugin::test_quickfix_MoveFuncDefToDecl_CtorWithInitialization() " int a;\n" " float b;\n" "};\n"; - testFiles << TestDocument::create(original, expected, QLatin1String("file.h")); + testFiles << TestDocument::create("file.h", original, expected); // Source File original = @@ -3465,11 +3364,10 @@ void CppEditorPlugin::test_quickfix_MoveFuncDefToDecl_CtorWithInitialization() "Foo::F@oo() : a(42), b(3.141) {}" ; expected ="#include \"file.h\"\n\n\n"; - testFiles << TestDocument::create(original, expected, QLatin1String("file.cpp")); + testFiles << TestDocument::create("file.cpp", original, expected); MoveFuncDefToDecl factory; - TestCase data(testFiles); - data.run(&factory); + QuickFixTestCase(testFiles, &factory); } /// Check: Definition should not be placed behind the variable. QTCREATORBUG-10303 @@ -3495,8 +3393,7 @@ void CppEditorPlugin::test_quickfix_MoveFuncDefToDecl_structWithAssignedVariable "} bar;\n\n\n"; MoveFuncDefToDecl factory; - TestCase data(original, expected); - data.run(&factory); + QuickFixTestCase(singleDocument(original, expected), &factory); } void CppEditorPlugin::test_quickfix_AssignToLocalVariable_templates() @@ -3515,7 +3412,7 @@ void CppEditorPlugin::test_quickfix_AssignToLocalVariable_templates() "};\n" ; expected = original + "\n"; - testFiles << TestDocument::create(original, expected, QLatin1String("file.h")); + testFiles << TestDocument::create("file.h", original, expected); // Source File original = @@ -3530,11 +3427,10 @@ void CppEditorPlugin::test_quickfix_AssignToLocalVariable_templates() " List<int> list;\n" " int localFirst = list.first();\n" "}\n"; - testFiles << TestDocument::create(original, expected, QLatin1String("file.cpp")); + testFiles << TestDocument::create("file.cpp", original, expected); AssignToLocalVariable factory; - TestCase data(testFiles); - data.run(&factory); + QuickFixTestCase(testFiles, &factory); } void CppEditorPlugin::test_quickfix_ExtractLiteralAsParameter_typeDeduction_data() @@ -3598,8 +3494,7 @@ void CppEditorPlugin::test_quickfix_ExtractLiteralAsParameter_typeDeduction() } ExtractLiteralAsParameter factory; - TestCase data(original, expected); - data.run(&factory); + QuickFixTestCase(singleDocument(original, expected), &factory); } void CppEditorPlugin::test_quickfix_ExtractLiteralAsParameter_freeFunction_separateFiles() @@ -3613,7 +3508,7 @@ void CppEditorPlugin::test_quickfix_ExtractLiteralAsParameter_freeFunction_separ "void foo(const char *a, long b = 1);"; expected = "void foo(const char *a, long b = 1, int newParameter = 156);\n"; - testFiles << TestDocument::create(original, expected, QLatin1String("file.h")); + testFiles << TestDocument::create("file.h", original, expected); // Source File original = @@ -3622,11 +3517,10 @@ void CppEditorPlugin::test_quickfix_ExtractLiteralAsParameter_freeFunction_separ expected = "void foo(const char *a, long b, int newParameter)\n" "{return newParameter + 123 + newParameter;}\n"; - testFiles << TestDocument::create(original, expected, QLatin1String("file.cpp")); + testFiles << TestDocument::create("file.cpp", original, expected); ExtractLiteralAsParameter factory; - TestCase data(testFiles); - data.run(&factory); + QuickFixTestCase(testFiles, &factory); } void CppEditorPlugin::test_quickfix_ExtractLiteralAsParameter_memberFunction_separateFiles() @@ -3646,7 +3540,7 @@ void CppEditorPlugin::test_quickfix_ExtractLiteralAsParameter_memberFunction_sep "public:\n" " int zort(int newParameter = 155);\n" "};\n"; - testFiles << TestDocument::create(original, expected, QLatin1String("file.h")); + testFiles << TestDocument::create("file.h", original, expected); // Source File original = @@ -3657,11 +3551,10 @@ void CppEditorPlugin::test_quickfix_ExtractLiteralAsParameter_memberFunction_sep "#include \"file.h\"\n\n" "int Narf::zort(int newParameter)\n" "{ return newParameter + 1; }\n"; - testFiles << TestDocument::create(original, expected, QLatin1String("file.cpp")); + testFiles << TestDocument::create("file.cpp", original, expected); ExtractLiteralAsParameter factory; - TestCase data(testFiles); - data.run(&factory); + QuickFixTestCase(testFiles, &factory); } Q_DECLARE_METATYPE(InsertVirtualMethodsDialog::ImplementationMode) @@ -3966,8 +3859,7 @@ void CppEditorPlugin::test_quickfix_InsertVirtualMethods() InsertVirtualMethods factory( new InsertVirtualMethodsDialogTest(implementationMode, insertVirtualKeyword)); - TestCase data(original, expected); - data.run(&factory); + QuickFixTestCase(singleDocument(original, expected), &factory); } /// Check: Insert in implementation file @@ -4000,7 +3892,7 @@ void CppEditorPlugin::test_quickfix_InsertVirtualMethods_implementationFile() "public:\n" " virtual int a();\n" "};\n"; - testFiles << TestDocument::create(original, expected, QLatin1String("file.h")); + testFiles << TestDocument::create("file.h", original, expected); // Source File original = "#include \"file.h\"\n"; @@ -4009,12 +3901,11 @@ void CppEditorPlugin::test_quickfix_InsertVirtualMethods_implementationFile() "\n\n" "int Derived::a()\n" "{\n}\n"; - testFiles << TestDocument::create(original, expected, QLatin1String("file.cpp")); + testFiles << TestDocument::create("file.cpp", original, expected); InsertVirtualMethods factory(new InsertVirtualMethodsDialogTest( InsertVirtualMethodsDialog::ModeImplementationFile, true)); - TestCase data(testFiles); - data.run(&factory); + QuickFixTestCase(testFiles, &factory); } /// Check: Qualified names. @@ -4053,7 +3944,7 @@ void CppEditorPlugin::test_quickfix_InsertVirtualMethods_BaseClassInNamespace() "public:\n" " virtual BaseNS::BaseEnum a(BaseNS::BaseEnum e);\n" "};\n"; - testFiles << TestDocument::create(original, expected, QLatin1String("file.h")); + testFiles << TestDocument::create("file.h", original, expected); // Source File original = "#include \"file.h\"\n"; @@ -4062,10 +3953,9 @@ void CppEditorPlugin::test_quickfix_InsertVirtualMethods_BaseClassInNamespace() "\n\n" "BaseNS::BaseEnum Derived::a(BaseNS::BaseEnum e)\n" "{\n}\n"; - testFiles << TestDocument::create(original, expected, QLatin1String("file.cpp")); + testFiles << TestDocument::create("file.cpp", original, expected); InsertVirtualMethods factory(new InsertVirtualMethodsDialogTest( InsertVirtualMethodsDialog::ModeImplementationFile, true)); - TestCase data(testFiles); - data.run(&factory); + QuickFixTestCase(testFiles, &factory); } diff --git a/src/plugins/cppeditor/cppquickfixassistant.cpp b/src/plugins/cppeditor/cppquickfixassistant.cpp index 2f84cf80d0..6e0c253f62 100644 --- a/src/plugins/cppeditor/cppquickfixassistant.cpp +++ b/src/plugins/cppeditor/cppquickfixassistant.cpp @@ -87,7 +87,7 @@ const IAssistProvider *CppQuickFixAssistProcessor::provider() const CppQuickFixAssistInterface::CppQuickFixAssistInterface(CPPEditorWidget *editor, TextEditor::AssistReason reason) : DefaultAssistInterface(editor->document(), editor->position(), - editor->editorDocument()->filePath(), reason) + editor->baseTextDocument()->filePath(), reason) , m_editor(editor) , m_semanticInfo(editor->semanticInfo()) , m_snapshot(CppTools::CppModelManagerInterface::instance()->snapshot()) diff --git a/src/plugins/cppeditor/cppquickfixes.cpp b/src/plugins/cppeditor/cppquickfixes.cpp index be06973d6e..92c4615a43 100644 --- a/src/plugins/cppeditor/cppquickfixes.cpp +++ b/src/plugins/cppeditor/cppquickfixes.cpp @@ -158,10 +158,8 @@ InsertionLocation insertLocationForMethodDefinition(Symbol *symbol, const bool u = locator.methodDefinition(symbol, useSymbolFinder, fileName); for (int i = 0; i < list.count(); ++i) { InsertionLocation location = list.at(i); - if (location.isValid() && location.fileName() == fileName) { + if (location.isValid() && location.fileName() == fileName) return location; - break; - } } // ...failed, @@ -4769,11 +4767,11 @@ public: ? Qt::Checked : Qt::Unchecked; for (Scope::iterator it = clazz->firstMember(); it != clazz->lastMember(); ++it) { if (const Function *func = (*it)->type()->asFunctionType()) { - if (!func->isVirtual()) + // Filter virtual destructors + if (func->name()->asDestructorNameId()) continue; - // Filter virtual destructors - if (printer.prettyName(func->name()).startsWith(QLatin1Char('~'))) + if (!func->isVirtual()) continue; // Filter OQbject's @@ -4917,16 +4915,12 @@ public: switch (spec) { case InsertionPointLocator::Private: return InsertionPointLocator::PrivateSlot; - break; case InsertionPointLocator::Protected: return InsertionPointLocator::ProtectedSlot; - break; case InsertionPointLocator::Public: return InsertionPointLocator::PublicSlot; - break; default: return spec; - break; } } return spec; diff --git a/src/plugins/cppeditor/cppvirtualfunctionassistprovider.cpp b/src/plugins/cppeditor/cppvirtualfunctionassistprovider.cpp index 132f999397..c014aee126 100644 --- a/src/plugins/cppeditor/cppvirtualfunctionassistprovider.cpp +++ b/src/plugins/cppeditor/cppvirtualfunctionassistprovider.cpp @@ -30,8 +30,8 @@ #include "cppvirtualfunctionassistprovider.h" +#include "cppeditor.h" #include "cppeditorconstants.h" -#include "cppelementevaluator.h" #include "cppvirtualfunctionproposalitem.h" #include <cplusplus/Icons.h> @@ -40,7 +40,9 @@ #include <coreplugin/actionmanager/actionmanager.h> #include <coreplugin/actionmanager/command.h> +#include <cpptools/functionutils.h> #include <cpptools/symbolfinder.h> +#include <cpptools/typehierarchybuilder.h> #include <texteditor/codeassist/basicproposalitemlistmodel.h> #include <texteditor/codeassist/genericproposal.h> @@ -48,11 +50,13 @@ #include <texteditor/codeassist/iassistinterface.h> #include <texteditor/codeassist/iassistprocessor.h> #include <texteditor/codeassist/iassistproposal.h> +#include <texteditor/texteditorconstants.h> #include <utils/qtcassert.h> using namespace CPlusPlus; using namespace CppEditor::Internal; +using namespace CppTools; using namespace TextEditor; /// Activate current item with the same shortcut that is configured for Follow Symbol Under Cursor. @@ -141,7 +145,7 @@ public: if (!functionsClass) return 0; - const QList<Symbol *> overrides = FunctionHelper::overrides( + const QList<Symbol *> overrides = FunctionUtils::overrides( m_params.function, functionsClass, m_params.staticClass, m_params.snapshot); if (overrides.isEmpty()) return 0; @@ -206,219 +210,3 @@ IAssistProcessor *VirtualFunctionAssistProvider::createProcessor() const { return new VirtualFunctionsAssistProcessor(m_params); } - -enum VirtualType { Virtual, PureVirtual }; - -static bool isVirtualFunction_helper(const Function *function, - const Snapshot &snapshot, - VirtualType virtualType) -{ - if (!function) - return false; - - if (virtualType == PureVirtual) - return function->isPureVirtual(); - - if (function->isVirtual()) - return true; - - const QString filePath = QString::fromUtf8(function->fileName(), function->fileNameLength()); - if (Document::Ptr document = snapshot.document(filePath)) { - LookupContext context(document, snapshot); - QList<LookupItem> results = context.lookup(function->name(), function->enclosingScope()); - if (!results.isEmpty()) { - const bool isDestructor = function->name()->isDestructorNameId(); - foreach (const LookupItem &item, results) { - if (Symbol *symbol = item.declaration()) { - if (Function *functionType = symbol->type()->asFunctionType()) { - if (functionType->name()->isDestructorNameId() != isDestructor) - continue; - if (functionType == function) // already tested - continue; - if (functionType->isFinal()) - return false; - if (functionType->isVirtual()) - return true; - } - } - } - } - } - - return false; -} - -bool FunctionHelper::isVirtualFunction(const Function *function, const Snapshot &snapshot) -{ - return isVirtualFunction_helper(function, snapshot, Virtual); -} - -bool FunctionHelper::isPureVirtualFunction(const Function *function, const Snapshot &snapshot) -{ - return isVirtualFunction_helper(function, snapshot, PureVirtual); -} - -QList<Symbol *> FunctionHelper::overrides(Function *function, Class *functionsClass, - Class *staticClass, const Snapshot &snapshot) -{ - QList<Symbol *> result; - QTC_ASSERT(function && functionsClass && staticClass, return result); - - FullySpecifiedType referenceType = function->type(); - const Name *referenceName = function->name(); - QTC_ASSERT(referenceName && referenceType.isValid(), return result); - - // Find overrides - CppEditor::Internal::CppClass cppClass = CppClass(functionsClass); - cppClass.lookupDerived(staticClass, snapshot); - - QList<CppClass> l; - l << cppClass; - - while (!l.isEmpty()) { - // Add derived - CppClass clazz = l.takeFirst(); - - QTC_ASSERT(clazz.declaration, continue); - Class *c = clazz.declaration->asClass(); - QTC_ASSERT(c, continue); - - foreach (const CppClass &d, clazz.derived) { - if (!l.contains(d)) - l << d; - } - - // Check member functions - for (int i = 0, total = c->memberCount(); i < total; ++i) { - Symbol *candidate = c->memberAt(i); - const Name *candidateName = candidate->name(); - const FullySpecifiedType candidateType = candidate->type(); - if (!candidateName || !candidateType.isValid()) - continue; - if (candidateName->isEqualTo(referenceName) && candidateType.isEqualTo(referenceType)) - result << candidate; - } - } - - return result; -} - -#ifdef WITH_TESTS -#include "cppeditorplugin.h" - -#include <QList> -#include <QTest> - -namespace CppEditor { -namespace Internal { - -enum Virtuality -{ - NotVirtual, - Virtual, - PureVirtual -}; -typedef QList<Virtuality> VirtualityList; -} // Internal namespace -} // CppEditor namespace - -Q_DECLARE_METATYPE(CppEditor::Internal::Virtuality) -Q_DECLARE_METATYPE(CppEditor::Internal::VirtualityList) - -namespace CppEditor { -namespace Internal { - -void CppEditorPlugin::test_functionhelper_virtualFunctions() -{ - // Create and parse document - QFETCH(QByteArray, source); - QFETCH(VirtualityList, virtualityList); - Document::Ptr document = Document::create(QLatin1String("virtuals")); - document->setUtf8Source(source); - document->check(); // calls parse(); - QCOMPARE(document->diagnosticMessages().size(), 0); - QVERIFY(document->translationUnit()->ast()); - - // Iterate through Function symbols - Snapshot snapshot; - snapshot.insert(document); - Control *control = document->translationUnit()->control(); - Symbol **end = control->lastSymbol(); - for (Symbol **it = control->firstSymbol(); it != end; ++it) { - const CPlusPlus::Symbol *symbol = *it; - if (const Function *function = symbol->asFunction()) { - QTC_ASSERT(!virtualityList.isEmpty(), return); - Virtuality virtuality = virtualityList.takeFirst(); - if (FunctionHelper::isVirtualFunction(function, snapshot)) { - if (FunctionHelper::isPureVirtualFunction(function, snapshot)) - QCOMPARE(virtuality, PureVirtual); - else - QCOMPARE(virtuality, Virtual); - } else { - QCOMPARE(virtuality, NotVirtual); - } - } - } - QVERIFY(virtualityList.isEmpty()); -} - -void CppEditorPlugin::test_functionhelper_virtualFunctions_data() -{ - typedef QByteArray _; - QTest::addColumn<QByteArray>("source"); - QTest::addColumn<VirtualityList>("virtualityList"); - - QTest::newRow("none") - << _("struct None { void foo() {} };\n") - << (VirtualityList() << NotVirtual); - - QTest::newRow("single-virtual") - << _("struct V { virtual void foo() {} };\n") - << (VirtualityList() << Virtual); - - QTest::newRow("single-pure-virtual") - << _("struct PV { virtual void foo() = 0; };\n") - << (VirtualityList() << PureVirtual); - - QTest::newRow("virtual-derived-with-specifier") - << _("struct Base { virtual void foo() {} };\n" - "struct Derived : Base { virtual void foo() {} };\n") - << (VirtualityList() << Virtual << Virtual); - - QTest::newRow("virtual-derived-implicit") - << _("struct Base { virtual void foo() {} };\n" - "struct Derived : Base { void foo() {} };\n") - << (VirtualityList() << Virtual << Virtual); - - QTest::newRow("not-virtual-then-virtual") - << _("struct Base { void foo() {} };\n" - "struct Derived : Base { virtual void foo() {} };\n") - << (VirtualityList() << NotVirtual << Virtual); - - QTest::newRow("virtual-final-not-virtual") - << _("struct Base { virtual void foo() {} };\n" - "struct Derived : Base { void foo() final {} };\n" - "struct Derived2 : Derived { void foo() {} };") - << (VirtualityList() << Virtual << Virtual << NotVirtual); - - QTest::newRow("virtual-then-pure") - << _("struct Base { virtual void foo() {} };\n" - "struct Derived : Base { virtual void foo() = 0; };\n" - "struct Derived2 : Derived { void foo() {} };") - << (VirtualityList() << Virtual << PureVirtual << Virtual); - - QTest::newRow("virtual-virtual-final-not-virtual") - << _("struct Base { virtual void foo() {} };\n" - "struct Derived : Base { virtual void foo() final {} };\n" - "struct Derived2 : Derived { void foo() {} };") - << (VirtualityList() << Virtual << Virtual << NotVirtual); - - QTest::newRow("ctor-virtual-dtor") - << _("struct Base { Base() {} virtual ~Base() {} };\n") - << (VirtualityList() << NotVirtual << Virtual); -} - -} // namespace Internal -} // namespace CppEditor - -#endif diff --git a/src/plugins/cppeditor/cppvirtualfunctionassistprovider.h b/src/plugins/cppeditor/cppvirtualfunctionassistprovider.h index f0a3a6c013..45d6245ca7 100644 --- a/src/plugins/cppeditor/cppvirtualfunctionassistprovider.h +++ b/src/plugins/cppeditor/cppvirtualfunctionassistprovider.h @@ -70,21 +70,6 @@ private: Parameters m_params; }; -class FunctionHelper -{ -public: - static bool isVirtualFunction(const CPlusPlus::Function *function, - const CPlusPlus::Snapshot &snapshot); - - static bool isPureVirtualFunction(const CPlusPlus::Function *function, - const CPlusPlus::Snapshot &snapshot); - - static QList<CPlusPlus::Symbol *> overrides(CPlusPlus::Function *function, - CPlusPlus::Class *functionsClass, - CPlusPlus::Class *staticClass, - const CPlusPlus::Snapshot &snapshot); -}; - } // namespace Internal } // namespace CppEditor diff --git a/src/plugins/cppeditor/fileandtokenactions_test.cpp b/src/plugins/cppeditor/fileandtokenactions_test.cpp index 51c56fef06..156a4d0676 100644 --- a/src/plugins/cppeditor/fileandtokenactions_test.cpp +++ b/src/plugins/cppeditor/fileandtokenactions_test.cpp @@ -27,26 +27,28 @@ ** ****************************************************************************/ -#include <cplusplus/CppDocument.h> -#include <cplusplus/TranslationUnit.h> +#include "cppeditor.h" +#include "cppeditorplugin.h" +#include "cppeditortestcase.h" +#include "cppquickfix.h" +#include "cppquickfix_test_utils.h" +#include "cppquickfixassistant.h" +#include "cppquickfixes.h" #include <coreplugin/editormanager/editormanager.h> -#include <cppeditor/cppeditor.h> -#include <cppeditor/cppeditorplugin.h> -#include <cppeditor/cppquickfixassistant.h> -#include <cppeditor/cppquickfixes.h> -#include <cppeditor/cppquickfix.h> -#include <cppeditor/cppquickfix_test_utils.h> #include <cpptools/cppmodelmanagerinterface.h> #include <cpptools/cpptoolsplugin.h> #include <extensionsystem/pluginmanager.h> -#include <projectexplorer/projectexplorer.h> #include <projectexplorer/project.h> +#include <projectexplorer/projectexplorer.h> #include <texteditor/basetextdocument.h> +#include <cplusplus/CppDocument.h> +#include <cplusplus/TranslationUnit.h> + #include <QDebug> -#include <QtAlgorithms> #include <QTextDocument> +#include <QtAlgorithms> #include <QtTest> #if QT_VERSION >= 0x050000 @@ -81,7 +83,7 @@ using namespace TextEditor; namespace { -class TestActionsTestCase +class TestActionsTestCase : public CppEditor::Internal::Tests::TestCase { public: class AbstractAction @@ -97,8 +99,8 @@ public: public: /// Run the given fileActions for each file and the given tokenActions for each token. /// The cursor is positioned on the very first character of each token. - void run(const Actions &tokenActions = Actions(), - const Actions &fileActions = Actions()); + TestActionsTestCase(const Actions &tokenActions = Actions(), + const Actions &fileActions = Actions()); /// Simulate pressing ESC, which will close popups, search results pane, etc... /// This works only if the Qt Creator window is active. @@ -132,14 +134,21 @@ bool TestActionsTestCase::allProjectsConfigured = false; typedef TestActionsTestCase::Actions Actions; typedef TestActionsTestCase::ActionPointer ActionPointer; -void TestActionsTestCase::run(const Actions &tokenActions, const Actions &fileActions) +Actions singleAction(const ActionPointer &action) +{ + return Actions() << action; +} + +TestActionsTestCase::TestActionsTestCase(const Actions &tokenActions, const Actions &fileActions) + : CppEditor::Internal::Tests::TestCase(/*runGarbageCollector=*/false) { - CppModelManagerInterface *mm = CppModelManagerInterface::instance(); + QVERIFY(succeededSoFar()); // Collect files to process QStringList filesToOpen; QList<QPointer<ProjectExplorer::Project> > projects; - const QList<CppModelManagerInterface::ProjectInfo> projectInfos = mm->projectInfos(); + const QList<CppModelManagerInterface::ProjectInfo> projectInfos + = m_modelManager->projectInfos(); if (projectInfos.isEmpty()) MSKIP_SINGLE("No project(s) loaded. Test operates only on loaded projects."); @@ -175,18 +184,16 @@ void TestActionsTestCase::run(const Actions &tokenActions, const Actions &fileAc // Open editor QCOMPARE(EditorManager::documentModel()->openedDocuments().size(), 0); - CPPEditor *editor = dynamic_cast<CPPEditor *>(EditorManager::openEditor(filePath)); - QVERIFY(editor); + CPPEditor *editor; + CPPEditorWidget *editorWidget; + QVERIFY(openCppEditor(filePath, &editor, &editorWidget)); + QCOMPARE(EditorManager::documentModel()->openedDocuments().size(), 1); - QVERIFY(mm->isCppEditor(editor)); - QVERIFY(mm->workingCopy().contains(filePath)); + QVERIFY(m_modelManager->isCppEditor(editor)); + QVERIFY(m_modelManager->workingCopy().contains(filePath)); // Rehighlight - CPPEditorWidget *editorWidget = dynamic_cast<CPPEditorWidget *>(editor->editorWidget()); - QVERIFY(editorWidget); - editorWidget->semanticRehighlight(true); - while (editorWidget->semanticInfo().doc.isNull()) - QApplication::processEvents(); + waitForRehighlightedSemanticDocument(editorWidget); // Run all file actions executeActionsOnEditorWidget(editorWidget, fileActions); @@ -194,7 +201,7 @@ void TestActionsTestCase::run(const Actions &tokenActions, const Actions &fileAc if (tokenActions.empty()) continue; - Snapshot snapshot = mm->snapshot(); + const Snapshot snapshot = globalSnapshot(); Document::Ptr document = snapshot.preprocessedDocument( editorWidget->document()->toPlainText().toUtf8(), filePath); QVERIFY(document); @@ -436,8 +443,7 @@ void InvokeCompletionTokenAction::run(CPPEditorWidget *editorWidget) // editorWidget->setFocus(); QApplication::processEvents(); - BaseTextDocument *doc = qobject_cast<BaseTextDocument *>(editorWidget->editorDocument()); - TestActionsTestCase::undoChangesInDocument(doc); + TestActionsTestCase::undoChangesInDocument(editorWidget->baseTextDocument()); } class RunAllQuickFixesTokenAction : public TestActionsTestCase::AbstractAction @@ -511,88 +517,51 @@ void SwitchHeaderSourceFileAction::run(CPPEditorWidget *) void CppEditorPlugin::test_openEachFile() { - TestActionsTestCase test; - test.run(); + TestActionsTestCase(); } void CppEditorPlugin::test_switchHeaderSourceOnEachFile() { - Actions fileActions; - fileActions << ActionPointer(new SwitchHeaderSourceFileAction); - - TestActionsTestCase test; - test.run(Actions(), fileActions); + TestActionsTestCase(Actions(), singleAction(ActionPointer(new SwitchHeaderSourceFileAction))); } void CppEditorPlugin::test_moveTokenWiseThroughEveryFile() { - Actions tokenActions; - tokenActions << ActionPointer(new NoOpTokenAction()); - - TestActionsTestCase test; - test.run(tokenActions); + TestActionsTestCase(singleAction(ActionPointer(new NoOpTokenAction))); } /// May block if file does not exists (e.g. a not generated ui_* file). void CppEditorPlugin::test_moveTokenWiseThroughEveryFileAndFollowSymbol() { - Actions tokenActions; - tokenActions << ActionPointer(new FollowSymbolUnderCursorTokenAction()); - - TestActionsTestCase test; - test.run(tokenActions); + TestActionsTestCase(singleAction(ActionPointer(new FollowSymbolUnderCursorTokenAction))); } void CppEditorPlugin::test_moveTokenWiseThroughEveryFileAndSwitchDeclarationDefinition() { - Actions tokenActions; - tokenActions << ActionPointer(new SwitchDeclarationDefinitionTokenAction()); - - TestActionsTestCase test; - test.run(tokenActions); + TestActionsTestCase(singleAction(ActionPointer(new SwitchDeclarationDefinitionTokenAction))); } void CppEditorPlugin::test_moveTokenWiseThroughEveryFileAndFindUsages() { - Actions tokenActions; - tokenActions << ActionPointer(new FindUsagesTokenAction()); - - TestActionsTestCase test; - test.run(tokenActions); + TestActionsTestCase(singleAction(ActionPointer(new FindUsagesTokenAction))); } void CppEditorPlugin::test_moveTokenWiseThroughEveryFileAndRenameUsages() { - Actions tokenActions; - tokenActions << ActionPointer(new RenameSymbolUnderCursorTokenAction()); - - TestActionsTestCase test; - test.run(tokenActions); + TestActionsTestCase(singleAction(ActionPointer(new RenameSymbolUnderCursorTokenAction))); } void CppEditorPlugin::test_moveTokenWiseThroughEveryFileAndOpenTypeHierarchy() { - Actions tokenActions; - tokenActions << ActionPointer(new OpenTypeHierarchyTokenAction()); - - TestActionsTestCase test; - test.run(tokenActions); + TestActionsTestCase(singleAction(ActionPointer(new OpenTypeHierarchyTokenAction))); } void CppEditorPlugin::test_moveTokenWiseThroughEveryFileAndInvokeCompletion() { - Actions tokenActions; - tokenActions << ActionPointer(new InvokeCompletionTokenAction()); - - TestActionsTestCase test; - test.run(tokenActions); + TestActionsTestCase(singleAction(ActionPointer(new InvokeCompletionTokenAction))); } void CppEditorPlugin::test_moveTokenWiseThroughEveryFileAndTriggerQuickFixes() { - Actions tokenActions; - tokenActions << ActionPointer(new RunAllQuickFixesTokenAction()); - - TestActionsTestCase test; - test.run(tokenActions); + TestActionsTestCase(singleAction(ActionPointer(new RunAllQuickFixesTokenAction))); } diff --git a/src/plugins/cppeditor/followsymbol_switchmethoddecldef_test.cpp b/src/plugins/cppeditor/followsymbol_switchmethoddecldef_test.cpp index e41974f8ad..c9e763cb3a 100644 --- a/src/plugins/cppeditor/followsymbol_switchmethoddecldef_test.cpp +++ b/src/plugins/cppeditor/followsymbol_switchmethoddecldef_test.cpp @@ -29,20 +29,21 @@ #include "cppeditor.h" #include "cppeditorplugin.h" +#include "cppeditortestcase.h" #include "cppelementevaluator.h" #include "cppvirtualfunctionassistprovider.h" #include "cppvirtualfunctionproposalitem.h" -#include <texteditor/codeassist/iassistproposal.h> -#include <texteditor/codeassist/iassistprocessor.h> #include <texteditor/codeassist/basicproposalitemlistmodel.h> +#include <texteditor/codeassist/iassistprocessor.h> +#include <texteditor/codeassist/iassistproposal.h> + #include <utils/fileutils.h> #include <QDebug> #include <QDir> #include <QtTest> - /*! Tests for Follow Symbol Under Cursor and Switch Between Function Declaration/Definition @@ -53,6 +54,7 @@ You can find potential test code for Follow Symbol Under Cursor on the bottom of this file. */ + using namespace CPlusPlus; using namespace CppEditor; using namespace CppEditor::Internal; @@ -168,74 +170,53 @@ typedef QSharedPointer<TestDocument> TestDocumentPtr; * - a '@' character denotes the initial text cursor position * - a '$' character denotes the target text cursor position */ -class TestDocument +class TestDocument : public CppEditor::Internal::Tests::TestDocument { public: - TestDocument(const QByteArray &theSource, const QString &fileName) - : source(theSource) - , fileName(fileName) - , initialCursorPosition(source.indexOf('@')) - , targetCursorPosition(source.indexOf('$')) - , editor(0) - , editorWidget(0) + TestDocument(const QByteArray &source, const QByteArray &fileName) + : CppEditor::Internal::Tests::TestDocument(fileName, source) + , m_targetCursorPosition(source.indexOf('$')) { - if (initialCursorPosition != -1 || targetCursorPosition != -1) - QVERIFY(initialCursorPosition != targetCursorPosition); - - if (initialCursorPosition > targetCursorPosition) { - source.remove(initialCursorPosition, 1); - if (targetCursorPosition != -1) { - source.remove(targetCursorPosition, 1); - --initialCursorPosition; + if (m_cursorPosition != -1 || m_targetCursorPosition != -1) + QVERIFY(m_cursorPosition != m_targetCursorPosition); + + if (m_cursorPosition > m_targetCursorPosition) { + m_source.remove(m_cursorPosition, 1); + if (m_targetCursorPosition != -1) { + m_source.remove(m_targetCursorPosition, 1); + --m_cursorPosition; } } else { - source.remove(targetCursorPosition, 1); - if (initialCursorPosition != -1) { - source.remove(initialCursorPosition, 1); - --targetCursorPosition; + m_source.remove(m_targetCursorPosition, 1); + if (m_cursorPosition != -1) { + m_source.remove(m_cursorPosition, 1); + --m_targetCursorPosition; } } } - static TestDocumentPtr create(const QByteArray &theOriginalSource, const QString &fileName) - { - return TestDocumentPtr(new TestDocument(theOriginalSource, fileName)); - } - - bool hasInitialCursorMarker() const { return initialCursorPosition != -1; } - bool hasTargetCursorMarker() const { return targetCursorPosition != -1; } - - QString filePath() const - { - if (directoryPath.isEmpty()) - qDebug() << "directoryPath not set!"; - return directoryPath + QLatin1Char('/') + fileName; - } - - void writeToDisk() const + static TestDocumentPtr create(const QByteArray &source, const QByteArray &fileName) { - Utils::FileSaver srcSaver(filePath()); - srcSaver.write(source); - srcSaver.finalize(); + return TestDocumentPtr(new TestDocument(source, fileName)); } - QByteArray source; + bool hasTargetCursorMarker() const { return m_targetCursorPosition != -1; } - const QString fileName; - QString directoryPath; - int initialCursorPosition; - int targetCursorPosition; - - CPPEditor *editor; - CPPEditorWidget *editorWidget; +public: + int m_targetCursorPosition; }; +QList<TestDocumentPtr> singleDocument(const QByteArray &source) +{ + return QList<TestDocumentPtr>() << TestDocument::create(source, "file.cpp"); +} + /** * Encapsulates the whole process of setting up several editors, positioning the cursor, * executing Follow Symbol Under Cursor or Switch Between Function Declaration/Definition * and checking the result. */ -class TestCase +class F2TestCase : public CppEditor::Internal::Tests::TestCase { public: enum CppEditorAction { @@ -243,163 +224,66 @@ public: SwitchBetweenMethodDeclarationDefinitionAction }; - TestCase(CppEditorAction action, const QByteArray &source, - const OverrideItemList &expectedVirtualFunctionProposal = OverrideItemList()); - TestCase(CppEditorAction action, const QList<TestDocumentPtr> theTestFiles, - const OverrideItemList &expectedVirtualFunctionProposal = OverrideItemList()); - ~TestCase(); - - void run(); + F2TestCase(CppEditorAction action, + const QList<TestDocumentPtr> testFiles, + const OverrideItemList &expectedVirtualFunctionProposal = OverrideItemList()); private: - TestCase(const TestCase &); - TestCase &operator=(const TestCase &); - - void init(); - - TestDocumentPtr testFileWithInitialCursorMarker(); - TestDocumentPtr testFileWithTargetCursorMarker(); - -private: - CppEditorAction m_action; - QList<TestDocumentPtr> m_testFiles; - OverrideItemList m_expectedVirtualFunctionProposal; + static TestDocumentPtr testFileWithInitialCursorMarker(const QList<TestDocumentPtr> &testFiles); + static TestDocumentPtr testFileWithTargetCursorMarker(const QList<TestDocumentPtr> &testFiles); }; -/// Convenience function for creating a TestDocument. -/// See TestDocument. -TestCase::TestCase(CppEditorAction action, const QByteArray &source, - const OverrideItemList &expectedVirtualFunctionProposal) - : m_action(action) - , m_expectedVirtualFunctionProposal(expectedVirtualFunctionProposal) -{ - m_testFiles << TestDocument::create(source, QLatin1String("file.cpp")); - init(); -} - /// Creates a test case with multiple test files. /// Exactly one test document must be provided that contains '@', the initial position marker. /// Exactly one test document must be provided that contains '$', the target position marker. /// It can be the same document. -TestCase::TestCase(CppEditorAction action, const QList<TestDocumentPtr> theTestFiles, - const OverrideItemList &expectedVirtualFunctionProposal) - : m_action(action) - , m_testFiles(theTestFiles) - , m_expectedVirtualFunctionProposal(expectedVirtualFunctionProposal) +F2TestCase::F2TestCase(CppEditorAction action, + const QList<TestDocumentPtr> testFiles, + const OverrideItemList &expectedVirtualFunctionProposal) { - init(); -} + QVERIFY(succeededSoFar()); -void TestCase::init() -{ // Check if there are initial and target position markers - QVERIFY2(testFileWithInitialCursorMarker(), + TestDocumentPtr initialTestFile = testFileWithInitialCursorMarker(testFiles); + QVERIFY2(initialTestFile, "No test file with initial cursor marker is provided."); - QVERIFY2(testFileWithTargetCursorMarker(), + TestDocumentPtr targetTestFile = testFileWithTargetCursorMarker(testFiles); + QVERIFY2(targetTestFile, "No test file with target cursor marker is provided."); // Write files to disk - const QString directoryPath = QDir::tempPath(); - foreach (TestDocumentPtr testFile, m_testFiles) { - testFile->directoryPath = directoryPath; - testFile->writeToDisk(); - } + foreach (TestDocumentPtr testFile, testFiles) + QVERIFY(testFile->writeToDisk()); // Update Code Model QStringList filePaths; - foreach (const TestDocumentPtr &testFile, m_testFiles) + foreach (const TestDocumentPtr &testFile, testFiles) filePaths << testFile->filePath(); - CppTools::CppModelManagerInterface::instance()->updateSourceFiles(filePaths); - - // Wait for the indexer to process all files. - // All these files are "Fast Checked", that is the function bodies are not processed. - QStringList filePathsNotYetInSnapshot(filePaths); - forever { - Snapshot snapshot = CppTools::CppModelManagerInterface::instance()->snapshot(); - foreach (const QString &filePath, filePathsNotYetInSnapshot) { - if (snapshot.contains(filePath)) - filePathsNotYetInSnapshot.removeOne(filePath); - } - if (filePathsNotYetInSnapshot.isEmpty()) - break; - QCoreApplication::processEvents(); - } + QVERIFY(parseFiles(filePaths)); // Open Files - foreach (TestDocumentPtr testFile, m_testFiles) { - testFile->editor - = dynamic_cast<CPPEditor *>(EditorManager::openEditor(testFile->filePath())); - QVERIFY(testFile->editor); - - testFile->editorWidget = dynamic_cast<CPPEditorWidget *>(testFile->editor->editorWidget()); - QVERIFY(testFile->editorWidget); + foreach (TestDocumentPtr testFile, testFiles) { + QVERIFY(openCppEditor(testFile->filePath(), &testFile->m_editor, + &testFile->m_editorWidget)); + closeEditorAtEndOfTestCase(testFile->m_editor); // Wait until the indexer processed the just opened file. // The file is "Full Checked" since it is in the working copy now, // that is the function bodies are processed. forever { - Snapshot snapshot = CppTools::CppModelManagerInterface::instance()->snapshot(); - if (Document::Ptr document = snapshot.document(testFile->filePath())) { - if (document->checkMode() == Document::FullCheck) - break; - QCoreApplication::processEvents(); - } + const Document::Ptr document = waitForFileInGlobalSnapshot(testFile->filePath()); + if (document->checkMode() == Document::FullCheck) + break; } // Rehighlight - testFile->editorWidget->semanticRehighlight(true); - // Wait for the semantic info from the future - while (testFile->editorWidget->semanticInfo().doc.isNull()) - QCoreApplication::processEvents(); - } -} - -TestCase::~TestCase() -{ - // Close editors - QList<Core::IEditor *> editorsToClose; - foreach (const TestDocumentPtr testFile, m_testFiles) { - if (testFile->editor) - editorsToClose << testFile->editor; - } - EditorManager::closeEditors(editorsToClose, false); - QCoreApplication::processEvents(); // process any pending events - - // Remove the test files from the code-model - CppModelManagerInterface *mmi = CppTools::CppModelManagerInterface::instance(); - mmi->GC(); - QCOMPARE(mmi->snapshot().size(), 0); -} - -TestDocumentPtr TestCase::testFileWithInitialCursorMarker() -{ - foreach (const TestDocumentPtr testFile, m_testFiles) { - if (testFile->hasInitialCursorMarker()) - return testFile; - } - return TestDocumentPtr(); -} - -TestDocumentPtr TestCase::testFileWithTargetCursorMarker() -{ - foreach (const TestDocumentPtr testFile, m_testFiles) { - if (testFile->hasTargetCursorMarker()) - return testFile; + waitForRehighlightedSemanticDocument(testFile->m_editorWidget); } - return TestDocumentPtr(); -} - -void TestCase::run() -{ - TestDocumentPtr initialTestFile = testFileWithInitialCursorMarker(); - QVERIFY(initialTestFile); - TestDocumentPtr targetTestFile = testFileWithTargetCursorMarker(); - QVERIFY(targetTestFile); // Activate editor of initial test file - EditorManager::activateEditor(initialTestFile->editor); + EditorManager::activateEditor(initialTestFile->m_editor); - initialTestFile->editor->setCursorPosition(initialTestFile->initialCursorPosition); + initialTestFile->m_editor->setCursorPosition(initialTestFile->m_cursorPosition); // qDebug() << "Initial line:" << initialTestFile->editor->currentLine(); // qDebug() << "Initial column:" << initialTestFile->editor->currentColumn() - 1; @@ -407,9 +291,9 @@ void TestCase::run() OverrideItemList finalVirtualSymbolResults; // Trigger the action - switch (m_action) { + switch (action) { case FollowSymbolUnderCursorAction: { - CPPEditorWidget *widget = initialTestFile->editorWidget; + CPPEditorWidget *widget = initialTestFile->m_editorWidget; FollowSymbolUnderCursor *delegate = widget->followSymbolUnderCursorDelegate(); VirtualFunctionAssistProvider *original = delegate->virtualFunctionAssistProvider(); @@ -417,7 +301,7 @@ void TestCase::run() QScopedPointer<VirtualFunctionTestAssistProvider> testProvider( new VirtualFunctionTestAssistProvider(widget)); delegate->setVirtualFunctionAssistProvider(testProvider.data()); - initialTestFile->editorWidget->openLinkUnderCursor(); + initialTestFile->m_editorWidget->openLinkUnderCursor(); immediateVirtualSymbolResults = testProvider->m_immediateItems; finalVirtualSymbolResults = testProvider->m_finalItems; @@ -442,7 +326,7 @@ void TestCase::run() QCOMPARE(currentTextEditor->document()->filePath(), targetTestFile->filePath()); int expectedLine, expectedColumn; - currentTextEditor->convertPosition(targetTestFile->targetCursorPosition, + currentTextEditor->convertPosition(targetTestFile->m_targetCursorPosition, &expectedLine, &expectedColumn); // qDebug() << "Expected line:" << expectedLine; // qDebug() << "Expected column:" << expectedColumn; @@ -454,13 +338,30 @@ void TestCase::run() // qDebug() << immediateVirtualSymbolResults; // qDebug() << finalVirtualSymbolResults; OverrideItemList expectedImmediate; - if (!m_expectedVirtualFunctionProposal.isEmpty()) { - expectedImmediate << m_expectedVirtualFunctionProposal.first(); + if (!expectedVirtualFunctionProposal.isEmpty()) { + expectedImmediate << expectedVirtualFunctionProposal.first(); expectedImmediate << OverrideItem(QLatin1String("...searching overrides")); } QCOMPARE(immediateVirtualSymbolResults, expectedImmediate); - QEXPECT_FAIL("differentReturnTypes", "Doesn't work", Abort); - QCOMPARE(finalVirtualSymbolResults, m_expectedVirtualFunctionProposal); + QCOMPARE(finalVirtualSymbolResults, expectedVirtualFunctionProposal); +} + +TestDocumentPtr F2TestCase::testFileWithInitialCursorMarker(const QList<TestDocumentPtr> &testFiles) +{ + foreach (const TestDocumentPtr testFile, testFiles) { + if (testFile->hasCursorMarker()) + return testFile; + } + return TestDocumentPtr(); +} + +TestDocumentPtr F2TestCase::testFileWithTargetCursorMarker(const QList<TestDocumentPtr> &testFiles) +{ + foreach (const TestDocumentPtr testFile, testFiles) { + if (testFile->hasTargetCursorMarker()) + return testFile; + } + return TestDocumentPtr(); } } // anonymous namespace @@ -558,12 +459,11 @@ void CppEditorPlugin::test_SwitchMethodDeclarationDefinition() QFETCH(QByteArray, header); QFETCH(QByteArray, source); - QList<TestDocumentPtr> testFiles; - testFiles << TestDocument::create(header, QLatin1String("file.h")); - testFiles << TestDocument::create(source, QLatin1String("file.cpp")); + const QList<TestDocumentPtr> testFiles = QList<TestDocumentPtr>() + << TestDocument::create(header, "file.h") + << TestDocument::create(source, "file.cpp"); - TestCase test(TestCase::SwitchBetweenMethodDeclarationDefinitionAction, testFiles); - test.run(); + F2TestCase(F2TestCase::SwitchBetweenMethodDeclarationDefinitionAction, testFiles); } void CppEditorPlugin::test_FollowSymbolUnderCursor_data() @@ -834,8 +734,8 @@ void CppEditorPlugin::test_FollowSymbolUnderCursor_data() ); // 3.3.10 Name hiding (par 2.), from text - // A class name (9.1) or enumeration name (7.2) can be hidden by the name of a variable, data member, - // function, or enumerator declared in the same scope. + // A class name (9.1) or enumeration name (7.2) can be hidden by the name of a variable, + // data member, function, or enumerator declared in the same scope. QTest::newRow("funLocalVarHidesOuterClass") << _( "struct C {};\n" "\n" @@ -923,8 +823,7 @@ void CppEditorPlugin::test_FollowSymbolUnderCursor_data() void CppEditorPlugin::test_FollowSymbolUnderCursor() { QFETCH(QByteArray, source); - TestCase test(TestCase::FollowSymbolUnderCursorAction, source); - test.run(); + F2TestCase(F2TestCase::FollowSymbolUnderCursorAction, singleDocument(source)); } void CppEditorPlugin::test_FollowSymbolUnderCursor_multipleDocuments_data() @@ -933,27 +832,25 @@ void CppEditorPlugin::test_FollowSymbolUnderCursor_multipleDocuments_data() QTest::newRow("skipForwardDeclarationBasic") << (QList<TestDocumentPtr>() << TestDocument::create("class $Foo {};\n", - QLatin1String("defined.h")) + "defined.h") << TestDocument::create("class Foo;\n" "@Foo foo;\n", - QLatin1String("forwardDeclaredAndUsed.h")) + "forwardDeclaredAndUsed.h") ); QTest::newRow("skipForwardDeclarationTemplates") << (QList<TestDocumentPtr>() << TestDocument::create("template <class E> class $Container {};\n", - QLatin1String("defined.h")) + "defined.h") << TestDocument::create("template <class E> class Container;\n" "@Container<int> container;\n", - QLatin1String("forwardDeclaredAndUsed.h")) + "forwardDeclaredAndUsed.h") ); } void CppEditorPlugin::test_FollowSymbolUnderCursor_multipleDocuments() { QFETCH(QList<TestDocumentPtr>, documents); - - TestCase test(TestCase::FollowSymbolUnderCursorAction, documents); - test.run(); + F2TestCase(F2TestCase::FollowSymbolUnderCursorAction, documents); } void CppEditorPlugin::test_FollowSymbolUnderCursor_QObject_connect_data() @@ -1042,8 +939,7 @@ void CppEditorPlugin::test_FollowSymbolUnderCursor_QObject_connect() return; } - TestCase test(TestCase::FollowSymbolUnderCursorAction, source); - test.run(); + F2TestCase(F2TestCase::FollowSymbolUnderCursorAction, singleDocument(source)); } void CppEditorPlugin::test_FollowSymbolUnderCursor_classOperator_onOperatorToken_data() @@ -1066,8 +962,7 @@ void CppEditorPlugin::test_FollowSymbolUnderCursor_classOperator_onOperatorToken "}\n"; if (toDeclaration) source.replace('@', '#').replace('$', '@').replace('#', '$'); - TestCase test(TestCase::FollowSymbolUnderCursorAction, source); - test.run(); + F2TestCase(F2TestCase::FollowSymbolUnderCursorAction, singleDocument(source)); } void CppEditorPlugin::test_FollowSymbolUnderCursor_classOperator_data() @@ -1092,8 +987,7 @@ void CppEditorPlugin::test_FollowSymbolUnderCursor_classOperator() else source.replace("@2", QByteArray()).replace("$2", QByteArray()) .replace("@1", "@").replace("$1", "$"); - TestCase test(TestCase::FollowSymbolUnderCursorAction, source); - test.run(); + F2TestCase(F2TestCase::FollowSymbolUnderCursorAction, singleDocument(source)); } void CppEditorPlugin::test_FollowSymbolUnderCursor_classOperator_inOp_data() @@ -1118,8 +1012,7 @@ void CppEditorPlugin::test_FollowSymbolUnderCursor_classOperator_inOp() else source.replace("@2", QByteArray()).replace("$2", QByteArray()) .replace("@1", "@").replace("$1", "$"); - TestCase test(TestCase::FollowSymbolUnderCursorAction, source); - test.run(); + F2TestCase(F2TestCase::FollowSymbolUnderCursorAction, singleDocument(source)); } void CppEditorPlugin::test_FollowSymbolUnderCursor_virtualFunctionCall_data() @@ -1178,7 +1071,8 @@ void CppEditorPlugin::test_FollowSymbolUnderCursor_virtualFunctionCall_data() << OverrideItem(QLatin1String("CD1::virt"), 11) << OverrideItem(QLatin1String("CD2::virt"), 14)); - /// Check: Virtual function call in member of class hierarchy, only possible overrides are presented. + /// Check: Virtual function call in member of class hierarchy, + /// only possible overrides are presented. QTest::newRow("possibleOverrides2") << _( "struct A { virtual void virt(); };\n" "void A::virt() {}\n" @@ -1400,8 +1294,7 @@ void CppEditorPlugin::test_FollowSymbolUnderCursor_virtualFunctionCall() QFETCH(QByteArray, source); QFETCH(OverrideItemList, results); - TestCase test(TestCase::FollowSymbolUnderCursorAction, source, results); - test.run(); + F2TestCase(F2TestCase::FollowSymbolUnderCursorAction, singleDocument(source), results); } /// Check: Base classes can be found although these might be defined in distinct documents. @@ -1409,21 +1302,20 @@ void CppEditorPlugin::test_FollowSymbolUnderCursor_virtualFunctionCall_multipleD { QList<TestDocumentPtr> testFiles = QList<TestDocumentPtr>() << TestDocument::create("struct A { virtual void virt(int) = 0; };\n", - QLatin1String("a.h")) + "a.h") << TestDocument::create("#include \"a.h\"\n" "struct B : A { void virt(int) {} };\n", - QLatin1String("b.h")) + "b.h") << TestDocument::create("#include \"a.h\"\n" "void f(A *o) { o->$@virt(42); }\n", - QLatin1String("u.cpp")) + "u.cpp") ; const OverrideItemList finalResults = OverrideItemList() << OverrideItem(QLatin1String("A::virt"), 1) << OverrideItem(QLatin1String("B::virt"), 2); - TestCase test(TestCase::FollowSymbolUnderCursorAction, testFiles, finalResults); - test.run(); + F2TestCase(F2TestCase::FollowSymbolUnderCursorAction, testFiles, finalResults); } /* diff --git a/src/plugins/cpptools/completionsettingspage.cpp b/src/plugins/cpptools/completionsettingspage.cpp index 111183b2d7..cdf58d94ff 100644 --- a/src/plugins/cpptools/completionsettingspage.cpp +++ b/src/plugins/cpptools/completionsettingspage.cpp @@ -56,67 +56,54 @@ CompletionSettingsPage::~CompletionSettingsPage() delete m_page; } -QWidget *CompletionSettingsPage::createPage(QWidget *parent) +QWidget *CompletionSettingsPage::widget() { - QWidget *w = new QWidget(parent); - m_page = new Ui::CompletionSettingsPage; - m_page->setupUi(w); - - const TextEditor::CompletionSettings &settings = - TextEditor::TextEditorSettings::completionSettings(); - - int caseSensitivityIndex = 0; - switch (settings.m_caseSensitivity) { - case TextEditor::CaseSensitive: - caseSensitivityIndex = 0; - break; - case TextEditor::CaseInsensitive: - caseSensitivityIndex = 1; - break; - case TextEditor::FirstLetterCaseSensitive: - caseSensitivityIndex = 2; - break; + if (!m_widget) { + m_widget = new QWidget; + m_page = new Ui::CompletionSettingsPage; + m_page->setupUi(m_widget); + + const TextEditor::CompletionSettings &settings = + TextEditor::TextEditorSettings::completionSettings(); + + int caseSensitivityIndex = 0; + switch (settings.m_caseSensitivity) { + case TextEditor::CaseSensitive: + caseSensitivityIndex = 0; + break; + case TextEditor::CaseInsensitive: + caseSensitivityIndex = 1; + break; + case TextEditor::FirstLetterCaseSensitive: + caseSensitivityIndex = 2; + break; + } + + int completionTriggerIndex = 0; + switch (settings.m_completionTrigger) { + case TextEditor::ManualCompletion: + completionTriggerIndex = 0; + break; + case TextEditor::TriggeredCompletion: + completionTriggerIndex = 1; + break; + case TextEditor::AutomaticCompletion: + completionTriggerIndex = 2; + break; + } + + m_page->caseSensitivity->setCurrentIndex(caseSensitivityIndex); + m_page->completionTrigger->setCurrentIndex(completionTriggerIndex); + m_page->autoInsertBrackets->setChecked(settings.m_autoInsertBrackets); + m_page->surroundSelectedText->setChecked(settings.m_surroundingAutoBrackets); + m_page->partiallyComplete->setChecked(settings.m_partiallyComplete); + m_page->spaceAfterFunctionName->setChecked(settings.m_spaceAfterFunctionName); + m_page->enableDoxygenCheckBox->setChecked(m_commentsSettings.m_enableDoxygen); + m_page->generateBriefCheckBox->setChecked(m_commentsSettings.m_generateBrief); + m_page->leadingAsterisksCheckBox->setChecked(m_commentsSettings.m_leadingAsterisks); + m_page->generateBriefCheckBox->setEnabled(m_page->enableDoxygenCheckBox->isChecked()); } - - int completionTriggerIndex = 0; - switch (settings.m_completionTrigger) { - case TextEditor::ManualCompletion: - completionTriggerIndex = 0; - break; - case TextEditor::TriggeredCompletion: - completionTriggerIndex = 1; - break; - case TextEditor::AutomaticCompletion: - completionTriggerIndex = 2; - break; - } - - m_page->caseSensitivity->setCurrentIndex(caseSensitivityIndex); - m_page->completionTrigger->setCurrentIndex(completionTriggerIndex); - m_page->autoInsertBrackets->setChecked(settings.m_autoInsertBrackets); - m_page->surroundSelectedText->setChecked(settings.m_surroundingAutoBrackets); - m_page->partiallyComplete->setChecked(settings.m_partiallyComplete); - m_page->spaceAfterFunctionName->setChecked(settings.m_spaceAfterFunctionName); - m_page->enableDoxygenCheckBox->setChecked(m_commentsSettings.m_enableDoxygen); - m_page->generateBriefCheckBox->setChecked(m_commentsSettings.m_generateBrief); - m_page->leadingAsterisksCheckBox->setChecked(m_commentsSettings.m_leadingAsterisks); - - if (m_searchKeywords.isEmpty()) { - QTextStream(&m_searchKeywords) << m_page->caseSensitivityLabel->text() - << ' ' << m_page->autoInsertBrackets->text() - << ' ' << m_page->surroundSelectedText->text() - << ' ' << m_page->completionTriggerLabel->text() - << ' ' << m_page->partiallyComplete->text() - << ' ' << m_page->spaceAfterFunctionName->text() - << ' ' << m_page->enableDoxygenCheckBox->text() - << ' ' << m_page->generateBriefCheckBox->text() - << ' ' << m_page->leadingAsterisksCheckBox->text(); - m_searchKeywords.remove(QLatin1Char('&')); - } - - m_page->generateBriefCheckBox->setEnabled(m_page->enableDoxygenCheckBox->isChecked()); - - return w; + return m_widget; } void CompletionSettingsPage::apply() @@ -144,11 +131,6 @@ void CompletionSettingsPage::apply() emit commentsSettingsChanged(m_commentsSettings); } -bool CompletionSettingsPage::matches(const QString &s) const -{ - return m_searchKeywords.contains(s, Qt::CaseInsensitive); -} - TextEditor::CaseSensitivity CompletionSettingsPage::caseSensitivity() const { switch (m_page->caseSensitivity->currentIndex()) { @@ -175,6 +157,7 @@ TextEditor::CompletionTrigger CompletionSettingsPage::completionTrigger() const void CompletionSettingsPage::finish() { + delete m_widget; if (!m_page) // page was never shown return; delete m_page; diff --git a/src/plugins/cpptools/completionsettingspage.h b/src/plugins/cpptools/completionsettingspage.h index 592117fb69..83fa49cec8 100644 --- a/src/plugins/cpptools/completionsettingspage.h +++ b/src/plugins/cpptools/completionsettingspage.h @@ -35,6 +35,8 @@ #include <texteditor/completionsettings.h> #include <texteditor/texteditoroptionspage.h> +#include <QPointer> + namespace CppTools { namespace Internal { @@ -52,10 +54,9 @@ public: CompletionSettingsPage(QObject *parent); ~CompletionSettingsPage(); - QWidget *createPage(QWidget *parent); + QWidget *widget(); void apply(); void finish(); - bool matches(const QString &) const; const CommentsSettings &commentsSettings() const; @@ -69,7 +70,7 @@ private: bool requireCommentsSettingsUpdate() const; Ui::CompletionSettingsPage *m_page; - QString m_searchKeywords; + QPointer<QWidget> m_widget; CommentsSettings m_commentsSettings; }; diff --git a/src/plugins/cpptools/completionsettingspage.ui b/src/plugins/cpptools/completionsettingspage.ui index b500f9095e..3ff7358375 100644 --- a/src/plugins/cpptools/completionsettingspage.ui +++ b/src/plugins/cpptools/completionsettingspage.ui @@ -107,7 +107,7 @@ <item row="2" column="0"> <widget class="QCheckBox" name="partiallyComplete"> <property name="toolTip"> - <string>Insert the common prefix of available completion items.</string> + <string>Inserts the common prefix of available completion items.</string> </property> <property name="text"> <string>Autocomplete common &prefix</string> @@ -120,7 +120,7 @@ <item row="3" column="0" colspan="2"> <widget class="QCheckBox" name="autoInsertBrackets"> <property name="toolTip"> - <string>Automatically insert semicolons and closing brackets, parentheses, curly braces, and quotes when appropriate.</string> + <string>Automatically inserts semicolons and closing brackets, parentheses, curly braces, and quotes when appropriate.</string> </property> <property name="text"> <string>&Automatically insert matching characters</string> @@ -151,7 +151,7 @@ <item> <widget class="QCheckBox" name="surroundSelectedText"> <property name="toolTip"> - <string>When typing a matching character and there is a text selection, instead of removing the selection, surround it with the corresponding characters.</string> + <string>When typing a matching character and there is a text selection, instead of removing the selection, surrounds it with the corresponding characters.</string> </property> <property name="text"> <string>Surround &text selections</string> @@ -205,7 +205,7 @@ <item> <widget class="QCheckBox" name="enableDoxygenCheckBox"> <property name="toolTip"> - <string>Automatically create a Doxygen comment upon pressing enter after a /** or /*!</string> + <string>Automatically creates a Doxygen comment upon pressing enter after a /**, /*!, //! or ///</string> </property> <property name="text"> <string>Enable Doxygen blocks</string> @@ -233,7 +233,7 @@ <item> <widget class="QCheckBox" name="generateBriefCheckBox"> <property name="toolTip"> - <string>Generate a <i>brief</i> command with an initial description for the corresponding declaration</string> + <string>Generates a <i>brief</i> command with an initial description for the corresponding declaration</string> </property> <property name="text"> <string>Generate brief description</string> @@ -245,7 +245,7 @@ <item> <widget class="QCheckBox" name="leadingAsterisksCheckBox"> <property name="toolTip"> - <string>Add leading asterisks when continuing Qt (/*!) and Java (/**) style comments on new lines</string> + <string>Adds leading asterisks when continuing Qt (/*!) and Java (/**) style comments on new lines</string> </property> <property name="text"> <string>Add leading asterisks</string> diff --git a/src/plugins/cpptools/cppcodegen_test.cpp b/src/plugins/cpptools/cppcodegen_test.cpp index 43da0763f0..9b708e5419 100644 --- a/src/plugins/cpptools/cppcodegen_test.cpp +++ b/src/plugins/cpptools/cppcodegen_test.cpp @@ -27,8 +27,9 @@ ** ****************************************************************************/ -#include "insertionpointlocator.h" #include "cpptoolsplugin.h" +#include "cpptoolstestcase.h" +#include "insertionpointlocator.h" #include <utils/fileutils.h> @@ -361,9 +362,7 @@ void CppToolsPlugin::test_codegen_definition_empty_class() "\n"; Document::Ptr src = Document::create(QDir::tempPath() + QLatin1String("/file.h")); - Utils::FileSaver srcSaver(src->fileName()); - srcSaver.write(srcText); - srcSaver.finalize(); + QVERIFY(CppTools::Tests::TestCase::writeFile(src->fileName(), srcText)); src->setUtf8Source(srcText); src->parse(); src->check(); @@ -371,9 +370,7 @@ void CppToolsPlugin::test_codegen_definition_empty_class() QCOMPARE(src->globalSymbolCount(), 1U); Document::Ptr dst = Document::create(QDir::tempPath() + QLatin1String("/file.cpp")); - Utils::FileSaver dstSaver(dst->fileName()); - dstSaver.write(dstText); - dstSaver.finalize(); + QVERIFY(CppTools::Tests::TestCase::writeFile(dst->fileName(), dstText)); dst->setUtf8Source(dstText); dst->parse(); dst->check(); @@ -429,9 +426,7 @@ void CppToolsPlugin::test_codegen_definition_first_member() "int y;\n").arg(QDir::tempPath()).toLatin1(); Document::Ptr src = Document::create(QDir::tempPath() + QLatin1String("/file.h")); - Utils::FileSaver srcSaver(src->fileName()); - srcSaver.write(srcText); - srcSaver.finalize(); + QVERIFY(CppTools::Tests::TestCase::writeFile(src->fileName(), srcText)); src->setUtf8Source(srcText); src->parse(); src->check(); @@ -441,9 +436,7 @@ void CppToolsPlugin::test_codegen_definition_first_member() Document::Ptr dst = Document::create(QDir::tempPath() + QLatin1String("/file.cpp")); dst->addIncludeFile(Document::Include(QLatin1String("file.h"), src->fileName(), 1, Client::IncludeLocal)); - Utils::FileSaver dstSaver(dst->fileName()); - dstSaver.write(dstText); - dstSaver.finalize(); + QVERIFY(CppTools::Tests::TestCase::writeFile(dst->fileName(), dstText)); dst->setUtf8Source(dstText); dst->parse(); dst->check(); @@ -499,9 +492,7 @@ void CppToolsPlugin::test_codegen_definition_last_member() "int y;\n").arg(QDir::tempPath()).toLatin1(); Document::Ptr src = Document::create(QDir::tempPath() + QLatin1String("/file.h")); - Utils::FileSaver srcSaver(src->fileName()); - srcSaver.write(srcText); - srcSaver.finalize(); + QVERIFY(CppTools::Tests::TestCase::writeFile(src->fileName(), srcText)); src->setUtf8Source(srcText); src->parse(); src->check(); @@ -511,9 +502,7 @@ void CppToolsPlugin::test_codegen_definition_last_member() Document::Ptr dst = Document::create(QDir::tempPath() + QLatin1String("/file.cpp")); dst->addIncludeFile(Document::Include(QLatin1String("file.h"), src->fileName(), 1, Client::IncludeLocal)); - Utils::FileSaver dstSaver(dst->fileName()); - dstSaver.write(dstText); - dstSaver.finalize(); + QVERIFY(CppTools::Tests::TestCase::writeFile(dst->fileName(), dstText)); dst->setUtf8Source(dstText); dst->parse(); dst->check(); @@ -575,9 +564,7 @@ void CppToolsPlugin::test_codegen_definition_middle_member() "int y;\n").arg(QDir::tempPath()).toLatin1(); Document::Ptr src = Document::create(QDir::tempPath() + QLatin1String("/file.h")); - Utils::FileSaver srcSaver(src->fileName()); - srcSaver.write(srcText); - srcSaver.finalize(); + QVERIFY(CppTools::Tests::TestCase::writeFile(src->fileName(), srcText)); src->setUtf8Source(srcText); src->parse(); src->check(); @@ -587,9 +574,7 @@ void CppToolsPlugin::test_codegen_definition_middle_member() Document::Ptr dst = Document::create(QDir::tempPath() + QLatin1String("/file.cpp")); dst->addIncludeFile(Document::Include(QLatin1String("file.h"), src->fileName(), 1, Client::IncludeLocal)); - Utils::FileSaver dstSaver(dst->fileName()); - dstSaver.write(dstText); - dstSaver.finalize(); + QVERIFY(CppTools::Tests::TestCase::writeFile(dst->fileName(), dstText)); dst->setUtf8Source(dstText); dst->parse(); dst->check(); @@ -647,9 +632,7 @@ void CppToolsPlugin::test_codegen_definition_middle_member_surrounded_by_undefin "int y;\n").arg(QDir::tempPath()).toLatin1(); Document::Ptr src = Document::create(QDir::tempPath() + QLatin1String("/file.h")); - Utils::FileSaver srcSaver(src->fileName()); - srcSaver.write(srcText); - srcSaver.finalize(); + QVERIFY(CppTools::Tests::TestCase::writeFile(src->fileName(), srcText)); src->setUtf8Source(srcText); src->parse(); src->check(); @@ -659,9 +642,7 @@ void CppToolsPlugin::test_codegen_definition_middle_member_surrounded_by_undefin Document::Ptr dst = Document::create(QDir::tempPath() + QLatin1String("/file.cpp")); dst->addIncludeFile(Document::Include(QLatin1String("file.h"), src->fileName(), 1, Client::IncludeLocal)); - Utils::FileSaver dstSaver(dst->fileName()); - dstSaver.write(dstText); - dstSaver.finalize(); + QVERIFY(CppTools::Tests::TestCase::writeFile(dst->fileName(), dstText)); dst->setUtf8Source(dstText); dst->parse(); dst->check(); @@ -722,9 +703,7 @@ void CppToolsPlugin::test_codegen_definition_member_specific_file() "int y;\n").arg(QDir::tempPath()).toLatin1(); Document::Ptr src = Document::create(QDir::tempPath() + QLatin1String("/file.h")); - Utils::FileSaver srcSaver(src->fileName()); - srcSaver.write(srcText); - srcSaver.finalize(); + QVERIFY(CppTools::Tests::TestCase::writeFile(src->fileName(), srcText)); src->setUtf8Source(srcText); src->parse(); src->check(); @@ -734,9 +713,7 @@ void CppToolsPlugin::test_codegen_definition_member_specific_file() Document::Ptr dst = Document::create(QDir::tempPath() + QLatin1String("/file.cpp")); dst->addIncludeFile(Document::Include(QLatin1String("file.h"), src->fileName(), 1, Client::IncludeLocal)); - Utils::FileSaver dstSaver(dst->fileName()); - dstSaver.write(dstText); - dstSaver.finalize(); + QVERIFY(CppTools::Tests::TestCase::writeFile(dst->fileName(), dstText)); dst->setUtf8Source(dstText); dst->parse(); dst->check(); diff --git a/src/plugins/cpptools/cppcodemodelsettings.cpp b/src/plugins/cpptools/cppcodemodelsettings.cpp index 207cc5141f..074ee34fe2 100644 --- a/src/plugins/cpptools/cppcodemodelsettings.cpp +++ b/src/plugins/cpptools/cppcodemodelsettings.cpp @@ -34,6 +34,9 @@ using namespace CppTools; using namespace CppTools::Internal; +static QLatin1String cppHeaderMimeType(Constants::CPP_HEADER_MIMETYPE); +static QLatin1String cHeaderMimeType(Constants::C_HEADER_MIMETYPE); + void CppCodeModelSettings::fromSettings(QSettings *s) { s->beginGroup(QLatin1String(Constants::CPPTOOLS_SETTINGSGROUP)); @@ -42,6 +45,7 @@ void CppCodeModelSettings::fromSettings(QSettings *s) setIdForMimeType(supporters, QLatin1String(Constants::CPP_SOURCE_MIMETYPE)); setIdForMimeType(supporters, QLatin1String(Constants::OBJECTIVE_C_SOURCE_MIMETYPE)); setIdForMimeType(supporters, QLatin1String(Constants::OBJECTIVE_CPP_SOURCE_MIMETYPE)); + setIdForMimeType(supporters, QLatin1String(Constants::CPP_HEADER_MIMETYPE)); QVariant v = s->value(QLatin1String(Constants::CPPTOOLS_MODEL_MANAGER_PCH_USAGE), PchUse_None); setPCHUsage(static_cast<PCHUsage>(v.toInt())); s->endGroup(); @@ -65,6 +69,23 @@ void CppCodeModelSettings::setModelManagerSupports(const QList<ModelManagerSuppo m_availableModelManagerSupportersByName[supporter->displayName()] = supporter->id(); } +QString CppCodeModelSettings::modelManagerSupportId(const QString &mimeType) const +{ + if (mimeType == cHeaderMimeType) + return m_modelManagerSupportByMimeType.value(cppHeaderMimeType); + else + return m_modelManagerSupportByMimeType.value(mimeType); +} + +void CppCodeModelSettings::setModelManagerSupportId(const QString &mimeType, + const QString &supporter) +{ + if (mimeType == cHeaderMimeType) + m_modelManagerSupportByMimeType.insert(cppHeaderMimeType, supporter); + else + m_modelManagerSupportByMimeType.insert(mimeType, supporter); +} + void CppCodeModelSettings::setIdForMimeType(const QVariant &var, const QString &mimeType) { QHash<QString, QVariant> mimeToId = var.toHash(); diff --git a/src/plugins/cpptools/cppcodemodelsettings.h b/src/plugins/cpptools/cppcodemodelsettings.h index 21039593f6..5836b4d6d7 100644 --- a/src/plugins/cpptools/cppcodemodelsettings.h +++ b/src/plugins/cpptools/cppcodemodelsettings.h @@ -46,7 +46,7 @@ class CppCodeModelSettings public: enum PCHUsage { PchUse_None = 1, - PchUse_BuildSystem = 2, + PchUse_BuildSystem = 2 }; public: @@ -57,8 +57,8 @@ public: void setModelManagerSupports(const QList<ModelManagerSupport *> &supporters); - QString &modelManagerSupportId(const QString &mimeType) - { return m_modelManagerSupportByMimeType[mimeType]; } + QString modelManagerSupportId(const QString &mimeType) const; + void setModelManagerSupportId(const QString &mimeType, const QString &supporter); const QHash<QString, QString> &availableModelManagerSupportersByName() const { return m_availableModelManagerSupportersByName; } diff --git a/src/plugins/cpptools/cppcodemodelsettingspage.cpp b/src/plugins/cpptools/cppcodemodelsettingspage.cpp index 2b9e8653b5..5b975217da 100644 --- a/src/plugins/cpptools/cppcodemodelsettingspage.cpp +++ b/src/plugins/cpptools/cppcodemodelsettingspage.cpp @@ -44,7 +44,7 @@ CppCodeModelSettingsWidget::CppCodeModelSettingsWidget(QWidget *parent) { m_ui->setupUi(this); - m_ui->theGroupBox->setVisible(false); + m_ui->theGroupBox->setVisible(true); } CppCodeModelSettingsWidget::~CppCodeModelSettingsWidget() @@ -60,6 +60,7 @@ void CppCodeModelSettingsWidget::setSettings(const QSharedPointer<CppCodeModelSe applyToWidget(m_ui->cppChooser, QLatin1String(Constants::CPP_SOURCE_MIMETYPE)); applyToWidget(m_ui->objcChooser, QLatin1String(Constants::OBJECTIVE_C_SOURCE_MIMETYPE)); applyToWidget(m_ui->objcppChooser, QLatin1String(Constants::OBJECTIVE_CPP_SOURCE_MIMETYPE)); + applyToWidget(m_ui->hChooser, QLatin1String(Constants::C_HEADER_MIMETYPE)); m_ui->ignorePCHCheckBox->setChecked(s->pchUsage() == CppCodeModelSettings::PchUse_None); } @@ -88,6 +89,8 @@ void CppCodeModelSettingsWidget::applyToSettings() const QLatin1String(Constants::OBJECTIVE_C_SOURCE_MIMETYPE)); changed |= applyToSettings(m_ui->objcppChooser, QLatin1String(Constants::OBJECTIVE_CPP_SOURCE_MIMETYPE)); + changed |= applyToSettings(m_ui->hChooser, + QLatin1String(Constants::C_HEADER_MIMETYPE)); if (m_ui->ignorePCHCheckBox->isChecked() != (m_settings->pchUsage() == CppCodeModelSettings::PchUse_None)) { @@ -101,31 +104,14 @@ void CppCodeModelSettingsWidget::applyToSettings() const m_settings->toSettings(Core::ICore::settings()); } -QString CppCodeModelSettingsWidget::searchKeywords() const -{ - QString rc; - QTextStream ts(&rc); - ts << m_ui->theGroupBox->title() - << ' ' << m_ui->cLabel->text() - << ' ' << m_ui->cppLabel->text() - << ' ' << m_ui->objcLabel->text() - << ' ' << m_ui->objcppLabel->text() - << ' ' << m_ui->anotherGroupBox->title() - << ' ' << m_ui->ignorePCHCheckBox->text(); - foreach (const QString &mmsNames, m_settings->availableModelManagerSupportersByName().keys()) - ts << ' ' << mmsNames; - rc.remove(QLatin1Char('&')); - return rc; -} - bool CppCodeModelSettingsWidget::applyToSettings(QComboBox *chooser, const QString &mimeType) const { QString newId = chooser->itemData(chooser->currentIndex()).toString(); - QString ¤tId = m_settings->modelManagerSupportId(mimeType); + QString currentId = m_settings->modelManagerSupportId(mimeType); if (newId == currentId) return false; - currentId = newId; + m_settings->setModelManagerSupportId(mimeType, newId); return true; } @@ -141,12 +127,12 @@ CppCodeModelSettingsPage::CppCodeModelSettingsPage(QSharedPointer<CppCodeModelSe setCategoryIcon(QLatin1String(Constants::SETTINGS_CATEGORY_CPP_ICON)); } -QWidget *CppCodeModelSettingsPage::createPage(QWidget *parent) +QWidget *CppCodeModelSettingsPage::widget() { - m_widget = new CppCodeModelSettingsWidget(parent); - m_widget->setSettings(m_settings); - if (m_searchKeywords.isEmpty()) - m_searchKeywords = m_widget->searchKeywords(); + if (!m_widget) { + m_widget = new CppCodeModelSettingsWidget; + m_widget->setSettings(m_settings); + } return m_widget; } @@ -156,7 +142,7 @@ void CppCodeModelSettingsPage::apply() m_widget->applyToSettings(); } -bool CppCodeModelSettingsPage::matches(const QString &s) const +void CppCodeModelSettingsPage::finish() { - return m_searchKeywords.contains(s, Qt::CaseInsensitive); + delete m_widget; } diff --git a/src/plugins/cpptools/cppcodemodelsettingspage.h b/src/plugins/cpptools/cppcodemodelsettingspage.h index 343d81e6af..3989be4bab 100644 --- a/src/plugins/cpptools/cppcodemodelsettingspage.h +++ b/src/plugins/cpptools/cppcodemodelsettingspage.h @@ -56,8 +56,6 @@ public: void setSettings(const QSharedPointer<CppCodeModelSettings> &s); void applyToSettings() const; - QString searchKeywords() const; - private: bool applyToSettings(QComboBox *chooser, const QString &mimeType) const; void applyToWidget(QComboBox *chooser, const QString &mimeType) const; @@ -73,15 +71,13 @@ public: explicit CppCodeModelSettingsPage(QSharedPointer<CppCodeModelSettings> &settings, QObject *parent = 0); - QWidget *createPage(QWidget *parent); + QWidget *widget(); void apply(); - void finish() { } - bool matches(const QString &s) const; + void finish(); private: const QSharedPointer<CppCodeModelSettings> m_settings; QPointer<CppCodeModelSettingsWidget> m_widget; - QString m_searchKeywords; }; } // Internal namespace diff --git a/src/plugins/cpptools/cppcodemodelsettingspage.ui b/src/plugins/cpptools/cppcodemodelsettingspage.ui index 310c01d966..1109649ceb 100644 --- a/src/plugins/cpptools/cppcodemodelsettingspage.ui +++ b/src/plugins/cpptools/cppcodemodelsettingspage.ui @@ -74,6 +74,16 @@ <item row="3" column="1"> <widget class="QComboBox" name="objcppChooser"/> </item> + <item row="4" column="0"> + <widget class="QLabel" name="hLabel"> + <property name="text"> + <string>Headers</string> + </property> + </widget> + </item> + <item row="4" column="1"> + <widget class="QComboBox" name="hChooser"/> + </item> </layout> </widget> </item> diff --git a/src/plugins/cpptools/cppcodestylesettingspage.cpp b/src/plugins/cpptools/cppcodestylesettingspage.cpp index eba4b33974..2a5d889583 100644 --- a/src/plugins/cpptools/cppcodestylesettingspage.cpp +++ b/src/plugins/cpptools/cppcodestylesettingspage.cpp @@ -428,44 +428,6 @@ void CppCodeStylePreferencesWidget::slotCurrentPreferencesChanged(TextEditor::IC updatePreview(); } -QString CppCodeStylePreferencesWidget::searchKeywords() const -{ - QString rc; - QLatin1Char sep(' '); - QTextStream(&rc) - << sep << m_ui->tabSettingsWidget->searchKeywords() - << sep << m_ui->indentBlockBraces->text() - << sep << m_ui->indentBlockBody->text() - << sep << m_ui->indentClassBraces->text() - << sep << m_ui->indentEnumBraces->text() - << sep << m_ui->indentNamespaceBraces->text() - << sep << m_ui->indentNamespaceBody->text() - << sep << m_ui->indentAccessSpecifiers->text() - << sep << m_ui->indentDeclarationsRelativeToAccessSpecifiers->text() - << sep << m_ui->indentFunctionBody->text() - << sep << m_ui->indentFunctionBraces->text() - << sep << m_ui->indentSwitchLabels->text() - << sep << m_ui->indentCaseStatements->text() - << sep << m_ui->indentCaseBlocks->text() - << sep << m_ui->indentCaseBreak->text() - << sep << m_ui->bindStarToIdentifier->text() - << sep << m_ui->bindStarToTypeName->text() - << sep << m_ui->bindStarToLeftSpecifier->text() - << sep << m_ui->bindStarToRightSpecifier->text() - << sep << m_ui->contentGroupBox->title() - << sep << m_ui->bracesGroupBox->title() - << sep << m_ui->switchGroupBox->title() - << sep << m_ui->alignmentGroupBox->title() - << sep << m_ui->pointerReferencesGroupBox->title() - << sep << m_ui->extraPaddingConditions->text() - << sep << m_ui->alignAssignments->text() - ; - for (int i = 0; i < m_ui->categoryTab->count(); i++) - QTextStream(&rc) << sep << m_ui->categoryTab->tabText(i); - rc.remove(QLatin1Char('&')); - return rc; -} - void CppCodeStylePreferencesWidget::slotCodeStyleSettingsChanged() { if (m_blockUpdates) @@ -564,19 +526,20 @@ CppCodeStyleSettingsPage::CppCodeStyleSettingsPage(QWidget *parent) : setCategoryIcon(QLatin1String(Constants::SETTINGS_CATEGORY_CPP_ICON)); } -QWidget *CppCodeStyleSettingsPage::createPage(QWidget *parent) +QWidget *CppCodeStyleSettingsPage::widget() { - CppCodeStylePreferences *originalCodeStylePreferences - = CppToolsSettings::instance()->cppCodeStyle(); - m_pageCppCodeStylePreferences = new CppCodeStylePreferences(m_widget); - m_pageCppCodeStylePreferences->setDelegatingPool(originalCodeStylePreferences->delegatingPool()); - m_pageCppCodeStylePreferences->setCodeStyleSettings(originalCodeStylePreferences->codeStyleSettings()); - m_pageCppCodeStylePreferences->setCurrentDelegate(originalCodeStylePreferences->currentDelegate()); - // we set id so that it won't be possible to set delegate to the original prefs - m_pageCppCodeStylePreferences->setId(originalCodeStylePreferences->id()); - m_widget = new CodeStyleEditor(TextEditorSettings::codeStyleFactory(CppTools::Constants::CPP_SETTINGS_ID), - m_pageCppCodeStylePreferences, parent); - + if (!m_widget) { + CppCodeStylePreferences *originalCodeStylePreferences + = CppToolsSettings::instance()->cppCodeStyle(); + m_pageCppCodeStylePreferences = new CppCodeStylePreferences(m_widget); + m_pageCppCodeStylePreferences->setDelegatingPool(originalCodeStylePreferences->delegatingPool()); + m_pageCppCodeStylePreferences->setCodeStyleSettings(originalCodeStylePreferences->codeStyleSettings()); + m_pageCppCodeStylePreferences->setCurrentDelegate(originalCodeStylePreferences->currentDelegate()); + // we set id so that it won't be possible to set delegate to the original prefs + m_pageCppCodeStylePreferences->setId(originalCodeStylePreferences->id()); + m_widget = new CodeStyleEditor(TextEditorSettings::codeStyleFactory(CppTools::Constants::CPP_SETTINGS_ID), + m_pageCppCodeStylePreferences); + } return m_widget; } @@ -601,9 +564,9 @@ void CppCodeStyleSettingsPage::apply() } } -bool CppCodeStyleSettingsPage::matches(const QString &s) const +void CppCodeStyleSettingsPage::finish() { - return m_searchKeywords.contains(s, Qt::CaseInsensitive); + delete m_widget; } } // namespace Internal diff --git a/src/plugins/cpptools/cppcodestylesettingspage.h b/src/plugins/cpptools/cppcodestylesettingspage.h index 4328e1faac..c9719bed48 100644 --- a/src/plugins/cpptools/cppcodestylesettingspage.h +++ b/src/plugins/cpptools/cppcodestylesettingspage.h @@ -66,8 +66,6 @@ public: void setCodeStyle(CppTools::CppCodeStylePreferences *codeStylePreferences); - QString searchKeywords() const; - private slots: void decorateEditors(const TextEditor::FontSettings &fontSettings); void setVisualizeWhitespace(bool on); @@ -95,13 +93,11 @@ class CppCodeStyleSettingsPage : public Core::IOptionsPage public: explicit CppCodeStyleSettingsPage(QWidget *parent = 0); - QWidget *createPage(QWidget *parent); + QWidget *widget(); void apply(); - void finish() { } - bool matches(const QString &) const; + void finish(); private: - QString m_searchKeywords; CppCodeStylePreferences *m_pageCppCodeStylePreferences; QPointer<TextEditor::CodeStyleEditor> m_widget; }; diff --git a/src/plugins/cpptools/cppcompletion_test.cpp b/src/plugins/cpptools/cppcompletion_test.cpp index ff3f5c38b1..da0ef9fa3e 100644 --- a/src/plugins/cpptools/cppcompletion_test.cpp +++ b/src/plugins/cpptools/cppcompletion_test.cpp @@ -27,21 +27,22 @@ ** ****************************************************************************/ -#include "cpptoolsplugin.h" #include "cppcompletionassist.h" #include "cppmodelmanager.h" +#include "cpptoolsplugin.h" +#include "cpptoolstestcase.h" -#include <texteditor/plaintexteditor.h> #include <texteditor/codeassist/iassistproposal.h> #include <texteditor/convenience.h> +#include <texteditor/plaintexteditor.h> #include <utils/changeset.h> #include <utils/fileutils.h> -#include <QtTest> #include <QDebug> -#include <QTextDocument> #include <QDir> +#include <QTextDocument> +#include <QtTest> /*! Tests for code completion. @@ -52,57 +53,56 @@ using namespace CppTools::Internal; using namespace TextEditor; using namespace Core; -class CompletionTestCase +namespace { + +typedef QByteArray _; + +class CompletionTestCase : public CppTools::Tests::TestCase { public: CompletionTestCase(const QByteArray &sourceText, const QByteArray &textToInsert = QByteArray()) - : position(-1), editorWidget(0), textDocument(0), editor(0), - cmm(CppModelManager::instance()) + : m_position(-1), m_editorWidget(0), m_textDocument(0), m_editor(0) { - source = sourceText; - position = source.indexOf('@'); - QVERIFY(position != -1); - source[position] = ' '; + QVERIFY(succeededSoFar()); + m_succeededSoFar = false; + + m_source = sourceText; + m_position = m_source.indexOf('@'); + QVERIFY(m_position != -1); + m_source[m_position] = ' '; // Write source to file const QString fileName = QDir::tempPath() + QLatin1String("/file.h"); - Utils::FileSaver srcSaver(fileName); - srcSaver.write(source); - srcSaver.finalize(); + QVERIFY(writeFile(fileName, m_source)); // Open in editor - editor = EditorManager::openEditor(fileName); - QVERIFY(editor); - editorWidget = qobject_cast<TextEditor::BaseTextEditorWidget *>(editor->widget()); - QVERIFY(editorWidget); + m_editor = EditorManager::openEditor(fileName); + QVERIFY(m_editor); + closeEditorAtEndOfTestCase(m_editor); + m_editorWidget = qobject_cast<TextEditor::BaseTextEditorWidget *>(m_editor->widget()); + QVERIFY(m_editorWidget); - textDocument = editorWidget->document(); + m_textDocument = m_editorWidget->document(); // Get Document - while (!cmm->snapshot().contains(fileName)) - QCoreApplication::processEvents(); - Document::Ptr document = cmm->snapshot().document(fileName); + waitForFileInGlobalSnapshot(fileName); + const Document::Ptr document = globalSnapshot().document(fileName); - snapshot.insert(document); + m_snapshot.insert(document); if (!textToInsert.isEmpty()) insertText(textToInsert); - } - ~CompletionTestCase() - { - EditorManager::closeEditor(editor, /*askAboutModifiedEditors=*/ false); - cmm->GC(); - QVERIFY(cmm->snapshot().isEmpty()); + m_succeededSoFar = true; } QStringList getCompletions(bool *replaceAccessOperator = 0) const { QStringList completions; CppCompletionAssistInterface *ai - = new CppCompletionAssistInterface(editorWidget->document(), position, - editorWidget->editorDocument()->filePath(), - ExplicitlyInvoked, snapshot, + = new CppCompletionAssistInterface(m_editorWidget->document(), m_position, + m_editorWidget->baseTextDocument()->filePath(), + ExplicitlyInvoked, m_snapshot, QStringList(), QStringList()); CppCompletionAssistProcessor processor; IAssistProposal *proposal = processor.perform(ai); @@ -116,8 +116,9 @@ public: return completions; const int pos = proposal->basePosition(); - const int length = position - pos; - const QString prefix = Convenience::textAt(QTextCursor(editorWidget->document()), pos, length); + const int length = m_position - pos; + const QString prefix = Convenience::textAt(QTextCursor(m_editorWidget->document()), pos, + length); if (!prefix.isEmpty()) listmodel->filter(prefix); if (listmodel->isSortable(prefix)) @@ -135,52 +136,199 @@ public: void insertText(const QByteArray &text) { Utils::ChangeSet change; - change.insert(position, QLatin1String(text)); - QTextCursor cursor(textDocument); + change.insert(m_position, QLatin1String(text)); + QTextCursor cursor(m_textDocument); change.apply(&cursor); - position += text.length(); + m_position += text.length(); } private: - QByteArray source; - int position; - Snapshot snapshot; - BaseTextEditorWidget *editorWidget; - QTextDocument *textDocument; - IEditor *editor; - - CppModelManager *cmm; + QByteArray m_source; + int m_position; + Snapshot m_snapshot; + BaseTextEditorWidget *m_editorWidget; + QTextDocument *m_textDocument; + IEditor *m_editor; }; -void CppToolsPlugin::test_completion_forward_declarations_present() +} // anonymous namespace + +void CppToolsPlugin::test_completion_basic_1() +{ + const QByteArray source = + "class Foo\n" + "{\n" + " void foo();\n" + " int m;\n" + "};\n" + "\n" + "void func() {\n" + " Foo f;\n" + " @\n" + "}"; + CompletionTestCase test(source); + QVERIFY(test.succeededSoFar()); + + QStringList basicCompletions = test.getCompletions(); + QVERIFY(!basicCompletions.contains(QLatin1String("foo"))); + QVERIFY(!basicCompletions.contains(QLatin1String("m"))); + QVERIFY(basicCompletions.contains(QLatin1String("Foo"))); + QVERIFY(basicCompletions.contains(QLatin1String("func"))); + QVERIFY(basicCompletions.contains(QLatin1String("f"))); + + test.insertText("f."); + + QStringList memberCompletions = test.getCompletions(); + QVERIFY(memberCompletions.contains(QLatin1String("foo"))); + QVERIFY(memberCompletions.contains(QLatin1String("m"))); + QVERIFY(!memberCompletions.contains(QLatin1String("func"))); + QVERIFY(!memberCompletions.contains(QLatin1String("f"))); +} + +void CppToolsPlugin::test_completion_prefix_first_QTCREATORBUG_8737() +{ + const QByteArray source = + "void f()\n" + "{\n" + " int a_b_c, a_c, a_c_a;\n" + " @;\n" + "}\n" + ; + CompletionTestCase test(source, "a_c"); + QVERIFY(test.succeededSoFar()); + + QStringList completions = test.getCompletions(); + + QVERIFY(completions.size() >= 2); + QCOMPARE(completions.at(0), QLatin1String("a_c")); + QCOMPARE(completions.at(1), QLatin1String("a_c_a")); + QVERIFY(completions.contains(QLatin1String("a_b_c"))); +} + +void CppToolsPlugin::test_completion_prefix_first_QTCREATORBUG_9236() { const QByteArray source = - "\n" - "class Foo\n" - "{\n" - " struct Bar;\n" - " int i;\n" - "};\n" - "\n" - "struct Foo::Bar \n" - "{\n" - " Bar() {}\n" - "};\n" - "\n" - "@\n" - "// padding so we get the scope right\n"; - CompletionTestCase test(source, "Foo::Bar::"); - - QStringList expected; - expected.append(QLatin1String("Bar")); - - const QStringList completions = test.getCompletions(); - QCOMPARE(completions, expected); + "class r_etclass\n" + "{\n" + "public:\n" + " int raEmTmber;\n" + " void r_e_t(int re_t)\n" + " {\n" + " int r_et;\n" + " int rETUCASE;\n" + " @\n" + " }\n" + "};\n" + ; + CompletionTestCase test(source, "ret"); + QVERIFY(test.succeededSoFar()); + + QStringList completions = test.getCompletions(); + QVERIFY(completions.size() >= 2); + QCOMPARE(completions.at(0), QLatin1String("return")); + QCOMPARE(completions.at(1), QLatin1String("rETUCASE")); + QVERIFY(completions.contains(QLatin1String("r_etclass"))); + QVERIFY(completions.contains(QLatin1String("raEmTmber"))); + QVERIFY(completions.contains(QLatin1String("r_e_t"))); + QVERIFY(completions.contains(QLatin1String("re_t"))); + QVERIFY(completions.contains(QLatin1String("r_et"))); } -void CppToolsPlugin::test_completion_inside_parentheses_c_style_conversion() +void CppToolsPlugin::test_completion_template_function() { - const QByteArray source = "\n" + QFETCH(QByteArray, code); + QFETCH(QStringList, expectedCompletions); + + CompletionTestCase test(code); + QVERIFY(test.succeededSoFar()); + + QStringList actualCompletions = test.getCompletions(); + QString errorPattern(QLatin1String("Completion not found: %1")); + foreach (const QString &completion, expectedCompletions) { + QByteArray errorMessage = errorPattern.arg(completion).toUtf8(); + QVERIFY2(actualCompletions.contains(completion), errorMessage.data()); + } +} + +void CppToolsPlugin::test_completion_template_function_data() +{ + QTest::addColumn<QByteArray>("code"); + QTest::addColumn<QStringList>("expectedCompletions"); + + QByteArray code; + QStringList completions; + + code = + "template <class tclass, typename tname, int tint>\n" + "tname Hello(const tclass &e)\n" + "{\n" + " tname e2 = e;\n" + " @\n" + "}"; + + completions.append(QLatin1String("tclass")); + completions.append(QLatin1String("tname")); + completions.append(QLatin1String("tint")); + + QTest::newRow("case: template parameters in template function body") + << code << completions; + + completions.clear(); + + code = + "template <class tclass, typename tname, int tint>\n" + "tname Hello(const tclass &e, @)\n" + "{\n" + " tname e2 = e;\n" + "}"; + + completions.append(QLatin1String("tclass")); + completions.append(QLatin1String("tname")); + completions.append(QLatin1String("tint")); + + QTest::newRow("case: template parameters in template function parameters list") + << code << completions; +} + +void CppToolsPlugin::test_completion() +{ + QFETCH(QByteArray, code); + QFETCH(QByteArray, prefix); + QFETCH(QStringList, expectedCompletions); + + CompletionTestCase test(code, prefix); + QVERIFY(test.succeededSoFar()); + + QStringList actualCompletions = test.getCompletions(); + actualCompletions.sort(); + expectedCompletions.sort(); + + QCOMPARE(actualCompletions, expectedCompletions); +} + +void CppToolsPlugin::test_completion_data() +{ + QTest::addColumn<QByteArray>("code"); + QTest::addColumn<QByteArray>("prefix"); + QTest::addColumn<QStringList>("expectedCompletions"); + + QTest::newRow("forward_declarations_present") << _( + "class Foo\n" + "{\n" + " struct Bar;\n" + " int i;\n" + "};\n" + "\n" + "struct Foo::Bar \n" + "{\n" + " Bar() {}\n" + "};\n" + "\n" + "@\n" + ) << _("Foo::Bar::") << (QStringList() + << QLatin1String("Bar")); + + QTest::newRow("inside_parentheses_c_style_conversion") << _( "class Base\n" "{\n" " int i_base;\n" @@ -197,20 +345,13 @@ void CppToolsPlugin::test_completion_inside_parentheses_c_style_conversion() " if (1)\n" " @\n" "}\n" - ; - CompletionTestCase test(source, "((Derived *)b)->"); - - const QStringList completions = test.getCompletions(); - QCOMPARE(completions.size(), 4); - QVERIFY(completions.contains(QLatin1String("Derived"))); - QVERIFY(completions.contains(QLatin1String("Base"))); - QVERIFY(completions.contains(QLatin1String("i_derived"))); - QVERIFY(completions.contains(QLatin1String("i_base"))); -} + ) << _("((Derived *)b)->") << (QStringList() + << QLatin1String("Derived") + << QLatin1String("Base") + << QLatin1String("i_derived") + << QLatin1String("i_base")); -void CppToolsPlugin::test_completion_inside_parentheses_cast_operator_conversion() -{ - const QByteArray source = "\n" + QTest::newRow("inside_parentheses_cast_operator_conversion") << _( "class Base\n" "{\n" " int i_base;\n" @@ -227,52 +368,13 @@ void CppToolsPlugin::test_completion_inside_parentheses_cast_operator_conversion " if (1)\n" " @\n" "}\n" - ; - CompletionTestCase test(source, "(static_cast<Derived *>(b))->"); - - const QStringList completions = test.getCompletions(); - QCOMPARE(completions.size(), 4); - QVERIFY(completions.contains(QLatin1String("Derived"))); - QVERIFY(completions.contains(QLatin1String("Base"))); - QVERIFY(completions.contains(QLatin1String("i_derived"))); - QVERIFY(completions.contains(QLatin1String("i_base"))); -} - -void CppToolsPlugin::test_completion_basic_1() -{ - const QByteArray source = "\n" - "class Foo\n" - "{\n" - " void foo();\n" - " int m;\n" - "};\n" - "\n" - "void func() {\n" - " Foo f;\n" - " @\n" - " // padding so we get the scope right\n" - "}"; - CompletionTestCase test(source); - - QStringList basicCompletions = test.getCompletions(); - QVERIFY(!basicCompletions.contains(QLatin1String("foo"))); - QVERIFY(!basicCompletions.contains(QLatin1String("m"))); - QVERIFY(basicCompletions.contains(QLatin1String("Foo"))); - QVERIFY(basicCompletions.contains(QLatin1String("func"))); - QVERIFY(basicCompletions.contains(QLatin1String("f"))); - - test.insertText("f."); + ) << _("(static_cast<Derived *>(b))->") << (QStringList() + << QLatin1String("Derived") + << QLatin1String("Base") + << QLatin1String("i_derived") + << QLatin1String("i_base")); - QStringList memberCompletions = test.getCompletions(); - QVERIFY(memberCompletions.contains(QLatin1String("foo"))); - QVERIFY(memberCompletions.contains(QLatin1String("m"))); - QVERIFY(!memberCompletions.contains(QLatin1String("func"))); - QVERIFY(!memberCompletions.contains(QLatin1String("f"))); -} - -void CppToolsPlugin::test_completion_template_1() -{ - const QByteArray source = "\n" + QTest::newRow("template_1") << _( "template <class T>\n" "class Foo\n" "{\n" @@ -284,22 +386,14 @@ void CppToolsPlugin::test_completion_template_1() "void func() {\n" " Foo f;\n" " @\n" - " // padding so we get the scope right\n" - "}"; - CompletionTestCase test(source, "Foo::"); - - const QStringList completions = test.getCompletions(); - QVERIFY(completions.contains(QLatin1String("Type"))); - QVERIFY(completions.contains(QLatin1String("foo"))); - QVERIFY(completions.contains(QLatin1String("m"))); - QVERIFY(!completions.contains(QLatin1String("T"))); - QVERIFY(!completions.contains(QLatin1String("f"))); - QVERIFY(!completions.contains(QLatin1String("func"))); -} + "}" + ) << _("Foo::") << (QStringList() + << QLatin1String("Foo") + << QLatin1String("Type") + << QLatin1String("foo") + << QLatin1String("m")); -void CppToolsPlugin::test_completion_template_2() -{ - const QByteArray source = "\n" + QTest::newRow("template_2") << _( "template <class T>\n" "struct List\n" "{\n" @@ -311,20 +405,13 @@ void CppToolsPlugin::test_completion_template_2() "void func() {\n" " List<Tupple> l;\n" " @\n" - " // padding so we get the scope right\n" - "}"; - CompletionTestCase test(source, "l.at(0)."); - - const QStringList completions = test.getCompletions(); - QCOMPARE(completions.size(), 3); - QVERIFY(completions.contains(QLatin1String("Tupple"))); - QVERIFY(completions.contains(QLatin1String("a"))); - QVERIFY(completions.contains(QLatin1String("b"))); -} + "}" + ) << _("l.at(0).") << (QStringList() + << QLatin1String("Tupple") + << QLatin1String("a") + << QLatin1String("b")); -void CppToolsPlugin::test_completion_template_3() -{ - const QByteArray source = "\n" + QTest::newRow("template_3") << _( "template <class T>\n" "struct List\n" "{\n" @@ -336,20 +423,13 @@ void CppToolsPlugin::test_completion_template_3() "void func() {\n" " List<Tupple> l;\n" " @\n" - " // padding so we get the scope right\n" - "}"; - CompletionTestCase test(source, "l.t."); - - const QStringList completions = test.getCompletions(); - QCOMPARE(completions.size(), 3); - QVERIFY(completions.contains(QLatin1String("Tupple"))); - QVERIFY(completions.contains(QLatin1String("a"))); - QVERIFY(completions.contains(QLatin1String("b"))); -} + "}" + ) << _("l.t.") << (QStringList() + << QLatin1String("Tupple") + << QLatin1String("a") + << QLatin1String("b")); -void CppToolsPlugin::test_completion_template_4() -{ - const QByteArray source = "\n" + QTest::newRow("template_4") << _( "template <class T>\n" "struct List\n" "{\n" @@ -362,20 +442,13 @@ void CppToolsPlugin::test_completion_template_4() "void func() {\n" " List<Tupple> l;\n" " @\n" - " // padding so we get the scope right\n" - "}"; - CompletionTestCase test(source, "l.u."); - - const QStringList completions = test.getCompletions(); - QCOMPARE(completions.size(), 3); - QVERIFY(completions.contains(QLatin1String("Tupple"))); - QVERIFY(completions.contains(QLatin1String("a"))); - QVERIFY(completions.contains(QLatin1String("b"))); -} + "}" + ) << _("l.u.") << (QStringList() + << QLatin1String("Tupple") + << QLatin1String("a") + << QLatin1String("b")); -void CppToolsPlugin::test_completion_template_5() -{ - const QByteArray source = "\n" + QTest::newRow("template_5") << _( "template <class T>\n" "struct List\n" "{\n" @@ -388,20 +461,13 @@ void CppToolsPlugin::test_completion_template_5() " typedef List<Tupple> LT;\n" " LT l;" " @\n" - " // padding so we get the scope right\n" - "}"; - CompletionTestCase test(source, "l.u."); - - const QStringList completions = test.getCompletions(); - QCOMPARE(completions.size(), 3); - QVERIFY(completions.contains(QLatin1String("Tupple"))); - QVERIFY(completions.contains(QLatin1String("a"))); - QVERIFY(completions.contains(QLatin1String("b"))); -} + "}" + ) << _("l.u.") << (QStringList() + << QLatin1String("Tupple") + << QLatin1String("a") + << QLatin1String("b")); -void CppToolsPlugin::test_completion_template_6() -{ - const QByteArray source = "\n" + QTest::newRow("template_6") << _( "class Item\n" "{\n" " int i;\n" @@ -419,18 +485,11 @@ void CppToolsPlugin::test_completion_template_6() "{};\n" "ItemContainer container;\n" "@\n" - ; - CompletionTestCase test(source, "container.get()."); - - const QStringList completions = test.getCompletions(); - QCOMPARE(completions.size(), 2); - QVERIFY(completions.contains(QLatin1String("Item"))); - QVERIFY(completions.contains(QLatin1String("i"))); -} + ) << _("container.get().") << (QStringList() + << QLatin1String("Item") + << QLatin1String("i")); -void CppToolsPlugin::test_completion_template_7() -{ - const QByteArray source = "\n" + QTest::newRow("template_7") << _( "struct Test\n" "{\n" " int i;\n" @@ -451,36 +510,22 @@ void CppToolsPlugin::test_completion_template_7() "\n" "TemplateClass<Test> p(new Test);\n" "@\n" - ; - CompletionTestCase test(source, "p->"); - - const QStringList completions = test.getCompletions(); - QCOMPARE(completions.size(), 2); - QVERIFY(completions.contains(QLatin1String("Test"))); - QVERIFY(completions.contains(QLatin1String("i"))); -} + ) << _("p->") << (QStringList() + << QLatin1String("Test") + << QLatin1String("i")); -void CppToolsPlugin::test_completion_type_of_pointer_is_typedef() -{ - const QByteArray source = "\n" + QTest::newRow("type_of_pointer_is_typedef") << _( "typedef struct Foo\n" "{\n" " int foo;\n" "} Foo;\n" "Foo *bar;\n" "@\n" - ; - CompletionTestCase test(source, "bar->"); - - const QStringList completions = test.getCompletions(); - QCOMPARE(completions.size(), 2); - QVERIFY(completions.contains(QLatin1String("Foo"))); - QVERIFY(completions.contains(QLatin1String("foo"))); -} + ) << _("bar->") << (QStringList() + << QLatin1String("Foo") + << QLatin1String("foo")); -void CppToolsPlugin::test_completion_instantiate_full_specialization() -{ - const QByteArray source = "\n" + QTest::newRow("instantiate_full_specialization") << _( "template<typename T>\n" "struct Template\n" "{\n" @@ -495,59 +540,25 @@ void CppToolsPlugin::test_completion_instantiate_full_specialization() "\n" "Template<char> templateChar;\n" "@\n" - ; - CompletionTestCase test(source, "templateChar."); - - const QStringList completions = test.getCompletions(); - QCOMPARE(completions.size(), 2); - QVERIFY(completions.contains(QLatin1String("Template"))); - QVERIFY(completions.contains(QLatin1String("templateChar_i"))); -} - -void CppToolsPlugin::test_completion() -{ - QFETCH(QByteArray, code); - QFETCH(QStringList, expectedCompletions); - - CompletionTestCase test(code, "c."); - - QStringList actualCompletions = test.getCompletions(); - actualCompletions.sort(); - expectedCompletions.sort(); - - QCOMPARE(actualCompletions, expectedCompletions); -} + ) << _("templateChar.") << (QStringList() + << QLatin1String("Template") + << QLatin1String("templateChar_i")); -void CppToolsPlugin::test_completion_template_as_base() -{ - test_completion(); -} - -void CppToolsPlugin::test_completion_template_as_base_data() -{ - QTest::addColumn<QByteArray>("code"); - QTest::addColumn<QStringList>("expectedCompletions"); - - QByteArray code; - QStringList completions; - - code = "\n" + QTest::newRow("template_as_base: base as template directly") << _( "class Data { int dataMember; };\n" "template <class T> class Other : public T { int otherMember; };\n" "\n" "void func() {\n" " Other<Data> c;\n" " @\n" - " // padding so we get the scope right\n" - "}"; - completions.append(QLatin1String("Data")); - completions.append(QLatin1String("dataMember")); - completions.append(QLatin1String("Other")); - completions.append(QLatin1String("otherMember")); - QTest::newRow("case: base as template directly") << code << completions; + "}" + ) << _("c.") << (QStringList() + << QLatin1String("Data") + << QLatin1String("dataMember") + << QLatin1String("Other") + << QLatin1String("otherMember")); - completions.clear(); - code = "\n" + QTest::newRow("template_as_base: base as class template") << _( "class Data { int dataMember; };\n" "template <class T> class Other : public T { int otherMember; };\n" "template <class T> class More : public Other<T> { int moreMember; };\n" @@ -555,18 +566,16 @@ void CppToolsPlugin::test_completion_template_as_base_data() "void func() {\n" " More<Data> c;\n" " @\n" - " // padding so we get the scope right\n" - "}"; - completions.append(QLatin1String("Data")); - completions.append(QLatin1String("dataMember")); - completions.append(QLatin1String("Other")); - completions.append(QLatin1String("otherMember")); - completions.append(QLatin1String("More")); - completions.append(QLatin1String("moreMember")); - QTest::newRow("case: base as class template") << code << completions; - - completions.clear(); - code = "\n" + "}" + ) << _("c.") << (QStringList() + << QLatin1String("Data") + << QLatin1String("dataMember") + << QLatin1String("Other") + << QLatin1String("otherMember") + << QLatin1String("More") + << QLatin1String("moreMember")); + + QTest::newRow("template_as_base: base as globally qualified class template") << _( "class Data { int dataMember; };\n" "template <class T> class Other : public T { int otherMember; };\n" "template <class T> class More : public ::Other<T> { int moreMember; };\n" @@ -574,18 +583,16 @@ void CppToolsPlugin::test_completion_template_as_base_data() "void func() {\n" " More<Data> c;\n" " @\n" - " // padding so we get the scope right\n" - "}"; - completions.append(QLatin1String("Data")); - completions.append(QLatin1String("dataMember")); - completions.append(QLatin1String("Other")); - completions.append(QLatin1String("otherMember")); - completions.append(QLatin1String("More")); - completions.append(QLatin1String("moreMember")); - QTest::newRow("case: base as globally qualified class template") << code << completions; - - completions.clear(); - code = "\n" + "}" + ) << _("c.") << (QStringList() + << QLatin1String("Data") + << QLatin1String("dataMember") + << QLatin1String("Other") + << QLatin1String("otherMember") + << QLatin1String("More") + << QLatin1String("moreMember")); + + QTest::newRow("template_as_base: base as namespace qualified class template") << _( "class Data { int dataMember; };\n" "namespace NS {\n" "template <class T> class Other : public T { int otherMember; };\n" @@ -595,18 +602,16 @@ void CppToolsPlugin::test_completion_template_as_base_data() "void func() {\n" " More<Data> c;\n" " @\n" - " // padding so we get the scope right\n" - "}"; - completions.append(QLatin1String("Data")); - completions.append(QLatin1String("dataMember")); - completions.append(QLatin1String("Other")); - completions.append(QLatin1String("otherMember")); - completions.append(QLatin1String("More")); - completions.append(QLatin1String("moreMember")); - QTest::newRow("case: base as namespace qualified class template") << code << completions; - - completions.clear(); - code = "\n" + "}" + ) << _("c.") << (QStringList() + << QLatin1String("Data") + << QLatin1String("dataMember") + << QLatin1String("Other") + << QLatin1String("otherMember") + << QLatin1String("More") + << QLatin1String("moreMember")); + + QTest::newRow("template_as_base: base as nested template name") << _( "class Data { int dataMember; };\n" "namespace NS {\n" "template <class T> class Delegate { typedef Data<T> Type; };\n" @@ -616,16 +621,14 @@ void CppToolsPlugin::test_completion_template_as_base_data() "void func() {\n" " Final<Data> c;\n" " @\n" - " // padding so we get the scope right\n" - "}"; - completions.append(QLatin1String("Data")); - completions.append(QLatin1String("dataMember")); - completions.append(QLatin1String("Final")); - completions.append(QLatin1String("finalMember")); - QTest::newRow("case: base as nested template name") << code << completions; + "}" + ) << _("c.") << (QStringList() + << QLatin1String("Data") + << QLatin1String("dataMember") + << QLatin1String("Final") + << QLatin1String("finalMember")); - completions.clear(); - code = "\n" + QTest::newRow("template_as_base: base as nested template name in non-template") << _( "class Data { int dataMember; };\n" "namespace NS {\n" "template <class T> class Delegate { typedef Data<T> Type; };\n" @@ -635,16 +638,14 @@ void CppToolsPlugin::test_completion_template_as_base_data() "void func() {\n" " Final c;\n" " @\n" - " // padding so we get the scope right\n" - "}"; - completions.append(QLatin1String("Data")); - completions.append(QLatin1String("dataMember")); - completions.append(QLatin1String("Final")); - completions.append(QLatin1String("finalMember")); - QTest::newRow("case: base as nested template name in non-template") << code << completions; + "}" + ) << _("c.") << (QStringList() + << QLatin1String("Data") + << QLatin1String("dataMember") + << QLatin1String("Final") + << QLatin1String("finalMember")); - completions.clear(); - code = "\n" + QTest::newRow("template_as_base: base as template name in non-template") << _( "class Data { int dataMember; };\n" "namespace NS {\n" "template <class T> class Other : public T { int otherMember; };\n" @@ -654,31 +655,16 @@ void CppToolsPlugin::test_completion_template_as_base_data() "void func() {\n" " Final c;\n" " @\n" - " // padding so we get the scope right\n" - "}"; - completions.append(QLatin1String("Data")); - completions.append(QLatin1String("dataMember")); - completions.append(QLatin1String("Final")); - completions.append(QLatin1String("finalMember")); - completions.append(QLatin1String("Other")); - completions.append(QLatin1String("otherMember")); - QTest::newRow("case: base as template name in non-template") << code << completions; -} - -void CppToolsPlugin::test_completion_use_global_identifier_as_base_class() -{ - test_completion(); -} - -void CppToolsPlugin::test_completion_use_global_identifier_as_base_class_data() -{ - QTest::addColumn<QByteArray>("code"); - QTest::addColumn<QStringList>("expectedCompletions"); - - QByteArray code; - QStringList completions; - - code = "\n" + "}" + ) << _("c.") << (QStringList() + << QLatin1String("Data") + << QLatin1String("dataMember") + << QLatin1String("Final") + << QLatin1String("finalMember") + << QLatin1String("Other") + << QLatin1String("otherMember")); + + QTest::newRow("use_global_identifier_as_base_class: derived as global and base as global") << _( "struct Global\n" "{\n" " int int_global;\n" @@ -691,17 +677,14 @@ void CppToolsPlugin::test_completion_use_global_identifier_as_base_class_data() "\n" "Final c;\n" "@\n" - "// padding so we get the scope right\n"; - - completions.append(QLatin1String("int_global")); - completions.append(QLatin1String("int_final")); - completions.append(QLatin1String("Final")); - completions.append(QLatin1String("Global")); - QTest::newRow("case: derived as global and base as global") << code << completions; - - completions.clear(); - - code = "\n" + ) << _("c.") << (QStringList() + << QLatin1String("int_global") + << QLatin1String("int_final") + << QLatin1String("Final") + << QLatin1String("Global")); + + QTest::newRow("use_global_identifier_as_base_class: derived is inside namespace. " + "base as global") << _( "struct Global\n" "{\n" " int int_global;\n" @@ -717,61 +700,37 @@ void CppToolsPlugin::test_completion_use_global_identifier_as_base_class_data() "\n" "NS::Final c;\n" "@\n" - "// padding so we get the scope right\n"; - - completions.append(QLatin1String("int_global")); - completions.append(QLatin1String("int_final")); - completions.append(QLatin1String("Final")); - completions.append(QLatin1String("Global")); - QTest::newRow("case: derived is inside namespace, base as global") - << code << completions; - - completions.clear(); - - // This test does not work due to the bug QTCREATORBUG-7912 -// code = "\n" -// "struct Global\n" -// "{\n" -// " int int_global;\n" -// "};\n" -// "\n" -// "template <typename T>\n" -// "struct Enclosing\n" -// "{\n" -// "struct Final : ::Global\n" -// "{\n" -// " int int_final;\n" -// "};\n" -// "}\n" -// "\n" -// "Enclosing<int>::Final c;\n" -// "@\n" -// "// padding so we get the scope right\n"; - -// completions.append(QLatin1String("int_global")); -// completions.append(QLatin1String("int_final")); -// completions.append(QLatin1String("Final")); -// completions.append(QLatin1String("Global")); -// QTest::newRow("case: derived is enclosed by template, base as global") -// << code << completions; - -// completions.clear(); -} - -void CppToolsPlugin::test_completion_base_class_has_name_the_same_as_derived() -{ - test_completion(); -} - -void CppToolsPlugin::test_completion_base_class_has_name_the_same_as_derived_data() -{ - QTest::addColumn<QByteArray>("code"); - QTest::addColumn<QStringList>("expectedCompletions"); - - QByteArray code; - QStringList completions; + ) << _("c.") << (QStringList() + << QLatin1String("int_global") + << QLatin1String("int_final") + << QLatin1String("Final") + << QLatin1String("Global")); + + QTest::newRow("use_global_identifier_as_base_class: derived is enclosed by template. " + "base as global") << _( + "struct Global\n" + "{\n" + " int int_global;\n" + "};\n" + "\n" + "template <typename T>\n" + "struct Enclosing\n" + "{\n" + "struct Final : ::Global\n" + "{\n" + " int int_final;\n" + "};\n" + "};\n" + "\n" + "Enclosing<int>::Final c;\n" + "@\n" + ) << _("c.") << (QStringList() + << QLatin1String("int_global") + << QLatin1String("int_final") + << QLatin1String("Final") + << QLatin1String("Global")); - code = "\n" + QTest::newRow("base_class_has_name_the_same_as_derived: base class is derived class") << _( "struct A : A\n" "{\n" " int int_a;\n" @@ -779,15 +738,12 @@ void CppToolsPlugin::test_completion_base_class_has_name_the_same_as_derived_dat "\n" "A c;\n" "@\n" - "// padding so we get the scope right\n"; - - completions.append(QLatin1String("int_a")); - completions.append(QLatin1String("A")); - QTest::newRow("case: base class is derived class") << code << completions; + ) << _("c.") << (QStringList() + << QLatin1String("int_a") + << QLatin1String("A")); - completions.clear(); - - code = "\n" + QTest::newRow("base_class_has_name_the_same_as_derived: base class is derived class. " + "class is in namespace") << _( "namespace NS\n" "{\n" "struct A : A\n" @@ -798,16 +754,12 @@ void CppToolsPlugin::test_completion_base_class_has_name_the_same_as_derived_dat "\n" "NS::A c;\n" "@\n" - "// padding so we get the scope right\n"; - - completions.append(QLatin1String("int_a")); - completions.append(QLatin1String("A")); - QTest::newRow("case: base class is derived class. class is in namespace") - << code << completions; + ) << _("c.") << (QStringList() + << QLatin1String("int_a") + << QLatin1String("A")); - completions.clear(); - - code = "\n" + QTest::newRow("base_class_has_name_the_same_as_derived: base class is derived class. " + "class is in namespace. use scope operator for base class") << _( "namespace NS\n" "{\n" "struct A : NS::A\n" @@ -818,16 +770,12 @@ void CppToolsPlugin::test_completion_base_class_has_name_the_same_as_derived_dat "\n" "NS::A c;\n" "@\n" - "// padding so we get the scope right\n"; - - completions.append(QLatin1String("int_a")); - completions.append(QLatin1String("A")); - QTest::newRow("case: base class is derived class. class is in namespace. " - "use scope operator for base class") << code << completions; - - completions.clear(); + ) << _("c.") << (QStringList() + << QLatin1String("int_a") + << QLatin1String("A")); - code = "\n" + QTest::newRow("base_class_has_name_the_same_as_derived: base class has the same name as " + "derived but in different namespace") << _( "namespace NS1\n" "{\n" "struct A\n" @@ -845,17 +793,13 @@ void CppToolsPlugin::test_completion_base_class_has_name_the_same_as_derived_dat "\n" "NS2::A c;\n" "@\n" - "// padding so we get the scope right\n"; - - completions.append(QLatin1String("int_ns1_a")); - completions.append(QLatin1String("int_ns2_a")); - completions.append(QLatin1String("A")); - QTest::newRow("case: base class has the same name as derived but in different namespace") - << code << completions; - - completions.clear(); + ) << _("c.") << (QStringList() + << QLatin1String("int_ns1_a") + << QLatin1String("int_ns2_a") + << QLatin1String("A")); - code = "\n" + QTest::newRow("base_class_has_name_the_same_as_derived: base class has the same name as " + "derived (in namespace) but is nested by different class") << _( "struct Enclosing\n" "{\n" "struct A\n" @@ -873,17 +817,13 @@ void CppToolsPlugin::test_completion_base_class_has_name_the_same_as_derived_dat "\n" "NS2::A c;\n" "@\n" - "// padding so we get the scope right\n"; - - completions.append(QLatin1String("int_enclosing_a")); - completions.append(QLatin1String("int_ns2_a")); - completions.append(QLatin1String("A")); - QTest::newRow("case: base class has the same name as derived(in namespace) " - "but is nested by different class") << code << completions; - - completions.clear(); + ) << _("c.") << (QStringList() + << QLatin1String("int_enclosing_a") + << QLatin1String("int_ns2_a") + << QLatin1String("A")); - code = "\n" + QTest::newRow("base_class_has_name_the_same_as_derived: base class has the same name as " + "derived (nested) but is nested by different class") << _( "struct EnclosingBase\n" "{\n" "struct A\n" @@ -901,17 +841,13 @@ void CppToolsPlugin::test_completion_base_class_has_name_the_same_as_derived_dat "\n" "EnclosingDerived::A c;\n" "@\n" - "// padding so we get the scope right\n"; + ) << _("c.") << (QStringList() + << QLatin1String("int_enclosing_base_a") + << QLatin1String("int_enclosing_derived_a") + << QLatin1String("A")); - completions.append(QLatin1String("int_enclosing_base_a")); - completions.append(QLatin1String("int_enclosing_derived_a")); - completions.append(QLatin1String("A")); - QTest::newRow("case: base class has the same name as derived(nested) " - "but is nested by different class") << code << completions; - - completions.clear(); - - code = "\n" + QTest::newRow("base_class_has_name_the_same_as_derived: base class is derived class. " + "class is a template") << _( "template <typename T>\n" "struct A : A\n" "{\n" @@ -920,45 +856,24 @@ void CppToolsPlugin::test_completion_base_class_has_name_the_same_as_derived_dat "\n" "A<int> c;\n" "@\n" - "// padding so we get the scope right\n"; - - completions.append(QLatin1String("int_a")); - completions.append(QLatin1String("A")); - QTest::newRow("case: base class is derived class. class is a template") - << code << completions; - - completions.clear(); -} - -void CppToolsPlugin::test_completion_cyclic_inheritance() -{ - test_completion(); -} - -void CppToolsPlugin::test_completion_cyclic_inheritance_data() -{ - QTest::addColumn<QByteArray>("code"); - QTest::addColumn<QStringList>("expectedCompletions"); - - QByteArray code; - QStringList completions; + ) << _("c.") << (QStringList() + << QLatin1String("int_a") + << QLatin1String("A")); - code = "\n" + QTest::newRow("cyclic_inheritance: direct cyclic inheritance") << _( "struct B;\n" "struct A : B { int _a; };\n" "struct B : A { int _b; };\n" "\n" "A c;\n" "@\n" - ; - completions.append(QLatin1String("A")); - completions.append(QLatin1String("_a")); - completions.append(QLatin1String("B")); - completions.append(QLatin1String("_b")); - QTest::newRow("case: direct cyclic inheritance") << code << completions; + ) << _("c.") << (QStringList() + << QLatin1String("A") + << QLatin1String("_a") + << QLatin1String("B") + << QLatin1String("_b")); - completions.clear(); - code = "\n" + QTest::newRow("cyclic_inheritance: indirect cyclic inheritance") << _( "struct C;\n" "struct A : C { int _a; };\n" "struct B : A { int _b; };\n" @@ -966,17 +881,15 @@ void CppToolsPlugin::test_completion_cyclic_inheritance_data() "\n" "A c;\n" "@\n" - ; - completions.append(QLatin1String("A")); - completions.append(QLatin1String("_a")); - completions.append(QLatin1String("B")); - completions.append(QLatin1String("_b")); - completions.append(QLatin1String("C")); - completions.append(QLatin1String("_c")); - QTest::newRow("case: indirect cyclic inheritance") << code << completions; - - completions.clear(); - code = "\n" + ) << _("c.") << (QStringList() + << QLatin1String("A") + << QLatin1String("_a") + << QLatin1String("B") + << QLatin1String("_b") + << QLatin1String("C") + << QLatin1String("_c")); + + QTest::newRow("cyclic_inheritance: indirect cyclic inheritance") << _( "struct B;\n" "struct A : B { int _a; };\n" "struct C { int _c; };\n" @@ -984,17 +897,15 @@ void CppToolsPlugin::test_completion_cyclic_inheritance_data() "\n" "A c;\n" "@\n" - ; - completions.append(QLatin1String("A")); - completions.append(QLatin1String("_a")); - completions.append(QLatin1String("B")); - completions.append(QLatin1String("_b")); - completions.append(QLatin1String("C")); - completions.append(QLatin1String("_c")); - QTest::newRow("case: indirect cyclic inheritance") << code << completions; - - completions.clear(); - code = "\n" + ) << _("c.") << (QStringList() + << QLatin1String("A") + << QLatin1String("_a") + << QLatin1String("B") + << QLatin1String("_b") + << QLatin1String("C") + << QLatin1String("_c")); + + QTest::newRow("cyclic_inheritance: direct cyclic inheritance with templates") << _( "template< typename T > struct C;\n" "template< typename T, typename S > struct D : C< S >\n" "{\n" @@ -1008,17 +919,14 @@ void CppToolsPlugin::test_completion_cyclic_inheritance_data() "\n" "D<int, float> c;\n" "@\n" - ; - completions.append(QLatin1String("D")); - completions.append(QLatin1String("_d_t")); - completions.append(QLatin1String("_d_s")); - completions.append(QLatin1String("C")); - completions.append(QLatin1String("_c_t")); - QTest::newRow("case: direct cyclic inheritance with templates") - << code << completions; - - completions.clear(); - code = "\n" + ) << _("c.") << (QStringList() + << QLatin1String("D") + << QLatin1String("_d_t") + << QLatin1String("_d_s") + << QLatin1String("C") + << QLatin1String("_c_t")); + + QTest::newRow("cyclic_inheritance: indirect cyclic inheritance with templates") << _( "template< typename T > struct C;\n" "template< typename T, typename S > struct D : C< S >\n" "{\n" @@ -1036,19 +944,17 @@ void CppToolsPlugin::test_completion_cyclic_inheritance_data() "\n" "D<int, float> c;\n" "@\n" - ; - completions.append(QLatin1String("D")); - completions.append(QLatin1String("_d_t")); - completions.append(QLatin1String("_d_s")); - completions.append(QLatin1String("C")); - completions.append(QLatin1String("_c_t")); - completions.append(QLatin1String("B")); - completions.append(QLatin1String("_b_t")); - QTest::newRow("case: indirect cyclic inheritance with templates") - << code << completions; - - completions.clear(); - code = "\n" + ) << _("c.") << (QStringList() + << QLatin1String("D") + << QLatin1String("_d_t") + << QLatin1String("_d_s") + << QLatin1String("C") + << QLatin1String("_c_t") + << QLatin1String("B") + << QLatin1String("_b_t")); + + QTest::newRow("cyclic_inheritance: direct cyclic inheritance with templates. " + "more complex situation") << _( "namespace NS\n" "{\n" "template <typename T> struct SuperClass\n" @@ -1076,86 +982,14 @@ void CppToolsPlugin::test_completion_cyclic_inheritance_data() "\n" "Class<int> c;\n" "@\n" - ; - completions.append(QLatin1String("Class")); - completions.append(QLatin1String("ClassRecurse")); - completions.append(QLatin1String("class_t")); - completions.append(QLatin1String("class_recurse_s")); - completions.append(QLatin1String("class_recurse_t")); - QTest::newRow("case: direct cyclic inheritance with templates, more complex situation") - << code << completions; -} - -void CppToolsPlugin::test_completion_template_function() -{ - QFETCH(QByteArray, code); - QFETCH(QStringList, expectedCompletions); - - CompletionTestCase test(code); - - QStringList actualCompletions = test.getCompletions(); - actualCompletions.sort(); - expectedCompletions.sort(); - - QString errorPattern(QLatin1String("Completion not found: %1")); - foreach (const QString &completion, expectedCompletions) { - QByteArray errorMessage = errorPattern.arg(completion).toUtf8(); - QVERIFY2(actualCompletions.contains(completion), errorMessage.data()); - } -} - -void CppToolsPlugin::test_completion_template_function_data() -{ - QTest::addColumn<QByteArray>("code"); - QTest::addColumn<QStringList>("expectedCompletions"); - - QByteArray code; - QStringList completions; - - code = "\n" - "template <class tclass, typename tname, int tint>\n" - "tname Hello(const tclass &e)\n" - "{\n" - " tname e2 = e;\n" - " @\n" - "}"; - - completions.append(QLatin1String("tclass")); - completions.append(QLatin1String("tname")); - completions.append(QLatin1String("tint")); - QTest::newRow("case: template parameters in template function body") - << code << completions; - - completions.clear(); - - code = "\n" - "template <class tclass, typename tname, int tint>\n" - "tname Hello(const tclass &e, @)\n" - "{\n" - " tname e2 = e;\n" - "}"; - - completions.append(QLatin1String("tclass")); - completions.append(QLatin1String("tname")); - completions.append(QLatin1String("tint")); - QTest::newRow("case: template parameters in template function parameters list") - << code << completions; -} - -void CppToolsPlugin::test_completion_enclosing_template_class() -{ - test_completion(); -} - -void CppToolsPlugin::test_completion_enclosing_template_class_data() -{ - QTest::addColumn<QByteArray>("code"); - QTest::addColumn<QStringList>("expectedCompletions"); - - QByteArray code; - QStringList completions; - - code = "\n" + ) << _("c.") << (QStringList() + << QLatin1String("Class") + << QLatin1String("ClassRecurse") + << QLatin1String("class_t") + << QLatin1String("class_recurse_s") + << QLatin1String("class_recurse_t")); + + QTest::newRow("enclosing_template_class: nested class with enclosing template class") << _( "template<typename T>\n" "struct Enclosing\n" "{\n" @@ -1164,15 +998,13 @@ void CppToolsPlugin::test_completion_enclosing_template_class_data() "};\n" "\n" "Enclosing<int>::Nested c;" - "@\n"; - completions.append(QLatin1String("Nested")); - completions.append(QLatin1String("int_nested")); - QTest::newRow("case: nested class with enclosing template class") - << code << completions; - - completions.clear(); + "@\n" + ) << _("c.") << (QStringList() + << QLatin1String("Nested") + << QLatin1String("int_nested")); - code = "\n" + QTest::newRow("enclosing_template_class: nested template class with enclosing template " + "class") << _( "template<typename T>\n" "struct Enclosing\n" "{\n" @@ -1181,16 +1013,12 @@ void CppToolsPlugin::test_completion_enclosing_template_class_data() "};\n" "\n" "Enclosing<int>::Nested<int> c;" - "@\n"; - completions.append(QLatin1String("Nested")); - completions.append(QLatin1String("int_nested")); - QTest::newRow("case: nested template class with enclosing template class") - << code << completions; -} + "@\n" + ) << _("c.") << (QStringList() + << QLatin1String("Nested") + << QLatin1String("int_nested")); -void CppToolsPlugin::test_completion_instantiate_nested_class_when_enclosing_is_template() -{ - const QByteArray source = "\n" + QTest::newRow("instantiate_nested_class_when_enclosing_is_template") << _( "struct Foo \n" "{\n" " int foo_i;\n" @@ -1209,18 +1037,11 @@ void CppToolsPlugin::test_completion_instantiate_nested_class_when_enclosing_is_ "\n" "Enclosing<Foo> enclosing;\n" "@\n" - ; - CompletionTestCase test(source, "enclosing.nested.nested_t."); - - const QStringList completions = test.getCompletions(); - QCOMPARE(completions.size(), 2); - QVERIFY(completions.contains(QLatin1String("Foo"))); - QVERIFY(completions.contains(QLatin1String("foo_i"))); -} + ) << _("enclosing.nested.nested_t.") << (QStringList() + << QLatin1String("Foo") + << QLatin1String("foo_i")); -void CppToolsPlugin::test_completion_instantiate_nested_of_nested_class_when_enclosing_is_template() -{ - const QByteArray source = "\n" + QTest::newRow("instantiate_nested_of_nested_class_when_enclosing_is_template") << _( "struct Foo \n" "{\n" " int foo_i;\n" @@ -1243,18 +1064,11 @@ void CppToolsPlugin::test_completion_instantiate_nested_of_nested_class_when_enc "\n" "Enclosing<Foo> enclosing;\n" "@\n" - ; - CompletionTestCase test(source, "enclosing.nested.nestedNested.nestedNested_t."); - - const QStringList completions = test.getCompletions(); - QCOMPARE(completions.size(), 2); - QVERIFY(completions.contains(QLatin1String("Foo"))); - QVERIFY(completions.contains(QLatin1String("foo_i"))); -} + ) << _("enclosing.nested.nestedNested.nestedNested_t.") << (QStringList() + << QLatin1String("Foo") + << QLatin1String("foo_i")); -void CppToolsPlugin::test_completion_instantiate_template_with_default_argument_type() -{ - const QByteArray source = "\n" + QTest::newRow("instantiate_template_with_default_argument_type") << _( "struct Foo\n" "{\n" " int bar;\n" @@ -1268,18 +1082,11 @@ void CppToolsPlugin::test_completion_instantiate_template_with_default_argument_ "\n" "Template<> templateWithDefaultTypeOfArgument;\n" "@\n" - ; - CompletionTestCase test(source, "templateWithDefaultTypeOfArgument.t."); - - const QStringList completions = test.getCompletions(); - QCOMPARE(completions.size(), 2); - QVERIFY(completions.contains(QLatin1String("Foo"))); - QVERIFY(completions.contains(QLatin1String("bar"))); -} + ) << _("templateWithDefaultTypeOfArgument.t.") << (QStringList() + << QLatin1String("Foo") + << QLatin1String("bar")); -void CppToolsPlugin::test_completion_instantiate_template_with_default_argument_type_as_template() -{ - const QByteArray source = "\n" + QTest::newRow("instantiate_template_with_default_argument_type_as_template") << _( "struct Foo\n" "{\n" " int bar;\n" @@ -1298,222 +1105,35 @@ void CppToolsPlugin::test_completion_instantiate_template_with_default_argument_ "\n" "Template<Foo> templateWithDefaultTypeOfArgument;\n" "@\n" - ; - CompletionTestCase test(source, "templateWithDefaultTypeOfArgument.s.t."); - - const QStringList completions = test.getCompletions(); - QCOMPARE(completions.size(), 2); - QVERIFY(completions.contains(QLatin1String("Foo"))); - QVERIFY(completions.contains(QLatin1String("bar"))); -} - -void CppToolsPlugin::test_completion_member_access_operator_1() -{ - const QByteArray source = "\n" - "struct S { void t(); };\n" - "void f() { S *s;\n" - "@\n" - "}\n" - ; - CompletionTestCase test(source, "s."); - - bool replaceAccessOperator = false; - const QStringList completions = test.getCompletions(&replaceAccessOperator); - QCOMPARE(completions.size(), 2); - QVERIFY(completions.contains(QLatin1String("S"))); - QVERIFY(completions.contains(QLatin1String("t"))); - QVERIFY(replaceAccessOperator); -} - -void CppToolsPlugin::test_completion_typedef_of_type_and_decl_of_type_no_replace_access_operator() -{ - const QByteArray source = "\n" - "struct S { int m; };\n" - "typedef S SType;\n" - "SType p;\n" - "@\n" - "}\n" - ; - CompletionTestCase test(source, "p."); - - bool replaceAccessOperator = false; - const QStringList completions = test.getCompletions(&replaceAccessOperator); - QCOMPARE(completions.size(), 2); - QVERIFY(completions.contains(QLatin1String("S"))); - QVERIFY(completions.contains(QLatin1String("m"))); - QVERIFY(!replaceAccessOperator); -} - -void CppToolsPlugin::test_completion_typedef_of_pointer_and_decl_of_pointer_no_replace_access_operator() -{ - const QByteArray source = "\n" - "struct S { int m; };\n" - "typedef S *SType;\n" - "SType *p;\n" - "@\n" - "}\n" - ; - CompletionTestCase test(source, "p."); - - bool replaceAccessOperator = false; - const QStringList completions = test.getCompletions(&replaceAccessOperator); - QCOMPARE(completions.size(), 0); - QVERIFY(!replaceAccessOperator); -} - -void CppToolsPlugin::test_completion_typedef_of_type_and_decl_of_pointer_replace_access_operator() -{ - const QByteArray source = "\n" - "struct S { int m; };\n" - "typedef S SType;\n" - "SType *p;\n" - "@\n" - "}\n" - ; - CompletionTestCase test(source, "p."); + ) << _("templateWithDefaultTypeOfArgument.s.t.") << (QStringList() + << QLatin1String("Foo") + << QLatin1String("bar")); - bool replaceAccessOperator = false; - const QStringList completions = test.getCompletions(&replaceAccessOperator); - QCOMPARE(completions.size(), 2); - QVERIFY(completions.contains(QLatin1String("S"))); - QVERIFY(completions.contains(QLatin1String("m"))); - QVERIFY(replaceAccessOperator); -} - -void CppToolsPlugin::test_completion_typedef_of_pointer_and_decl_of_type_replace_access_operator() -{ - const QByteArray source = "\n" - "struct S { int m; };\n" - "typedef S* SPtr;\n" - "SPtr p;\n" - "@\n" - "}\n" - ; - CompletionTestCase test(source, "p."); - - bool replaceAccessOperator = false; - const QStringList completions = test.getCompletions(&replaceAccessOperator); - QCOMPARE(completions.size(), 2); - QVERIFY(completions.contains(QLatin1String("S"))); - QVERIFY(completions.contains(QLatin1String("m"))); - QVERIFY(replaceAccessOperator); -} - -void CppToolsPlugin::test_completion_predecl_typedef_of_type_and_decl_of_pointer_replace_access_operator() -{ - const QByteArray source = "\n" - "typedef struct S SType;\n" - "struct S { int m; };\n" - "SType *p;\n" - "@\n" - "}\n" - ; - CompletionTestCase test(source, "p."); - - bool replaceAccessOperator = false; - const QStringList completions = test.getCompletions(&replaceAccessOperator); - QCOMPARE(completions.size(), 2); - QVERIFY(completions.contains(QLatin1String("S"))); - QVERIFY(completions.contains(QLatin1String("m"))); - QVERIFY(replaceAccessOperator); -} - -void CppToolsPlugin::test_completion_predecl_typedef_of_type_and_decl_type_no_replace_access_operator() -{ - const QByteArray source = "\n" - "typedef struct S SType;\n" - "struct S { int m; };\n" - "SType p;\n" - "@\n" - "}\n" - ; - CompletionTestCase test(source, "p."); - - bool replaceAccessOperator = false; - const QStringList completions = test.getCompletions(&replaceAccessOperator); - QCOMPARE(completions.size(), 2); - QVERIFY(completions.contains(QLatin1String("S"))); - QVERIFY(completions.contains(QLatin1String("m"))); - QVERIFY(!replaceAccessOperator); -} - -void CppToolsPlugin::test_completion_predecl_typedef_of_pointer_and_decl_of_pointer_no_replace_access_operator() -{ - const QByteArray source = "\n" - "typedef struct S *SType;\n" - "struct S { int m; };\n" - "SType *p;\n" - "@\n" - "}\n" - ; - CompletionTestCase test(source, "p."); - - bool replaceAccessOperator = false; - const QStringList completions = test.getCompletions(&replaceAccessOperator); - QCOMPARE(completions.size(), 0); - QVERIFY(!replaceAccessOperator); -} - -void CppToolsPlugin::test_completion_predecl_typedef_of_pointer_and_decl_of_type_replace_access_operator() -{ - const QByteArray source = "\n" - "typedef struct S *SType;\n" - "struct S { int m; };\n" - "SType p;\n" - "@\n" - "}\n" - ; - CompletionTestCase test(source, "p."); - - bool replaceAccessOperator = false; - const QStringList completions = test.getCompletions(&replaceAccessOperator); - QCOMPARE(completions.size(), 2); - QVERIFY(completions.contains(QLatin1String("S"))); - QVERIFY(completions.contains(QLatin1String("m"))); - QVERIFY(replaceAccessOperator); -} - -void CppToolsPlugin::test_completion_typedef_of_pointer() -{ - const QByteArray source = "\n" + QTest::newRow("typedef_of_pointer") << _( "struct Foo { int bar; };\n" "typedef Foo *FooPtr;\n" "void main()\n" "{\n" " FooPtr ptr;\n" " @\n" - " // padding so we get the scope right\n" - "}"; - CompletionTestCase test(source, "ptr->"); - - const QStringList completions = test.getCompletions(); - QCOMPARE(completions.size(), 2); - QVERIFY(completions.contains(QLatin1String("Foo"))); - QVERIFY(completions.contains(QLatin1String("bar"))); -} + "}" + ) << _("ptr->") << (QStringList() + << QLatin1String("Foo") + << QLatin1String("bar")); -void CppToolsPlugin::test_completion_typedef_of_pointer_inside_function() -{ - const QByteArray source = "\n" + QTest::newRow("typedef_of_pointer_inside_function") << _( "struct Foo { int bar; };\n" "void f()\n" "{\n" " typedef Foo *FooPtr;\n" " FooPtr ptr;\n" " @\n" - " // padding so we get the scope right\n" - "}"; - CompletionTestCase test(source, "ptr->"); - - const QStringList completions = test.getCompletions(); - QCOMPARE(completions.size(), 2); - QVERIFY(completions.contains(QLatin1String("Foo"))); - QVERIFY(completions.contains(QLatin1String("bar"))); -} + "}" + ) << _("ptr->") << (QStringList() + << QLatin1String("Foo") + << QLatin1String("bar")); -void CppToolsPlugin::test_completion_typedef_is_inside_function_before_declaration_block() -{ - const QByteArray source = "\n" + QTest::newRow("typedef_is_inside_function_before_declaration_block") << _( "struct Foo { int bar; };\n" "void f()\n" "{\n" @@ -1521,21 +1141,13 @@ void CppToolsPlugin::test_completion_typedef_is_inside_function_before_declarati " if (true) {\n" " FooPtr ptr;\n" " @\n" - " // padding so we get the scope right\n" " }" "}" - ; - CompletionTestCase test(source, "ptr->"); - - const QStringList completions = test.getCompletions(); - QCOMPARE(completions.size(), 2); - QVERIFY(completions.contains(QLatin1String("Foo"))); - QVERIFY(completions.contains(QLatin1String("bar"))); -} + ) << _("ptr->") << (QStringList() + << QLatin1String("Foo") + << QLatin1String("bar")); -void CppToolsPlugin::test_completion_resolve_complex_typedef_with_template() -{ - const QByteArray source = "\n" + QTest::newRow("resolve_complex_typedef_with_template") << _( "template <typename T>\n" "struct Template2\n" "{\n" @@ -1555,21 +1167,13 @@ void CppToolsPlugin::test_completion_resolve_complex_typedef_with_template() "{\n" " Template2<Foo> template2;\n" " @\n" - " // padding so we get the scope right\n" "}\n" - ; - CompletionTestCase test(source, "template2.templateTypedef."); - - const QStringList completions = test.getCompletions(); - QCOMPARE(completions.size(), 3); - QVERIFY(completions.contains(QLatin1String("Foo"))); - QVERIFY(completions.contains(QLatin1String("bar"))); - QVERIFY(completions.contains(QLatin1String("Template1"))); -} + ) << _("template2.templateTypedef.") << (QStringList() + << QLatin1String("Foo") + << QLatin1String("bar") + << QLatin1String("Template1")); -void CppToolsPlugin::test_completion_template_specialization_with_pointer() -{ - const QByteArray source = "\n" + QTest::newRow("template_specialization_with_pointer") << _( "template <typename T>\n" "struct Template\n" "{\n" @@ -1582,18 +1186,11 @@ void CppToolsPlugin::test_completion_template_specialization_with_pointer() "};\n" "Template<int*> templ;\n" "@\n" - ; - CompletionTestCase test(source, "templ."); - - const QStringList completions = test.getCompletions(); - QCOMPARE(completions.size(), 2); - QVERIFY(completions.contains(QLatin1String("Template"))); - QVERIFY(completions.contains(QLatin1String("pointer"))); -} + ) << _("templ.") << (QStringList() + << QLatin1String("Template") + << QLatin1String("pointer")); -void CppToolsPlugin::test_completion_typedef_using_templates1() -{ - const QByteArray source = "\n" + QTest::newRow("typedef_using_templates1") << _( "namespace NS1\n" "{\n" "template<typename T>\n" @@ -1623,20 +1220,12 @@ void CppToolsPlugin::test_completion_typedef_using_templates1() "{\n" " NS2::NS2Struct<Foo> s;\n" " @\n" - " // padding so we get the scope right\n" "}\n" - ; - CompletionTestCase test(source, "s.p->"); - - const QStringList completions = test.getCompletions(); - QCOMPARE(completions.size(), 2); - QVERIFY(completions.contains(QLatin1String("Foo"))); - QVERIFY(completions.contains(QLatin1String("bar"))); -} + ) << _("s.p->") << (QStringList() + << QLatin1String("Foo") + << QLatin1String("bar")); -void CppToolsPlugin::test_completion_typedef_using_templates2() -{ - const QByteArray source = "\n" + QTest::newRow("typedef_using_templates2") << _( "namespace NS1\n" "{\n" "template<typename T>\n" @@ -1666,20 +1255,12 @@ void CppToolsPlugin::test_completion_typedef_using_templates2() "{\n" " NS2::NS2Struct<Foo>::pointer p;\n" " @\n" - " // padding so we get the scope right\n" "}\n" - ; - CompletionTestCase test(source, "p->"); - - const QStringList completions = test.getCompletions(); - QCOMPARE(completions.size(), 2); - QVERIFY(completions.contains(QLatin1String("Foo"))); - QVERIFY(completions.contains(QLatin1String("bar"))); -} + ) << _("p->") << (QStringList() + << QLatin1String("Foo") + << QLatin1String("bar")); -void CppToolsPlugin::test_completion_namespace_alias_with_many_namespace_declarations() -{ - const QByteArray source = + QTest::newRow("namespace_alias_with_many_namespace_declarations") << _( "namespace NS1\n" "{\n" "namespace NS2\n" @@ -1704,20 +1285,12 @@ void CppToolsPlugin::test_completion_namespace_alias_with_many_namespace_declara "int main()\n" "{\n" " @\n" - " // padding so we get the scope right\n" "}\n" - ; - CompletionTestCase test(source, "NS::"); - - const QStringList completions = test.getCompletions(); - QCOMPARE(completions.size(), 2); - QVERIFY(completions.contains(QLatin1String("Foo1"))); - QVERIFY(completions.contains(QLatin1String("Foo2"))); -} + ) << _("NS::") << (QStringList() + << QLatin1String("Foo1") + << QLatin1String("Foo2")); -void CppToolsPlugin::test_completion_QTCREATORBUG9098() -{ - const QByteArray source = + QTest::newRow("QTCREATORBUG9098") << _( "template <typename T>\n" "class B\n" "{\n" @@ -1732,32 +1305,13 @@ void CppToolsPlugin::test_completion_QTCREATORBUG9098() " void fun()\n" " {\n" " @\n" - " // padding so we get the scope right\n" " }\n" "};\n" - ; - CompletionTestCase test(source, "b."); - - const QStringList completions = test.getCompletions(); - QCOMPARE(completions.size(), 2); - QVERIFY(completions.contains(QLatin1String("c"))); - QVERIFY(completions.contains(QLatin1String("B"))); -} - -void CppToolsPlugin::test_completion_type_and_using_declaration() -{ - test_completion(); -} - -void CppToolsPlugin::test_completion_type_and_using_declaration_data() -{ - QTest::addColumn<QByteArray>("code"); - QTest::addColumn<QStringList>("expectedCompletions"); + ) << _("b.") << (QStringList() + << QLatin1String("c") + << QLatin1String("B")); - QByteArray code; - QStringList completions; - - code = "\n" + QTest::newRow("type_and_using_declaration: type and using declaration inside function") << _( "namespace NS\n" "{\n" "struct C { int m; };\n" @@ -1767,16 +1321,13 @@ void CppToolsPlugin::test_completion_type_and_using_declaration_data() " using NS::C;\n" " C c;\n" " @\n" - " // padding so we get the scope right\n" - "}\n"; - completions.append(QLatin1String("C")); - completions.append(QLatin1String("m")); - QTest::newRow("case: type and using declaration inside function") - << code << completions; - - completions.clear(); + "}\n" + ) << _("c.") << (QStringList() + << QLatin1String("C") + << QLatin1String("m")); - code = "\n" + QTest::newRow("type_and_using_declaration: type and using declaration in global " + "namespace") << _( "namespace NS\n" "{\n" "struct C { int m; };\n" @@ -1786,16 +1337,13 @@ void CppToolsPlugin::test_completion_type_and_using_declaration_data() "{\n" " C c;\n" " @\n" - " // padding so we get the scope right\n" - "}\n"; - completions.append(QLatin1String("C")); - completions.append(QLatin1String("m")); - QTest::newRow("case: type and using declaration in global namespace") - << code << completions; - - completions.clear(); + "}\n" + ) << _("c.") << (QStringList() + << QLatin1String("C") + << QLatin1String("m")); - code = "\n" + QTest::newRow("type_and_using_declaration: type in global namespace and using declaration in " + "NS namespace") << _( "struct C { int m; };\n" "namespace NS\n" "{\n" @@ -1804,17 +1352,14 @@ void CppToolsPlugin::test_completion_type_and_using_declaration_data() " {\n" " C c;\n" " @\n" - " // padding so we get the scope right\n" " }\n" - "}\n"; - completions.append(QLatin1String("C")); - completions.append(QLatin1String("m")); - QTest::newRow("case: type in global namespace and using declaration in NS namespace") - << code << completions; - - completions.clear(); + "}\n" + ) << _("c.") << (QStringList() + << QLatin1String("C") + << QLatin1String("m")); - code = "\n" + QTest::newRow("type_and_using_declaration: type in global namespace and using declaration " + "inside function in NS namespace") << _( "struct C { int m; };\n" "namespace NS\n" "{\n" @@ -1823,17 +1368,14 @@ void CppToolsPlugin::test_completion_type_and_using_declaration_data() " using ::C;\n" " C c;\n" " @\n" - " // padding so we get the scope right\n" " }\n" - "}\n"; - completions.append(QLatin1String("C")); - completions.append(QLatin1String("m")); - QTest::newRow("case: type in global namespace and using declaration inside function in NS namespace") - << code << completions; - - completions.clear(); + "}\n" + ) << _("c.") << (QStringList() + << QLatin1String("C") + << QLatin1String("m")); - code = "\n" + QTest::newRow("type_and_using_declaration: type inside namespace NS1 and using declaration in " + "function inside NS2 namespace") << _( "namespace NS1\n" "{\n" "struct C { int m; };\n" @@ -1845,17 +1387,14 @@ void CppToolsPlugin::test_completion_type_and_using_declaration_data() " using NS1::C;\n" " C c;\n" " @\n" - " // padding so we get the scope right\n" " }\n" - "}\n"; - completions.append(QLatin1String("C")); - completions.append(QLatin1String("m")); - QTest::newRow("case: type inside namespace NS1 and using declaration in function inside NS2 namespace") - << code << completions; - - completions.clear(); + "}\n" + ) << _("c.") << (QStringList() + << QLatin1String("C") + << QLatin1String("m")); - code = "\n" + QTest::newRow("type_and_using_declaration: type inside namespace NS1 and using declaration " + "inside NS2 namespace") << _( "namespace NS1\n" "{\n" "struct C { int m; };\n" @@ -1867,18 +1406,13 @@ void CppToolsPlugin::test_completion_type_and_using_declaration_data() " {\n" " C c;\n" " @\n" - " // padding so we get the scope right\n" " }\n" - "}\n"; - completions.append(QLatin1String("C")); - completions.append(QLatin1String("m")); - QTest::newRow("case: type inside namespace NS1 and using declaration inside NS2 namespace") - << code << completions; -} + "}\n" + ) << _("c.") << (QStringList() + << QLatin1String("C") + << QLatin1String("m")); -void CppToolsPlugin::test_completion_instantiate_template_with_anonymous_class() -{ - const QByteArray source = + QTest::newRow("instantiate_template_with_anonymous_class") << _( "template <typename T>\n" "struct S\n" "{\n" @@ -1888,39 +1422,23 @@ void CppToolsPlugin::test_completion_instantiate_template_with_anonymous_class() "{\n" " S<int> s;\n" " @\n" - " // padding so we get the scope right\n" "}\n" - ; - CompletionTestCase test(source, "s."); + ) << _("s.") << (QStringList() + << QLatin1String("S")); - const QStringList completions = test.getCompletions(); - QCOMPARE(completions.size(), 1); - QVERIFY(completions.contains(QLatin1String("S"))); -} - -void CppToolsPlugin::test_completion_instantiate_template_function() -{ - const QByteArray source = + QTest::newRow("instantiate_template_function") << _( "template <typename T>\n" "T* templateFunction() { return 0; }\n" "struct A { int a; };\n" "void foo()\n" "{\n" " @\n" - " // padding so we get the scope right\n" "}\n" - ; - CompletionTestCase test(source, "templateFunction<A>()->"); - - const QStringList completions = test.getCompletions(); - QCOMPARE(completions.size(), 2); - QVERIFY(completions.contains(QLatin1String("A"))); - QVERIFY(completions.contains(QLatin1String("a"))); -} + ) << _("templateFunction<A>()->") << (QStringList() + << QLatin1String("A") + << QLatin1String("a")); -void CppToolsPlugin::test_completion_crash_cloning_template_class_QTCREATORBUG9329() -{ - const QByteArray source = + QTest::newRow("crash_cloning_template_class_QTCREATORBUG9329") << _( "struct A {};\n" "template <typename T>\n" "struct Templ {};\n" @@ -1929,76 +1447,44 @@ void CppToolsPlugin::test_completion_crash_cloning_template_class_QTCREATORBUG93 " int f()\n" " {\n" " @\n" - " // padding so we get the scope right\n" " }\n" "};\n" - ; - CompletionTestCase test(source, "this->"); - - const QStringList completions = test.getCompletions(); - QCOMPARE(completions.size(), 4); - QVERIFY(completions.contains(QLatin1String("A"))); - QVERIFY(completions.contains(QLatin1String("B"))); - QVERIFY(completions.contains(QLatin1String("Templ"))); - QVERIFY(completions.contains(QLatin1String("f"))); -} + ) << _("this->") << (QStringList() + << QLatin1String("A") + << QLatin1String("B") + << QLatin1String("Templ") + << QLatin1String("f")); -void CppToolsPlugin::test_completion_recursive_auto_declarations1_QTCREATORBUG9503() -{ - const QByteArray source = + QTest::newRow("recursive_auto_declarations1_QTCREATORBUG9503") << _( "void f()\n" "{\n" " auto object2 = object1;\n" " auto object1 = object2;\n" " @;\n" - " // padding so we get the scope right\n" "}\n" - ; - CompletionTestCase test(source, "object1."); - - const QStringList completions = test.getCompletions(); - QCOMPARE(completions.size(), 0); -} + ) << _("object1.") << (QStringList()); -void CppToolsPlugin::test_completion_recursive_auto_declarations2_QTCREATORBUG9503() -{ - const QByteArray source = + QTest::newRow("recursive_auto_declarations2_QTCREATORBUG9503") << _( "void f()\n" "{\n" " auto object3 = object1;\n" " auto object2 = object3;\n" " auto object1 = object2;\n" " @;\n" - " // padding so we get the scope right\n" "}\n" - ; - CompletionTestCase test(source, "object1."); - - const QStringList completions = test.getCompletions(); - QCOMPARE(completions.size(), 0); -} + ) << _("object1.") << (QStringList()); -void CppToolsPlugin::test_completion_recursive_typedefs_declarations1() -{ - const QByteArray source = + QTest::newRow("recursive_typedefs_declarations1") << _( "void f()\n" "{\n" " typedef A B;\n" " typedef B A;\n" " A a;\n" " @;\n" - " // padding so we get the scope right\n" "}\n" - ; - CompletionTestCase test(source, "a."); + ) << _("a.") << (QStringList()); - const QStringList completions = test.getCompletions(); - QCOMPARE(completions.size(), 0); -} - -void CppToolsPlugin::test_completion_recursive_typedefs_declarations2() -{ - const QByteArray source = + QTest::newRow("recursive_typedefs_declarations2") << _( "void f()\n" "{\n" " typedef A C;\n" @@ -2006,36 +1492,20 @@ void CppToolsPlugin::test_completion_recursive_typedefs_declarations2() " typedef B A;\n" " A a;\n" " @;\n" - " // padding so we get the scope right\n" "}\n" - ; - CompletionTestCase test(source, "a."); - - const QStringList completions = test.getCompletions(); - QCOMPARE(completions.size(), 0); -} + ) << _("a.") << (QStringList()); -void CppToolsPlugin::test_completion_recursive_using_declarations1() -{ - const QByteArray source = + QTest::newRow("recursive_using_declarations1") << _( "void f()\n" "{\n" " using B = A;\n" " using A = B;\n" " A a;\n" " @;\n" - " // padding so we get the scope right\n" "}\n" - ; - CompletionTestCase test(source, "a."); - - const QStringList completions = test.getCompletions(); - QCOMPARE(completions.size(), 0); -} + ) << _("a.") << (QStringList()); -void CppToolsPlugin::test_completion_recursive_using_declarations2() -{ - const QByteArray source = + QTest::newRow("recursive_using_declarations2") << _( "void f()\n" "{\n" " using C = A;\n" @@ -2043,36 +1513,20 @@ void CppToolsPlugin::test_completion_recursive_using_declarations2() " using A = B;\n" " A a;\n" " @;\n" - " // padding so we get the scope right\n" "}\n" - ; - CompletionTestCase test(source, "a."); - - const QStringList completions = test.getCompletions(); - QCOMPARE(completions.size(), 0); -} + ) << _("a.") << (QStringList()); -void CppToolsPlugin::test_completion_recursive_using_typedef_declarations() -{ - const QByteArray source = + QTest::newRow("recursive_using_typedef_declarations") << _( "void f()\n" "{\n" " using B = A;\n" " typedef B A;\n" " A a;\n" " @;\n" - " // padding so we get the scope right\n" "}\n" - ; - CompletionTestCase test(source, "a."); + ) << _("a.") << (QStringList()); - const QStringList completions = test.getCompletions(); - QCOMPARE(completions.size(), 0); -} - -void CppToolsPlugin::test_completion_recursive_typedefs_in_templates1() -{ - const QByteArray source = + QTest::newRow("recursive_typedefs_in_templates1") << _( "template<typename From>\n" "struct simplify_type {\n" " typedef From SimpleType;\n" @@ -2093,16 +1547,9 @@ void CppToolsPlugin::test_completion_recursive_typedefs_in_templates1() "{\n" " @;\n" "}\n" - ; - CompletionTestCase test(source, "cast_retty<T1, T2>::ret_type."); - - const QStringList completions = test.getCompletions(); - QCOMPARE(completions.size(), 0); -} + ) << _("cast_retty<T1, T2>::ret_type.") << (QStringList()); -void CppToolsPlugin::test_completion_recursive_typedefs_in_templates2() -{ - const QByteArray source = + QTest::newRow("recursive_typedefs_in_templates2") << _( "template<class T>\n" "struct recursive {\n" " typedef typename recursive<T>::ret_type ret_type;\n" @@ -2112,110 +1559,36 @@ void CppToolsPlugin::test_completion_recursive_typedefs_in_templates2() "{\n" " @;\n" "}\n" - ; - CompletionTestCase test(source, "recursive<T1>::ret_type.foo"); - - const QStringList completions = test.getCompletions(); - QCOMPARE(completions.size(), 0); -} + ) << _("recursive<T1>::ret_type.foo") << (QStringList()); -void CppToolsPlugin::test_completion_prefix_first_QTCREATORBUG_8737() -{ - const QByteArray source = - "void f()\n" - "{\n" - " int a_b_c, a_c, a_c_a;\n" - " @;\n" - " // padding so we get the scope right\n" - "}\n" - ; - CompletionTestCase test(source, "a_c"); - - QStringList completions = test.getCompletions(); - - QVERIFY(completions.size() >= 2); - QCOMPARE(completions.at(0), QLatin1String("a_c")); - QCOMPARE(completions.at(1), QLatin1String("a_c_a")); - QVERIFY(completions.contains(QLatin1String("a_b_c"))); -} - -void CppToolsPlugin::test_completion_prefix_first_QTCREATORBUG_9236() -{ - const QByteArray source = - "class r_etclass\n" - "{\n" - "public:\n" - " int raEmTmber;\n" - " void r_e_t(int re_t)\n" - " {\n" - " int r_et;\n" - " int rETUCASE;\n" - " @\n" - " // padding so we get the scope right\n" - " }\n" - "};\n" - ; - CompletionTestCase test(source, "ret"); - - QStringList completions = test.getCompletions(); - - QVERIFY(completions.size() >= 2); - QCOMPARE(completions.at(0), QLatin1String("return")); - QCOMPARE(completions.at(1), QLatin1String("rETUCASE")); - QVERIFY(completions.contains(QLatin1String("r_etclass"))); - QVERIFY(completions.contains(QLatin1String("raEmTmber"))); - QVERIFY(completions.contains(QLatin1String("r_e_t"))); - QVERIFY(completions.contains(QLatin1String("re_t"))); - QVERIFY(completions.contains(QLatin1String("r_et"))); -} - -void CppToolsPlugin::test_completion_class_declaration_inside_function_or_block_QTCREATORBUG3620() -{ - test_completion(); -} - -void CppToolsPlugin::test_completion_class_declaration_inside_function_or_block_QTCREATORBUG3620_data() -{ - QTest::addColumn<QByteArray>("code"); - QTest::addColumn<QStringList>("expectedCompletions"); - - QByteArray code; - QStringList completions; - - code = "\n" + QTest::newRow("class_declaration_inside_function_or_block_QTCREATORBUG3620: " + "class definition inside function") << _( "void foo()\n" "{\n" " struct C { int m; };\n" " C c;\n" " @\n" - " // padding so we get the scope right\n" - "}\n"; - completions.append(QLatin1String("C")); - completions.append(QLatin1String("m")); - QTest::newRow("case: class definition inside function") - << code << completions; - - completions.clear(); + "}\n" + ) << _("c.") << (QStringList() + << QLatin1String("C") + << QLatin1String("m")); - code = "\n" + QTest::newRow("class_declaration_inside_function_or_block_QTCREATORBUG3620: " + "class definition inside block inside function") << _( "void foo()\n" "{\n" " {\n" " struct C { int m; };\n" " C c;\n" " @\n" - " // padding so we get the scope right\n" " }\n" "}\n" - ; - completions.append(QLatin1String("C")); - completions.append(QLatin1String("m")); - QTest::newRow("case: class definition inside block inside function") - << code << completions; + ) << _("c.") << (QStringList() + << QLatin1String("C") + << QLatin1String("m")); - completions.clear(); - - code = "\n" + QTest::newRow("class_declaration_inside_function_or_block_QTCREATORBUG3620: " + "class definition with the same name inside different block inside function") << _( "void foo()\n" "{\n" " {\n" @@ -2225,32 +1598,14 @@ void CppToolsPlugin::test_completion_class_declaration_inside_function_or_block_ " struct C { int m2; };\n" " C c;\n" " @\n" - " // padding so we get the scope right\n" " }\n" "}\n" - ; - completions.append(QLatin1String("C")); - completions.append(QLatin1String("m2")); - QTest::newRow("case: class definition with the same name inside different block inside function") - << code << completions; + ) << _("c.") << (QStringList() + << QLatin1String("C") + << QLatin1String("m2")); - completions.clear(); -} - -void CppToolsPlugin::test_completion_namespace_alias_inside_function_or_block_QTCREATORBUG166() -{ - test_completion(); -} - -void CppToolsPlugin::test_completion_namespace_alias_inside_function_or_block_QTCREATORBUG166_data() -{ - QTest::addColumn<QByteArray>("code"); - QTest::addColumn<QStringList>("expectedCompletions"); - - QByteArray code; - QStringList completions; - - code = "\n" + QTest::newRow("namespace_alias_inside_function_or_block_QTCREATORBUG166: " + "namespace alias inside function") << _( "namespace NS1\n" "{\n" "namespace NS2\n" @@ -2265,17 +1620,13 @@ void CppToolsPlugin::test_completion_namespace_alias_inside_function_or_block_QT " namespace NS = NS1::NS2;\n" " NS::C c;\n" " @\n" - " // padding so we get the scope right\n" "}\n" - ; - completions.append(QLatin1String("C")); - completions.append(QLatin1String("m")); - QTest::newRow("case: namespace alias inside function") - << code << completions; - - completions.clear(); + ) << _("c.") << (QStringList() + << QLatin1String("C") + << QLatin1String("m")); - code = "\n" + QTest::newRow("namespace_alias_inside_function_or_block_QTCREATORBUG166: " + "namespace alias inside block inside function") << _( "namespace NS1\n" "{\n" "namespace NS2\n" @@ -2291,21 +1642,13 @@ void CppToolsPlugin::test_completion_namespace_alias_inside_function_or_block_QT " namespace NS = NS1::NS2;\n" " NS::C c;\n" " @\n" - " // padding so we get the scope right\n" " }\n" "}\n" - ; - completions.append(QLatin1String("C")); - completions.append(QLatin1String("m")); - QTest::newRow("case: namespace alias inside block inside function") - << code << completions; - - completions.clear(); -} + ) << _("c.") << (QStringList() + << QLatin1String("C") + << QLatin1String("m")); -void CppToolsPlugin::test_completion_class_declaration_inside_function_or_block_QTCREATORBUG3620_static_member() -{ - const QByteArray source = + QTest::newRow("class_declaration_inside_function_or_block_QTCREATORBUG3620_static_member") << _( "void foo()\n" "{\n" " {\n" @@ -2314,162 +1657,164 @@ void CppToolsPlugin::test_completion_class_declaration_inside_function_or_block_ " {\n" " struct C { static void staticFun2(); int m2; };\n" " @\n" - " // padding so we get the scope right\n" " }\n" "}\n" - ; - CompletionTestCase test(source, "C::"); + ) << _("C::") << (QStringList() + << QLatin1String("C") + << QLatin1String("staticFun2") + << QLatin1String("m2")); - const QStringList completions = test.getCompletions(); - - QCOMPARE(completions.size(), 3); - QVERIFY(completions.contains(QLatin1String("C"))); - QVERIFY(completions.contains(QLatin1String("staticFun2"))); - QVERIFY(completions.contains(QLatin1String("m2"))); -} - -void CppToolsPlugin::test_completion_enum_inside_block_inside_function_QTCREATORBUG5456() -{ - const QByteArray source = + QTest::newRow("enum_inside_block_inside_function_cxx11_QTCREATORBUG5456") << _( "void foo()\n" "{\n" " {\n" " enum E { e1, e2, e3 };\n" " @\n" - " // padding so we get the scope right\n" " }\n" "}\n" - ; - CompletionTestCase test(source, "E::"); + ) << _("E::") << (QStringList() + << QLatin1String("E") + << QLatin1String("e1") + << QLatin1String("e2") + << QLatin1String("e3")); - const QStringList completions = test.getCompletions(); - - QCOMPARE(completions.size(), 4); - QVERIFY(completions.contains(QLatin1String("E"))); - QVERIFY(completions.contains(QLatin1String("e1"))); - QVERIFY(completions.contains(QLatin1String("e2"))); - QVERIFY(completions.contains(QLatin1String("e3"))); -} - -void CppToolsPlugin::test_completion_enum_inside_function_QTCREATORBUG5456() -{ - const QByteArray source = + QTest::newRow("enum_inside_function_cxx11_QTCREATORBUG5456") << _( "void foo()\n" "{\n" " enum E { e1, e2, e3 };\n" " @\n" - " // padding so we get the scope right\n" "}\n" - ; - CompletionTestCase test(source, "E::"); + ) << _("E::") << (QStringList() + << QLatin1String("E") + << QLatin1String("e1") + << QLatin1String("e2") + << QLatin1String("e3")); - const QStringList completions = test.getCompletions(); + QTest::newRow("enum_inside_class") << _( + "struct Foo\n" + "{\n" + " enum E { val1, val2, val3 };\n" + " @\n" + "};\n" + "@\n" + ) << _("Foo::v") << (QStringList() + << QLatin1String("val1") + << QLatin1String("val2") + << QLatin1String("val3")); - QCOMPARE(completions.size(), 4); - QVERIFY(completions.contains(QLatin1String("E"))); - QVERIFY(completions.contains(QLatin1String("e1"))); - QVERIFY(completions.contains(QLatin1String("e2"))); - QVERIFY(completions.contains(QLatin1String("e3"))); -} + QTest::newRow("enum_inside_class_cxx11") << _( + "struct Foo\n" + "{\n" + " enum E { val1, val2, val3 };\n" + " @\n" + "};\n" + "@\n" + ) << _("Foo::E::") << (QStringList() + << QLatin1String("E") + << QLatin1String("val1") + << QLatin1String("val2") + << QLatin1String("val3")); -void CppToolsPlugin::test_completion_lambdaCalls_1() -{ - const QByteArray source = - "struct S { int bar; };\n" - "void foo()\n" + QTest::newRow("anon_enum_inside_class") << _( + "struct Foo\n" "{\n" + " enum { val1, val2, val3 };\n" " @\n" - " // padding so we get the scope right\n" - "}\n" - ; - CompletionTestCase test(source, "[](){ return new S; } ()->"); + "};\n" + "@\n" + ) << _("Foo::v") << (QStringList() + << QLatin1String("val1") + << QLatin1String("val2") + << QLatin1String("val3")); - const QStringList completions = test.getCompletions(); + QTest::newRow("enum_inside_namespace") << _( + "namespace Ns\n" + "{\n" + " enum E { val1, val2, val3 };\n" + " @\n" + "};\n" + "@\n" + ) << _("Ns::v") << (QStringList() + << QLatin1String("val1") + << QLatin1String("val2") + << QLatin1String("val3")); - QCOMPARE(completions.size(), 2); - QVERIFY(completions.contains(QLatin1String("S"))); - QVERIFY(completions.contains(QLatin1String("bar"))); -} + QTest::newRow("enum_inside_namespace_cxx11") << _( + "namespace Ns\n" + "{\n" + " enum E { val1, val2, val3 };\n" + " @\n" + "};\n" + "@\n" + ) << _("Ns::E::") << (QStringList() + << QLatin1String("E") + << QLatin1String("val1") + << QLatin1String("val2") + << QLatin1String("val3")); -void CppToolsPlugin::test_completion_lambdaCalls_2() -{ - const QByteArray source = + QTest::newRow("anon_enum_inside_namespace") << _( + "namespace Ns\n" + "{\n" + " enum { val1, val2, val3 };\n" + " @\n" + "};\n" + "@\n" + ) << _("Ns::v") << (QStringList() + << QLatin1String("val1") + << QLatin1String("val2") + << QLatin1String("val3")); + + QTest::newRow("lambdaCalls_1") << _( "struct S { int bar; };\n" "void foo()\n" "{\n" " @\n" - " // padding so we get the scope right\n" "}\n" - ; - CompletionTestCase test(source, "[] { return new S; } ()->"); - - const QStringList completions = test.getCompletions(); - - QCOMPARE(completions.size(), 2); - QVERIFY(completions.contains(QLatin1String("S"))); - QVERIFY(completions.contains(QLatin1String("bar"))); -} + ) << _("[](){ return new S; } ()->") << (QStringList() + << QLatin1String("S") + << QLatin1String("bar")); -void CppToolsPlugin::test_completion_lambdaCalls_3() -{ - const QByteArray source = + QTest::newRow("lambdaCalls_2") << _( "struct S { int bar; };\n" "void foo()\n" "{\n" " @\n" - " // padding so we get the scope right\n" "}\n" - ; - CompletionTestCase test(source, "[]() ->S* { return new S; } ()->"); - - const QStringList completions = test.getCompletions(); + ) << _("[] { return new S; } ()->") << (QStringList() + << QLatin1String("S") + << QLatin1String("bar")); - QCOMPARE(completions.size(), 2); - QVERIFY(completions.contains(QLatin1String("S"))); - QVERIFY(completions.contains(QLatin1String("bar"))); -} - -void CppToolsPlugin::test_completion_lambdaCalls_4() -{ - const QByteArray source = + QTest::newRow("lambdaCalls_3") << _( "struct S { int bar; };\n" "void foo()\n" "{\n" " @\n" - " // padding so we get the scope right\n" "}\n" - ; - CompletionTestCase test(source, "[]() throw() { return new S; } ()->"); - - const QStringList completions = test.getCompletions(); - - QCOMPARE(completions.size(), 2); - QVERIFY(completions.contains(QLatin1String("S"))); - QVERIFY(completions.contains(QLatin1String("bar"))); -} + ) << _("[]() ->S* { return new S; } ()->") << (QStringList() + << QLatin1String("S") + << QLatin1String("bar")); -void CppToolsPlugin::test_completion_lambdaCalls_5() -{ - const QByteArray source = + QTest::newRow("lambdaCalls_4") << _( "struct S { int bar; };\n" "void foo()\n" "{\n" " @\n" - " // padding so we get the scope right\n" "}\n" - ; - CompletionTestCase test(source, "[]() throw()->S* { return new S; } ()->"); - - const QStringList completions = test.getCompletions(); + ) << _("[]() throw() { return new S; } ()->") << (QStringList() + << QLatin1String("S") + << QLatin1String("bar")); - QCOMPARE(completions.size(), 2); - QVERIFY(completions.contains(QLatin1String("S"))); - QVERIFY(completions.contains(QLatin1String("bar"))); -} + QTest::newRow("lambdaCalls_5") << _( + "struct S { int bar; };\n" + "void foo()\n" + "{\n" + " @\n" + "}\n" + ) << _("[]() throw()->S* { return new S; } ()->") << (QStringList() + << QLatin1String("S") + << QLatin1String("bar")); -void CppToolsPlugin::test_completion_local_type_and_member_1() -{ - const QByteArray source = + QTest::newRow("local_type_and_member_1") << _( "struct OtherType { int otherTypeMember; };\n" "void foo()\n" "{\n" @@ -2480,21 +1825,12 @@ void CppToolsPlugin::test_completion_local_type_and_member_1() " };\n" " LocalType lt;\n" " @\n" - " // padding so we get the scope right\n" "}\n" - ; - CompletionTestCase test(source, "lt.ot."); - - const QStringList completions = test.getCompletions(); - - QCOMPARE(completions.size(), 2); - QVERIFY(completions.contains(QLatin1String("OtherType"))); - QVERIFY(completions.contains(QLatin1String("otherTypeMember"))); -} + ) << _("lt.ot.") << (QStringList() + << QLatin1String("OtherType") + << QLatin1String("otherTypeMember")); -void CppToolsPlugin::test_completion_local_type_and_member_2() -{ - const QByteArray source = + QTest::newRow("local_type_and_member_2") << _( "void foo()\n" "{\n" " struct OtherType { int otherTypeMember; };\n" @@ -2505,21 +1841,12 @@ void CppToolsPlugin::test_completion_local_type_and_member_2() " };\n" " LocalType lt;\n" " @\n" - " // padding so we get the scope right\n" "}\n" - ; - CompletionTestCase test(source, "lt.ot."); - - const QStringList completions = test.getCompletions(); + ) << _("lt.ot.") << (QStringList() + << QLatin1String("OtherType") + << QLatin1String("otherTypeMember")); - QCOMPARE(completions.size(), 2); - QVERIFY(completions.contains(QLatin1String("OtherType"))); - QVERIFY(completions.contains(QLatin1String("otherTypeMember"))); -} - -void CppToolsPlugin::test_completion_local_type_and_member_3() -{ - const QByteArray source = + QTest::newRow("local_type_and_member_3") << _( "void foo()\n" "{\n" " struct OtherType { int otherTypeMember; };\n" @@ -2531,22 +1858,13 @@ void CppToolsPlugin::test_completion_local_type_and_member_3() " };\n" " LocalType lt;\n" " @\n" - " // padding so we get the scope right\n" " }\n" "}\n" - ; - CompletionTestCase test(source, "lt.ot."); - - const QStringList completions = test.getCompletions(); - - QCOMPARE(completions.size(), 2); - QVERIFY(completions.contains(QLatin1String("OtherType"))); - QVERIFY(completions.contains(QLatin1String("otherTypeMember"))); -} + ) << _("lt.ot.") << (QStringList() + << QLatin1String("OtherType") + << QLatin1String("otherTypeMember")); -void CppToolsPlugin::test_completion_local_type_and_member_4() -{ - const QByteArray source = + QTest::newRow("local_type_and_member_4") << _( "namespace NS {struct OtherType { int otherTypeMember; };}\n" "void foo()\n" "{\n" @@ -2557,21 +1875,12 @@ void CppToolsPlugin::test_completion_local_type_and_member_4() " };\n" " LocalType lt;\n" " @\n" - " // padding so we get the scope right\n" "}\n" - ; - CompletionTestCase test(source, "lt.ot."); - - const QStringList completions = test.getCompletions(); + ) << _("lt.ot.") << (QStringList() + << QLatin1String("OtherType") + << QLatin1String("otherTypeMember")); - QCOMPARE(completions.size(), 2); - QVERIFY(completions.contains(QLatin1String("OtherType"))); - QVERIFY(completions.contains(QLatin1String("otherTypeMember"))); -} - -void CppToolsPlugin::test_completion_local_type_and_member_5() -{ - const QByteArray source = + QTest::newRow("local_type_and_member_5") << _( "namespace NS {struct OtherType { int otherTypeMember; };}\n" "void foo()\n" "{\n" @@ -2583,21 +1892,12 @@ void CppToolsPlugin::test_completion_local_type_and_member_5() " };\n" " LocalType lt;\n" " @\n" - " // padding so we get the scope right\n" "}\n" - ; - CompletionTestCase test(source, "lt.ot."); - - const QStringList completions = test.getCompletions(); - - QCOMPARE(completions.size(), 2); - QVERIFY(completions.contains(QLatin1String("OtherType"))); - QVERIFY(completions.contains(QLatin1String("otherTypeMember"))); -} + ) << _("lt.ot.") << (QStringList() + << QLatin1String("OtherType") + << QLatin1String("otherTypeMember")); -void CppToolsPlugin::test_completion_local_type_and_member_6() -{ - const QByteArray source = + QTest::newRow("local_type_and_member_6") << _( "namespace NS {struct OtherType { int otherTypeMember; };}\n" "void foo()\n" "{\n" @@ -2609,21 +1909,12 @@ void CppToolsPlugin::test_completion_local_type_and_member_6() " };\n" " LocalType lt;\n" " @\n" - " // padding so we get the scope right\n" "}\n" - ; - CompletionTestCase test(source, "lt.ot."); + ) << _("lt.ot.") << (QStringList() + << QLatin1String("OtherType") + << QLatin1String("otherTypeMember")); - const QStringList completions = test.getCompletions(); - - QCOMPARE(completions.size(), 2); - QVERIFY(completions.contains(QLatin1String("OtherType"))); - QVERIFY(completions.contains(QLatin1String("otherTypeMember"))); -} - -void CppToolsPlugin::test_completion_template_parameter_defined_inside_scope_of_declaration_QTCREATORBUG9169_1() -{ - const QByteArray source = + QTest::newRow("template_parameter_defined_inside_scope_of_declaration_QTCREATORBUG9169_1") << _( "struct A\n" "{\n" " void foo();\n" @@ -2649,21 +1940,12 @@ void CppToolsPlugin::test_completion_template_parameter_defined_inside_scope_of_ "{\n" " Template<B> templ;\n" " @\n" - " // padding so we get the scope right\n" "}\n" - ; - CompletionTestCase test(source, "templ.get()->"); + ) << _("templ.get()->") << (QStringList() + << QLatin1String("B") + << QLatin1String("b")); - const QStringList completions = test.getCompletions(); - - QCOMPARE(completions.size(), 2); - QVERIFY(completions.contains(QLatin1String("B"))); - QVERIFY(completions.contains(QLatin1String("b"))); -} - -void CppToolsPlugin::test_completion_template_parameter_defined_inside_scope_of_declaration_QTCREATORBUG9169_2() -{ - const QByteArray source = + QTest::newRow("template_parameter_defined_inside_scope_of_declaration_QTCREATORBUG9169_2") << _( "struct A\n" "{\n" " void foo();\n" @@ -2689,21 +1971,12 @@ void CppToolsPlugin::test_completion_template_parameter_defined_inside_scope_of_ "{\n" " Template<B> templ;\n" " @\n" - " // padding so we get the scope right\n" "}\n" - ; - CompletionTestCase test(source, "templ.t."); + ) << _("templ.t.") << (QStringList() + << QLatin1String("B") + << QLatin1String("b")); - const QStringList completions = test.getCompletions(); - - QCOMPARE(completions.size(), 2); - QVERIFY(completions.contains(QLatin1String("B"))); - QVERIFY(completions.contains(QLatin1String("b"))); -} - -void CppToolsPlugin::test_completion_template_parameter_defined_inside_scope_of_declaration_QTCREATORBUG8852_1() -{ - const QByteArray source = + QTest::newRow("template_parameter_defined_inside_scope_of_declaration_QTCREATORBUG8852_1") << _( "template <typename T>\n" "struct QList\n" "{\n" @@ -2716,22 +1989,13 @@ void CppToolsPlugin::test_completion_template_parameter_defined_inside_scope_of_ " {\n" " QList<Foo> list;\n" " @\n" - " // padding so we get the scope right\n" " }\n" "}\n" - ; - CompletionTestCase test(source, "list.at(0)."); + ) << _("list.at(0).") << (QStringList() + << QLatin1String("Foo") + << QLatin1String("bar")); - const QStringList completions = test.getCompletions(); - - QCOMPARE(completions.size(), 2); - QVERIFY(completions.contains(QLatin1String("Foo"))); - QVERIFY(completions.contains(QLatin1String("bar"))); -} - -void CppToolsPlugin::test_completion_template_parameter_defined_inside_scope_of_declaration_QTCREATORBUG8852_2() -{ - const QByteArray source = + QTest::newRow("template_parameter_defined_inside_scope_of_declaration_QTCREATORBUG8852_2") << _( "template <typename T>\n" "struct QList\n" "{\n" @@ -2746,23 +2010,14 @@ void CppToolsPlugin::test_completion_template_parameter_defined_inside_scope_of_ " {\n" " QList<Foo> list;\n" " @\n" - " // padding so we get the scope right\n" " }\n" " }\n" "}\n" - ; - CompletionTestCase test(source, "list.at(0)."); - - const QStringList completions = test.getCompletions(); - - QCOMPARE(completions.size(), 2); - QVERIFY(completions.contains(QLatin1String("Foo"))); - QVERIFY(completions.contains(QLatin1String("bar"))); -} + ) << _("list.at(0).") << (QStringList() + << QLatin1String("Foo") + << QLatin1String("bar")); -void CppToolsPlugin::test_completion_template_parameter_defined_inside_scope_of_declaration_QTCREATORBUG8852_3() -{ - const QByteArray source = + QTest::newRow("template_parameter_defined_inside_scope_of_declaration_QTCREATORBUG8852_3") << _( "template <typename T>\n" "struct QList\n" "{\n" @@ -2777,72 +2032,49 @@ void CppToolsPlugin::test_completion_template_parameter_defined_inside_scope_of_ " using namespace ns;\n" " QList<Foo> list;\n" " @\n" - " // padding so we get the scope right\n" "}\n" - ; - CompletionTestCase test(source, "list.at(0)."); + ) << _("list.at(0).") << (QStringList() + << QLatin1String("Foo") + << QLatin1String("bar")); - const QStringList completions = test.getCompletions(); - - QCOMPARE(completions.size(), 2); - QVERIFY(completions.contains(QLatin1String("Foo"))); - QVERIFY(completions.contains(QLatin1String("bar"))); -} - -void CppToolsPlugin::test_completion_signals_hide_QPrivateSignal() -{ - const QByteArray source = - "#define SIGNAL(a) #a\n" - "#define SLOT(a) #a\n" - "#define signals public\n" - "#define Q_OBJECT struct QPrivateSignal {};\n" - "\n" - "class QObject\n" - "{\n" - "public:\n" - " void connect(QObject *, char *, QObject *, char *);\n" - "};\n" - "\n" - "class Timer : public QObject\n" - "{\n" - " Q_OBJECT\n" - "signals:\n" - " void timeout(QPrivateSignal);\n" - "};\n" - "\n" - "void client()\n" - "{\n" - " Timer *timer = new Timer;\n" - " connect(timer, SIGNAL(@\n" - "}\n"; - CompletionTestCase test(source); - - const QStringList completions = test.getCompletions(); - QCOMPARE(completions.size(), 1); - QVERIFY(completions.contains(QLatin1String("timeout()"))); -} + QTest::newRow("signals_hide_QPrivateSignal") << _( + "#define SIGNAL(a) #a\n" + "#define SLOT(a) #a\n" + "#define signals public\n" + "#define Q_OBJECT struct QPrivateSignal {};\n" + "\n" + "class QObject\n" + "{\n" + "public:\n" + " void connect(QObject *, char *, QObject *, char *);\n" + "};\n" + "\n" + "class Timer : public QObject\n" + "{\n" + " Q_OBJECT\n" + "signals:\n" + " void timeout(QPrivateSignal);\n" + "};\n" + "\n" + "void client()\n" + "{\n" + " Timer *timer = new Timer;\n" + " connect(timer, SIGNAL(@\n" + "}\n" + ) << _() << (QStringList() + << QLatin1String("timeout()")); -void CppToolsPlugin::test_completion_member_of_class_accessed_by_using_QTCREATORBUG9037_1() -{ - const QByteArray source = + QTest::newRow("member_of_class_accessed_by_using_QTCREATORBUG9037_1") << _( "namespace NS { struct S { int member; void fun(); }; }\n" "using NS::S;\n" "void S::fun()\n" "{\n" " @\n" - " // padding so we get the scope right\n" "}\n" - ; - CompletionTestCase test(source, "mem"); - - QStringList completions = test.getCompletions(); - QCOMPARE(completions.size(), 1); - QVERIFY(completions.contains(QLatin1String("member"))); -} + ) << _("mem") << (QStringList() + << QLatin1String("member")); -void CppToolsPlugin::test_completion_member_of_class_accessed_by_using_QTCREATORBUG9037_2() -{ - const QByteArray source = + QTest::newRow("member_of_class_accessed_by_using_QTCREATORBUG9037_2") << _( "namespace NS \n" "{\n" " namespace Internal\n" @@ -2855,12 +2087,129 @@ void CppToolsPlugin::test_completion_member_of_class_accessed_by_using_QTCREATOR "void S::fun()\n" "{\n" " @\n" - " // padding so we get the scope right\n" "}\n" - ; - CompletionTestCase test(source, "mem"); + ) << _("mem") << (QStringList() + << QLatin1String("member")); +} - QStringList completions = test.getCompletions(); - QCOMPARE(completions.size(), 1); - QVERIFY(completions.contains(QLatin1String("member"))); +void CppToolsPlugin::test_completion_member_access_operator() +{ + QFETCH(QByteArray, code); + QFETCH(QByteArray, prefix); + QFETCH(QStringList, expectedCompletions); + QFETCH(bool, expectedReplaceAccessOperator); + + CompletionTestCase test(code, prefix); + QVERIFY(test.succeededSoFar()); + + bool replaceAccessOperator = false; + QStringList completions = test.getCompletions(&replaceAccessOperator); + + completions.sort(); + expectedCompletions.sort(); + + QCOMPARE(completions, expectedCompletions); + QCOMPARE(replaceAccessOperator, expectedReplaceAccessOperator); +} + +void CppToolsPlugin::test_completion_member_access_operator_data() +{ + QTest::addColumn<QByteArray>("code"); + QTest::addColumn<QByteArray>("prefix"); + QTest::addColumn<QStringList>("expectedCompletions"); + QTest::addColumn<bool>("expectedReplaceAccessOperator"); + + QTest::newRow("member_access_operator") << _( + "struct S { void t(); };\n" + "void f() { S *s;\n" + "@\n" + "}\n" + ) << _("s.") << (QStringList() + << QLatin1String("S") + << QLatin1String("t")) + << true; + + QTest::newRow("typedef_of_type_and_decl_of_type_no_replace_access_operator") << _( + "struct S { int m; };\n" + "typedef S SType;\n" + "SType p;\n" + "@\n" + "}\n" + ) << _("p.") << (QStringList() + << QLatin1String("S") + << QLatin1String("m")) + << false; + + QTest::newRow("typedef_of_pointer_and_decl_of_pointer_no_replace_access_operator") << _( + "struct S { int m; };\n" + "typedef S *SType;\n" + "SType *p;\n" + "@\n" + "}\n" + ) << _("p.") << (QStringList()) + << false; + + QTest::newRow("typedef_of_type_and_decl_of_pointer_replace_access_operator") << _( + "struct S { int m; };\n" + "typedef S SType;\n" + "SType *p;\n" + "@\n" + "}\n" + ) << _("p.") << (QStringList() + << QLatin1String("S") + << QLatin1String("m")) + << true; + + QTest::newRow("typedef_of_pointer_and_decl_of_type_replace_access_operator") << _( + "struct S { int m; };\n" + "typedef S* SPtr;\n" + "SPtr p;\n" + "@\n" + "}\n" + ) << _("p.") << (QStringList() + << QLatin1String("S") + << QLatin1String("m")) + << true; + + QTest::newRow("predecl_typedef_of_type_and_decl_of_pointer_replace_access_operator") << _( + "typedef struct S SType;\n" + "struct S { int m; };\n" + "SType *p;\n" + "@\n" + "}\n" + ) << _("p.") << (QStringList() + << QLatin1String("S") + << QLatin1String("m")) + << true; + + QTest::newRow("predecl_typedef_of_type_and_decl_type_no_replace_access_operator") << _( + "typedef struct S SType;\n" + "struct S { int m; };\n" + "SType p;\n" + "@\n" + "}\n" + ) << _("p.") << (QStringList() + << QLatin1String("S") + << QLatin1String("m")) + << false; + + QTest::newRow("predecl_typedef_of_pointer_and_decl_of_pointer_no_replace_access_operator") << _( + "typedef struct S *SType;\n" + "struct S { int m; };\n" + "SType *p;\n" + "@\n" + "}\n" + ) << _("p.") << (QStringList()) + << false; + + QTest::newRow("predecl_typedef_of_pointer_and_decl_of_type_replace_access_operator") << _( + "typedef struct S *SType;\n" + "struct S { int m; };\n" + "SType p;\n" + "@\n" + "}\n" + ) << _("p.") << (QStringList() + << QLatin1String("S") + << QLatin1String("m")) + << true; } diff --git a/src/plugins/cpptools/cppcompletionassist.cpp b/src/plugins/cpptools/cppcompletionassist.cpp index 2c8878a24a..f5c58bee34 100644 --- a/src/plugins/cpptools/cppcompletionassist.cpp +++ b/src/plugins/cpptools/cppcompletionassist.cpp @@ -1362,19 +1362,14 @@ void CppCompletionAssistProcessor::globalCompletion(CPlusPlus::Scope *currentSco for (Scope *scope = currentScope; scope; scope = scope->enclosingScope()) { if (scope->isBlock()) { - for (unsigned i = 0; i < scope->memberCount(); ++i) { + for (unsigned i = 0; i < scope->memberCount(); ++i) addCompletionItem(scope->memberAt(i), FunctionLocalsOrder); - } - } else if (scope->isFunction()) { - Function *fun = scope->asFunction(); - for (unsigned i = 0, argc = fun->argumentCount(); i < argc; ++i) { + } else if (Function *fun = scope->asFunction()) { + for (unsigned i = 0, argc = fun->argumentCount(); i < argc; ++i) addCompletionItem(fun->argumentAt(i), FunctionArgumentsOrder); - } - } else if (scope->isTemplate()) { - Template *templ = scope->asTemplate(); - for (unsigned i = 0, argc = templ->templateParameterCount(); i < argc; ++i) { + } else if (Template *templ = scope->asTemplate()) { + for (unsigned i = 0, argc = templ->templateParameterCount(); i < argc; ++i) addCompletionItem(templ->templateParameterAt(i), FunctionArgumentsOrder); - } break; } } @@ -1513,9 +1508,8 @@ void CppCompletionAssistProcessor::completeNamespace(CPlusPlus::ClassOrNamespace scopesToVisit.append(scope); } - foreach (Enum *e, binding->unscopedEnums()) { + foreach (Enum *e, binding->unscopedEnums()) scopesToVisit.append(e); - } while (!scopesToVisit.isEmpty()) { Scope *scope = scopesToVisit.takeFirst(); @@ -1729,9 +1723,8 @@ void CppCompletionAssistProcessor::addMacros_helper(const CPlusPlus::Snapshot &s processed->insert(doc->fileName()); - foreach (const Document::Include &i, doc->resolvedIncludes()) { + foreach (const Document::Include &i, doc->resolvedIncludes()) addMacros_helper(snapshot, i.resolvedFileName(), processed, definedMacros); - } foreach (const Macro ¯o, doc->definedMacros()) { const QString macroName = QString::fromUtf8(macro.name().constData(), macro.name().length()); diff --git a/src/plugins/cpptools/cppfilesettingspage.cpp b/src/plugins/cpptools/cppfilesettingspage.cpp index c5cac630ef..cb2a39949d 100644 --- a/src/plugins/cpptools/cppfilesettingspage.cpp +++ b/src/plugins/cpptools/cppfilesettingspage.cpp @@ -254,6 +254,7 @@ CppFileSettingsWidget::CppFileSettingsWidget(QWidget *parent) : foreach (const QString &suffix, headerMt.suffixes()) m_ui->headerSuffixComboBox->addItem(suffix); m_ui->licenseTemplatePathChooser->setExpectedKind(Utils::PathChooser::File); + m_ui->licenseTemplatePathChooser->setHistoryCompleter(QLatin1String("Cpp.LicenseTemplate.History")); m_ui->licenseTemplatePathChooser->addButton(tr("Edit..."), this, SLOT(slotEdit())); } @@ -292,21 +293,6 @@ CppFileSettings CppFileSettingsWidget::settings() const return rc; } -QString CppFileSettingsWidget::searchKeywords() const -{ - QString rc; - 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('&')); - return rc; -} - static inline void setComboText(QComboBox *cb, const QString &text, int defaultIndex = 0) { const int index = cb->findText(text); @@ -354,13 +340,13 @@ CppFileSettingsPage::CppFileSettingsPage(QSharedPointer<CppFileSettings> &settin setCategoryIcon(QLatin1String(Constants::SETTINGS_CATEGORY_CPP_ICON)); } -QWidget *CppFileSettingsPage::createPage(QWidget *parent) +QWidget *CppFileSettingsPage::widget() { - m_widget = new CppFileSettingsWidget(parent); - m_widget->setSettings(*m_settings); - if (m_searchKeywords.isEmpty()) - m_searchKeywords = m_widget->searchKeywords(); + if (!m_widget) { + m_widget = new CppFileSettingsWidget; + m_widget->setSettings(*m_settings); + } return m_widget; } @@ -377,9 +363,9 @@ void CppFileSettingsPage::apply() } } -bool CppFileSettingsPage::matches(const QString &s) const +void CppFileSettingsPage::finish() { - return m_searchKeywords.contains(s, Qt::CaseInsensitive); + delete m_widget; } } // namespace Internal diff --git a/src/plugins/cpptools/cppfilesettingspage.h b/src/plugins/cpptools/cppfilesettingspage.h index df66260303..0ca3284246 100644 --- a/src/plugins/cpptools/cppfilesettingspage.h +++ b/src/plugins/cpptools/cppfilesettingspage.h @@ -80,8 +80,6 @@ public: CppFileSettings settings() const; void setSettings(const CppFileSettings &s); - QString searchKeywords() const; - private slots: void slotEdit(); @@ -98,15 +96,13 @@ public: explicit CppFileSettingsPage(QSharedPointer<CppFileSettings> &settings, QObject *parent = 0); - QWidget *createPage(QWidget *parent); + QWidget *widget(); void apply(); - void finish() { } - bool matches(const QString &) const; + void finish(); private: const QSharedPointer<CppFileSettings> m_settings; QPointer<CppFileSettingsWidget> m_widget; - QString m_searchKeywords; }; } // namespace Internal diff --git a/src/plugins/cpptools/cppheadersource_test.cpp b/src/plugins/cpptools/cppheadersource_test.cpp index 95cb2cf1c6..bbc4efa24f 100644 --- a/src/plugins/cpptools/cppheadersource_test.cpp +++ b/src/plugins/cpptools/cppheadersource_test.cpp @@ -46,8 +46,8 @@ void CppToolsPlugin::test_headersource() QFETCH(QString, headerFileName); bool wasHeader; - Core::Internal::Tests::TestDataDir dataDir( - _(SRCDIR "/../../../tests/cppheadersource/") + _(QTest::currentDataTag())); + Core::Tests::TestDataDir dataDir(_(SRCDIR "/../../../tests/cppheadersource/") + + _(QTest::currentDataTag())); const QString sourcePath = dataDir.file(sourceFileName); const QString headerPath = dataDir.file(headerFileName); diff --git a/src/plugins/cpptools/cpphighlightingsupport.h b/src/plugins/cpptools/cpphighlightingsupport.h index 97f2e9d475..757c3b04df 100644 --- a/src/plugins/cpptools/cpphighlightingsupport.h +++ b/src/plugins/cpptools/cpphighlightingsupport.h @@ -57,7 +57,8 @@ public: LabelUse, MacroUse, FunctionUse, - PseudoKeywordUse + PseudoKeywordUse, + StringUse }; public: diff --git a/src/plugins/cpptools/cpplocatorfilter_test.cpp b/src/plugins/cpptools/cpplocatorfilter_test.cpp index bc3b9341fd..5a0bb04654 100644 --- a/src/plugins/cpptools/cpplocatorfilter_test.cpp +++ b/src/plugins/cpptools/cpplocatorfilter_test.cpp @@ -34,6 +34,7 @@ #include "cppfunctionsfilter.h" #include "cpplocatorfilter.h" #include "cppmodelmanager.h" +#include "cpptoolstestcase.h" #include <coreplugin/editormanager/editormanager.h> #include <coreplugin/testdatadir.h> @@ -46,99 +47,96 @@ #include <QtTest> using namespace Core; -using namespace Core::Internal::Tests; +using namespace Core::Tests; using namespace CppTools::Internal; using namespace ExtensionSystem; using namespace Locator; -using namespace Locator::Internal; -using namespace Locator::Internal::Tests; +using namespace Locator::Tests; using namespace Utils; Q_DECLARE_METATYPE(ILocatorFilter *) namespace { -class MyTestDataDir : public Core::Internal::Tests::TestDataDir -{ -public: - MyTestDataDir(const QString &testDataDirectory) - : TestDataDir(QLatin1String(SRCDIR "/../../../tests/cpplocators/") + testDataDirectory) {} -}; +QTC_DECLARE_MYTESTDATADIR("../../../tests/cpplocators/") + +inline QString _(const QByteArray &ba) { return QString::fromLatin1(ba, ba.size()); } -class CppLocatorFilterTest : public BasicLocatorFilterTest +class CppLocatorFilterTestCase + : public BasicLocatorFilterTest + , public CppTools::Tests::TestCase { public: - CppLocatorFilterTest(ILocatorFilter *filter, const QString &fileName) + CppLocatorFilterTestCase(ILocatorFilter *filter, + const QString &fileName, + const QString &searchText, + const ResultDataList &expectedResults) : BasicLocatorFilterTest(filter) - , m_modelManager(CppModelManager::instance()) , m_fileName(fileName) { + QVERIFY(succeededSoFar()); QVERIFY(!m_fileName.isEmpty()); - m_modelManager->GC(); - QVERIFY(m_modelManager->snapshot().isEmpty()); - } + QVERIFY(garbageCollectGlobalSnapshot()); -private: - virtual void doBeforeLocatorRun() - { - m_modelManager->updateSourceFiles(QStringList() << m_fileName).waitForFinished(); - QVERIFY(m_modelManager->snapshot().contains(m_fileName)); - QCoreApplication::processEvents(); + ResultDataList results = ResultData::fromFilterEntryList(matchesFor(searchText)); +// ResultData::printFilterEntries(results); + QVERIFY(!results.isEmpty()); + QCOMPARE(results, expectedResults); } - virtual void doAfterLocatorRun() - { - m_modelManager->GC(); - QVERIFY(m_modelManager->snapshot().isEmpty()); - } +private: + void doBeforeLocatorRun() { QVERIFY(parseFiles(m_fileName)); } + void doAfterLocatorRun() { QVERIFY(garbageCollectGlobalSnapshot()); } - CppModelManager *m_modelManager; +private: const QString m_fileName; }; -class CppCurrentDocumentFilterTest : public BasicLocatorFilterTest +class CppCurrentDocumentFilterTestCase + : public BasicLocatorFilterTest + , public CppTools::Tests::TestCase { public: - CppCurrentDocumentFilterTest(const QString &fileName) + CppCurrentDocumentFilterTestCase(const QString &fileName, + const ResultDataList &expectedResults) : BasicLocatorFilterTest(PluginManager::getObject<CppCurrentDocumentFilter>()) - , m_modelManager(CppModelManager::instance()) , m_editor(0) , m_fileName(fileName) { + QVERIFY(succeededSoFar()); QVERIFY(!m_fileName.isEmpty()); - m_modelManager->GC(); - QVERIFY(m_modelManager->snapshot().isEmpty()); + + ResultDataList results = ResultData::fromFilterEntryList(matchesFor()); +// ResultData::printFilterEntries(results); + QVERIFY(!results.isEmpty()); + QCOMPARE(results, expectedResults); } private: - virtual void doBeforeLocatorRun() + void doBeforeLocatorRun() { QVERIFY(EditorManager::documentModel()->openedDocuments().isEmpty()); - m_modelManager->GC(); - QVERIFY(m_modelManager->snapshot().isEmpty()); + QVERIFY(garbageCollectGlobalSnapshot()); m_editor = EditorManager::openEditor(m_fileName); QVERIFY(m_editor); - while (!m_modelManager->snapshot().contains(m_fileName)) - QCoreApplication::processEvents(); + + waitForFileInGlobalSnapshot(m_fileName); } - virtual void doAfterLocatorRun() + void doAfterLocatorRun() { - EditorManager::closeEditor(m_editor, /*askAboutModifiedEditors=*/ false); + QVERIFY(closeEditorWithoutGarbageCollectorInvocation(m_editor)); QCoreApplication::processEvents(); QVERIFY(EditorManager::documentModel()->openedDocuments().isEmpty()); - m_modelManager->GC(); - QVERIFY(m_modelManager->snapshot().isEmpty()); + QVERIFY(garbageCollectGlobalSnapshot()); } - CppModelManager *m_modelManager; +private: IEditor *m_editor; const QString m_fileName; }; -inline QString _(const QByteArray &ba) { return QString::fromLatin1(ba, ba.size()); } - } // anonymous namespace void CppToolsPlugin::test_cpplocatorfilters_CppLocatorFilter() @@ -148,11 +146,7 @@ void CppToolsPlugin::test_cpplocatorfilters_CppLocatorFilter() QFETCH(QString, searchText); QFETCH(ResultDataList, expectedResults); - CppLocatorFilterTest test(filter, testFile); - ResultDataList results = ResultData::fromFilterEntryList(test.matchesFor(searchText)); -// ResultData::printFilterEntries(results); - QVERIFY(!results.isEmpty()); - QCOMPARE(results, expectedResults); + CppLocatorFilterTestCase(filter, testFile, searchText, expectedResults); } void CppToolsPlugin::test_cpplocatorfilters_CppLocatorFilter_data() @@ -173,15 +167,18 @@ void CppToolsPlugin::test_cpplocatorfilters_CppLocatorFilter_data() QTest::newRow("CppFunctionsFilter") << testFile << cppFunctionsFilter - << QString::fromLatin1("function") + << _("function") << (QList<ResultData>() << ResultData(_("functionDefinedInClass(bool, int)"), _("MyClass")) << ResultData(_("functionDefinedInClass(bool, int)"), _("MyNamespace::MyClass")) - << ResultData(_("functionDefinedInClass(bool, int)"), _("<anonymous namespace>::MyClass")) + << ResultData(_("functionDefinedInClass(bool, int)"), + _("<anonymous namespace>::MyClass")) << ResultData(_("functionDefinedOutSideClass(char)"), _("MyClass")) << ResultData(_("functionDefinedOutSideClass(char)"), _("MyNamespace::MyClass")) - << ResultData(_("functionDefinedOutSideClass(char)"), _("<anonymous namespace>::MyClass")) - << ResultData(_("functionDefinedOutSideClassAndNamespace(float)"), _("MyNamespace::MyClass")) + << ResultData(_("functionDefinedOutSideClass(char)"), + _("<anonymous namespace>::MyClass")) + << ResultData(_("functionDefinedOutSideClassAndNamespace(float)"), + _("MyNamespace::MyClass")) << ResultData(_("myFunction(bool, int)"), testFileShort) << ResultData(_("myFunction(bool, int)"), _("MyNamespace")) << ResultData(_("myFunction(bool, int)"), _("<anonymous namespace>")) @@ -195,7 +192,8 @@ void CppToolsPlugin::test_cpplocatorfilters_CppLocatorFilter_data() << ResultData(_("MyClass()"), _("MyNamespace::MyClass")) << ResultData(_("functionDefinedInClass(bool, int)"), _("MyNamespace::MyClass")) << ResultData(_("functionDefinedOutSideClass(char)"), _("MyNamespace::MyClass")) - << ResultData(_("functionDefinedOutSideClassAndNamespace(float)"), _("MyNamespace::MyClass")) + << ResultData(_("functionDefinedOutSideClassAndNamespace(float)"), + _("MyNamespace::MyClass")) << ResultData(_("myFunction(bool, int)"), _("MyNamespace")) ); @@ -225,7 +223,8 @@ void CppToolsPlugin::test_cpplocatorfilters_CppLocatorFilter_data() << (QList<ResultData>() << ResultData(_("<anonymous namespace>::MyClass"), testFileShort) << ResultData(_("<anonymous namespace>::MyClass::MyClass"), _("()")) - << ResultData(_("<anonymous namespace>::MyClass::functionDefinedOutSideClass"), _("(char)")) + << ResultData(_("<anonymous namespace>::MyClass::functionDefinedOutSideClass"), + _("(char)")) << ResultData(_("<anonymous namespace>::MyEnum"), testFileShort) << ResultData(_("<anonymous namespace>::myFunction"), _("(bool, int)")) << ResultData(_("MyClass"), testFileShort) @@ -234,8 +233,10 @@ void CppToolsPlugin::test_cpplocatorfilters_CppLocatorFilter_data() << ResultData(_("MyEnum"), testFileShort) << ResultData(_("MyNamespace::MyClass"), testFileShort) << ResultData(_("MyNamespace::MyClass::MyClass"), _("()")) - << ResultData(_("MyNamespace::MyClass::functionDefinedOutSideClass"), _("(char)")) - << ResultData(_("MyNamespace::MyClass::functionDefinedOutSideClassAndNamespace"), _("(float)")) + << ResultData(_("MyNamespace::MyClass::functionDefinedOutSideClass"), + _("(char)")) + << ResultData(_("MyNamespace::MyClass::functionDefinedOutSideClassAndNamespace"), + _("(float)")) << ResultData(_("MyNamespace::MyEnum"), testFileShort) << ResultData(_("MyNamespace::myFunction"), _("(bool, int)")) << ResultData(_("myFunction"), _("(bool, int)")) @@ -269,9 +270,11 @@ void CppToolsPlugin::test_cpplocatorfilters_CppCurrentDocumentFilter() << ResultData(_("functionDeclaredOnly()"), _("MyNamespace::MyClass")) << ResultData(_("functionDefinedInClass(bool, int)"), _("MyNamespace::MyClass")) << ResultData(_("functionDefinedOutSideClass(char)"), _("MyNamespace::MyClass")) - << ResultData(_("functionDefinedOutSideClassAndNamespace(float)"), _("MyNamespace::MyClass")) + << ResultData(_("functionDefinedOutSideClassAndNamespace(float)"), + _("MyNamespace::MyClass")) << ResultData(_("functionDefinedOutSideClass(char)"), _("MyNamespace::MyClass")) - << ResultData(_("functionDefinedOutSideClassAndNamespace(float)"), _("MyNamespace::MyClass")) + << ResultData(_("functionDefinedOutSideClassAndNamespace(float)"), + _("MyNamespace::MyClass")) << ResultData(_("int myVariable"), _("<anonymous namespace>")) << ResultData(_("myFunction(bool, int)"), _("<anonymous namespace>")) << ResultData(_("MyEnum"), _("<anonymous namespace>")) @@ -286,9 +289,5 @@ void CppToolsPlugin::test_cpplocatorfilters_CppCurrentDocumentFilter() << ResultData(_("main()"), _("")) ; - CppCurrentDocumentFilterTest test(testFile); - ResultDataList results = ResultData::fromFilterEntryList(test.matchesFor()); -// ResultData::printFilterEntries(results); - QVERIFY(!results.isEmpty()); - QCOMPARE(expectedResults, results); + CppCurrentDocumentFilterTestCase(testFile, expectedResults); } diff --git a/src/plugins/cpptools/cppmodelmanager.cpp b/src/plugins/cpptools/cppmodelmanager.cpp index 1ea0c77de4..f4a5329b33 100644 --- a/src/plugins/cpptools/cppmodelmanager.cpp +++ b/src/plugins/cpptools/cppmodelmanager.cpp @@ -64,7 +64,8 @@ namespace CppTools { uint qHash(const ProjectPart &p) { - uint h = qHash(p.defines) ^ p.cVersion ^ p.cxxVersion ^ p.cxxExtensions ^ p.qtVersion; + uint h = qHash(p.toolchainDefines) ^ qHash(p.projectDefines) ^ p.cVersion ^ p.cxxVersion + ^ p.cxxExtensions ^ p.qtVersion; foreach (const QString &i, p.includePaths) h ^= qHash(i); @@ -78,7 +79,9 @@ uint qHash(const ProjectPart &p) bool operator==(const ProjectPart &p1, const ProjectPart &p2) { - if (p1.defines != p2.defines) + if (p1.toolchainDefines != p2.toolchainDefines) + return false; + if (p1.projectDefines != p2.projectDefines) return false; if (p1.cVersion != p2.cVersion) return false; @@ -363,6 +366,22 @@ QStringList CppModelManager::internalFrameworkPaths() const return frameworkPaths; } +static void addUnique(const QList<QByteArray> &defs, QByteArray *macros, QSet<QByteArray> *alreadyIn) +{ + Q_ASSERT(macros); + Q_ASSERT(alreadyIn); + + foreach (const QByteArray &def, defs) { + if (def.trimmed().isEmpty()) + continue; + if (!alreadyIn->contains(def)) { + macros->append(def); + macros->append('\n'); + alreadyIn->insert(def); + } + } +} + QByteArray CppModelManager::internalDefinedMacros() const { QByteArray macros; @@ -372,14 +391,8 @@ QByteArray CppModelManager::internalDefinedMacros() const it.next(); const ProjectInfo pinfo = it.value(); foreach (const ProjectPart::Ptr &part, pinfo.projectParts()) { - const QList<QByteArray> defs = part->defines.split('\n'); - foreach (const QByteArray &def, defs) { - if (!alreadyIn.contains(def)) { - macros += def; - macros.append('\n'); - alreadyIn.insert(def); - } - } + addUnique(part->toolchainDefines.split('\n'), ¯os, &alreadyIn); + addUnique(part->projectDefines.split('\n'), ¯os, &alreadyIn); } } return macros; @@ -422,7 +435,8 @@ void CppModelManager::dumpModelManagerConfiguration() qDebug() << "cxxExtensions:" << cxxExtensions; qDebug() << "Qt version:" << part->qtVersion; qDebug() << "precompiled header:" << part->precompiledHeaders; - qDebug() << "defines:" << part->defines; + qDebug() << "toolchain defines:" << part->toolchainDefines; + qDebug() << "project defines:" << part->projectDefines; qDebug() << "includes:" << part->includePaths; qDebug() << "frameworkPaths:" << part->frameworkPaths; qDebug() << "files:" << part->files; @@ -802,7 +816,7 @@ ProjectPart::Ptr CppModelManager::fallbackProjectPart() const { ProjectPart::Ptr part(new ProjectPart); - part->defines = m_definedMacros; + part->projectDefines = m_definedMacros; part->includePaths = m_includePaths; part->frameworkPaths = m_frameworkPaths; part->cVersion = ProjectPart::C11; @@ -832,7 +846,8 @@ void CppModelManager::onProjectAdded(ProjectExplorer::Project *) void CppModelManager::delayedGC() { - m_delayedGcTimer->start(500); + if (m_enableGC) + m_delayedGcTimer->start(500); } void CppModelManager::onAboutToRemoveProject(ProjectExplorer::Project *project) @@ -978,6 +993,12 @@ CppIndexingSupport *CppModelManager::indexingSupport() return m_indexingSupporter ? m_indexingSupporter : m_internalIndexingSupport; } +void CppModelManager::enableGarbageCollector(bool enable) +{ + m_delayedGcTimer->stop(); + m_enableGC = enable; +} + void CppModelManager::setExtraDiagnostics(const QString &fileName, const QString &kind, const QList<Document::DiagnosticMessage> &diagnostics) diff --git a/src/plugins/cpptools/cppmodelmanager.h b/src/plugins/cpptools/cppmodelmanager.h index e8e922b604..96a5796a2d 100644 --- a/src/plugins/cpptools/cppmodelmanager.h +++ b/src/plugins/cpptools/cppmodelmanager.h @@ -47,7 +47,6 @@ namespace TextEditor { class BaseTextEditorWidget; } namespace CppTools { class CppEditorSupport; -class CppHighlightingSupportFactory; namespace Internal { @@ -153,6 +152,8 @@ public: return m_definedMacros; } + void enableGarbageCollector(bool enable); + static QStringList timeStampModifiedFiles(const QList<Document::Ptr> documentsToCheck); signals: diff --git a/src/plugins/cpptools/cppmodelmanager_test.cpp b/src/plugins/cpptools/cppmodelmanager_test.cpp index 91e89fced7..7f2b7765c8 100644 --- a/src/plugins/cpptools/cppmodelmanager_test.cpp +++ b/src/plugins/cpptools/cppmodelmanager_test.cpp @@ -27,9 +27,10 @@ ** ****************************************************************************/ -#include "cpptoolsplugin.h" #include "cpppreprocessor.h" #include "cpptoolseditorsupport.h" +#include "cpptoolsplugin.h" +#include "cpptoolstestcase.h" #include "modelmanagertesthelper.h" #include <coreplugin/editormanager/editormanager.h> @@ -62,7 +63,7 @@ namespace { inline QString _(const QByteArray &ba) { return QString::fromLatin1(ba, ba.size()); } -class MyTestDataDir : public Core::Internal::Tests::TestDataDir +class MyTestDataDir : public Core::Tests::TestDataDir { public: MyTestDataDir(const QString &dir) @@ -181,25 +182,30 @@ public: return isFetchOk; } - void writeContents(const QByteArray &contents) const + bool writeContents(const QByteArray &contents) const { - Utils::FileSaver fileSaver(m_filePath); - fileSaver.write(contents); - fileSaver.finalize(); + return CppTools::Tests::TestCase::writeFile(m_filePath, contents); } private: void restoreContents() const { - Utils::FileSaver fileSaver(m_filePath); - fileSaver.write(m_originalFileContents); - fileSaver.finalize(); + CppTools::Tests::TestCase::writeFile(m_filePath, m_originalFileContents); } QByteArray m_originalFileContents; const QString &m_filePath; }; +static QStringList updateProjectInfo(CppModelManager *modelManager, ModelManagerTestHelper *helper, + const ProjectInfo &projectInfo) +{ + helper->resetRefreshedSourceFiles(); + modelManager->updateProjectInfo(projectInfo).waitForFinished(); + QCoreApplication::processEvents(); + return helper->waitForRefreshedSourceFiles(); +} + } // anonymous namespace /// Check: The preprocessor cleans include and framework paths. @@ -217,7 +223,7 @@ void CppToolsPlugin::test_modelmanager_paths_are_clean() ProjectPart::Ptr part(new ProjectPart); part->cxxVersion = ProjectPart::CXX98; part->qtVersion = ProjectPart::Qt5; - part->defines = QByteArray("#define OH_BEHAVE -1\n"); + part->projectDefines = QByteArray("#define OH_BEHAVE -1\n"); part->includePaths = QStringList() << testDataDir.includeDir(false); part->frameworkPaths = QStringList() << testDataDir.frameworksDir(false); pi.appendProjectPart(part); @@ -251,7 +257,7 @@ void CppToolsPlugin::test_modelmanager_framework_headers() ProjectPart::Ptr part(new ProjectPart); part->cxxVersion = ProjectPart::CXX98; part->qtVersion = ProjectPart::Qt5; - part->defines = QByteArray("#define OH_BEHAVE -1\n"); + part->projectDefines = QByteArray("#define OH_BEHAVE -1\n"); part->includePaths << testDataDir.includeDir(); part->frameworkPaths << testDataDir.frameworksDir(); const QString &source = testDataDir.fileFromSourcesDir( @@ -300,14 +306,12 @@ void CppToolsPlugin::test_modelmanager_refresh_also_includes_of_project_files() ProjectPart::Ptr part(new ProjectPart); part->cxxVersion = ProjectPart::CXX98; part->qtVersion = ProjectPart::Qt5; - part->defines = QByteArray("#define OH_BEHAVE -1\n"); + part->projectDefines = QByteArray("#define OH_BEHAVE -1\n"); part->includePaths = QStringList() << testDataDir.includeDir(false); part->files.append(ProjectFile(testCpp, ProjectFile::CXXSource)); pi.appendProjectPart(part); - mm->updateProjectInfo(pi); - - QStringList refreshedFiles = helper.waitForRefreshedSourceFiles(); + QStringList refreshedFiles = updateProjectInfo(mm, &helper, pi); QCOMPARE(refreshedFiles.size(), 1); QVERIFY(refreshedFiles.contains(testCpp)); CPlusPlus::Snapshot snapshot = mm->snapshot(); @@ -320,12 +324,11 @@ void CppToolsPlugin::test_modelmanager_refresh_also_includes_of_project_files() QVERIFY(macrosInHeaderBefore.first().name() == "test_modelmanager_refresh_h"); // Introduce a define that will enable another define once the document is reparsed. - part->defines = QByteArray("#define TEST_DEFINE 1\n"); + part->projectDefines = QByteArray("#define TEST_DEFINE 1\n"); pi.clearProjectParts(); pi.appendProjectPart(part); - mm->updateProjectInfo(pi); - refreshedFiles = helper.waitForRefreshedSourceFiles(); + refreshedFiles = updateProjectInfo(mm, &helper, pi); QCOMPARE(refreshedFiles.size(), 1); QVERIFY(refreshedFiles.contains(testCpp)); @@ -377,7 +380,7 @@ void CppToolsPlugin::test_modelmanager_refresh_several_times() ProjectPart::Ptr part(new ProjectPart); // Simulate project configuration change by having different defines each time. defines += "\n#define ANOTHER_DEFINE"; - part->defines = defines; + part->projectDefines = defines; part->cxxVersion = ProjectPart::CXX98; part->qtVersion = ProjectPart::Qt5; part->files.append(ProjectFile(testHeader1, ProjectFile::CXXHeader)); @@ -385,11 +388,9 @@ void CppToolsPlugin::test_modelmanager_refresh_several_times() part->files.append(ProjectFile(testCpp, ProjectFile::CXXSource)); pi.appendProjectPart(part); - mm->updateProjectInfo(pi); - - refreshedFiles = helper.waitForRefreshedSourceFiles(); - + refreshedFiles = updateProjectInfo(mm, &helper, pi); QCOMPARE(refreshedFiles.size(), 3); + QVERIFY(refreshedFiles.contains(testHeader1)); QVERIFY(refreshedFiles.contains(testHeader2)); QVERIFY(refreshedFiles.contains(testCpp)); @@ -432,8 +433,10 @@ void CppToolsPlugin::test_modelmanager_refresh_test_for_changes() pi.appendProjectPart(part); // Reindexing triggers a reparsing thread + helper.resetRefreshedSourceFiles(); QFuture<void> firstFuture = mm->updateProjectInfo(pi); QVERIFY(firstFuture.isStarted() || firstFuture.isRunning()); + firstFuture.waitForFinished(); const QStringList refreshedFiles = helper.waitForRefreshedSourceFiles(); QCOMPARE(refreshedFiles.size(), 1); QVERIFY(refreshedFiles.contains(testCpp)); @@ -470,8 +473,7 @@ void CppToolsPlugin::test_modelmanager_refresh_added_and_purge_removed() CPlusPlus::Snapshot snapshot; QStringList refreshedFiles; - mm->updateProjectInfo(pi); - refreshedFiles = helper.waitForRefreshedSourceFiles(); + refreshedFiles = updateProjectInfo(mm, &helper, pi); QCOMPARE(refreshedFiles.size(), 2); QVERIFY(refreshedFiles.contains(testHeader1)); @@ -490,8 +492,7 @@ void CppToolsPlugin::test_modelmanager_refresh_added_and_purge_removed() newPart->files.append(ProjectFile(testHeader2, ProjectFile::CXXHeader)); pi.appendProjectPart(newPart); - mm->updateProjectInfo(pi); - refreshedFiles = helper.waitForRefreshedSourceFiles(); + refreshedFiles = updateProjectInfo(mm, &helper, pi); // Only the added project file was reparsed QCOMPARE(refreshedFiles.size(), 1); @@ -530,8 +531,7 @@ void CppToolsPlugin::test_modelmanager_refresh_timeStampModified_if_sourcefiles_ CPlusPlus::Snapshot snapshot; QStringList refreshedFiles; - mm->updateProjectInfo(pi); - refreshedFiles = helper.waitForRefreshedSourceFiles(); + refreshedFiles = updateProjectInfo(mm, &helper, pi); QCOMPARE(refreshedFiles.size(), initialProjectFiles.size()); snapshot = mm->snapshot(); @@ -551,7 +551,7 @@ void CppToolsPlugin::test_modelmanager_refresh_timeStampModified_if_sourcefiles_ QByteArray originalContents; QVERIFY(fileChangerAndRestorer.readContents(&originalContents)); const QByteArray newFileContentes = originalContents + "\nint addedOtherGlobal;"; - fileChangerAndRestorer.writeContents(newFileContentes); + QVERIFY(fileChangerAndRestorer.writeContents(newFileContentes)); // Add or remove source file. The configuration stays the same. part->files.clear(); @@ -560,8 +560,7 @@ void CppToolsPlugin::test_modelmanager_refresh_timeStampModified_if_sourcefiles_ pi.clearProjectParts(); pi.appendProjectPart(part); - mm->updateProjectInfo(pi); - refreshedFiles = helper.waitForRefreshedSourceFiles(); + refreshedFiles = updateProjectInfo(mm, &helper, pi); QCOMPARE(refreshedFiles.size(), finalProjectFiles.size()); snapshot = mm->snapshot(); @@ -618,8 +617,7 @@ void CppToolsPlugin::test_modelmanager_snapshot_after_two_projects() << _("foo.cpp") << _("main.cpp")); - mm->updateProjectInfo(project1.projectInfo); - refreshedFiles = helper.waitForRefreshedSourceFiles(); + refreshedFiles = updateProjectInfo(mm, &helper, project1.projectInfo); QCOMPARE(refreshedFiles.toSet(), project1.projectFiles.toSet()); const int snapshotSizeAfterProject1 = mm->snapshot().size(); @@ -633,8 +631,7 @@ void CppToolsPlugin::test_modelmanager_snapshot_after_two_projects() << _("bar.cpp") << _("main.cpp")); - mm->updateProjectInfo(project2.projectInfo); - refreshedFiles = helper.waitForRefreshedSourceFiles(); + refreshedFiles = updateProjectInfo(mm, &helper, project2.projectInfo); QCOMPARE(refreshedFiles.toSet(), project2.projectFiles.toSet()); const int snapshotSizeAfterProject2 = mm->snapshot().size(); @@ -710,6 +707,7 @@ void CppToolsPlugin::test_modelmanager_gc_if_last_cppeditor_closed() const QString file = testDataDirectory.file(_("main.cpp")); CppModelManager *mm = CppModelManager::instance(); + helper.resetRefreshedSourceFiles(); // Open a file in the editor QCOMPARE(Core::EditorManager::documentModel()->openedDocuments().size(), 0); @@ -740,6 +738,7 @@ void CppToolsPlugin::test_modelmanager_dont_gc_opened_files() const QString file = testDataDirectory.file(_("main.cpp")); CppModelManager *mm = CppModelManager::instance(); + helper.resetRefreshedSourceFiles(); // Open a file in the editor QCOMPARE(Core::EditorManager::documentModel()->openedDocuments().size(), 0); @@ -750,6 +749,7 @@ void CppToolsPlugin::test_modelmanager_dont_gc_opened_files() // Wait until the file is refreshed and check whether it is in the working copy helper.waitForRefreshedSourceFiles(); + QVERIFY(mm->workingCopy().contains(file)); // Run the garbage collector @@ -771,8 +771,9 @@ struct EditorCloser { EditorCloser(Core::IEditor *editor): editor(editor) {} ~EditorCloser() { + using namespace CppTools; if (editor) - Core::EditorManager::closeEditor(editor); + QVERIFY(Tests::TestCase::closeEditorWithoutGarbageCollectorInvocation(editor)); } }; @@ -811,7 +812,7 @@ void CppToolsPlugin::test_modelmanager_defines_per_project() part1->files.append(ProjectFile(header, ProjectFile::CXXHeader)); part1->cxxVersion = ProjectPart::CXX11; part1->qtVersion = ProjectPart::NoQt; - part1->defines = QByteArray("#define SUB1\n"); + part1->projectDefines = QByteArray("#define SUB1\n"); part1->includePaths = QStringList() << testDataDirectory.includeDir(false); ProjectPart::Ptr part2(new ProjectPart); @@ -820,17 +821,14 @@ void CppToolsPlugin::test_modelmanager_defines_per_project() part2->files.append(ProjectFile(header, ProjectFile::CXXHeader)); part2->cxxVersion = ProjectPart::CXX11; part2->qtVersion = ProjectPart::NoQt; - part2->defines = QByteArray("#define SUB2\n"); + part2->projectDefines = QByteArray("#define SUB2\n"); part2->includePaths = QStringList() << testDataDirectory.includeDir(false); ProjectInfo pi = mm->projectInfo(project); pi.appendProjectPart(part1); pi.appendProjectPart(part2); - mm->updateProjectInfo(pi); - - helper.waitForRefreshedSourceFiles(); - + updateProjectInfo(mm, &helper, pi); QCOMPARE(mm->snapshot().size(), 4); // Open a file in the editor @@ -901,10 +899,7 @@ void CppToolsPlugin::test_modelmanager_defines_per_project_pch() pi.appendProjectPart(part1); pi.appendProjectPart(part2); - mm->updateProjectInfo(pi); - - helper.waitForRefreshedSourceFiles(); - + updateProjectInfo(mm, &helper, pi); QCOMPARE(mm->snapshot().size(), 4); // Open a file in the editor @@ -972,9 +967,7 @@ void CppToolsPlugin::test_modelmanager_defines_per_editor() pi.appendProjectPart(part1); pi.appendProjectPart(part2); - mm->updateProjectInfo(pi); - - helper.waitForRefreshedSourceFiles(); + updateProjectInfo(mm, &helper, pi); QCOMPARE(mm->snapshot().size(), 4); diff --git a/src/plugins/cpptools/cppmodelmanagerinterface.cpp b/src/plugins/cpptools/cppmodelmanagerinterface.cpp index d3fb0fda86..e755783bef 100644 --- a/src/plugins/cpptools/cppmodelmanagerinterface.cpp +++ b/src/plugins/cpptools/cppmodelmanagerinterface.cpp @@ -161,13 +161,7 @@ void ProjectPart::evaluateToolchain(const ToolChain *tc, else includePaths << header.path(); - const QByteArray macros = tc->predefinedMacros(cxxflags); - if (!macros.isEmpty()) { - if (!defines.isEmpty()) - defines += '\n'; - defines += macros; - defines += '\n'; - } + toolchainDefines = tc->predefinedMacros(cxxflags); } static CppModelManagerInterface *g_instance = 0; @@ -235,5 +229,6 @@ void CppModelManagerInterface::ProjectInfo::appendProjectPart(const ProjectPart: // Update defines if (!m_defines.isEmpty()) m_defines.append('\n'); - m_defines.append(part->defines); + m_defines.append(part->toolchainDefines); + m_defines.append(part->projectDefines); } diff --git a/src/plugins/cpptools/cppmodelmanagerinterface.h b/src/plugins/cpptools/cppmodelmanagerinterface.h index 000b921113..84cca908b7 100644 --- a/src/plugins/cpptools/cppmodelmanagerinterface.h +++ b/src/plugins/cpptools/cppmodelmanagerinterface.h @@ -56,7 +56,6 @@ class ModelManagerSupport; class CppCompletionAssistProvider; class CppEditorSupport; class CppHighlightingSupport; -class CppHighlightingSupportFactory; class CppIndexingSupport; class CPPTOOLS_EXPORT ProjectPart @@ -106,7 +105,8 @@ public: QString projectFile; ProjectExplorer::Project *project; QList<ProjectFile> files; - QByteArray defines; + QByteArray projectDefines; + QByteArray toolchainDefines; QStringList includePaths; QStringList frameworkPaths; QStringList precompiledHeaders; @@ -268,6 +268,9 @@ public: virtual void setIndexingSupport(CppTools::CppIndexingSupport *indexingSupport) = 0; virtual CppIndexingSupport *indexingSupport() = 0; + virtual void setIncludePaths(const QStringList &includePaths) = 0; + virtual void enableGarbageCollector(bool enable) = 0; + signals: /// Project data might be locked while this is emitted. void aboutToRemoveFiles(const QStringList &files); diff --git a/src/plugins/cpptools/cpppointerdeclarationformatter_test.cpp b/src/plugins/cpptools/cpppointerdeclarationformatter_test.cpp index 14d6fef5a4..cb9e11f3ef 100644 --- a/src/plugins/cpptools/cpppointerdeclarationformatter_test.cpp +++ b/src/plugins/cpptools/cpppointerdeclarationformatter_test.cpp @@ -27,9 +27,10 @@ ** ****************************************************************************/ -#include "cpptoolsplugin.h" #include "cpppointerdeclarationformatter.h" #include "cpptoolsplugin.h" +#include "cpptoolsplugin.h" +#include "cpptoolstestcase.h" #include <texteditor/plaintexteditor.h> @@ -48,11 +49,14 @@ using namespace CppTools; using namespace CppTools::Internal; using Utils::ChangeSet; -typedef Utils::ChangeSet::Range Range; Q_DECLARE_METATYPE(Overview) -static QString stripCursor(const QString &source) +namespace { + +typedef Utils::ChangeSet::Range Range; + +QString stripCursor(const QString &source) { QString copy(source); const int pos = copy.indexOf(QLatin1Char('@')); @@ -63,19 +67,16 @@ static QString stripCursor(const QString &source) return copy; } -struct TestEnvironment +class PointerDeclarationFormatterTestCase : public CppTools::Tests::TestCase { - QByteArray source; - Snapshot snapshot; - CppRefactoringFilePtr cppRefactoringFile; - TextEditor::BaseTextEditorWidget *editor; - Document::Ptr document; - QTextDocument *textDocument; - TranslationUnit *translationUnit; - Environment env; - - TestEnvironment(const QByteArray &source, Document::ParseMode parseMode) +public: + PointerDeclarationFormatterTestCase(const QByteArray &source, + const QString &expectedSource, + Document::ParseMode parseMode, + PointerDeclarationFormatter::CursorHandling cursorHandling) { + QVERIFY(succeededSoFar()); + // Find cursor position and remove cursor marker '@' int cursorPosition = 0; QString sourceWithoutCursorMarker = QLatin1String(source); @@ -87,35 +88,37 @@ struct TestEnvironment // Write source to temprorary file const QString filePath = QDir::tempPath() + QLatin1String("/file.h"); - document = Document::create(filePath); - Utils::FileSaver documentSaver(document->fileName()); - documentSaver.write(sourceWithoutCursorMarker.toLatin1()); - documentSaver.finalize(); + Document::Ptr document = Document::create(filePath); + QVERIFY(writeFile(document->fileName(), sourceWithoutCursorMarker.toLatin1())); // Preprocess source + Environment env; Preprocessor preprocess(0, &env); const QByteArray preprocessedSource = preprocess.run(filePath, sourceWithoutCursorMarker); document->setUtf8Source(preprocessedSource); document->parse(parseMode); document->check(); - translationUnit = document->translationUnit(); - snapshot.insert(document); - editor = new TextEditor::PlainTextEditorWidget(0); + AST *ast = document->translationUnit()->ast(); + QVERIFY(ast); + + // Open file + QScopedPointer<TextEditor::BaseTextEditorWidget> editorWidget( + new TextEditor::PlainTextEditorWidget(0)); QString error; - editor->open(&error, document->fileName(), document->fileName()); + editorWidget->open(&error, document->fileName(), document->fileName()); + QVERIFY(error.isEmpty()); // Set cursor position - QTextCursor cursor = editor->textCursor(); + QTextCursor cursor = editorWidget->textCursor(); cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor, cursorPosition); - editor->setTextCursor(cursor); + editorWidget->setTextCursor(cursor); - textDocument = editor->document(); - cppRefactoringFile = CppRefactoringChanges::file(editor, document); - } + QTextDocument *textDocument = editorWidget->document(); + CppRefactoringFilePtr cppRefactoringFile + = CppRefactoringChanges::file(editorWidget.data(), document); - void applyFormatting(AST *ast, PointerDeclarationFormatter::CursorHandling cursorHandling) - { + // Prepare for formatting Overview overview; overview.showReturnTypes = true; overview.showArgumentNames = true; @@ -126,23 +129,25 @@ struct TestEnvironment ChangeSet change = formatter.format(ast); // ChangeSet may be empty. // Apply change - QTextCursor cursor(textDocument); - change.apply(&cursor); + QTextCursor changeCursor(textDocument); + change.apply(&changeCursor); + + // Compare + QCOMPARE(textDocument->toPlainText(), expectedSource); } }; +} // anonymous namespace + void CppToolsPlugin::test_format_pointerdeclaration_in_simpledeclarations() { QFETCH(QString, source); QFETCH(QString, reformattedSource); - TestEnvironment env(source.toLatin1(), Document::ParseDeclaration); - AST *ast = env.translationUnit->ast(); - QVERIFY(ast); - - env.applyFormatting(ast, PointerDeclarationFormatter::RespectCursor); - - QCOMPARE(env.textDocument->toPlainText(), reformattedSource); + PointerDeclarationFormatterTestCase(source.toLatin1(), + reformattedSource, + Document::ParseDeclaration, + PointerDeclarationFormatter::RespectCursor); } void CppToolsPlugin::test_format_pointerdeclaration_in_simpledeclarations_data() @@ -363,13 +368,10 @@ void CppToolsPlugin::test_format_pointerdeclaration_in_controlflowstatements() QFETCH(QString, source); QFETCH(QString, reformattedSource); - TestEnvironment env(source.toLatin1(), Document::ParseStatement); - AST *ast = env.translationUnit->ast(); - QVERIFY(ast); - - env.applyFormatting(ast, PointerDeclarationFormatter::RespectCursor); - - QCOMPARE(env.textDocument->toPlainText(), reformattedSource); + PointerDeclarationFormatterTestCase(source.toLatin1(), + reformattedSource, + Document::ParseStatement, + PointerDeclarationFormatter::RespectCursor); } void CppToolsPlugin::test_format_pointerdeclaration_in_controlflowstatements_data() @@ -441,13 +443,10 @@ void CppToolsPlugin::test_format_pointerdeclaration_multiple_declarators() QFETCH(QString, source); QFETCH(QString, reformattedSource); - TestEnvironment env(source.toLatin1(), Document::ParseDeclaration); - AST *ast = env.translationUnit->ast(); - QVERIFY(ast); - - env.applyFormatting(ast, PointerDeclarationFormatter::RespectCursor); - - QCOMPARE(env.textDocument->toPlainText(), reformattedSource); + PointerDeclarationFormatterTestCase(source.toLatin1(), + reformattedSource, + Document::ParseDeclaration, + PointerDeclarationFormatter::RespectCursor); } void CppToolsPlugin::test_format_pointerdeclaration_multiple_declarators_data() @@ -499,13 +498,10 @@ void CppToolsPlugin::test_format_pointerdeclaration_multiple_matches() QFETCH(QString, source); QFETCH(QString, reformattedSource); - TestEnvironment env(source.toLatin1(), Document::ParseTranlationUnit); - AST *ast = env.translationUnit->ast(); - QVERIFY(ast); - - env.applyFormatting(ast, PointerDeclarationFormatter::IgnoreCursor); - - QCOMPARE(env.textDocument->toPlainText(), reformattedSource); + PointerDeclarationFormatterTestCase(source.toLatin1(), + reformattedSource, + Document::ParseTranlationUnit, + PointerDeclarationFormatter::IgnoreCursor); } void CppToolsPlugin::test_format_pointerdeclaration_multiple_matches_data() @@ -585,13 +581,10 @@ void CppToolsPlugin::test_format_pointerdeclaration_macros() QFETCH(QString, source); QFETCH(QString, reformattedSource); - TestEnvironment env(source.toLatin1(), Document::ParseTranlationUnit); - AST *ast = env.translationUnit->ast(); - QVERIFY(ast); - - env.applyFormatting(ast, PointerDeclarationFormatter::RespectCursor); - - QCOMPARE(env.textDocument->toPlainText(), reformattedSource); + PointerDeclarationFormatterTestCase(source.toLatin1(), + reformattedSource, + Document::ParseTranlationUnit, + PointerDeclarationFormatter::RespectCursor); } void CppToolsPlugin::test_format_pointerdeclaration_macros_data() diff --git a/src/plugins/cpptools/cpppreprocessertesthelper.cpp b/src/plugins/cpptools/cpppreprocessertesthelper.cpp index 88dd073bda..7f27f6d140 100644 --- a/src/plugins/cpptools/cpppreprocessertesthelper.cpp +++ b/src/plugins/cpptools/cpppreprocessertesthelper.cpp @@ -32,7 +32,8 @@ #include <QDir> -using namespace CppTools; +namespace CppTools { +namespace Tests { QString TestIncludePaths::includeBaseDirectory() { @@ -54,3 +55,11 @@ QString TestIncludePaths::directoryOfTestFile() { return QDir::cleanPath(includeBaseDirectory() + QLatin1String("/local")); } + +QString TestIncludePaths::testFilePath(const QString &fileName) +{ + return Tests::TestIncludePaths::directoryOfTestFile() + QLatin1Char('/') + fileName; +} + +} // namespace Tests +} // namespace CppTools diff --git a/src/plugins/cpptools/cpppreprocessertesthelper.h b/src/plugins/cpptools/cpppreprocessertesthelper.h index cd6084dddf..de830aca42 100644 --- a/src/plugins/cpptools/cpppreprocessertesthelper.h +++ b/src/plugins/cpptools/cpppreprocessertesthelper.h @@ -34,20 +34,26 @@ #include "cpptools_global.h" #include <QtGlobal> +#include <QString> QT_FORWARD_DECLARE_CLASS(QString) namespace CppTools { +namespace Tests { class CPPTOOLS_EXPORT TestIncludePaths { + Q_DISABLE_COPY(TestIncludePaths) + public: static QString includeBaseDirectory(); static QString globalQtCoreIncludePath(); static QString globalIncludePath(); static QString directoryOfTestFile(); + static QString testFilePath(const QString &fileName = QLatin1String("file.cpp")); }; +} // namespace Tests } // namespace CppTools #endif // CPPPREPROCESSERTESTHELPER_H diff --git a/src/plugins/cpptools/cpppreprocessor_test.cpp b/src/plugins/cpptools/cpppreprocessor_test.cpp index 000f19b997..d2557e81e2 100644 --- a/src/plugins/cpptools/cpppreprocessor_test.cpp +++ b/src/plugins/cpptools/cpppreprocessor_test.cpp @@ -32,6 +32,7 @@ #include "cppmodelmanager.h" #include "cpppreprocessertesthelper.h" #include "cpppreprocessor.h" +#include "cpptoolstestcase.h" #include <cplusplus/CppDocument.h> #include <utils/fileutils.h> @@ -42,6 +43,7 @@ using namespace CPlusPlus; using namespace CppTools; +using namespace CppTools::Tests; using namespace CppTools::Internal; typedef Document::Include Include; @@ -57,14 +59,11 @@ public: Document::Ptr run(const QByteArray &source) { - const QString fileName = TestIncludePaths::directoryOfTestFile() - + QLatin1String("/file.cpp"); + const QString fileName = TestIncludePaths::testFilePath(); if (QFileInfo(fileName).exists()) return Document::Ptr(); // Test file was not removed. - Utils::FileSaver srcSaver(fileName); - srcSaver.write(source); - srcSaver.finalize(); + TestCase::writeFile(fileName, source); CppPreprocessor pp((QPointer<CppModelManager>(m_cmm))); pp.setIncludePaths(QStringList(TestIncludePaths::directoryOfTestFile())); @@ -108,7 +107,7 @@ void CppToolsPlugin::test_cpppreprocessor_includes() QVERIFY(resolvedIncludes.at(0).type() == Client::IncludeLocal); QCOMPARE(resolvedIncludes.at(0).unresolvedFileName(), QLatin1String("header.h")); const QString expectedResolvedFileName - = TestIncludePaths::directoryOfTestFile() + QLatin1String("/header.h"); + = TestIncludePaths::testFilePath(QLatin1String("header.h")); QCOMPARE(resolvedIncludes.at(0).resolvedFileName(), expectedResolvedFileName); const QList<Document::Include> unresolvedIncludes = document->unresolvedIncludes(); diff --git a/src/plugins/cpptools/cppsnapshotupdater.cpp b/src/plugins/cpptools/cppsnapshotupdater.cpp index ae8e4e325a..85a282ae34 100644 --- a/src/plugins/cpptools/cppsnapshotupdater.cpp +++ b/src/plugins/cpptools/cppsnapshotupdater.cpp @@ -69,7 +69,8 @@ void SnapshotUpdater::update(CppModelManager::WorkingCopy workingCopy) } if (m_projectPart) { - configFile += m_projectPart->defines; + configFile += m_projectPart->toolchainDefines; + configFile += m_projectPart->projectDefines; includePaths = m_projectPart->includePaths; frameworkPaths = m_projectPart->frameworkPaths; if (m_usePrecompiledHeaders) diff --git a/src/plugins/cpptools/cpptools.pro b/src/plugins/cpptools/cpptools.pro index 9700ea853a..059cc06342 100644 --- a/src/plugins/cpptools/cpptools.pro +++ b/src/plugins/cpptools/cpptools.pro @@ -1,129 +1,136 @@ -include(../../qtcreatorplugin.pri) - DEFINES += CPPTOOLS_LIBRARY win32-msvc*:DEFINES += _SCL_SECURE_NO_WARNINGS -HEADERS += completionsettingspage.h \ +include(../../qtcreatorplugin.pri) + +HEADERS += \ + abstracteditorsupport.h \ + builtinindexingsupport.h \ + commentssettings.h \ + completionsettingspage.h \ + cppchecksymbols.h \ cppclassesfilter.h \ + cppcodeformatter.h \ + cppcodemodelsettings.h \ + cppcodemodelsettingspage.h \ + cppcodestylepreferences.h \ + cppcodestylepreferencesfactory.h \ + cppcodestylesettings.h \ + cppcodestylesettingspage.h \ + cppcompletionassist.h \ + cppcompletionassistprovider.h \ cppcurrentdocumentfilter.h \ + cppdoxygen.h \ + cppfilesettingspage.h \ + cppfindreferences.h \ cppfunctionsfilter.h \ + cpphighlightingsupport.h \ + cpphighlightingsupportinternal.h \ + cppindexingsupport.h \ + cpplocalsymbols.h \ + cpplocatordata.h \ + cpplocatorfilter.h \ cppmodelmanager.h \ cppmodelmanagerinterface.h \ - cpplocatorfilter.h \ + cppmodelmanagersupport.h \ + cppmodelmanagersupportinternal.h \ + cpppointerdeclarationformatter.h \ + cpppreprocessor.h \ + cppprojectfile.h \ + cppqtstyleindenter.h \ + cpprefactoringchanges.h \ + cppsemanticinfo.h \ + cppsnapshotupdater.h \ cpptools_global.h \ cpptoolsconstants.h \ cpptoolseditorsupport.h \ - cppsnapshotupdater.h \ cpptoolsplugin.h \ - cppqtstyleindenter.h \ - searchsymbols.h \ - cppdoxygen.h \ - cppfilesettingspage.h \ - cppfindreferences.h \ - cppcodeformatter.h \ - symbolsfindfilter.h \ - insertionpointlocator.h \ - cpprefactoringchanges.h \ - abstracteditorsupport.h \ - cppcompletionassist.h \ - cppcodestylesettingspage.h \ - cpptoolssettings.h \ - cppcodestylesettings.h \ - cppcodestylepreferencesfactory.h \ - cppcodestylepreferences.h \ cpptoolsreuse.h \ + cpptoolssettings.h \ doxygengenerator.h \ - commentssettings.h \ - symbolfinder.h \ - cppmodelmanagersupport.h \ - cpphighlightingsupport.h \ - cpphighlightingsupportinternal.h \ - cppchecksymbols.h \ - cpplocalsymbols.h \ - cppsemanticinfo.h \ - cppcompletionassistprovider.h \ - typehierarchybuilder.h \ - cppindexingsupport.h \ - builtinindexingsupport.h \ - cpppointerdeclarationformatter.h \ - cppprojectfile.h \ - cpppreprocessor.h \ + functionutils.h \ includeutils.h \ - cpplocatordata.h \ - cppmodelmanagersupportinternal.h \ - cppcodemodelsettings.h \ - cppcodemodelsettingspage.h + insertionpointlocator.h \ + searchsymbols.h \ + symbolfinder.h \ + symbolsfindfilter.h \ + typehierarchybuilder.h -SOURCES += completionsettingspage.cpp \ +SOURCES += \ + abstracteditorsupport.cpp \ + builtinindexingsupport.cpp \ + commentssettings.cpp \ + completionsettingspage.cpp \ + cppchecksymbols.cpp \ cppclassesfilter.cpp \ + cppcodeformatter.cpp \ + cppcodemodelsettings.cpp \ + cppcodemodelsettingspage.cpp \ + cppcodestylepreferences.cpp \ + cppcodestylepreferencesfactory.cpp \ + cppcodestylesettings.cpp \ + cppcodestylesettingspage.cpp \ + cppcompletionassist.cpp \ + cppcompletionassistprovider.cpp \ cppcurrentdocumentfilter.cpp \ - cppfunctionsfilter.cpp \ - cppmodelmanager.cpp \ - cppmodelmanagerinterface.cpp \ - cpplocatorfilter.cpp \ - cpptoolseditorsupport.cpp \ - cppsnapshotupdater.cpp \ - cpptoolsplugin.cpp \ - cppqtstyleindenter.cpp \ - searchsymbols.cpp \ cppdoxygen.cpp \ cppfilesettingspage.cpp \ - abstracteditorsupport.cpp \ cppfindreferences.cpp \ - cppcodeformatter.cpp \ - symbolsfindfilter.cpp \ - insertionpointlocator.cpp \ - cpprefactoringchanges.cpp \ - cppcompletionassist.cpp \ - cppcodestylesettingspage.cpp \ - cpptoolssettings.cpp \ - cppcodestylesettings.cpp \ - cppcodestylepreferencesfactory.cpp \ - cppcodestylepreferences.cpp \ - cpptoolsreuse.cpp \ - doxygengenerator.cpp \ - commentssettings.cpp \ - symbolfinder.cpp \ - cppmodelmanagersupport.cpp \ + cppfunctionsfilter.cpp \ cpphighlightingsupport.cpp \ cpphighlightingsupportinternal.cpp \ - cppchecksymbols.cpp \ - cpplocalsymbols.cpp \ - cppsemanticinfo.cpp \ - cppcompletionassistprovider.cpp \ - typehierarchybuilder.cpp \ cppindexingsupport.cpp \ - builtinindexingsupport.cpp \ + cpplocalsymbols.cpp \ + cpplocatordata.cpp \ + cpplocatorfilter.cpp \ + cppmodelmanager.cpp \ + cppmodelmanagerinterface.cpp \ + cppmodelmanagersupport.cpp \ + cppmodelmanagersupportinternal.cpp \ cpppointerdeclarationformatter.cpp \ - cppprojectfile.cpp \ cpppreprocessor.cpp \ + cppprojectfile.cpp \ + cppqtstyleindenter.cpp \ + cpprefactoringchanges.cpp \ + cppsemanticinfo.cpp \ + cppsnapshotupdater.cpp \ + cpptoolseditorsupport.cpp \ + cpptoolsplugin.cpp \ + cpptoolsreuse.cpp \ + cpptoolssettings.cpp \ + doxygengenerator.cpp \ + functionutils.cpp \ includeutils.cpp \ - cpplocatordata.cpp \ - cppmodelmanagersupportinternal.cpp \ - cppcodemodelsettings.cpp \ - cppcodemodelsettingspage.cpp + insertionpointlocator.cpp \ + searchsymbols.cpp \ + symbolfinder.cpp \ + symbolsfindfilter.cpp \ + typehierarchybuilder.cpp -FORMS += completionsettingspage.ui \ - cppfilesettingspage.ui \ +FORMS += \ + completionsettingspage.ui \ + cppcodemodelsettingspage.ui \ cppcodestylesettingspage.ui \ - cppcodemodelsettingspage.ui + cppfilesettingspage.ui equals(TEST, 1) { + HEADERS += \ + cpppreprocessertesthelper.h \ + cpptoolstestcase.h \ + modelmanagertesthelper.h + SOURCES += \ cppcodegen_test.cpp \ cppcompletion_test.cpp \ + cppheadersource_test.cpp \ + cpplocatorfilter_test.cpp \ cppmodelmanager_test.cpp \ - modelmanagertesthelper.cpp \ cpppointerdeclarationformatter_test.cpp \ - cpplocatorfilter_test.cpp \ - symbolsearcher_test.cpp \ - cpppreprocessor_test.cpp \ cpppreprocessertesthelper.cpp \ - cppheadersource_test.cpp \ + cpppreprocessor_test.cpp \ + cpptoolstestcase.cpp \ + modelmanagertesthelper.cpp \ + symbolsearcher_test.cpp \ typehierarchybuilder_test.cpp - HEADERS += \ - cpppreprocessertesthelper.h \ - modelmanagertesthelper.h - DEFINES += SRCDIR=\\\"$$PWD\\\" } diff --git a/src/plugins/cpptools/cpptools.qbs b/src/plugins/cpptools/cpptools.qbs index e955c80e3a..18cf784cb8 100644 --- a/src/plugins/cpptools/cpptools.qbs +++ b/src/plugins/cpptools/cpptools.qbs @@ -22,108 +22,57 @@ QtcPlugin { } files: [ - "abstracteditorsupport.cpp", - "abstracteditorsupport.h", - "commentssettings.cpp", - "commentssettings.h", - "completionsettingspage.cpp", - "completionsettingspage.h", - "completionsettingspage.ui", - "cppchecksymbols.cpp", - "cppchecksymbols.h", - "cppclassesfilter.cpp", - "cppclassesfilter.h", - "cppcodeformatter.cpp", - "cppcodeformatter.h", - "cppcodestylepreferences.cpp", - "cppcodestylepreferences.h", - "cppcodestylepreferencesfactory.cpp", - "cppcodestylepreferencesfactory.h", - "cppcodestylesettings.cpp", - "cppcodestylesettings.h", - "cppcodestylesettingspage.cpp", - "cppcodestylesettingspage.h", - "cppcodestylesettingspage.ui", - "cppcompletionassist.cpp", - "cppcompletionassist.h", - "cppcompletionassistprovider.cpp", - "cppcompletionassistprovider.h", - "cppcurrentdocumentfilter.cpp", - "cppcurrentdocumentfilter.h", - "cppdoxygen.cpp", - "cppdoxygen.h", - "cppfilesettingspage.cpp", - "cppfilesettingspage.h", - "cppfilesettingspage.ui", - "cppfindreferences.cpp", - "cppfindreferences.h", - "cppfunctionsfilter.cpp", - "cppfunctionsfilter.h", - "cpphighlightingsupport.cpp", - "cpphighlightingsupport.h", - "cpphighlightingsupportinternal.cpp", - "cpphighlightingsupportinternal.h", - "cppindexingsupport.cpp", - "cppindexingsupport.h", - "cpplocalsymbols.cpp", - "cpplocalsymbols.h", - "cpplocatordata.cpp", - "cpplocatordata.h", - "cpplocatorfilter.cpp", - "cpplocatorfilter.h", - "cppmodelmanager.cpp", - "cppmodelmanager.h", - "cppmodelmanagersupport.h", - "cppmodelmanagersupport.cpp", - "cppmodelmanagersupportinternal.h", - "cppmodelmanagersupportinternal.cpp", - "cppmodelmanagerinterface.cpp", - "cppmodelmanagerinterface.h", - "cppqtstyleindenter.cpp", - "cppqtstyleindenter.h", - "cpppointerdeclarationformatter.cpp", - "cpppointerdeclarationformatter.h", - "cppprojectfile.cpp", - "cppprojectfile.h", - "cpprefactoringchanges.cpp", - "cpprefactoringchanges.h", - "cppsemanticinfo.cpp", - "cppsemanticinfo.h", + "abstracteditorsupport.cpp", "abstracteditorsupport.h", + "builtinindexingsupport.cpp", "builtinindexingsupport.h", + "commentssettings.cpp", "commentssettings.h", + "completionsettingspage.cpp", "completionsettingspage.h", "completionsettingspage.ui", + "cppchecksymbols.cpp", "cppchecksymbols.h", + "cppclassesfilter.cpp", "cppclassesfilter.h", + "cppcodeformatter.cpp", "cppcodeformatter.h", + "cppcodemodelsettings.cpp", "cppcodemodelsettings.h", + "cppcodemodelsettingspage.cpp", "cppcodemodelsettingspage.h", "cppcodemodelsettingspage.ui", + "cppcodestylepreferences.cpp", "cppcodestylepreferences.h", + "cppcodestylepreferencesfactory.cpp", "cppcodestylepreferencesfactory.h", + "cppcodestylesettings.cpp", "cppcodestylesettings.h", + "cppcodestylesettingspage.cpp", "cppcodestylesettingspage.h", "cppcodestylesettingspage.ui", + "cppcompletionassist.cpp", "cppcompletionassist.h", + "cppcompletionassistprovider.cpp", "cppcompletionassistprovider.h", + "cppcurrentdocumentfilter.cpp", "cppcurrentdocumentfilter.h", + "cppdoxygen.cpp", "cppdoxygen.h", + "cppfilesettingspage.cpp", "cppfilesettingspage.h", "cppfilesettingspage.ui", + "cppfindreferences.cpp", "cppfindreferences.h", + "cppfunctionsfilter.cpp", "cppfunctionsfilter.h", + "cpphighlightingsupport.cpp", "cpphighlightingsupport.h", + "cpphighlightingsupportinternal.cpp", "cpphighlightingsupportinternal.h", + "cppindexingsupport.cpp", "cppindexingsupport.h", + "cpplocalsymbols.cpp", "cpplocalsymbols.h", + "cpplocatordata.cpp", "cpplocatordata.h", + "cpplocatorfilter.cpp", "cpplocatorfilter.h", + "cppmodelmanager.cpp", "cppmodelmanager.h", + "cppmodelmanagerinterface.cpp", "cppmodelmanagerinterface.h", + "cppmodelmanagersupport.cpp", "cppmodelmanagersupport.h", + "cppmodelmanagersupportinternal.cpp", "cppmodelmanagersupportinternal.h", + "cpppointerdeclarationformatter.cpp", "cpppointerdeclarationformatter.h", + "cpppreprocessor.cpp", "cpppreprocessor.h", + "cppprojectfile.cpp", "cppprojectfile.h", + "cppqtstyleindenter.cpp", "cppqtstyleindenter.h", + "cpprefactoringchanges.cpp", "cpprefactoringchanges.h", + "cppsemanticinfo.cpp", "cppsemanticinfo.h", + "cppsnapshotupdater.cpp", "cppsnapshotupdater.h", "cpptools_global.h", "cpptoolsconstants.h", - "cpptoolseditorsupport.cpp", - "cpptoolseditorsupport.h", - "cpptoolsplugin.cpp", - "cpptoolsplugin.h", - "cpptoolsreuse.cpp", - "cpptoolsreuse.h", - "cpptoolssettings.cpp", - "cpptoolssettings.h", - "doxygengenerator.cpp", - "doxygengenerator.h", - "insertionpointlocator.cpp", - "insertionpointlocator.h", - "searchsymbols.cpp", - "searchsymbols.h", - "symbolfinder.cpp", - "symbolfinder.h", - "symbolsfindfilter.cpp", - "symbolsfindfilter.h", - "typehierarchybuilder.cpp", - "typehierarchybuilder.h", - "builtinindexingsupport.cpp", - "builtinindexingsupport.h", - "cpppreprocessor.cpp", - "cpppreprocessor.h", - "includeutils.cpp", - "includeutils.h", - "cppcodemodelsettings.cpp", - "cppcodemodelsettings.h", - "cppcodemodelsettingspage.cpp", - "cppcodemodelsettingspage.h", - "cppcodemodelsettingspage.ui", - "cppsnapshotupdater.cpp", - "cppsnapshotupdater.h", + "cpptoolseditorsupport.cpp", "cpptoolseditorsupport.h", + "cpptoolsplugin.cpp", "cpptoolsplugin.h", + "cpptoolsreuse.cpp", "cpptoolsreuse.h", + "cpptoolssettings.cpp", "cpptoolssettings.h", + "doxygengenerator.cpp", "doxygengenerator.h", + "functionutils.cpp", "functionutils.h", + "includeutils.cpp", "includeutils.h", + "insertionpointlocator.cpp", "insertionpointlocator.h", + "searchsymbols.cpp", "searchsymbols.h", + "symbolfinder.cpp", "symbolfinder.h", + "symbolsfindfilter.cpp", "symbolsfindfilter.h", + "typehierarchybuilder.cpp", "typehierarchybuilder.h", ] Group { @@ -133,14 +82,15 @@ QtcPlugin { "cppcodegen_test.cpp", "cppcompletion_test.cpp", "cppheadersource_test.cpp", + "cpplocatorfilter_test.cpp", "cppmodelmanager_test.cpp", - "modelmanagertesthelper.cpp", "modelmanagertesthelper.h", "cpppointerdeclarationformatter_test.cpp", - "cpplocatorfilter_test.cpp", - "symbolsearcher_test.cpp", - "cpppreprocessor_test.cpp", "cpppreprocessertesthelper.cpp", "cpppreprocessertesthelper.h", - "typehierarchybuilder_test.cpp" + "cpppreprocessor_test.cpp", + "cpptoolstestcase.cpp", "cpptoolstestcase.h", + "modelmanagertesthelper.cpp", "modelmanagertesthelper.h", + "symbolsearcher_test.cpp", + "typehierarchybuilder_test.cpp", ] cpp.defines: outer.concat(['SRCDIR="' + FileInfo.path(filePath) + '"']) diff --git a/src/plugins/cpptools/cpptoolseditorsupport.cpp b/src/plugins/cpptools/cpptoolseditorsupport.cpp index a07c686cb5..faf2af37fe 100644 --- a/src/plugins/cpptools/cpptoolseditorsupport.cpp +++ b/src/plugins/cpptools/cpptoolseditorsupport.cpp @@ -186,7 +186,7 @@ QByteArray CppEditorSupport::contents() const const int editorRev = editorRevision(); if (m_cachedContentsEditorRevision != editorRev && !m_fileIsBeingReloaded) { m_cachedContentsEditorRevision = editorRev; - m_cachedContents = m_textEditor->textDocument()->contents().toUtf8(); + m_cachedContents = m_textEditor->textDocument()->plainText().toUtf8(); } return m_cachedContents; @@ -392,10 +392,14 @@ void CppEditorSupport::startHighlighting() m_lastHighlightRevision = revision; emit highlighterStarted(&m_highlighter, m_lastHighlightRevision); } else { + const unsigned revision = currentSource(false).revision; + if (m_lastHighlightRevision == revision) + return; + + m_lastHighlightRevision = revision; static const Document::Ptr dummyDoc; static const Snapshot dummySnapshot; m_highlighter = m_highlightingSupport->highlightingFuture(dummyDoc, dummySnapshot); - m_lastHighlightRevision = editorRevision(); emit highlighterStarted(&m_highlighter, m_lastHighlightRevision); } } @@ -407,7 +411,7 @@ void CppEditorSupport::onDiagnosticsChanged() QList<Document::DiagnosticMessage> allDiagnostics; { QMutexLocker locker(&m_diagnosticsMutex); - foreach (const QList<Document::DiagnosticMessage> &msgs, m_allDiagnostics.values()) + foreach (const QList<Document::DiagnosticMessage> &msgs, m_allDiagnostics) allDiagnostics.append(msgs); } @@ -506,7 +510,7 @@ SemanticInfo::Source CppEditorSupport::currentSource(bool force) int line = 0, column = 0; m_textEditor->convertPosition(m_textEditor->editorWidget()->position(), &line, &column); - const Snapshot snapshot = m_snapshotUpdater->snapshot(); + const Snapshot snapshot = snapshotUpdater()->snapshot(); QByteArray code; if (force || m_lastSemanticInfo.revision != editorRevision()) diff --git a/src/plugins/cpptools/cpptoolsplugin.h b/src/plugins/cpptools/cpptoolsplugin.h index 067c484bb5..c5d573e1cb 100644 --- a/src/plugins/cpptools/cpptoolsplugin.h +++ b/src/plugins/cpptools/cpptoolsplugin.h @@ -91,106 +91,20 @@ private slots: void test_codegen_definition_middle_member_surrounded_by_undefined(); void test_codegen_definition_member_specific_file(); - void test_completion_forward_declarations_present(); - void test_completion_inside_parentheses_c_style_conversion(); - void test_completion_inside_parentheses_cast_operator_conversion(); void test_completion_basic_1(); - void test_completion_template_1(); - void test_completion_template_2(); - void test_completion_template_3(); - void test_completion_template_4(); - void test_completion_template_5(); - void test_completion_template_6(); - void test_completion_template_7(); - void test_completion_type_of_pointer_is_typedef(); - void test_completion_instantiate_full_specialization(); - void test_completion_template_as_base(); - void test_completion_template_as_base_data(); - void test_completion_use_global_identifier_as_base_class(); - void test_completion_use_global_identifier_as_base_class_data(); - void test_completion_base_class_has_name_the_same_as_derived(); - void test_completion_base_class_has_name_the_same_as_derived_data(); - void test_completion_cyclic_inheritance(); - void test_completion_cyclic_inheritance_data(); - void test_completion_template_function(); + void test_completion_template_function_data(); - void test_completion_enclosing_template_class(); - void test_completion_enclosing_template_class_data(); - void test_completion_instantiate_nested_class_when_enclosing_is_template(); - void test_completion_instantiate_nested_of_nested_class_when_enclosing_is_template(); - void test_completion_instantiate_template_with_default_argument_type(); - void test_completion_instantiate_template_with_default_argument_type_as_template(); - void test_completion_member_access_operator_1(); - - void test_completion_typedef_of_type_and_decl_of_type_no_replace_access_operator(); - void test_completion_typedef_of_pointer_and_decl_of_pointer_no_replace_access_operator(); - void test_completion_typedef_of_type_and_decl_of_pointer_replace_access_operator(); - void test_completion_typedef_of_pointer_and_decl_of_type_replace_access_operator(); - - void test_completion_predecl_typedef_of_type_and_decl_of_pointer_replace_access_operator(); - void test_completion_predecl_typedef_of_type_and_decl_type_no_replace_access_operator(); - void test_completion_predecl_typedef_of_pointer_and_decl_of_pointer_no_replace_access_operator(); - void test_completion_predecl_typedef_of_pointer_and_decl_of_type_replace_access_operator(); - - void test_completion_typedef_of_pointer(); - void test_completion_typedef_of_pointer_inside_function(); - void test_completion_typedef_is_inside_function_before_declaration_block(); - void test_completion_resolve_complex_typedef_with_template(); - void test_completion_template_specialization_with_pointer(); - void test_completion_typedef_using_templates1(); - void test_completion_typedef_using_templates2(); - void test_completion_namespace_alias_with_many_namespace_declarations(); - void test_completion_QTCREATORBUG9098(); - void test_completion_type_and_using_declaration(); - void test_completion_type_and_using_declaration_data(); - void test_completion_instantiate_template_with_anonymous_class(); - void test_completion_instantiate_template_function(); - void test_completion_crash_cloning_template_class_QTCREATORBUG9329(); - void test_completion_recursive_auto_declarations1_QTCREATORBUG9503(); - void test_completion_recursive_auto_declarations2_QTCREATORBUG9503(); - void test_completion_recursive_typedefs_declarations1(); - void test_completion_recursive_typedefs_declarations2(); - void test_completion_recursive_using_declarations1(); - void test_completion_recursive_using_declarations2(); - void test_completion_recursive_using_typedef_declarations(); - void test_completion_recursive_typedefs_in_templates1(); - void test_completion_recursive_typedefs_in_templates2(); + void test_completion_template_function(); + + void test_completion_data(); + void test_completion(); + + void test_completion_member_access_operator_data(); + void test_completion_member_access_operator(); + void test_completion_prefix_first_QTCREATORBUG_8737(); void test_completion_prefix_first_QTCREATORBUG_9236(); - void test_completion_class_declaration_inside_function_or_block_QTCREATORBUG3620(); - void test_completion_class_declaration_inside_function_or_block_QTCREATORBUG3620_data(); - void test_completion_namespace_alias_inside_function_or_block_QTCREATORBUG166(); - void test_completion_namespace_alias_inside_function_or_block_QTCREATORBUG166_data(); - void test_completion_class_declaration_inside_function_or_block_QTCREATORBUG3620_static_member(); - void test_completion_enum_inside_block_inside_function_QTCREATORBUG5456(); - void test_completion_enum_inside_function_QTCREATORBUG5456(); - - void test_completion_template_parameter_defined_inside_scope_of_declaration_QTCREATORBUG9169_1(); - void test_completion_template_parameter_defined_inside_scope_of_declaration_QTCREATORBUG9169_2(); - void test_completion_template_parameter_defined_inside_scope_of_declaration_QTCREATORBUG8852_1(); - void test_completion_template_parameter_defined_inside_scope_of_declaration_QTCREATORBUG8852_2(); - void test_completion_template_parameter_defined_inside_scope_of_declaration_QTCREATORBUG8852_3(); - - //lambda - void test_completion_lambdaCalls_1(); - void test_completion_lambdaCalls_2(); - void test_completion_lambdaCalls_3(); - void test_completion_lambdaCalls_4(); - void test_completion_lambdaCalls_5(); - - void test_completion_member_of_class_accessed_by_using_QTCREATORBUG9037_1(); - void test_completion_member_of_class_accessed_by_using_QTCREATORBUG9037_2(); - - void test_completion_local_type_and_member_1(); - void test_completion_local_type_and_member_2(); - void test_completion_local_type_and_member_3(); - void test_completion_local_type_and_member_4(); - void test_completion_local_type_and_member_5(); - void test_completion_local_type_and_member_6(); - - void test_completion_signals_hide_QPrivateSignal(); - void test_format_pointerdeclaration_in_simpledeclarations(); void test_format_pointerdeclaration_in_simpledeclarations_data(); void test_format_pointerdeclaration_in_controlflowstatements(); @@ -204,6 +118,9 @@ private slots: void test_cpppreprocessor_includes(); + void test_functionutils_virtualFunctions(); + void test_functionutils_virtualFunctions_data(); + void test_modelmanager_paths_are_clean(); void test_modelmanager_framework_headers(); void test_modelmanager_refresh_also_includes_of_project_files(); @@ -232,9 +149,6 @@ private slots: void test_typehierarchy_data(); void test_typehierarchy(); - -private: - void test_completion(); #endif private: diff --git a/src/plugins/cpptools/cpptoolstestcase.cpp b/src/plugins/cpptools/cpptoolstestcase.cpp new file mode 100644 index 0000000000..e75df702d8 --- /dev/null +++ b/src/plugins/cpptools/cpptoolstestcase.cpp @@ -0,0 +1,179 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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 "cpptoolstestcase.h" + +#include <coreplugin/editormanager/editormanager.h> + +#include <cplusplus/CppDocument.h> +#include <utils/fileutils.h> + +#include <QtTest> + +static bool closeEditorsWithoutGarbageCollectorInvocation(const QList<Core::IEditor *> &editors) +{ + CppTools::CppModelManagerInterface::instance()->enableGarbageCollector(false); + const bool closeEditorsSucceeded = Core::EditorManager::closeEditors(editors, false); + CppTools::CppModelManagerInterface::instance()->enableGarbageCollector(true); + return closeEditorsSucceeded; +} + +static bool snapshotContains(const CPlusPlus::Snapshot &snapshot, const QStringList &filePaths) +{ + foreach (const QString &filePath, filePaths) { + if (!snapshot.contains(filePath)) { + const QString warning = QLatin1String("Missing file in snapshot: ") + filePath; + QWARN(qPrintable(warning)); + return false; + } + } + return true; +} + +namespace CppTools { +namespace Tests { + +TestDocument::TestDocument(const QByteArray &fileName, const QByteArray &source, char cursorMarker) + : m_fileName(fileName), m_source(source), m_cursorMarker(cursorMarker) +{} + +QString TestDocument::filePath() const +{ + const QString fileNameAsString = QString::fromUtf8(m_fileName); + if (!QFileInfo(fileNameAsString).isAbsolute()) + return QDir::tempPath() + QLatin1Char('/') + fileNameAsString; + return fileNameAsString; +} + +bool TestDocument::writeToDisk() const +{ + return TestCase::writeFile(filePath(), m_source); +} + +TestCase::TestCase(bool runGarbageCollector) + : m_modelManager(CppModelManagerInterface::instance()) + , m_succeededSoFar(false) + , m_runGarbageCollector(runGarbageCollector) +{ + if (m_runGarbageCollector) + QVERIFY(garbageCollectGlobalSnapshot()); + m_succeededSoFar = true; +} + +TestCase::~TestCase() +{ + QVERIFY(closeEditorsWithoutGarbageCollectorInvocation(m_editorsToClose)); + QCoreApplication::processEvents(); + + if (m_runGarbageCollector) + QVERIFY(garbageCollectGlobalSnapshot()); +} + +bool TestCase::succeededSoFar() const +{ + return m_succeededSoFar; +} + +CPlusPlus::Snapshot TestCase::globalSnapshot() +{ + return CppModelManagerInterface::instance()->snapshot(); +} + +bool TestCase::garbageCollectGlobalSnapshot() +{ + CppModelManagerInterface::instance()->GC(); + return globalSnapshot().isEmpty(); +} + +bool TestCase::parseFiles(const QStringList &filePaths) +{ + CppModelManagerInterface::instance()->updateSourceFiles(filePaths).waitForFinished(); + QCoreApplication::processEvents(); + const CPlusPlus::Snapshot snapshot = globalSnapshot(); + if (snapshot.isEmpty()) { + QWARN("After parsing: snapshot is empty."); + return false; + } + if (!snapshotContains(snapshot, filePaths)) { + QWARN("After parsing: snapshot does not contain all expected files."); + return false; + } + return true; +} + +bool TestCase::parseFiles(const QString &filePath) +{ + return parseFiles(QStringList(filePath)); +} + +void TestCase::closeEditorAtEndOfTestCase(Core::IEditor *editor) +{ + if (editor && !m_editorsToClose.contains(editor)) + m_editorsToClose.append(editor); +} + +bool TestCase::closeEditorWithoutGarbageCollectorInvocation(Core::IEditor *editor) +{ + return closeEditorsWithoutGarbageCollectorInvocation(QList<Core::IEditor *>() << editor); +} + +CPlusPlus::Document::Ptr TestCase::waitForFileInGlobalSnapshot(const QString &filePath) +{ + return waitForFilesInGlobalSnapshot(QStringList(filePath)).first(); +} + +QList<CPlusPlus::Document::Ptr> TestCase::waitForFilesInGlobalSnapshot( + const QStringList &filePaths) +{ + QList<CPlusPlus::Document::Ptr> result; + foreach (const QString &filePath, filePaths) { + forever { + if (CPlusPlus::Document::Ptr document = globalSnapshot().document(filePath)) { + result.append(document); + break; + } + QCoreApplication::processEvents(); + } + } + return result; +} + +bool TestCase::writeFile(const QString &filePath, const QByteArray &contents) +{ + Utils::FileSaver saver(filePath); + if (!saver.write(contents) || !saver.finalize()) { + const QString warning = QLatin1String("Failed to write file to disk: ") + filePath; + QWARN(qPrintable(warning)); + return false; + } + return true; +} + +} // namespace Tests +} // namespace CppTools diff --git a/src/plugins/cpptools/cpptoolstestcase.h b/src/plugins/cpptools/cpptoolstestcase.h new file mode 100644 index 0000000000..c0f161bef2 --- /dev/null +++ b/src/plugins/cpptools/cpptoolstestcase.h @@ -0,0 +1,102 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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. +** +****************************************************************************/ + +#ifndef CPPTOOLSTESTCASE_H +#define CPPTOOLSTESTCASE_H + +#include "cppmodelmanagerinterface.h" +#include "cpptools_global.h" + +#include <coreplugin/editormanager/ieditor.h> + +#include <QStringList> + +namespace CPlusPlus { +class Document; +class Snapshot; +} +namespace Core { +class IEditor; +} + +namespace CppTools { +namespace Tests { + +class CPPTOOLS_EXPORT TestDocument +{ +public: + TestDocument(const QByteArray &fileName, const QByteArray &source, char cursorMarker = '@'); + + QString filePath() const; + bool writeToDisk() const; + +public: + QByteArray m_fileName; + QByteArray m_source; + char m_cursorMarker; +}; + +class CPPTOOLS_EXPORT TestCase +{ + Q_DISABLE_COPY(TestCase) + +public: + TestCase(bool runGarbageCollector = true); + ~TestCase(); + + bool succeededSoFar() const; + void closeEditorAtEndOfTestCase(Core::IEditor *editor); + + static bool closeEditorWithoutGarbageCollectorInvocation(Core::IEditor *editor); + + static bool parseFiles(const QString &filePath); + static bool parseFiles(const QStringList &filePaths); + + static CPlusPlus::Snapshot globalSnapshot(); + static bool garbageCollectGlobalSnapshot(); + + static CPlusPlus::Document::Ptr waitForFileInGlobalSnapshot(const QString &filePath); + static QList<CPlusPlus::Document::Ptr> waitForFilesInGlobalSnapshot( + const QStringList &filePaths); + + static bool writeFile(const QString &filePath, const QByteArray &contents); + +protected: + CppModelManagerInterface *m_modelManager; + bool m_succeededSoFar; + +private: + QList<Core::IEditor *> m_editorsToClose; + bool m_runGarbageCollector; +}; + +} // namespace Tests +} // namespace CppTools + +#endif // CPPTOOLSTESTCASE_H diff --git a/src/plugins/cpptools/functionutils.cpp b/src/plugins/cpptools/functionutils.cpp new file mode 100644 index 0000000000..0d9b236003 --- /dev/null +++ b/src/plugins/cpptools/functionutils.cpp @@ -0,0 +1,323 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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 "functionutils.h" + +#include "typehierarchybuilder.h" + +#include <cplusplus/CppDocument.h> +#include <cplusplus/LookupContext.h> +#include <cplusplus/Symbols.h> +#include <utils/qtcassert.h> + +#include <QList> + +using namespace CPlusPlus; +using namespace CppTools; + +enum VirtualType { Virtual, PureVirtual }; + +static bool isVirtualFunction_helper(const Function *function, + const LookupContext &context, + VirtualType virtualType, + const Function **firstVirtual) +{ + enum { Unknown, False, True } res = Unknown; + + if (firstVirtual) + *firstVirtual = 0; + + if (!function) + return false; + + if (virtualType == PureVirtual) + res = function->isPureVirtual() ? True : False; + + if (function->isVirtual()) { + if (firstVirtual) + *firstVirtual = function; + if (res == Unknown) + res = True; + } + + if (!firstVirtual && res != Unknown) + return res == True; + + QList<LookupItem> results = context.lookup(function->name(), function->enclosingScope()); + if (!results.isEmpty()) { + const bool isDestructor = function->name()->isDestructorNameId(); + foreach (const LookupItem &item, results) { + if (Symbol *symbol = item.declaration()) { + if (Function *functionType = symbol->type()->asFunctionType()) { + if (functionType->name()->isDestructorNameId() != isDestructor) + continue; + if (functionType == function) // already tested + continue; + if (functionType->isFinal()) + return res == True; + if (functionType->isVirtual()) { + if (!firstVirtual) + return true; + if (res == Unknown) + res = True; + *firstVirtual = functionType; + } + } + } + } + } + + return res == True; +} + +bool FunctionUtils::isVirtualFunction(const Function *function, + const LookupContext &context, + const Function **firstVirtual) +{ + return isVirtualFunction_helper(function, context, Virtual, firstVirtual); +} + +bool FunctionUtils::isPureVirtualFunction(const Function *function, + const LookupContext &context, + const Function **firstVirtual) +{ + return isVirtualFunction_helper(function, context, PureVirtual, firstVirtual); +} + +QList<Symbol *> FunctionUtils::overrides(Function *function, Class *functionsClass, + Class *staticClass, const Snapshot &snapshot) +{ + QList<Symbol *> result; + QTC_ASSERT(function && functionsClass && staticClass, return result); + + FullySpecifiedType referenceType = function->type(); + const Name *referenceName = function->name(); + QTC_ASSERT(referenceName && referenceType.isValid(), return result); + + // Find overrides + TypeHierarchyBuilder builder(staticClass, snapshot); + const TypeHierarchy &staticClassHierarchy = builder.buildDerivedTypeHierarchy(); + + QList<TypeHierarchy> l; + l.append(TypeHierarchy(functionsClass)); + l.append(staticClassHierarchy); + + while (!l.isEmpty()) { + // Add derived + const TypeHierarchy hierarchy = l.takeFirst(); + QTC_ASSERT(hierarchy.symbol(), continue); + Class *c = hierarchy.symbol()->asClass(); + QTC_ASSERT(c, continue); + + foreach (const TypeHierarchy &t, hierarchy.hierarchy()) { + if (!l.contains(t)) + l << t; + } + + // Check member functions + for (int i = 0, total = c->memberCount(); i < total; ++i) { + Symbol *candidate = c->memberAt(i); + const Name *candidateName = candidate->name(); + const Function *candidateFunc = candidate->type()->asFunctionType(); + if (!candidateName || !candidateFunc) + continue; + if (candidateName->isEqualTo(referenceName) + && candidateFunc->isSignatureEqualTo(function)) { + result << candidate; + } + } + } + + return result; +} + +#ifdef WITH_TESTS +#include "cpptoolsplugin.h" + +#include <QTest> + +namespace CppTools { +namespace Internal { + +enum Virtuality +{ + NotVirtual, + Virtual, + PureVirtual +}; +typedef QList<Virtuality> VirtualityList; +} // Internal namespace +} // CppTools namespace + +Q_DECLARE_METATYPE(CppTools::Internal::Virtuality) +Q_DECLARE_METATYPE(CppTools::Internal::VirtualityList) +Q_DECLARE_METATYPE(QList<int>) + +namespace CppTools { +namespace Internal { + +void CppToolsPlugin::test_functionutils_virtualFunctions() +{ + // Create and parse document + QFETCH(QByteArray, source); + QFETCH(VirtualityList, virtualityList); + QFETCH(QList<int>, firstVirtualList); + Document::Ptr document = Document::create(QLatin1String("virtuals")); + document->setUtf8Source(source); + document->check(); // calls parse(); + QCOMPARE(document->diagnosticMessages().size(), 0); + QVERIFY(document->translationUnit()->ast()); + QList<const Function *> allFunctions; + const Function *firstVirtual = 0; + + // Iterate through Function symbols + Snapshot snapshot; + snapshot.insert(document); + const LookupContext context(document, snapshot); + Control *control = document->translationUnit()->control(); + Symbol **end = control->lastSymbol(); + for (Symbol **it = control->firstSymbol(); it != end; ++it) { + if (const Function *function = (*it)->asFunction()) { + allFunctions.append(function); + QTC_ASSERT(!virtualityList.isEmpty(), return); + Virtuality virtuality = virtualityList.takeFirst(); + QTC_ASSERT(!firstVirtualList.isEmpty(), return); + int firstVirtualIndex = firstVirtualList.takeFirst(); + bool isVirtual = FunctionUtils::isVirtualFunction(function, context, &firstVirtual); + bool isPureVirtual = FunctionUtils::isPureVirtualFunction(function, context, + &firstVirtual); + + // Test for regressions introduced by firstVirtual + QCOMPARE(FunctionUtils::isVirtualFunction(function, context), isVirtual); + QCOMPARE(FunctionUtils::isPureVirtualFunction(function, context), isPureVirtual); + if (isVirtual) { + if (isPureVirtual) + QCOMPARE(virtuality, PureVirtual); + else + QCOMPARE(virtuality, Virtual); + } else { + QEXPECT_FAIL("virtual-dtor-dtor", "Not implemented", Abort); + if (allFunctions.size() == 3) + QEXPECT_FAIL("dtor-virtual-dtor-dtor", "Not implemented", Abort); + QCOMPARE(virtuality, NotVirtual); + } + if (firstVirtualIndex == -1) + QVERIFY(!firstVirtual); + else + QCOMPARE(firstVirtual, allFunctions.at(firstVirtualIndex)); + } + } + QVERIFY(virtualityList.isEmpty()); + QVERIFY(firstVirtualList.isEmpty()); +} + +void CppToolsPlugin::test_functionutils_virtualFunctions_data() +{ + typedef QByteArray _; + QTest::addColumn<QByteArray>("source"); + QTest::addColumn<VirtualityList>("virtualityList"); + QTest::addColumn<QList<int> >("firstVirtualList"); + + QTest::newRow("none") + << _("struct None { void foo() {} };\n") + << (VirtualityList() << NotVirtual) + << (QList<int>() << -1); + + QTest::newRow("single-virtual") + << _("struct V { virtual void foo() {} };\n") + << (VirtualityList() << Virtual) + << (QList<int>() << 0); + + QTest::newRow("single-pure-virtual") + << _("struct PV { virtual void foo() = 0; };\n") + << (VirtualityList() << PureVirtual) + << (QList<int>() << 0); + + QTest::newRow("virtual-derived-with-specifier") + << _("struct Base { virtual void foo() {} };\n" + "struct Derived : Base { virtual void foo() {} };\n") + << (VirtualityList() << Virtual << Virtual) + << (QList<int>() << 0 << 0); + + QTest::newRow("virtual-derived-implicit") + << _("struct Base { virtual void foo() {} };\n" + "struct Derived : Base { void foo() {} };\n") + << (VirtualityList() << Virtual << Virtual) + << (QList<int>() << 0 << 0); + + QTest::newRow("not-virtual-then-virtual") + << _("struct Base { void foo() {} };\n" + "struct Derived : Base { virtual void foo() {} };\n") + << (VirtualityList() << NotVirtual << Virtual) + << (QList<int>() << -1 << 1); + + QTest::newRow("virtual-final-not-virtual") + << _("struct Base { virtual void foo() {} };\n" + "struct Derived : Base { void foo() final {} };\n" + "struct Derived2 : Derived { void foo() {} };") + << (VirtualityList() << Virtual << Virtual << NotVirtual) + << (QList<int>() << 0 << 0 << -1); + + QTest::newRow("virtual-then-pure") + << _("struct Base { virtual void foo() {} };\n" + "struct Derived : Base { virtual void foo() = 0; };\n" + "struct Derived2 : Derived { void foo() {} };") + << (VirtualityList() << Virtual << PureVirtual << Virtual) + << (QList<int>() << 0 << 0 << 0); + + QTest::newRow("virtual-virtual-final-not-virtual") + << _("struct Base { virtual void foo() {} };\n" + "struct Derived : Base { virtual void foo() final {} };\n" + "struct Derived2 : Derived { void foo() {} };") + << (VirtualityList() << Virtual << Virtual << NotVirtual) + << (QList<int>() << 0 << 0 << -1); + + QTest::newRow("ctor-virtual-dtor") + << _("struct Base { Base() {} virtual ~Base() {} };\n") + << (VirtualityList() << NotVirtual << Virtual) + << (QList<int>() << -1 << 1); + + QTest::newRow("virtual-dtor-dtor") + << _("struct Base { virtual ~Base() {} };\n" + "struct Derived : Base { ~Derived() {} };\n") + << (VirtualityList() << Virtual << Virtual) + << (QList<int>() << 0 << 0); + + QTest::newRow("dtor-virtual-dtor-dtor") + << _("struct Base { ~Base() {} };\n" + "struct Derived : Base { virtual ~Derived() {} };\n" + "struct Derived2 : Derived { ~Derived2() {} };\n") + << (VirtualityList() << NotVirtual << Virtual << Virtual) + << (QList<int>() << -1 << 1 << 1); +} + +} // namespace Internal +} // namespace CppTools + +#endif diff --git a/src/plugins/qmlprofiler/canvas/qdeclarativecanvastimer.cpp b/src/plugins/cpptools/functionutils.h index 33d94d8e54..0ca362a89d 100644 --- a/src/plugins/qmlprofiler/canvas/qdeclarativecanvastimer.cpp +++ b/src/plugins/cpptools/functionutils.h @@ -27,59 +27,42 @@ ** ****************************************************************************/ -#include "qdeclarativecanvastimer_p.h" +#ifndef FUNCTIONUTILS_H +#define FUNCTIONUTILS_H -#include <QJSEngine> -#include <QJSValue> -#include <qtimer.h> +#include "cpptools_global.h" QT_BEGIN_NAMESPACE +template <class> class QList; +QT_END_NAMESPACE -Q_GLOBAL_STATIC(QList<CanvasTimer*> , activeTimers); - -CanvasTimer::CanvasTimer(QObject *parent, const QJSValue &data) - : QTimer(parent), m_value(data) -{ -} - -void CanvasTimer::handleTimeout() -{ - Q_ASSERT(m_value.isCallable()); - m_value.call(); - if (isSingleShot()) - removeTimer(this); -} - -void CanvasTimer::createTimer(QObject *parent, const QJSValue &val, long timeout, bool singleshot) -{ +namespace CPlusPlus { +class Class; +class Function; +class LookupContext; +class Snapshot; +class Symbol; +} // namespace CPlusPlus - CanvasTimer *timer = new CanvasTimer(parent, val); - timer->setInterval(timeout); - timer->setSingleShot(singleshot); - connect(timer, SIGNAL(timeout()), timer, SLOT(handleTimeout())); - activeTimers()->append(timer); - timer->start(); -} +namespace CppTools { -void CanvasTimer::removeTimer(CanvasTimer *timer) +class CPPTOOLS_EXPORT FunctionUtils { - activeTimers()->removeAll(timer); - timer->deleteLater(); -} +public: + static bool isVirtualFunction(const CPlusPlus::Function *function, + const CPlusPlus::LookupContext &context, + const CPlusPlus::Function **firstVirtual = 0); -void CanvasTimer::removeTimer(const QJSValue &val) -{ - if (!val.isCallable()) - return; + static bool isPureVirtualFunction(const CPlusPlus::Function *function, + const CPlusPlus::LookupContext &context, + const CPlusPlus::Function **firstVirtual = 0); - for (int i = 0 ; i < activeTimers()->count() ; ++i) { - CanvasTimer *timer = activeTimers()->at(i); - if (timer->equals(val)) { - removeTimer(timer); - return; - } - } -} + static QList<CPlusPlus::Symbol *> overrides(CPlusPlus::Function *function, + CPlusPlus::Class *functionsClass, + CPlusPlus::Class *staticClass, + const CPlusPlus::Snapshot &snapshot); +}; -QT_END_NAMESPACE +} // namespace CppTools +#endif // FUNCTIONUTILS_H diff --git a/src/plugins/cpptools/modelmanagertesthelper.cpp b/src/plugins/cpptools/modelmanagertesthelper.cpp index 98de70ddff..b53cd40506 100644 --- a/src/plugins/cpptools/modelmanagertesthelper.cpp +++ b/src/plugins/cpptools/modelmanagertesthelper.cpp @@ -39,6 +39,7 @@ TestProject::TestProject(const QString &name, QObject *parent) : m_name (name) { setParent(parent); + setId(Core::Id::fromString(name)); } TestProject::~TestProject() @@ -102,10 +103,14 @@ ModelManagerTestHelper::Project *ModelManagerTestHelper::createProject(const QSt return tp; } -QStringList ModelManagerTestHelper::waitForRefreshedSourceFiles() +void ModelManagerTestHelper::resetRefreshedSourceFiles() { + m_lastRefreshedSourceFiles.clear(); m_refreshHappened = false; +} +QStringList ModelManagerTestHelper::waitForRefreshedSourceFiles() +{ while (!m_refreshHappened) QCoreApplication::processEvents(); diff --git a/src/plugins/cpptools/modelmanagertesthelper.h b/src/plugins/cpptools/modelmanagertesthelper.h index 3923c96cbe..3292e1950e 100644 --- a/src/plugins/cpptools/modelmanagertesthelper.h +++ b/src/plugins/cpptools/modelmanagertesthelper.h @@ -48,9 +48,6 @@ public: virtual QString displayName() const { return m_name; } - virtual Core::Id id() const - { return Core::Id::fromString(m_name); } - virtual Core::IDocument *document() const { return 0; } @@ -83,6 +80,7 @@ public: Project *createProject(const QString &name); + void resetRefreshedSourceFiles(); QStringList waitForRefreshedSourceFiles(); void waitForFinishedGc(); diff --git a/src/plugins/cpptools/symbolsearcher_test.cpp b/src/plugins/cpptools/symbolsearcher_test.cpp index 0426435029..5056b94263 100644 --- a/src/plugins/cpptools/symbolsearcher_test.cpp +++ b/src/plugins/cpptools/symbolsearcher_test.cpp @@ -31,6 +31,7 @@ #include "builtinindexingsupport.h" #include "cppmodelmanager.h" +#include "cpptoolstestcase.h" #include "searchsymbols.h" #include <coreplugin/testdatadir.h> @@ -43,14 +44,9 @@ using namespace CppTools::Internal; namespace { -class MyTestDataDir : public Core::Internal::Tests::TestDataDir -{ -public: - MyTestDataDir(const QString &testDataDirectory) - : TestDataDir(QLatin1String(SRCDIR "/../../../tests/cppsymbolsearcher/") - + testDataDirectory) - {} -}; +QTC_DECLARE_MYTESTDATADIR("../../../tests/cppsymbolsearcher/") + +inline QString _(const QByteArray &ba) { return QString::fromLatin1(ba, ba.size()); } class ResultData { @@ -84,59 +80,52 @@ public: } } +public: QString m_symbolName; QString m_scope; }; typedef ResultData::ResultDataList ResultDataList; -class SymbolSearcherTest +class SymbolSearcherTestCase : public CppTools::Tests::TestCase { public: /// Takes no ownership of indexingSupportToUse - SymbolSearcherTest(const QString &testFile, CppIndexingSupport *indexingSupportToUse) - : m_modelManager(CppModelManager::instance()) + SymbolSearcherTestCase(const QString &testFile, + CppIndexingSupport *indexingSupportToUse, + const SymbolSearcher::Parameters &searchParameters, + const ResultDataList &expectedResults) + : m_indexingSupportToRestore(0) , m_indexingSupportToUse(indexingSupportToUse) - , m_testFile(testFile) { - QVERIFY(m_indexingSupportToUse); - QVERIFY(m_modelManager->snapshot().isEmpty()); - m_modelManager->updateSourceFiles(QStringList() << m_testFile).waitForFinished(); - QVERIFY(m_modelManager->snapshot().contains(m_testFile)); + QVERIFY(succeededSoFar()); + QVERIFY(m_indexingSupportToUse); + QVERIFY(parseFiles(testFile)); m_indexingSupportToRestore = m_modelManager->indexingSupport(); m_modelManager->setIndexingSupport(m_indexingSupportToUse); - QCoreApplication::processEvents(); - } - ResultDataList run(const SymbolSearcher::Parameters &searchParameters) const - { CppIndexingSupport *indexingSupport = m_modelManager->indexingSupport(); - SymbolSearcher *symbolSearcher - = indexingSupport->createSymbolSearcher(searchParameters, QSet<QString>() << m_testFile); + SymbolSearcher *symbolSearcher = indexingSupport->createSymbolSearcher(searchParameters, + QSet<QString>() << testFile); QFuture<Find::SearchResultItem> search = QtConcurrent::run(&SymbolSearcher::runSearch, symbolSearcher); search.waitForFinished(); ResultDataList results = ResultData::fromSearchResultList(search.results()); - return results; + QCOMPARE(results, expectedResults); } - ~SymbolSearcherTest() + ~SymbolSearcherTestCase() { - m_modelManager->setIndexingSupport(m_indexingSupportToRestore); - m_modelManager->GC(); - QVERIFY(m_modelManager->snapshot().isEmpty()); + if (m_indexingSupportToRestore) + m_modelManager->setIndexingSupport(m_indexingSupportToRestore); } private: - CppModelManager *m_modelManager; CppIndexingSupport *m_indexingSupportToRestore; CppIndexingSupport *m_indexingSupportToUse; - const QString m_testFile; }; -inline QString _(const QByteArray &ba) { return QString::fromLatin1(ba, ba.size()); } - } // anonymous namespace Q_DECLARE_METATYPE(ResultData) @@ -161,10 +150,10 @@ void CppToolsPlugin::test_builtinsymbolsearcher() QFETCH(ResultDataList, expectedResults); QScopedPointer<CppIndexingSupport> builtinIndexingSupport(new BuiltinIndexingSupport); - - SymbolSearcherTest test(testFile, builtinIndexingSupport.data()); - const ResultDataList results = test.run(searchParameters); - QCOMPARE(results, expectedResults); + SymbolSearcherTestCase(testFile, + builtinIndexingSupport.data(), + searchParameters, + expectedResults); } void CppToolsPlugin::test_builtinsymbolsearcher_data() @@ -211,9 +200,11 @@ void CppToolsPlugin::test_builtinsymbolsearcher_data() << ResultData(_("functionDeclaredOnly()"), _("MyNamespace::MyClass")) << ResultData(_("functionDefinedInClass(bool, int)"), _("MyNamespace::MyClass")) << ResultData(_("functionDefinedOutSideClass(char)"), _("MyNamespace::MyClass")) - << ResultData(_("functionDefinedOutSideClassAndNamespace(float)"), _("MyNamespace::MyClass")) + << ResultData(_("functionDefinedOutSideClassAndNamespace(float)"), + _("MyNamespace::MyClass")) << ResultData(_("functionDefinedOutSideClass(char)"), _("MyNamespace::MyClass")) - << ResultData(_("functionDefinedOutSideClassAndNamespace(float)"), _("MyNamespace::MyClass")) + << ResultData(_("functionDefinedOutSideClassAndNamespace(float)"), + _("MyNamespace::MyClass")) << ResultData(_("int myVariable"), _("<anonymous namespace>")) << ResultData(_("myFunction(bool, int)"), _("<anonymous namespace>")) << ResultData(_("MyEnum"), _("<anonymous namespace>")) @@ -222,9 +213,12 @@ void CppToolsPlugin::test_builtinsymbolsearcher_data() << ResultData(_("MyClass"), _("<anonymous namespace>")) << ResultData(_("MyClass()"), _("<anonymous namespace>::MyClass")) << ResultData(_("functionDeclaredOnly()"), _("<anonymous namespace>::MyClass")) - << ResultData(_("functionDefinedInClass(bool, int)"), _("<anonymous namespace>::MyClass")) - << ResultData(_("functionDefinedOutSideClass(char)"), _("<anonymous namespace>::MyClass")) - << ResultData(_("functionDefinedOutSideClass(char)"), _("<anonymous namespace>::MyClass")) + << ResultData(_("functionDefinedInClass(bool, int)"), + _("<anonymous namespace>::MyClass")) + << ResultData(_("functionDefinedOutSideClass(char)"), + _("<anonymous namespace>::MyClass")) + << ResultData(_("functionDefinedOutSideClass(char)"), + _("<anonymous namespace>::MyClass")) << ResultData(_("main()"), _("")) ); @@ -260,10 +254,13 @@ void CppToolsPlugin::test_builtinsymbolsearcher_data() << ResultData(_("myFunction(bool, int)"), _("MyNamespace")) << ResultData(_("functionDefinedInClass(bool, int)"), _("MyNamespace::MyClass")) << ResultData(_("functionDefinedOutSideClass(char)"), _("MyNamespace::MyClass")) - << ResultData(_("functionDefinedOutSideClassAndNamespace(float)"), _("MyNamespace::MyClass")) + << ResultData(_("functionDefinedOutSideClassAndNamespace(float)"), + _("MyNamespace::MyClass")) << ResultData(_("myFunction(bool, int)"), _("<anonymous namespace>")) - << ResultData(_("functionDefinedInClass(bool, int)"), _("<anonymous namespace>::MyClass")) - << ResultData(_("functionDefinedOutSideClass(char)"), _("<anonymous namespace>::MyClass")) + << ResultData(_("functionDefinedInClass(bool, int)"), + _("<anonymous namespace>::MyClass")) + << ResultData(_("functionDefinedOutSideClass(char)"), + _("<anonymous namespace>::MyClass")) ); // Check Enums diff --git a/src/plugins/cpptools/typehierarchybuilder.h b/src/plugins/cpptools/typehierarchybuilder.h index 0a4153ea9f..99241d0099 100644 --- a/src/plugins/cpptools/typehierarchybuilder.h +++ b/src/plugins/cpptools/typehierarchybuilder.h @@ -53,6 +53,9 @@ public: CPlusPlus::Symbol *symbol() const; const QList<TypeHierarchy> &hierarchy() const; + bool operator==(const TypeHierarchy &other) const + { return _symbol == other._symbol; } + private: CPlusPlus::Symbol *_symbol; QList<TypeHierarchy> _hierarchy; diff --git a/src/plugins/cpptools/typehierarchybuilder_test.cpp b/src/plugins/cpptools/typehierarchybuilder_test.cpp index 34b78326d1..e417383532 100644 --- a/src/plugins/cpptools/typehierarchybuilder_test.cpp +++ b/src/plugins/cpptools/typehierarchybuilder_test.cpp @@ -30,10 +30,11 @@ #include "cpptoolsplugin.h" #include "cppmodelmanagerinterface.h" +#include "cpptoolstestcase.h" #include "typehierarchybuilder.h" -#include <cplusplus/SymbolVisitor.h> #include <cplusplus/Overview.h> +#include <cplusplus/SymbolVisitor.h> #include <utils/fileutils.h> #include <QDir> @@ -43,6 +44,8 @@ using namespace CPlusPlus; using namespace CppTools; using namespace CppTools::Internal; +Q_DECLARE_METATYPE(QList<Tests::TestDocument>) + namespace { bool hierarchySorter(const TypeHierarchy &h1, const TypeHierarchy &h2) @@ -89,48 +92,30 @@ private: return true; } +private: Class *m_clazz; }; -struct TestDocument -{ -public: - TestDocument(const QString &fileName, const QString &contents) - : fileName(fileName), contents(contents) {} - - QString fileName; - QString contents; -}; - -class TestCase +class TypeHierarchyBuilderTestCase : public CppTools::Tests::TestCase { public: - TestCase(const QList<TestDocument> &documents, const QString &expectedHierarchy) - : m_modelManager(CppModelManagerInterface::instance()) - , m_documents(documents) - , m_expectedHierarchy(expectedHierarchy) + TypeHierarchyBuilderTestCase(const QList<Tests::TestDocument> &documents, + const QString &expectedHierarchy) { - QVERIFY(m_modelManager->snapshot().isEmpty()); - } + QVERIFY(succeededSoFar()); - void run() - { // Write files QStringList filePaths; - foreach (const TestDocument &document, m_documents) { - const QString filePath = QDir::tempPath() + QLatin1Char('/') + document.fileName; - Utils::FileSaver documentSaver(filePath); - documentSaver.write(document.contents.toUtf8()); - documentSaver.finalize(); - filePaths << filePath; + foreach (const Tests::TestDocument &document, documents) { + QVERIFY(document.writeToDisk()); + filePaths << document.filePath(); } // Parse files - m_modelManager->updateSourceFiles(filePaths).waitForFinished(); + QVERIFY(parseFiles(filePaths)); + const Snapshot snapshot = globalSnapshot(); // Get class for which to generate the hierarchy - const Snapshot snapshot = m_modelManager->snapshot(); - QVERIFY(!snapshot.isEmpty()); const Document::Ptr firstDocument = snapshot.document(filePaths.first()); Class *clazz = FindFirstClassInDocument()(firstDocument); QVERIFY(clazz); @@ -142,40 +127,27 @@ public: const QString actualHierarchy = toString(hierarchy); // Uncomment for updating/generating reference data: // qDebug() << actualHierarchy; - QCOMPARE(actualHierarchy, m_expectedHierarchy); - } - - ~TestCase() - { - m_modelManager->GC(); - QVERIFY(m_modelManager->snapshot().isEmpty()); + QCOMPARE(actualHierarchy, expectedHierarchy); } - -private: - CppModelManagerInterface *m_modelManager; - QList<TestDocument> m_documents; - QString m_expectedHierarchy; }; } // anonymous namespace -Q_DECLARE_METATYPE(QList<TestDocument>) - void CppToolsPlugin::test_typehierarchy_data() { - QTest::addColumn<QList<TestDocument> >("documents"); + QTest::addColumn<QList<Tests::TestDocument> >("documents"); QTest::addColumn<QString>("expectedHierarchy"); - typedef QLatin1String _; + typedef Tests::TestDocument TestDocument; QTest::newRow("basic-single-document") << (QList<TestDocument>() - << TestDocument(_("a.h"), - _("class A {};\n" - "class B : public A {};\n" - "class C1 : public B {};\n" - "class C2 : public B {};\n" - "class D : public C1 {};\n"))) + << TestDocument("a.h", + "class A {};\n" + "class B : public A {};\n" + "class C1 : public B {};\n" + "class C2 : public B {};\n" + "class D : public C1 {};\n")) << QString::fromLatin1( "A\n" " B\n" @@ -185,20 +157,20 @@ void CppToolsPlugin::test_typehierarchy_data() QTest::newRow("basic-multiple-documents") << (QList<TestDocument>() - << TestDocument(_("a.h"), - _("class A {};")) - << TestDocument(_("b.h"), - _("#include \"a.h\"\n" - "class B : public A {};")) - << TestDocument(_("c1.h"), - _("#include \"b.h\"\n" - "class C1 : public B {};")) - << TestDocument(_("c2.h"), - _("#include \"b.h\"\n" - "class C2 : public B {};")) - << TestDocument(_("d.h"), - _("#include \"c1.h\"\n" - "class D : public C1 {};"))) + << TestDocument("a.h", + "class A {};") + << TestDocument("b.h", + "#include \"a.h\"\n" + "class B : public A {};") + << TestDocument("c1.h", + "#include \"b.h\"\n" + "class C1 : public B {};") + << TestDocument("c2.h", + "#include \"b.h\"\n" + "class C2 : public B {};") + << TestDocument("d.h", + "#include \"c1.h\"\n" + "class D : public C1 {};")) << QString::fromLatin1( "A\n" " B\n" @@ -210,9 +182,8 @@ void CppToolsPlugin::test_typehierarchy_data() void CppToolsPlugin::test_typehierarchy() { - QFETCH(QList<TestDocument>, documents); + QFETCH(QList<Tests::TestDocument>, documents); QFETCH(QString, expectedHierarchy); - TestCase testCase(documents, expectedHierarchy); - testCase.run(); + TypeHierarchyBuilderTestCase(documents, expectedHierarchy); } diff --git a/src/plugins/cvs/checkoutwizard.cpp b/src/plugins/cvs/checkoutwizard.cpp index 99dbf3a938..b9660f3d8a 100644 --- a/src/plugins/cvs/checkoutwizard.cpp +++ b/src/plugins/cvs/checkoutwizard.cpp @@ -68,7 +68,7 @@ VcsBase::Command *CheckoutWizard::createCommand(const QList<QWizardPage*> ¶m const CheckoutWizardPage *cwp = qobject_cast<const CheckoutWizardPage *>(parameterPages.front()); QTC_ASSERT(cwp, return 0); const CvsSettings settings = CvsPlugin::instance()->settings(); - const QString binary = settings.cvsBinaryPath; + const QString binary = settings.binaryPath(); QStringList args; const QString repository = cwp->repository(); args << QLatin1String("checkout") << repository; diff --git a/src/plugins/cvs/cvs.pro b/src/plugins/cvs/cvs.pro index 9ecb32b06e..be1d4af402 100644 --- a/src/plugins/cvs/cvs.pro +++ b/src/plugins/cvs/cvs.pro @@ -2,6 +2,7 @@ include(../../qtcreatorplugin.pri) HEADERS += annotationhighlighter.h \ cvsplugin.h \ + cvsclient.h \ cvscontrol.h \ settingspage.h \ cvseditor.h \ @@ -14,6 +15,7 @@ HEADERS += annotationhighlighter.h \ SOURCES += annotationhighlighter.cpp \ cvsplugin.cpp \ + cvsclient.cpp \ cvscontrol.cpp \ settingspage.cpp \ cvseditor.cpp \ diff --git a/src/plugins/cvs/cvs.qbs b/src/plugins/cvs/cvs.qbs index 8d858a7b24..a1021cfc6e 100644 --- a/src/plugins/cvs/cvs.qbs +++ b/src/plugins/cvs/cvs.qbs @@ -20,6 +20,8 @@ QtcPlugin { "checkoutwizardpage.cpp", "checkoutwizardpage.h", "cvs.qrc", + "cvsclient.cpp", + "cvsclient.h", "cvsconstants.h", "cvscontrol.cpp", "cvscontrol.h", diff --git a/src/plugins/cvs/cvsclient.cpp b/src/plugins/cvs/cvsclient.cpp new file mode 100644 index 0000000000..2dfe577e01 --- /dev/null +++ b/src/plugins/cvs/cvsclient.cpp @@ -0,0 +1,182 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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 "cvsclient.h" +#include "cvssettings.h" +#include "cvsconstants.h" + +#include <vcsbase/vcsbaseplugin.h> +#include <vcsbase/vcsbaseeditor.h> +#include <vcsbase/vcsbaseconstants.h> +#include <vcsbase/vcsbaseeditorparameterwidget.h> +#include <utils/synchronousprocess.h> + +#include <QDir> +#include <QFileInfo> +#include <QTextStream> +#include <QDebug> + +namespace Cvs { +namespace Internal { + +class CvsDiffExitCodeInterpreter : public Utils::ExitCodeInterpreter +{ + Q_OBJECT +public: + CvsDiffExitCodeInterpreter(QObject *parent) : Utils::ExitCodeInterpreter(parent) {} + Utils::SynchronousProcessResponse::Result interpretExitCode(int code) const; + +}; + +Utils::SynchronousProcessResponse::Result CvsDiffExitCodeInterpreter::interpretExitCode(int code) const +{ + if (code < 0 || code > 2) + return Utils::SynchronousProcessResponse::FinishedError; + return Utils::SynchronousProcessResponse::Finished; +} + +// Collect all parameters required for a diff to be able to associate them +// with a diff editor and re-run the diff with parameters. +struct CvsDiffParameters +{ + QString workingDir; + QStringList extraOptions; + QStringList files; +}; + +// Parameter widget controlling whitespace diff mode, associated with a parameter +class CvsDiffParameterWidget : public VcsBase::VcsBaseEditorParameterWidget +{ + Q_OBJECT +public: + explicit CvsDiffParameterWidget(CvsClient *client, + const CvsDiffParameters &p, + QWidget *parent = 0); + QStringList arguments() const; + void executeCommand(); + +private: + + CvsClient *m_client; + const CvsDiffParameters m_params; +}; + +CvsDiffParameterWidget::CvsDiffParameterWidget(CvsClient *client, + const CvsDiffParameters &p, + QWidget *parent) + : VcsBase::VcsBaseEditorParameterWidget(parent), m_client(client), m_params(p) +{ + mapSetting(addToggleButton(QLatin1String("-w"), tr("Ignore Whitespace")), + client->settings()->boolPointer(CvsSettings::diffIgnoreWhiteSpaceKey)); + mapSetting(addToggleButton(QLatin1String("-B"), tr("Ignore Blank Lines")), + client->settings()->boolPointer(CvsSettings::diffIgnoreBlankLinesKey)); +} + +QStringList CvsDiffParameterWidget::arguments() const +{ + QStringList args; + args = m_client->settings()->stringValue(CvsSettings::diffOptionsKey).split(QLatin1Char(' '), QString::SkipEmptyParts); + args += VcsBaseEditorParameterWidget::arguments(); + return args; +} + +void CvsDiffParameterWidget::executeCommand() +{ + m_client->diff(m_params.workingDir, m_params.files, m_params.extraOptions); +} + +CvsClient::CvsClient(CvsSettings *settings) : + VcsBase::VcsBaseClient(settings) +{ +} + +CvsSettings *CvsClient::settings() const +{ + return dynamic_cast<CvsSettings *>(VcsBase::VcsBaseClient::settings()); +} + +Core::Id CvsClient::vcsEditorKind(VcsCommand cmd) const +{ + switch (cmd) { + case DiffCommand: + return "CVS Diff Editor"; // TODO: replace by string from cvsconstants.h + default: + return Core::Id(); + } +} + +Utils::ExitCodeInterpreter *CvsClient::exitCodeInterpreter(VcsCommand cmd, QObject *parent) const +{ + switch (cmd) { + case DiffCommand: + return new CvsDiffExitCodeInterpreter(parent); + default: + return 0; + } +} + +void CvsClient::diff(const QString &workingDir, const QStringList &files, + const QStringList &extraOptions) +{ + VcsBaseClient::diff(workingDir, files, extraOptions); +} + +QString CvsClient::findTopLevelForFile(const QFileInfo &file) const +{ + Q_UNUSED(file) + return QString(); +} + +QStringList CvsClient::revisionSpec(const QString &revision) const +{ + Q_UNUSED(revision) + return QStringList(); +} + +VcsBase::VcsBaseClient::StatusItem CvsClient::parseStatusLine(const QString &line) const +{ + Q_UNUSED(line) + return VcsBase::VcsBaseClient::StatusItem(); +} + +VcsBase::VcsBaseEditorParameterWidget *CvsClient::createDiffEditor( + const QString &workingDir, const QStringList &files, const QStringList &extraOptions) +{ + Q_UNUSED(extraOptions) + CvsDiffParameters p; + p.workingDir = workingDir; + p.files = files; + p.extraOptions = extraOptions; + return new CvsDiffParameterWidget(this, p); +} + +} // namespace Internal +} // namespace Cvs + +#include "cvsclient.moc" diff --git a/src/plugins/cvs/cvsclient.h b/src/plugins/cvs/cvsclient.h new file mode 100644 index 0000000000..62ad232bcf --- /dev/null +++ b/src/plugins/cvs/cvsclient.h @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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. +** +****************************************************************************/ + +#ifndef CVSCLIENT_H +#define CVSCLIENT_H + +#include "cvssettings.h" +#include <vcsbase/vcsbaseclient.h> + +namespace Cvs { +namespace Internal { + +class CvsSettings; + +class CvsClient : public VcsBase::VcsBaseClient +{ + Q_OBJECT + +public: + CvsClient(CvsSettings *settings); + + CvsSettings *settings() const; + void diff(const QString &workingDir, const QStringList &files, + const QStringList &extraOptions = QStringList()); + QString findTopLevelForFile(const QFileInfo &file) const; + QStringList revisionSpec(const QString &revision) const; + StatusItem parseStatusLine(const QString &line) const; + + +protected: + Utils::ExitCodeInterpreter *exitCodeInterpreter(VcsCommand cmd, QObject *parent) const; + Core::Id vcsEditorKind(VcsCommand cmd) const; + VcsBase::VcsBaseEditorParameterWidget *createDiffEditor(const QString &workingDir, + const QStringList &files, + const QStringList &extraOptions); +private: +}; + +} // namespace Internal +} // namespace Cvs + +#endif // CVSCLIENT_H diff --git a/src/plugins/cvs/cvscontrol.cpp b/src/plugins/cvs/cvscontrol.cpp index 6627779327..af1b9c0b9d 100644 --- a/src/plugins/cvs/cvscontrol.cpp +++ b/src/plugins/cvs/cvscontrol.cpp @@ -55,7 +55,7 @@ Core::Id CvsControl::id() const bool CvsControl::isConfigured() const { - const QString binary = m_plugin->settings().cvsBinaryPath; + const QString binary = m_plugin->settings().binaryPath(); if (binary.isEmpty()) return false; QFileInfo fi(binary); @@ -81,8 +81,9 @@ bool CvsControl::supportsOperation(Operation operation) const return rc; } -Core::IVersionControl::OpenSupportMode CvsControl::openSupportMode() const +Core::IVersionControl::OpenSupportMode CvsControl::openSupportMode(const QString &fileName) const { + Q_UNUSED(fileName); return OpenOptional; } diff --git a/src/plugins/cvs/cvscontrol.h b/src/plugins/cvs/cvscontrol.h index 7c07517637..9efb2831dd 100644 --- a/src/plugins/cvs/cvscontrol.h +++ b/src/plugins/cvs/cvscontrol.h @@ -52,7 +52,7 @@ public: bool isConfigured() const; bool supportsOperation(Operation operation) const; - OpenSupportMode openSupportMode() const; + OpenSupportMode openSupportMode(const QString &fileName) const; bool vcsOpen(const QString &fileName); bool vcsAdd(const QString &fileName); bool vcsDelete(const QString &filename); diff --git a/src/plugins/cvs/cvsplugin.cpp b/src/plugins/cvs/cvsplugin.cpp index a431190ccc..28a06acda8 100644 --- a/src/plugins/cvs/cvsplugin.cpp +++ b/src/plugins/cvs/cvsplugin.cpp @@ -31,6 +31,7 @@ #include "settingspage.h" #include "cvseditor.h" #include "cvssubmiteditor.h" +#include "cvsclient.h" #include "cvsconstants.h" #include "cvscontrol.h" #include "checkoutwizard.h" @@ -192,6 +193,7 @@ CvsPlugin::CvsPlugin() : CvsPlugin::~CvsPlugin() { + delete m_client; cleanCommitMessageFile(); } @@ -234,7 +236,8 @@ bool CvsPlugin::initialize(const QStringList &arguments, QString *errorMessage) if (!MimeDatabase::addMimeTypes(QLatin1String(":/trolltech.cvs/CVS.mimetypes.xml"), errorMessage)) return false; - m_settings.fromSettings(ICore::settings()); + m_settings.readSettings(ICore::settings()); + m_client = new CvsClient(&m_settings); addAutoReleasedObject(new SettingsPage); @@ -470,7 +473,8 @@ bool CvsPlugin::submitEditorAboutToClose() editor->promptSubmit(tr("Closing CVS Editor"), tr("Do you want to commit the change?"), tr("The commit message check failed. Do you want to commit the change?"), - &newSettings.promptToSubmit, !m_submitActionTriggered); + newSettings.boolPointer(CvsSettings::promptOnSubmitKey), + !m_submitActionTriggered); m_submitActionTriggered = false; switch (answer) { case VcsBaseSubmitEditor::SubmitCanceled: @@ -497,7 +501,7 @@ bool CvsPlugin::submitEditorAboutToClose() void CvsPlugin::diffCommitFiles(const QStringList &files) { - cvsDiff(m_commitRepository, files); + m_client->diff(m_commitRepository, files); } static void setDiffBaseDirectory(IEditor *editor, const QString &db) @@ -506,114 +510,6 @@ static void setDiffBaseDirectory(IEditor *editor, const QString &db) ve->setWorkingDirectory(db); } -// Collect all parameters required for a diff to be able to associate them -// with a diff editor and re-run the diff with parameters. -struct CvsDiffParameters -{ - QString workingDir; - QStringList arguments; - QStringList files; -}; - -// Parameter widget controlling whitespace diff mode, associated with a parameter -// struct. -class CvsDiffParameterWidget : public VcsBaseEditorParameterWidget -{ - Q_OBJECT - -public: - explicit CvsDiffParameterWidget(const CvsDiffParameters &p, QWidget *parent = 0); - -signals: - void reRunDiff(const Cvs::Internal::CvsDiffParameters &); - -public slots: - void triggerReRun(); - -private: - const CvsDiffParameters m_parameters; -}; - -CvsDiffParameterWidget::CvsDiffParameterWidget(const CvsDiffParameters &p, QWidget *parent) : - VcsBaseEditorParameterWidget(parent), m_parameters(p) -{ - setBaseArguments(p.arguments); - addToggleButton(QLatin1String("-w"), tr("Ignore Whitespace")); - addToggleButton(QLatin1String("-B"), tr("Ignore Blank Lines")); - connect(this, SIGNAL(argumentsChanged()), - this, SLOT(triggerReRun())); -} - -void CvsDiffParameterWidget::triggerReRun() -{ - CvsDiffParameters effectiveParameters = m_parameters; - effectiveParameters.arguments = arguments(); - emit reRunDiff(effectiveParameters); -} - -void CvsPlugin::cvsDiff(const QString &workingDir, const QStringList &files) -{ - CvsDiffParameters p; - p.workingDir = workingDir; - p.files = files; - p.arguments = m_settings.cvsDiffOptions.split(QLatin1Char(' '), QString::SkipEmptyParts); - cvsDiff(p); -} - -void CvsPlugin::cvsDiff(const Cvs::Internal::CvsDiffParameters &p) -{ - if (Constants::debug) - qDebug() << Q_FUNC_INFO << p.files; - const QString source = VcsBaseEditorWidget::getSource(p.workingDir, p.files); - QTextCodec *codec = VcsBaseEditorWidget::getCodec(p.workingDir, p.files); - const QString id = VcsBaseEditorWidget::getTitleId(p.workingDir, p.files); - - QStringList args(QLatin1String("diff")); - args.append(p.arguments); - args.append(p.files); - - // CVS returns the diff exit code (1 if files differ), which is - // undistinguishable from a "file not found" error, unfortunately. - const CvsResponse response = - runCvs(p.workingDir, args, m_settings.timeOutMS(), 0, codec); - switch (response.result) { - case CvsResponse::NonNullExitCode: - case CvsResponse::Ok: - break; - case CvsResponse::OtherError: - return; - } - - QString output = fixDiffOutput(response.stdOut); - if (output.isEmpty()) - output = tr("The files do not differ."); - // diff of a single file? re-use an existing view if possible to support - // the common usage pattern of continuously changing and diffing a file - // Show in the same editor if diff has been executed before - const QString tag = VcsBaseEditorWidget::editorTag(DiffOutput, p.workingDir, p.files); - if (IEditor *existingEditor = VcsBaseEditorWidget::locateEditorByTag(tag)) { - existingEditor->document()->setContents(output.toUtf8()); - EditorManager::activateEditor(existingEditor); - setDiffBaseDirectory(existingEditor, p.workingDir); - return; - } - const QString title = QString::fromLatin1("cvs diff %1").arg(id); - IEditor *editor = showOutputInEditor(title, output, DiffOutput, source, codec); - VcsBaseEditorWidget::tagEditor(editor, tag); - setDiffBaseDirectory(editor, p.workingDir); - CvsEditor *diffEditorWidget = qobject_cast<CvsEditor*>(editor->widget()); - QTC_ASSERT(diffEditorWidget, return); - - // Wire up the parameter widget to trigger a re-run on - // parameter change and 'revert' from inside the diff editor. - CvsDiffParameterWidget *pw = new CvsDiffParameterWidget(p); - connect(pw, SIGNAL(reRunDiff(Cvs::Internal::CvsDiffParameters)), - this, SLOT(cvsDiff(Cvs::Internal::CvsDiffParameters))); - connect(diffEditorWidget, SIGNAL(diffChunkReverted(VcsBase::DiffChunk)), - pw, SLOT(triggerReRun())); - diffEditorWidget->setConfigurationWidget(pw); -} - CvsSubmitEditor *CvsPlugin::openCVSSubmitEditor(const QString &fileName) { IEditor *editor = EditorManager::openEditor(fileName, Constants::CVSCOMMITEDITOR_ID); @@ -678,7 +574,7 @@ void CvsPlugin::revertAll() QStringList args; args << QLatin1String("update") << QLatin1String("-C") << state.topLevel(); const CvsResponse revertResponse = - runCvs(state.topLevel(), args, m_settings.timeOutMS(), + runCvs(state.topLevel(), args, m_settings.timeOutMs(), SshPasswordPrompt|ShowStdOutInLogWindow); if (revertResponse.result == CvsResponse::Ok) cvsVersionControl()->emitRepositoryChanged(state.topLevel()); @@ -693,7 +589,7 @@ void CvsPlugin::revertCurrentFile() QStringList args; args << QLatin1String("diff") << state.relativeCurrentFile(); const CvsResponse diffResponse = - runCvs(state.currentFileTopLevel(), args, m_settings.timeOutMS(), 0); + runCvs(state.currentFileTopLevel(), args, m_settings.timeOutMs(), 0); switch (diffResponse.result) { case CvsResponse::Ok: return; // Not modified, diff exit code 0 @@ -715,7 +611,7 @@ void CvsPlugin::revertCurrentFile() args.clear(); args << QLatin1String("update") << QLatin1String("-C") << state.relativeCurrentFile(); const CvsResponse revertResponse = - runCvs(state.currentFileTopLevel(), args, m_settings.timeOutMS(), + runCvs(state.currentFileTopLevel(), args, m_settings.timeOutMs(), SshPasswordPrompt|ShowStdOutInLogWindow); if (revertResponse.result == CvsResponse::Ok) cvsVersionControl()->emitFilesChanged(QStringList(state.currentFile())); @@ -726,7 +622,7 @@ void CvsPlugin::diffProject() const VcsBasePluginState state = currentState(); QTC_ASSERT(state.hasProject(), return); const QString relativeProject = state.relativeCurrentProject(); - cvsDiff(state.currentProjectTopLevel(), + m_client->diff(state.currentProjectTopLevel(), relativeProject.isEmpty() ? QStringList() : QStringList(relativeProject)); } @@ -734,7 +630,7 @@ void CvsPlugin::diffCurrentFile() { const VcsBasePluginState state = currentState(); QTC_ASSERT(state.hasFile(), return); - cvsDiff(state.currentFileTopLevel(), QStringList(state.relativeCurrentFile())); + m_client->diff(state.currentFileTopLevel(), QStringList(state.relativeCurrentFile())); } void CvsPlugin::startCommitCurrentFile() @@ -767,7 +663,7 @@ void CvsPlugin::startCommit(const QString &workingDir, const QString &file) // where we are, so, have stdout/stderr channels merged. QStringList args = QStringList(QLatin1String("status")); const CvsResponse response = - runCvs(workingDir, args, m_settings.timeOutMS(), MergeOutputChannels); + runCvs(workingDir, args, m_settings.timeOutMs(), MergeOutputChannels); if (response.result != CvsResponse::Ok) return; // Get list of added/modified/deleted files and purge out undesired ones @@ -815,7 +711,7 @@ bool CvsPlugin::commit(const QString &messageFile, args << QLatin1String("-F") << messageFile; args.append(fileList); const CvsResponse response = - runCvs(m_commitRepository, args, m_settings.longTimeOutMS(), + runCvs(m_commitRepository, args, 10 * m_settings.timeOutMs(), SshPasswordPrompt|ShowStdOutInLogWindow); return response.result == CvsResponse::Ok ; } @@ -853,7 +749,7 @@ void CvsPlugin::filelog(const QString &workingDir, args << QLatin1String("log"); args.append(file); const CvsResponse response = - runCvs(workingDir, args, m_settings.timeOutMS(), + runCvs(workingDir, args, m_settings.timeOutMs(), SshPasswordPrompt, codec); if (response.result != CvsResponse::Ok) return; @@ -887,7 +783,7 @@ bool CvsPlugin::update(const QString &topLevel, const QString &file) if (!file.isEmpty()) args.append(file); const CvsResponse response = - runCvs(topLevel, args, m_settings.longTimeOutMS(), + runCvs(topLevel, args, 10 * m_settings.timeOutMs(), SshPasswordPrompt|ShowStdOutInLogWindow); const bool ok = response.result == CvsResponse::Ok; if (ok) @@ -934,7 +830,7 @@ bool CvsPlugin::edit(const QString &topLevel, const QStringList &files) QStringList args(QLatin1String("edit")); args.append(files); const CvsResponse response = - runCvs(topLevel, args, m_settings.timeOutMS(), + runCvs(topLevel, args, m_settings.timeOutMs(), ShowStdOutInLogWindow|SshPasswordPrompt); return response.result == CvsResponse::Ok; } @@ -946,7 +842,7 @@ bool CvsPlugin::diffCheckModified(const QString &topLevel, const QStringList &fi QStringList args(QLatin1String("-q")); args << QLatin1String("diff"); args.append(files); - const CvsResponse response = runCvs(topLevel, args, m_settings.timeOutMS(), 0); + const CvsResponse response = runCvs(topLevel, args, m_settings.timeOutMs(), 0); if (response.result == CvsResponse::OtherError) return false; *modified = response.result == CvsResponse::NonNullExitCode; @@ -974,7 +870,7 @@ bool CvsPlugin::unedit(const QString &topLevel, const QStringList &files) args.append(QLatin1String("-y")); args.append(files); const CvsResponse response = - runCvs(topLevel, args, m_settings.timeOutMS(), + runCvs(topLevel, args, m_settings.timeOutMs(), ShowStdOutInLogWindow|SshPasswordPrompt); return response.result == CvsResponse::Ok; } @@ -993,7 +889,7 @@ void CvsPlugin::annotate(const QString &workingDir, const QString &file, args << QLatin1String("-r") << revision; args << file; const CvsResponse response = - runCvs(workingDir, args, m_settings.timeOutMS(), + runCvs(workingDir, args, m_settings.timeOutMs(), SshPasswordPrompt, codec); if (response.result != CvsResponse::Ok) return; @@ -1022,7 +918,7 @@ bool CvsPlugin::status(const QString &topLevel, const QString &file, const QStri if (!file.isEmpty()) args.append(file); const CvsResponse response = - runCvs(topLevel, args, m_settings.timeOutMS(), 0); + runCvs(topLevel, args, m_settings.timeOutMs(), 0); const bool ok = response.result == CvsResponse::Ok; if (ok) showOutputInEditor(title, response.stdOut, OtherContent, topLevel, 0); @@ -1047,7 +943,7 @@ void CvsPlugin::diffRepository() { const VcsBasePluginState state = currentState(); QTC_ASSERT(state.hasTopLevel(), return); - cvsDiff(state.topLevel(), QStringList()); + m_client->diff(state.topLevel(), QStringList()); } void CvsPlugin::statusRepository() @@ -1105,7 +1001,7 @@ bool CvsPlugin::describe(const QString &toplevel, const QString &file, const QStringList args; args << QLatin1String("log") << (QLatin1String("-r") + changeNr) << file; const CvsResponse logResponse = - runCvs(toplevel, args, m_settings.timeOutMS(), SshPasswordPrompt); + runCvs(toplevel, args, m_settings.timeOutMs(), SshPasswordPrompt); if (logResponse.result != CvsResponse::Ok) { *errorMessage = logResponse.message; return false; @@ -1115,7 +1011,7 @@ bool CvsPlugin::describe(const QString &toplevel, const QString &file, const *errorMessage = msgLogParsingFailed(); return false; } - if (m_settings.describeByCommitId) { + if (m_settings.boolValue(CvsSettings::describeByCommitIdKey)) { // Run a log command over the repo, filtering by the commit date // and commit id, collecting all files touched by the commit. const QString commitId = fileLog.front().revisions.front().commitId; @@ -1127,7 +1023,7 @@ bool CvsPlugin::describe(const QString &toplevel, const QString &file, const args << QLatin1String("log") << QLatin1String("-d") << (dateS + QLatin1Char('<') + nextDayS); const CvsResponse repoLogResponse = - runCvs(toplevel, args, m_settings.longTimeOutMS(), SshPasswordPrompt); + runCvs(toplevel, args, 10 * m_settings.timeOutMs(), SshPasswordPrompt); if (repoLogResponse.result != CvsResponse::Ok) { *errorMessage = repoLogResponse.message; return false; @@ -1164,7 +1060,7 @@ bool CvsPlugin::describe(const QString &repositoryPath, QStringList args(QLatin1String("log")); args << (QLatin1String("-r") + it->revisions.front().revision) << it->file; const CvsResponse logResponse = - runCvs(repositoryPath, args, m_settings.timeOutMS(), SshPasswordPrompt); + runCvs(repositoryPath, args, m_settings.timeOutMs(), SshPasswordPrompt); if (logResponse.result != CvsResponse::Ok) { *errorMessage = logResponse.message; return false; @@ -1177,11 +1073,11 @@ bool CvsPlugin::describe(const QString &repositoryPath, if (!isFirstRevision(revision)) { const QString previousRev = previousRevision(revision); QStringList args(QLatin1String("diff")); - args << m_settings.cvsDiffOptions << QLatin1String("-r") << previousRev + args << m_settings.stringValue(CvsSettings::diffOptionsKey) << QLatin1String("-r") << previousRev << QLatin1String("-r") << it->revisions.front().revision << it->file; const CvsResponse diffResponse = - runCvs(repositoryPath, args, m_settings.timeOutMS(), 0, codec); + runCvs(repositoryPath, args, m_settings.timeOutMs(), 0, codec); switch (diffResponse.result) { case CvsResponse::Ok: case CvsResponse::NonNullExitCode: // Diff exit code != 0 @@ -1228,7 +1124,7 @@ CvsResponse CvsPlugin::runCvs(const QString &workingDirectory, unsigned flags, QTextCodec *outputCodec) const { - const QString executable = m_settings.cvsBinaryPath; + const QString executable = m_settings.binaryPath(); CvsResponse response; if (executable.isEmpty()) { response.result = CvsResponse::OtherError; @@ -1286,9 +1182,8 @@ IEditor *CvsPlugin::showOutputInEditor(const QString& title, const QString &outp e->setSource(source); if (codec) e->setCodec(codec); - IEditor *ie = e->editor(); - EditorManager::activateEditor(ie); - return ie; + EditorManager::activateEditor(editor); + return editor; } CvsSettings CvsPlugin::settings() const @@ -1300,7 +1195,7 @@ void CvsPlugin::setSettings(const CvsSettings &s) { if (s != m_settings) { m_settings = s; - m_settings.toSettings(ICore::settings()); + m_settings.writeSettings(ICore::settings()); cvsVersionControl()->emitConfigurationChanged(); } } @@ -1316,7 +1211,7 @@ bool CvsPlugin::vcsAdd(const QString &workingDir, const QString &rawFileName) QStringList args; args << QLatin1String("add") << rawFileName; const CvsResponse response = - runCvs(workingDir, args, m_settings.timeOutMS(), + runCvs(workingDir, args, m_settings.timeOutMs(), SshPasswordPrompt|ShowStdOutInLogWindow); return response.result == CvsResponse::Ok; } @@ -1326,7 +1221,7 @@ bool CvsPlugin::vcsDelete(const QString &workingDir, const QString &rawFileName) QStringList args; args << QLatin1String("remove") << QLatin1String("-f") << rawFileName; const CvsResponse response = - runCvs(workingDir, args, m_settings.timeOutMS(), + runCvs(workingDir, args, m_settings.timeOutMs(), SshPasswordPrompt|ShowStdOutInLogWindow); return response.result == CvsResponse::Ok; } @@ -1372,7 +1267,7 @@ bool CvsPlugin::managesFile(const QString &workingDirectory, const QString &file QStringList args; args << QLatin1String("status") << fileName; const CvsResponse response = - runCvs(workingDirectory, args, m_settings.timeOutMS(), SshPasswordPrompt); + runCvs(workingDirectory, args, m_settings.timeOutMs(), SshPasswordPrompt); if (response.result != CvsResponse::Ok) return false; return !response.stdOut.contains(QLatin1String("Status: Unknown")); @@ -1442,5 +1337,3 @@ void CvsPlugin::testLogResolving() } // namespace Cvs Q_EXPORT_PLUGIN(Cvs::Internal::CvsPlugin) - -#include "cvsplugin.moc" diff --git a/src/plugins/cvs/cvsplugin.h b/src/plugins/cvs/cvsplugin.h index 521e33d361..cccd7af7ba 100644 --- a/src/plugins/cvs/cvsplugin.h +++ b/src/plugins/cvs/cvsplugin.h @@ -56,6 +56,7 @@ namespace Internal { struct CvsDiffParameters; class CvsSubmitEditor; class CvsControl; +class CvsClient; struct CvsResponse { @@ -79,8 +80,6 @@ public: bool initialize(const QStringList &arguments, QString *errorMessage); - void cvsDiff(const QString &workingDir, const QStringList &files); - CvsSubmitEditor *openCVSSubmitEditor(const QString &fileName); CvsSettings settings() const; @@ -124,7 +123,6 @@ private slots: void editCurrentFile(); void uneditCurrentFile(); void uneditCurrentRepository(); - void cvsDiff(const Cvs::Internal::CvsDiffParameters &p); #ifdef WITH_TESTS void testDiffFileResolving_data(); void testDiffFileResolving(); @@ -168,6 +166,8 @@ private: inline CvsControl *cvsVersionControl() const; CvsSettings m_settings; + CvsClient *m_client; + QString m_commitMessageFileName; QString m_commitRepository; diff --git a/src/plugins/cvs/cvssettings.cpp b/src/plugins/cvs/cvssettings.cpp index 9d2629d062..a340d9c32e 100644 --- a/src/plugins/cvs/cvssettings.cpp +++ b/src/plugins/cvs/cvssettings.cpp @@ -35,71 +35,34 @@ #include <QSettings> #include <QTextStream> -static const char groupC[] = "CVS"; -static const char commandKeyC[] = "Command"; -static const char rootC[] = "Root"; -static const char promptToSubmitKeyC[] = "PromptForSubmit"; -static const char diffOptionsKeyC[] = "DiffOptions"; -static const char describeByCommitIdKeyC[] = "DescribeByCommitId"; -static const char defaultDiffOptions[] = "-du"; -static const char timeOutKeyC[] = "TimeOut"; - -enum { defaultTimeOutS = 30 }; - -static QString defaultCommand() -{ - return QLatin1String("cvs" QTC_HOST_EXE_SUFFIX); -} - namespace Cvs { namespace Internal { -CvsSettings::CvsSettings() : - cvsCommand(defaultCommand()), - cvsDiffOptions(QLatin1String(defaultDiffOptions)), - timeOutS(defaultTimeOutS), - promptToSubmit(true), - describeByCommitId(true) -{ -} +const QLatin1String CvsSettings::cvsRootKey("Root"); +const QLatin1String CvsSettings::diffOptionsKey("DiffOptions"); +const QLatin1String CvsSettings::describeByCommitIdKey("DescribeByCommitId"); +const QLatin1String CvsSettings::diffIgnoreWhiteSpaceKey("DiffIgnoreWhiteSpace"); +const QLatin1String CvsSettings::diffIgnoreBlankLinesKey("DiffIgnoreBlankLines"); -void CvsSettings::fromSettings(QSettings *settings) +CvsSettings::CvsSettings() { - settings->beginGroup(QLatin1String(groupC)); - cvsCommand = settings->value(QLatin1String(commandKeyC), defaultCommand()).toString(); - cvsBinaryPath = Utils::Environment::systemEnvironment().searchInPath(cvsCommand); - promptToSubmit = settings->value(QLatin1String(promptToSubmitKeyC), true).toBool(); - cvsRoot = settings->value(QLatin1String(rootC), QString()).toString(); - cvsDiffOptions = settings->value(QLatin1String(diffOptionsKeyC), QLatin1String(defaultDiffOptions)).toString(); - describeByCommitId = settings->value(QLatin1String(describeByCommitIdKeyC), true).toBool(); - timeOutS = settings->value(QLatin1String(timeOutKeyC), defaultTimeOutS).toInt(); - settings->endGroup(); + setSettingsGroup(QLatin1String("CVS")); + declareKey(binaryPathKey, QLatin1String("cvs" QTC_HOST_EXE_SUFFIX)); + declareKey(cvsRootKey, QLatin1String("")); + declareKey(diffOptionsKey, QLatin1String("-du")); + declareKey(describeByCommitIdKey, true); + declareKey(diffIgnoreWhiteSpaceKey, false); + declareKey(diffIgnoreBlankLinesKey, false); } -void CvsSettings::toSettings(QSettings *settings) const +int CvsSettings::timeOutMs() const { - settings->beginGroup(QLatin1String(groupC)); - settings->setValue(QLatin1String(commandKeyC), cvsCommand); - settings->setValue(QLatin1String(promptToSubmitKeyC), promptToSubmit); - settings->setValue(QLatin1String(rootC), cvsRoot); - settings->setValue(QLatin1String(diffOptionsKeyC), cvsDiffOptions); - settings->setValue(QLatin1String(timeOutKeyC), timeOutS); - settings->setValue(QLatin1String(describeByCommitIdKeyC), describeByCommitId); - settings->endGroup(); -} - -bool CvsSettings::equals(const CvsSettings &s) const -{ - return promptToSubmit == s.promptToSubmit - && describeByCommitId == s.describeByCommitId - && cvsCommand == s.cvsCommand - && cvsRoot == s.cvsRoot - && timeOutS == s.timeOutS - && cvsDiffOptions == s.cvsDiffOptions; + return 1000 * intValue(timeoutKey); } QStringList CvsSettings::addOptions(const QStringList &args) const { + const QString cvsRoot = stringValue(cvsRootKey); if (cvsRoot.isEmpty()) return args; @@ -110,5 +73,19 @@ QStringList CvsSettings::addOptions(const QStringList &args) const return rc; } +void CvsSettings::readLegacySettings(const QSettings *settings) +{ + const QString keyRoot = settingsGroup() + QLatin1Char('/'); + const QString oldBinaryPathKey = keyRoot + QLatin1String("Command"); + const QString oldPromptOnSubmitKey = keyRoot + QLatin1String("PromptForSubmit"); + const QString oldTimeoutKey = keyRoot + QLatin1String("TimeOut"); + if (settings->contains(oldBinaryPathKey)) + this->setValue(binaryPathKey, settings->value(oldBinaryPathKey).toString()); + if (settings->contains(oldPromptOnSubmitKey)) + this->setValue(promptOnSubmitKey, settings->value(oldPromptOnSubmitKey).toBool()); + if (settings->contains(oldTimeoutKey)) + this->setValue(timeoutKey, settings->value(oldTimeoutKey).toInt()); +} + } // namespace Internal } // namespace Cvs diff --git a/src/plugins/cvs/cvssettings.h b/src/plugins/cvs/cvssettings.h index b0f4ebc665..1b58ba2c68 100644 --- a/src/plugins/cvs/cvssettings.h +++ b/src/plugins/cvs/cvssettings.h @@ -30,44 +30,30 @@ #ifndef CVSSETTINGS_H #define CVSSETTINGS_H -#include <QStringList> - -QT_BEGIN_NAMESPACE -class QSettings; -QT_END_NAMESPACE +#include <vcsbase/vcsbaseclientsettings.h> namespace Cvs { namespace Internal { -struct CvsSettings +class CvsSettings : public VcsBase::VcsBaseClientSettings { - CvsSettings(); +public: + static const QLatin1String cvsRootKey; + static const QLatin1String diffOptionsKey; + static const QLatin1String describeByCommitIdKey; + static const QLatin1String diffIgnoreWhiteSpaceKey; + static const QLatin1String diffIgnoreBlankLinesKey; - void fromSettings(QSettings *); - void toSettings(QSettings *) const; + CvsSettings(); - int timeOutMS() const { return timeOutS * 1000; } - int longTimeOutMS() const { return timeOutS * 10000; } + int timeOutMs() const; - // Add common options to the command line QStringList addOptions(const QStringList &args) const; - bool equals(const CvsSettings &s) const; - - QString cvsCommand; - QString cvsBinaryPath; - QString cvsRoot; - QString cvsDiffOptions; - int timeOutS; - bool promptToSubmit; - bool describeByCommitId; +protected: + void readLegacySettings(const QSettings *settings); }; -inline bool operator==(const CvsSettings &p1, const CvsSettings &p2) - { return p1.equals(p2); } -inline bool operator!=(const CvsSettings &p1, const CvsSettings &p2) - { return !p1.equals(p2); } - } // namespace Internal } // namespace Cvs diff --git a/src/plugins/cvs/settingspage.cpp b/src/plugins/cvs/settingspage.cpp index 60c41f4d1d..e6d1a16499 100644 --- a/src/plugins/cvs/settingspage.cpp +++ b/src/plugins/cvs/settingspage.cpp @@ -48,48 +48,30 @@ SettingsPageWidget::SettingsPageWidget(QWidget *parent) : { m_ui.setupUi(this); m_ui.commandPathChooser->setExpectedKind(PathChooser::ExistingCommand); + m_ui.commandPathChooser->setHistoryCompleter(QLatin1String("Cvs.Command.History")); m_ui.commandPathChooser->setPromptDialogTitle(tr("CVS Command")); } CvsSettings SettingsPageWidget::settings() const { CvsSettings rc; - rc.cvsCommand = m_ui.commandPathChooser->rawPath(); - rc.cvsBinaryPath = m_ui.commandPathChooser->path(); - rc.cvsRoot = m_ui.rootLineEdit->text(); - rc.cvsDiffOptions = m_ui.diffOptionsLineEdit->text(); - rc.timeOutS = m_ui.timeOutSpinBox->value(); - rc.promptToSubmit = m_ui.promptToSubmitCheckBox->isChecked(); - rc.describeByCommitId = m_ui.describeByCommitIdCheckBox->isChecked(); + rc.setValue(CvsSettings::binaryPathKey, m_ui.commandPathChooser->rawPath()); + rc.setValue(CvsSettings::cvsRootKey, m_ui.rootLineEdit->text()); + rc.setValue(CvsSettings::diffOptionsKey, m_ui.diffOptionsLineEdit->text()); + rc.setValue(CvsSettings::timeoutKey, m_ui.timeOutSpinBox->value()); + rc.setValue(CvsSettings::promptOnSubmitKey, m_ui.promptToSubmitCheckBox->isChecked()); + rc.setValue(CvsSettings::describeByCommitIdKey, m_ui.describeByCommitIdCheckBox->isChecked()); return rc; } void SettingsPageWidget::setSettings(const CvsSettings &s) { - m_ui.commandPathChooser->setPath(s.cvsCommand); - m_ui.rootLineEdit->setText(s.cvsRoot); - m_ui.diffOptionsLineEdit->setText(s.cvsDiffOptions); - m_ui.timeOutSpinBox->setValue(s.timeOutS); - m_ui.promptToSubmitCheckBox->setChecked(s.promptToSubmit); - m_ui.describeByCommitIdCheckBox->setChecked(s.describeByCommitId); -} - -QString SettingsPageWidget::searchKeywords() const -{ - QString rc; - QLatin1Char sep(' '); - QTextStream(&rc) - << sep << m_ui.configGroupBox->title() - << sep << m_ui.commandLabel->text() - << sep << m_ui.rootLabel->text() - << sep << m_ui.miscGroupBox->title() - << sep << m_ui.timeOutLabel->text() - << sep << m_ui.diffOptionsLabel->text() - << sep << m_ui.promptToSubmitCheckBox->text() - << sep << m_ui.describeByCommitIdCheckBox->text() - ; - rc.remove(QLatin1Char('&')); - return rc; + m_ui.commandPathChooser->setPath(s.binaryPath()); + m_ui.rootLineEdit->setText(s.stringValue(CvsSettings::cvsRootKey)); + m_ui.diffOptionsLineEdit->setText(s.stringValue(CvsSettings::diffOptionsKey)); + m_ui.timeOutSpinBox->setValue(s.intValue(CvsSettings::timeoutKey)); + m_ui.promptToSubmitCheckBox->setChecked(s.boolValue(CvsSettings::promptOnSubmitKey)); + m_ui.describeByCommitIdCheckBox->setChecked(s.boolValue(CvsSettings::describeByCommitIdKey)); } SettingsPage::SettingsPage() @@ -98,12 +80,12 @@ SettingsPage::SettingsPage() setDisplayName(tr("CVS")); } -QWidget *SettingsPage::createPage(QWidget *parent) +QWidget *SettingsPage::widget() { - m_widget = new SettingsPageWidget(parent); - m_widget->setSettings(CvsPlugin::instance()->settings()); - if (m_searchKeywords.isEmpty()) - m_searchKeywords = m_widget->searchKeywords(); + if (!m_widget) { + m_widget = new SettingsPageWidget; + m_widget->setSettings(CvsPlugin::instance()->settings()); + } return m_widget; } @@ -112,7 +94,7 @@ void SettingsPage::apply() CvsPlugin::instance()->setSettings(m_widget->settings()); } -bool SettingsPage::matches(const QString &s) const +void SettingsPage::finish() { - return m_searchKeywords.contains(s, Qt::CaseInsensitive); + delete m_widget; } diff --git a/src/plugins/cvs/settingspage.h b/src/plugins/cvs/settingspage.h index 729fa33f64..6f3c270775 100644 --- a/src/plugins/cvs/settingspage.h +++ b/src/plugins/cvs/settingspage.h @@ -45,7 +45,7 @@ QT_END_NAMESPACE namespace Cvs { namespace Internal { -struct CvsSettings; +class CvsSettings; class SettingsPageWidget : public QWidget { @@ -57,8 +57,6 @@ public: CvsSettings settings() const; void setSettings(const CvsSettings &); - QString searchKeywords() const; - private: Ui::SettingsPage m_ui; }; @@ -71,14 +69,12 @@ class SettingsPage : public VcsBase::VcsBaseOptionsPage public: SettingsPage(); - QWidget *createPage(QWidget *parent); + QWidget *widget(); void apply(); - void finish() {} - bool matches(const QString &) const; + void finish(); private: - QString m_searchKeywords; - SettingsPageWidget *m_widget; + QPointer<SettingsPageWidget> m_widget; }; } // namespace Cvs diff --git a/src/plugins/debugger/breakwindow.cpp b/src/plugins/debugger/breakwindow.cpp index ec4be8cc61..cf76805536 100644 --- a/src/plugins/debugger/breakwindow.cpp +++ b/src/plugins/debugger/breakwindow.cpp @@ -170,6 +170,7 @@ BreakpointDialog::BreakpointDialog(BreakpointModelId id, QWidget *parent) m_labelType->setBuddy(m_comboBoxType); m_pathChooserFileName = new Utils::PathChooser(groupBoxBasic); + m_pathChooserFileName->setHistoryCompleter(QLatin1String("Debugger.Breakpoint.File.History")); m_pathChooserFileName->setExpectedKind(Utils::PathChooser::File); m_labelFileName = new QLabel(tr("&File name:"), groupBoxBasic); m_labelFileName->setBuddy(m_pathChooserFileName); diff --git a/src/plugins/debugger/cdb/cdbengine.cpp b/src/plugins/debugger/cdb/cdbengine.cpp index c19ad017d6..388535eba5 100644 --- a/src/plugins/debugger/cdb/cdbengine.cpp +++ b/src/plugins/debugger/cdb/cdbengine.cpp @@ -339,7 +339,6 @@ void addCdbOptionPages(QList<Core::IOptionsPage *> *opts) CdbEngine::CdbEngine(const DebuggerStartParameters &sp) : DebuggerEngine(sp), - m_creatorExtPrefix("<qtcreatorcdbext>|"), m_tokenPrefix("<token>"), m_effectiveStartMode(NoStartMode), m_accessible(false), @@ -2554,11 +2553,12 @@ void CdbEngine::parseOutputLine(QByteArray line) while (isCdbPrompt(line)) line.remove(0, CdbPromptLength); // An extension notification (potentially consisting of several chunks) - if (line.startsWith(m_creatorExtPrefix)) { + static const QByteArray creatorExtPrefix = "<qtcreatorcdbext>|"; + if (line.startsWith(creatorExtPrefix)) { // "<qtcreatorcdbext>|type_char|token|remainingChunks|serviceName|message" - const char type = line.at(m_creatorExtPrefix.size()); + const char type = line.at(creatorExtPrefix.size()); // integer token - const int tokenPos = m_creatorExtPrefix.size() + 2; + const int tokenPos = creatorExtPrefix.size() + 2; const int tokenEndPos = line.indexOf('|', tokenPos); QTC_ASSERT(tokenEndPos != -1, return); const int token = line.mid(tokenPos, tokenEndPos - tokenPos).toInt(); diff --git a/src/plugins/debugger/cdb/cdbengine.h b/src/plugins/debugger/cdb/cdbengine.h index 3296344f9f..0030334385 100644 --- a/src/plugins/debugger/cdb/cdbengine.h +++ b/src/plugins/debugger/cdb/cdbengine.h @@ -251,7 +251,6 @@ private: unsigned parseStackTrace(const GdbMi &data, bool sourceStepInto); void mergeStartParametersSourcePathMap(); - const QByteArray m_creatorExtPrefix; const QByteArray m_tokenPrefix; QProcess m_process; diff --git a/src/plugins/debugger/cdb/cdboptionspage.cpp b/src/plugins/debugger/cdb/cdboptionspage.cpp index 5060a8ce15..e709191ecc 100644 --- a/src/plugins/debugger/cdb/cdboptionspage.cpp +++ b/src/plugins/debugger/cdb/cdboptionspage.cpp @@ -198,22 +198,6 @@ QStringList CdbOptionsPageWidget::breakEvents() const return m_breakEventWidget->breakEvents(); } -static QString stripColon(QString s) -{ - const int lastColon = s.lastIndexOf(QLatin1Char(':')); - if (lastColon != -1) - s.truncate(lastColon); - return s; -} - -QString CdbOptionsPageWidget::searchKeywords() const -{ - QString rc; - QTextStream(&rc) << stripColon(m_ui.additionalArgumentsLabel->text()); - rc.remove(QLatin1Char('&')); - return rc; -} - // ---------- CdbOptionsPage CdbOptionsPage::CdbOptionsPage() @@ -230,11 +214,10 @@ CdbOptionsPage::~CdbOptionsPage() { } -QWidget *CdbOptionsPage::createPage(QWidget *parent) +QWidget *CdbOptionsPage::widget() { - m_widget = new CdbOptionsPageWidget(parent); - if (m_searchKeywords.isEmpty()) - m_searchKeywords = m_widget->searchKeywords(); + if (!m_widget) + m_widget = new CdbOptionsPageWidget; return m_widget; } @@ -248,13 +231,10 @@ void CdbOptionsPage::apply() void CdbOptionsPage::finish() { - if (m_widget) + if (m_widget) { m_widget->group.finish(); -} - -bool CdbOptionsPage::matches(const QString &s) const -{ - return m_searchKeywords.contains(s, Qt::CaseInsensitive); + delete m_widget; + } } // ---------- CdbPathsPage @@ -266,8 +246,6 @@ public: Utils::SavedActionSet group; // CdbPaths m_paths; - QString m_searchKeywords; - CdbSymbolPathListEditor *m_symbolPathListEditor; Utils::PathListEditor *m_sourcePathListEditor; @@ -280,7 +258,6 @@ CdbPathsPageWidget::CdbPathsPageWidget(QWidget *parent) : QVBoxLayout *layout = new QVBoxLayout(this); QString title = tr("Symbol Paths"); - m_searchKeywords.append(title); QGroupBox* gbSymbolPath = new QGroupBox(this); gbSymbolPath->setTitle(title); QVBoxLayout *gbSymbolPathLayout = new QVBoxLayout(gbSymbolPath); @@ -288,7 +265,6 @@ CdbPathsPageWidget::CdbPathsPageWidget(QWidget *parent) : gbSymbolPathLayout->addWidget(m_symbolPathListEditor); title = tr("Source Paths"); - m_searchKeywords.append(title); QGroupBox* gbSourcePath = new QGroupBox(this); gbSourcePath->setTitle(title); QVBoxLayout *gbSourcePathLayout = new QVBoxLayout(gbSourcePath); @@ -318,12 +294,10 @@ CdbPathsPage::~CdbPathsPage() { } -QWidget *CdbPathsPage::createPage(QWidget *parent) +QWidget *CdbPathsPage::widget() { if (!m_widget) - m_widget = new CdbPathsPageWidget(parent); - else - m_widget->setParent(parent); + m_widget = new CdbPathsPageWidget; return m_widget; } @@ -335,14 +309,10 @@ void CdbPathsPage::apply() void CdbPathsPage::finish() { - if (m_widget) + if (m_widget) { m_widget->group.finish(); -} - -bool CdbPathsPage::matches(const QString &searchKeyWord) const -{ - return m_widget && - m_widget->m_searchKeywords.contains(searchKeyWord, Qt::CaseInsensitive); + delete m_widget; + } } } // namespace Internal diff --git a/src/plugins/debugger/cdb/cdboptionspage.h b/src/plugins/debugger/cdb/cdboptionspage.h index f26ab7cc3e..f8fd0bcc35 100644 --- a/src/plugins/debugger/cdb/cdboptionspage.h +++ b/src/plugins/debugger/cdb/cdboptionspage.h @@ -78,9 +78,8 @@ class CdbOptionsPageWidget : public QWidget Q_OBJECT public: - explicit CdbOptionsPageWidget(QWidget *parent); + explicit CdbOptionsPageWidget(QWidget *parent = 0); QStringList breakEvents() const; - QString searchKeywords() const; Utils::SavedActionSet group; @@ -103,17 +102,15 @@ public: virtual ~CdbOptionsPage(); // IOptionsPage - QWidget *createPage(QWidget *parent); + QWidget *widget(); void apply(); void finish(); - bool matches(const QString &) const; static const char *crtDbgReport; private: Utils::SavedActionSet group; QPointer<CdbOptionsPageWidget> m_widget; - QString m_searchKeywords; }; class CdbPathsPage : public Core::IOptionsPage @@ -127,10 +124,9 @@ public: static CdbPathsPage *instance(); // IOptionsPage - QWidget *createPage(QWidget *parent); + QWidget *widget(); void apply(); void finish(); - bool matches(const QString &searchKeyWord) const; private: QPointer<CdbPathsPageWidget> m_widget; diff --git a/src/plugins/debugger/commonoptionspage.cpp b/src/plugins/debugger/commonoptionspage.cpp index 5493450d5e..bd4fe189e3 100644 --- a/src/plugins/debugger/commonoptionspage.cpp +++ b/src/plugins/debugger/commonoptionspage.cpp @@ -202,29 +202,6 @@ CommonOptionsPageWidget::CommonOptionsPageWidget } } -QString CommonOptionsPageWidget::searchKeyWords() const -{ - QString rc; - const QLatin1Char sep(' '); - QTextStream stream(&rc); - stream << sep << checkBoxUseAlternatingRowColors->text() - << sep << checkBoxFontSizeFollowsEditor->text() - << sep << checkBoxUseToolTipsInMainEditor->text() - << sep << checkBoxListSourceFiles->text() - << sep << checkBoxBreakpointsFullPath->text() - << sep << checkBoxCloseBuffersOnExit->text() - << sep << checkBoxSwitchModeOnExit->text() - << sep << labelMaximalStackDepth->text() - << sep << checkBoxBringToForegroundOnInterrrupt->text() - << sep << checkBoxShowQmlObjectTree->text() - << sep << checkBoxWarnOnReleaseBuilds->text(); - if (Utils::HostOsInfo::isWindowsHost()) - stream << sep << checkBoxRegisterForPostMortem->text(); - - rc.remove(QLatin1Char('&')); - return rc; -} - GlobalDebuggerOptions CommonOptionsPageWidget::globalOptions() const { GlobalDebuggerOptions o; @@ -274,22 +251,19 @@ void CommonOptionsPage::finish() { if (!m_group.isNull()) m_group->finish(); + delete m_widget; } -QWidget *CommonOptionsPage::createPage(QWidget *parent) +QWidget *CommonOptionsPage::widget() { if (m_group.isNull()) m_group = QSharedPointer<Utils::SavedActionSet>(new Utils::SavedActionSet); - m_widget = new CommonOptionsPageWidget(m_group, parent); - m_widget->setGlobalOptions(*m_options); - if (m_searchKeywords.isEmpty()) - m_searchKeywords = m_widget->searchKeyWords(); - return m_widget; -} -bool CommonOptionsPage::matches(const QString &s) const -{ - return m_searchKeywords.contains(s, Qt::CaseInsensitive); + if (!m_widget) { + m_widget = new CommonOptionsPageWidget(m_group); + m_widget->setGlobalOptions(*m_options); + } + return m_widget; } QString CommonOptionsPage::msgSetBreakpointAtFunction(const char *function) @@ -301,7 +275,7 @@ QString CommonOptionsPage::msgSetBreakpointAtFunctionToolTip(const char *functio const QString &hint) { QString result = QLatin1String("<html><head/><body>"); - result += tr("Always add a breakpoint on the <i>%1()</i> function.").arg(QLatin1String(function)); + result += tr("Always adds a breakpoint on the <i>%1()</i> function.").arg(QLatin1String(function)); if (!hint.isEmpty()) { result += QLatin1String("<br>"); result += hint; @@ -334,57 +308,43 @@ void LocalsAndExpressionsOptionsPage::apply() void LocalsAndExpressionsOptionsPage::finish() { m_group.finish(); + delete m_widget; } -QWidget *LocalsAndExpressionsOptionsPage::createPage(QWidget *parent) +QWidget *LocalsAndExpressionsOptionsPage::widget() { - QWidget *w = new QWidget(parent); - m_ui.setupUi(w); + if (!m_widget) { + m_widget = new QWidget; + m_ui.setupUi(m_widget); - m_group.clear(); - DebuggerCore *dc = debuggerCore(); + m_group.clear(); + DebuggerCore *dc = debuggerCore(); - m_group.insert(dc->action(UseDebuggingHelpers), - m_ui.debuggingHelperGroupBox); + m_group.insert(dc->action(UseDebuggingHelpers), + m_ui.debuggingHelperGroupBox); - m_group.insert(dc->action(UseCodeModel), - m_ui.checkBoxUseCodeModel); - m_ui.checkBoxUseCodeModel->setToolTip(dc->action(UseCodeModel)->toolTip()); + m_group.insert(dc->action(UseCodeModel), + m_ui.checkBoxUseCodeModel); + m_ui.checkBoxUseCodeModel->setToolTip(dc->action(UseCodeModel)->toolTip()); - m_group.insert(dc->action(ShowThreadNames), - m_ui.checkBoxShowThreadNames); - m_group.insert(dc->action(ShowStdNamespace), m_ui.checkBoxShowStdNamespace); - m_group.insert(dc->action(ShowQtNamespace), m_ui.checkBoxShowQtNamespace); + m_group.insert(dc->action(ShowThreadNames), + m_ui.checkBoxShowThreadNames); + m_group.insert(dc->action(ShowStdNamespace), m_ui.checkBoxShowStdNamespace); + m_group.insert(dc->action(ShowQtNamespace), m_ui.checkBoxShowQtNamespace); #ifndef QT_DEBUG #if 0 - cmd = am->registerAction(m_dumpLogAction, - DUMP_LOG, globalcontext); - //cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+D,Ctrl+L"))); - cmd->setDefaultKeySequence(QKeySequence(QCoreApplication::translate("Debugger", "Ctrl+Shift+F11"))); - mdebug->addAction(cmd); + cmd = am->registerAction(m_dumpLogAction, + DUMP_LOG, globalcontext); + //cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+D,Ctrl+L"))); + cmd->setDefaultKeySequence(QKeySequence(QCoreApplication::translate("Debugger", "Ctrl+Shift+F11"))); + mdebug->addAction(cmd); #endif #endif - - if (m_searchKeywords.isEmpty()) { - QTextStream(&m_searchKeywords) - << ' ' << m_ui.debuggingHelperGroupBox->title() - << ' ' << m_ui.checkBoxUseCodeModel->text() - << ' ' << m_ui.checkBoxShowThreadNames->text() - << ' ' << m_ui.checkBoxShowStdNamespace->text() - << ' ' << m_ui.checkBoxShowQtNamespace->text(); - - m_searchKeywords.remove(QLatin1Char('&')); } - return w; -} - -bool LocalsAndExpressionsOptionsPage::matches(const QString &s) const -{ - return m_searchKeywords.contains(s, Qt::CaseInsensitive); + return m_widget; } - } // namespace Internal } // namespace Debugger diff --git a/src/plugins/debugger/commonoptionspage.h b/src/plugins/debugger/commonoptionspage.h index 508fb20240..a35bb66654 100644 --- a/src/plugins/debugger/commonoptionspage.h +++ b/src/plugins/debugger/commonoptionspage.h @@ -60,7 +60,6 @@ class CommonOptionsPageWidget : public QWidget public: explicit CommonOptionsPageWidget(const QSharedPointer<Utils::SavedActionSet> &group, QWidget *parent = 0); - QString searchKeyWords() const; GlobalDebuggerOptions globalOptions() const; void setGlobalOptions(const GlobalDebuggerOptions &go); @@ -94,10 +93,9 @@ public: ~CommonOptionsPage(); // IOptionsPage - QWidget *createPage(QWidget *parent); + QWidget *widget(); void apply(); void finish(); - bool matches(const QString &s) const; static QString msgSetBreakpointAtFunction(const char *function); static QString msgSetBreakpointAtFunctionToolTip(const char *function, @@ -106,7 +104,6 @@ public: private: const QSharedPointer<GlobalDebuggerOptions> m_options; QSharedPointer<Utils::SavedActionSet> m_group; - QString m_searchKeywords; QPointer<CommonOptionsPageWidget> m_widget; }; @@ -123,15 +120,14 @@ public: LocalsAndExpressionsOptionsPage(); // IOptionsPage - QWidget *createPage(QWidget *parent); + QWidget *widget(); void apply(); void finish(); - bool matches(const QString &s) const; private: + QPointer<QWidget> m_widget; Ui::DebuggingHelperOptionPage m_ui; Utils::SavedActionSet m_group; - QString m_searchKeywords; }; } // namespace Internal diff --git a/src/plugins/debugger/debugger.pro b/src/plugins/debugger/debugger.pro index 7798cf0bfe..c9f794d55d 100644 --- a/src/plugins/debugger/debugger.pro +++ b/src/plugins/debugger/debugger.pro @@ -151,7 +151,6 @@ include(cdb/cdb.pri) include(gdb/gdb.pri) include(pdb/pdb.pri) include(lldb/lldb.pri) -include(lldblib/lldbhost.pri) include(qml/qml.pri) include(namedemangler/namedemangler.pri) diff --git a/src/plugins/debugger/debugger.qbs b/src/plugins/debugger/debugger.qbs index 709dc128c3..290929db46 100644 --- a/src/plugins/debugger/debugger.qbs +++ b/src/plugins/debugger/debugger.qbs @@ -109,14 +109,12 @@ QtcPlugin { prefix: "gdb/" files: [ "attachgdbadapter.cpp", "attachgdbadapter.h", - "classicgdbengine.cpp", "coregdbadapter.cpp", "coregdbadapter.h", "gdb.qrc", "gdbengine.cpp", "gdbengine.h", "gdboptionspage.cpp", "gdboptionspage.h", "gdbprocess.cpp", "gdbprocess.h", "gdbplainengine.cpp", "gdbplainengine.h", - "pythongdbengine.cpp", "remotegdbserveradapter.cpp", "remotegdbserveradapter.h", "startgdbserverdialog.cpp", "startgdbserverdialog.h", "termgdbadapter.cpp", "termgdbadapter.h" @@ -132,16 +130,6 @@ QtcPlugin { } Group { - name: "lldblib" - id: lldblib - prefix: "lldblib/" - files: [ - "ipcenginehost.cpp", "ipcenginehost.h", - "lldbenginehost.cpp", "lldbenginehost.h" - ] - } - - Group { name: "pdb" prefix: "pdb/" files: ["pdbengine.cpp", "pdbengine.h"] @@ -247,16 +235,6 @@ QtcPlugin { ] } - Group { - name: "LLDBOptions" - condition: qbs.targetOS.contains("osx") - files: [ - "lldblib/lldboptionspage.cpp", - "lldblib/lldboptionspage.h", - "lldblib/lldboptionspagewidget.ui", - ] - } - Properties { condition: qbs.targetOS.contains("windows") cpp.dynamicLibraries: [ diff --git a/src/plugins/debugger/debuggerconstants.h b/src/plugins/debugger/debuggerconstants.h index 941e9c2431..8a8e9d3d25 100644 --- a/src/plugins/debugger/debuggerconstants.h +++ b/src/plugins/debugger/debuggerconstants.h @@ -190,14 +190,12 @@ enum DebuggerEngineType PdbEngineType = 0x008, QmlEngineType = 0x020, QmlCppEngineType = 0x040, - LldbLibEngineType = 0x080, LldbEngineType = 0x100, AllEngineTypes = GdbEngineType | CdbEngineType | PdbEngineType | QmlEngineType | QmlCppEngineType - | LldbLibEngineType | LldbEngineType }; diff --git a/src/plugins/debugger/debuggercore.h b/src/plugins/debugger/debuggercore.h index 7b056ca44e..304d317323 100644 --- a/src/plugins/debugger/debuggercore.h +++ b/src/plugins/debugger/debuggercore.h @@ -118,7 +118,6 @@ public: const QVector<Section> §ions) = 0; virtual void openMemoryEditor() = 0; virtual void languagesChanged() = 0; - virtual void executeDebuggerCommand(const QString &command, DebuggerLanguages languages) = 0; virtual Utils::SavedAction *action(int code) const = 0; virtual bool boolSetting(int code) const = 0; diff --git a/src/plugins/debugger/debuggerdialogs.cpp b/src/plugins/debugger/debuggerdialogs.cpp index 3be9af3872..afcea28a58 100644 --- a/src/plugins/debugger/debuggerdialogs.cpp +++ b/src/plugins/debugger/debuggerdialogs.cpp @@ -279,6 +279,7 @@ StartApplicationDialog::StartApplicationDialog(QWidget *parent) d->debuginfoPathChooser->setToolTip(tr( "Base path for external debug information and debug sources. " "If empty, $SYSROOT/usr/lib/debug will be chosen.")); + d->debuginfoPathChooser->setHistoryCompleter(QLatin1String("Debugger.DebugLocation.History")); QFrame *line = new QFrame(this); line->setFrameShape(QFrame::HLine); diff --git a/src/plugins/debugger/debuggerengine.cpp b/src/plugins/debugger/debuggerengine.cpp index b6b69ff4e4..7d060f4c73 100644 --- a/src/plugins/debugger/debuggerengine.cpp +++ b/src/plugins/debugger/debuggerengine.cpp @@ -1045,7 +1045,7 @@ void DebuggerEnginePrivate::doFinishDebugger() void DebuggerEnginePrivate::setRemoteSetupState(RemoteSetupState state) { - bool allowedTransition = true; + bool allowedTransition = false; if (m_remoteSetupState == RemoteSetupNone) { if (state == RemoteSetupRequested) allowedTransition = true; diff --git a/src/plugins/debugger/debuggerengine.h b/src/plugins/debugger/debuggerengine.h index 2dbb083960..539d85a8d2 100644 --- a/src/plugins/debugger/debuggerengine.h +++ b/src/plugins/debugger/debuggerengine.h @@ -189,6 +189,7 @@ public: virtual void setRegisterValue(int regnr, const QString &value); virtual void addOptionPages(QList<Core::IOptionsPage*> *) const; virtual bool hasCapability(unsigned cap) const = 0; + virtual void debugLastCommand() {} virtual bool isSynchronous() const; virtual QByteArray qtNamespace() const; @@ -205,6 +206,8 @@ public: virtual void changeBreakpoint(BreakpointModelId id); // FIXME: make pure virtual bool acceptsDebuggerCommands() const { return true; } + virtual void executeDebuggerCommand(const QString &command, DebuggerLanguages languages); + virtual void assignValueInDebugger(const Internal::WatchData *data, const QString &expr, const QVariant &value); virtual void selectThread(Internal::ThreadId threadId) = 0; @@ -347,7 +350,6 @@ protected: virtual void executeRunToLine(const Internal::ContextData &data); virtual void executeRunToFunction(const QString &functionName); virtual void executeJumpToLine(const Internal::ContextData &data); - virtual void executeDebuggerCommand(const QString &command, DebuggerLanguages languages); virtual void frameUp(); virtual void frameDown(); diff --git a/src/plugins/debugger/debuggeritem.cpp b/src/plugins/debugger/debuggeritem.cpp index 0f22707df8..967dd01057 100644 --- a/src/plugins/debugger/debuggeritem.cpp +++ b/src/plugins/debugger/debuggeritem.cpp @@ -47,6 +47,7 @@ static const char DEBUGGER_INFORMATION_DISPLAYNAME[] = "DisplayName"; static const char DEBUGGER_INFORMATION_ID[] = "Id"; static const char DEBUGGER_INFORMATION_ENGINETYPE[] = "EngineType"; static const char DEBUGGER_INFORMATION_AUTODETECTED[] = "AutoDetected"; +static const char DEBUGGER_INFORMATION_AUTODETECTION_SOURCE[] = "AutoDetectionSource"; static const char DEBUGGER_INFORMATION_ABIS[] = "Abis"; namespace Debugger { @@ -74,6 +75,7 @@ DebuggerItem::DebuggerItem(const QVariantMap &data) m_id = data.value(QLatin1String(DEBUGGER_INFORMATION_ID)).toString(); m_displayName = data.value(QLatin1String(DEBUGGER_INFORMATION_DISPLAYNAME)).toString(); m_isAutoDetected = data.value(QLatin1String(DEBUGGER_INFORMATION_AUTODETECTED), false).toBool(); + m_autoDetectionSource = data.value(QLatin1String(DEBUGGER_INFORMATION_AUTODETECTION_SOURCE)).toString(); m_engineType = DebuggerEngineType(data.value(QLatin1String(DEBUGGER_INFORMATION_ENGINETYPE), static_cast<int>(NoEngineType)).toInt()); @@ -185,6 +187,7 @@ QVariantMap DebuggerItem::toMap() const data.insert(QLatin1String(DEBUGGER_INFORMATION_COMMAND), m_command.toUserOutput()); data.insert(QLatin1String(DEBUGGER_INFORMATION_ENGINETYPE), int(m_engineType)); data.insert(QLatin1String(DEBUGGER_INFORMATION_AUTODETECTED), m_isAutoDetected); + data.insert(QLatin1String(DEBUGGER_INFORMATION_AUTODETECTION_SOURCE), m_autoDetectionSource); data.insert(QLatin1String(DEBUGGER_INFORMATION_ABIS), abiNames()); return data; } @@ -209,6 +212,11 @@ void DebuggerItem::setAutoDetected(bool isAutoDetected) m_isAutoDetected = isAutoDetected; } +void DebuggerItem::setAutoDetectionSource(const QString &autoDetectionSource) +{ + m_autoDetectionSource = autoDetectionSource; +} + void DebuggerItem::setAbis(const QList<ProjectExplorer::Abi> &abis) { m_abis = abis; diff --git a/src/plugins/debugger/debuggeritem.h b/src/plugins/debugger/debuggeritem.h index 9d3a36d341..a9910b0ad5 100644 --- a/src/plugins/debugger/debuggeritem.h +++ b/src/plugins/debugger/debuggeritem.h @@ -79,6 +79,9 @@ public: bool isAutoDetected() const { return m_isAutoDetected; } void setAutoDetected(bool isAutoDetected); + QString autoDetectionSource() const { return m_autoDetectionSource; } + void setAutoDetectionSource(const QString &autoDetectionSource); + QList<ProjectExplorer::Abi> abis() const { return m_abis; } void setAbis(const QList<ProjectExplorer::Abi> &abis); void setAbi(const ProjectExplorer::Abi &abi); @@ -99,6 +102,7 @@ private: DebuggerEngineType m_engineType; Utils::FileName m_command; bool m_isAutoDetected; + QString m_autoDetectionSource; QList<ProjectExplorer::Abi> m_abis; friend class Internal::DebuggerItemConfigWidget; diff --git a/src/plugins/debugger/debuggeroptionspage.cpp b/src/plugins/debugger/debuggeroptionspage.cpp index 75dbaf5aee..6fe0a19135 100644 --- a/src/plugins/debugger/debuggeroptionspage.cpp +++ b/src/plugins/debugger/debuggeroptionspage.cpp @@ -217,63 +217,61 @@ DebuggerOptionsPage::DebuggerOptionsPage() setCategoryIcon(QLatin1String(ProjectExplorer::Constants::PROJECTEXPLORER_SETTINGS_CATEGORY_ICON)); } -QWidget *DebuggerOptionsPage::createPage(QWidget *parent) +QWidget *DebuggerOptionsPage::widget() { - m_configWidget = new QWidget(parent); - - m_addButton = new QPushButton(tr("Add"), m_configWidget); - m_cloneButton = new QPushButton(tr("Clone"), m_configWidget); - m_delButton = new QPushButton(tr("Remove"), m_configWidget); - - m_container = new DetailsWidget(m_configWidget); - m_container->setState(DetailsWidget::NoSummary); - m_container->setVisible(false); - - m_model = new DebuggerItemModel(parent); - - m_debuggerView = new QTreeView(m_configWidget); - m_debuggerView->setModel(m_model); - m_debuggerView->setUniformRowHeights(true); - m_debuggerView->setSelectionMode(QAbstractItemView::SingleSelection); - m_debuggerView->setSelectionBehavior(QAbstractItemView::SelectRows); - m_debuggerView->expandAll(); - - QHeaderView *header = m_debuggerView->header(); - header->setStretchLastSection(false); - header->setResizeMode(0, QHeaderView::ResizeToContents); - header->setResizeMode(1, QHeaderView::ResizeToContents); - header->setResizeMode(2, QHeaderView::Stretch); - - QVBoxLayout *buttonLayout = new QVBoxLayout(); - buttonLayout->setSpacing(6); - buttonLayout->setContentsMargins(0, 0, 0, 0); - buttonLayout->addWidget(m_addButton); - buttonLayout->addWidget(m_cloneButton); - buttonLayout->addWidget(m_delButton); - buttonLayout->addItem(new QSpacerItem(10, 40, QSizePolicy::Minimum, QSizePolicy::Expanding)); - - QVBoxLayout *verticalLayout = new QVBoxLayout(); - verticalLayout->addWidget(m_debuggerView); - verticalLayout->addWidget(m_container); - - QHBoxLayout *horizontalLayout = new QHBoxLayout(m_configWidget); - horizontalLayout->addLayout(verticalLayout); - horizontalLayout->addLayout(buttonLayout); - - connect(m_debuggerView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), - this, SLOT(debuggerSelectionChanged())); - - connect(m_addButton, SIGNAL(clicked()), this, SLOT(addDebugger()), Qt::QueuedConnection); - connect(m_cloneButton, SIGNAL(clicked()), this, SLOT(cloneDebugger()), Qt::QueuedConnection); - connect(m_delButton, SIGNAL(clicked()), this, SLOT(removeDebugger()), Qt::QueuedConnection); - - m_searchKeywords = tr("Debuggers"); - - m_itemConfigWidget = new DebuggerItemConfigWidget(m_model); - m_container->setWidget(m_itemConfigWidget); - - updateState(); - + if (!m_configWidget) { + m_configWidget = new QWidget; + + m_addButton = new QPushButton(tr("Add"), m_configWidget); + m_cloneButton = new QPushButton(tr("Clone"), m_configWidget); + m_delButton = new QPushButton(tr("Remove"), m_configWidget); + + m_container = new DetailsWidget(m_configWidget); + m_container->setState(DetailsWidget::NoSummary); + m_container->setVisible(false); + + m_debuggerView = new QTreeView(m_configWidget); + m_model = new DebuggerItemModel(m_debuggerView); + m_debuggerView->setModel(m_model); + m_debuggerView->setUniformRowHeights(true); + m_debuggerView->setSelectionMode(QAbstractItemView::SingleSelection); + m_debuggerView->setSelectionBehavior(QAbstractItemView::SelectRows); + m_debuggerView->expandAll(); + + QHeaderView *header = m_debuggerView->header(); + header->setStretchLastSection(false); + header->setResizeMode(0, QHeaderView::ResizeToContents); + header->setResizeMode(1, QHeaderView::ResizeToContents); + header->setResizeMode(2, QHeaderView::Stretch); + + QVBoxLayout *buttonLayout = new QVBoxLayout(); + buttonLayout->setSpacing(6); + buttonLayout->setContentsMargins(0, 0, 0, 0); + buttonLayout->addWidget(m_addButton); + buttonLayout->addWidget(m_cloneButton); + buttonLayout->addWidget(m_delButton); + buttonLayout->addItem(new QSpacerItem(10, 40, QSizePolicy::Minimum, QSizePolicy::Expanding)); + + QVBoxLayout *verticalLayout = new QVBoxLayout(); + verticalLayout->addWidget(m_debuggerView); + verticalLayout->addWidget(m_container); + + QHBoxLayout *horizontalLayout = new QHBoxLayout(m_configWidget); + horizontalLayout->addLayout(verticalLayout); + horizontalLayout->addLayout(buttonLayout); + + connect(m_debuggerView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), + this, SLOT(debuggerSelectionChanged())); + + connect(m_addButton, SIGNAL(clicked()), this, SLOT(addDebugger()), Qt::QueuedConnection); + connect(m_cloneButton, SIGNAL(clicked()), this, SLOT(cloneDebugger()), Qt::QueuedConnection); + connect(m_delButton, SIGNAL(clicked()), this, SLOT(removeDebugger()), Qt::QueuedConnection); + + m_itemConfigWidget = new DebuggerItemConfigWidget(m_model); + m_container->setWidget(m_itemConfigWidget); + + updateState(); + } return m_configWidget; } @@ -322,10 +320,10 @@ void DebuggerOptionsPage::removeDebugger() void DebuggerOptionsPage::finish() { - // Deleted by settingsdialog. - m_configWidget = 0; + delete m_configWidget; // Children of m_configWidget. + m_model = 0; m_container = 0; m_debuggerView = 0; m_addButton = 0; @@ -333,11 +331,6 @@ void DebuggerOptionsPage::finish() m_delButton = 0; } -bool DebuggerOptionsPage::matches(const QString &s) const -{ - return m_searchKeywords.contains(s, Qt::CaseInsensitive); -} - void DebuggerOptionsPage::debuggerSelectionChanged() { QTC_ASSERT(m_container, return); diff --git a/src/plugins/debugger/debuggeroptionspage.h b/src/plugins/debugger/debuggeroptionspage.h index f214bf99bf..158362d9ed 100644 --- a/src/plugins/debugger/debuggeroptionspage.h +++ b/src/plugins/debugger/debuggeroptionspage.h @@ -34,6 +34,7 @@ #include <coreplugin/dialogs/ioptionspage.h> +#include <QPointer> #include <QWidget> QT_BEGIN_NAMESPACE @@ -97,10 +98,9 @@ class DebuggerOptionsPage : public Core::IOptionsPage public: DebuggerOptionsPage(); - QWidget *createPage(QWidget *parent); + QWidget *widget(); void apply(); void finish(); - bool matches(const QString &) const; private slots: void debuggerSelectionChanged(); @@ -111,8 +111,7 @@ private slots: void removeDebugger(); private: - QWidget *m_configWidget; - QString m_searchKeywords; + QPointer<QWidget> m_configWidget; DebuggerItemModel *m_model; DebuggerItemConfigWidget *m_itemConfigWidget; diff --git a/src/plugins/debugger/debuggerplugin.cpp b/src/plugins/debugger/debuggerplugin.cpp index 21a3e206cf..64349b68bf 100644 --- a/src/plugins/debugger/debuggerplugin.cpp +++ b/src/plugins/debugger/debuggerplugin.cpp @@ -684,7 +684,7 @@ static bool currentTextEditorPosition(ContextData *data) data->fileName = document->filePath(); if (document->property(Constants::OPENED_WITH_DISASSEMBLY).toBool()) { int lineNumber = textEditor->currentLine(); - QString line = textEditor->textDocument()->contents() + QString line = textEditor->textDocument()->plainText() .section(QLatin1Char('\n'), lineNumber - 1, lineNumber - 1); data->address = DisassemblerLine::addressFromDisassemblyLine(line); } else { @@ -847,14 +847,6 @@ public slots: } } - void synchronizeWatchers() - { - for (int i = 0, n = m_snapshotHandler->size(); i != n; ++i) { - if (DebuggerEngine *engine = m_snapshotHandler->at(i)) - engine->watchHandler()->updateWatchers(); - } - } - void editorOpened(Core::IEditor *editor); void updateBreakMenuItem(Core::IEditor *editor); void setBusyCursor(bool busy); @@ -931,7 +923,6 @@ public slots: void aboutToUnloadSession(); void aboutToSaveSession(); - void executeDebuggerCommand(const QString &command, DebuggerLanguages languages); void coreShutdown(); #ifdef WITH_TESTS @@ -1026,7 +1017,6 @@ public slots: void handleExecJumpToLine() { - //removeTooltip(); currentEngine()->resetLocation(); ContextData data; if (currentTextEditorPosition(&data)) @@ -1035,7 +1025,6 @@ public slots: void handleExecRunToLine() { - //removeTooltip(); currentEngine()->resetLocation(); ContextData data; if (currentTextEditorPosition(&data)) @@ -1843,7 +1832,7 @@ void DebuggerPluginPrivate::requestContextMenu(ITextEditor *editor, ITextEditorDocument *document = editor->textDocument(); args.fileName = document->filePath(); if (document->property(Constants::OPENED_WITH_DISASSEMBLY).toBool()) { - QString line = document->contents() + QString line = document->plainText() .section(QLatin1Char('\n'), lineNumber - 1, lineNumber - 1); BreakpointResponse needle; needle.type = BreakpointByAddress; @@ -1956,7 +1945,7 @@ void DebuggerPluginPrivate::toggleBreakpoint() QTC_ASSERT(textEditor, return); const int lineNumber = textEditor->currentLine(); if (textEditor->property(Constants::OPENED_WITH_DISASSEMBLY).toBool()) { - QString line = textEditor->textDocument()->contents() + QString line = textEditor->textDocument()->plainText() .section(QLatin1Char('\n'), lineNumber - 1, lineNumber - 1); quint64 address = DisassemblerLine::addressFromDisassemblyLine(line); toggleBreakpointByAddress(address); @@ -2013,7 +2002,7 @@ void DebuggerPluginPrivate::requestMark(ITextEditor *editor, return; if (editor->property("DisassemblerView").toBool()) { - QString line = editor->textDocument()->contents() + QString line = editor->textDocument()->plainText() .section(QLatin1Char('\n'), lineNumber - 1, lineNumber - 1); quint64 address = DisassemblerLine::addressFromDisassemblyLine(line); toggleBreakpointByAddress(address); @@ -2449,14 +2438,6 @@ void DebuggerPluginPrivate::aboutToSaveSession() m_breakHandler->saveSessionData(); } -void DebuggerPluginPrivate::executeDebuggerCommand(const QString &command, DebuggerLanguages languages) -{ - if (currentEngine()->acceptsDebuggerCommands()) - currentEngine()->executeDebuggerCommand(command, languages); - else - showStatusMessage(tr("User commands are not accepted in the current state.")); -} - void DebuggerPluginPrivate::showStatusMessage(const QString &msg0, int timeout) { showMessage(msg0, LogStatus); @@ -2594,10 +2575,6 @@ static QString formatStartParameters(DebuggerStartParameters &sp) } str << "Sysroot: " << sp.sysRoot << '\n'; str << "Debug Source Location: " << sp.debugSourceLocation.join(QLatin1String(":")) << '\n'; - str << "Dumper libraries: " << QDir::toNativeSeparators(sp.dumperLibrary); - foreach (const QString &dl, sp.dumperLibraryLocations) - str << ' ' << QDir::toNativeSeparators(dl); - str << '\n'; return rc; } diff --git a/src/plugins/debugger/debuggerrunner.cpp b/src/plugins/debugger/debuggerrunner.cpp index 9ccb36c5b8..224bff04a5 100644 --- a/src/plugins/debugger/debuggerrunner.cpp +++ b/src/plugins/debugger/debuggerrunner.cpp @@ -75,7 +75,6 @@ DebuggerEngine *createGdbEngine(const DebuggerStartParameters &sp); DebuggerEngine *createPdbEngine(const DebuggerStartParameters &sp); DebuggerEngine *createQmlEngine(const DebuggerStartParameters &sp); DebuggerEngine *createQmlCppEngine(const DebuggerStartParameters &sp, QString *error); -DebuggerEngine *createLldbLibEngine(const DebuggerStartParameters &sp); DebuggerEngine *createLldbEngine(const DebuggerStartParameters &sp); static const char *engineTypeName(DebuggerEngineType et) @@ -93,8 +92,6 @@ static const char *engineTypeName(DebuggerEngineType et) return "QML engine"; case Debugger::QmlCppEngineType: return "QML C++ engine"; - case Debugger::LldbLibEngineType: - return "LLDB binary engine"; case Debugger::LldbEngineType: return "LLDB command line engine"; case Debugger::AllEngineTypes: @@ -354,8 +351,6 @@ static DebuggerStartParameters localStartParameters(RunConfiguration *runConfigu sp.processArgs = rc->commandLineArguments(); sp.useTerminal = rc->runMode() == LocalApplicationRunConfiguration::Console; - sp.dumperLibrary = rc->dumperLibrary(); - sp.dumperLibraryLocations = rc->dumperLibraryLocations(); if (target) { if (const Project *project = target->project()) { @@ -399,11 +394,6 @@ static DebuggerStartParameters localStartParameters(RunConfiguration *runConfigu } sp.startMode = StartInternal; - - // FIXME: If it's not yet build this will be empty and not filled - // when rebuild as the runConfiguration is not stored and therefore - // cannot be used to retrieve the dumper location. - //qDebug() << "DUMPER: " << sp.dumperLibrary << sp.dumperLibraryLocations; sp.displayName = rc->displayName(); return sp; @@ -518,8 +508,6 @@ DebuggerEngine *DebuggerRunControlFactory::createEngine(DebuggerEngineType et, return createQmlEngine(sp); case LldbEngineType: return createLldbEngine(sp); - case LldbLibEngineType: - return createLldbLibEngine(sp); case QmlCppEngineType: return createQmlCppEngine(sp, errorMessage); default: diff --git a/src/plugins/debugger/debuggersourcepathmappingwidget.cpp b/src/plugins/debugger/debuggersourcepathmappingwidget.cpp index ec7aa34b60..86d3c673f9 100644 --- a/src/plugins/debugger/debuggersourcepathmappingwidget.cpp +++ b/src/plugins/debugger/debuggersourcepathmappingwidget.cpp @@ -244,6 +244,7 @@ DebuggerSourcePathMappingWidget::DebuggerSourcePathMappingWidget(QWidget *parent // Edit part m_targetChooser->setExpectedKind(PathChooser::ExistingDirectory); + m_targetChooser->setHistoryCompleter(QLatin1String("Debugger.MappingTarget.History")); connect(m_sourceLineEdit, SIGNAL(textChanged(QString)), this, SLOT(slotEditSourceFieldChanged())); connect(m_targetChooser, SIGNAL(changed(QString)), diff --git a/src/plugins/debugger/debuggerstartparameters.h b/src/plugins/debugger/debuggerstartparameters.h index b66500a841..52d1054b0a 100644 --- a/src/plugins/debugger/debuggerstartparameters.h +++ b/src/plugins/debugger/debuggerstartparameters.h @@ -133,9 +133,7 @@ public: bool useContinueInsteadOfRun; // if connected to a hw debugger run is not possible but continue is used QByteArray commandsAfterConnect; // additional commands to post after connection to debug target - QString dumperLibrary; QStringList solibSearchPath; - QStringList dumperLibraryLocations; DebuggerStartMode startMode; DebuggerCloseMode closeMode; diff --git a/src/plugins/debugger/disassembleragent.cpp b/src/plugins/debugger/disassembleragent.cpp index 6748ff9fe1..2fec392687 100644 --- a/src/plugins/debugger/disassembleragent.cpp +++ b/src/plugins/debugger/disassembleragent.cpp @@ -192,7 +192,7 @@ void DisassemblerAgent::resetLocation() if (d->resetLocationScheduled) { d->resetLocationScheduled = false; if (d->locationMark) - d->editor->markableInterface()->removeMark(d->locationMark); + d->editor->textDocument()->markableInterface()->removeMark(d->locationMark); } } @@ -346,14 +346,14 @@ void DisassemblerAgent::updateLocationMarker() int lineNumber = contents.lineForAddress(d->location.address()); if (d->location.needsMarker()) { if (d->locationMark) - d->editor->markableInterface()->removeMark(d->locationMark); + d->editor->textDocument()->markableInterface()->removeMark(d->locationMark); delete d->locationMark; d->locationMark = 0; if (lineNumber) { d->locationMark = new ITextMark(lineNumber); d->locationMark->setIcon(debuggerCore()->locationMarkIcon()); d->locationMark->setPriority(TextEditor::ITextMark::HighPriority); - d->editor->markableInterface()->addMark(d->locationMark); + d->editor->textDocument()->markableInterface()->addMark(d->locationMark); } } @@ -379,7 +379,7 @@ void DisassemblerAgent::updateBreakpointMarkers() const DisassemblerLines contents = d->contentsAtCurrentLocation(); foreach (TextEditor::ITextMark *marker, d->breakpointMarks) - d->editor->markableInterface()->removeMark(marker); + d->editor->textDocument()->markableInterface()->removeMark(marker); qDeleteAll(d->breakpointMarks); d->breakpointMarks.clear(); foreach (BreakpointModelId id, ids) { @@ -393,7 +393,7 @@ void DisassemblerAgent::updateBreakpointMarkers() marker->setIcon(handler->icon(id)); marker->setPriority(ITextMark::NormalPriority); d->breakpointMarks.append(marker); - d->editor->markableInterface()->addMark(marker); + d->editor->textDocument()->markableInterface()->addMark(marker); } } diff --git a/src/plugins/debugger/dumper.pro b/src/plugins/debugger/dumper.pro deleted file mode 100644 index 80898b06b5..0000000000 --- a/src/plugins/debugger/dumper.pro +++ /dev/null @@ -1,24 +0,0 @@ -# This is a compile check for the dumpers only. Don't install the library! - -include(../../../qtcreator.pri) - -TEMPLATE = lib -TARGET = DebuggingHelper -DESTDIR = $$IDE_LIBRARY_PATH # /tmp would be better in some respect ... - -linux-* { -CONFIG -= release -CONFIG += debug -# The following line works around a linker issue with gcc 4.1.2 -QMAKE_CXXFLAGS *= -O2 -} - -true { - QT = core -} else { - DEFINES += USE_QT_GUI=1 - QT = core gui -} - -SOURCES += ../../../share/qtcreator/debugger/dumper.cpp - diff --git a/src/plugins/debugger/gdb/attachgdbadapter.cpp b/src/plugins/debugger/gdb/attachgdbadapter.cpp index 038aa5618c..1291689219 100644 --- a/src/plugins/debugger/gdb/attachgdbadapter.cpp +++ b/src/plugins/debugger/gdb/attachgdbadapter.cpp @@ -97,7 +97,7 @@ void GdbAttachEngine::handleAttach(const GdbResponse &response) break; case GdbResultError: if (response.data["msg"].data() == "ptrace: Operation not permitted.") { - notifyInferiorSetupFailed(DumperHelper::msgPtraceError(startParameters().startMode)); + notifyInferiorSetupFailed(msgPtraceError(startParameters().startMode)); break; } // if msg != "ptrace: ..." fall through diff --git a/src/plugins/debugger/gdb/attachgdbadapter.h b/src/plugins/debugger/gdb/attachgdbadapter.h index ca900b0650..9a5db0dff3 100644 --- a/src/plugins/debugger/gdb/attachgdbadapter.h +++ b/src/plugins/debugger/gdb/attachgdbadapter.h @@ -50,8 +50,6 @@ public: explicit GdbAttachEngine(const DebuggerStartParameters &startParameters); private: - DumperHandling dumperHandling() const { return DumperLoadedByGdb; } - void setupEngine(); void setupInferior(); void runEngine(); diff --git a/src/plugins/debugger/gdb/classicgdbengine.cpp b/src/plugins/debugger/gdb/classicgdbengine.cpp deleted file mode 100644 index 518faf29fc..0000000000 --- a/src/plugins/debugger/gdb/classicgdbengine.cpp +++ /dev/null @@ -1,1535 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). -** 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 "gdbengine.h" - -#include <debugger/debuggeractions.h> -#include <debugger/debuggercore.h> -#include <debugger/debuggerprotocol.h> -#include <debugger/debuggerstartparameters.h> -#include <debugger/debuggerstringutils.h> -#include <debugger/sourceutils.h> -#include <debugger/stackhandler.h> - -#include <coreplugin/icore.h> -#include <projectexplorer/projectexplorerconstants.h> -#include <qtsupport/qtsupportconstants.h> -#include <utils/qtcassert.h> -#include <utils/savedaction.h> -#include <QFileInfo> -#include <QMessageBox> -#include <QPushButton> - -#if !defined(Q_OS_WIN) -#include <dlfcn.h> -#endif - -#include <cctype> - -#define PRECONDITION QTC_CHECK(!hasPython()) - -#define CB(callback) &GdbEngine::callback, STRINGIFY(callback) - -enum { - debugPending = 0, - debugSubItem = 0, - debug = 0 -}; - -namespace Debugger { -namespace Internal { - -// ----------------- QtDumperHelper::TypeData -DumperHelper::TypeData::TypeData() : - type(UnknownType), - isTemplate(false) -{ -} - -void DumperHelper::TypeData::clear() -{ - isTemplate = false; - type = UnknownType; - tmplate.clear(); - inner.clear(); -} - -// ----------------- QtDumperHelper -DumperHelper::DumperHelper() : - m_qtVersion(0), - m_dumperVersion(1.0) -{ - qFill(m_specialSizes, m_specialSizes + SpecialSizeCount, 0); - setQClassPrefixes(QByteArray()); -} - -void DumperHelper::clear() -{ - m_nameTypeMap.clear(); - m_qtVersion = 0; - m_dumperVersion = 1.0; - m_qtNamespace.clear(); - m_sizeCache.clear(); - qFill(m_specialSizes, m_specialSizes + SpecialSizeCount, 0); - m_expressionCache.clear(); - setQClassPrefixes(QByteArray()); -} - -QString DumperHelper::msgDumperOutdated(double requiredVersion, double currentVersion) -{ - return QCoreApplication::translate("QtDumperHelper", - "Found an outdated version of the debugging helper library (%1); " - "version %2 is required."). - arg(currentVersion).arg(requiredVersion); -} - -QString DumperHelper::msgPtraceError(DebuggerStartMode sm) -{ - if (sm == StartInternal) { - return QCoreApplication::translate("QtDumperHelper", - "ptrace: Operation not permitted.\n\n" - "Could not attach to the process. " - "Make sure no other debugger traces this process.\n" - "Check the settings of\n" - "/proc/sys/kernel/yama/ptrace_scope\n" - "For more details, see /etc/sysctl.d/10-ptrace.conf\n"); - } - return QCoreApplication::translate("QtDumperHelper", - "ptrace: Operation not permitted.\n\n" - "Could not attach to the process. " - "Make sure no other debugger traces this process.\n" - "If your uid matches the uid\n" - "of the target process, check the settings of\n" - "/proc/sys/kernel/yama/ptrace_scope\n" - "For more details, see /etc/sysctl.d/10-ptrace.conf\n"); -} - -static inline void formatQtVersion(int v, QTextStream &str) -{ - str << ((v >> 16) & 0xFF) << '.' << ((v >> 8) & 0xFF) << '.' << (v & 0xFF); -} - -QString DumperHelper::toString(bool debug) const -{ - if (debug) { - QString rc; - QTextStream str(&rc); - str << "version="; - formatQtVersion(m_qtVersion, str); - str << "dumperversion='" << m_dumperVersion << "' namespace='" << m_qtNamespace << "'," << m_nameTypeMap.size() << " known types <type enum>: "; - const NameTypeMap::const_iterator cend = m_nameTypeMap.constEnd(); - for (NameTypeMap::const_iterator it = m_nameTypeMap.constBegin(); it != cend; ++it) { - str <<",[" << it.key() << ',' << it.value() << ']'; - } - str << "\nSpecial size: "; - for (int i = 0; i < SpecialSizeCount; i++) - str << ' ' << m_specialSizes[i]; - str << "\nSize cache: "; - const SizeCache::const_iterator scend = m_sizeCache.constEnd(); - for (SizeCache::const_iterator it = m_sizeCache.constBegin(); it != scend; ++it) { - str << ' ' << it.key() << '=' << it.value() << '\n'; - } - str << "\nExpression cache: (" << m_expressionCache.size() << ")\n"; - const ExpressionCache::const_iterator excend = m_expressionCache.constEnd(); - for (ExpressionCache::const_iterator it = m_expressionCache.constBegin(); it != excend; ++it) - str << " " << it.key() << ' ' << it.value() << '\n'; - return rc; - } - const QString nameSpace = m_qtNamespace.isEmpty() - ? QCoreApplication::translate("QtDumperHelper", "<none>") : QLatin1String(m_qtNamespace); - return QCoreApplication::translate("QtDumperHelper", - "%n known types, Qt version: %1, Qt namespace: %2 Dumper version: %3", - 0, QCoreApplication::CodecForTr, - m_nameTypeMap.size()).arg(QLatin1String(qtVersionString()), nameSpace).arg(m_dumperVersion); -} - -DumperHelper::Type DumperHelper::simpleType(const QByteArray &simpleType) const -{ - return m_nameTypeMap.value(simpleType, UnknownType); -} - -int DumperHelper::qtVersion() const -{ - return m_qtVersion; -} - -QByteArray DumperHelper::qtNamespace() const -{ - return m_qtNamespace; -} - -int DumperHelper::typeCount() const -{ - return m_nameTypeMap.size(); -} - -// Look up unnamespaced 'std' types. -static DumperHelper::Type stdType(const QByteArray &type) -{ - if (type == "vector") - return DumperHelper::StdVectorType; - if (type == "deque") - return DumperHelper::StdDequeType; - if (type == "set") - return DumperHelper::StdSetType; - if (type == "stack") - return DumperHelper::StdStackType; - if (type == "map") - return DumperHelper::StdMapType; - if (type == "basic_string") - return DumperHelper::StdStringType; - return DumperHelper::UnknownType; -} - -static DumperHelper::Type specialType(QByteArray type) -{ - // Std classes. - if (type.startsWith("std::")) - return stdType(type.mid(5)); - - // Strip namespace - // FIXME: that's not a good idea as it makes all namespaces equal. - const int namespaceIndex = type.lastIndexOf("::"); - if (namespaceIndex == -1) { - // None ... check for std.. - const DumperHelper::Type sType = stdType(type); - if (sType != DumperHelper::UnknownType) - return sType; - } else { - type = type.mid(namespaceIndex + 2); - } - - if (type == "QAbstractItem") - return DumperHelper::QAbstractItemType; - if (type == "QMap") - return DumperHelper::QMapType; - if (type == "QMapNode") - return DumperHelper::QMapNodeType; - if (type == "QMultiMap") - return DumperHelper::QMultiMapType; - if (type == "QObject") - return DumperHelper::QObjectType; - if (type == "QObjectSignal") - return DumperHelper::QObjectSignalType; - if (type == "QObjectSlot") - return DumperHelper::QObjectSlotType; - if (type == "QStack") - return DumperHelper::QStackType; - if (type == "QVector") - return DumperHelper::QVectorType; - if (type == "QWidget") - return DumperHelper::QWidgetType; - return DumperHelper::UnknownType; -} - -QByteArray DumperHelper::qtVersionString() const -{ - QString rc; - QTextStream str(&rc); - formatQtVersion(m_qtVersion, str); - return rc.toLatin1(); -} - -// Parse a list of types. -typedef QList<QByteArray> QByteArrayList; - -static QByteArray qClassName(const QByteArray &qtNamespace, const char *className) -{ - if (qtNamespace.isEmpty()) - return className; - QByteArray rc = qtNamespace; - rc += "::"; - rc += className; - return rc; -} - -static double getDumperVersion(const GdbMi &contents) -{ - const GdbMi dumperVersionG = contents["dumperversion"]; - if (dumperVersionG.type() != GdbMi::Invalid) { - bool ok; - const double v = QString::fromLatin1(dumperVersionG.data()).toDouble(&ok); - if (ok) - return v; - } - return 1.0; -} - - -void DumperHelper::setQClassPrefixes(const QByteArray &qNamespace) -{ - // Prefixes with namespaces - m_qPointerPrefix = qClassName(qNamespace, "QPointer"); - m_qSharedPointerPrefix = qClassName(qNamespace, "QSharedPointer"); - m_qSharedDataPointerPrefix = qClassName(qNamespace, "QSharedDataPointer"); - m_qWeakPointerPrefix = qClassName(qNamespace, "QWeakPointer"); - m_qListPrefix = qClassName(qNamespace, "QList"); - m_qLinkedListPrefix = qClassName(qNamespace, "QLinkedList"); - m_qVectorPrefix = qClassName(qNamespace, "QVector"); - m_qQueuePrefix = qClassName(qNamespace, "QQueue"); -} - -bool DumperHelper::parseQuery(const GdbMi &contents) -{ - clear(); - if (debug > 1) - qDebug() << "parseQuery" << contents.toString(true, 2); - - // Common info, dumper version, etc - QByteArray ns = contents["namespace"].data(); - setQtNamespace(ns); - int qtv = 0; - const GdbMi qtversion = contents["qtversion"]; - if (qtversion.children().size() == 3) { - qtv = (qtversion.childAt(0).toInt() << 16) - + (qtversion.childAt(1).toInt() << 8) - + qtversion.childAt(2).toInt(); - } - m_qtVersion = qtv; - // Get list of helpers - QByteArrayList availableSimpleDebuggingHelpers; - foreach (const GdbMi &item, contents["dumpers"].children()) - availableSimpleDebuggingHelpers.append(item.data()); - - // Parse types - m_nameTypeMap.clear(); - foreach (const QByteArray &type, availableSimpleDebuggingHelpers) { - const Type t = specialType(type); - m_nameTypeMap.insert(type, t != UnknownType ? t : SupportedType); - } - - m_dumperVersion = getDumperVersion(contents); - // Parse sizes - foreach (const GdbMi &sizesList, contents["sizes"].children()) { - const int childCount = sizesList.childCount(); - if (childCount > 1) { - const int size = sizesList.childAt(0).toInt(); - for (int c = 1; c < childCount; c++) - addSize(sizesList.childAt(c).data(), size); - } - } - // Parse expressions - foreach (const GdbMi &exprList, contents["expressions"].children()) - if (exprList.childCount() == 2) - m_expressionCache.insert(exprList.childAt(0).data(), - exprList.childAt(1).data()); - return true; -} - -void DumperHelper::addSize(const QByteArray &name, int size) -{ - // Special interest cases - if (name == "char*") { - m_specialSizes[PointerSize] = size; - return; - } - const SpecialSizeType st = specialSizeType(name); - if (st != SpecialSizeCount) { - m_specialSizes[st] = size; - return; - } - do { - // CDB helpers - if (name == "std::string") { - m_sizeCache.insert("std::basic_string<char,std::char_traits<char>,std::allocator<char> >", size); - m_sizeCache.insert("basic_string<char,char_traits<char>,allocator<char> >", size); - break; - } - if (name == "std::wstring") { - m_sizeCache.insert("basic_string<unsigned short,char_traits<unsignedshort>,allocator<unsignedshort> >", size); - m_sizeCache.insert("std::basic_string<unsigned short,std::char_traits<unsigned short>,std::allocator<unsigned short> >", size); - break; - } - } while (false); - m_sizeCache.insert(name, size); -} - -DumperHelper::Type DumperHelper::type(const QByteArray &typeName) const -{ - const DumperHelper::TypeData td = typeData(typeName); - return td.type; -} - -static bool extractTemplate(const QByteArray &type, QByteArray *tmplate, QByteArray *inner) -{ - // Input "Template<Inner1,Inner2,...>::Foo" will return "Template::Foo" in - // 'tmplate' and "Inner1@Inner2@..." etc in 'inner'. Result indicates - // whether parsing was successful - // Gdb inserts a blank after each comma which we would like to avoid - tmplate->clear(); - inner->clear(); - if (!type.contains('<')) - return false; - int level = 0; - bool skipSpace = false; - const int size = type.size(); - - for (int i = 0; i != size; ++i) { - const char c = type.at(i); - switch (c) { - case '<': - *(level == 0 ? tmplate : inner) += c; - ++level; - break; - case '>': - --level; - *(level == 0 ? tmplate : inner) += c; - break; - case ',': - *inner += (level == 1) ? '@' : ','; - skipSpace = true; - break; - default: - if (!skipSpace || c != ' ') { - *(level == 0 ? tmplate : inner) += c; - skipSpace = false; - } - break; - } - } - *tmplate = tmplate->trimmed(); - tmplate->replace("<>", ""); - tmplate->replace("'", ""); // Sometimes 'std::vector' is reported, with quotes. - *inner = inner->trimmed(); - // qDebug() << "EXTRACT TEMPLATE: " << *tmplate << *inner << " FROM " << type; - return !inner->isEmpty(); -} - -DumperHelper::TypeData DumperHelper::typeData(const QByteArray &typeName) const -{ - TypeData td; - td.type = UnknownType; - const Type st = simpleType(typeName); - if (st != UnknownType) { - td.isTemplate = false; - td.type = st; - return td; - } - // Try template - td.isTemplate = extractTemplate(typeName, &td.tmplate, &td.inner); - if (!td.isTemplate) - return td; - // Check the template type QMap<X,Y> -> 'QMap' - td.type = simpleType(td.tmplate); - return td; -} - -static QByteArray sizeofTypeExpression(const QByteArray &type) -{ - if (type.endsWith('*')) - return "sizeof(void*)"; - if (type.endsWith('>')) - return "sizeof(" + type + ')'; - return "sizeof(" + gdbQuoteTypes(type) + ')'; -} - -// Format an expression to have the debugger query the -// size. Use size cache if possible -QByteArray DumperHelper::evaluationSizeofTypeExpression(const QByteArray &typeName) const -{ - // Look up special size types - const SpecialSizeType st = specialSizeType(typeName); - if (st != SpecialSizeCount) { - if (const int size = m_specialSizes[st]) - return QByteArray::number(size); - } - // Look up size cache - const SizeCache::const_iterator sit = m_sizeCache.constFind(typeName); - if (sit != m_sizeCache.constEnd()) - return QByteArray::number(sit.value()); - // Finally have the debugger evaluate - return sizeofTypeExpression(typeName); -} - -DumperHelper::SpecialSizeType DumperHelper::specialSizeType(const QByteArray &typeName) const -{ - if (isPointerType(typeName)) - return PointerSize; - if (typeName == "int") - return IntSize; - if (typeName.startsWith("std::allocator")) - return StdAllocatorSize; - if (typeName.startsWith(m_qPointerPrefix)) - return QPointerSize; - if (typeName.startsWith(m_qSharedPointerPrefix)) - return QSharedPointerSize; - if (typeName.startsWith(m_qSharedDataPointerPrefix)) - return QSharedDataPointerSize; - if (typeName.startsWith(m_qWeakPointerPrefix)) - return QWeakPointerSize; - if (typeName.startsWith(m_qListPrefix)) - return QListSize; - if (typeName.startsWith(m_qLinkedListPrefix)) - return QLinkedListSize; - if (typeName.startsWith(m_qVectorPrefix)) - return QVectorSize; - if (typeName.startsWith(m_qQueuePrefix)) - return QQueueSize; - return SpecialSizeCount; -} - -static inline bool isInteger(const QByteArray &n) -{ - const int size = n.size(); - if (!size) - return false; - for (int i = 0; i < size; i++) - if (!std::isdigit(n.at(i))) - return false; - return true; -} - -// Return debugger expression to get the offset of a map node. -static inline QByteArray qMapNodeValueOffsetExpression(const QByteArray &type) -{ - return "(size_t)&(('" + type + "'*)0)->value"; -} - -void DumperHelper::evaluationParameters(const WatchData &data, - const TypeData &td, QByteArray *inBuffer, QByteArrayList *extraArgsIn) const -{ - enum { maxExtraArgCount = 4 }; - - QByteArrayList &extraArgs = *extraArgsIn; - - // See extractTemplate for parameters - QByteArrayList inners = td.inner.split('@'); - if (inners.at(0).isEmpty()) - inners.clear(); - for (int i = 0; i != inners.size(); ++i) - inners[i] = inners[i].simplified(); - - QByteArray outertype = td.isTemplate ? td.tmplate : data.type; - // adjust the data extract - if (outertype == m_qtNamespace + "QWidget") - outertype = m_qtNamespace + "QObject"; - - QByteArray inner = td.inner; - const QByteArray zero = "0"; - - extraArgs.clear(); - - if (!inners.empty()) { - // "generic" template dumpers: passing sizeof(argument) - // gives already most information the dumpers need - const int count = qMin(int(maxExtraArgCount), inners.size()); - for (int i = 0; i < count; i++) - extraArgs.push_back(evaluationSizeofTypeExpression(inners.at(i))); - } - - // Pad with zeros - while (extraArgs.size() < maxExtraArgCount) - extraArgs.push_back("0"); - - // in rare cases we need more or less: - switch (td.type) { - case QAbstractItemType: - if (data.dumperFlags.isEmpty()) - qWarning("Internal error: empty dumper state '%s'.", data.iname.constData()); - else - inner = data.dumperFlags.mid(1); - break; - case QObjectSlotType: - case QObjectSignalType: { - // we need the number out of something like - // iname="local.ob.slots.2" // ".deleteLater()"? - const int pos = data.iname.lastIndexOf('.'); - const QByteArray slotNumber = data.iname.mid(pos + 1); - QTC_CHECK(slotNumber.toInt() != -1); - extraArgs[0] = slotNumber; - } - break; - case QMapType: - case QMultiMapType: { - QByteArray nodetype; - if (m_qtVersion >= 0x040500) { - nodetype = m_qtNamespace + "QMapNode"; - nodetype += data.type.mid(outertype.size()); - } else { - // FIXME: doesn't work for QMultiMap - nodetype = data.type + "::Node"; - } - //qDebug() << "OUTERTYPE: " << outertype << " NODETYPE: " << nodetype - // << "QT VERSION" << m_qtVersion << ((4 << 16) + (5 << 8) + 0); - extraArgs[2] = evaluationSizeofTypeExpression(nodetype); - extraArgs[3] = qMapNodeValueOffsetExpression(nodetype); - } - break; - case QMapNodeType: - extraArgs[2] = evaluationSizeofTypeExpression(data.type); - extraArgs[3] = qMapNodeValueOffsetExpression(data.type); - break; - case StdVectorType: - //qDebug() << "EXTRACT TEMPLATE: " << outertype << inners; - if (inners.at(0) == "bool") - outertype = "std::vector::bool"; - break; - case StdDequeType: - extraArgs[1] = "0"; - break; - case StdStackType: - // remove 'std::allocator<...>': - extraArgs[1] = "0"; - break; - case StdSetType: - // remove 'std::less<...>': - extraArgs[1] = "0"; - // remove 'std::allocator<...>': - extraArgs[2] = "0"; - break; - case StdMapType: { - // We need the offset of the second item in the value pair. - // We read the type of the pair from the allocator argument because - // that gets the constness "right" (in the sense that gdb/cdb can - // read it back: "std::allocator<std::pair<Key,Value> >" - // -> "std::pair<Key,Value>". Different debuggers have varying - // amounts of terminating blanks... - extraArgs[2].clear(); - extraArgs[3] = "0"; - QByteArray pairType = inners.at(3); - int bracketPos = pairType.indexOf('<'); - if (bracketPos != -1) - pairType.remove(0, bracketPos + 1); - // We don't want the comparator and the allocator confuse gdb. - const char closingBracket = '>'; - bracketPos = pairType.lastIndexOf(closingBracket); - if (bracketPos != -1) - bracketPos = pairType.lastIndexOf(closingBracket, bracketPos - pairType.size() - 1); - if (bracketPos != -1) - pairType.truncate(bracketPos + 1); - extraArgs[2] = "(size_t)&(('"; - extraArgs[2] += pairType; - extraArgs[2] += "'*)0)->second"; - } - break; - case StdStringType: - //qDebug() << "EXTRACT TEMPLATE: " << outertype << inners; - if (inners.at(0) == "char") - outertype = "std::string"; - else if (inners.at(0) == "wchar_t") - outertype = "std::wstring"; - qFill(extraArgs, zero); - break; - case UnknownType: - qWarning("Unknown type encountered in %s.\n", Q_FUNC_INFO); - break; - case SupportedType: - case QVectorType: - case QStackType: - case QObjectType: - case QWidgetType: - break; - } - - // Look up expressions in the cache - if (!m_expressionCache.empty()) { - const ExpressionCache::const_iterator excCend = m_expressionCache.constEnd(); - const QByteArrayList::iterator eend = extraArgs.end(); - for (QByteArrayList::iterator it = extraArgs.begin(); it != eend; ++it) { - QByteArray &e = *it; - if (!e.isEmpty() && e != zero && !isInteger(e)) { - const ExpressionCache::const_iterator eit = m_expressionCache.constFind(e); - if (eit != excCend) - e = eit.value(); - } - } - } - - inBuffer->clear(); - inBuffer->append(outertype); - inBuffer->append('\0'); - inBuffer->append(data.iname); - inBuffer->append('\0'); - inBuffer->append(data.exp); - inBuffer->append('\0'); - inBuffer->append(inner); - inBuffer->append('\0'); - inBuffer->append(data.iname); - inBuffer->append('\0'); - - if (debug) - qDebug() << '\n' << Q_FUNC_INFO << '\n' << data.toString() << "\n-->" << outertype << td.type << extraArgs; -} - -QDebug operator<<(QDebug in, const DumperHelper::TypeData &d) -{ - QDebug nsp = in.nospace(); - nsp << " type=" << d.type << " tpl=" << d.isTemplate; - if (d.isTemplate) - nsp << d.tmplate << '<' << d.inner << '>'; - return in; -} - -static bool isAccessSpecifier(const QByteArray &ba) -{ - return ba == "private" || ba == "protected" || ba == "public"; -} - -// reads a MI-encoded item frome the consolestream -static bool parseConsoleStream(const GdbResponse &response, GdbMi *contents) -{ - QByteArray out = response.consoleStreamOutput; - - int markerPos = out.indexOf('"') + 1; // position of 'success marker' - if (markerPos == 0 || out.at(markerPos) == 'f') { // 't' or 'f' - // custom dumper produced no output - return false; - } - - out = out.mid(markerPos + 1); - out = out.left(out.lastIndexOf('"')); - // optimization: dumper output never needs real C unquoting - out.replace('\\', ""); - - contents->fromStringMultiple(out); - //qDebug() << "CONTENTS" << contents->toString(true); - return contents->isValid(); -} - -void GdbEngine::updateLocalsClassic() -{ - PRECONDITION; - //m_pendingWatchRequests = 0; - m_pendingBreakpointRequests = 0; - m_processedNames.clear(); - - QByteArray level = QByteArray::number(currentFrame()); - // '2' is 'list with type and value' - QByteArray cmd = "-stack-list-arguments 2 " + level + ' ' + level; - postCommand(cmd, Discardable, - CB(handleStackListArgumentsClassic)); - // '2' is 'list with type and value' - postCommand("-stack-list-locals 2", Discardable, - CB(handleStackListLocalsClassic)); // stage 2/2 -} - -static inline QString msgRetrievingWatchData(int pending) -{ - return GdbEngine::tr("Retrieving data for watch view (%n requests pending)...", 0, pending); -} - -void GdbEngine::runDirectDebuggingHelperClassic(const WatchData &data, bool dumpChildren) -{ - Q_UNUSED(dumpChildren) - QByteArray type = data.type; - QByteArray cmd; - - if (type == "QString" || type.endsWith("::QString")) - cmd = "qdumpqstring (&(" + data.exp + "))"; - else if (type == "QStringList" || type.endsWith("::QStringList")) - cmd = "qdumpqstringlist (&(" + data.exp + "))"; - - QVariant var; - var.setValue(data); - postCommand(cmd, Discardable, CB(handleDebuggingHelperValue3Classic), var); - - showStatusMessage(msgRetrievingWatchData(m_uncompleted.size()), 10000); -} - -void GdbEngine::runDebuggingHelperClassic(const WatchData &data0, bool dumpChildren) -{ - PRECONDITION; - if (m_debuggingHelperState != DebuggingHelperAvailable) { - runDirectDebuggingHelperClassic(data0, dumpChildren); - return; - } - WatchData data = data0; - - // Avoid endless loops created by faulty dumpers. - QByteArray processedName = QByteArray::number(dumpChildren) + '-' + data.iname; - if (m_processedNames.contains(processedName)) { - showMessage( - _("<Breaking endless loop for " + data.iname + '>'), LogMiscInput); - data.setAllUnneeded(); - data.setValue(_("<unavailable>")); - data.setHasChildren(false); - insertData(data); - return; - } - m_processedNames.insert(processedName); - - QByteArray params; - QList<QByteArray> extraArgs; - const DumperHelper::TypeData td = m_dumperHelper.typeData(data0.type); - m_dumperHelper.evaluationParameters(data, td, ¶ms, &extraArgs); - - //int protocol = (data.iname.startsWith("watch") && data.type == "QImage") ? 3 : 2; - //int protocol = data.iname.startsWith("watch") ? 3 : 2; - const int protocol = 2; - //int protocol = isDisplayedIName(data.iname) ? 3 : 2; - - QByteArray addr; - if (data.address) - addr = "(void*)" + data.hexAddress(); - else if (data.exp.isEmpty()) // happens e.g. for QAbstractItem - addr = QByteArray(1, '0'); - else - addr = "&(" + data.exp + ')'; - - sendWatchParameters(params); - - QByteArray cmd = "call (void*)qDumpObjectData440(" - + QByteArray::number(protocol) - + ",0," - + addr - + ',' - + (dumpChildren ? '1' : '0'); - foreach (const QByteArray &ex, extraArgs) - cmd += ',' + ex; - cmd += ')'; - - postCommand(cmd, Discardable | NonCriticalResponse); - - showStatusMessage(msgRetrievingWatchData(m_uncompleted.size()), 10000); - - // retrieve response - postCommand("p (char*)&qDumpOutBuffer", Discardable, - CB(handleDebuggingHelperValue2Classic), qVariantFromValue(data)); -} - -void GdbEngine::createGdbVariableClassic(const WatchData &data) -{ - PRECONDITION; - postCommand("-var-delete \"" + data.iname + '"', Discardable); - QByteArray exp = data.exp; - if (exp.isEmpty() && data.address) - exp = "*(" + gdbQuoteTypes(data.type) + "*)" + data.hexAddress(); - QVariant val = QVariant::fromValue<WatchData>(data); - postCommand("-var-create \"" + data.iname + "\" * \"" + exp + '"', - Discardable, CB(handleVarCreate), val); -} - -void GdbEngine::updateSubItemClassic(const WatchData &data0) -{ - PRECONDITION; - WatchData data = data0; - if (debugSubItem) - qDebug() << "UPDATE SUBITEM:" << data.toString(); - QTC_ASSERT(data.isValid(), return); - - // in any case we need the type first - if (data.isTypeNeeded()) { - // This should only happen if we don't have a variable yet. - // Let's play safe, though. - if (!data.variable.isEmpty()) { - // Update: It does so for out-of-scope watchers. -# if 1 - qDebug() << "FIXME: GdbEngine::updateSubItem:" - << data.toString() << "should not happen"; -# else - data.setType(WatchData::msgNotInScope()); - data.setValue(WatchData::msgNotInScope()); - data.setHasChildren(false); - insertData(data); - return; -# endif - } - // The WatchVarCreate handler will receive type information - // and re-insert a WatchData item with correct type, so - // we will not re-enter this bit. - // FIXME: Concurrency issues? - createGdbVariableClassic(data); - return; - } - - // We should have a type now. This is relied upon further below. - QTC_ASSERT(!data.type.isEmpty(), return); - - // A common case that can be easily solved. - if (data.isChildrenNeeded() && isPointerType(data.type) - && !hasDebuggingHelperForType(data.type)) { - // We sometimes know what kind of children pointers have. - if (debugSubItem) - qDebug() << "IT'S A POINTER"; - - if (debuggerCore()->boolSetting(AutoDerefPointers)) { - // Try automatic dereferentiation - data.exp = "(*(" + data.exp + "))"; - data.type = data.type + '.'; // FIXME: fragile HACK to avoid recursion - if (data.value.startsWith(QLatin1String("0x"))) - data.value.insert(0, QLatin1Char('@')); - insertData(data); - } else { - data.setChildrenUnneeded(); - insertData(data); - WatchData data1; - data1.iname = data.iname + ".*"; - data1.name = QLatin1Char('*') + data.name; - data1.exp = "(*(" + data.exp + "))"; - data1.type = stripPointerType(data.type); - data1.setValueNeeded(); - data1.setChildrenUnneeded(); - insertData(data1); - } - return; - } - - if (data.isValueNeeded() && hasDebuggingHelperForType(data.type)) { - if (debugSubItem) - qDebug() << "UPDATE SUBITEM: CUSTOMVALUE"; - runDebuggingHelperClassic(data, - watchHandler()->isExpandedIName(data.iname)); - return; - } - -/* - if (data.isValueNeeded() && data.exp.isEmpty()) { - if (debugSubItem) - qDebug() << "UPDATE SUBITEM: NO EXPRESSION?"; - data.setError("<no expression given>"); - insertData(data); - return; - } -*/ - - if (data.isValueNeeded() && data.variable.isEmpty()) { - if (debugSubItem) - qDebug() << "UPDATE SUBITEM: VARIABLE NEEDED FOR VALUE"; - createGdbVariableClassic(data); - // the WatchVarCreate handler will re-insert a WatchData - // item, with valueNeeded() set. - return; - } - - if (data.isValueNeeded()) { - QTC_ASSERT(!data.variable.isEmpty(), return); // tested above - if (debugSubItem) - qDebug() << "UPDATE SUBITEM: VALUE"; - QByteArray cmd = "-var-evaluate-expression \"" + data.iname + '"'; - postCommand(cmd, Discardable, - CB(handleEvaluateExpressionClassic), QVariant::fromValue(data)); - return; - } - - if (data.isChildrenNeeded() && hasDebuggingHelperForType(data.type)) { - if (debugSubItem) - qDebug() << "UPDATE SUBITEM: CUSTOMVALUE WITH CHILDREN"; - runDebuggingHelperClassic(data, true); - return; - } - - if (data.isChildrenNeeded() && data.variable.isEmpty()) { - if (debugSubItem) - qDebug() << "UPDATE SUBITEM: VARIABLE NEEDED FOR CHILDREN"; - createGdbVariableClassic(data); - // the WatchVarCreate handler will re-insert a WatchData - // item, with childrenNeeded() set. - return; - } - - if (data.isChildrenNeeded()) { - QTC_ASSERT(!data.variable.isEmpty(), return); // tested above - QByteArray cmd = "-var-list-children --all-values \"" + data.variable + '"'; - postCommand(cmd, Discardable, - CB(handleVarListChildrenClassic), QVariant::fromValue(data)); - return; - } - - if (data.isHasChildrenNeeded() && hasDebuggingHelperForType(data.type)) { - if (debugSubItem) - qDebug() << "UPDATE SUBITEM: CUSTOMVALUE WITH CHILDREN"; - runDebuggingHelperClassic(data, watchHandler()->isExpandedIName(data.iname)); - return; - } - -//#if !X - if (data.isHasChildrenNeeded() && data.variable.isEmpty()) { - if (debugSubItem) - qDebug() << "UPDATE SUBITEM: VARIABLE NEEDED FOR CHILDCOUNT"; - createGdbVariableClassic(data); - // the WatchVarCreate handler will re-insert a WatchData - // item, with childrenNeeded() set. - return; - } -//#endif - - if (data.isHasChildrenNeeded()) { - QTC_ASSERT(!data.variable.isEmpty(), return); // tested above - QByteArray cmd = "-var-list-children --all-values \"" + data.variable + '"'; - postCommand(cmd, Discardable, - CB(handleVarListChildrenClassic), QVariant::fromValue(data)); - return; - } - - qDebug() << "FIXME: UPDATE SUBITEM:" << data.toString(); - QTC_ASSERT(false, return); -} - -void GdbEngine::handleDebuggingHelperValue2Classic(const GdbResponse &response) -{ - PRECONDITION; - WatchData data = response.cookie.value<WatchData>(); - QTC_ASSERT(data.isValid(), return); - - // The real dumper might have aborted without giving any answers. - // Remove traces of the question, too. - if (m_cookieForToken.contains(response.token - 1)) { - m_cookieForToken.remove(response.token - 1); - showMessage(_("DETECTING LOST COMMAND %1").arg(response.token - 1)); - // --m_pendingWatchRequests; - data.setError(WatchData::msgNotInScope()); - insertData(data); - return; - } - - //qDebug() << "CUSTOM VALUE RESULT:" << response.toString(); - //qDebug() << "FOR DATA:" << data.toString() << response.resultClass; - if (response.resultClass != GdbResultDone) { - qDebug() << "STRANGE CUSTOM DUMPER RESULT DATA:" << data.toString(); - return; - } - - GdbMi contents; - if (!parseConsoleStream(response, &contents)) { - data.setError(WatchData::msgNotInScope()); - insertData(data); - return; - } - - data.updateType(response.data["type"]); - data.updateDisplayedType(response.data["displaytype"]); - QList<WatchData> list; - parseWatchData(watchHandler()->expandedINames(), data, contents, &list); - //for (int i = 0; i != list.size(); ++i) - // qDebug() << "READ: " << list.at(i).toString(); - foreach (const WatchData &data, list) - insertData(data); -} - -void GdbEngine::handleDebuggingHelperValue3Classic(const GdbResponse &response) -{ - if (response.resultClass == GdbResultDone) { - WatchData data = response.cookie.value<WatchData>(); - QByteArray out = response.consoleStreamOutput; - while (out.endsWith(' ') || out.endsWith('\n')) - out.chop(1); - QList<QByteArray> list = out.split(' '); - if (list.isEmpty()) { - data.setError(WatchData::msgNotInScope()); - data.setAllUnneeded(); - insertData(data); - } else if (data.type == "QString" - || data.type.endsWith("::QString")) { - QList<QByteArray> list = out.split(' '); - QString str; - int l = out.isEmpty() ? 0 : list.size(); - for (int i = 0; i < l; ++i) - str.append(list.at(i).toInt()); - data.setValue(QLatin1Char('"') + str + QLatin1Char('"')); - data.setHasChildren(false); - data.setAllUnneeded(); - insertData(data); - } else if (data.type == "QStringList" - || data.type.endsWith("::QStringList")) { - if (out.isEmpty()) { - data.setValue(tr("<0 items>")); - data.setHasChildren(false); - data.setAllUnneeded(); - insertData(data); - } else { - int l = list.size(); - //: In string list - data.setValue(tr("<%n items>", 0, l)); - data.setHasChildren(!list.empty()); - data.setAllUnneeded(); - insertData(data); - for (int i = 0; i < l; ++i) { - WatchData data1; - data1.name = _("[%1]").arg(i); - data1.type = data.type.left(data.type.size() - 4); - data1.iname = data.iname + '.' + QByteArray::number(i); - const QByteArray &addressSpec = list.at(i); - if (addressSpec.startsWith("0x")) - data.setHexAddress(addressSpec); - else - data.dumperFlags = addressSpec; // Item model dumpers pull tricks - data1.exp = "((" + gdbQuoteTypes(data1.type) + "*)" + addressSpec + ')'; - data1.setHasChildren(false); - data1.setValueNeeded(); - QByteArray cmd = "qdumpqstring (" + data1.exp + ')'; - QVariant var; - var.setValue(data1); - postCommand(cmd, Discardable, - CB(handleDebuggingHelperValue3Classic), var); - } - } - } else { - data.setError(WatchData::msgNotInScope()); - data.setAllUnneeded(); - insertData(data); - } - } else { - WatchData data = response.cookie.value<WatchData>(); - data.setError(WatchData::msgNotInScope()); - data.setAllUnneeded(); - insertData(data); - } -} - -void GdbEngine::tryLoadDebuggingHelpersClassic() -{ - if (m_forceAsyncModel) - return; - - PRECONDITION; - if (dumperHandling() == GdbEngine::DumperNotAvailable) { - // Load at least gdb macro based dumpers. - m_debuggingHelperState = DebuggingHelperLoadTried; - postCommand(Utils::FileReader::fetchQrc(_(":/gdb/gdbmacros.txt"))); - return; - } - - if (debugPending) - qDebug() << "TRY LOAD CUSTOM DUMPERS"; - m_debuggingHelperState = DebuggingHelperUnavailable; - if (!checkDebuggingHelpersClassic()) - return; - - m_debuggingHelperState = DebuggingHelperLoadTried; - - // Do not use STRINGIFY for RTLD_NOW as we really want to expand that to a number. -#if defined(Q_OS_WIN) - // We are using Python on Windows. - QTC_CHECK(false); -#elif defined(Q_OS_MAC) - QByteArray dlopenLib = startParameters().dumperLibrary.toLocal8Bit(); - //postCommand("sharedlibrary libc"); // for malloc - //postCommand("sharedlibrary libdl"); // for dlopen - const QByteArray flag = QByteArray::number(RTLD_NOW); - postCommand("call (void)dlopen(\"" + GdbMi::escapeCString(dlopenLib) - + "\", " + flag + ")", - CB(handleDebuggingHelperSetup)); - //postCommand("sharedlibrary " + dotEscape(dlopenLib)); -#else - QByteArray dlopenLib = startParameters().dumperLibrary.toLocal8Bit(); - //postCommand("p dlopen"); - const QByteArray flag = QByteArray::number(RTLD_NOW); - postCommand("sharedlibrary libc"); // for malloc - postCommand("sharedlibrary libdl"); // for dlopen - postCommand("call (void*)dlopen(\"" + GdbMi::escapeCString(dlopenLib) - + "\", " + flag + ")", - CB(handleDebuggingHelperSetup)); - // Some older systems like CentOS 4.6 prefer this: - postCommand("call (void*)__dlopen(\"" + GdbMi::escapeCString(dlopenLib) - + "\", " + flag + ")", - CB(handleDebuggingHelperSetup)); - postCommand("sharedlibrary " + dotEscape(dlopenLib)); -#endif - - // Retrieve list of dumpable classes. - postCommand("call (void*)qDumpObjectData440(1,0,0,0,0,0,0,0)"); - postCommand("p (char*)&qDumpOutBuffer", CB(handleQueryDebuggingHelperClassic)); -} - -// Called from CoreAdapter and AttachAdapter -void GdbEngine::updateAllClassic() -{ - PRECONDITION; - if (debugPending) - qDebug() << "UPDATING ALL\n"; - QTC_ASSERT(state() == InferiorUnrunnable || state() == InferiorStopOk, - qDebug() << state()); - tryLoadDebuggingHelpersClassic(); - reloadModulesInternal(); - postCommand("-stack-list-frames", Discardable, - CB(handleStackListFrames), - QVariant::fromValue<StackCookie>(StackCookie(false, true))); - stackHandler()->setCurrentIndex(0); - if (supportsThreads()) - postCommand("-thread-list-ids", Discardable, CB(handleThreadListIds), 0); - reloadRegisters(); - updateLocals(); -} - -void GdbEngine::setDebuggingHelperStateClassic(DebuggingHelperState s) -{ - PRECONDITION; - m_debuggingHelperState = s; -} - -void GdbEngine::handleStackListArgumentsClassic(const GdbResponse &response) -{ - PRECONDITION; - // stage 1/2 - - // Linux: - // 12^done,stack-args= - // [frame={level="0",args=[ - // {name="argc",type="int",value="1"}, - // {name="argv",type="char **",value="(char **) 0x7..."}]}] - // Mac: - // 78^done,stack-args= - // {frame={level="0",args={ - // varobj= - // {exp="this",value="0x38a2fab0",name="var21",numchild="3", - // type="CurrentDocumentFind * const",typecode="PTR", - // dynamic_type="",in_scope="true",block_start_addr="0x3938e946", - // block_end_addr="0x3938eb2d"}, - // varobj= - // {exp="before",value="@0xbfffb9f8: {d = 0x3a7f2a70}", - // name="var22",numchild="1",type="const QString ...} }}} - // - // In both cases, iterating over the children of stack-args/frame/args - // is ok. - m_currentFunctionArgs.clear(); - if (response.resultClass == GdbResultDone) { - const GdbMi list = response.data["stack-args"]; - const GdbMi frame = list["frame"]; - const GdbMi args = frame["args"]; - m_currentFunctionArgs = args.children(); - } else { - // Seems to occur on "RedHat 4 based Linux" gdb 7.0.1: - // ^error,msg="Cannot access memory at address 0x0" - showMessage(_("UNEXPECTED RESPONSE: ") + QLatin1String(response.toString())); - } -} - -void GdbEngine::handleStackListLocalsClassic(const GdbResponse &response) -{ - PRECONDITION; - // stage 2/2 - - // There could be shadowed variables - QList<GdbMi> locals = response.data["locals"].children(); - locals += m_currentFunctionArgs; - QMap<QByteArray, int> seen; - // If desired, retrieve list of uninitialized variables looking at - // the current frame. This is invoked first time after a stop from - // handleStop1, which passes on the frame as cookie. The whole stack - // is not known at this point. - QStringList uninitializedVariables; - if (debuggerCore()->action(UseCodeModel)->isChecked()) { - const StackFrame frame = - response.cookie.canConvert<Debugger::Internal::StackFrame>() - ? qvariant_cast<Debugger::Internal::StackFrame>(response.cookie) - : stackHandler()->currentFrame(); - if (frame.isUsable()) - getUninitializedVariables(debuggerCore()->cppCodeModelSnapshot(), - frame.function, frame.file, frame.line, - &uninitializedVariables); - } - WatchHandler *handler = watchHandler(); - insertData(*handler->findData("local")); - - foreach (const GdbMi &item, locals) { - const WatchData data = localVariable(item, uninitializedVariables, &seen); - if (data.isValid()) - insertData(data); - } - - if (!m_resultVarName.isEmpty()) { - WatchData rd; - rd.iname = "return.0"; - rd.name = QLatin1String("return"); - rd.exp = m_resultVarName; - insertData(rd); - } - - handler->updateWatchers(); -} - -static void showQtDumperLibraryWarning(const QString &details) -{ - QMessageBox dialog(Core::ICore::mainWindow()); - QPushButton *qtPref = dialog.addButton(DebuggerCore::tr("Open Qt Options"), - QMessageBox::ActionRole); - QPushButton *helperOff = dialog.addButton(DebuggerCore::tr("Turn off Helper Usage"), - QMessageBox::ActionRole); - QPushButton *justContinue = dialog.addButton(DebuggerCore::tr("Continue Anyway"), - QMessageBox::AcceptRole); - dialog.setDefaultButton(justContinue); - dialog.setWindowTitle(DebuggerCore::tr("Debugging Helper Missing")); - dialog.setText(DebuggerCore::tr("The debugger could not load the debugging helper library.")); - dialog.setInformativeText(DebuggerCore::tr( - "The debugging helper is used to nicely format the values of some Qt " - "and Standard Library data types. " - "It must be compiled for each used Qt version separately. " - "In the Qt Creator Build and Run preferences page, select a Qt version, " - "expand the Details section and click Build All.")); - if (!details.isEmpty()) - dialog.setDetailedText(details); -#if defined(Q_OS_MAC) && QT_VERSION >= 0x050000 - dialog.setWindowModality(Qt::WindowModal); -#endif - dialog.exec(); - if (dialog.clickedButton() == qtPref) { - Core::ICore::showOptionsDialog( - ProjectExplorer::Constants::PROJECTEXPLORER_SETTINGS_CATEGORY, - QtSupport::Constants::QTVERSION_SETTINGS_PAGE_ID); - } else if (dialog.clickedButton() == helperOff) { - debuggerCore()->action(UseDebuggingHelpers)->setValue(qVariantFromValue(false), false); - } -} - -bool GdbEngine::checkDebuggingHelpersClassic() -{ - PRECONDITION; - if (!debuggerCore()->boolSetting(UseDebuggingHelpers)) - return false; - const QString lib = startParameters().dumperLibrary; - if (QFileInfo(lib).exists()) - return true; - const QStringList &locations = startParameters().dumperLibraryLocations; - const QString loc = locations.join(QLatin1String(", ")); - const QString msg = tr("The debugging helper library was not found at %1.") - .arg(loc); - showMessage(msg); - // This can happen for remote debugging. - if (!locations.isEmpty()) - showQtDumperLibraryWarning(msg); // This might build the library. - return QFileInfo(lib).exists(); -} - -void GdbEngine::handleQueryDebuggingHelperClassic(const GdbResponse &response) -{ - const double dumperVersionRequired = 1.0; - //qDebug() << "DATA DUMPER TRIAL:" << response.toString(); - - GdbMi contents; - QTC_ASSERT(parseConsoleStream(response, &contents), qDebug() << response.toString()); - const bool ok = m_dumperHelper.parseQuery(contents) - && m_dumperHelper.typeCount(); - if (ok) { - // Get version and sizes from dumpers. Expression cache - // currently causes errors. - const double dumperVersion = getDumperVersion(contents); - if (dumperVersion < dumperVersionRequired) { - showQtDumperLibraryWarning( - DumperHelper::msgDumperOutdated(dumperVersionRequired, dumperVersion)); - m_debuggingHelperState = DebuggingHelperUnavailable; - return; - } - m_debuggingHelperState = DebuggingHelperAvailable; - const QString successMsg = tr("Dumper version %1, %n custom dumpers found.", - 0, m_dumperHelper.typeCount()).arg(dumperVersion); - showStatusMessage(successMsg); - - // Sanity check for Qt version of dumpers and debuggee. - QByteArray ns = m_dumperHelper.qtNamespace(); - postCommand("-var-create A@ * '" + ns + "qVersion'()", - CB(handleDebuggingHelperVersionCheckClassic)); - postCommand("-var-delete A@"); - } else { - // Retry if thread has not terminated yet. - m_debuggingHelperState = DebuggingHelperUnavailable; - showStatusMessage(tr("Debugging helpers not found.")); - } - //qDebug() << m_dumperHelper.toString(true); - //qDebug() << m_availableSimpleDebuggingHelpers << "DATA DUMPERS AVAILABLE"; -} - -void GdbEngine::handleDebuggingHelperVersionCheckClassic(const GdbResponse &response) -{ - if (response.resultClass == GdbResultDone) { - QString value = _(response.data["value"].data()); - QString debuggeeQtVersion = value.section(QLatin1Char('"'), 1, 1); - QString dumperQtVersion = QLatin1String(m_dumperHelper.qtVersionString()); - if (debuggeeQtVersion.isEmpty()) { - showMessage(_("DUMPER VERSION CHECK SKIPPED, NO qVersion() OUTPUT IN") - + QLatin1String(response.toString())); - } else if (dumperQtVersion.isEmpty()) { - showMessage(_("DUMPER VERSION CHECK SKIPPED, NO VERSION STRING")); - } else if (dumperQtVersion != debuggeeQtVersion) { - showMessageBox(QMessageBox::Warning, - tr("Debugging helpers: Qt version mismatch"), - tr("The Qt version used to build the debugging helpers (%1) " - "does not match the Qt version used to build the debugged " - "application (%2).\nThis might yield incorrect results.") - .arg(dumperQtVersion).arg(debuggeeQtVersion)); - } else { - showMessage(_("DUMPER VERSION CHECK SUCCESSFUL: ") - + dumperQtVersion); - } - } else { - showMessage(QLatin1String("DUMPER VERSION CHECK NOT COMPLETED")); - } -} - -void GdbEngine::handleVarListChildrenHelperClassic(const GdbMi &item, - const WatchData &parent, int sortId) -{ - //qDebug() << "VAR_LIST_CHILDREN: PARENT" << parent.toString(); - //qDebug() << "VAR_LIST_CHILDREN: ITEM" << item.toString(); - QByteArray exp = item["exp"].data(); - QByteArray name = item["name"].data(); - if (isAccessSpecifier(exp)) { - // Suppress 'private'/'protected'/'public' level. - WatchData data; - data.variable = name; - data.iname = parent.iname; - //data.iname = data.variable; - data.exp = parent.exp; - data.setTypeUnneeded(); - data.setValueUnneeded(); - data.setHasChildrenUnneeded(); - data.setChildrenUnneeded(); - QByteArray cmd = "-var-list-children --all-values \"" + data.variable + '"'; - //iname += '.' + exp; - postCommand(cmd, Discardable, - CB(handleVarListChildrenClassic), QVariant::fromValue(data)); - } else if (!startsWithDigit(QLatin1String(exp)) - && item["numchild"].data() == "0") { - // Happens for structs without data, e.g. interfaces. - WatchData data; - data.name = _(exp); - data.iname = parent.iname + '.' + data.name.toLatin1(); - data.variable = name; - data.updateType(item["type"]); - data.updateValue(item); - data.updateAddress(item["addr"]); - data.setHasChildren(false); - insertData(data); - } else if (parent.iname.endsWith('.')) { - // Happens with anonymous unions. - WatchData data; - data.iname = name; - QByteArray cmd = "-var-list-children --all-values \"" + data.variable + '"'; - postCommand(cmd, Discardable, - CB(handleVarListChildrenClassic), QVariant::fromValue(data)); - } else if (exp == "staticMetaObject") { - // && item.findChild("type").data() == "const QMetaObject") - // FIXME: Namespaces? - // { do nothing } FIXME: make configurable? - // special "clever" hack to avoid clutter in the GUI. - // I am not sure this is a good idea... - } else { - // Suppress 'private'/'protected'/'public' level. - WatchData data; - data.iname = parent.iname + '.' + exp; - data.variable = name; - data.sortId = sortId; - data.updateType(item["type"]); - data.updateValue(item); - data.updateAddress(item["addr"]); - data.updateChildCount(item["numchild"]); - if (!watchHandler()->isExpandedIName(data.iname)) - data.setChildrenUnneeded(); - - data.name = _(exp); - if (data.name == QLatin1String(data.type)) { - if (isPointerType(parent.type)) { - data.exp = "*(" + parent.exp + ')'; - data.name = _("*") + parent.name; - } else { - // A type we derive from? gdb crashes when creating variables here - data.exp = parent.exp; - } - } else if (exp.startsWith('*')) { - // A pointer - data.exp = "*(" + parent.exp + ')'; - } else if (startsWithDigit(data.name)) { - // An array. No variables needed? - data.name = QLatin1Char('[') + data.name + QLatin1Char(']'); - data.exp = parent.exp + '[' + exp + ']'; - } else if (0 && parent.name.endsWith(QLatin1Char('.'))) { - // Happens with anonymous unions - data.exp = parent.exp + data.name.toLatin1(); - //data.name = "<anonymous union>"; - } else if (exp.isEmpty()) { - // Happens with anonymous unions - data.exp = parent.exp; - data.name = tr("<n/a>"); - data.iname = parent.iname + ".@"; - data.type = tr("<anonymous union>").toUtf8(); - } else { - // A structure. Hope there's nothing else... - data.exp = '(' + parent.exp + ")." + data.name.toLatin1(); - } - - if (hasDebuggingHelperForType(data.type)) { - // we do not trust gdb if we have a custom dumper - data.setValueNeeded(); - data.setHasChildrenNeeded(); - } - - //qDebug() << "VAR_LIST_CHILDREN: PARENT 3" << parent.toString(); - //qDebug() << "VAR_LIST_CHILDREN: APPENDEE" << data.toString(); - insertData(data); - } -} - -void GdbEngine::handleVarListChildrenClassic(const GdbResponse &response) -{ - //WatchResultCounter dummy(this, WatchVarListChildren); - WatchData data = response.cookie.value<WatchData>(); - if (!data.isValid()) - return; - if (response.resultClass == GdbResultDone) { - //qDebug() << "VAR_LIST_CHILDREN: PARENT" << data.toString(); - QList<GdbMi> children = response.data["children"].children(); - - if (children.isEmpty()) { - // happens e.g. if no debug information is present or - // if the class really has no children - WatchData data1; - data1.iname = data.iname + ".child"; - //: About variable's value - data1.value = tr("<no information>"); - data1.hasChildren = false; - data1.setAllUnneeded(); - insertData(data1); - data.setAllUnneeded(); - insertData(data); - } else { - if (data.variable.endsWith("private") - || data.variable.endsWith("protected") - || data.variable.endsWith("public")) { - // this skips the spurious "public", "private" etc levels - // gdb produces - } else { - data.setChildrenUnneeded(); - insertData(data); - } - for (int i = 0; i != children.size(); ++i) - handleVarListChildrenHelperClassic(children.at(i), data, i); - } - } else { - data.setError(QString::fromLocal8Bit(response.data["msg"].data())); - } -} - -void GdbEngine::handleEvaluateExpressionClassic(const GdbResponse &response) -{ - WatchData data = response.cookie.value<WatchData>(); - QTC_ASSERT(data.isValid(), qDebug() << "HUH?"); - if (response.resultClass == GdbResultDone) { - //if (col == 0) - // data.name = response.data.findChild("value").data(); - //else - data.updateValue(response.data); - } else { - data.setError(QString::fromLocal8Bit(response.data["msg"].data())); - } - //qDebug() << "HANDLE EVALUATE EXPRESSION:" << data.toString(); - insertData(data); - //updateWatchModel2(); -} - -} // namespace Internal -} // namespace Debugger diff --git a/src/plugins/debugger/gdb/coregdbadapter.h b/src/plugins/debugger/gdb/coregdbadapter.h index 4f274fb26a..af1a45c309 100644 --- a/src/plugins/debugger/gdb/coregdbadapter.h +++ b/src/plugins/debugger/gdb/coregdbadapter.h @@ -52,8 +52,6 @@ public: ~GdbCoreEngine(); private: - DumperHandling dumperHandling() const { return DumperNotAvailable; } - void setupEngine(); void setupInferior(); void runEngine(); diff --git a/src/plugins/debugger/gdb/gdb.pri b/src/plugins/debugger/gdb/gdb.pri index 64607124f8..8fc016a19a 100644 --- a/src/plugins/debugger/gdb/gdb.pri +++ b/src/plugins/debugger/gdb/gdb.pri @@ -11,8 +11,6 @@ HEADERS += \ SOURCES += \ $$PWD/gdbengine.cpp \ - $$PWD/classicgdbengine.cpp \ - $$PWD/pythongdbengine.cpp \ $$PWD/gdboptionspage.cpp \ $$PWD/attachgdbadapter.cpp \ $$PWD/coregdbadapter.cpp \ diff --git a/src/plugins/debugger/gdb/gdbengine.cpp b/src/plugins/debugger/gdb/gdbengine.cpp index 0ff3993168..2fdb7f336d 100644 --- a/src/plugins/debugger/gdb/gdbengine.cpp +++ b/src/plugins/debugger/gdb/gdbengine.cpp @@ -34,7 +34,6 @@ #include "gdbplainengine.h" #include "termgdbadapter.h" #include "remotegdbserveradapter.h" - #include "gdboptionspage.h" #include <debugger/debuggerstartparameters.h> @@ -92,6 +91,7 @@ enum { debugPending = 0 }; #define CB(callback) &GdbEngine::callback, STRINGIFY(callback) + QByteArray GdbEngine::tooltipIName(const QString &exp) { return "tooltip." + exp.toLatin1().toHex(); @@ -206,13 +206,8 @@ GdbEngine::GdbEngine(const DebuggerStartParameters &startParameters) setObjectName(_("GdbEngine")); m_busy = false; - m_debuggingHelperState = DebuggingHelperUninitialized; m_gdbVersion = 100; - m_gdbBuildVersion = -1; - m_isMacGdb = false; m_isQnxGdb = false; - m_hasBreakpointNotifications = false; - m_hasPython = false; m_registerNamesListed = false; m_sourcesListUpdating = false; m_oldestAcceptableToken = -1; @@ -222,16 +217,11 @@ GdbEngine::GdbEngine(const DebuggerStartParameters &startParameters) m_commandsDoneCallback = 0; m_stackNeeded = false; m_preparedForQmlBreak = false; - m_disassembleUsesComma = false; m_terminalTrap = startParameters.useTerminal; m_fullStartDone = false; m_systemDumpersLoaded = false; - m_forceAsyncModel = false; - m_pythonAttemptedToLoad = false; m_gdbProc = new GdbProcess(this); - invalidateSourcesList(); - m_debugInfoTaskHandler = new DebugInfoTaskHandler(this); //ExtensionSystem::PluginManager::addObject(m_debugInfoTaskHandler); @@ -463,7 +453,6 @@ void GdbEngine::handleResponse(const QByteArray &buff) if (!id.isEmpty()) showStatusMessage(tr("Library %1 loaded").arg(_(id)), 1000); progressPing(); - invalidateSourcesList(); Module module; module.startAddress = 0; module.endAddress = 0; @@ -478,7 +467,6 @@ void GdbEngine::handleResponse(const QByteArray &buff) QByteArray id = result["id"].data(); progressPing(); showStatusMessage(tr("Library %1 unloaded").arg(_(id)), 1000); - invalidateSourcesList(); } else if (asyncClass == "thread-group-added") { // 7.1-symbianelf has "{id="i1"}" } else if (asyncClass == "thread-group-created" @@ -522,33 +510,6 @@ void GdbEngine::handleResponse(const QByteArray &buff) QByteArray id = result["id"].data(); showStatusMessage(tr("Thread %1 selected").arg(_(id)), 1000); //"{id="2"}" - } else if (m_isMacGdb && asyncClass == "shlibs-updated") { - // Apple's gdb announces updated libs. - invalidateSourcesList(); - } else if (m_isMacGdb && asyncClass == "shlibs-added") { - // Apple's gdb announces added libs. - // {shlib-info={num="2", name="libmathCommon.A_debug.dylib", - // kind="-", dyld-addr="0x7f000", reason="dyld", requested-state="Y", - // state="Y", path="/usr/lib/system/libmathCommon.A_debug.dylib", - // description="/usr/lib/system/libmathCommon.A_debug.dylib", - // loaded_addr="0x7f000", slide="0x7f000", prefix=""}} - invalidateSourcesList(); - } else if (m_isMacGdb && asyncClass == "resolve-pending-breakpoint") { - // Apple's gdb announces resolved breakpoints. - // new_bp="1",pended_bp="1",new_expr="\"gdbengine.cpp\":1584", - // bkpt={number="1",type="breakpoint",disp="keep",enabled="y", - // addr="0x0000000115cc3ddf",func="foo()",file="../foo.cpp", - // line="1584",shlib="/../libFoo_debug.dylib",times="0"} - const GdbMi bkpt = result["bkpt"]; - const BreakpointResponseId rid(bkpt["number"].data()); - if (!isQmlStepBreakpoint(rid)) { - BreakHandler *handler = breakHandler(); - BreakpointModelId id = handler->findBreakpointByResponseId(rid); - BreakpointResponse br = handler->response(id); - updateResponse(br, bkpt); - handler->setResponse(id, br); - attemptAdjustBreakpointLocation(id); - } } else if (asyncClass == "breakpoint-modified") { // New in FSF gdb since 2011-04-27. // "{bkpt={number="3",type="breakpoint",disp="keep", @@ -557,7 +518,7 @@ void GdbEngine::handleResponse(const QByteArray &buff) // {number="3.1",enabled="y",addr="0x0805ff68", // func="Vector<int>::Vector(int)", // file="simple_gdbtest_app.cpp", - // fullname="/data/...line="135"},{number="3.2"...}}" + // fullname="/data/...line="135"},{number="3.2"...}}.." // Note the leading comma in original-location. Filter it out. // We don't need the field anyway. @@ -596,13 +557,12 @@ void GdbEngine::handleResponse(const QByteArray &buff) } } } - m_hasBreakpointNotifications = true; } else if (asyncClass == "breakpoint-created") { // "{bkpt={number="1",type="breakpoint",disp="del",enabled="y", // addr="<PENDING>",pending="main",times="0", // original-location="main"}}" -- or -- // {bkpt={number="2",type="hw watchpoint",disp="keep",enabled="y", - // what="*0xbfffed48",times="0",original-location="*0xbfffed48" + // what="*0xbfffed48",times="0",original-location="*0xbfffed48"}} BreakHandler *handler = breakHandler(); foreach (const GdbMi &bkpt, result.children()) { BreakpointResponse br; @@ -663,7 +623,6 @@ void GdbEngine::handleResponse(const QByteArray &buff) if (data.startsWith("Reading symbols from ")) { showStatusMessage(tr("Reading %1...").arg(_(data.mid(21))), 1000); progressPing(); - invalidateSourcesList(); } else if (data.startsWith("[New ") || data.startsWith("[Thread ")) { if (data.endsWith('\n')) data.chop(1); @@ -1283,10 +1242,15 @@ void GdbEngine::executeDebuggerCommand(const QString &command, DebuggerLanguages // This is called from CoreAdapter and AttachAdapter. void GdbEngine::updateAll() { - if (hasPython()) - updateAllPython(); - else - updateAllClassic(); + //PENDING_DEBUG("UPDATING ALL\n"); + QTC_CHECK(state() == InferiorUnrunnable || state() == InferiorStopOk); + reloadModulesInternal(); + postCommand("-stack-list-frames", CB(handleStackListFrames), + QVariant::fromValue<StackCookie>(StackCookie(false, true))); + stackHandler()->setCurrentIndex(0); + postCommand("-thread-info", CB(handleThreadInfo), 0); + reloadRegisters(); + updateLocals(); } void GdbEngine::handleQuerySources(const GdbResponse &response) @@ -1578,29 +1542,7 @@ void GdbEngine::handleStop1(const GdbMi &data) postCommand("importPlainDumpers"); } - bool initHelpers = m_debuggingHelperState == DebuggingHelperUninitialized - || m_debuggingHelperState == DebuggingHelperLoadTried; - // Don't load helpers on stops triggered by signals unless it's - // an intentional trap. - if (initHelpers - && dumperHandling() != DumperLoadedByGdbPreload - && reason == "signal-received") { - const QByteArray name = data["signal-name"].data(); - const DebuggerStartParameters &sp = startParameters(); - if (name != stopSignal(sp.toolChainAbi)) - initHelpers = false; - } - if (isSynchronous()) - initHelpers = false; - if (initHelpers) { - tryLoadDebuggingHelpersClassic(); - QVariant var = QVariant::fromValue<GdbMi>(data); - postCommand("p 4", CB(handleStop2), var); // dummy - } else { - handleStop2(data); - } - // Dumper loading is sequenced, as otherwise the display functions - // will start requesting data without knowing that dumpers are available. + handleStop2(data); } void GdbEngine::handleStop2(const GdbResponse &response) @@ -1667,15 +1609,6 @@ void GdbEngine::handleStop2(const GdbMi &data) isStopperThread = true; } - if (m_breakListOutdated) { - reloadBreakListInternal(); - } else { - // Older gdb versions do not produce "library loaded" messages - // so the breakpoint update is not triggered. - if (m_gdbVersion < 70000 && !m_isMacGdb && breakHandler()->size() > 0) - reloadBreakListInternal(); - } - if (reason == "watchpoint-trigger") { // *stopped,reason="watchpoint-trigger",wpt={number="2",exp="*0xbfffed40"}, // value={old="1",new="0"},frame={addr="0x00451e1b", @@ -1745,14 +1678,8 @@ void GdbEngine::handleStop2() if (!m_stackNeeded) return; - if (supportsThreads()) { - if (m_isMacGdb || m_gdbVersion < 70100) { - postCommand("-thread-list-ids", Discardable, CB(handleThreadListIds)); - } else { - // This is only available in gdb 7.1+. - postCommand("-thread-info", Discardable, CB(handleThreadInfo)); - } - } + // This is only available in gdb 7.1+. + postCommand("-thread-info", Discardable, CB(handleThreadInfo)); } void GdbEngine::handleInfoProc(const GdbResponse &response) @@ -1769,40 +1696,31 @@ void GdbEngine::handleShowVersion(const GdbResponse &response) { showMessage(_("PARSING VERSION: " + response.toString())); if (response.resultClass == GdbResultDone) { + bool isMacGdb = false; + int gdbBuildVersion = -1; m_gdbVersion = 100; - m_gdbBuildVersion = -1; - m_isMacGdb = false; m_isQnxGdb = false; QString msg = QString::fromLocal8Bit(response.consoleStreamOutput); extractGdbVersion(msg, - &m_gdbVersion, &m_gdbBuildVersion, &m_isMacGdb, &m_isQnxGdb); + &m_gdbVersion, &gdbBuildVersion, &isMacGdb, &m_isQnxGdb); // On Mac, fsf gdb does not work sufficiently well, // and on Linux and Windows we require at least 7.2. // Older versions with python still work, but can // be significantly slower. - bool isSupported = m_isMacGdb ? m_gdbVersion < 70000 - : (m_gdbVersion > 70200 && m_gdbVersion < 200000); + bool isSupported = m_gdbVersion >= 70500; if (isSupported) showMessage(_("SUPPORTED GDB VERSION ") + msg); else showMessage(_("UNSUPPORTED GDB VERSION ") + msg); showMessage(_("USING GDB VERSION: %1, BUILD: %2%3").arg(m_gdbVersion) - .arg(m_gdbBuildVersion).arg(_(m_isMacGdb ? " (APPLE)" : ""))); - - if (m_gdbVersion > 70300) - m_hasBreakpointNotifications = true; + .arg(gdbBuildVersion).arg(_(isMacGdb ? " (APPLE)" : ""))); - if (m_gdbVersion > 70100) - m_disassembleUsesComma = true; - - if (m_gdbVersion > 70100) { - if (usesExecInterrupt()) - postCommand("set target-async on", ConsoleCommand); - else - postCommand("set target-async off", ConsoleCommand); - } + if (usesExecInterrupt()) + postCommand("set target-async on", ConsoleCommand); + else + postCommand("set target-async off", ConsoleCommand); if (startParameters().multiProcess) postCommand("set detach-on-fork off", ConsoleCommand); @@ -1819,10 +1737,7 @@ void GdbEngine::handleListFeatures(const GdbResponse &response) void GdbEngine::handleHasPython(const GdbResponse &response) { - if (response.resultClass == GdbResultDone) - m_hasPython = true; - else - pythonDumpersFailed(); + Q_UNUSED(response); } void GdbEngine::handlePythonSetup(const GdbResponse &response) @@ -1834,7 +1749,6 @@ void GdbEngine::handlePythonSetup(const GdbResponse &response) postCommand("bbsetup"); } - m_hasPython = true; GdbMi data; data.fromStringMultiple(response.consoleStreamOutput); const GdbMi dumpers = data["dumpers"]; @@ -1855,24 +1769,6 @@ void GdbEngine::handlePythonSetup(const GdbResponse &response) } } -void GdbEngine::pythonDumpersFailed() -{ - m_hasPython = false; - const DebuggerStartParameters &sp = startParameters(); - if (dumperHandling() == DumperLoadedByGdbPreload && checkDebuggingHelpersClassic()) { - QByteArray cmd = "set environment "; - if (sp.toolChainAbi.os() == Abi::MacOS) - cmd += "DYLD_INSERT_LIBRARIES"; - else - cmd += "LD_PRELOAD"; - cmd += ' '; - if (sp.startMode != StartRemoteGdb) - cmd += sp.dumperLibrary.toLocal8Bit(); - postCommand(cmd); - m_debuggingHelperState = DebuggingHelperLoadTried; - } -} - void GdbEngine::showExecutionError(const QString &message) { showMessageBox(QMessageBox::Critical, tr("Execution Error"), @@ -2310,24 +2206,13 @@ void GdbEngine::executeRunToLine(const ContextData &data) showStatusMessage(tr("Run to line %1 requested...").arg(data.lineNumber), 5000); #if 1 QByteArray loc; - if (m_isMacGdb) { - if (data.address) - loc = addressSpec(data.address); - else - loc = "\"\\\"" + breakLocation(data.fileName).toLocal8Bit() + "\\\":" - + QByteArray::number(data.lineNumber) + '"'; - // "tbreak/continue" does _not_ work on Mac. See #4619 - postCommand("-break-insert -t -l -1 -f " + loc); - postCommand("-exec-continue", RunRequest, CB(handleExecuteRunToLine)); - } else { - if (data.address) - loc = addressSpec(data.address); - else - loc = '"' + breakLocation(data.fileName).toLocal8Bit() + '"' + ':' - + QByteArray::number(data.lineNumber); - postCommand("tbreak " + loc); - postCommand("continue", RunRequest, CB(handleExecuteRunToLine)); - } + if (data.address) + loc = addressSpec(data.address); + else + loc = '"' + breakLocation(data.fileName).toLocal8Bit() + '"' + ':' + + QByteArray::number(data.lineNumber); + postCommand("tbreak " + loc); + postCommand("continue", RunRequest, CB(handleExecuteRunToLine)); #else // Seems to jump to unpredicatable places. Observed in the manual // tests in the Foo::Foo() constructor with both gdb 6.8 and 7.1. @@ -2482,7 +2367,7 @@ void GdbEngine::updateResponse(BreakpointResponse &response, const GdbMi &bkpt) } else if (child.hasName("type")) { // "breakpoint", "hw breakpoint", "tracepoint", "hw watchpoint" // {bkpt={number="2",type="hw watchpoint",disp="keep",enabled="y", - // what="*0xbfffed48",times="0",original-location="*0xbfffed48" + // what="*0xbfffed48",times="0",original-location="*0xbfffed48"}} if (child.data().contains("tracepoint")) { response.tracepoint = true; } else if (child.data() == "hw watchpoint" || child.data() == "watchpoint") { @@ -2536,7 +2421,6 @@ void GdbEngine::updateResponse(BreakpointResponse &response, const GdbMi &bkpt) QString GdbEngine::breakLocation(const QString &file) const { - //QTC_CHECK(!m_breakListOutdated); QString where = m_fullToShortName.value(file); if (where.isEmpty()) return QFileInfo(file).fileName(); @@ -2627,31 +2511,12 @@ void GdbEngine::handleWatchInsert(const GdbResponse &response) } } -void GdbEngine::attemptAdjustBreakpointLocation(BreakpointModelId id) -{ - if (m_hasBreakpointNotifications) - return; - if (!debuggerCore()->boolSetting(AdjustBreakpointLocations)) - return; - BreakpointResponse response = breakHandler()->response(id); - if (response.address == 0 || response.correctedLineNumber != 0) - return; - // Prevent endless loop. - response.correctedLineNumber = -1; - breakHandler()->setResponse(id, response); - postCommand("info line *0x" + QByteArray::number(response.address, 16), - NeedsStop | RebuildBreakpointModel, - CB(handleInfoLine), QVariant::fromValue(id)); -} - void GdbEngine::handleCatchInsert(const GdbResponse &response) { BreakHandler *handler = breakHandler(); BreakpointModelId id = response.cookie.value<BreakpointModelId>(); - if (response.resultClass == GdbResultDone) { + if (response.resultClass == GdbResultDone) handler->notifyBreakpointInsertOk(id); - attemptAdjustBreakpointLocation(id); - } } void GdbEngine::handleBkpt(const GdbMi &bkpt, const BreakpointModelId &id) @@ -2727,13 +2592,6 @@ void GdbEngine::handleBreakInsert1(const GdbResponse &response) } else { handler->notifyBreakpointInsertOk(id); } - BreakpointResponse br = handler->response(id); - attemptAdjustBreakpointLocation(id); - // Remove if we only support 7.4 or later. - if (br.multiple && !m_hasBreakpointNotifications) - postCommand("info break " + QByteArray::number(br.id.majorPart()), - NeedsStop, CB(handleBreakListMultiple), - QVariant::fromValue(id)); } } else if (response.data["msg"].data().contains("Unknown option")) { // Older version of gdb don't know the -a option to set tracepoints @@ -2743,9 +2601,7 @@ void GdbEngine::handleBreakInsert1(const GdbResponse &response) QByteArray cmd = "trace " "\"" + GdbMi::escapeCString(fileName.toLocal8Bit()) + "\":" + QByteArray::number(lineNumber); - QVariant vid = QVariant::fromValue(id); - postCommand(cmd, NeedsStop | RebuildBreakpointModel, - CB(handleTraceInsert2), vid); + postCommand(cmd, NeedsStop | RebuildBreakpointModel); } else { // Some versions of gdb like "GNU gdb (GDB) SUSE (6.8.91.20090930-2.4)" // know how to do pending breakpoints using CLI but not MI. So try @@ -2761,7 +2617,6 @@ void GdbEngine::handleBreakInsert2(const GdbResponse &response) { if (response.resultClass == GdbResultDone) { BreakpointModelId id = response.cookie.value<BreakpointModelId>(); - attemptAdjustBreakpointLocation(id); breakHandler()->notifyBreakpointInsertOk(id); } else { // Note: gdb < 60800 doesn't "do" pending breakpoints. @@ -2771,94 +2626,6 @@ void GdbEngine::handleBreakInsert2(const GdbResponse &response) } } -void GdbEngine::handleTraceInsert2(const GdbResponse &response) -{ - if (response.resultClass == GdbResultDone) - reloadBreakListInternal(); -} - -void GdbEngine::reloadBreakListInternal() -{ - if (m_hasBreakpointNotifications) { - // Assume this properly handles breakpoint notifications. - return; - } - postCommand("-break-list", NeedsStop | RebuildBreakpointModel, - CB(handleBreakList)); -} - -void GdbEngine::handleBreakList(const GdbResponse &response) -{ - // 45^done,BreakpointTable={nr_rows="2",nr_cols="6",hdr=[ - // {width="3",alignment="-1",col_name="number",colhdr="Num"}, ... - // body=[bkpt={number="1",type="breakpoint",disp="keep",enabled="y", - // addr="0x000000000040109e",func="main",file="app.cpp", - // fullname="/home/apoenitz/dev/work/plugintest/app/app.cpp", - // line="11",times="1"}, - // bkpt={number="2",type="breakpoint",disp="keep",enabled="y", - // addr="<PENDING>",pending="plugin.cpp:7",times="0"}] ... } - - if (response.resultClass == GdbResultDone) { - GdbMi table = response.data["BreakpointTable"]; - if (table.isValid()) - handleBreakList(table); - } -} - -void GdbEngine::handleBreakList(const GdbMi &table) -{ - const GdbMi body = table["body"]; - QList<GdbMi> bkpts; - if (body.isValid()) { - // Non-Mac - bkpts = body.children(); - } else { - // Mac - bkpts = table.children(); - // Remove the 'hdr' and artificial items. - for (int i = bkpts.size(); --i >= 0; ) { - int num = bkpts.at(i)["number"].toInt(); - if (num <= 0) - bkpts.removeAt(i); - } - } - - BreakHandler *handler = breakHandler(); - foreach (const GdbMi &bkpt, bkpts) { - BreakpointResponse needle; - needle.id = BreakpointResponseId(bkpt["number"].data()); - if (isQmlStepBreakpoint2(needle.id)) - continue; - if (isQFatalBreakpoint(needle.id)) - continue; - BreakpointModelId id = handler->findSimilarBreakpoint(needle); - if (id.isValid()) { - BreakpointResponse response = handler->response(id); - updateResponse(response, bkpt); - handler->setResponse(id, response); - attemptAdjustBreakpointLocation(id); - response = handler->response(id); - if (response.multiple) - postCommand("info break " + response.id.toString().toLatin1(), - NeedsStop, CB(handleBreakListMultiple), - QVariant::fromValue(id)); - } else { - qDebug() << " NOTHING SUITABLE FOUND"; - showMessage(_("CANNOT FIND BP: " + bkpt.toString())); - } - } - - m_breakListOutdated = false; -} - -void GdbEngine::handleBreakListMultiple(const GdbResponse &response) -{ - QTC_CHECK(response.resultClass == GdbResultDone); - const BreakpointModelId id = response.cookie.value<BreakpointModelId>(); - const QString str = QString::fromLocal8Bit(response.consoleStreamOutput); - extractDataFromInfoBreak(str, id); -} - void GdbEngine::handleBreakDisable(const GdbResponse &response) { QTC_CHECK(response.resultClass == GdbResultDone); @@ -2948,164 +2715,14 @@ void GdbEngine::handleBreakCondition(const GdbResponse &response) // the output stream data. // The following happens on Mac: // QByteArray msg = response.data.findChild("msg").data(); - // if (1 || msg.startsWith("Error parsing breakpoint condition. " - // " Will try again when we hit the breakpoint.")) { + // if (msg.startsWith("Error parsing breakpoint condition. " + // " Will try again when we hit the breakpoint.")) BreakpointResponse br = handler->response(id); br.condition = handler->condition(id); handler->setResponse(id, br); changeBreakpoint(id); // Maybe there's more to do. } -void GdbEngine::extractDataFromInfoBreak(const QString &output, BreakpointModelId id) -{ - //qDebug() << output; - if (output.isEmpty()) - return; - // "Num Type Disp Enb Address What - // 4 breakpoint keep y <MULTIPLE> 0x00000000004066ad - // 4.1 y 0x00000000004066ad in CTorTester - // at /data5/dev/ide/main/tests/manual/gdbdebugger/simple/app.cpp:124 - // - or - - // everything on a single line on Windows for constructors of classes - // within namespaces. - // Sometimes the path is relative too. - - // 2 breakpoint keep y <MULTIPLE> 0x0040168e - // 2.1 y 0x0040168e in MainWindow::MainWindow(QWidget*) at mainwindow.cpp:7 - // 2.2 y 0x00401792 in MainWindow::MainWindow(QWidget*) at mainwindow.cpp:7 - - // "Num Type Disp Enb Address What - // 3 breakpoint keep y <MULTIPLE> \n" - // 3.1 y 0x0806094e in Vector<int>::Vector(int) at simple.cpp:141 - // 3.2 y 0x08060994 in Vector<float>::Vector(int) at simple.cpp:141 - // 3.3 y 0x080609da in Vector<double>::Vector(int) at simple.cpp:141 - // 3.4 y 0x08060a1d in Vector<char>::Vector(int) at simple.cpp:141 - - BreakHandler *handler = breakHandler(); - BreakpointResponse response = handler->response(id); - int posMultiple = output.indexOf(_("<MULTIPLE>")); - if (posMultiple != -1) { - QByteArray data = output.toUtf8(); - data.replace('\n', ' '); - data.replace(" ", " "); - data.replace(" ", " "); - data.replace(" ", " "); - int majorPart = 0; - int minorPart = 0; - int hitCount = 0; - bool hitCountComing = false; - bool functionComing = false; - bool locationComing = false; - QByteArray location; - QByteArray function; - qulonglong address = 0; - foreach (const QByteArray &part, data.split(' ')) { - if (part.isEmpty()) - continue; - //qDebug() << "PART: " << part; - if (majorPart == 0) { - majorPart = part.toInt(); - if (majorPart > 0) - continue; - } - if (part == "hit") { - hitCountComing = true; - continue; - } - if (hitCountComing) { - hitCountComing = false; - hitCount = part.toInt(); - continue; - } - if (part == "at") { - locationComing = true; - continue; - } - if (locationComing) { - locationComing = false; - location = part; - continue; - } - if (part == "in") { - functionComing = true; - continue; - } - if (functionComing) { - functionComing = false; - function = part; - continue; - } - if (part.startsWith("0x")) { - address = part.toInt(0, 0); - continue; - } - if (part.size() >= 3 && part.count('.') == 1) { - BreakpointResponseId subId(part); - int tmpMajor = subId.majorPart(); - int tmpMinor = subId.minorPart(); - if (tmpMajor == majorPart) { - if (minorPart) { - // Commit what we had before. - BreakpointResponse sub; - sub.address = address; - sub.functionName = QString::fromUtf8(function); - sub.updateLocation(location); - sub.id = BreakpointResponseId(majorPart, minorPart); - sub.type = response.type; - sub.address = address; - sub.hitCount = hitCount; - handler->insertSubBreakpoint(id, sub); - location.clear(); - function.clear(); - address = 0; - } - - // Now start new. - minorPart = tmpMinor; - continue; - } - } - } - if (minorPart) { - // Commit last chunk. - BreakpointResponse sub; - sub.address = address; - sub.functionName = QString::fromUtf8(function); - sub.updateLocation(location); - sub.id = BreakpointResponseId(majorPart, minorPart); - sub.type = response.type; - sub.hitCount = hitCount; - handler->insertSubBreakpoint(id, sub); - location.clear(); - function.clear(); - address = 0; - } - } else { - qDebug() << "COULD NOT MATCH" << output; - response.id = BreakpointResponseId(); // Unavailable. - } - //handler->setResponse(id, response); -} - -void GdbEngine::handleInfoLine(const GdbResponse &response) -{ - if (response.resultClass == GdbResultDone) { - // Old-style output: "Line 1102 of \"simple/app.cpp\" starts - // at address 0x80526aa <_Z10...+131> and ends at 0x80526b5 - // <_Z10testQStackv+142>.\n" - QByteArray ba = response.consoleStreamOutput; - const BreakpointModelId id = response.cookie.value<BreakpointModelId>(); - const int pos = ba.indexOf(' ', 5); - if (ba.startsWith("Line ") && pos != -1) { - const int line = ba.mid(5, pos - 5).toInt(); - BreakpointResponse br = breakHandler()->response(id); - br.lineNumber = line; - br.correctedLineNumber = line; - breakHandler()->setResponse(id, br); - } - } -} - bool GdbEngine::stateAcceptsBreakpointChanges() const { switch (state()) { @@ -3177,26 +2794,18 @@ void GdbEngine::insertBreakpoint(BreakpointModelId id) QByteArray cmd; if (handler->isTracepoint(id)) { cmd = "-break-insert -a -f "; - } else if (m_isMacGdb) { - cmd = "-break-insert -l -1 -f "; - } else if (m_gdbVersion >= 70000) { + } else { int spec = handler->threadSpec(id); cmd = "-break-insert "; if (spec >= 0) cmd += "-p " + QByteArray::number(spec); cmd += " -f "; - } else if (m_gdbVersion >= 60800) { - // Probably some earlier version would work as well. - cmd = "-break-insert -f "; - } else { - cmd = "-break-insert "; } if (handler->isOneShot(id)) cmd += "-t "; - // FIXME: -d does not work on Mac gdb. - if (!handler->isEnabled(id) && !m_isMacGdb) + if (!handler->isEnabled(id)) cmd += "-d "; if (int ignoreCount = handler->ignoreCount(id)) @@ -3278,7 +2887,6 @@ void GdbEngine::changeBreakpoint(BreakpointModelId id) return; } handler->notifyBreakpointChangeOk(id); - attemptAdjustBreakpointLocation(id); } void GdbEngine::removeBreakpoint(BreakpointModelId id) @@ -3314,7 +2922,6 @@ void GdbEngine::loadSymbols(const QString &modulePath) // FIXME: gdb does not understand quoted names here (tested with 6.8) postCommand("sharedlibrary " + dotEscape(modulePath.toLocal8Bit())); reloadModulesInternal(); - reloadBreakListInternal(); reloadStack(true); updateLocals(); } @@ -3323,7 +2930,6 @@ void GdbEngine::loadAllSymbols() { postCommand("sharedlibrary .*"); reloadModulesInternal(); - reloadBreakListInternal(); reloadStack(true); updateLocals(); } @@ -3347,7 +2953,6 @@ void GdbEngine::loadSymbolsForStack() } if (needUpdate) { //reloadModulesInternal(); - reloadBreakListInternal(); reloadStack(true); updateLocals(); } @@ -3558,11 +3163,6 @@ void GdbEngine::examineModules() // ////////////////////////////////////////////////////////////////////// -void GdbEngine::invalidateSourcesList() -{ - m_breakListOutdated = true; -} - void GdbEngine::reloadSourceFiles() { if ((state() == InferiorRunOk || state() == InferiorStopOk) @@ -3575,10 +3175,6 @@ void GdbEngine::reloadSourceFilesInternal() QTC_CHECK(!m_sourcesListUpdating); m_sourcesListUpdating = true; postCommand("-file-list-exec-source-files", NeedsStop, CB(handleQuerySources)); -#if 0 - if (m_gdbVersion < 70000 && !m_isMacGdb) - postCommand("set stop-on-solib-events 1"); -#endif } @@ -3644,8 +3240,7 @@ StackFrame GdbEngine::parseStackFrame(const GdbMi &frameMi, int level) void GdbEngine::handleStackListFrames(const GdbResponse &response) { - bool handleIt = (m_isMacGdb || response.resultClass == GdbResultDone); - if (!handleIt) { + if (response.resultClass != GdbResultDone) { // That always happens on symbian gdb with // ^error,data={msg="Previous frame identical to this frame (corrupt stack?)" // logStreamOutput: "Previous frame identical to this frame (corrupt stack?)\n" @@ -3747,7 +3342,7 @@ void GdbEngine::handleThreadInfo(const GdbResponse &response) selectThread(other); } updateViews(); // Adjust Threads combobox. - if (m_hasPython && debuggerCore()->boolSetting(ShowThreadNames)) { + if (debuggerCore()->boolSetting(ShowThreadNames)) { postCommand("threadnames " + debuggerCore()->action(MaximalStackDepth)->value().toByteArray(), Discardable, CB(handleThreadNames)); @@ -3918,20 +3513,6 @@ void GdbEngine::handleRegisterListValues(const GdbResponse &response) ////////////////////////////////////////////////////////////////////// // -// Thread specific stuff -// -////////////////////////////////////////////////////////////////////// - -bool GdbEngine::supportsThreads() const -{ - // FSF gdb 6.3 crashes happily on -thread-list-ids. So don't use it. - // The test below is a semi-random pick, 6.8 works fine - return m_isMacGdb || m_gdbVersion > 60500; -} - - -////////////////////////////////////////////////////////////////////// -// // Tooltip specific stuff // ////////////////////////////////////////////////////////////////////// @@ -4015,19 +3596,11 @@ bool GdbEngine::setToolTipExpression(const QPoint &mousePos, if (DebuggerToolTipManager::debug()) qDebug() << "GdbEngine::setToolTipExpression2 " << exp << (*m_toolTipContext); - if (isSynchronous()) { - UpdateParameters params; - params.tryPartial = true; - params.tooltipOnly = true; - params.varList = iname; - updateLocalsPython(params); - } else { - WatchData toolTip; - toolTip.exp = exp.toLatin1(); - toolTip.name = exp; - toolTip.iname = iname; - watchHandler()->insertData(toolTip); - } + UpdateParameters params; + params.tryPartial = true; + params.tooltipOnly = true; + params.varList = iname; + updateLocalsPython(params); return true; } @@ -4044,66 +3617,41 @@ void GdbEngine::reloadLocals() updateLocals(); } -bool GdbEngine::hasDebuggingHelperForType(const QByteArray &type) const +void GdbEngine::updateWatchData(const WatchData &data, const WatchUpdateFlags &flags) { - if (!debuggerCore()->boolSetting(UseDebuggingHelpers)) - return false; + // This should only be called for fresh expanded items, not for + // items that had their children retrieved earlier. + //qDebug() << "\nUPDATE WATCH DATA: " << data.toString() << "\n"; + if (data.iname.endsWith(".")) + return; - if (dumperHandling() == DumperNotAvailable) { - // Inferior calls are not possible in gdb when looking at core files. - return type == "QString" || type.endsWith("::QString") - || type == "QStringList" || type.endsWith("::QStringList"); + // Avoid endless loops created by faulty dumpers. + QByteArray processedName = "1-" + data.iname; + //qDebug() << "PROCESSED NAMES: " << processedName << m_processedNames; + if (m_processedNames.contains(processedName)) { + WatchData data1 = data; + showMessage(_("<Breaking endless loop for " + data.iname + '>'), + LogMiscInput); + data1.setAllUnneeded(); + data1.setValue(_("<unavailable>")); + data1.setHasChildren(false); + insertData(data1); + return; } + m_processedNames.insert(processedName); - if (m_debuggingHelperState != DebuggingHelperAvailable) - return false; + // FIXME: Is this sufficient when "external" changes are + // triggered e.g. by manually entered command in the gdb console? + //qDebug() << "TRY PARTIAL: " << flags.tryIncremental + // << (m_pendingBreakpointRequests == 0); - // Simple types. - return m_dumperHelper.type(type) != DumperHelper::UnknownType; -} - -void GdbEngine::updateWatchData(const WatchData &data, const WatchUpdateFlags &flags) -{ - if (isSynchronous()) { - // This should only be called for fresh expanded items, not for - // items that had their children retrieved earlier. - //qDebug() << "\nUPDATE WATCH DATA: " << data.toString() << "\n"; - if (data.iname.endsWith(".")) - return; + UpdateParameters params; + params.tooltipOnly = data.iname.startsWith("tooltip"); + params.tryPartial = flags.tryIncremental + && m_pendingBreakpointRequests == 0; + params.varList = data.iname; - // Avoid endless loops created by faulty dumpers. - QByteArray processedName = "1-" + data.iname; - //qDebug() << "PROCESSED NAMES: " << processedName << m_processedNames; - if (m_processedNames.contains(processedName)) { - WatchData data1 = data; - showMessage(_("<Breaking endless loop for " + data.iname + '>'), - LogMiscInput); - data1.setAllUnneeded(); - data1.setValue(_("<unavailable>")); - data1.setHasChildren(false); - insertData(data1); - return; - } - m_processedNames.insert(processedName); - - // FIXME: Is this sufficient when "external" changes are - // triggered e.g. by manually entered command in the gdb console? - //qDebug() << "TRY PARTIAL: " << flags.tryIncremental - // << hasPython() - // << (m_pendingBreakpointRequests == 0); - - UpdateParameters params; - params.tooltipOnly = data.iname.startsWith("tooltip"); - params.tryPartial = flags.tryIncremental - && hasPython() - && m_pendingBreakpointRequests == 0; - params.varList = data.iname; - - updateLocalsPython(params); - } else { - PENDING_DEBUG("UPDATE WATCH BUMPS PENDING UP TO " << m_uncompleted.size()); - updateSubItemClassic(data); - } + updateLocalsPython(params); } void GdbEngine::rebuildWatchModel() @@ -4112,8 +3660,6 @@ void GdbEngine::rebuildWatchModel() QTC_CHECK(m_uncompleted.isEmpty()); static int count = 0; ++count; - if (!isSynchronous()) - m_processedNames.clear(); PENDING_DEBUG("REBUILDING MODEL" << count); if (debuggerCore()->boolSetting(LogTimeStamps)) showMessage(LogWindow::logTimeStamp(), LogMiscInput); @@ -4122,38 +3668,6 @@ void GdbEngine::rebuildWatchModel() showToolTip(); } -static QByteArray arrayFillCommand(const char *array, const QByteArray ¶ms) -{ - QString buf; - buf.sprintf("set {char[%d]} &%s = {", params.size(), array); - QByteArray encoded; - encoded.append(buf.toLocal8Bit()); - const int size = params.size(); - for (int i = 0; i != size; ++i) { - buf.sprintf("%d,", int(params[i])); - encoded.append(buf.toLocal8Bit()); - } - encoded[encoded.size() - 1] = '}'; - return encoded; -} - -void GdbEngine::sendWatchParameters(const QByteArray ¶ms0) -{ - QByteArray params = params0; - params.append('\0'); - const QByteArray inBufferCmd = arrayFillCommand("qDumpInBuffer", params); - - params.replace('\0','!'); - showMessage(QString::fromUtf8(params), LogMiscInput); - - params.clear(); - params.append('\0'); - const QByteArray outBufferCmd = arrayFillCommand("qDumpOutBuffer", params); - - postCommand(inBufferCmd); - postCommand(outBufferCmd); -} - void GdbEngine::handleVarAssign(const GdbResponse &) { // Everything might have changed, force re-evaluation. @@ -4161,127 +3675,10 @@ void GdbEngine::handleVarAssign(const GdbResponse &) updateLocals(); } -void GdbEngine::handleVarCreate(const GdbResponse &response) -{ - WatchData data = response.cookie.value<WatchData>(); - // Happens e.g. when we already issued a var-evaluate command. - if (!data.isValid()) - return; - //qDebug() << "HANDLE VARIABLE CREATION:" << data.toString(); - if (response.resultClass == GdbResultDone) { - data.variable = data.iname; - data.updateType(response.data["type"]); - if (watchHandler()->isExpandedIName(data.iname) - && !response.data["children"].isValid()) - data.setChildrenNeeded(); - else - data.setChildrenUnneeded(); - data.updateChildCount(response.data["numchild"]); - insertData(data); - } else { - data.setError(QString::fromLocal8Bit(response.data["msg"].data())); - if (data.isWatcher()) { - data.value = WatchData::msgNotInScope(); - data.type = " "; - data.setAllUnneeded(); - data.setHasChildren(false); - data.valueEnabled = false; - data.valueEditable = false; - insertData(data); - } - } -} - -void GdbEngine::handleDebuggingHelperSetup(const GdbResponse &response) -{ - if (response.resultClass == GdbResultDone) { - } else { - QString msg = QString::fromLocal8Bit(response.data["msg"].data()); - showStatusMessage(tr("Custom dumper setup: %1").arg(msg), 10000); - } -} - void GdbEngine::updateLocals() { watchHandler()->resetValueCache(); - if (hasPython()) - updateLocalsPython(UpdateParameters()); - else - updateLocalsClassic(); -} - -// Parse a local variable from GdbMi. -WatchData GdbEngine::localVariable(const GdbMi &item, - const QStringList &uninitializedVariables, - QMap<QByteArray, int> *seen) -{ - // Local variables of inlined code are reported as - // 26^done,locals={varobj={exp="this",value="",name="var4",exp="this", - // numchild="1",type="const QtSharedPointer::Basic<CPlusPlus::..."}} - // We do not want these at all. Current hypotheses is that those - // "spurious" locals have _two_ "exp" field. Try to filter them: - QByteArray name; - if (m_isMacGdb) { - int numExps = 0; - foreach (const GdbMi &child, item.children()) - numExps += int(child.name() == "exp"); - if (numExps > 1) - return WatchData(); - name = item["exp"].data(); - } else { - name = item["name"].data(); - } - const QMap<QByteArray, int>::iterator it = seen->find(name); - if (it != seen->end()) { - const int n = it.value(); - ++(it.value()); - WatchData data; - QString nam = _(name); - data.iname = "local." + name + QByteArray::number(n + 1); - data.name = WatchData::shadowedName(nam, n); - if (uninitializedVariables.contains(data.name)) { - data.setError(WatchData::msgNotInScope()); - return data; - } - data.updateValue(item); - //: Type of local variable or parameter shadowed by another - //: variable of the same name in a nested block. - data.setType(GdbEngine::tr("<shadowed>").toUtf8()); - data.setHasChildren(false); - return data; - } - seen->insert(name, 1); - WatchData data; - QString nam = _(name); - data.iname = "local." + name; - data.name = nam; - data.exp = name; - data.updateType(item["type"]); - if (uninitializedVariables.contains(data.name)) { - data.setError(WatchData::msgNotInScope()); - return data; - } - if (isSynchronous()) { - data.updateValue(item); - // We know that the complete list of children is - // somewhere in the response. - data.setChildrenUnneeded(); - } else { - // Set value only directly if it is simple enough, otherwise - // pass through the insertData() machinery. - if (isIntOrFloatType(data.type) || isPointerType(data.type)) - data.updateValue(item); - } - - if (!watchHandler()->isExpandedIName(data.iname)) - data.setChildrenUnneeded(); - - GdbMi t = item["numchild"]; - if (t.isValid()) - data.setHasChildren(t.toInt() > 0); - else if (isPointerType(data.type) || data.name == QLatin1String("this")) - data.setHasChildren(true); - return data; + updateLocalsPython(UpdateParameters()); } void GdbEngine::insertData(const WatchData &data) @@ -4306,7 +3703,7 @@ void GdbEngine::insertData(const WatchData &data) void GdbEngine::assignValueInDebugger(const WatchData *data, const QString &expression, const QVariant &value) { - if (hasPython() && !isIntOrFloatType(data->type)) { + if (!isIntOrFloatType(data->type)) { QByteArray cmd = "bbedit " + data->type.toHex() + ',' + expression.toUtf8().toHex() + ',' @@ -4582,8 +3979,7 @@ void GdbEngine::fetchDisassemblerByCliRangeMixed(const DisassemblerAgentCookie & const quint64 address = ac.agent->address(); QByteArray start = QByteArray::number(address - 20, 16); QByteArray end = QByteArray::number(address + 100, 16); - const char sep = m_disassembleUsesComma ? ',' : ' '; - QByteArray cmd = "disassemble /m 0x" + start + sep + "0x" + end; + QByteArray cmd = "disassemble /m 0x" + start + ",0x" + end; postCommand(cmd, Discardable|ConsoleCommand, CB(handleFetchDisassemblerByCliRangeMixed), QVariant::fromValue(ac)); } @@ -4595,8 +3991,7 @@ void GdbEngine::fetchDisassemblerByCliRangePlain(const DisassemblerAgentCookie & const quint64 address = ac.agent->address(); QByteArray start = QByteArray::number(address - 20, 16); QByteArray end = QByteArray::number(address + 100, 16); - const char sep = m_disassembleUsesComma ? ',' : ' '; - QByteArray cmd = "disassemble 0x" + start + sep + "0x" + end; + QByteArray cmd = "disassemble 0x" + start + ",0x" + end; postCommand(cmd, Discardable, CB(handleFetchDisassemblerByCliRangePlain), QVariant::fromValue(ac)); } @@ -4987,14 +4382,6 @@ void GdbEngine::loadInitScript() void GdbEngine::tryLoadPythonDumpers() { - if (m_forceAsyncModel) - return; - if (!m_hasPython) - return; - if (m_pythonAttemptedToLoad) - return; - m_pythonAttemptedToLoad = true; - const QByteArray dumperSourcePath = Core::ICore::resourcePath().toLocal8Bit() + "/debugger/"; @@ -5010,11 +4397,7 @@ void GdbEngine::tryLoadPythonDumpers() void GdbEngine::reloadDebuggingHelpers() { - // Only supported for python. - if (m_hasPython) { - m_pythonAttemptedToLoad = false; - tryLoadPythonDumpers(); - } + tryLoadPythonDumpers(); } void GdbEngine::handleGdbError(QProcess::ProcessError error) @@ -5270,11 +4653,6 @@ void GdbEngine::handleAdapterCrashed(const QString &msg) showMessageBox(QMessageBox::Critical, tr("Adapter crashed"), msg); } -bool GdbEngine::hasPython() const -{ - return m_hasPython; -} - void GdbEngine::createFullBacktrace() { postCommand("thread apply all bt full", @@ -5363,9 +4741,6 @@ bool GdbEngine::isHiddenBreakpoint(const BreakpointResponseId &id) const bool GdbEngine::usesExecInterrupt() const { - if (m_gdbVersion < 70000) - return false; - // debuggerCore()->boolSetting(TargetAsync) DebuggerStartMode mode = startParameters().startMode; return (mode == AttachToRemoteServer || mode == AttachToRemoteProcess) @@ -5390,9 +4765,6 @@ void GdbEngine::requestDebugInformation(const DebugInfoTask &task) bool GdbEngine::attemptQuickStart() const { - if (m_forceAsyncModel) - return false; - // Don't try if the user does not ask for it. if (!debuggerCore()->boolSetting(AttemptQuickStart)) return false; @@ -5484,6 +4856,11 @@ QByteArray GdbEngine::dotEscape(QByteArray str) return str; } +void GdbEngine::debugLastCommand() +{ + postCommand(m_lastDebuggableCommand, Discardable); +} + // // Factory // @@ -5511,6 +4888,192 @@ void addGdbOptionPages(QList<Core::IOptionsPage *> *opts) opts->push_back(new GdbOptionsPage2()); } + +void GdbEngine::updateLocalsPython(const UpdateParameters ¶ms) +{ + //m_pendingWatchRequests = 0; + m_pendingBreakpointRequests = 0; + m_processedNames.clear(); + + WatchHandler *handler = watchHandler(); + QByteArray expanded = "expanded:" + handler->expansionRequests() + ' '; + expanded += "typeformats:" + handler->typeFormatRequests() + ' '; + expanded += "formats:" + handler->individualFormatRequests(); + + QByteArray cutOff = " stringcutoff:" + + debuggerCore()->action(MaximalStringLength)->value().toByteArray(); + + QByteArray watchers; + const QString fileName = stackHandler()->currentFrame().file; + const QString function = stackHandler()->currentFrame().function; + if (!fileName.isEmpty()) { + // Re-create tooltip items that are not filters on existing local variables in + // the tooltip model. + DebuggerToolTipContexts toolTips = + DebuggerToolTipManager::treeWidgetExpressions(fileName, objectName(), function); + + const QString currentExpression = tooltipExpression(); + if (!currentExpression.isEmpty()) { + int currentIndex = -1; + for (int i = 0; i < toolTips.size(); ++i) { + if (toolTips.at(i).expression == currentExpression) { + currentIndex = i; + break; + } + } + if (currentIndex < 0) { + DebuggerToolTipContext context; + context.expression = currentExpression; + context.iname = tooltipIName(currentExpression); + toolTips.push_back(context); + } + } + + foreach (const DebuggerToolTipContext &p, toolTips) { + if (p.iname.startsWith("tooltip")) { + if (!watchers.isEmpty()) + watchers += "##"; + watchers += p.expression.toLatin1(); + watchers += '#'; + watchers += p.iname; + } + } + } + + QHash<QByteArray, int> watcherNames = handler->watcherNames(); + QHashIterator<QByteArray, int> it(watcherNames); + while (it.hasNext()) { + it.next(); + if (!watchers.isEmpty()) + watchers += "##"; + watchers += it.key() + "#watch." + QByteArray::number(it.value()); + } + + const static bool alwaysVerbose = !qgetenv("QTC_DEBUGGER_PYTHON_VERBOSE").isEmpty(); + QByteArray options; + if (alwaysVerbose) + options += "pe,"; + if (debuggerCore()->boolSetting(UseDebuggingHelpers)) + options += "fancy,"; + if (debuggerCore()->boolSetting(AutoDerefPointers)) + options += "autoderef,"; + if (debuggerCore()->boolSetting(UseDynamicType)) + options += "dyntype,"; + if (options.isEmpty()) + options += "defaults,"; + if (params.tryPartial) + options += "partial,"; + if (params.tooltipOnly) + options += "tooltiponly,"; + options.chop(1); + + QByteArray resultVar; + if (!m_resultVarName.isEmpty()) + resultVar = "resultvarname:" + m_resultVarName + ' '; + + m_lastDebuggableCommand = + "bb options:pe," + options + " vars:" + params.varList + ' ' + + expanded + " watchers:" + watchers.toHex() + cutOff; + + postCommand("bb options:" + options + " vars:" + params.varList + ' ' + + resultVar + expanded + " watchers:" + watchers.toHex() + cutOff, + Discardable, CB(handleStackFramePython), QVariant(params.tryPartial)); +} + +void GdbEngine::handleStackFramePython(const GdbResponse &response) +{ + if (response.resultClass == GdbResultDone) { + const bool partial = response.cookie.toBool(); + QByteArray out = response.consoleStreamOutput; + while (out.endsWith(' ') || out.endsWith('\n')) + out.chop(1); + int pos = out.indexOf("data="); + if (pos != 0) { + showMessage(_("DISCARDING JUNK AT BEGIN OF RESPONSE: " + + out.left(pos))); + out = out.mid(pos); + } + GdbMi all; + all.fromStringMultiple(out); + GdbMi data = all["data"]; + + WatchHandler *handler = watchHandler(); + QList<WatchData> list; + + if (!partial) { + list.append(*handler->findData("local")); + list.append(*handler->findData("watch")); + list.append(*handler->findData("return")); + } + + foreach (const GdbMi &child, data.children()) { + WatchData dummy; + dummy.iname = child["iname"].data(); + GdbMi wname = child["wname"]; + if (wname.isValid()) { + // Happens (only) for watched expressions. They are encoded as + // base64 encoded 8 bit data, without quotes + dummy.name = decodeData(wname.data(), Base64Encoded8Bit); + dummy.exp = dummy.name.toUtf8(); + } else { + dummy.name = _(child["name"].data()); + } + parseWatchData(handler->expandedINames(), dummy, child, &list); + } + const GdbMi typeInfo = all["typeinfo"]; + if (typeInfo.type() == GdbMi::List) { + foreach (const GdbMi &s, typeInfo.children()) { + const GdbMi name = s["name"]; + const GdbMi size = s["size"]; + if (name.isValid() && size.isValid()) + m_typeInfoCache.insert(QByteArray::fromBase64(name.data()), + TypeInfo(size.data().toUInt())); + } + } + for (int i = 0; i != list.size(); ++i) { + const TypeInfo ti = m_typeInfoCache.value(list.at(i).type); + if (ti.size) + list[i].size = ti.size; + } + + handler->insertData(list); + + //PENDING_DEBUG("AFTER handleStackFrame()"); + // FIXME: This should only be used when updateLocals() was + // triggered by expanding an item in the view. + //if (m_pendingWatchRequests <= 0) { + //PENDING_DEBUG("\n\n .... AND TRIGGERS MODEL UPDATE\n"); + rebuildWatchModel(); + //} + if (!partial) + emit stackFrameCompleted(); + } else { + showMessage(_("DUMPER FAILED: " + response.toString())); + } +} + +QString GdbEngine::msgPtraceError(DebuggerStartMode sm) +{ + if (sm == StartInternal) { + return QCoreApplication::translate("QtDumperHelper", + "ptrace: Operation not permitted.\n\n" + "Could not attach to the process. " + "Make sure no other debugger traces this process.\n" + "Check the settings of\n" + "/proc/sys/kernel/yama/ptrace_scope\n" + "For more details, see /etc/sysctl.d/10-ptrace.conf\n"); + } + return QCoreApplication::translate("QtDumperHelper", + "ptrace: Operation not permitted.\n\n" + "Could not attach to the process. " + "Make sure no other debugger traces this process.\n" + "If your uid matches the uid\n" + "of the target process, check the settings of\n" + "/proc/sys/kernel/yama/ptrace_scope\n" + "For more details, see /etc/sysctl.d/10-ptrace.conf\n"); +} + + } // namespace Internal } // namespace Debugger diff --git a/src/plugins/debugger/gdb/gdbengine.h b/src/plugins/debugger/gdb/gdbengine.h index 7146a6c10f..c4f3a78fa1 100644 --- a/src/plugins/debugger/gdb/gdbengine.h +++ b/src/plugins/debugger/gdb/gdbengine.h @@ -58,131 +58,6 @@ class WatchData; class DisassemblerAgentCookie; class DisassemblerLines; -enum DebuggingHelperState -{ - DebuggingHelperUninitialized, - DebuggingHelperLoadTried, - DebuggingHelperAvailable, - DebuggingHelperUnavailable -}; - -/* This is only used with Mac gdb since 2.2 - * - * "Custom dumper" is a library compiled against the current - * Qt containing functions to evaluate values of Qt classes - * (such as QString, taking pointers to their addresses). - * The library must be loaded into the debuggee. - * It provides a function that takes input from an input buffer - * and some parameters and writes output into an output buffer. - * Parameter 1 is the protocol: - * 1) Query. Fills output buffer with known types, Qt version and namespace. - * This information is parsed and stored by this class (special type - * enumeration). - * 2) Evaluate symbol, taking address and some additional parameters - * depending on type. */ - -class DumperHelper -{ -public: - enum Type { - UnknownType, - SupportedType, // A type that requires no special handling by the dumper - // Below types require special handling - QAbstractItemType, - QObjectType, QWidgetType, QObjectSlotType, QObjectSignalType, - QVectorType, QMapType, QMultiMapType, QMapNodeType, QStackType, - StdVectorType, StdDequeType, StdSetType, StdMapType, StdStackType, - StdStringType - }; - - // Type/Parameter struct required for building a value query - struct TypeData { - TypeData(); - void clear(); - - Type type; - bool isTemplate; - QByteArray tmplate; - QByteArray inner; - }; - - DumperHelper(); - void clear(); - - double dumperVersion() const { return m_dumperVersion; } - - int typeCount() const; - // Look up a simple, non-template type - Type simpleType(const QByteArray &simpleType) const; - // Look up a (potentially) template type and fill parameter struct - TypeData typeData(const QByteArray &typeName) const; - Type type(const QByteArray &typeName) const; - - int qtVersion() const; - QByteArray qtVersionString() const; - QByteArray qtNamespace() const; - void setQtNamespace(const QByteArray &ba) - { if (!ba.isEmpty()) m_qtNamespace = ba; } - - // Complete parse of "query" (protocol 1) response from debuggee buffer. - // 'data' excludes the leading indicator character. - bool parseQuery(const GdbMi &data); - // Sizes can be added as the debugger determines them - void addSize(const QByteArray &type, int size); - - // Determine the parameters required for an "evaluate" (protocol 2) call - void evaluationParameters(const WatchData &data, - const TypeData &td, - QByteArray *inBuffer, - QList<QByteArray> *extraParameters) const; - - QString toString(bool debug = false) const; - - static QString msgDumperOutdated(double requiredVersion, double currentVersion); - static QString msgPtraceError(DebuggerStartMode sm); - -private: - typedef QMap<QByteArray, Type> NameTypeMap; - typedef QMap<QByteArray, int> SizeCache; - - // Look up a simple (namespace) type - QByteArray evaluationSizeofTypeExpression(const QByteArray &typeName) const; - - NameTypeMap m_nameTypeMap; - SizeCache m_sizeCache; - - // The initial dumper query function returns sizes of some special - // types to aid CDB since it cannot determine the size of classes. - // They are not complete (std::allocator<X>). - enum SpecialSizeType { IntSize, PointerSize, StdAllocatorSize, - QSharedPointerSize, QSharedDataPointerSize, - QWeakPointerSize, QPointerSize, - QListSize, QLinkedListSize, QVectorSize, QQueueSize, - SpecialSizeCount }; - - // Resolve name to enumeration or SpecialSizeCount (invalid) - SpecialSizeType specialSizeType(const QByteArray &type) const; - - int m_specialSizes[SpecialSizeCount]; - - typedef QMap<QByteArray, QByteArray> ExpressionCache; - ExpressionCache m_expressionCache; - int m_qtVersion; - double m_dumperVersion; - QByteArray m_qtNamespace; - - void setQClassPrefixes(const QByteArray &qNamespace); - - QByteArray m_qPointerPrefix; - QByteArray m_qSharedPointerPrefix; - QByteArray m_qSharedDataPointerPrefix; - QByteArray m_qWeakPointerPrefix; - QByteArray m_qListPrefix; - QByteArray m_qLinkedListPrefix; - QByteArray m_qVectorPrefix; - QByteArray m_qQueuePrefix; -}; - class GdbEngine : public Debugger::DebuggerEngine { Q_OBJECT @@ -206,9 +81,8 @@ private: ////////// General Interface ////////// virtual bool acceptsDebuggerCommands() const; virtual void executeDebuggerCommand(const QString &command, DebuggerLanguages languages); - virtual QByteArray qtNamespace() const { return m_dumperHelper.qtNamespace(); } - virtual void setQtNamespace(const QByteArray &ns) - { return m_dumperHelper.setQtNamespace(ns); } + virtual QByteArray qtNamespace() const { return m_qtNamespace; } + virtual void setQtNamespace(const QByteArray &ns) { m_qtNamespace = ns; } private: ////////// General State ////////// @@ -228,7 +102,6 @@ protected: ////////// Gdb Process Management ////////// void loadInitScript(); void tryLoadPythonDumpers(); - void pythonDumpersFailed(); // Something went wrong with the adapter *before* adapterStarted() was emitted. // Make sure to clean up everything before emitting this signal. @@ -359,7 +232,6 @@ private: CommandsDoneCallback m_commandsDoneCallback; QList<GdbCommand> m_commandsToRunOnTemporaryBreak; - int gdbVersion() const { return m_gdbVersion; } private: ////////// Gdb Output, State & Capability Handling ////////// protected: @@ -374,9 +246,7 @@ protected: StackFrame parseStackFrame(const GdbMi &mi, int level); void resetCommandQueue(); - bool isSynchronous() const { return hasPython(); } - virtual bool hasPython() const; - bool supportsThreads() const; + bool isSynchronous() const { return true; } // Gdb initialization sequence void handleShowVersion(const GdbResponse &response); @@ -384,12 +254,8 @@ protected: void handleHasPython(const GdbResponse &response); void handlePythonSetup(const GdbResponse &response); - int m_gdbVersion; // 6.8.0 is 60800 - int m_gdbBuildVersion; // MAC only? - bool m_isMacGdb; + int m_gdbVersion; // 7.6.1 is 70601 bool m_isQnxGdb; - bool m_hasBreakpointNotifications; - bool m_hasPython; private: ////////// Inferior Management ////////// @@ -428,9 +294,9 @@ private: ////////// Inferior Management ////////// void maybeHandleInferiorPidChanged(const QString &pid); void handleInfoProc(const GdbResponse &response); + QString msgPtraceError(DebuggerStartMode sm); private: ////////// View & Data Stuff ////////// - protected: void selectThread(ThreadId threadId); void activateFrame(int index); @@ -439,10 +305,7 @@ private: ////////// View & Data Stuff ////////// // // Breakpoint specific stuff // - void handleBreakList(const GdbResponse &response); - void handleBreakList(const GdbMi &table); void handleBreakModifications(const GdbMi &bkpts); - void handleBreakListMultiple(const GdbResponse &response); void handleBreakIgnore(const GdbResponse &response); void handleBreakDisable(const GdbResponse &response); void handleBreakEnable(const GdbResponse &response); @@ -455,18 +318,15 @@ private: ////////// View & Data Stuff ////////// void handleWatchInsert(const GdbResponse &response); void handleCatchInsert(const GdbResponse &response); void handleBkpt(const GdbMi &bkpt, const BreakpointModelId &id); - void handleInfoLine(const GdbResponse &response); - void extractDataFromInfoBreak(const QString &output, BreakpointModelId); void updateResponse(BreakpointResponse &response, const GdbMi &bkpt); QByteArray breakpointLocation(BreakpointModelId id); // For gdb/MI. QByteArray breakpointLocation2(BreakpointModelId id); // For gdb/CLI fallback. QString breakLocation(const QString &file) const; - void reloadBreakListInternal(); - void attemptAdjustBreakpointLocation(BreakpointModelId id); // // Modules specific stuff // + protected: void loadSymbols(const QString &moduleName); Q_SLOT void loadAllSymbols(); void loadSymbolsForStack(); @@ -474,6 +334,7 @@ private: ////////// View & Data Stuff ////////// void requestModuleSections(const QString &moduleName); void reloadModules(); void examineModules(); + void reloadModulesInternal(); void handleModulesList(const GdbResponse &response); void handleShowModuleSymbols(const GdbResponse &response); @@ -522,8 +383,6 @@ private: ////////// View & Data Stuff ////////// DisassemblerLines parseMiDisassembler(const GdbMi &response); Q_SLOT void reloadDisassembly(); - bool m_disassembleUsesComma; - // // Source file specific stuff // @@ -539,17 +398,13 @@ private: ////////// View & Data Stuff ////////// QMap<QString, QString> m_fullToShortName; QMultiMap<QString, QString> m_baseNameToFullName; - void invalidateSourcesList(); bool m_sourcesListUpdating; - bool m_breakListOutdated; // // Stack specific stuff // protected: void updateAll(); - void updateAllClassic(); - void updateAllPython(); void handleStackListFrames(const GdbResponse &response); void handleStackSelectThread(const GdbResponse &response); void handleStackSelectFrame(const GdbResponse &response); @@ -581,34 +436,14 @@ protected: virtual void watchPoint(const QPoint &); void handleWatchPoint(const GdbResponse &response); - void updateSubItemClassic(const WatchData &data); - void updateWatchData(const WatchData &data, const WatchUpdateFlags &flags); void rebuildWatchModel(); void showToolTip(); void insertData(const WatchData &data); - void sendWatchParameters(const QByteArray ¶ms0); - void createGdbVariableClassic(const WatchData &data); - - void runDebuggingHelperClassic(const WatchData &data, bool dumpChildren); - void runDirectDebuggingHelperClassic(const WatchData &data, bool dumpChildren); - bool hasDebuggingHelperForType(const QByteArray &type) const; - void handleVarListChildrenClassic(const GdbResponse &response); - void handleVarListChildrenHelperClassic(const GdbMi &child, - const WatchData &parent, int sortId); - void handleVarCreate(const GdbResponse &response); void handleVarAssign(const GdbResponse &response); - void handleEvaluateExpressionClassic(const GdbResponse &response); - void handleQueryDebuggingHelperClassic(const GdbResponse &response); - void handleDebuggingHelperValue2Classic(const GdbResponse &response); - void handleDebuggingHelperValue3Classic(const GdbResponse &response); - void handleDebuggingHelperEditValue(const GdbResponse &response); - void handleDebuggingHelperSetup(const GdbResponse &response); - void handleDebuggingHelperVersionCheckClassic(const GdbResponse &response); void handleDetach(const GdbResponse &response); - void handleThreadGroupCreated(const GdbMi &result); void handleThreadGroupExited(const GdbMi &result); @@ -616,17 +451,10 @@ protected: void handleCreateFullBacktrace(const GdbResponse &response); void updateLocals(); - void updateLocalsClassic(); void updateLocalsPython(const UpdateParameters ¶meters); void handleStackFramePython(const GdbResponse &response); - void handleStackListLocalsClassic(const GdbResponse &response); - - WatchData localVariable(const GdbMi &item, - const QStringList &uninitializedVariables, - QMap<QByteArray, int> *seen); void setLocals(const QList<GdbMi> &locals); - void handleStackListArgumentsClassic(const GdbResponse &response); QSet<QByteArray> m_processedNames; struct TypeInfo @@ -641,13 +469,9 @@ protected: // // Dumper Management // - bool checkDebuggingHelpersClassic(); - void setDebuggingHelperStateClassic(DebuggingHelperState); - void tryLoadDebuggingHelpersClassic(); void reloadDebuggingHelpers(); - DebuggingHelperState m_debuggingHelperState; - DumperHelper m_dumperHelper; + QByteArray m_qtNamespace; QString m_gdb; // @@ -699,10 +523,8 @@ protected: bool attemptQuickStart() const; bool m_fullStartDone; bool m_systemDumpersLoaded; - bool m_pythonAttemptedToLoad; // Test - bool m_forceAsyncModel; QList<WatchData> m_completed; QSet<QByteArray> m_uncompleted; @@ -714,19 +536,12 @@ protected: static QString msgConnectRemoteServerFailed(const QString &why); static QByteArray dotEscape(QByteArray str); -protected: - enum DumperHandling - { - DumperNotAvailable, - DumperLoadedByAdapter, - DumperLoadedByGdbPreload, - DumperLoadedByGdb - }; + void debugLastCommand(); + QByteArray m_lastDebuggableCommand; +protected: virtual void write(const QByteArray &data); - virtual DumperHandling dumperHandling() const = 0; - protected: bool prepareCommand(); void interruptLocalInferior(qint64 pid); @@ -735,7 +550,6 @@ protected: ProjectExplorer::DeviceProcessSignalOperation::Ptr m_signalOperation; }; - } // namespace Internal } // namespace Debugger diff --git a/src/plugins/debugger/gdb/gdboptionspage.cpp b/src/plugins/debugger/gdb/gdboptionspage.cpp index 518a3f2841..719778a743 100644 --- a/src/plugins/debugger/gdb/gdboptionspage.cpp +++ b/src/plugins/debugger/gdb/gdboptionspage.cpp @@ -54,7 +54,7 @@ namespace Internal { class GdbOptionsPageWidget : public QWidget { public: - explicit GdbOptionsPageWidget(QWidget *parent); + explicit GdbOptionsPageWidget(QWidget *parent = 0); QGroupBox *groupBoxGeneral; QLabel *labelGdbWatchdogTimeout; @@ -83,7 +83,6 @@ public: //QLineEdit *lineEditSelectedPluginBreakpointsPattern; Utils::SavedActionSet group; - QString searchKeywords; }; GdbOptionsPageWidget::GdbOptionsPageWidget(QWidget *parent) @@ -297,19 +296,6 @@ GdbOptionsPageWidget::GdbOptionsPageWidget(QWidget *parent) // setEnabled(dc->action(SelectedPluginBreakpoints)->value().toBool()); //connect(radioButtonSelectedPluginBreakpoints, SIGNAL(toggled(bool)), // lineEditSelectedPluginBreakpointsPattern, SLOT(setEnabled(bool))); - - const QLatin1Char sep(' '); - QTextStream(&searchKeywords) - << sep << groupBoxGeneral->title() - << sep << checkBoxLoadGdbInit->text() - << sep << checkBoxLoadGdbDumpers->text() - << sep << checkBoxUseDynamicType->text() - << sep << labelGdbWatchdogTimeout->text() - << sep << checkBoxSkipKnownFrames->text() - << sep << checkBoxUseMessageBoxForSignals->text() - << sep << checkBoxAdjustBreakpointLocations->text(); - ; - searchKeywords.remove(QLatin1Char('&')); } GdbOptionsPage::GdbOptionsPage() @@ -325,9 +311,10 @@ GdbOptionsPage::~GdbOptionsPage() { } -QWidget *GdbOptionsPage::createPage(QWidget *parent) +QWidget *GdbOptionsPage::widget() { - m_widget = new GdbOptionsPageWidget(parent); + if (!m_widget) + m_widget = new GdbOptionsPageWidget; return m_widget; } @@ -339,13 +326,10 @@ void GdbOptionsPage::apply() void GdbOptionsPage::finish() { - if (m_widget) + if (m_widget) { m_widget->group.finish(); -} - -bool GdbOptionsPage::matches(const QString &s) const -{ - return m_widget && m_widget->searchKeywords.contains(s, Qt::CaseInsensitive); + delete m_widget; + } } ///////////////////////////////////////////////////////////////////////// @@ -357,7 +341,7 @@ bool GdbOptionsPage::matches(const QString &s) const class GdbOptionsPageWidget2 : public QWidget { public: - explicit GdbOptionsPageWidget2(QWidget *parent); + explicit GdbOptionsPageWidget2(QWidget *parent = 0); QGroupBox *groupBoxDangerous; QLabel *labelDangerous; @@ -371,7 +355,6 @@ public: QCheckBox *checkBoxMultiInferior; Utils::SavedActionSet group; - QString searchKeywords; }; GdbOptionsPageWidget2::GdbOptionsPageWidget2(QWidget *parent) @@ -398,7 +381,7 @@ GdbOptionsPageWidget2::GdbOptionsPageWidget2(QWidget *parent) checkBoxAutoEnrichParameters->setText(GdbOptionsPage::tr( "Use common locations for debug information")); checkBoxAutoEnrichParameters->setToolTip(GdbOptionsPage::tr( - "<html><head/><body>Add common paths to locations " + "<html><head/><body>Adds common paths to locations " "of debug information such as <i>/usr/src/debug</i> " "when starting GDB.</body></html>")); @@ -418,7 +401,7 @@ GdbOptionsPageWidget2::GdbOptionsPageWidget2(QWidget *parent) checkBoxEnableReverseDebugging = new QCheckBox(groupBoxDangerous); checkBoxEnableReverseDebugging->setText(GdbOptionsPage::tr("Enable reverse debugging")); checkBoxEnableReverseDebugging->setToolTip(GdbOptionsPage::tr( - "<html><head/><body><p>Enable stepping backwards.</p><p>" + "<html><head/><body><p>Enables stepping backwards.</p><p>" "<b>Note:</b> This feature is very slow and unstable on the GDB side. " "It exhibits unpredictable behavior when going backwards over system " "calls and is very likely to destroy your debugging session.</p></body></html>")); @@ -426,14 +409,14 @@ GdbOptionsPageWidget2::GdbOptionsPageWidget2(QWidget *parent) checkBoxAttemptQuickStart = new QCheckBox(groupBoxDangerous); checkBoxAttemptQuickStart->setText(GdbOptionsPage::tr("Attempt quick start")); checkBoxAttemptQuickStart->setToolTip(GdbOptionsPage::tr( - "<html><head/><body>Postpone reading debug information as long as possible. " + "<html><head/><body>Postpones reading debug information as long as possible. " "This can result in faster startup times at the price of not being able to " "set breakpoints by file and number.</body></html>")); checkBoxMultiInferior = new QCheckBox(groupBoxDangerous); checkBoxMultiInferior->setText(GdbOptionsPage::tr("Debug all children")); checkBoxMultiInferior->setToolTip(GdbOptionsPage::tr( - "<html><head/><body>Keep debugging all children after a fork." + "<html><head/><body>Keeps debugging all children after a fork." "</body></html>")); @@ -460,16 +443,6 @@ GdbOptionsPageWidget2::GdbOptionsPageWidget2(QWidget *parent) group.insert(dc->action(AttemptQuickStart), checkBoxAttemptQuickStart); group.insert(dc->action(MultiInferior), checkBoxMultiInferior); group.insert(dc->action(EnableReverseDebugging), checkBoxEnableReverseDebugging); - - const QLatin1Char sep(' '); - QTextStream(&searchKeywords) - << sep << groupBoxDangerous->title() - << sep << checkBoxTargetAsync->text() - << sep << checkBoxEnableReverseDebugging->text() - << sep << checkBoxAttemptQuickStart->text() - << sep << checkBoxMultiInferior->text() - ; - searchKeywords.remove(QLatin1Char('&')); } GdbOptionsPage2::GdbOptionsPage2() @@ -485,9 +458,10 @@ GdbOptionsPage2::~GdbOptionsPage2() { } -QWidget *GdbOptionsPage2::createPage(QWidget *parent) +QWidget *GdbOptionsPage2::widget() { - m_widget = new GdbOptionsPageWidget2(parent); + if (!m_widget) + m_widget = new GdbOptionsPageWidget2; return m_widget; } @@ -499,13 +473,10 @@ void GdbOptionsPage2::apply() void GdbOptionsPage2::finish() { - if (m_widget) + if (m_widget) { m_widget->group.finish(); -} - -bool GdbOptionsPage2::matches(const QString &s) const -{ - return m_widget && m_widget->searchKeywords.contains(s, Qt::CaseInsensitive); + delete m_widget; + } } } // namespace Internal diff --git a/src/plugins/debugger/gdb/gdboptionspage.h b/src/plugins/debugger/gdb/gdboptionspage.h index 8dacb9bb2d..beda3989dc 100644 --- a/src/plugins/debugger/gdb/gdboptionspage.h +++ b/src/plugins/debugger/gdb/gdboptionspage.h @@ -46,10 +46,9 @@ class GdbOptionsPage : public Core::IOptionsPage public: GdbOptionsPage(); ~GdbOptionsPage(); - QWidget *createPage(QWidget *parent); + QWidget *widget(); void apply(); void finish(); - bool matches(const QString &) const; private: QPointer<GdbOptionsPageWidget> m_widget; @@ -63,10 +62,9 @@ class GdbOptionsPage2 : public Core::IOptionsPage public: GdbOptionsPage2(); ~GdbOptionsPage2(); - QWidget *createPage(QWidget *parent); + QWidget *widget(); void apply(); void finish(); - bool matches(const QString &) const; private: QPointer<GdbOptionsPageWidget2> m_widget; diff --git a/src/plugins/debugger/gdb/gdbplainengine.cpp b/src/plugins/debugger/gdb/gdbplainengine.cpp index c9651c9ee0..366395db64 100644 --- a/src/plugins/debugger/gdb/gdbplainengine.cpp +++ b/src/plugins/debugger/gdb/gdbplainengine.cpp @@ -112,13 +112,6 @@ void GdbPlainEngine::handleExecRun(const GdbResponse &response) } } -GdbEngine::DumperHandling GdbPlainEngine::dumperHandling() const -{ - // LD_PRELOAD fails for System-Qt on Mac. - return Utils::HostOsInfo::isWindowsHost() || Utils::HostOsInfo::isMacHost() - ? DumperLoadedByGdb : DumperLoadedByGdbPreload; -} - void GdbPlainEngine::setupEngine() { QTC_ASSERT(state() == EngineSetupRequested, qDebug() << state()); diff --git a/src/plugins/debugger/gdb/gdbplainengine.h b/src/plugins/debugger/gdb/gdbplainengine.h index 0b9f79d278..df2894ecc4 100644 --- a/src/plugins/debugger/gdb/gdbplainengine.h +++ b/src/plugins/debugger/gdb/gdbplainengine.h @@ -55,8 +55,6 @@ private: void interruptInferior2(); void shutdownEngine(); - DumperHandling dumperHandling() const; - QByteArray execFilePath() const; QByteArray toLocalEncoding(const QString &s) const; QString fromLocalEncoding(const QByteArray &b) const; diff --git a/src/plugins/debugger/gdb/pythongdbengine.cpp b/src/plugins/debugger/gdb/pythongdbengine.cpp deleted file mode 100644 index fa560cdca0..0000000000 --- a/src/plugins/debugger/gdb/pythongdbengine.cpp +++ /dev/null @@ -1,227 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). -** 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 "gdbengine.h" - -#include <debugger/debuggerprotocol.h> -#include <debugger/debuggeractions.h> -#include <debugger/debuggercore.h> -#include <debugger/debuggerstringutils.h> -#include <debugger/debuggertooltipmanager.h> - -#include <debugger/stackhandler.h> - -#include <utils/savedaction.h> -#include <utils/qtcassert.h> - -#define PRECONDITION QTC_CHECK(hasPython()) -#define CB(callback) &GdbEngine::callback, STRINGIFY(callback) - - -namespace Debugger { -namespace Internal { - -void GdbEngine::updateLocalsPython(const UpdateParameters ¶ms) -{ - PRECONDITION; - //m_pendingWatchRequests = 0; - m_pendingBreakpointRequests = 0; - m_processedNames.clear(); - - WatchHandler *handler = watchHandler(); - QByteArray expanded = "expanded:" + handler->expansionRequests() + ' '; - expanded += "typeformats:" + handler->typeFormatRequests() + ' '; - expanded += "formats:" + handler->individualFormatRequests(); - - QByteArray cutOff = " stringcutoff:" - + debuggerCore()->action(MaximalStringLength)->value().toByteArray(); - - QByteArray watchers; - const QString fileName = stackHandler()->currentFrame().file; - const QString function = stackHandler()->currentFrame().function; - if (!fileName.isEmpty()) { - // Re-create tooltip items that are not filters on existing local variables in - // the tooltip model. - DebuggerToolTipContexts toolTips = - DebuggerToolTipManager::treeWidgetExpressions(fileName, objectName(), function); - - const QString currentExpression = tooltipExpression(); - if (!currentExpression.isEmpty()) { - int currentIndex = -1; - for (int i = 0; i < toolTips.size(); ++i) { - if (toolTips.at(i).expression == currentExpression) { - currentIndex = i; - break; - } - } - if (currentIndex < 0) { - DebuggerToolTipContext context; - context.expression = currentExpression; - context.iname = tooltipIName(currentExpression); - toolTips.push_back(context); - } - } - - foreach (const DebuggerToolTipContext &p, toolTips) { - if (p.iname.startsWith("tooltip")) { - if (!watchers.isEmpty()) - watchers += "##"; - watchers += p.expression.toLatin1(); - watchers += '#'; - watchers += p.iname; - } - } - } - - QHash<QByteArray, int> watcherNames = handler->watcherNames(); - QHashIterator<QByteArray, int> it(watcherNames); - while (it.hasNext()) { - it.next(); - if (!watchers.isEmpty()) - watchers += "##"; - watchers += it.key() + "#watch." + QByteArray::number(it.value()); - } - - const static bool alwaysVerbose = !qgetenv("QTC_DEBUGGER_PYTHON_VERBOSE").isEmpty(); - QByteArray options; - if (alwaysVerbose) - options += "pe,"; - if (debuggerCore()->boolSetting(UseDebuggingHelpers)) - options += "fancy,"; - if (debuggerCore()->boolSetting(AutoDerefPointers)) - options += "autoderef,"; - if (debuggerCore()->boolSetting(UseDynamicType)) - options += "dyntype,"; - if (options.isEmpty()) - options += "defaults,"; - if (params.tryPartial) - options += "partial,"; - if (params.tooltipOnly) - options += "tooltiponly,"; - options.chop(1); - - QByteArray resultVar; - if (!m_resultVarName.isEmpty()) - resultVar = "resultvarname:" + m_resultVarName + ' '; - - postCommand("bb options:" + options + " vars:" + params.varList + ' ' - + resultVar + expanded + " watchers:" + watchers.toHex() + cutOff, - Discardable, CB(handleStackFramePython), QVariant(params.tryPartial)); -} - -void GdbEngine::handleStackFramePython(const GdbResponse &response) -{ - PRECONDITION; - if (response.resultClass == GdbResultDone) { - const bool partial = response.cookie.toBool(); - QByteArray out = response.consoleStreamOutput; - while (out.endsWith(' ') || out.endsWith('\n')) - out.chop(1); - int pos = out.indexOf("data="); - if (pos != 0) { - showMessage(_("DISCARDING JUNK AT BEGIN OF RESPONSE: " - + out.left(pos))); - out = out.mid(pos); - } - GdbMi all; - all.fromStringMultiple(out); - GdbMi data = all["data"]; - - WatchHandler *handler = watchHandler(); - QList<WatchData> list; - - if (!partial) { - list.append(*handler->findData("local")); - list.append(*handler->findData("watch")); - list.append(*handler->findData("return")); - } - - foreach (const GdbMi &child, data.children()) { - WatchData dummy; - dummy.iname = child["iname"].data(); - GdbMi wname = child["wname"]; - if (wname.isValid()) { - // Happens (only) for watched expressions. They are encoded as - // base64 encoded 8 bit data, without quotes - dummy.name = decodeData(wname.data(), Base64Encoded8Bit); - dummy.exp = dummy.name.toUtf8(); - } else { - dummy.name = _(child["name"].data()); - } - parseWatchData(handler->expandedINames(), dummy, child, &list); - } - const GdbMi typeInfo = all["typeinfo"]; - if (typeInfo.type() == GdbMi::List) { - foreach (const GdbMi &s, typeInfo.children()) { - const GdbMi name = s["name"]; - const GdbMi size = s["size"]; - if (name.isValid() && size.isValid()) - m_typeInfoCache.insert(QByteArray::fromBase64(name.data()), - TypeInfo(size.data().toUInt())); - } - } - for (int i = 0; i != list.size(); ++i) { - const TypeInfo ti = m_typeInfoCache.value(list.at(i).type); - if (ti.size) - list[i].size = ti.size; - } - - handler->insertData(list); - - //PENDING_DEBUG("AFTER handleStackFrame()"); - // FIXME: This should only be used when updateLocals() was - // triggered by expanding an item in the view. - //if (m_pendingWatchRequests <= 0) { - //PENDING_DEBUG("\n\n .... AND TRIGGERS MODEL UPDATE\n"); - rebuildWatchModel(); - //} - if (!partial) - emit stackFrameCompleted(); - } else { - showMessage(_("DUMPER FAILED: " + response.toString())); - } -} - -// Called from CoreAdapter and AttachAdapter -void GdbEngine::updateAllPython() -{ - PRECONDITION; - //PENDING_DEBUG("UPDATING ALL\n"); - QTC_CHECK(state() == InferiorUnrunnable || state() == InferiorStopOk); - reloadModulesInternal(); - postCommand("-stack-list-frames", CB(handleStackListFrames), - QVariant::fromValue<StackCookie>(StackCookie(false, true))); - stackHandler()->setCurrentIndex(0); - postCommand("-thread-info", CB(handleThreadInfo), 0); - reloadRegisters(); - updateLocals(); -} - -} // namespace Internal -} // namespace Debugger diff --git a/src/plugins/debugger/gdb/remotegdbserveradapter.cpp b/src/plugins/debugger/gdb/remotegdbserveradapter.cpp index 9b62f422f6..3554ade37f 100644 --- a/src/plugins/debugger/gdb/remotegdbserveradapter.cpp +++ b/src/plugins/debugger/gdb/remotegdbserveradapter.cpp @@ -75,16 +75,6 @@ GdbRemoteServerEngine::GdbRemoteServerEngine(const DebuggerStartParameters &star SLOT(uploadProcFinished())); } -GdbEngine::DumperHandling GdbRemoteServerEngine::dumperHandling() const -{ - using namespace ProjectExplorer; - const Abi abi = startParameters().toolChainAbi; - if (abi.os() == Abi::WindowsOS - || abi.binaryFormat() == Abi::ElfFormat) - return DumperLoadedByGdb; - return DumperLoadedByGdbPreload; -} - void GdbRemoteServerEngine::setupEngine() { QTC_ASSERT(state() == EngineSetupRequested, qDebug() << state()); @@ -378,7 +368,7 @@ void GdbRemoteServerEngine::handleAttach(const GdbResponse &response) } case GdbResultError: if (response.data["msg"].data() == "ptrace: Operation not permitted.") { - notifyInferiorSetupFailed(DumperHelper::msgPtraceError(startParameters().startMode)); + notifyInferiorSetupFailed(msgPtraceError(startParameters().startMode)); break; } // if msg != "ptrace: ..." fall through diff --git a/src/plugins/debugger/gdb/remotegdbserveradapter.h b/src/plugins/debugger/gdb/remotegdbserveradapter.h index 38daa7dbc9..b6548a98b6 100644 --- a/src/plugins/debugger/gdb/remotegdbserveradapter.h +++ b/src/plugins/debugger/gdb/remotegdbserveradapter.h @@ -49,8 +49,6 @@ public: explicit GdbRemoteServerEngine(const DebuggerStartParameters &startParameters); private: - DumperHandling dumperHandling() const; - void setupEngine(); void setupInferior(); void runEngine(); diff --git a/src/plugins/debugger/gdb/termgdbadapter.cpp b/src/plugins/debugger/gdb/termgdbadapter.cpp index 5f372e8386..9251be8f7c 100644 --- a/src/plugins/debugger/gdb/termgdbadapter.cpp +++ b/src/plugins/debugger/gdb/termgdbadapter.cpp @@ -74,14 +74,6 @@ GdbTermEngine::~GdbTermEngine() m_stubProc.disconnect(); // Avoid spurious state transitions from late exiting stub } -GdbEngine::DumperHandling GdbTermEngine::dumperHandling() const -{ - // LD_PRELOAD fails for System-Qt on Mac. - return Utils::HostOsInfo::isWindowsHost() || Utils::HostOsInfo::isMacHost() - ? DumperLoadedByGdb - : DumperLoadedByAdapter; // Handles loading itself via LD_PRELOAD -} - void GdbTermEngine::setupEngine() { QTC_ASSERT(state() == EngineSetupRequested, qDebug() << state()); @@ -169,7 +161,7 @@ void GdbTermEngine::handleStubAttached(const GdbResponse &response) break; case GdbResultError: if (response.data["msg"].data() == "ptrace: Operation not permitted.") { - notifyInferiorSetupFailed(DumperHelper::msgPtraceError(startParameters().startMode)); + notifyInferiorSetupFailed(msgPtraceError(startParameters().startMode)); break; } notifyInferiorSetupFailed(QString::fromLocal8Bit(response.data["msg"].data())); diff --git a/src/plugins/debugger/gdb/termgdbadapter.h b/src/plugins/debugger/gdb/termgdbadapter.h index ac84271d39..f91602f973 100644 --- a/src/plugins/debugger/gdb/termgdbadapter.h +++ b/src/plugins/debugger/gdb/termgdbadapter.h @@ -52,8 +52,6 @@ public: ~GdbTermEngine(); private: - DumperHandling dumperHandling() const; - void setupEngine(); void handleGdbStartFailed(); void setupInferior(); diff --git a/src/plugins/debugger/lldblib/guest/README b/src/plugins/debugger/lldblib/guest/README deleted file mode 100644 index be9d4fbee7..0000000000 --- a/src/plugins/debugger/lldblib/guest/README +++ /dev/null @@ -1,31 +0,0 @@ -LLDB Guest Engine - -You can use the LLDB debugger from the LLVM project with the Qt Creator debugger -plugin on Mac OS. - -For the Qt Creator build to pick up the LLDB Guest Engine, -you must download the LLDB debugger and configure it -to be included in the Qt Creator build. - -To debug an application, Qt Creator must access the memory of the application. -On Mac OS X, this requires code signing. - -To enable LLDB debugger support in Qt Creator: - -1. To download the LLDB debugger, enter the following command: - svn co http://llvm.org/svn/llvm-project/lldb/trunk lldb - -2. To sign the code, follow the instructions in lldb/docs/code-signing.txt. - -3. To open LLDB in Xcode for building, enter the following command: - open lldb.xcodeproj - then select the Release target and press the build button. - -4. In Xcode, press the build button. - -5. type the following to have the qt creator build system find your lldb build: - export WITH_LLDB=/path/to/lldb - -6. To rebuild Qt Creator, change back to the top level directory of - the Qt Creator source, and enter the following command: - qmake -r && make diff --git a/src/plugins/debugger/lldblib/guest/lldbengineguest.cpp b/src/plugins/debugger/lldblib/guest/lldbengineguest.cpp deleted file mode 100644 index 15b82c8503..0000000000 --- a/src/plugins/debugger/lldblib/guest/lldbengineguest.cpp +++ /dev/null @@ -1,761 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). -** 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. -** -****************************************************************************/ - -#define QT_NO_CAST_FROM_ASCII - -#include "lldbengineguest.h" - -#include "debuggeractions.h" -#include "debuggerconstants.h" -#include "debuggerdialogs.h" -#include "debuggerplugin.h" -#include "debuggerstringutils.h" - -#include "breakhandler.h" -#include "breakpoint.h" -#include "moduleshandler.h" -#include "registerhandler.h" -#include "stackhandler.h" -#include "watchhandler.h" -#include "watchutils.h" -#include "threadshandler.h" - -#include <utils/qtcassert.h> -#include <QDebug> -#include <QProcess> -#include <QFileInfo> -#include <QThread> -#include <QMutexLocker> - -#include <lldb/API/LLDB.h> - -#define DEBUG_FUNC_ENTER \ - showMessage(QString(QLatin1String("LLDB guest engine: %1 ")) \ - .arg(QLatin1String(Q_FUNC_INFO))); \ - qDebug("%s", Q_FUNC_INFO) - -#define SYNC_INFERIOR_OR(x) if (m_running) { x; } - - -namespace Debugger { -namespace Internal { - -void LldbEventListener::listen(lldb::SBListener *listener) -{ - while (true) { - lldb::SBEvent event; - if (listener->WaitForEvent(1000, event)) - emit lldbEvent(&event); - } -} - -LldbEngineGuest::LldbEngineGuest() - : IPCEngineGuest() - , m_running (false) - , m_worker (new LldbEventListener) - , m_lldb (new lldb::SBDebugger) - , m_target (new lldb::SBTarget) - , m_process (new lldb::SBProcess) - , m_listener(new lldb::SBListener("bla")) - , m_relistFrames (false) -#if defined(HAVE_LLDB_PRIVATE) - , py (new PythonLLDBToGdbMiHack) -#endif -{ - qRegisterMetaType<lldb::SBListener *>("lldb::SBListener *"); - qRegisterMetaType<lldb::SBEvent *>("lldb::SBEvent *"); - - m_worker->moveToThread(&m_wThread); - connect(m_worker, SIGNAL(lldbEvent(lldb::SBEvent*)), this, - SLOT(lldbEvent(lldb::SBEvent*)), Qt::BlockingQueuedConnection); - m_wThread.start(); - setObjectName(QLatin1String("LLDBEngineGuest")); -} - -LldbEngineGuest::~LldbEngineGuest() -{ - delete m_lldb; - delete m_target; - delete m_process; - delete m_listener; -} - - -void LldbEngineGuest::nuke() -{ - ::exit(4); -} - -void LldbEngineGuest::setupEngine() -{ - DEBUG_FUNC_ENTER; - - lldb::SBDebugger::Initialize(); - - *m_lldb = lldb::SBDebugger::Create(); - m_lldb->Initialize(); - if (m_lldb->IsValid()) - notifyEngineSetupOk(); - else - notifyEngineSetupFailed(); - -} - -void LldbEngineGuest::setupInferior(const QString &executable, - const QStringList &args, const QStringList &env) -{ - DEBUG_FUNC_ENTER; - - foreach (const QString &s, args) { - m_arguments.append(s.toLocal8Bit()); - } - foreach (const QString &s, env) { - m_environment.append(s.toLocal8Bit()); - } - - qDebug("creating target for %s", executable.toLocal8Bit().data()); - showStatusMessage(QLatin1String("starting ") + executable); - *m_target = m_lldb->CreateTarget(executable.toLocal8Bit().data()); - if (!m_target->IsValid()) { - notifyInferiorSetupFailed(); - return; - } - DEBUG_FUNC_ENTER; - - const char **argp = new const char *[m_arguments.count() + 1]; - argp[m_arguments.count()] = 0; - for (int i = 0; i < m_arguments.count(); i++) { - argp[i] = m_arguments[i].data(); - } - - const char **envp = new const char *[m_environment.count() + 1]; - envp[m_environment.count()] = 0; - for (int i = 0; i < m_environment.count(); i++) { - envp[i] = m_environment[i].data(); - } - lldb::SBError err; - *m_process = m_target->Launch(argp, envp, NULL, NULL, true, err); - - if (!err.Success()) { - showMessage(QString::fromLocal8Bit(err.GetCString())); - qDebug() << err.GetCString(); - notifyInferiorSetupFailed(); - } - - /* - * note, the actual string ptrs are still valid. They are in m_environment. - * They probably leak. Considered the marvelous API, there is not much we can do - */ - delete [] envp; - - if (!m_process->IsValid()) - notifyEngineRunFailed(); - QTC_ASSERT(m_listener->IsValid(), qDebug() << false); - m_listener->StartListeningForEvents(m_process->GetBroadcaster(), UINT32_MAX); - QMetaObject::invokeMethod(m_worker, "listen", Qt::QueuedConnection, - Q_ARG(lldb::SBListener *, m_listener)); - notifyInferiorSetupOk(); -} - -void LldbEngineGuest::runEngine() -{ - DEBUG_FUNC_ENTER; - m_process->Continue(); -} - -void LldbEngineGuest::shutdownInferior() -{ - DEBUG_FUNC_ENTER; - m_process->Kill(); -} - -void LldbEngineGuest::shutdownEngine() -{ - DEBUG_FUNC_ENTER; - m_currentFrame = lldb::SBFrame(); - m_currentThread = lldb::SBThread(); - m_breakpoints.clear(); - m_localesCache.clear(); - - /* - * this leaks. However, Terminate is broken and lldb leaks anyway - * We should kill the engine guest process - */ - - *m_lldb = lldb::SBDebugger(); - // leakd.Terminate(); - notifyEngineShutdownOk(); -} - -void LldbEngineGuest::detachDebugger() -{ - DEBUG_FUNC_ENTER; -} - -void LldbEngineGuest::executeStep() -{ - DEBUG_FUNC_ENTER; - - if (!m_currentThread.IsValid()) - return; - m_currentThread.StepInto(); -} - -void LldbEngineGuest::executeStepOut() -{ - DEBUG_FUNC_ENTER; - - if (!m_currentThread.IsValid()) - return; - m_currentThread.StepOut(); -} - -void LldbEngineGuest::executeNext() -{ - DEBUG_FUNC_ENTER; - - if (!m_currentThread.IsValid()) - return; - m_currentThread.StepOver(); -} - -void LldbEngineGuest::executeStepI() -{ - DEBUG_FUNC_ENTER; - - if (!m_currentThread.IsValid()) - return; - m_currentThread.StepInstruction(false); -} - -void LldbEngineGuest::executeNextI() -{ - DEBUG_FUNC_ENTER; - - if (!m_currentThread.IsValid()) - return; - m_currentThread.StepInstruction(true); -} - -void LldbEngineGuest::continueInferior() -{ - DEBUG_FUNC_ENTER; - - notifyInferiorRunRequested(); - m_process->Continue(); - showStatusMessage(QLatin1String("resuming inferior")); -} -void LldbEngineGuest::interruptInferior() -{ - DEBUG_FUNC_ENTER; - - m_process->Stop(); - notifyInferiorStopOk(); - m_relistFrames = true; - updateThreads(); -} - -void LldbEngineGuest::executeRunToLine(const ContextData &data); -{ - DEBUG_FUNC_ENTER; - - // TODO - Q_UNUSED(data); -} - -void LldbEngineGuest::executeRunToFunction(const QString &functionName) -{ - DEBUG_FUNC_ENTER; - - // TODO - Q_UNUSED(functionName); -} -void LldbEngineGuest::executeJumpToLine(const ContextData &data); -{ - DEBUG_FUNC_ENTER; - - // TODO - Q_UNUSED(data); -} - -void LldbEngineGuest::activateFrame(qint64 token) -{ - DEBUG_FUNC_ENTER; - SYNC_INFERIOR_OR(showMessage(QLatin1String( - "activateFrame called while inferior running")); return); - - currentFrameChanged(token); - m_localesCache.clear(); - - lldb::SBFrame fr = m_currentThread.GetFrameAtIndex(token); - m_currentFrame = fr; - lldb::SBSymbolContext context = fr.GetSymbolContext(lldb::eSymbolContextEverything); - lldb::SBValueList values = fr.GetVariables(true, true, false, true); - QList<WatchData> wd; - QByteArray iname = "local"; - for (uint i = 0; i < values.GetSize(); i++) { - lldb::SBValue v = values.GetValueAtIndex(i); - if (!v.IsInScope(fr)) - continue; - getWatchDataR(v, 1, iname, wd); - } - updateWatchData(true, wd); -} - -void LldbEngineGuest::requestUpdateWatchData(const Internal::WatchData &data, - const Internal::WatchUpdateFlags &) -{ - DEBUG_FUNC_ENTER; - SYNC_INFERIOR_OR(return); - - lldb::SBValue v = m_localesCache.value(QString::fromUtf8(data.iname)); - QList<WatchData> wd; - for (uint j = 0; j < v.GetNumChildren(); j++) { - lldb::SBValue vv = v.GetChildAtIndex(j); - getWatchDataR(vv, 1, data.iname, wd); - } - updateWatchData(false, wd); -} - -void LldbEngineGuest::getWatchDataR(lldb::SBValue v, int level, - const QByteArray &p_iname, QList<WatchData> &wd) -{ - QByteArray iname = p_iname + '.' + QByteArray(v.GetName()); - m_localesCache.insert(QString::fromLocal8Bit(iname), v); - -#if defined(HAVE_LLDB_PRIVATE) - wd += py->expand(p_iname, v, m_currentFrame, *m_process); -#else - WatchData d; - d.name = QString::fromLocal8Bit(v.GetName()); - d.iname = iname; - d.type = QByteArray(v.GetTypeName()).trimmed(); - d.value = (QString::fromLocal8Bit(v.GetValue(m_currentFrame))); - d.hasChildren = v.GetNumChildren(); - d.state = WatchData::State(0); - wd.append(d); -#endif - - if (--level > 0) { - for (uint j = 0; j < v.GetNumChildren(); j++) { - lldb::SBValue vv = v.GetChildAtIndex(j); - getWatchDataR(vv, level, iname, wd); - } - } -} - -void LldbEngineGuest::disassemble(quint64 pc) -{ - DEBUG_FUNC_ENTER; - SYNC_INFERIOR_OR(return); - - if (!m_currentThread.IsValid()) - return; - for (uint j = 0; j < m_currentThread.GetNumFrames(); j++) { - lldb::SBFrame fr = m_currentThread.GetFrameAtIndex(j); - if (pc == fr.GetPCAddress().GetLoadAddress(*m_target)) { - QString linesStr = QString::fromLocal8Bit(fr.Disassemble()); - DisassemblerLines lines; - foreach (const QString &lineStr, linesStr.split(QLatin1Char('\n'))) { - lines.appendLine(DisassemblerLine(lineStr)); - } - disassembled(pc, lines); - } - } -} -void LldbEngineGuest::fetchFrameSource(qint64 frame) -{ - QFile f(m_frame_to_file.value(frame)); - f.open(QFile::ReadOnly); - frameSourceFetched(frame, QFileInfo(m_frame_to_file.value(frame)).fileName() - , QString::fromLocal8Bit(f.readAll())); -} - -void LldbEngineGuest::addBreakpoint(BreakpointId id, - const Internal::BreakpointParameters &bp_) -{ - DEBUG_FUNC_ENTER; - SYNC_INFERIOR_OR(notifyAddBreakpointFailed(id); return); - - Internal::BreakpointParameters bp(bp_); - - lldb::SBBreakpoint llbp = m_target->BreakpointCreateByLocation( - bp.fileName.toLocal8Bit().constData(), bp.lineNumber); - if (llbp.IsValid()) { - m_breakpoints.insert(id, llbp); - - llbp.SetIgnoreCount(bp.ignoreCount); - bp.ignoreCount = llbp.GetIgnoreCount(); - bp.enabled = llbp.IsEnabled(); - - lldb::SBBreakpointLocation location = llbp.GetLocationAtIndex(0); - if (location.IsValid()) { - bp.address = location.GetLoadAddress(); - - // FIXME get those from lldb - bp.lineNumber = bp.lineNumber; - bp.fileName = bp.fileName; - notifyAddBreakpointOk(id); - showMessage(QLatin1String("[BB] ok.")); - notifyBreakpointAdjusted(id, bp); - } else { - m_breakpoints.take(id); - showMessage(QLatin1String("[BB] failed. cant resolve yet")); -// notifyAddBreakpointFailed(id); -// notifyAddBreakpointOk(id); - } - } else { - showMessage(QLatin1String("[BB] failed. dunno.")); - notifyAddBreakpointFailed(id); - } -} - -void LldbEngineGuest::removeBreakpoint(BreakpointId id) -{ - DEBUG_FUNC_ENTER; - SYNC_INFERIOR_OR(notifyRemoveBreakpointFailed(id); return); - - lldb::SBBreakpoint llbp = m_breakpoints.take(id); - llbp.SetEnabled(false); - notifyRemoveBreakpointOk(id); -} - -void LldbEngineGuest::changeBreakpoint(BreakpointId id, - const Internal::BreakpointParameters &bp) -{ - DEBUG_FUNC_ENTER; - - // TODO - Q_UNUSED(id); - Q_UNUSED(bp); -} - -void LldbEngineGuest::selectThread(qint64 token) -{ - DEBUG_FUNC_ENTER; - SYNC_INFERIOR_OR(return); - - m_frame_to_file.clear(); - for (uint i = 0; i < m_process->GetNumThreads(); i++) { - lldb::SBThread t = m_process->GetThreadAtIndex(i); - if (t.GetThreadID() == token) { - m_currentThread = t; - StackFrames frames; - int firstResolvableFrame = -1; - for (uint j = 0; j < t.GetNumFrames(); j++) { - lldb::SBFrame fr = t.GetFrameAtIndex(j); - if (!fr.IsValid()) { - qDebug("warning: frame %i is garbage", j); - continue; - } - lldb::SBSymbolContext context = - fr.GetSymbolContext(lldb::eSymbolContextEverything); - lldb::SBSymbol sym = fr.GetSymbol(); - lldb::SBFunction func = fr.GetFunction(); - lldb::SBCompileUnit tu = fr.GetCompileUnit(); - lldb::SBModule module = fr.GetModule(); - lldb::SBBlock block = fr.GetBlock(); - lldb::SBBlock fblock = fr.GetFrameBlock(); - lldb::SBLineEntry le = fr.GetLineEntry(); - lldb::SBValueList values = fr.GetVariables(true, true, true, false); -#if 0 - qDebug()<<"\tframe "<<fr.GetFrameID(); - qDebug() << "\t\tPC: " << ("0x" + QByteArray::number( - fr.GetPCAddress().GetLoadAddress(*m_target), 16)).data(); - qDebug() << "\t\tFP: " << ("0x" + QByteArray::number(fr.GetFP(), 16)).data(); - qDebug() << "\t\tSP: " << ("0x" + QByteArray::number(fr.GetSP(), 16)).data(); - qDebug() << "\t\tsymbol: " << sym.IsValid() << sym.GetName() << sym.GetMangledName(); - qDebug() << "\t\tfunction:" << func.IsValid(); - qDebug() << "\t\ttu: " << tu.IsValid(); - if (tu.IsValid()) - - qDebug() << "\t\tmodule: " << module.IsValid() << module.GetFileSpec().IsValid() - << module.GetFileSpec().GetFilename(); - qDebug() << "\t\tblock: " << block.IsValid() << block.GetInlinedName(); - qDebug() << "\t\tfblock: " << block.IsValid() << block.GetInlinedName(); - qDebug() << "\t\tle: " << le.IsValid() << le.GetLine()<<le.GetColumn(); - qDebug() << "\t\tvalues: "<<values.IsValid() << values.GetSize(); - qDebug() << "\t\tcontext: " << context.IsValid(); - qDebug() << "\t\t\tmodule: " << context.GetModule().IsValid(); - qDebug() << "\t\t\tfunction: " << context.GetFunction().IsValid(); - qDebug() << "\t\t\tblock: " << context.GetBlock().IsValid(); - qDebug() << "\t\t\tle: " << context.GetLineEntry().IsValid(); - qDebug() << "\t\t\tsymbol: " << context.GetSymbol().IsValid(); -// qDebug() << "\t\tdisassemly -->\n" << fr.Disassemble() << "<--"; -#endif - - QString sourceFile; - QString sourceFilePath; - int lineNumber = 0; - if (le.IsValid()) { - lineNumber = le.GetLine(); - if (le.GetFileSpec().IsValid()) { - sourceFile = QString::fromLocal8Bit(le.GetFileSpec().GetFilename()); - sourceFilePath = QString::fromLocal8Bit(le.GetFileSpec().GetDirectory()) - + QLatin1String("/") + sourceFile; - if (firstResolvableFrame < 0) - firstResolvableFrame = j; - } - } - sourceFilePath = QFileInfo(sourceFilePath).canonicalFilePath(); - - QString functionName; - if (func.IsValid()) - functionName = QString::fromLocal8Bit(func.GetName()); - else - functionName = QString::fromLocal8Bit(sym.GetName()); - - StackFrame frame; - frame.level = fr.GetFrameID(); - if (func.IsValid()) - frame.function = QString::fromLocal8Bit(func.GetName()); - else - frame.function = QString::fromLocal8Bit(sym.GetName()); - frame.from = QString::fromLocal8Bit(module.GetFileSpec().GetFilename()); - frame.address = fr.GetPCAddress().GetLoadAddress(*m_target); - frame.line = lineNumber; - frame.file = sourceFilePath; - frame.usable = QFileInfo(frame.file).isReadable(); - frames.append(frame); - m_frame_to_file.insert(j, frame.file); - } - currentThreadChanged(token); - listFrames(frames); - activateFrame(firstResolvableFrame > -1 ? firstResolvableFrame : 0); - return; - } - } -} - -void LldbEngineGuest::updateThreads() -{ - DEBUG_FUNC_ENTER; - SYNC_INFERIOR_OR(return); - - /* There is no way to find the StopReason of a _process_ - * We try to emulate gdb here, by assuming there must be exactly one 'guilty' thread. - * However, if there are no threads at all, it must be that the process - * no longer exists. Let's tear down the whole session. - */ - if (m_process->GetNumThreads() < 1) { - notifyEngineSpontaneousShutdown(); - m_process->Kill(); - m_process->Destroy(); - } - - Threads threads; - for (uint i = 0; i < m_process->GetNumThreads(); i++) { - lldb::SBThread t = m_process->GetThreadAtIndex(i); - if (!t.IsValid()) { - qDebug("warning: thread %i is garbage", i); - continue; - } - ThreadData thread; - thread.id = t.GetThreadID(); - thread.targetId = QString::number(t.GetThreadID()); - thread.core.clear(); - thread.state = QString::number(t.GetStopReason()); - - switch (t.GetStopReason()) { - case lldb::eStopReasonInvalid: - case lldb::eStopReasonNone: - case lldb::eStopReasonTrace: - thread.state = QLatin1String("running"); - break; - case lldb::eStopReasonBreakpoint: - case lldb::eStopReasonWatchpoint: - showStatusMessage(QLatin1String("hit breakpoint")); - thread.state = QLatin1String("hit breakpoint"); - if (m_currentThread.GetThreadID() != t.GetThreadID()) { - m_currentThread = t; - currentThreadChanged(t.GetThreadID()); - m_relistFrames = true; - } - break; - case lldb::eStopReasonSignal: - showStatusMessage(QLatin1String("stopped")); - thread.state = QLatin1String("stopped"); - if (m_currentThread.GetThreadID() != t.GetThreadID()) { - m_currentThread = t; - currentThreadChanged(t.GetThreadID()); - m_relistFrames = true; - } - break; - case lldb::eStopReasonException: - showStatusMessage(QLatin1String("application crashed.")); - thread.state = QLatin1String("crashed"); - if (m_currentThread.GetThreadID() != t.GetThreadID()) { - m_currentThread = t; - currentThreadChanged(t.GetThreadID()); - m_relistFrames = true; - } - break; - case lldb::eStopReasonPlanComplete: - thread.state = QLatin1String("crazy things happened"); - break; - }; - - thread.lineNumber = 0; - thread.name = QString::fromLocal8Bit(t.GetName()); - - lldb::SBFrame fr = t.GetFrameAtIndex(0); - if (!fr.IsValid()) { - qDebug("warning: frame 0 is garbage"); - continue; - } - lldb::SBSymbolContext context = fr.GetSymbolContext(lldb::eSymbolContextEverything); - lldb::SBSymbol sym = fr.GetSymbol(); - lldb::SBFunction func = fr.GetFunction(); - lldb::SBLineEntry le = fr.GetLineEntry(); - QString sourceFile; - QString sourceFilePath; - int lineNumber = 0; - if (le.IsValid()) { - lineNumber = le.GetLine(); - if (le.GetFileSpec().IsValid()) { - sourceFile = QString::fromLocal8Bit(le.GetFileSpec().GetFilename()); - sourceFilePath = QString::fromLocal8Bit(le.GetFileSpec().GetDirectory()) - + QLatin1String("/") + sourceFile; - } - } - QString functionName; - if (func.IsValid()) - functionName = QString::fromLocal8Bit(func.GetName()); - else - functionName = QString::fromLocal8Bit(sym.GetName()); - - lldb::SBValueList values = fr.GetVariables(true, true, false, false); - thread.fileName = sourceFile; - thread.function = functionName; - thread.address = fr.GetPCAddress().GetLoadAddress(*m_target); - thread.lineNumber = lineNumber; - threads.append(thread); - } - listThreads(threads); - if (m_relistFrames) { - selectThread(m_currentThread.GetThreadID()); - m_relistFrames = false; - } -} - -void LldbEngineGuest::lldbEvent(lldb::SBEvent *ev) -{ - qDebug() << "lldbevent" << ev->GetType() << - m_process->GetState() << (int)state(); - - uint32_t etype = ev->GetType(); - switch (etype) { - // ProcessEvent - case 1: - switch (m_process->GetState()) { - case lldb::eStateRunning: // 5 - if (!m_running) - m_running = true; - notifyInferiorPid(m_process->GetProcessID()); - switch (state()) { - case EngineRunRequested: - notifyEngineRunAndInferiorRunOk(); - break; - case InferiorRunRequested: - notifyInferiorRunOk(); - break; - case InferiorStopOk: - notifyInferiorRunRequested(); - notifyInferiorRunOk(); - break; - default: - break; - } - break; - case lldb::eStateExited: // 9 - if (m_running) - m_running = false; - switch (state()) { - case InferiorShutdownRequested: - notifyInferiorShutdownOk(); - break; - case InferiorRunOk: - m_relistFrames = true; - updateThreads(); - notifyEngineSpontaneousShutdown(); - m_process->Kill(); - m_process->Destroy(); - break; - default: - updateThreads(); - break; - } - break; - case lldb::eStateStopped: // 4 - if (m_running) - m_running = false; - switch (state()) { - case InferiorShutdownRequested: - notifyInferiorShutdownOk(); - break; - case InferiorRunOk: - m_relistFrames = true; - updateThreads(); - notifyInferiorSpontaneousStop(); - // fall - default: - m_relistFrames = true; - updateThreads(); - break; - } - break; - case lldb::eStateCrashed: // 7 - if (m_running) - m_running = false; - switch (state()) { - case InferiorShutdownRequested: - notifyInferiorShutdownOk(); - break; - case InferiorRunOk: - m_relistFrames = true; - updateThreads(); - notifyInferiorSpontaneousStop(); - break; - default: - break; - } - break; - default: - qDebug("unexpected ProcessEvent"); - break; - } - break; - default: - break; - }; -} - - -} // namespace Internal -} // namespace Debugger diff --git a/src/plugins/debugger/lldblib/guest/lldbengineguest.h b/src/plugins/debugger/lldblib/guest/lldbengineguest.h deleted file mode 100644 index b2af9842b0..0000000000 --- a/src/plugins/debugger/lldblib/guest/lldbengineguest.h +++ /dev/null @@ -1,136 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). -** 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. -** -****************************************************************************/ - -#ifndef DEBUGGER_LLDBENGINE_GUEST_H -#define DEBUGGER_LLDBENGINE_GUEST_H - -#include "ipcengineguest.h" - -#include <QQueue> -#include <QVariant> -#include <QThread> -#include <QStringList> - -#include <lldb/API/LLDB.h> - -#if defined(HAVE_LLDB_PRIVATE) -#include "pygdbmiemu.h" -#endif - -Q_DECLARE_METATYPE (lldb::SBListener *) -Q_DECLARE_METATYPE (lldb::SBEvent *) - -namespace Debugger { -namespace Internal { - -class LldbEventListener : public QObject -{ -Q_OBJECT -public slots: - void listen(lldb::SBListener *listener); -signals: - // lldb API uses non thread safe implicit sharing with no explicit copy feature - // additionally the scope is undefined, hence this signal needs to be connected BlockingQueued - // whutever, works for now. - void lldbEvent(lldb::SBEvent *ev); -}; - - -class LldbEngineGuest : public IPCEngineGuest -{ - Q_OBJECT - -public: - explicit LldbEngineGuest(); - ~LldbEngineGuest(); - - void nuke(); - void setupEngine(); - void setupInferior(const QString &executable, const QStringList &arguments, - const QStringList &environment); - void runEngine(); - void shutdownInferior(); - void shutdownEngine(); - void detachDebugger(); - void executeStep(); - void executeStepOut() ; - void executeNext(); - void executeStepI(); - void executeNextI(); - void continueInferior(); - void interruptInferior(); - void executeRunToLine(const ContextData &data); - void executeRunToFunction(const QString &functionName); - void executeJumpToLine(const ContextData &data); - void activateFrame(qint64); - void selectThread(qint64); - void disassemble(quint64 pc); - void addBreakpoint(BreakpointModelId id, const BreakpointParameters &bp); - void removeBreakpoint(BreakpointModelId id); - void changeBreakpoint(BreakpointModelId id, const BreakpointParameters &bp); - void requestUpdateWatchData(const WatchData &data, - const WatchUpdateFlags &flags); - void fetchFrameSource(qint64 frame); - -private: - bool m_running; - - QList<QByteArray> m_arguments; - QList<QByteArray> m_environment; - QThread m_wThread; - LldbEventListener *m_worker; - lldb::SBDebugger *m_lldb; - lldb::SBTarget *m_target; - lldb::SBProcess *m_process; - lldb::SBListener *m_listener; - - lldb::SBFrame m_currentFrame; - lldb::SBThread m_currentThread; - bool m_relistFrames; - QHash<QString, lldb::SBValue> m_localesCache; - QHash<BreakpointModelId, lldb::SBBreakpoint> m_breakpoints; - QHash<qint64, QString> m_frame_to_file; - - void updateThreads(); - void getWatchDataR(lldb::SBValue v, int level, - const QByteArray &p_iname, QList<WatchData> &wd); - -#if defined(HAVE_LLDB_PRIVATE) - PythonLLDBToGdbMiHack * py; -#endif - -private slots: - void lldbEvent(lldb::SBEvent *ev); -}; - -} // namespace Internal -} // namespace Debugger - -#endif // DEBUGGER_LLDBENGINE_H -#define SYNC_INFERIOR diff --git a/src/plugins/debugger/lldblib/guest/main.cpp b/src/plugins/debugger/lldblib/guest/main.cpp deleted file mode 100644 index dfb382d38d..0000000000 --- a/src/plugins/debugger/lldblib/guest/main.cpp +++ /dev/null @@ -1,151 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). -** 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 "lldbengineguest.h" - -#include <QLocalSocket> -#include <QCoreApplication> -#include <QSocketNotifier> -#include <QQueue> - -#include <cstdio> - -// #define DO_STDIO_DEBUG 1 -#ifdef DO_STDIO_DEBUG -#define D_STDIO0(x) qDebug(x) -#define D_STDIO1(x,a1) qDebug(x,a1) -#define D_STDIO2(x,a1,a2) qDebug(x,a1,a2) -#define D_STDIO3(x,a1,a2,a3) qDebug(x,a1,a2,a3) -#else -#define D_STDIO0(x) -#define D_STDIO1(x,a1) -#define D_STDIO2(x,a1,a2) -#define D_STDIO3(x,a1,a2,a3) -#endif - -class Stdio : public QIODevice -{ - Q_OBJECT -public: - QSocketNotifier notify; - Stdio() - : QIODevice() - , notify(fileno(stdin), QSocketNotifier::Read) - , buckethead(0) - { - setvbuf(stdin , NULL , _IONBF , 0); - setvbuf(stdout , NULL , _IONBF , 0); - setOpenMode(QIODevice::ReadWrite | QIODevice::Unbuffered); - connect(¬ify, SIGNAL(activated(int)), this, SLOT(activated())); - } - virtual qint64 bytesAvailable () const - { - qint64 r = QIODevice::bytesAvailable(); - foreach (const QByteArray &bucket, buckets) - r += bucket.size(); - r-= buckethead; - return r; - } - - virtual qint64 readData (char * data, qint64 maxSize) - { - D_STDIO1("readData %lli",maxSize); - qint64 size = maxSize; - while (size > 0) { - if (!buckets.size()) { - D_STDIO1("done prematurely with %lli", maxSize - size); - return maxSize - size; - } - QByteArray &bucket = buckets.head(); - if ((size + buckethead) >= bucket.size()) { - int d = bucket.size() - buckethead; - D_STDIO3("read (over bucket) d: %i buckethead: %i bucket.size(): %i", - d, buckethead, bucket.size()); - memcpy(data, bucket.data() + buckethead, d); - data += d; - size -= d; - buckets.dequeue(); - buckethead = 0; - } else { - D_STDIO1("read (in bucket) size: %lli", size); - memcpy(data, bucket.data() + buckethead, size); - data += size; - buckethead += size; - size = 0; - } - } - D_STDIO1("done with %lli",(maxSize - size)); - return maxSize - size; - } - - virtual qint64 writeData (const char * data, qint64 maxSize) - { - return ::write(fileno(stdout), data, maxSize); - } - - QQueue<QByteArray> buckets; - int buckethead; - -private slots: - void activated() - { - QByteArray a; - a.resize(1000); - int ret = ::read(fileno(stdin), a.data(), 1000); - if (ret == 0) - ::exit(0); - assert(ret <= 1000); - D_STDIO1("activated %i", ret); - a.resize(ret); - buckets.enqueue(a); - emit readyRead(); - } -}; - -int main(int argc, char **argv) -{ - QCoreApplication app(argc, argv); - qDebug() << "guest engine operational"; - - Debugger::Internal::LldbEngineGuest lldb; - - - Stdio stdio; - lldb.setHostDevice(&stdio); - - return app.exec(); -} - -extern "C" { -extern const unsigned char lldbVersionString[] __attribute__ ((used)) = "@(#)PROGRAM:lldb PROJECT:lldb-26" "\n"; -extern const double lldbVersionNumber __attribute__ ((used)) = (double)26.; -extern const double LLDBVersionNumber __attribute__ ((used)) = (double)26.; -} - -#include "main.moc" diff --git a/src/plugins/debugger/lldblib/guest/qtcreator-lldb.plist b/src/plugins/debugger/lldblib/guest/qtcreator-lldb.plist deleted file mode 100644 index c0a3b2beaf..0000000000 --- a/src/plugins/debugger/lldblib/guest/qtcreator-lldb.plist +++ /dev/null @@ -1,21 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> -<plist version="1.0"> -<dict> - <key>CFBundleDevelopmentRegion</key> - <string>English</string> - <key>CFBundleIdentifier</key> - <string>org.qt-project.qtcreator-lldb</string> - <key>CFBundleInfoDictionaryVersion</key> - <string>6.0</string> - <key>CFBundleName</key> - <string>Qt Creator LLDB Guest</string> - <key>CFBundleVersion</key> - <string>1.0</string> - <key>SecTaskAccess</key> - <array> - <string>allowed</string> - <string>safe</string> - </array> -</dict> -</plist> diff --git a/src/plugins/debugger/lldblib/guest/qtcreator-lldb.pri b/src/plugins/debugger/lldblib/guest/qtcreator-lldb.pri deleted file mode 100644 index f44c3e9c84..0000000000 --- a/src/plugins/debugger/lldblib/guest/qtcreator-lldb.pri +++ /dev/null @@ -1,2 +0,0 @@ -WITH_LLDB = $$(WITH_LLDB) -macx: !isEmpty(WITH_LLDB) : SUBDIRS += $$PWD/qtcreator-lldb.pro diff --git a/src/plugins/debugger/lldblib/guest/qtcreator-lldb.pro b/src/plugins/debugger/lldblib/guest/qtcreator-lldb.pro deleted file mode 100644 index b6f5437284..0000000000 --- a/src/plugins/debugger/lldblib/guest/qtcreator-lldb.pro +++ /dev/null @@ -1,61 +0,0 @@ -WITH_LLDB = $$(WITH_LLDB) - -!macx: error (This can only be built on mac) -!exists($${WITH_LLDB}/include/lldb/lldb-enumerations.h): error(please see the README for build instructions) - -QT = core network - -include(../../../../../qtcreator.pri) -TEMPLATE = app -CONFIG -= app_bundle -CONFIG += debug -TARGET = qtcreator-lldb -DEPENDPATH += . .. ../.. ../../.. -INCLUDEPATH += . .. ../.. ../../.. -DESTDIR = $$IDE_LIBEXEC_PATH - -MOC_DIR=.tmp -OBJECTS_DIR=.tmp - -HEADERS += ../ipcengineguest.h \ - ../debuggerstreamops.h \ - ../breakpoint.h \ - ../watchdata.h \ - ../stackframe.h \ - ../disassemblerlines.h \ - lldbengineguest.h - -SOURCES += ../ipcengineguest.cpp \ - ../debuggerstreamops.cpp \ - ../breakpoint.cpp \ - ../watchdata.cpp \ - ../stackframe.cpp \ - ../disassemblerlines.cpp \ - lldbengineguest.cpp \ - main.cpp - - -LIBS += -sectcreate __TEXT __info_plist $$PWD/qtcreator-lldb.plist - -POSTL = rm -rf \'$${IDE_LIBEXEC_PATH}/LLDB.framework\' $$escape_expand(\\n\\t) \ - $$QMAKE_COPY_DIR $${WITH_LLDB}/build/Release/* \'$$IDE_LIBEXEC_PATH\' $$escape_expand(\\n\\t) \ - install_name_tool -change '@rpath/LLDB.framework/Versions/A/LLDB' '@executable_path/LLDB.framework/Versions/A/LLDB' $(TARGET) $$escape_expand(\\n\\t) \ - codesign -s lldb_codesign $(TARGET) - -!isEmpty(QMAKE_POST_LINK):QMAKE_POST_LINK = $$escape_expand(\\n\\t)$$QMAKE_POST_LINK -QMAKE_POST_LINK = $$POSTL $$QMAKE_POST_LINK -silent:QMAKE_POST_LINK = @echo signing $@ && $$QMAKE_POST_LINK - -LIBS += -framework Security -framework Python - -DEFINES += __STDC_LIMIT_MACROS __STDC_CONSTANT_MACROS - -INCLUDEPATH += $${WITH_LLDB}/include $${WITH_LLDB}/llvm/include/ -LIBS += -F$${WITH_LLDB}/build/Release -framework LLDB - -# include (lldb.pri) -# DEFINES += HAVE_LLDB_PRIVATE -# HEADERS += pygdbmiemu.h -# SOURCES += pygdbmiemu.cpp - - diff --git a/src/plugins/debugger/lldblib/ipcengineguest.cpp b/src/plugins/debugger/lldblib/ipcengineguest.cpp deleted file mode 100644 index 4c5d9c814e..0000000000 --- a/src/plugins/debugger/lldblib/ipcengineguest.cpp +++ /dev/null @@ -1,637 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). -** 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 "ipcengineguest.h" -#include "ipcenginehost.h" -#include "breakpoint.h" -#include "stackframe.h" -#include "threaddata.h" -#include "debuggerstreamops.h" - -#include <utils/qtcassert.h> - -#include <QLocalSocket> - -#include <QSysInfo> -#include <QDebug> -#include <QFileInfo> -#include <QTimer> - -#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN -#define SET_NATIVE_BYTE_ORDER(x) x.setByteOrder(QDataStream::LittleEndian) -#else -#define SET_NATIVE_BYTE_ORDER(x) x.setByteOrder(QDataStream::BigEndian) -#endif - -namespace Debugger { -namespace Internal { - -IPCEngineGuest::IPCEngineGuest() - : QObject() - , m_local_host(0) - , m_nextMessagePayloadSize(0) - , m_cookie(1) - , m_device(0) -{ -} - -IPCEngineGuest::~IPCEngineGuest() -{ -} - -void IPCEngineGuest::setLocalHost(IPCEngineHost *host) -{ - m_local_host = host; -} - -void IPCEngineGuest::setHostDevice(QIODevice *device) -{ - if (m_device) { - disconnect(m_device, SIGNAL(readyRead()), this, SLOT(readyRead())); - delete m_device; - } - m_device = device; - if (m_device) - connect(m_device, SIGNAL(readyRead()), SLOT(readyRead())); -} - -void IPCEngineGuest::rpcCall(Function f, QByteArray payload) -{ -#if 0 - if (m_local_host) { - QMetaObject::invokeMethod(m_local_host, - "rpcCallback", - Qt::QueuedConnection, - Q_ARG(quint64, f), - Q_ARG(QByteArray, payload)); - } else -#endif - if (m_device) { - { - QDataStream s(m_device); - SET_NATIVE_BYTE_ORDER(s); - s << m_cookie++; - s << quint64(f); - s << quint64(payload.size()); - } - m_device->write(payload); - m_device->putChar('T'); - QLocalSocket *sock = qobject_cast<QLocalSocket *>(m_device); - if (sock) - sock->flush(); - } -} - -void IPCEngineGuest::readyRead() -{ - if (!m_nextMessagePayloadSize) { - if (quint64(m_device->bytesAvailable()) < 3 * sizeof(quint64)) - return; - QDataStream s(m_device); - SET_NATIVE_BYTE_ORDER(s); - s >> m_nextMessageCookie; - s >> m_nextMessageFunction; - s >> m_nextMessagePayloadSize; - m_nextMessagePayloadSize += 1; // terminator and "got header" marker - } - - quint64 ba = m_device->bytesAvailable(); - if (ba < m_nextMessagePayloadSize) - return; - - qint64 rrr = m_nextMessagePayloadSize; - QByteArray payload = m_device->read(rrr); - if (quint64(payload.size()) != m_nextMessagePayloadSize || !payload.endsWith('T')) { - qDebug("IPC Error: corrupted frame"); - showMessage(QLatin1String("[guest] IPC Error: corrupted frame"), LogError); - nuke(); - return; - } - payload.chop(1); - rpcCallback(m_nextMessageFunction, payload); - m_nextMessagePayloadSize = 0; - - if (quint64(m_device->bytesAvailable ()) >= 3 * sizeof(quint64)) - QTimer::singleShot(0, this, SLOT(readyRead())); -} - -void IPCEngineGuest::rpcCallback(quint64 f, QByteArray payload) -{ - switch (f) { - default: - qDebug("IPC Error: unhandled id in host to guest call"); - showMessage(QLatin1String("IPC Error: unhandled id in host to guest call"), LogError); - nuke(); - break; - case IPCEngineHost::SetupIPC: - { - QDataStream s(payload); - SET_NATIVE_BYTE_ORDER(s); - int version; - s >> version; - Q_ASSERT(version == 1); - } - break; - case IPCEngineHost::StateChanged: - { - QDataStream s(payload); - SET_NATIVE_BYTE_ORDER(s); - quint64 st; - s >> st; - m_state = (DebuggerState)st; - } - break; - case IPCEngineHost::SetupEngine: - setupEngine(); - break; - case IPCEngineHost::SetupInferior: - { - QDataStream s(payload); - SET_NATIVE_BYTE_ORDER(s); - QString executable; - QStringList arguments; - QStringList environment; - s >> executable; - s >> arguments; - s >> environment; - setupInferior(executable, arguments, environment); - } - break; - case IPCEngineHost::RunEngine: - runEngine(); - break; - case IPCEngineHost::ShutdownInferior: - shutdownInferior(); - break; - case IPCEngineHost::ShutdownEngine: - shutdownEngine(); - break; - case IPCEngineHost::DetachDebugger: - detachDebugger(); - break; - case IPCEngineHost::ExecuteStep: - executeStep(); - break; - case IPCEngineHost::ExecuteStepOut: - executeStepOut(); - break; - case IPCEngineHost::ExecuteNext: - executeNext(); - break; - case IPCEngineHost::ExecuteStepI: - executeStepI(); - break; - case IPCEngineHost::ExecuteNextI: - executeNextI(); - break; - case IPCEngineHost::ContinueInferior: - continueInferior(); - break; - case IPCEngineHost::InterruptInferior: - interruptInferior(); - break; - case IPCEngineHost::ExecuteRunToLine: - { - QDataStream s(payload); - SET_NATIVE_BYTE_ORDER(s); - ContextData data; - s >> data.fileName; - s >> data.lineNumber; - executeRunToLine(data); - } - break; - case IPCEngineHost::ExecuteRunToFunction: - { - QDataStream s(payload); - SET_NATIVE_BYTE_ORDER(s); - QString functionName; - s >> functionName; - executeRunToFunction(functionName); - } - break; - case IPCEngineHost::ExecuteJumpToLine: - { - QDataStream s(payload); - SET_NATIVE_BYTE_ORDER(s); - ContextData data; - s >> data.fileName; - s >> data.lineNumber; - executeJumpToLine(data); - } - break; - case IPCEngineHost::ActivateFrame: - { - QDataStream s(payload); - SET_NATIVE_BYTE_ORDER(s); - quint64 id; - s >> id; - activateFrame(id); - } - break; - case IPCEngineHost::SelectThread: - { - QDataStream s(payload); - SET_NATIVE_BYTE_ORDER(s); - quint64 id; - s >> id; - selectThread(id); - } - break; - case IPCEngineHost::Disassemble: - { - QDataStream s(payload); - SET_NATIVE_BYTE_ORDER(s); - quint64 pc; - s >> pc; - disassemble(pc); - } - break; - case IPCEngineHost::AddBreakpoint: - { - QDataStream s(payload); - SET_NATIVE_BYTE_ORDER(s); - BreakpointModelId id; - BreakpointParameters d; - s >> id; - s >> d; - addBreakpoint(id, d); - } - break; - case IPCEngineHost::RemoveBreakpoint: - { - QDataStream s(payload); - SET_NATIVE_BYTE_ORDER(s); - BreakpointModelId id; - s >> id; - removeBreakpoint(id); - } - break; - case IPCEngineHost::ChangeBreakpoint: - { - QDataStream s(payload); - SET_NATIVE_BYTE_ORDER(s); - BreakpointModelId id; - BreakpointParameters d; - s >> id; - s >> d; - changeBreakpoint(id, d); - } - break; - case IPCEngineHost::RequestUpdateWatchData: - { - QDataStream s(payload); - SET_NATIVE_BYTE_ORDER(s); - WatchData data; - s >> data; - requestUpdateWatchData(data); - } - break; - case IPCEngineHost::FetchFrameSource: - { - QDataStream s(payload); - SET_NATIVE_BYTE_ORDER(s); - qint64 id; - s >> id; - fetchFrameSource(id); - } - break; - }; -} - -DebuggerState IPCEngineGuest::state() const -{ - return m_state; -} - -void IPCEngineGuest::notifyEngineSetupOk() -{ - rpcCall(NotifyEngineSetupOk); -} - -void IPCEngineGuest::notifyEngineSetupFailed() -{ - rpcCall(NotifyEngineSetupFailed); -} - -void IPCEngineGuest::notifyEngineRunFailed() -{ - rpcCall(NotifyEngineRunFailed); -} - -void IPCEngineGuest::notifyInferiorSetupOk() -{ - rpcCall(NotifyInferiorSetupOk); -} - -void IPCEngineGuest::notifyInferiorSetupFailed() -{ - rpcCall(NotifyInferiorSetupFailed); -} - -void IPCEngineGuest::notifyEngineRunAndInferiorRunOk() -{ - rpcCall(NotifyEngineRunAndInferiorRunOk); -} - -void IPCEngineGuest::notifyEngineRunAndInferiorStopOk() -{ - rpcCall(NotifyEngineRunAndInferiorStopOk); -} - -void IPCEngineGuest::notifyInferiorRunRequested() -{ - rpcCall(NotifyInferiorRunRequested); -} - -void IPCEngineGuest::notifyInferiorRunOk() -{ - rpcCall(NotifyInferiorRunOk); -} - -void IPCEngineGuest::notifyInferiorRunFailed() -{ - rpcCall(NotifyInferiorRunFailed); -} - -void IPCEngineGuest::notifyInferiorStopOk() -{ - rpcCall(NotifyInferiorStopOk); -} - -void IPCEngineGuest::notifyInferiorSpontaneousStop() -{ - rpcCall(NotifyInferiorSpontaneousStop); -} - -void IPCEngineGuest::notifyInferiorStopFailed() -{ - rpcCall(NotifyInferiorStopFailed); -} - -void IPCEngineGuest::notifyInferiorExited() -{ - rpcCall(NotifyInferiorExited); -} - -void IPCEngineGuest::notifyInferiorShutdownOk() -{ - rpcCall(NotifyInferiorShutdownOk); -} - -void IPCEngineGuest::notifyInferiorShutdownFailed() -{ - rpcCall(NotifyInferiorShutdownFailed); -} - -void IPCEngineGuest::notifyEngineSpontaneousShutdown() -{ - rpcCall(NotifyEngineSpontaneousShutdown); -} - -void IPCEngineGuest::notifyEngineShutdownOk() -{ - rpcCall(NotifyEngineShutdownOk); -} - -void IPCEngineGuest::notifyEngineShutdownFailed() -{ - rpcCall(NotifyEngineShutdownFailed); -} - -void IPCEngineGuest::notifyInferiorIll() -{ - rpcCall(NotifyInferiorIll); -} - -void IPCEngineGuest::notifyEngineIll() -{ - rpcCall(NotifyEngineIll); -} - -void IPCEngineGuest::notifyInferiorPid(qint64 pid) -{ - QByteArray p; - { - QDataStream s(&p, QIODevice::WriteOnly); - SET_NATIVE_BYTE_ORDER(s); - s << pid; - } - rpcCall(NotifyInferiorPid, p); -} - -void IPCEngineGuest::showStatusMessage(const QString &msg, quint64 timeout) -{ - QByteArray p; - { - QDataStream s(&p, QIODevice::WriteOnly); - SET_NATIVE_BYTE_ORDER(s); - s << msg; - s << (qint64)timeout; - } - rpcCall(ShowStatusMessage, p); -} - -void IPCEngineGuest::showMessage(const QString &msg, quint16 channel, quint64 timeout) -{ - QByteArray p; - { - QDataStream s(&p, QIODevice::WriteOnly); - SET_NATIVE_BYTE_ORDER(s); - s << msg; - s << (qint64)channel; - s << (qint64)timeout; - } - rpcCall(ShowMessage, p); -} - -void IPCEngineGuest::currentFrameChanged(qint64 osid) -{ - QByteArray p; - { - QDataStream s(&p, QIODevice::WriteOnly); - SET_NATIVE_BYTE_ORDER(s); - s << osid; - } - rpcCall(CurrentFrameChanged, p); -} - -void IPCEngineGuest::currentThreadChanged(qint64 osid) -{ - QByteArray p; - { - QDataStream s(&p, QIODevice::WriteOnly); - SET_NATIVE_BYTE_ORDER(s); - s << osid; - } - rpcCall(CurrentThreadChanged, p); -} - -void IPCEngineGuest::listFrames(const StackFrames &frames) -{ - QByteArray p; - { - QDataStream s(&p, QIODevice::WriteOnly); - SET_NATIVE_BYTE_ORDER(s); - s << frames; - } - rpcCall(ListFrames, p); -} - -void IPCEngineGuest::listThreads(const Threads &threads) -{ - QByteArray p; - { - QDataStream s(&p, QIODevice::WriteOnly); - SET_NATIVE_BYTE_ORDER(s); - s << threads; - } - rpcCall(ListThreads, p); -} - -void IPCEngineGuest::disassembled(quint64 pc, const DisassemblerLines &da) -{ - QByteArray p; - { - QDataStream s(&p, QIODevice::WriteOnly); - SET_NATIVE_BYTE_ORDER(s); - s << pc; - s << da; - } - rpcCall(Disassembled, p); -} - -void IPCEngineGuest::notifyAddBreakpointOk(BreakpointModelId id) -{ - QByteArray p; - { - QDataStream s(&p, QIODevice::WriteOnly); - SET_NATIVE_BYTE_ORDER(s); - s << id; - } - rpcCall(NotifyAddBreakpointOk, p); -} - -void IPCEngineGuest::notifyAddBreakpointFailed(BreakpointModelId id) -{ - QByteArray p; - { - QDataStream s(&p, QIODevice::WriteOnly); - SET_NATIVE_BYTE_ORDER(s); - s << id; - } - rpcCall(NotifyAddBreakpointFailed, p); -} - -void IPCEngineGuest::notifyRemoveBreakpointOk(BreakpointModelId id) -{ - QByteArray p; - { - QDataStream s(&p, QIODevice::WriteOnly); - SET_NATIVE_BYTE_ORDER(s); - s << id; - } - rpcCall(NotifyRemoveBreakpointOk, p); -} - -void IPCEngineGuest::notifyRemoveBreakpointFailed(BreakpointModelId id) -{ - QByteArray p; - { - QDataStream s(&p, QIODevice::WriteOnly); - SET_NATIVE_BYTE_ORDER(s); - s << id; - } - rpcCall(NotifyRemoveBreakpointFailed, p); -} - -void IPCEngineGuest::notifyChangeBreakpointOk(BreakpointModelId id) -{ - QByteArray p; - { - QDataStream s(&p, QIODevice::WriteOnly); - SET_NATIVE_BYTE_ORDER(s); - s << id; - } - rpcCall(NotifyChangeBreakpointOk, p); -} - -void IPCEngineGuest::notifyChangeBreakpointFailed(BreakpointModelId id) -{ - QByteArray p; - { - QDataStream s(&p, QIODevice::WriteOnly); - SET_NATIVE_BYTE_ORDER(s); - s << id; - } - rpcCall(NotifyChangeBreakpointFailed, p); -} - -void IPCEngineGuest::notifyBreakpointAdjusted(BreakpointModelId id, - const BreakpointParameters &bp) -{ - QByteArray p; - { - QDataStream s(&p, QIODevice::WriteOnly); - SET_NATIVE_BYTE_ORDER(s); - s << id << bp; - } - rpcCall(NotifyBreakpointAdjusted, p); -} - -void IPCEngineGuest::updateWatchData(bool fullCycle, const QList<WatchData> &wd) -{ - QByteArray p; - { - QDataStream s(&p, QIODevice::WriteOnly); - SET_NATIVE_BYTE_ORDER(s); - s << fullCycle; - s << quint64(wd.count()); - for (int i = 0; i < wd.count(); ++i) - s << wd.at(i); - } - rpcCall(UpdateWatchData, p); -} - -void IPCEngineGuest::frameSourceFetched(qint64 id, const QString &name, const QString &source) -{ - QByteArray p; - { - QDataStream s(&p, QIODevice::WriteOnly); - SET_NATIVE_BYTE_ORDER(s); - s << id; - s << name; - s << source; - } - rpcCall(FrameSourceFetched, p); -} - -} // namespace Internal -} // namespace Debugger - - diff --git a/src/plugins/debugger/lldblib/ipcengineguest.h b/src/plugins/debugger/lldblib/ipcengineguest.h deleted file mode 100644 index 75e278cfba..0000000000 --- a/src/plugins/debugger/lldblib/ipcengineguest.h +++ /dev/null @@ -1,191 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). -** 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. -** -****************************************************************************/ - -#ifndef IPCENGINEGUEST_H -#define IPCENGINEGUEST_H - -#include <debugger/breakhandler.h> -#include <debugger/debuggerengine.h> -#include <debugger/disassemblerlines.h> -#include <debugger/stackhandler.h> -#include <debugger/threadshandler.h> - -#include <QQueue> -#include <QThread> -#include <QVariant> - -namespace Debugger { -namespace Internal { - -class IPCEngineHost; -class IPCEngineGuest : public QObject -{ - Q_OBJECT - -public: - IPCEngineGuest(); - virtual ~IPCEngineGuest(); - - void setLocalHost(IPCEngineHost *); - void setHostDevice(QIODevice *); - - virtual void nuke() = 0; - virtual void setupEngine() = 0; - virtual void setupInferior(const QString &executeable, - const QStringList &arguments, const QStringList &environment) = 0; - virtual void runEngine() = 0; - virtual void shutdownInferior() = 0; - virtual void shutdownEngine() = 0; - virtual void detachDebugger() = 0; - virtual void executeStep() = 0; - virtual void executeStepOut() = 0; - virtual void executeNext() = 0; - virtual void executeStepI() = 0; - virtual void executeNextI() = 0; - virtual void continueInferior() = 0; - virtual void interruptInferior() = 0; - virtual void executeRunToLine(const ContextData &data) = 0; - virtual void executeRunToFunction(const QString &functionName) = 0; - virtual void executeJumpToLine(const ContextData &data) = 0; - virtual void activateFrame(qint64 token) = 0; - virtual void selectThread(qint64 token) = 0; - virtual void disassemble(quint64 pc) = 0; - virtual void addBreakpoint(BreakpointModelId id, const BreakpointParameters &bp) = 0; - virtual void removeBreakpoint(BreakpointModelId id) = 0; - virtual void changeBreakpoint(BreakpointModelId id, const BreakpointParameters &bp) = 0; - virtual void requestUpdateWatchData(const WatchData &data, - const WatchUpdateFlags & flags = WatchUpdateFlags()) = 0; - virtual void fetchFrameSource(qint64 frame) = 0; - - enum Function - { - NotifyEngineSetupOk = 1, - NotifyEngineSetupFailed = 2, - NotifyEngineRunFailed = 3, - NotifyInferiorSetupOk = 4, - NotifyInferiorSetupFailed = 5, - NotifyEngineRunAndInferiorRunOk = 6, - NotifyEngineRunAndInferiorStopOk = 7, - NotifyInferiorRunRequested = 8, - NotifyInferiorRunOk = 9, - NotifyInferiorRunFailed = 10, - NotifyInferiorStopOk = 11, - NotifyInferiorSpontaneousStop = 12, - NotifyInferiorStopFailed = 13, - NotifyInferiorExited = 14, - NotifyInferiorShutdownOk = 15, - NotifyInferiorShutdownFailed = 16, - NotifyEngineSpontaneousShutdown = 17, - NotifyEngineShutdownOk = 18, - NotifyEngineShutdownFailed = 19, - NotifyInferiorIll = 20, - NotifyEngineIll = 21, - NotifyInferiorPid = 22, - ShowStatusMessage = 23, - ShowMessage = 24, - CurrentFrameChanged = 25, - CurrentThreadChanged = 26, - ListFrames = 27, - ListThreads = 28, - Disassembled = 29, - NotifyAddBreakpointOk = 30, - NotifyAddBreakpointFailed = 31, - NotifyRemoveBreakpointOk = 32, - NotifyRemoveBreakpointFailed = 33, - NotifyChangeBreakpointOk = 34, - NotifyChangeBreakpointFailed = 35, - NotifyBreakpointAdjusted = 36, - UpdateWatchData = 47, - FrameSourceFetched = 48 - }; - Q_ENUMS(Function) - - DebuggerState state() const; - void notifyEngineSetupOk(); - void notifyEngineSetupFailed(); - void notifyEngineRunFailed(); - void notifyInferiorSetupOk(); - void notifyInferiorSetupFailed(); - void notifyEngineRunAndInferiorRunOk(); - void notifyEngineRunAndInferiorStopOk(); - void notifyInferiorRunRequested(); - void notifyInferiorRunOk(); - void notifyInferiorRunFailed(); - void notifyInferiorStopOk(); - void notifyInferiorSpontaneousStop(); - void notifyInferiorStopFailed(); - void notifyInferiorExited(); - void notifyInferiorShutdownOk(); - void notifyInferiorShutdownFailed(); - void notifyEngineSpontaneousShutdown(); - void notifyEngineShutdownOk(); - void notifyEngineShutdownFailed(); - void notifyInferiorIll(); - void notifyEngineIll(); - void notifyInferiorPid(qint64 pid); - void showMessage(const QString &msg, quint16 channel = LogDebug, quint64 timeout = quint64(-1)); - void showStatusMessage(const QString &msg, quint64 timeout = quint64(-1)); - - void currentFrameChanged(qint64 token); - void currentThreadChanged(qint64 token); - void listFrames(const StackFrames &); - void listThreads(const Threads &); - void disassembled(quint64 pc, const DisassemblerLines &da); - - void notifyAddBreakpointOk(BreakpointModelId id); - void notifyAddBreakpointFailed(BreakpointModelId id); - void notifyRemoveBreakpointOk(BreakpointModelId id); - void notifyRemoveBreakpointFailed(BreakpointModelId id); - void notifyChangeBreakpointOk(BreakpointModelId id); - void notifyChangeBreakpointFailed(BreakpointModelId id); - void notifyBreakpointAdjusted(BreakpointModelId id, const BreakpointParameters &bp); - - void updateWatchData(bool fullCycle, const QList<WatchData> &); - - void frameSourceFetched(qint64 frame, const QString &name, const QString &sourceCode); - - void rpcCall(Function f, QByteArray payload = QByteArray()); -public slots: - void rpcCallback(quint64 f, QByteArray payload = QByteArray()); -private slots: - void readyRead(); -private: - IPCEngineHost *m_local_host; - quint64 m_nextMessageCookie; - quint64 m_nextMessageFunction; - quint64 m_nextMessagePayloadSize; - quint64 m_cookie; - QIODevice *m_device; - DebuggerState m_state; -}; - -} // namespace Internal -} // namespace Debugger - -#endif // IPCENGINEGUEST_H diff --git a/src/plugins/debugger/lldblib/ipcenginehost.cpp b/src/plugins/debugger/lldblib/ipcenginehost.cpp deleted file mode 100644 index 3203231d16..0000000000 --- a/src/plugins/debugger/lldblib/ipcenginehost.cpp +++ /dev/null @@ -1,661 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). -** 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 "ipcenginehost.h" - -#include "ipcengineguest.h" -#include <debugger/debuggerstartparameters.h> -#include <debugger/breakhandler.h> -#include <debugger/breakpoint.h> -#include <debugger/disassemblerlines.h> -#include <debugger/moduleshandler.h> -#include <debugger/registerhandler.h> -#include <debugger/stackhandler.h> -#include <debugger/watchhandler.h> -#include <debugger/watchutils.h> -#include <debugger/threadshandler.h> -#include <debugger/disassembleragent.h> -#include <debugger/memoryagent.h> -#include <debugger/debuggerstreamops.h> -#include <debugger/debuggercore.h> - -#include <utils/qtcassert.h> - -#include <QSysInfo> -#include <QDebug> -#include <QFileInfo> -#include <QTimer> -#include <QLocalSocket> - -#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN -#define SET_NATIVE_BYTE_ORDER(x) x.setByteOrder(QDataStream::LittleEndian) -#else -#define SET_NATIVE_BYTE_ORDER(x) x.setByteOrder(QDataStream::BigEndian) -#endif - -namespace Debugger { -namespace Internal { - -IPCEngineHost::IPCEngineHost (const DebuggerStartParameters &startParameters) - : DebuggerEngine(startParameters) - , m_localGuest(0) - , m_nextMessagePayloadSize(0) - , m_cookie(1) - , m_device(0) -{ - connect(this, SIGNAL(stateChanged(Debugger::DebuggerState)), SLOT(m_stateChanged(Debugger::DebuggerState))); -} - -IPCEngineHost::~IPCEngineHost() -{ - delete m_device; -} - -void IPCEngineHost::setLocalGuest(IPCEngineGuest *guest) -{ - m_localGuest = guest; -} - -void IPCEngineHost::setGuestDevice(QIODevice *device) -{ - if (m_device) { - disconnect(m_device, SIGNAL(readyRead()), this, SLOT(readyRead())); - delete m_device; - } - m_device = device; - if (m_device) - connect(m_device, SIGNAL(readyRead()), this, SLOT(readyRead())); -} - -void IPCEngineHost::setupEngine() -{ - QTC_ASSERT(state() == EngineSetupRequested, qDebug() << state()); - rpcCall(SetupEngine); -} - -void IPCEngineHost::setupInferior() -{ - QTC_ASSERT(state() == InferiorSetupRequested, qDebug() << state()); - QByteArray p; - { - QDataStream s(&p, QIODevice::WriteOnly); - SET_NATIVE_BYTE_ORDER(s); - s << QFileInfo(startParameters().executable).absoluteFilePath(); - s << startParameters().processArgs; - s << startParameters().environment.toStringList(); - } - rpcCall(SetupInferior, p); -} - -void IPCEngineHost::runEngine() -{ - QTC_ASSERT(state() == EngineRunRequested, qDebug() << state()); - rpcCall(RunEngine); -} - -void IPCEngineHost::shutdownInferior() -{ - QTC_ASSERT(state() == InferiorShutdownRequested, qDebug() << state()); - rpcCall(ShutdownInferior); -} - -void IPCEngineHost::shutdownEngine() -{ - rpcCall(ShutdownEngine); -} - -void IPCEngineHost::detachDebugger() -{ - rpcCall(DetachDebugger); -} - -void IPCEngineHost::executeStep() -{ - rpcCall(ExecuteStep); -} - -void IPCEngineHost::executeStepOut() -{ - rpcCall(ExecuteStepOut); -} - -void IPCEngineHost::executeNext() -{ - rpcCall(ExecuteNext); -} - -void IPCEngineHost::executeStepI() -{ - rpcCall(ExecuteStepI); -} - -void IPCEngineHost::executeNextI() -{ - rpcCall(ExecuteNextI); -} - -void IPCEngineHost::continueInferior() -{ - QTC_ASSERT(state() == InferiorStopOk, qDebug() << state()); - resetLocation(); - rpcCall(ContinueInferior); -} - -void IPCEngineHost::interruptInferior() -{ - QTC_ASSERT(state() == InferiorStopRequested, qDebug() << state()); - rpcCall(InterruptInferior); -} - -void IPCEngineHost::executeRunToLine(const ContextData &data) -{ - QByteArray p; - { - QDataStream s(&p, QIODevice::WriteOnly); - SET_NATIVE_BYTE_ORDER(s); - s << data.fileName; - s << quint64(data.lineNumber); - } - rpcCall(ExecuteRunToLine, p); -} - -void IPCEngineHost::executeRunToFunction(const QString &functionName) -{ - QByteArray p; - { - QDataStream s(&p, QIODevice::WriteOnly); - SET_NATIVE_BYTE_ORDER(s); - s << functionName; - } - rpcCall(ExecuteRunToFunction, p); -} - -void IPCEngineHost::executeJumpToLine(const ContextData &data) -{ - QByteArray p; - { - QDataStream s(&p, QIODevice::WriteOnly); - SET_NATIVE_BYTE_ORDER(s); - s << data.fileName; - s << quint64(data.lineNumber); - } - rpcCall(ExecuteJumpToLine, p); -} - -void IPCEngineHost::activateFrame(int index) -{ - resetLocation(); - QByteArray p; - { - QDataStream s(&p, QIODevice::WriteOnly); - SET_NATIVE_BYTE_ORDER(s); - s << quint64(index); - } - rpcCall(ActivateFrame, p); -} - -void IPCEngineHost::selectThread(ThreadId id) -{ - resetLocation(); - QTC_ASSERT(id.isValid(), return); - QByteArray p; - { - QDataStream s(&p, QIODevice::WriteOnly); - SET_NATIVE_BYTE_ORDER(s); - s << id.raw(); - } - rpcCall(SelectThread, p); -} - -void IPCEngineHost::fetchDisassembler(DisassemblerAgent *v) -{ - quint64 address = v->location().address(); - m_frameToDisassemblerAgent.insert(address, v); - QByteArray p; - { - QDataStream s(&p, QIODevice::WriteOnly); - SET_NATIVE_BYTE_ORDER(s); - s << address; - } - rpcCall(Disassemble, p); -} - -void IPCEngineHost::insertBreakpoint(BreakpointModelId id) -{ - breakHandler()->notifyBreakpointInsertProceeding(id); - QByteArray p; - { - QDataStream s(&p, QIODevice::WriteOnly); - SET_NATIVE_BYTE_ORDER(s); - s << id; - s << breakHandler()->breakpointData(id); - } - rpcCall(AddBreakpoint, p); -} - -void IPCEngineHost::removeBreakpoint(BreakpointModelId id) -{ - breakHandler()->notifyBreakpointRemoveProceeding(id); - QByteArray p; - { - QDataStream s(&p, QIODevice::WriteOnly); - SET_NATIVE_BYTE_ORDER(s); - s << id; - } - rpcCall(RemoveBreakpoint, p); -} - -void IPCEngineHost::changeBreakpoint(BreakpointModelId id) -{ - breakHandler()->notifyBreakpointChangeProceeding(id); - QByteArray p; - { - QDataStream s(&p, QIODevice::WriteOnly); - SET_NATIVE_BYTE_ORDER(s); - s << id; - s << breakHandler()->breakpointData(id); - } - rpcCall(RemoveBreakpoint, p); -} - -void IPCEngineHost::updateWatchData(const WatchData &data, - const WatchUpdateFlags &flags) -{ - Q_UNUSED(flags); - QByteArray p; - { - QDataStream s(&p, QIODevice::WriteOnly); - SET_NATIVE_BYTE_ORDER(s); - s << data; - } - rpcCall(RequestUpdateWatchData, p); -} - -void IPCEngineHost::fetchFrameSource(qint64 id) -{ - QByteArray p; - { - QDataStream s(&p, QIODevice::WriteOnly); - SET_NATIVE_BYTE_ORDER(s); - s << id; - } - rpcCall(FetchFrameSource, p); -} - -void IPCEngineHost::rpcCallback(quint64 f, QByteArray payload) -{ - switch (f) { - default: { - showMessage(QLatin1String("IPC Error: unhandled id in guest to host call")); - const QString logMessage = tr("Fatal engine shutdown. Incompatible binary or IPC error."); - showMessage(logMessage, LogError); - showStatusMessage(logMessage); - } - nuke(); - break; - case IPCEngineGuest::NotifyEngineSetupOk: - notifyEngineSetupOk(); - break; - case IPCEngineGuest::NotifyEngineSetupFailed: - notifyEngineSetupFailed(); - break; - case IPCEngineGuest::NotifyEngineRunFailed: - notifyEngineRunFailed(); - break; - case IPCEngineGuest::NotifyInferiorSetupOk: - attemptBreakpointSynchronization(); - notifyInferiorSetupOk(); - break; - case IPCEngineGuest::NotifyInferiorSetupFailed: - notifyInferiorSetupFailed(); - break; - case IPCEngineGuest::NotifyEngineRunAndInferiorRunOk: - notifyEngineRunAndInferiorRunOk(); - break; - case IPCEngineGuest::NotifyEngineRunAndInferiorStopOk: - notifyEngineRunAndInferiorStopOk(); - break; - case IPCEngineGuest::NotifyInferiorRunRequested: - notifyInferiorRunRequested(); - break; - case IPCEngineGuest::NotifyInferiorRunOk: - notifyInferiorRunOk(); - break; - case IPCEngineGuest::NotifyInferiorRunFailed: - notifyInferiorRunFailed(); - break; - case IPCEngineGuest::NotifyInferiorStopOk: - notifyInferiorStopOk(); - break; - case IPCEngineGuest::NotifyInferiorSpontaneousStop: - notifyInferiorSpontaneousStop(); - break; - case IPCEngineGuest::NotifyInferiorStopFailed: - notifyInferiorStopFailed(); - break; - case IPCEngineGuest::NotifyInferiorExited: - notifyInferiorExited(); - break; - case IPCEngineGuest::NotifyInferiorShutdownOk: - notifyInferiorShutdownOk(); - break; - case IPCEngineGuest::NotifyInferiorShutdownFailed: - notifyInferiorShutdownFailed(); - break; - case IPCEngineGuest::NotifyEngineSpontaneousShutdown: - notifyEngineSpontaneousShutdown(); - break; - case IPCEngineGuest::NotifyEngineShutdownOk: - notifyEngineShutdownOk(); - break; - case IPCEngineGuest::NotifyEngineShutdownFailed: - notifyEngineShutdownFailed(); - break; - case IPCEngineGuest::NotifyInferiorIll: - notifyInferiorIll(); - break; - case IPCEngineGuest::NotifyEngineIll: - notifyEngineIll(); - break; - case IPCEngineGuest::NotifyInferiorPid: - { - QDataStream s(payload); - SET_NATIVE_BYTE_ORDER(s); - quint64 pid; - s >> pid; - notifyInferiorPid(pid); - } - break; - case IPCEngineGuest::ShowStatusMessage: - { - QDataStream s(payload); - SET_NATIVE_BYTE_ORDER(s); - QString msg; - qint64 timeout; - s >> msg; - s >> timeout; - showStatusMessage(msg, timeout); - } - break; - case IPCEngineGuest::ShowMessage: - { - QDataStream s(payload); - SET_NATIVE_BYTE_ORDER(s); - QString msg; - qint16 channel; - qint64 timeout; - s >> msg; - s >> channel; - s >> timeout; - showMessage(msg, channel, timeout); - } - break; - case IPCEngineGuest::CurrentFrameChanged: - { - QDataStream s(payload); - SET_NATIVE_BYTE_ORDER(s); - quint64 token; - s >> token; - - resetLocation(); - StackHandler *sh = stackHandler(); - sh->setCurrentIndex(token); - if (!sh->currentFrame().isUsable() || QFileInfo(sh->currentFrame().file).exists()) - gotoLocation(Location(sh->currentFrame(), true)); - else if (!m_sourceAgents.contains(sh->currentFrame().file)) - fetchFrameSource(token); - foreach (SourceAgent *agent, m_sourceAgents.values()) - agent->updateLocationMarker(); - } - break; - case IPCEngineGuest::CurrentThreadChanged: - { - QDataStream s(payload); - SET_NATIVE_BYTE_ORDER(s); - quint64 token; - s >> token; - threadsHandler()->setCurrentThread(ThreadId(token)); - } - break; - case IPCEngineGuest::ListFrames: - { - QDataStream s(payload); - SET_NATIVE_BYTE_ORDER(s); - StackFrames frames; - s >> frames; - stackHandler()->setFrames(frames); - } - break; - case IPCEngineGuest::ListThreads: - { - QDataStream s(payload); - SET_NATIVE_BYTE_ORDER(s); - Threads threads; - s >> threads; - threadsHandler()->setThreads(threads); - } - break; - case IPCEngineGuest::Disassembled: - { - QDataStream s(payload); - SET_NATIVE_BYTE_ORDER(s); - quint64 pc; - DisassemblerLines lines; - s >> pc; - s >> lines; - DisassemblerAgent *view = m_frameToDisassemblerAgent.take(pc); - if (view) - view->setContents(lines); - } - break; - case IPCEngineGuest::UpdateWatchData: - { - QDataStream s(payload); - SET_NATIVE_BYTE_ORDER(s); - bool fullCycle; - qint64 count; - QList<WatchData> wd; - s >> fullCycle; - s >> count; - for (qint64 i = 0; i < count; ++i) { - WatchData d; - s >> d; - wd.append(d); - } - WatchHandler *wh = watchHandler(); - if (!wh) - break; - wh->insertData(wd); - } - break; - case IPCEngineGuest::NotifyAddBreakpointOk: - { - attemptBreakpointSynchronization(); - QDataStream s(payload); - SET_NATIVE_BYTE_ORDER(s); - quint64 d; - s >> d; - BreakpointModelId id = BreakpointModelId::fromInternalId(d); - breakHandler()->notifyBreakpointInsertOk(id); - } - break; - case IPCEngineGuest::NotifyAddBreakpointFailed: - { - QDataStream s(payload); - SET_NATIVE_BYTE_ORDER(s); - quint64 d; - s >> d; - BreakpointModelId id = BreakpointModelId::fromInternalId(d); - breakHandler()->notifyBreakpointInsertFailed(id); - } - break; - case IPCEngineGuest::NotifyRemoveBreakpointOk: - { - QDataStream s(payload); - SET_NATIVE_BYTE_ORDER(s); - quint64 d; - s >> d; - BreakpointModelId id = BreakpointModelId::fromInternalId(d); - breakHandler()->notifyBreakpointRemoveOk(id); - } - break; - case IPCEngineGuest::NotifyRemoveBreakpointFailed: - { - QDataStream s(payload); - SET_NATIVE_BYTE_ORDER(s); - quint64 d; - s >> d; - BreakpointModelId id = BreakpointModelId::fromInternalId(d); - breakHandler()->notifyBreakpointRemoveFailed(id); - } - break; - case IPCEngineGuest::NotifyChangeBreakpointOk: - { - QDataStream s(payload); - SET_NATIVE_BYTE_ORDER(s); - quint64 d; - s >> d; - BreakpointModelId id = BreakpointModelId::fromInternalId(d); - breakHandler()->notifyBreakpointChangeOk(id); - } - break; - case IPCEngineGuest::NotifyChangeBreakpointFailed: - { - QDataStream s(payload); - SET_NATIVE_BYTE_ORDER(s); - quint64 d; - s >> d; - BreakpointModelId id = BreakpointModelId::fromInternalId(d); - breakHandler()->notifyBreakpointChangeFailed(id); - } - break; - case IPCEngineGuest::NotifyBreakpointAdjusted: - { - QDataStream s(payload); - SET_NATIVE_BYTE_ORDER(s); - quint64 dd; - BreakpointParameters d; - s >> dd >> d; - BreakpointModelId id = BreakpointModelId::fromInternalId(dd); - breakHandler()->notifyBreakpointAdjusted(id, d); - } - break; - case IPCEngineGuest::FrameSourceFetched: - { - QDataStream s(payload); - SET_NATIVE_BYTE_ORDER(s); - qint64 token; - QString path; - QString source; - s >> token >> path >> source; - SourceAgent *agent = new SourceAgent(this); - agent->setSourceProducerName(startParameters().connParams.host); - agent->setContent(path, source); - m_sourceAgents.insert(path, agent); - agent->updateLocationMarker(); - } - break; - } -} - -void IPCEngineHost::m_stateChanged(Debugger::DebuggerState state) -{ - QByteArray p; - { - QDataStream s(&p, QIODevice::WriteOnly); - SET_NATIVE_BYTE_ORDER(s); - s << (qint64)state; - } - rpcCall(StateChanged, p); - -} - -void IPCEngineHost::rpcCall(Function f, QByteArray payload) -{ - if (m_localGuest) { - QMetaObject::invokeMethod(m_localGuest, - "rpcCallback", - Qt::QueuedConnection, - Q_ARG(quint64, f), - Q_ARG(QByteArray, payload)); - } else if (m_device) { - QByteArray header; - { - QDataStream s(&header, QIODevice::WriteOnly); - SET_NATIVE_BYTE_ORDER(s); - s << m_cookie++; - s << (quint64) f; - s << (quint64) payload.size(); - } - m_device->write(header); - m_device->write(payload); - m_device->putChar('T'); - QLocalSocket *sock = qobject_cast<QLocalSocket *>(m_device); - if (sock) - sock->flush(); - } -} - -void IPCEngineHost::readyRead() -{ - QDataStream s(m_device); - SET_NATIVE_BYTE_ORDER(s); - if (!m_nextMessagePayloadSize) { - if (quint64(m_device->bytesAvailable ()) < 3 * sizeof(quint64)) - return; - s >> m_nextMessageCookie; - s >> m_nextMessageFunction; - s >> m_nextMessagePayloadSize; - m_nextMessagePayloadSize += 1; // Terminator and "got header" marker. - } - - quint64 ba = m_device->bytesAvailable(); - if (ba < m_nextMessagePayloadSize) - return; - - QByteArray payload = m_device->read(m_nextMessagePayloadSize - 1); - - char terminator; - m_device->getChar(&terminator); - if (terminator != 'T') { - showStatusMessage(tr("Fatal engine shutdown. Incompatible binary or IPC error.")); - showMessage(QLatin1String("IPC Error: terminator missing")); - nuke(); - return; - } - rpcCallback(m_nextMessageFunction, payload); - m_nextMessagePayloadSize = 0; - if (quint64(m_device->bytesAvailable()) >= 3 * sizeof(quint64)) - QTimer::singleShot(0, this, SLOT(readyRead())); -} - -} // namespace Internal -} // namespace Debugger - - diff --git a/src/plugins/debugger/lldblib/ipcenginehost.h b/src/plugins/debugger/lldblib/ipcenginehost.h deleted file mode 100644 index 0175c78ec6..0000000000 --- a/src/plugins/debugger/lldblib/ipcenginehost.h +++ /dev/null @@ -1,140 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). -** 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. -** -****************************************************************************/ - -#ifndef DEBUGGER_IPCENGINE_HOST_H -#define DEBUGGER_IPCENGINE_HOST_H - -#include <debugger/debuggerengine.h> -#include <debugger/threadshandler.h> -#include <debugger/stackhandler.h> -#include <debugger/breakhandler.h> -#include <debugger/sourceagent.h> - -#include <QQueue> -#include <QVariant> -#include <QThread> - -namespace Debugger { -namespace Internal { - -class IPCEngineGuest; -class IPCEngineHost : public DebuggerEngine -{ - Q_OBJECT - -public: - explicit IPCEngineHost(const DebuggerStartParameters &startParameters); - ~IPCEngineHost(); - - // use either one - void setLocalGuest(IPCEngineGuest *); - void setGuestDevice(QIODevice *); - - enum Function - { - SetupIPC = 1, - StateChanged = 2, - SetupEngine = 3, - SetupInferior = 4, - RunEngine = 5, - ShutdownInferior = 6, - ShutdownEngine = 7, - DetachDebugger = 8, - ExecuteStep = 9, - ExecuteStepOut = 10, - ExecuteNext = 11, - ExecuteStepI = 12, - ExecuteNextI = 13, - ContinueInferior = 14, - InterruptInferior = 15, - ExecuteRunToLine = 16, - ExecuteRunToFunction = 17, - ExecuteJumpToLine = 18, - ActivateFrame = 19, - SelectThread = 20, - Disassemble = 21, - AddBreakpoint = 22, - RemoveBreakpoint = 23, - ChangeBreakpoint = 24, - RequestUpdateWatchData = 25, - FetchFrameSource = 26 - }; - Q_ENUMS(Function) - - void setupEngine(); - void setupInferior(); - void runEngine(); - void shutdownInferior(); - void shutdownEngine(); - void detachDebugger(); - void executeStep(); - void executeStepOut() ; - void executeNext(); - void executeStepI(); - void executeNextI(); - void continueInferior(); - void interruptInferior(); - void executeRunToLine(const ContextData &data); - void executeRunToFunction(const QString &functionName); - void executeJumpToLine(const ContextData &data); - void activateFrame(int index); - void selectThread(ThreadId index); - void fetchDisassembler(DisassemblerAgent *); - bool acceptsBreakpoint(BreakpointModelId) const { return true; } // FIXME - void insertBreakpoint(BreakpointModelId id); - void removeBreakpoint(BreakpointModelId id); - void changeBreakpoint(BreakpointModelId id); - void updateWatchData(const WatchData &data, - const WatchUpdateFlags &flags = WatchUpdateFlags()); - void fetchFrameSource(qint64 id); - bool hasCapability(unsigned) const { return false; } - - void rpcCall(Function f, QByteArray payload = QByteArray()); -protected: - virtual void nuke() = 0; -public slots: - void rpcCallback(quint64 f, QByteArray payload = QByteArray()); -private slots: - void m_stateChanged(Debugger::DebuggerState state); - void readyRead(); -private: - IPCEngineGuest *m_localGuest; - quint64 m_nextMessageCookie; - quint64 m_nextMessageFunction; - quint64 m_nextMessagePayloadSize; - quint64 m_cookie; - QIODevice *m_device; - QHash<quint64, DisassemblerAgent *> m_frameToDisassemblerAgent; - QHash<QString, SourceAgent *> m_sourceAgents; -}; - -} // namespace Internal -} // namespace Debugger - -#endif // DEBUGGER_LLDBENGINE_H diff --git a/src/plugins/debugger/lldblib/lldbenginehost.cpp b/src/plugins/debugger/lldblib/lldbenginehost.cpp deleted file mode 100644 index 2e484abe40..0000000000 --- a/src/plugins/debugger/lldblib/lldbenginehost.cpp +++ /dev/null @@ -1,232 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). -** 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 "lldbenginehost.h" - -#include <debugger/debuggerstartparameters.h> -#include <debugger/debuggeractions.h> -#include <debugger/debuggerconstants.h> -#include <debugger/debuggerdialogs.h> -#include <debugger/debuggerplugin.h> -#include <debugger/debuggerstringutils.h> - -#include <debugger/breakhandler.h> -#include <debugger/breakpoint.h> -#include <debugger/moduleshandler.h> -#include <debugger/registerhandler.h> -#include <debugger/stackhandler.h> -#include <debugger/watchhandler.h> -#include <debugger/watchutils.h> -#include <debugger/threadshandler.h> -#include <debugger/disassembleragent.h> -#include <debugger/memoryagent.h> - -#include <coreplugin/icore.h> -#include <utils/qtcassert.h> - -#include <QDebug> -#include <QProcess> -#include <QFileInfo> -#include <QThread> -#include <QCoreApplication> - -namespace Debugger { -namespace Internal { - -SshIODevice::SshIODevice(QSsh::SshRemoteProcessRunner *r) - : runner(r) - , buckethead(0) -{ - setOpenMode(QIODevice::ReadWrite | QIODevice::Unbuffered); - connect (runner, SIGNAL(processStarted()), this, SLOT(processStarted())); - connect(runner, SIGNAL(readyReadStandardOutput()), this, SLOT(outputAvailable())); - connect(runner, SIGNAL(readyReadStandardError()), this, SLOT(errorOutputAvailable())); -} - -SshIODevice::~SshIODevice() -{ - delete runner; -} - -qint64 SshIODevice::bytesAvailable () const -{ - qint64 r = QIODevice::bytesAvailable(); - foreach (const QByteArray &bucket, buckets) - r += bucket.size(); - r-= buckethead; - return r; -} -qint64 SshIODevice::writeData (const char * data, qint64 maxSize) -{ - if (proc == 0) { - startupbuffer += QByteArray::fromRawData(data, maxSize); - return maxSize; - } - proc->write(data, maxSize); - return maxSize; -} -qint64 SshIODevice::readData (char * data, qint64 maxSize) -{ - if (proc == 0) - return 0; - qint64 size = maxSize; - while (size > 0) { - if (!buckets.size()) - return maxSize - size; - QByteArray &bucket = buckets.head(); - if ((size + buckethead) >= bucket.size()) { - int d = bucket.size() - buckethead; - memcpy(data, bucket.data() + buckethead, d); - data += d; - size -= d; - buckets.dequeue(); - buckethead = 0; - } else { - memcpy(data, bucket.data() + buckethead, size); - data += size; - buckethead += size; - size = 0; - } - } - return maxSize - size; -} - -void SshIODevice::processStarted() -{ - runner->writeDataToProcess(startupbuffer); -} - -void SshIODevice::outputAvailable() -{ - buckets.enqueue(runner->readAllStandardOutput()); - emit readyRead(); -} - -void SshIODevice::errorOutputAvailable() -{ - fprintf(stderr, "%s", runner->readAllStandardError().data()); -} - - -LldbEngineHost::LldbEngineHost(const DebuggerStartParameters &startParameters) - :IPCEngineHost(startParameters), m_ssh(0) -{ - showMessage(QLatin1String("setting up coms")); - setObjectName(QLatin1String("LLDBEngine")); - - if (startParameters.startMode == StartRemoteEngine) - { - m_guestProcess = 0; - QSsh::SshRemoteProcessRunner * const runner = new QSsh::SshRemoteProcessRunner; - connect (runner, SIGNAL(connectionError(QSsh::SshError)), - this, SLOT(sshConnectionError(QSsh::SshError))); - runner->run(startParameters.serverStartScript.toUtf8(), startParameters.connParams); - setGuestDevice(new SshIODevice(runner)); - } else { - m_guestProcess = new QProcess(this); - - connect(m_guestProcess, SIGNAL(finished(int,QProcess::ExitStatus)), - this, SLOT(finished(int,QProcess::ExitStatus))); - - connect(m_guestProcess, SIGNAL(readyReadStandardError()), this, - SLOT(stderrReady())); - - - QString a = Core::ICore::resourcePath() + QLatin1String("/qtcreator-lldb"); - if (getenv("QTC_LLDB_GUEST") != 0) - a = QString::fromLocal8Bit(getenv("QTC_LLDB_GUEST")); - - showStatusMessage(QString(QLatin1String("starting %1")).arg(a)); - - m_guestProcess->start(a, QStringList(), QIODevice::ReadWrite | QIODevice::Unbuffered); - m_guestProcess->setReadChannel(QProcess::StandardOutput); - - if (!m_guestProcess->waitForStarted()) { - showStatusMessage(tr("qtcreator-lldb failed to start: %1").arg(m_guestProcess->errorString())); - notifyEngineSpontaneousShutdown(); - return; - } - - setGuestDevice(m_guestProcess); - } -} - -LldbEngineHost::~LldbEngineHost() -{ - showMessage(QLatin1String("tear down qtcreator-lldb")); - - if (m_guestProcess) { - disconnect(m_guestProcess, SIGNAL(finished(int,QProcess::ExitStatus)), - this, SLOT(finished(int,QProcess::ExitStatus))); - - - m_guestProcess->terminate(); - m_guestProcess->kill(); - } - if (m_ssh && m_ssh->isProcessRunning()) { - // TODO: openssh doesn't do that - - m_ssh->sendSignalToProcess(QSsh::SshRemoteProcess::KillSignal); - } -} - -void LldbEngineHost::nuke() -{ - stderrReady(); - showMessage(QLatin1String("Nuke engaged. Bug in Engine/IPC or incompatible IPC versions. "), LogError); - showStatusMessage(tr("Fatal engine shutdown. Consult debugger log for details.")); - m_guestProcess->terminate(); - m_guestProcess->kill(); - notifyEngineSpontaneousShutdown(); -} -void LldbEngineHost::sshConnectionError(QSsh::SshError e) -{ - showStatusMessage(tr("SSH connection error: %1").arg(e)); -} - -void LldbEngineHost::finished(int, QProcess::ExitStatus status) -{ - showMessage(QString(QLatin1String("guest went bye bye. exit status: %1 and code: %2")) - .arg(status).arg(m_guestProcess->exitCode()), LogError); - nuke(); -} - -void LldbEngineHost::stderrReady() -{ - fprintf(stderr,"%s", m_guestProcess->readAllStandardError().data()); -} - -DebuggerEngine *createLldbLibEngine(const DebuggerStartParameters &startParameters) -{ - return new LldbEngineHost(startParameters); -} - -} // namespace Internal -} // namespace Debugger - diff --git a/src/plugins/debugger/lldblib/lldbhost.pri b/src/plugins/debugger/lldblib/lldbhost.pri deleted file mode 100644 index 0e9297d7e3..0000000000 --- a/src/plugins/debugger/lldblib/lldbhost.pri +++ /dev/null @@ -1,20 +0,0 @@ -WITH_LLDB = $$(WITH_LLDB) - -HEADERS += $$PWD/ipcenginehost.h \ - $$PWD/lldbenginehost.h - -SOURCES += $$PWD/ipcenginehost.cpp \ - $$PWD/lldbenginehost.cpp - -INCLUDEPATH+= - -FORMS += - -RESOURCES += - -!isEmpty(WITH_LLDB) { - DEFINES += WITH_LLDB - HEADERS += $$PWD/lldboptionspage.h - SOURCES += $$PWD/lldboptionspage.cpp - FORMS += $$PWD/lldboptionspagewidget.ui -} diff --git a/src/plugins/debugger/lldblib/lldboptionspage.cpp b/src/plugins/debugger/lldblib/lldboptionspage.cpp deleted file mode 100644 index 14d414d128..0000000000 --- a/src/plugins/debugger/lldblib/lldboptionspage.cpp +++ /dev/null @@ -1,109 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). -** 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 "lldboptionspage.h" -#include <debugger/debuggerconstants.h> -#include <debugger/debuggerinternalconstants.h> - -#include <coreplugin/icore.h> - -#include <QCoreApplication> -#include <QUrl> -#include <QTextStream> -#include <QMessageBox> -#include <QDesktopServices> - -namespace Debugger { -namespace Internal { - -LldbOptionsPageWidget::LldbOptionsPageWidget(QWidget *parent, QSettings *s_) - : QWidget(parent) - , s(s_) -{ - m_ui.setupUi(this); - load(); -} - -void LldbOptionsPageWidget::save() -{ - s->beginGroup(QLatin1String("LLDB")); - s->setValue(QLatin1String("enabled"), m_ui.enableLldb->isChecked ()); - s->setValue(QLatin1String("gdbEmu"), m_ui.gdbEmu->isChecked ()); - s->endGroup(); -} - -void LldbOptionsPageWidget::load() -{ - s->beginGroup(QLatin1String("LLDB")); - m_ui.enableLldb->setChecked(s->value(QLatin1String("enabled"), false).toBool()); - m_ui.gdbEmu->setChecked(s->value(QLatin1String("gdbEmu"), true).toBool()); - s->endGroup(); -} - -// ---------- LldbOptionsPage -LldbOptionsPage::LldbOptionsPage() -{ - // m_options->fromSettings(Core::ICore::settings()); - setId("F.Lldb"); - setDisplayName(tr("LLDB")); - setCategory(Debugger::Constants::DEBUGGER_SETTINGS_CATEGORY); - setDisplayCategory(QCoreApplication::translate("Debugger", Constants::DEBUGGER_SETTINGS_TR_CATEGORY)); - setCategoryIcon(QLatin1String(Constants::DEBUGGER_COMMON_SETTINGS_CATEGORY_ICON)); -} - -QWidget *LldbOptionsPage::createPage(QWidget *parent) -{ - m_widget = new LldbOptionsPageWidget(parent, Core::ICore::settings()); - return m_widget; -} - -void LldbOptionsPage::apply() -{ - if (!m_widget) - return; - m_widget->save(); -} - -void LldbOptionsPage::finish() -{ -} - -bool LldbOptionsPage::matches(const QString &s) const -{ - return QString(s.toLower()).contains(QLatin1String("lldb")); -} - -void addLldbOptionPages(QList<Core::IOptionsPage *> *opts) -{ - opts->push_back(new LldbOptionsPage); -} - - -} // namespace Internal -} // namespace Debugger diff --git a/src/plugins/debugger/lldblib/lldboptionspagewidget.ui b/src/plugins/debugger/lldblib/lldboptionspagewidget.ui deleted file mode 100644 index 78e77699f0..0000000000 --- a/src/plugins/debugger/lldblib/lldboptionspagewidget.ui +++ /dev/null @@ -1,59 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<ui version="4.0"> - <class>Debugger::Internal::LldbOptionsPageWidget</class> - <widget class="QWidget" name="Debugger::Internal::LldbOptionsPageWidget"> - <property name="geometry"> - <rect> - <x>0</x> - <y>0</y> - <width>522</width> - <height>512</height> - </rect> - </property> - <layout class="QVBoxLayout" name="verticalLayout"> - <item> - <layout class="QHBoxLayout" name="horizontalLayout"/> - </item> - <item> - <widget class="QGroupBox" name="enableLldb"> - <property name="title"> - <string>Enable LLDB</string> - </property> - <property name="checkable"> - <bool>true</bool> - </property> - <property name="checked"> - <bool>false</bool> - </property> - <layout class="QVBoxLayout" name="verticalLayout_2"> - <item> - <widget class="QCheckBox" name="gdbEmu"> - <property name="text"> - <string>Use GDB Python dumpers</string> - </property> - <property name="checked"> - <bool>false</bool> - </property> - </widget> - </item> - </layout> - </widget> - </item> - <item> - <spacer name="verticalSpacer"> - <property name="orientation"> - <enum>Qt::Vertical</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>20</width> - <height>203</height> - </size> - </property> - </spacer> - </item> - </layout> - </widget> - <resources/> - <connections/> -</ui> diff --git a/src/plugins/debugger/loadcoredialog.cpp b/src/plugins/debugger/loadcoredialog.cpp index a8303c7006..63408b7505 100644 --- a/src/plugins/debugger/loadcoredialog.cpp +++ b/src/plugins/debugger/loadcoredialog.cpp @@ -236,14 +236,17 @@ AttachCoreDialog::AttachCoreDialog(QWidget *parent) d->forceLocalLabel->setBuddy(d->forceLocalCheckBox); d->localExecFileName = new PathChooser(this); + d->localExecFileName->setHistoryCompleter(QLatin1String("LocalExecutable")); d->localExecFileName->setExpectedKind(PathChooser::File); d->localExecFileName->setPromptDialogTitle(tr("Select Executable")); d->localCoreFileName = new PathChooser(this); + d->localCoreFileName->setHistoryCompleter(QLatin1String("Debugger.CoreFile.History")); d->localCoreFileName->setExpectedKind(PathChooser::File); d->localCoreFileName->setPromptDialogTitle(tr("Select Core File")); d->overrideStartScriptFileName = new PathChooser(this); + d->overrideStartScriptFileName->setHistoryCompleter(QLatin1String("Debugger.StartupScript.History")); d->overrideStartScriptFileName->setExpectedKind(PathChooser::File); d->overrideStartScriptFileName->setPromptDialogTitle(tr("Select Startup Script")); diff --git a/src/plugins/debugger/localsandexpressionsoptionspage.ui b/src/plugins/debugger/localsandexpressionsoptionspage.ui index ce60b3dfbd..d2b320ddaa 100644 --- a/src/plugins/debugger/localsandexpressionsoptionspage.ui +++ b/src/plugins/debugger/localsandexpressionsoptionspage.ui @@ -60,7 +60,7 @@ <item> <widget class="QCheckBox" name="checkBoxShowStdNamespace"> <property name="toolTip"> - <string>Show 'std::' prefix for types from the standard library.</string> + <string>Shows 'std::' prefix for types from the standard library.</string> </property> <property name="text"> <string>Show "std::" namespace for types</string> @@ -70,7 +70,7 @@ <item> <widget class="QCheckBox" name="checkBoxShowQtNamespace"> <property name="toolTip"> - <string>Show Qt namespace prefix for Qt types. This is only relevant if Qt was configured with '-qtnamespace'.</string> + <string>Shows Qt namespace prefix for Qt types. This is only relevant if Qt was configured with '-qtnamespace'.</string> </property> <property name="text"> <string>Show Qt's namespace for types</string> diff --git a/src/plugins/debugger/logwindow.cpp b/src/plugins/debugger/logwindow.cpp index e9859924de..6ad66dbbdd 100644 --- a/src/plugins/debugger/logwindow.cpp +++ b/src/plugins/debugger/logwindow.cpp @@ -433,27 +433,22 @@ LogWindow::LogWindow(QWidget *parent) void LogWindow::executeLine() { m_ignoreNextInputEcho = true; - debuggerCore()->executeDebuggerCommand(m_inputText->textCursor().block().text(), - CppLanguage); + debuggerCore()->currentEngine()-> + executeDebuggerCommand(m_inputText->textCursor().block().text(), CppLanguage); } void LogWindow::repeatLastCommand() { - QTextCursor tc = m_inputText->textCursor(); - QRegExp re = QRegExp(QLatin1String("^\\d+(bb options:)(.*)$")); - for (QTextBlock block = tc.block(); block.isValid(); block = block.previous()) { - QString line = block.text(); - if (re.exactMatch(line)) { - QString cmd = re.cap(1) + QLatin1String("pe,") + re.cap(2); - debuggerCore()->executeDebuggerCommand(cmd, CppLanguage); - return; - } - } + debuggerCore()->currentEngine()->debugLastCommand(); } void LogWindow::sendCommand() { - debuggerCore()->executeDebuggerCommand(m_commandEdit->text(), CppLanguage); + DebuggerEngine *engine = debuggerCore()->currentEngine(); + if (engine->acceptsDebuggerCommands()) + engine->executeDebuggerCommand(m_commandEdit->text(), CppLanguage); + else + showOutput(LogError, tr("User commands are not accepted in the current state.")); } void LogWindow::showOutput(int channel, const QString &output) diff --git a/src/plugins/debugger/qml/qmllivetextpreview.cpp b/src/plugins/debugger/qml/qmllivetextpreview.cpp index f01a04738c..ec82d9c280 100644 --- a/src/plugins/debugger/qml/qmllivetextpreview.cpp +++ b/src/plugins/debugger/qml/qmllivetextpreview.cpp @@ -713,7 +713,7 @@ void QmlLiveTextPreview::showSyncWarning( foreach (TextEditor::BaseTextEditorWidget *editor, m_editors) { if (editor) { - Core::InfoBar *infoBar = editor->editorDocument()->infoBar(); + Core::InfoBar *infoBar = editor->baseTextDocument()->infoBar(); Core::InfoBarEntry info(Core::Id(INFO_OUT_OF_SYNC), errorMessage); BaseToolsClient *toolsClient = m_inspectorAdapter->toolsClient(); if (toolsClient && toolsClient->supportReload()) @@ -734,7 +734,7 @@ void QmlLiveTextPreview::removeOutofSyncInfo() { foreach (TextEditor::BaseTextEditorWidget *editor, m_editors) { if (editor) { - Core::InfoBar *infoBar = editor->editorDocument()->infoBar(); + Core::InfoBar *infoBar = editor->baseTextDocument()->infoBar(); infoBar->removeInfo(Core::Id(INFO_OUT_OF_SYNC)); } } diff --git a/src/plugins/debugger/shared/cdbsymbolpathlisteditor.cpp b/src/plugins/debugger/shared/cdbsymbolpathlisteditor.cpp index e687762358..29874ab35f 100644 --- a/src/plugins/debugger/shared/cdbsymbolpathlisteditor.cpp +++ b/src/plugins/debugger/shared/cdbsymbolpathlisteditor.cpp @@ -56,6 +56,7 @@ CacheDirectoryDialog::CacheDirectoryDialog(QWidget *parent) : QFormLayout *formLayout = new QFormLayout; m_chooser->setExpectedKind(Utils::PathChooser::ExistingDirectory); + m_chooser->setHistoryCompleter(QLatin1String("Debugger.CdbCacheDir.History")); m_chooser->setMinimumWidth(400); formLayout->addRow(tr("Path:"), m_chooser); diff --git a/src/plugins/debugger/sourceagent.cpp b/src/plugins/debugger/sourceagent.cpp index ce63960d37..ddcd76e132 100644 --- a/src/plugins/debugger/sourceagent.cpp +++ b/src/plugins/debugger/sourceagent.cpp @@ -137,7 +137,7 @@ void SourceAgent::updateLocationMarker() QTC_ASSERT(d->editor, return); if (d->locationMark) - d->editor->markableInterface()->removeMark(d->locationMark); + d->editor->textDocument()->markableInterface()->removeMark(d->locationMark); delete d->locationMark; d->locationMark = 0; if (d->engine->stackHandler()->currentFrame().file == d->path) { @@ -145,7 +145,7 @@ void SourceAgent::updateLocationMarker() d->locationMark = new TextEditor::ITextMark(lineNumber); d->locationMark->setIcon(debuggerCore()->locationMarkIcon()); d->locationMark->setPriority(TextEditor::ITextMark::HighPriority); - d->editor->markableInterface()->addMark(d->locationMark); + d->editor->textDocument()->markableInterface()->addMark(d->locationMark); QPlainTextEdit *plainTextEdit = qobject_cast<QPlainTextEdit *>(d->editor->widget()); QTC_ASSERT(plainTextEdit, return); QTextCursor tc = plainTextEdit->textCursor(); diff --git a/src/plugins/debugger/watchhandler.cpp b/src/plugins/debugger/watchhandler.cpp index 240933dc08..677dd1669a 100644 --- a/src/plugins/debugger/watchhandler.cpp +++ b/src/plugins/debugger/watchhandler.cpp @@ -982,14 +982,6 @@ QString WatchModel::displayType(const WatchData &data) const return result; } -QString WatchModel::displayForAutoTest(const QByteArray &iname) const -{ - WatchItem *item = findItem(iname); - if (item) - return displayValue(*item) + QLatin1Char(' ') + displayType(*item); - return QString(); -} - QVariant WatchModel::data(const QModelIndex &idx, int role) const { if (!idx.isValid()) @@ -1230,8 +1222,10 @@ QStringList WatchModel::typeFormatList(const WatchData &data) const << tr("Local 8bit string") << tr("UTF16 string") << tr("UCS4 string") - << tr("Array of 10 items") - << tr("Array of 1000 items"); + << tr("Array of %1 items").arg(10) + << tr("Array of %1 items").arg(100) + << tr("Array of %1 items").arg(1000) + << tr("Array of %1 items").arg(10000); if (data.type.contains("char[") || data.type.contains("char [")) return QStringList() << tr("Latin1 string") @@ -1815,21 +1809,28 @@ void WatchHandler::saveWatchers() DebuggerCore::setSessionValue("Watchers", watchedExpressions()); } -void WatchHandler::loadTypeFormats() +void WatchHandler::loadFormats() { QVariant value = DebuggerCore::sessionValue("DefaultFormats"); - QMap<QString, QVariant> typeFormats = value.toMap(); - QMapIterator<QString, QVariant> it(typeFormats); + QMapIterator<QString, QVariant> it(value.toMap()); while (it.hasNext()) { it.next(); if (!it.key().isEmpty()) theTypeFormats.insert(it.key().toUtf8(), it.value().toInt()); } + + value = DebuggerCore::sessionValue("IndividualFormats"); + it = QMapIterator<QString, QVariant>(value.toMap()); + while (it.hasNext()) { + it.next(); + if (!it.key().isEmpty()) + theIndividualFormats.insert(it.key().toUtf8(), it.value().toInt()); + } } -void WatchHandler::saveTypeFormats() +void WatchHandler::saveFormats() { - QMap<QString, QVariant> typeFormats; + QMap<QString, QVariant> formats; QHashIterator<QByteArray, int> it(theTypeFormats); while (it.hasNext()) { it.next(); @@ -1837,21 +1838,32 @@ void WatchHandler::saveTypeFormats() if (format != DecimalFormat) { const QByteArray key = it.key().trimmed(); if (!key.isEmpty()) - typeFormats.insert(QLatin1String(key), format); + formats.insert(QString::fromLatin1(key), format); } } - DebuggerCore::setSessionValue("DefaultFormats", typeFormats); + DebuggerCore::setSessionValue("DefaultFormats", formats); + + formats.clear(); + it = QHashIterator<QByteArray, int>(theIndividualFormats); + while (it.hasNext()) { + it.next(); + const int format = it.value(); + const QByteArray key = it.key().trimmed(); + if (!key.isEmpty()) + formats.insert(QString::fromLatin1(key), format); + } + DebuggerCore::setSessionValue("IndividualFormats", formats); } void WatchHandler::saveSessionData() { saveWatchers(); - saveTypeFormats(); + saveFormats(); } void WatchHandler::loadSessionData() { - loadTypeFormats(); + loadFormats(); theWatcherNames.clear(); m_watcherCounter = 0; QVariant value = DebuggerCore::sessionValue("Watchers"); @@ -1860,20 +1872,6 @@ void WatchHandler::loadSessionData() watchExpression(exp); } -void WatchHandler::updateWatchers() -{ - m_model->destroyChildren(m_model->m_watchRoot); - // Copy over all watchers and mark all watchers as incomplete. - foreach (const QByteArray &exp, theWatcherNames.keys()) { - WatchData data; - data.iname = watcherName(exp); - data.setAllNeeded(); - data.name = QLatin1String(exp); - data.exp = exp; - insertIncompleteData(data); - } -} - QAbstractItemModel *WatchHandler::model() const { return m_model; @@ -1908,11 +1906,6 @@ const WatchData *WatchHandler::findCppLocalVariable(const QString &name) const return 0; } -QString WatchHandler::displayForAutoTest(const QByteArray &iname) const -{ - return m_model->displayForAutoTest(iname); -} - bool WatchHandler::hasItem(const QByteArray &iname) const { return m_model->findItem(iname); @@ -1925,7 +1918,7 @@ void WatchHandler::setFormat(const QByteArray &type0, int format) theTypeFormats.remove(type); else theTypeFormats[type] = format; - saveTypeFormats(); + saveFormats(); m_model->emitDataChanged(1); } @@ -2002,11 +1995,6 @@ QString WatchHandler::editorContents() return contents; } -void WatchHandler::removeTooltip() -{ - m_model->destroyChildren(m_model->m_tooltipRoot); -} - void WatchHandler::setTypeFormats(const TypeFormats &typeFormats) { m_model->m_reportedTypeFormats = typeFormats; diff --git a/src/plugins/debugger/watchhandler.h b/src/plugins/debugger/watchhandler.h index 73d8951415..0e610c40f3 100644 --- a/src/plugins/debugger/watchhandler.h +++ b/src/plugins/debugger/watchhandler.h @@ -80,20 +80,16 @@ public: void watchVariable(const QString &exp); Q_SLOT void clearWatches(); - void updateWatchers(); // Called after locals are fetched - void showEditValue(const WatchData &data); const WatchData *watchData(const QModelIndex &) const; const QModelIndex watchDataIndex(const QByteArray &iname) const; const WatchData *findData(const QByteArray &iname) const; const WatchData *findCppLocalVariable(const QString &name) const; - QString displayForAutoTest(const QByteArray &iname) const; bool hasItem(const QByteArray &iname) const; void loadSessionData(); void saveSessionData(); - void removeTooltip(); bool isExpandedIName(const QByteArray &iname) const; QSet<QByteArray> expandedINames() const; @@ -115,7 +111,6 @@ public: static int unprintableBase(); QByteArray watcherName(const QByteArray &exp); - void synchronizeWatchers(); QString editorContents(); void editTypeFormats(bool includeLocals, const QByteArray &iname); @@ -141,8 +136,8 @@ private: friend class WatchModel; void saveWatchers(); - static void loadTypeFormats(); - static void saveTypeFormats(); + static void loadFormats(); + static void saveFormats(); void setFormat(const QByteArray &type, int format); diff --git a/src/plugins/designer/cpp/cppsettingspage.cpp b/src/plugins/designer/cpp/cppsettingspage.cpp index 1355262f25..f3ddc32379 100644 --- a/src/plugins/designer/cpp/cppsettingspage.cpp +++ b/src/plugins/designer/cpp/cppsettingspage.cpp @@ -89,18 +89,6 @@ void CppSettingsPageWidget::setUiEmbedding(int v) } } -QString CppSettingsPageWidget::searchKeywords() const -{ - QString rc; - QTextStream(&rc) << m_ui.ptrAggregationRadioButton->text() - << ' ' << m_ui.aggregationButton->text() - << ' ' << m_ui.multipleInheritanceButton->text() - << ' ' << m_ui.retranslateCheckBox->text() - << ' ' << m_ui.includeQtModuleCheckBox->text(); - rc.remove(QLatin1Char('&')); - return rc; -} - // ---------- CppSettingsPage CppSettingsPage::CppSettingsPage(QObject *parent) : Core::IOptionsPage(parent) { @@ -112,12 +100,12 @@ CppSettingsPage::CppSettingsPage(QObject *parent) : Core::IOptionsPage(parent) setCategoryIcon(QLatin1String(Designer::Constants::SETTINGS_CATEGORY_ICON)); } -QWidget *CppSettingsPage::createPage(QWidget *parent) +QWidget *CppSettingsPage::widget() { - m_widget = new CppSettingsPageWidget(parent); - m_widget->setParameters(m_parameters); - if (m_searchKeywords.isEmpty()) - m_searchKeywords = m_widget->searchKeywords(); + if (!m_widget) { + m_widget = new CppSettingsPageWidget; + m_widget->setParameters(m_parameters); + } return m_widget; } @@ -134,11 +122,7 @@ void CppSettingsPage::apply() void CppSettingsPage::finish() { -} - -bool CppSettingsPage::matches(const QString &s) const -{ - return m_searchKeywords.contains(s, Qt::CaseInsensitive); + delete m_widget; } } // namespace Internal diff --git a/src/plugins/designer/cpp/cppsettingspage.h b/src/plugins/designer/cpp/cppsettingspage.h index 0594ec1124..644c6da33c 100644 --- a/src/plugins/designer/cpp/cppsettingspage.h +++ b/src/plugins/designer/cpp/cppsettingspage.h @@ -49,8 +49,6 @@ public: FormClassWizardGenerationParameters parameters() const; void setParameters(const FormClassWizardGenerationParameters &p); - QString searchKeywords() const; - private: int uiEmbedding() const; void setUiEmbedding(int); @@ -63,15 +61,13 @@ class CppSettingsPage : public Core::IOptionsPage public: explicit CppSettingsPage(QObject *parent = 0); - QWidget *createPage(QWidget *parent); + QWidget *widget(); void apply(); void finish(); - bool matches(const QString &s) const; private: QPointer<CppSettingsPageWidget> m_widget; FormClassWizardGenerationParameters m_parameters; - QString m_searchKeywords; }; } // namespace Internal diff --git a/src/plugins/designer/formwindoweditor.h b/src/plugins/designer/formwindoweditor.h index 2b71f08b94..7f84e71be0 100644 --- a/src/plugins/designer/formwindoweditor.h +++ b/src/plugins/designer/formwindoweditor.h @@ -39,7 +39,6 @@ class QDesignerFormWindowInterface; QT_END_NAMESPACE namespace TextEditor { - class BaseTextDocument; class PlainTextEditor; } diff --git a/src/plugins/designer/gotoslot_test.cpp b/src/plugins/designer/gotoslot_test.cpp index b8b8f0a58b..1adafcd638 100644 --- a/src/plugins/designer/gotoslot_test.cpp +++ b/src/plugins/designer/gotoslot_test.cpp @@ -34,10 +34,11 @@ #else #include "formeditorw.h" -#include <coreplugin/testdatadir.h> #include <coreplugin/editormanager/editormanager.h> +#include <coreplugin/testdatadir.h> #include <cpptools/cppmodelmanager.h> #include <cpptools/cpptoolseditorsupport.h> +#include <cpptools/cpptoolstestcase.h> #include <cplusplus/CppDocument.h> #include <cplusplus/Overview.h> @@ -49,7 +50,7 @@ #include <QtTest> using namespace Core; -using namespace Core::Internal::Tests; +using namespace Core::Tests; using namespace CppTools; using namespace CPlusPlus; using namespace Designer; @@ -57,28 +58,7 @@ using namespace Designer::Internal; namespace { -class MyTestDataDir : public Core::Internal::Tests::TestDataDir { -public: - MyTestDataDir() - : TestDataDir(QString()) - {} - MyTestDataDir(const QString &dir) - : TestDataDir(QLatin1String(SRCDIR "/../../../tests/designer/") + dir) - {} -}; - -QString expectedContentsForFile(const QString &filePath) -{ - QFileInfo fi(filePath); - const QString referenceFileName = QLatin1String("reference_") + fi.fileName(); - const QString referenceFilePath = fi.dir().absoluteFilePath(referenceFileName); - - Utils::FileReader fileReader; - const bool isFetchOk = fileReader.fetch(referenceFilePath); - if (isFetchOk) - return QString::fromUtf8(fileReader.data()); - return QString(); -} +QTC_DECLARE_MYTESTDATADIR("../../../tests/designer/") class DocumentContainsFunctionDefinition: protected SymbolVisitor { @@ -170,38 +150,30 @@ bool documentContainsMemberFunctionDeclaration(const Document::Ptr &document, return DocumentContainsDeclaration()(document->globalNamespace(), declaration); } -class GoToSlotTest +class GoToSlotTestCase : public CppTools::Tests::TestCase { public: - GoToSlotTest(const QStringList &files) - : m_files(files) - , m_modelManager(CppModelManagerInterface::instance()) + GoToSlotTestCase(const QStringList &files) { + QVERIFY(succeededSoFar()); QCOMPARE(files.size(), 3); - cleanup(); - } - ~GoToSlotTest() { cleanup(); } - void run() const - { QList<TextEditor::BaseTextEditor *> editors; - foreach (const QString &file, m_files) { + foreach (const QString &file, files) { IEditor *editor = EditorManager::openEditor(file); TextEditor::BaseTextEditor *e = qobject_cast<TextEditor::BaseTextEditor *>(editor); QVERIFY(e); + closeEditorAtEndOfTestCase(editor); editors << e; } TextEditor::BaseTextEditor *cppFileEditor = editors.at(0); TextEditor::BaseTextEditor *hFileEditor = editors.at(1); - const QString cppFile = m_files.at(0); - const QString hFile = m_files.at(1); + const QString cppFile = files.at(0); + const QString hFile = files.at(1); - QCOMPARE(EditorManager::documentModel()->openedDocuments().size(), m_files.size()); - while (!m_modelManager->snapshot().contains(cppFile) - || !m_modelManager->snapshot().contains(hFile)) { - QApplication::processEvents(); - } + QCOMPARE(EditorManager::documentModel()->openedDocuments().size(), files.size()); + waitForFilesInGlobalSnapshot(QStringList() << cppFile << hFile); // Execute "Go To Slot" FormEditorW *few = FormEditorW::instance(); @@ -231,20 +203,6 @@ public: QVERIFY(documentContainsMemberFunctionDeclaration(hDocument, QLatin1String("Form::on_pushButton_clicked"))); } - -private: - void cleanup() - { - EditorManager::closeAllEditors(/*askAboutModifiedEditors =*/ false); - QVERIFY(EditorManager::documentModel()->openedDocuments().isEmpty()); - - m_modelManager->GC(); - QVERIFY(m_modelManager->snapshot().isEmpty()); - } - -private: - QStringList m_files; - CppModelManagerInterface *m_modelManager; }; } // anonymous namespace @@ -256,9 +214,7 @@ void Designer::Internal::FormEditorPlugin::test_gotoslot() { #if QT_VERSION >= 0x050000 QFETCH(QStringList, files); - - GoToSlotTest test(files); - test.run(); + (GoToSlotTestCase(files)); #else QSKIP("Available only with >= Qt5", SkipSingle); #endif diff --git a/src/plugins/designer/qtcreatorintegration.cpp b/src/plugins/designer/qtcreatorintegration.cpp index be0ad306d3..8385a77bd2 100644 --- a/src/plugins/designer/qtcreatorintegration.cpp +++ b/src/plugins/designer/qtcreatorintegration.cpp @@ -350,7 +350,7 @@ static Document::Ptr addDefinition(const Snapshot &docTable, //! \todo use the InsertionPointLocator to insert at the correct place. // (we'll have to extend that class first to do definition insertions) - const QString contents = editable->textDocument()->contents(); + const QString contents = editable->textDocument()->plainText(); int column; editable->convertPosition(contents.length(), line, &column); editable->gotoLine(*line, column); diff --git a/src/plugins/designer/settingspage.cpp b/src/plugins/designer/settingspage.cpp index 8892f8ada5..b513027a44 100644 --- a/src/plugins/designer/settingspage.cpp +++ b/src/plugins/designer/settingspage.cpp @@ -51,10 +51,13 @@ SettingsPage::SettingsPage(QDesignerOptionsPageInterface *designerPage) : setCategoryIcon(QLatin1String(Designer::Constants::SETTINGS_CATEGORY_ICON)); } -QWidget *SettingsPage::createPage(QWidget *parent) +QWidget *SettingsPage::widget() { m_initialized = true; - return m_designerPage->createPage(parent); + if (!m_widget) + m_widget = m_designerPage->createPage(0); + return m_widget; + } void SettingsPage::apply() @@ -67,6 +70,7 @@ void SettingsPage::finish() { if (m_initialized) m_designerPage->finish(); + delete m_widget; } SettingsPageProvider::SettingsPageProvider(QObject *parent) @@ -87,3 +91,43 @@ QList<Core::IOptionsPage *> SettingsPageProvider::pages() const } return FormEditorW::instance()->optionsPages(); } + +bool SettingsPageProvider::matches(const QString &searchKeyWord) const +{ + // to avoid fully initializing designer when typing something in the options' filter edit + // we hardcode matching of UI text from the designer pages, which are taken if the designer pages + // were not yet loaded + // luckily linguist cannot resolve the translated texts, so we do not end up with duplicate + // translatable strings for Qt Creator + static const struct { const char *context; const char *value; } uitext[] = { + {"EmbeddedOptionsPage", "Embedded Design"}, + {"EmbeddedOptionsPage", "Device Profiles"}, + {"FormEditorOptionsPage", "Forms"}, + {"FormEditorOptionsPage", "Preview Zoom"}, + {"FormEditorOptionsPage", "Default Zoom"}, + {"FormEditorOptionsPage", "Default Grid"}, + {"qdesigner_internal::GridPanel", "Visible"}, + {"qdesigner_internal::GridPanel", "Snap"}, + {"qdesigner_internal::GridPanel", "Reset"}, + {"qdesigner_internal::GridPanel", "Grid"}, + {"qdesigner_internal::GridPanel", "Grid &X"}, + {"qdesigner_internal::GridPanel", "Grid &Y"}, + {"PreviewConfigurationWidget", "Print/Preview Configuration"}, + {"PreviewConfigurationWidget", "Style"}, + {"PreviewConfigurationWidget", "Style sheet"}, + {"PreviewConfigurationWidget", "Device skin"}, + {"TemplateOptionsPage", "Template Paths"}, + {"qdesigner_internal::TemplateOptionsWidget", "Additional Template Paths"} + }; + static const size_t itemCount = sizeof(uitext)/sizeof(uitext[0]); + if (m_keywords.isEmpty()) { + m_keywords.reserve(itemCount); + for (size_t i = 0; i < itemCount; ++i) + m_keywords << QCoreApplication::translate(uitext[i].context, uitext[i].value).remove(QLatin1Char('&')); + } + foreach (const QString &key, m_keywords) { + if (key.contains(searchKeyWord, Qt::CaseInsensitive)) + return true; + } + return false; +} diff --git a/src/plugins/designer/settingspage.h b/src/plugins/designer/settingspage.h index 0e6a9fcd3f..9aeac63426 100644 --- a/src/plugins/designer/settingspage.h +++ b/src/plugins/designer/settingspage.h @@ -32,6 +32,8 @@ #include <coreplugin/dialogs/ioptionspage.h> +#include <QPointer> + QT_BEGIN_NAMESPACE class QDesignerOptionsPageInterface; QT_END_NAMESPACE @@ -48,13 +50,14 @@ class SettingsPage : public Core::IOptionsPage public: explicit SettingsPage(QDesignerOptionsPageInterface *designerPage); - QWidget *createPage(QWidget *parent); + QWidget *widget(); void apply(); void finish(); private: QDesignerOptionsPageInterface *m_designerPage; bool m_initialized; + QPointer<QWidget> m_widget; }; class SettingsPageProvider : public Core::IOptionsPageProvider @@ -65,9 +68,11 @@ public: SettingsPageProvider(QObject *parent = 0); QList<Core::IOptionsPage *> pages() const; + bool matches(const QString &searchKeyWord) const; private: mutable bool m_initialized; + mutable QStringList m_keywords; }; } // namespace Internal diff --git a/src/plugins/diffeditor/diffeditorwidget.cpp b/src/plugins/diffeditor/diffeditorwidget.cpp index 9a4247d0f1..18284e8414 100644 --- a/src/plugins/diffeditor/diffeditorwidget.cpp +++ b/src/plugins/diffeditor/diffeditorwidget.cpp @@ -888,16 +888,6 @@ QTextCodec *DiffEditorWidget::codec() const return const_cast<QTextCodec *>(m_leftEditor->codec()); } -QString DiffEditorWidget::source() const -{ - return m_source; -} - -void DiffEditorWidget::setSource(const QString &source) -{ - m_source = source; -} - BaseTextEditorWidget *DiffEditorWidget::leftEditor() const { return m_leftEditor; diff --git a/src/plugins/diffeditor/diffeditorwidget.h b/src/plugins/diffeditor/diffeditorwidget.h index b819d4b978..97c131dccb 100644 --- a/src/plugins/diffeditor/diffeditorwidget.h +++ b/src/plugins/diffeditor/diffeditorwidget.h @@ -58,7 +58,6 @@ struct FileData; class DIFFEDITOR_EXPORT DiffEditorWidget : public QWidget { - Q_PROPERTY(QString source READ source WRITE setSource) Q_OBJECT public: struct DiffFileInfo { @@ -84,9 +83,6 @@ public: void setDiff(const QList<DiffFilesContents> &diffFileList, const QString &workingDirectory = QString()); QTextCodec *codec() const; - QString source() const; - void setSource(const QString &source); - #ifdef WITH_TESTS void testAssemblyRows(); #endif // WITH_TESTS diff --git a/src/plugins/fakevim/fakevim_test.cpp b/src/plugins/fakevim/fakevim_test.cpp index eef9ab0874..037eb3ef27 100644 --- a/src/plugins/fakevim/fakevim_test.cpp +++ b/src/plugins/fakevim/fakevim_test.cpp @@ -1902,6 +1902,19 @@ void FakeVimPlugin::test_vim_letter_case() KEYS("2gUU", " " X "ABC" N "DEF"); KEYS("u", " " X "abc" N "def"); KEYS("<c-r>", " " X "ABC" N "DEF"); + + // undo, redo and dot command + data.setText(" abcde" N " fgh" N " ijk"); + KEYS("3l" "<C-V>2l2j" "U", " a" X "BCDe" N " fGH" N " iJK"); + KEYS("u", " a" X "bcde" N " fgh" N " ijk"); + KEYS("<C-R>", " a" X "BCDe" N " fGH" N " iJK"); + KEYS("u", " a" X "bcde" N " fgh" N " ijk"); + KEYS("h.", " " X "ABCde" N " FGH" N " IJK"); + KEYS("u", " " X "abcde" N " fgh" N " ijk"); + KEYS("h.", " " X " ABcde" N " FGh" N " IJk"); + KEYS("u", " " X " abcde" N " fgh" N " ijk"); + KEYS("j.", " abcde" N " " X " FGh" N " IJk"); + KEYS("u", " abcde" N " " X " fgh" N " ijk"); } void FakeVimPlugin::test_vim_code_autoindent() @@ -2246,6 +2259,45 @@ void FakeVimPlugin::test_vim_ex_yank() data.setText("abc" N "def"); KEYS("\"xy$", X "abc" N "def"); KEYS("\"xP", "ab" X "cabc" N "def"); + + data.setText( + "abc def" N + "ghi jkl" N + ); + KEYS("yiwp", + "aab" X "cbc def" N + "ghi jkl" N + ); + KEYS("u", + X "abc def" N + "ghi jkl" N + ); + KEYS("\"0p", + "aab" X "cbc def" N + "ghi jkl" N + ); + KEYS("\"xyiw", + X "aabcbc def" N + "ghi jkl" N + ); + KEYS("\"0p", + "aab" X "cabcbc def" N + "ghi jkl" N + ); + KEYS("\"xp", + "aabcaabcb" X "cabcbc def" N + "ghi jkl" N + ); + + // register " is last yank + data.setText( + "abc def" N + "ghi jkl" N + ); + KEYS("yiwp\"xyiw\"\"p", + "aaabcb" X "cabcbc def" N + "ghi jkl" N + ); } void FakeVimPlugin::test_vim_ex_delete() @@ -2551,6 +2603,7 @@ void FakeVimPlugin::test_map() data.setText("abc" N "def"); data.doCommand(QString::fromUtf8("no \xc3\xb8 l|no l k|no k j|no j h")); KEYS(QString::fromUtf8("\xc3\xb8"), "a" X "bc" N "def"); + data.doCommand(QString::fromUtf8("unmap \xc3\xb8|unmap l|unmap k|unmap j")); // Don't handle mapping in sub-modes that are not followed by movement command. data.setText("abc" N "def"); @@ -2575,6 +2628,16 @@ void FakeVimPlugin::test_map() data.doCommand("onoremap iwwX 3iwX Y"); KEYS("ciwwX Z<esc>", "X Y " X "Z" N "ghi jkl"); data.doCommand("unmap <SPACE>X"); + + // use mapping for <ESC> in insert + data.setText("ab" X "c def" N "ghi jkl"); + data.doCommand("inoremap jk <ESC>"); + KEYS("<C-V>jll" "I__jk", "ab" X "__c def" N "gh__i jkl"); + INTEGRITY(false); + data.doCommand("unmap jk"); // shouldn't unmap for insert mode + KEYS("ijk", "a" X "b__c def" N "gh__i jkl"); + data.doCommand("iunmap jk"); + KEYS("ijk<ESC>", "aj" X "kb__c def" N "gh__i jkl"); } void FakeVimPlugin::test_vim_command_cc() @@ -3012,6 +3075,90 @@ void FakeVimPlugin::test_vim_command_y_dollar() KEYS("$y$P", l[0]+'\n'+ l[1]+">>|>>\n" + lmid(2)); } +void FakeVimPlugin::test_vim_command_percent() +{ + TestData data; + setup(&data); + + data.setText( + "bool f(int arg1) {" N + " Q_ASSERT(arg1 >= 0);" N + " if (arg1 > 0) return true; else if (arg1 <= 0) return false;" N + "}" N + ); + + KEYS("%", + "bool f(int arg1" X ") {" N + " Q_ASSERT(arg1 >= 0);" N + " if (arg1 > 0) return true; else if (arg1 <= 0) return false;" N + "}" N + ); + + KEYS("%", + "bool f" X "(int arg1) {" N + " Q_ASSERT(arg1 >= 0);" N + " if (arg1 > 0) return true; else if (arg1 <= 0) return false;" N + "}" N + ); + + KEYS("$h%", + "bool f(int arg1) {" N + " Q_ASSERT(arg1 >= 0);" N + " if (arg1 > 0) return true; else if (arg1 <= 0) return false;" N + X "}" N + ); + + KEYS("%", + "bool f(int arg1) " X "{" N + " Q_ASSERT(arg1 >= 0);" N + " if (arg1 > 0) return true; else if (arg1 <= 0) return false;" N + "}" N + ); + + KEYS("j%", + "bool f(int arg1) {" N + " Q_ASSERT" X "(arg1 >= 0);" N + " if (arg1 > 0) return true; else if (arg1 <= 0) return false;" N + "}" N + ); + + KEYS("%", + "bool f(int arg1) {" N + " Q_ASSERT(arg1 >= 0" X ");" N + " if (arg1 > 0) return true; else if (arg1 <= 0) return false;" N + "}" N + ); + + KEYS("j%", + "bool f(int arg1) {" N + " Q_ASSERT(arg1 >= 0);" N + " if (arg1 > 0) return true; else if (arg1 <= 0" X ") return false;" N + "}" N + ); + + KEYS("0%", + "bool f(int arg1) {" N + " Q_ASSERT(arg1 >= 0);" N + " if (arg1 > 0" X ") return true; else if (arg1 <= 0) return false;" N + "}" N + ); + + KEYS("%", + "bool f(int arg1) {" N + " Q_ASSERT(arg1 >= 0);" N + " if " X "(arg1 > 0) return true; else if (arg1 <= 0) return false;" N + "}" N + ); + + // jump to 50% of buffer + KEYS("50%", + "bool f(int arg1) {" N + " Q_ASSERT(arg1 >= 0);" N + " " X "if (arg1 > 0) return true; else if (arg1 <= 0) return false;" N + "}" N + ); +} + void FakeVimPlugin::test_vim_command_Yp() { TestData data; diff --git a/src/plugins/fakevim/fakevimhandler.cpp b/src/plugins/fakevim/fakevimhandler.cpp index 6b9fc7129d..7d471b8c13 100644 --- a/src/plugins/fakevim/fakevimhandler.cpp +++ b/src/plugins/fakevim/fakevimhandler.cpp @@ -705,6 +705,33 @@ static void setClipboardData(const QString &content, RangeMode mode, clipboard->setMimeData(data, clipboardMode); } +static QByteArray toLocalEncoding(const QString &text) +{ + return HostOsInfo::isWindowsHost() ? QString(text).replace(_("\n"), _("\r\n")).toLocal8Bit() + : text.toLocal8Bit(); +} + +static QString fromLocalEncoding(const QByteArray &data) +{ + return HostOsInfo::isWindowsHost() ? QString::fromLocal8Bit(data).replace(_("\n"), _("\r\n")) + : QString::fromLocal8Bit(data); +} + +static QString getProcessOutput(const QString &command, const QString &input) +{ + QProcess proc; + proc.start(command); + proc.waitForStarted(); + proc.write(toLocalEncoding(input)); + proc.closeWriteChannel(); + + // FIXME: Process should be interruptable by user. + // Solution is to create a QObject for each process and emit finished state. + proc.waitForFinished(); + + return fromLocalEncoding(proc.readAllStandardOutput()); +} + static const QMap<QString, int> &vimKeyNames() { static QMap<QString, int> k; @@ -816,6 +843,11 @@ QString Range::toString() const .arg(rangemode); } +bool Range::isValid() const +{ + return beginPos >= 0 && endPos >= 0; +} + QDebug operator<<(QDebug ts, const Range &range) { return ts << '[' << range.beginPos << ',' << range.endPos << ']'; @@ -1463,11 +1495,12 @@ private: // state of current mapping struct MappingState { MappingState() - : noremap(false), silent(false) {} - MappingState(bool noremap, bool silent) - : noremap(noremap), silent(silent) {} + : noremap(false), silent(false), editBlock(false) {} + MappingState(bool noremap, bool silent, bool editBlock) + : noremap(noremap), silent(silent), editBlock(editBlock) {} bool noremap; bool silent; + bool editBlock; }; class FakeVimHandler::Private : public QObject @@ -1631,12 +1664,10 @@ public: void moveToFirstNonBlankOnLine(); void moveToFirstNonBlankOnLine(QTextCursor *tc); + void moveToFirstNonBlankOnLineVisually(); + void moveToNonBlankOnLine(QTextCursor *tc); void moveToTargetColumn(); - void setTargetColumn() { - m_targetColumn = logicalCursorColumn(); - m_visualTargetColumn = m_targetColumn; - //qDebug() << "TARGET: " << m_targetColumn; - } + void setTargetColumn(); void moveToMatchingParanthesis(); void moveToBoundary(bool simple, bool forward = true); void moveToNextBoundary(bool end, int count, bool simple, bool forward); @@ -1652,10 +1683,15 @@ public: // Convenience wrappers to reduce line noise. void moveToStartOfLine(); + void moveToStartOfLineVisually(); void moveToEndOfLine(); + void moveToEndOfLineVisually(); + void moveToEndOfLineVisually(QTextCursor *tc); void moveBehindEndOfLine(); void moveUp(int n = 1) { moveDown(-n); } void moveDown(int n = 1); + void moveUpVisually(int n = 1) { moveDownVisually(-n); } + void moveDownVisually(int n = 1); void movePageDown(int count = 1); void movePageUp(int count = 1) { movePageDown(-count); } void dump(const char *msg) const { @@ -1861,7 +1897,7 @@ public: void setCurrentRange(const Range &range); Range currentRange() const { return Range(position(), anchor(), g.rangemode); } - void yankText(const Range &range, int toregister = '"'); + void yankText(const Range &range, int toregister); void pasteText(bool afterCursor); @@ -1913,6 +1949,7 @@ public: int m_targetColumn; // -1 if past end of line int m_visualTargetColumn; // 'l' can move past eol in visual mode only + int m_targetColumnWrapped; // column in current part of wrapped line // auto-indent QString tabExpand(int len) const; @@ -2099,6 +2136,7 @@ void FakeVimHandler::Private::init() m_lastVisualModeInverted = false; m_targetColumn = 0; m_visualTargetColumn = 0; + m_targetColumnWrapped = 0; m_ctrlVActive = false; m_oldInternalAnchor = -1; m_oldInternalPosition = -1; @@ -2646,9 +2684,13 @@ void FakeVimHandler::Private::prependMapping(const Inputs &inputs) ++g.mapDepth; g.pendingInput.prepend(Input()); prependInputs(inputs); - g.mapStates << MappingState(inputs.noremap(), inputs.silent()); g.commandBuffer.setHistoryAutoSave(false); - beginLargeEditBlock(); + + // start new edit block (undo/redo) only if necessary + bool editBlock = m_editBlockLevel == 0 && !(isInsertMode() && isInsertStateValid()); + if (editBlock) + beginLargeEditBlock(); + g.mapStates << MappingState(inputs.noremap(), inputs.silent(), editBlock); } bool FakeVimHandler::Private::expandCompleteMapping() @@ -2678,8 +2720,9 @@ void FakeVimHandler::Private::endMapping() --g.mapDepth; if (g.mapStates.isEmpty()) return; + if (g.mapStates.last().editBlock) + endEditBlock(); g.mapStates.pop_back(); - endEditBlock(); if (g.mapStates.isEmpty()) g.commandBuffer.setHistoryAutoSave(true); updateMiniBuffer(); @@ -2896,6 +2939,40 @@ void FakeVimHandler::Private::moveDown(int n) updateScrollOffset(); } +void FakeVimHandler::Private::moveDownVisually(int n) +{ + const QTextCursor::MoveOperation moveOperation = (n > 0) ? Down : Up; + int count = qAbs(n); + int oldPos = m_cursor.position(); + + while (count > 0) { + m_cursor.movePosition(moveOperation, KeepAnchor, 1); + if (oldPos == m_cursor.position()) + break; + oldPos = m_cursor.position(); + QTextBlock block = m_cursor.block(); + if (block.isVisible()) + --count; + } + + QTextCursor tc = m_cursor; + tc.movePosition(StartOfLine); + const int minPos = tc.position(); + moveToEndOfLineVisually(&tc); + const int maxPos = tc.position(); + + if (m_targetColumn == -1) { + setPosition(maxPos); + } else { + setPosition(qMin(maxPos, minPos + m_targetColumnWrapped)); + const int targetColumn = m_targetColumnWrapped; + setTargetColumn(); + m_targetColumnWrapped = targetColumn; + } + + updateScrollOffset(); +} + void FakeVimHandler::Private::movePageDown(int count) { const int scrollOffset = windowScrollOffset(); @@ -2950,6 +3027,26 @@ void FakeVimHandler::Private::moveToEndOfLine() bool onlyVisibleLines = isVisualMode() || g.submode != NoSubMode; const int id = onlyVisibleLines ? lineNumber(block()) : block().blockNumber() + 1; setPosition(lastPositionInLine(id, onlyVisibleLines)); + setTargetColumn(); +} + +void FakeVimHandler::Private::moveToEndOfLineVisually() +{ + moveToEndOfLineVisually(&m_cursor); + setTargetColumn(); +} + +void FakeVimHandler::Private::moveToEndOfLineVisually(QTextCursor *tc) +{ + // Moving to end of line ends up on following line if the line is wrapped. + tc->movePosition(StartOfLine); + const int minPos = tc->position(); + tc->movePosition(EndOfLine); + int maxPos = tc->position(); + tc->movePosition(StartOfLine); + if (minPos != tc->position()) + --maxPos; + tc->setPosition(maxPos); } void FakeVimHandler::Private::moveBehindEndOfLine() @@ -2962,12 +3059,14 @@ void FakeVimHandler::Private::moveBehindEndOfLine() void FakeVimHandler::Private::moveToStartOfLine() { -#if 0 - // does not work for "hidden" documents like in the autotests - tc.movePosition(StartOfLine, MoveAnchor); -#else setPosition(block().position()); -#endif + setTargetColumn(); +} + +void FakeVimHandler::Private::moveToStartOfLineVisually() +{ + m_cursor.movePosition(StartOfLine, KeepAnchor); + setTargetColumn(); } void FakeVimHandler::Private::fixSelection() @@ -3482,15 +3581,19 @@ bool FakeVimHandler::Private::handleMovement(const Input &input) return true; } else if (input.is('0')) { g.movetype = MoveExclusive; - moveToStartOfLine(); - setTargetColumn(); + if (g.gflag) + moveToStartOfLineVisually(); + else + moveToStartOfLine(); count = 1; } else if (input.is('a') || input.is('i')) { g.subsubmode = TextObjectSubSubMode; g.subsubdata = input; } else if (input.is('^') || input.is('_')) { - moveToFirstNonBlankOnLine(); - setTargetColumn(); + if (g.gflag) + moveToFirstNonBlankOnLineVisually(); + else + moveToFirstNonBlankOnLine(); g.movetype = MoveExclusive; } else if (0 && input.is(',')) { // FIXME: fakevim uses ',' by itself, so it is incompatible @@ -3572,9 +3675,15 @@ bool FakeVimHandler::Private::handleMovement(const Input &input) setTargetColumn(); movement = _("<HOME>"); } else if (input.is('$') || input.isKey(Key_End)) { - if (count > 1) - moveDown(count - 1); - moveToEndOfLine(); + if (g.gflag) { + if (count > 1) + moveDownVisually(count - 1); + moveToEndOfLineVisually(); + } else { + if (count > 1) + moveDown(count - 1); + moveToEndOfLine(); + } g.movetype = atEmptyLine() ? MoveExclusive : MoveInclusive; setTargetColumn(); if (g.submode == NoSubMode) @@ -3670,13 +3779,25 @@ bool FakeVimHandler::Private::handleMovement(const Input &input) handleStartOfLine(); } else if (input.is('j') || input.isKey(Key_Down) || input.isControl('j') || input.isControl('n')) { - g.movetype = MoveLineWise; - moveDown(count); - movement = _("j"); + if (g.gflag) { + g.movetype = MoveExclusive; + moveDownVisually(count); + movement = _("gj"); + } else { + g.movetype = MoveLineWise; + moveDown(count); + movement = _("j"); + } } else if (input.is('k') || input.isKey(Key_Up) || input.isControl('p')) { - g.movetype = MoveLineWise; - moveUp(count); - movement = _("k"); + if (g.gflag) { + g.movetype = MoveExclusive; + moveUpVisually(count); + movement = _("gk"); + } else { + g.movetype = MoveLineWise; + moveUp(count); + movement = _("k"); + } } else if (input.is('l') || input.isKey(Key_Right) || input.is(' ')) { g.movetype = MoveExclusive; bool pastEnd = count >= rightDist() - 1; @@ -4239,7 +4360,9 @@ bool FakeVimHandler::Private::handleNoSubMode(const Input &input) g.submode = CapitalZSubMode; } else if ((input.is('~') || input.is('u') || input.is('U'))) { g.movetype = MoveExclusive; + pushUndoState(); if (isVisualMode()) { + setDotCommand(visualDotCommand() + QString::number(count()) + input.raw()); if (isVisualLineMode()) g.rangemode = RangeLineMode; else if (isVisualBlockMode()) @@ -4253,7 +4376,6 @@ bool FakeVimHandler::Private::handleNoSubMode(const Input &input) g.submode = UpCaseSubMode; finishMovement(); } else if (g.gflag || (input.is('~') && hasConfig(ConfigTildeOp))) { - pushUndoState(); if (atEndOfLine()) moveLeft(); setAnchor(); @@ -5049,9 +5171,6 @@ bool FakeVimHandler::Private::parseExCommmand(QString *line, ExCommand *cmd) if (line->isEmpty()) return false; - // remove leading colons and spaces - line->remove(QRegExp(_("^\\s*(:+\\s*)*"))); - // parse range first if (!parseLineRange(line, cmd)) return false; @@ -5104,6 +5223,15 @@ bool FakeVimHandler::Private::parseExCommmand(QString *line, ExCommand *cmd) bool FakeVimHandler::Private::parseLineRange(QString *line, ExCommand *cmd) { + // remove leading colons and spaces + line->remove(QRegExp(_("^\\s*(:+\\s*)*"))); + + // special case ':!...' (use invalid range) + if (line->startsWith(QLatin1Char('!'))) { + cmd->range = Range(); + return true; + } + // FIXME: that seems to be different for %w and %s if (line->startsWith(QLatin1Char('%'))) line->replace(0, 1, _("1,$")); @@ -5666,22 +5794,15 @@ bool FakeVimHandler::Private::handleExBangCommand(const ExCommand &cmd) // :! if (!cmd.cmd.isEmpty() || !cmd.hasBang) return false; - setCurrentRange(cmd.range); - int targetPosition = firstPositionInLine(lineForPosition(cmd.range.beginPos)); - QString command = QString(cmd.cmd.mid(1) + QLatin1Char(' ') + cmd.args).trimmed(); - QString text = selectText(cmd.range); - QProcess proc; - proc.start(command); - proc.waitForStarted(); - if (HostOsInfo::isWindowsHost()) - text.replace(_("\n"), _("\r\n")); - proc.write(text.toUtf8()); - proc.closeWriteChannel(); - proc.waitForFinished(); - QString result = QString::fromUtf8(proc.readAllStandardOutput()); - if (text.isEmpty()) { - emit q->extraInformationChanged(result); - } else { + bool replaceText = cmd.range.isValid(); + const QString command = QString(cmd.cmd.mid(1) + QLatin1Char(' ') + cmd.args).trimmed(); + const QString input = replaceText ? selectText(cmd.range) : QString(); + + const QString result = getProcessOutput(command, input); + + if (replaceText) { + setCurrentRange(cmd.range); + int targetPosition = firstPositionInLine(lineForPosition(cmd.range.beginPos)); beginEditBlock(); removeText(currentRange()); insertText(result); @@ -5690,8 +5811,11 @@ bool FakeVimHandler::Private::handleExBangCommand(const ExCommand &cmd) // :! leaveVisualMode(); //qDebug() << "FILTER: " << command; showMessage(MessageInfo, FakeVimHandler::tr("%n lines filtered.", 0, - text.count(QLatin1Char('\n')))); + input.count(QLatin1Char('\n')))); + } else if (!result.isEmpty()) { + emit q->extraInformationChanged(result); } + return true; } @@ -6042,19 +6166,31 @@ void FakeVimHandler::Private::highlightMatches(const QString &needle) void FakeVimHandler::Private::moveToFirstNonBlankOnLine() { moveToFirstNonBlankOnLine(&m_cursor); + setTargetColumn(); } void FakeVimHandler::Private::moveToFirstNonBlankOnLine(QTextCursor *tc) { + tc->setPosition(tc->block().position(), KeepAnchor); + moveToNonBlankOnLine(tc); +} + +void FakeVimHandler::Private::moveToFirstNonBlankOnLineVisually() +{ + moveToStartOfLineVisually(); + moveToNonBlankOnLine(&m_cursor); + setTargetColumn(); +} + +void FakeVimHandler::Private::moveToNonBlankOnLine(QTextCursor *tc) +{ QTextDocument *doc = tc->document(); - int firstPos = tc->block().position(); - for (int i = firstPos, n = firstPos + block().length(); i < n; ++i) { - if (!doc->characterAt(i).isSpace() || i == n - 1) { - tc->setPosition(i, KeepAnchor); - return; - } - } - tc->setPosition(block().position(), KeepAnchor); + const QTextBlock block = tc->block(); + const int maxPos = block.position() + block.length() - 1; + int i = tc->position(); + while (doc->characterAt(i).isSpace() && i < maxPos) + ++i; + tc->setPosition(i, KeepAnchor); } void FakeVimHandler::Private::indentSelectedText(QChar typedChar) @@ -6157,6 +6293,16 @@ void FakeVimHandler::Private::moveToTargetColumn() setPosition(qMin(pos, physical)); } +void FakeVimHandler::Private::setTargetColumn() +{ + m_targetColumn = logicalCursorColumn(); + m_visualTargetColumn = m_targetColumn; + + QTextCursor tc = m_cursor; + tc.movePosition(StartOfLine); + m_targetColumnWrapped = m_cursor.position() - tc.position(); +} + /* if simple is given: * class 0: spaces * class 1: non-spaces @@ -6377,11 +6523,22 @@ void FakeVimHandler::Private::moveToMatchingParanthesis() const int anc = anchor(); QTextCursor tc = m_cursor; + + // If no known parenthesis symbol is under cursor find one on the current line after cursor. + static const QString parenthesesChars(_("([{}])")); + while (!parenthesesChars.contains(document()->characterAt(tc.position())) && !tc.atBlockEnd()) + tc.setPosition(tc.position() + 1); + + if (tc.atBlockEnd()) + tc = m_cursor; + emit q->moveToMatchingParenthesis(&moved, &forward, &tc); - if (moved && forward) - tc.movePosition(Left, KeepAnchor, 1); - setAnchorAndPosition(anc, tc.position()); - setTargetColumn(); + if (moved) { + if (forward) + tc.movePosition(Left, KeepAnchor, 1); + setAnchorAndPosition(anc, tc.position()); + setTargetColumn(); + } } int FakeVimHandler::Private::cursorLineOnScreen() const @@ -6655,7 +6812,26 @@ QString FakeVimHandler::Private::selectText(const Range &range) const void FakeVimHandler::Private::yankText(const Range &range, int reg) { - setRegister(reg, selectText(range), range.rangemode); + const QString text = selectText(range); + setRegister(reg, text, range.rangemode); + + // If register is not specified or " ... + if (m_register == '"') { + // copy to yank register 0 too + setRegister('0', text, range.rangemode); + + // with delete and change commands set register 1 (if text contains more lines) or + // small delete register - + if (g.submode == DeleteSubMode || g.submode == ChangeSubMode) { + if (text.contains(QLatin1Char('\n'))) + setRegister('1', text, range.rangemode); + else + setRegister('-', text, range.rangemode); + } + } else { + // Always copy to " register too. + setRegister('"', text, range.rangemode); + } const int lines = document()->findBlock(range.endPos).blockNumber() - document()->findBlock(range.beginPos).blockNumber() + 1; diff --git a/src/plugins/fakevim/fakevimhandler.h b/src/plugins/fakevim/fakevimhandler.h index 30176cd689..408b1c3cc3 100644 --- a/src/plugins/fakevim/fakevimhandler.h +++ b/src/plugins/fakevim/fakevimhandler.h @@ -52,6 +52,7 @@ struct Range Range(); Range(int b, int e, RangeMode m = RangeCharMode); QString toString() const; + bool isValid() const; int beginPos; int endPos; diff --git a/src/plugins/fakevim/fakevimoptions.ui b/src/plugins/fakevim/fakevimoptions.ui index 8974f2ae0c..6f5d21d6b1 100644 --- a/src/plugins/fakevim/fakevimoptions.ui +++ b/src/plugins/fakevim/fakevimoptions.ui @@ -92,7 +92,7 @@ <item row="6" column="1"> <widget class="QCheckBox" name="checkBoxPassControlKey"> <property name="toolTip"> - <string>Pass key sequences like Ctrl-S to Qt Creator core instead of interpreting them in FakeVim. This gives easier access to Qt Creator core functionality at the price of losing some features of FakeVim.</string> + <string>Passes key sequences like Ctrl-S to Qt Creator core instead of interpreting them in FakeVim. This gives easier access to Qt Creator core functionality at the price of losing some features of FakeVim.</string> </property> <property name="text"> <string>Pass control key</string> @@ -123,7 +123,7 @@ <item row="7" column="0"> <widget class="QCheckBox" name="checkBoxPassKeys"> <property name="toolTip"> - <string>Let Qt Creator handle some key presses in insert mode so that code can be properly completed and expanded.</string> + <string>Lets Qt Creator handle some key presses in insert mode so that code can be properly completed and expanded.</string> </property> <property name="text"> <string>Pass keys in insert mode</string> diff --git a/src/plugins/fakevim/fakevimplugin.cpp b/src/plugins/fakevim/fakevimplugin.cpp index 38d5456789..d0af40cd7d 100644 --- a/src/plugins/fakevim/fakevimplugin.cpp +++ b/src/plugins/fakevim/fakevimplugin.cpp @@ -87,6 +87,7 @@ #include <QFileDialog> #include <QtPlugin> #include <QObject> +#include <QPointer> #include <QSettings> #include <QStackedWidget> #include <QTextStream> @@ -254,10 +255,9 @@ public: setCategoryIcon(_(SETTINGS_CATEGORY_FAKEVIM_ICON)); } - QWidget *createPage(QWidget *parent); - void apply() { m_group.apply(ICore::settings()); } - void finish() { m_group.finish(); } - virtual bool matches(const QString &) const; + QWidget *widget(); + void apply(); + void finish(); private slots: void copyTextEditorSettings(); @@ -268,113 +268,98 @@ private slots: private: friend class DebuggerPlugin; + QPointer<QWidget> m_widget; Ui::FakeVimOptionPage m_ui; - QString m_searchKeywords; Utils::SavedActionSet m_group; }; -QWidget *FakeVimOptionPage::createPage(QWidget *parent) -{ - QWidget *w = new QWidget(parent); - m_ui.setupUi(w); - const QString vimrcDefault = Utils::HostOsInfo::isAnyUnixHost() ? - QLatin1String("$HOME/.vimrc") : QLatin1String("%USERPROFILE%\\_vimrc"); - m_ui.lineEditVimRcPath->setPlaceholderText(tr("Default: %1").arg(vimrcDefault)); - - m_group.clear(); - m_group.insert(theFakeVimSetting(ConfigUseFakeVim), - m_ui.checkBoxUseFakeVim); - m_group.insert(theFakeVimSetting(ConfigReadVimRc), - m_ui.checkBoxReadVimRc); - m_group.insert(theFakeVimSetting(ConfigVimRcPath), - m_ui.lineEditVimRcPath); - - m_group.insert(theFakeVimSetting(ConfigExpandTab), - m_ui.checkBoxExpandTab); - m_group.insert(theFakeVimSetting(ConfigHlSearch), - m_ui.checkBoxHlSearch); - m_group.insert(theFakeVimSetting(ConfigShiftWidth), - m_ui.spinBoxShiftWidth); - m_group.insert(theFakeVimSetting(ConfigShowMarks), - m_ui.checkBoxShowMarks); - - m_group.insert(theFakeVimSetting(ConfigSmartTab), - m_ui.checkBoxSmartTab); - m_group.insert(theFakeVimSetting(ConfigStartOfLine), - m_ui.checkBoxStartOfLine); - m_group.insert(theFakeVimSetting(ConfigPassKeys), - m_ui.checkBoxPassKeys); - m_group.insert(theFakeVimSetting(ConfigTabStop), - m_ui.spinBoxTabStop); - m_group.insert(theFakeVimSetting(ConfigScrollOff), - m_ui.spinBoxScrollOff); - m_group.insert(theFakeVimSetting(ConfigBackspace), - m_ui.lineEditBackspace); - m_group.insert(theFakeVimSetting(ConfigIsKeyword), - m_ui.lineEditIsKeyword); - - m_group.insert(theFakeVimSetting(ConfigPassControlKey), - m_ui.checkBoxPassControlKey); - m_group.insert(theFakeVimSetting(ConfigAutoIndent), - m_ui.checkBoxAutoIndent); - m_group.insert(theFakeVimSetting(ConfigSmartIndent), - m_ui.checkBoxSmartIndent); - - m_group.insert(theFakeVimSetting(ConfigIncSearch), - m_ui.checkBoxIncSearch); - m_group.insert(theFakeVimSetting(ConfigUseCoreSearch), - m_ui.checkBoxUseCoreSearch); - m_group.insert(theFakeVimSetting(ConfigSmartCase), - m_ui.checkBoxSmartCase); - m_group.insert(theFakeVimSetting(ConfigIgnoreCase), - m_ui.checkBoxIgnoreCase); - m_group.insert(theFakeVimSetting(ConfigWrapScan), - m_ui.checkBoxWrapScan); - - m_group.insert(theFakeVimSetting(ConfigShowCmd), - m_ui.checkBoxShowCmd); - - connect(m_ui.pushButtonCopyTextEditorSettings, SIGNAL(clicked()), - SLOT(copyTextEditorSettings())); - connect(m_ui.pushButtonSetQtStyle, SIGNAL(clicked()), - SLOT(setQtStyle())); - connect(m_ui.pushButtonSetPlainStyle, SIGNAL(clicked()), - SLOT(setPlainStyle())); - connect(m_ui.pushButtonVimRcPath, SIGNAL(clicked()), - SLOT(openVimRc())); - connect(m_ui.checkBoxReadVimRc, SIGNAL(stateChanged(int)), - SLOT(updateVimRcWidgets())); - updateVimRcWidgets(); - - if (m_searchKeywords.isEmpty()) { - QLatin1Char sep(' '); - QTextStream(&m_searchKeywords) - << sep << m_ui.checkBoxUseFakeVim->text() - << sep << m_ui.checkBoxReadVimRc->text() - << sep << m_ui.checkBoxAutoIndent->text() - << sep << m_ui.checkBoxSmartIndent->text() - << sep << m_ui.checkBoxExpandTab->text() - << sep << m_ui.checkBoxSmartTab->text() - << sep << m_ui.checkBoxHlSearch->text() - << sep << m_ui.checkBoxIncSearch->text() - << sep << m_ui.checkBoxStartOfLine->text() - << sep << m_ui.checkBoxUseCoreSearch->text() - << sep << m_ui.checkBoxSmartCase->text() - << sep << m_ui.checkBoxShowMarks->text() - << sep << m_ui.checkBoxPassControlKey->text() - << sep << m_ui.checkBoxPassKeys->text() - << sep << m_ui.checkBoxIgnoreCase->text() - << sep << m_ui.checkBoxWrapScan->text() - << sep << m_ui.checkBoxShowCmd->text() - << sep << m_ui.labelShiftWidth->text() - << sep << m_ui.labelTabulator->text() - << sep << m_ui.labelBackspace->text() - << sep << m_ui.labelIsKeyword->text() - << sep << m_ui.labelScrollOff->text() - << sep << m_ui.lineEditVimRcPath->text(); - m_searchKeywords.remove(QLatin1Char('&')); +QWidget *FakeVimOptionPage::widget() +{ + if (!m_widget) { + m_widget = new QWidget; + m_ui.setupUi(m_widget); + const QString vimrcDefault = Utils::HostOsInfo::isAnyUnixHost() ? + QLatin1String("$HOME/.vimrc") : QLatin1String("%USERPROFILE%\\_vimrc"); + m_ui.lineEditVimRcPath->setPlaceholderText(tr("Default: %1").arg(vimrcDefault)); + + m_group.clear(); + m_group.insert(theFakeVimSetting(ConfigUseFakeVim), + m_ui.checkBoxUseFakeVim); + m_group.insert(theFakeVimSetting(ConfigReadVimRc), + m_ui.checkBoxReadVimRc); + m_group.insert(theFakeVimSetting(ConfigVimRcPath), + m_ui.lineEditVimRcPath); + + m_group.insert(theFakeVimSetting(ConfigExpandTab), + m_ui.checkBoxExpandTab); + m_group.insert(theFakeVimSetting(ConfigHlSearch), + m_ui.checkBoxHlSearch); + m_group.insert(theFakeVimSetting(ConfigShiftWidth), + m_ui.spinBoxShiftWidth); + m_group.insert(theFakeVimSetting(ConfigShowMarks), + m_ui.checkBoxShowMarks); + + m_group.insert(theFakeVimSetting(ConfigSmartTab), + m_ui.checkBoxSmartTab); + m_group.insert(theFakeVimSetting(ConfigStartOfLine), + m_ui.checkBoxStartOfLine); + m_group.insert(theFakeVimSetting(ConfigPassKeys), + m_ui.checkBoxPassKeys); + m_group.insert(theFakeVimSetting(ConfigTabStop), + m_ui.spinBoxTabStop); + m_group.insert(theFakeVimSetting(ConfigScrollOff), + m_ui.spinBoxScrollOff); + m_group.insert(theFakeVimSetting(ConfigBackspace), + m_ui.lineEditBackspace); + m_group.insert(theFakeVimSetting(ConfigIsKeyword), + m_ui.lineEditIsKeyword); + + m_group.insert(theFakeVimSetting(ConfigPassControlKey), + m_ui.checkBoxPassControlKey); + m_group.insert(theFakeVimSetting(ConfigAutoIndent), + m_ui.checkBoxAutoIndent); + m_group.insert(theFakeVimSetting(ConfigSmartIndent), + m_ui.checkBoxSmartIndent); + + m_group.insert(theFakeVimSetting(ConfigIncSearch), + m_ui.checkBoxIncSearch); + m_group.insert(theFakeVimSetting(ConfigUseCoreSearch), + m_ui.checkBoxUseCoreSearch); + m_group.insert(theFakeVimSetting(ConfigSmartCase), + m_ui.checkBoxSmartCase); + m_group.insert(theFakeVimSetting(ConfigIgnoreCase), + m_ui.checkBoxIgnoreCase); + m_group.insert(theFakeVimSetting(ConfigWrapScan), + m_ui.checkBoxWrapScan); + + m_group.insert(theFakeVimSetting(ConfigShowCmd), + m_ui.checkBoxShowCmd); + + connect(m_ui.pushButtonCopyTextEditorSettings, SIGNAL(clicked()), + SLOT(copyTextEditorSettings())); + connect(m_ui.pushButtonSetQtStyle, SIGNAL(clicked()), + SLOT(setQtStyle())); + connect(m_ui.pushButtonSetPlainStyle, SIGNAL(clicked()), + SLOT(setPlainStyle())); + connect(m_ui.pushButtonVimRcPath, SIGNAL(clicked()), + SLOT(openVimRc())); + connect(m_ui.checkBoxReadVimRc, SIGNAL(stateChanged(int)), + SLOT(updateVimRcWidgets())); + updateVimRcWidgets(); + } - return w; + return m_widget; +} + +void FakeVimOptionPage::apply() +{ + m_group.apply(ICore::settings()); +} + +void FakeVimOptionPage::finish() +{ + m_group.finish(); + delete m_widget; } void FakeVimOptionPage::copyTextEditorSettings() @@ -431,11 +416,6 @@ void FakeVimOptionPage::updateVimRcWidgets() m_ui.pushButtonVimRcPath->setEnabled(enabled); } -bool FakeVimOptionPage::matches(const QString &s) const -{ - return m_searchKeywords.contains(s, Qt::CaseInsensitive); -} - //const char *FAKEVIM_CONTEXT = "FakeVim"; /////////////////////////////////////////////////////////////////////// @@ -461,7 +441,7 @@ public: setCategoryIcon(_(SETTINGS_CATEGORY_FAKEVIM_ICON)); } - QWidget *createPage(QWidget *parent); + QWidget *widget(); void initialize(); ExCommandMap &exCommandMap(); ExCommandMap &defaultExCommandMap(); @@ -477,9 +457,9 @@ private: FakeVimPluginPrivate *m_q; }; -QWidget *FakeVimExCommandsPage::createPage(QWidget *parent) +QWidget *FakeVimExCommandsPage::widget() { - QWidget *w = CommandMappings::createPage(parent); + QWidget *w = CommandMappings::widget(); setPageTitle(tr("Ex Command Mapping")); setTargetHeader(tr("Ex Trigger Expression")); setTargetLabelText(tr("Regular expression:")); @@ -686,37 +666,41 @@ public: void apply(); void finish() {} - QWidget *createPage(QWidget *parent); + QWidget *widget(); void initialize() {} UserCommandMap &userCommandMap(); UserCommandMap &defaultUserCommandMap(); private: FakeVimPluginPrivate *m_q; + QPointer<QGroupBox> m_widget; }; -QWidget *FakeVimUserCommandsPage::createPage(QWidget *parent) +QWidget *FakeVimUserCommandsPage::widget() { - QGroupBox *box = new QGroupBox(parent); - - FakeVimUserCommandsModel *model = new FakeVimUserCommandsModel(m_q); - QTreeView *widget = new QTreeView; - widget->setModel(model); - widget->resizeColumnToContents(0); + if (!m_widget) { + m_widget = new QGroupBox; - FakeVimUserCommandsDelegate *delegate = new FakeVimUserCommandsDelegate(widget); - widget->setItemDelegateForColumn(1, delegate); + FakeVimUserCommandsModel *model = new FakeVimUserCommandsModel(m_q); + QTreeView *widget = new QTreeView; + model->setParent(widget); + widget->setModel(model); + widget->resizeColumnToContents(0); - QGridLayout *layout = new QGridLayout(box); - layout->addWidget(widget, 0, 0); - box->setLayout(layout); + FakeVimUserCommandsDelegate *delegate = new FakeVimUserCommandsDelegate(widget); + widget->setItemDelegateForColumn(1, delegate); - return box; + QGridLayout *layout = new QGridLayout(m_widget); + layout->addWidget(widget, 0, 0); + m_widget->setLayout(layout); + } + return m_widget; } void FakeVimUserCommandsPage::apply() { //m_q->writeSettings(); + delete m_widget; } diff --git a/src/plugins/fakevim/fakevimplugin.h b/src/plugins/fakevim/fakevimplugin.h index 5adb0a6b30..db1ced99f0 100644 --- a/src/plugins/fakevim/fakevimplugin.h +++ b/src/plugins/fakevim/fakevimplugin.h @@ -136,6 +136,7 @@ private slots: void test_vim_command_x(); void test_vim_command_yyp(); void test_vim_command_y_dollar(); + void test_vim_command_percent(); void test_vim_visual_d(); void test_vim_Visual_d(); diff --git a/src/plugins/genericprojectmanager/filesselectionwizardpage.cpp b/src/plugins/genericprojectmanager/filesselectionwizardpage.cpp index a577f0a6e6..e22ddd878b 100644 --- a/src/plugins/genericprojectmanager/filesselectionwizardpage.cpp +++ b/src/plugins/genericprojectmanager/filesselectionwizardpage.cpp @@ -31,10 +31,10 @@ #include "genericprojectwizard.h" #include "genericprojectconstants.h" -#include "selectablefilesmodel.h" #include <coreplugin/mimedatabase.h> #include <coreplugin/icore.h> +#include <projectexplorer/selectablefilesmodel.h> #include <QVBoxLayout> #include <QLineEdit> @@ -117,12 +117,12 @@ void FilesSelectionWizardPage::initializePage() { m_view->setModel(0); delete m_model; - m_model = new SelectableFilesModel(m_genericProjectWizardDialog->path(), this); + m_model = new ProjectExplorer::SelectableFilesModel(this); connect(m_model, SIGNAL(parsingProgress(QString)), this, SLOT(parsingProgress(QString))); connect(m_model, SIGNAL(parsingFinished()), this, SLOT(parsingFinished())); - m_model->startParsing(); + m_model->startParsing(m_genericProjectWizardDialog->path()); m_hideFilesFilterLabel->setVisible(false); m_hideFilesfilterLineEdit->setVisible(false); @@ -139,7 +139,6 @@ void FilesSelectionWizardPage::initializePage() void FilesSelectionWizardPage::cleanupPage() { m_model->cancel(); - m_model->waitForFinished(); } void FilesSelectionWizardPage::parsingProgress(const QString &text) diff --git a/src/plugins/genericprojectmanager/filesselectionwizardpage.h b/src/plugins/genericprojectmanager/filesselectionwizardpage.h index 1758fdade4..142707875c 100644 --- a/src/plugins/genericprojectmanager/filesselectionwizardpage.h +++ b/src/plugins/genericprojectmanager/filesselectionwizardpage.h @@ -39,11 +39,14 @@ class QTreeView; class QLineEdit; QT_END_NAMESPACE +namespace ProjectExplorer { + class SelectableFilesModel; +} + namespace GenericProjectManager { namespace Internal { class GenericProjectWizardDialog; -class SelectableFilesModel; class FilesSelectionWizardPage : public QWizardPage { @@ -68,7 +71,7 @@ private: void createApplyButton(QVBoxLayout *layout); GenericProjectWizardDialog *m_genericProjectWizardDialog; - SelectableFilesModel *m_model; + ProjectExplorer::SelectableFilesModel *m_model; QLabel *m_hideFilesFilterLabel; QLineEdit *m_hideFilesfilterLineEdit; diff --git a/src/plugins/genericprojectmanager/genericproject.cpp b/src/plugins/genericprojectmanager/genericproject.cpp index d1102d6662..68d7714351 100644 --- a/src/plugins/genericprojectmanager/genericproject.cpp +++ b/src/plugins/genericprojectmanager/genericproject.cpp @@ -69,6 +69,7 @@ GenericProject::GenericProject(Manager *manager, const QString &fileName) : m_manager(manager), m_fileName(fileName) { + setId(Constants::GENERICPROJECT_ID); setProjectContext(Context(GenericProjectManager::Constants::PROJECTCONTEXT)); setProjectLanguages(Context(ProjectExplorer::Constants::LANG_CXX)); @@ -264,7 +265,7 @@ void GenericProject::refresh(RefreshOptions options) } part->cxxVersion = CppTools::ProjectPart::CXX11; // assume C++11 - part->defines += m_defines; + part->projectDefines += m_defines; // ### add _defines. @@ -357,11 +358,6 @@ QString GenericProject::displayName() const return m_projectName; } -Id GenericProject::id() const -{ - return Id(Constants::GENERICPROJECT_ID); -} - IDocument *GenericProject::document() const { return m_creatorIDocument; diff --git a/src/plugins/genericprojectmanager/genericproject.h b/src/plugins/genericprojectmanager/genericproject.h index ff8c8efe49..18ca8798c6 100644 --- a/src/plugins/genericprojectmanager/genericproject.h +++ b/src/plugins/genericprojectmanager/genericproject.h @@ -60,7 +60,6 @@ public: QString configFileName() const; QString displayName() const; - Core::Id id() const; Core::IDocument *document() const; ProjectExplorer::IProjectManager *projectManager() const; @@ -82,9 +81,6 @@ public: void refresh(RefreshOptions options); - QStringList includePaths() const; - void setIncludePaths(const QStringList &includePaths); - QByteArray defines() const; QStringList projectIncludePaths() const; QStringList files() const; diff --git a/src/plugins/genericprojectmanager/genericprojectfileseditor.cpp b/src/plugins/genericprojectmanager/genericprojectfileseditor.cpp index 32837c6a88..48f5b77c95 100644 --- a/src/plugins/genericprojectmanager/genericprojectfileseditor.cpp +++ b/src/plugins/genericprojectmanager/genericprojectfileseditor.cpp @@ -50,20 +50,21 @@ namespace Internal { // //////////////////////////////////////////////////////////////////////////////////////// -ProjectFilesFactory::ProjectFilesFactory(Manager *manager, TextEditorActionHandler *handler) - : Core::IEditorFactory(manager), - m_actionHandler(handler) +ProjectFilesFactory::ProjectFilesFactory(Manager *manager) + : Core::IEditorFactory(manager) { setId(Constants::FILES_EDITOR_ID); setDisplayName(QCoreApplication::translate("OpenWith::Editors", ".files Editor")); addMimeType(Constants::FILES_MIMETYPE); addMimeType(Constants::INCLUDES_MIMETYPE); addMimeType(Constants::CONFIG_MIMETYPE); + new TextEditor::TextEditorActionHandler(this, Constants::C_FILESEDITOR); + } Core::IEditor *ProjectFilesFactory::createEditor(QWidget *parent) { - ProjectFilesEditorWidget *ed = new ProjectFilesEditorWidget(parent, this, m_actionHandler); + ProjectFilesEditorWidget *ed = new ProjectFilesEditorWidget(parent, this); TextEditorSettings::initializeEditor(ed); return ed->editor(); } @@ -94,8 +95,7 @@ Core::IEditor *ProjectFilesEditor::duplicate(QWidget *parent) { ProjectFilesEditorWidget *parentEditor = qobject_cast<ProjectFilesEditorWidget *>(editorWidget()); ProjectFilesEditorWidget *editor = new ProjectFilesEditorWidget(parent, - parentEditor->factory(), - parentEditor->actionHandler()); + parentEditor->factory()); TextEditorSettings::initializeEditor(editor); return editor->editor(); } @@ -106,16 +106,12 @@ Core::IEditor *ProjectFilesEditor::duplicate(QWidget *parent) // //////////////////////////////////////////////////////////////////////////////////////// -ProjectFilesEditorWidget::ProjectFilesEditorWidget(QWidget *parent, ProjectFilesFactory *factory, - TextEditorActionHandler *handler) +ProjectFilesEditorWidget::ProjectFilesEditorWidget(QWidget *parent, ProjectFilesFactory *factory) : BaseTextEditorWidget(parent), - m_factory(factory), - m_actionHandler(handler) + m_factory(factory) { QSharedPointer<BaseTextDocument> doc(new BaseTextDocument()); setBaseTextDocument(doc); - - handler->setupActions(this); } ProjectFilesFactory *ProjectFilesEditorWidget::factory() const @@ -123,11 +119,6 @@ ProjectFilesFactory *ProjectFilesEditorWidget::factory() const return m_factory; } -TextEditorActionHandler *ProjectFilesEditorWidget::actionHandler() const -{ - return m_actionHandler; -} - BaseTextEditor *ProjectFilesEditorWidget::createEditor() { return new ProjectFilesEditor(this); diff --git a/src/plugins/genericprojectmanager/genericprojectfileseditor.h b/src/plugins/genericprojectmanager/genericprojectfileseditor.h index 4e1c7998fc..d737e47571 100644 --- a/src/plugins/genericprojectmanager/genericprojectfileseditor.h +++ b/src/plugins/genericprojectmanager/genericprojectfileseditor.h @@ -35,10 +35,6 @@ #include <coreplugin/editormanager/ieditorfactory.h> -namespace TextEditor { -class TextEditorActionHandler; -} - namespace GenericProjectManager { namespace Internal { @@ -52,12 +48,9 @@ class ProjectFilesFactory: public Core::IEditorFactory Q_OBJECT public: - ProjectFilesFactory(Manager *manager, TextEditor::TextEditorActionHandler *handler); + ProjectFilesFactory(Manager *manager); Core::IEditor *createEditor(QWidget *parent); - -private: - TextEditor::TextEditorActionHandler *m_actionHandler; }; class ProjectFilesEditor : public TextEditor::BaseTextEditor @@ -77,16 +70,13 @@ class ProjectFilesEditorWidget : public TextEditor::BaseTextEditorWidget Q_OBJECT public: - ProjectFilesEditorWidget(QWidget *parent, ProjectFilesFactory *factory, - TextEditor::TextEditorActionHandler *handler); + ProjectFilesEditorWidget(QWidget *parent, ProjectFilesFactory *factory); ProjectFilesFactory *factory() const; - TextEditor::TextEditorActionHandler *actionHandler() const; TextEditor::BaseTextEditor *createEditor(); private: ProjectFilesFactory *m_factory; - TextEditor::TextEditorActionHandler *m_actionHandler; }; } // namespace Internal diff --git a/src/plugins/genericprojectmanager/genericprojectmanager.pro b/src/plugins/genericprojectmanager/genericprojectmanager.pro index 65528bcb6c..3ccecd9f86 100644 --- a/src/plugins/genericprojectmanager/genericprojectmanager.pro +++ b/src/plugins/genericprojectmanager/genericprojectmanager.pro @@ -10,7 +10,6 @@ HEADERS = genericproject.h \ pkgconfigtool.h \ genericmakestep.h \ genericbuildconfiguration.h \ - selectablefilesmodel.h \ filesselectionwizardpage.h SOURCES = genericproject.cpp \ genericprojectplugin.cpp \ @@ -21,7 +20,6 @@ SOURCES = genericproject.cpp \ pkgconfigtool.cpp \ genericmakestep.cpp \ genericbuildconfiguration.cpp \ - selectablefilesmodel.cpp \ filesselectionwizardpage.cpp RESOURCES += genericproject.qrc FORMS += genericmakestep.ui diff --git a/src/plugins/genericprojectmanager/genericprojectmanager.qbs b/src/plugins/genericprojectmanager/genericprojectmanager.qbs index 70386b06ca..707264924e 100644 --- a/src/plugins/genericprojectmanager/genericprojectmanager.qbs +++ b/src/plugins/genericprojectmanager/genericprojectmanager.qbs @@ -39,7 +39,5 @@ QtcPlugin { "genericprojectwizard.h", "pkgconfigtool.cpp", "pkgconfigtool.h", - "selectablefilesmodel.cpp", - "selectablefilesmodel.h", ] } diff --git a/src/plugins/genericprojectmanager/genericprojectnodes.cpp b/src/plugins/genericprojectmanager/genericprojectnodes.cpp index 4250e95b1a..6e7c1dde76 100644 --- a/src/plugins/genericprojectmanager/genericprojectnodes.cpp +++ b/src/plugins/genericprojectmanager/genericprojectnodes.cpp @@ -237,6 +237,7 @@ QList<ProjectNode::ProjectAction> GenericProjectNode::supportedActions(Node *nod return QList<ProjectAction>() << AddNewFile << AddExistingFile + << AddExistingDirectory << RemoveFile << Rename; } diff --git a/src/plugins/genericprojectmanager/genericprojectplugin.cpp b/src/plugins/genericprojectmanager/genericprojectplugin.cpp index c798a70a88..65557e026d 100644 --- a/src/plugins/genericprojectmanager/genericprojectplugin.cpp +++ b/src/plugins/genericprojectmanager/genericprojectplugin.cpp @@ -36,7 +36,6 @@ #include "genericprojectfileseditor.h" #include "genericmakestep.h" #include "genericproject.h" -#include "selectablefilesmodel.h" #include <coreplugin/icore.h> #include <coreplugin/mimedatabase.h> @@ -45,10 +44,9 @@ #include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/projectexplorer.h> +#include <projectexplorer/selectablefilesmodel.h> -#include <texteditor/texteditoractionhandler.h> - #include <QtPlugin> #include <QDebug> @@ -76,10 +74,7 @@ bool GenericProjectPlugin::initialize(const QStringList &, QString *errorMessage Manager *manager = new Manager; - TextEditor::TextEditorActionHandler *actionHandler = - new TextEditor::TextEditorActionHandler(Constants::C_FILESEDITOR); - - m_projectFilesEditorFactory = new ProjectFilesFactory(manager, actionHandler); + m_projectFilesEditorFactory = new ProjectFilesFactory(manager); addObject(m_projectFilesEditorFactory); addAutoReleasedObject(manager); @@ -115,7 +110,7 @@ void GenericProjectPlugin::updateContextMenu(ProjectExplorer::Project *project, void GenericProjectPlugin::editFiles() { GenericProject *genericProject = static_cast<GenericProject *>(m_contextMenuProject); - SelectableFilesDialog sfd(QFileInfo(genericProject->projectFilePath()).path(), genericProject->files(), + ProjectExplorer::SelectableFilesDialogEditFiles sfd(QFileInfo(genericProject->projectFilePath()).path(), genericProject->files(), Core::ICore::mainWindow()); if (sfd.exec() == QDialog::Accepted) genericProject->setFiles(sfd.selectedFiles()); diff --git a/src/plugins/git/branchdialog.cpp b/src/plugins/git/branchdialog.cpp index 1641f2e009..9409c56f98 100644 --- a/src/plugins/git/branchdialog.cpp +++ b/src/plugins/git/branchdialog.cpp @@ -38,11 +38,14 @@ #include "stashdialog.h" // Label helpers #include <utils/qtcassert.h> +#include <utils/execmenu.h> #include <vcsbase/vcsbaseoutputwindow.h> +#include <QAction> #include <QItemSelectionModel> #include <QMessageBox> #include <QList> +#include <QMenu> #include <QDebug> @@ -321,8 +324,18 @@ void BranchDialog::merge() const QString branch = m_model->fullName(idx, true); GitClient *client = GitPlugin::instance()->gitClient(); + bool allowFastForward = true; + if (client->isFastForwardMerge(m_repository, branch)) { + QMenu popup; + QAction *fastForward = popup.addAction(tr("Fast-Forward")); + popup.addAction(tr("No Fast-Forward")); + QAction *chosen = Utils::execMenuAtWidget(&popup, m_ui->mergeButton); + if (!chosen) + return; + allowFastForward = (chosen == fastForward); + } if (client->beginStashScope(m_repository, QLatin1String("merge"), AllowUnstashed)) - client->synchronousMerge(m_repository, branch); + client->synchronousMerge(m_repository, branch, allowFastForward); } void BranchDialog::rebase() diff --git a/src/plugins/git/changeselectiondialog.cpp b/src/plugins/git/changeselectiondialog.cpp index d9552cd575..d260b35652 100644 --- a/src/plugins/git/changeselectiondialog.cpp +++ b/src/plugins/git/changeselectiondialog.cpp @@ -88,7 +88,7 @@ ChangeSelectionDialog::~ChangeSelectionDialog() QString ChangeSelectionDialog::change() const { - return m_ui->changeNumberEdit->text(); + return m_ui->changeNumberEdit->text().trimmed(); } void ChangeSelectionDialog::selectCommitFromRecentHistory() @@ -210,8 +210,14 @@ void ChangeSelectionDialog::recalculateDetails() m_ui->workingDirectoryEdit->setPalette(palette); } + const QString ref = change(); + if (ref.isEmpty()) { + m_ui->detailsText->setPlainText(QString()); + return; + } + QStringList args; - args << QLatin1String("log") << QLatin1String("-n1") << m_ui->changeNumberEdit->text(); + args << QLatin1String("log") << QLatin1String("-n1") << ref; m_process = new QProcess(this); m_process->setWorkingDirectory(workingDir); diff --git a/src/plugins/git/gerrit/gerritdialog.cpp b/src/plugins/git/gerrit/gerritdialog.cpp index 881b3f736c..58e402bc5a 100644 --- a/src/plugins/git/gerrit/gerritdialog.cpp +++ b/src/plugins/git/gerrit/gerritdialog.cpp @@ -156,6 +156,7 @@ GerritDialog::GerritDialog(const QSharedPointer<GerritParameters> &p, detailsLayout->addWidget(m_detailsBrowser); m_repositoryChooser->setExpectedKind(Utils::PathChooser::Directory); + m_repositoryChooser->setHistoryCompleter(QLatin1String("Git.RepoDir.History")); QHBoxLayout *repoPathLayout = new QHBoxLayout; repoPathLayout->addWidget(m_repositoryChooserLabel); repoPathLayout->addWidget(m_repositoryChooser); diff --git a/src/plugins/git/gerrit/gerritoptionspage.cpp b/src/plugins/git/gerrit/gerritoptionspage.cpp index a63b64b97f..a6c09bbb56 100644 --- a/src/plugins/git/gerrit/gerritoptionspage.cpp +++ b/src/plugins/git/gerrit/gerritoptionspage.cpp @@ -55,12 +55,13 @@ GerritOptionsPage::~GerritOptionsPage() delete m_widget; } -QWidget *GerritOptionsPage::createPage(QWidget *parent) +QWidget *GerritOptionsPage::widget() { - GerritOptionsWidget *gow = new GerritOptionsWidget(parent); - gow->setParameters(*m_parameters); - m_widget = gow; - return gow; + if (!m_widget) { + m_widget = new GerritOptionsWidget; + m_widget->setParameters(*m_parameters); + } + return m_widget; } void GerritOptionsPage::apply() @@ -78,9 +79,9 @@ void GerritOptionsPage::apply() } } -bool GerritOptionsPage::matches(const QString &s) const +void GerritOptionsPage::finish() { - return s.contains(QLatin1String("gerrit"), Qt::CaseInsensitive); + delete m_widget; } GerritOptionsWidget::GerritOptionsWidget(QWidget *parent) @@ -99,9 +100,11 @@ GerritOptionsWidget::GerritOptionsWidget(QWidget *parent) formLayout->addRow(tr("&User:"), m_userLineEdit); m_sshChooser->setExpectedKind(Utils::PathChooser::ExistingCommand); m_sshChooser->setCommandVersionArguments(QStringList(QLatin1String("-V"))); + m_sshChooser->setHistoryCompleter(QLatin1String("Git.SshCommand.History")); formLayout->addRow(tr("&ssh:"), m_sshChooser); formLayout->addRow(tr("&Repository:"), m_repositoryChooser); m_repositoryChooser->setToolTip(tr("Default repository where patches will be applied.")); + m_repositoryChooser->setHistoryCompleter(QLatin1String("Git.RepoDir.History")); formLayout->addRow(tr("Pr&ompt:"), m_promptPathCheckBox); m_promptPathCheckBox->setToolTip(tr("If checked, user will always be\n" "asked to confirm the repository path.")); diff --git a/src/plugins/git/gerrit/gerritoptionspage.h b/src/plugins/git/gerrit/gerritoptionspage.h index c027fd7a96..91eedde18b 100644 --- a/src/plugins/git/gerrit/gerritoptionspage.h +++ b/src/plugins/git/gerrit/gerritoptionspage.h @@ -78,10 +78,9 @@ public: QObject *parent = 0); ~GerritOptionsPage(); - QWidget *createPage(QWidget *parent); + QWidget *widget(); void apply(); - void finish() { } - bool matches(const QString &) const; + void finish(); private: const QSharedPointer<GerritParameters> &m_parameters; diff --git a/src/plugins/git/gerrit/gerritpushdialog.cpp b/src/plugins/git/gerrit/gerritpushdialog.cpp index 83d182c12a..1c8d8a3df5 100644 --- a/src/plugins/git/gerrit/gerritpushdialog.cpp +++ b/src/plugins/git/gerrit/gerritpushdialog.cpp @@ -40,6 +40,23 @@ namespace Gerrit { namespace Internal { +class PushItemDelegate : public Git::Internal::IconItemDelegate +{ +public: + PushItemDelegate(Git::Internal::LogChangeWidget *widget) + : IconItemDelegate(widget, QLatin1String(":/git/images/arrowup.png")) + { + } + +protected: + bool hasIcon(int row) const + { + return row >= currentRow(); + } +}; + + + GerritPushDialog::GerritPushDialog(const QString &workingDir, const QString &reviewerList, QWidget *parent) : QDialog(parent), m_workingDir(workingDir), @@ -55,6 +72,8 @@ GerritPushDialog::GerritPushDialog(const QString &workingDir, const QString &rev if (!m_ui->commitView->init(workingDir, QString(), false)) return; + PushItemDelegate *delegate = new PushItemDelegate(m_ui->commitView); + delegate->setParent(this); QString earliestCommit = m_ui->commitView->earliestCommit(); if (earliestCommit.isEmpty()) return; diff --git a/src/plugins/git/git.qrc b/src/plugins/git/git.qrc index 3371b45cd8..15b93521a3 100644 --- a/src/plugins/git/git.qrc +++ b/src/plugins/git/git.qrc @@ -2,6 +2,7 @@ <qresource prefix="/git"> <file>images/git.png</file> <file>images/gitorious.png</file> + <file>images/arrowup.png</file> <file>Git.mimetypes.xml</file> </qresource> </RCC> diff --git a/src/plugins/git/gitclient.cpp b/src/plugins/git/gitclient.cpp index 614761e166..01fed3f473 100644 --- a/src/plugins/git/gitclient.cpp +++ b/src/plugins/git/gitclient.cpp @@ -36,6 +36,8 @@ #include "gitsubmiteditor.h" #include "gitversioncontrol.h" #include "mergetool.h" +#include "branchadddialog.h" +#include "gerrit/gerritplugin.h" #include <vcsbase/submitfilemodel.h> @@ -987,11 +989,9 @@ QString GitClient::findGitDirForRepository(const QString &repositoryDir) const QString &res = repoDirCache[repositoryDir]; if (!res.isEmpty()) return res; - QByteArray outputText; - QStringList arguments; - arguments << QLatin1String("rev-parse") << QLatin1String("--git-dir"); - fullySynchronousGit(repositoryDir, arguments, &outputText, 0, false); - res = QString::fromLocal8Bit(outputText.trimmed()); + + synchronousRevParseCmd(repositoryDir, QLatin1String("--git-dir"), &res); + if (!QDir(res).isAbsolute()) res.prepend(repositoryDir + QLatin1Char('/')); return res; @@ -1045,7 +1045,7 @@ DiffEditor::DiffEditor *GitClient::createDiffEditor(const char *registerDynamicP Core::EditorManager::openEditorWithContents(editorId, &title, m_msgWait.toUtf8())); QTC_ASSERT(diffEditor, return 0); diffEditor->document()->setProperty(registerDynamicProperty, dynamicPropertyValue); - diffEditor->editorWidget()->setSource(source); + VcsBasePlugin::setSource(diffEditor, source); Core::EditorManager::activateEditor(diffEditor); return diffEditor; @@ -1554,12 +1554,10 @@ bool GitClient::synchronousCheckout(const QString &workingDirectory, { QByteArray outputText; QByteArray errorText; - QStringList arguments; - arguments << QLatin1String("checkout") << ref; + QStringList arguments = setupCheckoutArguments(workingDirectory, ref); const bool rc = fullySynchronousGit(workingDirectory, arguments, &outputText, &errorText, VcsBasePlugin::ExpectRepoChanges); - const QString output = commandOutputFromLocal8Bit(outputText); - outputWindow()->append(output); + outputWindow()->append(commandOutputFromLocal8Bit(outputText)); if (!rc) { msgCannotRun(arguments, workingDirectory, errorText, errorMessage); return false; @@ -1568,6 +1566,67 @@ bool GitClient::synchronousCheckout(const QString &workingDirectory, return true; } +/* method used to setup arguments for checkout, in case user wants to create local branch */ +QStringList GitClient::setupCheckoutArguments(const QString &workingDirectory, + const QString &ref) +{ + QStringList arguments(QLatin1String("checkout")); + arguments << ref; + + QStringList localBranches = synchronousRepositoryBranches(workingDirectory); + + if (localBranches.contains(ref)) + return arguments; + + if (QMessageBox::question(Core::ICore::mainWindow(), tr("Create Local Branch"), + tr("Would you like to create local branch?"), + QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) { + return arguments; + } + + if (synchronousCurrentLocalBranch(workingDirectory).isEmpty()) + localBranches.removeFirst(); + + QString refSha; + if (!synchronousRevParseCmd(workingDirectory, ref, &refSha)) + return arguments; + + QString output; + QStringList forEachRefArgs(QLatin1String("refs/remotes/")); + forEachRefArgs << QLatin1String("--format=%(objectname) %(refname:short)"); + if (!synchronousForEachRefCmd(workingDirectory, forEachRefArgs, &output)) + return arguments; + + QString remoteBranch; + const QString head(QLatin1String("/HEAD")); + + foreach (const QString &singleRef, output.split(QLatin1Char('\n'))) { + if (singleRef.startsWith(refSha)) { + // branch name might be origin/foo/HEAD + if (!singleRef.endsWith(head) || singleRef.count(QLatin1Char('/')) > 1) { + remoteBranch = singleRef.mid(refSha.length() + 1); + if (remoteBranch == ref) + break; + } + } + } + + BranchAddDialog branchAddDialog(localBranches, true, Core::ICore::mainWindow()); + branchAddDialog.setTrackedBranchName(remoteBranch, true); + + if (branchAddDialog.exec() != QDialog::Accepted) + return arguments; + + arguments.removeLast(); + arguments << QLatin1String("-b") << branchAddDialog.branchName(); + if (branchAddDialog.track()) + arguments << QLatin1String("--track") << remoteBranch; + else + arguments << QLatin1String("--no-track") << ref; + + return arguments; +} + void GitClient::reset(const QString &workingDirectory, const QString &argument, const QString &commit) { QStringList arguments; @@ -1957,25 +2016,28 @@ QString GitClient::synchronousTopic(const QString &workingDirectory) return data.topic = remoteBranch.isEmpty() ? tr("Detached HEAD") : remoteBranch; } +bool GitClient::synchronousRevParseCmd(const QString &workingDirectory, const QString &ref, + QString *output, QString *errorMessage) const +{ + QStringList arguments(QLatin1String("rev-parse")); + arguments << ref; + QByteArray outputText; + QByteArray errorText; + const bool rc = fullySynchronousGit(workingDirectory, arguments, &outputText, &errorText, + VcsBasePlugin::SuppressCommandLogging); + *output = commandOutputFromLocal8Bit(outputText.trimmed()); + if (!rc) + msgCannotRun(arguments, workingDirectory, errorText, errorMessage); + + return rc; +} + // Retrieve head revision QString GitClient::synchronousTopRevision(const QString &workingDirectory, QString *errorMessageIn) { - QByteArray outputTextData; - QByteArray errorText; - QStringList arguments; - QString errorMessage; - // get revision - arguments << QLatin1String("rev-parse") << QLatin1String(HEAD); - if (!fullySynchronousGit(workingDirectory, arguments, &outputTextData, &errorText, - VcsBasePlugin::SuppressCommandLogging)) { - errorMessage = tr("Cannot retrieve top revision of \"%1\": %2") - .arg(QDir::toNativeSeparators(workingDirectory), commandOutputFromLocal8Bit(errorText)); + QString revision; + if (!synchronousRevParseCmd(workingDirectory, QLatin1String(HEAD), &revision, errorMessageIn)) return QString(); - } - QString revision = commandOutputFromLocal8Bit(outputTextData); - revision.remove(QLatin1Char('\n')); - if (revision.isEmpty() && !errorMessage.isEmpty()) - msgCannotRun(errorMessage, errorMessageIn); return revision; } @@ -2044,6 +2106,17 @@ bool GitClient::isRemoteCommit(const QString &workingDirectory, const QString &c return !outputText.isEmpty(); } +bool GitClient::isFastForwardMerge(const QString &workingDirectory, const QString &branch) +{ + QStringList arguments; + QByteArray outputText; + arguments << QLatin1String("merge-base") << QLatin1String(HEAD) << branch; + fullySynchronousGit(workingDirectory, arguments, &outputText, 0, + VcsBasePlugin::SuppressCommandLogging); + return commandOutputFromLocal8Bit(outputText).trimmed() + == synchronousTopRevision(workingDirectory); +} + // Format an entry in a one-liner for selection list using git log. QString GitClient::synchronousShortDescription(const QString &workingDirectory, const QString &revision, const QString &format) @@ -2468,12 +2541,13 @@ QProcessEnvironment GitClient::processEnvironment() const return environment; } -bool GitClient::beginStashScope(const QString &workingDirectory, const QString &command, StashFlag flag) +bool GitClient::beginStashScope(const QString &workingDirectory, const QString &command, + StashFlag flag, PushAction pushAction) { const QString repoDirectory = findRepositoryForDirectory(workingDirectory); QTC_ASSERT(!repoDirectory.isEmpty(), return false); StashInfo &stashInfo = m_stashInfo[repoDirectory]; - return stashInfo.init(repoDirectory, command, flag); + return stashInfo.init(repoDirectory, command, flag, pushAction); } GitClient::StashInfo &GitClient::stashInfo(const QString &workingDirectory) @@ -2968,6 +3042,11 @@ bool GitClient::getCommitData(const QString &workingDirectory, commitData.commitEncoding = readConfigValue(workingDirectory, QLatin1String("i18n.commitEncoding")); + // Set default commit encoding to 'UTF-8', when it's not set, + // to solve displaying error of commit log with non-latin characters. + if (commitData.commitEncoding.isEmpty()) + commitData.commitEncoding = QLatin1String("UTF-8"); + // Get the commit template or the last commit message switch (commitData.commitType) { case AmendCommit: { @@ -3417,12 +3496,15 @@ void GitClient::push(const QString &workingDirectory, const QStringList &pushArg executeGit(workingDirectory, arguments, 0, true); } -bool GitClient::synchronousMerge(const QString &workingDirectory, const QString &branch) +bool GitClient::synchronousMerge(const QString &workingDirectory, const QString &branch, + bool allowFastForward) { QString command = QLatin1String("merge"); - QStringList arguments; + QStringList arguments(command); - arguments << command << branch; + if (!allowFastForward) + arguments << QLatin1String("--no-ff"); + arguments << branch; return executeAndHandleConflicts(workingDirectory, arguments, command); } @@ -3734,15 +3816,17 @@ unsigned GitClient::synchronousGitVersion(QString *errorMessage) const } GitClient::StashInfo::StashInfo() : - m_client(GitPlugin::instance()->gitClient()) + m_client(GitPlugin::instance()->gitClient()), + m_pushAction(NoPush) { } bool GitClient::StashInfo::init(const QString &workingDirectory, const QString &command, - StashFlag flag) + StashFlag flag, PushAction pushAction) { m_workingDir = workingDirectory; m_flags = flag; + m_pushAction = pushAction; QString errorMessage; QString statusOutput; switch (m_client->gitStatus(m_workingDir, StatusMode(NoUntracked | NoSubmodules), @@ -3841,6 +3925,13 @@ void GitClient::StashInfo::end() if (m_client->stashNameFromMessage(m_workingDir, m_message, &stashName)) m_client->stashPop(m_workingDir, stashName); } + + if (m_pushAction == NormalPush) + m_client->push(m_workingDir); + else if (m_pushAction == PushToGerrit) + GitPlugin::instance()->gerritPlugin()->push(m_workingDir); + + m_pushAction = NoPush; m_stashResult = NotStashed; } } // namespace Internal diff --git a/src/plugins/git/gitclient.h b/src/plugins/git/gitclient.h index 382222680e..aedb61bd4e 100644 --- a/src/plugins/git/gitclient.h +++ b/src/plugins/git/gitclient.h @@ -31,6 +31,7 @@ #define GITCLIENT_H #include "gitsettings.h" +#include "commitdata.h" #include <coreplugin/editormanager/ieditor.h> @@ -110,7 +111,8 @@ public: enum StashResult { StashUnchanged, StashCanceled, StashFailed, Stashed, NotStashed /* User did not want it */ }; - bool init(const QString &workingDirectory, const QString &command, StashFlag flag = Default); + bool init(const QString &workingDirectory, const QString &command, + StashFlag flag = Default, PushAction pushAction = NoPush); bool stashingFailed() const; void end(); StashResult result() const { return m_stashResult; } @@ -125,6 +127,7 @@ public: QString m_workingDir; GitClient *m_client; StashFlag m_flags; + PushAction m_pushAction; }; static const char *stashNamePrefix; @@ -177,9 +180,10 @@ public: QString revision = QString(), QString *errorMessage = 0, bool revertStaging = true); // Checkout branch - bool synchronousCheckout(const QString &workingDirectory, const QString &ref, QString *errorMessage); - bool synchronousCheckout(const QString &workingDirectory, const QString &ref) - { return synchronousCheckout(workingDirectory, ref, 0); } + bool synchronousCheckout(const QString &workingDirectory, const QString &ref, + QString *errorMessage = 0); + + QStringList setupCheckoutArguments(const QString &workingDirectory, const QString &ref); void updateSubmodulesIfNeeded(const QString &workingDirectory, bool prompt); // Do a stash and return identier. @@ -232,19 +236,23 @@ public: bool synchronousHeadRefs(const QString &workingDirectory, QStringList *output, QString *errorMessage = 0); QString synchronousTopic(const QString &workingDirectory); + bool synchronousRevParseCmd(const QString &workingDirectory, const QString &ref, + QString *output, QString *errorMessage = 0) const; QString synchronousTopRevision(const QString &workingDirectory, QString *errorMessage = 0); void synchronousTagsForCommit(const QString &workingDirectory, const QString &revision, QString &precedes, QString &follows); QStringList synchronousBranchesForCommit(const QString &workingDirectory, const QString &revision); bool isRemoteCommit(const QString &workingDirectory, const QString &commit); + bool isFastForwardMerge(const QString &workingDirectory, const QString &branch); bool cloneRepository(const QString &directory, const QByteArray &url); QString vcsGetRepositoryURL(const QString &directory); void fetch(const QString &workingDirectory, const QString &remote); bool synchronousPull(const QString &workingDirectory, bool rebase); void push(const QString &workingDirectory, const QStringList &pushArgs = QStringList()); - bool synchronousMerge(const QString &workingDirectory, const QString &branch); + bool synchronousMerge(const QString &workingDirectory, const QString &branch, + bool allowFastForward = true); bool canRebase(const QString &workingDirectory) const; void rebase(const QString &workingDirectory, const QString &baseBranch); bool synchronousRevert(const QString &workingDirectory, const QString &commit); @@ -315,7 +323,8 @@ public: QProcessEnvironment processEnvironment() const; - bool beginStashScope(const QString &workingDirectory, const QString &command, StashFlag flag = Default); + bool beginStashScope(const QString &workingDirectory, const QString &command, + StashFlag flag = Default, PushAction pushAction = NoPush); StashInfo &stashInfo(const QString &workingDirectory); void endStashScope(const QString &workingDirectory); bool isValidRevision(const QString &revision) const; diff --git a/src/plugins/git/giteditor.cpp b/src/plugins/git/giteditor.cpp index c9b5552acb..c21b6166dd 100644 --- a/src/plugins/git/giteditor.cpp +++ b/src/plugins/git/giteditor.cpp @@ -278,9 +278,9 @@ void GitEditor::init() VcsBase::VcsBaseEditorWidget::init(); Core::Id editorId = editor()->id(); if (editorId == Git::Constants::GIT_COMMIT_TEXT_EDITOR_ID) - new GitSubmitHighlighter(baseTextDocument().data()); + new GitSubmitHighlighter(baseTextDocument()); else if (editorId == Git::Constants::GIT_REBASE_EDITOR_ID) - new GitRebaseHighlighter(baseTextDocument().data()); + new GitRebaseHighlighter(baseTextDocument()); } void GitEditor::addDiffActions(QMenu *menu, const VcsBase::DiffChunk &chunk) diff --git a/src/plugins/git/gitplugin.cpp b/src/plugins/git/gitplugin.cpp index 1d148ec0ba..a3b75828ea 100644 --- a/src/plugins/git/gitplugin.cpp +++ b/src/plugins/git/gitplugin.cpp @@ -60,6 +60,7 @@ #include <coreplugin/editormanager/ieditor.h> #include <coreplugin/mimedatabase.h> #include <coreplugin/vcsmanager.h> +#include <coreplugin/coreconstants.h> #include <utils/qtcassert.h> #include <utils/parameteraction.h> @@ -802,6 +803,33 @@ void GitPlugin::undoUnstagedFileChanges() undoFileChanges(false); } +class ResetItemDelegate : public LogItemDelegate +{ +public: + ResetItemDelegate(LogChangeWidget *widget) : LogItemDelegate(widget) {} + void initStyleOption(QStyleOptionViewItem *option, const QModelIndex &index) const + { + if (index.row() < currentRow()) + option->font.setStrikeOut(true); + LogItemDelegate::initStyleOption(option, index); + } +}; + +class RebaseItemDelegate : public IconItemDelegate +{ +public: + RebaseItemDelegate(LogChangeWidget *widget) + : IconItemDelegate(widget, QLatin1String(Core::Constants::ICON_UNDO)) + { + } + +protected: + bool hasIcon(int row) const + { + return row <= currentRow(); + } +}; + void GitPlugin::resetRepository() { if (!ensureAllDocumentsSaved()) @@ -811,6 +839,7 @@ void GitPlugin::resetRepository() QString topLevel = state.topLevel(); LogChangeDialog dialog(true, Core::ICore::mainWindow()); + ResetItemDelegate delegate(dialog.widget()); dialog.setWindowTitle(tr("Undo Changes to %1").arg(QDir::toNativeSeparators(topLevel))); if (dialog.runDialog(topLevel)) m_gitClient->reset(topLevel, dialog.resetFlag(), dialog.commit()); @@ -825,14 +854,13 @@ void GitPlugin::startRebase() const QString topLevel = state.topLevel(); if (topLevel.isEmpty() || !m_gitClient->canRebase(topLevel)) return; - if (!m_gitClient->beginStashScope(topLevel, QLatin1String("Rebase-i"))) - return; LogChangeDialog dialog(false, Core::ICore::mainWindow()); + RebaseItemDelegate delegate(dialog.widget()); dialog.setWindowTitle(tr("Interactive Rebase")); - if (dialog.runDialog(topLevel, QString(), false)) + if (!dialog.runDialog(topLevel, QString(), false)) + return; + if (m_gitClient->beginStashScope(topLevel, QLatin1String("Rebase-i"))) m_gitClient->interactiveRebase(topLevel, dialog.commit(), false); - else - m_gitClient->endStashScope(topLevel); } void GitPlugin::startChangeRelatedAction() @@ -863,24 +891,22 @@ void GitPlugin::startChangeRelatedAction() if (!ensureAllDocumentsSaved()) return; - bool (GitClient::*commandFunction)(const QString&, const QString&); + switch (dialog.command()) { case CherryPick: - commandFunction = &GitClient::synchronousCherryPick; + m_gitClient->synchronousCherryPick(workingDirectory, change); break; case Revert: - commandFunction = &GitClient::synchronousRevert; + m_gitClient->synchronousRevert(workingDirectory, change); break; case Checkout: if (!m_gitClient->beginStashScope(workingDirectory, QLatin1String("Checkout"))) return; - commandFunction = &GitClient::synchronousCheckout; + m_gitClient->synchronousCheckout(workingDirectory, change); break; default: return; } - - (m_gitClient->*commandFunction)(workingDirectory, change); } void GitPlugin::stageFile() @@ -1103,8 +1129,10 @@ bool GitPlugin::submitEditorAboutToClose() return false; cleanCommitMessageFile(); if (commitType == FixupCommit) { - if (!m_gitClient->beginStashScope(m_submitRepository, QLatin1String("Rebase-fixup"), NoPrompt)) + if (!m_gitClient->beginStashScope(m_submitRepository, QLatin1String("Rebase-fixup"), + NoPrompt, editor->panelData().pushAction)) { return false; + } m_gitClient->interactiveRebase(m_submitRepository, amendSHA1, true); } else { m_gitClient->continueCommandIfNeeded(m_submitRepository); @@ -1477,6 +1505,11 @@ GitClient *GitPlugin::gitClient() const return m_gitClient; } +Gerrit::Internal::GerritPlugin *GitPlugin::gerritPlugin() const +{ + return m_gerritPlugin; +} + #ifdef WITH_TESTS #include <QTest> diff --git a/src/plugins/git/gitplugin.h b/src/plugins/git/gitplugin.h index 5a0467717f..4ebb2e48c3 100644 --- a/src/plugins/git/gitplugin.h +++ b/src/plugins/git/gitplugin.h @@ -99,6 +99,7 @@ public: void setSettings(const GitSettings &s); GitClient *gitClient() const; + Gerrit::Internal::GerritPlugin *gerritPlugin() const; public slots: void startCommit(); diff --git a/src/plugins/git/gitsubmiteditorwidget.cpp b/src/plugins/git/gitsubmiteditorwidget.cpp index a44e1ae578..3102bd270a 100644 --- a/src/plugins/git/gitsubmiteditorwidget.cpp +++ b/src/plugins/git/gitsubmiteditorwidget.cpp @@ -109,7 +109,7 @@ void GitSubmitEditorWidget::initialize(CommitType commitType, setPanelData(data); setPanelInfo(info); - if (enablePush && commitType != FixupCommit) { + if (enablePush) { QMenu *menu = new QMenu(this); menu->addAction(tr("&Commit only"), this, SLOT(commitOnlySlot())); menu->addAction(tr("Commit and &Push"), this, SLOT(commitAndPushSlot())); diff --git a/src/plugins/git/images/arrowup.png b/src/plugins/git/images/arrowup.png Binary files differnew file mode 100644 index 0000000000..5cdbc6ed9e --- /dev/null +++ b/src/plugins/git/images/arrowup.png diff --git a/src/plugins/git/logchangedialog.cpp b/src/plugins/git/logchangedialog.cpp index cdc42a3863..30cf2bb080 100644 --- a/src/plugins/git/logchangedialog.cpp +++ b/src/plugins/git/logchangedialog.cpp @@ -33,6 +33,8 @@ #include <vcsbase/vcsbaseoutputwindow.h> +#include <utils/qtcassert.h> + #include <QTreeView> #include <QLabel> #include <QPushButton> @@ -41,6 +43,7 @@ #include <QItemSelectionModel> #include <QVBoxLayout> #include <QComboBox> +#include <QPainter> namespace Git { namespace Internal { @@ -55,6 +58,7 @@ enum Columns LogChangeWidget::LogChangeWidget(QWidget *parent) : QTreeView(parent) , m_model(new QStandardItemModel(0, ColumnCount, this)) + , m_hasCustomDelegate(false) { QStringList headers; headers << tr("Sha1")<< tr("Subject"); @@ -104,6 +108,12 @@ QString LogChangeWidget::earliestCommit() const return QString(); } +void LogChangeWidget::setItemDelegate(QAbstractItemDelegate *delegate) +{ + QTreeView::setItemDelegate(delegate); + m_hasCustomDelegate = true; +} + void LogChangeWidget::emitDoubleClicked(const QModelIndex &index) { if (index.isValid()) { @@ -113,6 +123,26 @@ void LogChangeWidget::emitDoubleClicked(const QModelIndex &index) } } +void LogChangeWidget::selectionChanged(const QItemSelection &selected, + const QItemSelection &deselected) +{ + QTreeView::selectionChanged(selected, deselected); + if (!m_hasCustomDelegate) + return; + const QModelIndexList previousIndexes = deselected.indexes(); + if (previousIndexes.isEmpty()) + return; + const QModelIndex current = currentIndex(); + int row = current.row(); + int previousRow = previousIndexes.first().row(); + if (row < previousRow) + qSwap(row, previousRow); + for (int r = previousRow; r <= row; ++r) { + update(current.sibling(r, 0)); + update(current.sibling(r, 1)); + } +} + bool LogChangeWidget::populateLog(const QString &repository, const QString &commit, bool includeRemote) { const QString currentCommit = this->commit(); @@ -234,5 +264,40 @@ QString LogChangeDialog::resetFlag() const return m_resetTypeComboBox->itemData(m_resetTypeComboBox->currentIndex()).toString(); } +LogChangeWidget *LogChangeDialog::widget() const +{ + return m_widget; +} + +LogItemDelegate::LogItemDelegate(LogChangeWidget *widget) : m_widget(widget) +{ + m_widget->setItemDelegate(this); +} + +int LogItemDelegate::currentRow() const +{ + return m_widget->commitIndex(); +} + +IconItemDelegate::IconItemDelegate(LogChangeWidget *widget, const QString &icon) + : LogItemDelegate(widget) + , m_icon(icon) +{ +} + +void IconItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, + const QModelIndex &index) const +{ + QStyleOptionViewItem o = option; + if (index.column() == 0 && hasIcon(index.row())) { + const QSize size = option.decorationSize; + painter->save(); + painter->drawPixmap(o.rect.x(), o.rect.y(), m_icon.pixmap(size.width(), size.height())); + painter->restore(); + o.rect.translate(size.width(), 0); + } + QStyledItemDelegate::paint(painter, o, index); +} + } // namespace Internal } // namespace Git diff --git a/src/plugins/git/logchangedialog.h b/src/plugins/git/logchangedialog.h index 1c83cfce7a..603a7ff962 100644 --- a/src/plugins/git/logchangedialog.h +++ b/src/plugins/git/logchangedialog.h @@ -31,6 +31,8 @@ #define LOGCHANGEDDIALOG_H #include <QDialog> +#include <QIcon> +#include <QStyledItemDelegate> #include <QTreeView> QT_BEGIN_NAMESPACE @@ -57,6 +59,7 @@ public: QString commit() const; int commitIndex() const; QString earliestCommit() const; + void setItemDelegate(QAbstractItemDelegate *delegate); signals: void doubleClicked(const QString &commit); @@ -65,10 +68,12 @@ private slots: void emitDoubleClicked(const QModelIndex &index); private: + void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected); bool populateLog(const QString &repository, const QString &commit, bool includeRemote); const QStandardItem *currentItem(int column = 0) const; QStandardItemModel *m_model; + bool m_hasCustomDelegate; }; class LogChangeDialog : public QDialog @@ -83,6 +88,7 @@ public: QString commit() const; int commitIndex() const; QString resetFlag() const; + LogChangeWidget *widget() const; private: LogChangeWidget *m_widget; @@ -90,6 +96,31 @@ private: QComboBox *m_resetTypeComboBox; }; +class LogItemDelegate : public QStyledItemDelegate +{ +protected: + LogItemDelegate(LogChangeWidget *widget); + + int currentRow() const; + +private: + LogChangeWidget *m_widget; +}; + +class IconItemDelegate : public LogItemDelegate +{ +public: + IconItemDelegate(LogChangeWidget *widget, const QString &icon); + + virtual bool hasIcon(int row) const = 0; + + void paint(QPainter *painter, const QStyleOptionViewItem &option, + const QModelIndex &index) const; + +private: + QIcon m_icon; +}; + } // namespace Internal } // namespace Git diff --git a/src/plugins/git/settingspage.cpp b/src/plugins/git/settingspage.cpp index 339d353ce8..2b7d187b3a 100644 --- a/src/plugins/git/settingspage.cpp +++ b/src/plugins/git/settingspage.cpp @@ -61,6 +61,7 @@ SettingsPageWidget::SettingsPageWidget(QWidget *parent) : m_ui.winHomeCheckBox->setVisible(false); } m_ui.repBrowserCommandPathChooser->setExpectedKind(Utils::PathChooser::ExistingCommand); + m_ui.repBrowserCommandPathChooser->setHistoryCompleter(QLatin1String("Git.RepoCommand.History")); m_ui.repBrowserCommandPathChooser->setPromptDialogTitle(tr("Git Repository Browser Command")); } @@ -90,25 +91,6 @@ void SettingsPageWidget::setSettings(const GitSettings &s) m_ui.repBrowserCommandPathChooser->setPath(s.stringValue(GitSettings::repositoryBrowserCmd)); } -QString SettingsPageWidget::searchKeywords() const -{ - QString rc; - QLatin1Char sep(' '); - QTextStream(&rc) - << sep << m_ui.pathlabel->text() - << sep << m_ui.winHomeCheckBox->text() - << sep << m_ui.groupBox->title() - << sep << m_ui.logCountLabel->text() - << sep << m_ui.timeoutLabel->text() - << sep << m_ui.gitkGroupBox->title() - << sep << m_ui.gitkOptionsLabel->text() - << sep << m_ui.repBrowserGroupBox->title() - << sep << m_ui.repBrowserCommandLabel->text() - ; - rc.remove(QLatin1Char('&')); - return rc; -} - // -------- SettingsPage SettingsPage::SettingsPage() : m_widget(0) @@ -117,12 +99,12 @@ SettingsPage::SettingsPage() : setDisplayName(tr("Git")); } -QWidget *SettingsPage::createPage(QWidget *parent) +QWidget *SettingsPage::widget() { - m_widget = new SettingsPageWidget(parent); - m_widget->setSettings(GitPlugin::instance()->settings()); - if (m_searchKeywords.isEmpty()) - m_searchKeywords = m_widget->searchKeywords(); + if (!m_widget) { + m_widget = new SettingsPageWidget; + m_widget->setSettings(GitPlugin::instance()->settings()); + } return m_widget; } @@ -141,9 +123,9 @@ void SettingsPage::apply() GitPlugin::instance()->setSettings(newSettings); } -bool SettingsPage::matches(const QString &s) const +void SettingsPage::finish() { - return m_searchKeywords.contains(s, Qt::CaseInsensitive); + delete m_widget; } } diff --git a/src/plugins/git/settingspage.h b/src/plugins/git/settingspage.h index 7386d85dda..f2ca8dc396 100644 --- a/src/plugins/git/settingspage.h +++ b/src/plugins/git/settingspage.h @@ -30,12 +30,13 @@ #ifndef SETTINGSPAGE_H #define SETTINGSPAGE_H -#include <QWidget> - #include <vcsbase/vcsbaseoptionspage.h> #include "ui_settingspage.h" +#include <QPointer> +#include <QWidget> + QT_BEGIN_NAMESPACE class QSettings; QT_END_NAMESPACE @@ -53,8 +54,6 @@ public: GitSettings settings() const; void setSettings(const GitSettings &); - QString searchKeywords() const; - private: Ui::SettingsPage m_ui; }; @@ -66,14 +65,13 @@ class SettingsPage : public VcsBase::VcsBaseOptionsPage public: SettingsPage(); - QWidget *createPage(QWidget *parent); + QWidget *widget(); void apply(); - void finish() { } - bool matches(const QString &) const; + void finish(); private: QString m_searchKeywords; - SettingsPageWidget* m_widget; + QPointer<SettingsPageWidget> m_widget; }; } // namespace Internal diff --git a/src/plugins/glsleditor/glsleditor.cpp b/src/plugins/glsleditor/glsleditor.cpp index 67ff3afd33..a4936bc50f 100644 --- a/src/plugins/glsleditor/glsleditor.cpp +++ b/src/plugins/glsleditor/glsleditor.cpp @@ -157,7 +157,7 @@ GLSLTextEditorWidget::GLSLTextEditorWidget(QWidget *parent) : connect(this, SIGNAL(textChanged()), this, SLOT(updateDocument())); - new Highlighter(baseTextDocument().data()); + new Highlighter(baseTextDocument()); // if (m_modelManager) { // m_semanticHighlighter->setModelManager(m_modelManager); @@ -191,7 +191,7 @@ Core::IEditor *GLSLEditorEditable::duplicate(QWidget *parent) { GLSLTextEditorWidget *newEditor = new GLSLTextEditorWidget(parent); newEditor->duplicateFrom(editorWidget()); - GLSLEditorPlugin::initializeEditor(newEditor); + TextEditor::TextEditorSettings::initializeEditor(newEditor); return newEditor->editor(); } diff --git a/src/plugins/glsleditor/glsleditorfactory.cpp b/src/plugins/glsleditor/glsleditorfactory.cpp index 889db06caa..f25cb99fbc 100644 --- a/src/plugins/glsleditor/glsleditorfactory.cpp +++ b/src/plugins/glsleditor/glsleditorfactory.cpp @@ -37,6 +37,8 @@ #include <extensionsystem/pluginspec.h> #include <coreplugin/icore.h> +#include <texteditor/texteditoractionhandler.h> +#include <texteditor/texteditorsettings.h> #include <QCoreApplication> #include <QSettings> @@ -54,12 +56,17 @@ GLSLEditorFactory::GLSLEditorFactory(QObject *parent) addMimeType(GLSLEditor::Constants::GLSL_MIMETYPE_FRAG); addMimeType(GLSLEditor::Constants::GLSL_MIMETYPE_VERT_ES); addMimeType(GLSLEditor::Constants::GLSL_MIMETYPE_FRAG_ES); + new TextEditor::TextEditorActionHandler(this, Constants::C_GLSLEDITOR_ID, + TextEditor::TextEditorActionHandler::Format + | TextEditor::TextEditorActionHandler::UnCommentSelection + | TextEditor::TextEditorActionHandler::UnCollapseAll); + } Core::IEditor *GLSLEditorFactory::createEditor(QWidget *parent) { GLSLTextEditorWidget *rc = new GLSLTextEditorWidget(parent); - GLSLEditorPlugin::initializeEditor(rc); + TextEditor::TextEditorSettings::initializeEditor(rc); return rc->editor(); } diff --git a/src/plugins/glsleditor/glsleditorplugin.cpp b/src/plugins/glsleditor/glsleditorplugin.cpp index a9544e9ef5..815689cb51 100644 --- a/src/plugins/glsleditor/glsleditorplugin.cpp +++ b/src/plugins/glsleditor/glsleditorplugin.cpp @@ -48,9 +48,7 @@ #include <projectexplorer/taskhub.h> #include <extensionsystem/pluginmanager.h> #include <texteditor/texteditorconstants.h> -#include <texteditor/texteditorsettings.h> #include <texteditor/textfilewizard.h> -#include <texteditor/texteditoractionhandler.h> #include <utils/qtcassert.h> #include <glsl/glslengine.h> @@ -77,7 +75,6 @@ class GLSLEditorPluginPrivate public: GLSLEditorPluginPrivate() : m_editor(0), - m_actionHandler(0), m_glsl_120_frag(0), m_glsl_120_vert(0), m_glsl_120_common(0), @@ -88,7 +85,6 @@ public: ~GLSLEditorPluginPrivate() { - delete m_actionHandler; delete m_glsl_120_frag; delete m_glsl_120_vert; delete m_glsl_120_common; @@ -98,7 +94,6 @@ public: } GLSLEditorFactory *m_editor; - TextEditor::TextEditorActionHandler *m_actionHandler; QPointer<TextEditor::ITextEditor> m_currentTextEditable; GLSLEditorPlugin::InitFile *m_glsl_120_frag; @@ -145,12 +140,6 @@ bool GLSLEditorPlugin::initialize(const QStringList & /*arguments*/, QString *er addAutoReleasedObject(new GLSLCompletionAssistProvider); - dd->m_actionHandler = new TextEditorActionHandler(Constants::C_GLSLEDITOR_ID, - TextEditorActionHandler::Format - | TextEditorActionHandler::UnCommentSelection - | TextEditorActionHandler::UnCollapseAll); - dd->m_actionHandler->initializeActions(); - ActionContainer *contextMenu = ActionManager::createMenu(GLSLEditor::Constants::M_CONTEXT); ActionContainer *glslToolsMenu = ActionManager::createMenu(Id(Constants::M_TOOLS_GLSL)); glslToolsMenu->setOnAllDisabledBehavior(ActionContainer::Hide); @@ -245,13 +234,6 @@ ExtensionSystem::IPlugin::ShutdownFlag GLSLEditorPlugin::aboutToShutdown() return IPlugin::aboutToShutdown(); } -void GLSLEditorPlugin::initializeEditor(GLSLTextEditorWidget *editor) -{ - QTC_CHECK(m_instance); - dd->m_actionHandler->setupActions(editor); - TextEditorSettings::initializeEditor(editor); -} - static QByteArray glslFile(const QString &fileName) { QFile file(ICore::resourcePath() + QLatin1String("/glsl/") + fileName); diff --git a/src/plugins/glsleditor/glsleditorplugin.h b/src/plugins/glsleditor/glsleditorplugin.h index c9627e8501..d4fae86c25 100644 --- a/src/plugins/glsleditor/glsleditorplugin.h +++ b/src/plugins/glsleditor/glsleditorplugin.h @@ -52,8 +52,6 @@ public: void extensionsInitialized(); ShutdownFlag aboutToShutdown(); - static void initializeEditor(GLSLTextEditorWidget *editor); - struct InitFile { InitFile(GLSL::Engine *engine = 0, GLSL::TranslationUnitAST *ast = 0) diff --git a/src/plugins/help/docsettingspage.cpp b/src/plugins/help/docsettingspage.cpp index 9c2bdcb83f..17cb777306 100644 --- a/src/plugins/help/docsettingspage.cpp +++ b/src/plugins/help/docsettingspage.cpp @@ -50,27 +50,26 @@ DocSettingsPage::DocSettingsPage() setCategoryIcon(QLatin1String(Help::Constants::HELP_CATEGORY_ICON)); } -QWidget *DocSettingsPage::createPage(QWidget *parent) +QWidget *DocSettingsPage::widget() { - QWidget *widget = new QWidget(parent); - m_ui.setupUi(widget); + if (!m_widget) { + m_widget = new QWidget; + m_ui.setupUi(m_widget); - connect(m_ui.addButton, SIGNAL(clicked()), this, SLOT(addDocumentation())); - connect(m_ui.removeButton, SIGNAL(clicked()), this, SLOT(removeDocumentation())); + connect(m_ui.addButton, SIGNAL(clicked()), this, SLOT(addDocumentation())); + connect(m_ui.removeButton, SIGNAL(clicked()), this, SLOT(removeDocumentation())); - m_ui.docsListWidget->installEventFilter(this); + m_ui.docsListWidget->installEventFilter(this); - const QStringList nameSpaces = HelpManager::registeredNamespaces(); - foreach (const QString &nameSpace, nameSpaces) { - addItem(nameSpace, HelpManager::fileFromNamespace(nameSpace)); - m_filesToRegister.insert(nameSpace, HelpManager::fileFromNamespace(nameSpace)); - } - - m_filesToUnregister.clear(); + const QStringList nameSpaces = HelpManager::registeredNamespaces(); + foreach (const QString &nameSpace, nameSpaces) { + addItem(nameSpace, HelpManager::fileFromNamespace(nameSpace)); + m_filesToRegister.insert(nameSpace, HelpManager::fileFromNamespace(nameSpace)); + } - if (m_searchKeywords.isEmpty()) - m_searchKeywords = m_ui.groupBox->title(); - return widget; + m_filesToUnregister.clear(); + } + return m_widget; } void DocSettingsPage::addDocumentation() @@ -155,9 +154,9 @@ void DocSettingsPage::apply() m_filesToUnregister.clear(); } -bool DocSettingsPage::matches(const QString &s) const +void DocSettingsPage::finish() { - return m_searchKeywords.contains(s, Qt::CaseInsensitive); + delete m_widget; } bool DocSettingsPage::eventFilter(QObject *object, QEvent *event) diff --git a/src/plugins/help/docsettingspage.h b/src/plugins/help/docsettingspage.h index cb3db51ac4..64e884e1fc 100644 --- a/src/plugins/help/docsettingspage.h +++ b/src/plugins/help/docsettingspage.h @@ -33,6 +33,8 @@ #include "ui_docsettingspage.h" #include <coreplugin/dialogs/ioptionspage.h> +#include <QPointer> + namespace Help { namespace Internal { @@ -43,10 +45,9 @@ class DocSettingsPage : public Core::IOptionsPage public: DocSettingsPage(); - QWidget *createPage(QWidget *parent); + QWidget *widget(); void apply(); - void finish() {} - bool matches(const QString &s) const; + void finish(); private slots: void addDocumentation(); @@ -59,8 +60,8 @@ private: private: Ui::DocSettingsPage m_ui; + QPointer<QWidget> m_widget; - QString m_searchKeywords; QString m_recentDialogPath; typedef QHash<QString, QString> NameSpaceToPathHash; diff --git a/src/plugins/help/filtersettingspage.cpp b/src/plugins/help/filtersettingspage.cpp index 7606f32c38..b653ea2558 100644 --- a/src/plugins/help/filtersettingspage.cpp +++ b/src/plugins/help/filtersettingspage.cpp @@ -51,29 +51,26 @@ FilterSettingsPage::FilterSettingsPage() setCategoryIcon(QLatin1String(Help::Constants::HELP_CATEGORY_ICON)); } -QWidget *FilterSettingsPage::createPage(QWidget *parent) +QWidget *FilterSettingsPage::widget() { - QWidget *widget = new QWidget(parent); - m_ui.setupUi(widget); - - updateFilterPage(); - - connect(m_ui.attributeWidget, SIGNAL(itemChanged(QTreeWidgetItem*,int)), - this, SLOT(updateFilterMap())); - connect(m_ui.filterWidget, - SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)), this, - SLOT(updateAttributes(QListWidgetItem*))); - connect(m_ui.filterAddButton, SIGNAL(clicked()), this, SLOT(addFilter())); - connect(m_ui.filterRemoveButton, SIGNAL(clicked()), this, - SLOT(removeFilter())); - connect(HelpManager::instance(), SIGNAL(documentationChanged()), - this, SLOT(updateFilterPage())); - - if (m_searchKeywords.isEmpty()) { - m_searchKeywords = m_ui.filterGroupBox->title() + QLatin1Char(' ') - + m_ui.attributesGroupBox->title(); + if (!m_widget) { + m_widget = new QWidget; + m_ui.setupUi(m_widget); + + updateFilterPage(); + + connect(m_ui.attributeWidget, SIGNAL(itemChanged(QTreeWidgetItem*,int)), + this, SLOT(updateFilterMap())); + connect(m_ui.filterWidget, + SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)), this, + SLOT(updateAttributes(QListWidgetItem*))); + connect(m_ui.filterAddButton, SIGNAL(clicked()), this, SLOT(addFilter())); + connect(m_ui.filterRemoveButton, SIGNAL(clicked()), this, + SLOT(removeFilter())); + connect(HelpManager::instance(), SIGNAL(documentationChanged()), + this, SLOT(updateFilterPage())); } - return widget; + return m_widget; } void FilterSettingsPage::updateFilterPage() @@ -230,11 +227,7 @@ void FilterSettingsPage::finish() { disconnect(HelpManager::instance(), SIGNAL(documentationChanged()), this, SLOT(updateFilterPage())); -} - -bool FilterSettingsPage::matches(const QString &s) const -{ - return m_searchKeywords.contains(s, Qt::CaseInsensitive); + delete m_widget; } QString FilterSettingsPage::msgFilterLabel(const QString &filter) const diff --git a/src/plugins/help/filtersettingspage.h b/src/plugins/help/filtersettingspage.h index cc0ef99bc0..6574c4ac42 100644 --- a/src/plugins/help/filtersettingspage.h +++ b/src/plugins/help/filtersettingspage.h @@ -33,6 +33,8 @@ #include "ui_filtersettingspage.h" #include <coreplugin/dialogs/ioptionspage.h> +#include <QPointer> + namespace Help { namespace Internal { @@ -43,10 +45,9 @@ class FilterSettingsPage : public Core::IOptionsPage public: FilterSettingsPage(); - QWidget *createPage(QWidget *parent); + QWidget *widget(); void apply(); void finish(); - bool matches(const QString &s) const; signals: void filtersChanged(); @@ -64,12 +65,12 @@ private: private: QString msgFilterLabel(const QString &filter) const; Ui::FilterSettingsPage m_ui; + QPointer<QWidget> m_widget; typedef QMap<QString, QStringList> FilterMap; FilterMap m_filterMap; FilterMap m_filterMapBackup; - QString m_searchKeywords; QStringList m_removedFilters; }; diff --git a/src/plugins/help/generalsettingspage.cpp b/src/plugins/help/generalsettingspage.cpp index 39e495c96f..e687ed0cc0 100644 --- a/src/plugins/help/generalsettingspage.cpp +++ b/src/plugins/help/generalsettingspage.cpp @@ -72,59 +72,54 @@ GeneralSettingsPage::GeneralSettingsPage() setCategoryIcon(QLatin1String(Help::Constants::HELP_CATEGORY_ICON)); } -QWidget *GeneralSettingsPage::createPage(QWidget *parent) +QWidget *GeneralSettingsPage::widget() { - QWidget *widget = new QWidget(parent); - m_ui = new Ui::GeneralSettingsPage; - m_ui->setupUi(widget); - m_ui->sizeComboBox->setEditable(false); - m_ui->styleComboBox->setEditable(false); - - m_font = qvariant_cast<QFont>(HelpManager::customValue(QLatin1String("font"), m_font)); - - updateFontSize(); - updateFontStyle(); - updateFontFamily(); - - m_homePage = HelpManager::customValue(QLatin1String("HomePage"), QString()) - .toString(); - if (m_homePage.isEmpty()) { - m_homePage = HelpManager::customValue(QLatin1String("DefaultHomePage"), - Help::Constants::AboutBlank).toString(); - } - m_ui->homePageLineEdit->setText(m_homePage); + if (!m_widget) { + m_widget = new QWidget; + m_ui = new Ui::GeneralSettingsPage; + m_ui->setupUi(m_widget); + m_ui->sizeComboBox->setEditable(false); + m_ui->styleComboBox->setEditable(false); + + m_font = qvariant_cast<QFont>(HelpManager::customValue(QLatin1String("font"), m_font)); + + updateFontSize(); + updateFontStyle(); + updateFontFamily(); + + m_homePage = HelpManager::customValue(QLatin1String("HomePage"), QString()) + .toString(); + if (m_homePage.isEmpty()) { + m_homePage = HelpManager::customValue(QLatin1String("DefaultHomePage"), + Help::Constants::AboutBlank).toString(); + } + m_ui->homePageLineEdit->setText(m_homePage); - m_startOption = HelpManager::customValue(QLatin1String("StartOption"), - Help::Constants::ShowLastPages).toInt(); - m_ui->helpStartComboBox->setCurrentIndex(m_startOption); + m_startOption = HelpManager::customValue(QLatin1String("StartOption"), + Help::Constants::ShowLastPages).toInt(); + m_ui->helpStartComboBox->setCurrentIndex(m_startOption); - m_contextOption = HelpManager::customValue(QLatin1String("ContextHelpOption"), - Help::Constants::SideBySideIfPossible).toInt(); - m_ui->contextHelpComboBox->setCurrentIndex(m_contextOption); + m_contextOption = HelpManager::customValue(QLatin1String("ContextHelpOption"), + Help::Constants::SideBySideIfPossible).toInt(); + m_ui->contextHelpComboBox->setCurrentIndex(m_contextOption); - connect(m_ui->currentPageButton, SIGNAL(clicked()), this, SLOT(setCurrentPage())); - connect(m_ui->blankPageButton, SIGNAL(clicked()), this, SLOT(setBlankPage())); - connect(m_ui->defaultPageButton, SIGNAL(clicked()), this, SLOT(setDefaultPage())); + connect(m_ui->currentPageButton, SIGNAL(clicked()), this, SLOT(setCurrentPage())); + connect(m_ui->blankPageButton, SIGNAL(clicked()), this, SLOT(setBlankPage())); + connect(m_ui->defaultPageButton, SIGNAL(clicked()), this, SLOT(setDefaultPage())); - HelpViewer *viewer = CentralWidget::instance()->currentHelpViewer(); - if (!viewer) - m_ui->currentPageButton->setEnabled(false); + HelpViewer *viewer = CentralWidget::instance()->currentHelpViewer(); + if (!viewer) + m_ui->currentPageButton->setEnabled(false); - m_ui->errorLabel->setVisible(false); - connect(m_ui->importButton, SIGNAL(clicked()), this, SLOT(importBookmarks())); - connect(m_ui->exportButton, SIGNAL(clicked()), this, SLOT(exportBookmarks())); + m_ui->errorLabel->setVisible(false); + connect(m_ui->importButton, SIGNAL(clicked()), this, SLOT(importBookmarks())); + connect(m_ui->exportButton, SIGNAL(clicked()), this, SLOT(exportBookmarks())); - if (m_searchKeywords.isEmpty()) { - QTextStream(&m_searchKeywords) << ' ' << m_ui->contextHelpLabel->text() - << ' ' << m_ui->startPageLabel->text() << ' ' << m_ui->homePageLabel->text(); - m_searchKeywords.remove(QLatin1Char('&')); + m_returnOnClose = HelpManager::customValue(QLatin1String("ReturnOnClose"), + false).toBool(); + m_ui->m_returnOnClose->setChecked(m_returnOnClose); } - - m_returnOnClose = HelpManager::customValue(QLatin1String("ReturnOnClose"), - false).toBool(); - m_ui->m_returnOnClose->setChecked(m_returnOnClose); - - return widget; + return m_widget; } void GeneralSettingsPage::apply() @@ -342,13 +337,9 @@ int GeneralSettingsPage::closestPointSizeIndex(int desiredPointSize) const return closestIndex; } -bool GeneralSettingsPage::matches(const QString &s) const -{ - return m_searchKeywords.contains(s, Qt::CaseInsensitive); -} - void GeneralSettingsPage::finish() { + delete m_widget; if (!m_ui) // page was never shown return; delete m_ui; diff --git a/src/plugins/help/generalsettingspage.h b/src/plugins/help/generalsettingspage.h index e243369e46..11524abce2 100644 --- a/src/plugins/help/generalsettingspage.h +++ b/src/plugins/help/generalsettingspage.h @@ -33,6 +33,8 @@ #include "ui_generalsettingspage.h" #include <coreplugin/dialogs/ioptionspage.h> +#include <QPointer> + namespace Help { namespace Internal { @@ -45,10 +47,9 @@ class GeneralSettingsPage : public Core::IOptionsPage public: GeneralSettingsPage(); - QWidget *createPage(QWidget *parent); + QWidget *widget(); void apply(); void finish(); - bool matches(const QString &s) const; signals: void fontChanged(); @@ -79,7 +80,7 @@ private: int m_startOption; bool m_returnOnClose; - QString m_searchKeywords; + QPointer<QWidget> m_widget; Ui::GeneralSettingsPage *m_ui; }; diff --git a/src/plugins/ios/iosdeploystep.h b/src/plugins/ios/iosdeploystep.h index ea09b7e000..86a7c7c199 100644 --- a/src/plugins/ios/iosdeploystep.h +++ b/src/plugins/ios/iosdeploystep.h @@ -51,26 +51,6 @@ namespace Internal { class IosDeviceConfigListModel; class IosPackageCreationStep; -class DeployItem -{ -public: - DeployItem(const QString &_localFileName, - unsigned int _localTimeStamp, - const QString &_remoteFileName, - bool _needsStrip) - : localFileName(_localFileName), - remoteFileName(_remoteFileName), - localTimeStamp(_localTimeStamp), - remoteTimeStamp(0), - needsStrip(_needsStrip) - {} - QString localFileName; - QString remoteFileName; - unsigned int localTimeStamp; - unsigned int remoteTimeStamp; - bool needsStrip; -}; - class IosDeployStep : public ProjectExplorer::BuildStep { Q_OBJECT diff --git a/src/plugins/ios/iossettingspage.cpp b/src/plugins/ios/iossettingspage.cpp index 33cf2e2888..451afd89fd 100644 --- a/src/plugins/ios/iossettingspage.cpp +++ b/src/plugins/ios/iossettingspage.cpp @@ -48,16 +48,10 @@ IosSettingsPage::IosSettingsPage(QObject *parent) setCategoryIcon(QLatin1String(Constants::IOS_SETTINGS_CATEGORY_ICON)); } -bool IosSettingsPage::matches(const QString &searchKeyWord) const +QWidget *IosSettingsPage::widget() { - return m_keywords.contains(searchKeyWord, Qt::CaseInsensitive); -} - -QWidget *IosSettingsPage::createPage(QWidget *parent) -{ - m_widget = new IosSettingsWidget(parent); - if (m_keywords.isEmpty()) - m_keywords = m_widget->searchKeywords(); + if (!m_widget) + m_widget = new IosSettingsWidget; return m_widget; } @@ -69,6 +63,7 @@ void IosSettingsPage::apply() void IosSettingsPage::finish() { + delete m_widget; } } // namespace Internal diff --git a/src/plugins/ios/iossettingspage.h b/src/plugins/ios/iossettingspage.h index 32711977b6..f9a26cb12e 100644 --- a/src/plugins/ios/iossettingspage.h +++ b/src/plugins/ios/iossettingspage.h @@ -31,6 +31,8 @@ #include <coreplugin/dialogs/ioptionspage.h> +#include <QPointer> + namespace Ios { namespace Internal { @@ -43,14 +45,12 @@ class IosSettingsPage : public Core::IOptionsPage public: explicit IosSettingsPage(QObject *parent = 0); - bool matches(const QString &searchKeyWord) const; - QWidget *createPage(QWidget *parent); + QWidget *widget(); void apply(); void finish(); private: - QString m_keywords; - IosSettingsWidget *m_widget; + QPointer<IosSettingsWidget> m_widget; }; } // namespace Internal diff --git a/src/plugins/ios/iossettingswidget.cpp b/src/plugins/ios/iossettingswidget.cpp index 16eea3846e..6edfeec775 100644 --- a/src/plugins/ios/iossettingswidget.cpp +++ b/src/plugins/ios/iossettingswidget.cpp @@ -65,14 +65,6 @@ IosSettingsWidget::~IosSettingsWidget() delete m_ui; } -QString IosSettingsWidget::searchKeywords() const -{ - QString rc; - QTextStream(&rc) << m_ui->deviceAskCheckBox->text(); - rc.remove(QLatin1Char('&')); - return rc; -} - void IosSettingsWidget::initGui() { m_ui->setupUi(this); diff --git a/src/plugins/ios/iossettingswidget.h b/src/plugins/ios/iossettingswidget.h index 01e955c655..b7058af648 100644 --- a/src/plugins/ios/iossettingswidget.h +++ b/src/plugins/ios/iossettingswidget.h @@ -48,11 +48,10 @@ class IosSettingsWidget : public QWidget Q_OBJECT public: // Todo: This would be so much simpler if it just used Utils::PathChooser!!! - IosSettingsWidget(QWidget *parent); + IosSettingsWidget(QWidget *parent = 0); ~IosSettingsWidget(); void saveSettings(); - QString searchKeywords() const; private slots: diff --git a/src/plugins/locator/locator_test.cpp b/src/plugins/locator/locator_test.cpp index e9532fb36a..ea68c53c12 100644 --- a/src/plugins/locator/locator_test.cpp +++ b/src/plugins/locator/locator_test.cpp @@ -40,16 +40,11 @@ #include <QTextStream> #include <QtTest> -using namespace Locator::Internal::Tests; +using namespace Locator::Tests; namespace { -class MyTestDataDir : public Core::Internal::Tests::TestDataDir -{ -public: - MyTestDataDir(const QString &testDataDirectory) - : TestDataDir(QLatin1String(SRCDIR "/../../../tests/locators/") + testDataDirectory) {} -}; +QTC_DECLARE_MYTESTDATADIR("../../../tests/locators/") class MyBaseFileFilter : public Locator::BaseFileFilter { diff --git a/src/plugins/locator/locatorfiltertest.cpp b/src/plugins/locator/locatorfiltertest.cpp index a552594e3e..db5c63e4cf 100644 --- a/src/plugins/locator/locatorfiltertest.cpp +++ b/src/plugins/locator/locatorfiltertest.cpp @@ -39,8 +39,7 @@ #include <QTextStream> using namespace Locator; -using namespace Locator::Internal; -using namespace Locator::Internal::Tests; +using namespace Locator::Tests; BasicLocatorFilterTest::BasicLocatorFilterTest(ILocatorFilter *filter) : m_filter(filter) { diff --git a/src/plugins/locator/locatorfiltertest.h b/src/plugins/locator/locatorfiltertest.h index a2ddbf12fb..84217808b8 100644 --- a/src/plugins/locator/locatorfiltertest.h +++ b/src/plugins/locator/locatorfiltertest.h @@ -37,7 +37,6 @@ #include <QTest> namespace Locator { -namespace Internal { namespace Tests { /// Runs a locator filter for a search text and returns the results. @@ -77,16 +76,15 @@ public: typedef ResultData::ResultDataList ResultDataList; } // namespace Tests -} // namespace Internal } // namespace Locator -Q_DECLARE_METATYPE(Locator::Internal::Tests::ResultData) -Q_DECLARE_METATYPE(Locator::Internal::Tests::ResultDataList) +Q_DECLARE_METATYPE(Locator::Tests::ResultData) +Q_DECLARE_METATYPE(Locator::Tests::ResultDataList) QT_BEGIN_NAMESPACE namespace QTest { -template<> inline char *toString(const Locator::Internal::Tests::ResultData &data) +template<> inline char *toString(const Locator::Tests::ResultData &data) { QByteArray ba = "\"" + data.textColumn1.toUtf8() + "\", \"" + data.textColumn2.toUtf8() + "\""; return qstrdup(ba.data()); diff --git a/src/plugins/locator/settingspage.cpp b/src/plugins/locator/settingspage.cpp index 1813cbcb5e..2feec8d9a3 100644 --- a/src/plugins/locator/settingspage.cpp +++ b/src/plugins/locator/settingspage.cpp @@ -45,7 +45,7 @@ using namespace Locator; using namespace Locator::Internal; SettingsPage::SettingsPage(LocatorPlugin *plugin) - : m_plugin(plugin), m_page(0) + : m_plugin(plugin), m_widget(0) { setId(Constants::FILTER_OPTIONS_PAGE); setDisplayName(QCoreApplication::translate("Locator", Locator::Constants::FILTER_OPTIONS_PAGE)); @@ -54,33 +54,30 @@ SettingsPage::SettingsPage(LocatorPlugin *plugin) setCategoryIcon(QLatin1String(Core::Constants::SETTINGS_CATEGORY_CORE_ICON)); } -QWidget *SettingsPage::createPage(QWidget *parent) +QWidget *SettingsPage::widget() { - - m_page = new QWidget(parent); - m_ui.setupUi(m_page); - m_ui.refreshInterval->setToolTip(m_ui.refreshIntervalLabel->toolTip()); - connect(m_ui.filterList, SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)), - this, SLOT(updateButtonStates())); - connect(m_ui.filterList, SIGNAL(itemActivated(QListWidgetItem*)), - this, SLOT(configureFilter(QListWidgetItem*))); - connect(m_ui.editButton, SIGNAL(clicked()), - this, SLOT(configureFilter())); - connect(m_ui.addButton, SIGNAL(clicked()), - this, SLOT(addCustomFilter())); - connect(m_ui.removeButton, SIGNAL(clicked()), - this, SLOT(removeCustomFilter())); - - m_ui.refreshInterval->setValue(m_plugin->refreshInterval()); - m_filters = m_plugin->filters(); - m_customFilters = m_plugin->customFilters(); - saveFilterStates(); - updateFilterList(); - if (m_searchKeywords.isEmpty()) { - m_searchKeywords = m_ui.refreshIntervalLabel->text(); - m_searchKeywords.remove(QLatin1Char('&')); + if (!m_widget) { + m_widget = new QWidget; + m_ui.setupUi(m_widget); + m_ui.refreshInterval->setToolTip(m_ui.refreshIntervalLabel->toolTip()); + connect(m_ui.filterList, SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)), + this, SLOT(updateButtonStates())); + connect(m_ui.filterList, SIGNAL(itemActivated(QListWidgetItem*)), + this, SLOT(configureFilter(QListWidgetItem*))); + connect(m_ui.editButton, SIGNAL(clicked()), + this, SLOT(configureFilter())); + connect(m_ui.addButton, SIGNAL(clicked()), + this, SLOT(addCustomFilter())); + connect(m_ui.removeButton, SIGNAL(clicked()), + this, SLOT(removeCustomFilter())); + + m_ui.refreshInterval->setValue(m_plugin->refreshInterval()); + m_filters = m_plugin->filters(); + m_customFilters = m_plugin->customFilters(); + saveFilterStates(); + updateFilterList(); } - return m_page; + return m_widget; } void SettingsPage::apply() @@ -114,6 +111,7 @@ void SettingsPage::finish() m_filters.clear(); m_customFilters.clear(); m_refreshFilters.clear(); + delete m_widget; } void SettingsPage::requestRefresh() @@ -174,7 +172,7 @@ void SettingsPage::configureFilter(QListWidgetItem *item) if (!filter->isConfigurable()) return; bool needsRefresh = false; - filter->openConfigDialog(m_page, needsRefresh); + filter->openConfigDialog(m_widget, needsRefresh); if (needsRefresh && !m_refreshFilters.contains(filter)) m_refreshFilters.append(filter); updateFilterList(); @@ -184,7 +182,7 @@ void SettingsPage::addCustomFilter() { ILocatorFilter *filter = new DirectoryFilter; bool needsRefresh = false; - if (filter->openConfigDialog(m_page, needsRefresh)) { + if (filter->openConfigDialog(m_widget, needsRefresh)) { m_filters.append(filter); m_addedFilters.append(filter); m_customFilters.append(filter); @@ -210,8 +208,3 @@ void SettingsPage::removeCustomFilter() } updateFilterList(); } - -bool SettingsPage::matches(const QString &s) const -{ - return m_searchKeywords.contains(s, Qt::CaseInsensitive); -} diff --git a/src/plugins/locator/settingspage.h b/src/plugins/locator/settingspage.h index ea8e5dd523..2042d9aec6 100644 --- a/src/plugins/locator/settingspage.h +++ b/src/plugins/locator/settingspage.h @@ -32,10 +32,11 @@ #include "ui_settingspage.h" -#include <QHash> - #include <coreplugin/dialogs/ioptionspage.h> +#include <QHash> +#include <QPointer> + QT_BEGIN_NAMESPACE class QListWidgetItem; QT_END_NAMESPACE @@ -55,10 +56,9 @@ class SettingsPage : public Core::IOptionsPage public: explicit SettingsPage(LocatorPlugin *plugin); - QWidget *createPage(QWidget *parent); + QWidget *widget(); void apply(); void finish(); - bool matches(const QString &) const; private slots: void updateButtonStates(); @@ -74,14 +74,13 @@ private: Ui::SettingsWidget m_ui; LocatorPlugin *m_plugin; - QWidget *m_page; + QPointer<QWidget> m_widget; QList<ILocatorFilter *> m_filters; QList<ILocatorFilter *> m_addedFilters; QList<ILocatorFilter *> m_removedFilters; QList<ILocatorFilter *> m_customFilters; QList<ILocatorFilter *> m_refreshFilters; QHash<ILocatorFilter *, QByteArray> m_filterStates; - QString m_searchKeywords; }; } // namespace Internal diff --git a/src/plugins/macros/macrooptionspage.cpp b/src/plugins/macros/macrooptionspage.cpp index c3c259de99..c423b9c54d 100644 --- a/src/plugins/macros/macrooptionspage.cpp +++ b/src/plugins/macros/macrooptionspage.cpp @@ -53,9 +53,10 @@ MacroOptionsPage::MacroOptionsPage(QObject *parent) TextEditor::Constants::TEXT_EDITOR_SETTINGS_TR_CATEGORY)); } -QWidget *MacroOptionsPage::createPage(QWidget *parent) +QWidget *MacroOptionsPage::widget() { - m_widget = new MacroOptionsWidget(parent); + if (!m_widget) + m_widget = new MacroOptionsWidget; return m_widget; } @@ -67,5 +68,5 @@ void MacroOptionsPage::apply() void MacroOptionsPage::finish() { - // Nothing to do + delete m_widget; } diff --git a/src/plugins/macros/macrooptionspage.h b/src/plugins/macros/macrooptionspage.h index 767f6125c7..568d050baf 100644 --- a/src/plugins/macros/macrooptionspage.h +++ b/src/plugins/macros/macrooptionspage.h @@ -32,6 +32,8 @@ #include <coreplugin/dialogs/ioptionspage.h> +#include <QPointer> + namespace Macros { namespace Internal { @@ -45,12 +47,12 @@ public: MacroOptionsPage(QObject *parent = 0); // IOptionsPage implementation - QWidget *createPage(QWidget *parent); + QWidget *widget(); void apply(); void finish(); private: - Internal::MacroOptionsWidget *m_widget; + QPointer<MacroOptionsWidget> m_widget; }; } // namespace Internal diff --git a/src/plugins/mercurial/optionspage.cpp b/src/plugins/mercurial/optionspage.cpp index 3139cd61a4..390be856eb 100644 --- a/src/plugins/mercurial/optionspage.cpp +++ b/src/plugins/mercurial/optionspage.cpp @@ -68,37 +68,17 @@ void OptionsPageWidget::setSettings(const MercurialSettings &s) m_ui.timeout->setValue(s.intValue(MercurialSettings::timeoutKey)); } -QString OptionsPageWidget::searchKeywords() const -{ - QString rc; - QLatin1Char sep(' '); - QTextStream(&rc) - << sep << m_ui.configGroupBox->title() - << sep << m_ui.mercurialCommandLabel->text() - << sep << m_ui.userGroupBox->title() - << sep << m_ui.defaultUsernameLabel->text() - << sep << m_ui.defaultEmailLabel->text() - << sep << m_ui.miscGroupBox->title() - << sep << m_ui.showLogEntriesLabel->text() - << sep << m_ui.timeoutSecondsLabel->text() - ; - rc.remove(QLatin1Char('&')); - return rc; -} - OptionsPage::OptionsPage() { setId(VcsBase::Constants::VCS_ID_MERCURIAL); setDisplayName(tr("Mercurial")); } -QWidget *OptionsPage::createPage(QWidget *parent) +QWidget *OptionsPage::widget() { if (!optionsPageWidget) - optionsPageWidget = new OptionsPageWidget(parent); + optionsPageWidget = new OptionsPageWidget; optionsPageWidget->setSettings(MercurialPlugin::settings()); - if (m_searchKeywords.isEmpty()) - m_searchKeywords = optionsPageWidget->searchKeywords(); return optionsPageWidget; } @@ -115,7 +95,7 @@ void OptionsPage::apply() } } -bool OptionsPage::matches(const QString &s) const +void OptionsPage::finish() { - return m_searchKeywords.contains(s, Qt::CaseInsensitive); + delete optionsPageWidget; } diff --git a/src/plugins/mercurial/optionspage.h b/src/plugins/mercurial/optionspage.h index cb560c3d4b..828be3e396 100644 --- a/src/plugins/mercurial/optionspage.h +++ b/src/plugins/mercurial/optionspage.h @@ -51,7 +51,6 @@ public: MercurialSettings settings() const; void setSettings(const MercurialSettings &s); - QString searchKeywords() const; private: Ui::OptionsPage m_ui; @@ -65,16 +64,14 @@ class OptionsPage : public VcsBase::VcsBaseOptionsPage public: OptionsPage(); - QWidget *createPage(QWidget *parent); + QWidget *widget(); void apply(); - void finish() { } - bool matches(const QString &s) const; + void finish(); signals: void settingsChanged(); private: - QString m_searchKeywords; QPointer<OptionsPageWidget> optionsPageWidget; }; diff --git a/src/plugins/perforce/perforceplugin.cpp b/src/plugins/perforce/perforceplugin.cpp index 63872df622..99693b74eb 100644 --- a/src/plugins/perforce/perforceplugin.cpp +++ b/src/plugins/perforce/perforceplugin.cpp @@ -1185,9 +1185,8 @@ Core::IEditor *PerforcePlugin::showOutputInEditor(const QString &title, const QS e->setSuggestedFileName(s); if (codec) e->setCodec(codec); - Core::IEditor *ie = e->editor(); - Core::EditorManager::activateEditor(ie); - return ie; + Core::EditorManager::activateEditor(editor); + return editor; } void PerforcePlugin::slotSubmitDiff(const QStringList &files) diff --git a/src/plugins/perforce/perforceversioncontrol.cpp b/src/plugins/perforce/perforceversioncontrol.cpp index 970f078cab..73cda711b8 100644 --- a/src/plugins/perforce/perforceversioncontrol.cpp +++ b/src/plugins/perforce/perforceversioncontrol.cpp @@ -82,8 +82,9 @@ bool PerforceVersionControl::supportsOperation(Operation operation) const return false; } -Core::IVersionControl::OpenSupportMode PerforceVersionControl::openSupportMode() const +Core::IVersionControl::OpenSupportMode PerforceVersionControl::openSupportMode(const QString &fileName) const { + Q_UNUSED(fileName); return OpenOptional; } diff --git a/src/plugins/perforce/perforceversioncontrol.h b/src/plugins/perforce/perforceversioncontrol.h index 085eed0aab..7f9ab9d3cb 100644 --- a/src/plugins/perforce/perforceversioncontrol.h +++ b/src/plugins/perforce/perforceversioncontrol.h @@ -51,7 +51,7 @@ public: bool isConfigured() const; bool supportsOperation(Operation operation) const; - OpenSupportMode openSupportMode() const; + OpenSupportMode openSupportMode(const QString &fileName) const; bool vcsOpen(const QString &fileName); SettingsFlags settingsFlags() const; bool vcsAdd(const QString &fileName); diff --git a/src/plugins/perforce/settingspage.cpp b/src/plugins/perforce/settingspage.cpp index d2bae1dc2d..2391d5bd5d 100644 --- a/src/plugins/perforce/settingspage.cpp +++ b/src/plugins/perforce/settingspage.cpp @@ -48,6 +48,7 @@ SettingsPageWidget::SettingsPageWidget(QWidget *parent) : m_ui.setupUi(this); m_ui.errorLabel->clear(); m_ui.pathChooser->setPromptDialogTitle(tr("Perforce Command")); + m_ui.pathChooser->setHistoryCompleter(QLatin1String("Perforce.Command.History")); m_ui.pathChooser->setExpectedKind(PathChooser::Command); connect(m_ui.testPushButton, SIGNAL(clicked()), this, SLOT(slotTest())); } @@ -142,12 +143,12 @@ SettingsPage::SettingsPage() setDisplayName(tr("Perforce")); } -QWidget *SettingsPage::createPage(QWidget *parent) +QWidget *SettingsPage::widget() { - m_widget = new SettingsPageWidget(parent); - m_widget->setSettings(PerforcePlugin::settings()); - if (m_searchKeywords.isEmpty()) - m_searchKeywords = m_widget->searchKeywords(); + if (!m_widget) { + m_widget = new SettingsPageWidget; + m_widget->setSettings(PerforcePlugin::settings()); + } return m_widget; } @@ -156,7 +157,7 @@ void SettingsPage::apply() PerforcePlugin::setSettings(m_widget->settings()); } -bool SettingsPage::matches(const QString &s) const +void SettingsPage::finish() { - return m_searchKeywords.contains(s, Qt::CaseInsensitive); + delete m_widget; } diff --git a/src/plugins/perforce/settingspage.h b/src/plugins/perforce/settingspage.h index 84fba4f841..75e68e5642 100644 --- a/src/plugins/perforce/settingspage.h +++ b/src/plugins/perforce/settingspage.h @@ -49,7 +49,7 @@ class SettingsPageWidget : public QWidget Q_OBJECT public: - explicit SettingsPageWidget(QWidget *parent); + explicit SettingsPageWidget(QWidget *parent = 0); void setSettings(const PerforceSettings &); Settings settings() const; @@ -75,14 +75,12 @@ class SettingsPage : public VcsBase::VcsBaseOptionsPage public: SettingsPage(); - QWidget *createPage(QWidget *parent); + QWidget *widget(); void apply(); - void finish() { } - bool matches(const QString &) const; + void finish(); private: - QString m_searchKeywords; - SettingsPageWidget* m_widget; + QPointer<SettingsPageWidget> m_widget; }; } // namespace Internal diff --git a/src/plugins/plugins.pro b/src/plugins/plugins.pro index 31ea3da18a..4b4587e272 100644 --- a/src/plugins/plugins.pro +++ b/src/plugins/plugins.pro @@ -50,6 +50,12 @@ SUBDIRS = \ baremetal \ ios +# prefer qmake variable set on command line over env var +isEmpty(LLVM_INSTALL_DIR):LLVM_INSTALL_DIR=$$(LLVM_INSTALL_DIR) +!isEmpty(LLVM_INSTALL_DIR) { + SUBDIRS += clangcodemodel +} + isEmpty(QBS_INSTALL_DIR): QBS_INSTALL_DIR = $$(QBS_INSTALL_DIR) exists(../shared/qbs/qbs.pro)|!isEmpty(QBS_INSTALL_DIR): \ SUBDIRS += \ @@ -83,9 +89,6 @@ for(p, SUBDIRS) { $$pv = $$QTC_PLUGIN_DEPENDS } -SUBDIRS += debugger/dumper.pro linux-* { SUBDIRS += debugger/ptracepreload.pro } - -include (debugger/lldblib/guest/qtcreator-lldb.pri) diff --git a/src/plugins/plugins.qbs b/src/plugins/plugins.qbs index cf301e0ade..757ec3200f 100644 --- a/src/plugins/plugins.qbs +++ b/src/plugins/plugins.qbs @@ -11,6 +11,7 @@ Project { "bazaar/bazaar.qbs", "bineditor/bineditor.qbs", "bookmarks/bookmarks.qbs", + "clangcodemodel/clangcodemodel.qbs", "classview/classview.qbs", "clearcase/clearcase.qbs", "cmakeprojectmanager/cmakeprojectmanager.qbs", diff --git a/src/plugins/projectexplorer/appoutputpane.cpp b/src/plugins/projectexplorer/appoutputpane.cpp index 6606fd6127..6765d81689 100644 --- a/src/plugins/projectexplorer/appoutputpane.cpp +++ b/src/plugins/projectexplorer/appoutputpane.cpp @@ -101,7 +101,7 @@ void TabWidget::slotContextMenuRequested(const QPoint &pos) } AppOutputPane::RunControlTab::RunControlTab(RunControl *rc, Core::OutputWindow *w) : - runControl(rc), window(w), asyncClosing(false), behavivorOnOutput(Flash) + runControl(rc), window(w), asyncClosing(false), behaviorOnOutput(Flash) { } @@ -364,7 +364,7 @@ void AppOutputPane::appendMessage(RunControl *rc, const QString &out, Utils::Out Core::OutputWindow *window = m_runControlTabs.at(index).window; window->appendMessage(out, format); if (format != Utils::NormalMessageFormat) { - if (m_runControlTabs.at(index).behavivorOnOutput == Flash) + if (m_runControlTabs.at(index).behaviorOnOutput == Flash) flash(); else popup(NoModeSwitch); @@ -377,11 +377,11 @@ void AppOutputPane::showTabFor(RunControl *rc) m_tabWidget->setCurrentIndex(tabWidgetIndexOf(indexOf(rc))); } -void AppOutputPane::setBehaviorOnOutput(RunControl *rc, AppOutputPane::BehavivorOnOutput mode) +void AppOutputPane::setBehaviorOnOutput(RunControl *rc, AppOutputPane::BehaviorOnOutput mode) { const int index = indexOf(rc); if (index != -1) - m_runControlTabs[index].behavivorOnOutput = mode; + m_runControlTabs[index].behaviorOnOutput = mode; } void AppOutputPane::reRunRunControl() diff --git a/src/plugins/projectexplorer/appoutputpane.h b/src/plugins/projectexplorer/appoutputpane.h index 7929ea8c05..01a70c84f3 100644 --- a/src/plugins/projectexplorer/appoutputpane.h +++ b/src/plugins/projectexplorer/appoutputpane.h @@ -60,7 +60,7 @@ public: CloseTabWithPrompt }; - enum BehavivorOnOutput { + enum BehaviorOnOutput { Flash, Popup }; @@ -86,7 +86,7 @@ public: void createNewOutputWindow(RunControl *rc); void showTabFor(RunControl *rc); - void setBehaviorOnOutput(RunControl *rc, BehavivorOnOutput mode); + void setBehaviorOnOutput(RunControl *rc, BehaviorOnOutput mode); bool aboutToClose() const; bool closeTabs(CloseTabMode mode); @@ -130,7 +130,7 @@ private: Core::OutputWindow *window; // Is the run control stopping asynchronously, close the tab once it finishes bool asyncClosing; - BehavivorOnOutput behavivorOnOutput; + BehaviorOnOutput behaviorOnOutput; }; bool isRunning() const; diff --git a/src/plugins/projectexplorer/clangparser.h b/src/plugins/projectexplorer/clangparser.h index af646e70ca..dc2f6ad962 100644 --- a/src/plugins/projectexplorer/clangparser.h +++ b/src/plugins/projectexplorer/clangparser.h @@ -37,7 +37,7 @@ namespace ProjectExplorer { -class ClangParser : public ProjectExplorer::GccParser +class PROJECTEXPLORER_EXPORT ClangParser : public ProjectExplorer::GccParser { Q_OBJECT diff --git a/src/plugins/projectexplorer/customtoolchain.cpp b/src/plugins/projectexplorer/customtoolchain.cpp index 490350bdc6..104da390d4 100644 --- a/src/plugins/projectexplorer/customtoolchain.cpp +++ b/src/plugins/projectexplorer/customtoolchain.cpp @@ -501,7 +501,9 @@ CustomToolChainConfigWidget::CustomToolChainConfigWidget(CustomToolChain *tc) : m_cxx11Flags->setToolTip(tr("Comma-separated list of flags that turn on C++11 support.")); m_mkspecs->setToolTip(tr("Comma-separated list of mkspecs.")); m_compilerCommand->setExpectedKind(PathChooser::ExistingCommand); + m_compilerCommand->setHistoryCompleter(QLatin1String("PE.ToolChainCommand.History")); m_makeCommand->setExpectedKind(PathChooser::ExistingCommand); + m_makeCommand->setHistoryCompleter(QLatin1String("PE.MakeCommand.History")); m_mainLayout->addRow(tr("&Compiler path:"), m_compilerCommand); m_mainLayout->addRow(tr("&Make path:"), m_makeCommand); m_mainLayout->addRow(tr("&ABI:"), m_abiWidget); diff --git a/src/plugins/projectexplorer/customwizard/customwizardpage.cpp b/src/plugins/projectexplorer/customwizard/customwizardpage.cpp index 8fba42afce..379819824f 100644 --- a/src/plugins/projectexplorer/customwizard/customwizardpage.cpp +++ b/src/plugins/projectexplorer/customwizard/customwizardpage.cpp @@ -336,6 +336,7 @@ QWidget *CustomWizardFieldPage::registerPathChooser(const QString &fieldName, pathChooser->setExpectedKind(Utils::PathChooser::Command); else if (expectedKind == QLatin1String("any")) pathChooser->setExpectedKind(Utils::PathChooser::Any); + pathChooser->setHistoryCompleter(QString::fromLatin1("PE.Custom.") + m_parameters->id + QLatin1Char('.') + field.name); registerField(fieldName, pathChooser, "path", SIGNAL(changed(QString))); // Connect to completeChanged() for derived classes that reimplement isComplete() @@ -523,6 +524,7 @@ CustomWizardPage::CustomWizardPage(const QSharedPointer<CustomWizardContext> &ct CustomWizardFieldPage(ctx, parameters, parent), m_pathChooser(new Utils::PathChooser) { + m_pathChooser->setHistoryCompleter(QLatin1String("PE.ProjectDir.History")); addRow(tr("Path:"), m_pathChooser); connect(m_pathChooser, SIGNAL(validChanged()), this, SIGNAL(completeChanged())); } diff --git a/src/plugins/projectexplorer/customwizard/customwizardparameters.cpp b/src/plugins/projectexplorer/customwizard/customwizardparameters.cpp index 976cf733cb..f17cd3a843 100644 --- a/src/plugins/projectexplorer/customwizard/customwizardparameters.cpp +++ b/src/plugins/projectexplorer/customwizard/customwizardparameters.cpp @@ -587,6 +587,7 @@ CustomWizardParameters::ParseResult if (!booleanAttributeValue(reader, wizardEnabledAttributeC, true)) return ParseDisabled; bp->id = attributeValue(reader, idAttributeC); + id = bp->id; bp->category = attributeValue(reader, categoryAttributeC); bp->kind = kindAttribute(reader); bp->requiredFeatures = requiredFeatures(reader); diff --git a/src/plugins/projectexplorer/customwizard/customwizardparameters.h b/src/plugins/projectexplorer/customwizard/customwizardparameters.h index 3d92e6634a..c566e4979f 100644 --- a/src/plugins/projectexplorer/customwizard/customwizardparameters.h +++ b/src/plugins/projectexplorer/customwizard/customwizardparameters.h @@ -111,6 +111,7 @@ public: Core::IWizard::Data *bp, QString *errorMessage); QString toString() const; + QString id; QString directory; QString klass; QList<CustomWizardFile> files; diff --git a/src/plugins/projectexplorer/devicesupport/deviceapplicationrunner.cpp b/src/plugins/projectexplorer/devicesupport/deviceapplicationrunner.cpp index 04d0fc9b91..68c839ccc3 100644 --- a/src/plugins/projectexplorer/devicesupport/deviceapplicationrunner.cpp +++ b/src/plugins/projectexplorer/devicesupport/deviceapplicationrunner.cpp @@ -35,7 +35,6 @@ #include <utils/environment.h> #include <utils/qtcassert.h> -#include <QCoreApplication> #include <QStringList> #include <QTimer> @@ -101,8 +100,7 @@ void DeviceApplicationRunner::start(const IDevice::ConstPtr &device, } if (command.isEmpty()) { - emit reportError(QCoreApplication::translate("RemoteLinux::RemoteLinuxRunConfiguration", - "Don't know what to run.")); // FIXME: Transitional message for 3.0. + emit reportError(tr("Cannot run: No command given.")); setFinished(); return; } diff --git a/src/plugins/projectexplorer/devicesupport/devicesettingspage.cpp b/src/plugins/projectexplorer/devicesupport/devicesettingspage.cpp index d31973b04f..833b02a083 100644 --- a/src/plugins/projectexplorer/devicesupport/devicesettingspage.cpp +++ b/src/plugins/projectexplorer/devicesupport/devicesettingspage.cpp @@ -48,16 +48,10 @@ DeviceSettingsPage::DeviceSettingsPage(QObject *parent) setCategoryIcon(QLatin1String(":/projectexplorer/images/MaemoDevice.png")); } -bool DeviceSettingsPage::matches(const QString &searchKeyWord) const +QWidget *DeviceSettingsPage::widget() { - return m_keywords.contains(searchKeyWord, Qt::CaseInsensitive); -} - -QWidget *DeviceSettingsPage::createPage(QWidget *parent) -{ - m_widget = new DeviceSettingsWidget(parent); - if (m_keywords.isEmpty()) - m_keywords = m_widget->searchKeywords(); + if (!m_widget) + m_widget = new DeviceSettingsWidget; return m_widget; } @@ -68,6 +62,7 @@ void DeviceSettingsPage::apply() void DeviceSettingsPage::finish() { + delete m_widget; } } // namespace Internal diff --git a/src/plugins/projectexplorer/devicesupport/devicesettingspage.h b/src/plugins/projectexplorer/devicesupport/devicesettingspage.h index 2719694797..97c85dca13 100644 --- a/src/plugins/projectexplorer/devicesupport/devicesettingspage.h +++ b/src/plugins/projectexplorer/devicesupport/devicesettingspage.h @@ -31,6 +31,8 @@ #include <coreplugin/dialogs/ioptionspage.h> +#include <QPointer> + namespace ProjectExplorer { namespace Internal { @@ -43,14 +45,12 @@ class DeviceSettingsPage : public Core::IOptionsPage public: DeviceSettingsPage(QObject *parent = 0); - bool matches(const QString &searchKeyWord) const; - QWidget *createPage(QWidget *parent); + QWidget *widget(); void apply(); void finish(); private: - QString m_keywords; - DeviceSettingsWidget *m_widget; + QPointer<DeviceSettingsWidget> m_widget; }; } // namespace Internal diff --git a/src/plugins/projectexplorer/devicesupport/devicesettingswidget.h b/src/plugins/projectexplorer/devicesupport/devicesettingswidget.h index e69baeed2c..1d25a6e4c0 100644 --- a/src/plugins/projectexplorer/devicesupport/devicesettingswidget.h +++ b/src/plugins/projectexplorer/devicesupport/devicesettingswidget.h @@ -55,7 +55,7 @@ class DeviceSettingsWidget : public QWidget { Q_OBJECT public: - DeviceSettingsWidget(QWidget *parent); + DeviceSettingsWidget(QWidget *parent = 0); ~DeviceSettingsWidget(); void saveSettings(); diff --git a/src/plugins/projectexplorer/gccparser.h b/src/plugins/projectexplorer/gccparser.h index e46cd91c23..b426a00fb4 100644 --- a/src/plugins/projectexplorer/gccparser.h +++ b/src/plugins/projectexplorer/gccparser.h @@ -38,7 +38,7 @@ namespace ProjectExplorer { -class GccParser : public ProjectExplorer::IOutputParser +class PROJECTEXPLORER_EXPORT GccParser : public ProjectExplorer::IOutputParser { Q_OBJECT diff --git a/src/plugins/projectexplorer/gcctoolchain.cpp b/src/plugins/projectexplorer/gcctoolchain.cpp index ffef33ef95..f3dbef6285 100644 --- a/src/plugins/projectexplorer/gcctoolchain.cpp +++ b/src/plugins/projectexplorer/gcctoolchain.cpp @@ -852,6 +852,7 @@ GccToolChainConfigWidget::GccToolChainConfigWidget(GccToolChain *tc) : const QStringList gnuVersionArgs = QStringList(QLatin1String("--version")); m_compilerCommand->setExpectedKind(PathChooser::ExistingCommand); m_compilerCommand->setCommandVersionArguments(gnuVersionArgs); + m_compilerCommand->setHistoryCompleter(QLatin1String("PE.Gcc.Command.History")); m_mainLayout->addRow(tr("&Compiler path:"), m_compilerCommand); m_platformCodeGenFlagsLineEdit = new QLineEdit(this); m_platformCodeGenFlagsLineEdit->setText(QtcProcess::joinArgs(tc->platformCodeGenFlags())); diff --git a/src/plugins/projectexplorer/importwidget.cpp b/src/plugins/projectexplorer/importwidget.cpp index f39e6d11ea..eb7e34ee2e 100644 --- a/src/plugins/projectexplorer/importwidget.cpp +++ b/src/plugins/projectexplorer/importwidget.cpp @@ -59,6 +59,7 @@ ImportWidget::ImportWidget(QWidget *parent) : layout->addWidget(m_pathChooser); m_pathChooser->setExpectedKind(Utils::PathChooser::ExistingDirectory); + m_pathChooser->setHistoryCompleter(QLatin1String("SourceDir.History")); QPushButton *importButton = new QPushButton(tr("Import"), widget); layout->addWidget(importButton); diff --git a/src/plugins/projectexplorer/kit.cpp b/src/plugins/projectexplorer/kit.cpp index 208fdeaac0..1e25cbdfee 100644 --- a/src/plugins/projectexplorer/kit.cpp +++ b/src/plugins/projectexplorer/kit.cpp @@ -49,6 +49,7 @@ namespace { const char ID_KEY[] = "PE.Profile.Id"; const char DISPLAYNAME_KEY[] = "PE.Profile.Name"; const char AUTODETECTED_KEY[] = "PE.Profile.AutoDetected"; +const char AUTODETECTIONSOURCE_KEY[] = "PE.Profile.AutoDetectionSource"; const char SDK_PROVIDED_KEY[] = "PE.Profile.SDK"; const char DATA_KEY[] = "PE.Profile.Data"; const char ICON_KEY[] = "PE.Profile.Icon"; @@ -71,6 +72,7 @@ public: m_id(id), m_nestedBlockingLevel(0), m_autodetected(false), + m_autoDetectionSource(QString()), m_sdkProvided(false), m_isValid(true), m_hasWarning(false), @@ -89,6 +91,7 @@ public: Id m_id; int m_nestedBlockingLevel; bool m_autodetected; + QString m_autoDetectionSource; bool m_sdkProvided; bool m_isValid; bool m_hasWarning; @@ -124,6 +127,7 @@ Kit::Kit(const QVariantMap &data) : d->m_id = Id::fromSetting(data.value(QLatin1String(ID_KEY))); d->m_autodetected = data.value(QLatin1String(AUTODETECTED_KEY)).toBool(); + d->m_autoDetectionSource = data.value(QLatin1String(AUTODETECTIONSOURCE_KEY)).toString(); // if we don't have that setting assume that autodetected implies sdk QVariant value = data.value(QLatin1String(SDK_PROVIDED_KEY)); @@ -197,6 +201,7 @@ void Kit::copyFrom(const Kit *k) d->m_iconPath = k->d->m_iconPath; d->m_icon = k->d->m_icon; d->m_autodetected = k->d->m_autodetected; + d->m_autoDetectionSource = k->d->m_autoDetectionSource; d->m_displayName = k->d->m_displayName; d->m_mustNotify = true; d->m_mustNotifyAboutDisplayName = true; @@ -321,6 +326,11 @@ bool Kit::isAutoDetected() const return d->m_autodetected; } +QString Kit::autoDetectionSource() const +{ + return d->m_autoDetectionSource; +} + bool Kit::isSdkProvided() const { return d->m_sdkProvided; @@ -418,11 +428,12 @@ QVariantMap Kit::toMap() const data.insert(QLatin1String(ID_KEY), QString::fromLatin1(d->m_id.name())); data.insert(QLatin1String(DISPLAYNAME_KEY), d->m_displayName); data.insert(QLatin1String(AUTODETECTED_KEY), d->m_autodetected); + data.insert(QLatin1String(AUTODETECTIONSOURCE_KEY), d->m_autoDetectionSource); data.insert(QLatin1String(SDK_PROVIDED_KEY), d->m_sdkProvided); data.insert(QLatin1String(ICON_KEY), d->m_iconPath.toString()); QStringList mutableInfo; - foreach (const Core::Id &id, d->m_mutable.values()) + foreach (const Core::Id &id, d->m_mutable) mutableInfo << id.toString(); data.insert(QLatin1String(MUTABLE_INFO_KEY), mutableInfo); @@ -496,6 +507,11 @@ void Kit::setAutoDetected(bool detected) d->m_autodetected = detected; } +void Kit::setAutoDetectionSource(const QString &autoDetectionSource) +{ + d->m_autoDetectionSource = autoDetectionSource; +} + void Kit::setSdkProvided(bool sdkProvided) { d->m_sdkProvided = sdkProvided; diff --git a/src/plugins/projectexplorer/kit.h b/src/plugins/projectexplorer/kit.h index 284babf3e1..d8e3adc27e 100644 --- a/src/plugins/projectexplorer/kit.h +++ b/src/plugins/projectexplorer/kit.h @@ -76,6 +76,7 @@ public: QString fileSystemFriendlyName() const; bool isAutoDetected() const; + QString autoDetectionSource() const; bool isSdkProvided() const; Core::Id id() const; @@ -102,6 +103,7 @@ public: // Note: Stickyness is *not* saved! void setAutoDetected(bool detected); + void setAutoDetectionSource(const QString &autoDetectionSource); void makeSticky(); void setSticky(Core::Id id, bool b); void makeUnSticky(); diff --git a/src/plugins/projectexplorer/kitinformationconfigwidget.cpp b/src/plugins/projectexplorer/kitinformationconfigwidget.cpp index 98bcc340c8..e93b8cb4f1 100644 --- a/src/plugins/projectexplorer/kitinformationconfigwidget.cpp +++ b/src/plugins/projectexplorer/kitinformationconfigwidget.cpp @@ -61,6 +61,7 @@ SysRootInformationConfigWidget::SysRootInformationConfigWidget(Kit *k, const Kit { m_chooser = new Utils::PathChooser; m_chooser->setExpectedKind(Utils::PathChooser::ExistingDirectory); + m_chooser->setHistoryCompleter(QLatin1String("PE.SysRoot.History")); m_chooser->setFileName(SysRootKitInformation::sysRoot(k)); connect(m_chooser, SIGNAL(changed(QString)), this, SLOT(pathWasChanged())); } diff --git a/src/plugins/projectexplorer/kitoptionspage.cpp b/src/plugins/projectexplorer/kitoptionspage.cpp index 7f81bdbb23..4e81d40ca8 100644 --- a/src/plugins/projectexplorer/kitoptionspage.cpp +++ b/src/plugins/projectexplorer/kitoptionspage.cpp @@ -59,76 +59,75 @@ KitOptionsPage::KitOptionsPage() : setCategoryIcon(QLatin1String(Constants::PROJECTEXPLORER_SETTINGS_CATEGORY_ICON)); } -QWidget *KitOptionsPage::createPage(QWidget *parent) +QWidget *KitOptionsPage::widget() { - m_configWidget = new QWidget(parent); + if (!m_configWidget) { + m_configWidget = new QWidget; - m_kitsView = new QTreeView(m_configWidget); - m_kitsView->setUniformRowHeights(true); - m_kitsView->header()->setStretchLastSection(true); - m_kitsView->setSizePolicy(m_kitsView->sizePolicy().horizontalPolicy(), + m_kitsView = new QTreeView(m_configWidget); + m_kitsView->setUniformRowHeights(true); + m_kitsView->header()->setStretchLastSection(true); + m_kitsView->setSizePolicy(m_kitsView->sizePolicy().horizontalPolicy(), QSizePolicy::Ignored); - m_addButton = new QPushButton(tr("Add"), m_configWidget); - m_cloneButton = new QPushButton(tr("Clone"), m_configWidget); - m_delButton = new QPushButton(tr("Remove"), m_configWidget); - m_makeDefaultButton = new QPushButton(tr("Make Default"), m_configWidget); - - QVBoxLayout *buttonLayout = new QVBoxLayout(); - buttonLayout->setSpacing(6); - buttonLayout->setContentsMargins(0, 0, 0, 0); - buttonLayout->addWidget(m_addButton); - buttonLayout->addWidget(m_cloneButton); - buttonLayout->addWidget(m_delButton); - buttonLayout->addWidget(m_makeDefaultButton); - buttonLayout->addStretch(); - - QHBoxLayout *horizontalLayout = new QHBoxLayout(); - horizontalLayout->addWidget(m_kitsView); - horizontalLayout->addLayout(buttonLayout); - - QVBoxLayout *verticalLayout = new QVBoxLayout(m_configWidget); - verticalLayout->addLayout(horizontalLayout); - - m_model = new Internal::KitModel(verticalLayout); - connect(m_model, SIGNAL(kitStateChanged()), this, SLOT(updateState())); - verticalLayout->setStretch(0, 1); - verticalLayout->setStretch(1, 0); - - m_kitsView->setModel(m_model); - m_kitsView->header()->setResizeMode(0, QHeaderView::Stretch); - m_kitsView->expandAll(); - - m_selectionModel = m_kitsView->selectionModel(); - connect(m_selectionModel, SIGNAL(selectionChanged(QItemSelection,QItemSelection)), - this, SLOT(kitSelectionChanged())); - connect(KitManager::instance(), SIGNAL(kitAdded(ProjectExplorer::Kit*)), - this, SLOT(kitSelectionChanged())); - connect(KitManager::instance(), SIGNAL(kitRemoved(ProjectExplorer::Kit*)), - this, SLOT(kitSelectionChanged())); - connect(KitManager::instance(), SIGNAL(kitUpdated(ProjectExplorer::Kit*)), - this, SLOT(kitSelectionChanged())); - - // Set up add menu: - connect(m_addButton, SIGNAL(clicked()), this, SLOT(addNewKit())); - connect(m_cloneButton, SIGNAL(clicked()), this, SLOT(cloneKit())); - connect(m_delButton, SIGNAL(clicked()), this, SLOT(removeKit())); - connect(m_makeDefaultButton, SIGNAL(clicked()), this, SLOT(makeDefaultKit())); - - m_searchKeywords = tr("Kits"); - - updateState(); - - if (m_toShow) { - QModelIndex index = m_model->indexOf(m_toShow); - m_selectionModel->select(index, - QItemSelectionModel::Clear - | QItemSelectionModel::SelectCurrent - | QItemSelectionModel::Rows); - m_kitsView->scrollTo(index); + m_addButton = new QPushButton(tr("Add"), m_configWidget); + m_cloneButton = new QPushButton(tr("Clone"), m_configWidget); + m_delButton = new QPushButton(tr("Remove"), m_configWidget); + m_makeDefaultButton = new QPushButton(tr("Make Default"), m_configWidget); + + QVBoxLayout *buttonLayout = new QVBoxLayout(); + buttonLayout->setSpacing(6); + buttonLayout->setContentsMargins(0, 0, 0, 0); + buttonLayout->addWidget(m_addButton); + buttonLayout->addWidget(m_cloneButton); + buttonLayout->addWidget(m_delButton); + buttonLayout->addWidget(m_makeDefaultButton); + buttonLayout->addStretch(); + + QHBoxLayout *horizontalLayout = new QHBoxLayout(); + horizontalLayout->addWidget(m_kitsView); + horizontalLayout->addLayout(buttonLayout); + + QVBoxLayout *verticalLayout = new QVBoxLayout(m_configWidget); + verticalLayout->addLayout(horizontalLayout); + + m_model = new Internal::KitModel(verticalLayout); + connect(m_model, SIGNAL(kitStateChanged()), this, SLOT(updateState())); + verticalLayout->setStretch(0, 1); + verticalLayout->setStretch(1, 0); + + m_kitsView->setModel(m_model); + m_kitsView->header()->setResizeMode(0, QHeaderView::Stretch); + m_kitsView->expandAll(); + + m_selectionModel = m_kitsView->selectionModel(); + connect(m_selectionModel, SIGNAL(selectionChanged(QItemSelection,QItemSelection)), + this, SLOT(kitSelectionChanged())); + connect(KitManager::instance(), SIGNAL(kitAdded(ProjectExplorer::Kit*)), + this, SLOT(kitSelectionChanged())); + connect(KitManager::instance(), SIGNAL(kitRemoved(ProjectExplorer::Kit*)), + this, SLOT(kitSelectionChanged())); + connect(KitManager::instance(), SIGNAL(kitUpdated(ProjectExplorer::Kit*)), + this, SLOT(kitSelectionChanged())); + + // Set up add menu: + connect(m_addButton, SIGNAL(clicked()), this, SLOT(addNewKit())); + connect(m_cloneButton, SIGNAL(clicked()), this, SLOT(cloneKit())); + connect(m_delButton, SIGNAL(clicked()), this, SLOT(removeKit())); + connect(m_makeDefaultButton, SIGNAL(clicked()), this, SLOT(makeDefaultKit())); + + updateState(); + + if (m_toShow) { + QModelIndex index = m_model->indexOf(m_toShow); + m_selectionModel->select(index, + QItemSelectionModel::Clear + | QItemSelectionModel::SelectCurrent + | QItemSelectionModel::Rows); + m_kitsView->scrollTo(index); + } + m_toShow = 0; } - m_toShow = 0; - return m_configWidget; } @@ -145,18 +144,13 @@ void KitOptionsPage::finish() m_model = 0; } - m_configWidget = 0; // deleted by settingsdialog + delete m_configWidget; m_selectionModel = 0; // child of m_configWidget m_kitsView = 0; // child of m_configWidget m_currentWidget = 0; // deleted by the model m_toShow = 0; } -bool KitOptionsPage::matches(const QString &s) const -{ - return m_searchKeywords.contains(s, Qt::CaseInsensitive); -} - void KitOptionsPage::showKit(Kit *k) { m_toShow = k; diff --git a/src/plugins/projectexplorer/kitoptionspage.h b/src/plugins/projectexplorer/kitoptionspage.h index f08fe36b54..2f13b8bd41 100644 --- a/src/plugins/projectexplorer/kitoptionspage.h +++ b/src/plugins/projectexplorer/kitoptionspage.h @@ -35,6 +35,7 @@ #include <coreplugin/dialogs/ioptionspage.h> #include <QModelIndex> +#include <QPointer> QT_BEGIN_NAMESPACE class QItemSelectionModel; @@ -59,10 +60,9 @@ class PROJECTEXPLORER_EXPORT KitOptionsPage : public Core::IOptionsPage public: KitOptionsPage(); - QWidget *createPage(QWidget *parent); + QWidget *widget(); void apply(); void finish(); - bool matches(const QString &) const; void showKit(Kit *k); @@ -83,8 +83,7 @@ private: QPushButton *m_delButton; QPushButton *m_makeDefaultButton; - QWidget *m_configWidget; - QString m_searchKeywords; + QPointer<QWidget> m_configWidget; Internal::KitModel *m_model; QItemSelectionModel *m_selectionModel; diff --git a/src/plugins/projectexplorer/localapplicationrunconfiguration.h b/src/plugins/projectexplorer/localapplicationrunconfiguration.h index 6b80726060..415a17c003 100644 --- a/src/plugins/projectexplorer/localapplicationrunconfiguration.h +++ b/src/plugins/projectexplorer/localapplicationrunconfiguration.h @@ -54,8 +54,6 @@ public: virtual RunMode runMode() const = 0; virtual QString workingDirectory() const = 0; virtual QString commandLineArguments() const = 0; - virtual QString dumperLibrary() const = 0; - virtual QStringList dumperLibraryLocations() const = 0; virtual void addToBaseEnvironment(Utils::Environment &env) const; diff --git a/src/plugins/projectexplorer/msvcparser.h b/src/plugins/projectexplorer/msvcparser.h index 19f9aaf4cc..4576e86b00 100644 --- a/src/plugins/projectexplorer/msvcparser.h +++ b/src/plugins/projectexplorer/msvcparser.h @@ -38,7 +38,7 @@ namespace ProjectExplorer { -class MsvcParser : public ProjectExplorer::IOutputParser +class PROJECTEXPLORER_EXPORT MsvcParser : public ProjectExplorer::IOutputParser { Q_OBJECT diff --git a/src/plugins/projectexplorer/osparser.h b/src/plugins/projectexplorer/osparser.h index aa1261017b..cf8cf7d442 100644 --- a/src/plugins/projectexplorer/osparser.h +++ b/src/plugins/projectexplorer/osparser.h @@ -38,7 +38,7 @@ namespace ProjectExplorer { -class OsParser : public ProjectExplorer::IOutputParser +class PROJECTEXPLORER_EXPORT OsParser : public ProjectExplorer::IOutputParser { Q_OBJECT diff --git a/src/plugins/projectexplorer/processstep.cpp b/src/plugins/projectexplorer/processstep.cpp index 3f19933814..3d5376675b 100644 --- a/src/plugins/projectexplorer/processstep.cpp +++ b/src/plugins/projectexplorer/processstep.cpp @@ -236,6 +236,7 @@ ProcessStepConfigWidget::ProcessStepConfigWidget(ProcessStep *step) { m_ui.setupUi(this); m_ui.command->setExpectedKind(Utils::PathChooser::Command); + m_ui.command->setHistoryCompleter(QLatin1String("PE.ProcessStepCommand.History")); m_ui.workingDirectory->setExpectedKind(Utils::PathChooser::Directory); BuildConfiguration *bc = m_step->buildConfiguration(); diff --git a/src/plugins/projectexplorer/project.cpp b/src/plugins/projectexplorer/project.cpp index 2e2247b286..e95d6b9878 100644 --- a/src/plugins/projectexplorer/project.cpp +++ b/src/plugins/projectexplorer/project.cpp @@ -84,6 +84,7 @@ public: ProjectPrivate(); ~ProjectPrivate(); + Core::Id m_id; QList<Target *> m_targets; Target *m_activeTarget; EditorConfiguration *m_editorConfiguration; @@ -112,6 +113,12 @@ Project::~Project() delete d; } +Core::Id Project::id() const +{ + QTC_CHECK(d->m_id.isValid()); + return d->m_id; +} + QString Project::projectFilePath() const { return document()->filePath(); @@ -262,6 +269,11 @@ bool Project::setupTarget(Target *t) return true; } +void Project::setId(Core::Id id) +{ + d->m_id = id; +} + Target *Project::restoreTarget(const QVariantMap &data) { Core::Id id = idFromMap(data); diff --git a/src/plugins/projectexplorer/project.h b/src/plugins/projectexplorer/project.h index 09dd4c9a90..ba88092572 100644 --- a/src/plugins/projectexplorer/project.h +++ b/src/plugins/projectexplorer/project.h @@ -73,7 +73,7 @@ public: virtual ~Project(); virtual QString displayName() const = 0; - virtual Core::Id id() const = 0; + Core::Id id() const; virtual Core::IDocument *document() const = 0; virtual IProjectManager *projectManager() const = 0; @@ -161,6 +161,7 @@ protected: virtual bool fromMap(const QVariantMap &map); virtual bool setupTarget(Target *t); + void setId(Core::Id id); void setProjectContext(Core::Context context); void setProjectLanguages(Core::Context language); void addProjectLanguage(Core::Id id); diff --git a/src/plugins/projectexplorer/projectexplorer.cpp b/src/plugins/projectexplorer/projectexplorer.cpp index d6d7dd36af..7fe089e91f 100644 --- a/src/plugins/projectexplorer/projectexplorer.cpp +++ b/src/plugins/projectexplorer/projectexplorer.cpp @@ -76,6 +76,7 @@ #include "miniprojecttargetselector.h" #include "taskhub.h" #include "customtoolchain.h" +#include "selectablefilesmodel.h" #include <projectexplorer/customwizard/customwizard.h> #include "devicesupport/desktopdevice.h" #include "devicesupport/desktopdevicefactory.h" @@ -199,6 +200,7 @@ struct ProjectExplorerPluginPrivate { QAction *m_cancelBuildAction; QAction *m_addNewFileAction; QAction *m_addExistingFilesAction; + QAction *m_addExistingDirectoryAction; QAction *m_addNewSubprojectAction; QAction *m_removeFileAction; QAction *m_removeProjectAction; @@ -539,7 +541,7 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er d->m_openWithMenu->setTitle(tr("Open With")); connect(d->m_openWithMenu, SIGNAL(triggered(QAction*)), - DocumentManager::instance(), SLOT(slotExecuteOpenWithMenuAction(QAction*))); + DocumentManager::instance(), SLOT(executeOpenWithMenuAction(QAction*))); // // Separators @@ -796,6 +798,15 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er msubProjectContextMenu->addAction(cmd, Constants::G_PROJECT_FILES); mfolderContextMenu->addAction(cmd, Constants::G_FOLDER_FILES); + // add existing directory action + d->m_addExistingDirectoryAction = new QAction(tr("Add Existing Directory..."), this); + cmd = Core::ActionManager::registerAction(d->m_addExistingDirectoryAction, + ProjectExplorer::Constants::ADDEXISTINGDIRECTORY, + projecTreeContext); + mprojectContextMenu->addAction(cmd, Constants::G_PROJECT_FILES); + msubProjectContextMenu->addAction(cmd, Constants::G_PROJECT_FILES); + mfolderContextMenu->addAction(cmd, Constants::G_FOLDER_FILES); + // new subproject action d->m_addNewSubprojectAction = new QAction(tr("New Subproject..."), this); cmd = ActionManager::registerAction(d->m_addNewSubprojectAction, ProjectExplorer::Constants::ADDNEWSUBPROJECT, @@ -963,6 +974,7 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er connect(d->m_closeAllProjects, SIGNAL(triggered()), this, SLOT(closeAllProjects())); connect(d->m_addNewFileAction, SIGNAL(triggered()), this, SLOT(addNewFile())); connect(d->m_addExistingFilesAction, SIGNAL(triggered()), this, SLOT(addExistingFiles())); + connect(d->m_addExistingDirectoryAction, SIGNAL(triggered()), this, SLOT(addExistingDirectory())); connect(d->m_addNewSubprojectAction, SIGNAL(triggered()), this, SLOT(addNewSubproject())); connect(d->m_removeProjectAction, SIGNAL(triggered()), this, SLOT(removeProject())); connect(d->m_openFileAction, SIGNAL(triggered()), this, SLOT(openFile())); @@ -1373,8 +1385,10 @@ QList<Project *> ProjectExplorerPlugin::openProjects(const QStringList &fileName } if (const MimeType mt = MimeDatabase::findByFile(QFileInfo(fileName))) { + bool foundProjectManager = false; foreach (IProjectManager *manager, projectManagers) { if (manager->mimeType() == mt.type()) { + foundProjectManager = true; QString tmp; if (Project *pro = manager->openProject(filePath, &tmp)) { if (pro->restoreSettings()) { @@ -1385,6 +1399,8 @@ QList<Project *> ProjectExplorerPlugin::openProjects(const QStringList &fileName setCurrentNode(pro->rootProjectNode()); openedPro += pro; } else { + appendError(errorString, tr("Failed opening project '%1': Settings could not be restored") + .arg(QDir::toNativeSeparators(fileName))); delete pro; } } @@ -1393,6 +1409,14 @@ QList<Project *> ProjectExplorerPlugin::openProjects(const QStringList &fileName break; } } + if (!foundProjectManager) { + appendError(errorString, tr("Failed opening project '%1': No plugin can open project type '%2'.") + .arg(QDir::toNativeSeparators(fileName)) + .arg((mt.type()))); + } + } else { + appendError(errorString, tr("Failed opening project '%1': Unknown project type.") + .arg(QDir::toNativeSeparators(fileName))); } SessionManager::reportProjectLoadingProgress(); } @@ -2632,6 +2656,7 @@ void ProjectExplorerPlugin::invalidateProject(Project *project) void ProjectExplorerPlugin::updateContextMenuActions() { d->m_addExistingFilesAction->setEnabled(false); + d->m_addExistingDirectoryAction->setEnabled(false); d->m_addNewFileAction->setEnabled(false); d->m_addNewSubprojectAction->setEnabled(false); d->m_removeFileAction->setEnabled(false); @@ -2639,6 +2664,7 @@ void ProjectExplorerPlugin::updateContextMenuActions() d->m_renameFileAction->setEnabled(false); d->m_addExistingFilesAction->setVisible(true); + d->m_addExistingDirectoryAction->setVisible(true); d->m_removeFileAction->setVisible(true); d->m_deleteFileAction->setVisible(true); d->m_runActionContextMenu->setVisible(false); @@ -2676,6 +2702,7 @@ void ProjectExplorerPlugin::updateContextMenuActions() d->m_addNewSubprojectAction->setEnabled(d->m_currentNode->nodeType() == ProjectNodeType && actions.contains(ProjectNode::AddSubProject)); d->m_addExistingFilesAction->setEnabled(actions.contains(ProjectNode::AddExistingFile)); + d->m_addExistingDirectoryAction->setEnabled(actions.contains(ProjectNode::AddExistingDirectory)); d->m_renameFileAction->setEnabled(actions.contains(ProjectNode::Rename)); } else if (qobject_cast<FileNode*>(d->m_currentNode)) { // Enable and show remove / delete in magic ways: @@ -2785,6 +2812,17 @@ void ProjectExplorerPlugin::addExistingFiles() addExistingFiles(fileNames); } +void ProjectExplorerPlugin::addExistingDirectory() +{ + QTC_ASSERT(d->m_currentNode, return); + + const QString path = QFileInfo(d->m_currentNode->path()).absolutePath(); + SelectableFilesDialogAddDirectory dialog(path, QStringList(), Core::ICore::mainWindow()); + + if (dialog.exec() == QDialog::Accepted) + addExistingFiles(dialog.selectedFiles()); +} + void ProjectExplorerPlugin::addExistingFiles(const QStringList &filePaths) { ProjectNode *projectNode = qobject_cast<ProjectNode*>(d->m_currentNode->projectNode()); diff --git a/src/plugins/projectexplorer/projectexplorer.h b/src/plugins/projectexplorer/projectexplorer.h index 1773b9c4e8..db03ae0045 100644 --- a/src/plugins/projectexplorer/projectexplorer.h +++ b/src/plugins/projectexplorer/projectexplorer.h @@ -186,6 +186,7 @@ private slots: void addNewFile(); void addExistingFiles(); + void addExistingDirectory(); void addNewSubproject(); void removeProject(); void openFile(); diff --git a/src/plugins/projectexplorer/projectexplorer.pro b/src/plugins/projectexplorer/projectexplorer.pro index 16eee949d3..03daf8b229 100644 --- a/src/plugins/projectexplorer/projectexplorer.pro +++ b/src/plugins/projectexplorer/projectexplorer.pro @@ -141,7 +141,8 @@ HEADERS += projectexplorer.h \ projectmacroexpander.h \ customparser.h \ customparserconfigdialog.h \ - ipotentialkit.h + ipotentialkit.h \ + selectablefilesmodel.h SOURCES += projectexplorer.cpp \ abi.cpp \ @@ -269,7 +270,8 @@ SOURCES += projectexplorer.cpp \ projectmacroexpander.cpp \ customparser.cpp \ customparserconfigdialog.cpp \ - ipotentialkit.cpp + ipotentialkit.cpp \ + selectablefilesmodel.cpp FORMS += processstep.ui \ editorsettingspropertiespage.ui \ diff --git a/src/plugins/projectexplorer/projectexplorer.qbs b/src/plugins/projectexplorer/projectexplorer.qbs index 357a4c5351..1a10feb66d 100644 --- a/src/plugins/projectexplorer/projectexplorer.qbs +++ b/src/plugins/projectexplorer/projectexplorer.qbs @@ -121,6 +121,7 @@ QtcPlugin { "runconfiguration.cpp", "runconfiguration.h", "runconfigurationmodel.cpp", "runconfigurationmodel.h", "runsettingspropertiespage.cpp", "runsettingspropertiespage.h", + "selectablefilesmodel.cpp", "selectablefilesmodel.h", "session.cpp", "session.h", "sessiondialog.cpp", "sessiondialog.h", "sessiondialog.ui", "settingsaccessor.cpp", "settingsaccessor.h", @@ -141,7 +142,7 @@ QtcPlugin { "toolchainmanager.cpp", "toolchainmanager.h", "toolchainoptionspage.cpp", "toolchainoptionspage.h", "unconfiguredprojectpanel.cpp", "unconfiguredprojectpanel.h", - "vcsannotatetaskhandler.cpp", "vcsannotatetaskhandler.h", + "vcsannotatetaskhandler.cpp", "vcsannotatetaskhandler.h" ] } diff --git a/src/plugins/projectexplorer/projectexplorerconstants.h b/src/plugins/projectexplorer/projectexplorerconstants.h index 1f4c941552..0b1f8fc9f3 100644 --- a/src/plugins/projectexplorer/projectexplorerconstants.h +++ b/src/plugins/projectexplorer/projectexplorerconstants.h @@ -69,6 +69,7 @@ const char RUNCONTEXTMENU[] = "ProjectExplorer.RunContextMenu"; const char STOP[] = "ProjectExplorer.Stop"; const char ADDNEWFILE[] = "ProjectExplorer.AddNewFile"; const char ADDEXISTINGFILES[] = "ProjectExplorer.AddExistingFiles"; +const char ADDEXISTINGDIRECTORY[] = "ProjectExplorer.AddExistingDirectory"; const char ADDNEWSUBPROJECT[] = "ProjectExplorer.AddNewSubproject"; const char REMOVEPROJECT[] = "ProjectExplorer.RemoveProject"; const char OPENFILE[] = "ProjectExplorer.OpenFile"; @@ -245,6 +246,12 @@ const char VAR_CURRENTBUILD_TYPE[] = "CurrentBuild:Type"; const char VAR_CURRENTSESSION_PREFIX[] = "CurrentSession"; const char VAR_CURRENTSESSION_NAME[] = "CurrentSession:Name"; +const char HIDE_FILE_FILTER_SETTING[] = "GenericProject/FileFilter"; +const char HIDE_FILE_FILTER_DEFAULT[] = "Makefile*; *.o; *.obj; *~; *.files; *.config; *.creator; *.user; *.includes; *.autosave"; + +const char SHOW_FILE_FILTER_SETTING[] = "GenericProject/ShowFileFilter"; +const char SHOW_FILE_FILTER_DEFAULT[] = "*.c; *.cc; *.cpp; *.cp; *.cxx; *.c++; *.h; *.hh; *.hpp; *.hxx;"; + // Unconfigured Panel const char UNCONFIGURED_PANEL_PAGE_ID[] = "UnconfiguredPanel"; diff --git a/src/plugins/projectexplorer/projectexplorersettingspage.cpp b/src/plugins/projectexplorer/projectexplorersettingspage.cpp index 7afd153495..b231e4fcb1 100644 --- a/src/plugins/projectexplorer/projectexplorersettingspage.cpp +++ b/src/plugins/projectexplorer/projectexplorersettingspage.cpp @@ -150,29 +150,6 @@ void ProjectExplorerSettingsWidget::updateResetButton() m_ui.resetButton->setEnabled(buildDirectory() != QLatin1String(Core::Constants::DEFAULT_BUILD_DIRECTORY)); } -QString ProjectExplorerSettingsWidget::searchKeywords() const -{ - if (m_searchKeywords.isEmpty()) { - QLatin1Char sep(' '); - m_searchKeywords = m_ui.directoryGroupBox->title() - + sep + m_ui.currentDirectoryRadioButton->text() - + sep + m_ui.directoryRadioButton->text() - + sep + m_ui.buildAndRunGroupBox->title() - + sep + m_ui.saveAllFilesCheckBox->text() - + sep + m_ui.buildProjectBeforeDeployCheckBox->text() - + sep + m_ui.deployProjectBeforeRunCheckBox->text() - + sep + m_ui.showCompileOutputCheckBox->text() - + sep + m_ui.cleanOldAppOutputCheckBox->text() - + sep + m_ui.mergeStdErrAndStdOutCheckBox->text() - + sep + m_ui.mergeStdErrAndStdOutCheckBox->toolTip() - + sep + m_ui.wrapAppOutputCheckBox->text() - + sep + m_ui.jomLabel->text() - ; - m_searchKeywords.remove(QLatin1Char('&')); - } - return m_searchKeywords; -} - // ------------------ ProjectExplorerSettingsPage ProjectExplorerSettingsPage::ProjectExplorerSettingsPage() { @@ -188,15 +165,15 @@ ProjectExplorerSettingsPage::~ProjectExplorerSettingsPage() { } -QWidget *ProjectExplorerSettingsPage::createPage(QWidget *parent) +QWidget *ProjectExplorerSettingsPage::widget() { - m_widget = new ProjectExplorerSettingsWidget(parent); - m_widget->setSettings(ProjectExplorerPlugin::projectExplorerSettings()); - m_widget->setProjectsDirectory(Core::DocumentManager::projectsDirectory()); - m_widget->setUseProjectsDirectory(Core::DocumentManager::useProjectsDirectory()); - m_widget->setBuildDirectory(Core::DocumentManager::buildDirectory()); - if (m_searchKeywords.isEmpty()) - m_searchKeywords = m_widget->searchKeywords(); + if (!m_widget) { + m_widget = new ProjectExplorerSettingsWidget; + m_widget->setSettings(ProjectExplorerPlugin::projectExplorerSettings()); + m_widget->setProjectsDirectory(Core::DocumentManager::projectsDirectory()); + m_widget->setUseProjectsDirectory(Core::DocumentManager::useProjectsDirectory()); + m_widget->setBuildDirectory(Core::DocumentManager::buildDirectory()); + } return m_widget; } @@ -212,12 +189,7 @@ void ProjectExplorerSettingsPage::apply() void ProjectExplorerSettingsPage::finish() { - // Nothing to do -} - -bool ProjectExplorerSettingsPage::matches(const QString &s) const -{ - return m_searchKeywords.contains(s, Qt::CaseInsensitive); + delete m_widget; } } // namespace Internal diff --git a/src/plugins/projectexplorer/projectexplorersettingspage.h b/src/plugins/projectexplorer/projectexplorersettingspage.h index 5767ba6c73..c94fa16a52 100644 --- a/src/plugins/projectexplorer/projectexplorersettingspage.h +++ b/src/plugins/projectexplorer/projectexplorersettingspage.h @@ -59,8 +59,6 @@ public: QString buildDirectory() const; void setBuildDirectory(const QString &bd); - QString searchKeywords() const; - private slots: void slotDirectoryButtonGroupChanged(); void resetDefaultBuildDirectory(); @@ -70,7 +68,6 @@ private: void setJomVisible(bool); Ui::ProjectExplorerSettingsPageUi m_ui; - mutable QString m_searchKeywords; QUuid m_environmentId; }; @@ -82,13 +79,11 @@ public: ProjectExplorerSettingsPage(); ~ProjectExplorerSettingsPage(); - QWidget *createPage(QWidget *parent); + QWidget *widget(); void apply(); void finish(); - bool matches(const QString &s) const; private: - QString m_searchKeywords; QPointer<ProjectExplorerSettingsWidget> m_widget; }; diff --git a/src/plugins/projectexplorer/projectnodes.h b/src/plugins/projectexplorer/projectnodes.h index ce180cbb10..c351fdebf7 100644 --- a/src/plugins/projectexplorer/projectnodes.h +++ b/src/plugins/projectexplorer/projectnodes.h @@ -184,6 +184,9 @@ public: // the file is added AddNewFile, AddExistingFile, + // Add files, which match user defined filters, + // from an existing directory and its subdirectories + AddExistingDirectory, // Removes a file from the project, optionally also // delete it on disc RemoveFile, diff --git a/src/plugins/genericprojectmanager/selectablefilesmodel.cpp b/src/plugins/projectexplorer/selectablefilesmodel.cpp index 24e5f8ac34..64cf1d6918 100644 --- a/src/plugins/genericprojectmanager/selectablefilesmodel.cpp +++ b/src/plugins/projectexplorer/selectablefilesmodel.cpp @@ -28,7 +28,7 @@ ****************************************************************************/ #include "selectablefilesmodel.h" -#include "genericprojectconstants.h" +#include "projectexplorerconstants.h" #include <coreplugin/fileiconprovider.h> #include <coreplugin/icore.h> @@ -41,19 +41,14 @@ #include <QPushButton> #include <QTreeView> #include <QDir> +#include <utils/pathchooser.h> -namespace GenericProjectManager { -namespace Internal { +namespace ProjectExplorer { -SelectableFilesModel::SelectableFilesModel(const QString &baseDir, QObject *parent) - : QAbstractItemModel(parent), m_root(0), m_baseDir(baseDir), m_allFiles(true) +SelectableFilesModel::SelectableFilesModel(QObject *parent) + : QAbstractItemModel(parent), m_root(0), m_allFiles(true) { - // Dummy tree - m_root = new Tree; - m_root->name = QLatin1String("/"); - m_root->parent = 0; - m_root->fullPath = m_baseDir; - m_root->isDir = true; + connect(&m_watcher, SIGNAL(finished()), this, SLOT(buildTreeFinished())); } void SelectableFilesModel::setInitialMarkedFiles(const QStringList &files) @@ -68,27 +63,26 @@ void SelectableFilesModel::setInitialMarkedFiles(const QStringList &files) m_allFiles = false; } -void SelectableFilesModel::init() +void SelectableFilesModel::startParsing(const QString &baseDir) { -} + m_watcher.cancel(); + m_watcher.waitForFinished(); -void SelectableFilesModel::startParsing() -{ + m_baseDir = baseDir; // Build a tree in a future m_rootForFuture = new Tree; m_rootForFuture->name = QLatin1String("/"); m_rootForFuture->parent = 0; - m_rootForFuture->fullPath = m_baseDir; + m_rootForFuture->fullPath = baseDir; m_rootForFuture->isDir = true; - connect(&m_watcher, SIGNAL(finished()), this, SLOT(buildTreeFinished())); m_watcher.setFuture(QtConcurrent::run(&SelectableFilesModel::run, this)); } void SelectableFilesModel::run(QFutureInterface<void> &fi) { m_futureCount = 0; - buildTree(m_baseDir, m_rootForFuture, fi); + buildTree(m_baseDir, m_rootForFuture, fi, 5); } void SelectableFilesModel::buildTreeFinished() @@ -101,14 +95,10 @@ void SelectableFilesModel::buildTreeFinished() emit parsingFinished(); } -void SelectableFilesModel::waitForFinished() -{ - m_watcher.waitForFinished(); -} - void SelectableFilesModel::cancel() { m_watcher.cancel(); + m_watcher.waitForFinished(); } bool SelectableFilesModel::filter(Tree *t) @@ -141,8 +131,10 @@ bool SelectableFilesModel::filter(Tree *t) return false; } -void SelectableFilesModel::buildTree(const QString &baseDir, Tree *tree, QFutureInterface<void> &fi) +void SelectableFilesModel::buildTree(const QString &baseDir, Tree *tree, QFutureInterface<void> &fi, int symlinkDepth) { + if (symlinkDepth == 0) + return; const QFileInfoList fileInfoList = QDir(baseDir).entryInfoList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot); @@ -156,14 +148,12 @@ void SelectableFilesModel::buildTree(const QString &baseDir, Tree *tree, QFuture } ++m_futureCount; if (fileInfo.isDir()) { - if (fileInfo.isSymLink()) - continue; Tree *t = new Tree; t->parent = tree; t->name = fileInfo.fileName(); t->fullPath = fileInfo.filePath(); t->isDir = true; - buildTree(fileInfo.filePath(), t, fi); + buildTree(fileInfo.filePath(), t, fi, symlinkDepth - fileInfo.isSymLink()); allChecked &= t->checked == Qt::Checked; allUnchecked &= t->checked == Qt::Unchecked; tree->childDirectories.append(t); @@ -193,11 +183,15 @@ void SelectableFilesModel::buildTree(const QString &baseDir, Tree *tree, QFuture SelectableFilesModel::~SelectableFilesModel() { + m_watcher.cancel(); + m_watcher.waitForFinished(); deleteTree(m_root); } void SelectableFilesModel::deleteTree(Tree *tree) { + if (!tree) + return; foreach (Tree *t, tree->childDirectories) deleteTree(t); foreach (Tree *t, tree->files) @@ -234,6 +228,8 @@ QModelIndex SelectableFilesModel::parent(const QModelIndex &child) const { if (!child.isValid()) return QModelIndex(); + if (!child.internalPointer()) + return QModelIndex(); Tree *parent = static_cast<Tree *>(child.internalPointer())->parent; if (!parent) return QModelIndex(); @@ -398,6 +394,22 @@ void SelectableFilesModel::applyFilter(const QString &showFilesfilter, const QSt applyFilter(createIndex(0, 0, m_root)); } +void SelectableFilesModel::selectAllFiles() +{ + selectAllFiles(m_root); +} + +void SelectableFilesModel::selectAllFiles(Tree *root) +{ + root->checked = Qt::Checked; + + foreach (Tree *t, root->childDirectories) + selectAllFiles(t); + + foreach (Tree *t, root->visibleFiles) + t->checked = Qt::Checked; +} + Qt::CheckState SelectableFilesModel::applyFilter(const QModelIndex &index) { bool allChecked = true; @@ -511,10 +523,10 @@ Qt::CheckState SelectableFilesModel::applyFilter(const QModelIndex &index) } ////////// -// SelectableFilesDialog +// SelectableFilesDialogs ////////// -SelectableFilesDialog::SelectableFilesDialog(const QString &path, const QStringList files, QWidget *parent) +SelectableFilesDialogEditFiles::SelectableFilesDialogEditFiles(const QString &path, const QStringList files, QWidget *parent) : QDialog(parent) { QVBoxLayout *layout = new QVBoxLayout(); @@ -527,7 +539,7 @@ SelectableFilesDialog::SelectableFilesDialog(const QString &path, const QStringL createHideFileFilterControls(layout); createApplyButton(layout); - m_selectableFilesModel = new SelectableFilesModel(path, this); + m_selectableFilesModel = new SelectableFilesModel(this); m_selectableFilesModel->setInitialMarkedFiles(files); m_view->setModel(m_selectableFilesModel); m_view->setMinimumSize(500, 400); @@ -556,10 +568,10 @@ SelectableFilesDialog::SelectableFilesDialog(const QString &path, const QStringL connect(m_selectableFilesModel, SIGNAL(parsingFinished()), this, SLOT(parsingFinished())); - m_selectableFilesModel->startParsing(); + m_selectableFilesModel->startParsing(path); } -void SelectableFilesDialog::createHideFileFilterControls(QVBoxLayout *layout) +void SelectableFilesDialogEditFiles::createHideFileFilterControls(QVBoxLayout *layout) { QHBoxLayout *hbox = new QHBoxLayout; m_hideFilesFilterLabel = new QLabel; @@ -576,7 +588,7 @@ void SelectableFilesDialog::createHideFileFilterControls(QVBoxLayout *layout) layout->addLayout(hbox); } -void SelectableFilesDialog::createShowFileFilterControls(QVBoxLayout *layout) +void SelectableFilesDialogEditFiles::createShowFileFilterControls(QVBoxLayout *layout) { QHBoxLayout *hbox = new QHBoxLayout; m_showFilesFilterLabel = new QLabel; @@ -593,7 +605,7 @@ void SelectableFilesDialog::createShowFileFilterControls(QVBoxLayout *layout) layout->addLayout(hbox); } -void SelectableFilesDialog::createApplyButton(QVBoxLayout *layout) +void SelectableFilesDialogEditFiles::createApplyButton(QVBoxLayout *layout) { QHBoxLayout *hbox = new QHBoxLayout; @@ -608,18 +620,17 @@ void SelectableFilesDialog::createApplyButton(QVBoxLayout *layout) connect(m_applyFilterButton, SIGNAL(clicked()), this, SLOT(applyFilter())); } -SelectableFilesDialog::~SelectableFilesDialog() +SelectableFilesDialogEditFiles::~SelectableFilesDialogEditFiles() { m_selectableFilesModel->cancel(); - m_selectableFilesModel->waitForFinished(); } -void SelectableFilesDialog::parsingProgress(const QString &fileName) +void SelectableFilesDialogEditFiles::parsingProgress(const QString &fileName) { m_progressLabel->setText(tr("Generating file list...\n\n%1").arg(fileName)); } -void SelectableFilesDialog::parsingFinished() +void SelectableFilesDialogEditFiles::parsingFinished() { m_hideFilesFilterLabel->show(); m_hideFilesfilterLineEdit->show(); @@ -642,7 +653,7 @@ void SelectableFilesDialog::parsingFinished() } } -void SelectableFilesDialog::smartExpand(const QModelIndex &index) +void SelectableFilesDialogEditFiles::smartExpand(const QModelIndex &index) { if (m_view->model()->data(index, Qt::CheckStateRole) == Qt::PartiallyChecked) { m_view->expand(index); @@ -652,12 +663,12 @@ void SelectableFilesDialog::smartExpand(const QModelIndex &index) } } -QStringList SelectableFilesDialog::selectedFiles() const +QStringList SelectableFilesDialogEditFiles::selectedFiles() const { return m_selectableFilesModel->selectedFiles(); } -void SelectableFilesDialog::applyFilter() +void SelectableFilesDialogEditFiles::applyFilter() { const QString showFilesFilter = m_showFilesfilterLineEdit->text(); Core::ICore::settings()->setValue(QLatin1String(Constants::SHOW_FILE_FILTER_SETTING), showFilesFilter); @@ -668,7 +679,70 @@ void SelectableFilesDialog::applyFilter() m_selectableFilesModel->applyFilter(showFilesFilter, hideFilesFilter); } -} // namespace Internal -} // namespace GenericProjectManager +SelectableFilesDialogAddDirectory::SelectableFilesDialogAddDirectory(const QString &path, + const QStringList files, QWidget *parent) : + SelectableFilesDialogEditFiles(path, files, parent) +{ + setWindowTitle(tr("Add Existing Directory")); + + connect(m_selectableFilesModel, SIGNAL(parsingFinished()), this, SLOT(parsingFinished())); + + createPathChooser(static_cast<QVBoxLayout*>(layout()), path); +} + +void SelectableFilesDialogAddDirectory::createPathChooser(QVBoxLayout *layout, const QString &path) +{ + QHBoxLayout *hbox = new QHBoxLayout; + + m_pathChooser = new Utils::PathChooser; + m_pathChooser->setPath(path); + m_pathChooser->setHistoryCompleter(QLatin1String("PE.AddToProjectDir.History")); + m_sourceDirectoryLabel = new QLabel(tr("Source directory:")); + hbox->addWidget(m_sourceDirectoryLabel); + + hbox->addWidget(m_pathChooser); + layout->insertLayout(0, hbox); + + m_startParsingButton = new QPushButton(tr("Start Parsing")); + hbox->addWidget(m_startParsingButton); + + connect(m_pathChooser, SIGNAL(validChanged(bool)), this, SLOT(validityOfDirectoryChanged(bool))); + connect(m_startParsingButton, SIGNAL(clicked()), this, SLOT(startParsing())); +} + +void SelectableFilesDialogAddDirectory::validityOfDirectoryChanged(bool validState) +{ + m_startParsingButton->setEnabled(validState); +} + +void SelectableFilesDialogAddDirectory::parsingFinished() +{ + m_selectableFilesModel->selectAllFiles(); + m_selectableFilesModel->applyFilter(m_showFilesfilterLineEdit->text(), + m_hideFilesfilterLineEdit->text()); + + setWidgetsEnabled(true); +} + +void SelectableFilesDialogAddDirectory::startParsing() +{ + setWidgetsEnabled(false); + + m_selectableFilesModel->startParsing(m_pathChooser->path()); +} + +void SelectableFilesDialogAddDirectory::setWidgetsEnabled(bool enabled) +{ + m_hideFilesfilterLineEdit->setEnabled(enabled); + m_showFilesfilterLineEdit->setEnabled(enabled); + m_applyFilterButton->setEnabled(enabled); + m_view->setEnabled(enabled); + m_pathChooser->setEnabled(enabled); + m_startParsingButton->setVisible(enabled); + + m_progressLabel->setVisible(!enabled); +} + +} // namespace ProjectExplorer diff --git a/src/plugins/genericprojectmanager/selectablefilesmodel.h b/src/plugins/projectexplorer/selectablefilesmodel.h index adb98afaca..68561d9de9 100644 --- a/src/plugins/genericprojectmanager/selectablefilesmodel.h +++ b/src/plugins/projectexplorer/selectablefilesmodel.h @@ -37,13 +37,15 @@ #include <QDialog> #include <QTreeView> #include <QLabel> +#include "projectexplorer_export.h" + +namespace Utils { class PathChooser; } QT_BEGIN_NAMESPACE class QVBoxLayout; QT_END_NAMESPACE -namespace GenericProjectManager { -namespace Internal { +namespace ProjectExplorer { struct Tree { @@ -81,12 +83,12 @@ struct Glob } }; -class SelectableFilesModel : public QAbstractItemModel +class PROJECTEXPLORER_EXPORT SelectableFilesModel : public QAbstractItemModel { Q_OBJECT public: - SelectableFilesModel(const QString &baseDir, QObject *parent); + SelectableFilesModel(QObject *parent); ~SelectableFilesModel(); void setInitialMarkedFiles(const QStringList &files); @@ -104,12 +106,12 @@ public: QStringList selectedPaths() const; QStringList preservedFiles() const; - // only call this once - void startParsing(); - void waitForFinished(); + void startParsing(const QString &baseDir); void cancel(); void applyFilter(const QString &selectFilesfilter, const QString &hideFilesfilter); + void selectAllFiles(); + signals: void parsingFinished(); void parsingProgress(const QString &filename); @@ -121,14 +123,14 @@ private: QList<Glob> parseFilter(const QString &filter); Qt::CheckState applyFilter(const QModelIndex &index); bool filter(Tree *t); - void init(); void run(QFutureInterface<void> &fi); void collectFiles(Tree *root, QStringList *result) const; void collectPaths(Tree *root, QStringList *result) const; - void buildTree(const QString &baseDir, Tree *tree, QFutureInterface<void> &fi); + void buildTree(const QString &baseDir, Tree *tree, QFutureInterface<void> &fi, int symlinkDepth); void deleteTree(Tree *tree); void propagateUp(const QModelIndex &index); void propagateDown(const QModelIndex &index); + void selectAllFiles(Tree *root); Tree *m_root; // Used in the future thread need to all not used after calling startParsing QString m_baseDir; @@ -143,13 +145,13 @@ private: QList<Glob> m_showFilesFilter; }; -class SelectableFilesDialog : public QDialog +class PROJECTEXPLORER_EXPORT SelectableFilesDialogEditFiles : public QDialog { Q_OBJECT public: - SelectableFilesDialog(const QString &path, const QStringList files, QWidget *parent); - ~SelectableFilesDialog(); + SelectableFilesDialogEditFiles(const QString &path, const QStringList files, QWidget *parent); + ~SelectableFilesDialogEditFiles(); QStringList selectedFiles() const; private slots: @@ -157,11 +159,12 @@ private slots: void parsingProgress(const QString &fileName); void parsingFinished(); -private: +protected: void smartExpand(const QModelIndex &index); void createShowFileFilterControls(QVBoxLayout *layout); void createHideFileFilterControls(QVBoxLayout *layout); void createApplyButton(QVBoxLayout *layout); + SelectableFilesModel *m_selectableFilesModel; QLabel *m_hideFilesFilterLabel; @@ -177,8 +180,28 @@ private: QLabel *m_progressLabel; }; -} // namespace Internal -} // namespace GenericProjectManager +class SelectableFilesDialogAddDirectory : public SelectableFilesDialogEditFiles +{ + Q_OBJECT + +public: + SelectableFilesDialogAddDirectory(const QString &path, const QStringList files, QWidget *parent); + +private slots: + void validityOfDirectoryChanged(bool validState); + void parsingFinished(); + void startParsing(); + +private: + Utils::PathChooser *m_pathChooser; + QLabel *m_sourceDirectoryLabel; + QPushButton *m_startParsingButton; + + void setWidgetsEnabled(bool enabled); + void createPathChooser(QVBoxLayout *layout, const QString &path); +}; + +} // namespace ProjectExplorer #endif // SELECTABLEFILESMODEL_H diff --git a/src/plugins/projectexplorer/settingsaccessor.cpp b/src/plugins/projectexplorer/settingsaccessor.cpp index 16558d1c29..4dd3e427d3 100644 --- a/src/plugins/projectexplorer/settingsaccessor.cpp +++ b/src/plugins/projectexplorer/settingsaccessor.cpp @@ -1233,8 +1233,6 @@ QVariantMap Version0Handler::convertRunConfigurations(Project *project, const QV result.insert(QLatin1String("Qt4ProjectManager.MaemoRunConfiguration.DeviceId"), i.value()); else if (i.key() == QLatin1String("LastDeployed")) result.insert(QLatin1String("Qt4ProjectManager.MaemoRunConfiguration.LastDeployed"), i.value()); - else if (i.key() == QLatin1String("DebuggingHelpersLastDeployed")) - result.insert(QLatin1String("Qt4ProjectManager.MaemoRunConfiguration.DebuggingHelpersLastDeployed"), i.value()); else qWarning() << "Unknown MaemoRunConfiguration key found:" << i.key() << i.value(); } else if (QLatin1String("ProjectExplorer.CustomExecutableRunConfiguration") == id) { diff --git a/src/plugins/projectexplorer/targetselector.cpp b/src/plugins/projectexplorer/targetselector.cpp index 29d5129bb4..311516431a 100644 --- a/src/plugins/projectexplorer/targetselector.cpp +++ b/src/plugins/projectexplorer/targetselector.cpp @@ -116,13 +116,13 @@ void TargetSelector::menuAboutToHide() updateButtons(); } -void TargetSelector::insertTarget(int index, const QString &name) +void TargetSelector::insertTarget(int index, int subIndex, const QString &name) { QTC_ASSERT(index >= 0 && index <= m_targets.count(), return); Target target; target.name = name; - target.currentSubIndex = 0; + target.currentSubIndex = subIndex; m_targets.insert(index, target); diff --git a/src/plugins/projectexplorer/targetselector.h b/src/plugins/projectexplorer/targetselector.h index 188bf3bef5..d2107d3191 100644 --- a/src/plugins/projectexplorer/targetselector.h +++ b/src/plugins/projectexplorer/targetselector.h @@ -70,7 +70,7 @@ public: void setTargetMenu(QMenu *menu); public: - void insertTarget(int index, const QString &name); + void insertTarget(int index, int subIndex, const QString &name); void renameTarget(int index, const QString &name); void removeTarget(int index); void setCurrentIndex(int index); diff --git a/src/plugins/projectexplorer/targetsettingspanel.cpp b/src/plugins/projectexplorer/targetsettingspanel.cpp index f5b2d47feb..8a4128f1ef 100644 --- a/src/plugins/projectexplorer/targetsettingspanel.cpp +++ b/src/plugins/projectexplorer/targetsettingspanel.cpp @@ -181,9 +181,7 @@ void TargetSettingsPanelWidget::setupUi() // Now set the correct target int index = m_targets.indexOf(m_project->activeTarget()); m_selector->setCurrentIndex(index); - m_selector->setCurrentSubIndex(0); - - currentTargetChanged(index, 0); + currentTargetChanged(index, m_selector->currentSubIndex()); connect(m_selector, SIGNAL(currentChanged(int,int)), this, SLOT(currentTargetChanged(int,int))); @@ -488,7 +486,9 @@ void TargetSettingsPanelWidget::targetAdded(ProjectExplorer::Target *target) if (m_targets.count() == pos || m_targets.at(pos)->displayName() > target->displayName()) { m_targets.insert(pos, target); - m_selector->insertTarget(pos, target->displayName()); + m_selector->insertTarget(pos, m_project->hasActiveBuildSettings() ? 0 : 1, + target->displayName()); + break; } } diff --git a/src/plugins/projectexplorer/targetsettingswidget.cpp b/src/plugins/projectexplorer/targetsettingswidget.cpp index 3cc0cd4e72..e0cb60a886 100644 --- a/src/plugins/projectexplorer/targetsettingswidget.cpp +++ b/src/plugins/projectexplorer/targetsettingswidget.cpp @@ -83,9 +83,9 @@ TargetSettingsWidget::~TargetSettingsWidget() delete ui; } -void TargetSettingsWidget::insertTarget(int index, const QString &name) +void TargetSettingsWidget::insertTarget(int index, int subIndex, const QString &name) { - m_targetSelector->insertTarget(index, name); + m_targetSelector->insertTarget(index, subIndex, name); } void TargetSettingsWidget::renameTarget(int index, const QString &name) diff --git a/src/plugins/projectexplorer/targetsettingswidget.h b/src/plugins/projectexplorer/targetsettingswidget.h index 29b553360b..f22b537daf 100644 --- a/src/plugins/projectexplorer/targetsettingswidget.h +++ b/src/plugins/projectexplorer/targetsettingswidget.h @@ -62,7 +62,7 @@ public: int currentSubIndex() const; public: - void insertTarget(int index, const QString &name); + void insertTarget(int index, int subIndex, const QString &name); void renameTarget(int index, const QString &name); void removeTarget(int index); void setCurrentIndex(int index); diff --git a/src/plugins/projectexplorer/targetsetuppage.cpp b/src/plugins/projectexplorer/targetsetuppage.cpp index 2da697af1e..2d2db6873a 100644 --- a/src/plugins/projectexplorer/targetsetuppage.cpp +++ b/src/plugins/projectexplorer/targetsetuppage.cpp @@ -246,7 +246,7 @@ void TargetSetupPage::setKitSelected(Core::Id id, bool selected) bool TargetSetupPage::isComplete() const { - foreach (TargetSetupWidget *widget, m_widgets.values()) + foreach (TargetSetupWidget *widget, m_widgets) if (widget->isKitSelected()) return true; return false; @@ -275,7 +275,7 @@ void TargetSetupPage::setupWidgets() void TargetSetupPage::reset() { - foreach (TargetSetupWidget *widget, m_widgets.values()) { + foreach (TargetSetupWidget *widget, m_widgets) { Kit *k = widget->kit(); if (!k) continue; @@ -386,7 +386,7 @@ void TargetSetupPage::handleKitUpdate(Kit *k) void TargetSetupPage::selectAtLeastOneKit() { bool atLeastOneKitSelected = false; - foreach (TargetSetupWidget *w, m_widgets.values()) { + foreach (TargetSetupWidget *w, m_widgets) { if (w->isKitSelected()) { atLeastOneKitSelected = true; break; @@ -514,7 +514,7 @@ TargetSetupWidget *TargetSetupPage::addWidget(Kit *k) bool TargetSetupPage::setupProject(Project *project) { QList<const BuildInfo *> toSetUp; // Pointers are managed by the widgets! - foreach (TargetSetupWidget *widget, m_widgets.values()) { + foreach (TargetSetupWidget *widget, m_widgets) { if (!widget->isKitSelected()) continue; diff --git a/src/plugins/projectexplorer/targetsetupwidget.cpp b/src/plugins/projectexplorer/targetsetupwidget.cpp index 0c87247c4e..8e070965b3 100644 --- a/src/plugins/projectexplorer/targetsetupwidget.cpp +++ b/src/plugins/projectexplorer/targetsetupwidget.cpp @@ -180,6 +180,7 @@ void TargetSetupWidget::addBuildInfo(BuildInfo *info, bool isImport) pathChooser->setExpectedKind(Utils::PathChooser::Directory); pathChooser->setFileName(info->buildDirectory); pathChooser->setEnabled(info->supportsShadowBuild); + pathChooser->setHistoryCompleter(QLatin1String("BuildDir.History")); pathChooser->setReadOnly(!info->supportsShadowBuild || isImport); m_newBuildsLayout->addWidget(pathChooser, pos * 2, 1); diff --git a/src/plugins/projectexplorer/task.cpp b/src/plugins/projectexplorer/task.cpp index 8e1edfad88..462e7e6d07 100644 --- a/src/plugins/projectexplorer/task.cpp +++ b/src/plugins/projectexplorer/task.cpp @@ -32,6 +32,19 @@ namespace ProjectExplorer { +static QString taskTypeIcon(Task::TaskType t) +{ + switch (t) { + case Task::Warning: + return QLatin1String(":/projectexplorer/images/compile_warning.png"); + case Task::Error: + return QLatin1String(":/projectexplorer/images/compile_error.png"); + case Task::Unknown: + break; + } + return QString(); +} + unsigned int Task::s_nextId = 1; /*! @@ -44,9 +57,11 @@ Task::Task() : taskId(0), type(Unknown), line(-1) { } Task::Task(TaskType type_, const QString &description_, - const Utils::FileName &file_, int line_, Core::Id category_) : + const Utils::FileName &file_, int line_, Core::Id category_, + const Utils::FileName &iconFile) : taskId(s_nextId), type(type_), description(description_), - file(file_), line(line_), movedLine(line_), category(category_) + file(file_), line(line_), movedLine(line_), category(category_), + icon(iconFile.isEmpty() ? taskTypeIcon(type_) : iconFile.toString()) { ++s_nextId; } @@ -70,6 +85,7 @@ void Task::clear() movedLine = -1; category = Core::Id(); type = Task::Unknown; + icon = QIcon(); } // diff --git a/src/plugins/projectexplorer/task.h b/src/plugins/projectexplorer/task.h index 78640c9441..c6e0d6bc56 100644 --- a/src/plugins/projectexplorer/task.h +++ b/src/plugins/projectexplorer/task.h @@ -54,7 +54,8 @@ public: Task(); Task(TaskType type, const QString &description, - const Utils::FileName &file, int line, Core::Id category); + const Utils::FileName &file, int line, Core::Id category, + const Utils::FileName &iconName = Utils::FileName()); bool isNull() const; void clear(); @@ -66,6 +67,7 @@ public: int line; int movedLine; // contains a line number if the line was moved in the editor Core::Id category; + QIcon icon; void addMark(TextEditor::BaseTextMark *mark); // Having a QList<QTextLayout::FormatRange> in Task isn't that great diff --git a/src/plugins/projectexplorer/taskhub.cpp b/src/plugins/projectexplorer/taskhub.cpp index 685e6ee6f2..990734c09c 100644 --- a/src/plugins/projectexplorer/taskhub.cpp +++ b/src/plugins/projectexplorer/taskhub.cpp @@ -115,9 +115,8 @@ void TaskHub::addTask(Task::TaskType type, const QString &description, Core::Id void TaskHub::addTask(Task task) { if (task.line != -1 && !task.file.isEmpty()) { - bool visible = (task.type == Task::Warning || task.type == Task::Error); - TaskMark *mark = new TaskMark(task.taskId, task.file.toString(), task.line, visible); - mark->setIcon(taskTypeIcon(task.type)); + TaskMark *mark = new TaskMark(task.taskId, task.file.toString(), task.line, !task.icon.isNull()); + mark->setIcon(task.icon); mark->setPriority(TextEditor::ITextMark::LowPriority); task.addMark(mark); emit m_instance->taskAdded(task); @@ -167,16 +166,3 @@ void TaskHub::requestPopup() emit m_instance->popupRequested(Core::IOutputPane::NoModeSwitch); } -QIcon TaskHub::taskTypeIcon(Task::TaskType t) -{ - switch (t) { - case Task::Warning: - return m_instance->m_warningIcon; - case Task::Error: - return m_instance->m_errorIcon; - case Task::Unknown: - break; - } - return QIcon(); -} - diff --git a/src/plugins/projectexplorer/taskhub.h b/src/plugins/projectexplorer/taskhub.h index c34686f554..77cc75bf68 100644 --- a/src/plugins/projectexplorer/taskhub.h +++ b/src/plugins/projectexplorer/taskhub.h @@ -66,8 +66,6 @@ public: static void requestPopup(); - static QIcon taskTypeIcon(ProjectExplorer::Task::TaskType t); - signals: void categoryAdded(Core::Id categoryId, const QString &displayName, bool visible); void taskAdded(const ProjectExplorer::Task &task); diff --git a/src/plugins/projectexplorer/taskmodel.cpp b/src/plugins/projectexplorer/taskmodel.cpp index 24495c41a8..6ceb5e8ffe 100644 --- a/src/plugins/projectexplorer/taskmodel.cpp +++ b/src/plugins/projectexplorer/taskmodel.cpp @@ -255,7 +255,7 @@ QVariant TaskModel::data(const QModelIndex &index, int role) const else if (role == TaskModel::Category) return m_tasks.at(index.row()).category.uniqueIdentifier(); else if (role == TaskModel::Icon) - return TaskHub::taskTypeIcon(m_tasks.at(index.row()).type); + return m_tasks.at(index.row()).icon; else if (role == TaskModel::Task_t) return QVariant::fromValue(task(index)); return QVariant(); diff --git a/src/plugins/projectexplorer/taskwindow.cpp b/src/plugins/projectexplorer/taskwindow.cpp index 6835663ae2..8dc060e148 100644 --- a/src/plugins/projectexplorer/taskwindow.cpp +++ b/src/plugins/projectexplorer/taskwindow.cpp @@ -261,9 +261,9 @@ TaskWindow::TaskWindow() : d(new TaskWindowPrivate) d->m_listview->setContextMenuPolicy(Qt::ActionsContextMenu); - d->m_filterWarningsButton = createFilterButton(TaskHub::taskTypeIcon(Task::Warning), - tr("Show Warnings"), - this, SLOT(setShowWarnings(bool))); + d->m_filterWarningsButton = createFilterButton( + QIcon(QLatin1String(":/projectexplorer/images/compile_warning.png")), + tr("Show Warnings"), this, SLOT(setShowWarnings(bool))); d->m_categoriesButton = new QToolButton; d->m_categoriesButton->setIcon(QIcon(QLatin1String(Core::Constants::ICON_FILTER))); diff --git a/src/plugins/projectexplorer/toolchainoptionspage.cpp b/src/plugins/projectexplorer/toolchainoptionspage.cpp index 222ea810b2..aa5a3fffbd 100644 --- a/src/plugins/projectexplorer/toolchainoptionspage.cpp +++ b/src/plugins/projectexplorer/toolchainoptionspage.cpp @@ -450,92 +450,81 @@ ToolChainOptionsPage::ToolChainOptionsPage() : setCategoryIcon(QLatin1String(Constants::PROJECTEXPLORER_SETTINGS_CATEGORY_ICON)); } -QWidget *ToolChainOptionsPage::createPage(QWidget *parent) -{ - // Actual page setup: - m_configWidget = new QWidget(parent); - - m_toolChainView = new QTreeView(m_configWidget); - m_toolChainView->setUniformRowHeights(true); - m_toolChainView->header()->setStretchLastSection(false); - - m_addButton = new QPushButton(tr("Add"), m_configWidget); - m_cloneButton = new QPushButton(tr("Clone"), m_configWidget); - m_delButton = new QPushButton(tr("Remove"), m_configWidget); - - m_container = new Utils::DetailsWidget(m_configWidget); - m_container->setState(Utils::DetailsWidget::NoSummary); - m_container->setVisible(false); - - QVBoxLayout *buttonLayout = new QVBoxLayout(); - buttonLayout->setSpacing(6); - buttonLayout->setContentsMargins(0, 0, 0, 0); - buttonLayout->addWidget(m_addButton); - buttonLayout->addWidget(m_cloneButton); - buttonLayout->addWidget(m_delButton); - buttonLayout->addItem(new QSpacerItem(10, 40, QSizePolicy::Minimum, QSizePolicy::Expanding)); - - QVBoxLayout *verticalLayout = new QVBoxLayout(); - verticalLayout->addWidget(m_toolChainView); - verticalLayout->addWidget(m_container); - - QHBoxLayout *horizontalLayout = new QHBoxLayout(m_configWidget); - horizontalLayout->addLayout(verticalLayout); - horizontalLayout->addLayout(buttonLayout); - Q_ASSERT(!m_model); - m_model = new ToolChainModel(m_configWidget); - - connect(m_model, SIGNAL(toolChainStateChanged()), this, SLOT(updateState())); - - m_toolChainView->setModel(m_model); - m_toolChainView->header()->setResizeMode(0, QHeaderView::ResizeToContents); - m_toolChainView->header()->setResizeMode(1, QHeaderView::Stretch); - m_toolChainView->expandAll(); - - m_selectionModel = m_toolChainView->selectionModel(); - connect(m_selectionModel, SIGNAL(selectionChanged(QItemSelection,QItemSelection)), - this, SLOT(toolChainSelectionChanged())); - connect(ToolChainManager::instance(), SIGNAL(toolChainsChanged()), - this, SLOT(toolChainSelectionChanged())); - - // Get toolchainfactories: - m_factories = ExtensionSystem::PluginManager::getObjects<ToolChainFactory>(); - - // Set up add menu: - QMenu *addMenu = new QMenu(m_addButton); - QSignalMapper *mapper = new QSignalMapper(addMenu); - connect(mapper, SIGNAL(mapped(QObject*)), this, SLOT(createToolChain(QObject*))); - - foreach (ToolChainFactory *factory, m_factories) { - if (factory->canCreate()) { - QAction *action = new QAction(addMenu); - action->setText(factory->displayName()); - connect(action, SIGNAL(triggered()), mapper, SLOT(map())); - mapper->setMapping(action, static_cast<QObject *>(factory)); - - addMenu->addAction(action); +QWidget *ToolChainOptionsPage::widget() +{ + if (!m_configWidget) { + // Actual page setup: + m_configWidget = new QWidget; + + m_toolChainView = new QTreeView(m_configWidget); + m_toolChainView->setUniformRowHeights(true); + m_toolChainView->header()->setStretchLastSection(false); + + m_addButton = new QPushButton(tr("Add"), m_configWidget); + m_cloneButton = new QPushButton(tr("Clone"), m_configWidget); + m_delButton = new QPushButton(tr("Remove"), m_configWidget); + + m_container = new Utils::DetailsWidget(m_configWidget); + m_container->setState(Utils::DetailsWidget::NoSummary); + m_container->setVisible(false); + + QVBoxLayout *buttonLayout = new QVBoxLayout(); + buttonLayout->setSpacing(6); + buttonLayout->setContentsMargins(0, 0, 0, 0); + buttonLayout->addWidget(m_addButton); + buttonLayout->addWidget(m_cloneButton); + buttonLayout->addWidget(m_delButton); + buttonLayout->addItem(new QSpacerItem(10, 40, QSizePolicy::Minimum, QSizePolicy::Expanding)); + + QVBoxLayout *verticalLayout = new QVBoxLayout(); + verticalLayout->addWidget(m_toolChainView); + verticalLayout->addWidget(m_container); + + QHBoxLayout *horizontalLayout = new QHBoxLayout(m_configWidget); + horizontalLayout->addLayout(verticalLayout); + horizontalLayout->addLayout(buttonLayout); + Q_ASSERT(!m_model); + m_model = new ToolChainModel(m_configWidget); + + connect(m_model, SIGNAL(toolChainStateChanged()), this, SLOT(updateState())); + + m_toolChainView->setModel(m_model); + m_toolChainView->header()->setResizeMode(0, QHeaderView::ResizeToContents); + m_toolChainView->header()->setResizeMode(1, QHeaderView::Stretch); + m_toolChainView->expandAll(); + + m_selectionModel = m_toolChainView->selectionModel(); + connect(m_selectionModel, SIGNAL(selectionChanged(QItemSelection,QItemSelection)), + this, SLOT(toolChainSelectionChanged())); + connect(ToolChainManager::instance(), SIGNAL(toolChainsChanged()), + this, SLOT(toolChainSelectionChanged())); + + // Get toolchainfactories: + m_factories = ExtensionSystem::PluginManager::getObjects<ToolChainFactory>(); + + // Set up add menu: + QMenu *addMenu = new QMenu(m_addButton); + QSignalMapper *mapper = new QSignalMapper(addMenu); + connect(mapper, SIGNAL(mapped(QObject*)), this, SLOT(createToolChain(QObject*))); + + foreach (ToolChainFactory *factory, m_factories) { + if (factory->canCreate()) { + QAction *action = new QAction(addMenu); + action->setText(factory->displayName()); + connect(action, SIGNAL(triggered()), mapper, SLOT(map())); + mapper->setMapping(action, static_cast<QObject *>(factory)); + + addMenu->addAction(action); + } } - } - connect(m_cloneButton, SIGNAL(clicked()), mapper, SLOT(map())); - mapper->setMapping(m_cloneButton, static_cast<QObject *>(0)); - - m_addButton->setMenu(addMenu); - - connect(m_delButton, SIGNAL(clicked()), this, SLOT(removeToolChain())); + connect(m_cloneButton, SIGNAL(clicked()), mapper, SLOT(map())); + mapper->setMapping(m_cloneButton, static_cast<QObject *>(0)); - // setup keywords: - if (m_searchKeywords.isEmpty()) { - QLatin1Char sep(' '); - QTextStream stream(&m_searchKeywords); - stream << tr("Compilers"); - foreach (ToolChainFactory *f, m_factories) - stream << sep << f->displayName(); + m_addButton->setMenu(addMenu); - m_searchKeywords.remove(QLatin1Char('&')); + connect(m_delButton, SIGNAL(clicked()), this, SLOT(removeToolChain())); + updateState(); } - - updateState(); - return m_configWidget; } @@ -550,8 +539,7 @@ void ToolChainOptionsPage::finish() disconnect(ToolChainManager::instance(), SIGNAL(toolChainsChanged()), this, SLOT(toolChainSelectionChanged())); - // delete by settingsdialog; - m_configWidget = 0; + delete m_configWidget; // children of m_configWidget m_model = 0; @@ -563,11 +551,6 @@ void ToolChainOptionsPage::finish() m_delButton = 0; } -bool ToolChainOptionsPage::matches(const QString &s) const -{ - return m_searchKeywords.contains(s, Qt::CaseInsensitive); -} - void ToolChainOptionsPage::toolChainSelectionChanged() { if (!m_container) diff --git a/src/plugins/projectexplorer/toolchainoptionspage.h b/src/plugins/projectexplorer/toolchainoptionspage.h index 3758edaacc..cccff782d7 100644 --- a/src/plugins/projectexplorer/toolchainoptionspage.h +++ b/src/plugins/projectexplorer/toolchainoptionspage.h @@ -33,6 +33,7 @@ #include <coreplugin/dialogs/ioptionspage.h> #include <QAbstractItemModel> +#include <QPointer> QT_BEGIN_NAMESPACE class QItemSelectionModel; @@ -117,10 +118,9 @@ class ToolChainOptionsPage : public Core::IOptionsPage public: ToolChainOptionsPage(); - QWidget *createPage(QWidget *parent); + QWidget *widget(); void apply(); void finish(); - bool matches(const QString &) const; private slots: void toolChainSelectionChanged(); @@ -131,8 +131,7 @@ private slots: private: QModelIndex currentIndex() const; - QWidget *m_configWidget; - QString m_searchKeywords; + QPointer<QWidget> m_configWidget; ToolChainModel *m_model; QList<ToolChainFactory *> m_factories; diff --git a/src/plugins/pythoneditor/pythoneditor.cpp b/src/plugins/pythoneditor/pythoneditor.cpp index 1787f71d01..f327fd7e9e 100644 --- a/src/plugins/pythoneditor/pythoneditor.cpp +++ b/src/plugins/pythoneditor/pythoneditor.cpp @@ -62,7 +62,7 @@ Core::IEditor *PythonEditor::duplicate(QWidget *parent) { EditorWidget *widget = new EditorWidget(parent); widget->duplicateFrom(editorWidget()); - PythonEditorPlugin::initializeEditor(widget); + TextEditor::TextEditorSettings::initializeEditor(widget); return widget->editor(); } diff --git a/src/plugins/pythoneditor/pythoneditorfactory.cpp b/src/plugins/pythoneditor/pythoneditorfactory.cpp index eeb5f2cec4..acf42d7f8a 100644 --- a/src/plugins/pythoneditor/pythoneditorfactory.cpp +++ b/src/plugins/pythoneditor/pythoneditorfactory.cpp @@ -34,6 +34,7 @@ #include <coreplugin/icore.h> #include <coreplugin/editormanager/editormanager.h> +#include <texteditor/texteditoractionhandler.h> #include <texteditor/texteditorsettings.h> #include <QDebug> @@ -47,12 +48,17 @@ EditorFactory::EditorFactory(QObject *parent) setId(Constants::C_PYTHONEDITOR_ID); setDisplayName(tr(Constants::C_EDITOR_DISPLAY_NAME)); addMimeType(QLatin1String(Constants::C_PY_MIMETYPE)); + new TextEditor::TextEditorActionHandler(this, + Constants::C_PYTHONEDITOR_ID, + TextEditor::TextEditorActionHandler::Format + | TextEditor::TextEditorActionHandler::UnCommentSelection + | TextEditor::TextEditorActionHandler::UnCollapseAll); } Core::IEditor *EditorFactory::createEditor(QWidget *parent) { EditorWidget *widget = new EditorWidget(parent); - PythonEditorPlugin::initializeEditor(widget); + TextEditor::TextEditorSettings::initializeEditor(widget); return widget->editor(); } diff --git a/src/plugins/pythoneditor/pythoneditorplugin.cpp b/src/plugins/pythoneditor/pythoneditorplugin.cpp index 5ab39396ad..4be6691069 100644 --- a/src/plugins/pythoneditor/pythoneditorplugin.cpp +++ b/src/plugins/pythoneditor/pythoneditorplugin.cpp @@ -43,7 +43,6 @@ #include <coreplugin/editormanager/editormanager.h> #include <extensionsystem/pluginmanager.h> #include <texteditor/texteditorconstants.h> -#include <texteditor/texteditorsettings.h> #include <QtPlugin> #include <QCoreApplication> @@ -198,7 +197,6 @@ static void copyIdentifiers(const char * const words[], size_t bytesCount, QSet< PythonEditorPlugin::PythonEditorPlugin() : m_factory(0) - , m_actionHandler(0) { m_instance = this; copyIdentifiers(LIST_OF_PYTHON_KEYWORDS, sizeof(LIST_OF_PYTHON_KEYWORDS), m_keywords); @@ -223,13 +221,6 @@ bool PythonEditorPlugin::initialize(const QStringList &arguments, QString *error addObject(m_factory); // Initialize editor actions handler - m_actionHandler.reset(new TextEditor::TextEditorActionHandler( - C_PYTHONEDITOR_ID, - TextEditor::TextEditorActionHandler::Format - | TextEditor::TextEditorActionHandler::UnCommentSelection - | TextEditor::TextEditorActionHandler::UnCollapseAll)); - m_actionHandler->initializeActions(); - // Add MIME overlay icons (these icons displayed at Project dock panel) const QIcon icon = QIcon::fromTheme(QLatin1String(C_PY_MIME_ICON)); if (!icon.isNull()) @@ -247,12 +238,6 @@ void PythonEditorPlugin::extensionsInitialized() { } -void PythonEditorPlugin::initializeEditor(EditorWidget *widget) -{ - instance()->m_actionHandler->setupActions(widget); - TextEditor::TextEditorSettings::initializeEditor(widget); -} - QSet<QString> PythonEditorPlugin::keywords() { return instance()->m_keywords; diff --git a/src/plugins/pythoneditor/pythoneditorplugin.h b/src/plugins/pythoneditor/pythoneditorplugin.h index 4f54ea3874..2e8ac7d0fc 100644 --- a/src/plugins/pythoneditor/pythoneditorplugin.h +++ b/src/plugins/pythoneditor/pythoneditorplugin.h @@ -31,9 +31,7 @@ #define PYTHONEDITOR_PLUGIN_H #include <extensionsystem/iplugin.h> -#include <texteditor/texteditoractionhandler.h> #include <QSet> -#include <QScopedPointer> namespace PythonEditor { namespace Internal { @@ -57,7 +55,6 @@ public: virtual bool initialize(const QStringList &arguments, QString *errorMessage); virtual void extensionsInitialized(); static PythonEditorPlugin *instance() { return m_instance; } - static void initializeEditor(EditorWidget *widget); static QSet<QString> keywords(); static QSet<QString> magics(); @@ -66,7 +63,6 @@ public: private: static PythonEditorPlugin *m_instance; EditorFactory *m_factory; - QScopedPointer<TextEditor::TextEditorActionHandler> m_actionHandler; QSet<QString> m_keywords; QSet<QString> m_magics; QSet<QString> m_builtins; diff --git a/src/plugins/pythoneditor/pythoneditorwidget.cpp b/src/plugins/pythoneditor/pythoneditorwidget.cpp index 5e2f6b5ccf..8c37ab7be5 100644 --- a/src/plugins/pythoneditor/pythoneditorwidget.cpp +++ b/src/plugins/pythoneditor/pythoneditorwidget.cpp @@ -59,7 +59,7 @@ EditorWidget::EditorWidget(QWidget *parent) setCodeFoldingSupported(true); setIndenter(new PythonIndenter()); - new PythonHighlighter(baseTextDocument().data()); + new PythonHighlighter(baseTextDocument()); } EditorWidget::~EditorWidget() diff --git a/src/plugins/qbsprojectmanager/qbsbuildstep.cpp b/src/plugins/qbsprojectmanager/qbsbuildstep.cpp index 4cdbe41e9f..7bc8970c35 100644 --- a/src/plugins/qbsprojectmanager/qbsbuildstep.cpp +++ b/src/plugins/qbsprojectmanager/qbsbuildstep.cpp @@ -50,6 +50,7 @@ static const char QBS_CONFIG[] = "Qbs.Configuration"; static const char QBS_DRY_RUN[] = "Qbs.DryRun"; static const char QBS_KEEP_GOING[] = "Qbs.DryKeepGoing"; +static const char QBS_CHECK_TIMESTAMPS[] = "Qbs.CheckTimestamps"; static const char QBS_MAXJOBCOUNT[] = "Qbs.MaxJobs"; // -------------------------------------------------------------------- @@ -69,6 +70,7 @@ QbsBuildStep::QbsBuildStep(ProjectExplorer::BuildStepList *bsl) : { setDisplayName(tr("Qbs Build")); setQbsConfiguration(QVariantMap()); + m_qbsBuildOptions.setForceTimestampCheck(true); } QbsBuildStep::QbsBuildStep(ProjectExplorer::BuildStepList *bsl, const QbsBuildStep *other) : @@ -194,6 +196,11 @@ bool QbsBuildStep::keepGoing() const return m_qbsBuildOptions.keepGoing(); } +bool QbsBuildStep::checkTimestamps() const +{ + return m_qbsBuildOptions.forceTimestampCheck(); +} + int QbsBuildStep::maxJobs() const { if (m_qbsBuildOptions.maxJobCount() > 0) @@ -209,6 +216,7 @@ bool QbsBuildStep::fromMap(const QVariantMap &map) setQbsConfiguration(map.value(QLatin1String(QBS_CONFIG)).toMap()); m_qbsBuildOptions.setDryRun(map.value(QLatin1String(QBS_DRY_RUN)).toBool()); m_qbsBuildOptions.setKeepGoing(map.value(QLatin1String(QBS_KEEP_GOING)).toBool()); + m_qbsBuildOptions.setForceTimestampCheck(map.value(QLatin1String(QBS_CHECK_TIMESTAMPS), true).toBool()); m_qbsBuildOptions.setMaxJobCount(map.value(QLatin1String(QBS_MAXJOBCOUNT)).toInt()); return true; } @@ -219,6 +227,7 @@ QVariantMap QbsBuildStep::toMap() const map.insert(QLatin1String(QBS_CONFIG), m_qbsConfiguration); map.insert(QLatin1String(QBS_DRY_RUN), m_qbsBuildOptions.dryRun()); map.insert(QLatin1String(QBS_KEEP_GOING), m_qbsBuildOptions.keepGoing()); + map.insert(QLatin1String(QBS_CHECK_TIMESTAMPS), m_qbsBuildOptions.forceTimestampCheck()); map.insert(QLatin1String(QBS_MAXJOBCOUNT), m_qbsBuildOptions.maxJobCount()); return map; } @@ -333,6 +342,14 @@ void QbsBuildStep::setKeepGoing(bool kg) emit qbsBuildOptionsChanged(); } +void QbsBuildStep::setCheckTimestamps(bool ts) +{ + if (m_qbsBuildOptions.forceTimestampCheck() == ts) + return; + m_qbsBuildOptions.setForceTimestampCheck(ts); + emit qbsBuildOptionsChanged(); +} + void QbsBuildStep::setMaxJobs(int jobcount) { if (m_qbsBuildOptions.maxJobCount() == jobcount) @@ -362,6 +379,8 @@ QbsBuildStepConfigWidget::QbsBuildStepConfigWidget(QbsBuildStep *step) : this, SLOT(changeBuildVariant(int))); connect(m_ui->dryRunCheckBox, SIGNAL(toggled(bool)), this, SLOT(changeDryRun(bool))); connect(m_ui->keepGoingCheckBox, SIGNAL(toggled(bool)), this, SLOT(changeKeepGoing(bool))); + connect(m_ui->checkTimestampCheckBox, SIGNAL(toggled(bool)), + this, SLOT(changeCheckTimestamps(bool))); connect(m_ui->jobSpinBox, SIGNAL(valueChanged(int)), this, SLOT(changeJobCount(int))); connect(m_ui->propertyEdit, SIGNAL(propertiesChanged()), this, SLOT(changeProperties())); connect(m_ui->qmlDebuggingLibraryCheckBox, SIGNAL(toggled(bool)), @@ -386,6 +405,7 @@ void QbsBuildStepConfigWidget::updateState() if (!m_ignoreChange) { m_ui->dryRunCheckBox->setChecked(m_step->dryRun()); m_ui->keepGoingCheckBox->setChecked(m_step->keepGoing()); + m_ui->checkTimestampCheckBox->setChecked(m_step->checkTimestamps()); m_ui->jobSpinBox->setValue(m_step->maxJobs()); updatePropertyEdit(m_step->qbsConfiguration()); m_ui->qmlDebuggingLibraryCheckBox->setChecked(m_step->isQmlDebuggingEnabled()); @@ -402,6 +422,8 @@ void QbsBuildStepConfigWidget::updateState() command += QLatin1String("--dry-run "); if (m_step->keepGoing()) command += QLatin1String("--keep-going "); + if (m_step->checkTimestamps()) + command += QLatin1String("--check-timestamps "); command += QString::fromLatin1("--jobs %1 ").arg(m_step->maxJobs()); command += QString::fromLatin1("%1 profile:%2").arg(buildVariant, m_step->profile()); @@ -480,6 +502,13 @@ void QbsBuildStepConfigWidget::changeKeepGoing(bool kg) m_ignoreChange = false; } +void QbsBuildStepConfigWidget::changeCheckTimestamps(bool ts) +{ + m_ignoreChange = true; + m_step->setCheckTimestamps(ts); + m_ignoreChange = false; +} + void QbsBuildStepConfigWidget::changeJobCount(int count) { m_ignoreChange = true; diff --git a/src/plugins/qbsprojectmanager/qbsbuildstep.h b/src/plugins/qbsprojectmanager/qbsbuildstep.h index 5227128afc..3b6ceb77c2 100644 --- a/src/plugins/qbsprojectmanager/qbsbuildstep.h +++ b/src/plugins/qbsprojectmanager/qbsbuildstep.h @@ -65,6 +65,7 @@ public: bool dryRun() const; bool keepGoing() const; + bool checkTimestamps() const; int maxJobs() const; QString buildVariant() const; @@ -93,6 +94,7 @@ private: void setDryRun(bool dr); void setKeepGoing(bool kg); + void setCheckTimestamps(bool ts); void setMaxJobs(int jobcount); QVariantMap m_qbsConfiguration; @@ -129,6 +131,7 @@ private slots: void changeBuildVariant(int); void changeDryRun(bool dr); void changeKeepGoing(bool kg); + void changeCheckTimestamps(bool ts); void changeJobCount(int count); void changeProperties(); diff --git a/src/plugins/qbsprojectmanager/qbsbuildstepconfigwidget.ui b/src/plugins/qbsprojectmanager/qbsbuildstepconfigwidget.ui index d527af4f96..52767a8103 100644 --- a/src/plugins/qbsprojectmanager/qbsbuildstepconfigwidget.ui +++ b/src/plugins/qbsprojectmanager/qbsbuildstepconfigwidget.ui @@ -168,6 +168,13 @@ </widget> </item> <item> + <widget class="QCheckBox" name="checkTimestampCheckBox"> + <property name="text"> + <string>Check timestamps</string> + </property> + </widget> + </item> + <item> <spacer name="checkBoxSpacer"> <property name="orientation"> <enum>Qt::Horizontal</enum> diff --git a/src/plugins/qbsprojectmanager/qbsinstallstep.cpp b/src/plugins/qbsprojectmanager/qbsinstallstep.cpp index 1e7571e08f..738a2a6812 100644 --- a/src/plugins/qbsprojectmanager/qbsinstallstep.cpp +++ b/src/plugins/qbsprojectmanager/qbsinstallstep.cpp @@ -276,6 +276,7 @@ QbsInstallStepConfigWidget::QbsInstallStepConfigWidget(QbsInstallStep *step) : m_ui->installRootChooser->setPromptDialogTitle(tr("Qbs Install Prefix")); m_ui->installRootChooser->setExpectedKind(Utils::PathChooser::Directory); + m_ui->installRootChooser->setHistoryCompleter(QLatin1String("Qbs.InstallRoot.History")); connect(m_ui->installRootChooser, SIGNAL(changed(QString)), this, SLOT(changeInstallRoot())); connect(m_ui->removeFirstCheckBox, SIGNAL(toggled(bool)), this, SLOT(changeRemoveFirst(bool))); diff --git a/src/plugins/qbsprojectmanager/qbsproject.cpp b/src/plugins/qbsprojectmanager/qbsproject.cpp index 50e277cb73..14f0c112ae 100644 --- a/src/plugins/qbsprojectmanager/qbsproject.cpp +++ b/src/plugins/qbsprojectmanager/qbsproject.cpp @@ -102,6 +102,7 @@ QbsProject::QbsProject(QbsManager *manager, const QString &fileName) : { m_parsingDelay.setInterval(1000); // delay parsing by 1s. + setId(Constants::PROJECT_ID); setProjectContext(Context(Constants::PROJECT_ID)); setProjectLanguages(Context(ProjectExplorer::Constants::LANG_CXX)); @@ -111,7 +112,7 @@ QbsProject::QbsProject(QbsManager *manager, const QString &fileName) : this, SLOT(targetWasAdded(ProjectExplorer::Target*))); connect(this, SIGNAL(environmentChanged()), this, SLOT(delayParsing())); - connect(&m_parsingDelay, SIGNAL(timeout()), this, SLOT(parseCurrentBuildConfiguration())); + connect(&m_parsingDelay, SIGNAL(timeout()), this, SLOT(startParsing())); updateDocuments(QSet<QString>() << fileName); @@ -140,11 +141,6 @@ QString QbsProject::displayName() const return m_projectName; } -Id QbsProject::id() const -{ - return Constants::PROJECT_ID; -} - IDocument *QbsProject::document() const { foreach (IDocument *doc, m_qbsDocuments) { @@ -361,6 +357,11 @@ void QbsProject::buildConfigurationChanged(BuildConfiguration *bc) } } +void QbsProject::startParsing() +{ + parseCurrentBuildConfiguration(false); +} + void QbsProject::delayParsing() { m_parsingDelay.start(); @@ -372,10 +373,13 @@ void QbsProject::delayForcedParsing() delayParsing(); } -void QbsProject::parseCurrentBuildConfiguration() +void QbsProject::parseCurrentBuildConfiguration(bool force) { m_parsingDelay.stop(); + if (!m_forceParsing) + m_forceParsing = force; + if (!activeTarget()) return; QbsBuildConfiguration *bc = qobject_cast<QbsBuildConfiguration *>(activeTarget()->activeBuildConfiguration()); @@ -639,7 +643,7 @@ void QbsProject::updateCppCodeModel(const qbs::ProjectData &prj) part->includePaths += grpIncludePaths; part->frameworkPaths += grpFrameworkPaths; part->precompiledHeaders = QStringList(pch); - part->defines += grpDefines; + part->projectDefines += grpDefines; pinfo.appendProjectPart(part); } } diff --git a/src/plugins/qbsprojectmanager/qbsproject.h b/src/plugins/qbsprojectmanager/qbsproject.h index 2030ffe093..1ca9003717 100644 --- a/src/plugins/qbsprojectmanager/qbsproject.h +++ b/src/plugins/qbsprojectmanager/qbsproject.h @@ -74,7 +74,6 @@ public: ~QbsProject(); QString displayName() const; - Core::Id id() const; Core::IDocument *document() const; QbsManager *projectManager() const; @@ -91,6 +90,7 @@ public: QString profileForTarget(const ProjectExplorer::Target *t) const; bool isParsing() const; bool hasParseResult() const; + void parseCurrentBuildConfiguration(bool force); Utils::FileName defaultBuildDirectory() const; static Utils::FileName defaultBuildDirectory(const QString &path); @@ -102,7 +102,6 @@ public: public slots: void invalidate(); - void parseCurrentBuildConfiguration(); void delayParsing(); void delayForcedParsing(); @@ -118,6 +117,7 @@ private slots: void targetWasAdded(ProjectExplorer::Target *t); void changeActiveTarget(ProjectExplorer::Target *t); void buildConfigurationChanged(ProjectExplorer::BuildConfiguration *bc); + void startParsing(); private: bool fromMap(const QVariantMap &map); diff --git a/src/plugins/qbsprojectmanager/qbsprojectmanager.pro b/src/plugins/qbsprojectmanager/qbsprojectmanager.pro index 47e40a1f22..3b8da1c508 100644 --- a/src/plugins/qbsprojectmanager/qbsprojectmanager.pro +++ b/src/plugins/qbsprojectmanager/qbsprojectmanager.pro @@ -36,7 +36,6 @@ HEADERS = \ qbsprojectmanagerplugin.h \ qbspropertylineedit.h \ qbsrunconfiguration.h \ - qbsstep.h \ qbsconstants.h SOURCES = \ @@ -55,8 +54,7 @@ SOURCES = \ qbsprojectmanager.cpp \ qbsprojectmanagerplugin.cpp \ qbspropertylineedit.cpp \ - qbsrunconfiguration.cpp \ - qbsstep.cpp + qbsrunconfiguration.cpp FORMS = \ qbsbuildstepconfigwidget.ui \ diff --git a/src/plugins/qbsprojectmanager/qbsprojectmanager.qbs b/src/plugins/qbsprojectmanager/qbsprojectmanager.qbs index af2759e317..b4e8ab2420 100644 --- a/src/plugins/qbsprojectmanager/qbsprojectmanager.qbs +++ b/src/plugins/qbsprojectmanager/qbsprojectmanager.qbs @@ -96,9 +96,7 @@ QtcPlugin { "qbspropertylineedit.cpp", "qbspropertylineedit.h", "qbsrunconfiguration.cpp", - "qbsrunconfiguration.h", - "qbsstep.cpp", - "qbsstep.h" + "qbsrunconfiguration.h" ] } diff --git a/src/plugins/qbsprojectmanager/qbsprojectmanagerplugin.cpp b/src/plugins/qbsprojectmanager/qbsprojectmanagerplugin.cpp index b518df7acc..ae39dbe47a 100644 --- a/src/plugins/qbsprojectmanager/qbsprojectmanagerplugin.cpp +++ b/src/plugins/qbsprojectmanager/qbsprojectmanagerplugin.cpp @@ -405,7 +405,7 @@ void QbsProjectManagerPlugin::buildProducts(QbsProject *project, const QStringLi void QbsProjectManagerPlugin::reparseCurrentProject() { if (m_currentProject) - m_currentProject->parseCurrentBuildConfiguration(); + m_currentProject->parseCurrentBuildConfiguration(true); } } // namespace Internal diff --git a/src/plugins/qbsprojectmanager/qbsrunconfiguration.cpp b/src/plugins/qbsprojectmanager/qbsrunconfiguration.cpp index 4c5e76d6bd..522e375cf3 100644 --- a/src/plugins/qbsprojectmanager/qbsrunconfiguration.cpp +++ b/src/plugins/qbsprojectmanager/qbsrunconfiguration.cpp @@ -294,16 +294,6 @@ QString QbsRunConfiguration::qbsProduct() const return m_qbsProduct; } -QString QbsRunConfiguration::dumperLibrary() const -{ - return QtSupport::QtKitInformation::dumperLibrary(target()->kit()); -} - -QStringList QbsRunConfiguration::dumperLibraryLocations() const -{ - return QtSupport::QtKitInformation::dumperLibraryLocations(target()->kit()); -} - QString QbsRunConfiguration::defaultDisplayName() { QString defaultName; @@ -376,6 +366,7 @@ QbsRunConfigurationWidget::QbsRunConfigurationWidget(QbsRunConfiguration *rc, QW toplayout->addRow(argumentsLabel, m_argumentsLineEdit); m_workingDirectoryEdit = new Utils::PathChooser(this); + m_workingDirectoryEdit->setHistoryCompleter(QLatin1String("WorkingDir.History")); m_workingDirectoryEdit->setExpectedKind(Utils::PathChooser::Directory); ProjectExplorer::EnvironmentAspect *aspect = m_rc->extraAspect<ProjectExplorer::EnvironmentAspect>(); diff --git a/src/plugins/qbsprojectmanager/qbsrunconfiguration.h b/src/plugins/qbsprojectmanager/qbsrunconfiguration.h index c01b1e5d8a..baa201144e 100644 --- a/src/plugins/qbsprojectmanager/qbsrunconfiguration.h +++ b/src/plugins/qbsprojectmanager/qbsrunconfiguration.h @@ -83,8 +83,6 @@ public: bool forcedGuiMode() const; QString workingDirectory() const; QString commandLineArguments() const; - QString dumperLibrary() const; - QStringList dumperLibraryLocations() const; QVariantMap toMap() const; diff --git a/src/plugins/qbsprojectmanager/qbsstep.cpp b/src/plugins/qbsprojectmanager/qbsstep.cpp deleted file mode 100644 index 7348398824..0000000000 --- a/src/plugins/qbsprojectmanager/qbsstep.cpp +++ /dev/null @@ -1,242 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). -** 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 "qbsstep.h" - -#include "qbsbuildconfiguration.h" -#include "qbsparser.h" -#include "qbsproject.h" -#include "qbsprojectmanagerconstants.h" - -#include <projectexplorer/buildsteplist.h> -#include <projectexplorer/kit.h> -#include <projectexplorer/projectexplorerconstants.h> -#include <projectexplorer/target.h> -#include <utils/qtcassert.h> - -#include <qbs.h> - -#include <QTimer> - -// -------------------------------------------------------------------- -// Constants: -// -------------------------------------------------------------------- - -static const char QBS_DRY_RUN[] = "Qbs.DryRun"; -static const char QBS_KEEP_GOING[] = "Qbs.DryKeepGoing"; -static const char QBS_MAXJOBCOUNT[] = "Qbs.MaxJobs"; - -namespace QbsProjectManager { -namespace Internal { - -// -------------------------------------------------------------------- -// QbsStep: -// -------------------------------------------------------------------- - -QbsStep::QbsStep(ProjectExplorer::BuildStepList *bsl, Core::Id id) : - ProjectExplorer::BuildStep(bsl, id), - m_job(0) -{ - m_qbsBuildOptions.setMaxJobCount(QbsManager::preferences()->jobs()); -} - -QbsStep::QbsStep(ProjectExplorer::BuildStepList *bsl, const QbsStep *other) : - ProjectExplorer::BuildStep(bsl, Core::Id(Constants::QBS_BUILDSTEP_ID)), - m_qbsBuildOptions(other->m_qbsBuildOptions), m_job(0) -{ } - -QbsBuildConfiguration *QbsStep::currentBuildConfiguration() const -{ - QbsBuildConfiguration *bc = static_cast<QbsBuildConfiguration *>(buildConfiguration()); - if (!bc) - bc = static_cast<QbsBuildConfiguration *>(target()->activeBuildConfiguration()); - return bc; -} - -QbsStep::~QbsStep() -{ - cancel(); - m_job->deleteLater(); - m_job = 0; -} - -bool QbsStep::init() -{ - if (static_cast<QbsProject *>(project())->isParsing() || m_job) - return false; - - if (!currentBuildConfiguration()) - return false; - - return true; -} - -void QbsStep::run(QFutureInterface<bool> &fi) -{ - m_fi = &fi; - - m_job = createJob(); - - if (!m_job) { - jobDone(false); - return; - } - - m_progressBase = 0; - - connect(m_job, SIGNAL(finished(bool,qbs::AbstractJob*)), this, SLOT(jobDone(bool))); - connect(m_job, SIGNAL(taskStarted(QString,int,qbs::AbstractJob*)), - this, SLOT(handleTaskStarted(QString,int))); - connect(m_job, SIGNAL(taskProgress(int,qbs::AbstractJob*)), - this, SLOT(handleProgress(int))); -} - -QFutureInterface<bool> *QbsStep::future() const -{ - return m_fi; -} - -bool QbsStep::runInGuiThread() const -{ - return true; -} - -void QbsStep::cancel() -{ - if (m_job) - m_job->cancel(); -} - -bool QbsStep::dryRun() const -{ - return m_qbsBuildOptions.dryRun(); -} - -bool QbsStep::keepGoing() const -{ - return m_qbsBuildOptions.keepGoing(); -} - -int QbsStep::maxJobs() const -{ - return m_qbsBuildOptions.maxJobCount(); -} - -bool QbsStep::fromMap(const QVariantMap &map) -{ - if (!ProjectExplorer::BuildStep::fromMap(map)) - return false; - - m_qbsBuildOptions.setDryRun(map.value(QLatin1String(QBS_DRY_RUN)).toBool()); - m_qbsBuildOptions.setKeepGoing(map.value(QLatin1String(QBS_KEEP_GOING)).toBool()); - m_qbsBuildOptions.setMaxJobCount(map.value(QLatin1String(QBS_MAXJOBCOUNT)).toInt()); - - if (m_qbsBuildOptions.maxJobCount() <= 0) - m_qbsBuildOptions.setMaxJobCount(QbsManager::preferences()->jobs()); - - return true; -} - -QVariantMap QbsStep::toMap() const -{ - QVariantMap map = ProjectExplorer::BuildStep::toMap(); - map.insert(QLatin1String(QBS_DRY_RUN), m_qbsBuildOptions.dryRun()); - map.insert(QLatin1String(QBS_KEEP_GOING), m_qbsBuildOptions.keepGoing()); - map.insert(QLatin1String(QBS_MAXJOBCOUNT), m_qbsBuildOptions.maxJobCount()); - return map; -} - -void QbsStep::jobDone(bool success) -{ - // Report errors: - if (m_job) { - foreach (const qbs::ErrorItem &item, m_job->error().items()) - createTaskAndOutput(ProjectExplorer::Task::Error, item.description(), - item.codeLocation().fileName(), item.codeLocation().line()); - m_job->deleteLater(); - m_job = 0; - } - - QTC_ASSERT(m_fi, return); - m_fi->reportResult(success); - m_fi = 0; // do not delete, it is not ours - - emit finished(); -} - -void QbsStep::handleTaskStarted(const QString &desciption, int max) -{ - Q_UNUSED(desciption); - QTC_ASSERT(m_fi, return); - - m_progressBase = m_fi->progressValue(); - m_fi->setProgressRange(0, m_progressBase + max); -} - -void QbsStep::handleProgress(int value) -{ - QTC_ASSERT(m_fi, return); - m_fi->setProgressValue(m_progressBase + value); -} - -void QbsStep::createTaskAndOutput(ProjectExplorer::Task::TaskType type, const QString &message, - const QString &file, int line) -{ - emit addTask(ProjectExplorer::Task(type, message, - Utils::FileName::fromString(file), line, - ProjectExplorer::Constants::TASK_CATEGORY_COMPILE)); - emit addOutput(message, NormalOutput); -} - -void QbsStep::setDryRun(bool dr) -{ - if (m_qbsBuildOptions.dryRun() == dr) - return; - m_qbsBuildOptions.setDryRun(dr); - emit qbsBuildOptionsChanged(); -} - -void QbsStep::setKeepGoing(bool kg) -{ - if (m_qbsBuildOptions.keepGoing() == kg) - return; - m_qbsBuildOptions.setKeepGoing(kg); - emit qbsBuildOptionsChanged(); -} - -void QbsStep::setMaxJobs(int jobcount) -{ - if (m_qbsBuildOptions.maxJobCount() == jobcount) - return; - m_qbsBuildOptions.setMaxJobCount(jobcount); - emit qbsBuildOptionsChanged(); -} - -} // namespace Internal -} // namespace QbsProjectManager diff --git a/src/plugins/qbsprojectmanager/qbsstep.h b/src/plugins/qbsprojectmanager/qbsstep.h deleted file mode 100644 index 6ced81e85f..0000000000 --- a/src/plugins/qbsprojectmanager/qbsstep.h +++ /dev/null @@ -1,108 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). -** 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. -** -****************************************************************************/ - -#ifndef QBSSTEP_H -#define QBSSTEP_H - -#include "qbsbuildconfiguration.h" - -#include <projectexplorer/buildstep.h> -#include <projectexplorer/task.h> - -#include <qbs.h> - -namespace QbsProjectManager { -namespace Internal { - -class QbsStepConfigWidget; - -class QbsStep : public ProjectExplorer::BuildStep -{ - Q_OBJECT - -public: - ~QbsStep(); - - bool init(); - - void run(QFutureInterface<bool> &fi); - - QFutureInterface<bool> *future() const; - - bool runInGuiThread() const; - void cancel(); - - bool dryRun() const; - bool keepGoing() const; - int maxJobs() const; - QString buildVariant() const; - - bool fromMap(const QVariantMap &map); - QVariantMap toMap() const; - -signals: - void qbsBuildOptionsChanged(); - -private slots: - virtual void jobDone(bool success); - void handleTaskStarted(const QString &desciption, int max); - void handleProgress(int value); - -protected: - QbsStep(ProjectExplorer::BuildStepList *bsl, Core::Id id); - QbsStep(ProjectExplorer::BuildStepList *bsl, const QbsStep *other); - - QbsBuildConfiguration *currentBuildConfiguration() const; - - virtual qbs::AbstractJob *createJob() = 0; - - void createTaskAndOutput(ProjectExplorer::Task::TaskType type, - const QString &message, const QString &file, int line); - - qbs::AbstractJob *job() const { return m_job; } - qbs::BuildOptions buildOptions() const { return m_qbsBuildOptions; } - -private: - void setDryRun(bool dr); - void setKeepGoing(bool kg); - void setMaxJobs(int jobcount); - - qbs::BuildOptions m_qbsBuildOptions; - - QFutureInterface<bool> *m_fi; - qbs::AbstractJob *m_job; - int m_progressBase; - - friend class QbsStepConfigWidget; -}; - -} // namespace Internal -} // namespace QbsProjectManager - -#endif // QBSSTEP_H diff --git a/src/plugins/qmakeprojectmanager/customwidgetwizard/classdefinition.cpp b/src/plugins/qmakeprojectmanager/customwidgetwizard/classdefinition.cpp index efe8d7c94a..e374d09c44 100644 --- a/src/plugins/qmakeprojectmanager/customwidgetwizard/classdefinition.cpp +++ b/src/plugins/qmakeprojectmanager/customwidgetwizard/classdefinition.cpp @@ -40,6 +40,7 @@ ClassDefinition::ClassDefinition(QWidget *parent) : { m_ui.setupUi(this); m_ui.iconPathChooser->setExpectedKind(Utils::PathChooser::File); + m_ui.iconPathChooser->setHistoryCompleter(QLatin1String("Qmake.Icon.History")); m_ui.iconPathChooser->setPromptDialogTitle(tr("Select Icon")); m_ui.iconPathChooser->setPromptDialogFilter(tr("Icon files (*.png *.ico *.jpg *.xpm *.tif *.svg)")); } diff --git a/src/plugins/qmakeprojectmanager/desktopqmakerunconfiguration.cpp b/src/plugins/qmakeprojectmanager/desktopqmakerunconfiguration.cpp index b1dffee1b5..918546e484 100644 --- a/src/plugins/qmakeprojectmanager/desktopqmakerunconfiguration.cpp +++ b/src/plugins/qmakeprojectmanager/desktopqmakerunconfiguration.cpp @@ -217,6 +217,7 @@ DesktopQmakeRunConfigurationWidget::DesktopQmakeRunConfigurationWidget(DesktopQm m_workingDirectoryEdit = new PathChooser(this); m_workingDirectoryEdit->setExpectedKind(PathChooser::Directory); + m_workingDirectoryEdit->setHistoryCompleter(QLatin1String("WorkingDir.History")); m_workingDirectoryEdit->setPath(m_qmakeRunConfiguration->baseWorkingDirectory()); m_workingDirectoryEdit->setBaseDirectory(m_qmakeRunConfiguration->target()->project()->projectDirectory()); EnvironmentAspect *aspect = qmakeRunConfiguration->extraAspect<EnvironmentAspect>(); @@ -552,16 +553,6 @@ QString DesktopQmakeRunConfiguration::proFilePath() const return m_proFilePath; } -QString DesktopQmakeRunConfiguration::dumperLibrary() const -{ - return QtSupport::QtKitInformation::dumperLibrary(target()->kit()); -} - -QStringList DesktopQmakeRunConfiguration::dumperLibraryLocations() const -{ - return QtSupport::QtKitInformation::dumperLibraryLocations(target()->kit()); -} - QString DesktopQmakeRunConfiguration::defaultDisplayName() { QString defaultName; diff --git a/src/plugins/qmakeprojectmanager/desktopqmakerunconfiguration.h b/src/plugins/qmakeprojectmanager/desktopqmakerunconfiguration.h index c89cef28e5..bbcc27a56b 100644 --- a/src/plugins/qmakeprojectmanager/desktopqmakerunconfiguration.h +++ b/src/plugins/qmakeprojectmanager/desktopqmakerunconfiguration.h @@ -81,8 +81,6 @@ public: bool forcedGuiMode() const; virtual QString workingDirectory() const; virtual QString commandLineArguments() const; - QString dumperLibrary() const; - QStringList dumperLibraryLocations() const; bool isUsingDyldImageSuffix() const; void setUsingDyldImageSuffix(bool state); diff --git a/src/plugins/qmakeprojectmanager/librarydetailscontroller.cpp b/src/plugins/qmakeprojectmanager/librarydetailscontroller.cpp index a08539eae5..afe18ef262 100644 --- a/src/plugins/qmakeprojectmanager/librarydetailscontroller.cpp +++ b/src/plugins/qmakeprojectmanager/librarydetailscontroller.cpp @@ -620,6 +620,9 @@ NonInternalLibraryDetailsController::NonInternalLibraryDetailsController( setLibraryComboBoxVisible(false); setLibraryPathChooserVisible(true); + libraryDetailsWidget()->libraryPathChooser + ->setHistoryCompleter(QLatin1String("Qmake.LibDir.History")); + if (creatorPlatform() == CreatorWindows) { libraryDetailsWidget()->libraryPathChooser->setPromptDialogFilter( QLatin1String("Library file (*.lib lib*.a)")); diff --git a/src/plugins/qmakeprojectmanager/makestep.cpp b/src/plugins/qmakeprojectmanager/makestep.cpp index 2af7374072..d1b39bf2ed 100644 --- a/src/plugins/qmakeprojectmanager/makestep.cpp +++ b/src/plugins/qmakeprojectmanager/makestep.cpp @@ -326,7 +326,7 @@ MakeStepConfigWidget::MakeStepConfigWidget(MakeStep *makeStep) m_ui->makePathChooser->setExpectedKind(Utils::PathChooser::ExistingCommand); m_ui->makePathChooser->setBaseDirectory(Utils::PathChooser::homePath()); - + m_ui->makePathChooser->setHistoryCompleter(QLatin1String("PE.MakeCommand.History")); const QString &makeCmd = m_makeStep->makeCommand(); m_ui->makePathChooser->setPath(makeCmd); diff --git a/src/plugins/qmakeprojectmanager/profileeditor.cpp b/src/plugins/qmakeprojectmanager/profileeditor.cpp index 68cac3bf6a..1cbb4d839c 100644 --- a/src/plugins/qmakeprojectmanager/profileeditor.cpp +++ b/src/plugins/qmakeprojectmanager/profileeditor.cpp @@ -61,8 +61,7 @@ ProFileEditor::ProFileEditor(ProFileEditorWidget *editor) Core::IEditor *ProFileEditor::duplicate(QWidget *parent) { - ProFileEditorWidget *ret = new ProFileEditorWidget(parent, qobject_cast<ProFileEditorWidget*>(editorWidget())->factory(), - qobject_cast<ProFileEditorWidget*>(editorWidget())->actionHandler()); + ProFileEditorWidget *ret = new ProFileEditorWidget(parent, qobject_cast<ProFileEditorWidget*>(editorWidget())->factory()); ret->duplicateFrom(editorWidget()); TextEditor::TextEditorSettings::initializeEditor(ret); return ret->editor(); @@ -82,15 +81,13 @@ TextEditor::CompletionAssistProvider *ProFileEditor::completionAssistProvider() // ProFileEditorWidget // -ProFileEditorWidget::ProFileEditorWidget(QWidget *parent, ProFileEditorFactory *factory, TextEditor::TextEditorActionHandler *ah) - : BaseTextEditorWidget(parent), m_factory(factory), m_ah(ah) +ProFileEditorWidget::ProFileEditorWidget(QWidget *parent, ProFileEditorFactory *factory) + : BaseTextEditorWidget(parent), m_factory(factory) { QSharedPointer<ProFileDocument> doc(new ProFileDocument()); doc->setMimeType(QLatin1String(Constants::PROFILE_MIMETYPE)); setBaseTextDocument(doc); - ah->setupActions(this); - baseTextDocument()->setSyntaxHighlighter(new ProFileHighlighter); m_commentDefinition.clearCommentStyles(); m_commentDefinition.singleLine = QLatin1Char('#'); @@ -170,7 +167,7 @@ ProFileEditorWidget::Link ProFileEditorWidget::findLinkAt(const QTextCursor &cur } } - QDir dir(QFileInfo(editorDocument()->filePath()).absolutePath()); + QDir dir(QFileInfo(baseTextDocument()->filePath()).absolutePath()); QString fileName = dir.filePath(buffer); QFileInfo fi(fileName); if (fi.exists()) { diff --git a/src/plugins/qmakeprojectmanager/profileeditor.h b/src/plugins/qmakeprojectmanager/profileeditor.h index 31da55fbff..3274e98105 100644 --- a/src/plugins/qmakeprojectmanager/profileeditor.h +++ b/src/plugins/qmakeprojectmanager/profileeditor.h @@ -34,11 +34,6 @@ #include <texteditor/basetexteditor.h> #include <utils/uncommentselection.h> -namespace TextEditor { -class FontSettings; -class TextEditorActionHandler; -} - namespace QmakeProjectManager { namespace Internal { @@ -63,11 +58,9 @@ class ProFileEditorWidget : public TextEditor::BaseTextEditorWidget Q_OBJECT public: - ProFileEditorWidget(QWidget *parent, ProFileEditorFactory *factory, - TextEditor::TextEditorActionHandler *ah); + ProFileEditorWidget(QWidget *parent, ProFileEditorFactory *factory); ProFileEditorFactory *factory() { return m_factory; } - TextEditor::TextEditorActionHandler *actionHandler() const { return m_ah; } void unCommentSelection(); @@ -79,7 +72,6 @@ protected: private: ProFileEditorFactory *m_factory; - TextEditor::TextEditorActionHandler *m_ah; Utils::CommentDefinition m_commentDefinition; }; diff --git a/src/plugins/qmakeprojectmanager/profileeditorfactory.cpp b/src/plugins/qmakeprojectmanager/profileeditorfactory.cpp index d549513d54..82a1a8905e 100644 --- a/src/plugins/qmakeprojectmanager/profileeditorfactory.cpp +++ b/src/plugins/qmakeprojectmanager/profileeditorfactory.cpp @@ -35,6 +35,7 @@ #include <qtsupport/qtsupportconstants.h> #include <coreplugin/fileiconprovider.h> +#include <texteditor/texteditoractionhandler.h> #include <texteditor/texteditorsettings.h> #include <QCoreApplication> @@ -42,15 +43,17 @@ using namespace QmakeProjectManager; using namespace QmakeProjectManager::Internal; -ProFileEditorFactory::ProFileEditorFactory(QmakeManager *manager, TextEditor::TextEditorActionHandler *handler) : - m_manager(manager), - m_actionHandler(handler) +ProFileEditorFactory::ProFileEditorFactory(QmakeManager *manager) : + m_manager(manager) { setId(QmakeProjectManager::Constants::PROFILE_EDITOR_ID); setDisplayName(qApp->translate("OpenWith::Editors", QmakeProjectManager::Constants::PROFILE_EDITOR_DISPLAY_NAME)); addMimeType(QmakeProjectManager::Constants::PROFILE_MIMETYPE); addMimeType(QmakeProjectManager::Constants::PROINCLUDEFILE_MIMETYPE); addMimeType(QmakeProjectManager::Constants::PROFEATUREFILE_MIMETYPE); + new TextEditor::TextEditorActionHandler(this, Constants::C_PROFILEEDITOR, + TextEditor::TextEditorActionHandler::UnCommentSelection + | TextEditor::TextEditorActionHandler::JumpToFileUnderCursor); Core::FileIconProvider::registerIconOverlayForSuffix(QtSupport::Constants::ICON_QT_PROJECT, "pro"); Core::FileIconProvider::registerIconOverlayForSuffix(QtSupport::Constants::ICON_QT_PROJECT, "pri"); @@ -59,7 +62,7 @@ ProFileEditorFactory::ProFileEditorFactory(QmakeManager *manager, TextEditor::Te Core::IEditor *ProFileEditorFactory::createEditor(QWidget *parent) { - ProFileEditorWidget *editor = new ProFileEditorWidget(parent, this, m_actionHandler); + ProFileEditorWidget *editor = new ProFileEditorWidget(parent, this); TextEditor::TextEditorSettings::initializeEditor(editor); return editor->editor(); } diff --git a/src/plugins/qmakeprojectmanager/profileeditorfactory.h b/src/plugins/qmakeprojectmanager/profileeditorfactory.h index 52bd50ae8c..0a805ade48 100644 --- a/src/plugins/qmakeprojectmanager/profileeditorfactory.h +++ b/src/plugins/qmakeprojectmanager/profileeditorfactory.h @@ -32,8 +32,6 @@ #include <coreplugin/editormanager/ieditorfactory.h> -namespace TextEditor { class TextEditorActionHandler; } - namespace QmakeProjectManager { class QmakeManager; @@ -45,7 +43,7 @@ class ProFileEditorFactory : public Core::IEditorFactory Q_OBJECT public: - ProFileEditorFactory(QmakeManager *parent, TextEditor::TextEditorActionHandler *handler); + ProFileEditorFactory(QmakeManager *parent); Core::IEditor *createEditor(QWidget *parent); @@ -53,7 +51,6 @@ public: private: QmakeManager *m_manager; - TextEditor::TextEditorActionHandler *m_actionHandler; }; } // namespace Internal diff --git a/src/plugins/qmakeprojectmanager/qmakenodes.cpp b/src/plugins/qmakeprojectmanager/qmakenodes.cpp index 69a1bea7dd..32ad3e5adb 100644 --- a/src/plugins/qmakeprojectmanager/qmakenodes.cpp +++ b/src/plugins/qmakeprojectmanager/qmakenodes.cpp @@ -865,7 +865,7 @@ QList<ProjectNode::ProjectAction> QmakePriFileNode::supportedActions(Node *node) addExistingFiles = addExistingFiles && !deploysFolder(node->path()); if (addExistingFiles) - actions << AddExistingFile; + actions << AddExistingFile << AddExistingDirectory; break; } diff --git a/src/plugins/qmakeprojectmanager/qmakeparser.cpp b/src/plugins/qmakeprojectmanager/qmakeparser.cpp index f4e8ac63a0..8f05dde81c 100644 --- a/src/plugins/qmakeprojectmanager/qmakeparser.cpp +++ b/src/plugins/qmakeprojectmanager/qmakeparser.cpp @@ -33,7 +33,6 @@ #include <projectexplorer/projectexplorerconstants.h> using namespace QmakeProjectManager; -using namespace QmakeProjectManager::Internal; using ProjectExplorer::Task; QMakeParser::QMakeParser() : m_error(QLatin1String("^(.+):(\\d+):\\s(.+)$")) diff --git a/src/plugins/qmakeprojectmanager/qmakeparser.h b/src/plugins/qmakeprojectmanager/qmakeparser.h index 38d1d63f0f..3d54e71ede 100644 --- a/src/plugins/qmakeprojectmanager/qmakeparser.h +++ b/src/plugins/qmakeprojectmanager/qmakeparser.h @@ -30,14 +30,15 @@ #ifndef QMAKEPARSER_H #define QMAKEPARSER_H +#include "qmakeprojectmanager_global.h" + #include <projectexplorer/ioutputparser.h> #include <QRegExp> namespace QmakeProjectManager { -namespace Internal { -class QMakeParser : public ProjectExplorer::IOutputParser +class QMAKEPROJECTMANAGER_EXPORT QMakeParser : public ProjectExplorer::IOutputParser { Q_OBJECT @@ -49,7 +50,6 @@ private: QRegExp m_error; }; -} // namesapce Internal } // namespace QmakeProjectManager #endif // QMAKEPARSER_H diff --git a/src/plugins/qmakeprojectmanager/qmakeproject.cpp b/src/plugins/qmakeprojectmanager/qmakeproject.cpp index 5951e7a58d..b3ab28aed1 100644 --- a/src/plugins/qmakeprojectmanager/qmakeproject.cpp +++ b/src/plugins/qmakeprojectmanager/qmakeproject.cpp @@ -343,6 +343,7 @@ QmakeProject::QmakeProject(QmakeManager *manager, const QString &fileName) : m_centralizedFolderWatcher(0), m_activeTarget(0) { + setId(Constants::QMAKEPROJECT_ID); setProjectContext(Core::Context(QmakeProjectManager::Constants::PROJECT_ID)); setProjectLanguages(Core::Context(ProjectExplorer::Constants::LANG_CXX)); @@ -527,7 +528,7 @@ void QmakeProject::updateCppCodeModel() SysRootKitInformation::sysRoot(k)); // part->defines - part->defines += pro->cxxDefines(); + part->projectDefines += pro->cxxDefines(); // part->includePaths, part->frameworkPaths part->includePaths.append(pro->variableValue(IncludePathVar)); @@ -890,11 +891,6 @@ QString QmakeProject::displayName() const return QFileInfo(projectFilePath()).completeBaseName(); } -Core::Id QmakeProject::id() const -{ - return Core::Id(Constants::QMAKEPROJECT_ID); -} - Core::IDocument *QmakeProject::document() const { return m_fileInfo; diff --git a/src/plugins/qmakeprojectmanager/qmakeproject.h b/src/plugins/qmakeprojectmanager/qmakeproject.h index acb95159e8..cc49b9ec9c 100644 --- a/src/plugins/qmakeprojectmanager/qmakeproject.h +++ b/src/plugins/qmakeprojectmanager/qmakeproject.h @@ -76,7 +76,6 @@ public: virtual ~QmakeProject(); QString displayName() const; - Core::Id id() const; Core::IDocument *document() const; ProjectExplorer::IProjectManager *projectManager() const; QmakeManager *qmakeProjectManager() const; diff --git a/src/plugins/qmakeprojectmanager/qmakeprojectconfigwidget.cpp b/src/plugins/qmakeprojectmanager/qmakeprojectconfigwidget.cpp index 389f78deee..8f28bc8f55 100644 --- a/src/plugins/qmakeprojectmanager/qmakeprojectconfigwidget.cpp +++ b/src/plugins/qmakeprojectmanager/qmakeprojectconfigwidget.cpp @@ -67,6 +67,7 @@ QmakeProjectConfigWidget::QmakeProjectConfigWidget(QmakeBuildConfiguration *bc) m_ui->shadowBuildDirEdit->setPromptDialogTitle(tr("Shadow Build Directory")); m_ui->shadowBuildDirEdit->setExpectedKind(Utils::PathChooser::ExistingDirectory); + m_ui->shadowBuildDirEdit->setHistoryCompleter(QLatin1String("BuildDir.History")); m_ui->shadowBuildDirEdit->setEnvironment(bc->environment()); m_ui->shadowBuildDirEdit->setBaseDirectory(bc->target()->project()->projectDirectory()); bool isShadowBuild = bc->isShadowBuild(); diff --git a/src/plugins/qmakeprojectmanager/qmakeprojectmanager.cpp b/src/plugins/qmakeprojectmanager/qmakeprojectmanager.cpp index 160924023d..699d80be0e 100644 --- a/src/plugins/qmakeprojectmanager/qmakeprojectmanager.cpp +++ b/src/plugins/qmakeprojectmanager/qmakeprojectmanager.cpp @@ -55,14 +55,6 @@ using namespace ProjectExplorer; using namespace QmakeProjectManager; using namespace QmakeProjectManager::Internal; -// Known file types of a Qt 4 project -static const char *qmakeFileTypes[] = { - "CppHeaderFiles", - "CppSourceFiles", - "Qt4FormFiles", - "Qt4ResourceFiles" -}; - QmakeManager::QmakeManager(QmakeProjectManagerPlugin *plugin) : m_plugin(plugin), m_contextNode(0), @@ -140,10 +132,10 @@ void QmakeManager::setContextFile(ProjectExplorer::FileNode *file) void QmakeManager::addLibrary() { - ProFileEditorWidget *editor = - qobject_cast<ProFileEditorWidget*>(Core::EditorManager::currentEditor()->widget()); + ProFileEditor *editor = + qobject_cast<ProFileEditor*>(Core::EditorManager::currentEditor()); if (editor) - addLibrary(editor->editorDocument()->filePath(), editor); + addLibrary(editor->document()->filePath(), editor); } void QmakeManager::addLibraryContextMenu() @@ -153,34 +145,29 @@ void QmakeManager::addLibraryContextMenu() addLibrary(node->path()); } -void QmakeManager::addLibrary(const QString &fileName, ProFileEditorWidget *editor) +void QmakeManager::addLibrary(const QString &fileName, ProFileEditor *editor) { AddLibraryWizard wizard(fileName, Core::EditorManager::instance()); if (wizard.exec() != QDialog::Accepted) return; - TextEditor::BaseTextEditor *editable = 0; - if (editor) { - editable = editor->editor(); - } else { - editable = qobject_cast<TextEditor::BaseTextEditor *> - (Core::EditorManager::openEditor(fileName, QmakeProjectManager::Constants::PROFILE_EDITOR_ID, - Core::EditorManager::DoNotMakeVisible)); - } - if (!editable) + if (!editor) + editor = qobject_cast<ProFileEditor *> (Core::EditorManager::openEditor(fileName, + QmakeProjectManager::Constants::PROFILE_EDITOR_ID, Core::EditorManager::DoNotMakeVisible)); + if (!editor) return; - const int endOfDoc = editable->position(TextEditor::ITextEditor::EndOfDoc); - editable->setCursorPosition(endOfDoc); + const int endOfDoc = editor->position(TextEditor::ITextEditor::EndOfDoc); + editor->setCursorPosition(endOfDoc); QString snippet = wizard.snippet(); // add extra \n in case the last line is not empty int line, column; - editable->convertPosition(endOfDoc, &line, &column); - if (!editable->textDocument()->textAt(endOfDoc - column, column).simplified().isEmpty()) + editor->convertPosition(endOfDoc, &line, &column); + if (!editor->textDocument()->textAt(endOfDoc - column, column).simplified().isEmpty()) snippet = QLatin1Char('\n') + snippet; - editable->insert(snippet); + editor->insert(snippet); } @@ -310,22 +297,3 @@ void QmakeManager::handleSubDirContextMenu(QmakeManager::Action action, bool isF bc->setSubNodeBuild(0); bc->setFileNodeBuild(0); } - -QString QmakeManager::fileTypeId(ProjectExplorer::FileType type) -{ - switch (type) { - case HeaderType: - return QLatin1String(qmakeFileTypes[0]); - case SourceType: - return QLatin1String(qmakeFileTypes[1]); - case FormType: - return QLatin1String(qmakeFileTypes[2]); - case ResourceType: - return QLatin1String(qmakeFileTypes[3]); - case UnknownFileType: - default: - break; - } - return QString(); -} - diff --git a/src/plugins/qmakeprojectmanager/qmakeprojectmanager.h b/src/plugins/qmakeprojectmanager/qmakeprojectmanager.h index ca7314b6ec..a7fd326da4 100644 --- a/src/plugins/qmakeprojectmanager/qmakeprojectmanager.h +++ b/src/plugins/qmakeprojectmanager/qmakeprojectmanager.h @@ -47,7 +47,7 @@ class ToolChain; namespace QmakeProjectManager { namespace Internal { -class ProFileEditorWidget; +class ProFileEditor; class QmakeProjectManagerPlugin; } // namespace Internal @@ -76,9 +76,6 @@ public: ProjectExplorer::FileNode *contextFile() const; void setContextFile(ProjectExplorer::FileNode *file); - // Return the id string of a file - static QString fileTypeId(ProjectExplorer::FileType type); - enum Action { BUILD, REBUILD, CLEAN }; public slots: @@ -99,7 +96,7 @@ private: ProjectExplorer::Project *contextProject, ProjectExplorer::Node *contextNode, ProjectExplorer::FileNode *contextFile); - void addLibrary(const QString &fileName, Internal::ProFileEditorWidget *editor = 0); + void addLibrary(const QString &fileName, Internal::ProFileEditor *editor = 0); void runQMake(ProjectExplorer::Project *p, ProjectExplorer::Node *node); Internal::QmakeProjectManagerPlugin *m_plugin; diff --git a/src/plugins/qmakeprojectmanager/qmakeprojectmanagerplugin.cpp b/src/plugins/qmakeprojectmanager/qmakeprojectmanagerplugin.cpp index bd9aef8bd4..fd8886f653 100644 --- a/src/plugins/qmakeprojectmanager/qmakeprojectmanagerplugin.cpp +++ b/src/plugins/qmakeprojectmanager/qmakeprojectmanagerplugin.cpp @@ -111,12 +111,7 @@ bool QmakeProjectManagerPlugin::initialize(const QStringList &arguments, QString m_qmakeProjectManager = new QmakeManager(this); addObject(m_qmakeProjectManager); - TextEditor::TextEditorActionHandler *editorHandler - = new TextEditor::TextEditorActionHandler(Constants::C_PROFILEEDITOR, - TextEditor::TextEditorActionHandler::UnCommentSelection - | TextEditor::TextEditorActionHandler::JumpToFileUnderCursor); - - m_proFileEditorFactory = new ProFileEditorFactory(m_qmakeProjectManager, editorHandler); + m_proFileEditorFactory = new ProFileEditorFactory(m_qmakeProjectManager); ProjectExplorer::KitManager::registerKitInformation(new QmakeKitInformation); diff --git a/src/plugins/qmakeprojectmanager/wizards/abstractmobileapp.h b/src/plugins/qmakeprojectmanager/wizards/abstractmobileapp.h index 86f1a19fb6..2dd2fcda85 100644 --- a/src/plugins/qmakeprojectmanager/wizards/abstractmobileapp.h +++ b/src/plugins/qmakeprojectmanager/wizards/abstractmobileapp.h @@ -121,6 +121,7 @@ public: static const QString DeploymentPriFileName; protected: AbstractMobileApp(); + virtual QByteArray generateProFile(QString *errorMessage) const; static QString templatesRoot(); static void insertParameter(QString &line, const QString ¶meter); @@ -146,7 +147,6 @@ protected: private: QByteArray generateDesktopFile(QString *errorMessage, int fileType) const; QByteArray generateMainCpp(QString *errorMessage) const; - QByteArray generateProFile(QString *errorMessage) const; virtual QByteArray generateFileExtended(int fileType, bool *versionAndCheckSum, QString *comment, QString *errorMessage) const = 0; diff --git a/src/plugins/qmakeprojectmanager/wizards/html5appwizardpages.cpp b/src/plugins/qmakeprojectmanager/wizards/html5appwizardpages.cpp index b4c41d845b..6fdaccd035 100644 --- a/src/plugins/qmakeprojectmanager/wizards/html5appwizardpages.cpp +++ b/src/plugins/qmakeprojectmanager/wizards/html5appwizardpages.cpp @@ -45,6 +45,7 @@ Html5AppWizardOptionsPage::Html5AppWizardOptionsPage(QWidget *parent) { d->ui.setupUi(this); d->ui.importLineEdit->setExpectedKind(Utils::PathChooser::File); + d->ui.importLineEdit->setHistoryCompleter(QLatin1String("Qmake.Html.History")); d->ui.importLineEdit->setPromptDialogFilter(QLatin1String("*.html")); d->ui.importLineEdit->setPromptDialogTitle(tr("Select HTML File")); connect(d->ui.importLineEdit, SIGNAL(changed(QString)), SIGNAL(completeChanged())); diff --git a/src/plugins/qmakeprojectmanager/wizards/qtquickapp.cpp b/src/plugins/qmakeprojectmanager/wizards/qtquickapp.cpp index 0509239e56..4138525808 100644 --- a/src/plugins/qmakeprojectmanager/wizards/qtquickapp.cpp +++ b/src/plugins/qmakeprojectmanager/wizards/qtquickapp.cpp @@ -44,6 +44,11 @@ namespace QmakeProjectManager { namespace Internal { +static QString qtQuickApplicationViewerDirectory() +{ + return Core::ICore::resourcePath() + QLatin1String("/templates/shared/qtquickapplicationviewer/"); +} + static QString templateRootDirectory() { return Core::ICore::resourcePath() + QLatin1String("/templates/qtquick/"); @@ -208,11 +213,11 @@ QString QtQuickApp::pathExtended(int fileType) const case MainQmlDeployed: return qmlSubDir + mainQmlFile; case MainQmlOrigin: return qmlOriginDir + mainQmlFile; case AppViewerPri: return pathBase + appViewerTargetSubDir + fileName(AppViewerPri); - case AppViewerPriOrigin: return originsRoot() + appViewerOriginSubDir() + fileName(AppViewerPri); + case AppViewerPriOrigin: return qtQuickApplicationViewerDirectory() + appViewerOriginSubDir() + fileName(AppViewerPri); case AppViewerCpp: return pathBase + appViewerTargetSubDir + fileName(AppViewerCpp); - case AppViewerCppOrigin: return originsRoot() + appViewerOriginSubDir() + fileName(AppViewerCpp); + case AppViewerCppOrigin: return qtQuickApplicationViewerDirectory() + appViewerOriginSubDir() + fileName(AppViewerCpp); case AppViewerH: return pathBase + appViewerTargetSubDir + fileName(AppViewerH); - case AppViewerHOrigin: return originsRoot() + appViewerOriginSubDir() + fileName(AppViewerH); + case AppViewerHOrigin: return qtQuickApplicationViewerDirectory() + appViewerOriginSubDir() + fileName(AppViewerH); case QmlDirProFileRelative: return QString(qmlSubDir).remove(qmlSubDir.length() - 1, 1); default: qFatal("QtQuickApp::pathExtended() needs more work"); } @@ -294,6 +299,13 @@ QString QtQuickApp::appViewerOriginSubDir() const return appViewerBaseName() + QLatin1Char('/'); } +QByteArray QtQuickApp::generateProFile(QString *errorMessage) const +{ + QByteArray proFileContent = AbstractMobileApp::generateProFile(errorMessage); + proFileContent.replace("../../shared/qtquickapplicationviewer/", ""); + return proFileContent; +} + QByteArray QtQuickApp::generateFileExtended(int fileType, bool *versionAndCheckSum, QString *comment, QString *errorMessage) const { diff --git a/src/plugins/qmakeprojectmanager/wizards/qtquickapp.h b/src/plugins/qmakeprojectmanager/wizards/qtquickapp.h index 00234bbd2c..58d61fd0b8 100644 --- a/src/plugins/qmakeprojectmanager/wizards/qtquickapp.h +++ b/src/plugins/qmakeprojectmanager/wizards/qtquickapp.h @@ -95,7 +95,9 @@ public: static const int StubVersion; protected: - virtual QString appViewerBaseName() const; + virtual QByteArray generateProFile(QString *errorMessage) const; + + QString appViewerBaseName() const; QString fileName(ExtendedFileType type) const; QString appViewerOriginSubDir() const; diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp index 405f4c6d17..c612ff41c2 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp +++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp @@ -32,6 +32,7 @@ #include <cmath> #include <QMessageBox> +#include <QByteArray> #include <nodeabstractproperty.h> #include <nodemetainfo.h> #include <modelnode.h> @@ -211,7 +212,7 @@ void raise(const SelectionContext &selectionState) return; try { - RewriterTransaction transaction(selectionState.view()); + RewriterTransaction transaction(selectionState.view(), QByteArrayLiteral("DesignerActionManager|raise")); foreach (ModelNode modelNode, selectionState.selectedModelNodes()) { QmlItemNode node = modelNode; if (node.isValid()) { @@ -232,7 +233,7 @@ void lower(const SelectionContext &selectionState) return; try { - RewriterTransaction transaction(selectionState.view()); + RewriterTransaction transaction(selectionState.view(), QByteArrayLiteral("DesignerActionManager|lower")); foreach (ModelNode modelNode, selectionState.selectedModelNodes()) { QmlItemNode node = modelNode; if (node.isValid()) { @@ -302,7 +303,7 @@ void resetSize(const SelectionContext &selectionState) return; try { - RewriterTransaction transaction(selectionState.view()); + RewriterTransaction transaction(selectionState.view(), QByteArrayLiteral("DesignerActionManager|resetSize")); foreach (ModelNode node, selectionState.selectedModelNodes()) { node.removeProperty("width"); node.removeProperty("height"); @@ -318,7 +319,7 @@ void resetPosition(const SelectionContext &selectionState) return; try { - RewriterTransaction transaction(selectionState.view()); + RewriterTransaction transaction(selectionState.view(), QByteArrayLiteral("DesignerActionManager|resetPosition")); foreach (ModelNode node, selectionState.selectedModelNodes()) { node.removeProperty("x"); node.removeProperty("y"); @@ -342,7 +343,7 @@ void resetZ(const SelectionContext &selectionState) if (!selectionState.view()) return; - RewriterTransaction transaction(selectionState.view()); + RewriterTransaction transaction(selectionState.view(), QByteArrayLiteral("DesignerActionManager|resetZ")); foreach (ModelNode node, selectionState.selectedModelNodes()) { node.removeProperty("z"); } @@ -373,7 +374,7 @@ void anchorsFill(const SelectionContext &selectionState) if (!selectionState.view()) return; - RewriterTransaction transaction(selectionState.view()); + RewriterTransaction transaction(selectionState.view(), QByteArrayLiteral("DesignerActionManager|anchorsFill")); ModelNode modelNode = selectionState.currentSingleSelectedNode(); @@ -392,7 +393,7 @@ void anchorsReset(const SelectionContext &selectionState) if (!selectionState.view()) return; - RewriterTransaction transaction(selectionState.view()); + RewriterTransaction transaction(selectionState.view(), QByteArrayLiteral("DesignerActionManager|anchorsReset")); ModelNode modelNode = selectionState.currentSingleSelectedNode(); @@ -458,7 +459,7 @@ static void layoutHelperFunction(const SelectionContext &selectionContext, ModelNode layoutNode; { - RewriterTransaction transaction(selectionContext.view()); + RewriterTransaction transaction(selectionContext.view(), QByteArrayLiteral("DesignerActionManager|layoutHelperFunction1")); QmlItemNode parentNode = qmlItemNode.instanceParentItem(); @@ -470,7 +471,7 @@ static void layoutHelperFunction(const SelectionContext &selectionContext, } { - RewriterTransaction transaction(selectionContext.view()); + RewriterTransaction transaction(selectionContext.view(), QByteArrayLiteral("DesignerActionManager|layoutHelperFunction2")); QList<ModelNode> sortedSelectedNodes = selectionContext.selectedModelNodes(); qSort(sortedSelectedNodes.begin(), sortedSelectedNodes.end(), lessThan); diff --git a/src/plugins/qmldesigner/components/formeditor/anchorindicatorgraphicsitem.cpp b/src/plugins/qmldesigner/components/formeditor/anchorindicatorgraphicsitem.cpp index 7e7a21a67b..635c896a05 100644 --- a/src/plugins/qmldesigner/components/formeditor/anchorindicatorgraphicsitem.cpp +++ b/src/plugins/qmldesigner/components/formeditor/anchorindicatorgraphicsitem.cpp @@ -211,24 +211,26 @@ static void updateAnchorLinePoints(QPointF *firstPoint, QPointF *secondPoint, co void AnchorIndicatorGraphicsItem::updateAnchorIndicator(const AnchorLine &sourceAnchorLine, const AnchorLine targetAnchorLine) { - m_sourceAnchorLineType = sourceAnchorLine.type(); - m_targetAnchorLineType = targetAnchorLine.type(); + if (sourceAnchorLine.qmlItemNode().isValid() && targetAnchorLine.qmlItemNode().isValid()) { + m_sourceAnchorLineType = sourceAnchorLine.type(); + m_targetAnchorLineType = targetAnchorLine.type(); - m_startPoint = createAnchorPoint(sourceAnchorLine.qmlItemNode(), sourceAnchorLine.type()); + m_startPoint = createAnchorPoint(sourceAnchorLine.qmlItemNode(), sourceAnchorLine.type()); - if (targetAnchorLine.qmlItemNode() == sourceAnchorLine.qmlItemNode().instanceParentItem()) - m_endPoint = createParentAnchorPoint(targetAnchorLine.qmlItemNode(), targetAnchorLine.type(), sourceAnchorLine.qmlItemNode()); - else - m_endPoint = createAnchorPoint(targetAnchorLine.qmlItemNode(), targetAnchorLine.type()); + if (targetAnchorLine.qmlItemNode() == sourceAnchorLine.qmlItemNode().instanceParentItem()) + m_endPoint = createParentAnchorPoint(targetAnchorLine.qmlItemNode(), targetAnchorLine.type(), sourceAnchorLine.qmlItemNode()); + else + m_endPoint = createAnchorPoint(targetAnchorLine.qmlItemNode(), targetAnchorLine.type()); - m_firstControlPoint = createControlPoint(m_startPoint, sourceAnchorLine.type(), m_endPoint); - m_secondControlPoint = createControlPoint(m_endPoint, targetAnchorLine.type(), m_startPoint); + m_firstControlPoint = createControlPoint(m_startPoint, sourceAnchorLine.type(), m_endPoint); + m_secondControlPoint = createControlPoint(m_endPoint, targetAnchorLine.type(), m_startPoint); - updateAnchorLinePoints(&m_sourceAnchorLineFirstPoint, &m_sourceAnchorLineSecondPoint, sourceAnchorLine); - updateAnchorLinePoints(&m_targetAnchorLineFirstPoint, &m_targetAnchorLineSecondPoint, targetAnchorLine); + updateAnchorLinePoints(&m_sourceAnchorLineFirstPoint, &m_sourceAnchorLineSecondPoint, sourceAnchorLine); + updateAnchorLinePoints(&m_targetAnchorLineFirstPoint, &m_targetAnchorLineSecondPoint, targetAnchorLine); - updateBoundingRect(); - update(); + updateBoundingRect(); + update(); + } } void AnchorIndicatorGraphicsItem::updateBoundingRect() diff --git a/src/plugins/qmldesigner/components/formeditor/contentnoteditableindicator.cpp b/src/plugins/qmldesigner/components/formeditor/contentnoteditableindicator.cpp index 098cae7a17..5cba0e3565 100644 --- a/src/plugins/qmldesigner/components/formeditor/contentnoteditableindicator.cpp +++ b/src/plugins/qmldesigner/components/formeditor/contentnoteditableindicator.cpp @@ -30,6 +30,8 @@ #include "contentnoteditableindicator.h" #include "nodemetainfo.h" +#include <QSet> + namespace QmlDesigner { ContentNotEditableIndicator::ContentNotEditableIndicator(LayerItem *layerItem) @@ -68,6 +70,24 @@ void ContentNotEditableIndicator::setItems(const QList<FormEditorItem*> &itemLis addAddiationEntries(itemList); } +void ContentNotEditableIndicator::updateItems(const QList<FormEditorItem *> &itemList) +{ + QSet<FormEditorItem*> affectedFormEditorItemItems; + affectedFormEditorItemItems.unite(itemList.toSet()); + foreach (FormEditorItem *formEditorItem, itemList) + affectedFormEditorItemItems.unite(formEditorItem->offspringFormEditorItems().toSet()); + + foreach (const EntryPair &entryPair, m_entryList) { + foreach (FormEditorItem *formEditorItem, affectedFormEditorItemItems) { + if (formEditorItem == entryPair.first) { + QRectF boundingRectangleInSceneSpace = formEditorItem->qmlItemNode().instanceSceneTransform().mapRect(formEditorItem->qmlItemNode().instanceBoundingRect()); + entryPair.second->setRect(boundingRectangleInSceneSpace); + entryPair.second->update(); + } + } + } +} + void ContentNotEditableIndicator::addAddiationEntries(const QList<FormEditorItem *> &itemList) { foreach (FormEditorItem *formEditorItem, itemList) { diff --git a/src/plugins/qmldesigner/components/formeditor/contentnoteditableindicator.h b/src/plugins/qmldesigner/components/formeditor/contentnoteditableindicator.h index a61340f9f1..9cd3ac8e1d 100644 --- a/src/plugins/qmldesigner/components/formeditor/contentnoteditableindicator.h +++ b/src/plugins/qmldesigner/components/formeditor/contentnoteditableindicator.h @@ -49,6 +49,7 @@ public: void clear(); void setItems(const QList<FormEditorItem*> &itemList); + void updateItems(const QList<FormEditorItem*> &itemList); protected: void addAddiationEntries(const QList<FormEditorItem*> &itemList); diff --git a/src/plugins/qmldesigner/components/formeditor/dragtool.cpp b/src/plugins/qmldesigner/components/formeditor/dragtool.cpp index d2c7e321df..393ab9a454 100644 --- a/src/plugins/qmldesigner/components/formeditor/dragtool.cpp +++ b/src/plugins/qmldesigner/components/formeditor/dragtool.cpp @@ -324,7 +324,7 @@ void DragTool::dragEnterEvent(QGraphicsSceneDragDropEvent * event) if (!m_rewriterTransaction.isValid()) { view()->clearSelectedModelNodes(); - m_rewriterTransaction = view()->beginRewriterTransaction(); + m_rewriterTransaction = view()->beginRewriterTransaction(QByteArrayLiteral("DragTool::dragEnterEvent")); } } } diff --git a/src/plugins/qmldesigner/components/formeditor/formeditoritem.cpp b/src/plugins/qmldesigner/components/formeditor/formeditoritem.cpp index 14305590a2..805948f053 100644 --- a/src/plugins/qmldesigner/components/formeditor/formeditoritem.cpp +++ b/src/plugins/qmldesigner/components/formeditor/formeditoritem.cpp @@ -83,14 +83,28 @@ void FormEditorItem::setup() QRectF FormEditorItem::boundingRect() const { - return m_paintedBoundingRect; + return m_boundingRect; +} + +QPainterPath FormEditorItem::shape() const +{ + QPainterPath painterPath; + painterPath.addRect(m_selectionBoundingRect); + + return painterPath; +} + +bool FormEditorItem::contains(const QPointF &point) const +{ + return m_selectionBoundingRect.contains(point); } void FormEditorItem::updateGeometry() { prepareGeometryChange(); - m_boundingRect = qmlItemNode().instanceBoundingRect().adjusted(0, 0, 1., 1.); - m_paintedBoundingRect = qmlItemNode().instancePaintedBoundingRect().united(m_boundingRect); + m_selectionBoundingRect = qmlItemNode().instanceBoundingRect().adjusted(0, 0, 1., 1.); + m_paintedBoundingRect = qmlItemNode().instancePaintedBoundingRect(); + m_boundingRect = m_paintedBoundingRect.united(m_selectionBoundingRect); setTransform(qmlItemNode().instanceTransformWithContentTransform()); //the property for zValue is called z in QGraphicsObject if (qmlItemNode().instanceValue("z").isValid()) @@ -221,6 +235,8 @@ void FormEditorItem::paintBoundingRect(QPainter *painter) const void FormEditorItem::paintPlaceHolderForInvisbleItem(QPainter *painter) const { + painter->save(); + qreal stripesWidth = 12; QRegion innerRegion = QRegion(m_boundingRect.adjusted(stripesWidth, stripesWidth, -stripesWidth, -stripesWidth).toRect()); @@ -229,7 +245,6 @@ void FormEditorItem::paintPlaceHolderForInvisbleItem(QPainter *painter) const painter->setClipRegion(outerRegion); painter->setClipping(true); painter->fillRect(m_boundingRect.adjusted(1, 1, -1, -1), Qt::BDiagPattern); - painter->setClipping(false); QString displayText = qmlItemNode().id(); @@ -241,8 +256,6 @@ void FormEditorItem::paintPlaceHolderForInvisbleItem(QPainter *painter) const textOption.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere); if (m_boundingRect.height() > 60) { - painter->save(); - QFont font; font.setStyleHint(QFont::SansSerif); font.setBold(true); @@ -265,9 +278,9 @@ void FormEditorItem::paintPlaceHolderForInvisbleItem(QPainter *painter) const painter->setFont(font); painter->setPen(QColor(48, 48, 96, 255)); painter->drawText(rotatedBoundingBox, displayText, textOption); - - painter->restore(); } + + painter->restore(); } void FormEditorItem::paintComponentContentVisualisation(QPainter *painter, const QRectF &clippinRectangle) const @@ -276,6 +289,20 @@ void FormEditorItem::paintComponentContentVisualisation(QPainter *painter, const painter->fillRect(clippinRectangle, Qt::BDiagPattern); } +QList<FormEditorItem *> FormEditorItem::offspringFormEditorItemsRecursive(const FormEditorItem *formEditorItem) const +{ + QList<FormEditorItem*> formEditorItemList; + + foreach (QGraphicsItem *item, formEditorItem->childItems()) { + FormEditorItem *formEditorItem = fromQGraphicsItem(item); + if (formEditorItem) { + formEditorItemList.append(formEditorItem); + } + } + + return formEditorItemList; +} + void FormEditorItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) { if (!painter->isActive()) @@ -291,9 +318,9 @@ void FormEditorItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *, paintPlaceHolderForInvisbleItem(painter); } else { if (m_blurContent) - painter->drawPixmap(boundingRect().topLeft(), qmlItemNode().instanceBlurredRenderPixmap()); + painter->drawPixmap(m_paintedBoundingRect.topLeft(), qmlItemNode().instanceBlurredRenderPixmap()); else - painter->drawPixmap(boundingRect().topLeft(), qmlItemNode().instanceRenderPixmap()); + painter->drawPixmap(m_paintedBoundingRect.topLeft(), qmlItemNode().instanceRenderPixmap()); } if (!qmlItemNode().isRootModelNode()) @@ -381,6 +408,11 @@ QList<FormEditorItem*> FormEditorItem::childFormEditorItems() const return formEditorItemList; } +QList<FormEditorItem *> FormEditorItem::offspringFormEditorItems() const +{ + return offspringFormEditorItemsRecursive(this); +} + bool FormEditorItem::isContainer() const { NodeMetaInfo nodeMetaInfo = qmlItemNode().modelNode().metaInfo(); diff --git a/src/plugins/qmldesigner/components/formeditor/formeditoritem.h b/src/plugins/qmldesigner/components/formeditor/formeditoritem.h index b6558eff7f..3370d0dce9 100644 --- a/src/plugins/qmldesigner/components/formeditor/formeditoritem.h +++ b/src/plugins/qmldesigner/components/formeditor/formeditoritem.h @@ -86,10 +86,14 @@ public: SnapLineMap rightSnappingOffsets() const; QList<FormEditorItem*> childFormEditorItems() const; + QList<FormEditorItem*> offspringFormEditorItems() const; + FormEditorScene *scene() const; FormEditorItem *parentItem() const; QRectF boundingRect() const; + QPainterPath shape() const; + bool contains(const QPointF &point) const; void updateGeometry(); void updateVisibilty(); @@ -112,6 +116,7 @@ protected: void paintBoundingRect(QPainter *painter) const; void paintPlaceHolderForInvisbleItem(QPainter *painter) const; void paintComponentContentVisualisation(QPainter *painter, const QRectF &clippinRectangle) const; + QList<FormEditorItem*> offspringFormEditorItemsRecursive(const FormEditorItem *formEditorItem) const; private: // functions FormEditorItem(const QmlItemNode &qmlItemNode, FormEditorScene* scene); @@ -124,6 +129,7 @@ private: // variables QTransform m_inverseAttentionTransform; QRectF m_boundingRect; QRectF m_paintedBoundingRect; + QRectF m_selectionBoundingRect; double m_borderWidth; bool m_highlightBoundingRect; bool m_blurContent; diff --git a/src/plugins/qmldesigner/components/formeditor/movemanipulator.cpp b/src/plugins/qmldesigner/components/formeditor/movemanipulator.cpp index 0802bb166d..3cecfc4763 100644 --- a/src/plugins/qmldesigner/components/formeditor/movemanipulator.cpp +++ b/src/plugins/qmldesigner/components/formeditor/movemanipulator.cpp @@ -159,7 +159,7 @@ void MoveManipulator::begin(const QPointF &beginPoint) // setOpacityForAllElements(0.62); - m_rewriterTransaction = m_view->beginRewriterTransaction(); + m_rewriterTransaction = m_view->beginRewriterTransaction(QByteArrayLiteral("MoveManipulator::begin")); } @@ -421,7 +421,7 @@ void MoveManipulator::moveBy(double deltaX, double deltaY) void MoveManipulator::beginRewriterTransaction() { - m_rewriterTransaction = m_view->beginRewriterTransaction(); + m_rewriterTransaction = m_view->beginRewriterTransaction(QByteArrayLiteral("MoveManipulator::beginRewriterTransaction")); } void MoveManipulator::endRewriterTransaction() diff --git a/src/plugins/qmldesigner/components/formeditor/movetool.cpp b/src/plugins/qmldesigner/components/formeditor/movetool.cpp index e92fb8458d..6a05c2c1dd 100644 --- a/src/plugins/qmldesigner/components/formeditor/movetool.cpp +++ b/src/plugins/qmldesigner/components/formeditor/movetool.cpp @@ -190,7 +190,6 @@ void MoveTool::keyReleaseEvent(QKeyEvent *keyEvent) } if (!keyEvent->isAutoRepeat()) { - m_moveManipulator.beginRewriterTransaction(); m_moveManipulator.clear(); // m_selectionIndicator.show(); m_resizeIndicator.show(); @@ -401,6 +400,7 @@ void MoveTool::formEditorItemsChanged(const QList<FormEditorItem*> &itemList) m_resizeIndicator.updateItems(selectedItemList); m_anchorIndicator.updateItems(selectedItemList); m_bindingIndicator.updateItems(selectedItemList); + m_contentNotEditableIndicator.updateItems(selectedItemList); } } diff --git a/src/plugins/qmldesigner/components/formeditor/resizemanipulator.cpp b/src/plugins/qmldesigner/components/formeditor/resizemanipulator.cpp index ccb2135068..82e54505ef 100644 --- a/src/plugins/qmldesigner/components/formeditor/resizemanipulator.cpp +++ b/src/plugins/qmldesigner/components/formeditor/resizemanipulator.cpp @@ -81,7 +81,7 @@ void ResizeManipulator::begin(const QPointF &/*beginPoint*/) m_beginFromSceneToContentItemTransform = m_beginFromContentItemToSceneTransform.inverted(); m_beginFromItemToSceneTransform = m_resizeController.formEditorItem()->qmlItemNode().instanceSceneTransform(); m_beginToParentTransform = m_resizeController.formEditorItem()->qmlItemNode().instanceTransform(); - m_rewriterTransaction = m_view->beginRewriterTransaction(); + m_rewriterTransaction = m_view->beginRewriterTransaction(QByteArrayLiteral("ResizeManipulator::begin")); m_snapper.updateSnappingLines(m_resizeController.formEditorItem()); m_beginBottomRightPoint = m_beginToParentTransform.map(m_resizeController.formEditorItem()->qmlItemNode().instanceBoundingRect().bottomRight()); diff --git a/src/plugins/qmldesigner/components/formeditor/selectionindicator.cpp b/src/plugins/qmldesigner/components/formeditor/selectionindicator.cpp index f11bca4bff..473816a5e5 100644 --- a/src/plugins/qmldesigner/components/formeditor/selectionindicator.cpp +++ b/src/plugins/qmldesigner/components/formeditor/selectionindicator.cpp @@ -46,20 +46,20 @@ SelectionIndicator::~SelectionIndicator() void SelectionIndicator::show() { - foreach (QGraphicsPolygonItem *item, m_indicatorShapeHash.values()) + foreach (QGraphicsPolygonItem *item, m_indicatorShapeHash) item->show(); } void SelectionIndicator::hide() { - foreach (QGraphicsPolygonItem *item, m_indicatorShapeHash.values()) + foreach (QGraphicsPolygonItem *item, m_indicatorShapeHash) item->hide(); } void SelectionIndicator::clear() { if (m_layerItem) { - foreach (QGraphicsItem *item, m_indicatorShapeHash.values()) { + foreach (QGraphicsItem *item, m_indicatorShapeHash) { m_layerItem->scene()->removeItem(item); delete item; } @@ -120,7 +120,7 @@ void SelectionIndicator::setCursor(const QCursor &cursor) { m_cursor = cursor; - foreach (QGraphicsItem *item, m_indicatorShapeHash.values()) + foreach (QGraphicsItem *item, m_indicatorShapeHash) item->setCursor(cursor); } diff --git a/src/plugins/qmldesigner/components/formeditor/selectiontool.cpp b/src/plugins/qmldesigner/components/formeditor/selectiontool.cpp index fc7defbb88..6eceed6307 100644 --- a/src/plugins/qmldesigner/components/formeditor/selectiontool.cpp +++ b/src/plugins/qmldesigner/components/formeditor/selectiontool.cpp @@ -271,6 +271,7 @@ void SelectionTool::formEditorItemsChanged(const QList<FormEditorItem*> &itemLis m_resizeIndicator.updateItems(selectedItemList); m_anchorIndicator.updateItems(selectedItemList); m_bindingIndicator.updateItems(selectedItemList); + m_contentNotEditableIndicator.updateItems(selectedItemList); } void SelectionTool::instancesCompleted(const QList<FormEditorItem*> &/*itemList*/) diff --git a/src/plugins/qmldesigner/components/integration/designdocument.cpp b/src/plugins/qmldesigner/components/integration/designdocument.cpp index 1c0b6ecd99..d5a428e1a0 100644 --- a/src/plugins/qmldesigner/components/integration/designdocument.cpp +++ b/src/plugins/qmldesigner/components/integration/designdocument.cpp @@ -356,7 +356,7 @@ void DesignDocument::deleteSelected() return; try { - RewriterTransaction transaction(rewriterView()); + RewriterTransaction transaction(rewriterView(), QByteArrayLiteral("DesignDocument::deleteSelected")); QList<ModelNode> toDelete = view()->selectedModelNodes(); foreach (ModelNode node, toDelete) { if (node.isValid() && !node.isRootNode() && QmlObjectNode::isValidQmlObjectNode(node)) @@ -511,7 +511,7 @@ void DesignDocument::paste() QList<ModelNode> pastedNodeList; try { - RewriterTransaction transaction(rewriterView()); + RewriterTransaction transaction(rewriterView(), QByteArrayLiteral("DesignDocument::paste1")); int offset = double(qrand()) / RAND_MAX * 20 - 10; @@ -529,7 +529,7 @@ void DesignDocument::paste() } } else { try { - RewriterTransaction transaction(rewriterView()); + RewriterTransaction transaction(rewriterView(), QByteArrayLiteral("DesignDocument::paste2")); pasteModel->detachView(&view); currentModel()->attachView(&view); diff --git a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp index 9c7b567a06..f4f59b508b 100644 --- a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp +++ b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp @@ -510,7 +510,7 @@ void NavigatorTreeModel::moveNodesInteractive(NodeAbstractProperty parentPropert try { TypeName propertyQmlType = qmlTypeInQtContainer(parentProperty.parentModelNode().metaInfo().propertyTypeName(parentProperty.name())); - RewriterTransaction transaction = m_view->beginRewriterTransaction(); + RewriterTransaction transaction = m_view->beginRewriterTransaction(QByteArrayLiteral("NavigatorTreeModel::moveNodesInteractive")); foreach (const ModelNode &node, modelNodes) { if (!node.isValid()) continue; diff --git a/src/plugins/qmldesigner/components/propertyeditor/gradientlineqmladaptor.cpp b/src/plugins/qmldesigner/components/propertyeditor/gradientlineqmladaptor.cpp index feafc14c86..b63fa8af42 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/gradientlineqmladaptor.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/gradientlineqmladaptor.cpp @@ -131,7 +131,7 @@ void GradientLineQmlAdaptor::writeGradient() ModelNode gradientNode= modelNode.view()->createModelNode("QtQuick.Gradient", modelNode.view()->majorQtQuickVersion(), 0); modelNode.nodeProperty(gradientName().toUtf8()).reparentHere(gradientNode); - RewriterTransaction transaction = m_itemNode.modelNode().view()->beginRewriterTransaction(); + RewriterTransaction transaction = m_itemNode.modelNode().view()->beginRewriterTransaction(QByteArrayLiteral("GradientLineQmlAdaptor::writeGradient")); if (!oldId.isNull()) gradientNode.setId(oldId); @@ -173,7 +173,7 @@ void GradientLineQmlAdaptor::deleteGradient() if (m_itemNode.isInBaseState()) { if (modelNode.hasProperty(gradientName().toUtf8())) { - RewriterTransaction transaction = m_itemNode.modelNode().view()->beginRewriterTransaction(); + RewriterTransaction transaction = m_itemNode.modelNode().view()->beginRewriterTransaction(QByteArrayLiteral("GradientLineQmlAdaptor::deleteGradient")); ModelNode gradientNode = modelNode.nodeProperty(gradientName().toUtf8()).modelNode(); if (QmlObjectNode(gradientNode).isValid()) QmlObjectNode(gradientNode).destroy(); diff --git a/src/plugins/qmldesigner/components/propertyeditor/gradientmodel.cpp b/src/plugins/qmldesigner/components/propertyeditor/gradientmodel.cpp index d21c2fabec..855c475cfd 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/gradientmodel.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/gradientmodel.cpp @@ -143,7 +143,7 @@ void GradientModel::addGradient() if (!color.isValid()) color = QColor(Qt::white); - QmlDesigner::RewriterTransaction transaction = m_itemNode.modelNode().view()->beginRewriterTransaction(); + QmlDesigner::RewriterTransaction transaction = m_itemNode.modelNode().view()->beginRewriterTransaction(QByteArrayLiteral("GradientModel::addGradient")); QmlDesigner::ModelNode gradientNode = m_itemNode.modelNode().view()->createModelNode("QtQuick.Gradient", @@ -227,7 +227,7 @@ qreal GradientModel::getPosition(int index) const void GradientModel::removeStop(int index) { if (index < rowCount() - 1 && index != 0) { - QmlDesigner::RewriterTransaction transaction = m_itemNode.modelNode().view()->beginRewriterTransaction(); + QmlDesigner::RewriterTransaction transaction = m_itemNode.modelNode().view()->beginRewriterTransaction(QByteArrayLiteral("GradientModel::removeStop")); QmlDesigner::ModelNode gradientNode = m_itemNode.modelNode().nodeProperty(gradientPropertyName().toUtf8()).modelNode(); QmlDesigner::QmlObjectNode stop = gradientNode.nodeListProperty("stops").toModelNodeList().at(index); if (stop.isValid()) { @@ -251,7 +251,7 @@ void GradientModel::deleteGradient() if (m_itemNode.isInBaseState()) { if (modelNode.hasProperty(gradientPropertyName().toUtf8())) { - QmlDesigner::RewriterTransaction transaction = m_itemNode.modelNode().view()->beginRewriterTransaction(); + QmlDesigner::RewriterTransaction transaction = m_itemNode.modelNode().view()->beginRewriterTransaction(QByteArrayLiteral("GradientModel::deleteGradient")); QmlDesigner::ModelNode gradientNode = modelNode.nodeProperty(gradientPropertyName().toUtf8()).modelNode(); if (QmlDesigner::QmlObjectNode(gradientNode).isValid()) QmlDesigner::QmlObjectNode(gradientNode).destroy(); diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditortransaction.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditortransaction.cpp index d8a0c3d66a..e0014d2562 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditortransaction.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditortransaction.cpp @@ -44,7 +44,7 @@ void PropertyEditorTransaction::start() return; if (m_rewriterTransaction.isValid()) m_rewriterTransaction.commit(); - m_rewriterTransaction = m_propertyEditor->beginRewriterTransaction(); + m_rewriterTransaction = m_propertyEditor->beginRewriterTransaction(QByteArrayLiteral("PropertyEditorTransaction::start")); m_timerId = startTimer(4000); } diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp index 2ac59cb424..2177cf6522 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp @@ -151,7 +151,11 @@ void PropertyEditorValue::setValue(const QVariant &value) if (m_value.isValid()) emit valueChangedQml(); emit isBoundChanged(); +} +QString PropertyEditorValue::enumeration() const +{ + return m_value.value<QmlDesigner::Enumeration>().nameToString(); } QString PropertyEditorValue::expression() const @@ -267,6 +271,13 @@ void PropertyEditorValue::resetValue() } } +void PropertyEditorValue::setEnumeration(const QString &scope, const QString &name) +{ + QmlDesigner::Enumeration newEnumeration(scope, name); + + setValueWithEmit(QVariant::fromValue(newEnumeration)); +} + void PropertyEditorValue::registerDeclarativeTypes() { qmlRegisterType<PropertyEditorValue>("HelperWidgets",2,0,"PropertyEditorValue"); diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.h b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.h index 9ae815f44e..ff432abf01 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.h +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.h @@ -36,6 +36,7 @@ #include <QQmlPropertyMap> #include <QtQml> #include <modelnode.h> +#include <enumeration.h> class PropertyEditorValue; @@ -78,6 +79,7 @@ class PropertyEditorValue : public QObject { Q_OBJECT Q_PROPERTY(QVariant value READ value WRITE setValueWithEmit NOTIFY valueChangedQml) + Q_PROPERTY(QVariant enumeration READ enumeration NOTIFY valueChangedQml) Q_PROPERTY(QString expression READ expression WRITE setExpressionWithEmit NOTIFY expressionChanged FINAL) Q_PROPERTY(QString valueToString READ valueToString NOTIFY valueChangedQml FINAL) Q_PROPERTY(bool isInModel READ isInModel NOTIFY valueChangedQml FINAL) @@ -96,6 +98,8 @@ public: void setValueWithEmit(const QVariant &value); void setValue(const QVariant &value); + QString enumeration() const; + QString expression() const; void setExpressionWithEmit(const QString &expression); void setExpression(const QString &expression); @@ -125,6 +129,7 @@ public: public slots: void resetValue(); + void setEnumeration(const QString &scope, const QString &name); signals: void valueChanged(const QString &name, const QVariant&); diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp index 5ed4a00f28..f1dc42216b 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp @@ -240,7 +240,7 @@ void PropertyEditorView::changeExpression(const QString &propertyName) if (m_locked) return; - RewriterTransaction transaction = beginRewriterTransaction(); + RewriterTransaction transaction = beginRewriterTransaction(QByteArrayLiteral("PropertyEditorView::changeExpression")); try { PropertyName underscoreName(name); diff --git a/src/plugins/qmldesigner/components/propertyeditor/qmlanchorbindingproxy.cpp b/src/plugins/qmldesigner/components/propertyeditor/qmlanchorbindingproxy.cpp index 05e54f9063..e140692b57 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/qmlanchorbindingproxy.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/qmlanchorbindingproxy.cpp @@ -268,7 +268,7 @@ void QmlAnchorBindingProxy::setTopTarget(const QString &target) if (!newTarget.isValid()) return; - RewriterTransaction transaction = m_qmlItemNode.modelNode().view()->beginRewriterTransaction(); + RewriterTransaction transaction = m_qmlItemNode.modelNode().view()->beginRewriterTransaction(QByteArrayLiteral("QmlAnchorBindingProxy::setTopTarget")); m_topTarget = newTarget; calcTopMargin(); @@ -293,7 +293,7 @@ void QmlAnchorBindingProxy::setBottomTarget(const QString &target) if (!newTarget.isValid()) return; - RewriterTransaction transaction = m_qmlItemNode.modelNode().view()->beginRewriterTransaction(); + RewriterTransaction transaction = m_qmlItemNode.modelNode().view()->beginRewriterTransaction(QByteArrayLiteral("QmlAnchorBindingProxy::setBottomTarget")); m_bottomTarget = newTarget; calcBottomMargin(); @@ -314,7 +314,7 @@ void QmlAnchorBindingProxy::setLeftTarget(const QString &target) if (!newTarget.isValid()) return; - RewriterTransaction transaction = m_qmlItemNode.modelNode().view()->beginRewriterTransaction(); + RewriterTransaction transaction = m_qmlItemNode.modelNode().view()->beginRewriterTransaction(QByteArrayLiteral("QmlAnchorBindingProxy::setLeftTarget")); m_leftTarget = newTarget; calcLeftMargin(); @@ -335,7 +335,7 @@ void QmlAnchorBindingProxy::setRightTarget(const QString &target) if (!newTarget.isValid()) return; - RewriterTransaction transaction = m_qmlItemNode.modelNode().view()->beginRewriterTransaction(); + RewriterTransaction transaction = m_qmlItemNode.modelNode().view()->beginRewriterTransaction(QByteArrayLiteral("QmlAnchorBindingProxy::setRightTarget")); m_rightTarget = newTarget; calcRightMargin(); @@ -356,7 +356,7 @@ void QmlAnchorBindingProxy::setVerticalTarget(const QString &target) if (!newTarget.isValid()) return; - RewriterTransaction transaction = m_qmlItemNode.modelNode().view()->beginRewriterTransaction(); + RewriterTransaction transaction = m_qmlItemNode.modelNode().view()->beginRewriterTransaction(QByteArrayLiteral("QmlAnchorBindingProxy::setVerticalTarget")); m_verticalTarget = newTarget; m_qmlItemNode.anchors().setAnchor(AnchorLine::VerticalCenter, m_verticalTarget, AnchorLine::VerticalCenter); @@ -377,7 +377,7 @@ void QmlAnchorBindingProxy::setHorizontalTarget(const QString &target) if (!newTarget.isValid()) return; - RewriterTransaction transaction = m_qmlItemNode.modelNode().view()->beginRewriterTransaction(); + RewriterTransaction transaction = m_qmlItemNode.modelNode().view()->beginRewriterTransaction(QByteArrayLiteral("QmlAnchorBindingProxy::setHorizontalTarget")); m_horizontalTarget = newTarget; m_qmlItemNode.anchors().setAnchor(AnchorLine::HorizontalCenter, m_horizontalTarget, AnchorLine::HorizontalCenter); @@ -421,7 +421,7 @@ int QmlAnchorBindingProxy::indexOfPossibleTargetItem(const QString &targetName) } void QmlAnchorBindingProxy::resetLayout() { - RewriterTransaction transaction = m_qmlItemNode.modelNode().view()->beginRewriterTransaction(); + RewriterTransaction transaction = m_qmlItemNode.modelNode().view()->beginRewriterTransaction(QByteArrayLiteral("QmlAnchorBindingProxy::resetLayout")); m_qmlItemNode.anchors().removeAnchors(); m_qmlItemNode.anchors().removeMargins(); @@ -446,7 +446,7 @@ void QmlAnchorBindingProxy::setBottomAnchor(bool anchor) if (bottomAnchored() == anchor) return; - RewriterTransaction transaction = m_qmlItemNode.modelNode().view()->beginRewriterTransaction(); + RewriterTransaction transaction = m_qmlItemNode.modelNode().view()->beginRewriterTransaction(QByteArrayLiteral("QmlAnchorBindingProxy::setBottomAnchor")); if (!anchor) { removeBottomAnchor(); @@ -469,7 +469,7 @@ void QmlAnchorBindingProxy::setLeftAnchor(bool anchor) if (leftAnchored() == anchor) return; - RewriterTransaction transaction = m_qmlItemNode.modelNode().view()->beginRewriterTransaction(); + RewriterTransaction transaction = m_qmlItemNode.modelNode().view()->beginRewriterTransaction(QByteArrayLiteral("QmlAnchorBindingProxy::setLeftAnchor")); if (!anchor) { removeLeftAnchor(); @@ -493,7 +493,7 @@ void QmlAnchorBindingProxy::setRightAnchor(bool anchor) if (rightAnchored() == anchor) return; - RewriterTransaction transaction = m_qmlItemNode.modelNode().view()->beginRewriterTransaction(); + RewriterTransaction transaction = m_qmlItemNode.modelNode().view()->beginRewriterTransaction(QByteArrayLiteral("QmlAnchorBindingProxy::setRightAnchor")); if (!anchor) { removeRightAnchor(); @@ -634,7 +634,7 @@ void QmlAnchorBindingProxy::setTopAnchor(bool anchor) if (topAnchored() == anchor) return; - RewriterTransaction transaction = m_qmlItemNode.modelNode().view()->beginRewriterTransaction(); + RewriterTransaction transaction = m_qmlItemNode.modelNode().view()->beginRewriterTransaction(QByteArrayLiteral("QmlAnchorBindingProxy::setTopAnchor")); if (!anchor) { removeTopAnchor(); @@ -650,7 +650,7 @@ void QmlAnchorBindingProxy::setTopAnchor(bool anchor) } void QmlAnchorBindingProxy::removeTopAnchor() { - RewriterTransaction transaction = m_qmlItemNode.modelNode().view()->beginRewriterTransaction(); + RewriterTransaction transaction = m_qmlItemNode.modelNode().view()->beginRewriterTransaction(QByteArrayLiteral("QmlAnchorBindingProxy::removeTopAnchor")); m_qmlItemNode.anchors().removeAnchor(AnchorLine::Top); m_qmlItemNode.anchors().removeMargin(AnchorLine::Top); @@ -661,7 +661,7 @@ void QmlAnchorBindingProxy::removeTopAnchor() { } void QmlAnchorBindingProxy::removeBottomAnchor() { - RewriterTransaction transaction = m_qmlItemNode.modelNode().view()->beginRewriterTransaction(); + RewriterTransaction transaction = m_qmlItemNode.modelNode().view()->beginRewriterTransaction(QByteArrayLiteral("QmlAnchorBindingProxy::removeBottomAnchor")); m_qmlItemNode.anchors().removeAnchor(AnchorLine::Bottom); m_qmlItemNode.anchors().removeMargin(AnchorLine::Bottom); @@ -671,7 +671,7 @@ void QmlAnchorBindingProxy::removeBottomAnchor() { } void QmlAnchorBindingProxy::removeLeftAnchor() { - RewriterTransaction transaction = m_qmlItemNode.modelNode().view()->beginRewriterTransaction(); + RewriterTransaction transaction = m_qmlItemNode.modelNode().view()->beginRewriterTransaction(QByteArrayLiteral("QmlAnchorBindingProxy::removeLeftAnchor")); m_qmlItemNode.anchors().removeAnchor(AnchorLine::Left); m_qmlItemNode.anchors().removeMargin(AnchorLine::Left); @@ -681,7 +681,7 @@ void QmlAnchorBindingProxy::removeLeftAnchor() { } void QmlAnchorBindingProxy::removeRightAnchor() { - RewriterTransaction transaction = m_qmlItemNode.modelNode().view()->beginRewriterTransaction(); + RewriterTransaction transaction = m_qmlItemNode.modelNode().view()->beginRewriterTransaction(QByteArrayLiteral("QmlAnchorBindingProxy::removeRightAnchor")); m_qmlItemNode.anchors().removeAnchor(AnchorLine::Right); m_qmlItemNode.anchors().removeMargin(AnchorLine::Right); @@ -699,7 +699,7 @@ void QmlAnchorBindingProxy::setVerticalCentered(bool centered) m_locked = true; - RewriterTransaction transaction = m_qmlItemNode.modelNode().view()->beginRewriterTransaction(); + RewriterTransaction transaction = m_qmlItemNode.modelNode().view()->beginRewriterTransaction(QByteArrayLiteral("QmlAnchorBindingProxy::setVerticalCentered")); if (!centered) { m_qmlItemNode.anchors().removeAnchor(AnchorLine::VerticalCenter); @@ -723,7 +723,7 @@ void QmlAnchorBindingProxy::setHorizontalCentered(bool centered) m_locked = true; - RewriterTransaction transaction = m_qmlItemNode.modelNode().view()->beginRewriterTransaction(); + RewriterTransaction transaction = m_qmlItemNode.modelNode().view()->beginRewriterTransaction(QByteArrayLiteral("QmlAnchorBindingProxy::setHorizontalCentered")); if (!centered) { m_qmlItemNode.anchors().removeAnchor(AnchorLine::HorizontalCenter); @@ -780,7 +780,7 @@ bool QmlAnchorBindingProxy::horizontalCentered() void QmlAnchorBindingProxy::fill() { - RewriterTransaction transaction = m_qmlItemNode.modelNode().view()->beginRewriterTransaction(); + RewriterTransaction transaction = m_qmlItemNode.modelNode().view()->beginRewriterTransaction(QByteArrayLiteral("QmlAnchorBindingProxy::fill")); backupPropertyAndRemove(modelNode(), "x"); diff --git a/src/plugins/qmldesigner/designercore/designercore-lib.pri b/src/plugins/qmldesigner/designercore/designercore-lib.pri index 686867037e..32f2200bca 100644 --- a/src/plugins/qmldesigner/designercore/designercore-lib.pri +++ b/src/plugins/qmldesigner/designercore/designercore-lib.pri @@ -13,6 +13,7 @@ include (instances/instances.pri) include (../../../../share/qtcreator/qml/qmlpuppet/interfaces/interfaces.pri) include (../../../../share/qtcreator/qml/qmlpuppet/commands/commands.pri) include (../../../../share/qtcreator/qml/qmlpuppet/container/container.pri) +include (../../../../share/qtcreator/qml/qmlpuppet/types/types.pri) SOURCES += $$PWD/model/abstractview.cpp \ $$PWD/model/rewriterview.cpp \ diff --git a/src/plugins/qmldesigner/designercore/include/abstractview.h b/src/plugins/qmldesigner/designercore/include/abstractview.h index 41ad7406ae..653b0afccb 100644 --- a/src/plugins/qmldesigner/designercore/include/abstractview.h +++ b/src/plugins/qmldesigner/designercore/include/abstractview.h @@ -132,7 +132,7 @@ public: Model* model() const; bool isAttached() const; - RewriterTransaction beginRewriterTransaction(); + RewriterTransaction beginRewriterTransaction(const QByteArray &identifier); ModelNode createModelNode(const TypeName &typeName, int majorVersion, diff --git a/src/plugins/qmldesigner/designercore/include/variantproperty.h b/src/plugins/qmldesigner/designercore/include/variantproperty.h index eb548419fb..592556e0d3 100644 --- a/src/plugins/qmldesigner/designercore/include/variantproperty.h +++ b/src/plugins/qmldesigner/designercore/include/variantproperty.h @@ -32,6 +32,7 @@ #include "qmldesignercorelib_global.h" #include "abstractproperty.h" +#include "enumeration.h" QT_BEGIN_NAMESPACE class QTextStream; @@ -55,7 +56,12 @@ public: void setValue(const QVariant &value); QVariant value() const; + void setEnumeration(const EnumerationName &enumerationName); + Enumeration enumeration() const; + bool holdsEnumeration() const; + void setDynamicTypeNameAndValue(const TypeName &type, const QVariant &value); + void setDynamicTypeNameAndEnumeration(const TypeName &type, const EnumerationName &enumerationName); VariantProperty(); VariantProperty(const VariantProperty &property, AbstractView *view); diff --git a/src/plugins/qmldesigner/designercore/instances/nodeinstanceserverproxy.cpp b/src/plugins/qmldesigner/designercore/instances/nodeinstanceserverproxy.cpp index a52cf475a8..c5f2b9f80b 100644 --- a/src/plugins/qmldesigner/designercore/instances/nodeinstanceserverproxy.cpp +++ b/src/plugins/qmldesigner/designercore/instances/nodeinstanceserverproxy.cpp @@ -126,6 +126,20 @@ QString NodeInstanceServerProxy::creatorQmlPuppetPath() return applicationPath; } +bool NodeInstanceServerProxy::checkPuppetVersion(const QString &qmlPuppetPath) +{ + QProcess qmlPuppetVersionProcess; + qmlPuppetVersionProcess.start(qmlPuppetPath, QStringList() << "--version"); + qmlPuppetVersionProcess.waitForReadyRead(6000); + + QByteArray versionString = qmlPuppetVersionProcess.readAll(); + + bool canConvert; + unsigned int versionNumber = versionString.toUInt(&canConvert); + + return canConvert && versionNumber == 2; +} + NodeInstanceServerProxy::NodeInstanceServerProxy(NodeInstanceView *nodeInstanceView, RunModus runModus, const QString &pathToQt) : NodeInstanceServerInterface(nodeInstanceView), m_localServer(new QLocalServer(this)), @@ -140,11 +154,6 @@ NodeInstanceServerProxy::NodeInstanceServerProxy(NodeInstanceView *nodeInstanceV m_runModus(runModus), m_synchronizeId(-1) { - QString socketToken(QUuid::createUuid().toString()); - - m_localServer->listen(socketToken); - m_localServer->setMaxPendingConnections(3); - QString applicationPath = pathToQt + QLatin1String("/bin"); if (runModus == TestModus) { applicationPath = QCoreApplication::applicationDirPath() @@ -175,86 +184,94 @@ NodeInstanceServerProxy::NodeInstanceServerProxy(NodeInstanceView *nodeInstanceV #endif if (QFileInfo(applicationPath).exists()) { - m_qmlPuppetEditorProcess = new QProcess; - m_qmlPuppetEditorProcess->setProcessEnvironment(environment); - m_qmlPuppetEditorProcess->setObjectName("EditorProcess"); - connect(m_qmlPuppetEditorProcess.data(), SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(processFinished(int,QProcess::ExitStatus))); - connect(QCoreApplication::instance(), SIGNAL(aboutToQuit()), m_qmlPuppetEditorProcess.data(), SLOT(kill())); - bool fowardQmlpuppetOutput = !qgetenv("FORWARD_QMLPUPPET_OUTPUT").isEmpty(); - if (fowardQmlpuppetOutput) { - m_qmlPuppetEditorProcess->setProcessChannelMode(QProcess::MergedChannels); - connect(m_qmlPuppetEditorProcess.data(), SIGNAL(readyRead()), this, SLOT(printEditorProcessOutput())); - } - m_qmlPuppetEditorProcess->start(applicationPath, QStringList() << socketToken << "editormode" << "-graphicssystem raster"); - - if (runModus == NormalModus) { - m_qmlPuppetPreviewProcess = new QProcess; - m_qmlPuppetPreviewProcess->setProcessEnvironment(environment); - m_qmlPuppetPreviewProcess->setObjectName("PreviewProcess"); - connect(m_qmlPuppetPreviewProcess.data(), SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(processFinished(int,QProcess::ExitStatus))); - connect(QCoreApplication::instance(), SIGNAL(aboutToQuit()), m_qmlPuppetPreviewProcess.data(), SLOT(kill())); + if (checkPuppetVersion(applicationPath)) { + QString socketToken(QUuid::createUuid().toString()); + m_localServer->listen(socketToken); + m_localServer->setMaxPendingConnections(3); + + m_qmlPuppetEditorProcess = new QProcess; + m_qmlPuppetEditorProcess->setProcessEnvironment(environment); + m_qmlPuppetEditorProcess->setObjectName("EditorProcess"); + connect(m_qmlPuppetEditorProcess.data(), SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(processFinished(int,QProcess::ExitStatus))); + connect(QCoreApplication::instance(), SIGNAL(aboutToQuit()), m_qmlPuppetEditorProcess.data(), SLOT(kill())); + bool fowardQmlpuppetOutput = !qgetenv("FORWARD_QMLPUPPET_OUTPUT").isEmpty(); if (fowardQmlpuppetOutput) { - m_qmlPuppetPreviewProcess->setProcessChannelMode(QProcess::MergedChannels); - connect(m_qmlPuppetPreviewProcess.data(), SIGNAL(readyRead()), this, SLOT(printPreviewProcessOutput())); + m_qmlPuppetEditorProcess->setProcessChannelMode(QProcess::MergedChannels); + connect(m_qmlPuppetEditorProcess.data(), SIGNAL(readyRead()), this, SLOT(printEditorProcessOutput())); } - m_qmlPuppetPreviewProcess->start(applicationPath, QStringList() << socketToken << "previewmode" << "-graphicssystem raster"); + m_qmlPuppetEditorProcess->start(applicationPath, QStringList() << socketToken << "editormode" << "-graphicssystem raster"); + + if (runModus == NormalModus) { + m_qmlPuppetPreviewProcess = new QProcess; + m_qmlPuppetPreviewProcess->setProcessEnvironment(environment); + m_qmlPuppetPreviewProcess->setObjectName("PreviewProcess"); + connect(m_qmlPuppetPreviewProcess.data(), SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(processFinished(int,QProcess::ExitStatus))); + connect(QCoreApplication::instance(), SIGNAL(aboutToQuit()), m_qmlPuppetPreviewProcess.data(), SLOT(kill())); + if (fowardQmlpuppetOutput) { + m_qmlPuppetPreviewProcess->setProcessChannelMode(QProcess::MergedChannels); + connect(m_qmlPuppetPreviewProcess.data(), SIGNAL(readyRead()), this, SLOT(printPreviewProcessOutput())); + } + m_qmlPuppetPreviewProcess->start(applicationPath, QStringList() << socketToken << "previewmode" << "-graphicssystem raster"); + + m_qmlPuppetRenderProcess = new QProcess; + m_qmlPuppetRenderProcess->setProcessEnvironment(environment); + m_qmlPuppetRenderProcess->setObjectName("RenderProcess"); + connect(m_qmlPuppetRenderProcess.data(), SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(processFinished(int,QProcess::ExitStatus))); + connect(QCoreApplication::instance(), SIGNAL(aboutToQuit()), m_qmlPuppetRenderProcess.data(), SLOT(kill())); + if (fowardQmlpuppetOutput) { + m_qmlPuppetRenderProcess->setProcessChannelMode(QProcess::MergedChannels); + connect(m_qmlPuppetRenderProcess.data(), SIGNAL(readyRead()), this, SLOT(printRenderProcessOutput())); + } + m_qmlPuppetRenderProcess->start(applicationPath, QStringList() << socketToken << "rendermode" << "-graphicssystem raster"); - m_qmlPuppetRenderProcess = new QProcess; - m_qmlPuppetRenderProcess->setProcessEnvironment(environment); - m_qmlPuppetRenderProcess->setObjectName("RenderProcess"); - connect(m_qmlPuppetRenderProcess.data(), SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(processFinished(int,QProcess::ExitStatus))); - connect(QCoreApplication::instance(), SIGNAL(aboutToQuit()), m_qmlPuppetRenderProcess.data(), SLOT(kill())); - if (fowardQmlpuppetOutput) { - m_qmlPuppetRenderProcess->setProcessChannelMode(QProcess::MergedChannels); - connect(m_qmlPuppetRenderProcess.data(), SIGNAL(readyRead()), this, SLOT(printRenderProcessOutput())); } - m_qmlPuppetRenderProcess->start(applicationPath, QStringList() << socketToken << "rendermode" << "-graphicssystem raster"); - } + connect(QCoreApplication::instance(), SIGNAL(aboutToQuit()), this, SLOT(deleteLater())); - connect(QCoreApplication::instance(), SIGNAL(aboutToQuit()), this, SLOT(deleteLater())); + if (m_qmlPuppetEditorProcess->waitForStarted(10000)) { + connect(m_qmlPuppetEditorProcess.data(), SIGNAL(finished(int)), m_qmlPuppetEditorProcess.data(),SLOT(deleteLater())); - if (m_qmlPuppetEditorProcess->waitForStarted(10000)) { - connect(m_qmlPuppetEditorProcess.data(), SIGNAL(finished(int)), m_qmlPuppetEditorProcess.data(),SLOT(deleteLater())); + if (runModus == NormalModus) { + m_qmlPuppetPreviewProcess->waitForStarted(); + connect(m_qmlPuppetPreviewProcess.data(), SIGNAL(finished(int)), m_qmlPuppetPreviewProcess.data(),SLOT(deleteLater())); - if (runModus == NormalModus) { - m_qmlPuppetPreviewProcess->waitForStarted(); - connect(m_qmlPuppetPreviewProcess.data(), SIGNAL(finished(int)), m_qmlPuppetPreviewProcess.data(),SLOT(deleteLater())); + m_qmlPuppetRenderProcess->waitForStarted(); + connect(m_qmlPuppetRenderProcess.data(), SIGNAL(finished(int)), m_qmlPuppetRenderProcess.data(),SLOT(deleteLater())); + } - m_qmlPuppetRenderProcess->waitForStarted(); - connect(m_qmlPuppetRenderProcess.data(), SIGNAL(finished(int)), m_qmlPuppetRenderProcess.data(),SLOT(deleteLater())); - } + if (!m_localServer->hasPendingConnections()) + m_localServer->waitForNewConnection(10000); - if (!m_localServer->hasPendingConnections()) - m_localServer->waitForNewConnection(10000); + m_firstSocket = m_localServer->nextPendingConnection(); + connect(m_firstSocket.data(), SIGNAL(readyRead()), this, SLOT(readFirstDataStream())); - m_firstSocket = m_localServer->nextPendingConnection(); - connect(m_firstSocket.data(), SIGNAL(readyRead()), this, SLOT(readFirstDataStream())); + if (runModus == NormalModus) { + if (!m_localServer->hasPendingConnections()) + m_localServer->waitForNewConnection(10000); - if (runModus == NormalModus) { - if (!m_localServer->hasPendingConnections()) - m_localServer->waitForNewConnection(10000); + m_secondSocket = m_localServer->nextPendingConnection(); + connect(m_secondSocket.data(), SIGNAL(readyRead()), this, SLOT(readSecondDataStream())); - m_secondSocket = m_localServer->nextPendingConnection(); - connect(m_secondSocket.data(), SIGNAL(readyRead()), this, SLOT(readSecondDataStream())); + if (!m_localServer->hasPendingConnections()) + m_localServer->waitForNewConnection(10000); - if (!m_localServer->hasPendingConnections()) - m_localServer->waitForNewConnection(10000); + m_thirdSocket = m_localServer->nextPendingConnection(); + connect(m_thirdSocket.data(), SIGNAL(readyRead()), this, SLOT(readThirdDataStream())); + } - m_thirdSocket = m_localServer->nextPendingConnection(); - connect(m_thirdSocket.data(), SIGNAL(readyRead()), this, SLOT(readThirdDataStream())); + } else { + QMessageBox::warning(0, tr("Cannot Start QML Puppet Executable"), + tr("The executable of the QML Puppet process (%1) cannot be started. " + "Please check your installation. " + "QML Puppet is a process which runs in the background to render the items."). + arg(applicationPath)); } + m_localServer->close(); + } else { - QMessageBox::warning(0, tr("Cannot Start QML Puppet Executable"), - tr("The executable of the QML Puppet process (%1) cannot be started. " - "Please check your installation. " - "QML Puppet is a process which runs in the background to render the items."). - arg(applicationPath)); + QMessageBox::warning(0, tr("Wrong QML Puppet Executable Version"), tr("The QML Puppet version is incompatible with the Qt Creator version.")); } - - m_localServer->close(); - } else { QMessageBox::warning(0, tr("Cannot Find QML Puppet Executable"), missingQmlPuppetErrorMessage(applicationPath)); } diff --git a/src/plugins/qmldesigner/designercore/instances/nodeinstanceserverproxy.h b/src/plugins/qmldesigner/designercore/instances/nodeinstanceserverproxy.h index 4c0593e519..00bef05957 100644 --- a/src/plugins/qmldesigner/designercore/instances/nodeinstanceserverproxy.h +++ b/src/plugins/qmldesigner/designercore/instances/nodeinstanceserverproxy.h @@ -79,6 +79,8 @@ protected: QString qmlPuppetApplicationName() const; QString macOSBundlePath(const QString &path) const; QString creatorQmlPuppetPath(); + static bool checkPuppetVersion(const QString &qmlPuppetPath); + signals: void processCrashed(); diff --git a/src/plugins/qmldesigner/designercore/model/abstractview.cpp b/src/plugins/qmldesigner/designercore/model/abstractview.cpp index 76b699f4aa..c59dab43b9 100644 --- a/src/plugins/qmldesigner/designercore/model/abstractview.cpp +++ b/src/plugins/qmldesigner/designercore/model/abstractview.cpp @@ -71,9 +71,9 @@ void AbstractView::setModel(Model *model) m_model = model; } -RewriterTransaction AbstractView::beginRewriterTransaction() +RewriterTransaction AbstractView::beginRewriterTransaction(const QByteArray &identifier) { - return RewriterTransaction(this); + return RewriterTransaction(this, identifier); } ModelNode AbstractView::createModelNode(const TypeName &typeName, diff --git a/src/plugins/qmldesigner/designercore/model/import.cpp b/src/plugins/qmldesigner/designercore/model/import.cpp index 0dd477049b..67e9510c1c 100644 --- a/src/plugins/qmldesigner/designercore/model/import.cpp +++ b/src/plugins/qmldesigner/designercore/model/import.cpp @@ -92,7 +92,7 @@ QString Import::toString(bool skipAlias) const bool Import::operator==(const Import &other) const { - return url() == other.url() && file() == other.file() && version() == other.version() && alias() == other.alias(); + return url() == other.url() && file() == other.file() && (version() == other.version() || version().isEmpty() || other.version().isEmpty()); } bool Import::isSameModule(const Import &other) const diff --git a/src/plugins/qmldesigner/designercore/model/modelmerger.cpp b/src/plugins/qmldesigner/designercore/model/modelmerger.cpp index 0613b3d069..6fa0110b2b 100644 --- a/src/plugins/qmldesigner/designercore/model/modelmerger.cpp +++ b/src/plugins/qmldesigner/designercore/model/modelmerger.cpp @@ -154,7 +154,7 @@ static ModelNode createNodeFromNode(const ModelNode &modelNode,const QHash<QStri ModelNode ModelMerger::insertModel(const ModelNode &modelNode) { - RewriterTransaction transaction(view()->beginRewriterTransaction()); + RewriterTransaction transaction(view()->beginRewriterTransaction(QByteArrayLiteral("ModelMerger::insertModel"))); QList<Import> newImports; @@ -179,7 +179,7 @@ void ModelMerger::replaceModel(const ModelNode &modelNode) view()->model()->setFileUrl(modelNode.model()->fileUrl()); try { - RewriterTransaction transaction(view()->beginRewriterTransaction()); + RewriterTransaction transaction(view()->beginRewriterTransaction(QByteArrayLiteral("ModelMerger::replaceModel"))); ModelNode rootNode(view()->rootModelNode()); diff --git a/src/plugins/qmldesigner/designercore/model/qmlanchors.cpp b/src/plugins/qmldesigner/designercore/model/qmlanchors.cpp index cbd295f87b..23ee802bf2 100644 --- a/src/plugins/qmldesigner/designercore/model/qmlanchors.cpp +++ b/src/plugins/qmldesigner/designercore/model/qmlanchors.cpp @@ -176,7 +176,7 @@ void QmlAnchors::setAnchor(AnchorLine::Type sourceAnchorLine, const QmlItemNode &targetQmlItemNode, AnchorLine::Type targetAnchorLine) { - RewriterTransaction transaction = qmlItemNode().view()->beginRewriterTransaction(); + RewriterTransaction transaction = qmlItemNode().view()->beginRewriterTransaction(QByteArrayLiteral("QmlAnchors::setAnchor")); if (qmlItemNode().isInBaseState()) { if ((qmlItemNode().nodeInstance().hasAnchor("anchors.fill") && (sourceAnchorLine & AnchorLine::Fill)) || ((qmlItemNode().nodeInstance().hasAnchor("anchors.centerIn") && (sourceAnchorLine & AnchorLine::Center)))) { @@ -330,7 +330,7 @@ AnchorLine QmlAnchors::instanceAnchor(AnchorLine::Type sourceAnchorLine) const void QmlAnchors::removeAnchor(AnchorLine::Type sourceAnchorLine) { - RewriterTransaction transaction = qmlItemNode().view()->beginRewriterTransaction(); + RewriterTransaction transaction = qmlItemNode().view()->beginRewriterTransaction(QByteArrayLiteral("QmlAnchors::removeAnchor")); if (qmlItemNode().isInBaseState()) { const PropertyName propertyName = anchorPropertyName(sourceAnchorLine); if (qmlItemNode().nodeInstance().hasAnchor("anchors.fill") && (sourceAnchorLine & AnchorLine::Fill)) { @@ -352,7 +352,7 @@ void QmlAnchors::removeAnchor(AnchorLine::Type sourceAnchorLine) void QmlAnchors::removeAnchors() { - RewriterTransaction transaction = qmlItemNode().view()->beginRewriterTransaction(); + RewriterTransaction transaction = qmlItemNode().view()->beginRewriterTransaction(QByteArrayLiteral("QmlAnchors::removeAnchors")); if (qmlItemNode().nodeInstance().hasAnchor("anchors.fill")) qmlItemNode().modelNode().removeProperty("anchors.fill"); if (qmlItemNode().nodeInstance().hasAnchor("anchors.centerIn")) @@ -540,7 +540,7 @@ void QmlAnchors::removeMargin(AnchorLine::Type sourceAnchorLineType) void QmlAnchors::removeMargins() { - RewriterTransaction transaction = qmlItemNode().view()->beginRewriterTransaction(); + RewriterTransaction transaction = qmlItemNode().view()->beginRewriterTransaction(QByteArrayLiteral("QmlAnchors::removeMargins")); removeMargin(AnchorLine::Left); removeMargin(AnchorLine::Right); removeMargin(AnchorLine::Top); diff --git a/src/plugins/qmldesigner/designercore/model/qmlchangeset.cpp b/src/plugins/qmldesigner/designercore/model/qmlchangeset.cpp index 14f00a5168..c34cfe2381 100644 --- a/src/plugins/qmldesigner/designercore/model/qmlchangeset.cpp +++ b/src/plugins/qmldesigner/designercore/model/qmlchangeset.cpp @@ -72,7 +72,7 @@ bool QmlModelStateOperation::isValidQmlModelStateOperation(const ModelNode &mode void QmlPropertyChanges::removeProperty(const PropertyName &name) { - RewriterTransaction transaction(view()->beginRewriterTransaction()); + RewriterTransaction transaction(view()->beginRewriterTransaction(QByteArrayLiteral("QmlPropertyChanges::removeProperty"))); if (name == "name") return; modelNode().removeProperty(name); diff --git a/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp b/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp index b557cafeca..96ca55157f 100644 --- a/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp +++ b/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp @@ -100,7 +100,7 @@ QmlItemNode QmlItemNode::createQmlItemNode(AbstractView *view, const ItemLibrary QmlItemNode newQmlItemNode; try { - RewriterTransaction transaction = view->beginRewriterTransaction(); + RewriterTransaction transaction = view->beginRewriterTransaction(QByteArrayLiteral("QmlItemNode::createQmlItemNode")); NodeMetaInfo metaInfo = view->model()->metaInfo(itemLibraryEntry.typeName()); @@ -208,7 +208,7 @@ QmlItemNode QmlItemNode::createQmlItemNodeFromImage(AbstractView *view, const QS parentQmlItemNode = QmlItemNode(view->rootModelNode()); if (parentQmlItemNode.isValid()) { - RewriterTransaction transaction = view->beginRewriterTransaction(); + RewriterTransaction transaction = view->beginRewriterTransaction(QByteArrayLiteral("QmlItemNode::createQmlItemNodeFromImage")); checkImageImport(view); diff --git a/src/plugins/qmldesigner/designercore/model/qmltextgenerator.cpp b/src/plugins/qmldesigner/designercore/model/qmltextgenerator.cpp index 4be3d74103..5e505f8d58 100644 --- a/src/plugins/qmldesigner/designercore/model/qmltextgenerator.cpp +++ b/src/plugins/qmldesigner/designercore/model/qmltextgenerator.cpp @@ -113,9 +113,8 @@ QString QmlTextGenerator::toQml(const AbstractProperty &property, int indentDept if (false) { } - if (variantProperty.parentModelNode().metaInfo().isValid() && - variantProperty.parentModelNode().metaInfo().propertyIsEnumType(variantProperty.name())) { - return variantProperty.parentModelNode().metaInfo().propertyEnumScope(variantProperty.name()) + '.' + stringValue; + if (variantProperty.holdsEnumeration()) { + return variantProperty.enumeration().toString(); } else { switch (value.type()) { diff --git a/src/plugins/qmldesigner/designercore/model/rewriterview.cpp b/src/plugins/qmldesigner/designercore/model/rewriterview.cpp index 6f3e4efca2..c59ef15c95 100644 --- a/src/plugins/qmldesigner/designercore/model/rewriterview.cpp +++ b/src/plugins/qmldesigner/designercore/model/rewriterview.cpp @@ -203,7 +203,7 @@ void RewriterView::propertiesAboutToBeRemoved(const QList<AbstractProperty> &pro foreach (const AbstractProperty &property, propertyList) { if (property.isDefaultProperty() && property.isNodeListProperty()) { - m_removeDefaultPropertyTransaction = beginRewriterTransaction(); + m_removeDefaultPropertyTransaction = beginRewriterTransaction(QByteArrayLiteral("RewriterView::propertiesAboutToBeRemoved")); foreach (const ModelNode &node, property.toNodeListProperty().toModelNodeList()) { modelToTextMerger()->nodeRemoved(node, property.toNodeAbstractProperty(), AbstractView::NoAdditionalChanges); diff --git a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp index 3cedee0bdd..979defd1ab 100644 --- a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp +++ b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp @@ -40,6 +40,7 @@ #include "variantproperty.h" #include "signalhandlerproperty.h" #include "nodemetainfo.h" +#include "enumeration.h" #include <qmljs/qmljsevaluate.h> #include <qmljs/qmljslink.h> @@ -565,7 +566,7 @@ public: if (astValueList.count() == 2 //Check for global Qt enums && astValueList.first() == QLatin1String("Qt") && globalQtEnums().contains(astValueList.last())) - return QVariant(astValueList.last()); + return QVariant::fromValue(Enumeration(astValue)); ExpressionStatement *eStmt = cast<ExpressionStatement *>(rhs); if (!eStmt || !eStmt->expression) @@ -607,7 +608,7 @@ public: return QVariant(); if (rhsCppComponentValue->getEnum(lhsPropertyTypeName).hasKey(rhsValueName)) - return QVariant(rhsValueName); + return QVariant::fromValue(Enumeration(astValue)); else return QVariant(); } diff --git a/src/plugins/qmldesigner/designercore/model/variantproperty.cpp b/src/plugins/qmldesigner/designercore/model/variantproperty.cpp index bbbeb2bc5b..ab50777f24 100644 --- a/src/plugins/qmldesigner/designercore/model/variantproperty.cpp +++ b/src/plugins/qmldesigner/designercore/model/variantproperty.cpp @@ -89,6 +89,21 @@ QVariant VariantProperty::value() const return QVariant(); } +void VariantProperty::setEnumeration(const EnumerationName &enumerationName) +{ + setValue(QVariant::fromValue(Enumeration(enumerationName))); +} + +Enumeration VariantProperty::enumeration() const +{ + return value().value<Enumeration>(); +} + +bool VariantProperty::holdsEnumeration() const +{ + return value().canConvert<Enumeration>(); +} + void VariantProperty::setDynamicTypeNameAndValue(const TypeName &type, const QVariant &value) { Internal::WriteLocker locker(model()); @@ -111,7 +126,12 @@ void VariantProperty::setDynamicTypeNameAndValue(const TypeName &type, const QVa if (internalNode()->hasProperty(name()) && !internalNode()->property(name())->isVariantProperty()) model()->d->removeProperty(internalNode()->property(name())); - model()->d->setDynamicVariantProperty(internalNode(), name(), type, value); + model()->d->setDynamicVariantProperty(internalNode(), name(), type, value); +} + +void VariantProperty::setDynamicTypeNameAndEnumeration(const TypeName &type, const EnumerationName &enumerationName) +{ + setDynamicTypeNameAndValue(type, QVariant::fromValue(Enumeration(enumerationName))); } QDebug operator<<(QDebug debug, const VariantProperty &VariantProperty) diff --git a/src/plugins/qmldesigner/designercore/rewritertransaction.cpp b/src/plugins/qmldesigner/designercore/rewritertransaction.cpp index 8b00e96b17..3888bc3d98 100644 --- a/src/plugins/qmldesigner/designercore/rewritertransaction.cpp +++ b/src/plugins/qmldesigner/designercore/rewritertransaction.cpp @@ -35,13 +35,29 @@ namespace QmlDesigner { + +QList<QByteArray> RewriterTransaction::m_identifierList; +bool RewriterTransaction::m_activeIdentifier = !qgetenv("QML_DESIGNER_TRACE_REWRITER_TRANSACTION").isEmpty(); + RewriterTransaction::RewriterTransaction() : m_valid(false) { } -RewriterTransaction::RewriterTransaction(AbstractView *_view) : m_view(_view), m_valid(true) +RewriterTransaction::RewriterTransaction(AbstractView *_view, const QByteArray &identifier) + : m_view(_view), + m_identifier(identifier), + m_valid(true) { Q_ASSERT(view()); + + static int identifierNumber = 0; + m_identifierNumber = identifierNumber++; + + if (m_activeIdentifier) { + qDebug() << "Begin RewriterTransaction:" << m_identifier << m_identifierNumber; + m_identifierList.append(m_identifier + QByteArrayLiteral("-") + QByteArray::number(m_identifierNumber)); + } + view()->emitRewriterBeginTransaction(); } @@ -60,6 +76,12 @@ void RewriterTransaction::commit() if (m_valid) { m_valid = false; view()->emitRewriterEndTransaction(); + + if (m_activeIdentifier) { + qDebug() << "Commit RewriterTransaction:" << m_identifier << m_identifierNumber; + bool success = m_identifierList.removeOne(m_identifier + QByteArrayLiteral("-") + QByteArray::number(m_identifierNumber)); + Q_ASSERT(success); + } } } @@ -70,6 +92,11 @@ void RewriterTransaction::rollback() m_valid = false; view()->emitRewriterEndTransaction(); QmlDesignerPlugin::instance()->currentDesignDocument()->undo(); + + if (m_activeIdentifier) { + qDebug() << "Rollback RewriterTransaction:" << m_identifier << m_identifierNumber; + m_identifierList.removeOne(m_identifier + QByteArrayLiteral("-") + QByteArray::number(m_identifierNumber)); + } } } @@ -84,6 +111,8 @@ RewriterTransaction::RewriterTransaction(const RewriterTransaction &other) if (&other != this) { m_valid = other.m_valid; m_view = other.m_view; + m_identifier = other.m_identifier; + m_identifierNumber = other.m_identifierNumber; other.m_valid = false; } } @@ -93,6 +122,8 @@ RewriterTransaction& RewriterTransaction::operator=(const RewriterTransaction &o if (!m_valid && (&other != this)) { m_valid = other.m_valid; m_view = other.m_view; + m_identifier = other.m_identifier; + m_identifierNumber = other.m_identifierNumber; other.m_valid = false; } diff --git a/src/plugins/qmldesigner/designercore/rewritertransaction.h b/src/plugins/qmldesigner/designercore/rewritertransaction.h index ac58e5fb3a..815b0cb4bf 100644 --- a/src/plugins/qmldesigner/designercore/rewritertransaction.h +++ b/src/plugins/qmldesigner/designercore/rewritertransaction.h @@ -42,7 +42,7 @@ class QMLDESIGNERCORE_EXPORT RewriterTransaction { public: RewriterTransaction(); - RewriterTransaction(AbstractView *view); + RewriterTransaction(AbstractView *view, const QByteArray &identifier); ~RewriterTransaction(); void commit(); void rollback(); @@ -55,7 +55,11 @@ protected: AbstractView *view(); private: QWeakPointer<AbstractView> m_view; + QByteArray m_identifier; mutable bool m_valid; + int m_identifierNumber; + static QList<QByteArray> m_identifierList; + static bool m_activeIdentifier; }; } //QmlDesigner diff --git a/src/plugins/qmldesigner/qmldesignerplugin.cpp b/src/plugins/qmldesigner/qmldesignerplugin.cpp index 5108cab37f..69de3901de 100644 --- a/src/plugins/qmldesigner/qmldesignerplugin.cpp +++ b/src/plugins/qmldesigner/qmldesignerplugin.cpp @@ -94,6 +94,7 @@ QmlDesignerPlugin::QmlDesignerPlugin() : QmlDesignerPlugin::~QmlDesignerPlugin() { + Core::DesignMode::instance()->unregisterDesignWidget(m_mainWidget); Core::ICore::removeContextObject(m_context); m_context = 0; m_instance = 0; diff --git a/src/plugins/qmldesigner/settingspage.cpp b/src/plugins/qmldesigner/settingspage.cpp index af513e158e..884ad140fa 100644 --- a/src/plugins/qmldesigner/settingspage.cpp +++ b/src/plugins/qmldesigner/settingspage.cpp @@ -73,18 +73,6 @@ void SettingsPageWidget::setSettings(const DesignerSettings &designerSettings) m_ui.designerEnableDebuggerCheckBox->setChecked(designerSettings.enableDebugView); } -QString SettingsPageWidget::searchKeywords() const -{ - QString rc; - QTextStream(&rc) - << ' ' << m_ui.snapMarginLabel->text() - << ' ' << m_ui.itemSpacingLabel->text() - << ' ' << m_ui.canvasWidthLabel->text() - << ' ' << m_ui.canvasHeightLabel->text(); - rc.remove(QLatin1Char('&')); - return rc; -} - void SettingsPageWidget::debugViewEnabledToggled(bool b) { if (b && ! m_ui.designerShowDebuggerCheckBox->isChecked()) @@ -102,12 +90,12 @@ SettingsPage::SettingsPage() : setCategoryIcon(QLatin1String(Constants::SETTINGS_CATEGORY_QML_ICON)); } -QWidget *SettingsPage::createPage(QWidget *parent) +QWidget *SettingsPage::widget() { - m_widget = new SettingsPageWidget(parent); - m_widget->setSettings(QmlDesignerPlugin::instance()->settings()); - if (m_searchKeywords.isEmpty()) - m_searchKeywords = m_widget->searchKeywords(); + if (!m_widget) { + m_widget = new SettingsPageWidget; + m_widget->setSettings(QmlDesignerPlugin::instance()->settings()); + } return m_widget; } @@ -118,7 +106,7 @@ void SettingsPage::apply() QmlDesignerPlugin::instance()->setSettings(m_widget->settings()); } -bool SettingsPage::matches(const QString &s) const +void SettingsPage::finish() { - return m_searchKeywords.contains(s, Qt::CaseInsensitive); + delete m_widget; } diff --git a/src/plugins/qmldesigner/settingspage.h b/src/plugins/qmldesigner/settingspage.h index 22aedc9f4f..538b1aeb95 100644 --- a/src/plugins/qmldesigner/settingspage.h +++ b/src/plugins/qmldesigner/settingspage.h @@ -35,6 +35,7 @@ #include <coreplugin/dialogs/ioptionspage.h> +#include <QPointer> #include <QWidget> QT_BEGIN_NAMESPACE @@ -57,8 +58,6 @@ public: DesignerSettings settings() const; void setSettings(const DesignerSettings &designerSettings); - QString searchKeywords() const; - public slots: void debugViewEnabledToggled(bool b); @@ -74,14 +73,12 @@ class SettingsPage : public Core::IOptionsPage public: SettingsPage(); - QWidget *createPage(QWidget *parent); + QWidget *widget(); void apply(); - void finish() { } - bool matches(const QString &) const; + void finish(); private: - QString m_searchKeywords; - SettingsPageWidget* m_widget; + QPointer<SettingsPageWidget> m_widget; }; } // namespace Internal diff --git a/src/plugins/qmldesigner/settingspage.ui b/src/plugins/qmldesigner/settingspage.ui index 124950c424..528f621d13 100644 --- a/src/plugins/qmldesigner/settingspage.ui +++ b/src/plugins/qmldesigner/settingspage.ui @@ -58,7 +58,7 @@ <item row="0" column="1"> <widget class="QCheckBox" name="designerWarningsCheckBox"> <property name="toolTip"> - <string>Warn about QML features which are not properly supported by the Qt Quick Designer</string> + <string>Warns about QML features which are not properly supported by the Qt Quick Designer</string> </property> <property name="text"> <string>Warn about unsupported features in the Qt Quick Designer</string> @@ -68,7 +68,7 @@ <item row="1" column="1"> <widget class="QCheckBox" name="designerWarningsInEditorCheckBox"> <property name="toolTip"> - <string>Also warn in the code editor about QML features which are not properly supported by the Qt Quick Designer</string> + <string>Also warns in the code editor about QML features which are not properly supported by the Qt Quick Designer</string> </property> <property name="text"> <string>Warn about unsupported features of Qt Quick Designer in the code editor</string> diff --git a/src/plugins/qmljseditor/qmljscomponentnamedialog.cpp b/src/plugins/qmljseditor/qmljscomponentnamedialog.cpp index b65e7c99ea..cd0c0e10d1 100644 --- a/src/plugins/qmljseditor/qmljscomponentnamedialog.cpp +++ b/src/plugins/qmljseditor/qmljscomponentnamedialog.cpp @@ -66,6 +66,7 @@ void ComponentNameDialog::go(QString *proposedName, d.ui->componentNameEdit->setForceFirstCapitalLetter(true); d.ui->componentNameEdit->setText(*proposedName); d.ui->pathEdit->setExpectedKind(Utils::PathChooser::ExistingDirectory); + d.ui->pathEdit->setHistoryCompleter(QLatin1String("QmlJs.Component.History")); d.ui->pathEdit->setPath(*proposedPath); if (QDialog::Accepted == d.exec()) { diff --git a/src/plugins/qmljseditor/qmljseditor.cpp b/src/plugins/qmljseditor/qmljseditor.cpp index 8878d6d54c..bcd2b642b2 100644 --- a/src/plugins/qmljseditor/qmljseditor.cpp +++ b/src/plugins/qmljseditor/qmljseditor.cpp @@ -46,6 +46,7 @@ #include <qmljs/qmljsmodelmanagerinterface.h> #include <qmljs/qmljsutils.h> +#include <qmljstools/qmljstoolsconstants.h> #include <qmljstools/qmljsindenter.h> #include <qmljstools/qmljsqtstylecodeformatter.h> @@ -466,6 +467,7 @@ QmlJSTextEditorWidget::QmlJSTextEditorWidget(QWidget *parent) : setCodeFoldingSupported(true); setIndenter(new Indenter); setAutoCompleter(new AutoCompleter); + setLanguageSettingsId(QmlJSTools::Constants::QML_JS_SETTINGS_ID); m_updateDocumentTimer = new QTimer(this); m_updateDocumentTimer->setInterval(UPDATE_DOCUMENT_DEFAULT_INTERVAL); @@ -583,7 +585,7 @@ IEditor *QmlJSEditor::duplicate(QWidget *parent) { QmlJSTextEditorWidget *newEditor = new QmlJSTextEditorWidget(parent); newEditor->duplicateFrom(editorWidget()); - QmlJSEditorPlugin::instance()->initializeEditor(newEditor); + TextEditor::TextEditorSettings::initializeEditor(newEditor); return newEditor->editor(); } @@ -608,7 +610,7 @@ void QmlJSTextEditorWidget::reparseDocumentNow() { m_updateDocumentTimer->stop(); - const QString fileName = editorDocument()->filePath(); + const QString fileName = baseTextDocument()->filePath(); m_modelManager->updateSourceFiles(QStringList() << fileName, false); } @@ -650,7 +652,7 @@ static void appendExtraSelectionsForMessages( void QmlJSTextEditorWidget::onDocumentUpdated(QmlJS::Document::Ptr doc) { - if (editorDocument()->filePath() != doc->fileName()) + if (baseTextDocument()->filePath() != doc->fileName()) return; if (doc->editorRevision() != editorRevision()) { @@ -682,7 +684,7 @@ void QmlJSTextEditorWidget::onDocumentUpdated(QmlJS::Document::Ptr doc) void QmlJSTextEditorWidget::modificationChanged(bool changed) { if (!changed && m_modelManager) - m_modelManager->fileChangedOnDisk(editorDocument()->filePath()); + m_modelManager->fileChangedOnDisk(baseTextDocument()->filePath()); } void QmlJSTextEditorWidget::jumpToOutlineElement(int /*index*/) @@ -973,10 +975,6 @@ void QmlJSTextEditorWidget::setSelectedElements() emit selectedElementsChanged(offsets, wordAtCursor); } -void QmlJSTextEditorWidget::updateFileName() -{ -} - void QmlJSTextEditorWidget::setFontSettings(const TextEditor::FontSettings &fs) { TextEditor::BaseTextEditorWidget::setFontSettings(fs); @@ -1058,8 +1056,6 @@ void QmlJSTextEditorWidget::createToolBar(QmlJSEditor *editor) connect(m_outlineCombo, SIGNAL(activated(int)), this, SLOT(jumpToOutlineElement(int))); connect(this, SIGNAL(cursorPositionChanged()), m_updateOutlineIndexTimer, SLOT(start())); - connect(editorDocument(), SIGNAL(changed()), this, SLOT(updateFileName())); - editor->insertExtraToolBarWidget(TextEditor::BaseTextEditor::Left, m_outlineCombo); } @@ -1148,12 +1144,12 @@ TextEditor::BaseTextEditorWidget::Link QmlJSTextEditorWidget::findLinkAt(const Q void QmlJSTextEditorWidget::findUsages() { - m_findReferences->findUsages(editorDocument()->filePath(), textCursor().position()); + m_findReferences->findUsages(baseTextDocument()->filePath(), textCursor().position()); } void QmlJSTextEditorWidget::renameUsages() { - m_findReferences->renameUsages(editorDocument()->filePath(), textCursor().position()); + m_findReferences->renameUsages(baseTextDocument()->filePath(), textCursor().position()); } void QmlJSTextEditorWidget::showContextPane() diff --git a/src/plugins/qmljseditor/qmljseditor.h b/src/plugins/qmljseditor/qmljseditor.h index 21b5f00daf..6b9981ded7 100644 --- a/src/plugins/qmljseditor/qmljseditor.h +++ b/src/plugins/qmljseditor/qmljseditor.h @@ -142,7 +142,6 @@ private slots: void updateOutlineIndexNow(); void updateCursorPositionNow(); void showTextMarker(); - void updateFileName(); void updateUses(); void updateUsesNow(); diff --git a/src/plugins/qmljseditor/qmljseditorfactory.cpp b/src/plugins/qmljseditor/qmljseditorfactory.cpp index 006fe79d5c..5cba6ecb9b 100644 --- a/src/plugins/qmljseditor/qmljseditorfactory.cpp +++ b/src/plugins/qmljseditor/qmljseditorfactory.cpp @@ -34,6 +34,8 @@ #include "qmljseditorplugin.h" #include <qmljstools/qmljstoolsconstants.h> +#include <texteditor/texteditoractionhandler.h> +#include <texteditor/texteditorsettings.h> #include <QCoreApplication> @@ -52,12 +54,18 @@ QmlJSEditorFactory::QmlJSEditorFactory(QObject *parent) addMimeType(QmlJSTools::Constants::QMLTYPES_MIMETYPE); addMimeType(QmlJSTools::Constants::JS_MIMETYPE); addMimeType(QmlJSTools::Constants::JSON_MIMETYPE); + new TextEditor::TextEditorActionHandler(this, Constants::C_QMLJSEDITOR_ID, + TextEditor::TextEditorActionHandler::Format + | TextEditor::TextEditorActionHandler::UnCommentSelection + | TextEditor::TextEditorActionHandler::UnCollapseAll + | TextEditor::TextEditorActionHandler::FollowSymbolUnderCursor); + } Core::IEditor *QmlJSEditorFactory::createEditor(QWidget *parent) { QmlJSTextEditorWidget *rc = new QmlJSTextEditorWidget(parent); - QmlJSEditorPlugin::instance()->initializeEditor(rc); + TextEditor::TextEditorSettings::initializeEditor(rc); return rc->editor(); } diff --git a/src/plugins/qmljseditor/qmljseditorplugin.cpp b/src/plugins/qmljseditor/qmljseditorplugin.cpp index d63d612cb9..0cf2695503 100644 --- a/src/plugins/qmljseditor/qmljseditorplugin.cpp +++ b/src/plugins/qmljseditor/qmljseditorplugin.cpp @@ -62,9 +62,7 @@ #include <coreplugin/editormanager/editormanager.h> #include <projectexplorer/taskhub.h> #include <texteditor/texteditorconstants.h> -#include <texteditor/texteditorsettings.h> #include <texteditor/textfilewizard.h> -#include <texteditor/texteditoractionhandler.h> #include <utils/qtcassert.h> #include <utils/json.h> @@ -95,7 +93,6 @@ QmlJSEditorPlugin *QmlJSEditorPlugin::m_instance = 0; QmlJSEditorPlugin::QmlJSEditorPlugin() : m_modelManager(0), m_editor(0), - m_actionHandler(0), m_quickFixAssistProvider(0), m_reformatFileAction(0), m_currentEditor(0), @@ -109,7 +106,6 @@ QmlJSEditorPlugin::QmlJSEditorPlugin() : QmlJSEditorPlugin::~QmlJSEditorPlugin() { removeObject(m_editor); - delete m_actionHandler; m_instance = 0; } @@ -165,13 +161,6 @@ bool QmlJSEditorPlugin::initialize(const QStringList & /*arguments*/, QString *e wizard->setId(QLatin1String("Z.Js")); addAutoReleasedObject(wizard); - m_actionHandler = new TextEditor::TextEditorActionHandler(Constants::C_QMLJSEDITOR_ID, - TextEditor::TextEditorActionHandler::Format - | TextEditor::TextEditorActionHandler::UnCommentSelection - | TextEditor::TextEditorActionHandler::UnCollapseAll - | TextEditor::TextEditorActionHandler::FollowSymbolUnderCursor); - m_actionHandler->initializeActions(); - Core::ActionContainer *contextMenu = Core::ActionManager::createMenu(Constants::M_CONTEXT); Core::ActionContainer *qmlToolsMenu = Core::ActionManager::actionContainer(Core::Id(QmlJSTools::Constants::M_TOOLS_QMLJS)); @@ -262,16 +251,6 @@ ExtensionSystem::IPlugin::ShutdownFlag QmlJSEditorPlugin::aboutToShutdown() return IPlugin::aboutToShutdown(); } -void QmlJSEditorPlugin::initializeEditor(QmlJSTextEditorWidget *editor) -{ - QTC_CHECK(m_instance); - - m_actionHandler->setupActions(editor); - - editor->setLanguageSettingsId(QmlJSTools::Constants::QML_JS_SETTINGS_ID); - TextEditor::TextEditorSettings::initializeEditor(editor); -} - Utils::JsonSchemaManager *QmlJSEditorPlugin::jsonManager() const { return m_jsonManager.data(); diff --git a/src/plugins/qmljseditor/qmljseditorplugin.h b/src/plugins/qmljseditor/qmljseditorplugin.h index 2ee6e6ed76..791a653dfd 100644 --- a/src/plugins/qmljseditor/qmljseditorplugin.h +++ b/src/plugins/qmljseditor/qmljseditorplugin.h @@ -42,10 +42,6 @@ namespace Utils { class JsonSchemaManager; } -namespace TextEditor { -class TextEditorActionHandler; -} // namespace TextEditor - namespace Core { class Command; class ActionContainer; @@ -92,8 +88,6 @@ public: QmlJSQuickFixAssistProvider *quickFixAssistProvider() const; - void initializeEditor(QmlJSTextEditorWidget *editor); - Utils::JsonSchemaManager *jsonManager() const; public Q_SLOTS: @@ -115,7 +109,6 @@ private: QmlJS::ModelManagerInterface *m_modelManager; QmlJSEditorFactory *m_editor; - TextEditor::TextEditorActionHandler *m_actionHandler; QmlJSQuickFixAssistProvider *m_quickFixAssistProvider; QmlTaskManager *m_qmlTaskManager; diff --git a/src/plugins/qmljseditor/qmljsquickfixassist.cpp b/src/plugins/qmljseditor/qmljsquickfixassist.cpp index af91ea8ce4..1bccc67923 100644 --- a/src/plugins/qmljseditor/qmljsquickfixassist.cpp +++ b/src/plugins/qmljseditor/qmljsquickfixassist.cpp @@ -48,7 +48,7 @@ using namespace Internal; QmlJSQuickFixAssistInterface::QmlJSQuickFixAssistInterface(QmlJSTextEditorWidget *editor, TextEditor::AssistReason reason) : DefaultAssistInterface(editor->document(), editor->position(), - editor->editorDocument()->filePath(), reason) + editor->baseTextDocument()->filePath(), reason) , m_editor(editor) , m_semanticInfo(editor->semanticInfo()) , m_currentFile(QmlJSRefactoringChanges::file(m_editor, m_semanticInfo.document)) diff --git a/src/plugins/qmljseditor/qmljssemantichighlighter.cpp b/src/plugins/qmljseditor/qmljssemantichighlighter.cpp index 89a9e8d4a5..439f186629 100644 --- a/src/plugins/qmljseditor/qmljssemantichighlighter.cpp +++ b/src/plugins/qmljseditor/qmljssemantichighlighter.cpp @@ -568,7 +568,7 @@ void SemanticHighlighter::applyResults(int from, int to) if (m_startRevision != m_editor->editorRevision()) return; - TextEditor::BaseTextDocument *baseTextDocument = m_editor->baseTextDocument().data(); + TextEditor::BaseTextDocument *baseTextDocument = m_editor->baseTextDocument(); QTC_ASSERT(baseTextDocument, return); TextEditor::SyntaxHighlighter *highlighter = qobject_cast<TextEditor::SyntaxHighlighter *>(baseTextDocument->syntaxHighlighter()); QTC_ASSERT(highlighter, return); @@ -584,7 +584,7 @@ void SemanticHighlighter::finished() if (m_startRevision != m_editor->editorRevision()) return; - TextEditor::BaseTextDocument *baseTextDocument = m_editor->baseTextDocument().data(); + TextEditor::BaseTextDocument *baseTextDocument = m_editor->baseTextDocument(); QTC_ASSERT(baseTextDocument, return); TextEditor::SyntaxHighlighter *highlighter = qobject_cast<TextEditor::SyntaxHighlighter *>(baseTextDocument->syntaxHighlighter()); QTC_ASSERT(highlighter, return); diff --git a/src/plugins/qmljseditor/quicktoolbarsettingspage.cpp b/src/plugins/qmljseditor/quicktoolbarsettingspage.cpp index aca9d03dff..c7804178d2 100644 --- a/src/plugins/qmljseditor/quicktoolbarsettingspage.cpp +++ b/src/plugins/qmljseditor/quicktoolbarsettingspage.cpp @@ -101,16 +101,6 @@ void QuickToolBarSettingsPageWidget::setSettings(const QuickToolBarSettings &s) m_ui.textEditHelperCheckBoxPin->setChecked(s.pinContextPane); } -QString QuickToolBarSettingsPageWidget::searchKeywords() const -{ - QString rc; - QTextStream(&rc) - << ' ' << m_ui.textEditHelperCheckBox->text() - << ' ' << m_ui.textEditHelperCheckBoxPin->text(); - rc.remove(QLatin1Char('&')); - return rc; -} - QuickToolBarSettings QuickToolBarSettings::get() { QuickToolBarSettings settings; @@ -129,12 +119,12 @@ QuickToolBarSettingsPage::QuickToolBarSettingsPage() : setCategoryIcon(QLatin1String(QmlDesigner::Constants::SETTINGS_CATEGORY_QML_ICON)); } -QWidget *QuickToolBarSettingsPage::createPage(QWidget *parent) +QWidget *QuickToolBarSettingsPage::widget() { - m_widget = new QuickToolBarSettingsPageWidget(parent); - m_widget->setSettings(QuickToolBarSettings::get()); - if (m_searchKeywords.isEmpty()) - m_searchKeywords = m_widget->searchKeywords(); + if (!m_widget) { + m_widget = new QuickToolBarSettingsPageWidget; + m_widget->setSettings(QuickToolBarSettings::get()); + } return m_widget; } @@ -145,7 +135,7 @@ void QuickToolBarSettingsPage::apply() m_widget->settings().set(); } -bool QuickToolBarSettingsPage::matches(const QString &s) const +void QuickToolBarSettingsPage::finish() { - return m_searchKeywords.contains(s, Qt::CaseInsensitive); + delete m_widget; } diff --git a/src/plugins/qmljseditor/quicktoolbarsettingspage.h b/src/plugins/qmljseditor/quicktoolbarsettingspage.h index f9964629f4..896cda2e13 100644 --- a/src/plugins/qmljseditor/quicktoolbarsettingspage.h +++ b/src/plugins/qmljseditor/quicktoolbarsettingspage.h @@ -33,6 +33,7 @@ #include "ui_quicktoolbarsettingspage.h" #include <coreplugin/dialogs/ioptionspage.h> +#include <QPointer> #include <QWidget> QT_BEGIN_NAMESPACE @@ -76,7 +77,6 @@ public: QuickToolBarSettings settings() const; void setSettings(const QuickToolBarSettings &); - QString searchKeywords() const; static QuickToolBarSettings get(); private: @@ -91,14 +91,12 @@ class QuickToolBarSettingsPage : public Core::IOptionsPage public: QuickToolBarSettingsPage(); - QWidget *createPage(QWidget *parent); + QWidget *widget(); void apply(); - void finish() { } - bool matches(const QString &) const; + void finish(); private: - QString m_searchKeywords; - QuickToolBarSettingsPageWidget* m_widget; + QPointer<QuickToolBarSettingsPageWidget> m_widget; }; } // namespace Internal diff --git a/src/plugins/qmljstools/qmljscodestylesettingspage.cpp b/src/plugins/qmljstools/qmljscodestylesettingspage.cpp index 82d8664717..a6ff258beb 100644 --- a/src/plugins/qmljstools/qmljscodestylesettingspage.cpp +++ b/src/plugins/qmljstools/qmljscodestylesettingspage.cpp @@ -95,17 +95,6 @@ void QmlJSCodeStylePreferencesWidget::setPreferences(TextEditor::ICodeStylePrefe } -QString QmlJSCodeStylePreferencesWidget::searchKeywords() const -{ - QString rc; - QLatin1Char sep(' '); - QTextStream(&rc) - << sep << m_ui->tabPreferencesWidget->searchKeywords() - ; - rc.remove(QLatin1Char('&')); - return rc; -} - void QmlJSCodeStylePreferencesWidget::decorateEditor(const TextEditor::FontSettings &fontSettings) { const ISnippetProvider *provider = 0; @@ -171,18 +160,19 @@ QmlJSCodeStyleSettingsPage::QmlJSCodeStyleSettingsPage(/*QSharedPointer<CppFileS setCategoryIcon(QLatin1String(QmlDesigner::Constants::SETTINGS_CATEGORY_QML_ICON)); } -QWidget *QmlJSCodeStyleSettingsPage::createPage(QWidget *parent) +QWidget *QmlJSCodeStyleSettingsPage::widget() { - TextEditor::SimpleCodeStylePreferences *originalTabPreferences - = QmlJSToolsSettings::globalCodeStyle(); - m_pageTabPreferences = new TextEditor::SimpleCodeStylePreferences(m_widget); - m_pageTabPreferences->setDelegatingPool(originalTabPreferences->delegatingPool()); - m_pageTabPreferences->setTabSettings(originalTabPreferences->tabSettings()); - m_pageTabPreferences->setCurrentDelegate(originalTabPreferences->currentDelegate()); - m_pageTabPreferences->setId(originalTabPreferences->id()); - m_widget = new CodeStyleEditor(TextEditorSettings::codeStyleFactory(QmlJSTools::Constants::QML_JS_SETTINGS_ID), - m_pageTabPreferences, parent); - + if (!m_widget) { + TextEditor::SimpleCodeStylePreferences *originalTabPreferences + = QmlJSToolsSettings::globalCodeStyle(); + m_pageTabPreferences = new TextEditor::SimpleCodeStylePreferences(m_widget); + m_pageTabPreferences->setDelegatingPool(originalTabPreferences->delegatingPool()); + m_pageTabPreferences->setTabSettings(originalTabPreferences->tabSettings()); + m_pageTabPreferences->setCurrentDelegate(originalTabPreferences->currentDelegate()); + m_pageTabPreferences->setId(originalTabPreferences->id()); + m_widget = new CodeStyleEditor(TextEditorSettings::codeStyleFactory(QmlJSTools::Constants::QML_JS_SETTINGS_ID), + m_pageTabPreferences); + } return m_widget; } @@ -203,9 +193,9 @@ void QmlJSCodeStyleSettingsPage::apply() } } -bool QmlJSCodeStyleSettingsPage::matches(const QString &s) const +void QmlJSCodeStyleSettingsPage::finish() { - return m_searchKeywords.contains(s, Qt::CaseInsensitive); + delete m_widget; } } // namespace Internal diff --git a/src/plugins/qmljstools/qmljscodestylesettingspage.h b/src/plugins/qmljstools/qmljscodestylesettingspage.h index ddb05033d1..cf6782f50b 100644 --- a/src/plugins/qmljstools/qmljscodestylesettingspage.h +++ b/src/plugins/qmljstools/qmljscodestylesettingspage.h @@ -61,7 +61,6 @@ public: ~QmlJSCodeStylePreferencesWidget(); void setPreferences(TextEditor::ICodeStylePreferences *preferences); - QString searchKeywords() const; private slots: void decorateEditor(const TextEditor::FontSettings &fontSettings); @@ -82,13 +81,11 @@ class QmlJSCodeStyleSettingsPage : public Core::IOptionsPage public: explicit QmlJSCodeStyleSettingsPage(QWidget *parent = 0); - QWidget *createPage(QWidget *parent); + QWidget *widget(); void apply(); - void finish() { } - bool matches(const QString &) const; + void finish(); private: - QString m_searchKeywords; TextEditor::ICodeStylePreferences *m_pageTabPreferences; QPointer<TextEditor::CodeStyleEditor> m_widget; }; diff --git a/src/plugins/qmljstools/qmljsmodelmanager.cpp b/src/plugins/qmljstools/qmljsmodelmanager.cpp index 6c4a3a34a3..4342d9b126 100644 --- a/src/plugins/qmljstools/qmljsmodelmanager.cpp +++ b/src/plugins/qmljstools/qmljsmodelmanager.cpp @@ -329,7 +329,7 @@ ModelManagerInterface::WorkingCopy ModelManager::workingCopy() const if (TextEditor::BaseTextDocument *textDocument = qobject_cast<TextEditor::BaseTextDocument *>(document)) { // TODO the language should be a property on the document, not the editor if (documentModel->editorsForDocument(document).first()->context().contains(ProjectExplorer::Constants::LANG_QMLJS)) - workingCopy.insert(key, textDocument->contents(), textDocument->document()->revision()); + workingCopy.insert(key, textDocument->plainText(), textDocument->document()->revision()); } } diff --git a/src/plugins/qmlprofiler/abstracttimelinemodel.cpp b/src/plugins/qmlprofiler/abstracttimelinemodel.cpp index 99d555efef..a4823b3582 100644 --- a/src/plugins/qmlprofiler/abstracttimelinemodel.cpp +++ b/src/plugins/qmlprofiler/abstracttimelinemodel.cpp @@ -41,7 +41,6 @@ void AbstractTimelineModel::setModelManager(QmlProfilerModelManager *modelManage { m_modelManager = modelManager; connect(modelManager->simpleModel(),SIGNAL(changed()),this,SLOT(dataChanged())); - connect(modelManager,SIGNAL(stateChanged()),this,SLOT(dataChanged())); m_modelId = modelManager->registerModelProxy(); } @@ -79,5 +78,24 @@ int AbstractTimelineModel::getBindingLoopDest(int index) const return -1; } +void AbstractTimelineModel::dataChanged() +{ + switch (m_modelManager->state()) { + case QmlProfilerDataState::Done: + loadData(); + break; + case QmlProfilerDataState::ClearingData: + clear(); + break; + default: + break; + } + + emit stateChanged(); + emit dataAvailable(); + emit emptyChanged(); + emit expandedChanged(); +} + } diff --git a/src/plugins/qmlprofiler/abstracttimelinemodel.h b/src/plugins/qmlprofiler/abstracttimelinemodel.h index a139669b12..a5bff2eefe 100644 --- a/src/plugins/qmlprofiler/abstracttimelinemodel.h +++ b/src/plugins/qmlprofiler/abstracttimelinemodel.h @@ -79,6 +79,10 @@ public: virtual int getEventType(int index) const = 0; virtual int getEventCategory(int index) const = 0; virtual int getEventRow(int index) const = 0; + + virtual void loadData() = 0; + virtual void clear() = 0; + Q_INVOKABLE virtual qint64 getDuration(int index) const = 0; Q_INVOKABLE virtual qint64 getStartTime(int index) const = 0; Q_INVOKABLE virtual qint64 getEndTime(int index) const = 0; @@ -106,6 +110,9 @@ signals: protected: QmlProfilerModelManager *m_modelManager; int m_modelId; + +protected slots: + void dataChanged(); }; } diff --git a/src/plugins/qmlprofiler/canvas/canvas.pri b/src/plugins/qmlprofiler/canvas/canvas.pri deleted file mode 100644 index 2960e94527..0000000000 --- a/src/plugins/qmlprofiler/canvas/canvas.pri +++ /dev/null @@ -1,9 +0,0 @@ -HEADERS += $$PWD/qdeclarativecontext2d_p.h \ - $$PWD/qdeclarativecanvas_p.h \ - $$PWD/qmlprofilercanvas.h \ - $$PWD/qdeclarativecanvastimer_p.h - -SOURCES += $$PWD/qdeclarativecontext2d.cpp \ - $$PWD/qdeclarativecanvas.cpp \ - $$PWD/qmlprofilercanvas.cpp \ - $$PWD/qdeclarativecanvastimer.cpp diff --git a/src/plugins/qmlprofiler/canvas/qdeclarativecanvas.cpp b/src/plugins/qmlprofiler/canvas/qdeclarativecanvas.cpp deleted file mode 100644 index bb68bdeb4f..0000000000 --- a/src/plugins/qmlprofiler/canvas/qdeclarativecanvas.cpp +++ /dev/null @@ -1,242 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). -** 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 "qdeclarativecanvas_p.h" -#include "qdeclarativecanvastimer_p.h" -#include "qdeclarativecontext2d_p.h" - -#include <qpainter.h> - -QT_BEGIN_NAMESPACE - -Canvas::Canvas(QQuickPaintedItem *parent) - : QQuickPaintedItem(parent), - m_context(new Context2D(this)), - m_canvasWidth(0), - m_canvasHeight(0), - m_fillMode(Canvas::Stretch), - m_color(Qt::white) -{ -} - - -void Canvas::componentComplete() -{ - if (m_canvasWidth == 0 && m_canvasHeight == 0) - m_context->setSize(width(), height()); - else - m_context->setSize(m_canvasWidth, m_canvasHeight); - - connect(m_context, SIGNAL(changed()), this, SLOT(requestPaint())); - emit init(); - QQuickItem::componentComplete(); -} - -void Canvas::paint(QPainter *painter) -{ - m_context->setInPaint(true); - emit paint(); - - bool oldAA = painter->testRenderHint(QPainter::Antialiasing); - bool oldSmooth = painter->testRenderHint(QPainter::SmoothPixmapTransform); - if (smooth()) - painter->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform, smooth()); - - if (m_context->pixmap().isNull()) { - painter->fillRect(0, 0, width(), height(), m_color); - } else if (width() != m_context->pixmap().width() || height() != m_context->pixmap().height()) { - if (m_fillMode>= Tile) { - if (m_fillMode== Tile) { - painter->drawTiledPixmap(QRectF(0,0,width(),height()), m_context->pixmap()); - } else { - qreal widthScale = width() / qreal(m_context->pixmap().width()); - qreal heightScale = height() / qreal(m_context->pixmap().height()); - - QTransform scale; - if (m_fillMode== TileVertically) { - scale.scale(widthScale, 1.0); - QTransform old = painter->transform(); - painter->setWorldTransform(scale * old); - painter->drawTiledPixmap(QRectF(0,0,m_context->pixmap().width(),height()), m_context->pixmap()); - painter->setWorldTransform(old); - } else { - scale.scale(1.0, heightScale); - QTransform old = painter->transform(); - painter->setWorldTransform(scale * old); - painter->drawTiledPixmap(QRectF(0,0,width(),m_context->pixmap().height()), m_context->pixmap()); - painter->setWorldTransform(old); - } - } - } else { - qreal widthScale = width() / qreal(m_context->pixmap().width()); - qreal heightScale = height() / qreal(m_context->pixmap().height()); - - QTransform scale; - - if (m_fillMode== PreserveAspectFit) { - if (widthScale <= heightScale) { - heightScale = widthScale; - scale.translate(0, (height() - heightScale * m_context->pixmap().height()) / 2); - } else if (heightScale < widthScale) { - widthScale = heightScale; - scale.translate((width() - widthScale * m_context->pixmap().width()) / 2, 0); - } - } else if (m_fillMode== PreserveAspectCrop) { - if (widthScale < heightScale) { - widthScale = heightScale; - scale.translate((width() - widthScale * m_context->pixmap().width()) / 2, 0); - } else if (heightScale < widthScale) { - heightScale = widthScale; - scale.translate(0, (height() - heightScale * m_context->pixmap().height()) / 2); - } - } - if (clip()) { - painter->save(); - painter->setClipRect(boundingRect(), Qt::IntersectClip); - } - scale.scale(widthScale, heightScale); - QTransform old = painter->transform(); - painter->setWorldTransform(scale * old); - painter->drawPixmap(0, 0, m_context->pixmap()); - painter->setWorldTransform(old); - if (clip()) - painter->restore(); - } - } else { - painter->drawPixmap(0, 0, m_context->pixmap()); - } - - if (smooth()) { - painter->setRenderHint(QPainter::Antialiasing, oldAA); - painter->setRenderHint(QPainter::SmoothPixmapTransform, oldSmooth); - } - m_context->setInPaint(false); -} - -Context2D *Canvas::getContext(const QString &contextId) -{ - if (contextId == QLatin1String("2d")) - return m_context; - qDebug("Canvas:requesting unsupported context"); - return 0; -} - -void Canvas::requestPaint() -{ - update(); -} - -void Canvas::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) -{ - if (m_canvasWidth == 0 && m_canvasHeight == 0 - && newGeometry.width() > 0 && newGeometry.height() > 0) { - m_context->setSize(width(), height()); - } - QQuickItem::geometryChanged(newGeometry, oldGeometry); -} - -void Canvas::setCanvasWidth(int newWidth) -{ - if (m_canvasWidth != newWidth) { - m_canvasWidth = newWidth; - m_context->setSize(m_canvasWidth, m_canvasHeight); - emit canvasWidthChanged(); - } -} - -void Canvas::setCanvasHeight(int newHeight) -{ - if (m_canvasHeight != newHeight) { - m_canvasHeight = newHeight; - m_context->setSize(m_canvasWidth, m_canvasHeight); - emit canvasHeightChanged(); - } -} - -void Canvas::setFillMode(FillMode mode) -{ - if (m_fillMode == mode) - return; - - m_fillMode = mode; - update(); - emit fillModeChanged(); -} - -QColor Canvas::color() -{ - return m_color; -} - -void Canvas::setColor(const QColor &color) -{ - if (m_color !=color) { - m_color = color; - colorChanged(); - } -} - -Canvas::FillMode Canvas::fillMode() const -{ - return m_fillMode; -} - -bool Canvas::save(const QString &filename) const -{ - return m_context->pixmap().save(filename); -} - -CanvasImage *Canvas::toImage() const -{ - return new CanvasImage(m_context->pixmap()); -} - -void Canvas::setTimeout(const QJSValue &handler, long timeout) -{ - if (handler.isCallable()) - CanvasTimer::createTimer(this, handler, timeout, true); -} - -void Canvas::setInterval(const QJSValue &handler, long interval) -{ - if (handler.isCallable()) - CanvasTimer::createTimer(this, handler, interval, false); -} - -void Canvas::clearTimeout(const QJSValue &handler) -{ - CanvasTimer::removeTimer(handler); -} - -void Canvas::clearInterval(const QJSValue &handler) -{ - CanvasTimer::removeTimer(handler); -} - -QT_END_NAMESPACE diff --git a/src/plugins/qmlprofiler/canvas/qdeclarativecanvas_p.h b/src/plugins/qmlprofiler/canvas/qdeclarativecanvas_p.h deleted file mode 100644 index bf1af2431d..0000000000 --- a/src/plugins/qmlprofiler/canvas/qdeclarativecanvas_p.h +++ /dev/null @@ -1,107 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). -** 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. -** -****************************************************************************/ - -#ifndef QDECLARATIVECANVAS_P_H -#define QDECLARATIVECANVAS_P_H - -#include <QQuickPaintedItem> - -#include "qdeclarativecontext2d_p.h" -#include "qdeclarativecanvastimer_p.h" - -QT_BEGIN_NAMESPACE - -class Canvas : public QQuickPaintedItem -{ - Q_OBJECT - - Q_ENUMS(FillMode) - Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged); - Q_PROPERTY(int canvasWidth READ canvasWidth WRITE setCanvasWidth NOTIFY canvasWidthChanged); - Q_PROPERTY(int canvasHeight READ canvasHeight WRITE setCanvasHeight NOTIFY canvasHeightChanged); - Q_PROPERTY(FillMode fillMode READ fillMode WRITE setFillMode NOTIFY fillModeChanged) - -public: - Canvas(QQuickPaintedItem *parent = 0); - enum FillMode { Stretch, PreserveAspectFit, PreserveAspectCrop, Tile, TileVertically, TileHorizontally }; - - - void paint(QPainter *); - void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry); - void setCanvasWidth(int newWidth); - int canvasWidth() {return m_canvasWidth;} - - void setCanvasHeight(int canvasHeight); - int canvasHeight() {return m_canvasHeight;} - - void componentComplete(); - - -public Q_SLOTS: - Context2D *getContext(const QString & = QLatin1String("2d")); - void requestPaint(); - - FillMode fillMode() const; - void setFillMode(FillMode); - - QColor color(); - void setColor(const QColor &); - - // Save current canvas to disk - bool save(const QString& filename) const; - - // Timers - void setInterval(const QJSValue &handler, long timeout); - void setTimeout(const QJSValue &handler, long timeout); - void clearInterval(const QJSValue &handler); - void clearTimeout(const QJSValue &handler); - -Q_SIGNALS: - void fillModeChanged(); - void canvasWidthChanged(); - void canvasHeightChanged(); - void colorChanged(); - void init(); - void paint(); - -private: - // Return canvas contents as a drawable image - CanvasImage *toImage() const; - Context2D *m_context; - int m_canvasWidth; - int m_canvasHeight; - FillMode m_fillMode; - QColor m_color; - - friend class Context2D; -}; - -QT_END_NAMESPACE - -#endif //QDECLARATIVECANVAS_P_H diff --git a/src/plugins/qmlprofiler/canvas/qdeclarativecontext2d.cpp b/src/plugins/qmlprofiler/canvas/qdeclarativecontext2d.cpp deleted file mode 100644 index b56037f7ac..0000000000 --- a/src/plugins/qmlprofiler/canvas/qdeclarativecontext2d.cpp +++ /dev/null @@ -1,1134 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). -** 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 "qdeclarativecontext2d_p.h" - -#include "qdeclarativecanvas_p.h" - -#include <qdebug.h> -#include <math.h> - -#include <qgraphicsitem.h> -#include <qapplication.h> -#include <qgraphicseffect.h> - -#include <QImage> -#include <QWidget> - -QT_BEGIN_NAMESPACE - -static const double Q_PI = 3.14159265358979323846; // pi - -class CustomDropShadowEffect : public QGraphicsDropShadowEffect -{ -public: - void draw(QPainter *painter) { QGraphicsDropShadowEffect::draw(painter);} - void drawSource(QPainter *painter) { QGraphicsDropShadowEffect::drawSource(painter);} -}; - -// Note, this is exported but in a private header as qtopengl depends on it. -// But it really should be considered private API -void qt_blurImage(QPainter *p, QImage &blurImage, qreal radius, bool quality, bool alphaOnly, int transposed = 0); -void qt_blurImage(QImage &blurImage, qreal radius, bool quality, int transposed = 0); - -#define DEGREES(t) ((t) * 180.0 / Q_PI) - -#define qClamp(val, min, max) qMin(qMax(val, min), max) -static QList<qreal> parseNumbersList(QString::const_iterator &itr) -{ - QList<qreal> points; - QString temp; - while ((*itr).isSpace()) - ++itr; - while ((*itr).isNumber() || - (*itr) == QLatin1Char('-') || (*itr) == QLatin1Char('+') || (*itr) == QLatin1Char('.')) { - temp.clear(); - - if ((*itr) == QLatin1Char('-')) - temp += *itr++; - else if ((*itr) == QLatin1Char('+')) - temp += *itr++; - while ((*itr).isDigit()) - temp += *itr++; - if ((*itr) == QLatin1Char('.')) - temp += *itr++; - while ((*itr).isDigit()) - temp += *itr++; - while ((*itr).isSpace()) - ++itr; - if ((*itr) == QLatin1Char(',')) - ++itr; - points.append(temp.toDouble()); - //eat spaces - while ((*itr).isSpace()) - ++itr; - } - - return points; -} - -QColor colorFromString(const QString &name) -{ - QString::const_iterator itr = name.constBegin(); - QList<qreal> compo; - if (name.startsWith(QLatin1String("rgba("))) { - ++itr; ++itr; ++itr; ++itr; ++itr; - compo = parseNumbersList(itr); - if (compo.size() != 4) - return QColor(); - //alpha seems to be always between 0-1 - compo[3] *= 255; - return QColor((int)compo[0], (int)compo[1], - (int)compo[2], (int)compo[3]); - } else if (name.startsWith(QLatin1String("rgb("))) { - ++itr; ++itr; ++itr; ++itr; - compo = parseNumbersList(itr); - if (compo.size() != 3) - return QColor(); - return QColor((int)qClamp(compo[0], qreal(0), qreal(255)), - (int)qClamp(compo[1], qreal(0), qreal(255)), - (int)qClamp(compo[2], qreal(0), qreal(255))); - } else if (name.startsWith(QLatin1String("hsla("))) { - ++itr; ++itr; ++itr; ++itr; ++itr; - compo = parseNumbersList(itr); - if (compo.size() != 4) - return QColor(); - return QColor::fromHslF(compo[0], compo[1], - compo[2], compo[3]); - } else if (name.startsWith(QLatin1String("hsl("))) { - ++itr; ++itr; ++itr; ++itr; ++itr; - compo = parseNumbersList(itr); - if (compo.size() != 3) - return QColor(); - return QColor::fromHslF(compo[0], compo[1], - compo[2]); - } else { - //QRgb color; - //CSSParser::parseColor(name, color); - return QColor(name); - } -} - - -static QPainter::CompositionMode compositeOperatorFromString(const QString &compositeOperator) -{ - if (compositeOperator == QLatin1String("source-over")) - return QPainter::CompositionMode_SourceOver; - else if (compositeOperator == QLatin1String("source-out")) - return QPainter::CompositionMode_SourceOut; - else if (compositeOperator == QLatin1String("source-in")) - return QPainter::CompositionMode_SourceIn; - else if (compositeOperator == QLatin1String("source-atop")) - return QPainter::CompositionMode_SourceAtop; - else if (compositeOperator == QLatin1String("destination-atop")) - return QPainter::CompositionMode_DestinationAtop; - else if (compositeOperator == QLatin1String("destination-in")) - return QPainter::CompositionMode_DestinationIn; - else if (compositeOperator == QLatin1String("destination-out")) - return QPainter::CompositionMode_DestinationOut; - else if (compositeOperator == QLatin1String("destination-over")) - return QPainter::CompositionMode_DestinationOver; - else if (compositeOperator == QLatin1String("darker")) - return QPainter::CompositionMode_SourceOver; - else if (compositeOperator == QLatin1String("lighter")) - return QPainter::CompositionMode_SourceOver; - else if (compositeOperator == QLatin1String("copy")) - return QPainter::CompositionMode_Source; - else if (compositeOperator == QLatin1String("xor")) - return QPainter::CompositionMode_Xor; - - return QPainter::CompositionMode_SourceOver; -} - -static QString compositeOperatorToString(QPainter::CompositionMode op) -{ - switch (op) { - case QPainter::CompositionMode_SourceOver: - return QLatin1String("source-over"); - case QPainter::CompositionMode_DestinationOver: - return QLatin1String("destination-over"); - case QPainter::CompositionMode_Clear: - return QLatin1String("clear"); - case QPainter::CompositionMode_Source: - return QLatin1String("source"); - case QPainter::CompositionMode_Destination: - return QLatin1String("destination"); - case QPainter::CompositionMode_SourceIn: - return QLatin1String("source-in"); - case QPainter::CompositionMode_DestinationIn: - return QLatin1String("destination-in"); - case QPainter::CompositionMode_SourceOut: - return QLatin1String("source-out"); - case QPainter::CompositionMode_DestinationOut: - return QLatin1String("destination-out"); - case QPainter::CompositionMode_SourceAtop: - return QLatin1String("source-atop"); - case QPainter::CompositionMode_DestinationAtop: - return QLatin1String("destination-atop"); - case QPainter::CompositionMode_Xor: - return QLatin1String("xor"); - case QPainter::CompositionMode_Plus: - return QLatin1String("plus"); - case QPainter::CompositionMode_Multiply: - return QLatin1String("multiply"); - case QPainter::CompositionMode_Screen: - return QLatin1String("screen"); - case QPainter::CompositionMode_Overlay: - return QLatin1String("overlay"); - case QPainter::CompositionMode_Darken: - return QLatin1String("darken"); - case QPainter::CompositionMode_Lighten: - return QLatin1String("lighten"); - case QPainter::CompositionMode_ColorDodge: - return QLatin1String("color-dodge"); - case QPainter::CompositionMode_ColorBurn: - return QLatin1String("color-burn"); - case QPainter::CompositionMode_HardLight: - return QLatin1String("hard-light"); - case QPainter::CompositionMode_SoftLight: - return QLatin1String("soft-light"); - case QPainter::CompositionMode_Difference: - return QLatin1String("difference"); - case QPainter::CompositionMode_Exclusion: - return QLatin1String("exclusion"); - default: - break; - } - return QString(); -} - -void Context2D::save() -{ - m_stateStack.push(m_state); -} - - -void Context2D::restore() -{ - if (!m_stateStack.isEmpty()) { - m_state = m_stateStack.pop(); - m_state.flags = AllIsFullOfDirt; - } -} - - -void Context2D::scale(qreal x, qreal y) -{ - m_state.matrix.scale(x, y); - m_state.flags |= DirtyTransformationMatrix; -} - - -void Context2D::rotate(qreal angle) -{ - m_state.matrix.rotate(DEGREES(angle)); - m_state.flags |= DirtyTransformationMatrix; -} - - -void Context2D::translate(qreal x, qreal y) -{ - m_state.matrix.translate(x, y); - m_state.flags |= DirtyTransformationMatrix; -} - - -void Context2D::transform(qreal m11, qreal m12, qreal m21, qreal m22, - qreal dx, qreal dy) -{ - QMatrix mat(m11, m12, - m21, m22, - dx, dy); - m_state.matrix *= mat; - m_state.flags |= DirtyTransformationMatrix; -} - - -void Context2D::setTransform(qreal m11, qreal m12, qreal m21, qreal m22, - qreal dx, qreal dy) -{ - QMatrix mat(m11, m12, - m21, m22, - dx, dy); - m_state.matrix = mat; - m_state.flags |= DirtyTransformationMatrix; -} - - -QString Context2D::globalCompositeOperation() const -{ - return compositeOperatorToString(m_state.globalCompositeOperation); -} - -void Context2D::setGlobalCompositeOperation(const QString &op) -{ - QPainter::CompositionMode mode = - compositeOperatorFromString(op); - m_state.globalCompositeOperation = mode; - m_state.flags |= DirtyGlobalCompositeOperation; -} - -QVariant Context2D::strokeStyle() const -{ - return m_state.strokeStyle; -} - -void Context2D::setStrokeStyle(const QVariant &style) -{ - CanvasGradient * gradient= qobject_cast<CanvasGradient*>(style.value<QObject*>()); - if (gradient) { - m_state.strokeStyle = gradient->value(); - } else { - QColor color = colorFromString(style.toString()); - m_state.strokeStyle = color; - } - m_state.flags |= DirtyStrokeStyle; -} - -QVariant Context2D::fillStyle() const -{ - return m_state.fillStyle; -} - -void Context2D::setFillStyle(const QVariant &style) -{ - CanvasGradient * gradient= qobject_cast<CanvasGradient*>(style.value<QObject*>()); - if (gradient) { - m_state.fillStyle = gradient->value(); - } else { - QColor color = colorFromString(style.toString()); - m_state.fillStyle = color; - } - m_state.flags |= DirtyFillStyle; -} - -qreal Context2D::globalAlpha() const -{ - return m_state.globalAlpha; -} - -void Context2D::setGlobalAlpha(qreal alpha) -{ - m_state.globalAlpha = alpha; - m_state.flags |= DirtyGlobalAlpha; -} - -CanvasImage *Context2D::createImage(const QString &url) -{ - return new CanvasImage(url); -} - -CanvasGradient *Context2D::createLinearGradient(qreal x0, qreal y0, - qreal x1, qreal y1) -{ - QLinearGradient g(x0, y0, x1, y1); - return new CanvasGradient(g); -} - - -CanvasGradient *Context2D::createRadialGradient(qreal x0, qreal y0, - qreal r0, qreal x1, - qreal y1, qreal r1) -{ - QRadialGradient g(QPointF(x1, y1), r0+r1, QPointF(x0, y0)); - return new CanvasGradient(g); -} - -qreal Context2D::lineWidth() const -{ - return m_state.lineWidth; -} - -void Context2D::setLineWidth(qreal w) -{ - m_state.lineWidth = w; - m_state.flags |= DirtyLineWidth; -} - -QString Context2D::lineCap() const -{ - switch (m_state.lineCap) { - case Qt::FlatCap: - return QLatin1String("butt"); - case Qt::SquareCap: - return QLatin1String("square"); - case Qt::RoundCap: - return QLatin1String("round"); - default: ; - } - return QString(); -} - -void Context2D::setLineCap(const QString &capString) -{ - Qt::PenCapStyle style; - if (capString == QLatin1String("round")) - style = Qt::RoundCap; - else if (capString == QLatin1String("square")) - style = Qt::SquareCap; - else //if (capString == QLatin1String("butt")) - style = Qt::FlatCap; - m_state.lineCap = style; - m_state.flags |= DirtyLineCap; -} - -QString Context2D::lineJoin() const -{ - switch (m_state.lineJoin) { - case Qt::RoundJoin: - return QLatin1String("round"); - case Qt::BevelJoin: - return QLatin1String("bevel"); - case Qt::MiterJoin: - return QLatin1String("miter"); - default: ; - } - return QString(); -} - -void Context2D::setLineJoin(const QString &joinString) -{ - Qt::PenJoinStyle style; - if (joinString == QLatin1String("round")) - style = Qt::RoundJoin; - else if (joinString == QLatin1String("bevel")) - style = Qt::BevelJoin; - else //if (joinString == "miter") - style = Qt::MiterJoin; - m_state.lineJoin = style; - m_state.flags |= DirtyLineJoin; -} - -qreal Context2D::miterLimit() const -{ - return m_state.miterLimit; -} - -void Context2D::setMiterLimit(qreal m) -{ - m_state.miterLimit = m; - m_state.flags |= DirtyMiterLimit; -} - -void Context2D::setShadowOffsetX(qreal x) -{ - if (m_state.shadowOffsetX == x) - return; - m_state.shadowOffsetX = x; - updateShadowBuffer(); - if (m_painter.device() == &m_shadowbuffer && m_state.shadowBlur>0) - endPainting(); - m_state.flags |= DirtyShadowOffsetX; -} - -const QList<Context2D::MouseArea> &Context2D::mouseAreas() const -{ - return m_mouseAreas; -} - -void Context2D::updateShadowBuffer() { - if (m_shadowbuffer.isNull() || m_shadowbuffer.width() != m_width+m_state.shadowOffsetX || - m_shadowbuffer.height() != m_height+m_state.shadowOffsetY) { - m_shadowbuffer = QImage(m_width+m_state.shadowOffsetX, m_height+m_state.shadowOffsetY, QImage::Format_ARGB32); - m_shadowbuffer.fill(Qt::transparent); - } -} - -void Context2D::setShadowOffsetY(qreal y) -{ - if (m_state.shadowOffsetY == y) - return; - m_state.shadowOffsetY = y; - updateShadowBuffer(); - if (m_painter.device() == &m_shadowbuffer && m_state.shadowBlur>0) - endPainting(); - - m_state.flags |= DirtyShadowOffsetY; -} - -void Context2D::setShadowBlur(qreal b) -{ - if (m_state.shadowBlur == b) - return; - m_state.shadowBlur = b; - updateShadowBuffer(); - if (m_painter.device() == &m_shadowbuffer && m_state.shadowBlur>0) - endPainting(); - m_state.flags |= DirtyShadowBlur; -} - -void Context2D::setShadowColor(const QString &str) -{ - m_state.shadowColor = colorFromString(str); - if (m_painter.device() == &m_shadowbuffer && m_state.shadowBlur>0) - endPainting(); - m_state.flags |= DirtyShadowColor; -} - -QString Context2D::textBaseline() -{ - switch (m_state.textBaseline) { - case Context2D::Alphabetic: - return QLatin1String("alphabetic"); - case Context2D::Hanging: - return QLatin1String("hanging"); - case Context2D::Bottom: - return QLatin1String("bottom"); - case Context2D::Top: - return QLatin1String("top"); - case Context2D::Middle: - return QLatin1String("middle"); - default: - Q_ASSERT("invalid value"); - return QLatin1String("start"); - } -} - -void Context2D::setTextBaseline(const QString &baseline) -{ - if (baseline==QLatin1String("alphabetic")) { - m_state.textBaseline = Context2D::Alphabetic; - } else if (baseline == QLatin1String("hanging")) { - m_state.textBaseline = Context2D::Hanging; - } else if (baseline == QLatin1String("top")) { - m_state.textBaseline = Context2D::Top; - } else if (baseline == QLatin1String("bottom")) { - m_state.textBaseline = Context2D::Bottom; - } else if (baseline == QLatin1String("middle")) { - m_state.textBaseline = Context2D::Middle; - } else { - m_state.textBaseline = Context2D::Alphabetic; - qWarning() << (QLatin1String("Context2D: invalid baseline:") + baseline); - } - m_state.flags |= DirtyTextBaseline; -} - -QString Context2D::textAlign() -{ - switch (m_state.textAlign) { - case Context2D::Left: - return QLatin1String("left"); - case Context2D::Right: - return QLatin1String("right"); - case Context2D::Center: - return QLatin1String("center"); - case Context2D::Start: - return QLatin1String("start"); - case Context2D::End: - return QLatin1String("end"); - default: - Q_ASSERT("invalid value"); - qWarning() << ("Context2D::invalid textAlign"); - return QLatin1String("start"); - } -} - -void Context2D::setTextAlign(const QString &baseline) -{ - if (baseline==QLatin1String("start")) { - m_state.textAlign = Context2D::Start; - } else if (baseline == QLatin1String("end")) { - m_state.textAlign = Context2D::End; - } else if (baseline == QLatin1String("left")) { - m_state.textAlign = Context2D::Left; - } else if (baseline == QLatin1String("right")) { - m_state.textAlign = Context2D::Right; - } else if (baseline == QLatin1String("center")) { - m_state.textAlign = Context2D::Center; - } else { - m_state.textAlign= Context2D::Start; - qWarning("Context2D: invalid text align"); - } - // ### alphabetic, ideographic, hanging - m_state.flags |= DirtyTextBaseline; -} - -void Context2D::setFont(const QString &fontString) -{ - QFont font; - // ### this is simplified and incomplete - QStringList tokens = fontString.split(QLatin1Char(QLatin1Char(' '))); - foreach (const QString &token, tokens) { - if (token == QLatin1String("italic")) { - font.setItalic(true); - } else if (token == QLatin1String("bold")) { - font.setBold(true); - } else if (token.endsWith(QLatin1String("px"))) { - QString number = token; - number.remove(QLatin1String("px")); -#ifdef Q_OS_MACX - // compensating the extra antialias space with bigger fonts - // this class is only used by the QML Profiler - // not much harm can be inflicted by this dirty hack - font.setPointSizeF(number.trimmed().toFloat()*4.0f/3.0f); -#else - font.setPointSizeF(number.trimmed().toFloat()); -#endif - } else { - font.setFamily(token); - } - } - m_state.font = font; - m_state.flags |= DirtyFont; -} - -QString Context2D::font() -{ - return m_state.font.toString(); -} - -qreal Context2D::shadowOffsetX() const -{ - return m_state.shadowOffsetX; -} - -qreal Context2D::shadowOffsetY() const -{ - return m_state.shadowOffsetY; -} - - -qreal Context2D::shadowBlur() const -{ - return m_state.shadowBlur; -} - - -QString Context2D::shadowColor() const -{ - return m_state.shadowColor.name(); -} - - -void Context2D::clearRect(qreal x, qreal y, qreal w, qreal h) -{ - beginPainting(); - m_painter.save(); - m_painter.setMatrix(worldMatrix(), false); - m_painter.setCompositionMode(QPainter::CompositionMode_Source); - QColor fillColor = parent()->property("color").value<QColor>(); - - m_painter.fillRect(QRectF(x, y, w, h), fillColor); - m_painter.restore(); - scheduleChange(); -} - -void Context2D::fillRect(qreal x, qreal y, qreal w, qreal h) -{ - beginPainting(); - m_painter.save(); - m_painter.setMatrix(worldMatrix(), false); - m_painter.fillRect(QRectF(x, y, w, h), m_painter.brush()); - m_painter.restore(); - scheduleChange(); -} - -int Context2D::baseLineOffset(Context2D::TextBaseLine value, const QFontMetrics &metrics) -{ - int offset = 0; - switch (value) { - case Context2D::Top: - break; - case Context2D::Alphabetic: - case Context2D::Middle: - case Context2D::Hanging: - offset = metrics.ascent(); - break; - case Context2D::Bottom: - offset = metrics.height(); - break; - } - return offset; -} - -int Context2D::textAlignOffset(Context2D::TextAlign value, const QFontMetrics &metrics, const QString &text) -{ - int offset = 0; - if (value == Context2D::Start) - value = qApp->layoutDirection() == Qt::LeftToRight ? Context2D::Left : Context2D::Right; - else if (value == Context2D::End) - value = qApp->layoutDirection() == Qt::LeftToRight ? Context2D::Right: Context2D::Left; - switch (value) { - case Context2D::Center: - offset = metrics.width(text)/2; - break; - case Context2D::Right: - offset = metrics.width(text); - case Context2D::Left: - default: - break; - } - return offset; -} - -void Context2D::fillText(const QString &text, qreal x, qreal y) -{ - beginPainting(); - m_painter.save(); - m_painter.setPen(QPen(m_state.fillStyle, m_state.lineWidth)); - m_painter.setMatrix(worldMatrix(), false); - QFont font; - font.setBold(true); - m_painter.setFont(m_state.font); - int yoffset = baseLineOffset(m_state.textBaseline, m_painter.fontMetrics()); - int xoffset = textAlignOffset(m_state.textAlign, m_painter.fontMetrics(), text); - QTextOption opt; // Adjust baseLine etc - m_painter.drawText(QRectF(x-xoffset, y-yoffset, QWIDGETSIZE_MAX, m_painter.fontMetrics().height()), text, opt); - m_painter.restore(); - endPainting(); - scheduleChange(); -} - -void Context2D::strokeText(const QString &text, qreal x, qreal y) -{ - beginPainting(); - m_painter.save(); - m_painter.setPen(QPen(m_state.fillStyle,0)); - m_painter.setMatrix(worldMatrix(), false); - - QPainterPath textPath; - QFont font = m_state.font; - font.setStyleStrategy(QFont::ForceOutline); - m_painter.setFont(font); - const QFontMetrics &metrics = m_painter.fontMetrics(); - int yoffset = baseLineOffset(m_state.textBaseline, metrics); - int xoffset = textAlignOffset(m_state.textAlign, metrics, text); - textPath.addText(x-xoffset, y-yoffset+metrics.ascent(), font, text); - m_painter.strokePath(textPath, QPen(m_state.fillStyle, m_state.lineWidth)); - m_painter.restore(); - endPainting(); - scheduleChange(); -} - -void Context2D::strokeRect(qreal x, qreal y, qreal w, qreal h) -{ - QPainterPath path; - path.addRect(x, y, w, h); - beginPainting(); - m_painter.save(); - m_painter.setMatrix(worldMatrix(), false); - m_painter.strokePath(path, m_painter.pen()); - m_painter.restore(); - scheduleChange(); -} - -void Context2D::mouseArea(qreal x, qreal y, qreal w, qreal h, const QJSValue &callback, - const QJSValue &data) -{ - MouseArea a = { callback, data, QRectF(x, y, w, h), m_state.matrix }; - m_mouseAreas << a; -} - -void Context2D::beginPath() -{ - m_path = QPainterPath(); -} - - -void Context2D::closePath() -{ - m_path.closeSubpath(); -} - - -void Context2D::moveTo(qreal x, qreal y) -{ - QPointF pt = worldMatrix().map(QPointF(x, y)); - m_path.moveTo(pt); -} - - -void Context2D::lineTo(qreal x, qreal y) -{ - QPointF pt = worldMatrix().map(QPointF(x, y)); - m_path.lineTo(pt); -} - - -void Context2D::quadraticCurveTo(qreal cpx, qreal cpy, qreal x, qreal y) -{ - QPointF cp = worldMatrix().map(QPointF(cpx, cpy)); - QPointF xy = worldMatrix().map(QPointF(x, y)); - m_path.quadTo(cp, xy); -} - - -void Context2D::bezierCurveTo(qreal cp1x, qreal cp1y, - qreal cp2x, qreal cp2y, qreal x, qreal y) -{ - QPointF cp1 = worldMatrix().map(QPointF(cp1x, cp1y)); - QPointF cp2 = worldMatrix().map(QPointF(cp2x, cp2y)); - QPointF end = worldMatrix().map(QPointF(x, y)); - m_path.cubicTo(cp1, cp2, end); -} - - -void Context2D::arcTo(qreal x1, qreal y1, qreal x2, qreal y2, qreal radius) -{ - //FIXME: this is surely busted - QPointF st = worldMatrix().map(QPointF(x1, y1)); - QPointF end = worldMatrix().map(QPointF(x2, y2)); - m_path.arcTo(st.x(), st.y(), - end.x()-st.x(), end.y()-st.y(), - radius, 90); -} - - -void Context2D::rect(qreal x, qreal y, qreal w, qreal h) -{ - QPainterPath path; path.addRect(x, y, w, h); - path = worldMatrix().map(path); - m_path.addPath(path); -} - -void Context2D::arc(qreal xc, qreal yc, qreal radius, - qreal sar, qreal ear, - bool anticlockwise) -{ - //### HACK - // In Qt we don't switch the coordinate system for degrees - // and still use the 0,0 as bottom left for degrees so we need - // to switch - sar = -sar; - ear = -ear; - anticlockwise = !anticlockwise; - //end hack - - float sa = DEGREES(sar); - float ea = DEGREES(ear); - - double span = 0; - - double xs = xc - radius; - double ys = yc - radius; - double width = radius*2; - double height = radius*2; - - if (!anticlockwise && (ea < sa)) - span += 360; - else if (anticlockwise && (sa < ea)) - span -= 360; - - //### this is also due to switched coordinate system - // we would end up with a 0 span instead of 360 - if (!(qFuzzyCompare(span + (ea - sa) + 1, 1) && - qFuzzyCompare(qAbs(span), 360))) { - span += ea - sa; - } - - QPainterPath path; - path.moveTo(QPointF(xc + radius * cos(sar), - yc - radius * sin(sar))); - - path.arcTo(xs, ys, width, height, sa, span); - path = worldMatrix().map(path); - m_path.addPath(path); -} - - -void Context2D::fill() -{ - beginPainting(); - m_painter.fillPath(m_path, m_painter.brush()); - scheduleChange(); -} - - -void Context2D::stroke() -{ - beginPainting(); - m_painter.save(); - m_painter.setMatrix(worldMatrix(), false); - QPainterPath tmp = worldMatrix().inverted().map(m_path); - m_painter.strokePath(tmp, m_painter.pen()); - m_painter.restore(); - scheduleChange(); -} - - -void Context2D::clip() -{ - m_state.clipPath = m_path; - m_state.flags |= DirtyClippingRegion; -} - - -bool Context2D::isPointInPath(qreal x, qreal y) const -{ - return m_path.contains(QPointF(x, y)); -} - - -ImageData Context2D::getImageData(qreal sx, qreal sy, qreal sw, qreal sh) -{ - Q_UNUSED(sx); - Q_UNUSED(sy); - Q_UNUSED(sw); - Q_UNUSED(sh); - return ImageData(); -} - - -void Context2D::putImageData(ImageData image, qreal dx, qreal dy) -{ - Q_UNUSED(image); - Q_UNUSED(dx); - Q_UNUSED(dy); -} - -Context2D::Context2D(QObject *parent) - : QObject(parent), m_changeTimerId(-1), m_width(0), m_height(0), m_inPaint(false) -{ - reset(); -} - -void Context2D::setupPainter() -{ - m_painter.setRenderHint(QPainter::Antialiasing, true); - if ((m_state.flags & DirtyClippingRegion) && !m_state.clipPath.isEmpty()) - m_painter.setClipPath(m_state.clipPath); - if (m_state.flags & DirtyFillStyle) - m_painter.setBrush(m_state.fillStyle); - if (m_state.flags & DirtyGlobalAlpha) - m_painter.setOpacity(m_state.globalAlpha); - if (m_state.flags & DirtyGlobalCompositeOperation) - m_painter.setCompositionMode(m_state.globalCompositeOperation); - if (m_state.flags & MDirtyPen) { - QPen pen = m_painter.pen(); - if (m_state.flags & DirtyStrokeStyle) - pen.setBrush(m_state.strokeStyle); - if (m_state.flags & DirtyLineWidth) - pen.setWidthF(m_state.lineWidth); - if (m_state.flags & DirtyLineCap) - pen.setCapStyle(m_state.lineCap); - if (m_state.flags & DirtyLineJoin) - pen.setJoinStyle(m_state.lineJoin); - if (m_state.flags & DirtyMiterLimit) - pen.setMiterLimit(m_state.miterLimit); - m_painter.setPen(pen); - } -} - -void Context2D::beginPainting() -{ - if (m_pixmap.width() != m_width || m_pixmap.height() != m_height) { - if (m_painter.isActive()) - m_painter.end(); - m_pixmap = QPixmap(m_width, m_height); - m_pixmap.fill(parent()->property("color").value<QColor>()); - } - - if (m_state.shadowBlur > 0 && m_painter.device() != &m_shadowbuffer) { - if (m_painter.isActive()) - m_painter.end(); - updateShadowBuffer(); - m_painter.begin(&m_shadowbuffer); - m_painter.setViewport(m_state.shadowOffsetX, - m_state.shadowOffsetY, - m_shadowbuffer.width(), - m_shadowbuffer.height()); - m_shadowbuffer.fill(Qt::transparent); - } - - if (!m_painter.isActive()) { - m_painter.begin(&m_pixmap); - m_painter.setRenderHint(QPainter::Antialiasing); - if (!m_state.clipPath.isEmpty()) - m_painter.setClipPath(m_state.clipPath); - m_painter.setBrush(m_state.fillStyle); - m_painter.setOpacity(m_state.globalAlpha); - QPen pen; - pen.setBrush(m_state.strokeStyle); - if (pen.style() == Qt::NoPen) - pen.setStyle(Qt::SolidLine); - pen.setCapStyle(m_state.lineCap); - pen.setJoinStyle(m_state.lineJoin); - pen.setWidthF(m_state.lineWidth); - pen.setMiterLimit(m_state.miterLimit); - m_painter.setPen(pen); - } else { - setupPainter(); - m_state.flags = 0; - } -} - -void Context2D::endPainting() -{ - if (m_state.shadowBlur > 0) { - QImage alphaChannel = m_shadowbuffer.alphaChannel(); - - qt_blurImage(alphaChannel, m_state.shadowBlur, false, 1); - - QRect imageRect = m_shadowbuffer.rect(); - - if (m_shadowColorIndexBuffer.isEmpty() || m_shadowColorBuffer != m_state.shadowColor) { - m_shadowColorIndexBuffer.clear(); - m_shadowColorBuffer = m_state.shadowColor; - - for (int i = 0; i < 256; ++i) { - m_shadowColorIndexBuffer << qRgba(qRound(255 * m_state.shadowColor.redF()), - qRound(255 * m_state.shadowColor.greenF()), - qRound(255 * m_state.shadowColor.blueF()), - i); - } - } - alphaChannel.setColorTable(m_shadowColorIndexBuffer); - - if (m_painter.isActive()) - m_painter.end(); - - m_painter.begin(&m_pixmap); - - // draw the blurred drop shadow... - m_painter.save(); - QTransform tf = m_painter.transform(); - m_painter.translate(0, imageRect.height()); - m_painter.rotate(-90); - m_painter.drawImage(0, 0, alphaChannel); - m_painter.setTransform(tf); - m_painter.restore(); - - // draw source - m_painter.drawImage(-m_state.shadowOffsetX, -m_state.shadowOffsetY, m_shadowbuffer.copy()); - m_painter.end(); - } -} - -void Context2D::clear() -{ - m_painter.fillRect(QRect(QPoint(0,0), size()), Qt::white); -} - -void Context2D::reset() -{ - m_stateStack.clear(); - m_state.matrix = QMatrix(); - m_state.clipPath = QPainterPath(); - m_state.strokeStyle = Qt::black; - m_state.fillStyle = Qt::black; - m_state.globalAlpha = 1.0; - m_state.lineWidth = 1; - m_state.lineCap = Qt::FlatCap; - m_state.lineJoin = Qt::MiterJoin; - m_state.miterLimit = 10; - m_state.shadowOffsetX = 0; - m_state.shadowOffsetY = 0; - m_state.shadowBlur = 0; - m_state.shadowColor = qRgba(0, 0, 0, 0); - m_state.globalCompositeOperation = QPainter::CompositionMode_SourceOver; - m_state.font = QFont(); - m_state.textAlign = Start; - m_state.textBaseline = Alphabetic; - m_state.flags = AllIsFullOfDirt; - m_mouseAreas.clear(); - clear(); -} - -void Context2D::drawImage(const QVariant &var, qreal sx, qreal sy, - qreal sw = 0, qreal sh = 0) -{ - CanvasImage *image = qobject_cast<CanvasImage*>(var.value<QObject*>()); - if (!image) { - Canvas *canvas = qobject_cast<Canvas*>(var.value<QObject*>()); - if (canvas) - image = canvas->toImage(); - } - if (image) { - beginPainting(); - if (sw == sh && sh == 0) - m_painter.drawPixmap(QPointF(sx, sy), image->value()); - else - m_painter.drawPixmap(QRect(sx, sy, sw, sh), image->value()); - - scheduleChange(); - } -} - -void Context2D::setSize(int width, int height) -{ - endPainting(); - m_width = width; - m_height = height; - - scheduleChange(); -} - -void Context2D::setSize(const QSize &size) -{ - setSize(size.width(), size.height()); -} - -QSize Context2D::size() const -{ - return m_pixmap.size(); -} - -QPoint Context2D::painterTranslate() const -{ - return m_painterTranslate; -} - -void Context2D::setPainterTranslate(const QPoint &translate) -{ - m_painterTranslate = translate; - m_state.flags |= DirtyTransformationMatrix; -} - -void Context2D::scheduleChange() -{ - QMetaObject::invokeMethod(this, "onScheduleChange", Qt::QueuedConnection, Q_ARG(int, 0)); -} - -void Context2D::onScheduleChange(int interval) -{ - if (m_changeTimerId == -1 && !m_inPaint) - m_changeTimerId = startTimer(interval); -} - -void Context2D::timerEvent(QTimerEvent *e) -{ - if (e->timerId() == m_changeTimerId) { - killTimer(m_changeTimerId); - m_changeTimerId = -1; - endPainting(); - emit changed(); - } else { - QObject::timerEvent(e); - } -} - -QMatrix Context2D::worldMatrix() const -{ - QMatrix mat; - mat.translate(m_painterTranslate.x(), m_painterTranslate.y()); - mat *= m_state.matrix; - return mat; -} - -QT_END_NAMESPACE diff --git a/src/plugins/qmlprofiler/canvas/qdeclarativecontext2d_p.h b/src/plugins/qmlprofiler/canvas/qdeclarativecontext2d_p.h deleted file mode 100644 index c4add692cb..0000000000 --- a/src/plugins/qmlprofiler/canvas/qdeclarativecontext2d_p.h +++ /dev/null @@ -1,326 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). -** 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. -** -****************************************************************************/ - -#ifndef QDECLARATIVECONTEXT2D_P_H -#define QDECLARATIVECONTEXT2D_P_H - -#include <qpainter.h> -#include <qpainterpath.h> -#include <qpixmap.h> -#include <qstring.h> -#include <qstack.h> -#include <qmetatype.h> -#include <qcoreevent.h> -#include <qvariant.h> - -#include <QJSValue> - -QT_BEGIN_NAMESPACE - -QColor colorFromString(const QString &name); - -class CanvasGradient : public QObject -{ - Q_OBJECT -public: - CanvasGradient(const QGradient &gradient) : m_gradient(gradient) {} - -public slots: - QGradient value() { return m_gradient; } - void addColorStop(float pos, const QString &color) { m_gradient.setColorAt(pos, colorFromString(color));} - -public: - QGradient m_gradient; -}; - -class CanvasImage: public QObject -{ - Q_OBJECT - Q_PROPERTY(QString src READ src WRITE setSrc NOTIFY sourceChanged) - Q_PROPERTY(int width READ width) - Q_PROPERTY(int height READ height) - -public: - CanvasImage() {} - CanvasImage(const QString &url) : m_image(url), m_src(url) {} - CanvasImage(const QPixmap &pixmap) {m_image = pixmap;} - -public slots: - int width() { return m_image.width(); } - int height() { return m_image.height(); } - QPixmap &value() { return m_image; } - QString src() { return m_src; } - void setSrc(const QString &src) { m_src = src; m_image.load(src); emit sourceChanged();} -signals: - void sourceChanged(); - -private: - QPixmap m_image; - QString m_src; -}; - - -class ImageData { -}; - -class Context2D : public QObject -{ - Q_OBJECT - // compositing - Q_PROPERTY(qreal globalAlpha READ globalAlpha WRITE setGlobalAlpha) - Q_PROPERTY(QString globalCompositeOperation READ globalCompositeOperation WRITE setGlobalCompositeOperation) - Q_PROPERTY(QVariant strokeStyle READ strokeStyle WRITE setStrokeStyle) - Q_PROPERTY(QVariant fillStyle READ fillStyle WRITE setFillStyle) - // line caps/joins - Q_PROPERTY(qreal lineWidth READ lineWidth WRITE setLineWidth) - Q_PROPERTY(QString lineCap READ lineCap WRITE setLineCap) - Q_PROPERTY(QString lineJoin READ lineJoin WRITE setLineJoin) - Q_PROPERTY(qreal miterLimit READ miterLimit WRITE setMiterLimit) - // shadows - Q_PROPERTY(qreal shadowOffsetX READ shadowOffsetX WRITE setShadowOffsetX) - Q_PROPERTY(qreal shadowOffsetY READ shadowOffsetY WRITE setShadowOffsetY) - Q_PROPERTY(qreal shadowBlur READ shadowBlur WRITE setShadowBlur) - Q_PROPERTY(QString shadowColor READ shadowColor WRITE setShadowColor) - // fonts - Q_PROPERTY(QString font READ font WRITE setFont) - Q_PROPERTY(QString textBaseline READ textBaseline WRITE setTextBaseline) - Q_PROPERTY(QString textAlign READ textAlign WRITE setTextAlign) - - enum TextBaseLine { Alphabetic=0, Top, Middle, Bottom, Hanging}; - enum TextAlign { Start=0, End, Left, Right, Center}; - -public: - Context2D(QObject *parent = 0); - void setSize(int width, int height); - void setSize(const QSize &size); - QSize size() const; - - QPoint painterTranslate() const; - void setPainterTranslate(const QPoint &); - - void scheduleChange(); - void timerEvent(QTimerEvent *e); - - void clear(); - void reset(); - - QPixmap pixmap() { return m_pixmap; } - - // compositing - qreal globalAlpha() const; // (default 1.0) - QString globalCompositeOperation() const; // (default over) - QVariant strokeStyle() const; // (default black) - QVariant fillStyle() const; // (default black) - - void setGlobalAlpha(qreal alpha); - void setGlobalCompositeOperation(const QString &op); - void setStrokeStyle(const QVariant &style); - void setFillStyle(const QVariant &style); - - // line caps/joins - qreal lineWidth() const; // (default 1) - QString lineCap() const; // "butt", "round", "square" (default "butt") - QString lineJoin() const; // "round", "bevel", "miter" (default "miter") - qreal miterLimit() const; // (default 10) - - void setLineWidth(qreal w); - void setLineCap(const QString &s); - void setLineJoin(const QString &s); - void setMiterLimit(qreal m); - - void setFont(const QString &font); - QString font(); - void setTextBaseline(const QString &font); - QString textBaseline(); - void setTextAlign(const QString &font); - QString textAlign(); - - // shadows - qreal shadowOffsetX() const; // (default 0) - qreal shadowOffsetY() const; // (default 0) - qreal shadowBlur() const; // (default 0) - QString shadowColor() const; // (default black) - - void setShadowOffsetX(qreal x); - void setShadowOffsetY(qreal y); - void setShadowBlur(qreal b); - void setShadowColor(const QString &str); - - struct MouseArea { - QJSValue callback; - QJSValue data; - QRectF rect; - QMatrix matrix; - }; - const QList<MouseArea> &mouseAreas() const; - -public slots: - void save(); // push state on state stack - void restore(); // pop state stack and restore state - - void fillText(const QString &text, qreal x, qreal y); - void strokeText(const QString &text, qreal x, qreal y); - - void setInPaint(bool val){m_inPaint = val;} - void scale(qreal x, qreal y); - void rotate(qreal angle); - void translate(qreal x, qreal y); - void transform(qreal m11, qreal m12, qreal m21, qreal m22, - qreal dx, qreal dy); - void setTransform(qreal m11, qreal m12, qreal m21, qreal m22, - qreal dx, qreal dy); - - CanvasGradient *createLinearGradient(qreal x0, qreal y0, - qreal x1, qreal y1); - CanvasGradient *createRadialGradient(qreal x0, qreal y0, - qreal r0, qreal x1, - qreal y1, qreal r1); - - // rects - void clearRect(qreal x, qreal y, qreal w, qreal h); - void fillRect(qreal x, qreal y, qreal w, qreal h); - void strokeRect(qreal x, qreal y, qreal w, qreal h); - - // mouse - void mouseArea(qreal x, qreal y, qreal w, qreal h, const QJSValue &, const QJSValue & = QJSValue()); - - // path API - void beginPath(); - void closePath(); - void moveTo(qreal x, qreal y); - void lineTo(qreal x, qreal y); - void quadraticCurveTo(qreal cpx, qreal cpy, qreal x, qreal y); - void bezierCurveTo(qreal cp1x, qreal cp1y, - qreal cp2x, qreal cp2y, qreal x, qreal y); - void arcTo(qreal x1, qreal y1, qreal x2, qreal y2, qreal radius); - void rect(qreal x, qreal y, qreal w, qreal h); - void arc(qreal x, qreal y, qreal radius, - qreal startAngle, qreal endAngle, - bool anticlockwise); - void fill(); - void stroke(); - void clip(); - bool isPointInPath(qreal x, qreal y) const; - - CanvasImage *createImage(const QString &url); - - // drawing images (no overloads due to QTBUG-11604) - void drawImage(const QVariant &var, qreal dx, qreal dy, qreal dw, qreal dh); - - // pixel manipulation - ImageData getImageData(qreal sx, qreal sy, qreal sw, qreal sh); - void putImageData(ImageData image, qreal dx, qreal dy); - void endPainting(); - -private slots: - void onScheduleChange(int interval); - -signals: - void changed(); - -private: - void setupPainter(); - void beginPainting(); - void updateShadowBuffer(); - - int m_changeTimerId; - QPainterPath m_path; - - enum DirtyFlag { - DirtyTransformationMatrix = 0x00001, - DirtyClippingRegion = 0x00002, - DirtyStrokeStyle = 0x00004, - DirtyFillStyle = 0x00008, - DirtyGlobalAlpha = 0x00010, - DirtyLineWidth = 0x00020, - DirtyLineCap = 0x00040, - DirtyLineJoin = 0x00080, - DirtyMiterLimit = 0x00100, - MDirtyPen = DirtyStrokeStyle - | DirtyLineWidth - | DirtyLineCap - | DirtyLineJoin - | DirtyMiterLimit, - DirtyShadowOffsetX = 0x00200, - DirtyShadowOffsetY = 0x00400, - DirtyShadowBlur = 0x00800, - DirtyShadowColor = 0x01000, - DirtyGlobalCompositeOperation = 0x2000, - DirtyFont = 0x04000, - DirtyTextAlign = 0x08000, - DirtyTextBaseline = 0x10000, - AllIsFullOfDirt = 0xfffff - }; - - struct State { - State() : flags(0) {} - QMatrix matrix; - QPainterPath clipPath; - QBrush strokeStyle; - QBrush fillStyle; - qreal globalAlpha; - qreal lineWidth; - Qt::PenCapStyle lineCap; - Qt::PenJoinStyle lineJoin; - qreal miterLimit; - qreal shadowOffsetX; - qreal shadowOffsetY; - qreal shadowBlur; - QColor shadowColor; - QPainter::CompositionMode globalCompositeOperation; - QFont font; - Context2D::TextAlign textAlign; - Context2D::TextBaseLine textBaseline; - int flags; - }; - - int baseLineOffset(Context2D::TextBaseLine value, const QFontMetrics &metrics); - int textAlignOffset(Context2D::TextAlign value, const QFontMetrics &metrics, const QString &string); - - QMatrix worldMatrix() const; - - QPoint m_painterTranslate; - State m_state; - QStack<State> m_stateStack; - QPixmap m_pixmap; - QList<MouseArea> m_mouseAreas; - QImage m_shadowbuffer; - QVector<QRgb> m_shadowColorIndexBuffer; - QColor m_shadowColorBuffer; - QPainter m_painter; - int m_width, m_height; - bool m_inPaint; -}; - -QT_END_NAMESPACE - -Q_DECLARE_METATYPE(CanvasImage*) -Q_DECLARE_METATYPE(CanvasGradient*) - -#endif // QDECLARATIVECONTEXT2D_P_H diff --git a/src/plugins/qmlprofiler/canvas/qmlprofilercanvas.cpp b/src/plugins/qmlprofiler/canvas/qmlprofilercanvas.cpp deleted file mode 100644 index bfaa1185bc..0000000000 --- a/src/plugins/qmlprofiler/canvas/qmlprofilercanvas.cpp +++ /dev/null @@ -1,105 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). -** 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 "qmlprofilercanvas.h" - -#include "qdeclarativecontext2d_p.h" - -#include <qpixmap.h> -#include <qpainter.h> - -namespace QmlProfiler { -namespace Internal { - -QmlProfilerCanvas::QmlProfilerCanvas() - : m_context2d(new Context2D(this)) -{ - setAcceptedMouseButtons(Qt::LeftButton); - m_drawTimer.setSingleShot(true); - connect(&m_drawTimer, SIGNAL(timeout()), this, SLOT(draw())); - - m_drawTimer.start(); -} - -void QmlProfilerCanvas::requestPaint() -{ - if (m_context2d->size().width() != width() - || m_context2d->size().height() != height()) { - m_drawTimer.start(); - } else { - update(); - } -} - -void QmlProfilerCanvas::requestRedraw() -{ - m_drawTimer.start(); -} - -// called from GUI thread. Draws into m_context2d. -void QmlProfilerCanvas::draw() -{ - QMutexLocker lock(&m_pixmapMutex); - m_context2d->reset(); - m_context2d->setSize(width(), height()); - - if (width() > 0 && height() > 0) - emit drawRegion(m_context2d, QRect(0, 0, width(), height())); - update(); -} - -// called from OpenGL thread. Renders m_context2d into OpenGL buffer. -void QmlProfilerCanvas::paint(QPainter *p) -{ - QMutexLocker lock(&m_pixmapMutex); - p->drawPixmap(0, 0, m_context2d->pixmap()); -} - -void QmlProfilerCanvas::componentComplete() -{ - const QMetaObject *metaObject = this->metaObject(); - int propertyCount = metaObject->propertyCount(); - int requestPaintMethod = metaObject->indexOfMethod("requestPaint()"); - for (int ii = QmlProfilerCanvas::staticMetaObject.propertyCount(); ii < propertyCount; ++ii) { - QMetaProperty p = metaObject->property(ii); - if (p.hasNotifySignal()) - QMetaObject::connect(this, p.notifySignalIndex(), this, requestPaintMethod, 0, 0); - } - QQuickItem::componentComplete(); - requestRedraw(); -} - -void QmlProfilerCanvas::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) -{ - QQuickItem::geometryChanged(newGeometry, oldGeometry); - requestRedraw(); -} - -} -} diff --git a/src/plugins/qmlprofiler/qml/CategoryLabel.qml b/src/plugins/qmlprofiler/qml/CategoryLabel.qml index 948a129534..0ea83dc7aa 100644 --- a/src/plugins/qmlprofiler/qml/CategoryLabel.qml +++ b/src/plugins/qmlprofiler/qml/CategoryLabel.qml @@ -47,7 +47,7 @@ Item { onExpandedChanged: { qmlProfilerModelProxy.setExpanded(modelIndex, categoryIndex, expanded); - backgroundMarks.requestRedraw(); + backgroundMarks.requestPaint(); getDescriptions(); updateHeight(); } diff --git a/src/plugins/qmlprofiler/qml/Detail.qml b/src/plugins/qmlprofiler/qml/Detail.qml index 1f6f22b8c0..5683735867 100644 --- a/src/plugins/qmlprofiler/qml/Detail.qml +++ b/src/plugins/qmlprofiler/qml/Detail.qml @@ -28,32 +28,9 @@ ****************************************************************************/ import QtQuick 2.1 -import Monitor 1.0 -Item { - id: detail - property string label - property string content - - height: childrenRect.height+2 - width: childrenRect.width - Item { - id: guideline - x: 70 - width: 5 - } - Text { - y: 1 - id: lbl - text: label - font.pixelSize: 12 - font.bold: true - } - Text { - text: content - font.pixelSize: 12 - anchors.baseline: lbl.baseline - anchors.left: guideline.right - textFormat: Text.PlainText - } +Text { + font.pixelSize: 12 + font.bold: index % 2 === 0 + textFormat: Text.PlainText } diff --git a/src/plugins/qmlprofiler/qml/MainView.qml b/src/plugins/qmlprofiler/qml/MainView.qml index f34b003fd8..cad370244f 100644 --- a/src/plugins/qmlprofiler/qml/MainView.qml +++ b/src/plugins/qmlprofiler/qml/MainView.qml @@ -169,28 +169,6 @@ Rectangle { view.selectPrev(); } - function updateZoomCentered(centerX, relativeFactor) - { - var min_length = 1e5; // 0.1 ms - var windowLength = view.endTime - view.startTime; - if (windowLength < min_length) - windowLength = min_length; - var newWindowLength = windowLength * relativeFactor; - - if (newWindowLength > qmlProfilerModelProxy.traceDuration()) { - newWindowLength = qmlProfilerModelProxy.traceDuration(); - relativeFactor = newWindowLength / windowLength; - } - if (newWindowLength < min_length) { - newWindowLength = min_length; - relativeFactor = newWindowLength / windowLength; - } - - var fixedPoint = (centerX - flick.x) * windowLength / flick.width + view.startTime; - var startTime = fixedPoint - relativeFactor*(fixedPoint - view.startTime); - zoomControl.setRange(startTime, startTime + newWindowLength); - } - function recenter( centerPoint ) { var windowLength = view.endTime - view.startTime; var newStart = Math.floor(centerPoint - windowLength/2); @@ -215,21 +193,10 @@ Rectangle { } - function wheelZoom(wheelCenter, wheelDelta) { - if (qmlProfilerModelProxy.traceEndTime() > qmlProfilerModelProxy.traceStartTime() && - wheelDelta !== 0) { - if (wheelDelta>0) - updateZoomCentered(wheelCenter, 1/1.2); - else - updateZoomCentered(wheelCenter, 1.2); - } - } - function hideRangeDetails() { rangeDetails.visible = false; rangeDetails.duration = ""; rangeDetails.label = ""; - //rangeDetails.type = ""; rangeDetails.file = ""; rangeDetails.line = -1; rangeDetails.column = 0; diff --git a/src/plugins/qmlprofiler/qml/Overview.js b/src/plugins/qmlprofiler/qml/Overview.js index c6bb0300e7..7fe1084bdd 100644 --- a/src/plugins/qmlprofiler/qml/Overview.js +++ b/src/plugins/qmlprofiler/qml/Overview.js @@ -97,6 +97,7 @@ function drawData(canvas, ctxt, region) var ycenter = Math.round(bump + (modelRowStart + qmlProfilerModelProxy.getEventCategoryInModel(modelIndex, ii)) * blockHeight + blockHeight/2); + ctxt.beginPath(); ctxt.arc(xcenter, ycenter, radius, 0, 2*Math.PI, true); ctxt.stroke(); } diff --git a/src/plugins/qmlprofiler/qml/Overview.qml b/src/plugins/qmlprofiler/qml/Overview.qml index 9670fbb47f..9fed5a9c37 100644 --- a/src/plugins/qmlprofiler/qml/Overview.qml +++ b/src/plugins/qmlprofiler/qml/Overview.qml @@ -31,9 +31,10 @@ import QtQuick 2.1 import Monitor 1.0 import "Overview.js" as Plotter -Canvas2D { +Canvas { id: canvas objectName: "Overview" + contextType: "2d" // ***** properties height: 50 @@ -45,7 +46,7 @@ Canvas2D { function clearDisplay() { dataReady = false; - requestRedraw(); + requestPaint(); } function updateRange() { @@ -84,18 +85,18 @@ Canvas2D { target: qmlProfilerModelProxy onDataAvailable: { dataReady = true; - requestRedraw(); + requestPaint(); } } // ***** slots - onDrawRegion: { + onPaint: { Plotter.qmlProfilerModelProxy = qmlProfilerModelProxy; if (dataReady) { - Plotter.plot(canvas, ctxt, region); + Plotter.plot(canvas, context, region); } else { - Plotter.drawGraph(canvas, ctxt, region) //just draw the background + Plotter.drawGraph(canvas, context, region) //just draw the background } } diff --git a/src/plugins/qmlprofiler/qml/RangeDetails.qml b/src/plugins/qmlprofiler/qml/RangeDetails.qml index b7de8746d7..83988bfe54 100644 --- a/src/plugins/qmlprofiler/qml/RangeDetails.qml +++ b/src/plugins/qmlprofiler/qml/RangeDetails.qml @@ -43,7 +43,7 @@ Item { property bool locked: view.selectionLocked - width: col.width + 45 + width: col.width + 25 height: col.height + 30 z: 1 visible: false @@ -68,7 +68,8 @@ Item { rangeDetails.dialogTitle = eventData[0]["title"]; for (var i = 1; i < eventData.length; i++) { for (var k in eventData[i]) { - eventInfo.append({"key": k, "value":eventData[i][k]}); + eventInfo.append({"content" : k}); + eventInfo.append({"content" : eventData[i][k]}) } } rangeDetails.visible = true; @@ -163,24 +164,24 @@ Item { border.color: "#a0a0a0" //details - Column { + Grid { id: col x: 10 y: 5 + spacing: 5 + columns: 2 Repeater { model: eventInfo Detail { - label: key - content: value + text: content } } } } MouseArea { - width: col.width + 45 - height: col.height + 30 + anchors.fill: parent drag.target: parent drag.minimumX: 0 drag.maximumX: root.width - parent.width @@ -211,7 +212,7 @@ Item { Text { id: closeIcon - x: col.width + 30 + x: col.width + 10 y: 4 text:"X" color: "white" diff --git a/src/plugins/qmlprofiler/qml/SelectionRangeDetails.qml b/src/plugins/qmlprofiler/qml/SelectionRangeDetails.qml index eeef84550d..432fddb2cb 100644 --- a/src/plugins/qmlprofiler/qml/SelectionRangeDetails.qml +++ b/src/plugins/qmlprofiler/qml/SelectionRangeDetails.qml @@ -38,7 +38,7 @@ Item { property string duration property bool showDuration - width: 170 + width: Math.max(150, col.width + 25) height: col.height + 30 z: 1 visible: false @@ -126,30 +126,31 @@ Item { y: 20 border.width: 1 border.color: "#a0a0a0" - Column { + Grid { id: col x: 10 y: 5 - Detail { - label: qsTr("Start") - content: selectionRangeDetails.startTime - } - Detail { - label: qsTr("End") - visible: selectionRangeDetails.showDuration - content: selectionRangeDetails.endTime - } - Detail { - label: qsTr("Duration") - visible: selectionRangeDetails.showDuration - content: selectionRangeDetails.duration + spacing: 5 + columns: 2 + + Repeater { + model: [ + qsTr("Start"), + startTime, + showDuration ? qsTr("End") : "", + showDuration ? endTime : "", + showDuration ? qsTr("Duration") : "", + showDuration ? duration : "" + ] + Detail { + text: modelData + } } } } MouseArea { - width: col.width + 45 - height: col.height + 30 + anchors.fill: parent drag.target: parent drag.minimumX: 0 drag.maximumX: root.width - parent.width diff --git a/src/plugins/qmlprofiler/qml/TimeDisplay.qml b/src/plugins/qmlprofiler/qml/TimeDisplay.qml index 9c02665163..7ab1ab877d 100644 --- a/src/plugins/qmlprofiler/qml/TimeDisplay.qml +++ b/src/plugins/qmlprofiler/qml/TimeDisplay.qml @@ -30,9 +30,10 @@ import QtQuick 2.1 import Monitor 1.0 -Canvas2D { +Canvas { id: timeDisplay objectName: "TimeDisplay" + contextType: "2d" property real startTime : 0 property real endTime : 0 @@ -43,13 +44,13 @@ Canvas2D { onRangeChanged: { startTime = zoomControl.startTime(); endTime = zoomControl.endTime(); - requestRedraw(); + requestPaint(); } } - onDrawRegion: { - ctxt.fillStyle = "white"; - ctxt.fillRect(0, 0, width, height); + onPaint: { + context.fillStyle = "white"; + context.fillRect(0, 0, width, height); var totalTime = endTime - startTime; var spacing = width / totalTime; @@ -67,50 +68,50 @@ Canvas2D { var initialColor = Math.floor(realStartTime/timePerBlock) % 2; - ctxt.fillStyle = "#000000"; - ctxt.font = "8px sans-serif"; + context.fillStyle = "#000000"; + context.font = "8px sans-serif"; for (var ii = 0; ii < blockCount+1; ii++) { var x = Math.floor(ii*pixelsPerBlock - realStartPos); - ctxt.fillStyle = (ii+initialColor)%2 ? "#E6E6E6":"white"; - ctxt.fillRect(x, 0, pixelsPerBlock, height); + context.fillStyle = (ii+initialColor)%2 ? "#E6E6E6":"white"; + context.fillRect(x, 0, pixelsPerBlock, height); - ctxt.strokeStyle = "#B0B0B0"; - ctxt.beginPath(); - ctxt.moveTo(x, 0); - ctxt.lineTo(x, height); - ctxt.stroke(); + context.strokeStyle = "#B0B0B0"; + context.beginPath(); + context.moveTo(x, 0); + context.lineTo(x, height); + context.stroke(); - ctxt.fillStyle = "#000000"; - ctxt.fillText(prettyPrintTime(ii*timePerBlock + realStartTime), x + 5, height/2 + 5); + context.fillStyle = "#000000"; + context.fillText(prettyPrintTime(ii*timePerBlock + realStartTime), x + 5, height/2 + 5); } - ctxt.strokeStyle = "#525252"; - ctxt.beginPath(); - ctxt.moveTo(0, height-1); - ctxt.lineTo(width, height-1); - ctxt.stroke(); + context.strokeStyle = "#525252"; + context.beginPath(); + context.moveTo(0, height-1); + context.lineTo(width, height-1); + context.stroke(); // gradient borders var gradientDark = "rgba(0, 0, 0, 0.53125)"; var gradientClear = "rgba(0, 0, 0, 0)"; - var grad = ctxt.createLinearGradient(0, 0, 0, 6); + var grad = context.createLinearGradient(0, 0, 0, 6); grad.addColorStop(0,gradientDark); grad.addColorStop(1,gradientClear); - ctxt.fillStyle = grad; - ctxt.fillRect(0, 0, width, 6); + context.fillStyle = grad; + context.fillRect(0, 0, width, 6); - grad = ctxt.createLinearGradient(0, 0, 6, 0); + grad = context.createLinearGradient(0, 0, 6, 0); grad.addColorStop(0,gradientDark); grad.addColorStop(1,gradientClear); - ctxt.fillStyle = grad; - ctxt.fillRect(0, 0, 6, height); + context.fillStyle = grad; + context.fillRect(0, 0, 6, height); - grad = ctxt.createLinearGradient(width, 0, width-6, 0); + grad = context.createLinearGradient(width, 0, width-6, 0); grad.addColorStop(0,gradientDark); grad.addColorStop(1,gradientClear); - ctxt.fillStyle = grad; - ctxt.fillRect(width-6, 0, 6, height); + context.fillStyle = grad; + context.fillRect(width-6, 0, 6, height); } function prettyPrintTime( t ) diff --git a/src/plugins/qmlprofiler/qml/TimeMarks.qml b/src/plugins/qmlprofiler/qml/TimeMarks.qml index 9f87e0319a..6bf6be330c 100644 --- a/src/plugins/qmlprofiler/qml/TimeMarks.qml +++ b/src/plugins/qmlprofiler/qml/TimeMarks.qml @@ -30,9 +30,10 @@ import QtQuick 2.1 import Monitor 1.0 -Canvas2D { - id: timeDisplay +Canvas { + id: timeMarks objectName: "TimeMarks" + contextType: "2d" property real startTime property real endTime @@ -40,11 +41,13 @@ Canvas2D { Connections { target: labels - onHeightChanged: { requestRedraw(); } + onHeightChanged: requestPaint() } - onDrawRegion: { - drawBackgroundBars( ctxt, region ); + onYChanged: requestPaint() + + onPaint: { + drawBackgroundBars( context, region ); var totalTime = endTime - startTime; var spacing = width / totalTime; @@ -63,23 +66,23 @@ Canvas2D { var lineStart = y < 0 ? -y : 0; var lineEnd = Math.min(height, labels.height - y); - ctxt.fillStyle = "#000000"; - ctxt.font = "8px sans-serif"; + context.fillStyle = "#000000"; + context.font = "8px sans-serif"; for (var ii = 0; ii < blockCount+1; ii++) { var x = Math.floor(ii*pixelsPerBlock - realStartPos); - ctxt.strokeStyle = "#B0B0B0"; - ctxt.beginPath(); - ctxt.moveTo(x, lineStart); - ctxt.lineTo(x, lineEnd); - ctxt.stroke(); + context.strokeStyle = "#B0B0B0"; + context.beginPath(); + context.moveTo(x, lineStart); + context.lineTo(x, lineEnd); + context.stroke(); - ctxt.strokeStyle = "#CCCCCC"; + context.strokeStyle = "#CCCCCC"; for (var jj=1; jj < 5; jj++) { var xx = Math.floor(ii*pixelsPerBlock + jj*pixelsPerSection - realStartPos); - ctxt.beginPath(); - ctxt.moveTo(xx, lineStart); - ctxt.lineTo(xx, lineEnd); - ctxt.stroke(); + context.beginPath(); + context.moveTo(xx, lineStart); + context.lineTo(xx, lineEnd); + context.stroke(); } } } @@ -88,19 +91,19 @@ Canvas2D { if (startTime !== start || endTime !== end) { startTime = start; endTime = end; - requestRedraw(); + requestPaint(); } } - function drawBackgroundBars( ctxt, region ) { + function drawBackgroundBars( context, region ) { var colorIndex = true; // row background var backgroundOffset = y < 0 ? -y : -(y % (2 * root.singleRowHeight)); for (var currentY= backgroundOffset; currentY < Math.min(height, labels.height - y); currentY += root.singleRowHeight) { - ctxt.fillStyle = colorIndex ? "#f0f0f0" : "white"; - ctxt.strokeStyle = colorIndex ? "#f0f0f0" : "white"; - ctxt.fillRect(0, currentY, width, root.singleRowHeight); + context.fillStyle = colorIndex ? "#f0f0f0" : "white"; + context.strokeStyle = colorIndex ? "#f0f0f0" : "white"; + context.fillRect(0, currentY, width, root.singleRowHeight); colorIndex = !colorIndex; } @@ -112,18 +115,18 @@ Canvas2D { if (cumulatedHeight < y) continue; - ctxt.strokeStyle = "#B0B0B0"; - ctxt.beginPath(); - ctxt.moveTo(0, cumulatedHeight - y); - ctxt.lineTo(width, cumulatedHeight - y); - ctxt.stroke(); + context.strokeStyle = "#B0B0B0"; + context.beginPath(); + context.moveTo(0, cumulatedHeight - y); + context.lineTo(width, cumulatedHeight - y); + context.stroke(); } } // bottom if (height > labels.height - y) { - ctxt.fillStyle = "#f5f5f5"; - ctxt.fillRect(0, labels.height - y, width, Math.min(height - labels.height + y, labelsTail.height)); + context.fillStyle = "#f5f5f5"; + context.fillRect(0, labels.height - y, width, Math.min(height - labels.height + y, labelsTail.height)); } } } diff --git a/src/plugins/qmlprofiler/qmlprofiler.pro b/src/plugins/qmlprofiler/qmlprofiler.pro index 8be8423533..aa23d616c8 100644 --- a/src/plugins/qmlprofiler/qmlprofiler.pro +++ b/src/plugins/qmlprofiler/qmlprofiler.pro @@ -3,7 +3,6 @@ DEFINES += QMLPROFILER_LIBRARY QT += network qml quick include(../../qtcreatorplugin.pri) -include(canvas/canvas.pri) SOURCES += \ qmlprofilerplugin.cpp \ @@ -31,7 +30,8 @@ SOURCES += \ qmlprofilertracefile.cpp \ abstracttimelinemodel.cpp \ timelinemodelaggregator.cpp \ - qmlprofilerpainteventsmodelproxy.cpp + qmlprofilerpainteventsmodelproxy.cpp \ + sortedtimelinemodel.cpp HEADERS += \ qmlprofilerconstants.h \ @@ -62,7 +62,8 @@ HEADERS += \ qmlprofilertracefile.h \ abstracttimelinemodel.h \ timelinemodelaggregator.h \ - qmlprofilerpainteventsmodelproxy.h + qmlprofilerpainteventsmodelproxy.h \ + sortedtimelinemodel.h RESOURCES += \ qml/qmlprofiler.qrc diff --git a/src/plugins/qmlprofiler/qmlprofiler.qbs b/src/plugins/qmlprofiler/qmlprofiler.qbs index 507ce1093e..018019c49a 100644 --- a/src/plugins/qmlprofiler/qmlprofiler.qbs +++ b/src/plugins/qmlprofiler/qmlprofiler.qbs @@ -50,23 +50,13 @@ QtcPlugin { "qmlprofilerviewmanager.cpp", "qmlprofilerviewmanager.h", "qv8profilerdatamodel.cpp", "qv8profilerdatamodel.h", "qv8profilereventview.h", "qv8profilereventview.cpp", + "sortedtimelinemodel.h", "sortedtimelinemodel.cpp", "timelinemodelaggregator.cpp", "timelinemodelaggregator.h", "timelinerenderer.cpp", "timelinerenderer.h", ] } Group { - name: "Canvas" - prefix: "canvas/" - files: [ - "qdeclarativecanvas.cpp", "qdeclarativecanvas_p.h", - "qdeclarativecanvastimer.cpp", "qdeclarativecanvastimer_p.h", - "qdeclarativecontext2d.cpp", "qdeclarativecontext2d_p.h", - "qmlprofilercanvas.cpp", "qmlprofilercanvas.h" - ] - } - - Group { name: "QML" prefix: "qml/" files: [ diff --git a/src/plugins/qmlprofiler/qmlprofilereventsmodelproxy.cpp b/src/plugins/qmlprofiler/qmlprofilereventsmodelproxy.cpp index a8b7ceb633..cdc672c8a6 100644 --- a/src/plugins/qmlprofiler/qmlprofilereventsmodelproxy.cpp +++ b/src/plugins/qmlprofiler/qmlprofilereventsmodelproxy.cpp @@ -96,10 +96,10 @@ void QmlProfilerEventsModelProxy::limitToRange(qint64 rangeStart, qint64 rangeEn void QmlProfilerEventsModelProxy::dataChanged() { - if (d->modelManager->state() == QmlProfilerDataState::Empty) - clear(); - else + if (d->modelManager->state() == QmlProfilerDataState::Done) loadData(); + else if (d->modelManager->state() == QmlProfilerDataState::ClearingData) + clear(); } QSet<QString> QmlProfilerEventsModelProxy::eventsInBindingLoop() const diff --git a/src/plugins/qmlprofiler/qmlprofilermodelmanager.cpp b/src/plugins/qmlprofiler/qmlprofilermodelmanager.cpp index ab0e9aef8a..62751f3d22 100644 --- a/src/plugins/qmlprofiler/qmlprofilermodelmanager.cpp +++ b/src/plugins/qmlprofiler/qmlprofilermodelmanager.cpp @@ -57,6 +57,9 @@ void QmlProfilerDataState::setState(QmlProfilerDataState::State state) return; switch (state) { + case ClearingData: + QTC_ASSERT(m_state == Done || m_state == Empty, /**/); + break; case Empty: // if it's not empty, complain but go on QTC_ASSERT(m_modelManager->isEmpty(), /**/); @@ -345,6 +348,7 @@ QmlProfilerDataState::State QmlProfilerModelManager::state() const void QmlProfilerModelManager::clear() { + setState(QmlProfilerDataState::ClearingData); for (int i = 0; i < d->partialCounts.count(); i++) d->partialCounts[i] = 0; d->progress = 0; diff --git a/src/plugins/qmlprofiler/qmlprofilermodelmanager.h b/src/plugins/qmlprofiler/qmlprofilermodelmanager.h index 2954de4690..146c6acdfd 100644 --- a/src/plugins/qmlprofiler/qmlprofilermodelmanager.h +++ b/src/plugins/qmlprofiler/qmlprofilermodelmanager.h @@ -52,6 +52,7 @@ public: Empty, AcquiringData, ProcessingData, + ClearingData, Done }; diff --git a/src/plugins/qmlprofiler/qmlprofilerpainteventsmodelproxy.cpp b/src/plugins/qmlprofiler/qmlprofilerpainteventsmodelproxy.cpp index 630d25b38c..dcb3ecfee3 100644 --- a/src/plugins/qmlprofiler/qmlprofilerpainteventsmodelproxy.cpp +++ b/src/plugins/qmlprofiler/qmlprofilerpainteventsmodelproxy.cpp @@ -30,6 +30,7 @@ #include "qmlprofilerpainteventsmodelproxy.h" #include "qmlprofilermodelmanager.h" #include "qmlprofilersimplemodel.h" +#include "sortedtimelinemodel.h" #include <QCoreApplication> #include <QVector> @@ -49,7 +50,7 @@ struct CategorySpan { int contractedRows; }; -class PaintEventsModelProxy::PaintEventsModelProxyPrivate +class PaintEventsModelProxy::PaintEventsModelProxyPrivate : public SortedTimelineModel<QmlPaintEventData> { public: PaintEventsModelProxyPrivate(PaintEventsModelProxy *qq) : q(qq) {} @@ -58,7 +59,6 @@ public: QString displayTime(double time); void computeAnimationCountLimit(); - QVector <PaintEventsModelProxy::QmlPaintEventData> eventList; int minAnimationCount; int maxAnimationCount; bool expanded; @@ -94,49 +94,15 @@ QString PaintEventsModelProxy::name() const return QLatin1String("PaintEventsModelProxy"); } -const QVector<PaintEventsModelProxy::QmlPaintEventData> PaintEventsModelProxy::getData() const -{ - return d->eventList; -} - -const QVector<PaintEventsModelProxy::QmlPaintEventData> PaintEventsModelProxy::getData(qint64 fromTime, qint64 toTime) const -{ - int fromIndex = findFirstIndex(fromTime); - int toIndex = findLastIndex(toTime); - if (fromIndex != -1 && toIndex > fromIndex) - return d->eventList.mid(fromIndex, toIndex - fromIndex + 1); - else - return QVector<PaintEventsModelProxy::QmlPaintEventData>(); -} - void PaintEventsModelProxy::clear() { - d->eventList.clear(); + d->SortedTimelineModel::clear(); d->minAnimationCount = 1; d->maxAnimationCount = 1; d->expanded = false; m_modelManager->modelProxyCountUpdated(m_modelId, 0, 1); } -void PaintEventsModelProxy::dataChanged() -{ - if (m_modelManager->state() == QmlProfilerDataState::ProcessingData) - loadData(); - - if (m_modelManager->state() == QmlProfilerDataState::Empty) - clear(); - - emit stateChanged(); - emit dataAvailable(); - emit emptyChanged(); - emit expandedChanged(); -} - -bool compareStartTimes(const PaintEventsModelProxy::QmlPaintEventData &t1, const PaintEventsModelProxy::QmlPaintEventData &t2) -{ - return t1.startTime < t2.startTime; -} - bool PaintEventsModelProxy::eventAccepted(const QmlProfilerSimpleModel::QmlEventData &event) const { return (event.eventType == QmlDebug::Painting && event.bindingType == QmlDebug::AnimationFrame); @@ -151,6 +117,11 @@ void PaintEventsModelProxy::loadData() // collect events const QVector<QmlProfilerSimpleModel::QmlEventData> referenceList = simpleModel->getEvents(); + + QmlPaintEventData lastEvent; + qint64 lastStartTime = -1; + qint64 lastDuration = -1; + foreach (const QmlProfilerSimpleModel::QmlEventData &event, referenceList) { if (!eventAccepted(event)) continue; @@ -165,30 +136,29 @@ void PaintEventsModelProxy::loadData() // the duration of the events is estimated from the framerate // we need to correct it before appending a new event - if (d->eventList.count() > 0) { - QmlPaintEventData *lastEvent = &d->eventList[d->eventList.count()-1]; - if (lastEvent->startTime + lastEvent->duration >= realStartTime) { + if (lastStartTime != -1) { + if (lastStartTime + lastDuration >= realStartTime) { // 1 nanosecond less to prevent overlap - lastEvent->duration = realStartTime - lastEvent->startTime - 1; - lastEvent->framerate = 1e9/lastEvent->duration; + lastDuration = realStartTime - lastStartTime - 1; + lastEvent.framerate = 1e9 / lastDuration; } } - QmlPaintEventData newEvent = { - realStartTime, - estimatedDuration, - (int)event.numericData1, - (int)event.numericData2 - }; + d->insert(lastStartTime, lastDuration, lastEvent); - d->eventList.append(newEvent); + lastEvent.framerate = (int)event.numericData1; + lastEvent.animationcount = (int)event.numericData2; + lastStartTime = realStartTime; + lastDuration = estimatedDuration; - m_modelManager->modelProxyCountUpdated(m_modelId, d->eventList.count(), referenceList.count()); + m_modelManager->modelProxyCountUpdated(m_modelId, d->count(), referenceList.count()); } - d->computeAnimationCountLimit(); + if (lastStartTime != -1) + d->insert(lastStartTime, lastDuration, lastEvent); - qSort(d->eventList.begin(), d->eventList.end(), compareStartTimes); + d->computeAnimationCountLimit(); + d->computeNesting(); m_modelManager->modelProxyCountUpdated(m_modelId, 1, 1); @@ -204,12 +174,12 @@ bool PaintEventsModelProxy::isEmpty() const int PaintEventsModelProxy::count() const { - return d->eventList.count(); + return d->count(); } qint64 PaintEventsModelProxy::lastTimeMark() const { - return d->eventList.last().startTime + d->eventList.last().duration; + return d->lastEndTime(); } bool PaintEventsModelProxy::expanded(int ) const @@ -247,53 +217,17 @@ const QString PaintEventsModelProxy::categoryLabel(int categoryIndex) const int PaintEventsModelProxy::findFirstIndex(qint64 startTime) const { - return findFirstIndexNoParents(startTime); + return d->findFirstIndex(startTime); } int PaintEventsModelProxy::findFirstIndexNoParents(qint64 startTime) const { - if (d->eventList.isEmpty()) - return -1; - if (d->eventList.count() == 1 || d->eventList.first().startTime+d->eventList.first().duration >= startTime) - return 0; - else - if (d->eventList.last().startTime+d->eventList.last().duration <= startTime) - return -1; - - int fromIndex = 0; - int toIndex = d->eventList.count()-1; - while (toIndex - fromIndex > 1) { - int midIndex = (fromIndex + toIndex)/2; - if (d->eventList[midIndex].startTime + d->eventList[midIndex].duration < startTime) - fromIndex = midIndex; - else - toIndex = midIndex; - } - return toIndex; + return d->findFirstIndexNoParents(startTime); } int PaintEventsModelProxy::findLastIndex(qint64 endTime) const { - if (d->eventList.isEmpty()) - return -1; - if (d->eventList.first().startTime >= endTime) - return -1; - if (d->eventList.count() == 1) - return 0; - if (d->eventList.last().startTime <= endTime) - return d->eventList.count()-1; - - int fromIndex = 0; - int toIndex = d->eventList.count()-1; - while (toIndex - fromIndex > 1) { - int midIndex = (fromIndex + toIndex)/2; - if (d->eventList[midIndex].startTime < endTime) - fromIndex = midIndex; - else - toIndex = midIndex; - } - - return fromIndex; + return d->findLastIndex(endTime); } int PaintEventsModelProxy::getEventType(int index) const @@ -317,17 +251,17 @@ int PaintEventsModelProxy::getEventRow(int index) const qint64 PaintEventsModelProxy::getDuration(int index) const { - return d->eventList[index].duration; + return d->range(index).duration; } qint64 PaintEventsModelProxy::getStartTime(int index) const { - return d->eventList[index].startTime; + return d->range(index).start; } qint64 PaintEventsModelProxy::getEndTime(int index) const { - return d->eventList[index].startTime + d->eventList[index].duration; + return d->range(index).start + d->range(index).duration; } int PaintEventsModelProxy::getEventId(int index) const @@ -339,7 +273,7 @@ int PaintEventsModelProxy::getEventId(int index) const QColor PaintEventsModelProxy::getColor(int index) const { - double fpsFraction = d->eventList[index].framerate / 60.0; + double fpsFraction = d->range(index).framerate / 60.0; if (fpsFraction > 1.0) fpsFraction = 1.0; if (fpsFraction < 0.0) @@ -352,7 +286,7 @@ float PaintEventsModelProxy::getHeight(int index) const float scale = d->maxAnimationCount - d->minAnimationCount; float fraction = 1.0f; if (scale > 1) - fraction = (float)(d->eventList[index].animationcount - + fraction = (float)(d->range(index).animationcount - d->minAnimationCount) / scale; return fraction * 0.85f + 0.15f; @@ -388,14 +322,14 @@ void PaintEventsModelProxy::PaintEventsModelProxyPrivate::computeAnimationCountL { minAnimationCount = 1; maxAnimationCount = 1; - if (eventList.isEmpty()) + if (count() == 0) return; - for (int i=0; i < eventList.count(); i++) { - if (eventList[i].animationcount < minAnimationCount) - minAnimationCount = eventList[i].animationcount; - if (eventList[i].animationcount > maxAnimationCount) - maxAnimationCount = eventList[i].animationcount; + for (int i=0; i < count(); i++) { + if (range(i).animationcount < minAnimationCount) + minAnimationCount = range(i).animationcount; + else if (range(i).animationcount > maxAnimationCount) + maxAnimationCount = range(i).animationcount; } } @@ -414,21 +348,21 @@ const QVariantList PaintEventsModelProxy::getEventDetails(int index) const // duration { QVariantMap valuePair; - valuePair.insert(QCoreApplication::translate(trContext, "Duration:"), QVariant(d->displayTime(d->eventList[index].duration))); + valuePair.insert(QCoreApplication::translate(trContext, "Duration:"), QVariant(d->displayTime(d->range(index).duration))); result << valuePair; } // duration { QVariantMap valuePair; - valuePair.insert(QCoreApplication::translate(trContext, "Framerate:"), QVariant(QString::fromLatin1("%1 FPS").arg(d->eventList[index].framerate))); + valuePair.insert(QCoreApplication::translate(trContext, "Framerate:"), QVariant(QString::fromLatin1("%1 FPS").arg(d->range(index).framerate))); result << valuePair; } // duration { QVariantMap valuePair; - valuePair.insert(QCoreApplication::translate(trContext, "Animations:"), QVariant(QString::fromLatin1("%1").arg(d->eventList[index].animationcount))); + valuePair.insert(QCoreApplication::translate(trContext, "Animations:"), QVariant(QString::fromLatin1("%1").arg(d->range(index).animationcount))); result << valuePair; } diff --git a/src/plugins/qmlprofiler/qmlprofilerpainteventsmodelproxy.h b/src/plugins/qmlprofiler/qmlprofilerpainteventsmodelproxy.h index d77921e61f..9d362fc79b 100644 --- a/src/plugins/qmlprofiler/qmlprofilerpainteventsmodelproxy.h +++ b/src/plugins/qmlprofiler/qmlprofilerpainteventsmodelproxy.h @@ -56,8 +56,6 @@ class PaintEventsModelProxy : public AbstractTimelineModel public: struct QmlPaintEventData { - qint64 startTime; - qint64 duration; int framerate; int animationcount; }; @@ -70,8 +68,6 @@ public: QStringList categoryTitles() const; QString name() const; - const QVector<QmlPaintEventData> getData() const; - const QVector<QmlPaintEventData> getData(qint64 fromTime, qint64 toTime) const; void loadData(); Q_INVOKABLE int count() const; void clear(); @@ -108,8 +104,6 @@ public: private slots: bool eventAccepted(const QmlProfilerSimpleModel::QmlEventData &event) const; -protected slots: - void dataChanged(); private: class PaintEventsModelProxyPrivate; diff --git a/src/plugins/qmlprofiler/qmlprofilertimelinemodelproxy.cpp b/src/plugins/qmlprofiler/qmlprofilertimelinemodelproxy.cpp index ae9cc44684..0b03da6b51 100644 --- a/src/plugins/qmlprofiler/qmlprofilertimelinemodelproxy.cpp +++ b/src/plugins/qmlprofiler/qmlprofilertimelinemodelproxy.cpp @@ -30,6 +30,7 @@ #include "qmlprofilertimelinemodelproxy.h" #include "qmlprofilermodelmanager.h" #include "qmlprofilersimplemodel.h" +#include "sortedtimelinemodel.h" #include <QCoreApplication> #include <QVector> @@ -51,7 +52,7 @@ struct CategorySpan { bool empty; }; -class BasicTimelineModel::BasicTimelineModelPrivate +class BasicTimelineModel::BasicTimelineModelPrivate : public SortedTimelineModel<BasicTimelineModel::QmlRangeEventStartInstance> { public: BasicTimelineModelPrivate(BasicTimelineModel *qq) : q(qq) {} @@ -61,7 +62,6 @@ public: void prepare(); void computeNestingContracted(); void computeExpandedLevels(); - void buildEndTimeList(); void findBindingLoops(); void computeRowStarts(); @@ -69,8 +69,6 @@ public: QVector <BasicTimelineModel::QmlRangeEventData> eventDict; QVector <QString> eventHashes; - QVector <BasicTimelineModel::QmlRangeEventStartInstance> startTimeData; - QVector <BasicTimelineModel::QmlRangeEventEndInstance> endTimeData; QVector <CategorySpan> categorySpan; BasicTimelineModel *q; @@ -104,46 +102,16 @@ QString BasicTimelineModel::name() const return QLatin1String("BasicTimelineModel"); } -const QVector<BasicTimelineModel::QmlRangeEventStartInstance> BasicTimelineModel::getData() const -{ - return d->startTimeData; -} - -const QVector<BasicTimelineModel::QmlRangeEventStartInstance> BasicTimelineModel::getData(qint64 fromTime, qint64 toTime) const -{ - int fromIndex = findFirstIndex(fromTime); - int toIndex = findLastIndex(toTime); - if (fromIndex != -1 && toIndex > fromIndex) - return d->startTimeData.mid(fromIndex, toIndex - fromIndex + 1); - else - return QVector<BasicTimelineModel::QmlRangeEventStartInstance>(); -} - void BasicTimelineModel::clear() { + d->SortedTimelineModel::clear(); d->eventDict.clear(); d->eventHashes.clear(); - d->startTimeData.clear(); - d->endTimeData.clear(); d->categorySpan.clear(); m_modelManager->modelProxyCountUpdated(m_modelId, 0, 1); } -void BasicTimelineModel::dataChanged() -{ - if (m_modelManager->state() == QmlProfilerDataState::ProcessingData) - loadData(); - - if (m_modelManager->state() == QmlProfilerDataState::Empty) - clear(); - - emit stateChanged(); - emit dataAvailable(); - emit emptyChanged(); - emit expandedChanged(); -} - void BasicTimelineModel::BasicTimelineModelPrivate::prepare() { categorySpan.clear(); @@ -153,16 +121,6 @@ void BasicTimelineModel::BasicTimelineModelPrivate::prepare() } } -bool compareStartTimes(const BasicTimelineModel::QmlRangeEventStartInstance&t1, const BasicTimelineModel::QmlRangeEventStartInstance &t2) -{ - return t1.startTime < t2.startTime; -} - -bool compareEndTimes(const BasicTimelineModel::QmlRangeEventEndInstance &t1, const BasicTimelineModel::QmlRangeEventEndInstance &t2) -{ - return t1.endTime < t2.endTime; -} - bool BasicTimelineModel::eventAccepted(const QmlProfilerSimpleModel::QmlEventData &event) const { // only accept Qt4.x Painting events @@ -205,42 +163,30 @@ void BasicTimelineModel::loadData() } // store starttime-based instance - QmlRangeEventStartInstance eventStartInstance = { - event.startTime, - event.duration, - d->eventHashes.indexOf(eventHash), // event id - QmlDebug::Constants::QML_MIN_LEVEL, // displayRowExpanded; - QmlDebug::Constants::QML_MIN_LEVEL, // displayRowCollapsed; - 1, - -1 // bindingLoopHead - }; - d->startTimeData.append(eventStartInstance); - - m_modelManager->modelProxyCountUpdated(m_modelId, d->startTimeData.count(), eventList.count() * 7); + d->insert(event.startTime, event.duration, QmlRangeEventStartInstance(d->eventHashes.indexOf(eventHash))); + + m_modelManager->modelProxyCountUpdated(m_modelId, d->count(), eventList.count() * 6); } - qSort(d->startTimeData.begin(), d->startTimeData.end(), compareStartTimes); + m_modelManager->modelProxyCountUpdated(m_modelId, 2, 6); - m_modelManager->modelProxyCountUpdated(m_modelId, 2, 7); + // compute range nesting + d->computeNesting(); // compute nestingLevel - nonexpanded d->computeNestingContracted(); - m_modelManager->modelProxyCountUpdated(m_modelId, 3, 7); + m_modelManager->modelProxyCountUpdated(m_modelId, 3, 6); // compute nestingLevel - expanded d->computeExpandedLevels(); - m_modelManager->modelProxyCountUpdated(m_modelId, 4, 7); + m_modelManager->modelProxyCountUpdated(m_modelId, 4, 6); - // populate endtimelist - d->buildEndTimeList(); - - m_modelManager->modelProxyCountUpdated(m_modelId, 5, 7); d->findBindingLoops(); - m_modelManager->modelProxyCountUpdated(m_modelId, 6, 7); + m_modelManager->modelProxyCountUpdated(m_modelId, 5, 6); d->computeRowStarts(); @@ -252,15 +198,10 @@ void BasicTimelineModel::loadData() void BasicTimelineModel::BasicTimelineModelPrivate::computeNestingContracted() { int i; - int eventCount = startTimeData.count(); + int eventCount = count(); - QHash<int, qint64> endtimesPerLevel; QList<int> nestingLevels; QList< QHash<int, qint64> > endtimesPerNestingLevel; - int level = QmlDebug::Constants::QML_MIN_LEVEL; - endtimesPerLevel[QmlDebug::Constants::QML_MIN_LEVEL] = 0; - int lastBaseEventIndex = 0; - qint64 lastBaseEventEndTime = q->m_modelManager->traceTime()->startTime(); for (i = 0; i < QmlDebug::MaximumQmlEventType; i++) { nestingLevels << QmlDebug::Constants::QML_MIN_LEVEL; @@ -270,18 +211,9 @@ void BasicTimelineModel::BasicTimelineModelPrivate::computeNestingContracted() } for (i = 0; i < eventCount; i++) { - qint64 st = startTimeData[i].startTime; + qint64 st = ranges[i].start; int type = q->getEventType(i); - // general level - if (endtimesPerLevel[level] > st) { - level++; - } else { - while (level > QmlDebug::Constants::QML_MIN_LEVEL && endtimesPerLevel[level-1] <= st) - level--; - } - endtimesPerLevel[level] = st + startTimeData[i].duration; - // per type if (endtimesPerNestingLevel[type][nestingLevels[type]] > st) { nestingLevels[type]++; @@ -291,67 +223,42 @@ void BasicTimelineModel::BasicTimelineModelPrivate::computeNestingContracted() nestingLevels[type]--; } endtimesPerNestingLevel[type][nestingLevels[type]] = - st + startTimeData[i].duration; - - startTimeData[i].displayRowCollapsed = nestingLevels[type]; + st + ranges[i].duration; - if (level == QmlDebug::Constants::QML_MIN_LEVEL) { - if (lastBaseEventEndTime < startTimeData[i].startTime) { - lastBaseEventIndex = i; - lastBaseEventEndTime = startTimeData[i].startTime + startTimeData[i].duration; - } - } - startTimeData[i].baseEventIndex = lastBaseEventIndex; + ranges[i].displayRowCollapsed = nestingLevels[type]; } // nestingdepth for (i = 0; i < eventCount; i++) { int eventType = q->getEventType(i); categorySpan[eventType].empty = false; - if (categorySpan[eventType].contractedRows <= startTimeData[i].displayRowCollapsed) - categorySpan[eventType].contractedRows = startTimeData[i].displayRowCollapsed + 1; + if (categorySpan[eventType].contractedRows <= ranges[i].displayRowCollapsed) + categorySpan[eventType].contractedRows = ranges[i].displayRowCollapsed + 1; } } void BasicTimelineModel::BasicTimelineModelPrivate::computeExpandedLevels() { QHash<int, int> eventRow; - int eventCount = startTimeData.count(); + int eventCount = count(); for (int i = 0; i < eventCount; i++) { - int eventId = startTimeData[i].eventId; + int eventId = ranges[i].eventId; int eventType = eventDict[eventId].eventType; if (!eventRow.contains(eventId)) { categorySpan[eventType].empty = false; eventRow[eventId] = categorySpan[eventType].expandedRows++; } - startTimeData[i].displayRowExpanded = eventRow[eventId]; + ranges[i].displayRowExpanded = eventRow[eventId]; } } -void BasicTimelineModel::BasicTimelineModelPrivate::buildEndTimeList() -{ - endTimeData.clear(); - - int eventCount = startTimeData.count(); - for (int i = 0; i < eventCount; i++) { - BasicTimelineModel::QmlRangeEventEndInstance endInstance = { - i, - startTimeData[i].startTime + startTimeData[i].duration - }; - - endTimeData << endInstance; - } - - qSort(endTimeData.begin(), endTimeData.end(), compareEndTimes); -} - void BasicTimelineModel::BasicTimelineModelPrivate::findBindingLoops() { typedef QPair<QString, int> CallStackEntry; QStack<CallStackEntry> callStack; - for (int i = 0; i < startTimeData.size(); ++i) { - QmlRangeEventStartInstance *event = &startTimeData[i]; + for (int i = 0; i < count(); ++i) { + Range *event = &ranges[i]; BasicTimelineModel::QmlRangeEventData data = eventDict.at(event->eventId); @@ -363,14 +270,14 @@ void BasicTimelineModel::BasicTimelineModelPrivate::findBindingLoops() continue; const QString eventHash = eventHashes.at(event->eventId); - const QmlRangeEventStartInstance *potentialParent = callStack.isEmpty() - ? 0 : &startTimeData[callStack.top().second]; + const Range *potentialParent = callStack.isEmpty() + ? 0 : &ranges[callStack.top().second]; while (potentialParent - && !(potentialParent->startTime + potentialParent->duration > event->startTime)) { + && !(potentialParent->start + potentialParent->duration > event->start)) { callStack.pop(); potentialParent = callStack.isEmpty() ? 0 - : &startTimeData[callStack.top().second]; + : &ranges[callStack.top().second]; } // check whether event is already in stack @@ -406,12 +313,12 @@ bool BasicTimelineModel::isEmpty() const int BasicTimelineModel::count() const { - return d->startTimeData.count(); + return d->count(); } qint64 BasicTimelineModel::lastTimeMark() const { - return d->startTimeData.last().startTime + d->startTimeData.last().duration; + return d->lastEndTime(); } bool BasicTimelineModel::expanded(int category) const @@ -464,95 +371,22 @@ const QString BasicTimelineModel::categoryLabel(int categoryIndex) const int BasicTimelineModel::findFirstIndex(qint64 startTime) const { - int candidate = -1; - // in the "endtime" list, find the first event that ends after startTime - if (d->endTimeData.isEmpty()) - return -1; - if (d->endTimeData.count() == 1 || d->endTimeData.first().endTime >= startTime) - candidate = 0; - else - if (d->endTimeData.last().endTime <= startTime) - return -1; - - if (candidate == -1) - { - int fromIndex = 0; - int toIndex = d->endTimeData.count()-1; - while (toIndex - fromIndex > 1) { - int midIndex = (fromIndex + toIndex)/2; - if (d->endTimeData[midIndex].endTime < startTime) - fromIndex = midIndex; - else - toIndex = midIndex; - } - - candidate = toIndex; - } - - int eventIndex = d->endTimeData[candidate].startTimeIndex; - return d->startTimeData[eventIndex].baseEventIndex; - + return d->findFirstIndex(startTime); } int BasicTimelineModel::findFirstIndexNoParents(qint64 startTime) const { - int candidate = -1; - // in the "endtime" list, find the first event that ends after startTime - if (d->endTimeData.isEmpty()) - return -1; - if (d->endTimeData.count() == 1 || d->endTimeData.first().endTime >= startTime) - candidate = 0; - else - if (d->endTimeData.last().endTime <= startTime) - return -1; - - if (candidate == -1) { - int fromIndex = 0; - int toIndex = d->endTimeData.count()-1; - while (toIndex - fromIndex > 1) { - int midIndex = (fromIndex + toIndex)/2; - if (d->endTimeData[midIndex].endTime < startTime) - fromIndex = midIndex; - else - toIndex = midIndex; - } - - candidate = toIndex; - } - - int ndx = d->endTimeData[candidate].startTimeIndex; - - return ndx; + return d->findFirstIndexNoParents(startTime); } int BasicTimelineModel::findLastIndex(qint64 endTime) const { - // in the "starttime" list, find the last event that starts before endtime - if (d->startTimeData.isEmpty()) - return -1; - if (d->startTimeData.first().startTime >= endTime) - return -1; - if (d->startTimeData.count() == 1) - return 0; - if (d->startTimeData.last().startTime <= endTime) - return d->startTimeData.count()-1; - - int fromIndex = 0; - int toIndex = d->startTimeData.count()-1; - while (toIndex - fromIndex > 1) { - int midIndex = (fromIndex + toIndex)/2; - if (d->startTimeData[midIndex].startTime < endTime) - fromIndex = midIndex; - else - toIndex = midIndex; - } - - return fromIndex; + return d->findLastIndex(endTime); } int BasicTimelineModel::getEventType(int index) const { - return d->eventDict[d->startTimeData[index].eventId].eventType; + return d->eventDict[d->range(index).eventId].eventType; } int BasicTimelineModel::getEventCategory(int index) const @@ -567,34 +401,34 @@ int BasicTimelineModel::getEventCategory(int index) const int BasicTimelineModel::getEventRow(int index) const { if (d->categorySpan[getEventType(index)].expanded) - return d->startTimeData[index].displayRowExpanded + d->categorySpan[getEventType(index)].rowStart; + return d->range(index).displayRowExpanded + d->categorySpan[getEventType(index)].rowStart; else - return d->startTimeData[index].displayRowCollapsed + d->categorySpan[getEventType(index)].rowStart; + return d->range(index).displayRowCollapsed + d->categorySpan[getEventType(index)].rowStart; } qint64 BasicTimelineModel::getDuration(int index) const { - return d->startTimeData[index].duration; + return d->range(index).duration; } qint64 BasicTimelineModel::getStartTime(int index) const { - return d->startTimeData[index].startTime; + return d->range(index).start; } qint64 BasicTimelineModel::getEndTime(int index) const { - return d->startTimeData[index].startTime + d->startTimeData[index].duration; + return d->range(index).start + d->range(index).duration; } int BasicTimelineModel::getEventId(int index) const { - return d->startTimeData[index].eventId; + return d->range(index).eventId; } int BasicTimelineModel::getBindingLoopDest(int index) const { - return d->startTimeData[index].bindingLoopHead; + return d->range(index).bindingLoopHead; } QColor BasicTimelineModel::getColor(int index) const @@ -655,7 +489,7 @@ const QVariantList BasicTimelineModel::getEventDetails(int index) const // duration { QVariantMap valuePair; - valuePair.insert(QCoreApplication::translate(trContext, "Duration:"), QVariant(d->displayTime(d->startTimeData[index].duration))); + valuePair.insert(QCoreApplication::translate(trContext, "Duration:"), QVariant(d->displayTime(d->range(index).duration))); result << valuePair; } diff --git a/src/plugins/qmlprofiler/qmlprofilertimelinemodelproxy.h b/src/plugins/qmlprofiler/qmlprofilertimelinemodelproxy.h index 973376ce32..cf13b94959 100644 --- a/src/plugins/qmlprofiler/qmlprofilertimelinemodelproxy.h +++ b/src/plugins/qmlprofiler/qmlprofilertimelinemodelproxy.h @@ -65,22 +65,20 @@ public: }; struct QmlRangeEventStartInstance { - qint64 startTime; - qint64 duration; + QmlRangeEventStartInstance(int eventId = -1) : + eventId(eventId), + displayRowExpanded(QmlDebug::Constants::QML_MIN_LEVEL), + displayRowCollapsed(QmlDebug::Constants::QML_MIN_LEVEL), + bindingLoopHead(-1) {} + int eventId; // not-expanded, per type int displayRowExpanded; int displayRowCollapsed; - int baseEventIndex; // used by findfirstindex int bindingLoopHead; }; - struct QmlRangeEventEndInstance { - int startTimeIndex; - qint64 endTime; - }; - BasicTimelineModel(QObject *parent = 0); ~BasicTimelineModel(); @@ -89,8 +87,6 @@ public: QStringList categoryTitles() const; QString name() const; - const QVector<QmlRangeEventStartInstance> getData() const; - const QVector<QmlRangeEventStartInstance> getData(qint64 fromTime, qint64 toTime) const; void loadData(); Q_INVOKABLE int count() const; void clear(); @@ -131,8 +127,6 @@ public: private slots: bool eventAccepted(const QmlProfilerSimpleModel::QmlEventData &event) const; -protected slots: - void dataChanged(); private: class BasicTimelineModelPrivate; diff --git a/src/plugins/qmlprofiler/qmlprofilertool.cpp b/src/plugins/qmlprofiler/qmlprofilertool.cpp index 98b11a4244..5d0db79bb0 100644 --- a/src/plugins/qmlprofiler/qmlprofilertool.cpp +++ b/src/plugins/qmlprofiler/qmlprofilertool.cpp @@ -41,9 +41,6 @@ #include <analyzerbase/analyzermanager.h> #include <analyzerbase/analyzerruncontrol.h> -#include "canvas/qdeclarativecontext2d_p.h" -#include "canvas/qmlprofilercanvas.h" - #include <utils/fancymainwindow.h> #include <utils/fileinprojectfinder.h> #include <utils/qtcassert.h> @@ -119,9 +116,6 @@ QmlProfilerTool::QmlProfilerTool(QObject *parent) d->m_profilerState = 0; d->m_viewContainer = 0; - qmlRegisterType<QmlProfilerCanvas>("Monitor", 1, 0, "Canvas2D"); - qmlRegisterType<Context2D>(); - qmlRegisterType<CanvasGradient>(); qmlRegisterType<TimelineRenderer>("Monitor", 1, 0,"TimelineRenderer"); d->m_profilerState = new QmlProfilerStateManager(this); @@ -528,6 +522,8 @@ void QmlProfilerTool::profilerDataModelStateChanged() { switch (d->m_profilerModelManager->state()) { case QmlProfilerDataState::Empty : + break; + case QmlProfilerDataState::ClearingData : clearDisplay(); break; case QmlProfilerDataState::AcquiringData : diff --git a/src/plugins/qmlprofiler/qmlprofilertraceview.cpp b/src/plugins/qmlprofiler/qmlprofilertraceview.cpp index 54a6084ca0..15734d2b59 100644 --- a/src/plugins/qmlprofiler/qmlprofilertraceview.cpp +++ b/src/plugins/qmlprofiler/qmlprofilertraceview.cpp @@ -474,7 +474,8 @@ void QmlProfilerTraceView::setAppKilled() void QmlProfilerTraceView::profilerDataModelStateChanged() { switch (d->m_modelManager->state()) { - case QmlProfilerDataState::Empty: + case QmlProfilerDataState::Empty: break; + case QmlProfilerDataState::ClearingData: emit enableToolbar(false); break; case QmlProfilerDataState::AcquiringData: break; diff --git a/src/plugins/qmlprofiler/qmlprofilerviewmanager.cpp b/src/plugins/qmlprofiler/qmlprofilerviewmanager.cpp index aadd11fce9..ffb9389711 100644 --- a/src/plugins/qmlprofiler/qmlprofilerviewmanager.cpp +++ b/src/plugins/qmlprofiler/qmlprofilerviewmanager.cpp @@ -112,6 +112,8 @@ void QmlProfilerViewManager::createViews() d->v8profilerView = new QV8ProfilerEventsWidget(mw, d->profilerTool, this, d->profilerModelManager); + connect(d->v8profilerView, SIGNAL(gotoSourceLocation(QString,int,int)), this, + SIGNAL(gotoSourceLocation(QString,int,int))); connect(d->traceView, SIGNAL(gotoSourceLocation(QString,int,int)), d->v8profilerView, SLOT(selectBySourceLocation(QString,int,int))); connect(d->v8profilerView, SIGNAL(gotoSourceLocation(QString,int,int)), diff --git a/src/plugins/qmlprofiler/qv8profilerdatamodel.cpp b/src/plugins/qmlprofiler/qv8profilerdatamodel.cpp index 68e05039db..d6899b65a5 100644 --- a/src/plugins/qmlprofiler/qv8profilerdatamodel.cpp +++ b/src/plugins/qmlprofiler/qv8profilerdatamodel.cpp @@ -138,7 +138,7 @@ bool QV8ProfilerDataModel::isEmpty() const QV8EventData *QV8ProfilerDataModel::v8EventDescription(int eventId) const { - foreach (QV8EventData *event, d->v8EventHash.values()) { + foreach (QV8EventData *event, d->v8EventHash) { if (event->eventId == eventId) return event; } @@ -235,7 +235,7 @@ void QV8ProfilerDataModel::QV8ProfilerDataModelPrivate::collectV8Statistics() if (!v8EventHash.isEmpty()) { double totalTimes = v8MeasuredTime; double selfTimes = 0; - foreach (QV8EventData *v8event, v8EventHash.values()) { + foreach (const QV8EventData *v8event, v8EventHash) { selfTimes += v8event->selfTime; } @@ -261,7 +261,7 @@ void QV8ProfilerDataModel::QV8ProfilerDataModelPrivate::collectV8Statistics() *v8EventHash[rootEventHash] = v8RootEvent; } - foreach (QV8EventData *v8event, v8EventHash.values()) { + foreach (QV8EventData *v8event, v8EventHash) { v8event->totalPercent = v8event->totalTime * 100.0 / totalTimes; v8event->SelfTimeInPercent = v8event->selfTime * 100.0 / selfTimes; } @@ -300,7 +300,7 @@ void QV8ProfilerDataModel::save(QXmlStreamWriter &stream) { stream.writeStartElement(QLatin1String("v8profile")); // v8 profiler output stream.writeAttribute(QLatin1String("totalTime"), QString::number(d->v8MeasuredTime)); - foreach (QV8EventData *v8event, d->v8EventHash.values()) { + foreach (const QV8EventData *v8event, d->v8EventHash) { stream.writeStartElement(QLatin1String("event")); stream.writeAttribute(QLatin1String("index"), QString::number( @@ -319,7 +319,7 @@ void QV8ProfilerDataModel::save(QXmlStreamWriter &stream) QStringList childrenIndexes; QStringList childrenTimes; QStringList parentTimes; - foreach (QV8EventSub *v8child, v8event->childrenHash.values()) { + foreach (const QV8EventSub *v8child, v8event->childrenHash) { childrenIndexes << QString::number(v8child->reference->eventId); childrenTimes << QString::number(v8child->totalTime); parentTimes << QString::number(v8child->totalTime); @@ -468,7 +468,7 @@ void QV8ProfilerDataModel::load(QXmlStreamReader &stream) } } // store v8 events - foreach (QV8EventData *storedV8Event, v8eventBuffer.values()) { + foreach (QV8EventData *storedV8Event, v8eventBuffer) { storedV8Event->eventHashStr = getHashStringForV8Event( storedV8Event->displayName, storedV8Event->functionName); diff --git a/src/plugins/qmlprofiler/sortedtimelinemodel.cpp b/src/plugins/qmlprofiler/sortedtimelinemodel.cpp new file mode 100644 index 0000000000..567c44981f --- /dev/null +++ b/src/plugins/qmlprofiler/sortedtimelinemodel.cpp @@ -0,0 +1,110 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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. +** +****************************************************************************/ + +/*! + \class QmlProfiler::SortedTimelineModel + \brief Sorted model for timeline data + + The SortedTimelineModel lets you keep any kind of range data sorted by + both start and end times, so that visible ranges can easily be computed. +*/ + +/*! + \fn SortedTimelineModel::clear() + Clears the ranges and their end times. +*/ + +/*! + \fn int SortedTimelineModel::count() const + Returns the number of ranges in the model. +*/ + +/*! + \fn qint64 SortedTimelineModel::firstStartTime() const + Returns the begin of the first range in the model. +*/ + +/*! + \fn qint64 SortedTimelineModel::lastEndTime() const + Returns the end of the last range in the model. +*/ + +/*! + \fn const SortedTimelineModel<Data>::Range &SortedTimelineModel::range(int index) const + Returns the range data at the specified index. +*/ + +/*! + \fn Data &SortedTimelineModel::data(int index) + Returns modifiable user data for the range at the specified index. +*/ + +/*! + \fn int SortedTimelineModel::insert(qint64 startTime, qint64 duration, const Data &item) + Inserts the given data at the given time position and returns its index. +*/ + +/*! + \fn int SortedTimelineModel::insertStart(qint64 startTime, const Data &item) + Inserts the given data as range start at the given time position and + returns its index. The range end isn't set. +*/ + +/*! + \fn int SortedTimelineModel::insertEnd(int index, qint64 duration) + Adds a range end for the given start index. +*/ + +/*! + \fn int SortedTimelineModel::findFirstIndexNoParents(qint64 startTime) const + Looks up the first range with an end time greater than the given time and + returns its index. If no such range is found it returns -1. +*/ + +/*! + \fn int SortedTimelineModel::findFirstIndex(qint64 startTime) const + Looks up the first range with an end time greater than the given time and + returns its parent's index. If no such range is found it returns -1. If there + is no parent it returns the found range's index. The parent of a range is the + range with the lowest start time that completely covers the child range. + "Completely covers" means: + parent.startTime <= child.startTime && parent.endTime >= child.endTime +*/ + +/*! + \fn int SortedTimelineModel::findLastIndex(qint64 endTime) const + Looks up the last range with a start time smaller than the given time and + returns its index. If no such range is found it returns -1. +*/ + +/*! + \fn void computeNesting() + Compute all ranges' parents. + \sa findFirstIndex +*/ diff --git a/src/plugins/qmlprofiler/sortedtimelinemodel.h b/src/plugins/qmlprofiler/sortedtimelinemodel.h new file mode 100644 index 0000000000..80f78f2fe2 --- /dev/null +++ b/src/plugins/qmlprofiler/sortedtimelinemodel.h @@ -0,0 +1,187 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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. +** +****************************************************************************/ + +#ifndef SORTEDTIMELINEMODEL_H +#define SORTEDTIMELINEMODEL_H + +#include <QVector> +#include <QLinkedList> + +namespace QmlProfiler { + +template<class Data> +class SortedTimelineModel { +public: + struct Range : public Data { + Range() : Data(), start(-1), duration(-1), parent(-1) {} + Range(qint64 start, qint64 duration, const Data &item) : + Data(item), start(start), duration(duration), parent(-1) {} + qint64 start; + qint64 duration; + int parent; + inline qint64 timestamp() const {return start;} + }; + + struct RangeEnd { + RangeEnd() : startIndex(-1), end(-1) {} + RangeEnd(int startIndex, qint64 end) : + startIndex(startIndex), end(end) {} + int startIndex; + qint64 end; + inline qint64 timestamp() const {return end;} + }; + + void clear() + { + ranges.clear(); + endTimes.clear(); + } + + inline int count() const { return ranges.count(); } + + inline qint64 lastEndTime() const { return endTimes.last().end; } + inline qint64 firstStartTime() const { return ranges.first().start; } + + inline const Range &range(int index) { return ranges[index]; } + inline Data &data(int index) { return ranges[index]; } + + inline int insert(qint64 startTime, qint64 duration, const Data &item) + { + /* Doing insert-sort here is preferable as most of the time the times will actually be + * presorted in the right way. So usually this will just result in appending. */ + int index = insertSorted(ranges, Range(startTime, duration, item)); + insertSorted(endTimes, RangeEnd(index, startTime + duration)); + return index; + } + + inline int insertStart(qint64 startTime, const Data &item) + { + return insertSorted(ranges, Range(startTime, 0, item)); + } + + inline void insertEnd(int index, qint64 duration) + { + ranges[index].duration = duration; + insertSorted(endTimes, RangeEnd(index, ranges[index].start + duration)); + } + + inline int findFirstIndex(qint64 startTime) const + { + int index = findFirstIndexNoParents(startTime); + if (index == -1) + return -1; + int parent = ranges[index].parent; + return parent == -1 ? index : parent; + } + + inline int findFirstIndexNoParents(qint64 startTime) const + { + // in the "endtime" list, find the first event that ends after startTime + if (endTimes.isEmpty()) + return -1; + if (endTimes.count() == 1 || endTimes.first().end >= startTime) + return endTimes.first().startIndex; + if (endTimes.last().end <= startTime) + return -1; + + return endTimes[lowerBound(endTimes, startTime) + 1].startIndex; + } + + inline int findLastIndex(qint64 endTime) const + { + // in the "starttime" list, find the last event that starts before endtime + if (ranges.isEmpty() || ranges.first().start >= endTime) + return -1; + if (ranges.count() == 1) + return 0; + if (ranges.last().start <= endTime) + return ranges.count() - 1; + + return lowerBound(ranges, endTime); + } + + inline void computeNesting() + { + QLinkedList<int> parents; + for (int range = 0; range != count(); ++range) { + Range ¤t = ranges[range]; + for (QLinkedList<int>::iterator parent = parents.begin(); parent != parents.end();) { + qint64 parentEnd = ranges[*parent].start + ranges[*parent].duration; + if (parentEnd < current.start) { + parent = parents.erase(parent); + } else if (parentEnd >= current.start + current.duration) { + current.parent = *parent; + break; + } else { + ++parent; + } + } + parents.append(range); + } + } + +protected: + template<typename RangeDelimiter> + static inline int insertSorted(QVector<RangeDelimiter> &container, const RangeDelimiter &item) + { + for (int i = container.count();;) { + if (i == 0) { + container.prepend(item); + return 0; + } + if (container[--i].timestamp() <= item.timestamp()) { + container.insert(++i, item); + return i; + } + } + } + + template<typename RangeDelimiter> + static inline int lowerBound(const QVector<RangeDelimiter> container, qint64 time) + { + int fromIndex = 0; + int toIndex = container.count() - 1; + while (toIndex - fromIndex > 1) { + int midIndex = (fromIndex + toIndex)/2; + if (container[midIndex].timestamp() < time) + fromIndex = midIndex; + else + toIndex = midIndex; + } + + return fromIndex; + } + + QVector<Range> ranges; + QVector<RangeEnd> endTimes; +}; + +} + +#endif diff --git a/src/plugins/qmlprojectmanager/qmlapp.cpp b/src/plugins/qmlprojectmanager/qmlapp.cpp index 442b3c2e29..57a58b6a7a 100644 --- a/src/plugins/qmlprojectmanager/qmlapp.cpp +++ b/src/plugins/qmlprojectmanager/qmlapp.cpp @@ -154,7 +154,6 @@ static bool parseTemplateXml(QXmlStreamReader &reader, TemplateInfo *info) static const QLatin1String tag_template("template"); static const QLatin1String tag_displayName("displayname"); static const QLatin1String tag_description("description"); - static const QLatin1String attribute_id("id"); static const QLatin1String attribute_featuresRequired("featuresRequired"); static const QLatin1String attribute_openEditor("openeditor"); static const QLatin1String attribute_priority("priority"); @@ -169,9 +168,6 @@ static bool parseTemplateXml(QXmlStreamReader &reader, TemplateInfo *info) if (reader.attributes().hasAttribute(attribute_priority)) info->priority = reader.attributes().value(attribute_priority).toString(); - if (reader.attributes().hasAttribute(attribute_id)) - info->wizardId = reader.attributes().value(attribute_id).toString(); - if (reader.attributes().hasAttribute(attribute_featuresRequired)) info->featuresRequired = reader.attributes().value(attribute_featuresRequired).toString(); diff --git a/src/plugins/qmlprojectmanager/qmlapp.h b/src/plugins/qmlprojectmanager/qmlapp.h index e19a6d4183..b023caeb48 100644 --- a/src/plugins/qmlprojectmanager/qmlapp.h +++ b/src/plugins/qmlprojectmanager/qmlapp.h @@ -47,7 +47,6 @@ public: QString displayName; QString description; QString openFile; - QString wizardId; QString featuresRequired; QString priority; }; diff --git a/src/plugins/qmlprojectmanager/qmlproject.cpp b/src/plugins/qmlprojectmanager/qmlproject.cpp index b025fd55f0..76a07ce9de 100644 --- a/src/plugins/qmlprojectmanager/qmlproject.cpp +++ b/src/plugins/qmlprojectmanager/qmlproject.cpp @@ -111,6 +111,7 @@ QmlProject::QmlProject(Internal::Manager *manager, const QString &fileName) m_modelManager(QmlJS::ModelManagerInterface::instance()), m_activeTarget(0) { + setId("QmlProjectManager.QmlProject"); setProjectContext(Context(QmlProjectManager::Constants::PROJECTCONTEXT)); setProjectLanguages(Context(ProjectExplorer::Constants::LANG_QMLJS)); @@ -324,11 +325,6 @@ QString QmlProject::displayName() const return m_projectName; } -Id QmlProject::id() const -{ - return "QmlProjectManager.QmlProject"; -} - IDocument *QmlProject::document() const { return m_file; diff --git a/src/plugins/qmlprojectmanager/qmlproject.h b/src/plugins/qmlprojectmanager/qmlproject.h index cc9563bc9f..87626091fa 100644 --- a/src/plugins/qmlprojectmanager/qmlproject.h +++ b/src/plugins/qmlprojectmanager/qmlproject.h @@ -61,7 +61,6 @@ public: QString filesFileName() const; QString displayName() const; - Core::Id id() const; Core::IDocument *document() const; ProjectExplorer::IProjectManager *projectManager() const; diff --git a/src/plugins/qmlprojectmanager/qmlprojectnodes.cpp b/src/plugins/qmlprojectmanager/qmlprojectnodes.cpp index 493545ebb6..3e20f653e7 100644 --- a/src/plugins/qmlprojectmanager/qmlprojectnodes.cpp +++ b/src/plugins/qmlprojectmanager/qmlprojectnodes.cpp @@ -176,7 +176,11 @@ QList<ProjectExplorer::ProjectNode::ProjectAction> QmlProjectNode::supportedActi QList<ProjectAction> actions; actions.append(AddNewFile); actions.append(EraseFile); - actions.append(Rename); + if (node->nodeType() == ProjectExplorer::FileNodeType) { + ProjectExplorer::FileNode *fileNode = static_cast<ProjectExplorer::FileNode *>(node); + if (fileNode->fileType() != ProjectExplorer::ProjectFileType) + actions.append(Rename); + } return actions; } diff --git a/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp b/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp index 6f5b3b3e92..ec0f870ca3 100644 --- a/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp +++ b/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp @@ -140,16 +140,6 @@ QString QmlProjectRunConfiguration::commandLineArguments() const return args; } -QString QmlProjectRunConfiguration::dumperLibrary() const -{ - return QString(); -} - -QStringList QmlProjectRunConfiguration::dumperLibraryLocations() const -{ - return QStringList(); -} - QString QmlProjectRunConfiguration::workingDirectory() const { QFileInfo projectFile(target()->project()->projectFilePath()); diff --git a/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.h b/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.h index c83a1af18a..489d6c4ac2 100644 --- a/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.h +++ b/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.h @@ -65,8 +65,6 @@ public: QString executable() const; RunMode runMode() const; QString commandLineArguments() const; - QString dumperLibrary() const; - QStringList dumperLibraryLocations() const; QString workingDirectory() const; QtSupport::BaseQtVersion *qtVersion() const; diff --git a/src/plugins/qnx/bardescriptoreditor.cpp b/src/plugins/qnx/bardescriptoreditor.cpp index fea8c591ab..a52c98c910 100644 --- a/src/plugins/qnx/bardescriptoreditor.cpp +++ b/src/plugins/qnx/bardescriptoreditor.cpp @@ -38,11 +38,15 @@ #include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/task.h> #include <projectexplorer/taskhub.h> -#include <utils/qtcassert.h> #include <texteditor/texteditorconstants.h> #include <texteditor/basetexteditor.h> +#include <texteditor/tabsettings.h> +#include <utils/linecolumnlabel.h> +#include <utils/qtcassert.h> #include <QAction> +#include <QStyle> +#include <QTextBlock> #include <QToolBar> using namespace ProjectExplorer; @@ -83,6 +87,17 @@ BarDescriptorEditor::BarDescriptorEditor(BarDescriptorEditorWidget *editorWidget generalAction->setChecked(true); + m_cursorPositionLabel = new Utils::LineColumnLabel; + const int spacing = editorWidget->style()->pixelMetric(QStyle::PM_LayoutHorizontalSpacing) / 2; + m_cursorPositionLabel->setContentsMargins(spacing, 0, spacing, 0); + + QWidget *spacer = new QWidget; + spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + m_toolBar->addWidget(spacer); + + m_cursorPositionAction = m_toolBar->addWidget(m_cursorPositionLabel); + connect(editorWidget->sourceWidget(), SIGNAL(cursorPositionChanged()), this, SLOT(updateCursorPosition())); + setContext(Core::Context(Constants::QNX_BAR_DESCRIPTOR_EDITOR_CONTEXT, TextEditor::Constants::C_TEXTEDITOR)); } @@ -133,6 +148,7 @@ void BarDescriptorEditor::setActivePage(BarDescriptorEditor::EditorPage page) if (page == Source) { editorWidget->setXmlSource(m_file->xmlSource()); + updateCursorPosition(); } else if (prevPage == Source) { TaskHub::clearTasks(Constants::QNX_TASK_CATEGORY_BARDESCRIPTOR); QString errorMsg; @@ -150,8 +166,24 @@ void BarDescriptorEditor::setActivePage(BarDescriptorEditor::EditorPage page) } } + m_cursorPositionAction->setVisible(page == Source); editorWidget->setCurrentIndex(page); } +void BarDescriptorEditor::updateCursorPosition() +{ + BarDescriptorEditorWidget *editorWidget = qobject_cast<BarDescriptorEditorWidget *>(widget()); + QTC_ASSERT(editorWidget, return); + + const QTextCursor cursor = editorWidget->sourceWidget()->textCursor(); + const QTextBlock block = cursor.block(); + const int line = block.blockNumber() + 1; + const int column = cursor.position() - block.position(); + m_cursorPositionLabel->setText(tr("Line: %1, Col: %2").arg(line).arg(editorWidget->sourceWidget()->tabSettings().columnAt(block.text(), column)+1), + tr("Line: 9999, Col: 999")); + if (!block.isVisible()) + editorWidget->sourceWidget()->ensureCursorVisible(); +} + } // namespace Internal } // namespace Qnx diff --git a/src/plugins/qnx/bardescriptoreditor.h b/src/plugins/qnx/bardescriptoreditor.h index 3ecb63b3f0..abe319d88b 100644 --- a/src/plugins/qnx/bardescriptoreditor.h +++ b/src/plugins/qnx/bardescriptoreditor.h @@ -39,6 +39,10 @@ class QActionGroup; class QToolBar; QT_END_NAMESPACE +namespace Utils { +class LineColumnLabel; +} + namespace Qnx { namespace Internal { @@ -67,6 +71,7 @@ public: private slots: void changeEditorPage(QAction *action); + void updateCursorPosition(); private: void setActivePage(EditorPage page); @@ -75,6 +80,8 @@ private: QToolBar *m_toolBar; QActionGroup *m_actionGroup; + Utils::LineColumnLabel *m_cursorPositionLabel; + QAction *m_cursorPositionAction; }; } // namespace Internal diff --git a/src/plugins/qnx/bardescriptoreditorfactory.cpp b/src/plugins/qnx/bardescriptoreditorfactory.cpp index 1a8457b136..befa564bf0 100644 --- a/src/plugins/qnx/bardescriptoreditorfactory.cpp +++ b/src/plugins/qnx/bardescriptoreditorfactory.cpp @@ -44,8 +44,8 @@ using namespace Qnx::Internal; class BarDescriptorActionHandler : public TextEditor::TextEditorActionHandler { public: - BarDescriptorActionHandler() - : TextEditor::TextEditorActionHandler(Constants::QNX_BAR_DESCRIPTOR_EDITOR_CONTEXT) + BarDescriptorActionHandler(QObject *parent) + : TextEditor::TextEditorActionHandler(parent, Constants::QNX_BAR_DESCRIPTOR_EDITOR_CONTEXT) { } protected: @@ -58,20 +58,15 @@ protected: BarDescriptorEditorFactory::BarDescriptorEditorFactory(QObject *parent) : Core::IEditorFactory(parent) - , m_actionHandler(new BarDescriptorActionHandler) { setId(Constants::QNX_BAR_DESCRIPTOR_EDITOR_ID); setDisplayName(tr("Bar descriptor editor")); addMimeType(Constants::QNX_BAR_DESCRIPTOR_MIME_TYPE); -} - -BarDescriptorEditorFactory::~BarDescriptorEditorFactory() -{ - delete m_actionHandler; + new BarDescriptorActionHandler(this); } Core::IEditor *BarDescriptorEditorFactory::createEditor(QWidget *parent) { - BarDescriptorEditorWidget *editorWidget = new BarDescriptorEditorWidget(parent, m_actionHandler); + BarDescriptorEditorWidget *editorWidget = new BarDescriptorEditorWidget(parent); return editorWidget->editor(); } diff --git a/src/plugins/qnx/bardescriptoreditorfactory.h b/src/plugins/qnx/bardescriptoreditorfactory.h index 97d8c80550..9d4621ebc7 100644 --- a/src/plugins/qnx/bardescriptoreditorfactory.h +++ b/src/plugins/qnx/bardescriptoreditorfactory.h @@ -34,10 +34,6 @@ #include <coreplugin/editormanager/ieditorfactory.h> -namespace TextEditor { -class TextEditorActionHandler; -} - namespace Qnx { namespace Internal { @@ -47,12 +43,8 @@ class BarDescriptorEditorFactory : public Core::IEditorFactory public: explicit BarDescriptorEditorFactory(QObject *parent = 0); - ~BarDescriptorEditorFactory(); Core::IEditor *createEditor(QWidget *parent); - -private: - TextEditor::TextEditorActionHandler *m_actionHandler; }; } // namespace Internal diff --git a/src/plugins/qnx/bardescriptoreditorwidget.cpp b/src/plugins/qnx/bardescriptoreditorwidget.cpp index 1ea571ac76..88b51ed128 100644 --- a/src/plugins/qnx/bardescriptoreditorwidget.cpp +++ b/src/plugins/qnx/bardescriptoreditorwidget.cpp @@ -45,18 +45,15 @@ #include <projectexplorer/iprojectproperties.h> #include <projectexplorer/projectwindow.h> #include <texteditor/plaintexteditor.h> -#include <texteditor/texteditoractionhandler.h> #include <texteditor/texteditorsettings.h> #include <texteditor/texteditorconstants.h> using namespace Qnx; using namespace Qnx::Internal; -BarDescriptorEditorWidget::BarDescriptorEditorWidget( - QWidget *parent, TextEditor::TextEditorActionHandler *handler) +BarDescriptorEditorWidget::BarDescriptorEditorWidget(QWidget *parent) : QStackedWidget(parent) , m_editor(0) - , m_handler(handler) , m_dirty(false) { Core::IContext *myContext = new Core::IContext(this); @@ -161,7 +158,6 @@ void BarDescriptorEditorWidget::initSourcePage() addWidget(m_xmlSourceWidget); TextEditor::TextEditorSettings::initializeEditor(m_xmlSourceWidget); - m_handler->setupActions(m_xmlSourceWidget); m_xmlSourceWidget->configure(QLatin1String(Constants::QNX_BAR_DESCRIPTOR_MIME_TYPE)); connect(m_xmlSourceWidget, SIGNAL(textChanged()), this, SLOT(setDirty())); } @@ -224,7 +220,7 @@ TextEditor::BaseTextEditorWidget *BarDescriptorEditorWidget::sourceWidget() cons void BarDescriptorEditorWidget::setFilePath(const QString &filePath) { - Core::IDocument *doc = m_xmlSourceWidget->editorDocument(); + Core::IDocument *doc = m_xmlSourceWidget->baseTextDocument(); if (doc) { doc->setFilePath(filePath); // setFilePath() call leads to a textChanged() signal emitted diff --git a/src/plugins/qnx/bardescriptoreditorwidget.h b/src/plugins/qnx/bardescriptoreditorwidget.h index 00677c7332..ab089bbf05 100644 --- a/src/plugins/qnx/bardescriptoreditorwidget.h +++ b/src/plugins/qnx/bardescriptoreditorwidget.h @@ -46,7 +46,6 @@ class PanelsWidget; namespace TextEditor { class PlainTextEditorWidget; -class TextEditorActionHandler; class BaseTextEditorWidget; } @@ -67,7 +66,7 @@ class BarDescriptorEditorWidget : public QStackedWidget Q_OBJECT public: - explicit BarDescriptorEditorWidget(QWidget *parent, TextEditor::TextEditorActionHandler *handler); + explicit BarDescriptorEditorWidget(QWidget *parent); Core::IEditor *editor() const; @@ -107,7 +106,6 @@ private: mutable Core::IEditor *m_editor; - TextEditor::TextEditorActionHandler *m_handler; bool m_dirty; // New UI diff --git a/src/plugins/qnx/blackberrycheckdebugtokenstep.cpp b/src/plugins/qnx/blackberrycheckdebugtokenstep.cpp new file mode 100644 index 0000000000..3094913a8d --- /dev/null +++ b/src/plugins/qnx/blackberrycheckdebugtokenstep.cpp @@ -0,0 +1,156 @@ +/************************************************************************** +** +** Copyright (C) 2013 BlackBerry Limited. All rights reserved. +** +** Contact: BlackBerry (qt@blackberry.com) +** Contact: KDAB (info@kdab.com) +** +** 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 "blackberrycheckdebugtokenstep.h" + +#include "blackberrycheckdebugtokenstepconfigwidget.h" +#include "blackberrydeviceinformation.h" +#include "qnxconstants.h" + +#include <projectexplorer/buildconfiguration.h> +#include <projectexplorer/projectexplorerconstants.h> +#include <projectexplorer/target.h> +#include <projectexplorer/task.h> +#include <ssh/sshconnection.h> + +#include <qeventloop.h> + +using namespace Qnx; +using namespace Qnx::Internal; + +BlackBerryCheckDebugTokenStep::BlackBerryCheckDebugTokenStep(ProjectExplorer::BuildStepList *bsl) : + ProjectExplorer::BuildStep(bsl, Core::Id(Constants::QNX_CHECK_DEBUG_TOKEN_BS_ID)) + , m_deviceInfo(0) + , m_eventLoop(0) +{ + setDisplayName(tr("Check Debug Token")); +} + +BlackBerryCheckDebugTokenStep::BlackBerryCheckDebugTokenStep(ProjectExplorer::BuildStepList *bsl, BlackBerryCheckDebugTokenStep *bs) : + ProjectExplorer::BuildStep(bsl, bs) + , m_deviceInfo(0) + , m_eventLoop(0) +{ + setDisplayName(tr("Check Debug Token")); +} + +void BlackBerryCheckDebugTokenStep::checkDeviceInfo(int status) +{ + // Skip debug token check for internal non secure devices and simulators + if (m_deviceInfo->isProductionDevice() && !m_deviceInfo->isSimulator()) { + if (status != BlackBerryDeviceInformation::Success) { + switch (status) { + case BlackBerryDeviceInformation::AuthenticationFailed: + raiseError(tr("Authentication failed.")); + break; + case BlackBerryDeviceInformation::NoRouteToHost: + raiseError(tr("Cannot connect to device.")); + break; + case BlackBerryDeviceInformation::DevelopmentModeDisabled: + raiseError(tr("Device is not in the development mode.")); + break; + case BlackBerryDeviceInformation::InferiorProcessTimedOut: + raiseError(tr("Timeout querying device information.")); + break; + case BlackBerryDeviceInformation::FailedToStartInferiorProcess: + raiseError(tr("Failed to query device information.")); + break; + case BlackBerryDeviceInformation::InferiorProcessCrashed: + raiseError(tr("Process to query device information has crashed.")); + break; + default: + raiseError(tr("Cannot query device information.")); + break; + } + m_eventLoop->exit(false); + return; + } + + if (!m_deviceInfo->debugTokenValid()) { + raiseError(m_deviceInfo->debugTokenValidationError()); + m_eventLoop->exit(false); + return; + } + } + + m_eventLoop->exit(true); +} + +void BlackBerryCheckDebugTokenStep::emitOutputInfo() +{ + emit addOutput(tr("Checking debug token..."), BuildStep::MessageOutput); +} + +bool BlackBerryCheckDebugTokenStep::init() +{ + m_device = BlackBerryDeviceConfiguration::device(target()->kit()); + if (!m_device) + return false; + + if (m_device->sshParameters().host.isEmpty()) { + raiseError(tr("No hostname specified for the device")); + return false; + } + + return true; +} + +void BlackBerryCheckDebugTokenStep::run(QFutureInterface<bool> &fi) +{ + m_eventLoop = new QEventLoop; + m_deviceInfo = new BlackBerryDeviceInformation; + + connect(m_deviceInfo, SIGNAL(started()), this, SLOT(emitOutputInfo())); + connect(m_deviceInfo, SIGNAL(finished(int)), this, SLOT(checkDeviceInfo(int)), Qt::DirectConnection); + m_deviceInfo->setDeviceTarget(m_device->sshParameters().host, m_device->sshParameters().password); + + bool returnValue = m_eventLoop->exec(); + + delete m_eventLoop; + m_eventLoop = 0; + + delete m_deviceInfo; + m_deviceInfo = 0; + + return fi.reportResult(returnValue); +} + +ProjectExplorer::BuildStepConfigWidget *BlackBerryCheckDebugTokenStep::createConfigWidget() +{ + return new BlackBerryCheckDebugTokenConfigWidget(); +} + +void BlackBerryCheckDebugTokenStep::raiseError(const QString &errorMessage) +{ + emit addOutput(errorMessage, BuildStep::ErrorMessageOutput); + emit addTask(ProjectExplorer::Task(ProjectExplorer::Task::Error, errorMessage, Utils::FileName(), -1, + ProjectExplorer::Constants::TASK_CATEGORY_DEPLOYMENT)); +} diff --git a/src/plugins/qnx/blackberrycheckdebugtokenstep.h b/src/plugins/qnx/blackberrycheckdebugtokenstep.h new file mode 100644 index 0000000000..84152499fe --- /dev/null +++ b/src/plugins/qnx/blackberrycheckdebugtokenstep.h @@ -0,0 +1,77 @@ +/************************************************************************** +** +** Copyright (C) 2013 BlackBerry Limited. All rights reserved. +** +** Contact: BlackBerry (qt@blackberry.com) +** Contact: KDAB (info@kdab.com) +** +** 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. +** +****************************************************************************/ + +#ifndef QNX_INTERNAL_BLACKBERRYCHECKDEBUGTOKENSTEP_H +#define QNX_INTERNAL_BLACKBERRYCHECKDEBUGTOKENSTEP_H + +#include "blackberrydeviceconfiguration.h" + +#include <projectexplorer/buildstep.h> + +QT_BEGIN_NAMESPACE +class QEventLoop; +QT_END_NAMESPACE + +namespace Qnx { +namespace Internal { + +class BlackBerryDeviceInformation; +class BlackBerryCheckDebugTokenStep : public ProjectExplorer::BuildStep +{ + Q_OBJECT + friend class BlackBerryCheckDebugTokenStepFactory; + +public: + explicit BlackBerryCheckDebugTokenStep(ProjectExplorer::BuildStepList *bsl); + + bool init(); + void run(QFutureInterface<bool> &fi); + ProjectExplorer::BuildStepConfigWidget *createConfigWidget(); + + void raiseError(const QString& error); + +protected: + BlackBerryCheckDebugTokenStep(ProjectExplorer::BuildStepList *bsl, BlackBerryCheckDebugTokenStep *bs); + +protected slots: + void checkDeviceInfo(int status); + void emitOutputInfo(); + +private: + BlackBerryDeviceInformation *m_deviceInfo; + BlackBerryDeviceConfiguration::ConstPtr m_device; + QEventLoop *m_eventLoop; +}; + +} // namespace Internal +} // namespace Qnx + +#endif // QNX_INTERNAL_BLACKBERRYCHECKDEBUGTOKENSTEP_H diff --git a/src/plugins/qnx/blackberrycheckdevmodestepconfigwidget.cpp b/src/plugins/qnx/blackberrycheckdebugtokenstepconfigwidget.cpp index 45ef8c5361..3b19d5c32a 100644 --- a/src/plugins/qnx/blackberrycheckdevmodestepconfigwidget.cpp +++ b/src/plugins/qnx/blackberrycheckdebugtokenstepconfigwidget.cpp @@ -29,27 +29,27 @@ ** ****************************************************************************/ -#include "blackberrycheckdevmodestepconfigwidget.h" +#include "blackberrycheckdebugtokenstepconfigwidget.h" using namespace Qnx; using namespace Qnx::Internal; -BlackBerryCheckDevModeStepConfigWidget::BlackBerryCheckDevModeStepConfigWidget() : +BlackBerryCheckDebugTokenConfigWidget::BlackBerryCheckDebugTokenConfigWidget() : ProjectExplorer::BuildStepConfigWidget() { } -QString BlackBerryCheckDevModeStepConfigWidget::displayName() const +QString BlackBerryCheckDebugTokenConfigWidget::displayName() const { - return tr("<b>Check development mode</b>"); + return tr("<b>Check debug token</b>"); } -QString BlackBerryCheckDevModeStepConfigWidget::summaryText() const +QString BlackBerryCheckDebugTokenConfigWidget::summaryText() const { return displayName(); } -bool BlackBerryCheckDevModeStepConfigWidget::showWidget() const +bool BlackBerryCheckDebugTokenConfigWidget::showWidget() const { return false; } diff --git a/src/plugins/qnx/blackberrycheckdevmodestepconfigwidget.h b/src/plugins/qnx/blackberrycheckdebugtokenstepconfigwidget.h index 02c555492d..e7d6a16df0 100644 --- a/src/plugins/qnx/blackberrycheckdevmodestepconfigwidget.h +++ b/src/plugins/qnx/blackberrycheckdebugtokenstepconfigwidget.h @@ -29,19 +29,19 @@ ** ****************************************************************************/ -#ifndef QNX_INTERNAL_BLACKBERRYCHECKDEVMODESTEPCONFIGWIDGET_H -#define QNX_INTERNAL_BLACKBERRYCHECKDEVMODESTEPCONFIGWIDGET_H +#ifndef QNX_INTERNAL_BLACKBERRYCHECKDEBUGTOKENSTEPCONFIGWIDGET_H +#define QNX_INTERNAL_BLACKBERRYCHECKDEBUGTOKENSTEPCONFIGWIDGET_H #include <projectexplorer/buildstep.h> namespace Qnx { namespace Internal { -class BlackBerryCheckDevModeStepConfigWidget : public ProjectExplorer::BuildStepConfigWidget +class BlackBerryCheckDebugTokenConfigWidget : public ProjectExplorer::BuildStepConfigWidget { Q_OBJECT public: - explicit BlackBerryCheckDevModeStepConfigWidget(); + explicit BlackBerryCheckDebugTokenConfigWidget(); QString displayName() const; QString summaryText() const; diff --git a/src/plugins/qnx/blackberrycheckdevmodestepfactory.cpp b/src/plugins/qnx/blackberrycheckdebugtokenstepfactory.cpp index df1a378303..b20912bfde 100644 --- a/src/plugins/qnx/blackberrycheckdevmodestepfactory.cpp +++ b/src/plugins/qnx/blackberrycheckdebugtokenstepfactory.cpp @@ -29,9 +29,9 @@ ** ****************************************************************************/ -#include "blackberrycheckdevmodestepfactory.h" +#include "blackberrycheckdebugtokenstepfactory.h" -#include "blackberrycheckdevmodestep.h" +#include "blackberrycheckdebugtokenstep.h" #include "blackberrydeviceconfigurationfactory.h" #include "qnxconstants.h" @@ -43,12 +43,12 @@ using namespace Qnx; using namespace Qnx::Internal; -BlackBerryCheckDevModeStepFactory::BlackBerryCheckDevModeStepFactory(QObject *parent) : +BlackBerryCheckDebugTokenStepFactory::BlackBerryCheckDebugTokenStepFactory(QObject *parent) : ProjectExplorer::IBuildStepFactory(parent) { } -QList<Core::Id> BlackBerryCheckDevModeStepFactory::availableCreationIds(ProjectExplorer::BuildStepList *parent) const +QList<Core::Id> BlackBerryCheckDebugTokenStepFactory::availableCreationIds(ProjectExplorer::BuildStepList *parent) const { if (parent->id() != ProjectExplorer::Constants::BUILDSTEPS_DEPLOY) return QList<Core::Id>(); @@ -57,52 +57,52 @@ QList<Core::Id> BlackBerryCheckDevModeStepFactory::availableCreationIds(ProjectE if (deviceType != BlackBerryDeviceConfigurationFactory::deviceType()) return QList<Core::Id>(); - return QList<Core::Id>() << Core::Id(Constants::QNX_CHECK_DEVELOPMENT_MODE_BS_ID); + return QList<Core::Id>() << Core::Id(Constants::QNX_CHECK_DEBUG_TOKEN_BS_ID); } -QString BlackBerryCheckDevModeStepFactory::displayNameForId(const Core::Id id) const +QString BlackBerryCheckDebugTokenStepFactory::displayNameForId(const Core::Id id) const { - if (id == Constants::QNX_CHECK_DEVELOPMENT_MODE_BS_ID) - return tr("Check Development Mode"); + if (id == Constants::QNX_CHECK_DEBUG_TOKEN_BS_ID) + return tr("Check Debug Token"); return QString(); } -bool BlackBerryCheckDevModeStepFactory::canCreate(ProjectExplorer::BuildStepList *parent, const Core::Id id) const +bool BlackBerryCheckDebugTokenStepFactory::canCreate(ProjectExplorer::BuildStepList *parent, const Core::Id id) const { return availableCreationIds(parent).contains(id); } -ProjectExplorer::BuildStep *BlackBerryCheckDevModeStepFactory::create(ProjectExplorer::BuildStepList *parent, const Core::Id id) +ProjectExplorer::BuildStep *BlackBerryCheckDebugTokenStepFactory::create(ProjectExplorer::BuildStepList *parent, const Core::Id id) { if (!canCreate(parent, id)) return 0; - return new BlackBerryCheckDevModeStep(parent); + return new BlackBerryCheckDebugTokenStep(parent); } -bool BlackBerryCheckDevModeStepFactory::canRestore(ProjectExplorer::BuildStepList *parent, const QVariantMap &map) const +bool BlackBerryCheckDebugTokenStepFactory::canRestore(ProjectExplorer::BuildStepList *parent, const QVariantMap &map) const { return canCreate(parent, ProjectExplorer::idFromMap(map)); } -ProjectExplorer::BuildStep *BlackBerryCheckDevModeStepFactory::restore(ProjectExplorer::BuildStepList *parent, const QVariantMap &map) +ProjectExplorer::BuildStep *BlackBerryCheckDebugTokenStepFactory::restore(ProjectExplorer::BuildStepList *parent, const QVariantMap &map) { if (!canRestore(parent, map)) return 0; - BlackBerryCheckDevModeStep *bs = new BlackBerryCheckDevModeStep(parent); + BlackBerryCheckDebugTokenStep *bs = new BlackBerryCheckDebugTokenStep(parent); if (bs->fromMap(map)) return bs; delete bs; return 0; } -bool BlackBerryCheckDevModeStepFactory::canClone(ProjectExplorer::BuildStepList *parent, ProjectExplorer::BuildStep *product) const +bool BlackBerryCheckDebugTokenStepFactory::canClone(ProjectExplorer::BuildStepList *parent, ProjectExplorer::BuildStep *product) const { return canCreate(parent, product->id()); } -ProjectExplorer::BuildStep *BlackBerryCheckDevModeStepFactory::clone(ProjectExplorer::BuildStepList *parent, ProjectExplorer::BuildStep *product) +ProjectExplorer::BuildStep *BlackBerryCheckDebugTokenStepFactory::clone(ProjectExplorer::BuildStepList *parent, ProjectExplorer::BuildStep *product) { if (!canClone(parent, product)) return 0; - return new BlackBerryCheckDevModeStep(parent, static_cast<BlackBerryCheckDevModeStep *>(product)); + return new BlackBerryCheckDebugTokenStep(parent, static_cast<BlackBerryCheckDebugTokenStep *>(product)); } diff --git a/src/plugins/qnx/blackberrycheckdevmodestepfactory.h b/src/plugins/qnx/blackberrycheckdebugtokenstepfactory.h index c06bdc0f0d..be3676f0ad 100644 --- a/src/plugins/qnx/blackberrycheckdevmodestepfactory.h +++ b/src/plugins/qnx/blackberrycheckdebugtokenstepfactory.h @@ -29,19 +29,19 @@ ** ****************************************************************************/ -#ifndef QNX_INTERNAL_BLACKBERRYCHECKDEVMODESTEPFACTORY_H -#define QNX_INTERNAL_BLACKBERRYCHECKDEVMODESTEPFACTORY_H +#ifndef QNX_INTERNAL_BLACKBERRYCHECKDEBUGTOKENSTEPFACTORY_H +#define QNX_INTERNAL_BLACKBERRYCHECKDEBUGTOKENSTEPFACTORY_H #include <projectexplorer/buildstep.h> namespace Qnx { namespace Internal { -class BlackBerryCheckDevModeStepFactory : public ProjectExplorer::IBuildStepFactory +class BlackBerryCheckDebugTokenStepFactory : public ProjectExplorer::IBuildStepFactory { Q_OBJECT public: - explicit BlackBerryCheckDevModeStepFactory(QObject *parent = 0); + explicit BlackBerryCheckDebugTokenStepFactory(QObject *parent = 0); QList<Core::Id> availableCreationIds(ProjectExplorer::BuildStepList *parent) const; QString displayNameForId(const Core::Id id) const; diff --git a/src/plugins/qnx/blackberrycheckdevmodestep.cpp b/src/plugins/qnx/blackberrycheckdevmodestep.cpp deleted file mode 100644 index 5e5a956983..0000000000 --- a/src/plugins/qnx/blackberrycheckdevmodestep.cpp +++ /dev/null @@ -1,110 +0,0 @@ -/************************************************************************** -** -** Copyright (C) 2014 BlackBerry Limited. All rights reserved. -** -** Contact: BlackBerry (qt@blackberry.com) -** Contact: KDAB (info@kdab.com) -** -** 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 "blackberrycheckdevmodestep.h" - -#include "blackberrycheckdevmodestepconfigwidget.h" -#include "blackberrydeviceconfiguration.h" -#include "qnxconstants.h" - -#include <projectexplorer/buildconfiguration.h> -#include <projectexplorer/target.h> -#include <ssh/sshconnection.h> - -using namespace Qnx; -using namespace Qnx::Internal; - -BlackBerryCheckDevModeStep::BlackBerryCheckDevModeStep(ProjectExplorer::BuildStepList *bsl) : - BlackBerryAbstractDeployStep(bsl, Core::Id(Constants::QNX_CHECK_DEVELOPMENT_MODE_BS_ID)) -{ - setDisplayName(tr("Check Development Mode")); -} - -BlackBerryCheckDevModeStep::BlackBerryCheckDevModeStep(ProjectExplorer::BuildStepList *bsl, BlackBerryCheckDevModeStep *bs) : - BlackBerryAbstractDeployStep(bsl, bs) -{ - setDisplayName(tr("Check Development Mode")); -} - -bool BlackBerryCheckDevModeStep::init() -{ - if (!BlackBerryAbstractDeployStep::init()) - return false; - - QString deployCmd = target()->activeBuildConfiguration()->environment().searchInPath(QLatin1String(Constants::QNX_BLACKBERRY_DEPLOY_CMD)); - if (deployCmd.isEmpty()) { - raiseError(tr("Could not find command '%1' in the build environment") - .arg(QLatin1String(Constants::QNX_BLACKBERRY_DEPLOY_CMD))); - return false; - } - - BlackBerryDeviceConfiguration::ConstPtr device = BlackBerryDeviceConfiguration::device(target()->kit()); - QString deviceHost = device ? device->sshParameters().host : QString(); - if (deviceHost.isEmpty()) { - raiseError(tr("No hostname specified for device")); - return false; - } - - QStringList args; - args << QLatin1String("-listDeviceInfo"); - args << deviceHost; - if (!device->sshParameters().password.isEmpty()) { - args << QLatin1String("-password"); - args << device->sshParameters().password; - } - - addCommand(deployCmd, args); - - return true; -} - -ProjectExplorer::BuildStepConfigWidget *BlackBerryCheckDevModeStep::createConfigWidget() -{ - return new BlackBerryCheckDevModeStepConfigWidget(); -} - -QString BlackBerryCheckDevModeStep::password() const -{ - BlackBerryDeviceConfiguration::ConstPtr device = BlackBerryDeviceConfiguration::device(target()->kit()); - return device ? device->sshParameters().password : QString(); -} - -void BlackBerryCheckDevModeStep::processStarted(const ProjectExplorer::ProcessParameters ¶ms) -{ - QString arguments = params.prettyArguments(); - if (!password().isEmpty()) { - const QString passwordLine = QLatin1String(" -password ") + password(); - const QString hiddenPasswordLine = QLatin1String(" -password <hidden>"); - arguments.replace(passwordLine, hiddenPasswordLine); - } - - emitOutputInfo(params, arguments); -} diff --git a/src/plugins/qnx/blackberryconfiguration.cpp b/src/plugins/qnx/blackberryconfiguration.cpp index 7c51ed196d..83b47dedad 100644 --- a/src/plugins/qnx/blackberryconfiguration.cpp +++ b/src/plugins/qnx/blackberryconfiguration.cpp @@ -33,7 +33,8 @@ #include "blackberryqtversion.h" #include "qnxtoolchain.h" -#include "qnxutils.h" + +#include <utils/qtcassert.h> #include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/kitmanager.h> @@ -50,6 +51,8 @@ #include <debugger/debuggeritemmanager.h> #include <debugger/debuggerkitinformation.h> +#include <coreplugin/icore.h> + #include <QFileInfo> #include <QDir> #include <QMessageBox> @@ -62,14 +65,30 @@ using namespace Debugger; namespace Qnx { namespace Internal { -BlackBerryConfiguration::BlackBerryConfiguration(const FileName &ndkEnvFile, bool isAutoDetected, - const QString &displayName) +BlackBerryConfiguration::BlackBerryConfiguration(const NdkInstallInformation &ndkInstallInfo) + : m_isAutoDetected(true) +{ + QString envFilePath = QnxUtils::envFilePath(ndkInstallInfo.path, ndkInstallInfo.version); + QTC_ASSERT(!envFilePath.isEmpty(), return); + m_ndkEnvFile = Utils::FileName::fromString(envFilePath); + m_displayName = ndkInstallInfo.name; + m_qnxEnv = QnxUtils::qnxEnvironmentFromNdkFile(m_ndkEnvFile.toString()); + QString sep = QString::fromLatin1("/qnx6"); + // The QNX_TARGET value is using Unix-like separator on all platforms. + m_targetName = ndkInstallInfo.target.split(sep).first().split(QLatin1Char('/')).last(); + m_qnxHost = ndkInstallInfo.host; + m_sysRoot = FileName::fromString(ndkInstallInfo.target); + m_version = BlackBerryVersionNumber(ndkInstallInfo.version); + ctor(); +} + +BlackBerryConfiguration::BlackBerryConfiguration(const FileName &ndkEnvFile) + : m_isAutoDetected(false) { - Q_ASSERT(!QFileInfo(ndkEnvFile.toString()).isDir()); + QTC_ASSERT(!QFileInfo(ndkEnvFile.toString()).isDir(), return); m_ndkEnvFile = ndkEnvFile; - m_isAutoDetected = isAutoDetected; - QString ndkPath = ndkEnvFile.parentDir().toString(); - m_displayName = displayName.isEmpty() ? ndkPath.split(QDir::separator()).last() : displayName; + QString ndkPath = m_ndkEnvFile.parentDir().toString(); + m_displayName = ndkPath.split(QDir::separator()).last(); m_qnxEnv = QnxUtils::qnxEnvironmentFromNdkFile(m_ndkEnvFile.toString()); QString ndkTarget; @@ -89,6 +108,15 @@ BlackBerryConfiguration::BlackBerryConfiguration(const FileName &ndkEnvFile, boo if (QDir(ndkTarget).exists()) m_sysRoot = FileName::fromString(ndkTarget); + m_version = BlackBerryVersionNumber::fromNdkEnvFileName(QFileInfo(m_ndkEnvFile.toString()).baseName()); + if (m_version.isEmpty()) + m_version = BlackBerryVersionNumber::fromTargetName(m_targetName); + + ctor(); +} + +void BlackBerryConfiguration::ctor() +{ FileName qmake4Path = QnxUtils::executableWithExtension(FileName::fromString(m_qnxHost + QLatin1String("/usr/bin/qmake"))); FileName qmake5Path = QnxUtils::executableWithExtension(FileName::fromString(m_qnxHost + QLatin1String("/usr/bin/qt5/qmake"))); FileName gccPath = QnxUtils::executableWithExtension(FileName::fromString(m_qnxHost + QLatin1String("/usr/bin/qcc"))); @@ -131,6 +159,11 @@ QString BlackBerryConfiguration::qnxHost() const return m_qnxHost; } +BlackBerryVersionNumber BlackBerryConfiguration::version() const +{ + return m_version; +} + bool BlackBerryConfiguration::isAutoDetected() const { return m_isAutoDetected; @@ -138,7 +171,13 @@ bool BlackBerryConfiguration::isAutoDetected() const bool BlackBerryConfiguration::isActive() const { - return !findRegisteredQtVersions().isEmpty(); + foreach (Kit *kit, KitManager::kits()) { + if (kit->isAutoDetected() && + kit->autoDetectionSource() == m_ndkEnvFile.toString()) + return true; + } + + return false; } bool BlackBerryConfiguration::isValid() const @@ -246,6 +285,7 @@ Kit *BlackBerryConfiguration::createKit( kit->setIconPath(FileName::fromString(QLatin1String(Constants::QNX_BB_CATEGORY_ICON))); kit->setAutoDetected(true); + kit->setAutoDetectionSource(m_ndkEnvFile.toString()); kit->setMutable(DeviceKitInformation::id(), true); kit->setSticky(QtKitInformation::id(), true); @@ -278,7 +318,7 @@ bool BlackBerryConfiguration::activate() if (!m_simulatorDebugger.isEmpty()) errorMessage += QLatin1Char('\n') + tr("- No GDB debugger found for BB10 Simulator."); - QMessageBox::warning(0, tr("Cannot Set up BB10 Configuration"), + QMessageBox::warning(Core::ICore::mainWindow(), tr("Cannot Set up BB10 Configuration"), errorMessage, QMessageBox::Ok); return false; } @@ -339,50 +379,24 @@ bool BlackBerryConfiguration::activate() return true; } -QList<BaseQtVersion *> BlackBerryConfiguration::findRegisteredQtVersions() const -{ - QList<BaseQtVersion *> versions; - foreach (BaseQtVersion *version, QtVersionManager::versions()) { - if (version->type() == QLatin1String(Constants::QNX_BB_QT)) { - QnxAbstractQtVersion *qnxVersion = dynamic_cast<QnxAbstractQtVersion *>(version); - if (qnxVersion && qnxVersion->isAutodetected() - && (qnxVersion->qmakeCommand() == qmake4BinaryFile() - || qnxVersion->qmakeCommand() == qmake5BinaryFile())) - versions << qnxVersion; - } - } - return versions; -} - void BlackBerryConfiguration::deactivate() { - QList<BaseQtVersion *> versions = findRegisteredQtVersions(); - QList<ToolChain *> toolChains; foreach (Kit *kit, KitManager::kits()) { - if (kit->isAutoDetected()) { + if (kit->isAutoDetected() && + kit->autoDetectionSource() == ndkEnvFile().toString()) { BaseQtVersion *version = QtKitInformation::qtVersion(kit); - if (versions.contains(version)) { - ToolChain *toolChain = ToolChainKitInformation::toolChain(kit); - if (toolChain) - toolChains << toolChain; - KitManager::deregisterKit(kit); - } + ToolChain *toolChain = ToolChainKitInformation::toolChain(kit); + const DebuggerItem *debugger = DebuggerKitInformation::debugger(kit); + if (version) + QtVersionManager::removeVersion(version); + if (toolChain) + ToolChainManager::deregisterToolChain(toolChain); + if (debugger) + DebuggerItemManager::deregisterDebugger(debugger->id()); + + KitManager::deregisterKit(kit); } } - - foreach (const DebuggerItem &item, DebuggerItemManager::debuggers()) - if (item.isAutoDetected() && - (item.command() == m_simulatorDebugger || item.command() == m_deviceDebugger)) - DebuggerItemManager::deregisterDebugger(item.id()); - - foreach (ToolChain *toolChain, ToolChainManager::toolChains()) - if (toolChain->isAutoDetected() - && (toolChains.contains(toolChain) || toolChain->compilerCommand() == m_gccCompiler)) - ToolChainManager::deregisterToolChain(toolChain); - - foreach (BaseQtVersion *version, versions) - QtVersionManager::removeVersion(version); - } } // namespace Internal diff --git a/src/plugins/qnx/blackberryconfiguration.h b/src/plugins/qnx/blackberryconfiguration.h index 573336d848..275c7d71f9 100644 --- a/src/plugins/qnx/blackberryconfiguration.h +++ b/src/plugins/qnx/blackberryconfiguration.h @@ -32,6 +32,8 @@ #ifndef BLACKBERRYCONFIGURATIONS_H #define BLACKBERRYCONFIGURATIONS_H +#include "qnxutils.h" +#include "blackberryversionnumber.h" #include "qnxconstants.h" #include <utils/environment.h> @@ -60,13 +62,15 @@ class BlackBerryConfiguration { Q_DECLARE_TR_FUNCTIONS(Qnx::Internal::BlackBerryConfiguration) public: - BlackBerryConfiguration(const Utils::FileName &ndkEnvFile, bool isAutoDetected, const QString &displayName = QString()); + BlackBerryConfiguration(const NdkInstallInformation &ndkInstallInfo); + BlackBerryConfiguration(const Utils::FileName &ndkEnvFile); bool activate(); void deactivate(); QString ndkPath() const; QString displayName() const; QString targetName() const; QString qnxHost() const; + BlackBerryVersionNumber version() const; bool isAutoDetected() const; bool isActive() const; bool isValid() const; @@ -84,6 +88,7 @@ private: QString m_targetName; QString m_qnxHost; bool m_isAutoDetected; + BlackBerryVersionNumber m_version; Utils::FileName m_ndkEnvFile; Utils::FileName m_qmake4BinaryFile; Utils::FileName m_qmake5BinaryFile; @@ -93,6 +98,7 @@ private: Utils::FileName m_sysRoot; QList<Utils::EnvironmentItem> m_qnxEnv; + void ctor(); QnxAbstractQtVersion* createQtVersion( const Utils::FileName &qmakePath, Qnx::QnxArchitecture arch, const QString &versionName); QnxToolChain* createToolChain( @@ -102,7 +108,6 @@ private: ProjectExplorer::Kit* createKit( QnxAbstractQtVersion* version, QnxToolChain* toolChain, const QVariant &debuggerItemId); - QList<QtSupport::BaseQtVersion *> findRegisteredQtVersions() const; }; } // namespace Internal diff --git a/src/plugins/qnx/blackberryconfigurationmanager.cpp b/src/plugins/qnx/blackberryconfigurationmanager.cpp index 8d1368c632..33d0609b61 100644 --- a/src/plugins/qnx/blackberryconfigurationmanager.cpp +++ b/src/plugins/qnx/blackberryconfigurationmanager.cpp @@ -49,6 +49,8 @@ #include <qtsupport/qtversionmanager.h> #include <qtsupport/qtkitinformation.h> +#include <debugger/debuggerkitinformation.h> + #include <QMessageBox> #include <QFileInfo> @@ -61,11 +63,15 @@ namespace { const QLatin1String SettingsGroup("BlackBerryConfiguration"); const QLatin1String NDKLocationKey("NDKLocation"); // For 10.1 NDK support (< QTC 3.0) const QLatin1String NDKEnvFileKey("NDKEnvFile"); -const QLatin1String CertificateGroup("Certificates"); const QLatin1String ManualNDKsGroup("ManualNDKs"); const QLatin1String ActiveNDKsGroup("ActiveNDKs"); } +static bool sortConfigurationsByVersion(const BlackBerryConfiguration *a, const BlackBerryConfiguration *b) +{ + return a->version() > b->version(); +} + BlackBerryConfigurationManager::BlackBerryConfigurationManager(QObject *parent) :QObject(parent) { @@ -90,8 +96,7 @@ void BlackBerryConfigurationManager::loadManualConfigurations() ndkEnvPath = QnxUtils::envFilePath(ndkPath); } - BlackBerryConfiguration *config = new BlackBerryConfiguration(Utils::FileName::fromString(ndkEnvPath), - false); + BlackBerryConfiguration *config = new BlackBerryConfiguration(Utils::FileName::fromString(ndkEnvPath)); if (!addConfiguration(config)) delete config; @@ -106,9 +111,7 @@ void BlackBerryConfigurationManager::loadAutoDetectedConfigurations() { QStringList activePaths = activeConfigurationNdkEnvPaths(); foreach (const NdkInstallInformation &ndkInfo, QnxUtils::installedNdks()) { - QString envFilePath = QnxUtils::envFilePath(ndkInfo.path, ndkInfo.version); - BlackBerryConfiguration *config = new BlackBerryConfiguration(Utils::FileName::fromString(envFilePath), - true, ndkInfo.name); + BlackBerryConfiguration *config = new BlackBerryConfiguration(ndkInfo); if (!addConfiguration(config)) { delete config; continue; @@ -211,6 +214,23 @@ void BlackBerryConfigurationManager::clearInvalidConfigurations() } } +void BlackBerryConfigurationManager::setKitsAutoDetectionSource() +{ + foreach (Kit *kit, KitManager::kits()) { + if (kit->isAutoDetected() && + (DeviceTypeKitInformation::deviceTypeId(kit) == Constants::QNX_BB_CATEGORY_ICON) && + kit->autoDetectionSource().isEmpty()) { + QtSupport::BaseQtVersion *version = QtSupport::QtKitInformation::qtVersion(kit); + foreach (BlackBerryConfiguration *config, m_configs) { + if ((version && + (version->qmakeCommand() == config->qmake4BinaryFile() || version->qmakeCommand() == config->qmake5BinaryFile())) + && (SysRootKitInformation::sysRoot(kit) == config->sysRoot())) + kit->setAutoDetectionSource(config->ndkEnvFile().toString()); + } + } + } +} + // Switch to QnxToolchain for exisintg configuration using GccToolChain void BlackBerryConfigurationManager::checkToolChainConfiguration() { @@ -235,14 +255,16 @@ bool BlackBerryConfigurationManager::addConfiguration(BlackBerryConfiguration *c if (c->ndkPath() == config->ndkPath() && c->targetName() == config->targetName()) { if (!config->isAutoDetected()) - QMessageBox::warning(0, tr("NDK Already Known"), + QMessageBox::warning(Core::ICore::mainWindow(), tr("NDK Already Known"), tr("The NDK already has a configuration."), QMessageBox::Ok); return false; } } if (config->isValid()) { - m_configs.append(config); + QList<BlackBerryConfiguration *>::iterator it = qLowerBound(m_configs.begin(), m_configs.end(), + config, &sortConfigurationsByVersion); + m_configs.insert(it, config); return true; } @@ -292,6 +314,9 @@ QList<BlackBerryConfiguration *> BlackBerryConfigurationManager::activeConfigura BlackBerryConfiguration *BlackBerryConfigurationManager::configurationFromEnvFile(const Utils::FileName &envFile) const { + if (envFile.isEmpty()) + return 0; + foreach (BlackBerryConfiguration *config, m_configs) { if (config->ndkEnvFile() == envFile) return config; @@ -314,6 +339,9 @@ QList<Utils::EnvironmentItem> BlackBerryConfigurationManager::defaultQnxEnv() void BlackBerryConfigurationManager::loadSettings() { + // Backward compatibility: Set kit's auto detection source + // for existing BlackBerry kits that do not have it set yet. + setKitsAutoDetectionSource(); clearInvalidConfigurations(); loadAutoDetectedConfigurations(); loadManualConfigurations(); diff --git a/src/plugins/qnx/blackberryconfigurationmanager.h b/src/plugins/qnx/blackberryconfigurationmanager.h index 45290efe02..45aa4beeb6 100644 --- a/src/plugins/qnx/blackberryconfigurationmanager.h +++ b/src/plugins/qnx/blackberryconfigurationmanager.h @@ -84,6 +84,7 @@ private: void saveManualConfigurations(); void saveActiveConfigurationNdkEnvPath(); void clearInvalidConfigurations(); + void setKitsAutoDetectionSource(); QStringList activeConfigurationNdkEnvPaths(); }; diff --git a/src/plugins/qnx/blackberrydebugtokenrequestdialog.cpp b/src/plugins/qnx/blackberrydebugtokenrequestdialog.cpp index 5909927517..50ef16ba88 100644 --- a/src/plugins/qnx/blackberrydebugtokenrequestdialog.cpp +++ b/src/plugins/qnx/blackberrydebugtokenrequestdialog.cpp @@ -143,9 +143,24 @@ void BlackBerryDebugTokenRequestDialog::requestDebugToken() BlackBerryConfigurationManager &configuration = BlackBerryConfigurationManager::instance(); + bool ok; + const QString cskPassword = m_utils.cskPassword(this, &ok); + + if (!ok) { + setBusy(false); + return; + } + + const QString certificatePassword = m_utils.certificatePassword(this, &ok); + + if (!ok) { + setBusy(false); + return; + } + m_requester->requestDebugToken(m_ui->debugTokenPath->path(), - m_utils.cskPassword(), configuration.defaultKeystorePath(), - m_utils.certificatePassword(), m_ui->devicePin->text()); + cskPassword, configuration.defaultKeystorePath(), + certificatePassword, m_ui->devicePin->text()); } void BlackBerryDebugTokenRequestDialog::setDefaultPath() @@ -235,6 +250,11 @@ void BlackBerryDebugTokenRequestDialog::debugTokenArrived(int status) break; } + QFile file(m_ui->debugTokenPath->path()); + + if (file.exists()) + file.remove(); + QMessageBox::critical(this, tr("Error"), errorString); setBusy(false); diff --git a/src/plugins/qnx/blackberrydeployconfigurationfactory.cpp b/src/plugins/qnx/blackberrydeployconfigurationfactory.cpp index b968fc1b6f..04d4af8963 100644 --- a/src/plugins/qnx/blackberrydeployconfigurationfactory.cpp +++ b/src/plugins/qnx/blackberrydeployconfigurationfactory.cpp @@ -32,7 +32,7 @@ #include "blackberrydeployconfigurationfactory.h" #include "qnxconstants.h" -#include "blackberrycheckdevmodestep.h" +#include "blackberrycheckdebugtokenstep.h" #include "blackberrydeployconfiguration.h" #include "blackberrycreatepackagestep.h" #include "blackberrydeploystep.h" @@ -93,7 +93,7 @@ ProjectExplorer::DeployConfiguration *BlackBerryDeployConfigurationFactory::crea return 0; BlackBerryDeployConfiguration *dc = new BlackBerryDeployConfiguration(parent); - dc->stepList()->insertStep(0, new BlackBerryCheckDevModeStep(dc->stepList())); + dc->stepList()->insertStep(0, new BlackBerryCheckDebugTokenStep(dc->stepList())); dc->stepList()->insertStep(1, new BlackBerryCreatePackageStep(dc->stepList())); dc->stepList()->insertStep(2, new BlackBerryDeployStep(dc->stepList())); return dc; diff --git a/src/plugins/qnx/blackberrydeviceinformation.cpp b/src/plugins/qnx/blackberrydeviceinformation.cpp index a21142bc2d..0c8d440589 100644 --- a/src/plugins/qnx/blackberrydeviceinformation.cpp +++ b/src/plugins/qnx/blackberrydeviceinformation.cpp @@ -68,6 +68,7 @@ void BlackBerryDeviceInformation::resetResults() m_deviceOS.clear(); m_hardwareId.clear(); m_debugTokenAuthor.clear(); + m_debugTokenValidationError.clear(); m_scmBundle.clear(); m_hostName.clear(); m_debugTokenValid = false; @@ -95,6 +96,11 @@ QString BlackBerryDeviceInformation::debugTokenAuthor() const return m_debugTokenAuthor; } +QString BlackBerryDeviceInformation::debugTokenValidationError() const +{ + return m_debugTokenValidationError; +} + QString BlackBerryDeviceInformation::scmBundle() const { return m_scmBundle; @@ -125,8 +131,9 @@ void BlackBerryDeviceInformation::processData(const QString &line) static const QString devicepin = QLatin1String("devicepin::0x"); static const QString device_os = QLatin1String("device_os::"); static const QString hardwareid = QLatin1String("hardwareid::"); - static const QString debug_token_author = QLatin1String("debug_token_author::"); - static const QString debug_token_valid = QLatin1String("debug_token_valid:b:"); + static const QString debug_token_author = QLatin1String("[n]debug_token_author::"); + static const QString debug_token_validation_error = QLatin1String("[n]debug_token_validation_error::"); + static const QString debug_token_valid = QLatin1String("[n]debug_token_valid:b:"); static const QString simulator = QLatin1String("simulator:b:"); static const QString scmbundle = QLatin1String("scmbundle::"); static const QString hostname = QLatin1String("hostname::"); @@ -140,6 +147,8 @@ void BlackBerryDeviceInformation::processData(const QString &line) m_hardwareId = line.mid(hardwareid.size()).trimmed(); else if (line.startsWith(debug_token_author)) m_debugTokenAuthor = line.mid(debug_token_author.size()).trimmed(); + else if (line.startsWith(debug_token_validation_error)) + m_debugTokenValidationError = line.mid(debug_token_validation_error.size()).trimmed(); else if (line.startsWith(debug_token_valid)) m_debugTokenValid = line.mid(debug_token_valid.size()).trimmed() == QLatin1String("true"); else if (line.startsWith(simulator)) diff --git a/src/plugins/qnx/blackberrydeviceinformation.h b/src/plugins/qnx/blackberrydeviceinformation.h index 0bfe156291..f9e8794299 100644 --- a/src/plugins/qnx/blackberrydeviceinformation.h +++ b/src/plugins/qnx/blackberrydeviceinformation.h @@ -62,6 +62,7 @@ public: QString deviceOS() const; QString hardwareId() const; QString debugTokenAuthor() const; + QString debugTokenValidationError() const; bool debugTokenValid() const; QString scmBundle() const; QString hostName() const; @@ -75,6 +76,7 @@ private: QString m_debugTokenAuthor; QString m_scmBundle; QString m_hostName; + QString m_debugTokenValidationError; bool m_debugTokenValid; bool m_isSimulator; bool m_isProductionDevice; diff --git a/src/plugins/qnx/blackberryinstallwizardpages.cpp b/src/plugins/qnx/blackberryinstallwizardpages.cpp index 31cc440c11..8ca8986820 100644 --- a/src/plugins/qnx/blackberryinstallwizardpages.cpp +++ b/src/plugins/qnx/blackberryinstallwizardpages.cpp @@ -479,7 +479,7 @@ void BlackBerryInstallWizardFinalPage::initializePage() BlackBerryConfiguration *config = configManager.configurationFromEnvFile(Utils::FileName::fromString(m_data.ndkPath)); if (!config) { - config = new BlackBerryConfiguration(Utils::FileName::fromString(m_data.ndkPath), false); + config = new BlackBerryConfiguration(Utils::FileName::fromString(m_data.ndkPath)); if (!configManager.addConfiguration(config)) { delete config; // TODO: more explicit error message! diff --git a/src/plugins/qnx/blackberrykeyspage.cpp b/src/plugins/qnx/blackberrykeyspage.cpp index e9c173c324..f0435a8ffb 100644 --- a/src/plugins/qnx/blackberrykeyspage.cpp +++ b/src/plugins/qnx/blackberrykeyspage.cpp @@ -51,9 +51,10 @@ BlackBerryKeysPage::BlackBerryKeysPage(QObject *parent) : Constants::QNX_BB_CATEGORY_TR)); } -QWidget *BlackBerryKeysPage::createPage(QWidget *parent) +QWidget *BlackBerryKeysPage::widget() { - m_widget = new BlackBerryKeysWidget(parent); + if (!m_widget) + m_widget = new BlackBerryKeysWidget; return m_widget; } @@ -63,6 +64,7 @@ void BlackBerryKeysPage::apply() void BlackBerryKeysPage::finish() { + delete m_widget; } } // namespace Internal diff --git a/src/plugins/qnx/blackberrykeyspage.h b/src/plugins/qnx/blackberrykeyspage.h index 7c8c003a6e..dd2376e10c 100644 --- a/src/plugins/qnx/blackberrykeyspage.h +++ b/src/plugins/qnx/blackberrykeyspage.h @@ -34,6 +34,8 @@ #include <coreplugin/dialogs/ioptionspage.h> +#include <QPointer> + namespace Qnx { namespace Internal { @@ -44,12 +46,12 @@ class BlackBerryKeysPage : public Core::IOptionsPage Q_OBJECT public: explicit BlackBerryKeysPage(QObject *parent = 0); - QWidget *createPage(QWidget *parent); + QWidget *widget(); void apply(); void finish(); private: - BlackBerryKeysWidget *m_widget; + QPointer<BlackBerryKeysWidget> m_widget; }; } // namespace Internal diff --git a/src/plugins/qnx/blackberrykeyswidget.cpp b/src/plugins/qnx/blackberrykeyswidget.cpp index 905b95b667..8c5f6d22e2 100644 --- a/src/plugins/qnx/blackberrykeyswidget.cpp +++ b/src/plugins/qnx/blackberrykeyswidget.cpp @@ -68,6 +68,8 @@ void BlackBerryKeysWidget::certificateLoaded(int status) switch (status) { case BlackBerryCertificate::Success: m_ui->certificateAuthor->setText(m_utils.defaultCertificate()->author()); + m_ui->certificateAuthor->setVisible(true); + m_ui->authorLabel->setVisible(true); m_ui->openCertificateButton->setVisible(false); break; case BlackBerryCertificate::WrongPassword: @@ -130,9 +132,18 @@ void BlackBerryKeysWidget::updateCertificateSection() BlackBerryConfigurationManager &configManager = BlackBerryConfigurationManager::instance(); m_ui->certificatePath->setText(configManager.defaultKeystorePath()); - m_ui->certificateAuthor->setText(tr("Loading...")); - loadDefaultCertificate(); + const BlackBerryCertificate *certificate = m_utils.defaultCertificate(); + + if (certificate) { + m_ui->certificateAuthor->setText(certificate->author()); + m_ui->openCertificateButton->setVisible(false); + return; + } + + m_ui->openCertificateButton->setVisible(true); + m_ui->certificateAuthor->setVisible(false); + m_ui->authorLabel->setVisible(false); } else { setCreateCertificateVisible(true); } @@ -157,16 +168,8 @@ void BlackBerryKeysWidget::updateKeysSection() void BlackBerryKeysWidget::loadDefaultCertificate() { - const BlackBerryCertificate *certificate = m_utils.defaultCertificate(); - - if (certificate) { - m_ui->certificateAuthor->setText(certificate->author()); - m_ui->openCertificateButton->setVisible(false); - return; - } - connect(&m_utils, SIGNAL(defaultCertificateLoaded(int)), this, SLOT(certificateLoaded(int))); - m_utils.openDefaultCertificate(); + m_utils.openDefaultCertificate(this); } void BlackBerryKeysWidget::setCertificateError(const QString &error) diff --git a/src/plugins/qnx/blackberryndkprocess.cpp b/src/plugins/qnx/blackberryndkprocess.cpp index 9f26718d88..d0f5460757 100644 --- a/src/plugins/qnx/blackberryndkprocess.cpp +++ b/src/plugins/qnx/blackberryndkprocess.cpp @@ -49,6 +49,7 @@ BlackBerryNdkProcess::BlackBerryNdkProcess(const QString &command, QObject *pare m_process->setEnvironment(Utils::EnvironmentItem::toStringList( BlackBerryConfigurationManager::instance().defaultQnxEnv())); + connect(m_process, SIGNAL(started()), this, SIGNAL(started())); connect(m_process, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(processFinished())); connect(m_process, SIGNAL(error(QProcess::ProcessError)), diff --git a/src/plugins/qnx/blackberryndkprocess.h b/src/plugins/qnx/blackberryndkprocess.h index 13637de7e9..1532e1564e 100644 --- a/src/plugins/qnx/blackberryndkprocess.h +++ b/src/plugins/qnx/blackberryndkprocess.h @@ -65,6 +65,7 @@ public: signals: void finished(int status); + void started(); protected: explicit BlackBerryNdkProcess(const QString &command, QObject *parent = 0); diff --git a/src/plugins/qnx/blackberryndksettingspage.cpp b/src/plugins/qnx/blackberryndksettingspage.cpp index ff77f7c1cd..dcac82d87c 100644 --- a/src/plugins/qnx/blackberryndksettingspage.cpp +++ b/src/plugins/qnx/blackberryndksettingspage.cpp @@ -45,16 +45,17 @@ BlackBerryNDKSettingsPage::BlackBerryNDKSettingsPage(QObject *parent) : Core::IOptionsPage(parent) { setId(Core::Id(Constants::QNX_BB_NDK_SETTINGS_ID)); - setDisplayName(tr("NDK")); + setDisplayName(tr("API Level")); setCategory(Constants::QNX_BB_CATEGORY); setDisplayCategory(QCoreApplication::translate("BlackBerry", Constants::QNX_BB_CATEGORY_TR)); setCategoryIcon(QLatin1String(Constants::QNX_BB_CATEGORY_ICON)); } -QWidget *BlackBerryNDKSettingsPage::createPage(QWidget *parent) +QWidget *BlackBerryNDKSettingsPage::widget() { - m_widget = new BlackBerryNDKSettingsWidget(parent); + if (!m_widget) + m_widget = new BlackBerryNDKSettingsWidget; return m_widget; } @@ -73,6 +74,7 @@ void BlackBerryNDKSettingsPage::apply() void BlackBerryNDKSettingsPage::finish() { + delete m_widget; } } // namespace Internal diff --git a/src/plugins/qnx/blackberryndksettingspage.h b/src/plugins/qnx/blackberryndksettingspage.h index 2992f80da0..825b95a4ae 100644 --- a/src/plugins/qnx/blackberryndksettingspage.h +++ b/src/plugins/qnx/blackberryndksettingspage.h @@ -34,6 +34,8 @@ #include <coreplugin/dialogs/ioptionspage.h> +#include <QPointer> + namespace Qnx { namespace Internal { @@ -44,12 +46,12 @@ class BlackBerryNDKSettingsPage : public Core::IOptionsPage Q_OBJECT public: explicit BlackBerryNDKSettingsPage(QObject *parent = 0); - QWidget *createPage(QWidget *parent); + QWidget *widget(); void apply(); void finish(); private: - BlackBerryNDKSettingsWidget *m_widget; + QPointer<BlackBerryNDKSettingsWidget> m_widget; }; } // namespace Internal diff --git a/src/plugins/qnx/blackberryndksettingswidget.cpp b/src/plugins/qnx/blackberryndksettingswidget.cpp index 0e7d89ab8f..2765c954e0 100644 --- a/src/plugins/qnx/blackberryndksettingswidget.cpp +++ b/src/plugins/qnx/blackberryndksettingswidget.cpp @@ -60,14 +60,13 @@ BlackBerryNDKSettingsWidget::BlackBerryNDKSettingsWidget(QWidget *parent) : m_bbConfigManager = &BlackBerryConfigurationManager::instance(); m_ui->setupUi(this); - m_ui->activateNdkTargetButton->setEnabled(false); - m_ui->deactivateNdkTargetButton->setEnabled(false); + updateInfoTable(0); m_activatedTargets << m_bbConfigManager->activeConfigurations(); m_ui->ndksTreeWidget->header()->setResizeMode(QHeaderView::Stretch); m_ui->ndksTreeWidget->header()->setStretchLastSection(false); - m_ui->ndksTreeWidget->setHeaderItem(new QTreeWidgetItem(QStringList() << tr("NDK") << tr("NDK Environment File"))); + m_ui->ndksTreeWidget->setHeaderItem(new QTreeWidgetItem(QStringList() << tr("API Level") << tr("Environment File"))); m_ui->ndksTreeWidget->setTextElideMode(Qt::ElideNone); m_ui->ndksTreeWidget->setColumnCount(2); m_autoDetectedNdks = new QTreeWidgetItem(m_ui->ndksTreeWidget); @@ -81,14 +80,14 @@ BlackBerryNDKSettingsWidget::BlackBerryNDKSettingsWidget(QWidget *parent) : m_ui->ndksTreeWidget->expandAll(); - updateNdkList(); - connect(m_ui->wizardButton, SIGNAL(clicked()), this, SLOT(launchBlackBerrySetupWizard())); connect(m_ui->addNdkButton, SIGNAL(clicked()), this, SLOT(addNdkTarget())); connect(m_ui->removeNdkButton, SIGNAL(clicked()), this, SLOT(removeNdkTarget())); connect(m_ui->activateNdkTargetButton, SIGNAL(clicked()), this, SLOT(activateNdkTarget())); connect(m_ui->deactivateNdkTargetButton, SIGNAL(clicked()), this, SLOT(deactivateNdkTarget())); connect(m_ui->ndksTreeWidget, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)), this, SLOT(updateInfoTable(QTreeWidgetItem*))); + + updateNdkList(); } void BlackBerryNDKSettingsWidget::setWizardMessageVisible(bool visible) @@ -118,7 +117,7 @@ void BlackBerryNDKSettingsWidget::launchBlackBerrySetupWizard() const const bool alreadyConfigured = blackBerryUtils.hasRegisteredKeys() && blackBerryUtils.hasDefaultCertificate(); if (alreadyConfigured) { - QMessageBox::information(0, tr("Qt Creator"), + QMessageBox::information(Core::ICore::mainWindow(), tr("Qt Creator"), tr("It appears that your BlackBerry environment has already been configured.")); return; } @@ -130,37 +129,17 @@ void BlackBerryNDKSettingsWidget::launchBlackBerrySetupWizard() const void BlackBerryNDKSettingsWidget::updateInfoTable(QTreeWidgetItem* currentItem) { - if (!currentItem) - return; - - QString envFilePath = currentItem->text(1); - if (envFilePath.isEmpty()) { - m_ui->removeNdkButton->setEnabled(false); - m_ui->activateNdkTargetButton->setEnabled(false); - m_ui->deactivateNdkTargetButton->setEnabled(false); - return; - } - - BlackBerryConfiguration *config = m_bbConfigManager->configurationFromEnvFile(Utils::FileName::fromString(envFilePath)); - if (!config) - return; + BlackBerryConfiguration *config = m_bbConfigManager->configurationFromEnvFile( + Utils::FileName::fromString(currentItem ? currentItem->text(1) : QString())); + updateUi(currentItem, config); - m_ui->baseNameLabel->setText(config->displayName()); - m_ui->ndkPathLabel->setText(QDir::toNativeSeparators(config->ndkPath())); - m_ui->hostLabel->setText(QDir::toNativeSeparators(config->qnxHost())); - m_ui->targetLabel->setText(QDir::toNativeSeparators(config->sysRoot().toString())); - m_ui->versionLabel->clear(); - // TODO: Add a versionNumber attribute for the BlackBerryConfiguration class - if (config->isAutoDetected()) { - foreach (const NdkInstallInformation &ndkInfo, QnxUtils::installedNdks()) { - if (ndkInfo.name == config->displayName()) { - m_ui->versionLabel->setText(ndkInfo.version); - break; - } - } + m_ui->informationBox->setVisible(config); + if (config) { + m_ui->baseNameLabel->setText(config->displayName()); + m_ui->hostLabel->setText(QDir::toNativeSeparators(config->qnxHost())); + m_ui->targetLabel->setText(QDir::toNativeSeparators(config->sysRoot().toString())); + m_ui->versionLabel->setText(config->version().toString()); } - - updateUi(currentItem, config); } void BlackBerryNDKSettingsWidget::updateNdkList() @@ -179,10 +158,7 @@ void BlackBerryNDKSettingsWidget::updateNdkList() item->setFont(1, font); } - if (m_autoDetectedNdks->child(0)) { - m_autoDetectedNdks->child(0)->setSelected(true); - updateInfoTable(m_autoDetectedNdks->child(0)); - } + m_ui->ndksTreeWidget->setCurrentItem(m_autoDetectedNdks->child(0)); } void BlackBerryNDKSettingsWidget::addNdkTarget() @@ -260,17 +236,21 @@ void BlackBerryNDKSettingsWidget::deactivateNdkTarget() void BlackBerryNDKSettingsWidget::updateUi(QTreeWidgetItem *item, BlackBerryConfiguration *config) { - if (!item || !config) + if (!item || !config) { + m_ui->removeNdkButton->setEnabled(false); + m_ui->activateNdkTargetButton->setEnabled(false); + m_ui->deactivateNdkTargetButton->setEnabled(false); return; + } + const bool contains = m_activatedTargets.contains(config); QFont font; - font.setBold(m_activatedTargets.contains(config)); + font.setBold(contains); item->setFont(0, font); item->setFont(1, font); - m_ui->activateNdkTargetButton->setEnabled(!m_activatedTargets.contains(config)); - m_ui->deactivateNdkTargetButton->setEnabled(m_activatedTargets.contains(config) - && m_activatedTargets.size() > 1); + m_ui->activateNdkTargetButton->setEnabled(!contains); + m_ui->deactivateNdkTargetButton->setEnabled(contains && m_activatedTargets.size() > 1); // Disable remove button for auto detected pre-10.2 NDKs (uninstall wizard doesn't handle them) m_ui->removeNdkButton->setEnabled(!(config->isAutoDetected() && QnxUtils::sdkInstallerPath(config->ndkPath()).isEmpty())); diff --git a/src/plugins/qnx/blackberryndksettingswidget.ui b/src/plugins/qnx/blackberryndksettingswidget.ui index 57d608b1a5..bb2cfeff11 100644 --- a/src/plugins/qnx/blackberryndksettingswidget.ui +++ b/src/plugins/qnx/blackberryndksettingswidget.ui @@ -150,13 +150,82 @@ <item row="0" column="0"> <layout class="QHBoxLayout" name="horizontalLayout_2"> <item> - <widget class="QTreeWidget" name="ndksTreeWidget"> - <column> - <property name="text"> - <string notr="true">1</string> - </property> - </column> - </widget> + <layout class="QVBoxLayout" name="verticalLayout_4"> + <item> + <widget class="QTreeWidget" name="ndksTreeWidget"> + <column> + <property name="text"> + <string notr="true">1</string> + </property> + </column> + </widget> + </item> + <item> + <widget class="QGroupBox" name="informationBox"> + <property name="title"> + <string>API Level Information</string> + </property> + <layout class="QFormLayout" name="formLayout"> + <item row="2" column="1"> + <widget class="QLabel" name="versionLabel"> + <property name="text"> + <string/> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Name:</string> + </property> + </widget> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="label_3"> + <property name="text"> + <string>Version:</string> + </property> + </widget> + </item> + <item row="3" column="1"> + <widget class="QLabel" name="hostLabel"> + <property name="text"> + <string/> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QLabel" name="baseNameLabel"> + <property name="text"> + <string/> + </property> + </widget> + </item> + <item row="3" column="0"> + <widget class="QLabel" name="label_4"> + <property name="text"> + <string>Host:</string> + </property> + </widget> + </item> + <item row="4" column="0"> + <widget class="QLabel" name="label_5"> + <property name="text"> + <string>Target:</string> + </property> + </widget> + </item> + <item row="4" column="1"> + <widget class="QLabel" name="targetLabel"> + <property name="text"> + <string/> + </property> + </widget> + </item> + </layout> + </widget> + </item> + </layout> </item> <item> <layout class="QVBoxLayout" name="verticalLayout_2"> @@ -175,11 +244,20 @@ </widget> </item> <item> - <widget class="Line" name="line"> + <spacer name="verticalSpacer_2"> <property name="orientation"> - <enum>Qt::Horizontal</enum> + <enum>Qt::Vertical</enum> </property> - </widget> + <property name="sizeType"> + <enum>QSizePolicy::Fixed</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>10</width> + <height>20</height> + </size> + </property> + </spacer> </item> <item> <widget class="QPushButton" name="activateNdkTargetButton"> @@ -214,170 +292,6 @@ </item> </layout> </item> - <item> - <widget class="QGroupBox" name="groupBox"> - <property name="title"> - <string>BlackBerry NDK Information</string> - </property> - <layout class="QVBoxLayout" name="verticalLayout_7"> - <item> - <layout class="QHBoxLayout" name="horizontalLayout_7"> - <item> - <widget class="QLabel" name="label"> - <property name="text"> - <string><html><head/><body><p><span style=" font-weight:600;">NDK Base Name:</span></p></body></html></string> - </property> - </widget> - </item> - <item> - <widget class="QLabel" name="baseNameLabel"> - <property name="text"> - <string/> - </property> - </widget> - </item> - <item> - <spacer name="horizontalSpacer_2"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>40</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - </layout> - </item> - <item> - <layout class="QHBoxLayout" name="horizontalLayout_8"> - <item> - <widget class="QLabel" name="label_2"> - <property name="text"> - <string><html><head/><body><p><span style=" font-weight:600;">NDK Path:</span></p></body></html></string> - </property> - </widget> - </item> - <item> - <widget class="QLabel" name="ndkPathLabel"> - <property name="text"> - <string/> - </property> - </widget> - </item> - <item> - <spacer name="horizontalSpacer_3"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>40</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - </layout> - </item> - <item> - <layout class="QHBoxLayout" name="horizontalLayout_9"> - <item> - <widget class="QLabel" name="label_3"> - <property name="text"> - <string><html><head/><body><p><span style=" font-weight:600;">Version:</span></p></body></html></string> - </property> - </widget> - </item> - <item> - <widget class="QLabel" name="versionLabel"> - <property name="text"> - <string/> - </property> - </widget> - </item> - <item> - <spacer name="horizontalSpacer_4"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>40</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - </layout> - </item> - <item> - <layout class="QHBoxLayout" name="horizontalLayout_10"> - <item> - <widget class="QLabel" name="label_4"> - <property name="text"> - <string><html><head/><body><p><span style=" font-weight:600;">Host:</span></p></body></html></string> - </property> - </widget> - </item> - <item> - <widget class="QLabel" name="hostLabel"> - <property name="text"> - <string/> - </property> - </widget> - </item> - <item> - <spacer name="horizontalSpacer_5"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>40</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - </layout> - </item> - <item> - <layout class="QHBoxLayout" name="horizontalLayout_11"> - <item> - <widget class="QLabel" name="label_5"> - <property name="text"> - <string><html><head/><body><p><span style=" font-weight:600;">Target:</span></p></body></html></string> - </property> - </widget> - </item> - <item> - <widget class="QLabel" name="targetLabel"> - <property name="text"> - <string/> - </property> - </widget> - </item> - <item> - <spacer name="horizontalSpacer_6"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>40</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - </layout> - </item> - </layout> - </widget> - </item> </layout> </widget> <resources/> diff --git a/src/plugins/qnx/blackberrysetupwizard.cpp b/src/plugins/qnx/blackberrysetupwizard.cpp index 183f31d903..d525c55808 100644 --- a/src/plugins/qnx/blackberrysetupwizard.cpp +++ b/src/plugins/qnx/blackberrysetupwizard.cpp @@ -217,6 +217,13 @@ void BlackBerrySetupWizard::debugTokenArrived(int status) break; } + BlackBerryConfigurationManager &configuration = BlackBerryConfigurationManager::instance(); + + QFile dt(configuration.defaultKeystorePath()); + + if (dt.exists()) + dt.remove(); + QMessageBox::critical(this, tr("Error"), errorString); reset(); @@ -407,8 +414,13 @@ void BlackBerrySetupWizard::requestDebugToken() BlackBerryConfigurationManager &configuration = BlackBerryConfigurationManager::instance(); + bool ok; + const QString cskPassword = m_utils.cskPassword(this, &ok); + if (!ok) + return; + m_requester->requestDebugToken(configuration.defaultDebugTokenPath(), - m_utils.cskPassword(), configuration.defaultKeystorePath(), certificatePassword(), m_devicePin); + cskPassword, configuration.defaultKeystorePath(), certificatePassword(), m_devicePin); } void BlackBerrySetupWizard::uploadDebugToken() diff --git a/src/plugins/qnx/blackberrysetupwizardkeyspage.ui b/src/plugins/qnx/blackberrysetupwizardkeyspage.ui index c5cba65fa5..a71ed39883 100644 --- a/src/plugins/qnx/blackberrysetupwizardkeyspage.ui +++ b/src/plugins/qnx/blackberrysetupwizardkeyspage.ui @@ -6,7 +6,7 @@ <rect> <x>0</x> <y>0</y> - <width>503</width> + <width>581</width> <height>222</height> </rect> </property> @@ -15,14 +15,14 @@ </property> <layout class="QVBoxLayout" name="verticalLayout"> <item> - <spacer name="verticalSpacer"> + <spacer name="verticalSpacer_2"> <property name="orientation"> <enum>Qt::Vertical</enum> </property> <property name="sizeHint" stdset="0"> <size> <width>20</width> - <height>63</height> + <height>40</height> </size> </property> </spacer> @@ -127,7 +127,7 @@ <enum>QFrame::StyledPanel</enum> </property> <property name="text"> - <string><html><head/><body><p><span style=" font-weight:600;">Legacy keys detected</span></p><p>It appears you are using legacy key files. Please visit <a href="https://developer.blackberry.com/native/documentation/core/com.qnx.doc.native_sdk.devguide/com.qnx.doc.native_sdk.devguide/topic/bbid_to_sa.html"><span style=" text-decoration: underline; color:#004f69;">this page</span></a> to upgrade your keys.</p></body></html></string> + <string><html><head/><body><p><span style=" font-weight:600;">Legacy keys detected</span></p><p>It appears you are using legacy key files. Please visit <a href="https://developer.blackberry.com/native/documentation/core/com.qnx.doc.native_sdk.devguide/topic/bbid_to_sa.html"><span style=" text-decoration: underline; color:#004f69;">this page</span></a> to upgrade your keys.</p></body></html></string> </property> <property name="textFormat"> <enum>Qt::RichText</enum> @@ -266,6 +266,125 @@ </widget> </item> <item> + <widget class="QLabel" name="helpLabel"> + <property name="palette"> + <palette> + <active> + <colorrole role="Button"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>255</red> + <green>255</green> + <blue>255</blue> + </color> + </brush> + </colorrole> + <colorrole role="Base"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>255</red> + <green>255</green> + <blue>255</blue> + </color> + </brush> + </colorrole> + <colorrole role="Window"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>255</red> + <green>255</green> + <blue>255</blue> + </color> + </brush> + </colorrole> + </active> + <inactive> + <colorrole role="Button"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>255</red> + <green>255</green> + <blue>255</blue> + </color> + </brush> + </colorrole> + <colorrole role="Base"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>255</red> + <green>255</green> + <blue>255</blue> + </color> + </brush> + </colorrole> + <colorrole role="Window"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>255</red> + <green>255</green> + <blue>255</blue> + </color> + </brush> + </colorrole> + </inactive> + <disabled> + <colorrole role="Button"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>255</red> + <green>255</green> + <blue>255</blue> + </color> + </brush> + </colorrole> + <colorrole role="Base"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>255</red> + <green>255</green> + <blue>255</blue> + </color> + </brush> + </colorrole> + <colorrole role="Window"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>255</red> + <green>255</green> + <blue>255</blue> + </color> + </brush> + </colorrole> + </disabled> + </palette> + </property> + <property name="autoFillBackground"> + <bool>true</bool> + </property> + <property name="frameShape"> + <enum>QFrame::StyledPanel</enum> + </property> + <property name="text"> + <string><html><head/><body><p><span style=" font-weight:600;">Help</span></p><p>Check <a href="https://developer.blackberry.com/CodeSigningHelp/codesignhelp.html"><span style=" text-decoration: underline; color:#004f69;">this link</span></a> if you need help with code signing.</p></body></html></string> + </property> + <property name="textFormat"> + <enum>Qt::RichText</enum> + </property> + <property name="alignment"> + <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set> + </property> + <property name="wordWrap"> + <bool>false</bool> + </property> + <property name="openExternalLinks"> + <bool>false</bool> + </property> + <property name="textInteractionFlags"> + <set>Qt::TextBrowserInteraction</set> + </property> + </widget> + </item> + <item> <widget class="QLabel" name="statusLabel"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> @@ -291,14 +410,14 @@ </widget> </item> <item> - <spacer name="verticalSpacer_2"> + <spacer name="verticalSpacer"> <property name="orientation"> <enum>Qt::Vertical</enum> </property> <property name="sizeHint" stdset="0"> <size> <width>20</width> - <height>62</height> + <height>40</height> </size> </property> </spacer> diff --git a/src/plugins/qnx/blackberrysetupwizardpages.cpp b/src/plugins/qnx/blackberrysetupwizardpages.cpp index aee8907fc5..4a92c549c8 100644 --- a/src/plugins/qnx/blackberrysetupwizardpages.cpp +++ b/src/plugins/qnx/blackberrysetupwizardpages.cpp @@ -172,6 +172,8 @@ void BlackBerrySetupWizardKeysPage::initUi() this, SLOT(showKeysMessage(QString))); connect(m_ui->legacyLabel, SIGNAL(linkActivated(QString)), this, SLOT(showKeysMessage(QString))); + connect(m_ui->helpLabel, SIGNAL(linkActivated(QString)), + this, SLOT(showKeysMessage(QString))); } void BlackBerrySetupWizardKeysPage::setComplete(bool complete) @@ -321,7 +323,7 @@ BlackBerrySetupWizardFinishPage::BlackBerrySetupWizardFinishPage(QWidget *parent : QWizardPage(parent), m_ui(0) { - setSubTitle(tr("Your environment is ready to be configured.")); + setTitle(tr("Your environment is ready to be configured.")); m_ui = new Ui::BlackBerrySetupWizardFinishPage; m_ui->setupUi(this); diff --git a/src/plugins/qnx/blackberrysigningutils.cpp b/src/plugins/qnx/blackberrysigningutils.cpp index 9cfd77a0ca..eee1410def 100644 --- a/src/plugins/qnx/blackberrysigningutils.cpp +++ b/src/plugins/qnx/blackberrysigningutils.cpp @@ -82,18 +82,24 @@ bool BlackBerrySigningUtils::hasDefaultCertificate() return keystore.exists(); } -QString BlackBerrySigningUtils::cskPassword() +QString BlackBerrySigningUtils::cskPassword(QWidget *passwordPromptParent, bool *ok) { if (m_cskPassword.isEmpty()) - m_cskPassword = promptPassword(tr("Please provide your bbidtoken.csk PIN.")); + m_cskPassword = promptPassword(tr("Please provide your bbidtoken.csk PIN."), passwordPromptParent, ok); + else if (ok) + *ok = true; return m_cskPassword; } -QString BlackBerrySigningUtils::certificatePassword() +QString BlackBerrySigningUtils::certificatePassword(QWidget *passwordPromptParent, bool *ok) { - if (m_certificatePassword.isEmpty()) - m_certificatePassword = promptPassword(tr("Please enter your certificate password.")); + if (m_certificatePassword.isEmpty()) { + m_certificatePassword = + promptPassword(tr("Please enter your certificate password."), passwordPromptParent, ok); + } else if (ok) { + *ok = true; + } return m_certificatePassword; } @@ -103,14 +109,19 @@ const BlackBerryCertificate * BlackBerrySigningUtils::defaultCertificate() const return m_defaultCertificate; } -void BlackBerrySigningUtils::openDefaultCertificate() +void BlackBerrySigningUtils::openDefaultCertificate(QWidget *passwordPromptParent) { if (m_defaultCertificate) { emit defaultCertificateLoaded(BlackBerryCertificate::Success); return; } - const QString password = certificatePassword(); + bool ok; + const QString password = certificatePassword(passwordPromptParent, &ok); + + // action has been canceled + if (!ok) + return; BlackBerryConfigurationManager &configManager = BlackBerryConfigurationManager::instance(); @@ -165,16 +176,24 @@ void BlackBerrySigningUtils::certificateLoaded(int status) emit defaultCertificateLoaded(status); } -QString BlackBerrySigningUtils::promptPassword(const QString &message) const +QString BlackBerrySigningUtils::promptPassword(const QString &message, + QWidget *dialogParent, bool *ok) const { - QInputDialog dialog; + QInputDialog dialog(dialogParent); dialog.setWindowTitle(tr("Qt Creator")); dialog.setInputMode(QInputDialog::TextInput); dialog.setLabelText(message); dialog.setTextEchoMode(QLineEdit::Password); - if (dialog.exec() == QDialog::Rejected) + if (dialog.exec() == QDialog::Rejected) { + if (ok) + *ok = false; + return QString(); + } + + if (ok) + *ok = true; return dialog.textValue(); } diff --git a/src/plugins/qnx/blackberrysigningutils.h b/src/plugins/qnx/blackberrysigningutils.h index c141ac069a..388604b4fc 100644 --- a/src/plugins/qnx/blackberrysigningutils.h +++ b/src/plugins/qnx/blackberrysigningutils.h @@ -55,12 +55,12 @@ public: bool hasLegacyKeys(); bool hasDefaultCertificate(); - QString cskPassword(); - QString certificatePassword(); + QString cskPassword(QWidget *passwordPromptParent = 0, bool *ok = 0); + QString certificatePassword(QWidget *passwordPromptParent = 0, bool *ok = 0); const BlackBerryCertificate *defaultCertificate() const; - void openDefaultCertificate(); + void openDefaultCertificate(QWidget *passwordPromptParent = 0); void setDefaultCertificate(BlackBerryCertificate *certificate); void clearCskPassword(); void clearCertificatePassword(); @@ -77,7 +77,7 @@ private: BlackBerrySigningUtils(QObject *parent = 0); - QString promptPassword(const QString &message) const; + QString promptPassword(const QString &message, QWidget *dialogParent = 0, bool *ok = 0) const; BlackBerryCertificate *m_defaultCertificate; diff --git a/src/plugins/qnx/blackberryversionnumber.cpp b/src/plugins/qnx/blackberryversionnumber.cpp new file mode 100644 index 0000000000..4d198de3f6 --- /dev/null +++ b/src/plugins/qnx/blackberryversionnumber.cpp @@ -0,0 +1,125 @@ +/************************************************************************** +** +** Copyright (C) 2013 BlackBerry Limited. All rights reserved. +** +** Contact: BlackBerry (qt@blackberry.com) +** Contact: KDAB (info@kdab.com) +** +** 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 "blackberryversionnumber.h" + +#include <QDir> + +namespace Qnx { +namespace Internal { + +static const char NONDIGIT_SEGMENT_REGEXP[] = "(?<=\\d)(?=\\D)|(?<=\\D)(?=\\d)"; + +BlackBerryVersionNumber::BlackBerryVersionNumber(const QStringList &listNumber) + : m_segments(listNumber) +{ +} + +BlackBerryVersionNumber::BlackBerryVersionNumber(const QString &version) +{ + m_segments = version.split(QLatin1Char('.')); +} + +BlackBerryVersionNumber::BlackBerryVersionNumber() +{ +} + +QString BlackBerryVersionNumber::toString() const +{ + return m_segments.join(QLatin1String(".")); +} + +bool BlackBerryVersionNumber::operator >(const BlackBerryVersionNumber &b) const +{ + int minSize = size() > b.size() ? b.size() : size(); + for (int i = 0; i < minSize; i++) { + if (segment(i) != b.segment(i)) { + // Segment can contain digits and non digits expressions + QStringList aParts = segment(i).split(QLatin1String(NONDIGIT_SEGMENT_REGEXP)); + QStringList bParts = b.segment(i).split(QLatin1String(NONDIGIT_SEGMENT_REGEXP)); + + int minPartSize = aParts.length() > bParts.length() ? bParts.length() : aParts.length(); + for (int j = 0; j < minPartSize; j++) { + bool aOk = true; + bool bOk = true; + int aInt = aParts[j].toInt(&aOk); + int bInt = bParts[j].toInt(&bOk); + + if (aOk && bOk) + return aInt > bInt; + + return aParts[j].compare(bParts[j]) > 0; + } + } + } + + return false; +} + +QString BlackBerryVersionNumber::segment(int index) const +{ + if (index < m_segments.length()) + return m_segments.at(index); + + return QString(); +} + +BlackBerryVersionNumber BlackBerryVersionNumber::fromNdkEnvFileName(const QString &ndkEnvFileName) +{ + return fromFileName(ndkEnvFileName, QRegExp(QLatin1String("^bbndk-env_(.*)$"))); +} + +BlackBerryVersionNumber BlackBerryVersionNumber::fromTargetName(const QString &targetName) +{ + return fromFileName(targetName, QRegExp(QLatin1String("^target_(.*)$"))); +} + +BlackBerryVersionNumber BlackBerryVersionNumber::fromFileName(const QString &fileName, const QRegExp ®Exp) +{ + QStringList segments; + if (regExp.exactMatch(fileName) && regExp.captureCount() == 1) + segments << regExp.cap(1).split(QLatin1Char('_')); + + return BlackBerryVersionNumber(segments); +} + +int BlackBerryVersionNumber::size() const +{ + return m_segments.length(); +} + +bool BlackBerryVersionNumber::isEmpty() const +{ + return m_segments.isEmpty(); +} + +} // namespace Internal +} // namespace Qnx diff --git a/src/plugins/qnx/blackberrycheckdevmodestep.h b/src/plugins/qnx/blackberryversionnumber.h index 44b330f16c..86cf272b75 100644 --- a/src/plugins/qnx/blackberrycheckdevmodestep.h +++ b/src/plugins/qnx/blackberryversionnumber.h @@ -29,35 +29,36 @@ ** ****************************************************************************/ -#ifndef QNX_INTERNAL_BLACKBERRYCHECKDEVMODESTEP_H -#define QNX_INTERNAL_BLACKBERRYCHECKDEVMODESTEP_H +#ifndef BLACKBERRY_VERSION_NUMBER_H +#define BLACKBERRY_VERSION_NUMBER_H -#include "blackberryabstractdeploystep.h" +#include <QStringList> namespace Qnx { namespace Internal { - -class BlackBerryCheckDevModeStep : public BlackBerryAbstractDeployStep +class BlackBerryVersionNumber { - Q_OBJECT - friend class BlackBerryCheckDevModeStepFactory; - public: - explicit BlackBerryCheckDevModeStep(ProjectExplorer::BuildStepList *bsl); + BlackBerryVersionNumber(const QStringList &segments); + BlackBerryVersionNumber(const QString &version); + BlackBerryVersionNumber(); - bool init(); - ProjectExplorer::BuildStepConfigWidget *createConfigWidget(); + int size() const; + bool isEmpty() const; + QString segment(int index) const; + QString toString() const; -protected: - BlackBerryCheckDevModeStep(ProjectExplorer::BuildStepList *bsl, BlackBerryCheckDevModeStep *bs); + static BlackBerryVersionNumber fromNdkEnvFileName(const QString &ndkEnvFileName); + static BlackBerryVersionNumber fromTargetName(const QString &targetName); + static BlackBerryVersionNumber fromFileName(const QString &fileName, const QRegExp ®Exp); - void processStarted(const ProjectExplorer::ProcessParameters ¶ms); + bool operator >(const BlackBerryVersionNumber &b) const; private: - QString password() const; + QStringList m_segments; }; } // namespace Internal } // namespace Qnx -#endif // QNX_INTERNAL_BLACKBERRYCHECKDEVMODESTEP_H +#endif // VERSIONNUMBER_H diff --git a/src/plugins/qnx/qnx.pro b/src/plugins/qnx/qnx.pro index 2f0b403b76..25df0bf0fa 100644 --- a/src/plugins/qnx/qnx.pro +++ b/src/plugins/qnx/qnx.pro @@ -69,9 +69,9 @@ SOURCES += qnxplugin.cpp \ blackberrydebugtokenuploader.cpp \ blackberrydebugtokenreader.cpp \ blackberryndkprocess.cpp \ - blackberrycheckdevmodestepfactory.cpp \ - blackberrycheckdevmodestep.cpp \ - blackberrycheckdevmodestepconfigwidget.cpp \ + blackberrycheckdebugtokenstep.cpp \ + blackberrycheckdebugtokenstepconfigwidget.cpp \ + blackberrycheckdebugtokenstepfactory.cpp \ blackberrydeviceconnection.cpp \ blackberrydeviceconnectionmanager.cpp \ blackberrydeviceinformation.cpp \ @@ -98,7 +98,8 @@ SOURCES += qnxplugin.cpp \ qnxdeviceprocesssignaloperation.cpp \ qnxdeviceprocesslist.cpp \ qnxtoolchain.cpp \ - slog2inforunner.cpp + slog2inforunner.cpp \ + blackberryversionnumber.cpp HEADERS += qnxplugin.h\ qnxconstants.h \ @@ -167,9 +168,9 @@ HEADERS += qnxplugin.h\ blackberrydebugtokenuploader.h \ blackberrydebugtokenreader.h \ blackberryndkprocess.h \ - blackberrycheckdevmodestepfactory.h \ - blackberrycheckdevmodestep.h \ - blackberrycheckdevmodestepconfigwidget.h \ + blackberrycheckdebugtokenstep.h \ + blackberrycheckdebugtokenstepconfigwidget.h \ + blackberrycheckdebugtokenstepfactory.h \ blackberrydeviceconnection.h \ blackberrydeviceconnectionmanager.h \ blackberrydeviceinformation.h \ @@ -196,7 +197,8 @@ HEADERS += qnxplugin.h\ qnxdeviceprocesssignaloperation.h \ qnxdeviceprocesslist.h \ qnxtoolchain.h \ - slog2inforunner.h + slog2inforunner.h \ + blackberryversionnumber.h FORMS += \ diff --git a/src/plugins/qnx/qnx.qbs b/src/plugins/qnx/qnx.qbs index da95f3c55e..a425414e93 100644 --- a/src/plugins/qnx/qnx.qbs +++ b/src/plugins/qnx/qnx.qbs @@ -59,12 +59,12 @@ QtcPlugin { "blackberryabstractdeploystep.h", "blackberryapplicationrunner.cpp", "blackberryapplicationrunner.h", - "blackberrycheckdevmodestep.cpp", - "blackberrycheckdevmodestep.h", - "blackberrycheckdevmodestepconfigwidget.cpp", - "blackberrycheckdevmodestepconfigwidget.h", - "blackberrycheckdevmodestepfactory.cpp", - "blackberrycheckdevmodestepfactory.h", + "blackberrycheckdebugtokenstep.cpp", + "blackberrycheckdebugtokenstep.h", + "blackberrycheckdebugtokenstepconfigwidget.cpp", + "blackberrycheckdebugtokenstepconfigwidget.h", + "blackberrycheckdebugtokenstepfactory.cpp", + "blackberrycheckdebugtokenstepfactory.h", "blackberryconfigurationmanager.cpp", "blackberryconfigurationmanager.h", "blackberrycreatepackagestep.cpp", @@ -185,6 +185,8 @@ QtcPlugin { "blackberrysetupwizardfinishpage.ui", "blackberrysigningutils.cpp", "blackberrysigningutils.h", + "blackberryversionnumber.cpp", + "blackberryversionnumber.h", "pathchooserdelegate.cpp", "pathchooserdelegate.h", "qnxtoolchain.cpp", diff --git a/src/plugins/qnx/qnxconstants.h b/src/plugins/qnx/qnxconstants.h index 2c530d4610..cdddeb8ae2 100644 --- a/src/plugins/qnx/qnxconstants.h +++ b/src/plugins/qnx/qnxconstants.h @@ -69,7 +69,7 @@ const char QNX_QNX_RUNCONFIGURATION_PREFIX[] = "Qt4ProjectManager.QNX.QNXRunConf const char QNX_CREATE_PACKAGE_BS_ID[] = "Qt4ProjectManager.QnxCreatePackageBuildStep"; const char QNX_DEPLOY_PACKAGE_BS_ID[] = "Qt4ProjectManager.QnxDeployPackageBuildStep"; -const char QNX_CHECK_DEVELOPMENT_MODE_BS_ID[] = "Qt4ProjectManager.QnxCheckDevelopmentModeBuildStep"; +const char QNX_CHECK_DEBUG_TOKEN_BS_ID[] = "Qt4ProjectManager.QnxCheckDebugTokenBuildStep"; const char QNX_PROFILEPATH_KEY[] = "Qt4ProjectManager.QnxRunConfiguration.ProFilePath"; diff --git a/src/plugins/qnx/qnxplugin.cpp b/src/plugins/qnx/qnxplugin.cpp index 7d22da8f62..8cffb77c18 100644 --- a/src/plugins/qnx/qnxplugin.cpp +++ b/src/plugins/qnx/qnxplugin.cpp @@ -49,7 +49,7 @@ #include "bardescriptoreditorfactory.h" #include "bardescriptormagicmatcher.h" #include "blackberrykeyspage.h" -#include "blackberrycheckdevmodestepfactory.h" +#include "blackberrycheckdebugtokenstepfactory.h" #include "blackberrydeviceconnectionmanager.h" #include "blackberryconfigurationmanager.h" #include "cascadesimport/cascadesimportwizard.h" @@ -91,7 +91,7 @@ bool QNXPlugin::initialize(const QStringList &arguments, QString *errorString) addAutoReleasedObject(new BlackBerryRunControlFactory); addAutoReleasedObject(new BlackBerryNDKSettingsPage); addAutoReleasedObject(new BlackBerryKeysPage); - addAutoReleasedObject(new BlackBerryCheckDevModeStepFactory); + addAutoReleasedObject(new BlackBerryCheckDebugTokenStepFactory); addAutoReleasedObject(new CascadesImportWizard); BlackBerryDeviceConnectionManager::instance()->initialize(); diff --git a/src/plugins/qtsupport/baseqtversion.cpp b/src/plugins/qtsupport/baseqtversion.cpp index 8289af639f..78d58d614b 100644 --- a/src/plugins/qtsupport/baseqtversion.cpp +++ b/src/plugins/qtsupport/baseqtversion.cpp @@ -41,7 +41,6 @@ #include <projectexplorer/toolchain.h> #include <projectexplorer/projectexplorer.h> #include <projectexplorer/headerpath.h> -#include <qtsupport/debugginghelper.h> #include <qtsupport/debugginghelperbuildtask.h> #include <qtsupport/qtsupportconstants.h> @@ -137,7 +136,6 @@ int BaseQtVersion::getUniqueId() BaseQtVersion::BaseQtVersion(const FileName &qmakeCommand, bool isAutodetected, const QString &autodetectionSource) : m_id(getUniqueId()), m_isAutodetected(isAutodetected), - m_hasDebuggingHelper(false), m_hasQmlDump(false), m_mkspecUpToDate(false), m_mkspecReadUpToDate(false), @@ -158,7 +156,6 @@ BaseQtVersion::BaseQtVersion(const FileName &qmakeCommand, bool isAutodetected, BaseQtVersion::BaseQtVersion() : m_id(-1), m_isAutodetected(false), - m_hasDebuggingHelper(false), m_hasQmlDump(false), m_mkspecUpToDate(false), m_mkspecReadUpToDate(false), @@ -911,7 +908,6 @@ void BaseQtVersion::updateVersionInfo() const m_installed = true; m_hasExamples = false; m_hasDocumentation = false; - m_hasDebuggingHelper = false; m_hasQmlDump = false; if (!queryQMakeVariables(qmakeCommand(), qmakeRunEnvironment(), &m_versionInfo)) { @@ -927,7 +923,6 @@ void BaseQtVersion::updateVersionInfo() const const QString qtHeaderData = qmakeProperty(m_versionInfo, "QT_INSTALL_HEADERS"); if (!qtInstallData.isNull()) { if (!qtInstallData.isEmpty()) { - m_hasDebuggingHelper = !DebuggingHelperLibrary::debuggingHelperLibraryByInstallData(qtInstallData).isEmpty(); m_hasQmlDump = !QmlDumpTool::toolForQtPaths(qtInstallData, qtInstallBins, qtHeaderData, false).isEmpty() || !QmlDumpTool::toolForQtPaths(qtInstallData, qtInstallBins, qtHeaderData, true).isEmpty(); @@ -1072,13 +1067,6 @@ Environment BaseQtVersion::qmakeRunEnvironment() const return Environment::systemEnvironment(); } -bool BaseQtVersion::hasGdbDebuggingHelper() const -{ - updateVersionInfo(); - return m_hasDebuggingHelper; -} - - bool BaseQtVersion::hasQmlDump() const { updateVersionInfo(); @@ -1114,14 +1102,6 @@ Environment BaseQtVersion::qmlToolsEnvironment() const return environment; } -QString BaseQtVersion::gdbDebuggingHelperLibrary() const -{ - QString qtInstallData = qmakeProperty("QT_INSTALL_DATA"); - if (qtInstallData.isEmpty()) - return QString(); - return DebuggingHelperLibrary::debuggingHelperLibraryByInstallData(qtInstallData); -} - QString BaseQtVersion::qmlDumpTool(bool debugVersion) const { const QString qtInstallData = qmakeProperty("QT_INSTALL_DATA"); @@ -1132,21 +1112,6 @@ QString BaseQtVersion::qmlDumpTool(bool debugVersion) const return QmlDumpTool::toolForQtPaths(qtInstallData, qtInstallBins, qtHeaderData, debugVersion); } -QStringList BaseQtVersion::debuggingHelperLibraryLocations() const -{ - QString qtInstallData = qmakeProperty("QT_INSTALL_DATA"); - if (qtInstallData.isEmpty()) - return QStringList(); - return DebuggingHelperLibrary::debuggingHelperLibraryDirectories(qtInstallData); -} - -bool BaseQtVersion::supportsBinaryDebuggingHelper() const -{ - if (!isValid()) - return false; - return true; -} - void BaseQtVersion::recheckDumper() { m_versionInfoUpToDate = false; diff --git a/src/plugins/qtsupport/baseqtversion.h b/src/plugins/qtsupport/baseqtversion.h index 004d53068f..431ccef273 100644 --- a/src/plugins/qtsupport/baseqtversion.h +++ b/src/plugins/qtsupport/baseqtversion.h @@ -191,12 +191,8 @@ public: static void buildDebuggingHelper(ProjectExplorer::Kit *k, int tools); void buildDebuggingHelper(ProjectExplorer::ToolChain *tc, int tools); - virtual bool supportsBinaryDebuggingHelper() const; - virtual QString gdbDebuggingHelperLibrary() const; virtual QString qmlDumpTool(bool debugVersion) const; - virtual QStringList debuggingHelperLibraryLocations() const; - virtual bool hasGdbDebuggingHelper() const; virtual bool hasQmlDump() const; virtual bool hasQmlDumpWithRelocatableFlag() const; virtual bool needsQmlDump() const; @@ -263,7 +259,6 @@ private: int m_id; bool m_isAutodetected; - mutable bool m_hasDebuggingHelper; // controlled by m_versionInfoUpToDate mutable bool m_hasQmlDump; // controlled by m_versionInfoUpToDate mutable bool m_mkspecUpToDate; mutable bool m_mkspecReadUpToDate; diff --git a/src/plugins/qtsupport/customexecutableconfigurationwidget.cpp b/src/plugins/qtsupport/customexecutableconfigurationwidget.cpp index 9efa791ce7..6437902e5f 100644 --- a/src/plugins/qtsupport/customexecutableconfigurationwidget.cpp +++ b/src/plugins/qtsupport/customexecutableconfigurationwidget.cpp @@ -55,6 +55,7 @@ CustomExecutableConfigurationWidget::CustomExecutableConfigurationWidget(CustomE layout->setMargin(0); m_executableChooser = new Utils::PathChooser(this); + m_executableChooser->setHistoryCompleter(QLatin1String("Qt.CustomExecutable.History")); m_executableChooser->setExpectedKind(Utils::PathChooser::Command); layout->addRow(tr("Executable:"), m_executableChooser); @@ -63,6 +64,7 @@ CustomExecutableConfigurationWidget::CustomExecutableConfigurationWidget(CustomE layout->addRow(tr("Arguments:"), m_commandLineArgumentsLineEdit); m_workingDirectory = new Utils::PathChooser(this); + m_workingDirectory->setHistoryCompleter(QLatin1String("WorkingDir.History")); m_workingDirectory->setExpectedKind(Utils::PathChooser::Directory); m_workingDirectory->setBaseDirectory(rc->target()->project()->projectDirectory()); diff --git a/src/plugins/qtsupport/customexecutablerunconfiguration.cpp b/src/plugins/qtsupport/customexecutablerunconfiguration.cpp index f35a4dafdb..77d91c96c1 100644 --- a/src/plugins/qtsupport/customexecutablerunconfiguration.cpp +++ b/src/plugins/qtsupport/customexecutablerunconfiguration.cpp @@ -300,16 +300,6 @@ QWidget *CustomExecutableRunConfiguration::createConfigurationWidget() return new CustomExecutableConfigurationWidget(this); } -QString CustomExecutableRunConfiguration::dumperLibrary() const -{ - return QtKitInformation::dumperLibrary(target()->kit()); -} - -QStringList CustomExecutableRunConfiguration::dumperLibraryLocations() const -{ - return QtKitInformation::dumperLibraryLocations(target()->kit()); -} - ProjectExplorer::Abi CustomExecutableRunConfiguration::abi() const { return ProjectExplorer::Abi(); // return an invalid ABI: We do not know what we will end up running! diff --git a/src/plugins/qtsupport/customexecutablerunconfiguration.h b/src/plugins/qtsupport/customexecutablerunconfiguration.h index 3d3883905a..631c35029f 100644 --- a/src/plugins/qtsupport/customexecutablerunconfiguration.h +++ b/src/plugins/qtsupport/customexecutablerunconfiguration.h @@ -69,8 +69,6 @@ public: QString commandLineArguments() const; QWidget *createConfigurationWidget(); - QString dumperLibrary() const; - QStringList dumperLibraryLocations() const; ProjectExplorer::Abi abi() const; diff --git a/src/plugins/qtsupport/debugginghelper.cpp b/src/plugins/qtsupport/debugginghelper.cpp deleted file mode 100644 index 34a3211c6e..0000000000 --- a/src/plugins/qtsupport/debugginghelper.cpp +++ /dev/null @@ -1,113 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). -** 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 "debugginghelper.h" - -#include <coreplugin/icore.h> - -#include <QCoreApplication> -#include <QDir> -#include <QStringList> - -#include <QDesktopServices> - -using namespace QtSupport; - -static inline QStringList validBinaryFilenames() -{ - return QStringList() - << QLatin1String("debug/dumper.dll") - << QLatin1String("libdumper.dylib") - << QLatin1String("libdumper.so"); -} - -QStringList DebuggingHelperLibrary::debuggingHelperLibraryDirectories(const QString &qtInstallData) -{ - const QChar slash = QLatin1Char('/'); - const uint hash = qHash(qtInstallData); - QStringList directories; - directories - << (qtInstallData + QLatin1String("/qtc-debugging-helper/")) - << QDir::cleanPath((QCoreApplication::applicationDirPath() + QLatin1String("/../qtc-debugging-helper/") + QString::number(hash))) + slash - << (QDesktopServices::storageLocation(QDesktopServices::DataLocation) + QLatin1String("/qtc-debugging-helper/") + QString::number(hash)) + slash; - return directories; -} - -static QString sourcePath() -{ - return Core::ICore::resourcePath() + QLatin1String("/debugger/"); -} - -static QStringList sourceFileNames() -{ - return QStringList() - << QLatin1String("dumper.cpp") << QLatin1String("dumper_p.h") - << QLatin1String("dumper.h") << QLatin1String("dumper.pro") - << QLatin1String("LICENSE.LGPL") << QLatin1String("LGPL_EXCEPTION.TXT"); -} - -QString DebuggingHelperLibrary::debuggingHelperLibraryByInstallData(const QString &qtInstallData) -{ - if (!Core::ICore::instance()) - return QString(); - - const QStringList directories = DebuggingHelperLibrary::debuggingHelperLibraryDirectories(qtInstallData); - const QStringList binFilenames = validBinaryFilenames(); - - return byInstallDataHelper(sourcePath(), sourceFileNames(), directories, binFilenames, false); -} - -QString DebuggingHelperLibrary::copy(const QString &qtInstallData, - QString *errorMessage) -{ - // Locations to try: - // $QTDIR/qtc-debugging-helper - // $APPLICATION-DIR/qtc-debugging-helper/$hash - // $USERDIR/qtc-debugging-helper/$hash - const QStringList directories = DebuggingHelperLibrary::debuggingHelperLibraryDirectories(qtInstallData); - - // Try to find a writeable directory. - foreach (const QString &directory, directories) - if (copyFiles(sourcePath(), sourceFileNames(), directory, errorMessage)) { - errorMessage->clear(); - return directory; - } - *errorMessage = QCoreApplication::translate("ProjectExplorer::DebuggingHelperLibrary", - "The debugger helpers could not be built in any of the directories:\n- %1\n\nReason: %2") - .arg(directories.join(QLatin1String("\n- ")), *errorMessage); - return QString(); -} - -bool DebuggingHelperLibrary::build(BuildHelperArguments arguments, QString *log, QString *errorMessage) -{ - arguments.proFilename = QLatin1String("dumper.pro"); - arguments.helperName = QCoreApplication::translate("ProjectExplorer::DebuggingHelperLibrary", - "GDB helper"); - return buildHelper(arguments, log, errorMessage); -} diff --git a/src/plugins/qtsupport/debugginghelper.ui b/src/plugins/qtsupport/debugginghelper.ui index cd7dba4e4b..87f7a168c1 100644 --- a/src/plugins/qtsupport/debugginghelper.ui +++ b/src/plugins/qtsupport/debugginghelper.ui @@ -42,13 +42,6 @@ </property> </widget> </item> - <item row="2" column="2"> - <widget class="QPushButton" name="gdbHelperBuildButton"> - <property name="text"> - <string>Build</string> - </property> - </widget> - </item> <item row="1" column="1"> <widget class="QLabel" name="qmlDumpStatus"> <property name="sizePolicy"> @@ -62,29 +55,6 @@ </property> </widget> </item> - <item row="2" column="0"> - <widget class="QLabel" name="gdbHelperLabel"> - <property name="toolTip"> - <string>Helps showing content of Qt types. Only used in older versions of GDB.</string> - </property> - <property name="text"> - <string>GDB Helper:</string> - </property> - </widget> - </item> - <item row="2" column="1"> - <widget class="QLabel" name="gdbHelperStatus"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> - <horstretch>2</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="text"> - <string notr="true">TextLabel</string> - </property> - </widget> - </item> </layout> </item> <item> diff --git a/src/plugins/qtsupport/debugginghelperbuildtask.cpp b/src/plugins/qtsupport/debugginghelperbuildtask.cpp index 1f08154c7a..b5e95bfd21 100644 --- a/src/plugins/qtsupport/debugginghelperbuildtask.cpp +++ b/src/plugins/qtsupport/debugginghelperbuildtask.cpp @@ -28,7 +28,6 @@ ****************************************************************************/ #include "debugginghelperbuildtask.h" -#include "debugginghelper.h" #include "qmldumptool.h" #include "baseqtversion.h" #include "qtversionmanager.h" @@ -117,13 +116,6 @@ DebuggingHelperBuildTask::Tools DebuggingHelperBuildTask::availableTools(const B QTC_ASSERT(version, return 0); // Check the build requirements of the tools DebuggingHelperBuildTask::Tools tools = 0; - // Gdb helpers are needed on Mac/gdb only. - foreach (const Abi &abi, version->qtAbis()) { - if (abi.os() == Abi::MacOS) { - tools |= DebuggingHelperBuildTask::GdbDebugging; - break; - } - } if (QmlDumpTool::canBuild(version)) tools |= QmlDump; return tools; @@ -169,18 +161,6 @@ bool DebuggingHelperBuildTask::buildDebuggingHelper(QFutureInterface<void> &futu arguments.mkspec = m_mkspec; arguments.environment = m_environment; - if (m_tools & GdbDebugging) { - QString output, error; - bool success = true; - - arguments.directory = DebuggingHelperLibrary::copy(m_qtInstallData, &error); - if (arguments.directory.isEmpty() - || !DebuggingHelperLibrary::build(arguments, &output, &error)) - success = false; - log(output, error); - if (!success) - return false; - } future.setProgressValue(2); if (m_tools & QmlDump) { diff --git a/src/plugins/qtsupport/debugginghelperbuildtask.h b/src/plugins/qtsupport/debugginghelperbuildtask.h index f1c25ef531..a01553aea1 100644 --- a/src/plugins/qtsupport/debugginghelperbuildtask.h +++ b/src/plugins/qtsupport/debugginghelperbuildtask.h @@ -50,9 +50,8 @@ class QTSUPPORT_EXPORT DebuggingHelperBuildTask : public QObject public: enum DebuggingHelper { - GdbDebugging = 0x01, QmlDump = 0x02, - AllTools = GdbDebugging | QmlDump + AllTools = QmlDump }; Q_DECLARE_FLAGS(Tools, DebuggingHelper) diff --git a/src/plugins/qtsupport/gettingstartedwelcomepage.cpp b/src/plugins/qtsupport/gettingstartedwelcomepage.cpp index c610935c00..bd8f045762 100644 --- a/src/plugins/qtsupport/gettingstartedwelcomepage.cpp +++ b/src/plugins/qtsupport/gettingstartedwelcomepage.cpp @@ -181,16 +181,19 @@ public: // gets called by declarative in separate thread QImage requestImage(const QString &id, QSize *size, const QSize &requestedSize) { - Q_UNUSED(size) QMutexLocker lock(&m_mutex); QUrl url = QUrl::fromEncoded(id.toLatin1()); - if (!m_fetcher.asynchronousFetchData(url)) - return QImage(); - if (m_fetcher.data().isEmpty()) + if (!m_fetcher.asynchronousFetchData(url) || m_fetcher.data().isEmpty()) { + if (size) { + size->setWidth(0); + size->setHeight(0); + } return QImage(); + } + QByteArray data = m_fetcher.data(); QBuffer imgBuffer(&data); imgBuffer.open(QIODevice::ReadOnly); @@ -198,7 +201,11 @@ public: QImage img = reader.read(); m_fetcher.clearData(); - return ScreenshotCropper::croppedImage(img, id, requestedSize); + img = ScreenshotCropper::croppedImage(img, id, requestedSize); + if (size) + *size = img.size(); + return img; + } private: Fetcher m_fetcher; @@ -321,6 +328,7 @@ QString ExamplesWelcomePage::copyToAlternativeLocation(const QFileInfo& proFileI PathChooser *chooser = new PathChooser; txt->setBuddy(chooser); chooser->setExpectedKind(PathChooser::ExistingDirectory); + chooser->setHistoryCompleter(QLatin1String("Qt.WritableExamplesDir.History")); QSettings *settings = Core::ICore::settings(); chooser->setPath(settings->value(QString::fromLatin1(C_FALLBACK_ROOT), Core::DocumentManager::projectsDirectory()).toString()); diff --git a/src/plugins/qtsupport/qtkitinformation.cpp b/src/plugins/qtsupport/qtkitinformation.cpp index 8b7000cf04..07f28a8971 100644 --- a/src/plugins/qtsupport/qtkitinformation.cpp +++ b/src/plugins/qtsupport/qtkitinformation.cpp @@ -172,22 +172,6 @@ void QtKitInformation::setQtVersion(ProjectExplorer::Kit *k, const BaseQtVersion setQtVersionId(k, v->uniqueId()); } -QString QtKitInformation::dumperLibrary(const ProjectExplorer::Kit *k) -{ - BaseQtVersion *version = QtKitInformation::qtVersion(k); - if (version) - return version->gdbDebuggingHelperLibrary(); - return QString(); -} - -QStringList QtKitInformation::dumperLibraryLocations(const ProjectExplorer::Kit *k) -{ - BaseQtVersion *version = QtKitInformation::qtVersion(k); - if (version) - return version->debuggingHelperLibraryLocations(); - return QStringList(); -} - void QtKitInformation::qtVersionsChanged(const QList<int> &addedIds, const QList<int> &removedIds, const QList<int> &changedIds) diff --git a/src/plugins/qtsupport/qtkitinformation.h b/src/plugins/qtsupport/qtkitinformation.h index e5dbcc15ed..ebd5700246 100644 --- a/src/plugins/qtsupport/qtkitinformation.h +++ b/src/plugins/qtsupport/qtkitinformation.h @@ -66,11 +66,6 @@ public: static BaseQtVersion *qtVersion(const ProjectExplorer::Kit *k); static void setQtVersion(ProjectExplorer::Kit *k, const BaseQtVersion *v); - // Information derived from the Qt version: - // FIXME: This should be part of an RunConfigurationAspect... - static QString dumperLibrary(const ProjectExplorer::Kit *k); - static QStringList dumperLibraryLocations(const ProjectExplorer::Kit *k); - private slots: void qtVersionsChanged(const QList<int> &addedIds, const QList<int> &removedIds, diff --git a/src/plugins/qtsupport/qtoptionspage.cpp b/src/plugins/qtsupport/qtoptionspage.cpp index 6ab49361b4..6518442a26 100644 --- a/src/plugins/qtsupport/qtoptionspage.cpp +++ b/src/plugins/qtsupport/qtoptionspage.cpp @@ -75,11 +75,10 @@ QtOptionsPage::QtOptionsPage() setCategoryIcon(QLatin1String(ProjectExplorer::Constants::PROJECTEXPLORER_SETTINGS_CATEGORY_ICON)); } -QWidget *QtOptionsPage::createPage(QWidget *parent) +QWidget *QtOptionsPage::widget() { - m_widget = new QtOptionsPageWidget(parent); - if (m_searchKeywords.isEmpty()) - m_searchKeywords = m_widget->searchKeywords(); + if (!m_widget) + m_widget = new QtOptionsPageWidget; return m_widget; } @@ -92,9 +91,9 @@ void QtOptionsPage::apply() m_widget->apply(); } -bool QtOptionsPage::matches(const QString &s) const +void QtOptionsPage::finish() { - return m_searchKeywords.contains(s, Qt::CaseInsensitive); + delete m_widget; } //----------------------------------------------------- @@ -173,8 +172,6 @@ QtOptionsPageWidget::QtOptionsPageWidget(QWidget *parent) connect(m_debuggingHelperUi->rebuildButton, SIGNAL(clicked()), this, SLOT(buildDebuggingHelper())); - connect(m_debuggingHelperUi->gdbHelperBuildButton, SIGNAL(clicked()), - this, SLOT(buildGdbHelper())); connect(m_debuggingHelperUi->qmlDumpBuildButton, SIGNAL(clicked()), this, SLOT(buildQmlDump())); @@ -240,8 +237,6 @@ void QtOptionsPageWidget::debuggingHelperBuildFinished(int qtVersionId, const QS item->setData(0, BuildLogRole, output); bool success = true; - if (tools & DebuggingHelperBuildTask::GdbDebugging) - success &= version->hasGdbDebuggingHelper(); if (tools & DebuggingHelperBuildTask::QmlDump) success &= version->hasQmlDump(); @@ -450,10 +445,6 @@ void QtOptionsPageWidget::buildDebuggingHelper(DebuggingHelperBuildTask::Tools t Core::ProgressManager::addTask(task, taskName, "QmakeProjectManager::BuildHelpers"); } -void QtOptionsPageWidget::buildGdbHelper() -{ - buildDebuggingHelper(DebuggingHelperBuildTask::GdbDebugging); -} void QtOptionsPageWidget::buildQmlDump() { @@ -716,28 +707,22 @@ void QtOptionsPageWidget::updateDebuggingHelperUi() m_ui->debuggingHelperWidget->setVisible(false); } else { const DebuggingHelperBuildTask::Tools availableTools = DebuggingHelperBuildTask::availableTools(version); - const bool canBuildGdbHelper = availableTools & DebuggingHelperBuildTask::GdbDebugging; const bool canBuildQmlDumper = availableTools & DebuggingHelperBuildTask::QmlDump; - const bool hasGdbHelper = !version->gdbDebuggingHelperLibrary().isEmpty(); const bool hasQmlDumper = version->hasQmlDump(); const bool needsQmlDumper = version->needsQmlDump(); - bool isBuildingGdbHelper = false; bool isBuildingQmlDumper = false; if (currentItem) { DebuggingHelperBuildTask::Tools buildingTools = currentItem->data(0, BuildRunningRole).value<DebuggingHelperBuildTask::Tools>(); - isBuildingGdbHelper = buildingTools & DebuggingHelperBuildTask::GdbDebugging; isBuildingQmlDumper = buildingTools & DebuggingHelperBuildTask::QmlDump; } // get names of tools from labels QStringList helperNames; const QChar colon = QLatin1Char(':'); - if (hasGdbHelper) - helperNames << m_debuggingHelperUi->gdbHelperLabel->text().remove(colon); if (hasQmlDumper) helperNames << m_debuggingHelperUi->qmlDumpLabel->text().remove(colon); @@ -751,21 +736,6 @@ void QtOptionsPageWidget::updateDebuggingHelperUi() m_ui->debuggingHelperWidget->setSummaryText(status); - QString gdbHelperText; - Qt::TextInteractionFlags gdbHelperTextFlags = Qt::NoTextInteraction; - if (hasGdbHelper) { - gdbHelperText = QDir::toNativeSeparators(version->gdbDebuggingHelperLibrary()); - gdbHelperTextFlags = Qt::TextSelectableByMouse; - } else { - if (canBuildGdbHelper) - gdbHelperText = tr("<i>Not yet built.</i>"); - else - gdbHelperText = tr("<i>Not needed.</i>"); - } - m_debuggingHelperUi->gdbHelperStatus->setText(gdbHelperText); - m_debuggingHelperUi->gdbHelperStatus->setTextInteractionFlags(gdbHelperTextFlags); - m_debuggingHelperUi->gdbHelperBuildButton->setEnabled(canBuildGdbHelper && !isBuildingGdbHelper); - QString qmlDumpStatusText, qmlDumpStatusToolTip; Qt::TextInteractionFlags qmlDumpStatusTextFlags = Qt::NoTextInteraction; if (hasQmlDumper) { @@ -810,10 +780,8 @@ void QtOptionsPageWidget::updateDebuggingHelperUi() const bool hasLog = currentItem && !currentItem->data(0, BuildLogRole).toString().isEmpty(); m_debuggingHelperUi->showLogButton->setEnabled(hasLog); - const bool canBuild = canBuildGdbHelper - || canBuildQmlDumper; - const bool isBuilding = isBuildingGdbHelper - || isBuildingQmlDumper; + const bool canBuild = canBuildQmlDumper; + const bool isBuilding = isBuildingQmlDumper; m_debuggingHelperUi->rebuildButton->setEnabled(canBuild && !isBuilding); m_debuggingHelperUi->toolChainComboBox->setEnabled(canBuild && !isBuilding); @@ -1010,19 +978,5 @@ QList<BaseQtVersion *> QtOptionsPageWidget::versions() const return result; } -QString QtOptionsPageWidget::searchKeywords() const -{ - QString rc; - QLatin1Char sep(' '); - QTextStream ts(&rc); - ts << sep << m_versionUi->versionNameLabel->text() - << sep << m_versionUi->pathLabel->text() - << sep << m_debuggingHelperUi->gdbHelperLabel->text() - << sep << m_debuggingHelperUi->qmlDumpLabel->text(); - - rc.remove(QLatin1Char('&')); - return rc; -} - } // namespace Internal } // namespace QtSupport diff --git a/src/plugins/qtsupport/qtoptionspage.h b/src/plugins/qtsupport/qtoptionspage.h index 703c0747e8..9fc0454d61 100644 --- a/src/plugins/qtsupport/qtoptionspage.h +++ b/src/plugins/qtsupport/qtoptionspage.h @@ -33,8 +33,9 @@ #include "debugginghelperbuildtask.h" #include <coreplugin/dialogs/ioptionspage.h> -#include <QWidget> #include <QIcon> +#include <QPointer> +#include <QWidget> QT_BEGIN_NAMESPACE class QTreeWidgetItem; @@ -63,12 +64,11 @@ class QtOptionsPageWidget : public QWidget Q_OBJECT public: - QtOptionsPageWidget(QWidget *parent); + QtOptionsPageWidget(QWidget *parent = 0); ~QtOptionsPageWidget(); QList<BaseQtVersion *> versions() const; void finish(); void apply(); - QString searchKeywords() const; private: void updateDescriptionLabel(); @@ -106,7 +106,6 @@ private slots: void updateCurrentQtName(); void buildDebuggingHelper(DebuggingHelperBuildTask::Tools tools = DebuggingHelperBuildTask::AllTools); - void buildGdbHelper(); void buildQmlDump(); void slotShowDebuggingBuildLog(); void debuggingHelperBuildFinished(int qtVersionId, const QString &output, @@ -141,14 +140,12 @@ class QtOptionsPage : public Core::IOptionsPage public: QtOptionsPage(); - QWidget *createPage(QWidget *parent); + QWidget *widget(); void apply(); - void finish() {} - bool matches(const QString &) const; + void finish(); private: - QtOptionsPageWidget *m_widget; - QString m_searchKeywords; + QPointer<QtOptionsPageWidget> m_widget; }; } //namespace Internal diff --git a/src/plugins/qtsupport/qtparser.cpp b/src/plugins/qtsupport/qtparser.cpp index 131c978729..5a0820bf35 100644 --- a/src/plugins/qtsupport/qtparser.cpp +++ b/src/plugins/qtsupport/qtparser.cpp @@ -39,10 +39,12 @@ using ProjectExplorer::Task; #define FILE_PATTERN "^(([A-Za-z]:)?[^:]+\\.[^:]+)" QtParser::QtParser() : - m_mocRegExp(QLatin1String(FILE_PATTERN"[:\\(](\\d+)\\)?:\\s([Ww]arning|[Ee]rror):\\s(.+)$")) + m_mocRegExp(QLatin1String(FILE_PATTERN"[:\\(](\\d+)\\)?:\\s([Ww]arning|[Ee]rror):\\s(.+)$")), + m_translationRegExp(QLatin1String("^([Ww]arning|[Ee]rror):\\s+(.*) in '(.*)'$")) { setObjectName(QLatin1String("QtParser")); m_mocRegExp.setMinimal(true); + m_translationRegExp.setMinimal(true); } void QtParser::stdError(const QString &line) @@ -63,6 +65,17 @@ void QtParser::stdError(const QString &line) emit addTask(task); return; } + if (m_translationRegExp.indexIn(lne) > -1) { + Task::TaskType type = Task::Warning; + if (m_translationRegExp.cap(1) == QLatin1String("Error")) + type = Task::Error; + Task task(type, m_translationRegExp.cap(2), + Utils::FileName::fromUserInput(m_translationRegExp.cap(3)) /* filename */, + -1, + ProjectExplorer::Constants::TASK_CATEGORY_COMPILE); + emit addTask(task); + return; + } IOutputParser::stdError(line); } @@ -150,6 +163,15 @@ void QtSupportPlugin::testQtOutputParser_data() Utils::FileName::fromUserInput(QLatin1String("E:/sandbox/creator/loaden/src/libs/utils/iwelcomepage.h")), 54, ProjectExplorer::Constants::TASK_CATEGORY_COMPILE)) << QString(); + QTest::newRow("translation") + << QString::fromLatin1("Warning: dropping duplicate messages in '/some/place/qtcreator_fr.qm'") + << OutputParserTester::STDERR + << QString() << QString() + << (QList<ProjectExplorer::Task>() << Task(Task::Warning, + QLatin1String("dropping duplicate messages"), + Utils::FileName::fromUserInput(QLatin1String("/some/place/qtcreator_fr.qm")), -1, + ProjectExplorer::Constants::TASK_CATEGORY_COMPILE)) + << QString(); } void QtSupportPlugin::testQtOutputParser() diff --git a/src/plugins/qtsupport/qtparser.h b/src/plugins/qtsupport/qtparser.h index c58148265d..df1d121f3f 100644 --- a/src/plugins/qtsupport/qtparser.h +++ b/src/plugins/qtsupport/qtparser.h @@ -47,6 +47,7 @@ public: private: QRegExp m_mocRegExp; + QRegExp m_translationRegExp; }; } // namespace ProjectExplorer diff --git a/src/plugins/qtsupport/qtsupport.pro b/src/plugins/qtsupport/qtsupport.pro index df7fe51a81..dcea5b3330 100644 --- a/src/plugins/qtsupport/qtsupport.pro +++ b/src/plugins/qtsupport/qtsupport.pro @@ -20,7 +20,6 @@ HEADERS += \ qtoptionspage.h \ customexecutablerunconfiguration.h \ customexecutableconfigurationwidget.h \ - debugginghelper.h \ debugginghelperbuildtask.h \ qtsupportconstants.h \ profilereader.h \ @@ -49,7 +48,6 @@ SOURCES += \ qtoptionspage.cpp \ customexecutablerunconfiguration.cpp \ customexecutableconfigurationwidget.cpp \ - debugginghelper.cpp \ debugginghelperbuildtask.cpp \ profilereader.cpp \ qtparser.cpp \ diff --git a/src/plugins/qtsupport/qtsupport.qbs b/src/plugins/qtsupport/qtsupport.qbs index 72f0597a6a..dc9060481a 100644 --- a/src/plugins/qtsupport/qtsupport.qbs +++ b/src/plugins/qtsupport/qtsupport.qbs @@ -65,8 +65,6 @@ QtcPlugin { "customexecutableconfigurationwidget.h", "customexecutablerunconfiguration.cpp", "customexecutablerunconfiguration.h", - "debugginghelper.cpp", - "debugginghelper.h", "debugginghelper.ui", "debugginghelperbuildtask.cpp", "debugginghelperbuildtask.h", diff --git a/src/plugins/qtsupport/qtversionmanager.cpp b/src/plugins/qtsupport/qtversionmanager.cpp index 5966416121..dce5c211c9 100644 --- a/src/plugins/qtsupport/qtversionmanager.cpp +++ b/src/plugins/qtsupport/qtversionmanager.cpp @@ -35,13 +35,12 @@ #include "qtfeatureprovider.h" #include "qtsupportconstants.h" -#include <qtsupport/debugginghelper.h> - #include <coreplugin/icore.h> #include <coreplugin/helpmanager.h> #include <extensionsystem/pluginmanager.h> +#include <utils/buildablehelperlibrary.h> #include <utils/filesystemwatcher.h> #include <utils/hostosinfo.h> #include <utils/persistentsettings.h> @@ -386,7 +385,7 @@ void QtVersionManager::saveQtVersions() void QtVersionManager::findSystemQt() { - FileName systemQMakePath = QtSupport::DebuggingHelperLibrary::findSystemQt(Environment::systemEnvironment()); + FileName systemQMakePath = BuildableHelperLibrary::findSystemQt(Environment::systemEnvironment()); if (systemQMakePath.isNull()) return; diff --git a/src/plugins/remotelinux/genericlinuxdeviceconfigurationwidget.cpp b/src/plugins/remotelinux/genericlinuxdeviceconfigurationwidget.cpp index 91fddca290..29d68463fc 100644 --- a/src/plugins/remotelinux/genericlinuxdeviceconfigurationwidget.cpp +++ b/src/plugins/remotelinux/genericlinuxdeviceconfigurationwidget.cpp @@ -183,6 +183,7 @@ void GenericLinuxDeviceConfigurationWidget::initGui() m_ui->portsWarningLabel->setToolTip(QLatin1String("<font color=\"red\">") + tr("You will need at least one port.") + QLatin1String("</font>")); m_ui->keyFileLineEdit->setExpectedKind(PathChooser::File); + m_ui->keyFileLineEdit->setHistoryCompleter(QLatin1String("Ssh.KeyFile.History")); m_ui->keyFileLineEdit->lineEdit()->setMinimumWidth(0); QRegExpValidator * const portsValidator = new QRegExpValidator(QRegExp(PortList::regularExpression()), this); diff --git a/src/plugins/remotelinux/genericlinuxdeviceconfigurationwizardpages.cpp b/src/plugins/remotelinux/genericlinuxdeviceconfigurationwizardpages.cpp index 5ef777d5ff..fa6144b593 100644 --- a/src/plugins/remotelinux/genericlinuxdeviceconfigurationwizardpages.cpp +++ b/src/plugins/remotelinux/genericlinuxdeviceconfigurationwizardpages.cpp @@ -59,6 +59,7 @@ GenericLinuxDeviceConfigurationWizardSetupPage::GenericLinuxDeviceConfigurationW setTitle(tr("Connection")); setSubTitle(QLatin1String(" ")); // For Qt bug (background color) d->ui.privateKeyPathChooser->setExpectedKind(PathChooser::File); + d->ui.privateKeyPathChooser->setHistoryCompleter(QLatin1String("Ssh.KeyFile.History")); d->ui.privateKeyPathChooser->setPromptDialogTitle(tr("Choose a Private Key File")); connect(d->ui.nameLineEdit, SIGNAL(textChanged(QString)), SIGNAL(completeChanged())); connect(d->ui.hostNameLineEdit, SIGNAL(textChanged(QString)), SIGNAL(completeChanged())); diff --git a/src/plugins/resourceeditor/resourceeditorw.cpp b/src/plugins/resourceeditor/resourceeditorw.cpp index a05ba174de..ada5cdc8f2 100644 --- a/src/plugins/resourceeditor/resourceeditorw.cpp +++ b/src/plugins/resourceeditor/resourceeditorw.cpp @@ -110,7 +110,7 @@ ResourceEditorW::ResourceEditorW(const Core::Context &context, // (That is because this editor instance is deleted in executeOpenWithMenuAction // in that case.) connect(m_openWithMenu, SIGNAL(triggered(QAction*)), - Core::DocumentManager::instance(), SLOT(slotExecuteOpenWithMenuAction(QAction*)), + Core::DocumentManager::instance(), SLOT(executeOpenWithMenuAction(QAction*)), Qt::QueuedConnection); connect(m_resourceEditor, SIGNAL(dirtyChanged(bool)), m_resourceDocument, SLOT(dirtyChanged(bool))); diff --git a/src/plugins/subversion/checkoutwizard.cpp b/src/plugins/subversion/checkoutwizard.cpp index 5495e29741..ed1f6b03f6 100644 --- a/src/plugins/subversion/checkoutwizard.cpp +++ b/src/plugins/subversion/checkoutwizard.cpp @@ -30,6 +30,7 @@ #include "checkoutwizard.h" #include "checkoutwizardpage.h" #include "subversionplugin.h" +#include "subversionclient.h" #include <coreplugin/iversioncontrol.h> #include <vcsbase/command.h> @@ -78,7 +79,7 @@ VcsBase::Command *CheckoutWizard::createCommand(const QList<QWizardPage*> ¶m if (settings.hasAuthentication()) { const QString user = settings.stringValue(SubversionSettings::userKey); const QString pwd = settings.stringValue(SubversionSettings::passwordKey); - args = SubversionPlugin::addAuthenticationOptions(args, user, pwd); + args = SubversionClient::addAuthenticationOptions(args, user, pwd); } VcsBase::Command *command = new VcsBase::Command(binary, workingDirectory, QProcessEnvironment::systemEnvironment()); diff --git a/src/plugins/subversion/settingspage.cpp b/src/plugins/subversion/settingspage.cpp index 7ae0998938..41dec9b870 100644 --- a/src/plugins/subversion/settingspage.cpp +++ b/src/plugins/subversion/settingspage.cpp @@ -48,6 +48,7 @@ SettingsPageWidget::SettingsPageWidget(QWidget *parent) : { m_ui.setupUi(this); m_ui.pathChooser->setExpectedKind(PathChooser::ExistingCommand); + m_ui.pathChooser->setHistoryCompleter(QLatin1String("Subversion.Command.History")); m_ui.pathChooser->setPromptDialogTitle(tr("Subversion Command")); } @@ -81,26 +82,6 @@ void SettingsPageWidget::setSettings(const SubversionSettings &s) m_ui.logCountSpinBox->setValue(s.intValue(SubversionSettings::logCountKey)); } -QString SettingsPageWidget::searchKeywords() const -{ - QString rc; - QLatin1Char sep(' '); - QTextStream(&rc) - << sep << m_ui.generalGroupBox->title() - << sep << m_ui.commandLabel->text() - << sep << m_ui.userGroupBox->title() - << sep << m_ui.usernameLabel->text() - << sep << m_ui.passwordLabel->text() - << sep << m_ui.miscGroupBox->title() - << sep << m_ui.logCountLabel->text() - << sep << m_ui.timeOutLabel->text() - << sep << m_ui.promptToSubmitCheckBox->text() - << sep << m_ui.spaceIgnorantAnnotationCheckBox->text() - ; - rc.remove(QLatin1Char('&')); - return rc; -} - SettingsPage::SettingsPage() : m_widget(0) { @@ -108,12 +89,12 @@ SettingsPage::SettingsPage() : setDisplayName(tr("Subversion")); } -QWidget *SettingsPage::createPage(QWidget *parent) +QWidget *SettingsPage::widget() { - m_widget = new SettingsPageWidget(parent); - m_widget->setSettings(SubversionPlugin::instance()->settings()); - if (m_searchKeywords.isEmpty()) - m_searchKeywords = m_widget->searchKeywords(); + if (!m_widget) { + m_widget = new SettingsPageWidget; + m_widget->setSettings(SubversionPlugin::instance()->settings()); + } return m_widget; } @@ -122,7 +103,7 @@ void SettingsPage::apply() SubversionPlugin::instance()->setSettings(m_widget->settings()); } -bool SettingsPage::matches(const QString &s) const +void SettingsPage::finish() { - return m_searchKeywords.contains(s, Qt::CaseInsensitive); + delete m_widget; } diff --git a/src/plugins/subversion/settingspage.h b/src/plugins/subversion/settingspage.h index baa6134fa4..2bb5862dfc 100644 --- a/src/plugins/subversion/settingspage.h +++ b/src/plugins/subversion/settingspage.h @@ -57,8 +57,6 @@ public: SubversionSettings settings() const; void setSettings(const SubversionSettings &); - QString searchKeywords() const; - private: Ui::SettingsPage m_ui; }; @@ -71,14 +69,12 @@ class SettingsPage : public VcsBase::VcsBaseOptionsPage public: SettingsPage(); - QWidget *createPage(QWidget *parent); + QWidget *widget(); void apply(); - void finish() { } - bool matches(const QString &) const; + void finish(); private: - QString m_searchKeywords; - SettingsPageWidget* m_widget; + QPointer<SettingsPageWidget> m_widget; }; } // namespace Subversion diff --git a/src/plugins/subversion/subversion.pro b/src/plugins/subversion/subversion.pro index 6d2d9e21c0..ae8e211b62 100644 --- a/src/plugins/subversion/subversion.pro +++ b/src/plugins/subversion/subversion.pro @@ -2,6 +2,7 @@ include(../../qtcreatorplugin.pri) HEADERS += annotationhighlighter.h \ subversionplugin.h \ + subversionclient.h \ subversioncontrol.h \ settingspage.h \ subversioneditor.h \ @@ -13,6 +14,7 @@ HEADERS += annotationhighlighter.h \ SOURCES += annotationhighlighter.cpp \ subversionplugin.cpp \ + subversionclient.cpp \ subversioncontrol.cpp \ settingspage.cpp \ subversioneditor.cpp \ diff --git a/src/plugins/subversion/subversion.qbs b/src/plugins/subversion/subversion.qbs index d213ce0f77..6c3c48286f 100644 --- a/src/plugins/subversion/subversion.qbs +++ b/src/plugins/subversion/subversion.qbs @@ -23,6 +23,8 @@ QtcPlugin { "settingspage.h", "settingspage.ui", "subversion.qrc", + "subversionclient.cpp", + "subversionclient.h", "subversionconstants.h", "subversioncontrol.cpp", "subversioncontrol.h", diff --git a/src/plugins/subversion/subversionclient.cpp b/src/plugins/subversion/subversionclient.cpp new file mode 100644 index 0000000000..27ad0c655e --- /dev/null +++ b/src/plugins/subversion/subversionclient.cpp @@ -0,0 +1,216 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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 "subversionclient.h" +#include "subversionsettings.h" + +#include <vcsbase/vcsbaseplugin.h> +#include <vcsbase/vcsbaseconstants.h> +#include <vcsbase/vcsbaseeditorparameterwidget.h> +#include <utils/synchronousprocess.h> + +#include <QDir> +#include <QFileInfo> +#include <QTextStream> +#include <QDebug> + +namespace Subversion { +namespace Internal { + +// Collect all parameters required for a diff to be able to associate them +// with a diff editor and re-run the diff with parameters. +struct SubversionDiffParameters +{ + QString workingDir; + QStringList extraOptions; + QStringList files; +}; + +// Parameter widget controlling whitespace diff mode, associated with a parameter +class SubversionDiffParameterWidget : public VcsBase::VcsBaseEditorParameterWidget +{ + Q_OBJECT +public: + explicit SubversionDiffParameterWidget(SubversionClient *client, + const SubversionDiffParameters &p, + QWidget *parent = 0); + QStringList arguments() const; + void executeCommand(); + +private: + SubversionClient *m_client; + const SubversionDiffParameters m_params; +}; + +SubversionDiffParameterWidget::SubversionDiffParameterWidget(SubversionClient *client, + const SubversionDiffParameters &p, + QWidget *parent) + : VcsBase::VcsBaseEditorParameterWidget(parent), m_client(client), m_params(p) +{ + mapSetting(addToggleButton(QLatin1String("w"), tr("Ignore Whitespace")), + client->settings()->boolPointer(SubversionSettings::diffIgnoreWhiteSpaceKey)); +} + +QStringList SubversionDiffParameterWidget::arguments() const +{ + QStringList args; + // Subversion wants" -x -<ext-args>", default being -u + const QStringList formatArguments = VcsBaseEditorParameterWidget::arguments(); + if (!formatArguments.isEmpty()) { + args << QLatin1String("-x") + << (QLatin1String("-u") + formatArguments.join(QString())); + } + return args; +} + +void SubversionDiffParameterWidget::executeCommand() +{ + m_client->diff(m_params.workingDir, m_params.files, m_params.extraOptions); +} + +SubversionClient::SubversionClient(SubversionSettings *settings) : + VcsBase::VcsBaseClient(settings) +{ +} + +SubversionSettings *SubversionClient::settings() const +{ + return dynamic_cast<SubversionSettings *>(VcsBase::VcsBaseClient::settings()); +} + +Core::Id SubversionClient::vcsEditorKind(VcsCommand cmd) const +{ + switch (cmd) { + case DiffCommand: + return "Subversion Diff Editor"; // TODO: create subversionconstants.h + default: + return Core::Id(); + } +} + +SubversionClient::Version SubversionClient::svnVersion() +{ + if (m_svnVersionBinary != settings()->binaryPath()) { + QStringList args; + args << QLatin1String("--version") << QLatin1String("-q"); + const Utils::SynchronousProcessResponse response = + VcsBase::VcsBasePlugin::runVcs(QDir().absolutePath(), settings()->binaryPath(), + args, settings()->timeOutMs()); + if (response.result == Utils::SynchronousProcessResponse::Finished && + response.exitCode == 0) { + m_svnVersionBinary = settings()->binaryPath(); + m_svnVersion = response.stdOut.trimmed(); + } else { + m_svnVersionBinary.clear(); + m_svnVersion.clear(); + } + } + + SubversionClient::Version v; + if (::sscanf(m_svnVersion.toLatin1().constData(), "%d.%d.%d", + &v.majorVersion, &v.minorVersion, &v.patchVersion) != 3) { + v.majorVersion = v.minorVersion = v.patchVersion = -1; + } + + return v; +} + +// Add authorization options to the command line arguments. +// SVN pre 1.5 does not accept "--userName" for "add", which is most likely +// an oversight. As no password is needed for the option, generally omit it. +QStringList SubversionClient::addAuthenticationOptions(const QStringList &args, + const QString &userName, + const QString &password) +{ + if (userName.isEmpty()) + return args; + if (!args.empty() && args.front() == QLatin1String("add")) + return args; + QStringList rc; + rc.push_back(QLatin1String("--username")); + rc.push_back(userName); + if (!password.isEmpty()) { + rc.push_back(QLatin1String("--password")); + rc.push_back(password); + } + rc.append(args); + return rc; +} + +void SubversionClient::diff(const QString &workingDir, const QStringList &files, + const QStringList &extraOptions) +{ + QStringList args(extraOptions); + Version v = svnVersion(); + + // --internal-diff is new in v1.7.0 + if (v.majorVersion > 1 || (v.majorVersion == 1 && v.minorVersion >= 7)) + args.append(QLatin1String("--internal-diff")); + + const bool hasAuth = settings()->hasAuthentication(); + const QString userName = hasAuth ? settings()->stringValue(SubversionSettings::userKey) : QString(); + const QString password = hasAuth ? settings()->stringValue(SubversionSettings::passwordKey) : QString(); + args = addAuthenticationOptions(args, userName, password); + + VcsBaseClient::diff(workingDir, files, args); +} + +QString SubversionClient::findTopLevelForFile(const QFileInfo &file) const +{ + Q_UNUSED(file) + return QString(); +} + +QStringList SubversionClient::revisionSpec(const QString &revision) const +{ + Q_UNUSED(revision) + return QStringList(); +} + +VcsBase::VcsBaseClient::StatusItem SubversionClient::parseStatusLine(const QString &line) const +{ + Q_UNUSED(line) + return VcsBase::VcsBaseClient::StatusItem(); +} + +VcsBase::VcsBaseEditorParameterWidget *SubversionClient::createDiffEditor( + const QString &workingDir, const QStringList &files, const QStringList &extraOptions) +{ + Q_UNUSED(extraOptions) + SubversionDiffParameters p; + p.workingDir = workingDir; + p.files = files; + p.extraOptions = extraOptions; + return new SubversionDiffParameterWidget(this, p); +} + +} // namespace Internal +} // namespace Subversion + +#include "subversionclient.moc" diff --git a/src/plugins/subversion/subversionclient.h b/src/plugins/subversion/subversionclient.h new file mode 100644 index 0000000000..e95dcea2a7 --- /dev/null +++ b/src/plugins/subversion/subversionclient.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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. +** +****************************************************************************/ + +#ifndef SUBVERSIONCLIENT_H +#define SUBVERSIONCLIENT_H + +#include "subversionsettings.h" +#include <vcsbase/vcsbaseclient.h> + +namespace Subversion { +namespace Internal { + +class SubversionSettings; + +class SubversionClient : public VcsBase::VcsBaseClient +{ + Q_OBJECT + +public: + SubversionClient(SubversionSettings *settings); + + SubversionSettings *settings() const; + void diff(const QString &workingDir, const QStringList &files, + const QStringList &extraOptions = QStringList()); + QString findTopLevelForFile(const QFileInfo &file) const; + QStringList revisionSpec(const QString &revision) const; + StatusItem parseStatusLine(const QString &line) const; + + class Version { + public: + int majorVersion; + int minorVersion; + int patchVersion; + }; + + Version svnVersion(); + + // Add authorization options to the command line arguments. + static QStringList addAuthenticationOptions(const QStringList &args, + const QString &userName = QString(), + const QString &password = QString()); + +protected: + Core::Id vcsEditorKind(VcsCommand cmd) const; + VcsBase::VcsBaseEditorParameterWidget *createDiffEditor(const QString &workingDir, + const QStringList &files, + const QStringList &extraOptions); +private: + QString m_svnVersionBinary; + QString m_svnVersion; +}; + +} // namespace Internal +} // namespace Subversion + +#endif // SUBVERSIONCLIENT_H diff --git a/src/plugins/subversion/subversionplugin.cpp b/src/plugins/subversion/subversionplugin.cpp index 906afd31be..cc068dd144 100644 --- a/src/plugins/subversion/subversionplugin.cpp +++ b/src/plugins/subversion/subversionplugin.cpp @@ -33,6 +33,7 @@ #include "subversioneditor.h" #include "subversionsubmiteditor.h" +#include "subversionclient.h" #include "subversionconstants.h" #include "subversioncontrol.h" #include "checkoutwizard.h" @@ -209,6 +210,7 @@ SubversionPlugin::SubversionPlugin() : SubversionPlugin::~SubversionPlugin() { + delete m_client; cleanCommitMessageFile(); } @@ -251,6 +253,7 @@ bool SubversionPlugin::initialize(const QStringList & /*arguments */, QString *e return false; m_settings.readSettings(Core::ICore::settings()); + m_client = new SubversionClient(&m_settings); addAutoReleasedObject(new SettingsPage); @@ -497,117 +500,7 @@ bool SubversionPlugin::submitEditorAboutToClose() void SubversionPlugin::diffCommitFiles(const QStringList &files) { - svnDiff(m_commitRepository, files); -} - -// Collect all parameters required for a diff to be able to associate them -// with a diff editor and re-run the diff with parameters. -struct SubversionDiffParameters -{ - QString workingDir; - QStringList arguments; - QStringList files; - QString diffName; -}; - -// Parameter widget controlling whitespace diff mode, associated with a parameter -class SubversionDiffParameterWidget : public VcsBase::VcsBaseEditorParameterWidget -{ - Q_OBJECT -public: - explicit SubversionDiffParameterWidget(const SubversionDiffParameters &p, QWidget *parent = 0); - -signals: - void reRunDiff(const Subversion::Internal::SubversionDiffParameters &); - -private slots: - void triggerReRun(); - -private: - const SubversionDiffParameters m_parameters; -}; - -SubversionDiffParameterWidget::SubversionDiffParameterWidget(const SubversionDiffParameters &p, QWidget *parent) : - VcsBase::VcsBaseEditorParameterWidget(parent), m_parameters(p) -{ - setBaseArguments(p.arguments); - addToggleButton(QLatin1String("w"), tr("Ignore Whitespace")); - connect(this, SIGNAL(argumentsChanged()), this, SLOT(triggerReRun())); -} - -void SubversionDiffParameterWidget::triggerReRun() -{ - SubversionDiffParameters effectiveParameters = m_parameters; - // Subversion wants" -x -<ext-args>", default being -u - const QStringList a = arguments(); - if (!a.isEmpty()) - effectiveParameters.arguments << QLatin1String("-x") << (QLatin1String("-u") + a.join(QString())); - emit reRunDiff(effectiveParameters); -} - -static inline void setWorkingDirectory(Core::IEditor *editor, const QString &wd) -{ - if (VcsBase::VcsBaseEditorWidget *ve = qobject_cast<VcsBase::VcsBaseEditorWidget*>(editor->widget())) - ve->setWorkingDirectory(wd); -} - -void SubversionPlugin::svnDiff(const QString &workingDir, const QStringList &files, QString diffname) -{ - SubversionDiffParameters p; - p.workingDir = workingDir; - p.files = files; - p.diffName = diffname; - svnDiff(p); -} - -void SubversionPlugin::svnDiff(const Subversion::Internal::SubversionDiffParameters &p) -{ - if (Subversion::Constants::debug) - qDebug() << Q_FUNC_INFO << p.files << p.diffName; - const QString source = VcsBase::VcsBaseEditorWidget::getSource(p.workingDir, p.files); - QTextCodec *codec = source.isEmpty() ? static_cast<QTextCodec *>(0) : VcsBase::VcsBaseEditorWidget::getCodec(source); - - const QString diffName = p.files.count() == 1 && p.diffName.isEmpty() ? - QFileInfo(p.files.front()).fileName() : p.diffName; - - QStringList args(QLatin1String("diff")); - Version v = svnVersion(); - if (v.majorVersion > 1 - || (v.majorVersion == 1 && v.minorVersion >= 7)) // --internal-diff is new in v1.7.0 - args.append(QLatin1String("--internal-diff")); - args.append(p.arguments); - args << p.files; - - const SubversionResponse response = - runSvn(p.workingDir, args, m_settings.timeOutMs(), 0, codec); - if (response.error) - return; - - // diff of a single file? re-use an existing view if possible to support - // the common usage pattern of continuously changing and diffing a file - const QString tag = VcsBase::VcsBaseEditorWidget::editorTag(VcsBase::DiffOutput, p.workingDir, p.files); - // Show in the same editor if diff has been executed before - if (Core::IEditor *existingEditor = VcsBase::VcsBaseEditorWidget::locateEditorByTag(tag)) { - existingEditor->document()->setContents(response.stdOut.toUtf8()); - Core::EditorManager::activateEditor(existingEditor); - setWorkingDirectory(existingEditor, p.workingDir); - return; - } - const QString title = QString::fromLatin1("svn diff %1").arg(diffName); - Core::IEditor *editor = showOutputInEditor(title, response.stdOut, VcsBase::DiffOutput, source, codec); - setWorkingDirectory(editor, p.workingDir); - VcsBase::VcsBaseEditorWidget::tagEditor(editor, tag); - SubversionEditor *diffEditorWidget = qobject_cast<SubversionEditor *>(editor->widget()); - QTC_ASSERT(diffEditorWidget, return); - - // Wire up the parameter widget to trigger a re-run on - // parameter change and 'revert' from inside the diff editor. - SubversionDiffParameterWidget *pw = new SubversionDiffParameterWidget(p); - connect(pw, SIGNAL(reRunDiff(Subversion::Internal::SubversionDiffParameters)), - this, SLOT(svnDiff(Subversion::Internal::SubversionDiffParameters))); - connect(diffEditorWidget, SIGNAL(diffChunkReverted(VcsBase::DiffChunk)), - pw, SLOT(triggerReRun())); - diffEditorWidget->setConfigurationWidget(pw); + m_client->diff(m_commitRepository, files); } SubversionSubmitEditor *SubversionPlugin::openSubversionSubmitEditor(const QString &fileName) @@ -724,15 +617,16 @@ void SubversionPlugin::diffProject() { const VcsBase::VcsBasePluginState state = currentState(); QTC_ASSERT(state.hasProject(), return); - svnDiff(state.currentProjectTopLevel(), QStringList(state.relativeCurrentProject()), - state.currentProjectName()); + const QString relativeProject = state.relativeCurrentProject(); + m_client->diff(state.currentProjectTopLevel(), + relativeProject.isEmpty() ? QStringList() : QStringList(relativeProject)); } void SubversionPlugin::diffCurrentFile() { const VcsBase::VcsBasePluginState state = currentState(); QTC_ASSERT(state.hasFile(), return); - svnDiff(state.currentFileTopLevel(), QStringList(state.relativeCurrentFile())); + m_client->diff(state.currentFileTopLevel(), QStringList(state.relativeCurrentFile())); } void SubversionPlugin::startCommitCurrentFile() @@ -842,7 +736,7 @@ void SubversionPlugin::diffRepository() { const VcsBase::VcsBasePluginState state = currentState(); QTC_ASSERT(state.hasTopLevel(), return); - svnDiff(state.topLevel(), QStringList()); + m_client->diff(state.topLevel(), QStringList()); } void SubversionPlugin::statusRepository() @@ -1092,53 +986,6 @@ SubversionResponse arguments, timeOut, flags, outputCodec); } -// Add authorization options to the command line arguments. -// SVN pre 1.5 does not accept "--userName" for "add", which is most likely -// an oversight. As no password is needed for the option, generally omit it. -QStringList SubversionPlugin::addAuthenticationOptions(const QStringList &args, - const QString &userName, const QString &password) -{ - if (userName.isEmpty()) - return args; - if (!args.empty() && args.front() == QLatin1String("add")) - return args; - QStringList rc; - rc.push_back(QLatin1String("--username")); - rc.push_back(userName); - if (!password.isEmpty()) { - rc.push_back(QLatin1String("--password")); - rc.push_back(password); - } - rc.append(args); - return rc; -} - -SubversionPlugin::Version SubversionPlugin::svnVersion() -{ - if (m_svnVersionBinary != m_settings.binaryPath()) { - QStringList args; - args << QLatin1String("--version") << QLatin1String("-q"); - const Utils::SynchronousProcessResponse response = - VcsBase::VcsBasePlugin::runVcs(QDir().absolutePath(), m_settings.binaryPath(), - args, m_settings.timeOutMs()); - if (response.result == Utils::SynchronousProcessResponse::Finished && - response.exitCode == 0) { - m_svnVersionBinary = m_settings.binaryPath(); - m_svnVersion = response.stdOut.trimmed(); - } else { - m_svnVersionBinary.clear(); - m_svnVersion.clear(); - } - } - - SubversionPlugin::Version v; - if (::sscanf(m_svnVersion.toLatin1().constData(), "%d.%d.%d", - &v.majorVersion, &v.minorVersion, &v.patchVersion) != 3) - v.majorVersion = v.minorVersion = v.patchVersion = -1; - - return v; -} - SubversionResponse SubversionPlugin::runSvn(const QString &workingDir, const QString &userName, const QString &password, const QStringList &arguments, int timeOut, @@ -1152,7 +999,7 @@ SubversionResponse SubversionPlugin::runSvn(const QString &workingDir, return response; } - const QStringList completeArguments = SubversionPlugin::addAuthenticationOptions(arguments, userName, password); + const QStringList completeArguments = SubversionClient::addAuthenticationOptions(arguments, userName, password); const Utils::SynchronousProcessResponse sp_resp = VcsBase::VcsBasePlugin::runVcs(workingDir, executable, completeArguments, timeOut, flags, outputCodec); @@ -1189,9 +1036,8 @@ Core::IEditor *SubversionPlugin::showOutputInEditor(const QString &title, const e->setSource(source); if (codec) e->setCodec(codec); - Core::IEditor *ie = e->editor(); - Core::EditorManager::activateEditor(ie); - return ie; + Core::EditorManager::activateEditor(editor); + return editor; } SubversionSettings SubversionPlugin::settings() const @@ -1448,4 +1294,3 @@ void SubversionPlugin::testLogResolving() Q_EXPORT_PLUGIN(Subversion::Internal::SubversionPlugin) -#include "subversionplugin.moc" diff --git a/src/plugins/subversion/subversionplugin.h b/src/plugins/subversion/subversionplugin.h index e4f321a813..77a1a6c786 100644 --- a/src/plugins/subversion/subversionplugin.h +++ b/src/plugins/subversion/subversionplugin.h @@ -62,7 +62,7 @@ namespace Internal { class SubversionSubmitEditor; class SubversionControl; -struct SubversionDiffParameters; +class SubversionClient; struct SubversionResponse { @@ -84,8 +84,6 @@ public: bool initialize(const QStringList &arguments, QString *errorMessage); - void svnDiff(const QString &workingDir, const QStringList &files, QString diffname = QString()); - SubversionSubmitEditor *openSubversionSubmitEditor(const QString &fileName); SubversionSettings settings() const; @@ -102,24 +100,9 @@ public: static SubversionPlugin *instance(); - // Add authorization options to the command line arguments. - static QStringList addAuthenticationOptions(const QStringList &args, - const QString &userName = QString(), - const QString &password = QString()); - - class Version { - public: - int majorVersion; - int minorVersion; - int patchVersion; - }; - - Version svnVersion(); - public slots: void vcsAnnotate(const QString &workingDir, const QString &file, const QString &revision = QString(), int lineNumber = -1); - void svnDiff(const Subversion::Internal::SubversionDiffParameters &p); private slots: void addCurrentFile(); @@ -183,6 +166,7 @@ private: const QStringList m_svnDirectories; SubversionSettings m_settings; + SubversionClient *m_client; QString m_commitMessageFileName; QString m_commitRepository; @@ -214,9 +198,6 @@ private: QAction *m_menuAction; bool m_submitActionTriggered; - QString m_svnVersionBinary; - QString m_svnVersion; - static SubversionPlugin *m_subversionPluginInstance; }; diff --git a/src/plugins/subversion/subversionsettings.cpp b/src/plugins/subversion/subversionsettings.cpp index 3c568e4bec..d7109d302d 100644 --- a/src/plugins/subversion/subversionsettings.cpp +++ b/src/plugins/subversion/subversionsettings.cpp @@ -41,6 +41,7 @@ const QLatin1String SubversionSettings::useAuthenticationKey("Authentication"); const QLatin1String SubversionSettings::userKey("User"); const QLatin1String SubversionSettings::passwordKey("Password"); const QLatin1String SubversionSettings::spaceIgnorantAnnotationKey("SpaceIgnorantAnnotation"); +const QLatin1String SubversionSettings::diffIgnoreWhiteSpaceKey("DiffIgnoreWhiteSpace"); SubversionSettings::SubversionSettings() { @@ -51,6 +52,7 @@ SubversionSettings::SubversionSettings() declareKey(userKey, QLatin1String("")); declareKey(passwordKey, QLatin1String("")); declareKey(spaceIgnorantAnnotationKey, true); + declareKey(diffIgnoreWhiteSpaceKey, false); } bool SubversionSettings::hasAuthentication() const diff --git a/src/plugins/subversion/subversionsettings.h b/src/plugins/subversion/subversionsettings.h index 2c80ce8d5d..6cfdc62f3a 100644 --- a/src/plugins/subversion/subversionsettings.h +++ b/src/plugins/subversion/subversionsettings.h @@ -42,6 +42,7 @@ public: static const QLatin1String userKey; static const QLatin1String passwordKey; static const QLatin1String spaceIgnorantAnnotationKey; + static const QLatin1String diffIgnoreWhiteSpaceKey; SubversionSettings(); bool hasAuthentication() const; diff --git a/src/plugins/texteditor/basetextdocument.cpp b/src/plugins/texteditor/basetextdocument.cpp index 3d651eca3a..9d9a395d06 100644 --- a/src/plugins/texteditor/basetextdocument.cpp +++ b/src/plugins/texteditor/basetextdocument.cpp @@ -83,6 +83,17 @@ BaseTextDocumentPrivate::BaseTextDocumentPrivate(BaseTextDocument *q) : BaseTextDocument::BaseTextDocument() : d(new BaseTextDocumentPrivate(this)) { + connect(d->m_document, SIGNAL(modificationChanged(bool)), this, SIGNAL(changed())); + + // set new document layout + QTextOption opt = d->m_document->defaultTextOption(); + opt.setTextDirection(Qt::LeftToRight); + opt.setFlags(opt.flags() | QTextOption::IncludeTrailingSpaces + | QTextOption::AddSpaceForLineAndParagraphSeparators + ); + d->m_document->setDefaultTextOption(opt); + BaseTextDocumentLayout *documentLayout = new BaseTextDocumentLayout(d->m_document); + d->m_document->setDocumentLayout(documentLayout); } BaseTextDocument::~BaseTextDocument() @@ -92,7 +103,7 @@ BaseTextDocument::~BaseTextDocument() delete d; } -QString BaseTextDocument::contents() const +QString BaseTextDocument::plainText() const { return document()->toPlainText(); } @@ -195,7 +206,7 @@ SyntaxHighlighter *BaseTextDocument::syntaxHighlighter() const return d->m_highlighter; } -ITextMarkable *BaseTextDocument::documentMarker() const +ITextMarkable *BaseTextDocument::markableInterface() const { BaseTextDocumentLayout *documentLayout = qobject_cast<BaseTextDocumentLayout *>(d->m_document->documentLayout()); @@ -319,7 +330,6 @@ void BaseTextDocument::setFilePath(const QString &newName) return; const QFileInfo fi(newName); IDocument::setFilePath(QDir::cleanPath(fi.absoluteFilePath())); - emit titleChanged(fi.fileName()); } bool BaseTextDocument::isFileReadOnly() const diff --git a/src/plugins/texteditor/basetextdocument.h b/src/plugins/texteditor/basetextdocument.h index ca3d896d57..af6bb25333 100644 --- a/src/plugins/texteditor/basetextdocument.h +++ b/src/plugins/texteditor/basetextdocument.h @@ -58,7 +58,7 @@ public: virtual ~BaseTextDocument(); // ITextEditorDocument - QString contents() const; + QString plainText() const; QString textAt(int pos, int length) const; QChar characterAt(int pos) const; @@ -72,7 +72,7 @@ public: const TabSettings &tabSettings() const; const ExtraEncodingSettings &extraEncodingSettings() const; - ITextMarkable *documentMarker() const; + ITextMarkable *markableInterface() const; // IDocument implementation. bool save(QString *errorString, const QString &fileName, bool autoSave); @@ -104,7 +104,6 @@ public: void cleanWhitespace(const QTextCursor &cursor); signals: - void titleChanged(QString title); void mimeTypeChanged(); private: diff --git a/src/plugins/texteditor/basetexteditor.cpp b/src/plugins/texteditor/basetexteditor.cpp index d361997c22..7632efc87a 100644 --- a/src/plugins/texteditor/basetexteditor.cpp +++ b/src/plugins/texteditor/basetexteditor.cpp @@ -486,11 +486,6 @@ int BaseTextEditorWidgetPrivate::visualIndent(const QTextBlock &block) const return 0; } -ITextMarkable *BaseTextEditorWidget::markableInterface() const -{ - return baseTextDocument()->documentMarker(); -} - BaseTextEditor *BaseTextEditorWidget::editor() const { if (!d->m_editor) { @@ -498,8 +493,6 @@ BaseTextEditor *BaseTextEditorWidget::editor() const d->m_codeAssistant->configure(d->m_editor); connect(this, SIGNAL(textChanged()), d->m_editor, SIGNAL(contentsChanged())); - connect(qobject_cast<BaseTextDocument *>(d->m_editor->document()),SIGNAL(mimeTypeChanged()), - d->m_codeAssistant.data(), SLOT(reconfigure())); } return d->m_editor; } @@ -637,7 +630,7 @@ void BaseTextEditorWidget::setChangeSet(const Utils::ChangeSet &changeSet) } } -Core::IDocument *BaseTextEditorWidget::editorDocument() const +BaseTextDocument *BaseTextEditorWidget::baseTextDocument() const { return d->m_document.data(); } @@ -673,9 +666,6 @@ void BaseTextEditorWidget::editorContentsChange(int position, int charsRemoved, d->snippetCheckCursor(cursor); } - if (doc->isRedoAvailable()) - emit editor()->contentsChangedBecauseOfUndo(); - if (charsAdded != 0 && document()->characterAt(position + charsAdded - 1).isPrint()) d->m_assistRelevantContentAdded = true; } @@ -2089,12 +2079,6 @@ void BaseTextEditorWidget::duplicateFrom(BaseTextEditorWidget *widget) d->m_document = widget->d->m_document; } -QSharedPointer<BaseTextDocument> BaseTextEditorWidget::baseTextDocument() const -{ - return d->m_document; -} - - void BaseTextEditorWidget::setBaseTextDocument(const QSharedPointer<BaseTextDocument> &doc) { if (!doc.isNull()) { @@ -2451,7 +2435,6 @@ BaseTextEditorWidgetPrivate::BaseTextEditorWidgetPrivate() m_linkPressed(false), m_delayedUpdateTimer(0), m_editor(0), - m_actionHack(0), m_inBlockSelectionMode(false), m_moveLineUndoHack(false), m_findScopeVerticalBlockSelectionFirstColumn(-1), @@ -2473,34 +2456,23 @@ BaseTextEditorWidgetPrivate::~BaseTextEditorWidgetPrivate() void BaseTextEditorWidgetPrivate::setupDocumentSignals(const QSharedPointer<BaseTextDocument> &document) { - QSharedPointer<BaseTextDocument> oldDocument = q->baseTextDocument(); - if (!oldDocument.isNull()) { + BaseTextDocument *oldDocument = q->baseTextDocument(); + if (oldDocument) { q->disconnect(oldDocument->document(), 0, q, 0); - q->disconnect(oldDocument.data(), 0, q, 0); - q->disconnect(q, 0, oldDocument.data(), 0); + q->disconnect(oldDocument, 0, q, 0); + q->disconnect(q, 0, oldDocument, 0); } QTextDocument *doc = document->document(); - BaseTextDocumentLayout *documentLayout = qobject_cast<BaseTextDocumentLayout*>(doc->documentLayout()); - if (!documentLayout) { - QTextOption opt = doc->defaultTextOption(); - opt.setTextDirection(Qt::LeftToRight); - opt.setFlags(opt.flags() | QTextOption::IncludeTrailingSpaces - | QTextOption::AddSpaceForLineAndParagraphSeparators - ); - doc->setDefaultTextOption(opt); - documentLayout = new BaseTextDocumentLayout(doc); - doc->setDocumentLayout(documentLayout); - } - q->setDocument(doc); q->setCursorWidth(2); // Applies to the document layout + BaseTextDocumentLayout *documentLayout = qobject_cast<BaseTextDocumentLayout*>(doc->documentLayout()); + QTC_CHECK(documentLayout); QObject::connect(documentLayout, SIGNAL(updateBlock(QTextBlock)), q, SLOT(slotUpdateBlockNotify(QTextBlock))); QObject::connect(documentLayout, SIGNAL(updateExtraArea()), q, SLOT(slotUpdateExtraArea())); QObject::connect(q, SIGNAL(requestBlockUpdate(QTextBlock)), documentLayout, SIGNAL(updateBlock(QTextBlock))); QObject::connect(doc, SIGNAL(modificationChanged(bool)), q, SIGNAL(changed())); - QObject::connect(q, SIGNAL(changed()), document.data(), SIGNAL(changed())); QObject::connect(doc, SIGNAL(contentsChange(int,int,int)), q, SLOT(editorContentsChange(int,int,int)), Qt::DirectConnection); QObject::connect(document.data(), SIGNAL(aboutToReload()), q, SLOT(documentAboutToBeReloaded())); @@ -5182,16 +5154,6 @@ void BaseTextEditorWidget::_q_highlightBlocks() } } -void BaseTextEditorWidget::setActionHack(QObject *hack) -{ - d->m_actionHack = hack; -} - -QObject *BaseTextEditorWidget::actionHack() const -{ - return d->m_actionHack; -} - void BaseTextEditorWidget::changeEvent(QEvent *e) { QPlainTextEdit::changeEvent(e); @@ -6147,7 +6109,7 @@ void BaseTextEditorWidget::appendStandardContextMenuActions(QMenu *menu) if (a && a->isEnabled()) menu->addAction(a); - QSharedPointer<BaseTextDocument> doc = baseTextDocument(); + BaseTextDocument *doc = baseTextDocument(); if (doc->codec()->name() == QByteArray("UTF-8") && doc->supportsUtf8Bom()) { a = Core::ActionManager::command(Constants::SWITCH_UTF8BOM)->action(); if (a && a->isEnabled()) { diff --git a/src/plugins/texteditor/basetexteditor.h b/src/plugins/texteditor/basetexteditor.h index d464d879b6..cfe95b7464 100644 --- a/src/plugins/texteditor/basetexteditor.h +++ b/src/plugins/texteditor/basetexteditor.h @@ -30,6 +30,7 @@ #ifndef BASETEXTEDITOR_H #define BASETEXTEDITOR_H +#include "basetextdocument.h" #include "itexteditor.h" #include "codeassist/assistenums.h" @@ -69,7 +70,6 @@ namespace Internal { class ITextMarkable; -class BaseTextDocument; class BaseTextEditor; class FontSettings; class BehaviorSettings; @@ -135,12 +135,12 @@ public: const Utils::ChangeSet &changeSet() const; void setChangeSet(const Utils::ChangeSet &changeSet); - // EditorInterface - Core::IDocument *editorDocument() const; + BaseTextDocument *baseTextDocument() const; + + // IEditor virtual bool open(QString *errorString, const QString &fileName, const QString &realFileName); QByteArray saveState() const; bool restoreState(const QByteArray &state); - void gotoLine(int line, int column = 0); int position(ITextEditor::PositionOperation posOp = ITextEditor::Current, @@ -148,7 +148,6 @@ public: void convertPosition(int pos, int *line, int *column) const; BaseTextEditor *editor() const; - ITextMarkable *markableInterface() const; void print(QPrinter *); @@ -209,9 +208,6 @@ public: int columnCount() const; int rowCount() const; - void setActionHack(QObject *hack); - QObject *actionHack() const; - void setReadOnly(bool b); void setTextCursor(const QTextCursor &cursor); @@ -362,7 +358,6 @@ public: void duplicateFrom(BaseTextEditorWidget *editor); protected: - QSharedPointer<BaseTextDocument> baseTextDocument() const; void setBaseTextDocument(const QSharedPointer<BaseTextDocument> &doc); void setDefaultPath(const QString &defaultPath); @@ -597,9 +592,10 @@ public: friend class BaseTextEditorWidget; BaseTextEditorWidget *editorWidget() const { return m_editorWidget; } + BaseTextDocument *baseTextDocument() { return m_editorWidget->baseTextDocument(); } // IEditor - Core::IDocument *document() { return m_editorWidget->editorDocument(); } + Core::IDocument *document() { return m_editorWidget->baseTextDocument(); } bool open(QString *errorString, const QString &fileName, const QString &realFileName); QByteArray saveState() const { return m_editorWidget->saveState(); } @@ -624,8 +620,6 @@ public: QString selectedText() const; - ITextMarkable *markableInterface() { return m_editorWidget->markableInterface(); } - QString contextHelpId() const; // from IContext // ITextEditor diff --git a/src/plugins/texteditor/basetexteditor_p.h b/src/plugins/texteditor/basetexteditor_p.h index c6d823b8ed..ff712e483a 100644 --- a/src/plugins/texteditor/basetexteditor_p.h +++ b/src/plugins/texteditor/basetexteditor_p.h @@ -49,7 +49,6 @@ namespace TextEditor { class BaseTextDocument; -class TextEditorActionHandler; class CodeAssistant; namespace Internal { @@ -102,7 +101,6 @@ public: BaseTextEditorWidgetPrivate(); ~BaseTextEditorWidgetPrivate(); - void setupBasicEditActions(TextEditorActionHandler *actionHandler); void setupDocumentSignals(const QSharedPointer<BaseTextDocument> &document); void updateLineSelectionColor(); @@ -195,8 +193,6 @@ public: BaseTextEditor *m_editor; - QObject *m_actionHack; - QList<QTextEdit::ExtraSelection> m_extraSelections[BaseTextEditorWidget::NExtraSelectionKinds]; // block selection mode diff --git a/src/plugins/texteditor/basetextmark.cpp b/src/plugins/texteditor/basetextmark.cpp index f7ccdb7f4d..6d4e06d913 100644 --- a/src/plugins/texteditor/basetextmark.cpp +++ b/src/plugins/texteditor/basetextmark.cpp @@ -60,14 +60,11 @@ void BaseTextMarkRegistry::add(BaseTextMark *mark) { m_marks[FileName::fromString(mark->fileName())].insert(mark); DocumentModel *documentModel = EditorManager::documentModel(); - IDocument *document = documentModel->documentForFilePath(mark->fileName()); + ITextEditorDocument *document + = qobject_cast<ITextEditorDocument*>(documentModel->documentForFilePath(mark->fileName())); if (!document) return; - // TODO: markableInterface should be moved to ITextEditorDocument - if (ITextEditor *textEditor - = qobject_cast<ITextEditor *>(documentModel->editorsForDocument(document).first())) { - textEditor->markableInterface()->addMark(mark); - } + document->markableInterface()->addMark(mark); } bool BaseTextMarkRegistry::remove(BaseTextMark *mark) @@ -77,16 +74,14 @@ bool BaseTextMarkRegistry::remove(BaseTextMark *mark) void BaseTextMarkRegistry::editorOpened(Core::IEditor *editor) { - ITextEditor *textEditor = qobject_cast<ITextEditor *>(editor); - if (!textEditor) + ITextEditorDocument *document = qobject_cast<ITextEditorDocument *>(editor ? editor->document() : 0); + if (!document) return; - if (!m_marks.contains(FileName::fromString(editor->document()->filePath()))) + if (!m_marks.contains(FileName::fromString(document->filePath()))) return; - foreach (BaseTextMark *mark, m_marks.value(FileName::fromString(editor->document()->filePath()))) { - ITextMarkable *markableInterface = textEditor->markableInterface(); - markableInterface->addMark(mark); - } + foreach (BaseTextMark *mark, m_marks.value(FileName::fromString(document->filePath()))) + document->markableInterface()->addMark(mark); } void BaseTextMarkRegistry::documentRenamed(IDocument *document, const @@ -102,7 +97,7 @@ void BaseTextMarkRegistry::documentRenamed(IDocument *document, const return; QSet<BaseTextMark *> toBeMoved; - foreach (ITextMark *mark, baseTextDocument->documentMarker()->marks()) + foreach (ITextMark *mark, baseTextDocument->markableInterface()->marks()) if (BaseTextMark *baseTextMark = dynamic_cast<BaseTextMark *>(mark)) toBeMoved.insert(baseTextMark); diff --git a/src/plugins/texteditor/behaviorsettingspage.cpp b/src/plugins/texteditor/behaviorsettingspage.cpp index c232fc7485..2933e5fb99 100644 --- a/src/plugins/texteditor/behaviorsettingspage.cpp +++ b/src/plugins/texteditor/behaviorsettingspage.cpp @@ -49,6 +49,7 @@ #include <qmljseditor/qmljseditorconstants.h> #include <qmljstools/qmljstoolsconstants.h> +#include <QPointer> #include <QSettings> #include <QTextCodec> @@ -59,6 +60,7 @@ struct BehaviorSettingsPage::BehaviorSettingsPagePrivate explicit BehaviorSettingsPagePrivate(const BehaviorSettingsPageParameters &p); const BehaviorSettingsPageParameters m_parameters; + QPointer<QWidget> m_widget; Internal::Ui::BehaviorSettingsPage *m_page; void init(); @@ -70,8 +72,6 @@ struct BehaviorSettingsPage::BehaviorSettingsPagePrivate StorageSettings m_storageSettings; BehaviorSettings m_behaviorSettings; ExtraEncodingSettings m_extraEncodingSettings; - - QString m_searchKeywords; }; BehaviorSettingsPage::BehaviorSettingsPagePrivate::BehaviorSettingsPagePrivate @@ -114,30 +114,28 @@ BehaviorSettingsPage::~BehaviorSettingsPage() delete d; } -QWidget *BehaviorSettingsPage::createPage(QWidget *parent) +QWidget *BehaviorSettingsPage::widget() { - QWidget *w = new QWidget(parent); - d->m_page = new Internal::Ui::BehaviorSettingsPage; - d->m_page->setupUi(w); - if (Utils::HostOsInfo::isMacHost()) - d->m_page->gridLayout->setContentsMargins(-1, 0, -1, 0); // don't ask. - d->m_pageCodeStyle = new SimpleCodeStylePreferences(w); - d->m_pageCodeStyle->setDelegatingPool(d->m_codeStyle->delegatingPool()); - d->m_pageCodeStyle->setTabSettings(d->m_codeStyle->tabSettings()); - d->m_pageCodeStyle->setCurrentDelegate(d->m_codeStyle->currentDelegate()); - d->m_page->behaviorWidget->setCodeStyle(d->m_pageCodeStyle); - - TabSettingsWidget *tabSettingsWidget = d->m_page->behaviorWidget->tabSettingsWidget(); - tabSettingsWidget->setCodingStyleWarningVisible(true); - connect(tabSettingsWidget, SIGNAL(codingStyleLinkClicked(TextEditor::TabSettingsWidget::CodingStyleLink)), - this, SLOT(openCodingStylePreferences(TextEditor::TabSettingsWidget::CodingStyleLink))); - - settingsToUI(); - - if (d->m_searchKeywords.isEmpty()) - d->m_searchKeywords = d->m_page->behaviorWidget->collectUiKeywords(); - - return w; + if (!d->m_widget) { + d->m_widget = new QWidget; + d->m_page = new Internal::Ui::BehaviorSettingsPage; + d->m_page->setupUi(d->m_widget); + if (Utils::HostOsInfo::isMacHost()) + d->m_page->gridLayout->setContentsMargins(-1, 0, -1, 0); // don't ask. + d->m_pageCodeStyle = new SimpleCodeStylePreferences(d->m_widget); + d->m_pageCodeStyle->setDelegatingPool(d->m_codeStyle->delegatingPool()); + d->m_pageCodeStyle->setTabSettings(d->m_codeStyle->tabSettings()); + d->m_pageCodeStyle->setCurrentDelegate(d->m_codeStyle->currentDelegate()); + d->m_page->behaviorWidget->setCodeStyle(d->m_pageCodeStyle); + + TabSettingsWidget *tabSettingsWidget = d->m_page->behaviorWidget->tabSettingsWidget(); + tabSettingsWidget->setCodingStyleWarningVisible(true); + connect(tabSettingsWidget, SIGNAL(codingStyleLinkClicked(TextEditor::TabSettingsWidget::CodingStyleLink)), + this, SLOT(openCodingStylePreferences(TextEditor::TabSettingsWidget::CodingStyleLink))); + + settingsToUI(); + } + return d->m_widget; } void BehaviorSettingsPage::apply() @@ -227,6 +225,7 @@ void BehaviorSettingsPage::settingsToUI() void BehaviorSettingsPage::finish() { + delete d->m_widget; if (!d->m_page) // page was never shown return; delete d->m_page; @@ -263,11 +262,6 @@ const ExtraEncodingSettings &BehaviorSettingsPage::extraEncodingSettings() const return d->m_extraEncodingSettings; } -bool BehaviorSettingsPage::matches(const QString &s) const -{ - return d->m_searchKeywords.contains(s, Qt::CaseInsensitive); -} - void BehaviorSettingsPage::openCodingStylePreferences(TabSettingsWidget::CodingStyleLink link) { diff --git a/src/plugins/texteditor/behaviorsettingspage.h b/src/plugins/texteditor/behaviorsettingspage.h index d8126c4e0f..ba93f2aa51 100644 --- a/src/plugins/texteditor/behaviorsettingspage.h +++ b/src/plugins/texteditor/behaviorsettingspage.h @@ -62,10 +62,9 @@ public: ~BehaviorSettingsPage(); // IOptionsPage - QWidget *createPage(QWidget *parent); + QWidget *widget(); void apply(); void finish(); - bool matches(const QString &s) const; ICodeStylePreferences *codeStyle() const; CodeStylePool *codeStylePool() const; diff --git a/src/plugins/texteditor/behaviorsettingswidget.cpp b/src/plugins/texteditor/behaviorsettingswidget.cpp index 218f0f8eb4..b7b9948e59 100644 --- a/src/plugins/texteditor/behaviorsettingswidget.cpp +++ b/src/plugins/texteditor/behaviorsettingswidget.cpp @@ -223,35 +223,6 @@ void BehaviorSettingsWidget::assignedExtraEncodingSettings( (ExtraEncodingSettings::Utf8BomSetting)d->m_ui.utf8BomBox->currentIndex(); } -QString BehaviorSettingsWidget::collectUiKeywords() const -{ - static const QLatin1Char sep(' '); - QString keywords; - QTextStream(&keywords) - << sep << d->m_ui.tabPreferencesWidget->searchKeywords() - << sep << d->m_ui.autoIndent->text() - << sep << d->m_ui.smartBackspaceLabel->text() - << sep << d->m_ui.tabKeyBehaviorLabel->text() - << sep << d->m_ui.cleanWhitespace->text() - << sep << d->m_ui.inEntireDocument->text() - << sep << d->m_ui.cleanIndentation->text() - << sep << d->m_ui.addFinalNewLine->text() - << sep << d->m_ui.encodingLabel->text() - << sep << d->m_ui.utf8BomLabel->text() - << sep << d->m_ui.mouseNavigation->text() - << sep << d->m_ui.scrollWheelZooming->text() - << sep << d->m_ui.helpTooltipsLabel->text() - << sep << d->m_ui.constrainTooltipsBox->itemText(0) - << sep << d->m_ui.constrainTooltipsBox->itemText(1) - << sep << d->m_ui.camelCaseNavigation->text() - << sep << d->m_ui.keyboardTooltips->text() - << sep << d->m_ui.groupBoxStorageSettings->title() - << sep << d->m_ui.groupBoxEncodings->title() - << sep << d->m_ui.groupBoxMouse->title(); - keywords.remove(QLatin1Char('&')); - return keywords; -} - TabSettingsWidget *BehaviorSettingsWidget::tabSettingsWidget() const { return d->m_ui.tabPreferencesWidget->tabSettingsWidget(); diff --git a/src/plugins/texteditor/behaviorsettingswidget.h b/src/plugins/texteditor/behaviorsettingswidget.h index 11aac48594..b840d67534 100644 --- a/src/plugins/texteditor/behaviorsettingswidget.h +++ b/src/plugins/texteditor/behaviorsettingswidget.h @@ -76,8 +76,6 @@ public: void setAssignedExtraEncodingSettings(const ExtraEncodingSettings &encodingSettings); void assignedExtraEncodingSettings(ExtraEncodingSettings *encodingSettings) const; - QString collectUiKeywords() const; - TabSettingsWidget *tabSettingsWidget() const; signals: diff --git a/src/plugins/texteditor/behaviorsettingswidget.ui b/src/plugins/texteditor/behaviorsettingswidget.ui index 324313b74a..5aeb0f37c5 100644 --- a/src/plugins/texteditor/behaviorsettingswidget.ui +++ b/src/plugins/texteditor/behaviorsettingswidget.ui @@ -7,7 +7,7 @@ <x>0</x> <y>0</y> <width>802</width> - <height>416</height> + <height>441</height> </rect> </property> <layout class="QHBoxLayout" name="horizontalLayout"> @@ -207,7 +207,7 @@ Specifies how backspace interacts with indentation. </sizepolicy> </property> <property name="toolTip"> - <string>Clean whitespace in entire document instead of only for changed parts.</string> + <string>Cleans whitespace in entire document instead of only for changed parts.</string> </property> <property name="text"> <string>In entire &document</string> @@ -220,7 +220,7 @@ Specifies how backspace interacts with indentation. <bool>false</bool> </property> <property name="toolTip"> - <string>Correct leading whitespace according to tab settings.</string> + <string>Corrects leading whitespace according to tab settings.</string> </property> <property name="text"> <string>Clean indentation</string> @@ -230,7 +230,7 @@ Specifies how backspace interacts with indentation. <item row="3" column="0" colspan="2"> <widget class="QCheckBox" name="addFinalNewLine"> <property name="toolTip"> - <string>Always write a newline character at the end of the file.</string> + <string>Always writes a newline character at the end of the file.</string> </property> <property name="text"> <string>&Ensure newline at end of file</string> diff --git a/src/plugins/texteditor/codeassist/codeassistant.cpp b/src/plugins/texteditor/codeassist/codeassistant.cpp index d2618bc5ea..4d768a369e 100644 --- a/src/plugins/texteditor/codeassist/codeassistant.cpp +++ b/src/plugins/texteditor/codeassist/codeassistant.cpp @@ -44,11 +44,11 @@ #include <extensionsystem/pluginmanager.h> #include <utils/qtcassert.h> -#include <QObject> +#include <QKeyEvent> #include <QList> +#include <QObject> +#include <QScopedPointer> #include <QTimer> -#include <QDebug> -#include <QKeyEvent> using namespace TextEditor; using namespace Internal; @@ -185,6 +185,8 @@ void CodeAssistantPrivate::configure(BaseTextEditor *textEditor) filterEditorSpecificProviders(&m_quickFixProviders, m_textEditor->id()); m_textEditor->editorWidget()->installEventFilter(this); + connect(m_textEditor->baseTextDocument(),SIGNAL(mimeTypeChanged()), + m_q, SLOT(reconfigure())); } void CodeAssistantPrivate::reconfigure() diff --git a/src/plugins/texteditor/displaysettingspage.cpp b/src/plugins/texteditor/displaysettingspage.cpp index 9f3adbef4e..6005e5acdb 100644 --- a/src/plugins/texteditor/displaysettingspage.cpp +++ b/src/plugins/texteditor/displaysettingspage.cpp @@ -33,6 +33,7 @@ #include <coreplugin/icore.h> +#include <QPointer> #include <QTextStream> using namespace TextEditor; @@ -42,9 +43,9 @@ struct DisplaySettingsPage::DisplaySettingsPagePrivate explicit DisplaySettingsPagePrivate(const DisplaySettingsPageParameters &p); const DisplaySettingsPageParameters m_parameters; + QPointer<QWidget> m_widget; Internal::Ui::DisplaySettingsPage *m_page; DisplaySettings m_displaySettings; - QString m_searchKeywords; }; DisplaySettingsPage::DisplaySettingsPagePrivate::DisplaySettingsPagePrivate @@ -68,28 +69,15 @@ DisplaySettingsPage::~DisplaySettingsPage() delete d; } -QWidget *DisplaySettingsPage::createPage(QWidget *parent) +QWidget *DisplaySettingsPage::widget() { - QWidget *w = new QWidget(parent); - d->m_page = new Internal::Ui::DisplaySettingsPage; - d->m_page->setupUi(w); - settingsToUI(); - if (d->m_searchKeywords.isEmpty()) { - QTextStream(&d->m_searchKeywords) << d->m_page->displayLineNumbers->text() - << ' ' << d->m_page->highlightCurrentLine->text() - << ' ' << d->m_page->displayFoldingMarkers->text() - << ' ' << d->m_page->highlightBlocks->text() - << ' ' << d->m_page->visualizeWhitespace->text() - << ' ' << d->m_page->animateMatchingParentheses->text() - << ' ' << d->m_page->highlightMatchingParentheses->text() - << ' ' << d->m_page->enableTextWrapping->text() - << ' ' << d->m_page->autoFoldFirstComment->text() - << ' ' << d->m_page->centerOnScroll->text() - << ' ' << d->m_page->openLinksInNextSplit->text() - << ' ' << d->m_page->displayFileEncoding->text(); - d->m_searchKeywords.remove(QLatin1Char('&')); + if (!d->m_widget) { + d->m_widget = new QWidget; + d->m_page = new Internal::Ui::DisplaySettingsPage; + d->m_page->setupUi(d->m_widget); + settingsToUI(); } - return w; + return d->m_widget; } void DisplaySettingsPage::apply() @@ -104,6 +92,7 @@ void DisplaySettingsPage::apply() void DisplaySettingsPage::finish() { + delete d->m_widget; if (!d->m_page) // page was never shown return; delete d->m_page; @@ -163,8 +152,3 @@ void DisplaySettingsPage::setDisplaySettings(const DisplaySettings &newDisplaySe emit displaySettingsChanged(newDisplaySettings); } } - -bool DisplaySettingsPage::matches(const QString &s) const -{ - return d->m_searchKeywords.contains(s, Qt::CaseInsensitive); -} diff --git a/src/plugins/texteditor/displaysettingspage.h b/src/plugins/texteditor/displaysettingspage.h index 7c0483406e..6bbf516587 100644 --- a/src/plugins/texteditor/displaysettingspage.h +++ b/src/plugins/texteditor/displaysettingspage.h @@ -55,10 +55,9 @@ public: ~DisplaySettingsPage(); // IOptionsPage - QWidget *createPage(QWidget *parent); + QWidget *widget(); void apply(); void finish(); - bool matches(const QString &s) const; const DisplaySettings &displaySettings() const; diff --git a/src/plugins/texteditor/fontsettingspage.cpp b/src/plugins/texteditor/fontsettingspage.cpp index 58ff3ed36c..1a224330d5 100644 --- a/src/plugins/texteditor/fontsettingspage.cpp +++ b/src/plugins/texteditor/fontsettingspage.cpp @@ -36,14 +36,15 @@ #include <utils/stringutils.h> #include <utils/qtcassert.h> -#include <QDebug> -#include <QSettings> -#include <QTimer> #include <QFileDialog> #include <QFontDatabase> #include <QInputDialog> #include <QMessageBox> #include <QPalette> +#include <QPointer> +#include <QSettings> +#include <QTimer> +#include <QDebug> namespace TextEditor { namespace Internal { @@ -122,10 +123,10 @@ public: TextEditor::FormatDescriptions m_descriptions; FontSettings m_value; FontSettings m_lastValue; + QPointer<QWidget> m_widget; Ui::FontSettingsPage *m_ui; SchemeListModel *m_schemeListModel; bool m_refreshingSchemeList; - QString m_searchKeywords; }; } // namespace Internal @@ -326,49 +327,40 @@ FontSettingsPage::~FontSettingsPage() delete d_ptr; } -QWidget *FontSettingsPage::createPage(QWidget *parent) +QWidget *FontSettingsPage::widget() { - QWidget *w = new QWidget(parent); - d_ptr->m_ui = new Ui::FontSettingsPage; - d_ptr->m_ui->setupUi(w); - d_ptr->m_ui->schemeComboBox->setModel(d_ptr->m_schemeListModel); + if (!d_ptr->m_widget){ + d_ptr->m_widget = new QWidget; + d_ptr->m_ui = new Ui::FontSettingsPage; + d_ptr->m_ui->setupUi(d_ptr->m_widget); + d_ptr->m_ui->schemeComboBox->setModel(d_ptr->m_schemeListModel); - QFontDatabase db; - const QStringList families = db.families(); - d_ptr->m_ui->familyComboBox->addItems(families); - const int idx = families.indexOf(d_ptr->m_value.family()); - d_ptr->m_ui->familyComboBox->setCurrentIndex(idx); + QFontDatabase db; + const QStringList families = db.families(); + d_ptr->m_ui->familyComboBox->addItems(families); + const int idx = families.indexOf(d_ptr->m_value.family()); + d_ptr->m_ui->familyComboBox->setCurrentIndex(idx); - d_ptr->m_ui->antialias->setChecked(d_ptr->m_value.antialias()); - d_ptr->m_ui->zoomSpinBox->setValue(d_ptr->m_value.fontZoom()); + d_ptr->m_ui->antialias->setChecked(d_ptr->m_value.antialias()); + d_ptr->m_ui->zoomSpinBox->setValue(d_ptr->m_value.fontZoom()); - d_ptr->m_ui->schemeEdit->setFormatDescriptions(d_ptr->m_descriptions); - d_ptr->m_ui->schemeEdit->setBaseFont(d_ptr->m_value.font()); - d_ptr->m_ui->schemeEdit->setColorScheme(d_ptr->m_value.colorScheme()); + d_ptr->m_ui->schemeEdit->setFormatDescriptions(d_ptr->m_descriptions); + d_ptr->m_ui->schemeEdit->setBaseFont(d_ptr->m_value.font()); + d_ptr->m_ui->schemeEdit->setColorScheme(d_ptr->m_value.colorScheme()); - connect(d_ptr->m_ui->familyComboBox, SIGNAL(currentIndexChanged(QString)), this, SLOT(fontFamilySelected(QString))); - connect(d_ptr->m_ui->sizeComboBox, SIGNAL(currentIndexChanged(QString)), this, SLOT(fontSizeSelected(QString))); - connect(d_ptr->m_ui->zoomSpinBox, SIGNAL(valueChanged(int)), this, SLOT(fontZoomChanged())); - connect(d_ptr->m_ui->schemeComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(colorSchemeSelected(int))); - connect(d_ptr->m_ui->copyButton, SIGNAL(clicked()), this, SLOT(copyColorScheme())); - connect(d_ptr->m_ui->deleteButton, SIGNAL(clicked()), this, SLOT(confirmDeleteColorScheme())); + connect(d_ptr->m_ui->familyComboBox, SIGNAL(currentIndexChanged(QString)), this, SLOT(fontFamilySelected(QString))); + connect(d_ptr->m_ui->sizeComboBox, SIGNAL(currentIndexChanged(QString)), this, SLOT(fontSizeSelected(QString))); + connect(d_ptr->m_ui->zoomSpinBox, SIGNAL(valueChanged(int)), this, SLOT(fontZoomChanged())); + connect(d_ptr->m_ui->schemeComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(colorSchemeSelected(int))); + connect(d_ptr->m_ui->copyButton, SIGNAL(clicked()), this, SLOT(copyColorScheme())); + connect(d_ptr->m_ui->deleteButton, SIGNAL(clicked()), this, SLOT(confirmDeleteColorScheme())); - updatePointSizes(); - refreshColorSchemeList(); - d_ptr->m_lastValue = d_ptr->m_value; - if (d_ptr->m_searchKeywords.isEmpty()) { - QLatin1Char sep(' '); - d_ptr->m_searchKeywords = - d_ptr->m_ui->fontGroupBox->title() + sep - + d_ptr->m_ui->familyLabel->text() + sep - + d_ptr->m_ui->sizeLabel->text() + sep - + d_ptr->m_ui->zoomLabel->text() + sep - + d_ptr->m_ui->antialias->text() + sep - + d_ptr->m_ui->colorSchemeGroupBox->title(); - d_ptr->m_searchKeywords.remove(QLatin1Char('&')); + updatePointSizes(); + refreshColorSchemeList(); + d_ptr->m_lastValue = d_ptr->m_value; } - return w; + return d_ptr->m_widget; } void FontSettingsPage::fontFamilySelected(const QString &family) @@ -630,6 +622,7 @@ void FontSettingsPage::saveSettings() void FontSettingsPage::finish() { + delete d_ptr->m_widget; if (!d_ptr->m_ui) // page was never shown return; // If changes were applied, these are equal. Otherwise restores last value. @@ -642,8 +635,3 @@ const FontSettings &FontSettingsPage::fontSettings() const { return d_ptr->m_value; } - -bool FontSettingsPage::matches(const QString &s) const -{ - return d_ptr->m_searchKeywords.contains(s, Qt::CaseInsensitive); -} diff --git a/src/plugins/texteditor/fontsettingspage.h b/src/plugins/texteditor/fontsettingspage.h index f358652c09..82ff29ef72 100644 --- a/src/plugins/texteditor/fontsettingspage.h +++ b/src/plugins/texteditor/fontsettingspage.h @@ -92,10 +92,9 @@ public: ~FontSettingsPage(); - QWidget *createPage(QWidget *parent); + QWidget *widget(); void apply(); void finish(); - bool matches(const QString &) const; void saveSettings(); diff --git a/src/plugins/texteditor/generichighlighter/highlightersettingspage.cpp b/src/plugins/texteditor/generichighlighter/highlightersettingspage.cpp index 4271d0f097..6ae35d4ede 100644 --- a/src/plugins/texteditor/generichighlighter/highlightersettingspage.cpp +++ b/src/plugins/texteditor/generichighlighter/highlightersettingspage.cpp @@ -36,6 +36,7 @@ #include <coreplugin/icore.h> #include <QMessageBox> +#include <QPointer> using namespace TextEditor; using namespace Internal; @@ -50,10 +51,9 @@ struct HighlighterSettingsPage::HighlighterSettingsPagePrivate const QString m_displayName; const QString m_settingsPrefix; - QString m_searchKeywords; - HighlighterSettings m_settings; + QPointer<QWidget> m_widget; Ui::HighlighterSettingsPage *m_page; }; @@ -88,34 +88,30 @@ HighlighterSettingsPage::~HighlighterSettingsPage() delete m_d; } -QWidget *HighlighterSettingsPage::createPage(QWidget *parent) -{ - QWidget *w = new QWidget(parent); - m_d->m_page = new Ui::HighlighterSettingsPage; - m_d->m_page->setupUi(w); - m_d->m_page->definitionFilesPath->setExpectedKind(Utils::PathChooser::ExistingDirectory); - m_d->m_page->definitionFilesPath->addButton(tr("Download Definitions..."), this, - SLOT(requestAvailableDefinitionsMetaData())); - m_d->m_page->fallbackDefinitionFilesPath->setExpectedKind(Utils::PathChooser::ExistingDirectory); - m_d->m_page->fallbackDefinitionFilesPath->addButton(tr("Autodetect"), this, - SLOT(resetDefinitionsLocation())); - - settingsToUI(); - - if (m_d->m_searchKeywords.isEmpty()) { - QTextStream(&m_d->m_searchKeywords) << m_d->m_page->definitionFilesGroupBox->title() - << m_d->m_page->locationLabel->text() - << m_d->m_page->useFallbackLocation->text() - << m_d->m_page->ignoreLabel->text(); +QWidget *HighlighterSettingsPage::widget() +{ + if (!m_d->m_widget) { + m_d->m_widget = new QWidget; + m_d->m_page = new Ui::HighlighterSettingsPage; + m_d->m_page->setupUi(m_d->m_widget); + m_d->m_page->definitionFilesPath->setExpectedKind(Utils::PathChooser::ExistingDirectory); + m_d->m_page->definitionFilesPath->setHistoryCompleter(QLatin1String("TextEditor.Highlighter.History")); + m_d->m_page->definitionFilesPath->addButton(tr("Download Definitions..."), this, + SLOT(requestAvailableDefinitionsMetaData())); + m_d->m_page->fallbackDefinitionFilesPath->setExpectedKind(Utils::PathChooser::ExistingDirectory); + m_d->m_page->fallbackDefinitionFilesPath->setHistoryCompleter(QLatin1String("TextEditor.Highlighter.History")); + m_d->m_page->fallbackDefinitionFilesPath->addButton(tr("Autodetect"), this, + SLOT(resetDefinitionsLocation())); + + settingsToUI(); + + connect(m_d->m_page->useFallbackLocation, SIGNAL(clicked(bool)), + this, SLOT(setFallbackLocationState(bool))); + connect(m_d->m_page->definitionFilesPath, SIGNAL(validChanged(bool)), + this, SLOT(setDownloadDefinitionsState(bool))); + connect(m_d->m_widget, SIGNAL(destroyed()), this, SLOT(ignoreDownloadReply())); } - - connect(m_d->m_page->useFallbackLocation, SIGNAL(clicked(bool)), - this, SLOT(setFallbackLocationState(bool))); - connect(m_d->m_page->definitionFilesPath, SIGNAL(validChanged(bool)), - this, SLOT(setDownloadDefinitionsState(bool))); - connect(w, SIGNAL(destroyed()), this, SLOT(ignoreDownloadReply())); - - return w; + return m_d->m_widget; } void HighlighterSettingsPage::apply() @@ -133,17 +129,13 @@ void HighlighterSettingsPage::apply() void HighlighterSettingsPage::finish() { + delete m_d->m_widget; if (!m_d->m_page) // page was not shown return; delete m_d->m_page; m_d->m_page = 0; } -bool HighlighterSettingsPage::matches(const QString &s) const -{ - return m_d->m_searchKeywords.contains(s, Qt::CaseInsensitive); -} - const HighlighterSettings &HighlighterSettingsPage::highlighterSettings() const { m_d->ensureInitialized(); diff --git a/src/plugins/texteditor/generichighlighter/highlightersettingspage.h b/src/plugins/texteditor/generichighlighter/highlightersettingspage.h index 18c632ba14..69d1372f53 100644 --- a/src/plugins/texteditor/generichighlighter/highlightersettingspage.h +++ b/src/plugins/texteditor/generichighlighter/highlightersettingspage.h @@ -52,10 +52,9 @@ public: HighlighterSettingsPage(Core::Id id, QObject *parent); ~HighlighterSettingsPage(); - QWidget *createPage(QWidget *parent); + QWidget *widget(); void apply(); void finish(); - bool matches(const QString &s) const; const HighlighterSettings &highlighterSettings() const; diff --git a/src/plugins/texteditor/itexteditor.cpp b/src/plugins/texteditor/itexteditor.cpp index f325627c2b..19b8032770 100644 --- a/src/plugins/texteditor/itexteditor.cpp +++ b/src/plugins/texteditor/itexteditor.cpp @@ -46,7 +46,7 @@ QMap<QString, QString> ITextEditor::openedTextDocumentContents() if (!textEditorDocument) continue; QString fileName = textEditorDocument->filePath(); - workingCopy[fileName] = textEditorDocument->contents(); + workingCopy[fileName] = textEditorDocument->plainText(); } return workingCopy; } diff --git a/src/plugins/texteditor/itexteditor.h b/src/plugins/texteditor/itexteditor.h index 69e724fe79..385a82d12b 100644 --- a/src/plugins/texteditor/itexteditor.h +++ b/src/plugins/texteditor/itexteditor.h @@ -81,9 +81,11 @@ class TEXTEDITOR_EXPORT ITextEditorDocument : public Core::TextDocument public: explicit ITextEditorDocument(QObject *parent = 0); - virtual QString contents() const = 0; + virtual QString plainText() const = 0; virtual QString textAt(int pos, int length) const = 0; virtual QChar characterAt(int pos) const = 0; + + virtual ITextMarkable *markableInterface() const = 0; }; class TEXTEDITOR_EXPORT ITextEditor : public Core::IEditor @@ -126,8 +128,6 @@ public: /*! Selects text between current cursor position and \a toPos. */ virtual void select(int toPos) = 0; - virtual ITextMarkable *markableInterface() = 0; - virtual const Utils::CommentDefinition* commentDefinition() const = 0; static QMap<QString, QString> openedTextDocumentContents(); @@ -141,7 +141,6 @@ public: signals: void contentsChanged(); - void contentsChangedBecauseOfUndo(); void markRequested(TextEditor::ITextEditor *editor, int line, TextEditor::ITextEditor::MarkRequestKind kind); void markContextMenuRequested(TextEditor::ITextEditor *editor, int line, QMenu *menu); void tooltipOverrideRequested(TextEditor::ITextEditor *editor, const QPoint &globalPos, int position, bool *handled); diff --git a/src/plugins/texteditor/plaintexteditor.cpp b/src/plugins/texteditor/plaintexteditor.cpp index 27846929d6..08f2076f77 100644 --- a/src/plugins/texteditor/plaintexteditor.cpp +++ b/src/plugins/texteditor/plaintexteditor.cpp @@ -71,7 +71,7 @@ PlainTextEditorWidget::PlainTextEditorWidget(QWidget *parent) m_commentDefinition.clearCommentStyles(); // If configure() is called immediately the whole document is considered modified - connect(editorDocument(), SIGNAL(changed()), this, SLOT(configure()), Qt::QueuedConnection); + connect(baseTextDocument(), SIGNAL(changed()), this, SLOT(configure()), Qt::QueuedConnection); connect(Manager::instance(), SIGNAL(mimeTypesRegistered()), this, SLOT(configure())); } @@ -79,7 +79,7 @@ IEditor *PlainTextEditor::duplicate(QWidget *parent) { PlainTextEditorWidget *newWidget = new PlainTextEditorWidget(parent); newWidget->duplicateFrom(editorWidget()); - TextEditorPlugin::instance()->initializeEditor(newWidget); + TextEditorSettings::initializeEditor(newWidget); return newWidget->editor(); } @@ -107,8 +107,8 @@ void PlainTextEditorWidget::setTabSettings(const TextEditor::TabSettings &ts) void PlainTextEditorWidget::configure() { MimeType mimeType; - if (editorDocument()) - mimeType = MimeDatabase::findByFile(editorDocument()->filePath()); + if (baseTextDocument()) + mimeType = MimeDatabase::findByFile(baseTextDocument()->filePath()); configure(mimeType); } @@ -147,8 +147,8 @@ void PlainTextEditorWidget::configure(const MimeType &mimeType) setCodeFoldingSupported(true); } - } else if (editorDocument()) { - const QString &fileName = editorDocument()->filePath(); + } else if (baseTextDocument()) { + const QString &fileName = baseTextDocument()->filePath(); if (TextEditorSettings::highlighterSettings().isIgnoredFilePattern(fileName)) m_isMissingSyntaxDefinition = false; } diff --git a/src/plugins/texteditor/plaintexteditorfactory.cpp b/src/plugins/texteditor/plaintexteditorfactory.cpp index 408b159a81..f4836ce624 100644 --- a/src/plugins/texteditor/plaintexteditorfactory.cpp +++ b/src/plugins/texteditor/plaintexteditorfactory.cpp @@ -30,9 +30,10 @@ #include "plaintexteditorfactory.h" #include "plaintexteditor.h" #include "basetextdocument.h" +#include "texteditoractionhandler.h" #include "texteditorconstants.h" #include "texteditorplugin.h" -#include "texteditoractionhandler.h" +#include "texteditorsettings.h" #include <coreplugin/coreconstants.h> #include <coreplugin/infobar.h> @@ -50,22 +51,17 @@ PlainTextEditorFactory::PlainTextEditorFactory(QObject *parent) setDisplayName(qApp->translate("OpenWith::Editors", Core::Constants::K_DEFAULT_TEXT_EDITOR_DISPLAY_NAME)); addMimeType(QLatin1String(TextEditor::Constants::C_TEXTEDITOR_MIMETYPE_TEXT)); - m_actionHandler = new TextEditorActionHandler( + new TextEditorActionHandler(this, TextEditor::Constants::C_TEXTEDITOR, TextEditorActionHandler::Format | TextEditorActionHandler::UnCommentSelection | TextEditorActionHandler::UnCollapseAll); } -PlainTextEditorFactory::~PlainTextEditorFactory() -{ - delete m_actionHandler; -} - Core::IEditor *PlainTextEditorFactory::createEditor(QWidget *parent) { PlainTextEditorWidget *rc = new PlainTextEditorWidget(parent); - TextEditorPlugin::instance()->initializeEditor(rc); + TextEditorSettings::initializeEditor(rc); connect(rc, SIGNAL(configured(Core::IEditor*)), this, SLOT(updateEditorInfoBar(Core::IEditor*))); updateEditorInfoBar(rc->editor()); diff --git a/src/plugins/texteditor/plaintexteditorfactory.h b/src/plugins/texteditor/plaintexteditorfactory.h index 5c5393b425..e7c6b377fc 100644 --- a/src/plugins/texteditor/plaintexteditorfactory.h +++ b/src/plugins/texteditor/plaintexteditorfactory.h @@ -35,7 +35,6 @@ #include <QStringList> namespace TextEditor { -class TextEditorActionHandler; namespace Internal { class PlainTextEditorFactory : public Core::IEditorFactory @@ -44,17 +43,12 @@ class PlainTextEditorFactory : public Core::IEditorFactory public: PlainTextEditorFactory(QObject *parent = 0); - ~PlainTextEditorFactory(); using Core::IEditorFactory::addMimeType; Core::IEditor *createEditor(QWidget *parent); - TextEditor::TextEditorActionHandler *actionHandler() const { return m_actionHandler; } private slots: void updateEditorInfoBar(Core::IEditor *editor); - -private: - TextEditor::TextEditorActionHandler *m_actionHandler; }; } // namespace Internal diff --git a/src/plugins/texteditor/refactoringchanges.cpp b/src/plugins/texteditor/refactoringchanges.cpp index 482c3af017..605e6f4664 100644 --- a/src/plugins/texteditor/refactoringchanges.cpp +++ b/src/plugins/texteditor/refactoringchanges.cpp @@ -158,7 +158,7 @@ RefactoringFile::RefactoringFile(QTextDocument *document, const QString &fileNam { } RefactoringFile::RefactoringFile(BaseTextEditorWidget *editor) - : m_fileName(editor->editorDocument()->filePath()) + : m_fileName(editor->baseTextDocument()->filePath()) , m_document(0) , m_editor(editor) , m_openEditor(false) diff --git a/src/plugins/texteditor/simplecodestylepreferenceswidget.cpp b/src/plugins/texteditor/simplecodestylepreferenceswidget.cpp index b0d6725403..8bb44291b4 100644 --- a/src/plugins/texteditor/simplecodestylepreferenceswidget.cpp +++ b/src/plugins/texteditor/simplecodestylepreferenceswidget.cpp @@ -94,11 +94,6 @@ void SimpleCodeStylePreferencesWidget::slotTabSettingsChanged(const TextEditor:: current->setTabSettings(settings); } -QString SimpleCodeStylePreferencesWidget::searchKeywords() const -{ - return m_tabSettingsWidget->searchKeywords(); -} - void SimpleCodeStylePreferencesWidget::setFlat(bool on) { m_tabSettingsWidget->setFlat(on); diff --git a/src/plugins/texteditor/simplecodestylepreferenceswidget.h b/src/plugins/texteditor/simplecodestylepreferenceswidget.h index 1f8dad7c4c..992b242b34 100644 --- a/src/plugins/texteditor/simplecodestylepreferenceswidget.h +++ b/src/plugins/texteditor/simplecodestylepreferenceswidget.h @@ -52,7 +52,6 @@ public: explicit SimpleCodeStylePreferencesWidget(QWidget *parent = 0); void setPreferences(ICodeStylePreferences *tabPreferences); - QString searchKeywords() const; void setFlat(bool on); TabSettingsWidget *tabSettingsWidget() const; diff --git a/src/plugins/texteditor/snippets/snippetssettingspage.cpp b/src/plugins/texteditor/snippets/snippetssettingspage.cpp index 7062880ccf..e1db97ea96 100644 --- a/src/plugins/texteditor/snippets/snippetssettingspage.cpp +++ b/src/plugins/texteditor/snippets/snippetssettingspage.cpp @@ -39,11 +39,12 @@ #include <texteditor/texteditorsettings.h> #include <extensionsystem/pluginmanager.h> -#include <QModelIndex> #include <QAbstractTableModel> #include <QList> -#include <QTextStream> #include <QMessageBox> +#include <QModelIndex> +#include <QPointer> +#include <QTextStream> namespace TextEditor { namespace Internal { @@ -269,12 +270,13 @@ public: Core::Id id() const { return m_id; } const QString &displayName() const { return m_displayName; } - bool isKeyword(const QString &s) const { return m_keywords.contains(s, Qt::CaseInsensitive); } void configureUi(QWidget *parent); void apply(); void finish(); + QPointer<QWidget> m_widget; + private slots: void loadSnippetGroup(int index); void markSnippetsCollection(); @@ -302,7 +304,6 @@ private: const QString m_settingsPrefix; SnippetsTableModel *m_model; bool m_snippetsCollectionChanged; - QString m_keywords; SnippetsSettings m_settings; Ui::SnippetsSettingsPage m_ui; }; @@ -350,8 +351,6 @@ void SnippetsSettingsPagePrivate::configureUi(QWidget *w) m_ui.revertButton->setEnabled(false); - QTextStream(&m_keywords) << m_displayName; - loadSettings(); loadSnippetGroup(m_ui.groupCombo->currentIndex()); @@ -566,16 +565,13 @@ SnippetsSettingsPage::~SnippetsSettingsPage() delete d; } -bool SnippetsSettingsPage::matches(const QString &s) const +QWidget *SnippetsSettingsPage::widget() { - return d->isKeyword(s); -} - -QWidget *SnippetsSettingsPage::createPage(QWidget *parent) -{ - QWidget *w = new QWidget(parent); - d->configureUi(w); - return w; + if (!d->m_widget) { + d->m_widget = new QWidget; + d->configureUi(d->m_widget); + } + return d->m_widget; } void SnippetsSettingsPage::apply() @@ -586,6 +582,7 @@ void SnippetsSettingsPage::apply() void SnippetsSettingsPage::finish() { d->finish(); + delete d->m_widget; } } // Internal diff --git a/src/plugins/texteditor/snippets/snippetssettingspage.h b/src/plugins/texteditor/snippets/snippetssettingspage.h index 32e03e5cee..3ded2c558b 100644 --- a/src/plugins/texteditor/snippets/snippetssettingspage.h +++ b/src/plugins/texteditor/snippets/snippetssettingspage.h @@ -45,8 +45,7 @@ public: SnippetsSettingsPage(Core::Id id, QObject *parent); ~SnippetsSettingsPage(); - bool matches(const QString &s) const; - QWidget *createPage(QWidget *parent); + QWidget *widget(); void apply(); void finish(); diff --git a/src/plugins/texteditor/tabsettingswidget.cpp b/src/plugins/texteditor/tabsettingswidget.cpp index 4b37a56a01..ed49a4aaea 100644 --- a/src/plugins/texteditor/tabsettingswidget.cpp +++ b/src/plugins/texteditor/tabsettingswidget.cpp @@ -101,21 +101,6 @@ void TabSettingsWidget::setFlat(bool on) ui->tabsAndIndentationGroupBox->layout()->setContentsMargins(margin, -1, margin, margin); } -QString TabSettingsWidget::searchKeywords() const -{ - QString rc; - QLatin1Char sep(' '); - QTextStream(&rc) - << sep << ui->tabsAndIndentationGroupBox->title() - << sep << ui->tabPolicyLabel->text() - << sep << ui->tabSizeLabel->text() - << sep << ui->indentSizeLabel->text() - << sep << ui->continuationAlignBehaviorLabel->text() - ; - rc.remove(QLatin1Char('&')); - return rc; -} - void TabSettingsWidget::setCodingStyleWarningVisible(bool visible) { ui->codingStyleWarning->setVisible(visible); diff --git a/src/plugins/texteditor/tabsettingswidget.h b/src/plugins/texteditor/tabsettingswidget.h index b3ebf43255..e3bdb2e581 100644 --- a/src/plugins/texteditor/tabsettingswidget.h +++ b/src/plugins/texteditor/tabsettingswidget.h @@ -56,7 +56,6 @@ public: TabSettings tabSettings() const; void setFlat(bool on); - QString searchKeywords() const; void setCodingStyleWarningVisible(bool visible); public slots: diff --git a/src/plugins/texteditor/texteditoractionhandler.cpp b/src/plugins/texteditor/texteditoractionhandler.cpp index a8a95378f3..cef93738e9 100644 --- a/src/plugins/texteditor/texteditoractionhandler.cpp +++ b/src/plugins/texteditor/texteditoractionhandler.cpp @@ -49,9 +49,9 @@ using namespace TextEditor; using namespace TextEditor::Internal; -TextEditorActionHandler::TextEditorActionHandler(const char *context, +TextEditorActionHandler::TextEditorActionHandler(QObject *parent, Core::Id contextId, uint optionalActions) - : QObject(Core::ICore::instance()), + : QObject(parent), m_undoAction(0), m_redoAction(0), m_copyAction(0), @@ -103,10 +103,10 @@ TextEditorActionHandler::TextEditorActionHandler(const char *context, m_jumpToFileAction(0), m_jumpToFileInNextSplitAction(0), m_optionalActions(optionalActions), - m_currentEditor(0), - m_contextId(context), - m_initialized(false) + m_currentEditorWidget(0), + m_contextId(contextId) { + createActions(); connect(Core::EditorManager::instance(), SIGNAL(currentEditorChanged(Core::IEditor*)), this, SLOT(updateCurrentEditor(Core::IEditor*))); } @@ -115,25 +115,6 @@ TextEditorActionHandler::~TextEditorActionHandler() { } -void TextEditorActionHandler::setupActions(BaseTextEditorWidget *editor) -{ - initializeActions(); - editor->setActionHack(this); - QObject::connect(editor, SIGNAL(undoAvailable(bool)), this, SLOT(updateUndoAction())); - QObject::connect(editor, SIGNAL(redoAvailable(bool)), this, SLOT(updateRedoAction())); - QObject::connect(editor, SIGNAL(copyAvailable(bool)), this, SLOT(updateCopyAction())); - QObject::connect(editor, SIGNAL(readOnlyChanged()), this, SLOT(updateActions())); -} - - -void TextEditorActionHandler::initializeActions() -{ - if (!m_initialized) { - createActions(); - m_initialized = true; - } -} - void TextEditorActionHandler::createActions() { using namespace Core::Constants; @@ -371,6 +352,13 @@ void TextEditorActionHandler::createActions() m_modifyingActions << m_switchUtf8bomAction; m_modifyingActions << m_indentAction; m_modifyingActions << m_unindentAction; + + // set enabled state of optional actions + m_followSymbolAction->setEnabled(m_optionalActions & FollowSymbolUnderCursor); + m_followSymbolInNextSplitAction->setEnabled(m_optionalActions & FollowSymbolUnderCursor); + m_jumpToFileAction->setEnabled(m_optionalActions & JumpToFileUnderCursor); + m_jumpToFileInNextSplitAction->setEnabled(m_optionalActions & JumpToFileUnderCursor); + m_unfoldAllAction->setEnabled(m_optionalActions & UnCollapseAll); } QAction *TextEditorActionHandler::registerAction(const Core::Id &id, @@ -382,7 +370,7 @@ QAction *TextEditorActionHandler::registerAction(const Core::Id &id, Core::ActionContainer *container) { QAction *result = new QAction(title, this); - Core::Command *command = Core::ActionManager::registerAction(result, id, m_contextId, scriptable); + Core::Command *command = Core::ActionManager::registerAction(result, id, Core::Context(m_contextId), scriptable); if (!keySequence.isEmpty()) command->setDefaultKeySequence(keySequence); @@ -393,34 +381,16 @@ QAction *TextEditorActionHandler::registerAction(const Core::Id &id, return result; } -TextEditorActionHandler::UpdateMode TextEditorActionHandler::updateMode() const -{ - Q_ASSERT(m_currentEditor != 0); - return m_currentEditor->isReadOnly() ? ReadOnlyMode : WriteMode; -} - void TextEditorActionHandler::updateActions() { - if (!m_currentEditor || !m_initialized) - return; - updateActions(updateMode()); -} - -void TextEditorActionHandler::updateActions(UpdateMode um) -{ + QTC_ASSERT(m_currentEditorWidget, return); + bool isWritable = !m_currentEditorWidget->isReadOnly(); foreach (QAction *a, m_modifyingActions) - a->setEnabled(um != ReadOnlyMode); - m_formatAction->setEnabled((m_optionalActions & Format) && um != ReadOnlyMode); - m_unCommentSelectionAction->setEnabled((m_optionalActions & UnCommentSelection) && um != ReadOnlyMode); - m_followSymbolAction->setEnabled(m_optionalActions & FollowSymbolUnderCursor); - m_followSymbolInNextSplitAction->setEnabled(m_optionalActions & FollowSymbolUnderCursor); - m_jumpToFileAction->setEnabled(m_optionalActions & JumpToFileUnderCursor); - m_jumpToFileInNextSplitAction->setEnabled(m_optionalActions & JumpToFileUnderCursor); - - m_unfoldAllAction->setEnabled((m_optionalActions & UnCollapseAll)); - m_visualizeWhitespaceAction->setChecked(m_currentEditor->displaySettings().m_visualizeWhitespace); - if (m_textWrappingAction) - m_textWrappingAction->setChecked(m_currentEditor->displaySettings().m_textWrapping); + a->setEnabled(isWritable); + m_formatAction->setEnabled((m_optionalActions & Format) && isWritable); + m_unCommentSelectionAction->setEnabled((m_optionalActions & UnCommentSelection) && isWritable); + m_visualizeWhitespaceAction->setChecked(m_currentEditorWidget->displaySettings().m_visualizeWhitespace); + m_textWrappingAction->setChecked(m_currentEditorWidget->displaySettings().m_textWrapping); updateRedoAction(); updateUndoAction(); @@ -429,21 +399,22 @@ void TextEditorActionHandler::updateActions(UpdateMode um) void TextEditorActionHandler::updateRedoAction() { - if (m_redoAction) - m_redoAction->setEnabled(m_currentEditor && m_currentEditor->document()->isRedoAvailable()); + QTC_ASSERT(m_currentEditorWidget, return); + m_redoAction->setEnabled(m_currentEditorWidget->document()->isRedoAvailable()); } void TextEditorActionHandler::updateUndoAction() { - if (m_undoAction) - m_undoAction->setEnabled(m_currentEditor && m_currentEditor->document()->isUndoAvailable()); + QTC_ASSERT(m_currentEditorWidget, return); + m_undoAction->setEnabled(m_currentEditorWidget->document()->isUndoAvailable()); } void TextEditorActionHandler::updateCopyAction() { - const bool hasCopyableText = m_currentEditor && m_currentEditor->textCursor().hasSelection(); + QTC_ASSERT(m_currentEditorWidget, return); + const bool hasCopyableText = m_currentEditorWidget->textCursor().hasSelection(); if (m_cutAction) - m_cutAction->setEnabled(hasCopyableText && updateMode() == WriteMode); + m_cutAction->setEnabled(hasCopyableText && !m_currentEditorWidget->isReadOnly()); if (m_copyAction) m_copyAction->setEnabled(hasCopyableText); } @@ -459,37 +430,37 @@ void TextEditorActionHandler::gotoAction() void TextEditorActionHandler::printAction() { - if (m_currentEditor) - m_currentEditor->print(Core::ICore::printer()); + if (m_currentEditorWidget) + m_currentEditorWidget->print(Core::ICore::printer()); } void TextEditorActionHandler::setVisualizeWhitespace(bool checked) { - if (m_currentEditor) { - DisplaySettings ds = m_currentEditor->displaySettings(); + if (m_currentEditorWidget) { + DisplaySettings ds = m_currentEditorWidget->displaySettings(); ds.m_visualizeWhitespace = checked; - m_currentEditor->setDisplaySettings(ds); + m_currentEditorWidget->setDisplaySettings(ds); } } void TextEditorActionHandler::setTextWrapping(bool checked) { - if (m_currentEditor) { - DisplaySettings ds = m_currentEditor->displaySettings(); + if (m_currentEditorWidget) { + DisplaySettings ds = m_currentEditorWidget->displaySettings(); ds.m_textWrapping = checked; - m_currentEditor->setDisplaySettings(ds); + m_currentEditorWidget->setDisplaySettings(ds); } } #define FUNCTION(funcname) void TextEditorActionHandler::funcname ()\ {\ - if (m_currentEditor)\ - m_currentEditor->funcname ();\ + if (m_currentEditorWidget)\ + m_currentEditorWidget->funcname ();\ } #define FUNCTION2(funcname, funcname2) void TextEditorActionHandler::funcname ()\ {\ - if (m_currentEditor)\ - m_currentEditor->funcname2 ();\ + if (m_currentEditorWidget)\ + m_currentEditorWidget->funcname2 ();\ } @@ -568,20 +539,21 @@ BaseTextEditorWidget *TextEditorActionHandler::resolveTextEditorWidget(Core::IEd void TextEditorActionHandler::updateCurrentEditor(Core::IEditor *editor) { - m_currentEditor = 0; + if (m_currentEditorWidget) + m_currentEditorWidget->disconnect(this); + m_currentEditorWidget = 0; - if (!editor) + // don't need to do anything if the editor's context doesn't match + // (our actions will be disabled because our context will not be active) + if (!editor || !editor->context().contains(m_contextId)) return; - BaseTextEditorWidget *baseEditor = resolveTextEditorWidget(editor); - - if (baseEditor && baseEditor->actionHack() == this) { - m_currentEditor = baseEditor; - updateActions(); - } -} - -const QPointer<BaseTextEditorWidget> &TextEditorActionHandler::currentEditor() const -{ - return m_currentEditor; + BaseTextEditorWidget *editorWidget = resolveTextEditorWidget(editor); + QTC_ASSERT(editorWidget, return); // editor has our context id, so shouldn't happen + m_currentEditorWidget = editorWidget; + connect(m_currentEditorWidget, SIGNAL(undoAvailable(bool)), this, SLOT(updateUndoAction())); + connect(m_currentEditorWidget, SIGNAL(redoAvailable(bool)), this, SLOT(updateRedoAction())); + connect(m_currentEditorWidget, SIGNAL(copyAvailable(bool)), this, SLOT(updateCopyAction())); + connect(m_currentEditorWidget, SIGNAL(readOnlyChanged()), this, SLOT(updateActions())); + updateActions(); } diff --git a/src/plugins/texteditor/texteditoractionhandler.h b/src/plugins/texteditor/texteditoractionhandler.h index d365d3247c..fbb03d497e 100644 --- a/src/plugins/texteditor/texteditoractionhandler.h +++ b/src/plugins/texteditor/texteditoractionhandler.h @@ -65,23 +65,13 @@ public: JumpToFileUnderCursor = 16 }; - explicit TextEditorActionHandler(const char *context, uint optionalActions = None); + explicit TextEditorActionHandler(QObject *parent, Core::Id contextId, uint optionalActions = None); ~TextEditorActionHandler(); - void setupActions(BaseTextEditorWidget *editor); - - void initializeActions(); - -public slots: - void updateActions(); - void updateRedoAction(); - void updateUndoAction(); - void updateCopyAction(); - protected: virtual BaseTextEditorWidget *resolveTextEditorWidget(Core::IEditor *editor) const; - const QPointer<BaseTextEditorWidget> ¤tEditor() const; +private: QAction *registerAction(const Core::Id &id, const char *slot, bool scriptable = false, @@ -90,13 +80,14 @@ protected: const char *menueGroup = 0, Core::ActionContainer *container = 0); - enum UpdateMode { ReadOnlyMode, WriteMode }; - UpdateMode updateMode() const; - void createActions(); - void updateActions(UpdateMode um); private slots: + void updateActions(); + void updateRedoAction(); + void updateUndoAction(); + void updateCopyAction(); + void undoAction(); void redoAction(); void copyAction(); @@ -225,9 +216,8 @@ private: QList<QAction *> m_modifyingActions; uint m_optionalActions; - QPointer<BaseTextEditorWidget> m_currentEditor; - Core::Context m_contextId; - bool m_initialized; + QPointer<BaseTextEditorWidget> m_currentEditorWidget; + Core::Id m_contextId; }; } // namespace TextEditor diff --git a/src/plugins/texteditor/texteditorplugin.cpp b/src/plugins/texteditor/texteditorplugin.cpp index 53495e7882..fb1b52bf7f 100644 --- a/src/plugins/texteditor/texteditorplugin.cpp +++ b/src/plugins/texteditor/texteditorplugin.cpp @@ -203,11 +203,6 @@ bool TextEditorPlugin::initialize(const QStringList &arguments, QString *errorMe m_outlineFactory = new OutlineFactory; addAutoReleasedObject(m_outlineFactory); - // We have to initialize the actions because other plugins that - // depend upon the texteditorplugin expect that actions will be - // registered in the action manager at plugin initialization time. - m_editorFactory->actionHandler()->initializeActions(); - m_baseTextMarkRegistry = new BaseTextMarkRegistry(this); return true; @@ -246,14 +241,6 @@ void TextEditorPlugin::extensionsInitialized() this, SLOT(updateCurrentSelection(QString))); } -void TextEditorPlugin::initializeEditor(PlainTextEditorWidget *editor) -{ - // common actions - m_editorFactory->actionHandler()->setupActions(editor); - - TextEditorSettings::initializeEditor(editor); -} - void TextEditorPlugin::invokeCompletion() { Core::IEditor *iface = Core::EditorManager::currentEditor(); diff --git a/src/plugins/texteditor/texteditorplugin.h b/src/plugins/texteditor/texteditorplugin.h index 1dd5ea7123..81110efbd0 100644 --- a/src/plugins/texteditor/texteditorplugin.h +++ b/src/plugins/texteditor/texteditorplugin.h @@ -64,8 +64,6 @@ public: bool initialize(const QStringList &arguments, QString *errorMessage); void extensionsInitialized(); - void initializeEditor(PlainTextEditorWidget *editor); - PlainTextEditorFactory *editorFactory() { return m_editorFactory; } LineNumberFilter *lineNumberFilter() { return m_lineNumberFilter; } BaseTextMarkRegistry *baseTextMarkRegistry() { return m_baseTextMarkRegistry; } diff --git a/src/plugins/todo/optionsdialog.ui b/src/plugins/todo/optionsdialog.ui index d8b4f6d963..b07912b5e9 100644 --- a/src/plugins/todo/optionsdialog.ui +++ b/src/plugins/todo/optionsdialog.ui @@ -87,14 +87,14 @@ <bool>true</bool> </property> <property name="text"> - <string>Scan in the whole project</string> + <string>Scan the whole active project</string> </property> </widget> </item> <item> <widget class="QRadioButton" name="scanInCurrentFileRadioButton"> <property name="text"> - <string>Scan in the current opened file</string> + <string>Scan only the currently edited document</string> </property> <property name="checked"> <bool>true</bool> diff --git a/src/plugins/todo/optionspage.cpp b/src/plugins/todo/optionspage.cpp index 3724895524..94ecc6078c 100644 --- a/src/plugins/todo/optionspage.cpp +++ b/src/plugins/todo/optionspage.cpp @@ -45,7 +45,7 @@ namespace Internal { OptionsPage::OptionsPage(const Settings &settings, QObject *parent) : IOptionsPage(parent), - m_dialog(0) + m_widget(0) { setSettings(settings); @@ -61,16 +61,18 @@ void OptionsPage::setSettings(const Settings &settings) m_settings = settings; } -QWidget *OptionsPage::createPage(QWidget *parent) +QWidget *OptionsPage::widget() { - m_dialog = new OptionsDialog(parent); - m_dialog->setSettings(m_settings); - return m_dialog; + if (!m_widget) { + m_widget = new OptionsDialog; + m_widget->setSettings(m_settings); + } + return m_widget; } void OptionsPage::apply() { - Settings newSettings = m_dialog->settings(); + Settings newSettings = m_widget->settings(); if (newSettings != m_settings) { m_settings = newSettings; @@ -80,11 +82,7 @@ void OptionsPage::apply() void OptionsPage::finish() { -} - -bool OptionsPage::matches(const QString &searchKeyWord) const -{ - return searchKeyWord == QLatin1String("todo"); + delete m_widget; } } // namespace Internal diff --git a/src/plugins/todo/optionspage.h b/src/plugins/todo/optionspage.h index bec2354620..7b16e3aee9 100644 --- a/src/plugins/todo/optionspage.h +++ b/src/plugins/todo/optionspage.h @@ -35,6 +35,8 @@ #include <coreplugin/dialogs/ioptionspage.h> +#include <QPointer> + namespace Todo { namespace Internal { @@ -49,16 +51,15 @@ public: void setSettings(const Settings &settings); - QWidget *createPage(QWidget *parent); + QWidget *widget(); void apply(); void finish(); - bool matches(const QString &searchKeyWord) const; signals: void settingsChanged(const Settings &settings); private: - OptionsDialog *m_dialog; + QPointer<OptionsDialog> m_widget; Settings m_settings; }; diff --git a/src/plugins/todo/todooutputpane.cpp b/src/plugins/todo/todooutputpane.cpp index 2e90342145..c937c1cc99 100755 --- a/src/plugins/todo/todooutputpane.cpp +++ b/src/plugins/todo/todooutputpane.cpp @@ -194,13 +194,13 @@ void TodoOutputPane::createScopeButtons() { m_currentFileButton = new QToolButton(); m_currentFileButton->setCheckable(true); - m_currentFileButton->setText(tr("Current File")); - m_currentFileButton->setToolTip(tr("Scan in the current opened file")); + m_currentFileButton->setText(tr("Current Document")); + m_currentFileButton->setToolTip(tr("Scan only the currently edited document.")); m_wholeProjectButton = new QToolButton(); m_wholeProjectButton->setCheckable(true); - m_wholeProjectButton->setText(tr("Whole Project")); - m_wholeProjectButton->setToolTip(tr("Scan in the whole project")); + m_wholeProjectButton->setText(tr("Active Project")); + m_wholeProjectButton->setToolTip(tr("Scan the whole active project.")); m_scopeButtons = new QButtonGroup(); m_scopeButtons->addButton(m_wholeProjectButton); diff --git a/src/plugins/updateinfo/settingspage.cpp b/src/plugins/updateinfo/settingspage.cpp index 80ad3c9500..d5373c85a8 100644 --- a/src/plugins/updateinfo/settingspage.cpp +++ b/src/plugins/updateinfo/settingspage.cpp @@ -36,7 +36,7 @@ using namespace UpdateInfo; using namespace UpdateInfo::Internal; SettingsPage::SettingsPage(UpdateInfoPlugin *plugin) - : m_page(0) + : m_widget(0) , m_plugin(plugin) { setId(Constants::FILTER_OPTIONS_PAGE); @@ -46,15 +46,15 @@ SettingsPage::SettingsPage(UpdateInfoPlugin *plugin) setDisplayCategory(QCoreApplication::translate("Core", Core::Constants::SETTINGS_TR_CATEGORY_CORE)); } -QWidget *SettingsPage::createPage(QWidget *parent) +QWidget *SettingsPage::widget() { - m_page = new QWidget(parent); - m_ui.setupUi(m_page); - if (m_searchKeywords.isEmpty()) - m_searchKeywords = m_ui.m_info->text(); - m_ui.m_timeTable->setItemText(m_ui.m_timeTable->currentIndex(), QTime(m_plugin->scheduledUpdateTime()) - .toString(QLatin1String("hh:mm"))); - return m_page; + if (!m_widget) { + m_widget = new QWidget; + m_ui.setupUi(m_widget); + m_ui.m_timeTable->setItemText(m_ui.m_timeTable->currentIndex(), QTime(m_plugin->scheduledUpdateTime()) + .toString(QLatin1String("hh:mm"))); + } + return m_widget; } void SettingsPage::apply() @@ -66,9 +66,5 @@ void SettingsPage::apply() void SettingsPage::finish() { -} - -bool SettingsPage::matches(const QString &searchKey) const -{ - return m_searchKeywords.contains(searchKey, Qt::CaseInsensitive); + delete m_widget; } diff --git a/src/plugins/updateinfo/settingspage.h b/src/plugins/updateinfo/settingspage.h index 4c7c65b02a..24bf944452 100644 --- a/src/plugins/updateinfo/settingspage.h +++ b/src/plugins/updateinfo/settingspage.h @@ -34,6 +34,8 @@ #include <coreplugin/dialogs/ioptionspage.h> +#include <QPointer> + namespace UpdateInfo { namespace Internal { @@ -46,15 +48,13 @@ class SettingsPage : public Core::IOptionsPage public: explicit SettingsPage(UpdateInfoPlugin *plugin); - QWidget *createPage(QWidget *parent); + QWidget *widget(); void apply(); void finish(); - bool matches(const QString &searchKey) const; private: - QWidget *m_page; + QPointer<QWidget> m_widget; Ui::SettingsWidget m_ui; - QString m_searchKeywords; UpdateInfoPlugin *m_plugin; }; diff --git a/src/plugins/valgrind/suppressiondialog.cpp b/src/plugins/valgrind/suppressiondialog.cpp index 14fabc5f0c..c5b7bac661 100644 --- a/src/plugins/valgrind/suppressiondialog.cpp +++ b/src/plugins/valgrind/suppressiondialog.cpp @@ -158,6 +158,7 @@ SuppressionDialog::SuppressionDialog(MemcheckErrorView *view, const QList<Error> } m_fileChooser->setExpectedKind(Utils::PathChooser::File); + m_fileChooser->setHistoryCompleter(QLatin1String("Valgrind.Suppression.History")); m_fileChooser->setPath(defaultSuppFile.fileName()); m_fileChooser->setPromptDialogFilter(QLatin1String("*.supp")); m_fileChooser->setPromptDialogTitle(tr("Select Suppression File")); diff --git a/src/plugins/valgrind/valgrindconfigwidget.cpp b/src/plugins/valgrind/valgrindconfigwidget.cpp index 56b391b1c8..6efc85687c 100644 --- a/src/plugins/valgrind/valgrindconfigwidget.cpp +++ b/src/plugins/valgrind/valgrindconfigwidget.cpp @@ -55,6 +55,7 @@ ValgrindConfigWidget::ValgrindConfigWidget(ValgrindBaseSettings *settings, m_model = new QStandardItemModel(this); m_ui->valgrindExeChooser->setExpectedKind(Utils::PathChooser::ExistingCommand); + m_ui->valgrindExeChooser->setHistoryCompleter(QLatin1String("Valgrind.Command.History")); m_ui->valgrindExeChooser->setPromptDialogTitle(tr("Valgrind Command")); updateUi(); diff --git a/src/plugins/valgrind/valgrindplugin.cpp b/src/plugins/valgrind/valgrindplugin.cpp index 9ec9523048..d13aa27cb8 100644 --- a/src/plugins/valgrind/valgrindplugin.cpp +++ b/src/plugins/valgrind/valgrindplugin.cpp @@ -52,6 +52,7 @@ #include <QtPlugin> #include <QCoreApplication> +#include <QPointer> using namespace Analyzer; @@ -72,14 +73,25 @@ public: setCategoryIcon(QLatin1String(":/images/analyzer_category.png")); } - QWidget *createPage(QWidget *parent) { - return new ValgrindConfigWidget(theGlobalSettings, parent, true); + QWidget *widget() + { + if (!m_widget) + m_widget = new ValgrindConfigWidget(theGlobalSettings, 0, true); + return m_widget; } - void apply() { + void apply() + { theGlobalSettings->writeSettings(); } - void finish() {} + + void finish() + { + delete m_widget; + } + +private: + QPointer<QWidget> m_widget; }; class ValgrindAction : public AnalyzerAction diff --git a/src/plugins/vcsbase/basecheckoutwizardpage.cpp b/src/plugins/vcsbase/basecheckoutwizardpage.cpp index 5c2e60882b..672f3deea9 100644 --- a/src/plugins/vcsbase/basecheckoutwizardpage.cpp +++ b/src/plugins/vcsbase/basecheckoutwizardpage.cpp @@ -75,6 +75,7 @@ BaseCheckoutWizardPage::BaseCheckoutWizardPage(QWidget *parent) : this, SLOT(slotChanged())); d->ui.pathChooser->setExpectedKind(Utils::PathChooser::ExistingDirectory); + d->ui.pathChooser->setHistoryCompleter(QLatin1String("Vcs.CheckoutDir.History")); connect(d->ui.pathChooser, SIGNAL(validChanged()), this, SLOT(slotChanged())); d->ui.branchComboBox->setEnabled(false); diff --git a/src/plugins/vcsbase/basevcseditorfactory.cpp b/src/plugins/vcsbase/basevcseditorfactory.cpp index ad27f66663..186d308860 100644 --- a/src/plugins/vcsbase/basevcseditorfactory.cpp +++ b/src/plugins/vcsbase/basevcseditorfactory.cpp @@ -54,12 +54,10 @@ public: BaseVcsEditorFactoryPrivate(const VcsBaseEditorParameters *t); const VcsBaseEditorParameters *m_type; - TextEditor::TextEditorActionHandler *m_editorHandler; }; BaseVcsEditorFactoryPrivate::BaseVcsEditorFactoryPrivate(const VcsBaseEditorParameters *t) : - m_type(t), - m_editorHandler(new TextEditor::TextEditorActionHandler(t->context)) + m_type(t) { } @@ -71,6 +69,7 @@ BaseVcsEditorFactory::BaseVcsEditorFactory(const VcsBaseEditorParameters *t) setId(t->id); setDisplayName(QCoreApplication::translate("VCS", t->displayName)); addMimeType(t->mimeType); + new TextEditor::TextEditorActionHandler(this, t->context); } BaseVcsEditorFactory::~BaseVcsEditorFactory() @@ -83,12 +82,8 @@ Core::IEditor *BaseVcsEditorFactory::createEditor(QWidget *parent) VcsBaseEditorWidget *vcsEditor = createVcsBaseEditor(d->m_type, parent); vcsEditor->setMimeType(mimeTypes().front()); - d->m_editorHandler->setupActions(vcsEditor); - // Wire font settings and set initial values - connect(TextEditor::TextEditorSettings::instance(), SIGNAL(fontSettingsChanged(TextEditor::FontSettings)), - vcsEditor, SLOT(setFontSettings(TextEditor::FontSettings))); - vcsEditor->setFontSettings(TextEditor::TextEditorSettings::fontSettings()); + TextEditor::TextEditorSettings::initializeEditor(vcsEditor); return vcsEditor->editor(); } diff --git a/src/plugins/vcsbase/commonsettingspage.cpp b/src/plugins/vcsbase/commonsettingspage.cpp index ccb423df12..d10258fb27 100644 --- a/src/plugins/vcsbase/commonsettingspage.cpp +++ b/src/plugins/vcsbase/commonsettingspage.cpp @@ -48,13 +48,18 @@ CommonSettingsWidget::CommonSettingsWidget(QWidget *parent) : { m_ui->setupUi(this); m_ui->submitMessageCheckScriptChooser->setExpectedKind(Utils::PathChooser::ExistingCommand); + m_ui->submitMessageCheckScriptChooser->setHistoryCompleter(QLatin1String("Vcs.MessageCheckScript.History")); m_ui->nickNameFieldsFileChooser->setExpectedKind(Utils::PathChooser::File); + m_ui->nickNameFieldsFileChooser->setHistoryCompleter(QLatin1String("Vcs.NickFields.History")); m_ui->nickNameMailMapChooser->setExpectedKind(Utils::PathChooser::File); + m_ui->nickNameMailMapChooser->setHistoryCompleter(QLatin1String("Vcs.NickMap.History")); m_ui->sshPromptChooser->setExpectedKind(Utils::PathChooser::ExistingCommand); + m_ui->sshPromptChooser->setHistoryCompleter(QLatin1String("Vcs.SshPrompt.History")); const QString patchToolTip = tr("Command used for reverting diff chunks"); m_ui->patchCommandLabel->setToolTip(patchToolTip); m_ui->patchChooser->setToolTip(patchToolTip); m_ui->patchChooser->setExpectedKind(Utils::PathChooser::ExistingCommand); + m_ui->patchChooser->setHistoryCompleter(QLatin1String("Vcs.PatchCommand.History")); } CommonSettingsWidget::~CommonSettingsWidget() @@ -110,12 +115,12 @@ CommonOptionsPage::CommonOptionsPage(QObject *parent) : setDisplayName(QCoreApplication::translate("VcsBase", Constants::VCS_COMMON_SETTINGS_NAME)); } -QWidget *CommonOptionsPage::createPage(QWidget *parent) +QWidget *CommonOptionsPage::widget() { - m_widget = new CommonSettingsWidget(parent); - m_widget->setSettings(m_settings); - if (m_searchKeyWords.isEmpty()) - m_searchKeyWords = m_widget->searchKeyWordMatchString(); + if (!m_widget) { + m_widget = new CommonSettingsWidget; + m_widget->setSettings(m_settings); + } return m_widget; } @@ -131,9 +136,9 @@ void CommonOptionsPage::apply() } } -bool CommonOptionsPage::matches(const QString &key) const +void CommonOptionsPage::finish() { - return m_searchKeyWords.contains(key, Qt::CaseInsensitive); + delete m_widget; } } // namespace Internal diff --git a/src/plugins/vcsbase/commonsettingspage.h b/src/plugins/vcsbase/commonsettingspage.h index ebd8b5aac4..3917b86cdc 100644 --- a/src/plugins/vcsbase/commonsettingspage.h +++ b/src/plugins/vcsbase/commonsettingspage.h @@ -34,6 +34,7 @@ #include "vcsbaseoptionspage.h" +#include <QPointer> #include <QWidget> namespace VcsBase { @@ -65,10 +66,9 @@ class CommonOptionsPage : public VcsBaseOptionsPage public: explicit CommonOptionsPage(QObject *parent = 0); - QWidget *createPage(QWidget *parent); + QWidget *widget(); void apply(); - void finish() { } - bool matches(const QString &key) const; + void finish(); CommonVcsSettings settings() const { return m_settings; } @@ -76,9 +76,8 @@ signals: void settingsChanged(const VcsBase::Internal::CommonVcsSettings &s); private: - CommonSettingsWidget *m_widget; + QPointer<CommonSettingsWidget> m_widget; CommonVcsSettings m_settings; - QString m_searchKeyWords; }; } // namespace Internal diff --git a/src/plugins/vcsbase/vcsbaseclient.cpp b/src/plugins/vcsbase/vcsbaseclient.cpp index 31ab75cd64..b4ff6833d8 100644 --- a/src/plugins/vcsbase/vcsbaseclient.cpp +++ b/src/plugins/vcsbase/vcsbaseclient.cpp @@ -367,7 +367,9 @@ void VcsBaseClient::diff(const QString &workingDir, const QStringList &files, QStringList args; const QStringList paramArgs = paramWidget != 0 ? paramWidget->arguments() : QStringList(); args << vcsCmdString << extraOptions << paramArgs << files; + QTextCodec *codec = source.isEmpty() ? static_cast<QTextCodec *>(0) : VcsBase::VcsBaseEditorWidget::getCodec(source); Command *command = createCommand(workingDir, editor); + command->setCodec(codec); enqueueJob(command, args, exitCodeInterpreter(DiffCommand, command)); } diff --git a/src/plugins/vcsbase/vcsbaseeditor.cpp b/src/plugins/vcsbase/vcsbaseeditor.cpp index 7f42d12ada..acb1ccb008 100644 --- a/src/plugins/vcsbase/vcsbaseeditor.cpp +++ b/src/plugins/vcsbase/vcsbaseeditor.cpp @@ -562,7 +562,6 @@ public: const VcsBaseEditorParameters *m_parameters; - QString m_source; QString m_workingDirectory; QRegExp m_diffFilePattern; @@ -738,12 +737,12 @@ void VcsBaseEditorWidget::setForceReadOnly(bool b) QString VcsBaseEditorWidget::source() const { - return d->m_source; + return VcsBasePlugin::source(editor()); } void VcsBaseEditorWidget::setSource(const QString &source) { - d->m_source = source; + VcsBasePlugin::setSource(editor(), source); } QString VcsBaseEditorWidget::annotateRevisionTextFormat() const diff --git a/src/plugins/vcsbase/vcsbaseplugin.cpp b/src/plugins/vcsbase/vcsbaseplugin.cpp index 732349e0f6..2d3d261ad2 100644 --- a/src/plugins/vcsbase/vcsbaseplugin.cpp +++ b/src/plugins/vcsbase/vcsbaseplugin.cpp @@ -241,8 +241,7 @@ void StateListener::slotStateChanged() const QList<Core::IEditor *> editors = Core::EditorManager::documentModel()->editorsForDocument(currentDocument); if (!editors.isEmpty()) { - if (QWidget *editorWidget = editors.first()->widget()) - state.currentFile = editorWidget->property("source").toString(); + state.currentFile = VcsBasePlugin::source(editors.first()); } } } @@ -749,6 +748,18 @@ bool VcsBasePlugin::isSshPromptConfigured() return !sshPrompt().isEmpty(); } +static const char SOURCE_PROPERTY[] = "qtcreator_source"; + +void VcsBasePlugin::setSource(Core::IEditor *editor, const QString &source) +{ + editor->setProperty(SOURCE_PROPERTY, source); +} + +QString VcsBasePlugin::source(Core::IEditor *editor) +{ + return editor->property(SOURCE_PROPERTY).toString(); +} + void VcsBasePlugin::setProcessEnvironment(QProcessEnvironment *e, bool forceCLocale, const QString &sshPromptBinary) diff --git a/src/plugins/vcsbase/vcsbaseplugin.h b/src/plugins/vcsbase/vcsbaseplugin.h index 03d18b687b..193971b2d6 100644 --- a/src/plugins/vcsbase/vcsbaseplugin.h +++ b/src/plugins/vcsbase/vcsbaseplugin.h @@ -48,6 +48,7 @@ namespace Utils { struct SynchronousProcessResponse; } namespace Core { class IVersionControl; class Id; +class IEditor; } namespace VcsBase { @@ -155,6 +156,11 @@ public: // Returns whether an SSH prompt is configured. static bool isSshPromptConfigured(); + // Sets the source of editor contents, can be directory or file. + static void setSource(Core::IEditor *editor, const QString &source); + // Returns the source of editor contents. + static QString source(Core::IEditor *editor); + // Convenience to synchronously run VCS commands enum RunVcsFlags { ShowStdOutInLogWindow = 0x1, // Append standard output to VCS output window. |