feat: 完善调试器;增加契合 QT 框架的 AngelScript 预处理器;还有一些修复;
fix: 修复在 Linux 本地驱动器查找问题;修复在 Linux 下最近文件无法保存的问题;
|
@ -9,6 +9,6 @@ jobs:
|
|||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
- name: cmake-format lint
|
||||
uses: Wing-summer/cmake-format-action@main
|
||||
uses: neg-c/cmake-format-action@v0.1.1
|
||||
with:
|
||||
inplace: true
|
||||
|
|
|
@ -33,6 +33,7 @@ jobs:
|
|||
host: ${{ matrix.os == 'windows-latest' && 'windows' || 'linux' }}
|
||||
target: 'desktop'
|
||||
arch: ${{ matrix.arch }}
|
||||
cache: true
|
||||
|
||||
- name: Configure CMake
|
||||
# Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make.
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 318776e428741cd17087a13a2ddf6c83a05e24e0
|
||||
Subproject commit b3e7421d4a55ce85124db2e2ae712040cc758f0a
|
|
@ -72,13 +72,16 @@ QLineMarksInfoCenter::~QLineMarksInfoCenter() {}
|
|||
/*!
|
||||
\return the list of line marks set on a given file
|
||||
*/
|
||||
QLineMarkList QLineMarksInfoCenter::marks(const QString &file) {
|
||||
QLineMarkList QLineMarksInfoCenter::marks(const QString &file, int filterID) {
|
||||
QLineMarkList l;
|
||||
bool check = !file.isEmpty();
|
||||
|
||||
foreach (QLineMarkHandle m, m_lineMarks) {
|
||||
if (!check || (m.file == file))
|
||||
l << QLineMark(file, m.line->line() + 1, m.mark);
|
||||
for (auto &m : m_lineMarks) {
|
||||
if (!check || (m.file == file)) {
|
||||
if (filterID < 0 || (filterID >= 0 && m.mark == filterID)) {
|
||||
l << QLineMark(file, m.line->line() + 1, m.mark);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return l;
|
||||
|
@ -88,14 +91,16 @@ QLineMarkList QLineMarksInfoCenter::marks(const QString &file) {
|
|||
\brief Remove all line marks on all files
|
||||
*/
|
||||
void QLineMarksInfoCenter::clear() {
|
||||
foreach (QLineMarkHandle m, m_lineMarks) { removeLineMark(m); }
|
||||
for (auto &m : m_lineMarks) {
|
||||
removeLineMark(m);
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief Remove all line marks on a given file
|
||||
*/
|
||||
void QLineMarksInfoCenter::removeMarks(const QString &file) {
|
||||
foreach (QLineMarkHandle m, m_lineMarks)
|
||||
for (auto &m : m_lineMarks)
|
||||
if (m.file == file)
|
||||
removeLineMark(m);
|
||||
}
|
||||
|
@ -481,15 +486,9 @@ QString QLineMarksInfoCenter::priority(const QStringList &marks) {
|
|||
*/
|
||||
QList<QStringList> QLineMarksInfoCenter::marksLayout(const QString &context) {
|
||||
QList<QStringList> l;
|
||||
|
||||
foreach (QString id, availableMarkTypes(context)) { l << QStringList(id); }
|
||||
|
||||
/*
|
||||
foreach ( QLineMarkType t, availableMarks(context) )
|
||||
{
|
||||
|
||||
for (auto &id : availableMarkTypes(context)) {
|
||||
l << QStringList(id);
|
||||
}
|
||||
*/
|
||||
|
||||
return l;
|
||||
}
|
||||
|
|
|
@ -124,7 +124,7 @@ public:
|
|||
static QLineMarksInfoCenter *instance();
|
||||
static void destroy();
|
||||
|
||||
QLineMarkList marks(const QString &file = QString());
|
||||
QLineMarkList marks(const QString &file = QString(), int filterID = -1);
|
||||
|
||||
QString markTypeId(int id);
|
||||
int markTypeId(const QString &id);
|
||||
|
|
|
@ -35,12 +35,10 @@ if(MSVC)
|
|||
string(APPEND CMAKE_C_FLAGS " /utf-8")
|
||||
endif()
|
||||
|
||||
set(QWINDOWKIT_BUILD_STATIC
|
||||
TRUE
|
||||
CACHE BOOL "Build static libraries")
|
||||
set(QWINDOWKIT_INSTALL
|
||||
OFF
|
||||
CACHE BOOL "Install library")
|
||||
set(CMAKE_POLICY_DEFAULT_CMP0077 NEW)
|
||||
option(QWINDOWKIT_BUILD_STATIC "Build static libraries" TRUE)
|
||||
option(QWINDOWKIT_INSTALL "Install library" OFF)
|
||||
set(ADS_VERSION 4.3.1)
|
||||
|
||||
add_subdirectory(3rdparty/qwindowkit)
|
||||
add_subdirectory(3rdparty/QHexView)
|
||||
|
@ -63,8 +61,6 @@ set(ANGEL_SCRIPT_ADDON
|
|||
${ANGEL_SCRIPT_ADDON_ROOT}/scriptany/scriptany.h
|
||||
${ANGEL_SCRIPT_ADDON_ROOT}/scriptarray/scriptarray.cpp
|
||||
${ANGEL_SCRIPT_ADDON_ROOT}/scriptarray/scriptarray.h
|
||||
${ANGEL_SCRIPT_ADDON_ROOT}/scriptbuilder/scriptbuilder.cpp
|
||||
${ANGEL_SCRIPT_ADDON_ROOT}/scriptbuilder/scriptbuilder.h
|
||||
${ANGEL_SCRIPT_ADDON_ROOT}/scriptfile/scriptfile.cpp
|
||||
${ANGEL_SCRIPT_ADDON_ROOT}/scriptfile/scriptfile.h
|
||||
${ANGEL_SCRIPT_ADDON_ROOT}/scriptfile/scriptfilesystem.cpp
|
||||
|
@ -207,6 +203,8 @@ set(CLASS_SRC
|
|||
src/class/angelscripthelper.h
|
||||
src/class/ascompletion.cpp
|
||||
src/class/ascompletion.h
|
||||
src/class/asbuilder.h
|
||||
src/class/asbuilder.cpp
|
||||
src/class/qaslexer.cpp
|
||||
src/class/qaslexer.h
|
||||
src/class/qasparser.cpp
|
||||
|
@ -313,7 +311,7 @@ set(TRANSLATION_PATH
|
|||
${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/QJsonModel
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/QPathEdit
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/qcodeedit2
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/Qt-Advanced-Docking-System
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/Qt-Advanced-Docking-System/src
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/QWingRibbon
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/SingleApplication
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src)
|
||||
|
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 33 KiB |
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
|
@ -0,0 +1,978 @@
|
|||
#include "asbuilder.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
|
||||
BEGIN_AS_NAMESPACE
|
||||
|
||||
asBuilder::asBuilder(QObject *parent) : QObject(parent) {
|
||||
engine = nullptr;
|
||||
module = nullptr;
|
||||
|
||||
includeCallback = nullptr;
|
||||
includeParam = nullptr;
|
||||
|
||||
pragmaCallback = nullptr;
|
||||
pragmaParam = nullptr;
|
||||
}
|
||||
|
||||
void asBuilder::SetIncludeCallback(INCLUDECALLBACK_t callback,
|
||||
void *userParam) {
|
||||
includeCallback = callback;
|
||||
includeParam = userParam;
|
||||
}
|
||||
|
||||
void asBuilder::SetPragmaCallback(PRAGMACALLBACK_t callback, void *userParam) {
|
||||
pragmaCallback = callback;
|
||||
pragmaParam = userParam;
|
||||
}
|
||||
|
||||
int asBuilder::StartNewModule(asIScriptEngine *inEngine,
|
||||
const char *moduleName) {
|
||||
if (inEngine == nullptr)
|
||||
return -1;
|
||||
|
||||
engine = inEngine;
|
||||
module = inEngine->GetModule(moduleName, asGM_ALWAYS_CREATE);
|
||||
if (module == nullptr)
|
||||
return -1;
|
||||
|
||||
ClearAll();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
asIScriptEngine *asBuilder::GetEngine() { return engine; }
|
||||
|
||||
asIScriptModule *asBuilder::GetModule() { return module; }
|
||||
|
||||
unsigned int asBuilder::GetSectionCount() const {
|
||||
return (unsigned int)(includedScripts.size());
|
||||
}
|
||||
|
||||
QString asBuilder::GetSectionName(unsigned int idx) const {
|
||||
if (idx >= includedScripts.size())
|
||||
return {};
|
||||
|
||||
return includedScripts.at(idx);
|
||||
}
|
||||
|
||||
// Returns 1 if the section was included
|
||||
// Returns 0 if the section was not included because it had already been
|
||||
// included before Returns <0 if there was an error
|
||||
int asBuilder::AddSectionFromFile(const QString &filename) {
|
||||
// The file name stored in the set should be the fully resolved name because
|
||||
// it is possible to name the same file in multiple ways using relative
|
||||
// paths.
|
||||
auto fullpath = QFileInfo(filename).absoluteFilePath();
|
||||
|
||||
if (IncludeIfNotAlreadyIncluded(fullpath)) {
|
||||
int r = LoadScriptSection(fullpath);
|
||||
if (r < 0)
|
||||
return r;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Returns 1 if the section was included
|
||||
// Returns 0 if the section was not included because it had already been
|
||||
// included before Returns <0 if there was an error
|
||||
int asBuilder::AddSectionFromMemory(const char *sectionName,
|
||||
const char *scriptCode,
|
||||
unsigned int scriptLength, int lineOffset) {
|
||||
if (IncludeIfNotAlreadyIncluded(sectionName)) {
|
||||
int r = ProcessScriptSection(scriptCode, scriptLength, sectionName,
|
||||
lineOffset);
|
||||
if (r < 0)
|
||||
return r;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int asBuilder::BuildModule() { return Build(); }
|
||||
|
||||
void asBuilder::DefineWord(const QString &word) {
|
||||
if (!definedWords.contains(word)) {
|
||||
definedWords.append(word);
|
||||
}
|
||||
}
|
||||
|
||||
void asBuilder::ClearAll() {
|
||||
includedScripts.clear();
|
||||
|
||||
#if AS_PROCESS_METADATA == 1
|
||||
currentClass.clear();
|
||||
currentNamespace.clear();
|
||||
|
||||
foundDeclarations.clear();
|
||||
typeMetadataMap.clear();
|
||||
funcMetadataMap.clear();
|
||||
varMetadataMap.clear();
|
||||
#endif
|
||||
}
|
||||
|
||||
bool asBuilder::IncludeIfNotAlreadyIncluded(const QString &filename) {
|
||||
if (includedScripts.contains(filename)) {
|
||||
// Already included
|
||||
return false;
|
||||
}
|
||||
|
||||
// Add the file to the set of included sections
|
||||
includedScripts.append(filename);
|
||||
return true;
|
||||
}
|
||||
|
||||
int asBuilder::LoadScriptSection(const QString &filename) {
|
||||
// Open the script file
|
||||
|
||||
QFile f(filename);
|
||||
|
||||
if (!f.open(QFile::ReadOnly)) {
|
||||
// Write a message to the engine's message callback
|
||||
auto msg = tr("Failed to open script file ") + QStringLiteral("'") +
|
||||
QFileInfo(filename).absoluteFilePath() + QStringLiteral("'");
|
||||
engine->WriteMessage(filename.toUtf8(), 0, 0, asMSGTYPE_ERROR,
|
||||
msg.toUtf8());
|
||||
|
||||
// TODO: Write the file where this one was included from
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Read the entire file
|
||||
auto code = f.readAll();
|
||||
f.close();
|
||||
|
||||
// Process the script section even if it is zero length so that the name is
|
||||
// registered
|
||||
return ProcessScriptSection(code, code.length(), filename, 0);
|
||||
}
|
||||
|
||||
int asBuilder::ProcessScriptSection(const QByteArray &script, int length,
|
||||
const QString §ionname,
|
||||
int lineOffset) {
|
||||
QVector<QString> includes;
|
||||
|
||||
// Perform a superficial parsing of the script first to store the metadata
|
||||
if (length)
|
||||
modifiedScript = script.left(length);
|
||||
else
|
||||
modifiedScript = script;
|
||||
|
||||
// First perform the checks for #if directives to exclude code that
|
||||
// shouldn't be compiled
|
||||
unsigned int pos = 0;
|
||||
int nested = 0;
|
||||
while (pos < modifiedScript.size()) {
|
||||
asUINT len = 0;
|
||||
asETokenClass t = engine->ParseToken(modifiedScript.data() + pos,
|
||||
modifiedScript.size() - pos, &len);
|
||||
if (t == asTC_UNKNOWN && modifiedScript[pos] == '#' &&
|
||||
(pos + 1 < modifiedScript.size())) {
|
||||
int start = pos++;
|
||||
|
||||
// Is this an #if directive?
|
||||
t = engine->ParseToken(modifiedScript.data() + pos,
|
||||
modifiedScript.size() - pos, &len);
|
||||
Q_UNUSED(t);
|
||||
|
||||
QByteArray token = modifiedScript.mid(pos, len);
|
||||
pos += len;
|
||||
|
||||
if (token == "if") {
|
||||
t = engine->ParseToken(modifiedScript.data() + pos,
|
||||
modifiedScript.size() - pos, &len);
|
||||
if (t == asTC_WHITESPACE) {
|
||||
pos += len;
|
||||
t = engine->ParseToken(modifiedScript.data() + pos,
|
||||
modifiedScript.size() - pos, &len);
|
||||
}
|
||||
|
||||
if (t == asTC_IDENTIFIER) {
|
||||
QByteArray word = modifiedScript.mid(pos, len);
|
||||
|
||||
// Overwrite the #if directive with space characters to
|
||||
// avoid compiler error
|
||||
pos += len;
|
||||
OverwriteCode(start, pos - start);
|
||||
|
||||
// Has this identifier been defined by the application or
|
||||
// not?
|
||||
if (!definedWords.contains(word)) {
|
||||
// Exclude all the code until and including the #endif
|
||||
pos = ExcludeCode(pos);
|
||||
} else {
|
||||
nested++;
|
||||
}
|
||||
}
|
||||
} else if (token == "endif") {
|
||||
// Only remove the #endif if there was a matching #if
|
||||
if (nested > 0) {
|
||||
OverwriteCode(start, pos - start);
|
||||
nested--;
|
||||
}
|
||||
}
|
||||
} else
|
||||
pos += len;
|
||||
}
|
||||
|
||||
#if AS_PROCESS_METADATA == 1
|
||||
// Preallocate memory
|
||||
QString name, declaration;
|
||||
QVector<QString> metadata;
|
||||
declaration.reserve(100);
|
||||
#endif
|
||||
|
||||
// Then check for meta data and pre-processor directives
|
||||
pos = 0;
|
||||
while (pos < modifiedScript.size()) {
|
||||
asUINT len = 0;
|
||||
asETokenClass t = engine->ParseToken(modifiedScript.data() + pos,
|
||||
modifiedScript.size() - pos, &len);
|
||||
if (t == asTC_COMMENT || t == asTC_WHITESPACE) {
|
||||
pos += len;
|
||||
continue;
|
||||
}
|
||||
QString token = modifiedScript.mid(pos, len);
|
||||
|
||||
#if AS_PROCESS_METADATA == 1
|
||||
// Skip possible decorators before class and interface declarations
|
||||
if (token == "shared" || token == "abstract" || token == "mixin" ||
|
||||
token == "external") {
|
||||
pos += len;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if class or interface so the metadata for members can be
|
||||
// gathered
|
||||
if (currentClass.isEmpty() &&
|
||||
(token == "class" || token == "interface")) {
|
||||
// Get the identifier after "class"
|
||||
do {
|
||||
pos += len;
|
||||
if (pos >= modifiedScript.size()) {
|
||||
t = asTC_UNKNOWN;
|
||||
break;
|
||||
}
|
||||
t = engine->ParseToken(modifiedScript.data() + pos,
|
||||
modifiedScript.size() - pos, &len);
|
||||
} while (t == asTC_COMMENT || t == asTC_WHITESPACE);
|
||||
|
||||
if (t == asTC_IDENTIFIER) {
|
||||
currentClass = modifiedScript.mid(pos, len);
|
||||
|
||||
// Search until first { or ; is encountered
|
||||
while (pos < modifiedScript.length()) {
|
||||
engine->ParseToken(modifiedScript.data() + pos,
|
||||
modifiedScript.size() - pos, &len);
|
||||
|
||||
// If start of class section encountered stop
|
||||
if (modifiedScript[pos] == '{') {
|
||||
pos += len;
|
||||
break;
|
||||
} else if (modifiedScript[pos] == ';') {
|
||||
// The class declaration has ended and there are no
|
||||
// children
|
||||
currentClass.clear();
|
||||
pos += len;
|
||||
break;
|
||||
}
|
||||
|
||||
// Check next symbol
|
||||
pos += len;
|
||||
}
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if end of class
|
||||
if (currentClass != "" && token == "}") {
|
||||
currentClass = "";
|
||||
pos += len;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if namespace so the metadata for members can be gathered
|
||||
if (token == "namespace") {
|
||||
// Get the scope after "namespace". It can be composed of multiple
|
||||
// nested namespaces, e.g. A::B::C
|
||||
do {
|
||||
do {
|
||||
pos += len;
|
||||
t = engine->ParseToken(modifiedScript.data() + pos,
|
||||
modifiedScript.size() - pos, &len);
|
||||
} while (t == asTC_COMMENT || t == asTC_WHITESPACE);
|
||||
|
||||
if (t == asTC_IDENTIFIER) {
|
||||
if (currentNamespace != "")
|
||||
currentNamespace += "::";
|
||||
currentNamespace += modifiedScript.mid(pos, len);
|
||||
}
|
||||
} while (
|
||||
t == asTC_IDENTIFIER ||
|
||||
(t == asTC_KEYWORD && modifiedScript.mid(pos, len) == "::"));
|
||||
|
||||
// Search until first { is encountered
|
||||
while (pos < modifiedScript.length()) {
|
||||
engine->ParseToken(modifiedScript.data() + pos,
|
||||
modifiedScript.size() - pos, &len);
|
||||
|
||||
// If start of namespace section encountered stop
|
||||
if (modifiedScript[pos] == '{') {
|
||||
pos += len;
|
||||
break;
|
||||
}
|
||||
|
||||
// Check next symbol
|
||||
pos += len;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if end of namespace
|
||||
if (currentNamespace != "" && token == "}") {
|
||||
size_t found = currentNamespace.lastIndexOf("::");
|
||||
if (found >= 0) {
|
||||
currentNamespace.remove(found, currentNamespace.size() - found);
|
||||
} else {
|
||||
currentNamespace = "";
|
||||
}
|
||||
pos += len;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Is this the start of metadata?
|
||||
if (token == "[") {
|
||||
// Get the metadata string
|
||||
pos = ExtractMetadata(pos, metadata);
|
||||
|
||||
// Determine what this metadata is for
|
||||
int type;
|
||||
ExtractDeclaration(pos, name, declaration, type);
|
||||
|
||||
// Store away the declaration in a map for lookup after the build
|
||||
// has completed
|
||||
if (type > 0) {
|
||||
SMetadataDecl decl(metadata, name, declaration, type,
|
||||
currentClass, currentNamespace);
|
||||
foundDeclarations.push_back(decl);
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
// Is this a preprocessor directive?
|
||||
if (token == "#" && (pos + 1 < modifiedScript.size())) {
|
||||
int start = pos++;
|
||||
|
||||
t = engine->ParseToken(modifiedScript.data() + pos,
|
||||
modifiedScript.size() - pos, &len);
|
||||
if (t == asTC_IDENTIFIER) {
|
||||
token = modifiedScript.mid(pos, len);
|
||||
if (token == "include") {
|
||||
pos += len;
|
||||
t = engine->ParseToken(modifiedScript.data() + pos,
|
||||
modifiedScript.size() - pos,
|
||||
&len);
|
||||
if (t == asTC_WHITESPACE) {
|
||||
pos += len;
|
||||
t = engine->ParseToken(modifiedScript.data() + pos,
|
||||
modifiedScript.size() - pos,
|
||||
&len);
|
||||
}
|
||||
|
||||
if (t == asTC_VALUE && len > 2 &&
|
||||
(modifiedScript[pos] == '"' ||
|
||||
modifiedScript[pos] == '\'')) {
|
||||
// Get the include file
|
||||
QString includefile =
|
||||
modifiedScript.mid(pos + 1, len - 2);
|
||||
pos += len;
|
||||
|
||||
// Make sure the includeFile doesn't contain any
|
||||
// line breaks
|
||||
auto p = includefile.indexOf('\n');
|
||||
if (p >= 0) {
|
||||
// TODO: Show the correct line number for the
|
||||
// error
|
||||
auto str = tr("Invalid file name for #include; "
|
||||
"it contains a line-break: ") +
|
||||
QStringLiteral("'") +
|
||||
includefile.left(p) +
|
||||
QStringLiteral("'");
|
||||
engine->WriteMessage(sectionname.toUtf8(), 0, 0,
|
||||
asMSGTYPE_ERROR,
|
||||
str.toUtf8());
|
||||
} else {
|
||||
// Store it for later processing
|
||||
includes.push_back(includefile);
|
||||
|
||||
// Overwrite the include directive with space
|
||||
// characters to avoid compiler error
|
||||
OverwriteCode(start, pos - start);
|
||||
}
|
||||
}
|
||||
} else if (token == "pragma") {
|
||||
// Read until the end of the line
|
||||
pos += len;
|
||||
for (; pos < modifiedScript.size() &&
|
||||
modifiedScript[pos] != '\n';
|
||||
pos++)
|
||||
;
|
||||
|
||||
// Call the pragma callback
|
||||
auto pragmaText =
|
||||
modifiedScript.mid(start + 7, pos - start - 7);
|
||||
int r =
|
||||
pragmaCallback
|
||||
? pragmaCallback(pragmaText, this, pragmaParam)
|
||||
: -1;
|
||||
if (r < 0) {
|
||||
// TODO: Report the correct line number
|
||||
engine->WriteMessage(
|
||||
sectionname.toUtf8(), 0, 0, asMSGTYPE_ERROR,
|
||||
tr("Invalid #pragma directive").toUtf8());
|
||||
return r;
|
||||
}
|
||||
|
||||
// Overwrite the pragma directive with space characters
|
||||
// to avoid compiler error
|
||||
OverwriteCode(start, pos - start);
|
||||
}
|
||||
} else {
|
||||
// Check for lines starting with #!, e.g. shebang
|
||||
// interpreter directive. These will be treated as comments
|
||||
// and removed by the preprocessor
|
||||
if (modifiedScript[pos] == '!') {
|
||||
// Read until the end of the line
|
||||
pos += len;
|
||||
for (; pos < modifiedScript.size() &&
|
||||
modifiedScript[pos] != '\n';
|
||||
pos++)
|
||||
;
|
||||
|
||||
// Overwrite the directive with space characters to
|
||||
// avoid compiler error
|
||||
OverwriteCode(start, pos - start);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Don't search for metadata/includes within statement blocks or
|
||||
// between tokens in statements
|
||||
else {
|
||||
pos = SkipStatement(pos);
|
||||
}
|
||||
}
|
||||
|
||||
// Build the actual script
|
||||
engine->SetEngineProperty(asEP_COPY_SCRIPT_SECTIONS, true);
|
||||
module->AddScriptSection(sectionname.toUtf8(), modifiedScript.data(),
|
||||
modifiedScript.size(), lineOffset);
|
||||
|
||||
if (includes.size() > 0) {
|
||||
// If the callback has been set, then call it for each included file
|
||||
if (includeCallback) {
|
||||
for (QVector<QString>::size_type n = 0; n < includes.size(); n++) {
|
||||
int r = includeCallback(includes[n], sectionname, this,
|
||||
includeParam);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
} else {
|
||||
// By default we try to load the included file from the relative
|
||||
// directory of the current file
|
||||
|
||||
// Determine the path of the current script so that we can resolve
|
||||
// relative paths for includes
|
||||
auto path = QFileInfo(sectionname).filePath();
|
||||
|
||||
// Load the included scripts
|
||||
for (QVector<QString>::size_type n = 0; n < includes.size(); n++) {
|
||||
// If the include is a relative path, then prepend the path of
|
||||
// the originating script
|
||||
|
||||
auto inc = includes.at(n);
|
||||
if (!QFileInfo(inc).isAbsolute()) {
|
||||
includes[n] = path + QDir::separator() + inc;
|
||||
}
|
||||
|
||||
// Include the script section
|
||||
int r = AddSectionFromFile(includes[n]);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int asBuilder::Build() {
|
||||
int r = module->Build();
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
#if AS_PROCESS_METADATA == 1
|
||||
// After the script has been built, the metadata strings should be
|
||||
// stored for later lookup by function id, type id, and variable index
|
||||
for (int n = 0; n < (int)foundDeclarations.size(); n++) {
|
||||
SMetadataDecl &decl = foundDeclarations[n];
|
||||
module->SetDefaultNamespace(decl.nameSpace.toUtf8());
|
||||
if (decl.type == MDT_TYPE) {
|
||||
// Find the type id
|
||||
int typeId = module->GetTypeIdByDecl(decl.declaration.toUtf8());
|
||||
Q_ASSERT(typeId >= 0);
|
||||
if (typeId >= 0)
|
||||
typeMetadataMap.insert(typeId, decl.metadata);
|
||||
} else if (decl.type == MDT_FUNC) {
|
||||
if (decl.parentClass.isEmpty()) {
|
||||
// Find the function id
|
||||
asIScriptFunction *func =
|
||||
module->GetFunctionByDecl(decl.declaration.toUtf8());
|
||||
Q_ASSERT(func);
|
||||
if (func)
|
||||
funcMetadataMap.insert(func->GetId(), decl.metadata);
|
||||
} else {
|
||||
// Find the method id
|
||||
int typeId = module->GetTypeIdByDecl(decl.parentClass.toUtf8());
|
||||
Q_ASSERT(typeId > 0);
|
||||
|
||||
auto it = classMetadataMap.find(typeId);
|
||||
if (it == classMetadataMap.end()) {
|
||||
classMetadataMap.insert(typeId,
|
||||
SClassMetadata(decl.parentClass));
|
||||
it = classMetadataMap.find(typeId);
|
||||
}
|
||||
|
||||
asITypeInfo *type = engine->GetTypeInfoById(typeId);
|
||||
asIScriptFunction *func =
|
||||
type->GetMethodByDecl(decl.declaration.toUtf8());
|
||||
Q_ASSERT(func);
|
||||
if (func)
|
||||
it.value().funcMetadataMap.insert(func->GetId(),
|
||||
decl.metadata);
|
||||
}
|
||||
} else if (decl.type == MDT_VIRTPROP) {
|
||||
if (decl.parentClass.isEmpty()) {
|
||||
// Find the global virtual property accessors
|
||||
asIScriptFunction *func = module->GetFunctionByName(
|
||||
(QStringLiteral("get_") + decl.declaration).toUtf8());
|
||||
if (func)
|
||||
funcMetadataMap.insert(func->GetId(), decl.metadata);
|
||||
func = module->GetFunctionByName(
|
||||
(QStringLiteral("set_") + decl.declaration).toUtf8());
|
||||
if (func)
|
||||
funcMetadataMap.insert(func->GetId(), decl.metadata);
|
||||
} else {
|
||||
// Find the method virtual property accessors
|
||||
int typeId = module->GetTypeIdByDecl(decl.parentClass.toUtf8());
|
||||
Q_ASSERT(typeId > 0);
|
||||
auto it = classMetadataMap.find(typeId);
|
||||
if (it == classMetadataMap.end()) {
|
||||
classMetadataMap.insert(typeId,
|
||||
SClassMetadata(decl.parentClass));
|
||||
it = classMetadataMap.find(typeId);
|
||||
}
|
||||
|
||||
asITypeInfo *type = engine->GetTypeInfoById(typeId);
|
||||
asIScriptFunction *func = type->GetMethodByName(
|
||||
(QStringLiteral("get_") + decl.declaration).toUtf8());
|
||||
if (func)
|
||||
it.value().funcMetadataMap.insert(func->GetId(),
|
||||
decl.metadata);
|
||||
func = type->GetMethodByName(
|
||||
(QStringLiteral("set_") + decl.declaration).toUtf8());
|
||||
if (func)
|
||||
it.value().funcMetadataMap.insert(func->GetId(),
|
||||
decl.metadata);
|
||||
}
|
||||
} else if (decl.type == MDT_VAR) {
|
||||
if (decl.parentClass.isEmpty()) {
|
||||
// Find the global variable index
|
||||
int varIdx =
|
||||
module->GetGlobalVarIndexByName(decl.declaration.toUtf8());
|
||||
Q_ASSERT(varIdx >= 0);
|
||||
if (varIdx >= 0)
|
||||
varMetadataMap.insert(varIdx, decl.metadata);
|
||||
} else {
|
||||
int typeId = module->GetTypeIdByDecl(decl.parentClass.toUtf8());
|
||||
Q_ASSERT(typeId > 0);
|
||||
|
||||
// Add the classes if needed
|
||||
auto it = classMetadataMap.find(typeId);
|
||||
if (it == classMetadataMap.end()) {
|
||||
classMetadataMap.insert(typeId,
|
||||
SClassMetadata(decl.parentClass));
|
||||
it = classMetadataMap.find(typeId);
|
||||
}
|
||||
|
||||
// Add the variable to class
|
||||
asITypeInfo *objectType = engine->GetTypeInfoById(typeId);
|
||||
int idx = -1;
|
||||
|
||||
// Search through all properties to get proper declaration
|
||||
for (asUINT i = 0; i < (asUINT)objectType->GetPropertyCount();
|
||||
++i) {
|
||||
const char *name;
|
||||
objectType->GetProperty(i, &name);
|
||||
if (decl.declaration == name) {
|
||||
idx = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If found, add it
|
||||
Q_ASSERT(idx >= 0);
|
||||
if (idx >= 0)
|
||||
it.value().varMetadataMap.insert(idx, decl.metadata);
|
||||
}
|
||||
} else if (decl.type == MDT_FUNC_OR_VAR) {
|
||||
if (decl.parentClass.isEmpty()) {
|
||||
// Find the global variable index
|
||||
int varIdx =
|
||||
module->GetGlobalVarIndexByName(decl.name.toUtf8());
|
||||
if (varIdx >= 0)
|
||||
varMetadataMap.insert(varIdx, decl.metadata);
|
||||
else {
|
||||
asIScriptFunction *func =
|
||||
module->GetFunctionByDecl(decl.declaration.toUtf8());
|
||||
Q_ASSERT(func);
|
||||
if (func)
|
||||
funcMetadataMap.insert(func->GetId(), decl.metadata);
|
||||
}
|
||||
} else {
|
||||
int typeId = module->GetTypeIdByDecl(decl.parentClass.toUtf8());
|
||||
Q_ASSERT(typeId > 0);
|
||||
|
||||
// Add the classes if needed
|
||||
auto it = classMetadataMap.find(typeId);
|
||||
if (it == classMetadataMap.end()) {
|
||||
classMetadataMap.insert(typeId,
|
||||
SClassMetadata(decl.parentClass));
|
||||
it = classMetadataMap.find(typeId);
|
||||
}
|
||||
|
||||
// Add the variable to class
|
||||
asITypeInfo *objectType = engine->GetTypeInfoById(typeId);
|
||||
int idx = -1;
|
||||
|
||||
// Search through all properties to get proper declaration
|
||||
for (asUINT i = 0; i < (asUINT)objectType->GetPropertyCount();
|
||||
++i) {
|
||||
const char *name;
|
||||
objectType->GetProperty(i, &name);
|
||||
if (decl.name == name) {
|
||||
idx = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If found, add it
|
||||
if (idx >= 0)
|
||||
it.value().varMetadataMap.insert(idx, decl.metadata);
|
||||
else {
|
||||
// Look for the matching method instead
|
||||
asITypeInfo *type = engine->GetTypeInfoById(typeId);
|
||||
asIScriptFunction *func =
|
||||
type->GetMethodByDecl(decl.declaration.toUtf8());
|
||||
if (func)
|
||||
it.value().funcMetadataMap.insert(func->GetId(),
|
||||
decl.metadata);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
module->SetDefaultNamespace("");
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int asBuilder::SkipStatement(int pos) {
|
||||
asUINT len = 0;
|
||||
|
||||
// Skip until ; or { whichever comes first
|
||||
while (pos < (int)modifiedScript.length() && modifiedScript[pos] != ';' &&
|
||||
modifiedScript[pos] != '{') {
|
||||
engine->ParseToken(modifiedScript.data() + pos,
|
||||
modifiedScript.size() - pos, &len);
|
||||
pos += len;
|
||||
}
|
||||
|
||||
// Skip entire statement block
|
||||
if (pos < (int)modifiedScript.length() && modifiedScript[pos] == '{') {
|
||||
pos += 1;
|
||||
|
||||
// Find the end of the statement block
|
||||
int level = 1;
|
||||
while (level > 0 && pos < (int)modifiedScript.size()) {
|
||||
asETokenClass t = engine->ParseToken(
|
||||
modifiedScript.data() + pos, modifiedScript.size() - pos, &len);
|
||||
if (t == asTC_KEYWORD) {
|
||||
if (modifiedScript[pos] == '{')
|
||||
level++;
|
||||
else if (modifiedScript[pos] == '}')
|
||||
level--;
|
||||
}
|
||||
|
||||
pos += len;
|
||||
}
|
||||
} else
|
||||
pos += 1;
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
// Overwrite all code with blanks until the matching #endif
|
||||
int asBuilder::ExcludeCode(int pos) {
|
||||
asUINT len = 0;
|
||||
int nested = 0;
|
||||
while (pos < (int)modifiedScript.size()) {
|
||||
engine->ParseToken(modifiedScript.data() + pos,
|
||||
modifiedScript.size() - pos, &len);
|
||||
if (modifiedScript[pos] == '#') {
|
||||
modifiedScript[pos] = ' ';
|
||||
pos++;
|
||||
|
||||
// Is it an #if or #endif directive?
|
||||
engine->ParseToken(modifiedScript.data() + pos,
|
||||
modifiedScript.size() - pos, &len);
|
||||
QString token = modifiedScript.mid(pos, len);
|
||||
OverwriteCode(pos, len);
|
||||
|
||||
if (token == "if") {
|
||||
nested++;
|
||||
} else if (token == "endif") {
|
||||
if (nested-- == 0) {
|
||||
pos += len;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (modifiedScript[pos] != '\n') {
|
||||
OverwriteCode(pos, len);
|
||||
}
|
||||
pos += len;
|
||||
}
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
// Overwrite all characters except line breaks with blanks
|
||||
void asBuilder::OverwriteCode(int start, int len) {
|
||||
auto code = modifiedScript.data() + start;
|
||||
for (int n = 0; n < len; n++) {
|
||||
if (*code != '\n')
|
||||
*code = ' ';
|
||||
code++;
|
||||
}
|
||||
}
|
||||
|
||||
#if AS_PROCESS_METADATA == 1
|
||||
int asBuilder::ExtractMetadata(int pos, QVector<QString> &metadata) {
|
||||
metadata.clear();
|
||||
|
||||
// Extract all metadata. They can be separated by whitespace and comments
|
||||
for (;;) {
|
||||
QString metadataString;
|
||||
|
||||
// Overwrite the metadata with space characters to allow compilation
|
||||
modifiedScript[pos] = ' ';
|
||||
|
||||
// Skip opening brackets
|
||||
pos += 1;
|
||||
|
||||
int level = 1;
|
||||
asUINT len = 0;
|
||||
while (level > 0 && pos < (int)modifiedScript.size()) {
|
||||
asETokenClass t = engine->ParseToken(
|
||||
modifiedScript.data() + pos, modifiedScript.size() - pos, &len);
|
||||
if (t == asTC_KEYWORD) {
|
||||
if (modifiedScript[pos] == '[')
|
||||
level++;
|
||||
else if (modifiedScript[pos] == ']')
|
||||
level--;
|
||||
}
|
||||
|
||||
// Copy the metadata to our buffer
|
||||
if (level > 0)
|
||||
metadataString.append(modifiedScript.mid(pos, len));
|
||||
|
||||
// Overwrite the metadata with space characters to allow compilation
|
||||
if (t != asTC_WHITESPACE)
|
||||
OverwriteCode(pos, len);
|
||||
|
||||
pos += len;
|
||||
}
|
||||
|
||||
metadata.push_back(metadataString);
|
||||
|
||||
// Check for more metadata. Possibly separated by comments
|
||||
asETokenClass t = engine->ParseToken(modifiedScript.data() + pos,
|
||||
modifiedScript.size() - pos, &len);
|
||||
while (t == asTC_COMMENT || t == asTC_WHITESPACE) {
|
||||
pos += len;
|
||||
t = engine->ParseToken(modifiedScript.data() + pos,
|
||||
modifiedScript.size() - pos, &len);
|
||||
}
|
||||
|
||||
if (modifiedScript[pos] != '[')
|
||||
break;
|
||||
}
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
int asBuilder::ExtractDeclaration(int pos, QString &name, QString &declaration,
|
||||
int &type) {
|
||||
declaration.clear();
|
||||
type = 0;
|
||||
|
||||
int start = pos;
|
||||
|
||||
QString token;
|
||||
asUINT len = 0;
|
||||
asETokenClass t = asTC_WHITESPACE;
|
||||
|
||||
// Skip white spaces, comments, and leading decorators
|
||||
do {
|
||||
pos += len;
|
||||
t = engine->ParseToken(modifiedScript.data() + pos,
|
||||
modifiedScript.size() - pos, &len);
|
||||
token = modifiedScript.mid(pos, len);
|
||||
} while (t == asTC_WHITESPACE || t == asTC_COMMENT || token == "private" ||
|
||||
token == "protected" || token == "shared" || token == "external" ||
|
||||
token == "final" || token == "abstract");
|
||||
|
||||
// We're expecting, either a class, interface, function, or variable
|
||||
// declaration
|
||||
if (t == asTC_KEYWORD || t == asTC_IDENTIFIER) {
|
||||
token = modifiedScript.mid(pos, len);
|
||||
if (token == "interface" || token == "class" || token == "enum") {
|
||||
// Skip white spaces and comments
|
||||
do {
|
||||
pos += len;
|
||||
t = engine->ParseToken(modifiedScript.data() + pos,
|
||||
modifiedScript.size() - pos, &len);
|
||||
} while (t == asTC_WHITESPACE || t == asTC_COMMENT);
|
||||
|
||||
if (t == asTC_IDENTIFIER) {
|
||||
type = MDT_TYPE;
|
||||
declaration = modifiedScript.mid(pos, len);
|
||||
pos += len;
|
||||
return pos;
|
||||
}
|
||||
} else {
|
||||
// For function declarations, store everything up to the start of
|
||||
// the statement block, except for succeeding decorators (final,
|
||||
// override, etc)
|
||||
|
||||
// For variable declaration store just the name as there can only be
|
||||
// one
|
||||
|
||||
// We'll only know if the declaration is a variable or function
|
||||
// declaration when we see the statement block, or absense of a
|
||||
// statement block.
|
||||
bool hasParenthesis = false;
|
||||
int nestedParenthesis = 0;
|
||||
declaration.append(modifiedScript.mid(pos, len));
|
||||
pos += len;
|
||||
for (; pos < (int)modifiedScript.size();) {
|
||||
t = engine->ParseToken(modifiedScript.data() + pos,
|
||||
modifiedScript.size() - pos, &len);
|
||||
token = modifiedScript.mid(pos, len);
|
||||
if (t == asTC_KEYWORD) {
|
||||
if (token == "{" && nestedParenthesis == 0) {
|
||||
if (hasParenthesis) {
|
||||
// We've found the end of a function signature
|
||||
type = MDT_FUNC;
|
||||
} else {
|
||||
// We've found a virtual property. Just keep the
|
||||
// name
|
||||
declaration = name;
|
||||
type = MDT_VIRTPROP;
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
if ((token == "=" && !hasParenthesis) || token == ";") {
|
||||
if (hasParenthesis) {
|
||||
// The declaration is ambigous. It can be a variable
|
||||
// with initialization, or a function prototype
|
||||
type = MDT_FUNC_OR_VAR;
|
||||
} else {
|
||||
// Substitute the declaration with just the name
|
||||
declaration = name;
|
||||
type = MDT_VAR;
|
||||
}
|
||||
return pos;
|
||||
} else if (token == "(") {
|
||||
nestedParenthesis++;
|
||||
|
||||
// This is the first parenthesis we encounter. If the
|
||||
// parenthesis isn't followed by a statement block, then
|
||||
// this is a variable declaration, in which case we
|
||||
// should only store the type and name of the variable,
|
||||
// not the initialization parameters.
|
||||
hasParenthesis = true;
|
||||
} else if (token == ")") {
|
||||
nestedParenthesis--;
|
||||
}
|
||||
} else if (t == asTC_IDENTIFIER) {
|
||||
// If a parenthesis is already found then the name is
|
||||
// already known so it must not be overwritten
|
||||
if (!hasParenthesis)
|
||||
name = token;
|
||||
}
|
||||
|
||||
// Skip trailing decorators
|
||||
if (!hasParenthesis || nestedParenthesis > 0 ||
|
||||
t != asTC_IDENTIFIER ||
|
||||
(token != "final" && token != "override" &&
|
||||
token != "delete" && token != "property"))
|
||||
declaration += token;
|
||||
|
||||
pos += len;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return start;
|
||||
}
|
||||
|
||||
QVector<QString> asBuilder::GetMetadataForType(int typeId) {
|
||||
return typeMetadataMap.value(typeId);
|
||||
}
|
||||
|
||||
QVector<QString> asBuilder::GetMetadataForFunc(asIScriptFunction *func) {
|
||||
return funcMetadataMap.value(func->GetId());
|
||||
}
|
||||
|
||||
QVector<QString> asBuilder::GetMetadataForVar(int varIdx) {
|
||||
return varMetadataMap.value(varIdx);
|
||||
}
|
||||
|
||||
QVector<QString> asBuilder::GetMetadataForTypeProperty(int typeId, int varIdx) {
|
||||
if (classMetadataMap.contains(typeId)) {
|
||||
return varMetadataMap.value(varIdx);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
QVector<QString>
|
||||
asBuilder::GetMetadataForTypeMethod(int typeId, asIScriptFunction *method) {
|
||||
if (method) {
|
||||
if (classMetadataMap.contains(typeId)) {
|
||||
return funcMetadataMap.value(method->GetId());
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
#endif
|
||||
|
||||
END_AS_NAMESPACE
|
|
@ -0,0 +1,199 @@
|
|||
#ifndef ASBUILDER_H
|
||||
#define ASBUILDER_H
|
||||
|
||||
//---------------------------
|
||||
// Compilation settings
|
||||
//
|
||||
|
||||
// Set this flag to turn on/off metadata processing
|
||||
// 0 = off
|
||||
// 1 = on
|
||||
#ifndef AS_PROCESS_METADATA
|
||||
#define AS_PROCESS_METADATA 1
|
||||
#endif
|
||||
|
||||
// TODO: Implement flags for turning on/off include directives and conditional
|
||||
// programming
|
||||
|
||||
//---------------------------
|
||||
// Declaration
|
||||
//
|
||||
|
||||
#ifndef ANGELSCRIPT_H
|
||||
// Avoid having to inform include path if header is already include before
|
||||
#include <angelscript.h>
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER) && _MSC_VER <= 1200
|
||||
// disable the annoying warnings on MSVC 6
|
||||
#pragma warning(disable : 4786)
|
||||
#endif
|
||||
|
||||
#include <QMap>
|
||||
#include <QObject>
|
||||
#include <QSet>
|
||||
#include <QVector>
|
||||
|
||||
BEGIN_AS_NAMESPACE
|
||||
|
||||
class asBuilder;
|
||||
|
||||
// This callback will be called for each #include directive encountered by the
|
||||
// builder. The callback should call the AddSectionFromFile or
|
||||
// AddSectionFromMemory to add the included section to the script. If the
|
||||
// include cannot be resolved then the function should return a negative value
|
||||
// to abort the compilation.
|
||||
typedef int (*INCLUDECALLBACK_t)(const QString &include, const QString &from,
|
||||
asBuilder *builder, void *userParam);
|
||||
|
||||
// This callback will be called for each #pragma directive encountered by the
|
||||
// builder. The application can interpret the pragmaText and decide what do to
|
||||
// based on that. If the callback returns a negative value the builder will
|
||||
// report an error and abort the compilation.
|
||||
typedef int (*PRAGMACALLBACK_t)(const QByteArray &pragmaText,
|
||||
asBuilder *builder, void *userParam);
|
||||
|
||||
// Helper class for loading and pre-processing script files to
|
||||
// support include directives and metadata declarations
|
||||
|
||||
class asBuilder : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit asBuilder(QObject *parent = nullptr);
|
||||
|
||||
// Start a new module
|
||||
int StartNewModule(asIScriptEngine *engine, const char *moduleName);
|
||||
|
||||
// Load a script section from a file on disk
|
||||
// Returns 1 if the file was included
|
||||
// 0 if the file had already been included before
|
||||
// <0 on error
|
||||
int AddSectionFromFile(const QString &filename);
|
||||
|
||||
// Load a script section from memory
|
||||
// Returns 1 if the section was included
|
||||
// 0 if a section with the same name had already been included
|
||||
// before
|
||||
// <0 on error
|
||||
int AddSectionFromMemory(const char *sectionName, const char *scriptCode,
|
||||
unsigned int scriptLength = 0, int lineOffset = 0);
|
||||
|
||||
// Build the added script sections
|
||||
int BuildModule();
|
||||
|
||||
// Returns the engine
|
||||
asIScriptEngine *GetEngine();
|
||||
|
||||
// Returns the current module
|
||||
asIScriptModule *GetModule();
|
||||
|
||||
// Register the callback for resolving include directive
|
||||
void SetIncludeCallback(INCLUDECALLBACK_t callback, void *userParam);
|
||||
|
||||
// Register the callback for resolving pragma directive
|
||||
void SetPragmaCallback(PRAGMACALLBACK_t callback, void *userParam);
|
||||
|
||||
// Add a pre-processor define for conditional compilation
|
||||
void DefineWord(const QString &word);
|
||||
|
||||
// Enumerate included script sections
|
||||
unsigned int GetSectionCount() const;
|
||||
|
||||
QString GetSectionName(unsigned int idx) const;
|
||||
|
||||
#if AS_PROCESS_METADATA == 1
|
||||
// Get metadata declared for classes, interfaces, and enums
|
||||
QVector<QString> GetMetadataForType(int typeId);
|
||||
|
||||
// Get metadata declared for functions
|
||||
QVector<QString> GetMetadataForFunc(asIScriptFunction *func);
|
||||
|
||||
// Get metadata declared for global variables
|
||||
QVector<QString> GetMetadataForVar(int varIdx);
|
||||
|
||||
// Get metadata declared for class variables
|
||||
QVector<QString> GetMetadataForTypeProperty(int typeId, int varIdx);
|
||||
|
||||
// Get metadata declared for class methods
|
||||
QVector<QString> GetMetadataForTypeMethod(int typeId,
|
||||
asIScriptFunction *method);
|
||||
#endif
|
||||
|
||||
protected:
|
||||
void ClearAll();
|
||||
int Build();
|
||||
int ProcessScriptSection(const QByteArray &script, int length,
|
||||
const QString §ionname, int lineOffset);
|
||||
int LoadScriptSection(const QString &filename);
|
||||
bool IncludeIfNotAlreadyIncluded(const QString &filename);
|
||||
|
||||
int SkipStatement(int pos);
|
||||
|
||||
int ExcludeCode(int start);
|
||||
void OverwriteCode(int start, int len);
|
||||
|
||||
asIScriptEngine *engine;
|
||||
asIScriptModule *module;
|
||||
QByteArray modifiedScript;
|
||||
|
||||
INCLUDECALLBACK_t includeCallback;
|
||||
void *includeParam;
|
||||
|
||||
PRAGMACALLBACK_t pragmaCallback;
|
||||
void *pragmaParam;
|
||||
|
||||
#if AS_PROCESS_METADATA == 1
|
||||
int ExtractMetadata(int pos, QVector<QString> &outMetadata);
|
||||
int ExtractDeclaration(int pos, QString &outName, QString &outDeclaration,
|
||||
int &outType);
|
||||
|
||||
enum METADATATYPE {
|
||||
MDT_TYPE = 1,
|
||||
MDT_FUNC = 2,
|
||||
MDT_VAR = 3,
|
||||
MDT_VIRTPROP = 4,
|
||||
MDT_FUNC_OR_VAR = 5
|
||||
};
|
||||
|
||||
// Temporary structure for storing metadata and declaration
|
||||
struct SMetadataDecl {
|
||||
SMetadataDecl(const QVector<QString> &m, const QString &n,
|
||||
const QString &d, int t, const QString &c,
|
||||
const QString &ns)
|
||||
: metadata(m), name(n), declaration(d), type(t), parentClass(c),
|
||||
nameSpace(ns) {}
|
||||
QVector<QString> metadata;
|
||||
QString name;
|
||||
QString declaration;
|
||||
int type;
|
||||
QString parentClass;
|
||||
QString nameSpace;
|
||||
};
|
||||
QVector<SMetadataDecl> foundDeclarations;
|
||||
QString currentClass;
|
||||
QString currentNamespace;
|
||||
|
||||
// Storage of metadata for global declarations
|
||||
QMap<int, QVector<QString>> typeMetadataMap;
|
||||
QMap<int, QVector<QString>> funcMetadataMap;
|
||||
QMap<int, QVector<QString>> varMetadataMap;
|
||||
|
||||
// Storage of metadata for class member declarations
|
||||
struct SClassMetadata {
|
||||
SClassMetadata(const QString &aName) : className(aName) {}
|
||||
QString className;
|
||||
QMap<int, QVector<QString>> funcMetadataMap;
|
||||
QMap<int, QVector<QString>> varMetadataMap;
|
||||
};
|
||||
QMap<int, SClassMetadata> classMetadataMap;
|
||||
|
||||
#endif
|
||||
|
||||
QVector<QString> includedScripts;
|
||||
|
||||
QVector<QString> definedWords;
|
||||
};
|
||||
|
||||
END_AS_NAMESPACE
|
||||
|
||||
#endif // ASBUILDER_H
|
|
@ -7,20 +7,13 @@
|
|||
#include <QTextStream>
|
||||
#include <QThread>
|
||||
|
||||
asDebugger::asDebugger() : QObject() {
|
||||
_thread = new QThread;
|
||||
moveToThread(_thread);
|
||||
_thread->start();
|
||||
|
||||
asDebugger::asDebugger(QObject *parent) : QObject(parent) {
|
||||
m_action = CONTINUE;
|
||||
m_lastFunction = nullptr;
|
||||
m_engine = nullptr;
|
||||
}
|
||||
|
||||
asDebugger::~asDebugger() {
|
||||
setEngine(nullptr);
|
||||
_thread->deleteLater();
|
||||
}
|
||||
asDebugger::~asDebugger() { setEngine(nullptr); }
|
||||
|
||||
void asDebugger::registerToStringCallback(const asITypeInfo *ti,
|
||||
ToStringCallback callback) {
|
||||
|
@ -33,9 +26,24 @@ void asDebugger::registerBreakPointHitCallback(BreakPointHitCallback callback) {
|
|||
}
|
||||
|
||||
void asDebugger::takeCommands(asIScriptContext *ctx) {
|
||||
while (true) {
|
||||
emit onPullVariables(globalVariables(ctx), localVariables(ctx));
|
||||
emit onPullCallStack(retriveCallstack(ctx));
|
||||
emit onPullVariables(globalVariables(ctx), localVariables(ctx));
|
||||
emit onPullCallStack(retriveCallstack(ctx));
|
||||
|
||||
while (m_action == DebugAction::PAUSE) {
|
||||
switch (m_action) {
|
||||
case ABORT:
|
||||
ctx->Abort();
|
||||
return;
|
||||
case PAUSE:
|
||||
case CONTINUE:
|
||||
case STEP_INTO:
|
||||
break;
|
||||
case STEP_OVER:
|
||||
case STEP_OUT:
|
||||
m_lastCommandAtStackLevel = ctx ? ctx->GetCallstackSize() : 1;
|
||||
break;
|
||||
}
|
||||
|
||||
qApp->processEvents();
|
||||
}
|
||||
}
|
||||
|
@ -50,56 +58,55 @@ void asDebugger::lineCallback(asIScriptContext *ctx) {
|
|||
// By default we ignore callbacks when the context is not active.
|
||||
// An application might override this to for example disconnect the
|
||||
// debugger as the execution finished.
|
||||
if (m_action == CONTINUE && ctx->GetState() == asEXECUTION_SUSPENDED) {
|
||||
ctx->Execute();
|
||||
m_action = PAUSE;
|
||||
return;
|
||||
}
|
||||
|
||||
if (ctx->GetState() != asEXECUTION_ACTIVE)
|
||||
return;
|
||||
|
||||
if (m_action == CONTINUE) {
|
||||
switch (m_action) {
|
||||
case ABORT:
|
||||
ctx->Abort();
|
||||
return;
|
||||
case PAUSE:
|
||||
return;
|
||||
case CONTINUE:
|
||||
if (!checkBreakPoint(ctx))
|
||||
return;
|
||||
} else if (m_action == STEP_OVER) {
|
||||
break;
|
||||
case STEP_INTO:
|
||||
// Always break, but we call the check break point anyway
|
||||
// to tell user when break point has been reached
|
||||
checkBreakPoint(ctx);
|
||||
break;
|
||||
case STEP_OVER:
|
||||
case STEP_OUT:
|
||||
if (ctx->GetCallstackSize() > m_lastCommandAtStackLevel) {
|
||||
if (!checkBreakPoint(ctx))
|
||||
return;
|
||||
}
|
||||
} else if (m_action == STEP_OUT) {
|
||||
if (ctx->GetCallstackSize() >= m_lastCommandAtStackLevel) {
|
||||
if (!checkBreakPoint(ctx))
|
||||
return;
|
||||
}
|
||||
} else if (m_action == STEP_INTO) {
|
||||
checkBreakPoint(ctx);
|
||||
|
||||
// Always break, but we call the check break point anyway
|
||||
// to tell user when break point has been reached
|
||||
break;
|
||||
}
|
||||
|
||||
// stringstream s;
|
||||
// const char *file = 0;
|
||||
// int lineNbr = ctx->GetLineNumber(0, 0, &file);
|
||||
// s << (file ? file : "{unnamed}") << ":" << lineNbr << "; "
|
||||
// << ctx->GetFunction()->GetDeclaration() << endl;
|
||||
// Output(s.str());
|
||||
const char *file = 0;
|
||||
int lineNbr = ctx->GetLineNumber(0, nullptr, &file);
|
||||
emit onRunCurrentLine(file, lineNbr);
|
||||
|
||||
takeCommands(ctx);
|
||||
}
|
||||
|
||||
void asDebugger::addFileBreakPoint(const QString &file, int lineNbr) {
|
||||
// Store just file name, not entire path
|
||||
QFileInfo info(file);
|
||||
// Trim the file name
|
||||
QString actual = info.fileName().trimmed();
|
||||
|
||||
BreakPoint bp(actual, lineNbr, false);
|
||||
BreakPoint bp(file, lineNbr, false);
|
||||
m_breakPoints.push_back(bp);
|
||||
}
|
||||
|
||||
void asDebugger::removeFileBreakPoint(const QString &file, int lineNbr) {
|
||||
QFileInfo info(file);
|
||||
QString actual = info.fileName().trimmed();
|
||||
m_breakPoints.erase(std::remove_if(
|
||||
m_breakPoints.begin(), m_breakPoints.end(), [=](const BreakPoint &bp) {
|
||||
return bp.name == actual && bp.lineNbr == lineNbr &&
|
||||
bp.func == false;
|
||||
return bp.name == file && bp.lineNbr == lineNbr && bp.func == false;
|
||||
}));
|
||||
}
|
||||
|
||||
|
@ -212,7 +219,9 @@ bool asDebugger::checkBreakPoint(asIScriptContext *ctx) {
|
|||
return false;
|
||||
|
||||
const char *tmp = 0;
|
||||
int lineNbr = ctx->GetLineNumber(0, 0, &tmp);
|
||||
|
||||
int column = 0;
|
||||
int lineNbr = ctx->GetLineNumber(0, &column, &tmp);
|
||||
|
||||
QString file = tmp ? QString(tmp) : QString();
|
||||
|
||||
|
@ -265,7 +274,9 @@ bool asDebugger::checkBreakPoint(asIScriptContext *ctx) {
|
|||
#else
|
||||
bpName == file) {
|
||||
#endif
|
||||
ctx->Suspend();
|
||||
m_bphitCallback(m_breakPoints[n], this);
|
||||
m_action = PAUSE; // hit and pause script
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -403,6 +414,8 @@ asDebugger::GCStatistic asDebugger::gcStatistics() {
|
|||
return sta;
|
||||
}
|
||||
|
||||
void asDebugger::runDebugAction(DebugAction action) { m_action = action; }
|
||||
|
||||
void asDebugger::setEngine(asIScriptEngine *engine) {
|
||||
if (m_engine != engine) {
|
||||
if (m_engine)
|
||||
|
@ -410,7 +423,7 @@ void asDebugger::setEngine(asIScriptEngine *engine) {
|
|||
m_engine = engine;
|
||||
if (m_engine)
|
||||
m_engine->AddRef();
|
||||
m_breakPoints.clear();
|
||||
m_action = CONTINUE;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include <QList>
|
||||
#include <QMap>
|
||||
#include <QObject>
|
||||
#include <atomic>
|
||||
|
||||
// from AngelScript CDebugger, I modify it for Qt intergration and
|
||||
// and add some TODO features that easy to implement
|
||||
|
@ -53,8 +54,17 @@ public:
|
|||
ExpectedIdentifier
|
||||
};
|
||||
|
||||
enum DebugAction {
|
||||
ABORT, // try to abort the script
|
||||
PAUSE, // try to pause
|
||||
CONTINUE, // continue until next break point
|
||||
STEP_INTO, // stop at next instruction
|
||||
STEP_OVER, // stop at next instruction, skipping called functions
|
||||
STEP_OUT // run until returning from current function
|
||||
};
|
||||
|
||||
public:
|
||||
asDebugger();
|
||||
explicit asDebugger(QObject *parent = nullptr);
|
||||
virtual ~asDebugger();
|
||||
|
||||
// Register callbacks to handle to-string conversions of application types
|
||||
|
@ -96,6 +106,8 @@ public:
|
|||
|
||||
GCStatistic gcStatistics();
|
||||
|
||||
void runDebugAction(DebugAction action);
|
||||
|
||||
private:
|
||||
QVector<VariablesInfo> globalVariables(asIScriptContext *ctx);
|
||||
QVector<VariablesInfo> localVariables(asIScriptContext *ctx);
|
||||
|
@ -117,24 +129,17 @@ signals:
|
|||
void onPullVariables(const QVector<VariablesInfo> &globalvars,
|
||||
const QVector<VariablesInfo> &localvars);
|
||||
void onPullCallStack(const QList<CallStackItem> &callstacks);
|
||||
void onRunCurrentLine(const QString &file, int lineNr);
|
||||
|
||||
private:
|
||||
enum DebugAction {
|
||||
CONTINUE, // continue until next break point
|
||||
STEP_INTO, // stop at next instruction
|
||||
STEP_OVER, // stop at next instruction, skipping called functions
|
||||
STEP_OUT // run until returning from current function
|
||||
};
|
||||
std::atomic<DebugAction> m_action;
|
||||
|
||||
private:
|
||||
DebugAction m_action;
|
||||
asUINT m_lastCommandAtStackLevel;
|
||||
asIScriptFunction *m_lastFunction;
|
||||
|
||||
QVector<BreakPoint> m_breakPoints;
|
||||
|
||||
asIScriptEngine *m_engine = nullptr;
|
||||
QThread *_thread = nullptr;
|
||||
|
||||
// Registered callbacks for converting types to strings
|
||||
QMap<const asITypeInfo *, ToStringCallback> m_toStringCallbacks;
|
||||
|
|
|
@ -75,15 +75,11 @@ QString RecentFileManager::getDisplayFileName(const RecentInfo &info) {
|
|||
auto fileName = info.fileName;
|
||||
QString displayName;
|
||||
|
||||
auto drivers = Utilities::getStorageDevices();
|
||||
auto r = std::find_if(drivers.begin(), drivers.end(),
|
||||
[fileName](const QStorageInfo &info) {
|
||||
return info.device() == fileName;
|
||||
});
|
||||
if (r != drivers.end()) {
|
||||
displayName = r->displayName();
|
||||
auto driver = Utilities::getStorageDevice(fileName);
|
||||
if (driver.isValid()) {
|
||||
displayName = driver.displayName();
|
||||
if (displayName.isEmpty()) {
|
||||
displayName = r->device();
|
||||
displayName = driver.device();
|
||||
}
|
||||
} else {
|
||||
QFileInfo finfo(fileName);
|
||||
|
|
|
@ -149,7 +149,7 @@ bool ScriptMachine::configureEngine(asIScriptEngine *engine) {
|
|||
return false;
|
||||
}
|
||||
|
||||
_debugger = new asDebugger;
|
||||
_debugger = new asDebugger(this);
|
||||
|
||||
// Register the to-string callbacks so the user can see the contents of
|
||||
// strings
|
||||
|
@ -252,7 +252,7 @@ bool ScriptMachine::executeScript(const QString &script, bool isInDebug) {
|
|||
// ready to execute, so disable the automatic initialization
|
||||
_engine->SetEngineProperty(asEP_INIT_GLOBAL_VARS_AFTER_BUILD, false);
|
||||
|
||||
CScriptBuilder builder;
|
||||
asBuilder builder;
|
||||
|
||||
// Set the pragma callback so we can detect
|
||||
builder.SetPragmaCallback(&ScriptMachine::pragmaCallback, this);
|
||||
|
@ -271,8 +271,9 @@ bool ScriptMachine::executeScript(const QString &script, bool isInDebug) {
|
|||
|
||||
r = builder.BuildModule();
|
||||
if (r < 0) {
|
||||
_engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR,
|
||||
"Script failed to build");
|
||||
MessageInfo info;
|
||||
info.message = tr("Script failed to build");
|
||||
emit onOutput(MessageType::Error, info);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -368,8 +369,12 @@ bool ScriptMachine::executeScript(const QString &script, bool isInDebug) {
|
|||
_ctxPool.clear();
|
||||
|
||||
// Detach debugger
|
||||
Q_ASSERT(_debugger);
|
||||
_debugger->setEngine(nullptr);
|
||||
if (isInDebug) {
|
||||
Q_ASSERT(_debugger);
|
||||
_debugger->setEngine(nullptr);
|
||||
_debugger->clearBreakPoint();
|
||||
emit onDebugFinished();
|
||||
}
|
||||
|
||||
return r >= 0;
|
||||
}
|
||||
|
@ -433,7 +438,7 @@ asIScriptContext *ScriptMachine::requestContextCallback(asIScriptEngine *engine,
|
|||
}
|
||||
|
||||
// Attach the debugger if needed
|
||||
if (ctx && p->_debugger) {
|
||||
if (ctx && p->_debugger->getEngine()) {
|
||||
// Set the line callback for the debugging
|
||||
ctx->SetLineCallback(asMETHOD(asDebugger, lineCallback), p->_debugger,
|
||||
asCALL_THISCALL);
|
||||
|
@ -459,22 +464,22 @@ void ScriptMachine::returnContextCallback(asIScriptEngine *engine,
|
|||
p->_ctxPool.push_back(ctx);
|
||||
}
|
||||
|
||||
int ScriptMachine::pragmaCallback(const std::string &pragmaText,
|
||||
CScriptBuilder &builder, void *userParam) {
|
||||
asIScriptEngine *engine = builder.GetEngine();
|
||||
int ScriptMachine::pragmaCallback(const QByteArray &pragmaText,
|
||||
asBuilder *builder, void *userParam) {
|
||||
asIScriptEngine *engine = builder->GetEngine();
|
||||
|
||||
// Filter the pragmaText so only what is of interest remains
|
||||
// With this the user can add comments and use different whitespaces
|
||||
// without affecting the result
|
||||
asUINT pos = 0;
|
||||
asUINT length = 0;
|
||||
QList<std::string> tokens;
|
||||
QStringList tokens;
|
||||
while (pos < pragmaText.size()) {
|
||||
asETokenClass tokenClass =
|
||||
engine->ParseToken(pragmaText.c_str() + pos, 0, &length);
|
||||
engine->ParseToken(pragmaText.data() + pos, 0, &length);
|
||||
if (tokenClass == asTC_IDENTIFIER || tokenClass == asTC_KEYWORD ||
|
||||
tokenClass == asTC_VALUE) {
|
||||
std::string token = pragmaText.substr(pos, length);
|
||||
auto token = pragmaText.mid(pos, length);
|
||||
tokens << token;
|
||||
}
|
||||
if (tokenClass == asTC_UNKNOWN)
|
||||
|
@ -492,8 +497,8 @@ int ScriptMachine::pragmaCallback(const std::string &pragmaText,
|
|||
return -1;
|
||||
}
|
||||
|
||||
int ScriptMachine::includeCallback(const char *include, const char *from,
|
||||
CScriptBuilder *builder, void *userParam) {
|
||||
int ScriptMachine::includeCallback(const QString &include, const QString &from,
|
||||
asBuilder *builder, void *userParam) {
|
||||
asIScriptEngine *engine = builder->GetEngine();
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
#define SCRIPTMACHINE_H
|
||||
|
||||
#include "AngelScript/sdk/add_on/contextmgr/contextmgr.h"
|
||||
#include "AngelScript/sdk/add_on/scriptbuilder/scriptbuilder.h"
|
||||
#include "AngelScript/sdk/angelscript/include/angelscript.h"
|
||||
#include "class/asbuilder.h"
|
||||
|
||||
#include "asdebugger.h"
|
||||
|
||||
|
@ -92,11 +92,11 @@ private:
|
|||
static void returnContextCallback(asIScriptEngine *engine,
|
||||
asIScriptContext *ctx, void *param);
|
||||
|
||||
static int pragmaCallback(const std::string &pragmaText,
|
||||
CScriptBuilder &builder, void *userParam);
|
||||
static int pragmaCallback(const QByteArray &pragmaText, asBuilder *builder,
|
||||
void *userParam);
|
||||
|
||||
static int includeCallback(const char *include, const char *from,
|
||||
CScriptBuilder *builder, void *userParam);
|
||||
static int includeCallback(const QString &include, const QString &from,
|
||||
asBuilder *builder, void *userParam);
|
||||
|
||||
static QString processTranslation(const char *content);
|
||||
|
||||
|
@ -107,6 +107,8 @@ private:
|
|||
signals:
|
||||
void onOutput(MessageType type, const MessageInfo &message);
|
||||
|
||||
void onDebugFinished();
|
||||
|
||||
private:
|
||||
asIScriptEngine *_engine = nullptr;
|
||||
asDebugger *_debugger = nullptr;
|
||||
|
|
|
@ -43,6 +43,9 @@ const auto OTHER_LOG_LEVEL = QStringLiteral("sys.loglevel");
|
|||
SettingManager::SettingManager() {
|
||||
_defaultFont = qApp->font();
|
||||
qRegisterMetaType<RecentFileManager::RecentInfo>();
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||
qRegisterMetaTypeStreamOperators<RecentFileManager::RecentInfo>();
|
||||
#endif
|
||||
load();
|
||||
}
|
||||
|
||||
|
|
|
@ -16,10 +16,9 @@ DriverSelectorDialog::DriverSelectorDialog(QWidget *parent)
|
|||
drivers = new QListWidget(this);
|
||||
drivers->setSortingEnabled(false);
|
||||
auto ico = ICONRES("opendriver");
|
||||
auto infos = QStorageInfo::mountedVolumes();
|
||||
layout->addWidget(new QLabel(tr("PleaseChooseDriver"), this));
|
||||
layout->addSpacing(5);
|
||||
for (auto &item : Utilities::getStorageDevices()) {
|
||||
for (auto &item : QStorageInfo::mountedVolumes()) {
|
||||
auto device = item.device();
|
||||
#ifdef Q_OS_WINDOWS
|
||||
drivers->addItem(new QListWidgetItem(ico, item.rootPath()));
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "mainwindow.h"
|
||||
|
||||
#include "Qt-Advanced-Docking-System/src/DockAreaWidget.h"
|
||||
#include "Qt-Advanced-Docking-System/src/DockFocusController.h"
|
||||
#include "Qt-Advanced-Docking-System/src/DockSplitter.h"
|
||||
#include "Qt-Advanced-Docking-System/src/DockWidgetTab.h"
|
||||
#include "aboutsoftwaredialog.h"
|
||||
|
@ -273,6 +274,7 @@ void MainWindow::buildUpDockSystem(QWidget *container) {
|
|||
CDockWidget *CentralDockWidget =
|
||||
new CDockWidget(QStringLiteral("CentralWidget"));
|
||||
CentralDockWidget->setWidget(label);
|
||||
CentralDockWidget->setFeature(ads::CDockWidget::DockWidgetFocusable, false);
|
||||
CentralDockWidget->setFeature(ads::CDockWidget::NoTab, true);
|
||||
auto editorViewArea = m_dock->setCentralWidget(CentralDockWidget);
|
||||
|
||||
|
@ -2384,6 +2386,10 @@ void MainWindow::connectEditorView(EditorView *editor) {
|
|||
}
|
||||
|
||||
void MainWindow::swapEditor(EditorView *old, EditorView *cur) {
|
||||
if (old == cur) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (old != nullptr) {
|
||||
auto hexeditor = old->hexEditor();
|
||||
hexeditor->disconnect(SLOT(cursorLocationChanged()));
|
||||
|
|
|
@ -55,11 +55,13 @@ ScriptingDialog::ScriptingDialog(QWidget *parent)
|
|||
layout->addWidget(m_status);
|
||||
buildUpContent(cw);
|
||||
|
||||
QLineMarksInfoCenter::instance()->loadMarkTypes(
|
||||
QCE::fetchDataFile(":/qcodeedit/marks.qxm"));
|
||||
auto lmic = QLineMarksInfoCenter::instance();
|
||||
lmic->loadMarkTypes(QCE::fetchDataFile(":/qcodeedit/marks.qxm"));
|
||||
// get symbol ID
|
||||
m_symID.insert(Symbols::BreakPoint,
|
||||
QLineMarksInfoCenter::instance()->markTypeId("breakpoint"));
|
||||
m_symID.insert(Symbols::BreakPoint, lmic->markTypeId("breakpoint"));
|
||||
m_symID.insert(Symbols::DbgRunCurrentLine, lmic->markTypeId("current"));
|
||||
m_symID.insert(Symbols::DbgRunHitBreakPoint,
|
||||
lmic->markTypeId("breakpoint-current"));
|
||||
|
||||
updateEditModeEnabled();
|
||||
|
||||
|
@ -74,6 +76,65 @@ ScriptingDialog::ScriptingDialog(QWidget *parent)
|
|||
void ScriptingDialog::initConsole() {
|
||||
Q_ASSERT(m_consoleout);
|
||||
m_consoleout->init();
|
||||
auto machine = m_consoleout->machine();
|
||||
connect(machine, &ScriptMachine::onDebugFinished, this, [=] {
|
||||
this->setRunDebugMode(false);
|
||||
// clean up
|
||||
auto e = findEditorView(_lastCurLine.first);
|
||||
Q_ASSERT(e);
|
||||
auto line = e->editor()->document()->line(_lastCurLine.second - 1);
|
||||
line.removeMark(m_symID.value(Symbols::DbgRunCurrentLine));
|
||||
line.removeMark(m_symID.value(Symbols::DbgRunHitBreakPoint));
|
||||
});
|
||||
auto dbg = machine->debugger();
|
||||
Q_ASSERT(dbg);
|
||||
dbg->setProperty("scriptdlg", QVariant::fromValue(this));
|
||||
dbg->registerBreakPointHitCallback(
|
||||
[](const asDebugger::BreakPoint &bp, asDebugger *dbg) {
|
||||
auto p = dbg->property("scriptdlg");
|
||||
if (p.isValid()) {
|
||||
auto dlg = p.value<ScriptingDialog *>();
|
||||
QMessageBox::information(dlg, "", "Hint");
|
||||
}
|
||||
});
|
||||
connect(dbg, &asDebugger::onAdjustBreakPointLine, this,
|
||||
[=](const asDebugger::BreakPoint &old, int newLineNr) {
|
||||
|
||||
});
|
||||
connect(dbg, &asDebugger::onPullVariables, this,
|
||||
[=](const QVector<asDebugger::VariablesInfo> &globalvars,
|
||||
const QVector<asDebugger::VariablesInfo> &localvars) {
|
||||
|
||||
});
|
||||
connect(dbg, &asDebugger::onPullCallStack, m_callstack,
|
||||
&DbgCallStackModel::updateData);
|
||||
connect(
|
||||
dbg, &asDebugger::onRunCurrentLine, this,
|
||||
[=](const QString &file, int lineNr) {
|
||||
ScriptEditor *e = m_curEditor;
|
||||
#ifdef Q_OS_WIN
|
||||
if (file.compare(m_curEditor->fileName(), Qt::CaseInsensitive)) {
|
||||
#else
|
||||
if (file != e->fileName()) {
|
||||
#endif
|
||||
e = findEditorView(file);
|
||||
Q_ASSERT(e);
|
||||
e->setFocus();
|
||||
e->raise();
|
||||
}
|
||||
|
||||
auto doc = e->editor()->document();
|
||||
|
||||
auto curMark = m_symID.value(Symbols::DbgRunCurrentLine);
|
||||
Q_ASSERT(curMark >= 0);
|
||||
if (_lastCurLine.second >= 0) {
|
||||
auto line = doc->line(_lastCurLine.second - 1);
|
||||
line.removeMark(curMark);
|
||||
}
|
||||
auto line = doc->line(lineNr - 1);
|
||||
line.addMark(curMark);
|
||||
_lastCurLine = {file, lineNr};
|
||||
});
|
||||
}
|
||||
|
||||
void ScriptingDialog::buildUpRibbonBar() {
|
||||
|
@ -417,6 +478,7 @@ void ScriptingDialog::buildUpDockSystem(QWidget *container) {
|
|||
CDockWidget *CentralDockWidget =
|
||||
new CDockWidget(QStringLiteral("CentralWidget"));
|
||||
CentralDockWidget->setWidget(label);
|
||||
CentralDockWidget->setFeature(ads::CDockWidget::DockWidgetFocusable, false);
|
||||
CentralDockWidget->setFeature(ads::CDockWidget::NoTab, true);
|
||||
m_editorViewArea = m_dock->setCentralWidget(CentralDockWidget);
|
||||
|
||||
|
@ -468,6 +530,11 @@ void ScriptingDialog::registerEditorView(ScriptEditor *editor) {
|
|||
Q_ASSERT(editor);
|
||||
Q_ASSERT(m_views.contains(editor));
|
||||
|
||||
auto m = m_consoleout->machine();
|
||||
if (m->isInDebugMode()) {
|
||||
auto dbg = m->debugger();
|
||||
}
|
||||
|
||||
m_views.removeOne(editor);
|
||||
if (currentEditor() == editor) {
|
||||
m_curEditor = nullptr;
|
||||
|
@ -575,6 +642,14 @@ void ScriptingDialog::openFile(const QString &filename) {
|
|||
m_dock->addDockWidget(ads::CenterDockWidgetArea, editor, editorViewArea());
|
||||
}
|
||||
|
||||
void ScriptingDialog::runDbgCommand(asDebugger::DebugAction action) {
|
||||
auto machine = m_consoleout->machine();
|
||||
if (machine->isInDebugMode()) {
|
||||
auto dbg = machine->debugger();
|
||||
dbg->runDebugAction(action);
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptingDialog::on_newfile() {
|
||||
if (!newOpenFileSafeCheck()) {
|
||||
return;
|
||||
|
@ -783,7 +858,9 @@ void ScriptingDialog::on_runscript() {
|
|||
auto e = editor->editor();
|
||||
m_consoleout->clear();
|
||||
setRunDebugMode(true, false);
|
||||
m_consoleout->machine()->executeScript(e->fileName());
|
||||
if (!m_consoleout->machine()->executeScript(e->fileName())) {
|
||||
setRunDebugMode(false);
|
||||
}
|
||||
setRunDebugMode(false);
|
||||
}
|
||||
}
|
||||
|
@ -794,24 +871,42 @@ void ScriptingDialog::on_rundbgscript() {
|
|||
auto e = editor->editor();
|
||||
m_consoleout->clear();
|
||||
setRunDebugMode(true, true);
|
||||
m_consoleout->machine()->executeScript(e->fileName(), true);
|
||||
m_consoleout->appendCommandPrompt();
|
||||
|
||||
// add breakpoints
|
||||
auto marks = QLineMarksInfoCenter::instance()->marks(
|
||||
e->fileName(), m_symID.value(Symbols::BreakPoint));
|
||||
auto dbg = m_consoleout->machine()->debugger();
|
||||
for (auto &bp : marks) {
|
||||
dbg->addFileBreakPoint(bp.file, bp.line);
|
||||
}
|
||||
|
||||
if (!m_consoleout->machine()->executeScript(e->fileName(), true)) {
|
||||
setRunDebugMode(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptingDialog::on_pausescript() {}
|
||||
void ScriptingDialog::on_pausescript() { runDbgCommand(asDebugger::PAUSE); }
|
||||
|
||||
void ScriptingDialog::on_continuescript() {}
|
||||
void ScriptingDialog::on_continuescript() {
|
||||
runDbgCommand(asDebugger::CONTINUE);
|
||||
}
|
||||
|
||||
void ScriptingDialog::on_stopscript() {}
|
||||
void ScriptingDialog::on_stopscript() { runDbgCommand(asDebugger::ABORT); }
|
||||
|
||||
void ScriptingDialog::on_restartscript() {}
|
||||
|
||||
void ScriptingDialog::on_stepinscript() {}
|
||||
void ScriptingDialog::on_stepinscript() {
|
||||
runDbgCommand(asDebugger::STEP_INTO);
|
||||
}
|
||||
|
||||
void ScriptingDialog::on_stepoutscript() {}
|
||||
void ScriptingDialog::on_stepoutscript() {
|
||||
runDbgCommand(asDebugger::STEP_OUT);
|
||||
}
|
||||
|
||||
void ScriptingDialog::on_stepoverscript() {}
|
||||
void ScriptingDialog::on_stepoverscript() {
|
||||
runDbgCommand(asDebugger::STEP_OVER);
|
||||
}
|
||||
|
||||
void ScriptingDialog::on_togglebreakpoint() {
|
||||
auto editor = currentEditor();
|
||||
|
|
|
@ -146,6 +146,8 @@ private:
|
|||
|
||||
void openFile(const QString &filename);
|
||||
|
||||
void runDbgCommand(asDebugger::DebugAction action);
|
||||
|
||||
private slots:
|
||||
void on_newfile();
|
||||
void on_openfile();
|
||||
|
@ -202,6 +204,7 @@ private:
|
|||
Ribbon *m_ribbon = nullptr;
|
||||
|
||||
size_t m_newIndex = 1;
|
||||
QPair<QString, int> _lastCurLine = {QString(), -1};
|
||||
|
||||
QList<ScriptEditor *> m_views;
|
||||
QMap<Symbols, int> m_symID;
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<mark>
|
||||
<id>current</id>
|
||||
<icon>:/com.wingsummer.winghex/images/scriptdbg/hitcur.png</icon>
|
||||
<color>#F4FFA7</color>
|
||||
<color>#10F4FFA7</color>
|
||||
<focus>false</focus>
|
||||
<priority>2</priority>
|
||||
<persistency>2</persistency>
|
||||
|
@ -12,7 +12,7 @@
|
|||
<mark>
|
||||
<id>breakpoint-current</id>
|
||||
<icon>:/com.wingsummer.winghex/images/scriptdbg/hitbp.png</icon>
|
||||
<color>#F4FFA7</color>
|
||||
<color>#10F4FFA7</color>
|
||||
<focus>false</focus>
|
||||
<priority>2</priority>
|
||||
<persistency>2</persistency>
|
||||
|
|
|
@ -181,27 +181,8 @@ public:
|
|||
return t.inherits(QStringLiteral("text/plain"));
|
||||
}
|
||||
|
||||
static QList<QStorageInfo> getStorageDevices() {
|
||||
static QList<QStorageInfo> sdns;
|
||||
if (sdns.isEmpty()) {
|
||||
auto infos = QStorageInfo::mountedVolumes();
|
||||
for (auto &item : infos) {
|
||||
auto device = item.device();
|
||||
if (item.isValid()
|
||||
#ifdef Q_OS_LINUX
|
||||
&& std::filesystem::is_regular_file(
|
||||
std::filesystem::path(device.toStdString()))
|
||||
#endif
|
||||
) {
|
||||
sdns.append(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
return sdns;
|
||||
}
|
||||
|
||||
static bool isStorageDevice(const QString &path) {
|
||||
auto sdns = getStorageDevices();
|
||||
auto sdns = QStorageInfo::mountedVolumes();
|
||||
return std::find_if(sdns.begin(), sdns.end(),
|
||||
[path](const QStorageInfo &info) {
|
||||
return info.device() == path;
|
||||
|
@ -209,7 +190,7 @@ public:
|
|||
}
|
||||
|
||||
static QStorageInfo getStorageDevice(const QString &path) {
|
||||
auto sdns = getStorageDevices();
|
||||
auto sdns = QStorageInfo::mountedVolumes();
|
||||
auto r = std::find_if(
|
||||
sdns.begin(), sdns.end(),
|
||||
[path](const QStorageInfo &info) { return info.device() == path; });
|
||||
|
|