Skip to content

Commit

Permalink
feat: create a Script List panel, remove suggestions
Browse files Browse the repository at this point in the history
This was giving us some performance issues for little gain.
Rather have a panel with the list of scripts available and a filter.

Task-Id: KNUT-138
Change-Id: I3b432ff9579f09c64d07a4229595457795238596
Reviewed-on: https://codereview.kdab.com/c/knut/+/140346
Reviewed-by: Nicolas Arnaud-Cormos <nicolas@kdab.com>
Tested-by: Continuous Integration <build@kdab.com>
  • Loading branch information
narnaud committed Apr 12, 2024
1 parent 8c66aed commit 842c139
Show file tree
Hide file tree
Showing 16 changed files with 128 additions and 406 deletions.
83 changes: 3 additions & 80 deletions src/core/scriptmanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,80 +89,6 @@ void ScriptManager::runScript(const QString &fileName, bool async, bool log)
doRunScript(fileName, logEndScript);
}

void ScriptManager::runScriptInContext(const QString &fileName, const QueryMatch &context, bool async, bool log)
{
if (log)
spdlog::debug("==> Start script {} -- context: {}", fileName.toStdString(), context.toString().toStdString());
ScriptRunner::EndScriptFunc logEndScript;
if (log)
logEndScript = [fileName]() {
spdlog::debug("<== End script {}", fileName.toStdString());
};

if (async)
QTimer::singleShot(0, this, [this, fileName, logEndScript, context = std::move(context)]() {
doRunScript(fileName, logEndScript, context);
});
else
doRunScript(fileName, logEndScript, context);
}

static treesitter::QueryList readContextQueries(const QString &fileName, QTextStream &stream)
{
QString line;
QStringList contextQueryStrings;
QString contextQuery;
bool inContextQuery = false;

auto finishContextQuery = [&]() {
if (!contextQuery.isEmpty()) {
contextQueryStrings.push_back(contextQuery.simplified());
contextQuery.clear();
} else {
spdlog::warn("Encountered empty context query in {}", fileName.toStdString());
}

inContextQuery = false;
};

while ((line = stream.readLine()).startsWith("//")) {
auto content = line.mid(2);
if (content.simplified() == "CONTEXT QUERY") {
if (inContextQuery) {
// recover somewhat gracefully from a missing END CONTEXT QUERY
spdlog::warn("Encountered another 'CONTEXT QUERY' without a preceding 'END CONTEXT QUERY' in {}",
fileName.toStdString());
finishContextQuery();
}
inContextQuery = true;
} else if (content.simplified() == "END CONTEXT QUERY") {
finishContextQuery();
} else if (inContextQuery) {
contextQuery += content + '\n';
}
}
if (inContextQuery) {
spdlog::warn("Encountered 'CONTEXT QUERY' without a following 'END CONTEXT QUERY' in {}",
fileName.toStdString());
finishContextQuery();
}

auto constructQuery = [&fileName](const QString &query) -> std::shared_ptr<treesitter::Query> {
try {
return std::make_shared<treesitter::Query>(tree_sitter_cpp(), query);
} catch (treesitter::Query::Error &error) {
spdlog::error("ScriptManager - failed to parse CONTEXT QUERY in {} - query: `{}` error: {}",
fileName.toStdString(), query.toStdString(), error.description.toStdString());
return {};
}
};

auto contextQueries = kdalgorithms::filtered(kdalgorithms::transformed(contextQueryStrings, constructQuery),
&std::shared_ptr<treesitter::Query>::operator bool);

return contextQueries;
}

