Commit 76f69d0e authored by tomdeblauwe's avatar tomdeblauwe Committed by Daniel Teske

CMake: add auto completion for cmake editor

Original Patch by tomdeblauwe <tdb@traficon.be>

Change-Id: I54aea31755dd5702e6998c8343114d34a8752f7b
Reviewed-by: default avatarDaniel Teske <daniel.teske@digia.com>
parent 16f272e0
/****************************************************************************
**
** Copyright (C) 2012 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 "cmakefilecompletionassist.h"
#include "cmakeprojectconstants.h"
#include "cmakeprojectmanager.h"
#include <texteditor/codeassist/keywordscompletionassist.h>
using namespace CMakeProjectManager::Internal;
using namespace TextEditor;
// -------------------------------
// CMakeFileCompletionAssistProvider
// -------------------------------
CMakeFileCompletionAssistProvider::CMakeFileCompletionAssistProvider(CMakeSettingsPage *settingsPage)
: m_settingsPage(settingsPage)
{}
CMakeFileCompletionAssistProvider::~CMakeFileCompletionAssistProvider()
{}
bool CMakeFileCompletionAssistProvider::supportsEditor(const Core::Id &editorId) const
{
return editorId == CMakeProjectManager::Constants::CMAKE_EDITOR_ID;
}
IAssistProcessor *CMakeFileCompletionAssistProvider::createProcessor() const
{
return new KeywordsCompletionAssistProcessor(m_settingsPage->keywords());
}
...@@ -27,27 +27,30 @@ ...@@ -27,27 +27,30 @@
** **
****************************************************************************/ ****************************************************************************/
#ifndef PROFILEKEYWORDS_H #ifndef CMAKEFILECOMPLETIONASSIST_H
#define PROFILEKEYWORDS_H #define CMAKEFILECOMPLETIONASSIST_H
#include <QStringList> #include <texteditor/codeassist/completionassistprovider.h>
namespace Qt4ProjectManager {
namespace CMakeProjectManager {
namespace Internal { namespace Internal {
class ProFileKeywords class CMakeSettingsPage;
class CMakeFileCompletionAssistProvider : public TextEditor::CompletionAssistProvider
{ {
public: public:
static QStringList variables(); CMakeFileCompletionAssistProvider(CMakeSettingsPage *settingsPage);
static QStringList functions(); ~CMakeFileCompletionAssistProvider();
static bool isVariable(const QString &word);
static bool isFunction(const QString &word); bool supportsEditor(const Core::Id &editorId) const;
TextEditor::IAssistProcessor *createProcessor() const;
private: private:
ProFileKeywords(); CMakeSettingsPage *m_settingsPage;
}; };
} // namespace Internal } // Internal
} // namespace Qt4ProjectManager } // CMakeProjectManager
#endif // PROFILEKEYWORDS_H #endif // CMAKEFILECOMPLETIONASSIST_H
...@@ -250,102 +250,26 @@ CMakeSettingsPage::CMakeSettingsPage() ...@@ -250,102 +250,26 @@ CMakeSettingsPage::CMakeSettingsPage()
ProjectExplorer::Constants::PROJECTEXPLORER_SETTINGS_TR_CATEGORY)); ProjectExplorer::Constants::PROJECTEXPLORER_SETTINGS_TR_CATEGORY));
setCategoryIcon(QLatin1String(ProjectExplorer::Constants::PROJECTEXPLORER_SETTINGS_CATEGORY_ICON)); setCategoryIcon(QLatin1String(ProjectExplorer::Constants::PROJECTEXPLORER_SETTINGS_CATEGORY_ICON));
m_userCmake.process = 0;
m_pathCmake.process = 0;
m_userCmake.hasCodeBlocksMsvcGenerator = false;
m_pathCmake.hasCodeBlocksMsvcGenerator = false;
m_userCmake.hasCodeBlocksNinjaGenerator = false;
m_pathCmake.hasCodeBlocksNinjaGenerator = false;
QSettings *settings = Core::ICore::settings(); QSettings *settings = Core::ICore::settings();
settings->beginGroup(QLatin1String("CMakeSettings")); settings->beginGroup(QLatin1String("CMakeSettings"));
m_userCmake.executable = settings->value(QLatin1String("cmakeExecutable")).toString(); m_cmakeValidatorForUser.setCMakeExecutable(settings->value(QLatin1String("cmakeExecutable")).toString());
settings->endGroup(); settings->endGroup();
updateInfo(&m_userCmake); m_cmakeValidatorForSystem.setCMakeExecutable(findCmakeExecutable());
m_pathCmake.executable = findCmakeExecutable();
updateInfo(&m_pathCmake);
}
void CMakeSettingsPage::startProcess(CMakeValidator *cmakeValidator)
{
cmakeValidator->process = new QProcess();
if (cmakeValidator == &m_pathCmake) // ugly
connect(cmakeValidator->process, SIGNAL(finished(int)),
this, SLOT(userCmakeFinished()));
else
connect(cmakeValidator->process, SIGNAL(finished(int)),
this, SLOT(pathCmakeFinished()));
cmakeValidator->process->start(cmakeValidator->executable, QStringList(QLatin1String("--help")));
cmakeValidator->process->waitForStarted();
}
void CMakeSettingsPage::userCmakeFinished()
{
cmakeFinished(&m_userCmake);
}
void CMakeSettingsPage::pathCmakeFinished()
{
cmakeFinished(&m_pathCmake);
}
void CMakeSettingsPage::cmakeFinished(CMakeValidator *cmakeValidator) const
{
if (cmakeValidator->process) {
cmakeValidator->process->waitForFinished();
QString response = cmakeValidator->process->readAll();
QRegExp versionRegexp(QLatin1String("^cmake version ([\\d\\.]*)"));
versionRegexp.indexIn(response);
//m_supportsQtCreator = response.contains(QLatin1String("QtCreator"));
cmakeValidator->hasCodeBlocksNinjaGenerator = response.contains(QLatin1String("CodeBlocks - Ninja"));
cmakeValidator->hasCodeBlocksMsvcGenerator = response.contains(QLatin1String("CodeBlocks - NMake Makefiles"));
cmakeValidator->version = versionRegexp.cap(1);
if (!(versionRegexp.capturedTexts().size() > 3))
cmakeValidator->version += QLatin1Char('.') + versionRegexp.cap(3);
if (cmakeValidator->version.isEmpty())
cmakeValidator->state = CMakeValidator::INVALID;
else
cmakeValidator->state = CMakeValidator::VALID;
cmakeValidator->process->deleteLater();
cmakeValidator->process = 0;
}
} }
bool CMakeSettingsPage::isCMakeExecutableValid() const bool CMakeSettingsPage::isCMakeExecutableValid() const
{ {
if (m_userCmake.state == CMakeValidator::RUNNING) { if (m_cmakeValidatorForUser.isValid())
disconnect(m_userCmake.process, SIGNAL(finished(int)),
this, SLOT(userCmakeFinished()));
m_userCmake.process->waitForFinished();
// Parse the output now
cmakeFinished(&m_userCmake);
}
if (m_userCmake.state == CMakeValidator::VALID)
return true; return true;
if (m_pathCmake.state == CMakeValidator::RUNNING) {
disconnect(m_userCmake.process, SIGNAL(finished(int)), return m_cmakeValidatorForSystem.isValid();
this, SLOT(pathCmakeFinished()));
m_pathCmake.process->waitForFinished();
// Parse the output now
cmakeFinished(&m_pathCmake);
}
return m_pathCmake.state == CMakeValidator::VALID;
} }
CMakeSettingsPage::~CMakeSettingsPage() CMakeSettingsPage::~CMakeSettingsPage()
{ {
if (m_userCmake.process) m_cmakeValidatorForUser.cancel();
m_userCmake.process->waitForFinished(); m_cmakeValidatorForSystem.cancel();
delete m_userCmake.process;
if (m_pathCmake.process)
m_pathCmake.process->waitForFinished();
delete m_pathCmake.process;
} }
QString CMakeSettingsPage::findCmakeExecutable() const QString CMakeSettingsPage::findCmakeExecutable() const
...@@ -363,28 +287,15 @@ QWidget *CMakeSettingsPage::createPage(QWidget *parent) ...@@ -363,28 +287,15 @@ QWidget *CMakeSettingsPage::createPage(QWidget *parent)
m_pathchooser->setExpectedKind(Utils::PathChooser::ExistingCommand); m_pathchooser->setExpectedKind(Utils::PathChooser::ExistingCommand);
formLayout->addRow(tr("Executable:"), m_pathchooser); formLayout->addRow(tr("Executable:"), m_pathchooser);
formLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Ignored, QSizePolicy::MinimumExpanding)); formLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Ignored, QSizePolicy::MinimumExpanding));
m_pathchooser->setPath(m_userCmake.executable); m_pathchooser->setPath(m_cmakeValidatorForUser.cmakeExecutable());
return outerWidget; return outerWidget;
} }
void CMakeSettingsPage::updateInfo(CMakeValidator *cmakeValidator)
{
QFileInfo fi(cmakeValidator->executable);
if (fi.exists() && fi.isExecutable()) {
// Run it to find out more
cmakeValidator->state = CMakeValidator::RUNNING;
startProcess(cmakeValidator);
} else {
cmakeValidator->state = CMakeValidator::INVALID;
}
saveSettings();
}
void CMakeSettingsPage::saveSettings() const void CMakeSettingsPage::saveSettings() const
{ {
QSettings *settings = Core::ICore::settings(); QSettings *settings = Core::ICore::settings();
settings->beginGroup(QLatin1String("CMakeSettings")); settings->beginGroup(QLatin1String("CMakeSettings"));
settings->setValue(QLatin1String("cmakeExecutable"), m_userCmake.executable); settings->setValue(QLatin1String("cmakeExecutable"), m_cmakeValidatorForUser.cmakeExecutable());
settings->endGroup(); settings->endGroup();
} }
...@@ -392,10 +303,10 @@ void CMakeSettingsPage::apply() ...@@ -392,10 +303,10 @@ void CMakeSettingsPage::apply()
{ {
if (!m_pathchooser) // page was never shown if (!m_pathchooser) // page was never shown
return; return;
if (m_userCmake.executable == m_pathchooser->path()) if (m_cmakeValidatorForUser.cmakeExecutable() == m_pathchooser->path())
return; return;
m_userCmake.executable = m_pathchooser->path(); m_cmakeValidatorForUser.setCMakeExecutable(m_pathchooser->path());
updateInfo(&m_userCmake); saveSettings();
} }
void CMakeSettingsPage::finish() void CMakeSettingsPage::finish()
...@@ -407,36 +318,46 @@ QString CMakeSettingsPage::cmakeExecutable() const ...@@ -407,36 +318,46 @@ QString CMakeSettingsPage::cmakeExecutable() const
{ {
if (!isCMakeExecutableValid()) if (!isCMakeExecutableValid())
return QString(); return QString();
if (m_userCmake.state == CMakeValidator::VALID)
return m_userCmake.executable; if (m_cmakeValidatorForUser.isValid())
else return m_cmakeValidatorForUser.cmakeExecutable();
return m_pathCmake.executable; if (m_cmakeValidatorForSystem.isValid())
return m_cmakeValidatorForSystem.cmakeExecutable();
return QString();
} }
void CMakeSettingsPage::setCMakeExecutable(const QString &executable) void CMakeSettingsPage::setCMakeExecutable(const QString &executable)
{ {
if (m_userCmake.executable == executable) if (m_cmakeValidatorForUser.cmakeExecutable() == executable)
return; return;
m_userCmake.executable = executable; m_cmakeValidatorForUser.setCMakeExecutable(executable);
updateInfo(&m_userCmake);
} }
bool CMakeSettingsPage::hasCodeBlocksMsvcGenerator() const bool CMakeSettingsPage::hasCodeBlocksMsvcGenerator() const
{ {
if (!isCMakeExecutableValid()) if (m_cmakeValidatorForUser.isValid())
return false; return m_cmakeValidatorForUser.hasCodeBlocksMsvcGenerator();
if (m_userCmake.state == CMakeValidator::VALID) if (m_cmakeValidatorForSystem.isValid())
return m_userCmake.hasCodeBlocksMsvcGenerator; return m_cmakeValidatorForSystem.hasCodeBlocksMsvcGenerator();
else return false;
return m_pathCmake.hasCodeBlocksMsvcGenerator;
} }
bool CMakeSettingsPage::hasCodeBlocksNinjaGenerator() const bool CMakeSettingsPage::hasCodeBlocksNinjaGenerator() const
{ {
if (!isCMakeExecutableValid()) if (m_cmakeValidatorForUser.isValid())
return false; return m_cmakeValidatorForUser.hasCodeBlocksNinjaGenerator();
if (m_userCmake.state == CMakeValidator::VALID) if (m_cmakeValidatorForSystem.isValid())
return m_userCmake.hasCodeBlocksNinjaGenerator; return m_cmakeValidatorForSystem.hasCodeBlocksNinjaGenerator();
else return false;
return m_pathCmake.hasCodeBlocksNinjaGenerator; }
TextEditor::Keywords CMakeSettingsPage::keywords()
{
if (m_cmakeValidatorForUser.isValid())
return m_cmakeValidatorForUser.keywords();
if (m_cmakeValidatorForSystem.isValid())
return m_cmakeValidatorForSystem.keywords();
return TextEditor::Keywords(QStringList(), QStringList(), QMap<QString, QStringList>());
} }
...@@ -35,6 +35,7 @@ ...@@ -35,6 +35,7 @@
#include <projectexplorer/project.h> #include <projectexplorer/project.h>
#include <projectexplorer/projectnodes.h> #include <projectexplorer/projectnodes.h>
#include <coreplugin/icontext.h> #include <coreplugin/icontext.h>
#include <texteditor/codeassist/keywordscompletionassist.h>
#include <utils/environment.h> #include <utils/environment.h>
#include <utils/pathchooser.h> #include <utils/pathchooser.h>
...@@ -42,9 +43,11 @@ ...@@ -42,9 +43,11 @@
#include <QFuture> #include <QFuture>
#include <QStringList> #include <QStringList>
#include <QDir> #include <QDir>
#include <QVector>
#include <QAction> #include <QAction>
QT_FORWARD_DECLARE_CLASS(QProcess) #include "cmakevalidator.h"
QT_FORWARD_DECLARE_CLASS(QLabel) QT_FORWARD_DECLARE_CLASS(QLabel)
namespace Utils { namespace Utils {
...@@ -95,17 +98,6 @@ private: ...@@ -95,17 +98,6 @@ private:
ProjectExplorer::Project *m_contextProject; ProjectExplorer::Project *m_contextProject;
}; };
struct CMakeValidator
{
enum STATE { VALID, INVALID, RUNNING };
STATE state;
QProcess *process;
bool hasCodeBlocksMsvcGenerator;
bool hasCodeBlocksNinjaGenerator;
QString version;
QString executable;
};
class CMakeSettingsPage : public Core::IOptionsPage class CMakeSettingsPage : public Core::IOptionsPage
{ {
Q_OBJECT Q_OBJECT
...@@ -124,20 +116,15 @@ public: ...@@ -124,20 +116,15 @@ public:
bool hasCodeBlocksMsvcGenerator() const; bool hasCodeBlocksMsvcGenerator() const;
bool hasCodeBlocksNinjaGenerator() const; bool hasCodeBlocksNinjaGenerator() const;
private slots: TextEditor::Keywords keywords();
void userCmakeFinished();
void pathCmakeFinished();
private: private:
void cmakeFinished(CMakeValidator *cmakeValidator) const;
void saveSettings() const; void saveSettings() const;
QString findCmakeExecutable() const; QString findCmakeExecutable() const;
void startProcess(CMakeValidator *cmakeValidator);
void updateInfo(CMakeValidator *cmakeValidator);
Utils::PathChooser *m_pathchooser; Utils::PathChooser *m_pathchooser;
mutable CMakeValidator m_userCmake; CMakeValidator m_cmakeValidatorForUser;
mutable CMakeValidator m_pathCmake; CMakeValidator m_cmakeValidatorForSystem;
}; };
} // namespace Internal } // namespace Internal
......
...@@ -17,7 +17,9 @@ HEADERS = cmakeproject.h \ ...@@ -17,7 +17,9 @@ HEADERS = cmakeproject.h \
cmakeeditor.h \ cmakeeditor.h \
cmakehighlighter.h \ cmakehighlighter.h \
cmakeuicodemodelsupport.h \ cmakeuicodemodelsupport.h \
cmakelocatorfilter.h cmakelocatorfilter.h \
cmakefilecompletionassist.h \
cmakevalidator.h
SOURCES = cmakeproject.cpp \ SOURCES = cmakeproject.cpp \
cmakeprojectplugin.cpp \ cmakeprojectplugin.cpp \
...@@ -31,7 +33,9 @@ SOURCES = cmakeproject.cpp \ ...@@ -31,7 +33,9 @@ SOURCES = cmakeproject.cpp \
cmakeeditor.cpp \ cmakeeditor.cpp \
cmakehighlighter.cpp \ cmakehighlighter.cpp \
cmakeuicodemodelsupport.cpp \ cmakeuicodemodelsupport.cpp \
cmakelocatorfilter.cpp cmakelocatorfilter.cpp \
cmakefilecompletionassist.cpp \
cmakevalidator.cpp
RESOURCES += cmakeproject.qrc RESOURCES += cmakeproject.qrc
......
...@@ -22,6 +22,8 @@ QtcPlugin { ...@@ -22,6 +22,8 @@ QtcPlugin {
"cmakeeditor.h", "cmakeeditor.h",
"cmakeeditorfactory.cpp", "cmakeeditorfactory.cpp",
"cmakeeditorfactory.h", "cmakeeditorfactory.h",
"cmakefilecompletionassist.cpp",
"cmakefilecompletionassist.h",
"cmakehighlighter.cpp", "cmakehighlighter.cpp",
"cmakehighlighter.h", "cmakehighlighter.h",
"cmakelocatorfilter.cpp", "cmakelocatorfilter.cpp",
...@@ -42,6 +44,8 @@ QtcPlugin { ...@@ -42,6 +44,8 @@ QtcPlugin {
"cmakerunconfiguration.h", "cmakerunconfiguration.h",
"cmakeuicodemodelsupport.cpp", "cmakeuicodemodelsupport.cpp",
"cmakeuicodemodelsupport.h", "cmakeuicodemodelsupport.h",
"cmakevalidator.cpp",
"cmakevalidator.h",
"makestep.cpp", "makestep.cpp",
"makestep.h", "makestep.h",
] ]
......
...@@ -35,6 +35,7 @@ ...@@ -35,6 +35,7 @@
#include "makestep.h" #include "makestep.h"
#include "cmakeprojectconstants.h" #include "cmakeprojectconstants.h"
#include "cmakelocatorfilter.h" #include "cmakelocatorfilter.h"
#include "cmakefilecompletionassist.h"
#include <coreplugin/icore.h> #include <coreplugin/icore.h>
#include <coreplugin/mimedatabase.h> #include <coreplugin/mimedatabase.h>
...@@ -68,7 +69,7 @@ bool CMakeProjectPlugin::initialize(const QStringList & /*arguments*/, QString * ...@@ -68,7 +69,7 @@ bool CMakeProjectPlugin::initialize(const QStringList & /*arguments*/, QString *
addAutoReleasedObject(new CMakeEditorFactory(manager)); addAutoReleasedObject(new CMakeEditorFactory(manager));
addAutoReleasedObject(new CMakeLocatorFilter); addAutoReleasedObject(new CMakeLocatorFilter);
addAutoReleasedObject(new CMakeFileCompletionAssistProvider(cmp));
return true; return true;
} }
......
/****************************************************************************
**
** Copyright (C) 2012 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 "cmakevalidator.h"
#include <QProcess>
#include <QFileInfo>
#include <QTextDocument>
using namespace CMakeProjectManager::Internal;
///////////////////////////
// CMakeValidator
///////////////////////////
CMakeValidator::CMakeValidator()
: m_state(Invalid), m_process(0), m_hasCodeBlocksMsvcGenerator(false), m_hasCodeBlocksNinjaGenerator(false)
{
}
CMakeValidator::~CMakeValidator()
{
cancel();
}
void CMakeValidator::cancel()
{
if (m_process) {
disconnect(m_process, SIGNAL(finished(int)));
m_process->waitForFinished();
delete m_process;
m_process = 0;
}
}
void CMakeValidator::setCMakeExecutable(const QString &executable)
{
cancel();
m_process = new QProcess();
connect(m_process, SIGNAL(finished(int)),
this, SLOT(finished(int)));
m_executable = executable;
QFileInfo fi(m_executable);
if (fi.exists() && fi.isExecutable()) {
// Run it to find out more
m_state = CMakeValidator::RunningBasic;
if (!startProcess(QStringList(QLatin1String("--help"))))