Compare commits
4 Commits
ef8bb9aa3a
...
d8069aedde
Author | SHA1 | Date |
---|---|---|
|
d8069aedde | |
|
cf3d4da8e8 | |
|
2ee3051a7d | |
|
f59755e3f0 |
|
@ -109,6 +109,15 @@ QString QConsoleWidget::getCommandLine() {
|
|||
return code;
|
||||
}
|
||||
|
||||
void QConsoleWidget::paste() {
|
||||
QTextCursor textCursor = this->textCursor();
|
||||
const QMimeData *const clipboard = QApplication::clipboard()->mimeData();
|
||||
const QString text = clipboard->text();
|
||||
if (!text.isNull()) {
|
||||
textCursor.insertText(text, channelCharFormat(StandardInput));
|
||||
}
|
||||
}
|
||||
|
||||
void QConsoleWidget::handleReturnKey() {
|
||||
QString code = getCommandLine();
|
||||
|
||||
|
@ -189,12 +198,7 @@ void QConsoleWidget::keyPressEvent(QKeyEvent *e) {
|
|||
// Allow paste only if the selection is in the interactive area ...
|
||||
if (e->key() == Qt::Key_V && e->modifiers() == Qt::ControlModifier) {
|
||||
if (selectionInEditZone || isCursorInEditZone()) {
|
||||
const QMimeData *const clipboard =
|
||||
QApplication::clipboard()->mimeData();
|
||||
const QString text = clipboard->text();
|
||||
if (!text.isNull()) {
|
||||
textCursor.insertText(text, channelCharFormat(StandardInput));
|
||||
}
|
||||
paste();
|
||||
}
|
||||
|
||||
e->accept();
|
||||
|
@ -361,14 +365,14 @@ void QConsoleWidget::replaceCommandLine(const QString &str) {
|
|||
}
|
||||
|
||||
QString QConsoleWidget::currentCommandLine() const {
|
||||
// Select the text after the last command prompt ...
|
||||
QTextCursor textCursor = this->textCursor();
|
||||
textCursor.movePosition(QTextCursor::End);
|
||||
textCursor.setPosition(inpos_, QTextCursor::KeepAnchor);
|
||||
|
||||
return textCursor.selectedText();
|
||||
}
|
||||
|
||||
int QConsoleWidget::currentHeaderPos() const { return inpos_; }
|
||||
|
||||
void QConsoleWidget::write(const QString &message, const QTextCharFormat &fmt) {
|
||||
QTextCharFormat currfmt = currentCharFormat();
|
||||
QTextCursor tc = textCursor();
|
||||
|
|
|
@ -77,6 +77,10 @@ public:
|
|||
// get the current command line
|
||||
QString getCommandLine();
|
||||
|
||||
virtual void paste();
|
||||
|
||||
int currentHeaderPos() const;
|
||||
|
||||
public slots:
|
||||
|
||||
// write to StandardOutput
|
||||
|
@ -94,6 +98,7 @@ signals:
|
|||
protected:
|
||||
bool canPaste() const;
|
||||
bool canCut() const;
|
||||
|
||||
virtual void handleReturnKey();
|
||||
virtual void handleTabKey();
|
||||
// reimp QPlainTextEdit functions
|
||||
|
@ -112,7 +117,6 @@ protected:
|
|||
static History history_;
|
||||
ConsoleMode mode_;
|
||||
int inpos_;
|
||||
QString currentMultiLineCode_;
|
||||
QConsoleIODevice *iodevice_;
|
||||
QTextCharFormat chanFormat_[nConsoleChannels];
|
||||
};
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 4307a2552e52d9f7e5c8fc2bd1d3148748e968f9
|
||||
Subproject commit e5cef2dcf126037ffdc57a5aaf6a3b1d3f4c70ae
|
|
@ -33,7 +33,8 @@ QtLocalPeer::QtLocalPeer(QObject *parent, const QString &appId)
|
|||
#endif
|
||||
prefix = id.section(QLatin1Char('/'), -1);
|
||||
}
|
||||
prefix.remove(QRegularExpression("[^a-zA-Z]"));
|
||||
static QRegularExpression regex("[^a-zA-Z]");
|
||||
prefix.remove(regex);
|
||||
prefix.truncate(6);
|
||||
|
||||
QByteArray idc = id.toUtf8();
|
||||
|
@ -69,6 +70,8 @@ QtLocalPeer::QtLocalPeer(QObject *parent, const QString &appId)
|
|||
lockFile.open(QIODevice::ReadWrite);
|
||||
}
|
||||
|
||||
QtLocalPeer::~QtLocalPeer() { server->close(); }
|
||||
|
||||
bool QtLocalPeer::isClient() {
|
||||
if (lockFile.isLocked())
|
||||
return false;
|
||||
|
|
|
@ -15,6 +15,8 @@ class QtLocalPeer : public QObject {
|
|||
|
||||
public:
|
||||
QtLocalPeer(QObject *parent = nullptr, const QString &appId = QString());
|
||||
~QtLocalPeer();
|
||||
|
||||
bool isClient();
|
||||
bool sendMessage(const QByteArray &uMsg, int timeout);
|
||||
QString applicationId() const { return id; }
|
||||
|
|
|
@ -8,7 +8,7 @@ set(CMAKE_AUTORCC ON)
|
|||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(PROJECT_VERSION "2.2.1")
|
||||
set(PROJECT_VERSION "2.2.2")
|
||||
|
||||
find_package(
|
||||
QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets Network Concurrent
|
||||
|
@ -253,8 +253,6 @@ set(CLASS_SRC
|
|||
src/class/ascompletion.h
|
||||
src/class/asbuilder.h
|
||||
src/class/asbuilder.cpp
|
||||
src/class/ascontextmgr.h
|
||||
src/class/ascontextmgr.cpp
|
||||
src/class/clangformatmanager.h
|
||||
src/class/clangformatmanager.cpp
|
||||
src/class/aspreprocesser.h
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"Id": "WingAngelAPI",
|
||||
"Author": "wingsummer",
|
||||
"Version": "2.1.0",
|
||||
"Version": "2.2.2",
|
||||
"Vendor": "WingCloudStudio",
|
||||
"License": "AGPL-3.0",
|
||||
"Url": "https://github.com/Wing-summer/WingHexExplorer2"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"Id" : "WingCStruct",
|
||||
"Version" : "0.0.1",
|
||||
"Version" : "0.0.2",
|
||||
"Vendor" : "WingCloudStudio",
|
||||
"Author" : "wingsummer",
|
||||
"License" : "AGPL-v3.0",
|
||||
|
|
|
@ -69,7 +69,7 @@ QString AngelObjString::dictionaryToString(void *obj, asDebugger *dbg) {
|
|||
|
||||
auto engine = dic->GetEngine();
|
||||
|
||||
s << " [";
|
||||
s << " {";
|
||||
asUINT n = 0;
|
||||
for (CScriptDictionary::CIterator it = dic->begin(); it != dic->end();
|
||||
it++, n++) {
|
||||
|
@ -88,7 +88,7 @@ QString AngelObjString::dictionaryToString(void *obj, asDebugger *dbg) {
|
|||
if (n < dic->GetSize() - 1)
|
||||
s << ", ";
|
||||
}
|
||||
s << "]";
|
||||
s << "}";
|
||||
|
||||
return str;
|
||||
}
|
||||
|
|
|
@ -45,6 +45,8 @@ AppManager::AppManager(int &argc, char *argv[])
|
|||
: QtSingleApplication(argc, argv) {
|
||||
ASSERT_SINGLETON;
|
||||
|
||||
_instance = this;
|
||||
|
||||
LanguageManager::instance();
|
||||
InspectQtLogHelper::instance().init();
|
||||
CrashHandler::instance().init();
|
||||
|
@ -102,6 +104,8 @@ AppManager::AppManager(int &argc, char *argv[])
|
|||
his.add(cmd);
|
||||
}
|
||||
|
||||
_timer.start();
|
||||
|
||||
_w = new MainWindow(splash);
|
||||
|
||||
setActivationWindow(_w);
|
||||
|
@ -133,7 +137,6 @@ AppManager::AppManager(int &argc, char *argv[])
|
|||
|
||||
connect(_w, &MainWindow::closed, this,
|
||||
[]() { AppManager::instance()->exit(); });
|
||||
_instance = this;
|
||||
|
||||
if (splash)
|
||||
splash->close();
|
||||
|
@ -142,9 +145,9 @@ AppManager::AppManager(int &argc, char *argv[])
|
|||
AppManager::~AppManager() {
|
||||
InspectQtLogHelper::instance().destory();
|
||||
ClangFormatManager::instance().save();
|
||||
// CommandHistoryManager::save(QConsoleWidget::history().strings_);
|
||||
CommandHistoryManager::save(QConsoleWidget::history().strings_);
|
||||
|
||||
_w->deleteLater();
|
||||
delete _w;
|
||||
_w = nullptr;
|
||||
}
|
||||
|
||||
|
@ -152,6 +155,8 @@ AppManager *AppManager::instance() { return _instance; }
|
|||
|
||||
MainWindow *AppManager::mainWindow() const { return _w; }
|
||||
|
||||
uint AppManager::currentMSecsSinceEpoch() { return _timer.elapsed(); }
|
||||
|
||||
void AppManager::openFile(const QString &file, bool autoDetect) {
|
||||
EditorView *editor = nullptr;
|
||||
Q_ASSERT(_w);
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
#include "dialog/mainwindow.h"
|
||||
#include "qtsingleapplication/src/qtsingleapplication.h"
|
||||
|
||||
#include <QElapsedTimer>
|
||||
|
||||
class AppManager : public QtSingleApplication {
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
@ -36,6 +38,8 @@ public:
|
|||
QApplication::tr("WingCloudStudio");
|
||||
}
|
||||
|
||||
uint currentMSecsSinceEpoch();
|
||||
|
||||
public slots:
|
||||
void openFile(const QString &file, bool autoDetect = true);
|
||||
void openRawFile(const QString &file);
|
||||
|
@ -43,6 +47,8 @@ public slots:
|
|||
|
||||
private:
|
||||
MainWindow *_w = nullptr;
|
||||
QElapsedTimer _timer;
|
||||
|
||||
static AppManager *_instance;
|
||||
};
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include "wingcodeedit.h"
|
||||
|
||||
#include <QAbstractItemView>
|
||||
#include <QApplication>
|
||||
#include <QByteArray>
|
||||
#include <QDir>
|
||||
#include <QEvent>
|
||||
|
@ -41,8 +42,7 @@ Q_GLOBAL_STATIC_WITH_ARGS(QByteArray, LEFT_PARE_TRIGGER, ("("))
|
|||
Q_GLOBAL_STATIC_WITH_ARGS(QByteArray, SEMI_COLON_TRIGGER, (";"))
|
||||
|
||||
AsCompletion::AsCompletion(WingCodeEdit *p)
|
||||
: WingCompleter(p), parser(ScriptMachine::instance().engine()),
|
||||
m_parseDocument(true) {
|
||||
: WingCompleter(p), parser(ScriptMachine::instance().engine()) {
|
||||
setTriggerList({*DOT_TRIGGER, *DBL_COLON_TRIGGER,
|
||||
// unleash the power of call tips
|
||||
*LEFT_PARE_TRIGGER,
|
||||
|
@ -126,10 +126,43 @@ void AsCompletion::applyClassNodes(QList<CodeInfoTip> &nodes) {
|
|||
nodes = clsNodes;
|
||||
}
|
||||
|
||||
bool AsCompletion::parseDocument() const { return m_parseDocument; }
|
||||
int AsCompletion::includeCallBack(const QString &include, bool quotedInclude,
|
||||
const QString &from, AsPreprocesser *builder,
|
||||
void *userParam) {
|
||||
Q_UNUSED(userParam);
|
||||
|
||||
void AsCompletion::setParseDocument(bool newParseDocument) {
|
||||
m_parseDocument = newParseDocument;
|
||||
QFileInfo info(include);
|
||||
bool isAbsolute = info.isAbsolute();
|
||||
bool hasNoExt = info.suffix().isEmpty();
|
||||
QString inc;
|
||||
if (quotedInclude) {
|
||||
if (isAbsolute) {
|
||||
inc = include;
|
||||
} else {
|
||||
auto pwd = QFileInfo(from).absoluteDir();
|
||||
inc = pwd.absoluteFilePath(include);
|
||||
}
|
||||
} else {
|
||||
// absolute include is not allowed in #include<>
|
||||
if (isAbsolute) {
|
||||
// ignored in code completion
|
||||
return asSUCCESS;
|
||||
}
|
||||
|
||||
QDir dir(qApp->applicationDirPath());
|
||||
if (!dir.cd(QStringLiteral("aslib"))) {
|
||||
// someone crash the software, ignored
|
||||
return asSUCCESS;
|
||||
}
|
||||
inc = dir.absoluteFilePath(include);
|
||||
}
|
||||
|
||||
if (hasNoExt) {
|
||||
inc += QStringLiteral(".as");
|
||||
}
|
||||
|
||||
builder->loadSectionFromFile(inc);
|
||||
return asSUCCESS;
|
||||
}
|
||||
|
||||
void AsCompletion::clearFunctionTip() { emit onFunctionTip({}); }
|
||||
|
@ -139,26 +172,21 @@ QString AsCompletion::wordSeperators() const {
|
|||
return eow;
|
||||
}
|
||||
|
||||
void AsCompletion::processTrigger(const QString &trigger,
|
||||
bool AsCompletion::processTrigger(const QString &trigger,
|
||||
const QString &content) {
|
||||
if (content.isEmpty()) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (trigger == *SEMI_COLON_TRIGGER) {
|
||||
clearFunctionTip();
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
auto len = content.length();
|
||||
auto code = content.toUtf8();
|
||||
|
||||
QList<CodeInfoTip> nodes;
|
||||
QList<CodeInfoTip> docNodes;
|
||||
|
||||
if (m_parseDocument) {
|
||||
docNodes = parseDocument();
|
||||
}
|
||||
|
||||
if (!trigger.isEmpty() && trigger != *DOT_TRIGGER) {
|
||||
clearFunctionTip();
|
||||
|
@ -173,13 +201,14 @@ void AsCompletion::processTrigger(const QString &trigger,
|
|||
QByteArray content;
|
||||
};
|
||||
|
||||
auto engine = ScriptMachine::instance().engine();
|
||||
|
||||
// parse the tokens
|
||||
QVector<Token> tokens;
|
||||
qsizetype pos = 0;
|
||||
for (; p < end;) {
|
||||
asUINT tokenLen = 0;
|
||||
auto tt =
|
||||
ScriptMachine::instance().engine()->ParseToken(p, len, &tokenLen);
|
||||
auto tt = engine->ParseToken(p, len, &tokenLen);
|
||||
if (tt == asTC_WHITESPACE) {
|
||||
p += tokenLen;
|
||||
pos += tokenLen;
|
||||
|
@ -222,22 +251,35 @@ void AsCompletion::processTrigger(const QString &trigger,
|
|||
QByteArray fn;
|
||||
if (tokens.isEmpty()) {
|
||||
popup()->hide();
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
QString prefix;
|
||||
auto etoken = tokens.back();
|
||||
// it can not be any trigger, so take the last as prefix
|
||||
QString prefix = etoken.content;
|
||||
if (etoken.type == asTC_VALUE || etoken.type == asTC_COMMENT ||
|
||||
etoken.type == asTC_UNKNOWN) {
|
||||
popup()->hide();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (trigger.isEmpty() && popup()->isVisible()) {
|
||||
setCompletionPrefix(prefix);
|
||||
return true;
|
||||
}
|
||||
|
||||
QList<CodeInfoTip> docNodes = parseDocument();
|
||||
|
||||
// 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, docNodes);
|
||||
} else {
|
||||
etoken = tokens.back(); // checking later
|
||||
}
|
||||
} else {
|
||||
prefix.clear();
|
||||
}
|
||||
|
||||
if (nodes.isEmpty()) {
|
||||
|
@ -246,21 +288,66 @@ void AsCompletion::processTrigger(const QString &trigger,
|
|||
if (etoken.content == *DBL_COLON_TRIGGER) {
|
||||
processTrigger(*DBL_COLON_TRIGGER, content.left(etoken.pos));
|
||||
setCompletionPrefix(prefix);
|
||||
return;
|
||||
return true;
|
||||
} else if (etoken.content == *DOT_TRIGGER) {
|
||||
processTrigger(*DOT_TRIGGER, content.left(etoken.pos));
|
||||
setCompletionPrefix(prefix);
|
||||
return;
|
||||
return true;
|
||||
} else {
|
||||
applyEmptyNsNode(nodes, docNodes);
|
||||
}
|
||||
} else if (etoken.type != asTC_IDENTIFIER) {
|
||||
popup()->hide();
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (trigger == *DOT_TRIGGER) {
|
||||
applyClassNodes(nodes);
|
||||
// member type guessing ? basic match is enough. (>n<)
|
||||
auto isBasicType = [](const QString &type) {
|
||||
static QStringList basicType{
|
||||
"int", "int8", "int16", "int32", "int64",
|
||||
"uint", "uint8", "uint16", "uint32", "uint64",
|
||||
"float", "double", "byte"};
|
||||
|
||||
return basicType.contains(type);
|
||||
};
|
||||
|
||||
auto clsNodes = parser.headerNodes();
|
||||
|
||||
// filter the type we can use to auto-complete in docNodes
|
||||
for (auto &item : docNodes) {
|
||||
if (item.type == CodeInfoTip::Type::Class) {
|
||||
auto name = item.nameSpace;
|
||||
if (name.isEmpty()) {
|
||||
name = item.name;
|
||||
}
|
||||
clsNodes.insert(name, item.children);
|
||||
}
|
||||
// a typedef can only be used to define an alias
|
||||
// for primitive types, so NO NEED for auto-completing
|
||||
}
|
||||
|
||||
tokens.removeLast();
|
||||
auto ns = getNamespace(tokens);
|
||||
for (auto &item : docNodes) {
|
||||
if (etoken.content == item.name && ns == item.nameSpace) {
|
||||
auto retType = item.addinfo.value(CodeInfoTip::RetType);
|
||||
|
||||
// auto type inference is not supported.
|
||||
// PRs will be welcomed !!!
|
||||
if (isBasicType(retType)) {
|
||||
popup()->hide();
|
||||
return false;
|
||||
}
|
||||
|
||||
nodes.append(clsNodes.value(retType));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (nodes.isEmpty()) {
|
||||
applyClassNodes(nodes);
|
||||
}
|
||||
} else if (etoken.content.length() >= triggerAmount()) {
|
||||
// completion for a.b.c or a::b.c or a::b::c.d or ::a::b.c
|
||||
if (trigger == *DBL_COLON_TRIGGER) {
|
||||
|
@ -269,7 +356,7 @@ void AsCompletion::processTrigger(const QString &trigger,
|
|||
if (idx >= 0) {
|
||||
if (tokens.at(idx).content == *DOT_TRIGGER) {
|
||||
popup()->hide();
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
nodes = parser.headerNodes().value(ns) +
|
||||
|
@ -281,7 +368,7 @@ void AsCompletion::processTrigger(const QString &trigger,
|
|||
}
|
||||
|
||||
if (nodes.isEmpty()) {
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
} else if (trigger == *LEFT_PARE_TRIGGER) {
|
||||
// the first is function name, an identifier
|
||||
|
@ -297,7 +384,7 @@ void AsCompletion::processTrigger(const QString &trigger,
|
|||
if (idx >= 0 && idx < tokens.length()) {
|
||||
if (tokens.at(idx).content == *DOT_TRIGGER) {
|
||||
popup()->hide();
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -312,6 +399,7 @@ void AsCompletion::processTrigger(const QString &trigger,
|
|||
|
||||
setModel(new CodeCompletionModel(nodes, this));
|
||||
setCompletionPrefix(prefix);
|
||||
return true;
|
||||
}
|
||||
|
||||
QList<CodeInfoTip> AsCompletion::parseDocument() {
|
||||
|
@ -325,8 +413,7 @@ QList<CodeInfoTip> AsCompletion::parseDocument() {
|
|||
|
||||
// first preprocess the code
|
||||
AsPreprocesser prepc(engine);
|
||||
// TODO: set include callback
|
||||
// prepc.setIncludeCallback();
|
||||
prepc.setIncludeCallback(&AsCompletion::includeCallBack, this);
|
||||
|
||||
auto r = prepc.loadSectionFromMemory(QStringLiteral("ASCOMPLETION"),
|
||||
code.toUtf8());
|
||||
|
@ -338,61 +425,109 @@ QList<CodeInfoTip> AsCompletion::parseDocument() {
|
|||
QList<CodeInfoTip> ret;
|
||||
|
||||
for (auto &d : data) {
|
||||
QAsCodeParser parser(engine);
|
||||
auto syms =
|
||||
parser.parseAndIntell(editor->textCursor().position(), d.script);
|
||||
|
||||
for (auto &sym : syms) {
|
||||
CodeInfoTip tip;
|
||||
tip.name = sym.name;
|
||||
tip.nameSpace = QString::fromUtf8(sym.scope.join("::"));
|
||||
|
||||
switch (sym.symtype) {
|
||||
case QAsCodeParser::SymbolType::Function:
|
||||
case QAsCodeParser::SymbolType::FnDef:
|
||||
tip.type = CodeInfoTip::Type::Function;
|
||||
tip.addinfo.insert(CodeInfoTip::RetType,
|
||||
QString::fromUtf8(sym.type));
|
||||
tip.addinfo.insert(CodeInfoTip::Args,
|
||||
QString::fromUtf8(sym.additonalInfo));
|
||||
for (auto &var : sym.children) {
|
||||
CodeInfoTip va;
|
||||
va.dontAddGlobal = true;
|
||||
va.name = var.name;
|
||||
va.nameSpace = QString::fromUtf8(var.scope.join("::"));
|
||||
va.type = CodeInfoTip::Type::Variable;
|
||||
ret.append(va);
|
||||
}
|
||||
break;
|
||||
case QAsCodeParser::SymbolType::Enum:
|
||||
tip.type = CodeInfoTip::Type::Enum;
|
||||
for (auto &e : sym.children) {
|
||||
CodeInfoTip en;
|
||||
en.dontAddGlobal = true;
|
||||
en.name = e.name;
|
||||
en.nameSpace = QString::fromUtf8(e.scope.join("::"));
|
||||
en.type = CodeInfoTip::Type::Enumerater;
|
||||
if (!e.additonalInfo.isEmpty()) {
|
||||
en.addinfo.insert(CodeInfoTip::Comment,
|
||||
en.name + QStringLiteral(" = ") +
|
||||
e.additonalInfo);
|
||||
}
|
||||
ret.append(en);
|
||||
}
|
||||
break;
|
||||
case QAsCodeParser::SymbolType::TypeDef:
|
||||
tip.type = CodeInfoTip::Type::KeyWord;
|
||||
break;
|
||||
case QAsCodeParser::SymbolType::Variable:
|
||||
tip.type = CodeInfoTip::Type::Variable;
|
||||
break;
|
||||
case QAsCodeParser::SymbolType::Class:
|
||||
case QAsCodeParser::SymbolType::Invalid:
|
||||
continue;
|
||||
}
|
||||
|
||||
ret.append(tip);
|
||||
qsizetype offset = -1;
|
||||
if (d.section == QStringLiteral("ASCOMPLETION")) {
|
||||
offset = editor->textCursor().position();
|
||||
}
|
||||
ret.append(parseScriptData(offset, d.script));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
QList<CodeInfoTip> AsCompletion::parseScriptData(qsizetype offset,
|
||||
const QByteArray &code) {
|
||||
QList<CodeInfoTip> ret;
|
||||
|
||||
auto engine = ScriptMachine::instance().engine();
|
||||
QAsCodeParser parser(engine);
|
||||
auto syms = parser.parseAndIntell(offset, code);
|
||||
|
||||
for (auto &sym : syms) {
|
||||
CodeInfoTip tip;
|
||||
tip.name = sym.name;
|
||||
tip.nameSpace = QString::fromUtf8(sym.scope.join("::"));
|
||||
|
||||
switch (sym.symtype) {
|
||||
case QAsCodeParser::SymbolType::Function:
|
||||
case QAsCodeParser::SymbolType::FnDef:
|
||||
tip.type = CodeInfoTip::Type::Function;
|
||||
tip.addinfo.insert(CodeInfoTip::RetType,
|
||||
QString::fromUtf8(sym.type));
|
||||
tip.addinfo.insert(CodeInfoTip::Args,
|
||||
QString::fromUtf8(sym.additonalInfo));
|
||||
for (auto &var : sym.children) {
|
||||
CodeInfoTip va;
|
||||
va.dontAddGlobal = true;
|
||||
va.name = var.name;
|
||||
va.nameSpace = QString::fromUtf8(var.scope.join("::"));
|
||||
va.addinfo.insert(CodeInfoTip::RetType, var.type);
|
||||
va.type = CodeInfoTip::Type::Variable;
|
||||
ret.append(va);
|
||||
}
|
||||
break;
|
||||
case QAsCodeParser::SymbolType::Enum:
|
||||
tip.type = CodeInfoTip::Type::Enum;
|
||||
for (auto &e : sym.children) {
|
||||
CodeInfoTip en;
|
||||
en.dontAddGlobal = true;
|
||||
en.name = e.name;
|
||||
en.nameSpace = QString::fromUtf8(e.scope.join("::"));
|
||||
en.type = CodeInfoTip::Type::Enumerater;
|
||||
if (!e.additonalInfo.isEmpty()) {
|
||||
en.addinfo.insert(CodeInfoTip::Comment,
|
||||
en.name + QStringLiteral(" = ") +
|
||||
e.additonalInfo);
|
||||
}
|
||||
ret.append(en);
|
||||
}
|
||||
break;
|
||||
case QAsCodeParser::SymbolType::TypeDef:
|
||||
tip.type = CodeInfoTip::Type::TypeDef;
|
||||
break;
|
||||
case QAsCodeParser::SymbolType::Variable:
|
||||
tip.addinfo.insert(CodeInfoTip::RetType, sym.type);
|
||||
tip.type = CodeInfoTip::Type::Variable;
|
||||
break;
|
||||
case QAsCodeParser::SymbolType::Class:
|
||||
case QAsCodeParser::SymbolType::Interface:
|
||||
for (auto &mem : sym.children) {
|
||||
if (mem.vis != QAsCodeParser::Visiblity::Public) {
|
||||
continue;
|
||||
}
|
||||
CodeInfoTip ctip;
|
||||
ctip.name = mem.name;
|
||||
ctip.nameSpace = QString::fromUtf8(mem.scope.join("::"));
|
||||
if (mem.symtype == QAsCodeParser::SymbolType::Function) {
|
||||
ctip.type = CodeInfoTip::Type::Function;
|
||||
ctip.addinfo.insert(CodeInfoTip::RetType,
|
||||
QString::fromUtf8(mem.type));
|
||||
ctip.addinfo.insert(CodeInfoTip::Args,
|
||||
QString::fromUtf8(mem.additonalInfo));
|
||||
for (auto &var : mem.children) {
|
||||
CodeInfoTip va;
|
||||
va.dontAddGlobal = true;
|
||||
va.name = var.name;
|
||||
va.nameSpace = QString::fromUtf8(var.scope.join("::"));
|
||||
va.addinfo.insert(CodeInfoTip::RetType, var.type);
|
||||
va.type = CodeInfoTip::Type::Variable;
|
||||
tip.children.append(va);
|
||||
}
|
||||
tip.children.append(ctip);
|
||||
} else if (mem.symtype == QAsCodeParser::SymbolType::Variable) {
|
||||
ctip.addinfo.insert(CodeInfoTip::RetType, mem.type);
|
||||
ctip.type = CodeInfoTip::Type::Variable;
|
||||
tip.children.append(ctip);
|
||||
}
|
||||
}
|
||||
tip.type = CodeInfoTip::Type::Class;
|
||||
break;
|
||||
case QAsCodeParser::SymbolType::Invalid:
|
||||
case QAsCodeParser::SymbolType::Import:
|
||||
continue;
|
||||
}
|
||||
|
||||
ret.append(tip);
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
#include "WingCodeEdit/wingcompleter.h"
|
||||
#include "class/asdatabase.h"
|
||||
|
||||
class AsPreprocesser;
|
||||
|
||||
class AsCompletion : public WingCompleter {
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
@ -32,17 +34,17 @@ public:
|
|||
public:
|
||||
virtual QString wordSeperators() const override;
|
||||
|
||||
bool parseDocument() const;
|
||||
void setParseDocument(bool newParseDocument);
|
||||
|
||||
void clearFunctionTip();
|
||||
|
||||
protected:
|
||||
virtual void processTrigger(const QString &trigger,
|
||||
virtual bool processTrigger(const QString &trigger,
|
||||
const QString &content) override;
|
||||
|
||||
virtual QList<CodeInfoTip> parseDocument();
|
||||
|
||||
QList<CodeInfoTip> parseScriptData(qsizetype offset,
|
||||
const QByteArray &code);
|
||||
|
||||
signals:
|
||||
void onFunctionTip(const QString &content);
|
||||
|
||||
|
@ -55,9 +57,13 @@ private:
|
|||
const QList<CodeInfoTip> &docNodes);
|
||||
void applyClassNodes(QList<CodeInfoTip> &nodes);
|
||||
|
||||
private:
|
||||
static int includeCallBack(const QString &include, bool quotedInclude,
|
||||
const QString &from, AsPreprocesser *builder,
|
||||
void *userParam);
|
||||
|
||||
private:
|
||||
ASDataBase parser;
|
||||
bool m_parseDocument;
|
||||
};
|
||||
|
||||
#endif // _CPP_COMPLETION_H_
|
||||
|
|
|
@ -17,9 +17,73 @@
|
|||
|
||||
#include "asconsolecompletion.h"
|
||||
|
||||
#include "AngelScript/sdk/angelscript/source/as_module.h"
|
||||
#include "class/qascodeparser.h"
|
||||
#include "control/scriptingconsole.h"
|
||||
|
||||
AsConsoleCompletion::AsConsoleCompletion(ScriptingConsole *p)
|
||||
: AsCompletion(p), _console(p) {
|
||||
setParseDocument(false);
|
||||
: AsCompletion(p), _console(p) {}
|
||||
|
||||
QList<CodeInfoTip> AsConsoleCompletion::parseDocument() {
|
||||
auto editor = _console;
|
||||
if (editor == nullptr) {
|
||||
return {};
|
||||
}
|
||||
|
||||
auto code = editor->currentCodes();
|
||||
auto engine = ScriptMachine::instance().engine();
|
||||
|
||||
QAsCodeParser parser(engine);
|
||||
auto seg = parser.parse(code.toUtf8());
|
||||
if (std::find_if(seg.begin(), seg.end(),
|
||||
[](const QAsCodeParser::CodeSegment &seg) {
|
||||
return seg.isValid();
|
||||
}) == seg.end()) {
|
||||
// wrap it to let code-completion work
|
||||
code.prepend("void f(){").append('}');
|
||||
}
|
||||
|
||||
// first preprocess the code
|
||||
AsPreprocesser prepc(engine);
|
||||
|
||||
// including is not supported in console
|
||||
auto r = prepc.loadSectionFromMemory(QStringLiteral("ASConCOMPLETION"),
|
||||
code.toUtf8());
|
||||
if (r <= 0) {
|
||||
return {};
|
||||
}
|
||||
|
||||
QList<CodeInfoTip> citips;
|
||||
// first, global variables should be added into
|
||||
auto rmod = ScriptMachine::instance().module(ScriptMachine::Interactive);
|
||||
// I think hijack the internal will do the better
|
||||
auto mod = dynamic_cast<asCModule *>(rmod);
|
||||
if (mod) {
|
||||
auto it = mod->m_scriptGlobals.List();
|
||||
while (it) {
|
||||
// lamda expression is great!
|
||||
auto CString2ByteArray = [](const asCString &str) -> QByteArray {
|
||||
return QByteArray(str.AddressOf(), str.GetLength());
|
||||
};
|
||||
|
||||
CodeInfoTip tip;
|
||||
tip.type = CodeInfoTip::Type::Variable;
|
||||
tip.addinfo.insert(
|
||||
CodeInfoTip::RetType,
|
||||
CString2ByteArray(it->type.Format(mod->m_defaultNamespace)));
|
||||
tip.nameSpace = CString2ByteArray(it->nameSpace->name);
|
||||
tip.name = CString2ByteArray(it->name);
|
||||
citips.append(tip);
|
||||
|
||||
it++;
|
||||
}
|
||||
}
|
||||
|
||||
auto data = prepc.scriptData();
|
||||
if (data.size() == 1) {
|
||||
auto d = data[0];
|
||||
return citips + parseScriptData(d.script.length() - 1, d.script);
|
||||
} else {
|
||||
return citips;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,6 +28,9 @@ public:
|
|||
explicit AsConsoleCompletion(ScriptingConsole *p);
|
||||
virtual ~AsConsoleCompletion() = default;
|
||||
|
||||
protected:
|
||||
virtual QList<CodeInfoTip> parseDocument();
|
||||
|
||||
private:
|
||||
ScriptingConsole *_console;
|
||||
};
|
||||
|
|
|
@ -1,41 +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 "ascontextmgr.h"
|
||||
|
||||
// copy from base class
|
||||
struct SContextInfo {
|
||||
asUINT sleepUntil;
|
||||
std::vector<asIScriptContext *> coRoutines;
|
||||
asUINT currentCoRoutine;
|
||||
asIScriptContext *keepCtxAfterExecution;
|
||||
};
|
||||
|
||||
asContextMgr::asContextMgr() : CContextMgr() {}
|
||||
|
||||
bool asContextMgr::findThreadWithUserData(asPWORD index, void *data) const {
|
||||
for (auto &th : m_threads) {
|
||||
auto ctx = th->keepCtxAfterExecution;
|
||||
if (ctx) {
|
||||
auto user = ctx->GetUserData(index);
|
||||
if (user == data) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
|
@ -1,30 +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 ASCONTEXTMGR_H
|
||||
#define ASCONTEXTMGR_H
|
||||
|
||||
#include "AngelScript/sdk/add_on/contextmgr/contextmgr.h"
|
||||
|
||||
class asContextMgr : public CContextMgr {
|
||||
public:
|
||||
asContextMgr();
|
||||
|
||||
bool findThreadWithUserData(asPWORD index, void *data) const;
|
||||
};
|
||||
|
||||
#endif // ASCONTEXTMGR_H
|
|
@ -71,6 +71,9 @@ void asDebugger::takeCommands(asIScriptContext *ctx) {
|
|||
void asDebugger::lineCallback(asIScriptContext *ctx) {
|
||||
Q_ASSERT(ctx);
|
||||
|
||||
// prevent UI freezing
|
||||
qApp->processEvents();
|
||||
|
||||
// This should never happen, but it doesn't hurt to validate it
|
||||
if (ctx == nullptr)
|
||||
return;
|
||||
|
|
|
@ -36,6 +36,7 @@ QIcon CodeInfoTip::getDisplayIcon(Type type, CodeInfoVisibility vis) {
|
|||
case Type::Group:
|
||||
return icon(ICON_NAMESPACE);
|
||||
case Type::Class:
|
||||
case Type::TypeDef:
|
||||
return icon(ICON_CLASS);
|
||||
case Type::ClsFunction:
|
||||
case Type::Function:
|
||||
|
|
|
@ -36,6 +36,7 @@ public:
|
|||
Variable,
|
||||
Property = Variable,
|
||||
Enumerater,
|
||||
TypeDef
|
||||
};
|
||||
|
||||
enum CacheIndex {
|
||||
|
|
|
@ -151,6 +151,9 @@ void CTypeParser::initialize() {
|
|||
ADD_TYPE(longlong, QMetaType::LongLong);
|
||||
ADD_TYPE_U(ulonglong, QMetaType::ULongLong);
|
||||
|
||||
using uint = unsigned int;
|
||||
ADD_TYPE_U(uint, QMetaType::UInt);
|
||||
|
||||
using BOOL = bool;
|
||||
using BYTE = byte;
|
||||
using WORD = uint16;
|
||||
|
@ -242,6 +245,8 @@ void CTypeParser::initialize() {
|
|||
|
||||
#undef ADD_TYPE
|
||||
#undef ADD_TYPE_S
|
||||
|
||||
base_types_ = type_maps_.keys();
|
||||
}
|
||||
|
||||
void CTypeParser::setIncludePaths(const QStringList &paths) {
|
||||
|
@ -451,7 +456,7 @@ void CTypeParser::stripComments(QStringList &lines) const {
|
|||
QStringLiteral("]");
|
||||
|
||||
// search comment start
|
||||
while ((pos = line.indexOf(kSlash, pos)) != -1) {
|
||||
while ((pos = line.indexOf(char(kSlash), pos)) != -1) {
|
||||
if (line.length() <= pos + 1)
|
||||
break; // the 1st '/' is at the end of line, so not a comment
|
||||
|
||||
|
@ -627,7 +632,7 @@ TokenTypes CTypeParser::getTokenType(const QString &token) const {
|
|||
return keywords_.value(token);
|
||||
} else if (qualifiers_.contains(token)) {
|
||||
return kQualifier;
|
||||
} else if (type_maps_.contains(token)) {
|
||||
} else if (base_types_.contains(token)) {
|
||||
return kBasicDataType;
|
||||
} else if (struct_defs_.contains(token)) {
|
||||
return kStructName;
|
||||
|
@ -1065,7 +1070,7 @@ bool CTypeParser::parseDeclaration(const QString &line,
|
|||
if (line.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
if (line[line.length() - 1] != kSemicolon)
|
||||
if (line[line.length() - 1] != char(kSemicolon))
|
||||
return false;
|
||||
|
||||
QStringList tokens;
|
||||
|
@ -1085,7 +1090,7 @@ bool CTypeParser::parseDeclaration(const QString &line,
|
|||
return false;
|
||||
}
|
||||
|
||||
if (tokens[++index].at(0) == kAsterisk) {
|
||||
if (tokens[++index].at(0) == char(kAsterisk)) {
|
||||
decl.is_pointer = true;
|
||||
// size of a pointer is 4 types on a 32-bit system
|
||||
length =
|
||||
|
@ -1143,14 +1148,14 @@ bool CTypeParser::parseEnumDeclaration(const QString &line, int &last_value,
|
|||
break;
|
||||
|
||||
case 2:
|
||||
if (kComma != tokens[1].at(0)) {
|
||||
if (char(kComma) != tokens[1].at(0)) {
|
||||
return false;
|
||||
}
|
||||
decl.second = ++last_value;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
if (kEqual != tokens[1].at(0)) {
|
||||
if (char(kEqual) != tokens[1].at(0)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1166,7 +1171,8 @@ bool CTypeParser::parseEnumDeclaration(const QString &line, int &last_value,
|
|||
break;
|
||||
|
||||
case 4:
|
||||
if (!(kEqual == tokens[1].at(0) && kComma == tokens[3].at(0))) {
|
||||
if (!(char(kEqual) == tokens[1].at(0) &&
|
||||
char(kComma) == tokens[3].at(0))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1203,8 +1209,8 @@ bool CTypeParser::parseAssignExpression(const QString &line) {
|
|||
|
||||
// only 4 tokens for an assignment expression: var = number;
|
||||
if (4 == splitLineIntoTokens(line, tokens) &&
|
||||
kEqual == tokens[1].at(tokens[1].length() - 1) &&
|
||||
kSemicolon == tokens[3].at(tokens[3].length() - 1) &&
|
||||
char(kEqual) == tokens[1].at(tokens[1].length() - 1) &&
|
||||
char(kSemicolon) == tokens[3].at(tokens[3].length() - 1) &&
|
||||
isNumericToken(tokens[2], number)) {
|
||||
const_defs_.insert(tokens[0], number);
|
||||
return true;
|
||||
|
@ -1239,7 +1245,7 @@ bool CTypeParser::parsePreProcDirective(const QString &src, qsizetype &pos) {
|
|||
}
|
||||
|
||||
// only handle header file included with ""
|
||||
if (kQuotation == token[token.length() - 1]) {
|
||||
if (char(kQuotation) == token[token.length() - 1]) {
|
||||
// get included header file name
|
||||
if (!getNextToken(src, pos, token, false)) {
|
||||
return false;
|
||||
|
@ -1366,7 +1372,7 @@ bool CTypeParser::parseStructUnion(const bool is_struct, const bool is_typedef,
|
|||
if (is_typedef) {
|
||||
// format 1
|
||||
if (!(getNextToken(src, pos, next_token) &&
|
||||
kSemicolon == next_token.at(0))) {
|
||||
char(kSemicolon) == next_token.at(0))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1387,7 +1393,7 @@ bool CTypeParser::parseStructUnion(const bool is_struct, const bool is_typedef,
|
|||
type_maps_[type_name] = type_maps_[type_alias];
|
||||
}
|
||||
} else { // non-typedef
|
||||
if (kSemicolon == token.at(0)) {
|
||||
if (char(kSemicolon) == token.at(0)) {
|
||||
// format 2
|
||||
is_decl = false;
|
||||
if (type_name.isEmpty()) {
|
||||
|
@ -1542,7 +1548,7 @@ bool CTypeParser::parseEnum(const bool is_typedef, const QString &src,
|
|||
if (is_typedef) {
|
||||
// format 1
|
||||
if (!(getNextToken(src, pos, next_token) &&
|
||||
kSemicolon == next_token.at(0))) {
|
||||
char(kSemicolon) == next_token.at(0))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1559,7 +1565,7 @@ bool CTypeParser::parseEnum(const bool is_typedef, const QString &src,
|
|||
;
|
||||
}
|
||||
} else { // non-typedef
|
||||
if (kSemicolon == token.at(0)) {
|
||||
if (char(kSemicolon) == token.at(0)) {
|
||||
// format 2
|
||||
is_decl = false;
|
||||
if (type_name.isEmpty()) {
|
||||
|
|
|
@ -211,6 +211,9 @@ private:
|
|||
/// @note All enum types have fixed size, so they're not stored
|
||||
QHash<QString, QPair<QMetaType::Type, qsizetype>> type_maps_;
|
||||
|
||||
/// basic types
|
||||
QStringList base_types_;
|
||||
|
||||
/// unsigned types
|
||||
QStringList unsigned_types_;
|
||||
|
||||
|
|
|
@ -2840,6 +2840,10 @@ void PluginSystem::checkDirRootSafe(const QDir &dir) {
|
|||
}
|
||||
|
||||
void PluginSystem::registerFns(IWingPlugin *plg) {
|
||||
if (_angelplg == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
Q_ASSERT(plg);
|
||||
auto fns = plg->registeredScriptFns();
|
||||
if (fns.isEmpty()) {
|
||||
|
@ -2912,6 +2916,10 @@ void PluginSystem::registerFns(IWingPlugin *plg) {
|
|||
}
|
||||
|
||||
void PluginSystem::registerUnSafeFns(IWingPlugin *plg) {
|
||||
if (_angelplg == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
Q_ASSERT(plg);
|
||||
|
||||
auto fns = plg->registeredScriptUnsafeFns();
|
||||
|
|
|
@ -81,7 +81,6 @@ QAsCodeParser::parseIntell(qsizetype offset,
|
|||
case SymbolType::Function: {
|
||||
sym.type = seg.additonalInfos.at(0);
|
||||
sym.additonalInfo = seg.additonalInfos.at(1);
|
||||
sym.name = seg.name;
|
||||
if (offset > seg.offset && offset < seg.end()) {
|
||||
sym.children = parseStatementBlock(sym.scope, seg.codes,
|
||||
offset - seg.offset);
|
||||
|
@ -120,8 +119,28 @@ QAsCodeParser::parseIntell(qsizetype offset,
|
|||
continue;
|
||||
}
|
||||
break;
|
||||
case SymbolType::Class:
|
||||
case SymbolType::Class: {
|
||||
auto syms =
|
||||
parseClassContent(offset - seg.offset, sym.scope, seg.codes);
|
||||
// TODO: PRS, 'cause i have no need to code-complete a class
|
||||
sym.inherit = syms.first;
|
||||
sym.children = syms.second;
|
||||
if (offset > seg.offset && offset < seg.end()) {
|
||||
ret.append(syms.second);
|
||||
}
|
||||
} break;
|
||||
case SymbolType::Interface: {
|
||||
auto syms = parseInterfaceContent(offset - seg.offset, sym.scope,
|
||||
seg.codes);
|
||||
// TODO: PRS, 'cause i have no need to code-complete an interface
|
||||
sym.inherit = syms.first;
|
||||
sym.children = syms.second;
|
||||
if (offset > seg.offset && offset < seg.end()) {
|
||||
ret.append(syms.second);
|
||||
}
|
||||
} break;
|
||||
case SymbolType::Invalid:
|
||||
case SymbolType::Import:
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -440,6 +459,49 @@ QByteArray QAsCodeParser::parseType(bool allowConst, bool allowVariableType,
|
|||
return dtType;
|
||||
}
|
||||
|
||||
QAsCodeParser::Symbol
|
||||
QAsCodeParser::parseFuncDefContent(const QByteArrayList &ns) {
|
||||
Symbol sym;
|
||||
|
||||
sToken t1;
|
||||
getToken(&t1);
|
||||
|
||||
if (t1.type != ttFuncDef) {
|
||||
rewindErrorTo(&t1);
|
||||
return sym;
|
||||
}
|
||||
|
||||
sym.type = parseType(true);
|
||||
if (_isSyntaxError)
|
||||
return sym;
|
||||
|
||||
parseTypeMod(false);
|
||||
if (_isSyntaxError)
|
||||
return sym;
|
||||
|
||||
auto iden = parseIdentifier();
|
||||
if (_isSyntaxError)
|
||||
return sym;
|
||||
|
||||
sym.scope = ns;
|
||||
sym.name = getSymbolString(iden);
|
||||
sym.offset = iden.pos;
|
||||
|
||||
auto args = parseParameterListContent();
|
||||
if (_isSyntaxError)
|
||||
return sym;
|
||||
sym.children = args;
|
||||
|
||||
getToken(&t1);
|
||||
if (t1.type != ttEndStatement) {
|
||||
rewindErrorTo(&t1);
|
||||
return sym;
|
||||
}
|
||||
|
||||
sym.symtype = SymbolType::FnDef;
|
||||
return sym;
|
||||
}
|
||||
|
||||
QList<QAsCodeParser::Symbol>
|
||||
QAsCodeParser::parseStatementBlock(const QByteArrayList &ns,
|
||||
const QByteArray &code, qsizetype end) {
|
||||
|
@ -462,7 +524,8 @@ QAsCodeParser::parseStatementBlock(const QByteArrayList &ns,
|
|||
QList<QAsCodeParser::Symbol> ret;
|
||||
for (auto &symlist : syms) {
|
||||
for (auto &sym : symlist) {
|
||||
if (sym.symtype == SymbolType::Variable) {
|
||||
if (sym.symtype == SymbolType::Variable &&
|
||||
!sym.type.isEmpty()) {
|
||||
auto var = sym.name;
|
||||
auto n = std::find_if(
|
||||
ret.begin(), ret.end(),
|
||||
|
@ -744,10 +807,7 @@ sToken QAsCodeParser::superficiallyParseStatementBlock() {
|
|||
level--;
|
||||
else if (t1.type == ttStartStatementBlock)
|
||||
level++;
|
||||
else if (t1.type == ttNonTerminatedStringConstant) {
|
||||
rewindErrorTo(&t1);
|
||||
return t1;
|
||||
} else if (t1.type == ttEnd) {
|
||||
else if (t1.type == ttEnd) {
|
||||
rewindErrorTo(&start);
|
||||
return start;
|
||||
}
|
||||
|
@ -895,13 +955,14 @@ QAsCodeParser::CodeSegment QAsCodeParser::parseFuncDef() {
|
|||
auto begin = t1.pos;
|
||||
skipCodeBlock();
|
||||
getToken(&t1);
|
||||
rewindTo(&t1);
|
||||
auto end = t1.pos;
|
||||
|
||||
// seg.name is empty
|
||||
seg.scope = currentNs;
|
||||
seg.offset = begin;
|
||||
seg.type = SymbolType::FnDef;
|
||||
seg.codes = _code.sliced(begin, end - begin + 1);
|
||||
seg.codes = _code.sliced(begin, end - begin);
|
||||
return seg;
|
||||
}
|
||||
|
||||
|
@ -928,12 +989,13 @@ QAsCodeParser::CodeSegment QAsCodeParser::parseInterface() {
|
|||
|
||||
auto id = parseIdentifier();
|
||||
seg.name = getSymbolString(id);
|
||||
seg.offset = id.pos;
|
||||
|
||||
seg.scope = currentNs;
|
||||
seg.type = SymbolType::Class;
|
||||
seg.type = SymbolType::Interface;
|
||||
|
||||
// External shared declarations are ended with ';'
|
||||
getToken(&t);
|
||||
seg.offset = t.pos;
|
||||
if (t.type == ttEndStatement) {
|
||||
rewindTo(&t);
|
||||
parseToken(ttEndStatement);
|
||||
|
@ -981,6 +1043,7 @@ void QAsCodeParser::superficiallyParseVarInit() {
|
|||
} while (indentParan || indentBrace ||
|
||||
(t.type != ttListSeparator && t.type != ttEndStatement &&
|
||||
t.type != ttEndStatementBlock));
|
||||
rewindTo(&t);
|
||||
} else if (t.type == ttOpenParenthesis) {
|
||||
sToken start = t;
|
||||
|
||||
|
@ -1005,17 +1068,6 @@ void QAsCodeParser::superficiallyParseVarInit() {
|
|||
}
|
||||
}
|
||||
|
||||
void QAsCodeParser::parseStatement() {
|
||||
sToken t1;
|
||||
|
||||
getToken(&t1);
|
||||
rewindTo(&t1);
|
||||
|
||||
// TODO
|
||||
if (isVarDecl()) {
|
||||
}
|
||||
}
|
||||
|
||||
QList<QAsCodeParser::Symbol>
|
||||
QAsCodeParser::parseDeclaration(const QByteArrayList &ns, bool isClassProp,
|
||||
bool isGlobalVar) {
|
||||
|
@ -1107,10 +1159,14 @@ QList<QAsCodeParser::CodeSegment> QAsCodeParser::parseScript(bool inBlock) {
|
|||
rewindTo(&tStart);
|
||||
|
||||
if (t1.type == ttImport) {
|
||||
CodeSegment seg;
|
||||
seg.offset = t1.pos;
|
||||
|
||||
seg.type = SymbolType::Import;
|
||||
segs << seg;
|
||||
|
||||
// import we don't support just skip
|
||||
skipCodeBlock();
|
||||
// add empty invalid segment
|
||||
segs << CodeSegment();
|
||||
} else if (t1.type == ttEnum) // Handle enumerations
|
||||
segs << parseEnumeration();
|
||||
else if (t1.type == ttTypedef) // Handle primitive typedefs
|
||||
|
@ -1138,7 +1194,7 @@ QList<QAsCodeParser::CodeSegment> QAsCodeParser::parseScript(bool inBlock) {
|
|||
seg.offset = begin;
|
||||
seg.scope = currentNs;
|
||||
seg.type = SymbolType::Variable;
|
||||
seg.codes = _code.sliced(begin, end - begin + 1);
|
||||
seg.codes = _code.sliced(begin, end - begin);
|
||||
rewindTo(&t1);
|
||||
|
||||
segs.append(seg);
|
||||
|
@ -1235,12 +1291,13 @@ QAsCodeParser::CodeSegment QAsCodeParser::parseClass() {
|
|||
|
||||
auto cls = parseIdentifier();
|
||||
seg.name = getSymbolString(cls);
|
||||
seg.offset = cls.pos;
|
||||
|
||||
seg.scope = currentNs;
|
||||
seg.type = SymbolType::Class;
|
||||
|
||||
// External shared declarations are ended with ';'
|
||||
getToken(&t);
|
||||
seg.offset = t.pos;
|
||||
if (t.type == ttEndStatement) {
|
||||
rewindTo(&t);
|
||||
parseToken(ttEndStatement);
|
||||
|
@ -1268,6 +1325,7 @@ QAsCodeParser::CodeSegment QAsCodeParser::parseTypedef() {
|
|||
auto begin = token.pos;
|
||||
skipCodeBlock();
|
||||
getToken(&token);
|
||||
rewindTo(&token);
|
||||
auto end = token.pos;
|
||||
|
||||
// seg.name is empty
|
||||
|
@ -1383,112 +1441,108 @@ QAsCodeParser::parseGlobalVarDecls(const QByteArrayList &ns,
|
|||
return parseDeclaration(ns, false, true);
|
||||
}
|
||||
|
||||
QAsCodeParser::CodeSegment QAsCodeParser::parseFunction(bool isMethod) {
|
||||
CodeSegment fn;
|
||||
QAsCodeParser::CodeSegment QAsCodeParser::parseFunctionMethod(Visiblity &vis) {
|
||||
CodeSegment seg;
|
||||
|
||||
sToken t1;
|
||||
getToken(&t1);
|
||||
|
||||
if (!isMethod) {
|
||||
// A global function can be marked as shared and external
|
||||
while (t1.type == ttIdentifier) {
|
||||
if (identifierIs(t1, SHARED_TOKEN) ||
|
||||
identifierIs(t1, EXTERNAL_TOKEN)) {
|
||||
rewindTo(&t1);
|
||||
parseIdentifier();
|
||||
if (_isSyntaxError)
|
||||
return fn;
|
||||
} else
|
||||
break;
|
||||
|
||||
getToken(&t1);
|
||||
}
|
||||
}
|
||||
vis = Visiblity::Public;
|
||||
|
||||
// A class method can start with 'private' or 'protected'
|
||||
if (isMethod && t1.type == ttPrivate) {
|
||||
if (t1.type == ttPrivate) {
|
||||
rewindTo(&t1);
|
||||
parseToken(ttPrivate);
|
||||
getToken(&t1);
|
||||
} else if (isMethod && t1.type == ttProtected) {
|
||||
vis = Visiblity::Private;
|
||||
} else if (t1.type == ttProtected) {
|
||||
rewindTo(&t1);
|
||||
parseToken(ttProtected);
|
||||
getToken(&t1);
|
||||
vis = Visiblity::Protected;
|
||||
}
|
||||
|
||||
if (_isSyntaxError)
|
||||
return fn;
|
||||
return seg;
|
||||
|
||||
// If it is a global function, or a method, except constructor and
|
||||
// destructor, then the return type is parsed
|
||||
sToken t2;
|
||||
getToken(&t2);
|
||||
rewindTo(&t1);
|
||||
if (!isMethod || (t1.type != ttBitNot && t2.type != ttOpenParenthesis)) {
|
||||
if (t1.type != ttBitNot && t2.type != ttOpenParenthesis) {
|
||||
auto id = parseType(true);
|
||||
if (_isSyntaxError)
|
||||
return fn;
|
||||
fn.additonalInfos.append(id);
|
||||
return seg;
|
||||
seg.additonalInfos.append(id);
|
||||
|
||||
parseTypeMod(false);
|
||||
if (_isSyntaxError)
|
||||
return fn;
|
||||
return seg;
|
||||
}
|
||||
|
||||
// If this is a class destructor then it starts with ~, and no return type
|
||||
// is declared
|
||||
if (isMethod && t1.type == ttBitNot) {
|
||||
if (t1.type == ttBitNot) {
|
||||
parseToken(ttBitNot);
|
||||
if (_isSyntaxError)
|
||||
return fn;
|
||||
return seg;
|
||||
}
|
||||
|
||||
auto iden = parseIdentifier();
|
||||
if (_isSyntaxError)
|
||||
return fn;
|
||||
fn.name = getSymbolString(iden);
|
||||
fn.offset = iden.pos;
|
||||
return seg;
|
||||
seg.name = getSymbolString(iden);
|
||||
seg.offset = iden.pos;
|
||||
|
||||
auto params = parseParameterListContent();
|
||||
if (_isSyntaxError)
|
||||
return fn;
|
||||
if (!params.isEmpty()) {
|
||||
if (fn.additonalInfos.isEmpty()) {
|
||||
fn.additonalInfos.append(QByteArray());
|
||||
return seg;
|
||||
|
||||
if (seg.additonalInfos.isEmpty()) {
|
||||
seg.additonalInfos.append(QByteArray());
|
||||
}
|
||||
if (params.isEmpty()) {
|
||||
seg.additonalInfos.append(QByteArray());
|
||||
} else {
|
||||
QByteArrayList args;
|
||||
for (auto &p : params) {
|
||||
args.append(p.type + ' ' + p.name);
|
||||
}
|
||||
// fn.additonalInfos;
|
||||
seg.additonalInfos.append(args.join(", "));
|
||||
}
|
||||
|
||||
if (isMethod) {
|
||||
getToken(&t1);
|
||||
rewindTo(&t1);
|
||||
getToken(&t1);
|
||||
rewindTo(&t1);
|
||||
|
||||
// Is the method a const?
|
||||
if (t1.type == ttConst)
|
||||
parseToken(ttConst);
|
||||
}
|
||||
// Is the method a const?
|
||||
if (t1.type == ttConst)
|
||||
parseToken(ttConst);
|
||||
|
||||
// TODO: Should support abstract methods, in which case no statement block
|
||||
// should be provided
|
||||
parseMethodAttributes();
|
||||
if (_isSyntaxError)
|
||||
return fn;
|
||||
return seg;
|
||||
|
||||
// External shared functions must be ended with ';'
|
||||
getToken(&t1);
|
||||
rewindTo(&t1);
|
||||
|
||||
seg.scope = currentNs;
|
||||
|
||||
if (t1.type == ttEndStatement) {
|
||||
parseToken(ttEndStatement);
|
||||
// fn.ns = _curns;
|
||||
return fn;
|
||||
return seg;
|
||||
}
|
||||
|
||||
// We should just find the end of the statement block here. The statements
|
||||
// will be parsed on request by the compiler once it starts the compilation.
|
||||
superficiallyParseStatementBlock();
|
||||
// fn.ns = _curns;
|
||||
// fn.code = code.mid(t1.pos, sourcePos - t1.pos);
|
||||
// fn.valid = true;
|
||||
return fn;
|
||||
auto begin = t1.pos;
|
||||
|
||||
// We should just find the end of the statement block here.
|
||||
t1 = superficiallyParseStatementBlock();
|
||||
auto end = t1.pos;
|
||||
seg.codes = _code.sliced(begin, end - begin + 1);
|
||||
return seg;
|
||||
}
|
||||
|
||||
QAsCodeParser::Symbol
|
||||
|
@ -1496,54 +1550,16 @@ QAsCodeParser::parseFuncDefContent(const QByteArrayList &ns,
|
|||
const QByteArray &code) {
|
||||
reset();
|
||||
_code = code;
|
||||
|
||||
Symbol sym;
|
||||
|
||||
sToken t1;
|
||||
getToken(&t1);
|
||||
|
||||
if (t1.type != ttFuncDef) {
|
||||
rewindErrorTo(&t1);
|
||||
return sym;
|
||||
}
|
||||
|
||||
sym.type = parseType(true);
|
||||
if (_isSyntaxError)
|
||||
return sym;
|
||||
|
||||
parseTypeMod(false);
|
||||
if (_isSyntaxError)
|
||||
return sym;
|
||||
|
||||
auto iden = parseIdentifier();
|
||||
if (_isSyntaxError)
|
||||
return sym;
|
||||
|
||||
sym.scope = ns;
|
||||
sym.name = getSymbolString(iden);
|
||||
sym.offset = iden.pos;
|
||||
|
||||
auto args = parseParameterListContent();
|
||||
if (_isSyntaxError)
|
||||
return sym;
|
||||
sym.children = args;
|
||||
|
||||
getToken(&t1);
|
||||
if (t1.type != ttEndStatement) {
|
||||
rewindErrorTo(&t1);
|
||||
return sym;
|
||||
}
|
||||
|
||||
sym.symtype = SymbolType::FnDef;
|
||||
return sym;
|
||||
return parseFuncDefContent(ns);
|
||||
}
|
||||
|
||||
QList<QAsCodeParser::Symbol>
|
||||
QAsCodeParser::parseClassContent(const QByteArrayList &ns,
|
||||
QPair<QByteArrayList, QList<QAsCodeParser::Symbol>>
|
||||
QAsCodeParser::parseClassContent(qsizetype offset, const QByteArrayList &ns,
|
||||
const QByteArray &code) {
|
||||
reset();
|
||||
_code = code;
|
||||
|
||||
QByteArrayList inhertSyms;
|
||||
QList<Symbol> syms;
|
||||
|
||||
sToken t;
|
||||
|
@ -1552,20 +1568,30 @@ QAsCodeParser::parseClassContent(const QByteArrayList &ns,
|
|||
// Optional list of interfaces that are being implemented and classes that
|
||||
// are being inherited
|
||||
if (t.type == ttColon) {
|
||||
// TODO: dont support temperily, later
|
||||
parseOptionalScope();
|
||||
parseIdentifier();
|
||||
QByteArray inhertSym;
|
||||
|
||||
// assuming it as an interface
|
||||
inhertSym = parseOptionalScope().join("::");
|
||||
if (!inhertSym.isEmpty()) {
|
||||
inhertSym += "::";
|
||||
}
|
||||
inhertSym += getSymbolString(parseIdentifier());
|
||||
inhertSyms.append(inhertSym);
|
||||
getToken(&t);
|
||||
while (t.type == ttListSeparator) {
|
||||
parseOptionalScope();
|
||||
parseIdentifier();
|
||||
inhertSym = parseOptionalScope().join("::");
|
||||
if (!inhertSym.isEmpty()) {
|
||||
inhertSym += "::";
|
||||
}
|
||||
inhertSym += getSymbolString(parseIdentifier());
|
||||
inhertSyms.append(inhertSym);
|
||||
getToken(&t);
|
||||
}
|
||||
}
|
||||
|
||||
if (t.type != ttStartStatementBlock) {
|
||||
rewindErrorTo(&t);
|
||||
return syms;
|
||||
return {inhertSyms, syms};
|
||||
}
|
||||
|
||||
// Parse properties
|
||||
|
@ -1574,33 +1600,43 @@ QAsCodeParser::parseClassContent(const QByteArrayList &ns,
|
|||
while (t.type != ttEndStatementBlock && t.type != ttEnd) {
|
||||
// Is it a property or a method?
|
||||
if (t.type == ttFuncDef) {
|
||||
// auto fndef = parseFuncDefContent();
|
||||
// if (fndef.isValid()) {
|
||||
// clssym.content.append(fndef);
|
||||
// }
|
||||
auto fndef = parseFuncDefContent(ns);
|
||||
syms.append(fndef);
|
||||
} else if (isFuncDecl(true)) {
|
||||
auto fn = parseFunction(true);
|
||||
// if (fn.isValid()) {
|
||||
// clssym.codesegs.insert(fn.nameInSrc, fn);
|
||||
// }
|
||||
Symbol sym;
|
||||
auto fn = parseFunctionMethod(sym.vis);
|
||||
|
||||
// add function symbols
|
||||
sym.symtype = SymbolType::Function;
|
||||
sym.scope = fn.scope;
|
||||
sym.offset = fn.offset;
|
||||
sym.name = fn.name;
|
||||
sym.type = fn.additonalInfos.at(0);
|
||||
sym.additonalInfo = fn.additonalInfos.at(1);
|
||||
syms.append(sym);
|
||||
|
||||
// deep parsing
|
||||
if (offset >= fn.offset && offset < fn.end()) {
|
||||
auto ss =
|
||||
parseStatementBlock(fn.scope, fn.codes, offset - fn.offset);
|
||||
syms.append(ss);
|
||||
}
|
||||
} else if (isVirtualPropertyDecl()) {
|
||||
auto vp = parseVirtualPropertyDecl(true, false);
|
||||
// if (vp.isValid()) {
|
||||
// clssym.content.append(vp);
|
||||
// }
|
||||
syms.append(vp);
|
||||
} else if (isVarDecl()) {
|
||||
// auto decl = parseDeclaration(true);
|
||||
// clssym.content.append(decl);
|
||||
auto decl = parseDeclaration(ns, true);
|
||||
syms.append(decl);
|
||||
} else if (t.type == ttEndStatement)
|
||||
// Skip empty declarations
|
||||
getToken(&t);
|
||||
else {
|
||||
rewindErrorTo(&t);
|
||||
return syms;
|
||||
return {inhertSyms, syms};
|
||||
}
|
||||
|
||||
if (_isSyntaxError)
|
||||
return syms;
|
||||
return {inhertSyms, syms};
|
||||
|
||||
getToken(&t);
|
||||
rewindTo(&t);
|
||||
|
@ -1609,74 +1645,46 @@ QAsCodeParser::parseClassContent(const QByteArrayList &ns,
|
|||
getToken(&t);
|
||||
if (t.type != ttEndStatementBlock) {
|
||||
rewindErrorTo(&t);
|
||||
return syms;
|
||||
return {inhertSyms, syms};
|
||||
}
|
||||
|
||||
return syms;
|
||||
return {inhertSyms, syms};
|
||||
}
|
||||
|
||||
void QAsCodeParser::parseMixinContent() {
|
||||
sToken t;
|
||||
getToken(&t);
|
||||
|
||||
if (t.type != ttMixin) {
|
||||
rewindErrorTo(&t);
|
||||
return;
|
||||
}
|
||||
|
||||
// A mixin token must be followed by a class declaration
|
||||
// parseClassContent();
|
||||
}
|
||||
|
||||
void QAsCodeParser::parseInterfaceContent() {
|
||||
Symbol sym;
|
||||
QPair<QByteArrayList, QList<QAsCodeParser::Symbol>>
|
||||
QAsCodeParser::parseInterfaceContent(qsizetype offset, const QByteArrayList &ns,
|
||||
const QByteArray &code) {
|
||||
QByteArrayList inhertSyms;
|
||||
QList<Symbol> syms;
|
||||
|
||||
sToken t;
|
||||
|
||||
// Allow keywords 'external' and 'shared' before 'interface'
|
||||
getToken(&t);
|
||||
while (identifierIs(t, SHARED_TOKEN) || identifierIs(t, EXTERNAL_TOKEN)) {
|
||||
rewindTo(&t);
|
||||
parseIdentifier();
|
||||
|
||||
if (_isSyntaxError)
|
||||
return;
|
||||
|
||||
getToken(&t);
|
||||
}
|
||||
|
||||
if (t.type != ttInterface) {
|
||||
rewindErrorTo(&t);
|
||||
return;
|
||||
}
|
||||
|
||||
auto id = parseIdentifier();
|
||||
sym.name = getSymbolString(id);
|
||||
// sym.nameInSrc = id.pos;
|
||||
|
||||
// External shared declarations are ended with ';'
|
||||
getToken(&t);
|
||||
if (t.type == ttEndStatement) {
|
||||
rewindTo(&t);
|
||||
parseToken(ttEndStatement);
|
||||
return;
|
||||
}
|
||||
|
||||
// Can optionally have a list of interfaces that are inherited
|
||||
if (t.type == ttColon) {
|
||||
parseOptionalScope();
|
||||
parseIdentifier();
|
||||
QByteArray inhertSym;
|
||||
|
||||
inhertSym = parseOptionalScope().join("::");
|
||||
if (!inhertSym.isEmpty()) {
|
||||
inhertSym += "::";
|
||||
}
|
||||
inhertSym += getSymbolString(parseIdentifier());
|
||||
inhertSyms.append(inhertSym);
|
||||
getToken(&t);
|
||||
while (t.type == ttListSeparator) {
|
||||
parseOptionalScope();
|
||||
parseIdentifier();
|
||||
inhertSym = parseOptionalScope().join("::");
|
||||
if (!inhertSym.isEmpty()) {
|
||||
inhertSym += "::";
|
||||
}
|
||||
inhertSym += getSymbolString(parseIdentifier());
|
||||
inhertSyms.append(inhertSym);
|
||||
getToken(&t);
|
||||
}
|
||||
}
|
||||
|
||||
if (t.type != ttStartStatementBlock) {
|
||||
rewindErrorTo(&t);
|
||||
return;
|
||||
return {};
|
||||
}
|
||||
|
||||
// Parse interface methods
|
||||
|
@ -1685,22 +1693,18 @@ void QAsCodeParser::parseInterfaceContent() {
|
|||
while (t.type != ttEndStatementBlock && t.type != ttEnd) {
|
||||
if (isVirtualPropertyDecl()) {
|
||||
auto vp = parseVirtualPropertyDecl(true, true);
|
||||
// if (vp.isValid()) {
|
||||
// sym.content.append(vp);
|
||||
// }
|
||||
syms.append(vp);
|
||||
} else if (t.type == ttEndStatement) {
|
||||
// Skip empty declarations
|
||||
getToken(&t);
|
||||
} else {
|
||||
// Parse the method signature
|
||||
auto im = parseInterfaceMethod();
|
||||
// if (im.isValid()) {
|
||||
// sym.content.append(im);
|
||||
// }
|
||||
syms.append(im);
|
||||
}
|
||||
|
||||
if (_isSyntaxError)
|
||||
return;
|
||||
return {inhertSyms, syms};
|
||||
|
||||
getToken(&t);
|
||||
rewindTo(&t);
|
||||
|
@ -1709,8 +1713,10 @@ void QAsCodeParser::parseInterfaceContent() {
|
|||
getToken(&t);
|
||||
if (t.type != ttEndStatementBlock) {
|
||||
rewindErrorTo(&t);
|
||||
return;
|
||||
return {inhertSyms, syms};
|
||||
}
|
||||
|
||||
return {inhertSyms, syms};
|
||||
}
|
||||
|
||||
QAsCodeParser::Symbol QAsCodeParser::parseInterfaceMethod() {
|
||||
|
@ -1719,7 +1725,7 @@ QAsCodeParser::Symbol QAsCodeParser::parseInterfaceMethod() {
|
|||
auto ret = parseType(true);
|
||||
if (_isSyntaxError)
|
||||
return sym;
|
||||
// sym.typeStr = getSymbolString(ret);
|
||||
sym.type = ret;
|
||||
|
||||
parseTypeMod(false);
|
||||
if (_isSyntaxError)
|
||||
|
@ -1729,12 +1735,13 @@ QAsCodeParser::Symbol QAsCodeParser::parseInterfaceMethod() {
|
|||
if (_isSyntaxError)
|
||||
return sym;
|
||||
sym.name = getSymbolString(id);
|
||||
// sym.nameInSrc = id.pos;
|
||||
sym.offset = id.pos;
|
||||
|
||||
auto args = parseParameterListContent();
|
||||
if (_isSyntaxError)
|
||||
return sym;
|
||||
// sym.content = args;
|
||||
|
||||
sym.children = args;
|
||||
|
||||
// Parse an optional const after the method definition
|
||||
sToken t1;
|
||||
|
@ -1749,7 +1756,7 @@ QAsCodeParser::Symbol QAsCodeParser::parseInterfaceMethod() {
|
|||
return sym;
|
||||
}
|
||||
|
||||
// sym.type = SymbolType::FnDecl;
|
||||
sym.symtype = SymbolType::Function;
|
||||
return sym;
|
||||
}
|
||||
|
||||
|
@ -1777,7 +1784,7 @@ QAsCodeParser::parseVirtualPropertyDecl(bool isMethod, bool isInterface) {
|
|||
auto id = parseType(true);
|
||||
if (_isSyntaxError)
|
||||
return sym;
|
||||
// sym.typeStr = getSymbolString(id);
|
||||
sym.type = id;
|
||||
|
||||
parseTypeMod(false);
|
||||
if (_isSyntaxError)
|
||||
|
@ -1787,7 +1794,7 @@ QAsCodeParser::parseVirtualPropertyDecl(bool isMethod, bool isInterface) {
|
|||
if (_isSyntaxError)
|
||||
return sym;
|
||||
sym.name = getSymbolString(iden);
|
||||
// sym.nameInSrc = iden.pos;
|
||||
sym.offset = iden.pos;
|
||||
|
||||
getToken(&t1);
|
||||
if (t1.type != ttStartStatementBlock) {
|
||||
|
@ -1825,11 +1832,7 @@ QAsCodeParser::parseVirtualPropertyDecl(bool isMethod, bool isInterface) {
|
|||
if (t1.type == ttStartStatementBlock) {
|
||||
rewindTo(&t1);
|
||||
superficiallyParseStatementBlock();
|
||||
|
||||
// seg.valid = true;
|
||||
// seg.code = code.mid(t1.pos, sourcePos - t1.pos);
|
||||
// sym.codesegs.insert(t1.pos, seg);
|
||||
|
||||
// TODO: support deep parsing ?
|
||||
if (_isSyntaxError)
|
||||
return sym;
|
||||
} else if (t1.type == ttEndStatement) {
|
||||
|
@ -1861,7 +1864,7 @@ QAsCodeParser::parseVirtualPropertyDecl(bool isMethod, bool isInterface) {
|
|||
}
|
||||
}
|
||||
|
||||
// sym.type = SymbolType::Property;
|
||||
sym.symtype = SymbolType::Variable;
|
||||
return sym;
|
||||
}
|
||||
|
||||
|
@ -2206,75 +2209,6 @@ bool QAsCodeParser::isFuncDecl(
|
|||
return false;
|
||||
}
|
||||
|
||||
bool QAsCodeParser::isLambda() {
|
||||
bool isLambda = false;
|
||||
sToken t;
|
||||
getToken(&t);
|
||||
if (t.type == ttIdentifier && identifierIs(t, FUNCTION_TOKEN)) {
|
||||
sToken t2;
|
||||
getToken(&t2);
|
||||
if (t2.type == ttOpenParenthesis) {
|
||||
// Skip until )
|
||||
while (t2.type != ttCloseParenthesis && t2.type != ttEnd)
|
||||
getToken(&t2);
|
||||
|
||||
// The next token must be a {
|
||||
getToken(&t2);
|
||||
if (t2.type == ttStartStatementBlock)
|
||||
isLambda = true;
|
||||
}
|
||||
}
|
||||
|
||||
rewindTo(&t);
|
||||
return isLambda;
|
||||
}
|
||||
|
||||
bool QAsCodeParser::isFunctionCall() {
|
||||
sToken s;
|
||||
sToken t1, t2;
|
||||
|
||||
getToken(&s);
|
||||
t1 = s;
|
||||
|
||||
// A function call may be prefixed with scope resolution
|
||||
if (t1.type == ttScope)
|
||||
getToken(&t1);
|
||||
getToken(&t2);
|
||||
|
||||
while (t1.type == ttIdentifier && t2.type == ttScope) {
|
||||
getToken(&t1);
|
||||
getToken(&t2);
|
||||
}
|
||||
|
||||
// A function call starts with an identifier followed by an argument list
|
||||
// The parser doesn't have enough RewindErrorTormation about scope to
|
||||
// determine if the identifier is a datatype, so even if it happens to be
|
||||
// the parser will identify the expression as a function call rather than a
|
||||
// construct call. The compiler will sort this out later
|
||||
if (t1.type != ttIdentifier) {
|
||||
rewindTo(&s);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (t2.type == ttOpenParenthesis) {
|
||||
rewindTo(&s);
|
||||
return true;
|
||||
}
|
||||
|
||||
rewindTo(&s);
|
||||
return false;
|
||||
}
|
||||
|
||||
void QAsCodeParser::ParseStringConstant() {
|
||||
sToken t;
|
||||
getToken(&t);
|
||||
if (t.type != ttStringConstant && t.type != ttMultilineStringConstant &&
|
||||
t.type != ttHeredocStringConstant) {
|
||||
rewindErrorTo(&t);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool QAsCodeParser::findTokenAfterType(
|
||||
sToken &nextToken) { // Set a rewind point
|
||||
sToken t, t1;
|
||||
|
@ -2369,43 +2303,6 @@ bool QAsCodeParser::findTokenAfterType(
|
|||
return true;
|
||||
}
|
||||
|
||||
bool QAsCodeParser::findIdentifierAfterScope(sToken &nextToken) {
|
||||
sToken t1, t2, t3;
|
||||
|
||||
// Determine the last identifier after scope in order to check if it is a
|
||||
// type
|
||||
getToken(&t1);
|
||||
getToken(&t2);
|
||||
rewindTo(&t1);
|
||||
|
||||
if (t1.type != ttScope && t2.type != ttScope) {
|
||||
if (t1.type == ttIdentifier) {
|
||||
nextToken = t1;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (t1.type == ttScope)
|
||||
t3 = t2;
|
||||
else
|
||||
t3 = t1;
|
||||
rewindTo(&t3);
|
||||
getToken(&t2);
|
||||
while (t3.type == ttIdentifier) {
|
||||
t2 = t3;
|
||||
getToken(&t3);
|
||||
if (t3.type == ttScope)
|
||||
getToken(&t3);
|
||||
else
|
||||
break;
|
||||
}
|
||||
rewindTo(&t1);
|
||||
nextToken = t2;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool QAsCodeParser::isConstant(int tokenType) {
|
||||
if (tokenType == ttIntConstant || tokenType == ttFloatConstant ||
|
||||
tokenType == ttDoubleConstant || tokenType == ttStringConstant ||
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
#include <QMap>
|
||||
#include <QString>
|
||||
|
||||
// This class is the modification of as_parser.
|
||||
// This class comes from as_parser.h .
|
||||
// You can modified it to support more features.
|
||||
/** It's a complex thing to fully support AngelScript code intellisense.
|
||||
** I just support basic code completion.
|
||||
|
@ -41,16 +41,17 @@ public:
|
|||
public:
|
||||
enum class SymbolType {
|
||||
Invalid,
|
||||
Variable, // variable or property in class
|
||||
Enum, // an enum
|
||||
Class, // a class type
|
||||
Function, // a function
|
||||
TypeDef, // a typedef
|
||||
FnDef, // a funcdef
|
||||
Variable, // variable or property in class
|
||||
Enum, // an enum
|
||||
Class, // a class type
|
||||
Function, // a function
|
||||
TypeDef, // a typedef
|
||||
FnDef, // a funcdef
|
||||
Interface, // an interface
|
||||
Import, // import but not supported
|
||||
};
|
||||
|
||||
enum class Visiblity { Public, Private, Protected };
|
||||
|
||||
/**
|
||||
* @brief The CodeSegment class
|
||||
*/
|
||||
|
@ -89,6 +90,7 @@ public:
|
|||
Visiblity vis = QAsCodeParser::Visiblity::Public;
|
||||
QByteArray additonalInfo; // for other additonal info
|
||||
|
||||
QByteArrayList inherit;
|
||||
QList<Symbol> children;
|
||||
};
|
||||
|
||||
|
@ -133,6 +135,7 @@ private:
|
|||
CodeSegment parseInterface();
|
||||
CodeSegment parseFuncDef();
|
||||
CodeSegment parseFunction();
|
||||
CodeSegment parseFunctionMethod(Visiblity &vis);
|
||||
|
||||
private:
|
||||
// parse tokens
|
||||
|
@ -168,18 +171,20 @@ private:
|
|||
Symbol parseFuncDefContent(const QByteArrayList &ns,
|
||||
const QByteArray &code);
|
||||
|
||||
QList<Symbol> parseClassContent(const QByteArrayList &ns,
|
||||
const QByteArray &code);
|
||||
Symbol parseFuncDefContent(const QByteArrayList &ns);
|
||||
|
||||
QPair<QByteArrayList, QList<Symbol>>
|
||||
parseClassContent(qsizetype offset, const QByteArrayList &ns,
|
||||
const QByteArray &code);
|
||||
|
||||
QPair<QByteArrayList, QList<Symbol>>
|
||||
parseInterfaceContent(qsizetype offset, const QByteArrayList &ns,
|
||||
const QByteArray &code);
|
||||
|
||||
QList<Symbol> parseStatementBlock(const QByteArrayList &ns,
|
||||
const QByteArray &code, qsizetype end);
|
||||
|
||||
private:
|
||||
void parseStatement();
|
||||
void parseMixinContent();
|
||||
void parseInterfaceContent();
|
||||
CodeSegment parseFunction(bool isMethod);
|
||||
|
||||
Symbol parseVirtualPropertyDecl(bool isMethod, bool isInterface);
|
||||
QList<Symbol> parseParameterListContent();
|
||||
|
||||
|
@ -192,8 +197,6 @@ private:
|
|||
bool isVarDecl();
|
||||
bool isVirtualPropertyDecl();
|
||||
bool isFuncDecl(bool isMethod);
|
||||
bool isLambda();
|
||||
bool isFunctionCall();
|
||||
|
||||
void getToken(sToken *token);
|
||||
void rewindTo(const sToken *token);
|
||||
|
@ -209,14 +212,11 @@ private:
|
|||
|
||||
private:
|
||||
bool findTokenAfterType(sToken &nextToken);
|
||||
bool findIdentifierAfterScope(sToken &nextToken);
|
||||
|
||||
bool typeExist(const QString &t);
|
||||
|
||||
Symbol parseInterfaceMethod();
|
||||
|
||||
void ParseStringConstant();
|
||||
|
||||
private:
|
||||
bool _errorWhileParsing;
|
||||
bool _isSyntaxError;
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include "AngelScript/sdk/add_on/weakref/weakref.h"
|
||||
|
||||
#include "angelobjstring.h"
|
||||
#include "class/appmanager.h"
|
||||
#include "class/asbuilder.h"
|
||||
#include "class/pluginsystem.h"
|
||||
#include "class/qascodeparser.h"
|
||||
|
@ -42,13 +43,7 @@
|
|||
#include <QProcess>
|
||||
#include <QScopeGuard>
|
||||
|
||||
ScriptMachine::~ScriptMachine() {
|
||||
if (_ctxMgr) {
|
||||
delete _ctxMgr;
|
||||
}
|
||||
|
||||
destoryMachine();
|
||||
}
|
||||
ScriptMachine::~ScriptMachine() { destoryMachine(); }
|
||||
|
||||
bool ScriptMachine::init() {
|
||||
if (isInited()) {
|
||||
|
@ -74,9 +69,7 @@ bool ScriptMachine::init() {
|
|||
bool ScriptMachine::isInited() const { return _engine != nullptr; }
|
||||
|
||||
bool ScriptMachine::isRunning(ConsoleMode mode) const {
|
||||
return _ctxMgr->findThreadWithUserData(
|
||||
AsUserDataType::UserData_ContextMode,
|
||||
reinterpret_cast<void *>(asPWORD(mode)));
|
||||
return _ctx.value(mode) != nullptr;
|
||||
}
|
||||
|
||||
bool ScriptMachine::configureEngine() {
|
||||
|
@ -171,9 +164,12 @@ bool ScriptMachine::configureEngine() {
|
|||
return false;
|
||||
}
|
||||
|
||||
// Setup the context manager and register the support for co-routines
|
||||
_ctxMgr = new asContextMgr();
|
||||
_ctxMgr = new CContextMgr;
|
||||
_ctxMgr->RegisterCoRoutineSupport(_engine);
|
||||
_ctxMgr->SetGetTimeCallback([]() -> asUINT {
|
||||
return AppManager::instance()->currentMSecsSinceEpoch();
|
||||
});
|
||||
_ctxMgr->RegisterThreadSupport(_engine);
|
||||
|
||||
// Tell the engine to use our context pool. This will also
|
||||
// allow us to debug internal script calls made by the engine
|
||||
|
@ -227,6 +223,9 @@ QString ScriptMachine::getCallStack(asIScriptContext *context) {
|
|||
}
|
||||
|
||||
void ScriptMachine::destoryMachine() {
|
||||
_ctxMgr->AbortAll();
|
||||
delete _ctxMgr;
|
||||
|
||||
_debugger->setEngine(nullptr);
|
||||
_engine->ShutDownAndRelease();
|
||||
_engine = nullptr;
|
||||
|
@ -244,11 +243,12 @@ void ScriptMachine::exceptionCallback(asIScriptContext *context) {
|
|||
|
||||
const char *section;
|
||||
MessageInfo msg;
|
||||
msg.mode = mode;
|
||||
msg.row = context->GetExceptionLineNumber(&msg.col, §ion);
|
||||
msg.type = MessageType::Error;
|
||||
msg.message = message;
|
||||
|
||||
outputMessage(mode, msg);
|
||||
outputMessage(msg);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -259,10 +259,11 @@ void ScriptMachine::print(void *ref, int typeId) {
|
|||
context->GetUserData(AsUserDataType::UserData_ContextMode)));
|
||||
|
||||
MessageInfo info;
|
||||
info.mode = mode;
|
||||
info.type = MessageType::Print;
|
||||
info.message = _debugger->toString(ref, typeId, _engine);
|
||||
|
||||
outputMessage(mode, info);
|
||||
outputMessage(info);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -280,8 +281,8 @@ QString ScriptMachine::getInput() {
|
|||
return {};
|
||||
}
|
||||
|
||||
void ScriptMachine::outputMessage(ConsoleMode mode, const MessageInfo &info) {
|
||||
auto cbs = _regcalls.value(mode);
|
||||
void ScriptMachine::outputMessage(const MessageInfo &info) {
|
||||
auto cbs = _regcalls.value(info.mode);
|
||||
if (cbs.printMsgFn) {
|
||||
cbs.printMsgFn(info);
|
||||
}
|
||||
|
@ -357,12 +358,14 @@ bool ScriptMachine::executeScript(ConsoleMode mode, const QString &script,
|
|||
return false;
|
||||
}
|
||||
|
||||
_curMode = mode;
|
||||
r = builder.build(mod);
|
||||
if (r < 0) {
|
||||
MessageInfo info;
|
||||
info.mode = mode;
|
||||
info.message = tr("Script failed to build");
|
||||
info.type = MessageType::Error;
|
||||
outputMessage(mode, info);
|
||||
outputMessage(info);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -375,29 +378,33 @@ bool ScriptMachine::executeScript(ConsoleMode mode, const QString &script,
|
|||
|
||||
if (func == nullptr) {
|
||||
MessageInfo info;
|
||||
info.mode = mode;
|
||||
info.message = tr("Cannot find 'int main()' or 'void main()'");
|
||||
info.type = MessageType::Error;
|
||||
outputMessage(mode, info);
|
||||
outputMessage(info);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isInDebug) {
|
||||
// Allow the user to initialize the debugging before moving on
|
||||
MessageInfo info;
|
||||
info.mode = mode;
|
||||
info.message = tr("Debugging, waiting for commands.");
|
||||
info.type = MessageType::Info;
|
||||
outputMessage(mode, info);
|
||||
outputMessage(info);
|
||||
}
|
||||
|
||||
// Once we have the main function, we first need to initialize the global
|
||||
// variables Since we've set up the request context callback we will be able
|
||||
// to debug the initialization without passing in a pre-created context
|
||||
// Once we have the main function, we first need to initialize the
|
||||
// global variables Since we've set up the request context callback we
|
||||
// will be able to debug the initialization without passing in a
|
||||
// pre-created context
|
||||
r = mod->ResetGlobalVars(0);
|
||||
if (r < 0) {
|
||||
MessageInfo info;
|
||||
info.mode = mode;
|
||||
info.message = tr("Failed while initializing global variables");
|
||||
info.type = MessageType::Error;
|
||||
outputMessage(mode, info);
|
||||
outputMessage(info);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -405,9 +412,11 @@ bool ScriptMachine::executeScript(ConsoleMode mode, const QString &script,
|
|||
// The context manager will request the context from the
|
||||
// pool, which will automatically attach the debugger
|
||||
asIScriptContext *ctx = _ctxMgr->AddContext(_engine, func, true);
|
||||
_ctx[mode] = ctx;
|
||||
|
||||
ctx->SetUserData(reinterpret_cast<void *>(isDbg),
|
||||
AsUserDataType::UserData_isDbg);
|
||||
|
||||
mod->SetUserData(reinterpret_cast<void *>(isDbg),
|
||||
AsUserDataType::UserData_isDbg);
|
||||
|
||||
|
@ -421,34 +430,40 @@ bool ScriptMachine::executeScript(ConsoleMode mode, const QString &script,
|
|||
// Execute the script until completion
|
||||
// The script may create co-routines. These will automatically
|
||||
// be managed by the context manager
|
||||
while (_ctxMgr->ExecuteScripts())
|
||||
;
|
||||
while (_ctxMgr->ExecuteScripts()) {
|
||||
qApp->processEvents();
|
||||
}
|
||||
|
||||
_ctx[mode] = nullptr;
|
||||
|
||||
// Check if the main script finished normally
|
||||
r = ctx->GetState();
|
||||
if (r != asEXECUTION_FINISHED) {
|
||||
if (r == asEXECUTION_EXCEPTION) {
|
||||
MessageInfo info;
|
||||
info.mode = mode;
|
||||
info.message = tr("The script failed with an exception") +
|
||||
QStringLiteral("\n") +
|
||||
QString::fromStdString(GetExceptionInfo(ctx, true));
|
||||
info.type = MessageType::Error;
|
||||
outputMessage(mode, info);
|
||||
outputMessage(info);
|
||||
r = -1;
|
||||
} else if (r == asEXECUTION_ABORTED) {
|
||||
MessageInfo info;
|
||||
info.mode = mode;
|
||||
info.message = tr("The script was aborted");
|
||||
info.type = MessageType::Error;
|
||||
outputMessage(mode, info);
|
||||
outputMessage(info);
|
||||
r = -1;
|
||||
} else {
|
||||
auto e = QMetaEnum::fromType<asEContextState>();
|
||||
MessageInfo info;
|
||||
info.mode = mode;
|
||||
info.message = tr("The script terminated unexpectedly") +
|
||||
QStringLiteral(" (") + e.valueToKey(r) +
|
||||
QStringLiteral(")");
|
||||
info.type = MessageType::Error;
|
||||
outputMessage(mode, info);
|
||||
outputMessage(info);
|
||||
r = -1;
|
||||
}
|
||||
} else {
|
||||
|
@ -460,9 +475,10 @@ bool ScriptMachine::executeScript(ConsoleMode mode, const QString &script,
|
|||
}
|
||||
|
||||
MessageInfo info;
|
||||
info.mode = mode;
|
||||
info.message = tr("The script exited with ") + QString::number(r);
|
||||
info.type = MessageType::Info;
|
||||
outputMessage(mode, info);
|
||||
outputMessage(info);
|
||||
|
||||
// Return the context after retrieving the return value
|
||||
_ctxMgr->DoneWithContext(ctx);
|
||||
|
@ -479,13 +495,24 @@ bool ScriptMachine::executeScript(ConsoleMode mode, const QString &script,
|
|||
return r >= 0;
|
||||
}
|
||||
|
||||
void ScriptMachine::abortDbgScript(ConsoleMode mode) {
|
||||
void ScriptMachine::abortDbgScript() {
|
||||
if (_debugger->getEngine()) {
|
||||
_debugger->runDebugAction(asDebugger::ABORT);
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptMachine::abortScript() { _ctxMgr->AbortAll(); }
|
||||
void ScriptMachine::abortScript(ConsoleMode mode) {
|
||||
auto ctx = _ctx.value(mode, nullptr);
|
||||
if (ctx) {
|
||||
ctx->Abort();
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptMachine::abortScript() {
|
||||
abortScript(ConsoleMode::Interactive);
|
||||
abortScript(ConsoleMode::Scripting);
|
||||
abortScript(ConsoleMode::Background);
|
||||
}
|
||||
|
||||
void ScriptMachine::messageCallback(const asSMessageInfo *msg, void *param) {
|
||||
MessageType t = MessageType::Print;
|
||||
|
@ -507,12 +534,13 @@ void ScriptMachine::messageCallback(const asSMessageInfo *msg, void *param) {
|
|||
return;
|
||||
}
|
||||
MessageInfo info;
|
||||
info.mode = ins->_curMode;
|
||||
info.row = msg->row;
|
||||
info.col = msg->col;
|
||||
info.section = msg->section;
|
||||
info.message = m;
|
||||
info.type = t;
|
||||
ins->outputMessage(ins->_curMode, info);
|
||||
ins->outputMessage(info);
|
||||
}
|
||||
|
||||
void ScriptMachine::cleanUpDbgContext(asIScriptContext *context) {
|
||||
|
@ -1805,16 +1833,6 @@ asDebugger *ScriptMachine::debugger() const { return _debugger; }
|
|||
|
||||
bool ScriptMachine::executeCode(ConsoleMode mode, const QString &code) {
|
||||
asIScriptModule *mod = createModuleIfNotExist(mode);
|
||||
|
||||
QScopeGuard guard([mod, mode]() {
|
||||
if (mode != ConsoleMode::Interactive) {
|
||||
// Before leaving, allow the engine to clean up remaining objects by
|
||||
// discarding the module and doing a full garbage collection so that
|
||||
// this can also be debugged if desired
|
||||
mod->Discard();
|
||||
}
|
||||
});
|
||||
|
||||
_engine->SetEngineProperty(asEP_BUILD_WITHOUT_LINE_CUES, false);
|
||||
|
||||
// first, preparse the code
|
||||
|
@ -1824,112 +1842,145 @@ bool ScriptMachine::executeCode(ConsoleMode mode, const QString &code) {
|
|||
asIScriptFunction *func = nullptr;
|
||||
|
||||
auto ret = parser.parse(ccode);
|
||||
|
||||
// check whether there is any enum/class
|
||||
if (ret.isEmpty()) {
|
||||
if (std::find_if(ret.begin(), ret.end(),
|
||||
[](const QAsCodeParser::CodeSegment &seg) {
|
||||
switch (seg.type) {
|
||||
case QAsCodeParser::SymbolType::Enum:
|
||||
case QAsCodeParser::SymbolType::Class:
|
||||
case QAsCodeParser::SymbolType::Function:
|
||||
case QAsCodeParser::SymbolType::Interface:
|
||||
case QAsCodeParser::SymbolType::Import:
|
||||
case QAsCodeParser::SymbolType::Variable:
|
||||
return false;
|
||||
case QAsCodeParser::SymbolType::Invalid:
|
||||
case QAsCodeParser::SymbolType::TypeDef:
|
||||
case QAsCodeParser::SymbolType::FnDef:
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}) != ret.end()) {
|
||||
// ok, wrap the codes
|
||||
ccode.prepend("void main(){").append("}");
|
||||
ccode.prepend("void f(){").append("}");
|
||||
// start to compile
|
||||
auto r = mod->CompileFunction(nullptr, ccode, -1, 0, &func);
|
||||
if (r < 0) {
|
||||
_curMode = mode;
|
||||
auto cr = mod->CompileFunction(nullptr, ccode, 0, 0, &func);
|
||||
if (cr < 0) {
|
||||
MessageInfo info;
|
||||
info.mode = mode;
|
||||
info.message = tr("Script failed to build");
|
||||
info.type = MessageType::Error;
|
||||
outputMessage(mode, info);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
mod->AddScriptSection("Runner", ccode, ccode.size(), 0);
|
||||
|
||||
auto r = mod->Build();
|
||||
if (r < 0) {
|
||||
MessageInfo info;
|
||||
info.mode = mode;
|
||||
info.message = tr("Script failed to build");
|
||||
info.type = MessageType::Error;
|
||||
outputMessage(mode, info);
|
||||
outputMessage(info);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Find the main function
|
||||
func = mod->GetFunctionByDecl("void main()");
|
||||
if (func == nullptr) {
|
||||
// Try again with "int main()"
|
||||
func = mod->GetFunctionByDecl("int main()");
|
||||
// Set up a context to execute the script
|
||||
// The context manager will request the context from the
|
||||
// pool, which will automatically attach the debugger
|
||||
asIScriptContext *ctx = _ctxMgr->AddContext(_engine, func, true);
|
||||
_ctx[mode] = ctx;
|
||||
|
||||
asPWORD isDbg = 0;
|
||||
ctx->SetUserData(reinterpret_cast<void *>(isDbg),
|
||||
AsUserDataType::UserData_isDbg);
|
||||
mod->SetUserData(reinterpret_cast<void *>(isDbg),
|
||||
AsUserDataType::UserData_isDbg);
|
||||
|
||||
asPWORD umode = asPWORD(mode);
|
||||
ctx->SetUserData(reinterpret_cast<void *>(umode),
|
||||
AsUserDataType::UserData_ContextMode);
|
||||
|
||||
ctx->SetExceptionCallback(asMETHOD(ScriptMachine, exceptionCallback),
|
||||
this, asCALL_THISCALL);
|
||||
|
||||
// Execute the script until completion
|
||||
// The script may create co-routines. These will automatically
|
||||
// be managed by the context manager
|
||||
while (_ctxMgr->ExecuteScripts()) {
|
||||
qApp->processEvents();
|
||||
}
|
||||
|
||||
if (func == nullptr) {
|
||||
MessageInfo info;
|
||||
info.mode = mode;
|
||||
info.message = tr("Cannot find 'int main()' or 'void main()'");
|
||||
info.type = MessageType::Error;
|
||||
outputMessage(mode, info);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
_ctx[mode] = nullptr;
|
||||
|
||||
// Set up a context to execute the script
|
||||
// The context manager will request the context from the
|
||||
// pool, which will automatically attach the debugger
|
||||
asIScriptContext *ctx = _ctxMgr->AddContext(_engine, func, true);
|
||||
|
||||
asPWORD isDbg = 0;
|
||||
ctx->SetUserData(reinterpret_cast<void *>(isDbg),
|
||||
AsUserDataType::UserData_isDbg);
|
||||
mod->SetUserData(reinterpret_cast<void *>(isDbg),
|
||||
AsUserDataType::UserData_isDbg);
|
||||
|
||||
asPWORD umode = asPWORD(mode);
|
||||
ctx->SetUserData(reinterpret_cast<void *>(umode),
|
||||
AsUserDataType::UserData_ContextMode);
|
||||
|
||||
ctx->SetExceptionCallback(asMETHOD(ScriptMachine, exceptionCallback), this,
|
||||
asCALL_THISCALL);
|
||||
|
||||
// Execute the script until completion
|
||||
// The script may create co-routines. These will automatically
|
||||
// be managed by the context manager
|
||||
while (_ctxMgr->ExecuteScripts())
|
||||
;
|
||||
|
||||
// Check if the main script finished normally
|
||||
int r = ctx->GetState();
|
||||
if (r != asEXECUTION_FINISHED) {
|
||||
if (r == asEXECUTION_EXCEPTION) {
|
||||
MessageInfo info;
|
||||
info.mode = mode;
|
||||
info.message = tr("The script failed with an exception") +
|
||||
QStringLiteral("\n") +
|
||||
QString::fromStdString(GetExceptionInfo(ctx, true));
|
||||
info.type = MessageType::Error;
|
||||
outputMessage(mode, info);
|
||||
r = -1;
|
||||
} else if (r == asEXECUTION_ABORTED) {
|
||||
MessageInfo info;
|
||||
info.mode = mode;
|
||||
info.message = tr("The script was aborted");
|
||||
info.type = MessageType::Error;
|
||||
outputMessage(mode, info);
|
||||
r = -1;
|
||||
// Check if the main script finished normally
|
||||
int r = ctx->GetState();
|
||||
if (r != asEXECUTION_FINISHED) {
|
||||
if (r == asEXECUTION_EXCEPTION) {
|
||||
MessageInfo info;
|
||||
info.mode = mode;
|
||||
info.message =
|
||||
tr("The script failed with an exception") +
|
||||
QStringLiteral("\n") +
|
||||
QString::fromStdString(GetExceptionInfo(ctx, true));
|
||||
info.type = MessageType::Error;
|
||||
outputMessage(info);
|
||||
r = -1;
|
||||
} else if (r == asEXECUTION_ABORTED) {
|
||||
MessageInfo info;
|
||||
info.mode = mode;
|
||||
info.message = tr("The script was aborted");
|
||||
info.type = MessageType::Error;
|
||||
outputMessage(info);
|
||||
r = -1;
|
||||
} else {
|
||||
auto e = QMetaEnum::fromType<asEContextState>();
|
||||
MessageInfo info;
|
||||
info.mode = mode;
|
||||
info.message = tr("The script terminated unexpectedly") +
|
||||
QStringLiteral(" (") + e.valueToKey(r) +
|
||||
QStringLiteral(")");
|
||||
info.type = MessageType::Error;
|
||||
outputMessage(info);
|
||||
r = -1;
|
||||
}
|
||||
} else {
|
||||
auto e = QMetaEnum::fromType<asEContextState>();
|
||||
MessageInfo info;
|
||||
info.message = tr("The script terminated unexpectedly") +
|
||||
QStringLiteral(" (") + e.valueToKey(r) +
|
||||
QStringLiteral(")");
|
||||
info.type = MessageType::Error;
|
||||
outputMessage(mode, info);
|
||||
r = -1;
|
||||
r = 0;
|
||||
}
|
||||
|
||||
func->Release();
|
||||
|
||||
// Return the context after retrieving the return value
|
||||
_ctxMgr->DoneWithContext(ctx);
|
||||
_engine->GarbageCollect();
|
||||
|
||||
return r >= 0;
|
||||
} else {
|
||||
r = 0;
|
||||
if (std::all_of(ret.begin(), ret.end(),
|
||||
[](const QAsCodeParser::CodeSegment &seg) {
|
||||
return seg.type ==
|
||||
QAsCodeParser::SymbolType::Variable;
|
||||
})) {
|
||||
_curMode = mode;
|
||||
|
||||
for (auto &s : ret) {
|
||||
auto r = mod->CompileGlobalVar(nullptr, s.codes, 0);
|
||||
if (r < 0) {
|
||||
MessageInfo info;
|
||||
info.mode = mode;
|
||||
info.message = tr("BadDecl:") + s.codes;
|
||||
info.type = MessageType::Error;
|
||||
outputMessage(info);
|
||||
}
|
||||
}
|
||||
|
||||
if (mod->ResetGlobalVars() < 0) {
|
||||
MessageInfo info;
|
||||
info.mode = mode;
|
||||
info.message = tr("GlobalBadDecl");
|
||||
info.type = MessageType::Error;
|
||||
outputMessage(info);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
MessageInfo info;
|
||||
info.mode = mode;
|
||||
info.message = tr("Script failed to build");
|
||||
info.type = MessageType::Error;
|
||||
outputMessage(info);
|
||||
}
|
||||
|
||||
// Return the context after retrieving the return value
|
||||
_ctxMgr->DoneWithContext(ctx);
|
||||
_engine->GarbageCollect();
|
||||
|
||||
return r >= 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
QString ScriptMachine::scriptGetExceptionInfo() {
|
||||
|
|
|
@ -18,13 +18,15 @@
|
|||
#ifndef SCRIPTMACHINE_H
|
||||
#define SCRIPTMACHINE_H
|
||||
|
||||
#include "AngelScript/sdk/add_on/contextmgr/contextmgr.h"
|
||||
#include "AngelScript/sdk/angelscript/include/angelscript.h"
|
||||
|
||||
#include "class/aspreprocesser.h"
|
||||
|
||||
#include "asdebugger.h"
|
||||
#include "class/ascontextmgr.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <QQueue>
|
||||
|
||||
class ScriptMachine : public QObject {
|
||||
Q_OBJECT
|
||||
|
@ -97,13 +99,14 @@ private:
|
|||
explicit ScriptMachine();
|
||||
Q_DISABLE_COPY_MOVE(ScriptMachine)
|
||||
|
||||
public:
|
||||
private:
|
||||
asIScriptModule *createModule(ConsoleMode mode);
|
||||
asIScriptModule *createModuleIfNotExist(ConsoleMode mode);
|
||||
asIScriptModule *module(ConsoleMode mode);
|
||||
bool isModuleExists(ConsoleMode mode);
|
||||
|
||||
public:
|
||||
asIScriptModule *module(ConsoleMode mode);
|
||||
|
||||
static ScriptMachine &instance();
|
||||
|
||||
virtual ~ScriptMachine();
|
||||
|
@ -111,7 +114,7 @@ public:
|
|||
public:
|
||||
bool init();
|
||||
bool isInited() const;
|
||||
bool isRunning(ConsoleMode mode = Scripting) const;
|
||||
bool isRunning(ConsoleMode mode) const;
|
||||
|
||||
static void registerEngineAddon(asIScriptEngine *engine);
|
||||
static void registerEngineAssert(asIScriptEngine *engine);
|
||||
|
@ -143,7 +146,8 @@ public slots:
|
|||
bool executeScript(ConsoleMode mode, const QString &script,
|
||||
bool isInDebug = false);
|
||||
|
||||
void abortDbgScript(ConsoleMode mode);
|
||||
void abortDbgScript();
|
||||
void abortScript(ConsoleMode mode);
|
||||
void abortScript();
|
||||
|
||||
protected:
|
||||
|
@ -156,7 +160,8 @@ protected:
|
|||
private:
|
||||
void print(void *ref, int typeId);
|
||||
QString getInput();
|
||||
void outputMessage(ConsoleMode mode, const MessageInfo &info);
|
||||
|
||||
void outputMessage(const MessageInfo &info);
|
||||
|
||||
bool isType(asITypeInfo *tinfo, RegisteredType type);
|
||||
|
||||
|
@ -196,11 +201,13 @@ signals:
|
|||
private:
|
||||
asIScriptEngine *_engine = nullptr;
|
||||
asDebugger *_debugger = nullptr;
|
||||
asContextMgr *_ctxMgr = nullptr;
|
||||
QVector<asIScriptContext *> _ctxPool;
|
||||
CContextMgr *_ctxMgr = nullptr;
|
||||
|
||||
QQueue<asIScriptContext *> _ctxPool;
|
||||
|
||||
QVector<asITypeInfo *> _rtypes;
|
||||
QMap<ConsoleMode, RegCallBacks> _regcalls;
|
||||
QMap<ConsoleMode, asIScriptContext *> _ctx;
|
||||
ConsoleMode _curMode = ConsoleMode::Background;
|
||||
};
|
||||
|
||||
|
|
|
@ -250,16 +250,18 @@ ScriptManager::buildUpRibbonScriptRunner(RibbonButtonGroup *group) {
|
|||
}
|
||||
|
||||
void ScriptManager::runScript(const QString &filename) {
|
||||
// ScriptMachine::instance().executeScript(filename);
|
||||
auto &ins = ScriptMachine::instance();
|
||||
if (ins.isRunning(ScriptMachine::Background)) {
|
||||
auto ret = QMessageBox::question(nullptr, tr("ScriptRunning"),
|
||||
tr("ScriptRunningRequestLastStop?"));
|
||||
if (ret == QMessageBox::Yes) {
|
||||
ins.abortScript(ScriptMachine::Background);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Q_ASSERT(_console);
|
||||
// _console->setMode(ScriptingConsole::Output);
|
||||
// _console->stdWarn(tr("Excuting:") + filename);
|
||||
// _console->newLine();
|
||||
// // TODO
|
||||
// // _console->machine()->executeScript(filename);
|
||||
// _console->appendCommandPrompt();
|
||||
// _console->setMode(ScriptingConsole::Input);
|
||||
ins.executeScript(ScriptMachine::Background, filename);
|
||||
}
|
||||
|
||||
QStringList ScriptManager::usrScriptsDbCats() const {
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include "utilities.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QPushButton>
|
||||
#include <QResizeEvent>
|
||||
|
||||
WingMessageBox::WingMessageBox() {}
|
||||
|
@ -161,5 +162,10 @@ WingMessageBox::msgbox(QWidget *parent, QMessageBox::Icon icon,
|
|||
QObject::connect(msgbox, &QMessageBox::finished, &d,
|
||||
&FramelessDialogBase::done);
|
||||
|
||||
return static_cast<QMessageBox::StandardButton>(d.exec());
|
||||
auto ret = d.exec();
|
||||
if (ret == 0) {
|
||||
return msgbox->standardButton(msgbox->defaultButton());
|
||||
}
|
||||
|
||||
return static_cast<QMessageBox::StandardButton>(ret);
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@
|
|||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
constexpr qsizetype FILE_MAX_BUFFER = 0x6400000; // 100MB
|
||||
constexpr qsizetype FILE_MAX_BUFFER = 0x32000000; // 800MB
|
||||
constexpr auto CLONE_LIMIT = 3;
|
||||
|
||||
constexpr auto VIEW_PROPERTY = "__VIEW__";
|
||||
|
@ -121,6 +121,9 @@ EditorView::EditorView(QWidget *parent)
|
|||
m_metadata->setDocument(doc);
|
||||
});
|
||||
|
||||
connect(&_watcher, &QFileSystemWatcher::fileChanged, this,
|
||||
&EditorView::need2Reload);
|
||||
|
||||
applySettings();
|
||||
|
||||
// build up call tables
|
||||
|
@ -284,6 +287,9 @@ ErrFile EditorView::newFile(size_t index) {
|
|||
if (isCloneFile()) {
|
||||
return ErrFile::ClonedFile;
|
||||
}
|
||||
if (!m_fileName.isEmpty()) {
|
||||
_watcher.removePath(m_fileName);
|
||||
}
|
||||
auto istr = QString::number(index);
|
||||
m_fileName = tr("Untitled") + istr;
|
||||
this->setWindowTitle(m_fileName);
|
||||
|
@ -320,6 +326,10 @@ ErrFile EditorView::openFile(const QString &filename) {
|
|||
return ErrFile::Permission;
|
||||
}
|
||||
|
||||
if (!m_fileName.isEmpty()) {
|
||||
_watcher.removePath(m_fileName);
|
||||
}
|
||||
|
||||
m_hex->setDocument(QSharedPointer<QHexDocument>(p));
|
||||
m_hex->setLockedFile(readonly);
|
||||
m_hex->setKeepSize(true);
|
||||
|
@ -335,6 +345,8 @@ ErrFile EditorView::openFile(const QString &filename) {
|
|||
auto tab = this->tabWidget();
|
||||
tab->setIcon(Utilities::getIconFromFile(style(), m_fileName));
|
||||
tab->setToolTip(m_fileName);
|
||||
|
||||
_watcher.addPath(m_fileName);
|
||||
}
|
||||
|
||||
return ErrFile::Success;
|
||||
|
@ -372,6 +384,10 @@ ErrFile EditorView::openExtFile(const QString &ext, const QString &file) {
|
|||
return ErrFile::Error;
|
||||
}
|
||||
|
||||
if (!m_fileName.isEmpty()) {
|
||||
_watcher.removePath(m_fileName);
|
||||
}
|
||||
|
||||
m_hex->setDocument(QSharedPointer<QHexDocument>(p));
|
||||
m_hex->setLockedFile(readonly);
|
||||
m_hex->setKeepSize(true);
|
||||
|
@ -562,10 +578,14 @@ ErrFile EditorView::save(const QString &workSpaceName, const QString &path,
|
|||
file.close();
|
||||
|
||||
if (!isExport) {
|
||||
if (!m_fileName.isEmpty()) {
|
||||
_watcher.removePath(m_fileName);
|
||||
}
|
||||
m_fileName = QFileInfo(fileName).absoluteFilePath();
|
||||
m_isNewFile = false;
|
||||
m_docType = DocumentType::File;
|
||||
doc->setDocSaved();
|
||||
_watcher.addPath(m_fileName);
|
||||
}
|
||||
#ifdef Q_OS_LINUX
|
||||
adjustPermission();
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#ifndef EDITORVIEW_H
|
||||
#define EDITORVIEW_H
|
||||
|
||||
#include <QFileSystemWatcher>
|
||||
#include <QReadWriteLock>
|
||||
#include <QStackedWidget>
|
||||
|
||||
|
@ -553,6 +554,8 @@ signals:
|
|||
void sigOnMetadata();
|
||||
void sigOnBookMark();
|
||||
|
||||
void need2Reload();
|
||||
|
||||
protected:
|
||||
bool eventFilter(QObject *watched, QEvent *event) override;
|
||||
|
||||
|
@ -589,6 +592,7 @@ private:
|
|||
|
||||
QReadWriteLock _rwlock;
|
||||
CallTable _viewFns;
|
||||
QFileSystemWatcher _watcher;
|
||||
};
|
||||
|
||||
#endif // EDITORVIEW_H
|
||||
|
|
|
@ -17,17 +17,20 @@
|
|||
|
||||
#include "scriptingconsole.h"
|
||||
#include "QConsoleWidget/QConsoleIODevice.h"
|
||||
#include "class/logger.h"
|
||||
#include "class/scriptmachine.h"
|
||||
#include "class/scriptsettings.h"
|
||||
#include "class/skinmanager.h"
|
||||
#include "class/wingmessagebox.h"
|
||||
#include "model/codecompletionmodel.h"
|
||||
#include "utilities.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QClipboard>
|
||||
#include <QColor>
|
||||
#include <QIcon>
|
||||
#include <QKeyEvent>
|
||||
#include <QMenu>
|
||||
#include <QMimeData>
|
||||
#include <QRegularExpression>
|
||||
#include <QTextBlock>
|
||||
|
||||
|
@ -84,19 +87,15 @@ void ScriptingConsole::handleReturnKey() {
|
|||
if (iodevice_->isOpen())
|
||||
iodevice_->consoleWidgetInput(code);
|
||||
|
||||
emit consoleCommand(code);
|
||||
if (!_isWaitingRead) {
|
||||
emit consoleCommand(code);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptingConsole::init() {
|
||||
_getInputFn = std::bind(&ScriptingConsole::getInput, this);
|
||||
|
||||
// _sp = new ScriptConsoleMachine(_getInputFn, this);
|
||||
// connect(_sp, &ScriptConsoleMachine::onClearConsole, this,
|
||||
// &ScriptingConsole::clear);
|
||||
// connect(this, &ScriptingConsole::abortEvaluation, _sp,
|
||||
// &ScriptConsoleMachine::abortScript);
|
||||
|
||||
connect(this, &QConsoleWidget::consoleCommand, this,
|
||||
&ScriptingConsole::runConsoleCommand);
|
||||
|
||||
|
@ -107,9 +106,35 @@ void ScriptingConsole::init() {
|
|||
|
||||
void ScriptingConsole::clearConsole() {
|
||||
setMode(Output);
|
||||
|
||||
auto cur = this->textCursor();
|
||||
auto off = cur.position() - this->currentHeaderPos();
|
||||
auto lastCmd = this->currentCommandLine();
|
||||
auto dis = lastCmd.length() - off;
|
||||
|
||||
clear();
|
||||
appendCommandPrompt(lastCommandPrompt());
|
||||
|
||||
if (lastCommandPrompt()) {
|
||||
auto lines = _codes.split('\n');
|
||||
auto pl = lines.begin();
|
||||
appendCommandPrompt(false);
|
||||
writeStdOut(*pl);
|
||||
pl++;
|
||||
for (; pl != lines.end(); pl++) {
|
||||
appendCommandPrompt(true);
|
||||
writeStdOut(*pl);
|
||||
}
|
||||
appendCommandPrompt(true);
|
||||
} else {
|
||||
appendCommandPrompt(false);
|
||||
}
|
||||
|
||||
setMode(Input);
|
||||
replaceCommandLine(lastCmd);
|
||||
cur = this->textCursor();
|
||||
cur.movePosition(QTextCursor::EndOfBlock);
|
||||
cur.movePosition(QTextCursor::Left, QTextCursor::MoveAnchor, dis);
|
||||
setTextCursor(cur);
|
||||
}
|
||||
|
||||
void ScriptingConsole::processKeyEvent(QKeyEvent *e) { keyPressEvent(e); }
|
||||
|
@ -177,7 +202,7 @@ void ScriptingConsole::onOutput(const ScriptMachine::MessageInfo &message) {
|
|||
flush();
|
||||
break;
|
||||
case ScriptMachine::MessageType::Print:
|
||||
if (lastInfo.first != message.type) {
|
||||
if (lastInfo.first != message.type && isNotBlockStart) {
|
||||
newLine();
|
||||
}
|
||||
stdOut(message.message);
|
||||
|
@ -188,6 +213,13 @@ void ScriptingConsole::onOutput(const ScriptMachine::MessageInfo &message) {
|
|||
lastInfo.second = qMakePair(message.row, message.col);
|
||||
}
|
||||
|
||||
void ScriptingConsole::abortCurrentCode() {
|
||||
setMode(Output);
|
||||
_codes.clear();
|
||||
appendCommandPrompt();
|
||||
setMode(Input);
|
||||
}
|
||||
|
||||
void ScriptingConsole::applyScriptSettings() {
|
||||
auto &set = ScriptSettings::instance();
|
||||
auto dfont = QFont(set.consoleFontFamily());
|
||||
|
@ -219,8 +251,96 @@ void ScriptingConsole::applyScriptSettings() {
|
|||
|
||||
void ScriptingConsole::runConsoleCommand(const QString &code) {
|
||||
auto exec = code.trimmed();
|
||||
if (exec.endsWith('\\')) {
|
||||
if (exec == QStringLiteral("#ls")) {
|
||||
auto &ins = ScriptMachine::instance();
|
||||
auto mod = ins.module(ScriptMachine::Interactive);
|
||||
if (mod) {
|
||||
QList<QPair<QByteArray, QByteArray>> vars;
|
||||
auto total = mod->GetGlobalVarCount();
|
||||
|
||||
// generate codes to print
|
||||
QString codes;
|
||||
if (total == 0) {
|
||||
codes = QStringLiteral("print(\"<none>\");");
|
||||
} else {
|
||||
for (asUINT i = 0; i < total; ++i) {
|
||||
const char *name;
|
||||
int typeId;
|
||||
auto decl = mod->GetGlobalVarDeclaration(i);
|
||||
if (decl && mod->GetGlobalVar(i, &name) == asSUCCESS) {
|
||||
vars.emplaceBack(decl, name);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &var : vars) {
|
||||
codes.append("print(\"" + var.first + " = \");print(" +
|
||||
var.second + ");print(\";\\n\");");
|
||||
}
|
||||
}
|
||||
|
||||
setMode(Output);
|
||||
ScriptMachine::instance().executeCode(ScriptMachine::Interactive,
|
||||
codes);
|
||||
_codes.clear();
|
||||
appendCommandPrompt();
|
||||
setMode(Input);
|
||||
}
|
||||
} else if (exec.startsWith(QStringLiteral("#del"))) {
|
||||
// this is special command
|
||||
auto &ins = ScriptMachine::instance();
|
||||
auto mod = ins.module(ScriptMachine::Interactive);
|
||||
if (mod) {
|
||||
// first check whether contains \n
|
||||
auto idx = exec.indexOf('\n');
|
||||
if (idx >= 0) {
|
||||
setMode(Output);
|
||||
stdErr(tr("InvalidDelCmd"));
|
||||
} else {
|
||||
// ok, then tokens should be devided by the space
|
||||
exec.remove(0, 4);
|
||||
auto vars = exec.split(' ', Qt::SkipEmptyParts);
|
||||
|
||||
QList<asUINT> indices;
|
||||
|
||||
// then check
|
||||
setMode(Output);
|
||||
for (auto &v : vars) {
|
||||
auto idx = mod->GetGlobalVarIndexByName(v.toUtf8());
|
||||
if (idx >= 0) {
|
||||
indices.append(idx);
|
||||
} else {
|
||||
stdWarn(tr("NotFoundIgnore:") + v);
|
||||
}
|
||||
}
|
||||
|
||||
std::sort(indices.begin(), indices.end(), std::greater<int>());
|
||||
|
||||
// ok, remove
|
||||
for (auto &i : indices) {
|
||||
mod->RemoveGlobalVar(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
_codes.clear();
|
||||
appendCommandPrompt();
|
||||
setMode(Input);
|
||||
} else if (exec == QStringLiteral("#cls")) {
|
||||
auto &ins = ScriptMachine::instance();
|
||||
auto mod = ins.module(ScriptMachine::Interactive);
|
||||
if (mod) {
|
||||
auto total = mod->GetGlobalVarCount();
|
||||
asUINT i = total;
|
||||
do {
|
||||
--i;
|
||||
mod->RemoveGlobalVar(i);
|
||||
} while (i);
|
||||
}
|
||||
_codes.clear();
|
||||
appendCommandPrompt();
|
||||
setMode(Input);
|
||||
} else if (exec.endsWith('\\')) {
|
||||
static QRegularExpression ex(QStringLiteral("[\\\\\\s]+$"));
|
||||
_codes.append('\n');
|
||||
_codes += exec.remove(ex);
|
||||
setMode(Output);
|
||||
appendCommandPrompt(true);
|
||||
|
@ -228,10 +348,8 @@ void ScriptingConsole::runConsoleCommand(const QString &code) {
|
|||
} else {
|
||||
setMode(Output);
|
||||
_codes += exec;
|
||||
if (!ScriptMachine::instance().executeCode(ScriptMachine::Interactive,
|
||||
_codes)) {
|
||||
// WingMessageBox::
|
||||
}
|
||||
ScriptMachine::instance().executeCode(ScriptMachine::Interactive,
|
||||
_codes);
|
||||
_codes.clear();
|
||||
appendCommandPrompt();
|
||||
setMode(Input);
|
||||
|
@ -239,11 +357,18 @@ void ScriptingConsole::runConsoleCommand(const QString &code) {
|
|||
}
|
||||
|
||||
QString ScriptingConsole::getInput() {
|
||||
auto &s = consoleStream();
|
||||
appendCommandPrompt(true);
|
||||
setMode(Input);
|
||||
consoleStream().device()->waitForReadyRead(-1);
|
||||
s.status();
|
||||
auto d = s.device();
|
||||
auto ba = d->bytesAvailable();
|
||||
d->skip(ba);
|
||||
_isWaitingRead = true;
|
||||
d->waitForReadyRead(-1);
|
||||
QString instr;
|
||||
consoleStream() >> instr;
|
||||
s >> instr;
|
||||
_isWaitingRead = false;
|
||||
setMode(Output);
|
||||
return instr;
|
||||
}
|
||||
|
@ -275,8 +400,49 @@ void ScriptingConsole::onCompletion(const QModelIndex &index) {
|
|||
}
|
||||
}
|
||||
|
||||
void ScriptingConsole::paste() {
|
||||
if (ScriptMachine::instance().isRunning(ScriptMachine::Interactive)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const QMimeData *const clipboard = QApplication::clipboard()->mimeData();
|
||||
const QString text = clipboard->text();
|
||||
if (!text.isEmpty()) {
|
||||
if (text.indexOf('\n') < 0) {
|
||||
replaceCommandLine(text);
|
||||
} else {
|
||||
auto ret = WingMessageBox::question(
|
||||
nullptr, tr("MultiCodeCanNotUndo"), text);
|
||||
if (ret == QMessageBox::No) {
|
||||
return;
|
||||
}
|
||||
auto lines = text.split('\n');
|
||||
if (lines.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
setMode(Output);
|
||||
auto pl = lines.begin();
|
||||
auto pend = std::prev(lines.end());
|
||||
writeStdOut(*pl);
|
||||
pl++;
|
||||
for (; pl != pend; pl++) {
|
||||
appendCommandPrompt(true);
|
||||
writeStdOut(*pl);
|
||||
}
|
||||
appendCommandPrompt(true);
|
||||
setMode(Input);
|
||||
replaceCommandLine(*pl);
|
||||
lines.removeLast();
|
||||
_codes = lines.join('\n');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QString ScriptingConsole::currentCodes() const {
|
||||
return _codes + currentCommandLine();
|
||||
QTextCursor textCursor = this->textCursor();
|
||||
textCursor.setPosition(inpos_, QTextCursor::KeepAnchor);
|
||||
return _codes + textCursor.selectedText();
|
||||
}
|
||||
|
||||
void ScriptingConsole::contextMenuEvent(QContextMenuEvent *event) {
|
||||
|
@ -291,6 +457,15 @@ void ScriptingConsole::contextMenuEvent(QContextMenuEvent *event) {
|
|||
menu.addAction(QIcon(QStringLiteral(":/qeditor/paste.png")), tr("Paste"),
|
||||
QKeySequence(QKeySequence::Paste), this,
|
||||
&ScriptingConsole::paste);
|
||||
menu.addAction(ICONRES(QStringLiteral("del")), tr("Clear"),
|
||||
QKeySequence(Qt::ControlModifier | Qt::Key_L), this,
|
||||
&ScriptingConsole::clearConsole);
|
||||
menu.addSeparator();
|
||||
menu.addAction(ICONRES(QStringLiteral("dbgstop")), tr("AbortScript"),
|
||||
QKeySequence(Qt::ControlModifier | Qt::Key_Q), []() {
|
||||
ScriptMachine::instance().abortScript(
|
||||
ScriptMachine::Background);
|
||||
});
|
||||
|
||||
menu.exec(event->globalPos());
|
||||
}
|
||||
|
|
|
@ -49,6 +49,8 @@ public slots:
|
|||
|
||||
void onOutput(const ScriptMachine::MessageInfo &message);
|
||||
|
||||
void abortCurrentCode();
|
||||
|
||||
private slots:
|
||||
void applyScriptSettings();
|
||||
|
||||
|
@ -64,10 +66,12 @@ protected:
|
|||
|
||||
protected slots:
|
||||
virtual void onCompletion(const QModelIndex &index) override;
|
||||
virtual void paste() override;
|
||||
|
||||
private:
|
||||
QString _codes;
|
||||
|
||||
bool _isWaitingRead = false;
|
||||
std::function<QString(void)> _getInputFn;
|
||||
};
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ enum class CrashCode : int {
|
|||
AlreadyStart,
|
||||
LanguageFile,
|
||||
PluginSetting,
|
||||
ScriptInitFailed,
|
||||
GenericCallNotSupported
|
||||
};
|
||||
|
||||
|
|
|
@ -40,6 +40,7 @@
|
|||
#include "class/wingmessagebox.h"
|
||||
#include "class/wingupdater.h"
|
||||
#include "control/toast.h"
|
||||
#include "define.h"
|
||||
#include "encodingdialog.h"
|
||||
#include "fileinfodialog.h"
|
||||
#include "finddialog.h"
|
||||
|
@ -245,7 +246,8 @@ MainWindow::MainWindow(SplashDialog *splash) : FramelessMainWindow() {
|
|||
sm.registerCallBack(ScriptMachine::Interactive, callbacks);
|
||||
|
||||
callbacks.getInputFn = [this]() -> QString {
|
||||
return WingInputDialog::getText(this, tr(""), tr(""));
|
||||
return WingInputDialog::getText(this, tr("InputRequest"),
|
||||
tr("PleaseInput"));
|
||||
};
|
||||
callbacks.clearFn = [this]() { m_bgScriptOutput->clear(); };
|
||||
callbacks.printMsgFn =
|
||||
|
@ -253,7 +255,11 @@ MainWindow::MainWindow(SplashDialog *splash) : FramelessMainWindow() {
|
|||
std::placeholders::_1);
|
||||
sm.registerCallBack(ScriptMachine::Background, callbacks);
|
||||
} else {
|
||||
// TODO
|
||||
QMessageBox::critical(this, qAppName(),
|
||||
tr("ScriptEngineInitFailed"));
|
||||
set.setScriptEnabled(false);
|
||||
set.save(SettingManager::SCRIPT);
|
||||
throw CrashCode::ScriptInitFailed;
|
||||
}
|
||||
|
||||
// At this time, AngelScript service plugin has started
|
||||
|
@ -1010,6 +1016,15 @@ MainWindow::buildUpScriptConsoleDock(ads::CDockManager *dock,
|
|||
showStatus(QStringLiteral("<b><font color=\"gold\">") +
|
||||
content + QStringLiteral("</font></b>"));
|
||||
});
|
||||
connect(m_scriptConsole, &ScriptingConsole::abortEvaluation, this,
|
||||
[this]() {
|
||||
auto &sm = ScriptMachine::instance();
|
||||
if (sm.isRunning(ScriptMachine::Interactive)) {
|
||||
sm.abortScript(ScriptMachine::Interactive);
|
||||
} else {
|
||||
m_scriptConsole->abortCurrentCode();
|
||||
}
|
||||
});
|
||||
|
||||
auto dw = buildDockWidget(dock, QStringLiteral("ScriptConsole"),
|
||||
tr("ScriptConsole"), m_scriptConsole);
|
||||
|
@ -1021,8 +1036,32 @@ MainWindow::buildUpScriptBgOutputDock(ads::CDockManager *dock,
|
|||
ads::DockWidgetArea area,
|
||||
ads::CDockAreaWidget *areaw) {
|
||||
m_bgScriptOutput = new QPlainTextEdit(this);
|
||||
m_bgScriptOutput->setPlaceholderText(tr("BgScriptOutputHere"));
|
||||
m_bgScriptOutput->setReadOnly(true);
|
||||
|
||||
auto a = newAction(
|
||||
ICONRES(QStringLiteral("mStr")), tr("SelectAll"),
|
||||
[this]() { m_bgScriptOutput->selectAll(); }, QKeySequence::SelectAll);
|
||||
m_bgScriptOutput->addAction(a);
|
||||
a = newAction(
|
||||
ICONRES(QStringLiteral("copy")), tr("Copy"),
|
||||
[this]() { m_bgScriptOutput->copy(); }, QKeySequence::Copy);
|
||||
m_bgScriptOutput->addAction(a);
|
||||
a = newAction(ICONRES(QStringLiteral("del")), tr("Clear"),
|
||||
[this]() { m_bgScriptOutput->clear(); });
|
||||
m_bgScriptOutput->addAction(a);
|
||||
a = new QAction(this);
|
||||
a->setSeparator(true);
|
||||
m_bgScriptOutput->addAction(a);
|
||||
a = newAction(
|
||||
ICONRES(QStringLiteral("dbgstop")), tr("AbortScript"),
|
||||
[]() {
|
||||
ScriptMachine::instance().abortScript(ScriptMachine::Background);
|
||||
},
|
||||
QKeySequence(Qt::ControlModifier | Qt::Key_Q));
|
||||
m_bgScriptOutput->addAction(a);
|
||||
m_bgScriptOutput->setContextMenuPolicy(Qt::ActionsContextMenu);
|
||||
|
||||
auto dw = buildDockWidget(dock, QStringLiteral("BgScriptOutput"),
|
||||
tr("BgScriptOutput"), m_bgScriptOutput);
|
||||
return dock->addDockWidget(area, dw, areaw);
|
||||
|
@ -3162,6 +3201,35 @@ void MainWindow::connectEditorView(EditorView *editor) {
|
|||
connect(editor, &EditorView::sigOnPasteHex, this, &MainWindow::on_pastehex);
|
||||
connect(editor, &EditorView::sigOnPasteFile, this,
|
||||
&MainWindow::on_pastefile);
|
||||
|
||||
editor->setProperty("__RELOAD__", false);
|
||||
connect(editor, &EditorView::need2Reload, this, [editor, this]() {
|
||||
if (editor->isBigFile()) {
|
||||
auto fileName = editor->fileName();
|
||||
if (!QFile::exists(fileName)) {
|
||||
editor->raise();
|
||||
WingMessageBox::critical(this, tr("Error"),
|
||||
tr("FileCloseBigFile"));
|
||||
closeEditor(editor, true);
|
||||
}
|
||||
if (currentEditor() == editor) {
|
||||
editor->reload();
|
||||
} else {
|
||||
editor->setProperty("__RELOAD__", true);
|
||||
}
|
||||
} else {
|
||||
editor->hexEditor()->document()->setDocSaved(false);
|
||||
if (currentEditor() == editor) {
|
||||
auto ret = WingMessageBox::question(this, tr("Reload"),
|
||||
tr("ReloadNeededYesOrNo"));
|
||||
if (ret == QMessageBox::Yes) {
|
||||
editor->reload();
|
||||
}
|
||||
} else {
|
||||
editor->setProperty("__RELOAD__", true);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void MainWindow::swapEditor(EditorView *old, EditorView *cur) {
|
||||
|
@ -3188,6 +3256,19 @@ void MainWindow::swapEditor(EditorView *old, EditorView *cur) {
|
|||
|
||||
Q_ASSERT(cur);
|
||||
auto hexeditor = cur->hexEditor();
|
||||
auto needReload = cur->property("__RELOAD__").toBool();
|
||||
if (needReload) {
|
||||
if (cur->isBigFile()) {
|
||||
cur->reload();
|
||||
} else {
|
||||
auto ret = WingMessageBox::question(this, tr("Reload"),
|
||||
tr("ReloadNeededYesOrNo"));
|
||||
if (ret == QMessageBox::Yes) {
|
||||
cur->reload();
|
||||
}
|
||||
}
|
||||
cur->setProperty("__RELOAD__", false);
|
||||
}
|
||||
connect(hexeditor, &QHexView::cursorLocationChanged, this,
|
||||
&MainWindow::on_locChanged);
|
||||
connect(hexeditor, &QHexView::cursorSelectionChanged, this,
|
||||
|
@ -3786,13 +3867,15 @@ void MainWindow::closeEvent(QCloseEvent *event) {
|
|||
}
|
||||
|
||||
// then checking the scripting dialog
|
||||
if (!m_scriptDialog->about2Close()) {
|
||||
event->ignore();
|
||||
return;
|
||||
}
|
||||
if (m_scriptDialog) {
|
||||
if (!m_scriptDialog->about2Close()) {
|
||||
event->ignore();
|
||||
return;
|
||||
}
|
||||
|
||||
// then abort all script running
|
||||
ScriptMachine::instance().abortScript();
|
||||
// then abort all script running
|
||||
ScriptMachine::instance().abortScript();
|
||||
}
|
||||
|
||||
// then checking itself
|
||||
if (!m_views.isEmpty()) {
|
||||
|
@ -3843,9 +3926,11 @@ void MainWindow::closeEvent(QCloseEvent *event) {
|
|||
auto &set = SettingManager::instance();
|
||||
set.setDockLayout(m_dock->saveState());
|
||||
|
||||
m_scriptDialog->saveDockLayout();
|
||||
set.setRecentFiles(m_recentmanager->saveRecent());
|
||||
set.save();
|
||||
if (m_scriptDialog) {
|
||||
m_scriptDialog->saveDockLayout();
|
||||
set.setRecentFiles(m_recentmanager->saveRecent());
|
||||
set.save();
|
||||
}
|
||||
|
||||
PluginSystem::instance().destory();
|
||||
|
||||
|
|
|
@ -506,7 +506,7 @@ RibbonTabContent *ScriptingDialog::buildDebugPage(RibbonTabContent *tab) {
|
|||
bool isDbg = false;
|
||||
bool isPaused = false;
|
||||
|
||||
isRun = runner.isRunning();
|
||||
isRun = runner.isRunning(ScriptMachine::Scripting);
|
||||
isDbg = runner.isDebugMode();
|
||||
auto dbg = runner.debugger();
|
||||
isPaused = dbg->currentState() == asDebugger::PAUSE;
|
||||
|
@ -740,7 +740,8 @@ void ScriptingDialog::registerEditorView(ScriptEditor *editor) {
|
|||
Q_ASSERT(m_views.contains(editor));
|
||||
|
||||
auto &m = ScriptMachine::instance();
|
||||
if (m.isRunning() && _DebugingEditor == editor) {
|
||||
if (m.isRunning(ScriptMachine::Scripting) &&
|
||||
_DebugingEditor == editor) {
|
||||
if (WingMessageBox::warning(
|
||||
this, this->windowTitle(), tr("ScriptStillRunning"),
|
||||
QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) {
|
||||
|
@ -881,7 +882,7 @@ void ScriptingDialog::updateRunDebugMode(bool disable) {
|
|||
bool isDbg = false;
|
||||
bool isPaused = false;
|
||||
|
||||
isRun = runner.isRunning();
|
||||
isRun = runner.isRunning(ScriptMachine::Scripting);
|
||||
isDbg = runner.isDebugMode();
|
||||
auto dbg = runner.debugger();
|
||||
isPaused = dbg->currentState() == asDebugger::PAUSE;
|
||||
|
@ -1437,7 +1438,7 @@ void ScriptingDialog::on_removebreakpoint() {
|
|||
|
||||
void ScriptingDialog::closeEvent(QCloseEvent *event) {
|
||||
auto &runner = ScriptMachine::instance();
|
||||
if (runner.isRunning()) {
|
||||
if (runner.isRunning(ScriptMachine::Scripting)) {
|
||||
if (WingMessageBox::warning(
|
||||
this, this->windowTitle(), tr("ScriptStillRunning"),
|
||||
QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) {
|
||||
|
|
Loading…
Reference in New Issue