fix: 修复损坏的代码填充提示;修复控制台能被清空代码的问题;

This commit is contained in:
寂静的羽夏 2025-02-02 19:10:11 +08:00
parent 5efff1b4c0
commit 6b020b7f70
19 changed files with 1147 additions and 1473 deletions

View File

@ -222,7 +222,7 @@ void QConsoleWidget::keyPressEvent(QKeyEvent *e) {
cut();
else {
// cursor must be in edit zone
if (textCursor <= inpos_)
if (textCursor.selectionStart() <= inpos_)
QApplication::beep();
else
QEditor::keyPressEvent(e);
@ -264,7 +264,7 @@ bool QConsoleWidget::isSelectionInEditZone() const {
auto selectionStart = textCursor.selectionStart();
auto selectionEnd = textCursor.selectionEnd();
return selectionStart > inpos_ && selectionEnd >= inpos_;
return selectionStart >= inpos_ && selectionEnd > inpos_;
}
bool QConsoleWidget::isCursorInEditZone() const { return cursor() >= inpos_; }

View File

@ -29,8 +29,6 @@ set(DOCUMENT_SRC
lib/document/qdocument.cpp)
set(WIDGETS_SRC
lib/widgets/qcalltip.cpp
lib/widgets/qcalltip.h
lib/widgets/qfoldpanel.cpp
lib/widgets/qfoldpanel.h
lib/widgets/qlinechangepanel.cpp

View File

@ -123,8 +123,8 @@ void QCodeCompletionEngine::run() {
/*!
\brief Forced completion trigger
*/
void QCodeCompletionEngine::complete() {
complete(editor()->cursor(), QString());
void QCodeCompletionEngine::complete(const QString &trigger) {
complete(editor()->cursor(), trigger);
}
/*!

View File

@ -68,15 +68,16 @@ signals:
void completionTriggered(const QString &s);
public slots:
void complete();
void complete(const QString &trigger = {});
// make it public
virtual void complete(const QDocumentCursor &c, const QString &trigger);
void textEdited(QKeyEvent *e);
protected:
virtual void run();
virtual bool eventFilter(QObject *o, QEvent *e);
virtual void complete(const QDocumentCursor &c, const QString &trigger);
private:
void triggerWordLenComplete();

View File

@ -1938,6 +1938,11 @@ void QEditor::timerEvent(QTimerEvent *e) {
// startDrag();
} else if (id == m_click.timerId()) {
m_click.stop();
} else if (id == m_inputto.timerId()) {
m_inputto.stop();
if (!m_blink.isActive()) {
emit inputTimeOuted();
}
}
}
@ -2850,7 +2855,7 @@ void QEditor::focusInEvent(QFocusEvent *e) {
void QEditor::focusOutEvent(QFocusEvent *e) {
setFlag(CursorOn, false);
m_blink.stop();
m_inputto.start(3000, this);
QAbstractScrollArea::focusOutEvent(e);
}
@ -3439,7 +3444,8 @@ bool QEditor::processCursor(QDocumentCursor &c, QKeyEvent *e, bool &b) {
}
} else {
if (isPairedCloseChar(text)) {
if (c.previousChar() == getPairedBeginChar(text)) {
if (c.previousChar() == getPairedBeginChar(text) &&
c.nextChar() == text) {
c.movePosition(1);
break;
}

View File

@ -307,6 +307,8 @@ signals:
void zoomed();
void inputTimeOuted();
public slots:
void checkClipboard();
void reconnectWatcher();
@ -443,7 +445,7 @@ protected:
bool m_selection;
QRect m_crect, m_margins;
QPoint m_clickPoint, m_dragPoint;
QBasicTimer m_blink, m_scroll, m_click, m_drag;
QBasicTimer m_blink, m_scroll, m_click, m_drag, m_inputto;
QFont _docfont;
qreal _scaleRate = 1.0;

View File

@ -1,280 +0,0 @@
/*==============================================================================
** Copyright (C) 2024-2027 WingSummer
**
** This program is free software: you can redistribute it and/or modify it under
** the terms of the GNU Affero General Public License as published by the Free
** Software Foundation, version 3.
**
** This program is distributed in the hope that it will be useful, but WITHOUT
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
** FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
** details.
**
** You should have received a copy of the GNU Affero General Public License
** along with this program. If not, see <https://www.gnu.org/licenses/>.
** =============================================================================
*/
#include "qcalltip.h"
/*!
\file qcalltip.cpp
\brief Implementation of the QCallTip class
*/
#include <QMouseEvent>
#include <QPainter>
#include <QToolTip>
/*!
\class QCallTip
\brief A widget dedicated to calltips display
*/
QCallTip::QCallTip(QWidget *p) : QWidget(p), m_index(0) {
setCursor(Qt::ArrowCursor);
setFocusPolicy(Qt::StrongFocus);
setAttribute(Qt::WA_DeleteOnClose);
auto palette = QToolTip::palette();
_background = palette.color(QPalette::ToolTipBase);
_foreground = palette.color(QPalette::ToolTipText);
_borderColor = palette.color(QPalette::Shadow);
}
QCallTip::~QCallTip() {}
QStringList QCallTip::tips() const { return m_tips; }
void QCallTip::setTips(const QStringList &l) {
m_tips = l;
m_index = 0;
}
static int arrowWidth = 14;
void QCallTip::paintEvent(QPaintEvent *e) {
Q_UNUSED(e)
QPainter p(this);
p.setOpacity(_opacity);
QFontMetrics fm = fontMetrics();
m_up = m_down = QRect();
bool bPrev = m_index, bNext = (m_index + 1) < m_tips.count();
int offset = 3, whalf = arrowWidth / 2 - 3; //, hhalf = height() / 2;
QRect bg(0, 0, fm.horizontalAdvance(m_tips.at(m_index)) + 6, fm.height());
if (bPrev) {
bg.setWidth(bg.width() + arrowWidth);
}
if (bNext) {
bg.setWidth(bg.width() + arrowWidth);
}
p.fillRect(bg, _background);
// p.drawRect(bg);
p.save();
p.setPen(_borderColor);
p.drawLine(0, height() - 1, bg.width() - 1, height() - 1);
p.drawLine(bg.width() - 1, height() - 1, bg.width() - 1, 0);
p.drawLine(0, height() - 1, 0, 0);
p.drawLine(0, 0, bg.width() - 1, 0);
int top = height() / 3, bottom = height() - height() / 3;
if (bPrev) {
int x = offset + arrowWidth / 2 - 1;
QPoint pts[] = {QPoint(x - whalf, bottom), QPoint(x + whalf, bottom),
QPoint(x, top)};
p.drawPolygon(pts, sizeof(pts) / sizeof(QPoint), Qt::WindingFill);
m_up = QRect(offset, 0, offset + arrowWidth, height());
offset += arrowWidth;
}
if (bNext) {
int x = offset + arrowWidth / 2 - 1;
QPoint pts[] = {QPoint(x - whalf, top), QPoint(x + whalf, top),
QPoint(x, bottom)};
p.drawPolygon(pts, sizeof(pts) / sizeof(QPoint), Qt::WindingFill);
m_down = QRect(offset, 0, offset + arrowWidth, height());
offset += arrowWidth;
}
p.setPen(_foreground);
p.drawText(offset, fm.ascent() + 2, m_tips.at(m_index));
p.restore();
setFixedSize(bg.size() + QSize(1, 1));
}
void QCallTip::keyPressEvent(QKeyEvent *e) {
if (e->modifiers() &
(Qt::ControlModifier | Qt::AltModifier | Qt::MetaModifier)) {
close();
if (parentWidget())
parentWidget()->setFocus();
e->ignore();
return;
}
QString text = e->text();
switch (e->key()) {
case Qt::Key_Escape:
close();
if (parentWidget())
parentWidget()->setFocus();
e->accept();
break;
case Qt::Key_Enter:
case Qt::Key_Return:
case Qt::Key_Tab:
// hide();
close();
if (parentWidget())
parentWidget()->setFocus();
e->ignore();
break;
case Qt::Key_Up:
if (m_index)
--m_index;
e->accept();
update();
break;
case Qt::Key_Down:
if ((m_index + 1) < m_tips.count())
++m_index;
e->accept();
update();
break;
case Qt::Key_Backspace:
close();
if (parentWidget())
parentWidget()->setFocus();
e->ignore();
break;
case Qt::Key_Shift:
case Qt::Key_Alt:
case Qt::Key_Control:
e->ignore();
break;
default:
if (text.size() && text.at(0).isPrint()) {
} else {
close();
if (parentWidget())
parentWidget()->setFocus();
}
e->ignore();
break;
}
}
void QCallTip::focusInEvent(QFocusEvent *e) { QWidget::focusInEvent(e); }
void QCallTip::focusOutEvent(QFocusEvent *e) {
QWidget::focusOutEvent(e);
close();
if (parentWidget())
parentWidget()->setFocus();
}
void QCallTip::mousePressEvent(QMouseEvent *e) {
if (m_index && m_up.isValid() && m_up.contains(e->pos())) {
--m_index;
} else if (((m_index + 1) < m_tips.count()) && m_down.isValid() &&
m_down.contains(e->pos())) {
++m_index;
} else {
close();
if (parentWidget())
parentWidget()->setFocus();
}
e->accept();
update();
}
void QCallTip::mouseReleaseEvent(QMouseEvent *e) { e->accept(); }
qreal QCallTip::opacity() const { return _opacity; }
void QCallTip::setOpacity(qreal newOpacity) {
if (qFuzzyCompare(_opacity, newOpacity))
return;
_opacity = newOpacity;
emit opacityChanged();
}
QColor QCallTip::borderColor() const { return _borderColor; }
void QCallTip::setBorderColor(const QColor &newBorderColor) {
if (_borderColor == newBorderColor)
return;
_borderColor = newBorderColor;
emit borderColorChanged();
}
QColor QCallTip::foreground() const { return _foreground; }
void QCallTip::setForeground(const QColor &newForeground) {
if (_foreground == newForeground)
return;
_foreground = newForeground;
emit foregroundChanged();
}
QColor QCallTip::background() const { return _background; }
void QCallTip::setBackground(const QColor &newBackground) {
if (_background == newBackground)
return;
_background = newBackground;
emit backgroundChanged();
}

View File

@ -1,88 +0,0 @@
/*==============================================================================
** Copyright (C) 2024-2027 WingSummer
**
** This program is free software: you can redistribute it and/or modify it under
** the terms of the GNU Affero General Public License as published by the Free
** Software Foundation, version 3.
**
** This program is distributed in the hope that it will be useful, but WITHOUT
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
** FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
** details.
**
** You should have received a copy of the GNU Affero General Public License
** along with this program. If not, see <https://www.gnu.org/licenses/>.
** =============================================================================
*/
#ifndef _QCALL_TIP_H_
#define _QCALL_TIP_H_
#include "qce-config.h"
/*!
\file qcalltip.h
\brief Definition of the QCallTip class
*/
#include <QWidget>
class QCE_EXPORT QCallTip : public QWidget {
Q_OBJECT
Q_PROPERTY(QColor background READ background WRITE setBackground NOTIFY
backgroundChanged FINAL)
Q_PROPERTY(QColor foreground READ foreground WRITE setForeground NOTIFY
foregroundChanged FINAL)
Q_PROPERTY(QColor borderColor READ borderColor WRITE setBorderColor NOTIFY
borderColorChanged FINAL)
Q_PROPERTY(
qreal opacity READ opacity WRITE setOpacity NOTIFY opacityChanged FINAL)
public:
QCallTip(QWidget *p = nullptr);
virtual ~QCallTip();
QStringList tips() const;
void setTips(const QStringList &l);
QColor background() const;
void setBackground(const QColor &newBackground);
QColor foreground() const;
void setForeground(const QColor &newForeground);
QColor borderColor() const;
void setBorderColor(const QColor &newBorderColor);
qreal opacity() const;
void setOpacity(qreal newOpacity);
signals:
void backgroundChanged();
void foregroundChanged();
void borderColorChanged();
void opacityChanged();
protected:
virtual void paintEvent(QPaintEvent *e) override;
virtual void keyPressEvent(QKeyEvent *e) override;
virtual void focusInEvent(QFocusEvent *e) override;
virtual void focusOutEvent(QFocusEvent *e) override;
virtual void mousePressEvent(QMouseEvent *e) override;
virtual void mouseReleaseEvent(QMouseEvent *e) override;
private:
int m_index;
QStringList m_tips;
QRect m_up, m_down;
QColor _background;
QColor _foreground;
QColor _borderColor;
qreal _opacity = 1;
};
#endif

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -18,7 +18,6 @@
#include "ascompletion.h"
#include "qasparser.h"
#include "qcalltip.h"
#include "qdocumentcursor.h"
#include "qdocumentline.h"
@ -33,8 +32,9 @@
#include "control/qcodecompletionwidget.h"
Q_GLOBAL_STATIC_WITH_ARGS(QByteArray, DOT_TRIGGER, ("."))
Q_GLOBAL_STATIC_WITH_ARGS(QByteArray, SEMI_COLON_TRIGGER, ("::"))
Q_GLOBAL_STATIC_WITH_ARGS(QByteArray, DBL_COLON_TRIGGER, ("::"))
Q_GLOBAL_STATIC_WITH_ARGS(QByteArray, LEFT_PARE_TRIGGER, ("("))
Q_GLOBAL_STATIC_WITH_ARGS(QByteArray, SEMI_COLON_TRIGGER, (";"))
AsCompletion::AsCompletion(asIScriptEngine *engine, QObject *p)
: QCodeCompletionEngine(p), parser(engine), _engine(engine),
@ -42,10 +42,12 @@ AsCompletion::AsCompletion(asIScriptEngine *engine, QObject *p)
Q_ASSERT(engine);
addTrigger(*DOT_TRIGGER);
addTrigger(*SEMI_COLON_TRIGGER);
addTrigger(*DBL_COLON_TRIGGER);
// unleash the power of call tips
addTrigger(*LEFT_PARE_TRIGGER);
// clear the tips
addTrigger(*SEMI_COLON_TRIGGER);
setTrigWordLen(3);
}
@ -58,6 +60,9 @@ QCodeCompletionEngine *AsCompletion::clone() {
for (auto &t : triggers())
e->addTrigger(t);
connect(e, &AsCompletion::onFunctionTip, this,
&AsCompletion::onFunctionTip);
emit cloned(e);
return e;
@ -77,6 +82,12 @@ QStringList AsCompletion::extensions() const {
void AsCompletion::complete(const QDocumentCursor &c, const QString &trigger) {
auto txt = c.line().text();
if (txt.isEmpty()) {
emit onFunctionTip(this, {});
return;
}
if (trigger == *SEMI_COLON_TRIGGER) {
emit onFunctionTip(this, {});
return;
}
@ -97,8 +108,14 @@ void AsCompletion::complete(const QDocumentCursor &c, const QString &trigger) {
auto code = txt.mid(off, c.columnNumber() - off).toUtf8();
auto p = code.data();
auto len = code.length();
if (len < trigWordLen()) {
emit onFunctionTip(this, {});
pPopup->hide();
return;
}
auto p = code.data();
auto end = p + len;
struct Token {
@ -113,6 +130,11 @@ void AsCompletion::complete(const QDocumentCursor &c, const QString &trigger) {
for (; p < end;) {
asUINT tokenLen = 0;
auto tt = _engine->ParseToken(p, len, &tokenLen);
if (tt == asTC_WHITESPACE) {
p += tokenLen;
pos += tokenLen;
continue;
}
Token token;
token.pos = pos;
token.type = tt;
@ -122,143 +144,121 @@ void AsCompletion::complete(const QDocumentCursor &c, const QString &trigger) {
pos += tokenLen;
}
auto r =
std::find_if(tokens.rbegin(), tokens.rend(), [](const Token &token) {
return token.type != asTC_WHITESPACE;
});
if (r == tokens.rend()) {
return;
}
auto getNamespace = [](const QVector<Token> &tokens) -> QByteArrayList {
auto rbegin = tokens.rbegin();
auto rend = tokens.rend();
QByteArrayList nss;
bool semiFlag = true;
auto p = rbegin;
for (; p != rend; ++p) {
if (semiFlag) {
if (p->type != asTC_IDENTIFIER) {
break;
} else {
nss.prepend(p->content);
semiFlag = false;
}
} else {
if (p->content != *DBL_COLON_TRIGGER) {
break;
}
semiFlag = true;
}
}
return nss;
};
QByteArray fn;
QList<QCodeNode *> nodes;
QCodeCompletionWidget::Filter filter =
QCodeCompletionWidget::FilterFlag::KeepAll;
auto &_headerNodes = parser.headerNodes();
QFlags<QCodeCompletionWidget::FilterFlag> filter(
QCodeCompletionWidget::FilterFlag::Public |
QCodeCompletionWidget::FilterFlag::KeepAllFunctions |
QCodeCompletionWidget::FilterFlag::KeepAllSub);
if (trigger.isEmpty() && trigWordLen() <= r->content.length()) {
auto eb = tokens.back();
if (eb.type == asTC_KEYWORD) {
if (tokens.isEmpty()) {
pPopup->hide();
return;
}
QString prefix;
auto etoken = tokens.back();
// if trigger is empty, it's making editing
if (trigger.isEmpty()) {
// it can not be any trigger, so take the last as prefix
prefix = etoken.content;
tokens.removeLast();
if (tokens.isEmpty()) {
applyEmptyNsNode(nodes);
} else {
etoken = tokens.back(); // checking later
}
}
if (nodes.isEmpty()) {
if (etoken.type == asTC_KEYWORD) {
// only support these
if (eb.content == *SEMI_COLON_TRIGGER) {
complete(c, *SEMI_COLON_TRIGGER);
} else if (eb.content == *DOT_TRIGGER) {
complete(c, *DOT_TRIGGER);
auto cur = c;
cur.setColumnNumber(off + etoken.pos);
if (etoken.content == *DBL_COLON_TRIGGER) {
complete(cur, *DBL_COLON_TRIGGER);
pPopup->setCursor(c);
pPopup->setPrefix(prefix);
return;
} else if (etoken.content == *DOT_TRIGGER) {
complete(cur, *DOT_TRIGGER);
pPopup->setCursor(c);
pPopup->setPrefix(prefix);
return;
} else {
pPopup->hide();
}
return;
} else if (eb.type == asTC_IDENTIFIER) {
fn = eb.content;
// completion for a.b.c or a::b.c or a::b::c.d or ::a::b.c
// first validate
auto rbegin = tokens.rbegin();
auto rend = tokens.rend();
rbegin++;
if (rbegin == rend) {
applyEmptyNsNode(nodes);
} else {
if (rbegin->content == SEMI_COLON_TRIGGER) {
QByteArrayList nss;
bool semiFlag = true;
auto p = rbegin;
for (; p != rend; ++p) {
if (semiFlag) {
if (p->type != asTC_IDENTIFIER) {
break;
} else {
nss.prepend(p->content);
semiFlag = false;
}
} else {
if (p->content != SEMI_COLON_TRIGGER) {
break;
}
semiFlag = true;
}
}
}
} else if (etoken.type != asTC_IDENTIFIER) {
pPopup->hide();
return;
}
nodes = lookupNamespace(nss);
if (nodes.isEmpty()) {
auto &_headerNodes = parser.headerNodes();
if (etoken.content.length() >= trigWordLen()) {
// completion for a.b.c or a::b.c or a::b::c.d or ::a::b.c
if (trigger == *DBL_COLON_TRIGGER) {
auto ns = getNamespace(tokens);
auto idx = tokens.length() - ns.length() * 2;
if (idx >= 0) {
if (tokens.at(idx).content == *DOT_TRIGGER) {
pPopup->hide();
return;
}
} else if (rbegin->content == DOT_TRIGGER) {
// TODO only PR
} else {
}
nodes = lookupNamespace(ns);
if (nodes.isEmpty()) {
return;
}
} else if (trigger == *LEFT_PARE_TRIGGER) {
// the first is function name, an identifier
fn = etoken.content;
tokens.removeLast();
if (!tokens.isEmpty()) {
if (tokens.back().content == *DBL_COLON_TRIGGER) {
tokens.removeLast();
}
}
auto ns = getNamespace(tokens);
auto idx = tokens.length() - ns.length() * 2 + 1;
if (idx >= 0 && idx < tokens.length()) {
if (tokens.at(idx).content == *DOT_TRIGGER) {
pPopup->hide();
return;
}
}
nodes = lookupNamespace(ns);
if (nodes.isEmpty()) {
applyEmptyNsNode(nodes);
}
}
} else {
return;
}
// pPopup->setTemporaryNodes(temp);
pPopup->setPrefix(fn);
pPopup->setFilter(filter);
pPopup->setCompletions(nodes);
pPopup->popup();
} else {
if (trigger == *SEMI_COLON_TRIGGER) {
auto rbegin = tokens.rbegin();
auto rend = tokens.rend();
QByteArrayList nss;
bool semiFlag = true;
auto p = rbegin;
for (; p != rend; ++p) {
if (semiFlag) {
if (p->type != asTC_IDENTIFIER) {
break;
} else {
nss.prepend(p->content);
semiFlag = false;
}
} else {
if (p->content != *SEMI_COLON_TRIGGER) {
break;
}
semiFlag = true;
}
}
nodes = lookupNamespace(nss);
if (nodes.isEmpty()) {
return;
}
} else if (trigger == *LEFT_PARE_TRIGGER) {
if (r != tokens.rend()) {
auto pr = std::next(r);
if (pr != tokens.rend()) {
if (pr->content == *SEMI_COLON_TRIGGER) {
if (pr != tokens.rend()) {
auto prr = std::next(pr);
auto ns = prr->content;
if (prr->type == asTC_IDENTIFIER) {
for (auto &n : _headerNodes) {
auto name = n->qualifiedName();
if (name == ns) {
nodes << n;
break;
}
}
} else {
return;
}
}
}
}
}
}
if (nodes.count()) {
if (trigger == *LEFT_PARE_TRIGGER) {
QStringList tips;
// qDebug("fn %s", fn.constData());
QString tip;
for (auto &n : nodes) {
for (auto &f : n->children()) {
if (f->type() != QCodeNode::Function ||
@ -266,55 +266,34 @@ void AsCompletion::complete(const QDocumentCursor &c, const QString &trigger) {
continue;
}
auto tip =
QString::fromUtf8(f->role(QCodeNode::Arguments))
.prepend('(')
.append(')');
if (!tips.contains(tip))
tips << tip;
tip = QString::fromUtf8(f->role(QCodeNode::Return)) +
QLatin1Char(' ') +
QString::fromUtf8(f->role(QCodeNode::Name)) +
QString::fromUtf8(f->role(QCodeNode::Arguments))
.prepend('(')
.append(')');
break;
}
if (!tip.isEmpty()) {
break;
}
}
if (!tips.isEmpty()) {
QRect r = editor()->cursorRect();
QDocumentCursor cursor = editor()->cursor();
QDocumentLine line = cursor.line();
int hx = editor()->horizontalOffset(),
cx = line.cursorToX(cursor.columnNumber());
auto ct = new QCallTip(editor()->viewport());
ct->move(cx - hx, r.y() + r.height());
ct->setTips(tips);
ct->show();
ct->setFocus();
#ifdef TRACE_COMPLETION
qDebug("parsing + scoping + search + pre-display : elapsed "
"%i ms",
time.elapsed());
#endif
}
} else {
// pPopup->setTemporaryNodes(temp);
pPopup->setPrefix({});
pPopup->setFilter(QCodeCompletionWidget::Filter(filter));
pPopup->setCompletions(nodes);
#ifdef TRACE_COMPLETION
qDebug(
"parsing + scoping + search + pre-display : elapsed %i ms",
time.elapsed());
#endif
pPopup->popup();
emit onFunctionTip(this, tip);
}
} else {
// qDeleteAll(temp);
// qDebug("completion failed");
}
}
auto cur = c;
cur.movePosition(trigger.length());
pPopup->setCursor(cur);
pPopup->setPrefix(prefix);
pPopup->setFilter(filter);
pPopup->setCompletions(nodes);
if (!pPopup->isCompleting()) {
pPopup->popup();
}
}
void AsCompletion::applyEmptyNsNode(QList<QCodeNode *> &nodes) {

View File

@ -40,6 +40,9 @@ public:
QCodeCompletionWidget *codeCompletionWidget() const;
signals:
void onFunctionTip(AsCompletion *cp, const QString &content);
protected:
virtual void complete(const QDocumentCursor &c,
const QString &trigger) override;

View File

@ -13,7 +13,7 @@ LangService &LangService::instance() {
return ins;
}
void LangService::init(asIScriptEngine *engine) {
void LangService::init(ScriptingConsole *console) {
QFormatScheme *format = nullptr;
switch (SkinManager::instance().currentTheme()) {
@ -34,7 +34,25 @@ void LangService::init(asIScriptEngine *engine) {
m_language = new QLanguageFactory(format, this);
m_language->addDefinitionPath(QStringLiteral(":/qcodeedit"));
auto engine = console->machine()->engine();
_completion = new AsCompletion(engine, this);
connect(_completion, &AsCompletion::onFunctionTip, this,
[this, console](AsCompletion *cp, const QString &fn) {
QString header;
if (!fn.isEmpty()) {
header = QStringLiteral("<font color=\"gold\">") +
tr("AutoComplete:") + QStringLiteral("</font>");
}
auto editor = cp->editor();
if (editor == console) {
emit onConsoleTip(header + QStringLiteral("<b>") + fn +
QStringLiteral("</b>"));
} else {
emit onScriptEditorTip(header + QStringLiteral("<b>") + fn +
QStringLiteral("</b>"));
}
});
m_language->addCompletionEngine(_completion);
initAdditionalFormatScheme();

View File

@ -2,19 +2,18 @@
#define LANGSERVICE_H
#include "class/ascompletion.h"
#include "control/scriptingconsole.h"
#include "qlanguagefactory.h"
#include "angelscript.h"
#include <QObject>
class LangService : QObject {
class LangService : public QObject {
Q_OBJECT
public:
static LangService &instance();
void init(asIScriptEngine *engine);
void init(ScriptingConsole *console);
QLanguageFactory *languageFactory() const;
@ -26,6 +25,10 @@ public:
const QString defaultSchemeName() const;
signals:
void onConsoleTip(const QString &tip);
void onScriptEditorTip(const QString &tip);
private:
LangService();
~LangService();

View File

@ -92,7 +92,6 @@ private:
private:
asIScriptEngine *_engine;
// QScopedPointer<asCScriptCode> m_code;
QList<QCodeNode *> _nodes;
QHash<QString, QCodeNode *> _buffer;

View File

@ -31,8 +31,10 @@
#include <QRegularExpression>
#include <QScrollBar>
constexpr auto PADDING = 3;
QCodeCompletionWidget::QCodeCompletionWidget(QEditor *p)
: QListView(nullptr), offset(0) {
: QListView(nullptr), offset(PADDING) {
// setWindowFlags(Qt::FramelessWindowHint | Qt::SubWindow);
setBatchSize(10);
setMovement(Static);
@ -111,7 +113,13 @@ void QCodeCompletionWidget::adjustGeometry() {
setGeometry(x, y + lrect.height() - h, w, h);
}
offset = 0;
offset = PADDING;
}
QDocumentCursor QCodeCompletionWidget::cursor() const { return _cur; }
void QCodeCompletionWidget::setCursor(const QDocumentCursor &newCur) {
_cur = newCur;
}
QEditor *QCodeCompletionWidget::editor() const {
@ -123,7 +131,6 @@ void QCodeCompletionWidget::setEditor(QEditor *e) {
if (e) {
setParent(e->viewport());
}
// QObject::setParent(pEditor);
}
bool QCodeCompletionWidget::hasEntries() const { return pModel->rowCount(); }
@ -219,8 +226,15 @@ void QCodeCompletionWidget::complete(const QModelIndex &index) {
static QRegularExpression re("(\\bconst\\s*)?(=\\s*0)?$");
txt.remove(re);
if (prefix.length() && txt.startsWith(prefix))
txt.remove(0, prefix.length());
if (prefix.length() &&
prefix.compare(txt.left(prefix.length()), Qt::CaseInsensitive) == 0) {
if (_cur.isValid()) {
_cur.movePosition(prefix.length(),
QDocumentCursor::PreviousCharacter,
QDocumentCursor::KeepAnchor);
_cur.removeSelectedText();
}
}
e->write(txt);
@ -228,6 +242,9 @@ void QCodeCompletionWidget::complete(const QModelIndex &index) {
auto c = e->cursor();
c.movePosition(1, QDocumentCursor::PreviousCharacter);
e->setCursor(c);
auto cc = c;
cc.movePosition(1, QDocumentCursor::PreviousCharacter);
e->completionEngine()->complete(cc, QStringLiteral("("));
}
e->setFocus();
@ -330,7 +347,7 @@ void QCodeCompletionWidget::keyPressEvent(QKeyEvent *e) {
if (prefix.length()) {
prefix.chop(1);
pModel->setPrefix(prefix);
offset = -1;
offset = -1 + PADDING;
changed();
} else {
hide();
@ -353,7 +370,7 @@ void QCodeCompletionWidget::keyPressEvent(QKeyEvent *e) {
if (text.length() && text.at(0).isPrint() && pModel->rowCount()) {
pModel->setPrefix(prefix + text);
offset = text.length();
offset = text.length() + PADDING;
changed();
} else {
hide();
@ -384,11 +401,11 @@ void QCodeCompletionModel::clear() {
}
QString QCodeCompletionModel::prefix() const {
return QString::fromLocal8Bit(m_prefix);
return QString::fromUtf8(m_prefix);
}
void QCodeCompletionModel::setPrefix(const QString &prefix) {
m_prefix = prefix.toLocal8Bit();
m_prefix = prefix.toUtf8();
emit prefixChanged(prefix);
update();
}
@ -486,9 +503,9 @@ bool QCodeCompletionModel::match(QCodeNode *n,
QByteArray bcxt = n->parent()->qualifiedName(),
bnn = n->role(QCodeNode::Name);
if (!n || (prefix.length() && (!bnn.startsWith(prefix)))) {
// qDebug("prefix mismatch : %s not found in %s", qPrintable(prefix),
// bnn.constData());
if (!n ||
(!prefix.isEmpty() && (prefix.compare(bnn.left(prefix.length()),
Qt::CaseInsensitive) != 0))) {
return false;
}
@ -504,17 +521,6 @@ bool QCodeCompletionModel::match(QCodeNode *n,
int cxt_off = qMax(0, bcxt.lastIndexOf("::"));
/*
qDebug("filtering %s (in %s) according to %s (v:%i, s:%i, q:%i)",
name,
ctxt,
qPrintable(QString::number(filter, 2)),
visibility,
specifiers,
qualifiers
);
*/
if ((((type == QCodeNode::Class) || (type == QCodeNode::Typedef)) &&
!(filter & QCodeCompletionWidget::KeepSubTypes)) ||
((type == QCodeNode::Enum) &&
@ -546,12 +552,8 @@ bool QCodeCompletionModel::match(QCodeNode *n,
!(filter & QCodeCompletionWidget::KeepCtor)) ||
((*name == '~') && !qstrcmp(name + 1, ctxt) &&
!(filter & QCodeCompletionWidget::KeepDtor))))) {
// qDebug("node %s mismatched display conditions [Ox%x]", name,
// int(filter));
return false;
}
// qDebug("node %s matched display conditions", name);
return true;
}

View File

@ -80,6 +80,9 @@ public:
bool isCompleting() const;
QDocumentCursor cursor() const;
void setCursor(const QDocumentCursor &newCur);
public slots:
void popup();
void clear();
@ -104,6 +107,7 @@ private:
QCodeCompletionModel *pModel;
QPointer<QObject> pEditor;
QList<QCodeNode *> m_temps;
QDocumentCursor _cur;
bool _completing = false;
};

View File

@ -38,6 +38,7 @@
#include "class/winginputdialog.h"
#include "class/wingmessagebox.h"
#include "class/wingupdater.h"
#include "control/qcodecompletionwidget.h"
#include "control/toast.h"
#include "driverselectordialog.h"
#include "encodingdialog.h"
@ -231,8 +232,10 @@ MainWindow::MainWindow(SplashDialog *splash) : FramelessMainWindow() {
if (splash)
splash->setInfoText(tr("SetupScriptService"));
auto &langins = LangService::instance();
langins.init(m_scriptConsole->machine()->engine());
langins.init(m_scriptConsole);
langins.applyLanguageSerivce(m_scriptConsole);
connect(&langins, &LangService::onConsoleTip, this,
&MainWindow::showStatus);
m_scriptConsole->initOutput();
m_scriptConsole->machine()->setInsteadFoundDisabled(true);
@ -932,6 +935,17 @@ MainWindow::buildUpScriptConsoleDock(ads::CDockManager *dock,
ads::CDockAreaWidget *areaw) {
m_scriptConsole = new ScriptingConsole(this);
connect(m_scriptConsole, &ScriptingConsole::consoleCommand, this,
[this] { showStatus({}); });
connect(m_scriptConsole, &ScriptingConsole::inputTimeOuted, this, [this] {
auto e =
qobject_cast<AsCompletion *>(m_scriptConsole->completionEngine());
if (e && e->codeCompletionWidget()->isVisible()) {
return;
}
showStatus({});
});
auto dw = buildDockWidget(dock, QStringLiteral("ScriptConsole"),
tr("ScriptConsole"), m_scriptConsole);
return dock->addDockWidget(area, dw, areaw);

View File

@ -101,6 +101,9 @@ ScriptingDialog::ScriptingDialog(QWidget *parent)
m_dock->restoreState(set.scriptDockLayout());
_savedLayout = set.scriptDockLayout();
connect(&LangService::instance(), &LangService::onScriptEditorTip, m_status,
[this](const QString &message) { m_status->showMessage(message); });
this->setUpdatesEnabled(true);
}