Compare commits

..

4 Commits

38 changed files with 2723 additions and 2109 deletions

View File

@ -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();

View File

@ -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

View File

@ -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;

View File

@ -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; }

View File

@ -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

View File

@ -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"

View File

@ -1,6 +1,6 @@
{
"Id" : "WingCStruct",
"Version" : "0.0.1",
"Version" : "0.0.2",
"Vendor" : "WingCloudStudio",
"Author" : "wingsummer",
"License" : "AGPL-v3.0",

View File

@ -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;
}

View File

@ -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);

View File

@ -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;
};

View File

@ -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;

View File

@ -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_

View File

@ -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;
}
}

View File

@ -28,6 +28,9 @@ public:
explicit AsConsoleCompletion(ScriptingConsole *p);
virtual ~AsConsoleCompletion() = default;
protected:
virtual QList<CodeInfoTip> parseDocument();
private:
ScriptingConsole *_console;
};

View File

@ -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;
}

View File

@ -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

View File

@ -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;

View File

@ -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:

View File

@ -36,6 +36,7 @@ public:
Variable,
Property = Variable,
Enumerater,
TypeDef
};
enum CacheIndex {

View File

@ -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()) {

View File

@ -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_;

View File

@ -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();

View File

@ -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 ||

View File

@ -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;

View File

@ -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, &section);
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() {

View File

@ -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;
};

View File

@ -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 {

View File

@ -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);
}

View File

@ -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();

View File

@ -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

View File

@ -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());
}

View File

@ -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;
};

View File

@ -5,6 +5,7 @@ enum class CrashCode : int {
AlreadyStart,
LanguageFile,
PluginSetting,
ScriptInitFailed,
GenericCallNotSupported
};

View File

@ -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();

View File

@ -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) {