feat: 完善调试器;增加契合 QT 框架的 AngelScript 预处理器;还有一些修复;

fix: 修复在 Linux 本地驱动器查找问题;修复在 Linux 下最近文件无法保存的问题;
This commit is contained in:
寂静的羽夏 2024-09-16 22:00:32 +08:00
parent 5be4fc8b19
commit f49d6fb5e0
31 changed files with 2087 additions and 834 deletions

View File

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

View File

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

0
3rdparty/qcodeedit2/README.txt vendored Executable file → Normal file
View File

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

0
mkinstaller/deb/DEBIAN/control Executable file → Normal file
View File

0
mkinstaller/deb/DEBIAN/copyright Executable file → Normal file
View File

0
mkinstaller/deb/DEBIAN/postinst Executable file → Normal file
View File

0
mkinstaller/deb/DEBIAN/prerm Executable file → Normal file
View File

0
mkinstaller/deb/share/icon.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

0
mkinstaller/deb/share/winghexpro128.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 33 KiB

0
mkinstaller/deb/share/winghexpro32.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

0
mkinstaller/deb/share/winghexpro64.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

0
mkinstaller/deb/share/x-winghex.xml Executable file → Normal file
View File

978
src/class/asbuilder.cpp Normal file
View File

@ -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 &sectionname,
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

199
src/class/asbuilder.h Normal file
View File

@ -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 &sectionname, 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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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