feat: 完善调试器;增加契合 QT 框架的 AngelScript 预处理器;还有一些修复;
fix: 修复在 Linux 本地驱动器查找问题;修复在 Linux 下最近文件无法保存的问题;
|
@ -9,6 +9,6 @@ jobs:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
- name: cmake-format lint
|
- name: cmake-format lint
|
||||||
uses: Wing-summer/cmake-format-action@main
|
uses: neg-c/cmake-format-action@v0.1.1
|
||||||
with:
|
with:
|
||||||
inplace: true
|
inplace: true
|
||||||
|
|
|
@ -33,6 +33,7 @@ jobs:
|
||||||
host: ${{ matrix.os == 'windows-latest' && 'windows' || 'linux' }}
|
host: ${{ matrix.os == 'windows-latest' && 'windows' || 'linux' }}
|
||||||
target: 'desktop'
|
target: 'desktop'
|
||||||
arch: ${{ matrix.arch }}
|
arch: ${{ matrix.arch }}
|
||||||
|
cache: true
|
||||||
|
|
||||||
- name: Configure CMake
|
- 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.
|
# 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,14 +72,17 @@ QLineMarksInfoCenter::~QLineMarksInfoCenter() {}
|
||||||
/*!
|
/*!
|
||||||
\return the list of line marks set on a given file
|
\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;
|
QLineMarkList l;
|
||||||
bool check = !file.isEmpty();
|
bool check = !file.isEmpty();
|
||||||
|
|
||||||
foreach (QLineMarkHandle m, m_lineMarks) {
|
for (auto &m : m_lineMarks) {
|
||||||
if (!check || (m.file == file))
|
if (!check || (m.file == file)) {
|
||||||
|
if (filterID < 0 || (filterID >= 0 && m.mark == filterID)) {
|
||||||
l << QLineMark(file, m.line->line() + 1, m.mark);
|
l << QLineMark(file, m.line->line() + 1, m.mark);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return l;
|
return l;
|
||||||
}
|
}
|
||||||
|
@ -88,14 +91,16 @@ QLineMarkList QLineMarksInfoCenter::marks(const QString &file) {
|
||||||
\brief Remove all line marks on all files
|
\brief Remove all line marks on all files
|
||||||
*/
|
*/
|
||||||
void QLineMarksInfoCenter::clear() {
|
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
|
\brief Remove all line marks on a given file
|
||||||
*/
|
*/
|
||||||
void QLineMarksInfoCenter::removeMarks(const QString &file) {
|
void QLineMarksInfoCenter::removeMarks(const QString &file) {
|
||||||
foreach (QLineMarkHandle m, m_lineMarks)
|
for (auto &m : m_lineMarks)
|
||||||
if (m.file == file)
|
if (m.file == file)
|
||||||
removeLineMark(m);
|
removeLineMark(m);
|
||||||
}
|
}
|
||||||
|
@ -481,15 +486,9 @@ QString QLineMarksInfoCenter::priority(const QStringList &marks) {
|
||||||
*/
|
*/
|
||||||
QList<QStringList> QLineMarksInfoCenter::marksLayout(const QString &context) {
|
QList<QStringList> QLineMarksInfoCenter::marksLayout(const QString &context) {
|
||||||
QList<QStringList> l;
|
QList<QStringList> l;
|
||||||
|
for (auto &id : availableMarkTypes(context)) {
|
||||||
foreach (QString id, availableMarkTypes(context)) { l << QStringList(id); }
|
l << QStringList(id);
|
||||||
|
|
||||||
/*
|
|
||||||
foreach ( QLineMarkType t, availableMarks(context) )
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
return l;
|
return l;
|
||||||
}
|
}
|
||||||
|
|
|
@ -124,7 +124,7 @@ public:
|
||||||
static QLineMarksInfoCenter *instance();
|
static QLineMarksInfoCenter *instance();
|
||||||
static void destroy();
|
static void destroy();
|
||||||
|
|
||||||
QLineMarkList marks(const QString &file = QString());
|
QLineMarkList marks(const QString &file = QString(), int filterID = -1);
|
||||||
|
|
||||||
QString markTypeId(int id);
|
QString markTypeId(int id);
|
||||||
int markTypeId(const QString &id);
|
int markTypeId(const QString &id);
|
||||||
|
|
|
@ -35,12 +35,10 @@ if(MSVC)
|
||||||
string(APPEND CMAKE_C_FLAGS " /utf-8")
|
string(APPEND CMAKE_C_FLAGS " /utf-8")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(QWINDOWKIT_BUILD_STATIC
|
set(CMAKE_POLICY_DEFAULT_CMP0077 NEW)
|
||||||
TRUE
|
option(QWINDOWKIT_BUILD_STATIC "Build static libraries" TRUE)
|
||||||
CACHE BOOL "Build static libraries")
|
option(QWINDOWKIT_INSTALL "Install library" OFF)
|
||||||
set(QWINDOWKIT_INSTALL
|
set(ADS_VERSION 4.3.1)
|
||||||
OFF
|
|
||||||
CACHE BOOL "Install library")
|
|
||||||
|
|
||||||
add_subdirectory(3rdparty/qwindowkit)
|
add_subdirectory(3rdparty/qwindowkit)
|
||||||
add_subdirectory(3rdparty/QHexView)
|
add_subdirectory(3rdparty/QHexView)
|
||||||
|
@ -63,8 +61,6 @@ set(ANGEL_SCRIPT_ADDON
|
||||||
${ANGEL_SCRIPT_ADDON_ROOT}/scriptany/scriptany.h
|
${ANGEL_SCRIPT_ADDON_ROOT}/scriptany/scriptany.h
|
||||||
${ANGEL_SCRIPT_ADDON_ROOT}/scriptarray/scriptarray.cpp
|
${ANGEL_SCRIPT_ADDON_ROOT}/scriptarray/scriptarray.cpp
|
||||||
${ANGEL_SCRIPT_ADDON_ROOT}/scriptarray/scriptarray.h
|
${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.cpp
|
||||||
${ANGEL_SCRIPT_ADDON_ROOT}/scriptfile/scriptfile.h
|
${ANGEL_SCRIPT_ADDON_ROOT}/scriptfile/scriptfile.h
|
||||||
${ANGEL_SCRIPT_ADDON_ROOT}/scriptfile/scriptfilesystem.cpp
|
${ANGEL_SCRIPT_ADDON_ROOT}/scriptfile/scriptfilesystem.cpp
|
||||||
|
@ -207,6 +203,8 @@ set(CLASS_SRC
|
||||||
src/class/angelscripthelper.h
|
src/class/angelscripthelper.h
|
||||||
src/class/ascompletion.cpp
|
src/class/ascompletion.cpp
|
||||||
src/class/ascompletion.h
|
src/class/ascompletion.h
|
||||||
|
src/class/asbuilder.h
|
||||||
|
src/class/asbuilder.cpp
|
||||||
src/class/qaslexer.cpp
|
src/class/qaslexer.cpp
|
||||||
src/class/qaslexer.h
|
src/class/qaslexer.h
|
||||||
src/class/qasparser.cpp
|
src/class/qasparser.cpp
|
||||||
|
@ -313,7 +311,7 @@ set(TRANSLATION_PATH
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/QJsonModel
|
${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/QJsonModel
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/QPathEdit
|
${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/QPathEdit
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/qcodeedit2
|
${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/QWingRibbon
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/SingleApplication
|
${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/SingleApplication
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src)
|
${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 <QTextStream>
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
|
|
||||||
asDebugger::asDebugger() : QObject() {
|
asDebugger::asDebugger(QObject *parent) : QObject(parent) {
|
||||||
_thread = new QThread;
|
|
||||||
moveToThread(_thread);
|
|
||||||
_thread->start();
|
|
||||||
|
|
||||||
m_action = CONTINUE;
|
m_action = CONTINUE;
|
||||||
m_lastFunction = nullptr;
|
m_lastFunction = nullptr;
|
||||||
m_engine = nullptr;
|
m_engine = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
asDebugger::~asDebugger() {
|
asDebugger::~asDebugger() { setEngine(nullptr); }
|
||||||
setEngine(nullptr);
|
|
||||||
_thread->deleteLater();
|
|
||||||
}
|
|
||||||
|
|
||||||
void asDebugger::registerToStringCallback(const asITypeInfo *ti,
|
void asDebugger::registerToStringCallback(const asITypeInfo *ti,
|
||||||
ToStringCallback callback) {
|
ToStringCallback callback) {
|
||||||
|
@ -33,9 +26,24 @@ void asDebugger::registerBreakPointHitCallback(BreakPointHitCallback callback) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void asDebugger::takeCommands(asIScriptContext *ctx) {
|
void asDebugger::takeCommands(asIScriptContext *ctx) {
|
||||||
while (true) {
|
|
||||||
emit onPullVariables(globalVariables(ctx), localVariables(ctx));
|
emit onPullVariables(globalVariables(ctx), localVariables(ctx));
|
||||||
emit onPullCallStack(retriveCallstack(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();
|
qApp->processEvents();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -50,56 +58,55 @@ void asDebugger::lineCallback(asIScriptContext *ctx) {
|
||||||
// By default we ignore callbacks when the context is not active.
|
// By default we ignore callbacks when the context is not active.
|
||||||
// An application might override this to for example disconnect the
|
// An application might override this to for example disconnect the
|
||||||
// debugger as the execution finished.
|
// debugger as the execution finished.
|
||||||
|
if (m_action == CONTINUE && ctx->GetState() == asEXECUTION_SUSPENDED) {
|
||||||
|
ctx->Execute();
|
||||||
|
m_action = PAUSE;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (ctx->GetState() != asEXECUTION_ACTIVE)
|
if (ctx->GetState() != asEXECUTION_ACTIVE)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (m_action == CONTINUE) {
|
switch (m_action) {
|
||||||
|
case ABORT:
|
||||||
|
ctx->Abort();
|
||||||
|
return;
|
||||||
|
case PAUSE:
|
||||||
|
return;
|
||||||
|
case CONTINUE:
|
||||||
if (!checkBreakPoint(ctx))
|
if (!checkBreakPoint(ctx))
|
||||||
return;
|
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 (ctx->GetCallstackSize() > m_lastCommandAtStackLevel) {
|
||||||
if (!checkBreakPoint(ctx))
|
if (!checkBreakPoint(ctx))
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else if (m_action == STEP_OUT) {
|
break;
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// stringstream s;
|
const char *file = 0;
|
||||||
// const char *file = 0;
|
int lineNbr = ctx->GetLineNumber(0, nullptr, &file);
|
||||||
// int lineNbr = ctx->GetLineNumber(0, 0, &file);
|
emit onRunCurrentLine(file, lineNbr);
|
||||||
// s << (file ? file : "{unnamed}") << ":" << lineNbr << "; "
|
|
||||||
// << ctx->GetFunction()->GetDeclaration() << endl;
|
|
||||||
// Output(s.str());
|
|
||||||
|
|
||||||
takeCommands(ctx);
|
takeCommands(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
void asDebugger::addFileBreakPoint(const QString &file, int lineNbr) {
|
void asDebugger::addFileBreakPoint(const QString &file, int lineNbr) {
|
||||||
// Store just file name, not entire path
|
BreakPoint bp(file, lineNbr, false);
|
||||||
QFileInfo info(file);
|
|
||||||
// Trim the file name
|
|
||||||
QString actual = info.fileName().trimmed();
|
|
||||||
|
|
||||||
BreakPoint bp(actual, lineNbr, false);
|
|
||||||
m_breakPoints.push_back(bp);
|
m_breakPoints.push_back(bp);
|
||||||
}
|
}
|
||||||
|
|
||||||
void asDebugger::removeFileBreakPoint(const QString &file, int lineNbr) {
|
void asDebugger::removeFileBreakPoint(const QString &file, int lineNbr) {
|
||||||
QFileInfo info(file);
|
|
||||||
QString actual = info.fileName().trimmed();
|
|
||||||
m_breakPoints.erase(std::remove_if(
|
m_breakPoints.erase(std::remove_if(
|
||||||
m_breakPoints.begin(), m_breakPoints.end(), [=](const BreakPoint &bp) {
|
m_breakPoints.begin(), m_breakPoints.end(), [=](const BreakPoint &bp) {
|
||||||
return bp.name == actual && bp.lineNbr == lineNbr &&
|
return bp.name == file && bp.lineNbr == lineNbr && bp.func == false;
|
||||||
bp.func == false;
|
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -212,7 +219,9 @@ bool asDebugger::checkBreakPoint(asIScriptContext *ctx) {
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
const char *tmp = 0;
|
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();
|
QString file = tmp ? QString(tmp) : QString();
|
||||||
|
|
||||||
|
@ -265,7 +274,9 @@ bool asDebugger::checkBreakPoint(asIScriptContext *ctx) {
|
||||||
#else
|
#else
|
||||||
bpName == file) {
|
bpName == file) {
|
||||||
#endif
|
#endif
|
||||||
|
ctx->Suspend();
|
||||||
m_bphitCallback(m_breakPoints[n], this);
|
m_bphitCallback(m_breakPoints[n], this);
|
||||||
|
m_action = PAUSE; // hit and pause script
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -403,6 +414,8 @@ asDebugger::GCStatistic asDebugger::gcStatistics() {
|
||||||
return sta;
|
return sta;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void asDebugger::runDebugAction(DebugAction action) { m_action = action; }
|
||||||
|
|
||||||
void asDebugger::setEngine(asIScriptEngine *engine) {
|
void asDebugger::setEngine(asIScriptEngine *engine) {
|
||||||
if (m_engine != engine) {
|
if (m_engine != engine) {
|
||||||
if (m_engine)
|
if (m_engine)
|
||||||
|
@ -410,7 +423,7 @@ void asDebugger::setEngine(asIScriptEngine *engine) {
|
||||||
m_engine = engine;
|
m_engine = engine;
|
||||||
if (m_engine)
|
if (m_engine)
|
||||||
m_engine->AddRef();
|
m_engine->AddRef();
|
||||||
m_breakPoints.clear();
|
m_action = CONTINUE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include <QList>
|
#include <QList>
|
||||||
#include <QMap>
|
#include <QMap>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
// from AngelScript CDebugger, I modify it for Qt intergration and
|
// from AngelScript CDebugger, I modify it for Qt intergration and
|
||||||
// and add some TODO features that easy to implement
|
// and add some TODO features that easy to implement
|
||||||
|
@ -53,8 +54,17 @@ public:
|
||||||
ExpectedIdentifier
|
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:
|
public:
|
||||||
asDebugger();
|
explicit asDebugger(QObject *parent = nullptr);
|
||||||
virtual ~asDebugger();
|
virtual ~asDebugger();
|
||||||
|
|
||||||
// Register callbacks to handle to-string conversions of application types
|
// Register callbacks to handle to-string conversions of application types
|
||||||
|
@ -96,6 +106,8 @@ public:
|
||||||
|
|
||||||
GCStatistic gcStatistics();
|
GCStatistic gcStatistics();
|
||||||
|
|
||||||
|
void runDebugAction(DebugAction action);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QVector<VariablesInfo> globalVariables(asIScriptContext *ctx);
|
QVector<VariablesInfo> globalVariables(asIScriptContext *ctx);
|
||||||
QVector<VariablesInfo> localVariables(asIScriptContext *ctx);
|
QVector<VariablesInfo> localVariables(asIScriptContext *ctx);
|
||||||
|
@ -117,24 +129,17 @@ signals:
|
||||||
void onPullVariables(const QVector<VariablesInfo> &globalvars,
|
void onPullVariables(const QVector<VariablesInfo> &globalvars,
|
||||||
const QVector<VariablesInfo> &localvars);
|
const QVector<VariablesInfo> &localvars);
|
||||||
void onPullCallStack(const QList<CallStackItem> &callstacks);
|
void onPullCallStack(const QList<CallStackItem> &callstacks);
|
||||||
|
void onRunCurrentLine(const QString &file, int lineNr);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
enum DebugAction {
|
std::atomic<DebugAction> m_action;
|
||||||
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
|
|
||||||
};
|
|
||||||
|
|
||||||
private:
|
|
||||||
DebugAction m_action;
|
|
||||||
asUINT m_lastCommandAtStackLevel;
|
asUINT m_lastCommandAtStackLevel;
|
||||||
asIScriptFunction *m_lastFunction;
|
asIScriptFunction *m_lastFunction;
|
||||||
|
|
||||||
QVector<BreakPoint> m_breakPoints;
|
QVector<BreakPoint> m_breakPoints;
|
||||||
|
|
||||||
asIScriptEngine *m_engine = nullptr;
|
asIScriptEngine *m_engine = nullptr;
|
||||||
QThread *_thread = nullptr;
|
|
||||||
|
|
||||||
// Registered callbacks for converting types to strings
|
// Registered callbacks for converting types to strings
|
||||||
QMap<const asITypeInfo *, ToStringCallback> m_toStringCallbacks;
|
QMap<const asITypeInfo *, ToStringCallback> m_toStringCallbacks;
|
||||||
|
|
|
@ -75,15 +75,11 @@ QString RecentFileManager::getDisplayFileName(const RecentInfo &info) {
|
||||||
auto fileName = info.fileName;
|
auto fileName = info.fileName;
|
||||||
QString displayName;
|
QString displayName;
|
||||||
|
|
||||||
auto drivers = Utilities::getStorageDevices();
|
auto driver = Utilities::getStorageDevice(fileName);
|
||||||
auto r = std::find_if(drivers.begin(), drivers.end(),
|
if (driver.isValid()) {
|
||||||
[fileName](const QStorageInfo &info) {
|
displayName = driver.displayName();
|
||||||
return info.device() == fileName;
|
|
||||||
});
|
|
||||||
if (r != drivers.end()) {
|
|
||||||
displayName = r->displayName();
|
|
||||||
if (displayName.isEmpty()) {
|
if (displayName.isEmpty()) {
|
||||||
displayName = r->device();
|
displayName = driver.device();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
QFileInfo finfo(fileName);
|
QFileInfo finfo(fileName);
|
||||||
|
|
|
@ -149,7 +149,7 @@ bool ScriptMachine::configureEngine(asIScriptEngine *engine) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
_debugger = new asDebugger;
|
_debugger = new asDebugger(this);
|
||||||
|
|
||||||
// Register the to-string callbacks so the user can see the contents of
|
// Register the to-string callbacks so the user can see the contents of
|
||||||
// strings
|
// strings
|
||||||
|
@ -252,7 +252,7 @@ bool ScriptMachine::executeScript(const QString &script, bool isInDebug) {
|
||||||
// ready to execute, so disable the automatic initialization
|
// ready to execute, so disable the automatic initialization
|
||||||
_engine->SetEngineProperty(asEP_INIT_GLOBAL_VARS_AFTER_BUILD, false);
|
_engine->SetEngineProperty(asEP_INIT_GLOBAL_VARS_AFTER_BUILD, false);
|
||||||
|
|
||||||
CScriptBuilder builder;
|
asBuilder builder;
|
||||||
|
|
||||||
// Set the pragma callback so we can detect
|
// Set the pragma callback so we can detect
|
||||||
builder.SetPragmaCallback(&ScriptMachine::pragmaCallback, this);
|
builder.SetPragmaCallback(&ScriptMachine::pragmaCallback, this);
|
||||||
|
@ -271,8 +271,9 @@ bool ScriptMachine::executeScript(const QString &script, bool isInDebug) {
|
||||||
|
|
||||||
r = builder.BuildModule();
|
r = builder.BuildModule();
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
_engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR,
|
MessageInfo info;
|
||||||
"Script failed to build");
|
info.message = tr("Script failed to build");
|
||||||
|
emit onOutput(MessageType::Error, info);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -368,8 +369,12 @@ bool ScriptMachine::executeScript(const QString &script, bool isInDebug) {
|
||||||
_ctxPool.clear();
|
_ctxPool.clear();
|
||||||
|
|
||||||
// Detach debugger
|
// Detach debugger
|
||||||
|
if (isInDebug) {
|
||||||
Q_ASSERT(_debugger);
|
Q_ASSERT(_debugger);
|
||||||
_debugger->setEngine(nullptr);
|
_debugger->setEngine(nullptr);
|
||||||
|
_debugger->clearBreakPoint();
|
||||||
|
emit onDebugFinished();
|
||||||
|
}
|
||||||
|
|
||||||
return r >= 0;
|
return r >= 0;
|
||||||
}
|
}
|
||||||
|
@ -433,7 +438,7 @@ asIScriptContext *ScriptMachine::requestContextCallback(asIScriptEngine *engine,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attach the debugger if needed
|
// Attach the debugger if needed
|
||||||
if (ctx && p->_debugger) {
|
if (ctx && p->_debugger->getEngine()) {
|
||||||
// Set the line callback for the debugging
|
// Set the line callback for the debugging
|
||||||
ctx->SetLineCallback(asMETHOD(asDebugger, lineCallback), p->_debugger,
|
ctx->SetLineCallback(asMETHOD(asDebugger, lineCallback), p->_debugger,
|
||||||
asCALL_THISCALL);
|
asCALL_THISCALL);
|
||||||
|
@ -459,22 +464,22 @@ void ScriptMachine::returnContextCallback(asIScriptEngine *engine,
|
||||||
p->_ctxPool.push_back(ctx);
|
p->_ctxPool.push_back(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
int ScriptMachine::pragmaCallback(const std::string &pragmaText,
|
int ScriptMachine::pragmaCallback(const QByteArray &pragmaText,
|
||||||
CScriptBuilder &builder, void *userParam) {
|
asBuilder *builder, void *userParam) {
|
||||||
asIScriptEngine *engine = builder.GetEngine();
|
asIScriptEngine *engine = builder->GetEngine();
|
||||||
|
|
||||||
// Filter the pragmaText so only what is of interest remains
|
// Filter the pragmaText so only what is of interest remains
|
||||||
// With this the user can add comments and use different whitespaces
|
// With this the user can add comments and use different whitespaces
|
||||||
// without affecting the result
|
// without affecting the result
|
||||||
asUINT pos = 0;
|
asUINT pos = 0;
|
||||||
asUINT length = 0;
|
asUINT length = 0;
|
||||||
QList<std::string> tokens;
|
QStringList tokens;
|
||||||
while (pos < pragmaText.size()) {
|
while (pos < pragmaText.size()) {
|
||||||
asETokenClass tokenClass =
|
asETokenClass tokenClass =
|
||||||
engine->ParseToken(pragmaText.c_str() + pos, 0, &length);
|
engine->ParseToken(pragmaText.data() + pos, 0, &length);
|
||||||
if (tokenClass == asTC_IDENTIFIER || tokenClass == asTC_KEYWORD ||
|
if (tokenClass == asTC_IDENTIFIER || tokenClass == asTC_KEYWORD ||
|
||||||
tokenClass == asTC_VALUE) {
|
tokenClass == asTC_VALUE) {
|
||||||
std::string token = pragmaText.substr(pos, length);
|
auto token = pragmaText.mid(pos, length);
|
||||||
tokens << token;
|
tokens << token;
|
||||||
}
|
}
|
||||||
if (tokenClass == asTC_UNKNOWN)
|
if (tokenClass == asTC_UNKNOWN)
|
||||||
|
@ -492,8 +497,8 @@ int ScriptMachine::pragmaCallback(const std::string &pragmaText,
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ScriptMachine::includeCallback(const char *include, const char *from,
|
int ScriptMachine::includeCallback(const QString &include, const QString &from,
|
||||||
CScriptBuilder *builder, void *userParam) {
|
asBuilder *builder, void *userParam) {
|
||||||
asIScriptEngine *engine = builder->GetEngine();
|
asIScriptEngine *engine = builder->GetEngine();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
#define SCRIPTMACHINE_H
|
#define SCRIPTMACHINE_H
|
||||||
|
|
||||||
#include "AngelScript/sdk/add_on/contextmgr/contextmgr.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 "AngelScript/sdk/angelscript/include/angelscript.h"
|
||||||
|
#include "class/asbuilder.h"
|
||||||
|
|
||||||
#include "asdebugger.h"
|
#include "asdebugger.h"
|
||||||
|
|
||||||
|
@ -92,11 +92,11 @@ private:
|
||||||
static void returnContextCallback(asIScriptEngine *engine,
|
static void returnContextCallback(asIScriptEngine *engine,
|
||||||
asIScriptContext *ctx, void *param);
|
asIScriptContext *ctx, void *param);
|
||||||
|
|
||||||
static int pragmaCallback(const std::string &pragmaText,
|
static int pragmaCallback(const QByteArray &pragmaText, asBuilder *builder,
|
||||||
CScriptBuilder &builder, void *userParam);
|
void *userParam);
|
||||||
|
|
||||||
static int includeCallback(const char *include, const char *from,
|
static int includeCallback(const QString &include, const QString &from,
|
||||||
CScriptBuilder *builder, void *userParam);
|
asBuilder *builder, void *userParam);
|
||||||
|
|
||||||
static QString processTranslation(const char *content);
|
static QString processTranslation(const char *content);
|
||||||
|
|
||||||
|
@ -107,6 +107,8 @@ private:
|
||||||
signals:
|
signals:
|
||||||
void onOutput(MessageType type, const MessageInfo &message);
|
void onOutput(MessageType type, const MessageInfo &message);
|
||||||
|
|
||||||
|
void onDebugFinished();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
asIScriptEngine *_engine = nullptr;
|
asIScriptEngine *_engine = nullptr;
|
||||||
asDebugger *_debugger = nullptr;
|
asDebugger *_debugger = nullptr;
|
||||||
|
|
|
@ -43,6 +43,9 @@ const auto OTHER_LOG_LEVEL = QStringLiteral("sys.loglevel");
|
||||||
SettingManager::SettingManager() {
|
SettingManager::SettingManager() {
|
||||||
_defaultFont = qApp->font();
|
_defaultFont = qApp->font();
|
||||||
qRegisterMetaType<RecentFileManager::RecentInfo>();
|
qRegisterMetaType<RecentFileManager::RecentInfo>();
|
||||||
|
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||||
|
qRegisterMetaTypeStreamOperators<RecentFileManager::RecentInfo>();
|
||||||
|
#endif
|
||||||
load();
|
load();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,10 +16,9 @@ DriverSelectorDialog::DriverSelectorDialog(QWidget *parent)
|
||||||
drivers = new QListWidget(this);
|
drivers = new QListWidget(this);
|
||||||
drivers->setSortingEnabled(false);
|
drivers->setSortingEnabled(false);
|
||||||
auto ico = ICONRES("opendriver");
|
auto ico = ICONRES("opendriver");
|
||||||
auto infos = QStorageInfo::mountedVolumes();
|
|
||||||
layout->addWidget(new QLabel(tr("PleaseChooseDriver"), this));
|
layout->addWidget(new QLabel(tr("PleaseChooseDriver"), this));
|
||||||
layout->addSpacing(5);
|
layout->addSpacing(5);
|
||||||
for (auto &item : Utilities::getStorageDevices()) {
|
for (auto &item : QStorageInfo::mountedVolumes()) {
|
||||||
auto device = item.device();
|
auto device = item.device();
|
||||||
#ifdef Q_OS_WINDOWS
|
#ifdef Q_OS_WINDOWS
|
||||||
drivers->addItem(new QListWidgetItem(ico, item.rootPath()));
|
drivers->addItem(new QListWidgetItem(ico, item.rootPath()));
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#include "mainwindow.h"
|
#include "mainwindow.h"
|
||||||
|
|
||||||
#include "Qt-Advanced-Docking-System/src/DockAreaWidget.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/DockSplitter.h"
|
||||||
#include "Qt-Advanced-Docking-System/src/DockWidgetTab.h"
|
#include "Qt-Advanced-Docking-System/src/DockWidgetTab.h"
|
||||||
#include "aboutsoftwaredialog.h"
|
#include "aboutsoftwaredialog.h"
|
||||||
|
@ -273,6 +274,7 @@ void MainWindow::buildUpDockSystem(QWidget *container) {
|
||||||
CDockWidget *CentralDockWidget =
|
CDockWidget *CentralDockWidget =
|
||||||
new CDockWidget(QStringLiteral("CentralWidget"));
|
new CDockWidget(QStringLiteral("CentralWidget"));
|
||||||
CentralDockWidget->setWidget(label);
|
CentralDockWidget->setWidget(label);
|
||||||
|
CentralDockWidget->setFeature(ads::CDockWidget::DockWidgetFocusable, false);
|
||||||
CentralDockWidget->setFeature(ads::CDockWidget::NoTab, true);
|
CentralDockWidget->setFeature(ads::CDockWidget::NoTab, true);
|
||||||
auto editorViewArea = m_dock->setCentralWidget(CentralDockWidget);
|
auto editorViewArea = m_dock->setCentralWidget(CentralDockWidget);
|
||||||
|
|
||||||
|
@ -2384,6 +2386,10 @@ void MainWindow::connectEditorView(EditorView *editor) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::swapEditor(EditorView *old, EditorView *cur) {
|
void MainWindow::swapEditor(EditorView *old, EditorView *cur) {
|
||||||
|
if (old == cur) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (old != nullptr) {
|
if (old != nullptr) {
|
||||||
auto hexeditor = old->hexEditor();
|
auto hexeditor = old->hexEditor();
|
||||||
hexeditor->disconnect(SLOT(cursorLocationChanged()));
|
hexeditor->disconnect(SLOT(cursorLocationChanged()));
|
||||||
|
|
|
@ -55,11 +55,13 @@ ScriptingDialog::ScriptingDialog(QWidget *parent)
|
||||||
layout->addWidget(m_status);
|
layout->addWidget(m_status);
|
||||||
buildUpContent(cw);
|
buildUpContent(cw);
|
||||||
|
|
||||||
QLineMarksInfoCenter::instance()->loadMarkTypes(
|
auto lmic = QLineMarksInfoCenter::instance();
|
||||||
QCE::fetchDataFile(":/qcodeedit/marks.qxm"));
|
lmic->loadMarkTypes(QCE::fetchDataFile(":/qcodeedit/marks.qxm"));
|
||||||
// get symbol ID
|
// get symbol ID
|
||||||
m_symID.insert(Symbols::BreakPoint,
|
m_symID.insert(Symbols::BreakPoint, lmic->markTypeId("breakpoint"));
|
||||||
QLineMarksInfoCenter::instance()->markTypeId("breakpoint"));
|
m_symID.insert(Symbols::DbgRunCurrentLine, lmic->markTypeId("current"));
|
||||||
|
m_symID.insert(Symbols::DbgRunHitBreakPoint,
|
||||||
|
lmic->markTypeId("breakpoint-current"));
|
||||||
|
|
||||||
updateEditModeEnabled();
|
updateEditModeEnabled();
|
||||||
|
|
||||||
|
@ -74,6 +76,65 @@ ScriptingDialog::ScriptingDialog(QWidget *parent)
|
||||||
void ScriptingDialog::initConsole() {
|
void ScriptingDialog::initConsole() {
|
||||||
Q_ASSERT(m_consoleout);
|
Q_ASSERT(m_consoleout);
|
||||||
m_consoleout->init();
|
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() {
|
void ScriptingDialog::buildUpRibbonBar() {
|
||||||
|
@ -417,6 +478,7 @@ void ScriptingDialog::buildUpDockSystem(QWidget *container) {
|
||||||
CDockWidget *CentralDockWidget =
|
CDockWidget *CentralDockWidget =
|
||||||
new CDockWidget(QStringLiteral("CentralWidget"));
|
new CDockWidget(QStringLiteral("CentralWidget"));
|
||||||
CentralDockWidget->setWidget(label);
|
CentralDockWidget->setWidget(label);
|
||||||
|
CentralDockWidget->setFeature(ads::CDockWidget::DockWidgetFocusable, false);
|
||||||
CentralDockWidget->setFeature(ads::CDockWidget::NoTab, true);
|
CentralDockWidget->setFeature(ads::CDockWidget::NoTab, true);
|
||||||
m_editorViewArea = m_dock->setCentralWidget(CentralDockWidget);
|
m_editorViewArea = m_dock->setCentralWidget(CentralDockWidget);
|
||||||
|
|
||||||
|
@ -468,6 +530,11 @@ void ScriptingDialog::registerEditorView(ScriptEditor *editor) {
|
||||||
Q_ASSERT(editor);
|
Q_ASSERT(editor);
|
||||||
Q_ASSERT(m_views.contains(editor));
|
Q_ASSERT(m_views.contains(editor));
|
||||||
|
|
||||||
|
auto m = m_consoleout->machine();
|
||||||
|
if (m->isInDebugMode()) {
|
||||||
|
auto dbg = m->debugger();
|
||||||
|
}
|
||||||
|
|
||||||
m_views.removeOne(editor);
|
m_views.removeOne(editor);
|
||||||
if (currentEditor() == editor) {
|
if (currentEditor() == editor) {
|
||||||
m_curEditor = nullptr;
|
m_curEditor = nullptr;
|
||||||
|
@ -575,6 +642,14 @@ void ScriptingDialog::openFile(const QString &filename) {
|
||||||
m_dock->addDockWidget(ads::CenterDockWidgetArea, editor, editorViewArea());
|
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() {
|
void ScriptingDialog::on_newfile() {
|
||||||
if (!newOpenFileSafeCheck()) {
|
if (!newOpenFileSafeCheck()) {
|
||||||
return;
|
return;
|
||||||
|
@ -783,7 +858,9 @@ void ScriptingDialog::on_runscript() {
|
||||||
auto e = editor->editor();
|
auto e = editor->editor();
|
||||||
m_consoleout->clear();
|
m_consoleout->clear();
|
||||||
setRunDebugMode(true, false);
|
setRunDebugMode(true, false);
|
||||||
m_consoleout->machine()->executeScript(e->fileName());
|
if (!m_consoleout->machine()->executeScript(e->fileName())) {
|
||||||
|
setRunDebugMode(false);
|
||||||
|
}
|
||||||
setRunDebugMode(false);
|
setRunDebugMode(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -794,24 +871,42 @@ void ScriptingDialog::on_rundbgscript() {
|
||||||
auto e = editor->editor();
|
auto e = editor->editor();
|
||||||
m_consoleout->clear();
|
m_consoleout->clear();
|
||||||
setRunDebugMode(true, true);
|
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_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() {
|
void ScriptingDialog::on_togglebreakpoint() {
|
||||||
auto editor = currentEditor();
|
auto editor = currentEditor();
|
||||||
|
|
|
@ -146,6 +146,8 @@ private:
|
||||||
|
|
||||||
void openFile(const QString &filename);
|
void openFile(const QString &filename);
|
||||||
|
|
||||||
|
void runDbgCommand(asDebugger::DebugAction action);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void on_newfile();
|
void on_newfile();
|
||||||
void on_openfile();
|
void on_openfile();
|
||||||
|
@ -202,6 +204,7 @@ private:
|
||||||
Ribbon *m_ribbon = nullptr;
|
Ribbon *m_ribbon = nullptr;
|
||||||
|
|
||||||
size_t m_newIndex = 1;
|
size_t m_newIndex = 1;
|
||||||
|
QPair<QString, int> _lastCurLine = {QString(), -1};
|
||||||
|
|
||||||
QList<ScriptEditor *> m_views;
|
QList<ScriptEditor *> m_views;
|
||||||
QMap<Symbols, int> m_symID;
|
QMap<Symbols, int> m_symID;
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<mark>
|
<mark>
|
||||||
<id>current</id>
|
<id>current</id>
|
||||||
<icon>:/com.wingsummer.winghex/images/scriptdbg/hitcur.png</icon>
|
<icon>:/com.wingsummer.winghex/images/scriptdbg/hitcur.png</icon>
|
||||||
<color>#F4FFA7</color>
|
<color>#10F4FFA7</color>
|
||||||
<focus>false</focus>
|
<focus>false</focus>
|
||||||
<priority>2</priority>
|
<priority>2</priority>
|
||||||
<persistency>2</persistency>
|
<persistency>2</persistency>
|
||||||
|
@ -12,7 +12,7 @@
|
||||||
<mark>
|
<mark>
|
||||||
<id>breakpoint-current</id>
|
<id>breakpoint-current</id>
|
||||||
<icon>:/com.wingsummer.winghex/images/scriptdbg/hitbp.png</icon>
|
<icon>:/com.wingsummer.winghex/images/scriptdbg/hitbp.png</icon>
|
||||||
<color>#F4FFA7</color>
|
<color>#10F4FFA7</color>
|
||||||
<focus>false</focus>
|
<focus>false</focus>
|
||||||
<priority>2</priority>
|
<priority>2</priority>
|
||||||
<persistency>2</persistency>
|
<persistency>2</persistency>
|
||||||
|
|
|
@ -181,27 +181,8 @@ public:
|
||||||
return t.inherits(QStringLiteral("text/plain"));
|
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) {
|
static bool isStorageDevice(const QString &path) {
|
||||||
auto sdns = getStorageDevices();
|
auto sdns = QStorageInfo::mountedVolumes();
|
||||||
return std::find_if(sdns.begin(), sdns.end(),
|
return std::find_if(sdns.begin(), sdns.end(),
|
||||||
[path](const QStorageInfo &info) {
|
[path](const QStorageInfo &info) {
|
||||||
return info.device() == path;
|
return info.device() == path;
|
||||||
|
@ -209,7 +190,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
static QStorageInfo getStorageDevice(const QString &path) {
|
static QStorageInfo getStorageDevice(const QString &path) {
|
||||||
auto sdns = getStorageDevices();
|
auto sdns = QStorageInfo::mountedVolumes();
|
||||||
auto r = std::find_if(
|
auto r = std::find_if(
|
||||||
sdns.begin(), sdns.end(),
|
sdns.begin(), sdns.end(),
|
||||||
[path](const QStorageInfo &info) { return info.device() == path; });
|
[path](const QStorageInfo &info) { return info.device() == path; });
|
||||||
|
|