Commit 5ef70ab5 authored by Waqar Ahmed's avatar Waqar Ahmed
Browse files

Build: Allow run commands

Build plugin is great but it misses one functionality i.e., ability to
run a target. With this change you will be able to do so.
parent df435032
......@@ -88,6 +88,9 @@ QSize TargetHtmlDelegate::sizeHint(const QStyleOptionViewItem & /* option */, co
if (index.column() == 0 && index.internalId() != TargetModel::InvalidIndex) {
return doc.size().toSize() + QSize(30, 0); // add margin for the check-box;
}
if (index.column() == 1 && !index.parent().isValid()) {
return doc.size().toSize() + QSize(38, 0); // add space for "Dir"
}
return doc.size().toSize();
}
......
......@@ -45,7 +45,7 @@ void TargetModel::setDefaultCmd(int rootRow, const QString &defCmd)
}
for (int i = 0; i < m_targets[rootRow].commands.size(); i++) {
if (defCmd == m_targets[rootRow].commands[i].first) {
if (defCmd == m_targets[rootRow].commands[i].name) {
m_targets[rootRow].defaultCmd = defCmd;
return;
}
......@@ -70,7 +70,7 @@ QModelIndex TargetModel::addTargetSet(const QString &setName, const QString &wor
return index(m_targets.count() - 1, 0);
}
QModelIndex TargetModel::addCommand(const QModelIndex &parentIndex, const QString &cmdName, const QString &command)
QModelIndex TargetModel::addCommand(const QModelIndex &parentIndex, const QString &cmdName, const QString &buildCmd, const QString &runCmd)
{
int rootRow = parentIndex.row();
if (rootRow < 0 || rootRow >= m_targets.size()) {
......@@ -81,7 +81,7 @@ QModelIndex TargetModel::addCommand(const QModelIndex &parentIndex, const QStrin
// make the name unique
QString newName = cmdName;
for (int i = 0; i < m_targets[rootRow].commands.count(); i++) {
if (m_targets[rootRow].commands[i].first == newName) {
if (m_targets[rootRow].commands[i].name == newName) {
newName += QStringLiteral(" 2");
i = -1;
}
......@@ -89,7 +89,7 @@ QModelIndex TargetModel::addCommand(const QModelIndex &parentIndex, const QStrin
QModelIndex rootIndex = createIndex(rootRow, 0, InvalidIndex);
beginInsertRows(rootIndex, m_targets[rootRow].commands.count(), m_targets[rootRow].commands.count());
m_targets[rootRow].commands << QPair<QString, QString>(newName, command);
m_targets[rootRow].commands << Command{newName, buildCmd, runCmd};
endInsertRows();
return createIndex(m_targets[rootRow].commands.size() - 1, 0, rootRow);
}
......@@ -137,14 +137,15 @@ QModelIndex TargetModel::copyTargetOrSet(const QModelIndex &index)
QModelIndex rootIndex = createIndex(rootRow, 0, InvalidIndex);
beginInsertRows(rootIndex, m_targets[rootRow].commands.count(), m_targets[rootRow].commands.count());
QString newName = m_targets[rootRow].commands[index.row()].first + QStringLiteral(" 2");
const auto cmd = m_targets[rootRow].commands[index.row()];
QString newName = cmd.name + QStringLiteral(" 2");
for (int i = 0; i < m_targets[rootRow].commands.count(); i++) {
if (m_targets[rootRow].commands[i].first == newName) {
if (m_targets[rootRow].commands[i].name == newName) {
newName += QStringLiteral(" 2");
i = -1;
}
}
m_targets[rootRow].commands << QPair<QString, QString>(newName, m_targets[rootRow].commands[index.row()].second);
m_targets[rootRow].commands << Command{newName, cmd.buildCmd, cmd.runCmd};
endInsertRows();
return createIndex(m_targets[rootRow].commands.count() - 1, 0, rootRow);
......@@ -299,13 +300,10 @@ const QString TargetModel::targetName(const QModelIndex &itemIndex)
QVariant TargetModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid()) {
if (!index.isValid() || !hasIndex(index.row(), index.column(), index.parent())) {
return QVariant();
}
if (index.column() < 0 || index.column() > 1) {
return QVariant();
}
// Tooltip
if (role == Qt::ToolTipRole) {
if (index.column() == 0 && index.parent().isValid()) {
......@@ -323,6 +321,7 @@ QVariant TargetModel::data(const QModelIndex &index, int role) const
if (row < 0 || row >= m_targets.size() || role == Qt::CheckStateRole) {
return QVariant();
}
switch (index.column()) {
case 0:
return m_targets[row].name;
......@@ -342,13 +341,15 @@ QVariant TargetModel::data(const QModelIndex &index, int role) const
if (index.column() != 0) {
return QVariant();
}
return m_targets[rootIndex].commands[row].first == m_targets[rootIndex].defaultCmd ? Qt::Checked : Qt::Unchecked;
return m_targets[rootIndex].commands[row].buildCmd == m_targets[rootIndex].defaultCmd ? Qt::Checked : Qt::Unchecked;
} else {
switch (index.column()) {
case 0:
return m_targets[rootIndex].commands[row].first;
return m_targets[rootIndex].commands[row].name;
case 1:
return m_targets[rootIndex].commands[row].second;
return m_targets[rootIndex].commands[row].buildCmd;
case 2:
return m_targets[rootIndex].commands[row].runCmd;
}
}
}
......@@ -372,6 +373,9 @@ QVariant TargetModel::headerData(int section, Qt::Orientation orientation, int r
if (section == 1) {
return i18n("Working Directory / Command");
}
if (section == 2) {
return i18n("Run Command");
}
return QVariant();
}
......@@ -381,14 +385,11 @@ bool TargetModel::setData(const QModelIndex &index, const QVariant &value, int r
if (role != Qt::EditRole && role != Qt::CheckStateRole) {
return false;
}
if (!index.isValid()) {
return false;
}
if (index.column() < 0 || index.column() > 1) {
if (!index.isValid() || !hasIndex(index.row(), index.column(), index.parent())) {
return false;
}
int row = index.row();
int row = index.row();
if (index.internalId() == InvalidIndex) {
if (row < 0 || row >= m_targets.size()) {
return false;
......@@ -412,16 +413,16 @@ bool TargetModel::setData(const QModelIndex &index, const QVariant &value, int r
if (role == Qt::CheckStateRole) {
if (index.column() == 0) {
m_targets[rootIndex].defaultCmd = m_targets[rootIndex].commands[row].first;
m_targets[rootIndex].defaultCmd = m_targets[rootIndex].commands[row].buildCmd;
Q_EMIT dataChanged(createIndex(0, 0, rootIndex), createIndex(m_targets[rootIndex].commands.size() - 1, 0, rootIndex));
}
} else {
switch (index.column()) {
case 0:
m_targets[rootIndex].commands[row].first = value.toString();
m_targets[rootIndex].commands[row].name = value.toString();
return true;
case 1:
m_targets[rootIndex].commands[row].second = value.toString();
m_targets[rootIndex].commands[row].buildCmd = value.toString();
return true;
}
}
......@@ -462,7 +463,7 @@ int TargetModel::rowCount(const QModelIndex &parent) const
int TargetModel::columnCount(const QModelIndex &) const
{
return 2;
return 3;
}
QModelIndex TargetModel::index(int row, int column, const QModelIndex &parent) const
......
......@@ -15,12 +15,17 @@ class TargetModel : public QAbstractItemModel
{
Q_OBJECT
public:
struct Command {
QString name;
QString buildCmd;
QString runCmd;
};
struct TargetSet {
TargetSet(const QString &_name, const QString &_workDir);
QString name;
QString workDir;
QString defaultCmd;
QList<QPair<QString, QString>> commands;
QList<Command> commands;
};
TargetModel(QObject *parent = nullptr);
......@@ -39,7 +44,7 @@ public Q_SLOTS:
QModelIndex addTargetSet(const QString &setName, const QString &workDir);
/** This function adds a new command to a target-set and returns the model index */
QModelIndex addCommand(const QModelIndex &parentIndex, const QString &cmdName, const QString &command);
QModelIndex addCommand(const QModelIndex &parentIndex, const QString &cmdName, const QString &buildCmd, const QString &runCmd = {});
/** This function copies the target(-set) the model index points to and returns
* the model index of the copy. */
......
......@@ -141,6 +141,10 @@ KateBuildView::KateBuildView(KTextEditor::Plugin *plugin, KTextEditor::MainWindo
a->setText(i18n("Build Default Target"));
connect(a, &QAction::triggered, this, &KateBuildView::slotBuildDefaultTarget);
a = actionCollection()->addAction(QStringLiteral("build_and_run_default_target"));
a->setText(i18n("Build & Run Default Target"));
connect(a, &QAction::triggered, this, &KateBuildView::slotBuildAndRunDefaultTarget);
a = actionCollection()->addAction(QStringLiteral("build_previous_target"));
a->setText(i18n("Build Previous Target"));
connect(a, &QAction::triggered, this, &KateBuildView::slotBuildPreviousTarget);
......@@ -220,7 +224,12 @@ KateBuildView::KateBuildView(KTextEditor::Plugin *plugin, KTextEditor::MainWindo
connect(m_targetsUi->addButton, &QToolButton::clicked, this, &KateBuildView::slotAddTargetClicked);
connect(m_targetsUi->buildButton, &QToolButton::clicked, this, &KateBuildView::slotBuildActiveTarget);
connect(m_targetsUi->runButton, &QToolButton::clicked, this, [this] {
m_runAfterBuild = true;
slotBuildActiveTarget();
});
connect(m_targetsUi, &TargetsUi::enterPressed, this, &KateBuildView::slotBuildActiveTarget);
connect(m_targetsUi->targetsView->selectionModel(), &QItemSelectionModel::currentChanged, this, &KateBuildView::onSelectionChanged);
m_proc.setOutputChannelMode(KProcess::SeparateChannels);
connect(&m_proc, static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished), this, &KateBuildView::slotProcExited);
......@@ -304,10 +313,6 @@ void KateBuildView::readSessionConfig(const KConfigGroup &cg)
tmpCmd = cg.readEntry(QStringLiteral("Active Target Command"), 0);
}
m_targetsUi->targetsView->expandAll();
m_targetsUi->targetsView->resizeColumnToContents(0);
m_targetsUi->targetsView->collapseAll();
QModelIndex root = m_targetsUi->targetsModel.index(tmpIndex);
QModelIndex cmdIndex = m_targetsUi->targetsModel.index(tmpCmd, 0, root);
cmdIndex = m_targetsUi->proxyModel.mapFromSource(cmdIndex);
......@@ -318,6 +323,10 @@ void KateBuildView::readSessionConfig(const KConfigGroup &cg)
// Add project targets, if any
slotAddProjectTarget();
m_targetsUi->targetsView->expandAll();
m_targetsUi->targetsView->resizeColumnToContents(0);
m_targetsUi->targetsView->resizeColumnToContents(1);
}
/******************************************************************/
......@@ -336,8 +345,8 @@ void KateBuildView::writeSessionConfig(KConfigGroup &cg)
QStringList cmdNames;
for (int j = 0; j < targets[i].commands.count(); j++) {
const QString &cmdName = targets[i].commands[j].first;
const QString &buildCmd = targets[i].commands[j].second;
const QString &cmdName = targets[i].commands[j].name;
const QString &buildCmd = targets[i].commands[j].buildCmd;
cmdNames << cmdName;
cg.writeEntry(QStringLiteral("%1 BuildCmd %2").arg(i).arg(cmdName), buildCmd);
}
......@@ -850,6 +859,16 @@ void KateBuildView::slotBuildActiveTarget()
}
}
void KateBuildView::slotBuildAndRunDefaultTarget()
{
if (!m_targetsUi->targetsView->currentIndex().isValid()) {
slotSelectTarget();
} else {
m_runAfterBuild = true;
slotBuildDefaultTarget();
}
}
/******************************************************************/
void KateBuildView::slotBuildPreviousTarget()
{
......@@ -1023,6 +1042,33 @@ void KateBuildView::slotProcExited(int exitCode, QProcess::ExitStatus)
// add marks
slotViewChanged();
}
slotRunAfterBuild();
}
void KateBuildView::slotRunAfterBuild()
{
if (!m_runAfterBuild) {
return;
}
m_runAfterBuild = false;
if (!m_previousIndex.isValid()) {
return;
}
auto *projectPluginView = m_win->pluginView(QStringLiteral("kateprojectplugin"));
QModelIndex idx = m_previousIndex;
idx = idx.siblingAtColumn(2);
const QString runCmd = idx.data().toString();
if (runCmd.isEmpty()) {
return;
}
const QString workDir = TargetModel::workDir(idx);
if (workDir.isEmpty()) {
return;
}
if (projectPluginView) {
QMetaObject::invokeMethod(projectPluginView, "runCmdInTerminal", Q_ARG(QString, workDir), Q_ARG(QString, runCmd));
}
}
static void appendPlainTextTo(QPlainTextEdit *edit, const QString &text)
......@@ -1340,11 +1386,12 @@ void KateBuildView::slotAddProjectTarget()
QVariantMap targetMap = targetVariant.toMap();
QString tgtName = targetMap[QStringLiteral("name")].toString();
QString buildCmd = targetMap[QStringLiteral("build_cmd")].toString();
QString runCmd = targetMap[QStringLiteral("run_cmd")].toString();
if (tgtName.isEmpty() || buildCmd.isEmpty()) {
continue;
}
m_targetsUi->targetsModel.addCommand(set, tgtName, buildCmd);
m_targetsUi->targetsModel.addCommand(set, tgtName, buildCmd, runCmd);
}
if (!set.model()->index(0, 0, set).data().isValid()) {
......@@ -1362,6 +1409,24 @@ void KateBuildView::slotAddProjectTarget()
m_targetsUi->targetsModel.addCommand(set, i18n("quick"), quickCmd);
}
}
const auto index = m_targetsUi->proxyModel.mapFromSource(set);
if (index.isValid()) {
m_targetsUi->targetsView->expand(index);
}
}
void KateBuildView::onSelectionChanged(const QModelIndex &current, const QModelIndex &)
{
if (!current.isValid() || !current.parent().isValid()) {
m_targetsUi->buildButton->setEnabled(false);
m_targetsUi->runButton->setEnabled(false);
return;
}
const bool hasBuildCmd = !current.siblingAtColumn(1).data().toString().isEmpty();
const bool hasRunCmd = !current.siblingAtColumn(2).data().toString().isEmpty();
m_targetsUi->buildButton->setEnabled(hasBuildCmd);
// Run button can be enabled even if there is no build command
m_targetsUi->runButton->setEnabled(hasRunCmd);
}
/******************************************************************/
......
......@@ -72,6 +72,7 @@ private Q_SLOTS:
// Building
void slotSelectTarget();
void slotBuildAndRunDefaultTarget();
void slotBuildActiveTarget();
void slotBuildPreviousTarget();
void slotBuildDefaultTarget();
......@@ -81,6 +82,7 @@ private Q_SLOTS:
void slotProcExited(int exitCode, QProcess::ExitStatus exitStatus);
void slotReadReadyStdErr();
void slotReadReadyStdOut();
void slotRunAfterBuild();
// Selecting warnings/errors
void slotNext();
......@@ -110,6 +112,8 @@ private Q_SLOTS:
void slotProjectMapChanged();
void slotAddProjectTarget();
void onSelectionChanged(const QModelIndex &, const QModelIndex &current);
protected:
bool eventFilter(QObject *obj, QEvent *ev) override;
......@@ -140,6 +144,7 @@ private:
QString m_stdErr;
QString m_currentlyBuildingTarget;
bool m_buildCancelled;
bool m_runAfterBuild = false;
int m_displayModeBeforeBuild;
QString m_make_dir;
QStack<QString> m_make_dir_stack;
......
......@@ -49,6 +49,10 @@ TargetsUi::TargetsUi(QObject *view, QWidget *parent)
buildButton->setIcon(QIcon::fromTheme(QStringLiteral("run-build")));
buildButton->setToolTip(i18n("Build selected target"));
runButton = new QToolButton(this);
runButton->setIcon(QIcon::fromTheme(QStringLiteral("media-playback-start")));
runButton->setToolTip(i18n("Build & run selected target"));
targetsView = new QTreeView(this);
targetsView->setAlternatingRowColors(true);
......@@ -66,7 +70,8 @@ TargetsUi::TargetsUi(QObject *view, QWidget *parent)
tLayout->addWidget(targetCombo);
tLayout->addWidget(targetFilterEdit);
tLayout->addWidget(buildButton);
tLayout->addSpacing(20);
tLayout->addWidget(runButton);
tLayout->addSpacing(15);
tLayout->addWidget(addButton);
tLayout->addWidget(newTarget);
tLayout->addWidget(copyTarget);
......
......@@ -39,6 +39,7 @@ public:
QToolButton *addButton;
QToolButton *buildButton;
QToolButton *runButton;
public Q_SLOTS:
void targetSetSelected(int index);
......
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE gui SYSTEM "kpartgui.dtd">
<gui name="katebuild-plugin" library="katebuildplugin" version="11" translationDomain="katebuild-plugin">
<gui name="katebuild-plugin" library="katebuildplugin" version="12" translationDomain="katebuild-plugin">
<MenuBar>
<Menu name="Build Menubar">
<text>&amp;Build</text>
<Action name="select_target"/>
<Action name="build_default_target" />
<Action name="build_previous_target"/>
<Action name="build_and_run_default_target" />
<Action name="stop"/>
<Separator/>
<Action name="goto_prev"/>
......
......@@ -86,3 +86,10 @@ void KateProjectInfoView::resetTerminal(const QString &directory)
m_terminal->respawn(directory);
}
}
void KateProjectInfoView::runCmdInTerminal(const QString &workingDir, const QString &cmd)
{
if (auto terminal = qobject_cast<KateProjectInfoViewTerminal *>(currentWidget())) {
terminal->runCommand(workingDir, cmd);
}
}
......@@ -55,6 +55,8 @@ public:
void resetTerminal(const QString &directory);
void runCmdInTerminal(const QString &workingDir, const QString &cmd);
private:
/**
* our plugin view
......
......@@ -12,6 +12,7 @@
#include <KLocalizedString>
#include <KPluginFactory>
#include <KSharedConfig>
#include <KShell>
#include <kde_terminal_interface.h>
#include <ktexteditor_utils.h>
......@@ -188,3 +189,15 @@ bool KateProjectInfoViewTerminal::eventFilter(QObject *w, QEvent *e)
return QWidget::eventFilter(w, e);
}
void KateProjectInfoViewTerminal::runCommand(const QString &workingDir, const QString &cmd)
{
auto terminal = qobject_cast<TerminalInterface *>(m_konsolePart);
if (!terminal) {
loadTerminal();
}
terminal->sendInput(QStringLiteral("\x05\x15"));
const QString changeDirCmd = QStringLiteral("cd ") + KShell::quoteArg(workingDir) + QStringLiteral("\n");
terminal->sendInput(changeDirCmd);
terminal->sendInput(cmd.trimmed() + QStringLiteral("\n"));
}
......@@ -55,6 +55,8 @@ public:
bool eventFilter(QObject *o, QEvent *e) override;
void runCommand(const QString &workingDir, const QString &cmd);
private Q_SLOTS:
/**
* Construct a new terminal for this view
......
......@@ -575,6 +575,16 @@ void KateProjectPluginView::switchToProject(const QDir &dir)
}
}
void KateProjectPluginView::runCmdInTerminal(const QString &workingDir, const QString &cmd)
{
m_mainWindow->showToolView(m_toolInfoView);
auto widget = static_cast<KateProjectInfoView *>(m_stackedProjectInfoViews->currentWidget());
if (!widget) {
return;
}
widget->runCmdInTerminal(workingDir, cmd);
}
void KateProjectPluginView::slotViewCreated(KTextEditor::View *view)
{
/**
......
......@@ -132,6 +132,8 @@ public Q_SLOTS:
*/
void switchToProject(const QDir &dir);
void runCmdInTerminal(const QString &workingDir, const QString &cmd);
private Q_SLOTS:
/**
* Plugin config updated
......
......@@ -2087,6 +2087,7 @@ You can also mix version control and files based on filters.
{
"name": "all",
"build_cmd": "ninja"
"run_cmd": "./bin/kate"
},
{
"name": "kate",
......@@ -2100,6 +2101,9 @@ You can also mix version control and files based on filters.
<para>
The targets specified above will then appear in the <link linkend="kate-application-plugin-build">Build Plugin </link> under <emphasis role="bold">
"Project Plugin Targets"</emphasis>.
If the <code>"targets"</code> array is specified then <code>"build"</code>, <code>"clean"</code> and <code>"install"</code> are ignored. Each element in the array specifies a target. <code>"name"</code> is the name of the target, <code>"build_cmd"</code> will be used to build the target, <code>"run_cmd"</code> is will be used to run the target. Most important of all is <code> "directory"</code>, this is where the commands will be executed.
</para>
<para>
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment