fix: 更好的代码填充和一些 Bug 修复;

This commit is contained in:
寂静的羽夏 2025-04-27 11:31:15 +08:00
parent 2ee3051a7d
commit cf3d4da8e8
24 changed files with 1811 additions and 1657 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();

View File

@ -77,6 +77,8 @@ public:
// get the current command line
QString getCommandLine();
virtual void paste();
public slots:
// write to StandardOutput
@ -94,6 +96,7 @@ signals:
protected:
bool canPaste() const;
bool canCut() const;
virtual void handleReturnKey();
virtual void handleTabKey();
// reimp QPlainTextEdit functions

View File

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

@ -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();
@ -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({}); }
@ -235,10 +268,7 @@ bool AsCompletion::processTrigger(const QString &trigger,
return true;
}
QList<CodeInfoTip> docNodes;
if (m_parseDocument) {
docNodes = parseDocument();
}
QList<CodeInfoTip> docNodes = parseDocument();
// if trigger is empty, it's making editing
if (trigger.isEmpty()) {
@ -383,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());
@ -396,99 +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.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);
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,9 +34,6 @@ public:
public:
virtual QString wordSeperators() const override;
bool parseDocument() const;
void setParseDocument(bool newParseDocument);
void clearFunctionTip();
protected:
@ -43,6 +42,9 @@ protected:
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,46 @@
#include "asconsolecompletion.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 main(){").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 {};
}
auto data = prepc.scriptData();
if (data.size() == 1) {
auto d = data[0];
return parseScriptData(d.script.length() - 1, d.script);
} else {
return {};
}
}

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

@ -456,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
@ -1070,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;
@ -1090,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 =
@ -1148,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;
}
@ -1171,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;
}
@ -1208,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;
@ -1244,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;
@ -1371,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;
}
@ -1392,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()) {
@ -1547,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;
}
@ -1564,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

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

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;
@ -389,9 +388,10 @@ bool ScriptMachine::executeScript(ConsoleMode mode, const QString &script,
outputMessage(mode, 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;
@ -405,9 +405,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,8 +423,11 @@ 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();
@ -479,13 +484,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;
@ -1805,16 +1821,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,28 +1830,30 @@ bool ScriptMachine::executeCode(ConsoleMode mode, const QString &code) {
asIScriptFunction *func = nullptr;
auto ret = parser.parse(ccode);
// check whether there is any enum/class
if (std::find_if(ret.begin(), ret.end(),
[](const QAsCodeParser::CodeSegment &seg) {
return seg.isValid();
}) == ret.end()) {
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) {
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) {
auto cr = mod->CompileFunction(nullptr, ccode, 0, 0, &func);
if (cr < 0) {
MessageInfo info;
info.mode = mode;
info.message = tr("Script failed to build");
@ -1854,85 +1862,98 @@ bool ScriptMachine::executeCode(ConsoleMode mode, const QString &code) {
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(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;
} 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;
}
} 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 (ret.length() == 1) {
auto s = ret.first();
if (s.type == QAsCodeParser::SymbolType::Variable) {
auto r = mod->CompileGlobalVar(nullptr, ccode, 0);
if (r < 0 || mod->ResetGlobalVars() < 0) {
MessageInfo info;
info.mode = mode;
info.message = tr("GlobalBadDecl");
info.type = MessageType::Error;
outputMessage(mode, info);
return false;
}
return true;
}
}
MessageInfo info;
info.mode = mode;
info.message = tr("ScriptRunUnsupported");
info.type = MessageType::Error;
outputMessage(mode, 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,7 +99,7 @@ private:
explicit ScriptMachine();
Q_DISABLE_COPY_MOVE(ScriptMachine)
public:
private:
asIScriptModule *createModule(ConsoleMode mode);
asIScriptModule *createModuleIfNotExist(ConsoleMode mode);
asIScriptModule *module(ConsoleMode mode);
@ -143,7 +145,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:
@ -196,11 +199,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

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

View File

@ -17,7 +17,6 @@
#include "scriptingconsole.h"
#include "QConsoleWidget/QConsoleIODevice.h"
#include "class/logger.h"
#include "class/scriptmachine.h"
#include "class/scriptsettings.h"
#include "class/skinmanager.h"
@ -84,19 +83,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);
@ -177,7 +172,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);
@ -219,7 +214,13 @@ void ScriptingConsole::applyScriptSettings() {
void ScriptingConsole::runConsoleCommand(const QString &code) {
auto exec = code.trimmed();
if (exec.endsWith('\\')) {
if (exec == QStringLiteral("#ls")) {
} else if (exec == QStringLiteral("#del")) {
} else if (exec == QStringLiteral("#cls")) {
} else if (exec.endsWith('\\')) {
static QRegularExpression ex(QStringLiteral("[\\\\\\s]+$"));
_codes += exec.remove(ex);
setMode(Output);
@ -228,10 +229,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 +238,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;
}

View File

@ -68,6 +68,7 @@ protected slots:
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"
@ -254,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
@ -1011,6 +1016,10 @@ MainWindow::buildUpScriptConsoleDock(ads::CDockManager *dock,
showStatus(QStringLiteral("<b><font color=\"gold\">") +
content + QStringLiteral("</font></b>"));
});
connect(
m_scriptConsole, &ScriptingConsole::abortEvaluation, this, [this]() {
ScriptMachine::instance().abortScript(ScriptMachine::Interactive);
});
auto dw = buildDockWidget(dock, QStringLiteral("ScriptConsole"),
tr("ScriptConsole"), m_scriptConsole);
@ -3167,16 +3176,29 @@ void MainWindow::connectEditorView(EditorView *editor) {
editor->setProperty("__RELOAD__", false);
connect(editor, &EditorView::need2Reload, this, [editor, this]() {
if (editor->isBigFile()) {
editor->reload();
}
if (currentEditor() == editor) {
auto ret = WingMessageBox::question(this, tr("Reload"),
tr("ReloadNeededYesOrNo"));
if (ret == QMessageBox::Yes) {
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->setProperty("__RELOAD__", true);
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);
}
}
});
}
@ -3207,10 +3229,14 @@ void MainWindow::swapEditor(EditorView *old, EditorView *cur) {
auto hexeditor = cur->hexEditor();
auto needReload = cur->property("__RELOAD__").toBool();
if (needReload) {
auto ret = WingMessageBox::question(this, tr("Reload"),
tr("ReloadNeededYesOrNo"));
if (ret == QMessageBox::Yes) {
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);
}