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 @@
**
****************************************************************************/
#ifndef PROFILEKEYWORDS_H
#define PROFILEKEYWORDS_H
#ifndef CMAKEFILECOMPLETIONASSIST_H
#define CMAKEFILECOMPLETIONASSIST_H
#include <QStringList>
namespace Qt4ProjectManager {
#include <texteditor/codeassist/completionassistprovider.h>
namespace CMakeProjectManager {
namespace Internal {
class ProFileKeywords
class CMakeSettingsPage;
class CMakeFileCompletionAssistProvider : public TextEditor::CompletionAssistProvider
{
public:
static QStringList variables();
static QStringList functions();
static bool isVariable(const QString &word);
static bool isFunction(const QString &word);
CMakeFileCompletionAssistProvider(CMakeSettingsPage *settingsPage);
~CMakeFileCompletionAssistProvider();
bool supportsEditor(const Core::Id &editorId) const;
TextEditor::IAssistProcessor *createProcessor() const;
private:
ProFileKeywords();
CMakeSettingsPage *m_settingsPage;
};
} // namespace Internal
} // namespace Qt4ProjectManager
} // Internal
} // CMakeProjectManager
#endif // PROFILEKEYWORDS_H
#endif // CMAKEFILECOMPLETIONASSIST_H
......@@ -250,102 +250,26 @@ CMakeSettingsPage::CMakeSettingsPage()
ProjectExplorer::Constants::PROJECTEXPLORER_SETTINGS_TR_CATEGORY));
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();
settings->beginGroup(QLatin1String("CMakeSettings"));
m_userCmake.executable = settings->value(QLatin1String("cmakeExecutable")).toString();
m_cmakeValidatorForUser.setCMakeExecutable(settings->value(QLatin1String("cmakeExecutable")).toString());
settings->endGroup();
updateInfo(&m_userCmake);
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;
}
m_cmakeValidatorForSystem.setCMakeExecutable(findCmakeExecutable());
}
bool CMakeSettingsPage::isCMakeExecutableValid() const
{
if (m_userCmake.state == CMakeValidator::RUNNING) {
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)
if (m_cmakeValidatorForUser.isValid())
return true;
if (m_pathCmake.state == CMakeValidator::RUNNING) {
disconnect(m_userCmake.process, SIGNAL(finished(int)),
this, SLOT(pathCmakeFinished()));
m_pathCmake.process->waitForFinished();
// Parse the output now
cmakeFinished(&m_pathCmake);
}
return m_pathCmake.state == CMakeValidator::VALID;
return m_cmakeValidatorForSystem.isValid();
}
CMakeSettingsPage::~CMakeSettingsPage()
{
if (m_userCmake.process)
m_userCmake.process->waitForFinished();
delete m_userCmake.process;
if (m_pathCmake.process)
m_pathCmake.process->waitForFinished();
delete m_pathCmake.process;
m_cmakeValidatorForUser.cancel();
m_cmakeValidatorForSystem.cancel();
}
QString CMakeSettingsPage::findCmakeExecutable() const
......@@ -363,28 +287,15 @@ QWidget *CMakeSettingsPage::createPage(QWidget *parent)
m_pathchooser->setExpectedKind(Utils::PathChooser::ExistingCommand);
formLayout->addRow(tr("Executable:"), m_pathchooser);
formLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Ignored, QSizePolicy::MinimumExpanding));
m_pathchooser->setPath(m_userCmake.executable);
m_pathchooser->setPath(m_cmakeValidatorForUser.cmakeExecutable());
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
{
QSettings *settings = Core::ICore::settings();
settings->beginGroup(QLatin1String("CMakeSettings"));
settings->setValue(QLatin1String("cmakeExecutable"), m_userCmake.executable);
settings->setValue(QLatin1String("cmakeExecutable"), m_cmakeValidatorForUser.cmakeExecutable());
settings->endGroup();
}
......@@ -392,10 +303,10 @@ void CMakeSettingsPage::apply()
{
if (!m_pathchooser) // page was never shown
return;
if (m_userCmake.executable == m_pathchooser->path())
if (m_cmakeValidatorForUser.cmakeExecutable() == m_pathchooser->path())
return;
m_userCmake.executable = m_pathchooser->path();
updateInfo(&m_userCmake);
m_cmakeValidatorForUser.setCMakeExecutable(m_pathchooser->path());
saveSettings();
}
void CMakeSettingsPage::finish()
......@@ -407,36 +318,46 @@ QString CMakeSettingsPage::cmakeExecutable() const
{
if (!isCMakeExecutableValid())
return QString();
if (m_userCmake.state == CMakeValidator::VALID)
return m_userCmake.executable;
else
return m_pathCmake.executable;
if (m_cmakeValidatorForUser.isValid())
return m_cmakeValidatorForUser.cmakeExecutable();
if (m_cmakeValidatorForSystem.isValid())
return m_cmakeValidatorForSystem.cmakeExecutable();
return QString();
}
void CMakeSettingsPage::setCMakeExecutable(const QString &executable)
{
if (m_userCmake.executable == executable)
if (m_cmakeValidatorForUser.cmakeExecutable() == executable)
return;
m_userCmake.executable = executable;
updateInfo(&m_userCmake);
m_cmakeValidatorForUser.setCMakeExecutable(executable);
}
bool CMakeSettingsPage::hasCodeBlocksMsvcGenerator() const
{
if (!isCMakeExecutableValid())
return false;
if (m_userCmake.state == CMakeValidator::VALID)
return m_userCmake.hasCodeBlocksMsvcGenerator;
else
return m_pathCmake.hasCodeBlocksMsvcGenerator;
if (m_cmakeValidatorForUser.isValid())
return m_cmakeValidatorForUser.hasCodeBlocksMsvcGenerator();
if (m_cmakeValidatorForSystem.isValid())
return m_cmakeValidatorForSystem.hasCodeBlocksMsvcGenerator();
return false;
}
bool CMakeSettingsPage::hasCodeBlocksNinjaGenerator() const
{
if (!isCMakeExecutableValid())
return false;
if (m_userCmake.state == CMakeValidator::VALID)
return m_userCmake.hasCodeBlocksNinjaGenerator;
else
return m_pathCmake.hasCodeBlocksNinjaGenerator;
if (m_cmakeValidatorForUser.isValid())
return m_cmakeValidatorForUser.hasCodeBlocksNinjaGenerator();
if (m_cmakeValidatorForSystem.isValid())
return m_cmakeValidatorForSystem.hasCodeBlocksNinjaGenerator();
return false;
}
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 @@
#include <projectexplorer/project.h>
#include <projectexplorer/projectnodes.h>
#include <coreplugin/icontext.h>
#include <texteditor/codeassist/keywordscompletionassist.h>
#include <utils/environment.h>
#include <utils/pathchooser.h>
......@@ -42,9 +43,11 @@
#include <QFuture>
#include <QStringList>
#include <QDir>
#include <QVector>
#include <QAction>
QT_FORWARD_DECLARE_CLASS(QProcess)
#include "cmakevalidator.h"
QT_FORWARD_DECLARE_CLASS(QLabel)
namespace Utils {
......@@ -95,17 +98,6 @@ private:
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
{
Q_OBJECT
......@@ -124,20 +116,15 @@ public:
bool hasCodeBlocksMsvcGenerator() const;
bool hasCodeBlocksNinjaGenerator() const;
private slots:
void userCmakeFinished();
void pathCmakeFinished();
TextEditor::Keywords keywords();
private:
void cmakeFinished(CMakeValidator *cmakeValidator) const;
void saveSettings() const;
QString findCmakeExecutable() const;
void startProcess(CMakeValidator *cmakeValidator);
void updateInfo(CMakeValidator *cmakeValidator);
Utils::PathChooser *m_pathchooser;
mutable CMakeValidator m_userCmake;
mutable CMakeValidator m_pathCmake;
CMakeValidator m_cmakeValidatorForUser;
CMakeValidator m_cmakeValidatorForSystem;
};
} // namespace Internal
......
......@@ -17,7 +17,9 @@ HEADERS = cmakeproject.h \
cmakeeditor.h \
cmakehighlighter.h \
cmakeuicodemodelsupport.h \
cmakelocatorfilter.h
cmakelocatorfilter.h \
cmakefilecompletionassist.h \
cmakevalidator.h
SOURCES = cmakeproject.cpp \
cmakeprojectplugin.cpp \
......@@ -31,7 +33,9 @@ SOURCES = cmakeproject.cpp \
cmakeeditor.cpp \
cmakehighlighter.cpp \
cmakeuicodemodelsupport.cpp \
cmakelocatorfilter.cpp
cmakelocatorfilter.cpp \
cmakefilecompletionassist.cpp \
cmakevalidator.cpp
RESOURCES += cmakeproject.qrc
......
......@@ -22,6 +22,8 @@ QtcPlugin {
"cmakeeditor.h",
"cmakeeditorfactory.cpp",
"cmakeeditorfactory.h",
"cmakefilecompletionassist.cpp",
"cmakefilecompletionassist.h",
"cmakehighlighter.cpp",
"cmakehighlighter.h",
"cmakelocatorfilter.cpp",
......@@ -42,6 +44,8 @@ QtcPlugin {
"cmakerunconfiguration.h",
"cmakeuicodemodelsupport.cpp",
"cmakeuicodemodelsupport.h",
"cmakevalidator.cpp",
"cmakevalidator.h",
"makestep.cpp",
"makestep.h",
]
......
......@@ -35,6 +35,7 @@
#include "makestep.h"
#include "cmakeprojectconstants.h"
#include "cmakelocatorfilter.h"
#include "cmakefilecompletionassist.h"
#include <coreplugin/icore.h>
#include <coreplugin/mimedatabase.h>
......@@ -68,7 +69,7 @@ bool CMakeProjectPlugin::initialize(const QStringList & /*arguments*/, QString *
addAutoReleasedObject(new CMakeEditorFactory(manager));
addAutoReleasedObject(new CMakeLocatorFilter);
addAutoReleasedObject(new CMakeFileCompletionAssistProvider(cmp));
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"))))
m_state = CMakeValidator::Invalid;
} else {
m_state = CMakeValidator::Invalid;
}
}
void CMakeValidator::finished(int exitCode)
{
if (exitCode) {
m_state = CMakeValidator::Invalid;
return;
}
if (m_state == CMakeValidator::RunningBasic) {
QString response = m_process->readAll();
QRegExp versionRegexp(QLatin1String("^cmake version ([\\d\\.]*)"));
versionRegexp.indexIn(response);
//m_supportsQtCreator = response.contains(QLatin1String("QtCreator"));
m_hasCodeBlocksMsvcGenerator = response.contains(QLatin1String("CodeBlocks - NMake Makefiles"));
m_hasCodeBlocksNinjaGenerator = response.contains(QLatin1String("CodeBlocks - Ninja"));
m_version = versionRegexp.cap(1);
if (!(versionRegexp.capturedTexts().size() > 3))
m_version += QLatin1Char('.') + versionRegexp.cap(3);
if (m_version.isEmpty()) {
m_state = CMakeValidator::Invalid;
} else {
m_state = CMakeValidator::RunningFunctionList;
if (!startProcess(QStringList(QLatin1String("--help-command-list"))))
finished(0); // shoud never happen, just continue
}
} else if (m_state == CMakeValidator::RunningFunctionList) {
parseFunctionOutput(m_process->readAll());
m_state = CMakeValidator::RunningFunctionDetails;
if (!startProcess(QStringList(QLatin1String("--help-commands"))))
finished(0); // shoud never happen, just continue
} else if (m_state == CMakeValidator::RunningFunctionDetails) {
parseFunctionDetailsOutput(m_process->readAll());
m_state = CMakeValidator::ValidFunctionDetails;
}
}
bool CMakeValidator::isValid() const
{
if (m_state == CMakeValidator::Invalid)
return false;
if (m_state == CMakeValidator::RunningBasic)
m_process->waitForFinished();
return (m_state != CMakeValidator::Invalid);
}
bool CMakeValidator::startProcess(const QStringList &args)
{
m_process->start(m_executable, args);
return m_process->waitForStarted(2000);
}
QString CMakeValidator::cmakeExecutable() const
{
return m_executable;
}
bool CMakeValidator::hasCodeBlocksMsvcGenerator() const
{
if (!isValid())
return false;
return m_hasCodeBlocksMsvcGenerator;
}
bool CMakeValidator::hasCodeBlocksNinjaGenerator() const
{
if (!isValid())
return false;
return m_hasCodeBlocksNinjaGenerator;
}
TextEditor::Keywords CMakeValidator::keywords()
{
while (m_state != ValidFunctionDetails && m_state != CMakeValidator::Invalid) {
m_process->waitForFinished();
}
if (m_state == CMakeValidator::Invalid)
return TextEditor::Keywords(QStringList(), QStringList(), QMap<QString, QStringList>());
return TextEditor::Keywords(m_variables, m_functions, m_functionArgs);
}
static void extractKeywords(const QByteArray &input, QStringList *destination)
{
if (!destination)
return;
QString keyword;
int ignoreZone = 0;
for (int i = 0; i < input.count(); ++i) {
const QChar chr = input.at(i);
if (chr == QLatin1Char('{'