void ScriptManager::addScript(const QString &fileName)
{
QFile file(fileName);
Expand All @@ -173,11 +99,9 @@ void ScriptManager::addScript(const QString &fileName)
auto line = stream.readLine();
const QString description = line.startsWith("//") ? line.mid(2).simplified() : "";

const auto contextQueries = readContextQueries(fileName, stream);

QFileInfo fi(fileName);

Script script {fi.fileName(), fileName, description, contextQueries};
Script script {fi.fileName(), fileName, description};
emit aboutToAddScript(script, static_cast<int>(m_scriptList.size()));
m_scriptList.push_back(std::move(script));
emit scriptAdded(m_scriptList.back());
Expand Down Expand Up @@ -269,10 +193,9 @@ ScriptManager::ScriptList::iterator ScriptManager::removeScript(const ScriptList
return it;
}

void ScriptManager::doRunScript(const QString &fileName, const std::function<void()> &endFunc,
const std::optional<QueryMatch> &context)
void ScriptManager::doRunScript(const QString &fileName, const std::function<void()> &endFunc)
{
auto result = m_runner->runScript(fileName, endFunc, context);
auto result = m_runner->runScript(fileName, endFunc);
if (m_runner->hasError()) {
const auto errors = m_runner->errors();
for (const auto &error : errors)
Expand Down
13 changes: 1 addition & 12 deletions src/core/scriptmanager.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,6 @@
#include <functional>
#include <vector>

#include "querymatch.h"
#include "treesitter/query.h"

class QFileSystemWatcher;
class QAbstractItemModel;

Expand All @@ -34,11 +31,6 @@ class ScriptManager : public QObject
QString name;
QString fileName;
QString description;
// The context queries for this script.
// These are directly converted to Query instances from Strings in the ScriptManager.
// Profiling has revealed that creating Queries is actually quite expensive, so we
// do it once here and then reuse the same query every time.
treesitter::QueryList contextQueries;
};
using ScriptList = std::vector<Script>;

Expand All @@ -57,8 +49,6 @@ class ScriptManager : public QObject

public slots:
void runScript(const QString &fileName, bool async = true, bool log = true);
void runScriptInContext(const QString &fileName, const Core::QueryMatch &context, bool async = true,
bool log = true);

signals:
void scriptFinished(const QVariant &result);
Expand All @@ -79,8 +69,7 @@ public slots:
void addScriptsFromPath(const QString &path);
void removeScriptsFromPath(const QString &path);

void doRunScript(const QString &fileName, const std::function<void()> &endFunc = {},
const std::optional<QueryMatch> &context = {});
void doRunScript(const QString &fileName, const std::function<void()> &endFunc = {});

void updateDirectories();
void updateScriptDirectory(const QString &path);
Expand Down
2 changes: 0 additions & 2 deletions src/core/scriptmodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,6 @@ QVariant ScriptModel::data(const QModelIndex &index, int role) const
return script.description;
case PathRole:
return script.fileName;
case ContextQueriesRole:
return QVariant::fromValue(script.contextQueries);
default:
return QVariant {};
}
Expand Down
1 change: 0 additions & 1 deletion src/core/scriptmodel.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ class ScriptModel : public QAbstractTableModel

enum Role : int {
PathRole = Qt::UserRole,
ContextQueriesRole,
};
Q_ENUM(Role)

Expand Down
16 changes: 4 additions & 12 deletions src/core/scriptrunner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -139,8 +139,7 @@ ScriptRunner::ScriptRunner(QObject *parent)

ScriptRunner::~ScriptRunner() = default;

QVariant ScriptRunner::runScript(const QString &fileName, const std::function<void()> &endCallback,
const std::optional<QueryMatch> &context)
QVariant ScriptRunner::runScript(const QString &fileName, const std::function<void()> &endCallback)
{
QFileInfo fi(fileName);

Expand All @@ -158,12 +157,9 @@ QVariant ScriptRunner::runScript(const QString &fileName, const std::function<vo
connect(engine, &QObject::destroyed, this, endCallback);

if (fi.suffix() == "js") {
if (context.has_value()) {
spdlog::warn("ScriptRunner::runScript: context is not supported for javascript files");
}
result = runJavascript(fullName, engine);
} else {
result = runQml(fullName, engine, context);
result = runQml(fullName, engine);
}
// engine is deleted in runJavascript or runQml
} else {
Expand Down Expand Up @@ -230,7 +226,7 @@ QVariant ScriptRunner::runJavascript(const QString &fileName, QQmlEngine *engine
return QVariant(ErrorCode);
}

QVariant ScriptRunner::runQml(const QString &fileName, QQmlEngine *engine, const std::optional<QueryMatch> &context)
QVariant ScriptRunner::runQml(const QString &fileName, QQmlEngine *engine)
{
auto component = new QQmlComponent(engine, engine);
component->loadUrl(QUrl::fromLocalFile(fileName));
Expand Down Expand Up @@ -280,11 +276,7 @@ QVariant ScriptRunner::runQml(const QString &fileName, QQmlEngine *engine, const

// Run the run method if it exists
if (topLevel->metaObject()->indexOfMethod("run()") != -1) {
if (context.has_value()) {
QMetaObject::invokeMethod(topLevel, "run", Qt::DirectConnection, Q_ARG(Core::QueryMatch, *context));
} else {
QMetaObject::invokeMethod(topLevel, "run", Qt::DirectConnection);
}
QMetaObject::invokeMethod(topLevel, "run", Qt::DirectConnection);
if (m_hasError)
return ErrorCode;
QVariant result = topLevel->property("failed");
Expand Down
8 changes: 2 additions & 6 deletions src/core/scriptrunner.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,6 @@
#include <QString>

#include <functional>
#include <optional>

#include "querymatch.h"

namespace Core {

Expand All @@ -29,8 +26,7 @@ class ScriptRunner : public QObject

// Run a script
using EndScriptFunc = std::function<void()>;
QVariant runScript(const QString &filePath, const EndScriptFunc &endCallback = {},
const std::optional<QueryMatch> &context = {});
QVariant runScript(const QString &filePath, const EndScriptFunc &endCallback = {});

bool hasError() const { return m_hasError; }
QList<QQmlError> errors() const { return m_errors; }
Expand All @@ -40,7 +36,7 @@ class ScriptRunner : public QObject
private:
QQmlEngine *getEngine(const QString &fileName);
QVariant runJavascript(const QString &fileName, QQmlEngine *engine);
QVariant runQml(const QString &fileName, QQmlEngine *engine, const std::optional<QueryMatch> &context = {});
QVariant runQml(const QString &fileName, QQmlEngine *engine);
void filterErrors(const QQmlComponent &component);

private:
Expand Down
6 changes: 2 additions & 4 deletions src/gui/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,8 @@ set(PROJECT_SOURCES
runscriptwidget.ui
scriptpanel.h
scriptpanel.cpp
scriptsuggestions.cpp
scriptsuggestions.h
scriptsuggestionspanel.cpp
scriptsuggestionspanel.h
scriptlistpanel.cpp
scriptlistpanel.h
shortcutmanager.h
shortcutmanager.cpp
shortcutsettings.h
Expand Down
17 changes: 6 additions & 11 deletions src/gui/mainwindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,8 @@
#include "rctoqrcdialog.h"
#include "rctouidialog.h"
#include "runscriptwidget.h"
#include "scriptlistpanel.h"
#include "scriptpanel.h"
#include "scriptsuggestions.h"
#include "scriptsuggestionspanel.h"
#include "shortcutmanager.h"
#include "shortcutsettings.h"
#include "slintview.h"
Expand Down Expand Up @@ -70,10 +69,9 @@ MainWindow::MainWindow(QWidget *parent)
, m_palette(new Palette(this))
, m_historyPanel(new HistoryPanel(this))
, m_scriptPanel(new ScriptPanel(this))
, m_scriptSuggestionsPanel(new ScriptSuggestionsPanel(this))
, m_scriptlistpanel(new ScriptListPanel(this))
, m_documentPalette(new DocumentPalette(this))
, m_shortcutManager(new ShortcutManager(this))
, m_scriptSuggestions(new ScriptSuggestions(this))
{
// Initialize the settings before anything
GuiSettings::instance();
Expand All @@ -98,10 +96,9 @@ MainWindow::MainWindow(QWidget *parent)
createDock(logPanel, Qt::BottomDockWidgetArea, logPanel->toolBar());
createDock(m_historyPanel, Qt::BottomDockWidgetArea, m_historyPanel->toolBar());
auto scriptDock = createDock(m_scriptPanel, Qt::LeftDockWidgetArea, m_scriptPanel->toolBar());
auto scriptSuggestionsDock =
createDock(m_scriptSuggestionsPanel, Qt::BottomDockWidgetArea, m_scriptSuggestionsPanel->toolBar());
scriptSuggestionsDock->setAllowedAreas(Qt::AllDockWidgetAreas);
m_scriptSuggestionsPanel->setModel(m_scriptSuggestions);
auto scriptListDock = createDock(m_scriptlistpanel, Qt::BottomDockWidgetArea, m_scriptlistpanel->toolBar());
scriptListDock->setAllowedAreas(Qt::AllDockWidgetAreas);
m_scriptlistpanel->setModel(Core::ScriptManager::model());

// Ensure we display the script panel when a script is created
auto showScriptPanel = [scriptDock]() {
Expand Down Expand Up @@ -374,7 +371,7 @@ void MainWindow::initProject(const QString &path)
projects.removeLast();
settings.setValue(RecentProjectKey, projects);

// Initalize tree view
// Initialize tree view
auto index = m_fileModel->setRootPath(path);
m_projectView->setRootIndex(index);
connect(m_projectView->selectionModel(), &QItemSelectionModel::currentChanged, this, &MainWindow::openDocument);
Expand Down Expand Up @@ -799,8 +796,6 @@ void MainWindow::changeCurrentDocument()
if (windowIndex == -1) {
auto document = project->currentDocument();
auto widget = widgetForDocument(document);
if (auto textView = qobject_cast<TextView *>(widget))
textView->setScriptSuggestions(m_scriptSuggestions);

if (const auto actions = widget->actions(); !actions.isEmpty()) {
auto toolBar = new Toolbar(widget);
Expand Down
6 changes: 2 additions & 4 deletions src/gui/mainwindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,10 @@ namespace Gui {
class Palette;
class HistoryPanel;
class ScriptPanel;
class ScriptSuggestionsPanel;
class ScriptListPanel;
class DocumentPalette;
class ShortcutManager;
class TreeSitterInspector;
class ScriptSuggestions;

namespace Ui {
class MainWindow;
Expand Down Expand Up @@ -97,11 +96,10 @@ class MainWindow : public QMainWindow
Palette *const m_palette = nullptr;
HistoryPanel *const m_historyPanel = nullptr;
ScriptPanel *const m_scriptPanel = nullptr;
ScriptSuggestionsPanel *const m_scriptSuggestionsPanel = nullptr;
ScriptListPanel *const m_scriptlistpanel = nullptr;
DocumentPalette *const m_documentPalette = nullptr;
ShortcutManager *const m_shortcutManager = nullptr;
TreeSitterInspector *m_treeSitterInspector = nullptr;
ScriptSuggestions *const m_scriptSuggestions = nullptr;
};

} // namespace Gui
Loading

0 comments on commit 842c139

Please sign in to comment.