Skip to content
Extraits de code Groupes Projets
Vérifiée Valider dce87798 rédigé par Kubat's avatar Kubat
Parcourir les fichiers

FakeVim: Add the FakeVim editor proxy to the script views

parent 0213483e
Aucune branche associée trouvée
Aucune étiquette associée trouvée
1 requête de fusion!19Fake vim
......@@ -16,6 +16,9 @@
#include <qglobal.h>
#define VIVY_PRAGMA(x) _Pragma(#x)
#define TODO(x) VIVY_PRAGMA(message("\"TODO: " #x "\""))
#define VIVY_WIN_EXE_SUFFIX ".exe"
#define VIVY_ASSERT_STRINGIFY_HELPER(x) #x
#define VIVY_ASSERT_STRINGIFY(x) VIVY_ASSERT_STRINGIFY_HELPER(x)
......
#include "EditorProxy.hh"
#include "../FakeVim/FakeVimHandler.hh"
#include "../FakeVim/FakeVimActions.hh"
#include <QMessageBox>
#include <QStatusBar>
#include <QMainWindow>
#include <QTemporaryFile>
using namespace Vivy;
void
Vivy::initHandler(FakeVimHandler *handler)
{
handler->handleCommand(QStringLiteral("set nopasskeys"));
handler->handleCommand(QStringLiteral("set nopasscontrolkey"));
handler->installEventFilter();
handler->setupWidget();
}
void
Vivy::clearUndoRedo(QPlainTextEdit *scriptEditor)
{
scriptEditor->setUndoRedoEnabled(false);
scriptEditor->setUndoRedoEnabled(true);
}
Vivy::EditorProxy *
Vivy::connectSignals(FakeVimHandler *handler, QMainWindow *mainWindow, QPlainTextEdit *editor)
{
EditorProxy *proxy = new EditorProxy(editor, mainWindow, handler);
handler->commandBufferChanged.connect([proxy](const QString &contents, int cursorPos,
int /* anchorPos */,
int /* messageLevel */) noexcept -> void {
proxy->changeStatusMessage(contents, cursorPos);
});
handler->extraInformationChanged.connect(
[proxy](const QString &text) noexcept -> void { proxy->changeExtraInformation(text); });
handler->statusDataChanged.connect(
[proxy](const QString &text) noexcept -> void { proxy->changeStatusData(text); });
handler->highlightMatches.connect(
[proxy](const QString &needle) noexcept -> void { proxy->highlightMatches(needle); });
handler->handleExCommandRequested.connect(
[proxy](bool *handled, const ExCommand &cmd) noexcept -> void {
proxy->handleExCommand(handled, cmd);
});
handler->requestSetBlockSelection.connect([proxy](const QTextCursor &cursor) noexcept -> void {
proxy->requestSetBlockSelection(cursor);
});
handler->requestDisableBlockSelection.connect(
[proxy]() noexcept -> void { proxy->requestDisableBlockSelection(); });
handler->requestHasBlockSelection.connect(
[proxy](bool *on) noexcept -> void { proxy->requestHasBlockSelection(on); });
handler->indentRegion.connect(
[proxy](int beginBlock, int endBlock, QChar typedChar) noexcept -> void {
proxy->indentRegion(beginBlock, endBlock, typedChar);
});
handler->checkForElectricCharacter.connect([proxy](bool *result, QChar c) noexcept -> void {
proxy->checkForElectricCharacter(result, c);
});
return proxy;
}
EditorProxy::EditorProxy(QPlainTextEdit *widg, QMainWindow *mainWin, QObject *parent) noexcept
: QObject(parent)
, widget(widg)
, mainWindow(mainWin)
{
}
void
EditorProxy::openFile(const QString &filename) noexcept
{
emit handleInput(QString(QStringLiteral(":r %1<CR>")).arg(filename));
}
void
EditorProxy::changeStatusData(const QString &info) noexcept
{
statusData = info;
updateStatusBar();
}
void
EditorProxy::highlightMatches(const QString &pattern) noexcept
{
QTextDocument *doc = widget->document();
Q_ASSERT(doc);
QTextEdit::ExtraSelection selection;
selection.format.setBackground(Qt::yellow);
selection.format.setForeground(Qt::black);
// Highlight matches.
QRegExp re(pattern);
QTextCursor cur = doc->find(re);
searchSelection.clear();
int a = cur.position();
while (!cur.isNull()) {
if (cur.hasSelection()) {
selection.cursor = cur;
searchSelection.append(selection);
} else {
cur.movePosition(QTextCursor::NextCharacter);
}
cur = doc->find(re, cur);
int b = cur.position();
if (a == b) {
cur.movePosition(QTextCursor::NextCharacter);
cur = doc->find(re, cur);
b = cur.position();
if (a == b)
break;
}
a = b;
}
updateExtraSelections();
}
void
EditorProxy::changeStatusMessage(const QString &contents, int cursorPos) noexcept
{
statusMessage = (cursorPos == -1)
? contents
: contents.left(cursorPos) + QChar(10073) + contents.mid(cursorPos);
updateStatusBar();
}
void
EditorProxy::changeExtraInformation(const QString &info) noexcept
{
QMessageBox::information(widget, tr("Information"), info);
}
void
EditorProxy::updateStatusBar() noexcept
{
static constexpr int msgMaxSize = 80;
int slack = msgMaxSize - statusMessage.size() - statusData.size();
QString msg = statusMessage + QString(slack, QLatin1Char(' ')) + statusData;
mainWindow->statusBar()->showMessage(msg);
}
void
EditorProxy::handleExCommand(bool *handled, const ExCommand &cmd) noexcept
{
if (wantSaveAndQuit(cmd))
emit requestSaveAndQuit(); // :wq
else if (wantSave(cmd))
emit requestSave(); // :w
else if (wantQuit(cmd)) {
if (cmd.hasBang)
invalidate(); // :q!
else
emit requestQuit(); // :q
}
else if (wantRun(cmd))
emit requestRun();
else {
*handled = false;
return;
}
*handled = true;
}
void
EditorProxy::requestSetBlockSelection(const QTextCursor &tc) noexcept
{
const QPalette pal = widget->parentWidget() != nullptr ? widget->parentWidget()->palette()
: QApplication::palette();
blockSelection.clear();
clearSelection.clear();
QTextCursor cur = tc;
QTextEdit::ExtraSelection selection;
selection.format.setBackground(pal.color(QPalette::Base));
selection.format.setForeground(pal.color(QPalette::Text));
selection.cursor = cur;
clearSelection.append(selection);
selection.format.setBackground(pal.color(QPalette::Highlight));
selection.format.setForeground(pal.color(QPalette::HighlightedText));
const int from = cur.positionInBlock();
const int to = cur.anchor() - cur.document()->findBlock(cur.anchor()).position();
const int min = qMin(cur.position(), cur.anchor());
const int max = qMax(cur.position(), cur.anchor());
for (QTextBlock block = cur.document()->findBlock(min);
block.isValid() && block.position() < max; block = block.next()) {
cur.setPosition(block.position() + qMin(from, block.length()));
cur.setPosition(block.position() + qMin(to, block.length()), QTextCursor::KeepAnchor);
selection.cursor = cur;
blockSelection.append(selection);
}
disconnect(widget, &QPlainTextEdit::selectionChanged, this, &EditorProxy::updateBlockSelection);
widget->setTextCursor(tc);
connect(widget, &QPlainTextEdit::selectionChanged, this, &EditorProxy::updateBlockSelection);
QPalette pal2 = widget->palette();
pal2.setColor(QPalette::Highlight, Qt::transparent);
pal2.setColor(QPalette::HighlightedText, Qt::transparent);
widget->setPalette(pal2);
updateExtraSelections();
}
void
EditorProxy::requestDisableBlockSelection() noexcept
{
const QPalette pal = widget->parentWidget() != nullptr ? widget->parentWidget()->palette()
: QApplication::palette();
blockSelection.clear();
clearSelection.clear();
widget->setPalette(pal);
disconnect(widget, &QPlainTextEdit::selectionChanged, this, &EditorProxy::updateBlockSelection);
updateExtraSelections();
}
void
EditorProxy::updateBlockSelection() noexcept
{
requestSetBlockSelection(widget->textCursor());
}
void
EditorProxy::requestHasBlockSelection(bool *on) noexcept
{
*on = !blockSelection.isEmpty();
}
void
EditorProxy::indentRegion(int beginBlock, int endBlock, QChar typedChar) noexcept
{
QTextDocument *doc = widget->document();
Q_ASSERT(doc);
const int indentSize =
static_cast<int>(FakeVim::Internal::fakeVimSettings()->shiftWidth.value());
QTextBlock startBlock = doc->findBlockByNumber(beginBlock);
// Record line lenghts for mark adjustments
QVector<int> lineLengths(endBlock - beginBlock + 1);
QTextBlock block = startBlock;
for (int i = beginBlock; i <= endBlock; ++i) {
const QString line = block.text();
lineLengths[i - beginBlock] = line.length();
if (typedChar.unicode() == 0 && line.simplified().isEmpty()) {
// clear empty lines
QTextCursor cursor(block);
while (!cursor.atBlockEnd())
cursor.deleteChar();
}
else {
const QTextBlock previousBlock = block.previous();
const QString previousLine = previousBlock.isValid() ? previousBlock.text() : QString();
int indent = firstNonSpace(previousLine);
if (typedChar == '}')
indent = std::max(0, indent - indentSize);
else if (previousLine.endsWith("{"))
indent += indentSize;
const QString indentString = QString(" ").repeated(indent);
QTextCursor cursor(block);
cursor.beginEditBlock();
cursor.movePosition(QTextCursor::StartOfBlock);
cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor,
firstNonSpace(line));
cursor.removeSelectedText();
cursor.insertText(indentString);
cursor.endEditBlock();
}
block = block.next();
}
}
void
EditorProxy::checkForElectricCharacter(bool *result, QChar c) noexcept
{
*result = c == '{' || c == '}';
}
int
EditorProxy::firstNonSpace(const QString &text) noexcept
{
int indent = 0;
while (indent < text.length() && text.at(indent) == ' ')
++indent;
return indent;
}
void
EditorProxy::updateExtraSelections() noexcept
{
widget->setExtraSelections(clearSelection + searchSelection + blockSelection);
}
bool
EditorProxy::wantSaveAndQuit(const ExCommand &cmd) noexcept
{
return cmd.cmd == "wq";
}
bool
EditorProxy::wantSave(const ExCommand &cmd) noexcept
{
return cmd.matches("w", "write") || cmd.matches("wa", "wall");
}
bool
EditorProxy::wantQuit(const ExCommand &cmd) noexcept
{
return cmd.matches("q", "quit") || cmd.matches("qa", "qall");
}
bool
EditorProxy::wantRun(const ExCommand &cmd) noexcept
{
return cmd.matches("run", "run") || cmd.matches("make", "make");
}
bool
EditorProxy::save(const QString &fileName) noexcept
{
if (!hasChanges(fileName))
return true;
QTemporaryFile tmpFile;
if (!tmpFile.open()) {
qCritical() << tr("Cannot create temporary file: %1").arg(tmpFile.errorString());
return false;
}
QTextStream ts(&tmpFile);
ts << content();
ts.flush();
QFile::remove(fileName);
if (!QFile::copy(tmpFile.fileName(), fileName)) {
qCritical() << tr("Cannot write to file \"%1\"").arg(fileName);
return false;
}
return true;
}
void
EditorProxy::cancel(const QString &fileName) noexcept
{
if (hasChanges(fileName)) {
qCritical() << tr("File \"%1\" was changed").arg(fileName);
} else {
invalidate();
}
}
void
EditorProxy::invalidate() noexcept
{
TODO(Use a Vivy thing here !)
QApplication::quit();
}
bool
EditorProxy::hasChanges(const QString &fileName) noexcept
{
if (fileName.isEmpty() && content().isEmpty())
return false;
QFile f(fileName);
if (!f.open(QIODevice::ReadOnly))
return true;
QTextStream ts(&f);
return content() != ts.readAll();
}
QTextDocument *
EditorProxy::document() const noexcept
{
return widget->document();
}
QString
EditorProxy::content() const noexcept
{
return document()->toPlainText();
}
#pragma once
#include <QObject>
#include <QTextEdit>
#include "ScriptEditor.hh"
class QMainWindow;
class QTextDocument;
class QString;
class QWidget;
class QTextCursor;
namespace FakeVim::Internal
{
class FakeVimHandler;
struct ExCommand;
}
namespace Vivy
{
class EditorProxy;
using FakeVimHandler = FakeVim::Internal::FakeVimHandler;
using ExCommand = FakeVim::Internal::ExCommand;
class EditorProxy : public QObject {
Q_OBJECT
public:
explicit EditorProxy(QPlainTextEdit *widget, QMainWindow *mw,
QObject *parent = nullptr) noexcept;
void openFile(const QString &fileName) noexcept;
bool save(const QString &fileName) noexcept;
void cancel(const QString &fileName) noexcept;
signals:
void handleInput(const QString &keys);
void requestSave();
void requestSaveAndQuit();
void requestQuit();
void requestRun();
public slots:
void changeStatusData(const QString &info) noexcept;
void highlightMatches(const QString &pattern) noexcept;
void changeStatusMessage(const QString &contents, int cursorPos) noexcept;
void changeExtraInformation(const QString &info) noexcept;
void updateStatusBar() noexcept;
void handleExCommand(bool *handled, const FakeVim::Internal::ExCommand &cmd) noexcept;
void requestSetBlockSelection(const QTextCursor &tc) noexcept;
void requestDisableBlockSelection() noexcept;
void updateBlockSelection() noexcept;
void requestHasBlockSelection(bool *on) noexcept;
void indentRegion(int beginBlock, int endBlock, QChar typedChar) noexcept;
void checkForElectricCharacter(bool *result, QChar c) noexcept;
private:
static int firstNonSpace(const QString &text) noexcept;
void updateExtraSelections() noexcept;
bool wantSaveAndQuit(const FakeVim::Internal::ExCommand &cmd) noexcept;
bool wantSave(const FakeVim::Internal::ExCommand &cmd) noexcept;
bool wantQuit(const FakeVim::Internal::ExCommand &cmd) noexcept;
bool wantRun(const FakeVim::Internal::ExCommand &cmd) noexcept;
void invalidate() noexcept;
bool hasChanges(const QString &fileName) noexcept;
QTextDocument *document() const noexcept;
QString content() const noexcept;
QPlainTextEdit *widget;
QMainWindow *mainWindow;
QString statusMessage;
QString statusData;
QList<QTextEdit::ExtraSelection> searchSelection;
QList<QTextEdit::ExtraSelection> clearSelection;
QList<QTextEdit::ExtraSelection> blockSelection;
};
QWidget *createEditorWidget(bool usePlainTextEdit);
void initHandler(FakeVimHandler *handler);
void clearUndoRedo(QPlainTextEdit *editor);
EditorProxy *connectSignals(FakeVimHandler *handler, QMainWindow *mainWindow,
QPlainTextEdit *editor);
}
......@@ -44,8 +44,46 @@ ScriptEditor::ScriptEditor(QWidget *parent) noexcept
textFormat.setForeground(QBrush(Qt::white));
mergeCurrentCharFormat(textFormat);
QPlainTextEdit::setCursorWidth(0);
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setBackgroundVisible(true);
updateLineNumberAreaWidth(0);
setObjectName(QStringLiteral("Editor"));
setFocus();
}
void
ScriptEditor::paintEvent(QPaintEvent *e) noexcept
{
QPlainTextEdit::paintEvent(e);
if (!cursorRect.isNull() && e->rect().intersects(cursorRect)) {
QRect rect = cursorRect;
cursorRect = QRect();
QPlainTextEdit::viewport()->update(rect);
}
// Draw text cursor.
QRect rect = QPlainTextEdit::cursorRect();
if (e->rect().intersects(rect)) {
QPainter painter(QPlainTextEdit::viewport());
if (QPlainTextEdit::overwriteMode()) {
QFontMetrics fm(QPlainTextEdit::font());
const int position = QPlainTextEdit::textCursor().position();
const QChar c = QPlainTextEdit::document()->characterAt(position);
rect.setWidth(fm.horizontalAdvance(c));
painter.setPen(Qt::NoPen);
painter.setBrush(QPlainTextEdit::palette().color(QPalette::Base));
painter.setCompositionMode(QPainter::CompositionMode_Difference);
} else {
rect.setWidth(QPlainTextEdit::cursorWidth());
painter.setPen(QPlainTextEdit::palette().color(QPalette::Text));
}
painter.drawRect(rect);
cursorRect = rect;
}
}
void
......
......@@ -42,8 +42,9 @@ public slots:
void updateLastLuaError(int, QString);
protected:
void resizeEvent(QResizeEvent *event) noexcept override;
void keyPressEvent(QKeyEvent *e) noexcept override;
void resizeEvent(QResizeEvent *) noexcept override;
void keyPressEvent(QKeyEvent *) noexcept override;
void paintEvent(QPaintEvent *) noexcept override;
private slots:
void updateLineNumberAreaWidth(int newBlockCount) noexcept;
......@@ -51,5 +52,6 @@ private slots:
private:
QWidget *lineNumberArea{ nullptr };
QRect cursorRect{};
};
}
0% Chargement en cours ou .
You are about to add 0 people to the discussion. Proceed with caution.
Terminez d'abord l'édition de ce message.
Veuillez vous inscrire ou vous pour commenter