sam: extract into standalone [deft:nineify] sam plugin (deft_sam.so)
Stage 2a of the multi-.so split. sam becomes its own KTextEditor plugin:
new src/sam/samplugin.{h,cpp} (SamPlugin/SamView) + samplugin.json
("[deft:nineify] sam"), built as deft_sam.so. The sam tool-view wiring and
the runSamProgram/applySamToDocument/runSamFileLoop glue move out of
OllieView into SamView; sampanel.{h,cpp} move from src/plugin to src/sam.
The core deft plugin no longer links sam_lib or owns the sam panel.
This commit is contained in:
parent
d96cb63baf
commit
debe1226f7
|
|
@ -24,7 +24,7 @@ if(PkgConfig_FOUND)
|
|||
pkg_check_modules(WAYLAND_PROTOCOLS QUIET wayland-protocols)
|
||||
endif()
|
||||
|
||||
set(OLLIE_PLUGIN_SOURCES ollieplugin.cpp ollieplugin.h sampanel.cpp sampanel.h waylandcursorwarp.h)
|
||||
set(OLLIE_PLUGIN_SOURCES ollieplugin.cpp ollieplugin.h waylandcursorwarp.h)
|
||||
set(OLLIE_WAYLAND_PROTO_SRCS "")
|
||||
|
||||
if(WaylandScanner_FOUND AND WAYLAND_CLIENT_FOUND AND Qt6GuiPrivate_FOUND)
|
||||
|
|
@ -73,7 +73,6 @@ target_link_libraries(deft PRIVATE
|
|||
olliecommands_lib
|
||||
project_lib
|
||||
plumb_lib
|
||||
sam_lib
|
||||
KF6::TextEditor
|
||||
KF6::XmlGui
|
||||
KF6::CoreAddons
|
||||
|
|
|
|||
|
|
@ -11,8 +11,6 @@
|
|||
#include "projectindex.h"
|
||||
#include "symbolindex.h"
|
||||
#include "plumbresolve.h"
|
||||
#include "sampanel.h"
|
||||
#include "samengine.h"
|
||||
|
||||
#include <KTextEditor/Application>
|
||||
#include <KTextEditor/Command>
|
||||
|
|
@ -47,7 +45,6 @@
|
|||
#include <QStandardPaths>
|
||||
#include <QTimer>
|
||||
#include <QUrl>
|
||||
#include <QVBoxLayout>
|
||||
#include <QWidget>
|
||||
#include <QWindow>
|
||||
|
||||
|
|
@ -417,27 +414,6 @@ OllieView::OllieView(OlliePlugin *plugin, KTextEditor::MainWindow *mainWindow)
|
|||
});
|
||||
m_plumber.startReader();
|
||||
|
||||
// sam tool view: a dockable panel to run sam structural-regexp commands
|
||||
// against the active document (or project-wide via X/Y). Created on the
|
||||
// left sidebar; the host owns the returned widget.
|
||||
m_samToolView = m_mainWindow->createToolView(
|
||||
plugin, QStringLiteral("ollie_sam"), KTextEditor::MainWindow::Left,
|
||||
QIcon::fromTheme(QStringLiteral("edit-find-replace")), i18n("Sam"));
|
||||
if (m_samToolView) {
|
||||
m_samPanel = new SamPanel(m_samToolView);
|
||||
// The tool view returned by Kate (KateMDI::ToolView) already installs
|
||||
// its own layout; creating a second one here is silently rejected by Qt
|
||||
// and leaves SamPanel un-managed (tiny, pinned top-left). Add to the
|
||||
// existing layout when there is one, otherwise create it ourselves.
|
||||
QLayout *tvLayout = m_samToolView->layout();
|
||||
if (!tvLayout) {
|
||||
tvLayout = new QVBoxLayout(m_samToolView);
|
||||
tvLayout->setContentsMargins(0, 0, 0, 0);
|
||||
}
|
||||
tvLayout->addWidget(m_samPanel);
|
||||
connect(m_samPanel, &SamPanel::runRequested, this, &OllieView::runSamProgram);
|
||||
}
|
||||
|
||||
// Build on Kate's project plugin: attach to its view now (if present) and
|
||||
// follow it as plugins come and go. The bridge is read-only; the switchers
|
||||
// scope to its project when one is loaded, else to VCS discovery.
|
||||
|
|
@ -476,7 +452,6 @@ OllieView::~OllieView()
|
|||
delete m_filePalette;
|
||||
delete m_symbolPalette;
|
||||
delete m_radial;
|
||||
delete m_samToolView; // deletes the embedded SamPanel with it
|
||||
}
|
||||
|
||||
QList<OllieView::ActionEntry> OllieView::collectActions() const
|
||||
|
|
@ -1045,145 +1020,6 @@ void OllieView::onPlumbEdit(const QString &file, const QString &addr, const QStr
|
|||
}
|
||||
}
|
||||
|
||||
bool OllieView::applySamToDocument(KTextEditor::Document *doc, const QString &program,
|
||||
int dotStart, int dotEnd, QString *output, QString *error)
|
||||
{
|
||||
const QString text = doc->text();
|
||||
const SamResult res = SamEngine::run(program, text, dotStart, dotEnd);
|
||||
if (!res.ok) {
|
||||
if (error) {
|
||||
*error = res.error;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (output) {
|
||||
*output = res.output;
|
||||
}
|
||||
|
||||
// Apply all edits inside one transaction so a single undo reverts the whole
|
||||
// Run. Edits are in ORIGINAL-text offsets, non-overlapping and sorted by
|
||||
// start; apply back-to-front so earlier offsets remain valid.
|
||||
if (!res.edits.isEmpty()) {
|
||||
KTextEditor::Document::EditingTransaction transaction(doc);
|
||||
for (int i = res.edits.size() - 1; i >= 0; i--) {
|
||||
const SamEdit &e = res.edits[i];
|
||||
const KTextEditor::Cursor from = doc->offsetToCursor(e.start);
|
||||
const KTextEditor::Cursor to = doc->offsetToCursor(e.end);
|
||||
doc->replaceText(KTextEditor::Range(from, to), e.text);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void OllieView::runSamProgram(const QString &program)
|
||||
{
|
||||
if (!m_samPanel) {
|
||||
return;
|
||||
}
|
||||
const QString trimmed = program.trimmed();
|
||||
if (trimmed.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Multi-file X/Y: drive the sweep over the project's files.
|
||||
const SamEngine::FileLoop fl = SamEngine::peelFileLoop(trimmed);
|
||||
if (fl.matched) {
|
||||
runSamFileLoop(fl.negate, fl.fileRegex, fl.inner);
|
||||
return;
|
||||
}
|
||||
|
||||
// Single-file: run against the active document.
|
||||
KTextEditor::View *view = m_mainWindow->activeView();
|
||||
if (!view || !view->document()) {
|
||||
m_samPanel->showError(i18n("No active document."));
|
||||
return;
|
||||
}
|
||||
KTextEditor::Document *doc = view->document();
|
||||
|
||||
// Dot = current selection, else the whole document (sam's common idioms use
|
||||
// explicit ranges like `,` anyway; whole-doc is a sensible default dot).
|
||||
int dotStart = 0;
|
||||
int dotEnd = doc->text().size();
|
||||
if (view->selection()) {
|
||||
const KTextEditor::Range sel = view->selectionRange();
|
||||
dotStart = int(doc->cursorToOffset(sel.start()));
|
||||
dotEnd = int(doc->cursorToOffset(sel.end()));
|
||||
}
|
||||
|
||||
QString output;
|
||||
QString error;
|
||||
if (!applySamToDocument(doc, program, dotStart, dotEnd, &output, &error)) {
|
||||
m_samPanel->showError(error);
|
||||
return;
|
||||
}
|
||||
if (!output.isEmpty()) {
|
||||
m_samPanel->showOutput(output);
|
||||
} else {
|
||||
m_samPanel->showOutput(i18n("(done)"));
|
||||
}
|
||||
}
|
||||
|
||||
void OllieView::runSamFileLoop(bool negate, const QString &fileRegex, const QString &inner)
|
||||
{
|
||||
if (!m_samPanel) {
|
||||
return;
|
||||
}
|
||||
if (inner.trimmed().isEmpty()) {
|
||||
m_samPanel->showError(i18n("X/Y: no command to run on matched buffers."));
|
||||
return;
|
||||
}
|
||||
QRegularExpression fileRe(fileRegex);
|
||||
if (!fileRe.isValid()) {
|
||||
m_samPanel->showError(i18n("X/Y: bad file regexp: %1", fileRe.errorString()));
|
||||
return;
|
||||
}
|
||||
|
||||
// X/Y iterate the set of OPEN buffers (like acme's open-window set), not the
|
||||
// whole project on disk. Nothing is opened or read from disk: we edit the
|
||||
// documents Kate already has loaded, in place, each as its own undo step.
|
||||
KTextEditor::Editor *editor = KTextEditor::Editor::instance();
|
||||
KTextEditor::Application *app = editor ? editor->application() : nullptr;
|
||||
const QList<KTextEditor::Document *> docs =
|
||||
app ? app->documents() : QList<KTextEditor::Document *>{};
|
||||
if (docs.isEmpty()) {
|
||||
m_samPanel->showError(i18n("X/Y: no open buffers."));
|
||||
return;
|
||||
}
|
||||
|
||||
int matched = 0;
|
||||
int changed = 0;
|
||||
for (KTextEditor::Document *doc : docs) {
|
||||
if (!doc) {
|
||||
continue;
|
||||
}
|
||||
// Match the regex against the buffer's path (its local file path, or its
|
||||
// display name for an unsaved scratch buffer).
|
||||
const QUrl url = doc->url();
|
||||
const QString target =
|
||||
url.isLocalFile() ? url.toLocalFile() : doc->documentName();
|
||||
const bool hit = fileRe.match(target).hasMatch();
|
||||
if (hit == negate) {
|
||||
continue; // X runs on hits; Y runs on misses
|
||||
}
|
||||
matched++;
|
||||
QString output;
|
||||
QString error;
|
||||
const int dotEnd = doc->text().size();
|
||||
if (applySamToDocument(doc, inner, 0, dotEnd, &output, &error)) {
|
||||
changed++;
|
||||
if (!output.isEmpty()) {
|
||||
m_samPanel->showOutput(QStringLiteral("%1:\n%2").arg(target, output));
|
||||
}
|
||||
} else {
|
||||
// A "no match" in a given buffer is normal during a sweep; report it
|
||||
// quietly rather than as a hard error.
|
||||
m_samPanel->showOutput(QStringLiteral("%1: %2").arg(target, error));
|
||||
}
|
||||
}
|
||||
m_samPanel->showOutput(
|
||||
i18n("X/Y done: %1 open buffer(s) matched, %2 changed.", matched, changed));
|
||||
}
|
||||
|
||||
QAction *OllieView::findActionByName(const QString &nameOrText) const
|
||||
{
|
||||
KXMLGUIFactory *factory = m_mainWindow->guiFactory();
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@
|
|||
#include "projectindexer.h"
|
||||
#include "palettemodel.h" // PaletteItem
|
||||
#include "plumber.h"
|
||||
#include "sampanel.h"
|
||||
#include "waylandcursorwarp.h"
|
||||
|
||||
#include <QObject>
|
||||
|
|
@ -81,10 +80,6 @@ private Q_SLOTS:
|
|||
// addr (a plan9 edit-port address). Routed from the Plumber reader thread.
|
||||
void onPlumbEdit(const QString &file, const QString &addr, const QString &wdir);
|
||||
|
||||
// Run a sam program from the panel. Dispatches to the multi-file X/Y driver
|
||||
// when the program begins with X or Y, else runs against the active doc.
|
||||
void runSamProgram(const QString &program);
|
||||
|
||||
private:
|
||||
// Record one activation of \a id and persist. Feeds the frecency bonus so
|
||||
// habitual choices float up in every palette.
|
||||
|
|
@ -149,16 +144,6 @@ private:
|
|||
// matching pair (multi-line aware). Returns true if a pair was selected.
|
||||
bool selectBracketPairAt(KTextEditor::View *view, const QPoint &globalPos);
|
||||
|
||||
// Apply a sam program to a single document, returning the engine result.
|
||||
// Edits are applied inside one EditingTransaction (a single undo step).
|
||||
// \a doc must be non-null. Does not touch the panel.
|
||||
bool applySamToDocument(KTextEditor::Document *doc, const QString &program,
|
||||
int dotStart, int dotEnd, QString *output, QString *error);
|
||||
|
||||
// Drive a multi-file X/Y sweep over the project's files, opening each match
|
||||
// and applying \a inner. Reports progress/errors to the panel.
|
||||
void runSamFileLoop(bool negate, const QString &fileRegex, const QString &inner);
|
||||
|
||||
KTextEditor::MainWindow *m_mainWindow;
|
||||
PaletteWidget *m_palette = nullptr;
|
||||
PaletteWidget *m_filePalette = nullptr; // project "Go to File" switcher
|
||||
|
|
@ -178,8 +163,6 @@ private:
|
|||
ProjectIndexer m_indexer; // async, cached file/symbol indexing
|
||||
WaylandCursorWarp m_cursorWarp; // pointer warp on Wayland (no-op on X11)
|
||||
Plumber m_plumber; // plan9 plumbing client (send + edit-port reader)
|
||||
SamPanel *m_samPanel = nullptr; // sam command tool view (owned by the host)
|
||||
QWidget *m_samToolView = nullptr; // the dockable container from createToolView
|
||||
};
|
||||
|
||||
} // namespace deft
|
||||
|
|
|
|||
|
|
@ -24,3 +24,20 @@ if(Qt6Test_FOUND)
|
|||
target_link_libraries(test_samregex PRIVATE sam_lib Qt6::Test)
|
||||
add_test(NAME samregex COMMAND test_samregex)
|
||||
endif()
|
||||
|
||||
# --- [deft:nineify] sam — the Kate plugin ----------------------------------
|
||||
# The sam engine (sam_lib) is pure and always built. The plugin layer (tool
|
||||
# view + active-document glue) needs KF6::TextEditor; build it only when found.
|
||||
if(KF6TextEditor_FOUND)
|
||||
kcoreaddons_add_plugin(deft_sam
|
||||
SOURCES samplugin.cpp samplugin.h sampanel.cpp sampanel.h
|
||||
INSTALL_NAMESPACE "kf6/ktexteditor"
|
||||
)
|
||||
target_link_libraries(deft_sam PRIVATE
|
||||
sam_lib
|
||||
KF6::TextEditor
|
||||
KF6::CoreAddons
|
||||
KF6::I18n
|
||||
Qt6::Widgets
|
||||
)
|
||||
endif()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,222 @@
|
|||
/*
|
||||
* SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
#include "samplugin.h"
|
||||
|
||||
#include "samengine.h"
|
||||
#include "sampanel.h"
|
||||
|
||||
#include <KTextEditor/Application>
|
||||
#include <KTextEditor/Document>
|
||||
#include <KTextEditor/Editor>
|
||||
#include <KTextEditor/MainWindow>
|
||||
#include <KTextEditor/View>
|
||||
|
||||
#include <KLocalizedString>
|
||||
#include <KPluginFactory>
|
||||
|
||||
#include <QIcon>
|
||||
#include <QRegularExpression>
|
||||
#include <QVBoxLayout>
|
||||
#include <QWidget>
|
||||
|
||||
namespace deft
|
||||
{
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// SamPlugin
|
||||
// ---------------------------------------------------------------------------
|
||||
SamPlugin::SamPlugin(QObject *parent, const QVariantList &args)
|
||||
: KTextEditor::Plugin(parent)
|
||||
{
|
||||
Q_UNUSED(args);
|
||||
}
|
||||
|
||||
SamPlugin::~SamPlugin() = default;
|
||||
|
||||
QObject *SamPlugin::createView(KTextEditor::MainWindow *mainWindow)
|
||||
{
|
||||
return new SamView(this, mainWindow);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// SamView
|
||||
// ---------------------------------------------------------------------------
|
||||
SamView::SamView(SamPlugin *plugin, KTextEditor::MainWindow *mainWindow)
|
||||
: QObject(plugin)
|
||||
, m_mainWindow(mainWindow)
|
||||
{
|
||||
// sam tool view: a dockable panel to run sam structural-regexp commands
|
||||
// against the active document (or project-wide via X/Y). Created on the
|
||||
// left sidebar; the host owns the returned widget.
|
||||
m_samToolView = m_mainWindow->createToolView(
|
||||
plugin, QStringLiteral("deft_sam"), KTextEditor::MainWindow::Left,
|
||||
QIcon::fromTheme(QStringLiteral("edit-find-replace")), i18n("Sam"));
|
||||
if (m_samToolView) {
|
||||
m_samPanel = new SamPanel(m_samToolView);
|
||||
// The tool view returned by Kate (KateMDI::ToolView) already installs
|
||||
// its own layout; creating a second one here is silently rejected by Qt
|
||||
// and leaves SamPanel un-managed (tiny, pinned top-left). Add to the
|
||||
// existing layout when there is one, otherwise create it ourselves.
|
||||
QLayout *tvLayout = m_samToolView->layout();
|
||||
if (!tvLayout) {
|
||||
tvLayout = new QVBoxLayout(m_samToolView);
|
||||
tvLayout->setContentsMargins(0, 0, 0, 0);
|
||||
}
|
||||
tvLayout->addWidget(m_samPanel);
|
||||
connect(m_samPanel, &SamPanel::runRequested, this, &SamView::runSamProgram);
|
||||
}
|
||||
}
|
||||
|
||||
SamView::~SamView()
|
||||
{
|
||||
delete m_samToolView; // deletes the embedded SamPanel with it
|
||||
}
|
||||
|
||||
bool SamView::applySamToDocument(KTextEditor::Document *doc, const QString &program,
|
||||
int dotStart, int dotEnd, QString *output, QString *error)
|
||||
{
|
||||
const QString text = doc->text();
|
||||
const SamResult res = SamEngine::run(program, text, dotStart, dotEnd);
|
||||
if (!res.ok) {
|
||||
if (error) {
|
||||
*error = res.error;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (output) {
|
||||
*output = res.output;
|
||||
}
|
||||
|
||||
// Apply all edits inside one transaction so a single undo reverts the whole
|
||||
// Run. Edits are in ORIGINAL-text offsets, non-overlapping and sorted by
|
||||
// start; apply back-to-front so earlier offsets remain valid.
|
||||
if (!res.edits.isEmpty()) {
|
||||
KTextEditor::Document::EditingTransaction transaction(doc);
|
||||
for (int i = res.edits.size() - 1; i >= 0; i--) {
|
||||
const SamEdit &e = res.edits[i];
|
||||
const KTextEditor::Cursor from = doc->offsetToCursor(e.start);
|
||||
const KTextEditor::Cursor to = doc->offsetToCursor(e.end);
|
||||
doc->replaceText(KTextEditor::Range(from, to), e.text);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void SamView::runSamProgram(const QString &program)
|
||||
{
|
||||
if (!m_samPanel) {
|
||||
return;
|
||||
}
|
||||
const QString trimmed = program.trimmed();
|
||||
if (trimmed.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Multi-file X/Y: drive the sweep over the project's files.
|
||||
const SamEngine::FileLoop fl = SamEngine::peelFileLoop(trimmed);
|
||||
if (fl.matched) {
|
||||
runSamFileLoop(fl.negate, fl.fileRegex, fl.inner);
|
||||
return;
|
||||
}
|
||||
|
||||
// Single-file: run against the active document.
|
||||
KTextEditor::View *view = m_mainWindow->activeView();
|
||||
if (!view || !view->document()) {
|
||||
m_samPanel->showError(i18n("No active document."));
|
||||
return;
|
||||
}
|
||||
KTextEditor::Document *doc = view->document();
|
||||
|
||||
// Dot = current selection, else the whole document (sam's common idioms use
|
||||
// explicit ranges like `,` anyway; whole-doc is a sensible default dot).
|
||||
int dotStart = 0;
|
||||
int dotEnd = doc->text().size();
|
||||
if (view->selection()) {
|
||||
const KTextEditor::Range sel = view->selectionRange();
|
||||
dotStart = int(doc->cursorToOffset(sel.start()));
|
||||
dotEnd = int(doc->cursorToOffset(sel.end()));
|
||||
}
|
||||
|
||||
QString output;
|
||||
QString error;
|
||||
if (!applySamToDocument(doc, program, dotStart, dotEnd, &output, &error)) {
|
||||
m_samPanel->showError(error);
|
||||
return;
|
||||
}
|
||||
if (!output.isEmpty()) {
|
||||
m_samPanel->showOutput(output);
|
||||
} else {
|
||||
m_samPanel->showOutput(i18n("(done)"));
|
||||
}
|
||||
}
|
||||
|
||||
void SamView::runSamFileLoop(bool negate, const QString &fileRegex, const QString &inner)
|
||||
{
|
||||
if (!m_samPanel) {
|
||||
return;
|
||||
}
|
||||
if (inner.trimmed().isEmpty()) {
|
||||
m_samPanel->showError(i18n("X/Y: no command to run on matched buffers."));
|
||||
return;
|
||||
}
|
||||
QRegularExpression fileRe(fileRegex);
|
||||
if (!fileRe.isValid()) {
|
||||
m_samPanel->showError(i18n("X/Y: bad file regexp: %1", fileRe.errorString()));
|
||||
return;
|
||||
}
|
||||
|
||||
// X/Y iterate the set of OPEN buffers (like acme's open-window set), not the
|
||||
// whole project on disk. Nothing is opened or read from disk: we edit the
|
||||
// documents Kate already has loaded, in place, each as its own undo step.
|
||||
KTextEditor::Editor *editor = KTextEditor::Editor::instance();
|
||||
KTextEditor::Application *app = editor ? editor->application() : nullptr;
|
||||
const QList<KTextEditor::Document *> docs =
|
||||
app ? app->documents() : QList<KTextEditor::Document *>{};
|
||||
if (docs.isEmpty()) {
|
||||
m_samPanel->showError(i18n("X/Y: no open buffers."));
|
||||
return;
|
||||
}
|
||||
|
||||
int matched = 0;
|
||||
int changed = 0;
|
||||
for (KTextEditor::Document *doc : docs) {
|
||||
if (!doc) {
|
||||
continue;
|
||||
}
|
||||
// Match the regex against the buffer's path (its local file path, or its
|
||||
// display name for an unsaved scratch buffer).
|
||||
const QUrl url = doc->url();
|
||||
const QString target =
|
||||
url.isLocalFile() ? url.toLocalFile() : doc->documentName();
|
||||
const bool hit = fileRe.match(target).hasMatch();
|
||||
if (hit == negate) {
|
||||
continue; // X runs on hits; Y runs on misses
|
||||
}
|
||||
matched++;
|
||||
QString output;
|
||||
QString error;
|
||||
const int dotEnd = doc->text().size();
|
||||
if (applySamToDocument(doc, inner, 0, dotEnd, &output, &error)) {
|
||||
changed++;
|
||||
if (!output.isEmpty()) {
|
||||
m_samPanel->showOutput(QStringLiteral("%1:\n%2").arg(target, output));
|
||||
}
|
||||
} else {
|
||||
// A "no match" in a given buffer is normal during a sweep; report it
|
||||
// quietly rather than as a hard error.
|
||||
m_samPanel->showOutput(QStringLiteral("%1: %2").arg(target, error));
|
||||
}
|
||||
}
|
||||
m_samPanel->showOutput(
|
||||
i18n("X/Y done: %1 open buffer(s) matched, %2 changed.", matched, changed));
|
||||
}
|
||||
|
||||
} // namespace deft
|
||||
|
||||
// K_PLUGIN_FACTORY generates the factory and the moc-level registration. The
|
||||
// matching .moc include is required in this translation unit.
|
||||
K_PLUGIN_FACTORY_WITH_JSON(SamPluginFactory, "samplugin.json",
|
||||
registerPlugin<deft::SamPlugin>();)
|
||||
|
||||
#include "samplugin.moc"
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*
|
||||
* [deft:nineify] sam — the structural-regexp command plugin for Kate.
|
||||
*
|
||||
* SamPlugin is the KTextEditor::Plugin entry point. For every MainWindow it
|
||||
* creates a SamView, which installs a dockable "Sam" tool view (a SamPanel) and
|
||||
* runs sam programs against the active document, or project-wide via the X/Y
|
||||
* multi-file loop. The sam engine (sam_lib) is pure; this layer is only the
|
||||
* Kate glue (tool view, active document, EditingTransaction).
|
||||
*/
|
||||
#ifndef DEFT_SAMPLUGIN_H
|
||||
#define DEFT_SAMPLUGIN_H
|
||||
|
||||
#include <KTextEditor/Plugin>
|
||||
|
||||
#include "sampanel.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <QPointer>
|
||||
|
||||
namespace KTextEditor
|
||||
{
|
||||
class Document;
|
||||
class MainWindow;
|
||||
}
|
||||
|
||||
namespace deft
|
||||
{
|
||||
|
||||
class SamPlugin : public KTextEditor::Plugin
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit SamPlugin(QObject *parent, const QVariantList &args = {});
|
||||
~SamPlugin() override;
|
||||
|
||||
QObject *createView(KTextEditor::MainWindow *mainWindow) override;
|
||||
};
|
||||
|
||||
class SamView : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
SamView(SamPlugin *plugin, KTextEditor::MainWindow *mainWindow);
|
||||
~SamView() override;
|
||||
|
||||
private Q_SLOTS:
|
||||
// Run a sam program from the panel. Dispatches to the multi-file X/Y driver
|
||||
// when the program begins with X or Y, else runs against the active doc.
|
||||
void runSamProgram(const QString &program);
|
||||
|
||||
private:
|
||||
// Apply a sam program to a single document, returning the engine result.
|
||||
// Edits are applied inside one EditingTransaction (a single undo step).
|
||||
// \a doc must be non-null. Does not touch the panel.
|
||||
bool applySamToDocument(KTextEditor::Document *doc, const QString &program,
|
||||
int dotStart, int dotEnd, QString *output, QString *error);
|
||||
|
||||
// Drive a multi-file X/Y sweep over the open buffers, applying \a inner.
|
||||
// Reports progress/errors to the panel.
|
||||
void runSamFileLoop(bool negate, const QString &fileRegex, const QString &inner);
|
||||
|
||||
KTextEditor::MainWindow *m_mainWindow;
|
||||
SamPanel *m_samPanel = nullptr; // sam command tool view (owned by host)
|
||||
QWidget *m_samToolView = nullptr; // the dockable container from createToolView
|
||||
};
|
||||
|
||||
} // namespace deft
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"KPlugin": {
|
||||
"Authors": [
|
||||
{
|
||||
"Name": "Levi Neely"
|
||||
}
|
||||
],
|
||||
"Description": "Run plan9 sam structural-regexp commands against the active document or across open buffers (X/Y).",
|
||||
"Icon": "edit-find-replace",
|
||||
"License": "LGPL-2.0-or-later",
|
||||
"Name": "[deft:nineify] sam",
|
||||
"Version": "0.1.0"
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue