Commit 2982a763 authored by hjk's avatar hjk
Browse files

Variables: Use lambdas as callbacks to resolve variables



Instead of broadcasting for each resolution we ask the code
that knows about a specific variable directly.

Change-Id: I2f0f4f2acceba85a236995d236980594a3166bd8
Reviewed-by: default avatarEike Ziller <eike.ziller@digia.com>
Reviewed-by: default avatarDaniel Teske <daniel.teske@digia.com>
parent 000fbe63
......@@ -477,13 +477,25 @@ void EditorManager::init()
d->m_openEditorsFactory = new OpenEditorsViewFactory();
ExtensionSystem::PluginManager::addObject(d->m_openEditorsFactory);
VariableManager::registerFileVariables(kCurrentDocumentPrefix, tr("Current document"));
VariableManager::registerVariable(kCurrentDocumentXPos,
tr("X-coordinate of the current editor's upper left corner, relative to screen."));
VariableManager::registerVariable(kCurrentDocumentYPos,
tr("Y-coordinate of the current editor's upper left corner, relative to screen."));
connect(VariableManager::instance(), SIGNAL(variableUpdateRequested(QByteArray)),
m_instance, SLOT(updateVariable(QByteArray)));
VariableManager::registerFileVariables(kCurrentDocumentPrefix, tr("Current document"),
[]() -> QString {
IDocument *document = currentDocument();
return document ? document->filePath() : QString();
});
VariableManager::registerIntVariable(kCurrentDocumentXPos,
tr("X-coordinate of the current editor's upper left corner, relative to screen."),
[]() -> int {
IEditor *editor = currentEditor();
return editor ? editor->widget()->mapToGlobal(QPoint(0, 0)).x() : 0;
});
VariableManager::registerIntVariable(kCurrentDocumentYPos,
tr("Y-coordinate of the current editor's upper left corner, relative to screen."),
[]() -> int {
IEditor *editor = currentEditor();
return editor ? editor->widget()->mapToGlobal(QPoint(0, 0)).y() : 0;
});
}
void EditorManager::updateAutoSave()
......@@ -2579,30 +2591,3 @@ QString EditorManager::windowTitleVcsTopic()
{
return d->m_titleVcsTopic;
}
void EditorManager::updateVariable(const QByteArray &variable)
{
if (VariableManager::isFileVariable(variable, kCurrentDocumentPrefix)) {
QString value;
IDocument *document = currentDocument();
if (document) {
QString fileName = document->filePath();
if (!fileName.isEmpty())
value = VariableManager::fileVariableValue(variable, kCurrentDocumentPrefix,
fileName);
}
VariableManager::insert(variable, value);
} else if (variable == kCurrentDocumentXPos) {
QString value;
IEditor *curEditor = currentEditor();
if (curEditor)
value = QString::number(curEditor->widget()->mapToGlobal(QPoint(0,0)).x());
VariableManager::insert(variable, value);
} else if (variable == kCurrentDocumentYPos) {
QString value;
IEditor *curEditor = currentEditor();
if (curEditor)
value = QString::number(curEditor->widget()->mapToGlobal(QPoint(0,0)).y());
VariableManager::insert(variable, value);
}
}
......@@ -218,7 +218,6 @@ private slots:
static void vcsOpenCurrentEditor();
static void updateWindowTitle();
void handleDocumentStateChange();
static void updateVariable(const QByteArray &variable);
static void autoSave();
static void saveDocumentFromContextMenu();
......
......@@ -31,6 +31,7 @@
#include <utils/stringutils.h>
#include <QCoreApplication>
#include <QFileInfo>
#include <QMap>
#include <QDebug>
......@@ -56,7 +57,7 @@ public:
class VariableManagerPrivate
{
public:
QHash<QByteArray, QString> m_map;
QHash<QByteArray, VariableManager::StringFunction> m_map;
VMMapExpander m_macroExpander;
QMap<QByteArray, QString> m_descriptions;
};
......@@ -88,43 +89,31 @@ public:
Plugins can register variables together with a description through registerVariable(),
and then need to connect to the variableUpdateRequested() signal to actually give
the variable its value when requested. A typical setup is to
the variable its value when requested. A typical setup is to register
variables in the Plugin::initialize() function.
\list 1
\li Register the variables in ExtensionSystem::IPlugin::initialize():
\code
static const char kMyVariable[] = "MyVariable";
bool MyPlugin::initialize(const QStringList &arguments, QString *errorString)
{
[...]
VariableManager::registerVariable(kMyVariable, tr("The current value of whatever I want."));
connect(VariableManager::instance(), SIGNAL(variableUpdateRequested(QByteArray)),
this, SLOT(updateVariable(QByteArray)));
\code
bool MyPlugin::initialize(const QStringList &arguments, QString *errorString)
{
[...]
}
\endcode
\li Set the variable value when requested:
\code
void MyPlugin::updateVariable(const QByteArray &variable)
{
if (variable == kMyVariable) {
VariableManager::registerVariable(
"MyVariable",
tr("The current value of whatever I want."));
[]() -> QString {
QString value;
// do whatever is necessary to retrieve the value
[...]
VariableManager::insert(variable, value);
return value;
}
}
\endcode
\endlist
);
[...]
}
\endcode
If there are conditions where your variable is not valid, you should call
VariableManager::remove(kMyVariable) in updateVariable().
For variables that refer to a file, you should use the convenience functions
VariableManager::registerFileVariables(), VariableManager::fileVariableValue() and
VariableManager::isFileVariable(). The functions take a variable prefix, like \c MyFileVariable,
For variables that refer to a file, you should use the convenience function
VariableManager::registerFileVariables().
The functions take a variable prefix, like \c MyFileVariable,
and automatically handle standardized postfixes like \c{:FilePath},
\c{:Path} and \c{:FileBaseName}, resulting in the combined variables, such as
\c{MyFileVariable:FilePath}.
......@@ -205,46 +194,15 @@ VariableManager::~VariableManager()
}
/*!
* Used to set the \a value of a \a variable. Most of the time this is only done when
* requested by VariableManager::variableUpdateRequested(). If the value of the variable
* does not change, or changes very seldom, you can also keep the value up to date by calling
* this function whenever the value changes.
*
* As long as insert() was never called for a variable, it will not have a value, not even
* an empty string, meaning that the variable will not be expanded when expanding strings.
*
* \sa remove()
*/
void VariableManager::insert(const QByteArray &variable, const QString &value)
{
d->m_map.insert(variable, value);
}
/*!
* Removes any previous value for the given \a variable. This means that the variable
* will not be expanded at all when expanding strings, not even to an empty string.
*
* Returns true if the variable value could be removed, false if the variable value
* was not set when remove() was called.
*
* \sa insert()
*/
bool VariableManager::remove(const QByteArray &variable)
{
return d->m_map.remove(variable) > 0;
}
/*!
* Returns the value of the given \a variable. This will request an
* update of the variable's value first, by sending the variableUpdateRequested() signal.
* If \a found is given, it is set to true if the variable has a value at all, false if not.
* Returns the value of the given \a variable. If \a found is given, it is
* set to true if the variable has a value at all, false if not.
*/
QString VariableManager::value(const QByteArray &variable, bool *found)
{
variableManagerInstance->variableUpdateRequested(variable);
if (found)
*found = d->m_map.contains(variable);
return d->m_map.value(variable);
StringFunction f = d->m_map.value(variable);
return f ? f() : QString();
}
/*!
......@@ -273,24 +231,29 @@ Utils::AbstractMacroExpander *VariableManager::macroExpander()
}
/*!
* Returns the variable manager instance, for connecting to signals. All other functions are static
* and should be called as class functions, not through the instance.
* Makes the given string-valued \a variable known to the variable manager,
* together with a localized \a description.
*
* \sa registerFileVariables(), registerIntVariable()
*/
QObject *VariableManager::instance()
void VariableManager::registerVariable(const QByteArray &variable,
const QString &description, const StringFunction &value)
{
return variableManagerInstance;
d->m_descriptions.insert(variable, description);
d->m_map.insert(variable, value);
}
/*!
* Makes the given \a variable known to the variable manager, together with a localized
* \a description. It is not strictly necessary to register variables, but highly recommended,
* because this information is used and presented to the user by the VariableChooser.
* Makes the given integral-valued \a variable known to the variable manager,
* together with a localized \a description.
*
* \sa registerFileVariables()
* \sa registerVariable(), registerFileVariables()
*/
void VariableManager::registerVariable(const QByteArray &variable, const QString &description)
void VariableManager::registerIntVariable(const QByteArray &variable,
const QString &description, const VariableManager::IntFunction &value)
{
d->m_descriptions.insert(variable, description);
registerVariable(variable, description,
[=]() -> QString { return QString::number(value ? value() : 0); });
}
/*!
......@@ -300,71 +263,25 @@ void VariableManager::registerVariable(const QByteArray &variable, const QString
* For example \c{registerFileVariables("CurrentDocument", tr("Current Document"))} registers
* variables such as \c{CurrentDocument:FilePath} with description
* "Current Document: Full path including file name."
*
* \sa isFileVariable()
* \sa fileVariableValue()
*/
void VariableManager::registerFileVariables(const QByteArray &prefix, const QString &heading)
void VariableManager::registerFileVariables(const QByteArray &prefix,
const QString &heading, const StringFunction &base)
{
registerVariable(prefix + kFilePathPostfix, tr("%1: Full path including file name.").arg(heading));
registerVariable(prefix + kPathPostfix, tr("%1: Full path excluding file name.").arg(heading));
registerVariable(prefix + kFileNamePostfix, tr("%1: File name without path.").arg(heading));
registerVariable(prefix + kFileBaseNamePostfix, tr("%1: File base name without path and suffix.").arg(heading));
}
registerVariable(prefix + kFilePathPostfix,
QCoreApplication::translate("Core::VariableManager", "%1: Full path including file name.").arg(heading),
[=]() -> QString { return QFileInfo(base()).filePath(); });
/*!
* Returns whether the \a variable is a file kind of variable with the given \a prefix. For example
* \c{MyVariable:FilePath} is a file variable with prefix \c{MyVariable}.
*
* \sa registerFileVariables()
* \sa fileVariableValue()
*/
bool VariableManager::isFileVariable(const QByteArray &variable, const QByteArray &prefix)
{
return variable == prefix + kFilePathPostfix
|| variable == prefix + kPathPostfix
|| variable == prefix + kFileNamePostfix
|| variable == prefix + kFileBaseNamePostfix;
}
registerVariable(prefix + kPathPostfix,
QCoreApplication::translate("Core::VariableManager", "%1: Full path excluding file name.").arg(heading),
[=]() -> QString { return QFileInfo(base()).path(); });
/*!
* Checks if the \a variable is a variable of the file type with the given \a prefix, and returns
* the value of the variable by extracting the wanted information from the given absolute
* \a fileName.
* Returns an empty string if the variable does not have the prefix, or does not have a
* postfix that is used for file variables, or if the file name is empty.
*
* \sa registerFileVariables()
* \sa isFileVariable()
*/
QString VariableManager::fileVariableValue(const QByteArray &variable, const QByteArray &prefix,
const QString &fileName)
{
return fileVariableValue(variable, prefix, QFileInfo(fileName));
}
registerVariable(prefix + kFileNamePostfix,
QCoreApplication::translate("Core::VariableManager", "%1: File name without path.").arg(heading),
[=]() -> QString { return QFileInfo(base()).fileName(); });
/*!
* Checks if the \a variable is a variable of the file type with the given \a prefix, and returns
* the value of the variable by extracting the wanted information from the given
* \a fileInfo.
* Returns an empty string if the variable does not have the prefix, or does not have a
* postfix that is used for file variables, or if the file name is empty.
*
* \sa registerFileVariables()
* \sa isFileVariable()
*/
QString VariableManager::fileVariableValue(const QByteArray &variable, const QByteArray &prefix,
const QFileInfo &fileInfo)
{
if (variable == prefix + kFilePathPostfix)
return fileInfo.filePath();
else if (variable == prefix + kPathPostfix)
return fileInfo.path();
else if (variable == prefix + kFileNamePostfix)
return fileInfo.fileName();
else if (variable == prefix + kFileBaseNamePostfix)
return fileInfo.baseName();
return QString();
registerVariable(prefix + kFileBaseNamePostfix,
QCoreApplication::translate("Core::VariableManager", "%1: File base name without path and suffix.").arg(heading),
[=]() -> QString { return QFileInfo(base()).baseName(); });
}
/*!
......
......@@ -32,10 +32,10 @@
#include "core_global.h"
#include <QObject>
#include <QString>
#include <functional>
QT_FORWARD_DECLARE_CLASS(QFileInfo)
#include <QFileInfo>
#include <QString>
namespace Utils { class AbstractMacroExpander; }
......@@ -43,37 +43,30 @@ namespace Core {
namespace Internal { class MainWindow; }
class CORE_EXPORT VariableManager : public QObject
class CORE_EXPORT VariableManager
{
Q_OBJECT
public:
static QObject *instance();
static void insert(const QByteArray &variable, const QString &value);
static bool remove(const QByteArray &variable);
static QString value(const QByteArray &variable, bool *found = 0);
static QString expandedString(const QString &stringWithVariables);
static Utils::AbstractMacroExpander *macroExpander();
typedef std::function<QString()> StringFunction;
typedef std::function<int()> IntFunction;
static void registerVariable(const QByteArray &variable,
const QString &description);
const QString &description, const StringFunction &value);
static void registerIntVariable(const QByteArray &variable,
const QString &description, const IntFunction &value);
static void registerFileVariables(const QByteArray &prefix,
const QString &heading);
static bool isFileVariable(const QByteArray &variable, const QByteArray &prefix);
static QString fileVariableValue(const QByteArray &variable, const QByteArray &prefix,
const QString &fileName);
static QString fileVariableValue(const QByteArray &variable, const QByteArray &prefix,
const QFileInfo &fileInfo);
const QString &heading, const StringFunction &value);
static QList<QByteArray> variables();
static QString variableDescription(const QByteArray &variable);
signals:
void variableUpdateRequested(const QByteArray &variable);
private:
VariableManager();
~VariableManager();
......
......@@ -175,10 +175,10 @@ public:
connect(&m_locationTimer, SIGNAL(timeout()), SLOT(resetLocation()));
connect(debuggerCore()->action(IntelFlavor), SIGNAL(valueChanged(QVariant)),
SLOT(reloadDisassembly()));
VariableManager::registerFileVariables(PrefixDebugExecutable,
tr("Debugged executable"));
connect(VariableManager::instance(), SIGNAL(variableUpdateRequested(QByteArray)),
SLOT(updateVariable(QByteArray)));
tr("Debugged executable"),
[&]() { return this->m_startParameters.executable; });
}
public slots:
......@@ -190,13 +190,6 @@ public slots:
void doInterruptInferior();
void doFinishDebugger();
void updateVariable(const QByteArray &variable)
{
if (VariableManager::isFileVariable(variable, PrefixDebugExecutable))
VariableManager::insert(variable,
VariableManager::fileVariableValue(variable, PrefixDebugExecutable, m_startParameters.executable));
}
void reloadDisassembly()
{
m_disassemblerAgent.reload();
......
......@@ -164,6 +164,18 @@ using namespace Core;
namespace ProjectExplorer {
static Target *activeTarget()
{
Project *project = ProjectExplorerPlugin::currentProject();
return project ? project->activeTarget() : 0;
}
static BuildConfiguration *activeBuildConfiguration()
{
Target *target = activeTarget();
return target ? target->activeBuildConfiguration() : 0;
}
struct ProjectExplorerPluginPrivate {
ProjectExplorerPluginPrivate();
......@@ -323,6 +335,25 @@ bool ProjectExplorerPlugin::parseArguments(const QStringList &arguments, QString
return true;
}
static QString variableValue(const char *variable)
{
QString projectName;
QString projectFilePath;
Kit *kit = 0;
QString buildConfigurationName;
if (Project *project = ProjectExplorerPlugin::currentProject()) {
projectName = project->displayName();
if (IDocument *doc = project->document())
projectFilePath = doc->filePath();
if (BuildConfiguration *buildConfiguration = activeBuildConfiguration())
buildConfigurationName = buildConfiguration->displayName();
}
ProjectMacroExpander expander(projectFilePath, projectName, kit, buildConfigurationName);
QString result;
expander.resolveProjectMacro(QString::fromUtf8(variable), &result);
return result;
}
bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *error)
{
qRegisterMetaType<ProjectExplorer::RunControl *>();
......@@ -1016,30 +1047,80 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er
updateWelcomePage();
VariableManager::registerFileVariables(Constants::VAR_CURRENTPROJECT_PREFIX, tr("Current project's main file"));
VariableManager::registerFileVariables(Constants::VAR_CURRENTPROJECT_PREFIX,
tr("Current project's main file"),
[]() {
QString projectFilePath;
if (Project *project = ProjectExplorerPlugin::currentProject())
if (IDocument *doc = project->document())
projectFilePath = doc->filePath();
return projectFilePath;
});
VariableManager::registerVariable(Constants::VAR_CURRENTPROJECT_BUILDPATH,
tr("Full build path of the current project's active build configuration."));
VariableManager::registerVariable(Constants::VAR_CURRENTPROJECT_NAME, tr("The current project's name."));
VariableManager::registerVariable(Constants::VAR_CURRENTKIT_NAME, tr("The currently active kit's name."));
tr("Full build path of the current project's active build configuration."),
[]() -> QString {
BuildConfiguration *bc = activeBuildConfiguration();
return bc ? bc->buildDirectory().toUserOutput() : QString();
});
VariableManager::registerVariable(Constants::VAR_CURRENTPROJECT_NAME,
tr("The current project's name."),
[]() -> QString { return variableValue(Constants::VAR_CURRENTPROJECT_NAME); });
VariableManager::registerVariable(Constants::VAR_CURRENTKIT_NAME,
tr("The currently active kit's name."),
[]() -> QString { return variableValue(Constants::VAR_CURRENTKIT_NAME); });
VariableManager::registerVariable(Constants::VAR_CURRENTKIT_FILESYSTEMNAME,
tr("The currently active kit's name in a filesystem friendly version."));
VariableManager::registerVariable(Constants::VAR_CURRENTKIT_ID, tr("The currently active kit's id."));
tr("The currently active kit's name in a filesystem friendly version."),
[]() -> QString { return variableValue(Constants::VAR_CURRENTKIT_FILESYSTEMNAME); });
VariableManager::registerVariable(Constants::VAR_CURRENTKIT_ID,
tr("The currently active kit's id."),
[]() -> QString { return variableValue(Constants::VAR_CURRENTKIT_ID); });
VariableManager::registerVariable(Constants::VAR_CURRENTDEVICE_HOSTADDRESS,
tr("The host address of the device in the currently active kit."));
tr("The host address of the device in the currently active kit."),
[]() -> QString { return variableValue(Constants::VAR_CURRENTDEVICE_HOSTADDRESS); });
VariableManager::registerVariable(Constants::VAR_CURRENTDEVICE_SSHPORT,
tr("The SSH port of the device in the currently active kit."));
tr("The SSH port of the device in the currently active kit."),
[]() -> QString { return variableValue(Constants::VAR_CURRENTDEVICE_SSHPORT); });
VariableManager::registerVariable(Constants::VAR_CURRENTDEVICE_USERNAME,
tr("The user name with which to log into the device in the currently active kit."));
tr("The user name with which to log into the device in the currently active kit."),
[]() -> QString { return variableValue(Constants::VAR_CURRENTDEVICE_USERNAME); });
VariableManager::registerVariable(Constants::VAR_CURRENTDEVICE_PRIVATEKEYFILE,
tr("The private key file with which to authenticate when logging into the device "
"in the currently active kit."));
VariableManager::registerVariable(Constants::VAR_CURRENTBUILD_NAME, tr("The currently active build configuration's name."));
VariableManager::registerVariable(Constants::VAR_CURRENTBUILD_TYPE, tr("The currently active build configuration's type."));
VariableManager::registerFileVariables(Constants::VAR_CURRENTSESSION_PREFIX, tr("File where current session is saved."));
VariableManager::registerVariable(Constants::VAR_CURRENTSESSION_NAME, tr("Name of current session."));
tr("The private key file with which to authenticate when logging into the device "
"in the currently active kit."),
[]() -> QString { return variableValue(Constants::VAR_CURRENTDEVICE_PRIVATEKEYFILE); });
VariableManager::registerVariable(Constants::VAR_CURRENTBUILD_NAME,
tr("The currently active build configuration's name."),
[]() -> QString { return variableValue(Constants::VAR_CURRENTBUILD_NAME); });
VariableManager::registerVariable(Constants::VAR_CURRENTBUILD_TYPE,
tr("The currently active build configuration's type."),
[]() -> QString {
if (BuildConfiguration *bc = activeBuildConfiguration()) {
BuildConfiguration::BuildType type = bc->buildType();
if (type == BuildConfiguration::Debug)
return tr("debug");
if (type == BuildConfiguration::Release)
return tr("release");
}
return tr("unknown");
});
connect(VariableManager::instance(), SIGNAL(variableUpdateRequested(QByteArray)),
this, SLOT(updateVariable(QByteArray)));
VariableManager::registerFileVariables(Constants::VAR_CURRENTSESSION_PREFIX,
tr("File where current session is saved."),
[]() -> QString { return SessionManager::sessionNameToFileName(SessionManager::activeSession()).toString(); });
VariableManager::registerVariable(Constants::VAR_CURRENTSESSION_NAME,
tr("Name of current session."),
[]() -> QString { return SessionManager::activeSession(); });
return true;
}
......@@ -1154,67 +1235,6 @@ void ProjectExplorerPlugin::loadCustomWizards()
}
}
void ProjectExplorerPlugin::updateVariable(const QByteArray &variable)
{
if (variable == Constants::VAR_CURRENTPROJECT_BUILDPATH) {
if (currentProject() && currentProject()->activeTarget() && currentProject()->activeTarget()->activeBuildConfiguration()) {
VariableManager::insert(variable,
currentProject()->activeTarget()->activeBuildConfiguration()->buildDirectory().toUserOutput());
} else {
VariableManager::remove(variable);
}
} else if (variable == Constants::VAR_CURRENTBUILD_TYPE) {
if (currentProject() && currentProject()->activeTarget() && currentProject()->activeTarget()->activeBuildConfiguration()) {
BuildConfiguration::BuildType type = currentProject()->activeTarget()->activeBuildConfiguration()->buildType();
QString typeString;
if (type == BuildConfiguration::Debug)
typeString = tr("debug");
else if (type == BuildConfiguration::Release)
typeString = tr("release");
else
typeString = tr("unknown");
VariableManager::insert(variable, typeString);
} else {
VariableManager::remove(variable);
}
} else if (variable == Constants::VAR_CURRENTSESSION_NAME) {
if (!SessionManager::activeSession().isEmpty())
VariableManager::insert(variable, SessionManager::activeSession());
else
VariableManager::remove(variable);