Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@ set(codepointer_sources
src/widgets/bannerwidget.h
src/widgets/HistoryLineEdit.cpp
src/widgets/HistoryLineEdit.h
src/widgets/BoldItemDelegate.cpp
src/widgets/BoldItemDelegate.hpp
src/widgets/gotolineform.ui
src/widgets/bannermessage.ui
src/widgets/replaceform.ui
Expand Down
3 changes: 3 additions & 0 deletions src/GlobalCommands.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ inline constexpr const char *OpenFile = "OpenFile";
// Broadcast - a new file has been loaded. The payload will contain the filename
inline constexpr const char *LoadedFile = "LoadedFile";

// Broadcast - a file has been saved. The payload will contain the filename
inline constexpr const char *SavedFile = "SavedFile";

// Broadcast - a file has been closed
inline constexpr const char *ClosedFile = "ClosedFile";

Expand Down
227 changes: 151 additions & 76 deletions src/plugins/ProjectManager/ProjectManagerPlg.cpp
Original file line number Diff line number Diff line change
@@ -1,18 +1,23 @@
#include "qmdipluginconfig.h"
#include <QByteArray>
#include <QClipboard>
#include <QDesktopServices>
#include <QDockWidget>
#include <QFile>
#include <QFileDialog>
#include <QFileInfo>
#include <QJsonArray>
#include <QJsonDocument>
#include <QLineEdit>
#include <QMainWindow>
#include <QMenu>
#include <QMessageBox>
#include <QMimeDatabase>
#include <QMimeType>
#include <QScrollBar>
#include <QSettings>
#include <QSocketNotifier>
#include <QStandardPaths>
#include <QStringList>
#include <QStringListModel>
#include <QTimer>
#include <QWidgetAction>
Expand All @@ -33,6 +38,7 @@
#include <CommandPaletteWidget/CommandPalette>
#include <qmdiclient.h>
#include <qmdihost.h>
#include <qmdipluginconfig.h>
#include <qmdiserver.h>
#include <qmditabwidget.h>

Expand Down Expand Up @@ -148,6 +154,13 @@ auto static setupPty(QProcess &process, int &masterFd) -> bool {
#endif
}

auto static isClangFormatSupported(const QString &fileName) -> bool {
auto static supported =
QSet<QString>{"c", "cc", "cpp", "cxx", "h", "hh", "hpp", "hxx", "m", "mm", "cu", "cuh"};
auto ext = QFileInfo(fileName).suffix().toLower();
return supported.contains(ext);
}

void ProjectBuildModel::addConfig(std::shared_ptr<ProjectBuildConfig> config) {
int row = configs.size();
beginInsertRows(QModelIndex(), row, row);
Expand Down Expand Up @@ -233,14 +246,22 @@ QStringList ProjectBuildModel::getAllOpenDirs() const {
}

ProjectManagerPlugin::ProjectManagerPlugin() {
name = tr("Project manager");
name = "ProjectManager";
author = tr("Diego Iastrubni <diegoiast@gmail.com>");
iVersion = 0;
sVersion = "0.0.1";
autoEnabled = true;
alwaysEnabled = true;
auto monospacedFont = QFontDatabase::systemFont(QFontDatabase::FixedFont);

auto values = QStringList() << tr("Never") << tr("Always") << tr("Loaded projects");

#if defined(Q_OS_LINUX)
clangFormatExe = "/usr/bin/clang-format";
#else
clangFormatExe = R"(C:\Program Files\llvm\bin\ctags.exe)";
#endif

config.pluginName = tr("Project manager");
config.description = tr("Add support for building using CMake/Cargo/Go");
config.configItems.push_back(
Expand All @@ -266,6 +287,22 @@ ProjectManagerPlugin::ProjectManagerPlugin() {
.setDefaultValue(monospacedFont)
.setValue(monospacedFont)
.build());
config.configItems.push_back(qmdiConfigItem::Builder()
.setDisplayName(tr("clang-format exe"))
.setDescription(tr("Where do you have LLVM tools installed"))
.setKey(Config::ClangFormatExeKey)
.setType(qmdiConfigItem::Path)
.setDefaultValue(clangFormatExe)
.setPossibleValue(true) // Must be an existing file
.build());
config.configItems.push_back(qmdiConfigItem::Builder()
.setDisplayName(tr("Clang-format behaviour"))
.setDescription(tr("When to run clang-format"))
.setKey(Config::ClangFormatBehaviourKey)
.setType(qmdiConfigItem::OneOf)
.setPossibleValue(values)
.setDefaultValue(ClangFormatOnSave::InProjects)
.build());

/*
config.configItems.push_back(qmdiConfigItem::Builder()
Expand Down Expand Up @@ -807,19 +844,15 @@ int ProjectManagerPlugin::canHandleCommand(const QString &command, const Command
if (command == GlobalCommands::LoadedFile) {
return true;
}
if (command == GlobalCommands::SavedFile) {
return true;
}
if (command == GlobalCommands::ClosedFile) {
return true;
}
return false;
}

#include <QByteArray>
#include <QFile>
#include <QFileInfo>
#include <QMimeDatabase>
#include <QMimeType>
#include <QStringList>

#ifdef Q_OS_WIN
#include <windows.h>
#endif
Expand Down Expand Up @@ -861,73 +894,15 @@ auto verifyRunnable(const QString &fileName) -> bool {
CommandArgs ProjectManagerPlugin::handleCommand(const QString &command, const CommandArgs &args) {
if (command == GlobalCommands::LoadedFile) {
auto client = args.value(GlobalArguments::Client).value<qmdiClient *>();
if (!client) {
return {};
}

auto filename = args[GlobalArguments::FileName];
auto action = client->contextMenu.findActionNamed("runScript");
auto isScript = verifyRunnable(client->mdiClientFileName());
if (isScript) {
if (!action) {
auto shortcut = QKeySequence(Qt::ControlModifier | Qt::ShiftModifier | Qt::Key_R);
action = new QAction(tr("Run script"));
action->setObjectName("runScript");
action->setShortcut(shortcut);
action->setData(QVariant::fromValue(client));
client->contextMenu.addAction(action);
client->menus[tr("&Project")]->addAction(action);

connect(action, &QAction::triggered, action, [action, this]() {
auto client = action->data().value<qmdiClient *>();
if (!client->canCloseClient(CloseReason::CloseTab)) {
return;
}
auto fi = QFileInfo(client->mdiClientFileName());
auto workingDir = fi.dir().absolutePath();
auto scriptName = fi.absoluteFilePath();
auto arguments = QStringList();
auto env = QProcessEnvironment();
auto capture =
outputPanel ? outputPanel->captureTasksOutput->isChecked() : true;
this->runCommand(workingDir, scriptName, arguments, env, capture);
});
if (client == mdiServer->getCurrentClient()) {
this->mdiServer->mdiHost->unmergeClient(client);
this->mdiServer->mdiHost->mergeClient(client);
this->mdiServer->mdiHost->updateGUI();
}
}
} else {
client->contextMenu.removeAction(action);
client->menus[tr("&Project")]->removeAction(action);
if (client == mdiServer->getCurrentClient()) {
this->mdiServer->mdiHost->unmergeClient(client);
this->mdiServer->mdiHost->mergeClient(client);
this->mdiServer->mdiHost->updateGUI();
}
}

auto widget = dynamic_cast<QObject *>(client);
auto actionCopyFilePath = new QAction(tr("Copy path relative to project"), widget);
connect(actionCopyFilePath, &QAction::triggered, actionCopyFilePath, [client, this]() {
auto c = QApplication::clipboard();
auto fileName = client->mdiClientFileName();
auto project = projectModel->findProjectForFile(fileName);
if (!project || !fileName.startsWith(project->sourceDir)) {
c->setText(fileName);
return;
}
auto fixed = fileName;
auto l = project->sourceDir.length();
if (!project->sourceDir.endsWith('\\') && !project->sourceDir.endsWith('/')) {
l++;
}
fixed.remove(0, l);
c->setText(fixed);
});
client->contextMenu.addAction(actionCopyFilePath);

auto filename = args[GlobalArguments::FileName].toString();
fixClientsMenu(client, filename);
return {};
}
if (command == GlobalCommands::SavedFile) {
auto client = args.value(GlobalArguments::Client).value<qmdiClient *>();
auto filename = args[GlobalArguments::FileName].toString();
fixClientsMenu(client, filename);
saveFileExternalActions(client);
return {};
}
if (command == GlobalCommands::ClosedFile) {
Expand Down Expand Up @@ -1666,3 +1641,103 @@ auto ProjectManagerPlugin::tryScrollOutput(int line) -> bool {
vScrollBar->setValue(centerScrollValue);
return true;
}

auto ProjectManagerPlugin::fixClientsMenu(qmdiClient *client, const QString &fileName) -> void {
if (!client) {
return;
}

// TODO - do we even need this argument?
if (fileName.isEmpty()) {
return;
}

auto action = client->contextMenu.findActionNamed("runScript");
auto isScript = verifyRunnable(client->mdiClientFileName());
if (isScript) {
if (!action) {
auto shortcut = QKeySequence(Qt::ControlModifier | Qt::ShiftModifier | Qt::Key_R);
action = new QAction(tr("Run script"));
action->setObjectName("runScript");
action->setShortcut(shortcut);
action->setData(QVariant::fromValue(client));
client->contextMenu.addAction(action);
client->menus[tr("&Project")]->addAction(action);

connect(action, &QAction::triggered, action, [action, this]() {
auto client = action->data().value<qmdiClient *>();
if (!client->canCloseClient(CloseReason::CloseTab)) {
return;
}
auto fi = QFileInfo(client->mdiClientFileName());
auto workingDir = fi.dir().absolutePath();
auto scriptName = fi.absoluteFilePath();
auto arguments = QStringList();
auto env = QProcessEnvironment();
auto capture = outputPanel ? outputPanel->captureTasksOutput->isChecked() : true;
this->runCommand(workingDir, scriptName, arguments, env, capture);
});
this->mdiServer->mdiHost->unmergeClient(client);
this->mdiServer->mdiHost->mergeClient(client);
this->mdiServer->mdiHost->updateGUI();
}
} else {
client->contextMenu.removeAction(action);
client->menus[tr("&Project")]->removeAction(action);
this->mdiServer->mdiHost->unmergeClient(client);
this->mdiServer->mdiHost->mergeClient(client);
this->mdiServer->mdiHost->updateGUI();
}

auto widget = dynamic_cast<QObject *>(client);
auto actionCopyFilePath = new QAction(tr("Copy path relative to project"), widget);
connect(actionCopyFilePath, &QAction::triggered, actionCopyFilePath, [client, this]() {
auto c = QApplication::clipboard();
auto fileName = client->mdiClientFileName();
auto project = projectModel->findProjectForFile(fileName);
if (!project || !fileName.startsWith(project->sourceDir)) {
c->setText(fileName);
return;
}
auto fixed = fileName;
auto l = project->sourceDir.length();
if (!project->sourceDir.endsWith('\\') && !project->sourceDir.endsWith('/')) {
l++;
}
fixed.remove(0, l);
c->setText(fixed);
});
client->contextMenu.addAction(actionCopyFilePath);
}

auto ProjectManagerPlugin::saveFileExternalActions(qmdiClient *client) -> void {
// TODO how can we notify of errors?
if (getConfig().getClangFormatBehaviour() == ClangFormatOnSave::Never) {
return;
}

auto fileName = client->mdiClientFileName();
if (!isClangFormatSupported(fileName)) {
qDebug() << "saveFileExternalActions: not supported by clang-format" << fileName;
return;
}
if (getConfig().getClangFormatBehaviour() == ClangFormatOnSave::InProjects) {
auto project = projectModel->findProjectForFile(fileName);
if (!project || !fileName.startsWith(project->sourceDir)) {
qDebug() << "saveFileExternalActions: will not autoformat file" << fileName;
return;
}
}

auto clangFormat = getConfig().getClangFormatExe();
auto args = QStringList{"-style=file", "-fallback-style=none", "-i", fileName};
auto p = QProcess();
p.start(clangFormat, args);
p.waitForFinished();
auto exitCode = p.exitCode();
if (p.exitStatus() != QProcess::NormalExit || exitCode != 0) {
auto output = QString::fromUtf8(p.readAllStandardOutput()).trimmed();
qDebug() << "Running clang-format failed, exit=" << exitCode << output;
qDebug() << QString("clang ") + args.join(" ");
}
}
7 changes: 7 additions & 0 deletions src/plugins/ProjectManager/ProjectManagerPlg.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,16 @@ class ProjectBuildModel : public QAbstractListModel {
QStringList getAllOpenDirs() const;
};

enum class ClangFormatOnSave { Never, Always, InProjects };

class ProjectManagerPlugin : public IPlugin {

struct Config {
CONFIG_DEFINE(SaveBeforeTask, bool);
CONFIG_DEFINE(BlackConsole, bool);
CONFIG_DEFINE(ConsoleFont, QString)
CONFIG_DEFINE(ClangFormatExe, QString);
CONFIG_DEFINE(ClangFormatBehaviour, ClangFormatOnSave)
CONFIG_DEFINE(ExtraPath, QStringList);
CONFIG_DEFINE(OpenDirs, QStringList);
CONFIG_DEFINE(SelectedDirectory, QString);
Expand Down Expand Up @@ -113,6 +117,8 @@ class ProjectManagerPlugin : public IPlugin {
auto updateExecutablesUI(std::shared_ptr<ProjectBuildConfig> buildConfig) -> void;
auto tryOpenProject(const QString &filename, const QString &dir) -> bool;
auto tryScrollOutput(int line) -> bool;
auto fixClientsMenu(qmdiClient *client, const QString &filename) -> void;
auto saveFileExternalActions(qmdiClient *client) -> void;

int panelIndex = -1;
Ui::ProjectManagerGUI *gui = nullptr;
Expand All @@ -132,6 +138,7 @@ class ProjectManagerPlugin : public IPlugin {
ProjectBuildModel *projectModel = nullptr;
CommandPalette *commandPalette = nullptr;
ProjectSearch *searchPanelUI = nullptr;
QString clangFormatExe;

QAction *runAction = nullptr;
QAction *buildAction = nullptr;
Expand Down
1 change: 1 addition & 0 deletions src/plugins/git/GitPlugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "ui_GitCommands.h"
#include "ui_GitCommit.h"
#include "widgets/AutoShrinkLabel.hpp"
#include "widgets/BoldItemDelegate.hpp"
#include "widgets/qmdieditor.h"

QString shortGitSha1(const QString &fullSha1, int length = 7) {
Expand Down
Loading