WingHexExplorer2/src/class/ascompletion.cpp

213 lines
6.1 KiB
C++

/*==============================================================================
** 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 "ascompletion.h"
#include "qasparser.h"
#include "qcalltip.h"
#include "qdocumentcursor.h"
#include "qdocumentline.h"
#include <QDir>
#include <QLibraryInfo>
#include <QQueue>
#include <QTextStream>
#include <QTime>
#include <QTimer>
#include <QtDebug>
#include "control/qcodecompletionwidget.h"
const auto DOT_TRIGGER = QStringLiteral(".");
const auto SEMI_COLON_TRIGGER = QStringLiteral("::");
const auto LEFT_PARE_TRIGGER = QStringLiteral("(");
AsCompletion::AsCompletion(asIScriptEngine *engine, QObject *p)
: QCodeCompletionEngine(p), parser(engine), _engine(engine),
pPopup(new QCodeCompletionWidget()) {
Q_ASSERT(engine);
addTrigger(DOT_TRIGGER);
addTrigger(SEMI_COLON_TRIGGER);
// unleash the power of call tips
addTrigger(LEFT_PARE_TRIGGER);
}
AsCompletion::~AsCompletion() {}
QCodeCompletionEngine *AsCompletion::clone() {
AsCompletion *e = new AsCompletion(_engine, pPopup->editor());
for (auto &t : triggers())
e->addTrigger(t);
emit cloned(e);
return e;
}
QString AsCompletion::language() const { return "AngelScript"; }
QStringList AsCompletion::extensions() const {
QStringList l;
l << "as"
<< "angelscript";
return l;
}
void AsCompletion::complete(const QDocumentCursor &c, const QString &trigger) {
// TODO
// auto codes = c.document()->text(true, false);
// parser.parse(codes, this->editor()->fileName());
// QList<QCodeNode *> nodes = parser.codeNodes();
auto txt = c.line().text().left(c.columnNumber());
auto code = txt.toUtf8();
auto p = code.data();
auto len = code.length();
auto end = p + len;
struct Token {
asETokenClass type;
QByteArray content;
};
QVector<Token> tokens;
for (; p < end;) {
asUINT tokenLen = 0;
auto tt = _engine->ParseToken(p, len, &tokenLen);
Token token;
token.type = tt;
token.content = QByteArray(p, tokenLen);
tokens << token;
p += tokenLen;
}
QByteArray fn;
QList<QCodeNode *> nodes;
auto r =
std::find_if(tokens.rbegin(), tokens.rend(), [](const Token &token) {
return token.type == asTC_IDENTIFIER || token.type == asTC_VALUE;
});
if (r == tokens.rend()) {
return;
}
auto &_headerNodes = parser.headerNodes();
fn = r->content;
if (trigger == SEMI_COLON_TRIGGER) {
for (auto &n : _headerNodes) {
auto name = n->qualifiedName();
if (name == fn) {
nodes << n;
}
}
} else if (trigger == LEFT_PARE_TRIGGER) {
if (r != tokens.rend()) {
auto pr = std::next(r);
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;
}
}
} else {
return;
}
}
}
}
}
// TODO
// QList<QCodeNode *> temp; // internal CodeNodes
int filter = QCodeCompletionWidget::FilterFlag::KeepAll;
if (nodes.count()) {
if (trigger == "(") {
QStringList tips;
// qDebug("fn %s", fn.constData());
for (auto &n : nodes) {
for (auto &f : n->children()) {
if (f->type() != QCodeNode::Function ||
f->role(QCodeNode::Name) != fn)
continue;
auto tip = QString::fromUtf8(f->role(QCodeNode::Arguments))
.prepend('(')
.append(')');
if (!tips.contains(tip))
tips << tip;
}
}
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->setFilter(QCodeCompletionWidget::Filter(filter));
pPopup->setCompletions(nodes);
#ifdef TRACE_COMPLETION
qDebug("parsing + scoping + search + pre-display : elapsed %i ms",
time.elapsed());
#endif
pPopup->popup();
}
} else {
// qDeleteAll(temp);
qDebug("completion failed");
}
}
void AsCompletion::setEditor(QEditor *e) {
QCodeCompletionEngine::setEditor(e);
pPopup->setEditor(e);
}