Compare commits

...

25 Commits

Author SHA1 Message Date
寂静的羽夏 56304a75d0 fix: 缩小字体设置范围; 2025-05-11 13:13:52 +08:00
寂静的羽夏 57bd7b7c61 fix: 修复设置无法重置的问题;一些增强和修复; 2025-05-10 21:07:41 +08:00
寂静的羽夏 066ab5375b fix: 修复代码提示相关和字节搜索框问题; 2025-05-09 12:40:39 +08:00
寂静的羽夏 1be1f7322f fix: 修复和增强编辑器相关问题; 2025-05-09 12:18:17 +08:00
寂静的羽夏 b49523e952 fix: 修复增强预处理器和缺失的字符串函数 2025-05-06 13:44:59 +08:00
寂静的羽夏 960de3afb6 fix: 修复损坏的搜索上下文显示;增强脚本宏和增加内置宏; 2025-05-05 15:05:33 +08:00
寂静的羽夏 3f26766575 feat: print 多参数和 promise 支持;修复终端主题设置; 2025-05-04 11:53:30 +08:00
寂静的羽夏 098f08e906 ci: 更新 setup-python 2025-05-02 19:38:57 +08:00
寂静的羽夏 cb2e8743af feat: 功能微调和更新 2025-05-02 19:16:28 +08:00
寂静的羽夏 8ad73431c1 feat: 更新语言文件和README; 2025-05-02 18:11:43 +08:00
寂静的羽夏 db60affc22 fix: 修复通配符匹配功能损坏问题; 2025-05-02 17:53:15 +08:00
寂静的羽夏 32e27ba543 feat: 代码填充完善;脚本功能增强; 2025-05-02 14:02:31 +08:00
寂静的羽夏 c01ca038a3 feat: 一些 Bug 修复和脚本处理相关 2025-04-30 17:25:39 +08:00
寂静的羽夏 d8069aedde feat: 更好用的代码填充;更强大的控制台;一些 Bug 修复; 2025-04-27 23:32:03 +08:00
寂静的羽夏 cf3d4da8e8 fix: 更好的代码填充和一些 Bug 修复; 2025-04-27 11:31:15 +08:00
寂静的羽夏 2ee3051a7d feat: 更好的代码填充;自动重载文件相关; 2025-04-24 21:10:54 +08:00
寂静的羽夏 f59755e3f0 fix: 更好的代码提示支持;修复禁用脚本功能导致的程序崩溃; 2025-04-19 18:57:10 +08:00
寂静的羽夏 ef8bb9aa3a feat: 更好的代码填充; 2025-04-16 13:44:40 +08:00
寂静的羽夏 f87c0970be feat: 更好的代码填充; 2025-04-12 18:50:47 +08:00
寂静的羽夏 f51630c6f0 feat: 移除数据可视化功能以轻量化;优化 WingAngelAPI 脚本注册; 2025-04-10 12:51:13 +08:00
寂静的羽夏 d6680e3f11 feat: 脚本引擎单例化;更合理的格式化参数; 2025-04-09 14:05:30 +08:00
寂静的羽夏 7597663d76 feat: 数值增加无符号切换格式;初步代码内容提示; 2025-04-06 20:11:30 +08:00
寂静的羽夏 7ee69b5ced fix: 修复克隆页右键菜单无效;修复终端主题修改输出颜色不改变; 2025-03-31 13:27:39 +08:00
寂静的羽夏 570f78994c fix: 优化文本预览功能体验; 2025-03-30 13:27:49 +08:00
寂静的羽夏 5aaa59937d fix: 修复终端输出颜色不跟随主题;修复光标字节大小显示; 2025-03-30 10:18:20 +08:00
129 changed files with 10112 additions and 10171 deletions

View File

@ -40,7 +40,7 @@ jobs:
run: sudo apt install fakeroot patchelf -y
- name: Setup Python
uses: actions/setup-python@v2
uses: actions/setup-python@v5
with:
python-version: '3.8'
cache: 'pip'

View File

@ -43,7 +43,7 @@ jobs:
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}
- name: Setup Python
uses: actions/setup-python@v2
uses: actions/setup-python@v5
with:
python-version: '3.8'
cache: 'pip'

View File

@ -109,6 +109,15 @@ QString QConsoleWidget::getCommandLine() {
return code;
}
void QConsoleWidget::paste() {
QTextCursor textCursor = this->textCursor();
const QMimeData *const clipboard = QApplication::clipboard()->mimeData();
const QString text = clipboard->text();
if (!text.isNull()) {
textCursor.insertText(text, channelCharFormat(StandardInput));
}
}
void QConsoleWidget::handleReturnKey() {
QString code = getCommandLine();
@ -189,12 +198,7 @@ void QConsoleWidget::keyPressEvent(QKeyEvent *e) {
// Allow paste only if the selection is in the interactive area ...
if (e->key() == Qt::Key_V && e->modifiers() == Qt::ControlModifier) {
if (selectionInEditZone || isCursorInEditZone()) {
const QMimeData *const clipboard =
QApplication::clipboard()->mimeData();
const QString text = clipboard->text();
if (!text.isNull()) {
textCursor.insertText(text, channelCharFormat(StandardInput));
}
paste();
}
e->accept();
@ -361,16 +365,15 @@ void QConsoleWidget::replaceCommandLine(const QString &str) {
}
QString QConsoleWidget::currentCommandLine() const {
// Select the text after the last command prompt ...
QTextCursor textCursor = this->textCursor();
textCursor.movePosition(QTextCursor::End);
textCursor.setPosition(inpos_, QTextCursor::KeepAnchor);
return textCursor.selectedText();
}
int QConsoleWidget::currentHeaderPos() const { return inpos_; }
void QConsoleWidget::write(const QString &message, const QTextCharFormat &fmt) {
QTextCharFormat currfmt = currentCharFormat();
QTextCursor tc = textCursor();
if (mode() == Input) {
@ -396,8 +399,8 @@ void QConsoleWidget::write(const QString &message, const QTextCharFormat &fmt) {
inpos_ = tc.position() - inpos_;
// restore the edit pos
tc.movePosition(QTextCursor::Left, QTextCursor::MoveAnchor, editpos);
tc.setCharFormat({});
setTextCursor(tc);
setCurrentCharFormat(currfmt);
} else {
// in output mode messages are ed
QTextCursor tc1 = tc;
@ -412,6 +415,8 @@ void QConsoleWidget::write(const QString &message, const QTextCharFormat &fmt) {
textCursor().insertText(message, fmt);
ensureCursorVisible();
tc.setCharFormat({});
// restore cursor if needed
if (needsRestore)
setTextCursor(tc);

View File

@ -77,6 +77,10 @@ public:
// get the current command line
QString getCommandLine();
virtual void paste();
int currentHeaderPos() const;
public slots:
// write to StandardOutput
@ -94,6 +98,7 @@ signals:
protected:
bool canPaste() const;
bool canCut() const;
virtual void handleReturnKey();
virtual void handleTabKey();
// reimp QPlainTextEdit functions
@ -112,7 +117,6 @@ protected:
static History history_;
ConsoleMode mode_;
int inpos_;
QString currentMultiLineCode_;
QConsoleIODevice *iodevice_;
QTextCharFormat chanFormat_[nConsoleChannels];
};

View File

@ -61,7 +61,7 @@ qsizetype QHexCursor::selectionLength(qsizetype index) const {
qsizetype QHexCursor::currentSelectionLength() const {
if (hasPreviewSelection() && m_preMode != SelectionRemove) {
return qAbs(m_position - m_selection + 1);
return qAbs(m_position - m_selection) + 1;
}
qsizetype len = 0;

View File

@ -437,44 +437,30 @@ void QHexDocument::findAllBytes(qsizetype begin, qsizetype end,
}
}
qsizetype QHexDocument::findAllBytesExt(qsizetype begin, qsizetype end,
void QHexDocument::findAllBytesExt(qsizetype begin, qsizetype end,
const QString &pattern,
QList<qsizetype> &results,
const std::function<bool()> &pred) {
results.clear();
auto patterns = parseConvertPattern(pattern);
if (patterns.isEmpty()) {
return 0;
if (end < 0) {
end = m_buffer->length();
}
qsizetype p = begin > 0 ? begin : 0;
qsizetype e = end > begin ? end : -1;
qsizetype offset = 0;
for (auto &p : patterns) {
if (std::holds_alternative<QByteArray>(p)) {
offset += std::get<QByteArray>(p).length();
} else if (std::holds_alternative<size_t>(p)) {
offset += std::get<size_t>(p);
} else if (std::holds_alternative<HexWildItem>(p)) {
offset += 1;
if (pattern.isEmpty() || end > m_buffer->length() || begin >= end) {
return;
}
}
while (pred()) {
p = findNextExt(p, pattern);
if (p < 0 || (e > 0 && p > e)) {
qsizetype pos = begin;
qsizetype n = pattern.size();
while (pos + n <= end && pred()) {
qsizetype f = findNextExt(pos, pattern);
if (f < 0 || f + n > end)
break;
results.append(f);
pos = f + 1;
if (results.size() >= QHEXVIEW_FIND_LIMIT) {
break;
}
if (results.size() > QHEXVIEW_FIND_LIMIT) {
break;
}
results.append(p);
p += offset;
}
return offset;
}
bool QHexDocument::insert(qsizetype offset, uchar b) {
@ -538,287 +524,92 @@ bool QHexDocument::_remove(qsizetype offset, qsizetype len) {
return true;
}
bool QHexDocument::parsePattern(const QString &pattern,
QList<PatternByte> &out) {
out.clear();
QString p = pattern;
p.remove(' ');
if (p.size() % 2 != 0) {
return false;
}
for (int i = 0; i < p.size(); i += 2) {
QChar hi = p[i], lo = p[i + 1];
PatternByte pb;
// high nibble
if (hi != '?') {
int v = hex2Int(hi);
if (v < 0) {
return false;
}
pb.value |= quint8(v << 4);
pb.mask |= 0xF0;
}
// low nibble
if (lo != '?') {
int v = hex2Int(lo);
if (v < 0) {
return false;
}
pb.value |= quint8(v);
pb.mask |= 0x0F;
}
out.append(pb);
}
return true;
}
int QHexDocument::hex2Int(const QChar &c) {
ushort u = c.unicode();
if (u >= '0' && u <= '9')
return u - '0';
else if (u >= 'A' && u <= 'F')
return 10 + (u - 'A');
else if (u >= 'a' && u <= 'f')
return 10 + (u - 'a');
else
return -1;
}
qsizetype QHexDocument::findNextExt(qsizetype begin,
const QList<FindStep> &patterns) {
auto op = [this](qsizetype &pos, const FindStep &step,
qsizetype *begin = nullptr) -> bool {
if (pos < 0 || pos >= length()) {
return false;
}
if (std::holds_alternative<QByteArray>(step)) {
auto v = std::get<QByteArray>(step);
auto len = v.length();
auto r = findNext(pos, v);
if (r >= 0) {
if (begin) {
*begin = r;
} else {
if (r != pos) {
pos = -1;
return false;
}
}
pos = r + len;
return true;
} else {
pos = -1;
return false;
}
} else if (std::holds_alternative<HexWildItem>(step)) {
auto v = std::get<HexWildItem>(step);
auto wc = uchar(at(pos));
pos += 1;
if (v.higher == '?') {
if ((wc & 0xf) == v.lower) {
return true;
}
} else {
if ((wc >> 4) == v.higher) {
return true;
}
}
} else if (std::holds_alternative<size_t>(step)) {
auto v = std::get<size_t>(step);
pos += v;
if (v + pos < length()) {
return true;
}
}
return false;
};
while (begin < length()) {
auto pos = begin;
auto p = patterns.cbegin();
auto r = op(pos, *p, &begin);
if (!r) {
if (pos < 0) {
const QList<PatternByte> &patterns) {
if (patterns.isEmpty() || begin < 0 ||
begin + patterns.size() > m_buffer->length())
return -1;
}
continue;
}
++p;
qsizetype n = patterns.size();
for (qsizetype pos = begin; pos + n <= m_buffer->length(); ++pos) {
bool ok = true;
for (; p != patterns.cend(); ++p) {
auto r = op(pos, *p);
if (!r) {
for (qsizetype i = 0; i < n; ++i) {
if (!matchByte(m_buffer->at(pos + i), patterns[i])) {
ok = false;
if (pos < 0) {
return -1;
}
begin = pos;
break;
}
}
if (ok) {
return begin;
if (ok)
return pos;
}
}
return -1;
}
qsizetype QHexDocument::findPreviousExt(qsizetype begin,
const QList<FindStep> &patterns) {
auto op = [this](qsizetype &pos, const FindStep &step,
qsizetype *begin = nullptr) -> bool {
if (pos < 0 || pos >= length()) {
return false;
}
if (std::holds_alternative<QByteArray>(step)) {
auto v = std::get<QByteArray>(step);
auto len = v.length();
auto r = findPrevious(pos, v);
if (r >= 0) {
if (begin) {
*begin = r;
} else {
if (r + len != pos) {
pos = -1;
return false;
}
}
pos = r - len;
return true;
} else {
pos = -1;
return false;
}
} else if (std::holds_alternative<HexWildItem>(step)) {
auto v = std::get<HexWildItem>(step);
auto wc = uchar(at(pos));
pos -= 1;
if (v.higher == '?') {
if ((wc & 0xf) == v.lower) {
return true;
}
} else {
if ((wc >> 4) == v.higher) {
return true;
}
}
} else if (std::holds_alternative<size_t>(step)) {
auto v = std::get<size_t>(step);
pos -= v;
if (v - pos >= 0) {
return true;
}
}
return false;
};
while (begin >= 0) {
auto pos = begin;
auto p = patterns.crbegin();
auto r = op(pos, *p, &begin);
if (!r) {
if (pos < 0) {
const QList<PatternByte> &patterns) {
if (patterns.isEmpty() || begin < 0)
return -1;
}
continue;
}
++p;
qsizetype n = patterns.size();
qsizetype maxStart = qMin(begin, m_buffer->length() - n);
for (qsizetype pos = maxStart; pos >= 0; --pos) {
bool ok = true;
for (; p != patterns.crend(); ++p) {
auto r = op(pos, *p);
if (!r) {
for (qsizetype i = 0; i < n; ++i) {
if (!matchByte(m_buffer->at(pos + i), patterns[i])) {
ok = false;
if (pos < 0) {
return -1;
}
begin = pos;
break;
}
}
if (ok) {
return begin;
if (ok)
return pos;
}
}
return -1;
}
QList<QHexDocument::FindStep>
QHexDocument::parseConvertPattern(const QString &pattern) {
// process hex pattern
QList<HexWildItem> words;
std::optional<uchar> higher;
for (auto pchar = pattern.cbegin(); pchar != pattern.cend(); ++pchar) {
if (pchar->isSpace()) {
if (higher) {
return {};
} else {
continue;
}
}
auto c = pchar->unicode();
if (c >= '0' && c <= '9') {
if (higher) {
HexWildItem item;
item.higher = higher.value();
item.lower = uchar(c) - '0';
words.append(item);
higher.reset();
} else {
higher = uchar(c) - '0';
}
} else if (c >= 'A' && c <= 'F') {
if (higher) {
HexWildItem item;
item.higher = higher.value();
item.lower = uchar(c) - 'A' + 10;
words.append(item);
higher.reset();
} else {
higher = uchar(c) - 'A' + 10;
}
} else if (c >= 'a' && c <= 'f') {
if (higher) {
HexWildItem item;
item.higher = higher.value();
item.lower = uchar(c) - 'a' + 10;
words.append(item);
higher.reset();
} else {
higher = uchar(c) - 'a' + 10;
}
} else if (c == '?') {
if (higher) {
HexWildItem item;
item.higher = higher.value();
item.lower = '?';
words.append(item);
higher.reset();
} else {
higher = '?';
}
}
}
if (higher) {
return {};
}
if (!words.isEmpty()) {
QList<FindStep> steps;
// parsing...
QByteArray buffer;
size_t len = 0;
for (auto pw = words.cbegin(); pw != words.cend(); ++pw) {
auto higher = pw->higher;
auto lower = pw->lower;
if (higher == '?' || lower == '?') {
if (higher == '?' && lower == '?') {
if (!buffer.isEmpty()) {
steps.append(buffer);
buffer.clear();
}
len++;
} else {
if (len != 0) {
steps.append(len);
len = 0;
}
if (!buffer.isEmpty()) {
steps.append(buffer);
buffer.clear();
}
HexWildItem item;
item.higher = higher;
item.lower = lower;
steps.append(item);
}
} else {
if (len != 0) {
steps.append(len);
len = 0;
}
buffer.append(char(pw->higher << 4 | pw->lower));
}
}
// clean up
if (len != 0) {
steps.append(len);
}
if (!buffer.isEmpty()) {
steps.append(buffer);
}
return steps;
}
return {};
}
/*======================*/
// modified by wingsummer
@ -913,27 +704,31 @@ void QHexDocument::beginMarco(const QString &text) {
void QHexDocument::endMarco() { m_undostack->endMacro(); }
void QHexDocument::Insert(QHexCursor *cursor, qsizetype offset, uchar b,
bool QHexDocument::Insert(QHexCursor *cursor, qsizetype offset, uchar b,
int nibbleindex) {
this->Insert(cursor, offset, QByteArray(1, char(b)), nibbleindex);
return this->Insert(cursor, offset, QByteArray(1, char(b)), nibbleindex);
}
void QHexDocument::Replace(QHexCursor *cursor, qsizetype offset, uchar b,
bool QHexDocument::Replace(QHexCursor *cursor, qsizetype offset, uchar b,
int nibbleindex) {
this->Replace(cursor, offset, QByteArray(1, char(b)), nibbleindex);
return this->Replace(cursor, offset, QByteArray(1, char(b)), nibbleindex);
}
void QHexDocument::Insert(QHexCursor *cursor, qsizetype offset,
bool QHexDocument::Insert(QHexCursor *cursor, qsizetype offset,
const QByteArray &data, int nibbleindex) {
if (m_keepsize || m_readonly || m_islocked)
return;
if (m_keepsize || m_readonly || m_islocked) {
return false;
}
auto cmd = MakeInsert(nullptr, cursor, offset, data, nibbleindex);
if (cmd) {
m_undostack->push(cmd);
} else {
return false;
}
emit documentChanged();
return true;
}
void QHexDocument::Append(QHexCursor *cursor, uchar b, int nibbleindex) {
@ -951,15 +746,19 @@ void QHexDocument::Append(QHexCursor *cursor, const QByteArray &data,
emit documentChanged();
}
void QHexDocument::Replace(QHexCursor *cursor, qsizetype offset,
bool QHexDocument::Replace(QHexCursor *cursor, qsizetype offset,
const QByteArray &data, int nibbleindex) {
if (m_readonly || m_islocked)
return;
if (m_readonly || m_islocked) {
return false;
}
auto cmd = MakeReplace(nullptr, cursor, offset, data, nibbleindex);
if (cmd) {
m_undostack->push(cmd);
} else {
return false;
}
emit documentChanged();
return true;
}
bool QHexDocument::Remove(QHexCursor *cursor, qsizetype offset, qsizetype len,
@ -1063,21 +862,19 @@ qsizetype QHexDocument::findPrevious(qsizetype begin, const QByteArray &ba) {
}
qsizetype QHexDocument::findNextExt(qsizetype begin, const QString &pattern) {
auto patterns = parseConvertPattern(pattern);
if (patterns.isEmpty()) {
QList<PatternByte> patterns;
if (!parsePattern(pattern, patterns)) {
return -1;
}
return findNextExt(begin, patterns);
}
qsizetype QHexDocument::findPreviousExt(qsizetype begin,
const QString &pattern) {
auto patterns = parseConvertPattern(pattern);
if (patterns.isEmpty()) {
QList<PatternByte> patterns;
if (!parsePattern(pattern, patterns)) {
return -1;
}
return findPreviousExt(begin, patterns);
}

View File

@ -107,7 +107,7 @@ public:
QList<qsizetype> &results,
const std::function<bool()> &pred = [] { return true; });
qsizetype findAllBytesExt(
void findAllBytesExt(
qsizetype begin, qsizetype end, const QString &pattern,
QList<qsizetype> &results,
const std::function<bool()> &pred = [] { return true; });
@ -148,14 +148,14 @@ public slots:
void beginMarco(const QString &text);
void endMarco();
void Insert(QHexCursor *cursor, qsizetype offset, uchar b, int nibbleindex);
void Insert(QHexCursor *cursor, qsizetype offset, const QByteArray &data,
bool Insert(QHexCursor *cursor, qsizetype offset, uchar b, int nibbleindex);
bool Insert(QHexCursor *cursor, qsizetype offset, const QByteArray &data,
int nibbleindex);
void Append(QHexCursor *cursor, uchar b, int nibbleindex);
void Append(QHexCursor *cursor, const QByteArray &data, int nibbleindex);
void Replace(QHexCursor *cursor, qsizetype offset, uchar b,
bool Replace(QHexCursor *cursor, qsizetype offset, uchar b,
int nibbleindex);
void Replace(QHexCursor *cursor, qsizetype offset, const QByteArray &data,
bool Replace(QHexCursor *cursor, qsizetype offset, const QByteArray &data,
int nibbleindex = 0);
bool Remove(QHexCursor *cursor, qsizetype offset, qsizetype len,
int nibbleindex = 0);
@ -204,20 +204,28 @@ public slots:
bool _remove(qsizetype offset, qsizetype len);
private:
// AB
struct HexWildItem {
uchar higher; // A
uchar lower; // B
// PatternByte: represents a byte in the pattern, with nibble-level wildcard
// support
struct PatternByte {
quint8 value = 0; // fixed bits
quint8 mask =
0; // mask: which bits to match (1 = must match, 0 = wildcard)
};
// std::variant< find-content, hex with wildcard, all-wildcards >
using FindStep = std::variant<QByteArray, HexWildItem, size_t>;
// Parse pattern string (e.g., "00 ?? AB C? 88" or "00??ABC?88") into
// PatternByte list
bool parsePattern(const QString &pattern, QList<PatternByte> &out);
QList<FindStep> parseConvertPattern(const QString &pattern);
qsizetype findNextExt(qsizetype begin, const QList<FindStep> &patterns);
qsizetype findPreviousExt(qsizetype begin, const QList<FindStep> &patterns);
// Byte match using mask
inline bool matchByte(quint8 data, const PatternByte &pb) {
return (data & pb.mask) == (pb.value & pb.mask);
}
/*================================*/
int hex2Int(const QChar &c);
qsizetype findNextExt(qsizetype begin, const QList<PatternByte> &patterns);
qsizetype findPreviousExt(qsizetype begin,
const QList<PatternByte> &patterns);
/*================================*/
// modified by wingsummer

View File

@ -707,7 +707,6 @@ void QHexRenderer::drawHex(QPainter *painter, const QRect &linerect,
if (!dis)
this->applyMetadata(textcursor, line, Hex);
this->applyBookMark(textcursor, line, Hex);
this->applySelection(textcursor, line, Hex);
this->applyCursorHex(textcursor, line);
@ -718,24 +717,76 @@ void QHexRenderer::drawHex(QPainter *painter, const QRect &linerect,
ctx.palette.setColor(QPalette::Text, m_bytesColor);
textdocument.documentLayout()->draw(painter, ctx);
this->applyBookMark(painter, textcursor, line, Hex);
painter->restore();
}
void QHexRenderer::applyBookMark(QTextCursor &textcursor, qsizetype line,
Factor factor) {
void QHexRenderer::applyBookMark(QPainter *painter, QTextCursor &textcursor,
qsizetype line, Factor factor) {
if (!m_document->lineHasBookMark(line))
return;
painter->save();
auto pos = m_document->getLineBookmarksPos(line);
for (auto &item : pos) {
textcursor.setPosition(int((item % hexLineWidth()) * factor) + 2);
auto charformat = textcursor.charFormat();
textcursor.movePosition(QTextCursor::Left, QTextCursor::KeepAnchor,
factor - 1);
charformat.setFontWeight(QFont::Bold);
textcursor.setCharFormat(charformat);
auto off = item % hexLineWidth();
qreal begin, width;
auto height = lineHeight();
// add some paddings
if (factor == Hex) {
begin = getCellWidth() * off * 3 + 1;
begin += getCellWidth() / 2;
width = getCellWidth() * 2 + 2;
textcursor.setPosition(off * factor);
} else {
begin = getCellWidth() * off + 1;
width = getCellWidth();
textcursor.setPosition(m_cursor->currentColumn());
}
auto charformat = textcursor.charFormat();
auto textOutline = charformat.textOutline();
constexpr auto ALPHA = 180;
if (textOutline.style() != Qt::NoPen) {
auto outColor = textOutline.color();
outColor.setAlpha(ALPHA);
QPen pen(outColor, 1, Qt::DotLine);
painter->setPen(pen);
} else {
if (m_cursor->currentLine() == line &&
m_cursor->currentColumn() == off) {
auto color = m_bytesColor;
color.setAlpha(ALPHA);
QPen pen(color, 1, Qt::DotLine);
painter->setPen(pen);
} else {
auto foreground = charformat.foreground();
if (foreground.style() != Qt::NoBrush) {
auto textColor = foreground.color();
textColor.setAlpha(ALPHA);
QPen pen(textColor, 1, Qt::DotLine);
painter->setPen(pen);
} else {
auto color = m_bytesColor;
color.setAlpha(ALPHA);
QPen pen(color, 1, Qt::DotLine);
painter->setPen(pen);
}
}
}
painter->setBrush(Qt::transparent);
painter->setBackground(Qt::transparent);
painter->drawRect(begin, 0, width, height);
}
painter->restore();
}
void QHexRenderer::drawString(QPainter *painter, const QRect &linerect,
@ -770,6 +821,7 @@ void QHexRenderer::drawString(QPainter *painter, const QRect &linerect,
ctx.palette.setColor(QPalette::Text, m_bytesColor);
textdocument.documentLayout()->draw(painter, ctx);
this->applyBookMark(painter, textcursor, line, String);
painter->restore();
}

View File

@ -144,8 +144,10 @@ private:
qsizetype lineStart, qsizetype lineEnd, Factor factor,
bool strikeOut, bool hasSelection) const;
void applyBookMark(QTextCursor &textcursor, qsizetype line,
Factor factor); // added by wingsummer
// added by wingsummer
void applyBookMark(QPainter *painter, QTextCursor &textcursor,
qsizetype line, Factor factor);
void applyCursorAscii(QTextCursor &textcursor, qsizetype line) const;
void applyCursorHex(QTextCursor &textcursor, qsizetype line) const;
void drawAddress(QPainter *painter, const QRect &linerect, qsizetype line);

View File

@ -355,7 +355,7 @@ qsizetype QHexView::findPrevious(qsizetype begin, const QByteArray &ba) {
bool QHexView::RemoveSelection(int nibbleindex) {
if (!m_cursor->hasSelection())
return false;
return true;
auto total = m_cursor->selectionCount();
m_document->beginMarco(QStringLiteral("RemoveSelection"));
@ -375,7 +375,7 @@ bool QHexView::RemoveSelection(int nibbleindex) {
bool QHexView::removeSelection() {
if (!m_cursor->hasSelection())
return false;
return true;
// We essure selections are ordered by desending
// by selection-start, so it's safe to remove
@ -417,7 +417,7 @@ QByteArrayList QHexView::selectedBytes() const {
return res;
}
void QHexView::paste(bool hex) {
bool QHexView::paste(bool hex) {
QClipboard *c = qApp->clipboard();
QByteArray data;
@ -426,19 +426,28 @@ void QHexView::paste(bool hex) {
} else {
auto d = c->mimeData();
data = d->data(QStringLiteral("application/octet-stream"));
if (data.isEmpty()) {
data = d->text().toUtf8();
}
}
if (data.isEmpty())
return;
if (data.isEmpty()) {
return true;
}
this->removeSelection();
auto ret = this->removeSelection();
if (!ret) {
return false;
}
auto pos = m_cursor->position().offset();
if (!m_document->isKeepSize()) {
m_document->insert(pos, data);
bool ret = m_document->insert(pos, data);
m_cursor->moveTo(pos + data.length()); // added by wingsummer
} else
m_document->replace(pos, data);
return ret;
} else {
return m_document->replace(pos, data);
}
}
bool QHexView::Cut(bool hex, int nibbleindex) {
@ -457,7 +466,7 @@ bool QHexView::Cut(bool hex, int nibbleindex) {
}
}
void QHexView::Paste(bool hex, int nibbleindex) {
bool QHexView::Paste(bool hex, int nibbleindex) {
QClipboard *c = qApp->clipboard();
QByteArray data;
@ -466,19 +475,27 @@ void QHexView::Paste(bool hex, int nibbleindex) {
} else {
auto d = c->mimeData();
data = d->data(QStringLiteral("application/octet-stream"));
if (data.isEmpty()) {
data = d->text().toUtf8();
}
}
if (data.isEmpty())
return;
if (data.isEmpty()) {
return true;
}
this->RemoveSelection(nibbleindex);
auto ret = this->RemoveSelection(nibbleindex);
if (!ret) {
return false;
}
auto pos = m_cursor->position().offset();
if (m_cursor->insertionMode() == QHexCursor::InsertionMode::InsertMode) {
m_document->Insert(m_cursor, pos, data, nibbleindex);
auto ret = m_document->Insert(m_cursor, pos, data, nibbleindex);
m_cursor->moveTo(pos + data.length()); // added by wingsummer
return ret;
} else {
m_document->Replace(m_cursor, pos, data, nibbleindex);
return m_document->Replace(m_cursor, pos, data, nibbleindex);
}
}
@ -537,10 +554,11 @@ bool QHexView::copy(bool hex) {
qreal QHexView::fontSize() const { return m_fontSize; }
void QHexView::setScaleRate(qreal rate) {
if (m_scaleRate > 0) {
if (rate >= 0.2 && rate < 2.01) {
m_scaleRate = rate;
setFontSize(fontSize());
emit scaleRateChanged();
update();
}
}
@ -709,7 +727,7 @@ void QHexView::focusOutEvent(QFocusEvent *e) {
void QHexView::wheelEvent(QWheelEvent *e) {
if (qApp->keyboardModifiers() == Qt::ControlModifier) {
auto dela = e->angleDelta().y() / 1200.0 / 2;
auto dela = e->angleDelta().y() / 1200.0;
setScaleRate(scaleRate() + dela);
return;
}

View File

@ -135,10 +135,10 @@ public:
bool cut(bool hex);
bool copy(bool hex = false);
void paste(bool hex = false);
bool paste(bool hex = false);
bool Cut(bool hex = false, int nibbleindex = 0);
void Paste(bool hex = false, int nibbleindex = 0);
bool Paste(bool hex = false, int nibbleindex = 0);
void Replace(qsizetype offset, uchar b, int nibbleindex);
void Replace(qsizetype offset, const QByteArray &data, int nibbleindex = 0);

View File

@ -1,155 +0,0 @@
# macOS
.DS_Store
# Windows
Thumbs.db
ehthumbs.db
# Folder config file commonly created by Windows Explorer
Desktop.ini
# Recycle Bin used on file shares and remote volumes
$RECYCLE.BIN/
# Windows Installer files
*.cab
*.msi
*.msm
*.msp
# Windows shortcuts
*.lnk
# Thumbnail cache files created by Windows
Thumbs.db
Thumbs.db:encryptable
# Windows Desktop Search
*.pst
*.ost
*.log
# Compiled Object files, Static and Dynamic libs
*.o
*.lo
*.la
*.a
*.class
*.so
*.lib
*.dll
*.exe
# Python
__pycache__/
*.pyc
*.pyo
*.pyd
# Java
*.class
# Eclipse
.project
.classpath
.settings/
# IntelliJ
.idea/
# Visual Studio Code
.vscode/
.vscodium/
# Node.js
node_modules/
# Jupyter Notebook
.ipynb_checkpoints/
# Thumbnails
Thumbs/
Thumbs.db
# macOS metadata
._*
# TextMate
*.tmproj
*.tmproject
.tmtags
# Sublime Text
*.sublime-workspace
*.sublime-project
# VS Code directories
.vscode/
# CodeKit
.codekit-config.json
# Windows Installer files
*.cab
*.msi
*.msm
*.msp
# Compiled files
*.com
*.class
*.dll
*.exe
*.o
*.so
# Logs and databases
*.log
*.sql
*.sqlite
*.sqlite3
*.xml
# Binary and source packages
*.dmg
*.gz
*.iso
*.jar
*.tar
*.zip
*.rar
*.bin
*.war
*.ear
*.sar
*.bbl
*.pdf
*.xls
*.xlsx
*.ppt
*.pptx
# Virtual environment
venv/
env/
### Manually Entered
vim-debug/
**/out/bin
**/out/lib
**/out/share
_deps
.cache/
compile_commands.json
*.bak
docs/
*.old
# clangd cache
.cache/
.vim/
build/
debug/
realease/
Release/
Debug

View File

@ -1,26 +0,0 @@
cmake_minimum_required(VERSION 3.26)
project(
QJsonModel
VERSION 0.0.2
LANGUAGES C CXX
# Save this for later: HOMEPAGE_URL <URL>
DESCRIPTION
"QJsonModel is a json tree model class for Qt6/C++17 based on QAbstractItemModel. MIT License."
)
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets)
if(MSVC)
string(APPEND CMAKE_CXX_FLAGS " /utf-8")
string(APPEND CMAKE_C_FLAGS " /utf-8")
add_definitions(-D_CRT_SECURE_NO_WARNINGS)
endif()
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
add_library(QJsonModel STATIC include/details/QUtf8.hpp include/QJsonModel.hpp
QJsonModel.cpp)
target_link_libraries(QJsonModel PRIVATE Qt${QT_VERSION_MAJOR}::Widgets)

View File

@ -1,21 +0,0 @@
MIT License
Copyright (c) 2017 sacha schutz
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -1,518 +0,0 @@
/* QJsonModel.cpp
* Copyright (c) 2011 SCHUTZ Sacha
* Copyright © 2024 Saul D. Beniquez
*
* License:
* The MIT License (MIT)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
// NOLINTBEGIN
#include "QJsonModel.hpp"
#include <QDebug>
#include <QFile>
#include <QFont>
inline bool contains(const QStringList &list, const QString &value) {
for (auto val : list)
if (value.contains(val, Qt::CaseInsensitive))
return true;
return false;
}
QJsonTreeItem::QJsonTreeItem(QJsonTreeItem *parent) { mParent = parent; }
QJsonTreeItem::~QJsonTreeItem() { qDeleteAll(mChilds); }
void QJsonTreeItem::appendChild(QJsonTreeItem *item) { mChilds.append(item); }
QJsonTreeItem *QJsonTreeItem::child(int row) { return mChilds.value(row); }
QJsonTreeItem *QJsonTreeItem::parent() { return mParent; }
int QJsonTreeItem::childCount() const { return mChilds.count(); }
int QJsonTreeItem::row() const {
if (mParent)
return mParent->mChilds.indexOf(const_cast<QJsonTreeItem *>(this));
return 0;
}
void QJsonTreeItem::setKey(const QString &key) { mKey = key; }
void QJsonTreeItem::setValue(const QVariant &value) { mValue = value; }
void QJsonTreeItem::setType(const QJsonValue::Type &type) { mType = type; }
QString QJsonTreeItem::key() const { return mKey; }
QVariant QJsonTreeItem::value() const { return mValue; }
QJsonValue::Type QJsonTreeItem::type() const { return mType; }
QJsonTreeItem *QJsonTreeItem::load(const QJsonValue &value,
const QStringList &exceptions,
QJsonTreeItem *parent) {
QJsonTreeItem *rootItem = new QJsonTreeItem(parent);
rootItem->setKey("root");
if (value.isObject()) {
// Get all QJsonValue childs
const QStringList keys =
value.toObject().keys(); // To prevent clazy-range warning
for (const QString &key : keys) {
if (contains(exceptions, key)) {
continue;
}
QJsonValue v = value.toObject().value(key);
QJsonTreeItem *child = load(v, exceptions, rootItem);
child->setKey(key);
child->setType(v.type());
rootItem->appendChild(child);
}
} else if (value.isArray()) {
// Get all QJsonValue childs
int index = 0;
const QJsonArray array =
value.toArray(); // To prevent clazy-range warning
for (const QJsonValue &v : array) {
QJsonTreeItem *child = load(v, exceptions, rootItem);
child->setKey(QString::number(index));
child->setType(v.type());
rootItem->appendChild(child);
++index;
}
} else {
rootItem->setValue(value.toVariant());
rootItem->setType(value.type());
}
return rootItem;
}
//=========================================================================
inline uchar hexdig(uint u) { return (u < 0xa ? '0' + u : 'a' + u - 0xa); }
QByteArray escapedString(const QString &s) {
QByteArray ba(s.length(), Qt::Uninitialized);
uchar *cursor =
reinterpret_cast<uchar *>(const_cast<char *>(ba.constData()));
const uchar *ba_end = cursor + ba.length();
const ushort *src = reinterpret_cast<const ushort *>(s.constBegin());
const ushort *const end = reinterpret_cast<const ushort *>(s.constEnd());
while (src != end) {
if (cursor >= ba_end - 6) {
// ensure we have enough space
int pos = cursor - reinterpret_cast<const uchar *>(ba.constData());
ba.resize(ba.size() * 2);
cursor = reinterpret_cast<uchar *>(ba.data()) + pos;
ba_end =
reinterpret_cast<const uchar *>(ba.constData()) + ba.length();
}
uint u = *src++;
if (u < 0x80) {
if (u < 0x20 || u == 0x22 || u == 0x5c) {
*cursor++ = '\\';
switch (u) {
case 0x22:
*cursor++ = '"';
break;
case 0x5c:
*cursor++ = '\\';
break;
case 0x8:
*cursor++ = 'b';
break;
case 0xc:
*cursor++ = 'f';
break;
case 0xa:
*cursor++ = 'n';
break;
case 0xd:
*cursor++ = 'r';
break;
case 0x9:
*cursor++ = 't';
break;
default:
*cursor++ = 'u';
*cursor++ = '0';
*cursor++ = '0';
*cursor++ = hexdig(u >> 4);
*cursor++ = hexdig(u & 0xf);
}
} else {
*cursor++ = (uchar)u;
}
} else if (QUtf8Functions::toUtf8<QUtf8BaseTraits>(u, cursor, src,
end) < 0) {
// failed to get valid utf8 use JSON escape sequence
*cursor++ = '\\';
*cursor++ = 'u';
*cursor++ = hexdig(u >> 12 & 0x0f);
*cursor++ = hexdig(u >> 8 & 0x0f);
*cursor++ = hexdig(u >> 4 & 0x0f);
*cursor++ = hexdig(u & 0x0f);
}
}
ba.resize(cursor - reinterpret_cast<const uchar *>(ba.constData()));
return ba;
}
QJsonModel::QJsonModel(QObject *parent)
: QAbstractItemModel(parent), mRootItem{new QJsonTreeItem} {
mHeaders.append(tr("key"));
mHeaders.append(tr("value"));
}
QJsonModel::QJsonModel(const QString &fileName, QObject *parent)
: QAbstractItemModel(parent), mRootItem{new QJsonTreeItem} {
mHeaders.append(tr("key"));
mHeaders.append(tr("value"));
load(fileName);
}
QJsonModel::QJsonModel(QIODevice *device, QObject *parent)
: QAbstractItemModel(parent), mRootItem{new QJsonTreeItem} {
mHeaders.append(tr("key"));
mHeaders.append(tr("value"));
load(device);
}
QJsonModel::QJsonModel(const QByteArray &json, QObject *parent)
: QAbstractItemModel(parent), mRootItem{new QJsonTreeItem} {
mHeaders.append(tr("key"));
mHeaders.append(tr("value"));
loadJson(json);
}
QJsonModel::~QJsonModel() { delete mRootItem; }
bool QJsonModel::load(const QString &fileName) {
QFile file(fileName);
bool success = false;
if (file.open(QIODevice::ReadOnly)) {
success = load(&file);
file.close();
} else {
success = false;
}
return success;
}
bool QJsonModel::load(QIODevice *device) { return loadJson(device->readAll()); }
bool QJsonModel::loadJson(const QByteArray &json) {
QJsonParseError error;
auto const &jdoc = QJsonDocument::fromJson(json, &error);
if (error.error != QJsonParseError::NoError) {
return false;
}
if (!jdoc.isNull()) {
beginResetModel();
delete mRootItem;
if (jdoc.isArray()) {
mRootItem =
QJsonTreeItem::load(QJsonValue(jdoc.array()), mExceptions);
mRootItem->setType(QJsonValue::Array);
} else {
mRootItem =
QJsonTreeItem::load(QJsonValue(jdoc.object()), mExceptions);
mRootItem->setType(QJsonValue::Object);
}
endResetModel();
return true;
}
qDebug() << Q_FUNC_INFO << "cannot load json";
return false;
}
QVariant QJsonModel::data(const QModelIndex &index, int role) const {
if (!index.isValid())
return {};
QJsonTreeItem *item = static_cast<QJsonTreeItem *>(index.internalPointer());
if (role == Qt::DisplayRole) {
if (index.column() == 0)
return QString("%1").arg(item->key());
if (index.column() == 1)
return item->value();
} else if (Qt::EditRole == role) {
if (index.column() == 1)
return item->value();
}
return {};
}
bool QJsonModel::setData(const QModelIndex &index, const QVariant &value,
int role) {
int col = index.column();
if (Qt::EditRole == role) {
if (col == 1) {
QJsonTreeItem *item =
static_cast<QJsonTreeItem *>(index.internalPointer());
item->setValue(value);
emit dataChanged(index, index, {Qt::EditRole});
return true;
}
}
return false;
}
QVariant QJsonModel::headerData(int section, Qt::Orientation orientation,
int role) const {
if (role != Qt::DisplayRole)
return {};
if (orientation == Qt::Horizontal)
return mHeaders.value(section);
else
return {};
}
QModelIndex QJsonModel::index(int row, int column,
const QModelIndex &parent) const {
if (!hasIndex(row, column, parent))
return {};
QJsonTreeItem *parentItem;
if (!parent.isValid())
parentItem = mRootItem;
else
parentItem = static_cast<QJsonTreeItem *>(parent.internalPointer());
QJsonTreeItem *childItem = parentItem->child(row);
if (childItem)
return createIndex(row, column, childItem);
else
return {};
}
QModelIndex QJsonModel::parent(const QModelIndex &index) const {
if (!index.isValid())
return {};
QJsonTreeItem *childItem =
static_cast<QJsonTreeItem *>(index.internalPointer());
QJsonTreeItem *parentItem = childItem->parent();
if (parentItem == mRootItem)
return QModelIndex();
return createIndex(parentItem->row(), 0, parentItem);
}
int QJsonModel::rowCount(const QModelIndex &parent) const {
QJsonTreeItem *parentItem;
if (parent.column() > 0)
return 0;
if (!parent.isValid())
parentItem = mRootItem;
else
parentItem = static_cast<QJsonTreeItem *>(parent.internalPointer());
return parentItem->childCount();
}
int QJsonModel::columnCount(const QModelIndex &parent) const {
Q_UNUSED(parent)
return 2;
}
Qt::ItemFlags QJsonModel::flags(const QModelIndex &index) const {
int col = index.column();
auto item = static_cast<QJsonTreeItem *>(index.internalPointer());
auto isArray = QJsonValue::Array == item->type();
auto isObject = QJsonValue::Object == item->type();
if ((col == 1) && !(isArray || isObject))
return Qt::ItemIsEditable | QAbstractItemModel::flags(index);
else
return QAbstractItemModel::flags(index);
}
QByteArray QJsonModel::json(bool compact) {
auto jsonValue = genJson(mRootItem);
QByteArray json;
if (jsonValue.isNull())
return json;
if (jsonValue.isArray())
arrayToJson(jsonValue.toArray(), json, 0, compact);
else
objectToJson(jsonValue.toObject(), json, 0, compact);
return json;
}
void QJsonModel::objectToJson(QJsonObject jsonObject, QByteArray &json,
int indent, bool compact) {
json += compact ? "{" : "{\n";
objectContentToJson(jsonObject, json, indent + (compact ? 0 : 1), compact);
json += QByteArray(4 * indent, ' ');
json += compact ? "}" : "}\n";
}
void QJsonModel::arrayToJson(QJsonArray jsonArray, QByteArray &json, int indent,
bool compact) {
json += compact ? "[" : "[\n";
arrayContentToJson(jsonArray, json, indent + (compact ? 0 : 1), compact);
json += QByteArray(4 * indent, ' ');
json += compact ? "]" : "]\n";
}
void QJsonModel::arrayContentToJson(QJsonArray jsonArray, QByteArray &json,
int indent, bool compact) {
if (jsonArray.size() <= 0)
return;
QByteArray indentString(4 * indent, ' ');
int i = 0;
while (1) {
json += indentString;
valueToJson(jsonArray.at(i), json, indent, compact);
if (++i == jsonArray.size()) {
if (!compact)
json += '\n';
break;
}
json += compact ? "," : ",\n";
}
}
void QJsonModel::objectContentToJson(QJsonObject jsonObject, QByteArray &json,
int indent, bool compact) {
if (jsonObject.size() <= 0)
return;
QByteArray indentString(4 * indent, ' ');
int i = 0;
while (1) {
QString key = jsonObject.keys().at(i);
json += indentString;
json += '"';
json += escapedString(key);
json += compact ? "\":" : "\": ";
valueToJson(jsonObject.value(key), json, indent, compact);
if (++i == jsonObject.size()) {
if (!compact)
json += '\n';
break;
}
json += compact ? "," : ",\n";
}
}
void QJsonModel::valueToJson(QJsonValue jsonValue, QByteArray &json, int indent,
bool compact) {
QJsonValue::Type type = jsonValue.type();
switch (type) {
case QJsonValue::Bool:
json += jsonValue.toBool() ? "true" : "false";
break;
case QJsonValue::Double: {
const double d = jsonValue.toDouble();
if (qIsFinite(d)) {
json += QByteArray::number(d, 'f', QLocale::FloatingPointShortest);
} else {
json += "null"; // +INF || -INF || NaN (see RFC4627#section2.4)
}
break;
}
case QJsonValue::String:
json += '"';
json += escapedString(jsonValue.toString());
json += '"';
break;
case QJsonValue::Array:
json += compact ? "[" : "[\n";
arrayContentToJson(jsonValue.toArray(), json,
indent + (compact ? 0 : 1), compact);
json += QByteArray(4 * indent, ' ');
json += ']';
break;
case QJsonValue::Object:
json += compact ? "{" : "{\n";
objectContentToJson(jsonValue.toObject(), json,
indent + (compact ? 0 : 1), compact);
json += QByteArray(4 * indent, ' ');
json += '}';
break;
case QJsonValue::Null:
default:
json += "null";
}
}
void QJsonModel::addException(const QStringList &exceptions) {
mExceptions = exceptions;
}
QJsonValue QJsonModel::genJson(QJsonTreeItem *item) const {
auto type = item->type();
int nchild = item->childCount();
if (QJsonValue::Object == type) {
QJsonObject jo;
for (int i = 0; i < nchild; ++i) {
auto ch = item->child(i);
auto key = ch->key();
jo.insert(key, genJson(ch));
}
return jo;
} else if (QJsonValue::Array == type) {
QJsonArray arr;
for (int i = 0; i < nchild; ++i) {
auto ch = item->child(i);
arr.append(genJson(ch));
}
return arr;
} else {
QJsonValue va;
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
switch (item->value().typeId()) {
#else
switch (item->value().userType()) {
#endif
case QMetaType::Bool: {
va = item->value().toBool();
break;
}
default:
va = item->value().toString();
break;
}
return va;
}
}

View File

@ -1,56 +0,0 @@
# QJsonModel
QJsonModel is a JSON tree model class for Qt6/C++17 based on QAbstractItemModel.
QJsonModel was originally written by Sacha Shutz (https://github.com/dridk).
This fork is also released under the MIT License.
![QJsonModel](https://gitea.beniquez.me/sdaveb/QJsonModel/raw/branch/master/screen.png)
## Build Instructions
### Build Tools
- CMake (version 3.21 or higher)
- C++17-compatible compiler
### Building the Project
1. Clone the repository:
```
git clone <URL>
```
2. Navigate to the project directory:
```
cd elemental-game
```
3. Configure your build system:
```bash
cmake -B debug -G Unix Makefiles
# or
cmake -B debug -G Ninja # this is faster and more modern
```
4. Invoke your build system
```
cmake --build debug
```
### Usage - CMake
You can add this library to your CMake projects using FetchContent()
or CPM_AddPackage().
Here's how to do it with CPM_AddPackage:
```
COMING SOON
```
### Usage - C++
####
```cpp
QJsonModel * model = new QJsonModel;
QTreeView * view = new QTreeView;
view->setModel(model);
model->load("example.json")
```

View File

@ -1,115 +0,0 @@
/* QJsonModel.hpp
* Copyright (c) 2011 SCHUTZ Sacha
* Copyright © 2024 Saul D. Beniquez
*
* License:
* The MIT License (MIT)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#pragma once
#include <QAbstractItemModel>
#include <QIcon>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include "details/QUtf8.hpp"
class QJsonModel;
class QJsonItem;
class QJsonTreeItem {
public:
QJsonTreeItem(QJsonTreeItem *parent = nullptr);
~QJsonTreeItem();
void appendChild(QJsonTreeItem *item);
QJsonTreeItem *child(int row);
QJsonTreeItem *parent();
int childCount() const;
int row() const;
void setKey(const QString &key);
void setValue(const QVariant &value);
void setType(const QJsonValue::Type &type);
QString key() const;
QVariant value() const;
QJsonValue::Type type() const;
static QJsonTreeItem *load(const QJsonValue &value,
const QStringList &exceptions = {},
QJsonTreeItem *parent = nullptr);
protected:
private:
QString mKey;
QVariant mValue;
QJsonValue::Type mType;
QList<QJsonTreeItem *> mChilds;
QJsonTreeItem *mParent = nullptr;
};
//---------------------------------------------------
class QJsonModel : public QAbstractItemModel {
Q_OBJECT
public:
explicit QJsonModel(QObject *parent = nullptr);
QJsonModel(const QString &fileName, QObject *parent = nullptr);
QJsonModel(QIODevice *device, QObject *parent = nullptr);
QJsonModel(const QByteArray &json, QObject *parent = nullptr);
~QJsonModel();
bool load(const QString &fileName);
bool load(QIODevice *device);
bool loadJson(const QByteArray &json);
QVariant data(const QModelIndex &index, int role) const override;
bool setData(const QModelIndex &index, const QVariant &value,
int role = Qt::EditRole) override;
QVariant headerData(int section, Qt::Orientation orientation,
int role) const override;
QModelIndex index(int row, int column,
const QModelIndex &parent = QModelIndex()) const override;
QModelIndex parent(const QModelIndex &index) const override;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
Qt::ItemFlags flags(const QModelIndex &index) const override;
QByteArray json(bool compact = false);
QByteArray jsonToByte(QJsonValue jsonValue);
void objectToJson(QJsonObject jsonObject, QByteArray &json, int indent,
bool compact);
void arrayToJson(QJsonArray jsonArray, QByteArray &json, int indent,
bool compact);
void arrayContentToJson(QJsonArray jsonArray, QByteArray &json, int indent,
bool compact);
void objectContentToJson(QJsonObject jsonObject, QByteArray &json,
int indent, bool compact);
void valueToJson(QJsonValue jsonValue, QByteArray &json, int indent,
bool compact);
//! List of tags to skip during JSON parsing
void addException(const QStringList &exceptions);
private:
QJsonValue genJson(QJsonTreeItem *) const;
QJsonTreeItem *mRootItem = nullptr;
QStringList mHeaders;
//! List of exceptions (e.g. comments). Case insensitive, compairs on
//! "contains".
QStringList mExceptions;
};

View File

@ -1,194 +0,0 @@
/* QUtf8.hpp
* Copyright © 2024 Saul D. Beniquez
* License:
* The MIT License (MIT)
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <QJsonValue>
namespace QUtf8Functions {
/// returns 0 on success; errors can only happen if \a u is a surrogate:
/// Error if \a u is a low surrogate;
/// if \a u is a high surrogate, Error if the next isn't a low one,
/// EndOfString if we run into the end of the string.
template <typename Traits, typename OutputPtr, typename InputPtr>
inline int toUtf8(ushort u, OutputPtr &dst, InputPtr &src, InputPtr end) {
if (!Traits::skipAsciiHandling && u < 0x80) {
// U+0000 to U+007F (US-ASCII) - one byte
Traits::appendByte(dst, uchar(u));
return 0;
} else if (u < 0x0800) {
// U+0080 to U+07FF - two bytes
// first of two bytes
Traits::appendByte(dst, 0xc0 | uchar(u >> 6));
} else {
if (!QChar::isSurrogate(u)) {
// U+0800 to U+FFFF (except U+D800-U+DFFF) - three bytes
if (!Traits::allowNonCharacters && QChar::isNonCharacter(u))
return Traits::Error;
// first of three bytes
Traits::appendByte(dst, 0xe0 | uchar(u >> 12));
} else {
// U+10000 to U+10FFFF - four bytes
// need to get one extra codepoint
if (Traits::availableUtf16(src, end) == 0)
return Traits::EndOfString;
ushort low = Traits::peekUtf16(src);
if (!QChar::isHighSurrogate(u))
return Traits::Error;
if (!QChar::isLowSurrogate(low))
return Traits::Error;
Traits::advanceUtf16(src);
uint ucs4 = QChar::surrogateToUcs4(u, low);
if (!Traits::allowNonCharacters && QChar::isNonCharacter(ucs4))
return Traits::Error;
// first byte
Traits::appendByte(dst, 0xf0 | (uchar(ucs4 >> 18) & 0xf));
// second of four bytes
Traits::appendByte(dst, 0x80 | (uchar(ucs4 >> 12) & 0x3f));
// for the rest of the bytes
u = ushort(ucs4);
}
// second to last byte
Traits::appendByte(dst, 0x80 | (uchar(u >> 6) & 0x3f));
}
// last byte
Traits::appendByte(dst, 0x80 | (u & 0x3f));
return 0;
}
inline bool isContinuationByte(uchar b) { return (b & 0xc0) == 0x80; }
/// returns the number of characters consumed (including \a b) in case of
/// success; returns negative in case of error: Traits::Error or
/// Traits::EndOfString
template <typename Traits, typename OutputPtr, typename InputPtr>
inline int fromUtf8(uchar b, OutputPtr &dst, InputPtr &src, InputPtr end) {
int charsNeeded;
uint min_uc;
uint uc;
if (!Traits::skipAsciiHandling && b < 0x80) {
// US-ASCII
Traits::appendUtf16(dst, b);
return 1;
}
if (!Traits::isTrusted && Q_UNLIKELY(b <= 0xC1)) {
// an UTF-8 first character must be at least 0xC0
// however, all 0xC0 and 0xC1 first bytes can only produce overlong
// sequences
return Traits::Error;
} else if (b < 0xe0) {
charsNeeded = 2;
min_uc = 0x80;
uc = b & 0x1f;
} else if (b < 0xf0) {
charsNeeded = 3;
min_uc = 0x800;
uc = b & 0x0f;
} else if (b < 0xf5) {
charsNeeded = 4;
min_uc = 0x10000;
uc = b & 0x07;
} else {
// the last Unicode character is U+10FFFF
// it's encoded in UTF-8 as "\xF4\x8F\xBF\xBF"
// therefore, a byte higher than 0xF4 is not the UTF-8 first byte
return Traits::Error;
}
int bytesAvailable = Traits::availableBytes(src, end);
if (Q_UNLIKELY(bytesAvailable < charsNeeded - 1)) {
// it's possible that we have an error instead of just unfinished bytes
if (bytesAvailable > 0 && !isContinuationByte(Traits::peekByte(src, 0)))
return Traits::Error;
if (bytesAvailable > 1 && !isContinuationByte(Traits::peekByte(src, 1)))
return Traits::Error;
return Traits::EndOfString;
}
// first continuation character
b = Traits::peekByte(src, 0);
if (!isContinuationByte(b))
return Traits::Error;
uc <<= 6;
uc |= b & 0x3f;
if (charsNeeded > 2) {
// second continuation character
b = Traits::peekByte(src, 1);
if (!isContinuationByte(b))
return Traits::Error;
uc <<= 6;
uc |= b & 0x3f;
if (charsNeeded > 3) {
// third continuation character
b = Traits::peekByte(src, 2);
if (!isContinuationByte(b))
return Traits::Error;
uc <<= 6;
uc |= b & 0x3f;
}
}
// we've decoded something; safety-check it
if (!Traits::isTrusted) {
if (uc < min_uc)
return Traits::Error;
if (QChar::isSurrogate(uc) || uc > QChar::LastValidCodePoint)
return Traits::Error;
if (!Traits::allowNonCharacters && QChar::isNonCharacter(uc))
return Traits::Error;
}
// write the UTF-16 sequence
if (!QChar::requiresSurrogates(uc)) {
// UTF-8 decoded and no surrogates are required
// detach if necessary
Traits::appendUtf16(dst, ushort(uc));
} else {
// UTF-8 decoded to something that requires a surrogate pair
Traits::appendUcs4(dst, uc);
}
Traits::advanceByte(src, charsNeeded - 1);
return charsNeeded;
}
} // namespace QUtf8Functions
struct QUtf8BaseTraits {
static const bool isTrusted = false;
static const bool allowNonCharacters = true;
static const bool skipAsciiHandling = false;
static const int Error = -1;
static const int EndOfString = -2;
static bool isValidCharacter(uint u) { return int(u) >= 0; }
static void appendByte(uchar *&ptr, uchar b) { *ptr++ = b; }
static uchar peekByte(const uchar *ptr, int n = 0) { return ptr[n]; }
static qptrdiff availableBytes(const uchar *ptr, const uchar *end) {
return end - ptr;
}
static void advanceByte(const uchar *&ptr, int n = 1) { ptr += n; }
static void appendUtf16(ushort *&ptr, ushort uc) { *ptr++ = uc; }
static void appendUcs4(ushort *&ptr, uint uc) {
appendUtf16(ptr, QChar::highSurrogate(uc));
appendUtf16(ptr, QChar::lowSurrogate(uc));
}
static ushort peekUtf16(const ushort *ptr, int n = 0) { return ptr[n]; }
static qptrdiff availableUtf16(const ushort *ptr, const ushort *end) {
return end - ptr;
}
static void advanceUtf16(const ushort *&ptr, int n = 1) { ptr += n; }
// it's possible to output to UCS-4 too
static void appendUtf16(uint *&ptr, ushort uc) { *ptr++ = uc; }
static void appendUcs4(uint *&ptr, uint uc) { *ptr++ = uc; }
};

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

@ -1 +1 @@
Subproject commit fe0774c1d2ff5c86832d5850c1dccbbf3a6c3c66
Subproject commit 3a5bb0d8bc45b6150ed4a7513b0712b0a5954a74

2
3rdparty/cpptrace vendored

@ -1 +1 @@
Subproject commit fac4d08fd0473a94d99c143c6ba6b1f9e0bd7636
Subproject commit ce639ebfcec47a7c74233b4bab50017cb34e615b

View File

@ -33,7 +33,8 @@ QtLocalPeer::QtLocalPeer(QObject *parent, const QString &appId)
#endif
prefix = id.section(QLatin1Char('/'), -1);
}
prefix.remove(QRegularExpression("[^a-zA-Z]"));
static QRegularExpression regex("[^a-zA-Z]");
prefix.remove(regex);
prefix.truncate(6);
QByteArray idc = id.toUtf8();
@ -69,6 +70,8 @@ QtLocalPeer::QtLocalPeer(QObject *parent, const QString &appId)
lockFile.open(QIODevice::ReadWrite);
}
QtLocalPeer::~QtLocalPeer() { server->close(); }
bool QtLocalPeer::isClient() {
if (lockFile.isLocked())
return false;

View File

@ -15,6 +15,8 @@ class QtLocalPeer : public QObject {
public:
QtLocalPeer(QObject *parent = nullptr, const QString &appId = QString());
~QtLocalPeer();
bool isClient();
bool sendMessage(const QByteArray &uMsg, int timeout);
QString applicationId() const { return id; }

View File

@ -8,7 +8,7 @@ set(CMAKE_AUTORCC ON)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(PROJECT_VERSION "2.2.1")
set(PROJECT_VERSION "2.2.3")
find_package(
QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets Network Concurrent
@ -21,12 +21,16 @@ message("Build ${PROJECT_NAME} with ${CMAKE_BUILD_TYPE}.")
install(CODE "set(CMAKE_INSTALL_LOCAL_ONLY TRUE)" ALL_COMPONENTS)
option(WINGHEX_USE_FRAMELESS ON)
option(BUILD_TEST_PLUGIN OFF)
option(BUILD_SHARED_MEM_EXT OFF)
add_definitions(-DAS_NO_THREADS)
if(BUILD_TEST_PLUGIN)
add_subdirectory(TestPlugin)
endif()
option(BUILD_SHARED_MEM_EXT OFF)
if(BUILD_SHARED_MEM_EXT)
add_subdirectory(ShareMemoryDrv)
endif()
@ -36,13 +40,10 @@ add_definitions(-DWINGHEX_VERSION="${PROJECT_VERSION}"
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/WINGHEX_VERSION" ${PROJECT_VERSION})
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/QT_VERSION" ${QT_VERSION_MAJOR})
option(WINGHEX_USE_FRAMELESS "Use borderless windows to ensure UI uniformity"
TRUE)
if(WIN32)
find_package(QT NAMES Qt6 Qt5 REQUIRED AxContainer)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED AxContainer)
add_definitions(-DNOMINMAX -D_CRT_SECURE_NO_WARNINGS)
add_definitions(-DNOMINMAX)
endif()
if(${QT_VERSION_MAJOR} EQUAL 5)
@ -66,7 +67,7 @@ if(WINGHEX_USE_FRAMELESS)
option(QWINDOWKIT_BUILD_STATIC "Build static libraries" TRUE)
option(QWINDOWKIT_INSTALL "Install library" OFF)
add_subdirectory(3rdparty/qwindowkit)
add_definitions(-DWINGHEX_USE_FRAMELESS)
add_compile_definitions(WINGHEX_USE_FRAMELESS)
endif()
set(CMAKE_POLICY_DEFAULT_CMP0077 NEW)
@ -79,7 +80,6 @@ add_subdirectory(3rdparty/QHexView)
add_subdirectory(3rdparty/WingCodeEdit)
add_subdirectory(3rdparty/Qt-Advanced-Docking-System)
add_subdirectory(3rdparty/AngelScript/sdk/angelscript/projects/cmake)
add_subdirectory(3rdparty/QJsonModel)
set(ANGEL_SCRIPT_ADDON_ROOT
"${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/AngelScript/sdk/add_on")
@ -194,8 +194,6 @@ set(CONTROL_SRC
src/control/qlistviewext.cpp
src/control/asobjtreewidget.h
src/control/asobjtreewidget.cpp
src/control/qtlonglongspinbox.cpp
src/control/qtlonglongspinbox.h
src/control/dockwidgettab.h
src/control/dockwidgettab.cpp
src/control/qhextextedit.h
@ -209,7 +207,9 @@ set(CONTROL_SRC
src/control/gotolinewidget.h
src/control/gotolinewidget.cpp
src/control/codeeditcontrolwidget.h
src/control/codeeditcontrolwidget.cpp)
src/control/codeeditcontrolwidget.cpp
src/control/scrollablelabel.h
src/control/scrollablelabel.cpp)
set(CLASS_SRC
src/class/logger.cpp
@ -239,8 +239,6 @@ set(CLASS_SRC
src/class/settingmanager.cpp
src/class/asdebugger.h
src/class/asdebugger.cpp
src/class/scriptconsolemachine.h
src/class/scriptconsolemachine.cpp
src/class/angelobjstring.h
src/class/angelobjstring.cpp
src/class/scripteditortheme.h
@ -256,8 +254,6 @@ set(CLASS_SRC
src/class/ascompletion.h
src/class/asbuilder.h
src/class/asbuilder.cpp
src/class/ascontextmgr.h
src/class/ascontextmgr.cpp
src/class/clangformatmanager.h
src/class/clangformatmanager.cpp
src/class/aspreprocesser.h
@ -274,7 +270,6 @@ set(CLASS_SRC
src/class/dockcomponentsfactory.cpp
src/class/diffutil.h
src/class/diffutil.cpp
src/class/clickcallback.h
src/class/crashhandler.h
src/class/crashhandler.cpp
src/class/pluginsystem.h
@ -317,10 +312,6 @@ set(MODEL_SRC
src/model/metadatamodel.cpp
src/model/checksummodel.h
src/model/checksummodel.cpp
src/model/qjsontablemodel.h
src/model/qjsontablemodel.cpp
src/model/scriptobjmodel.h
src/model/scriptobjmodel.cpp
src/model/dbgcallstackmodel.h
src/model/dbgcallstackmodel.cpp
src/model/dbgvarshowmodel.h
@ -364,7 +355,8 @@ set(SCRIPT_ADDON_SRC
src/scriptaddon/scriptjson.h
src/scriptaddon/scriptjson.cpp
src/scriptaddon/scriptfile.cpp
src/scriptaddon/scriptfile.h)
src/scriptaddon/scriptfile.h
src/scriptaddon/aspromise.hpp)
# localization support
file(
@ -384,7 +376,6 @@ endforeach()
set(TRANSLATION_PATH
${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/QConsoleWidget
${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/QHexView
${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/QJsonModel
${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/Qt-Advanced-Docking-System/src
${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/QWingRibbon
${CMAKE_CURRENT_SOURCE_DIR}/src)
@ -518,7 +509,6 @@ target_link_libraries(
WingPlugin
QHexView
WingCodeEdit
QJsonModel
angelscript
qtadvanceddocking-qt${QT_VERSION_MAJOR})

View File

@ -115,8 +115,7 @@
1. 本软件源代码不得私自应用于闭源商业用途除非你完整开源GPL协议的要求。如果要将软件仓库的代码商用闭源必须找我购买商业授权签订合同价格私聊非诚勿扰。
2. 本软件是用我的业余时间编写,不能及时修复 Bug 或者提供技术支持,请见谅。
3. 本人非计算机专业,编写程序难免有 Bug ,欢迎提交 PR 。
4. 无论在什么时候有疑问,请详细阅读 Wiki 。
3. 无论在什么时候有疑问,请详细阅读 Wiki 。
### 打包者必读
@ -128,7 +127,7 @@
4. 包内最好含有主程序的校验和,但不强求。
5. 无论是哪种打包形式,都一定要声明其开源协议和自由属性,不得隐藏而借机收费。虽然收费我并不反感,下载服务器还是需要资金维护的,但借着信息差骗人收费,这是我极其深恶痛绝的。
6. 无论以任何为目的,如未获得我作者的授权,不得修改任意程序内指向的网络链接和软件关于信息,比如赞助和关于软件部分的内容等。
7. 不得在安装程序内插入任何含有商业推广的插件,并且严格遵守第二条内容
7. 不得在安装程序内插入任何含有商业推广的插件。
### issue 前必读
@ -174,7 +173,7 @@
## WIKI
&emsp;&emsp;如果想学习羽云十六进制编辑器的使用以及如何开发该软件的插件,请到 [该链接](https://github.com/Wing-summer/WingHexExplorer2/wiki/%E7%AE%80%E4%BB%8B) ,同时也欢迎大家指出 Wiki 的错误以及贡献优质内容。
&emsp;&emsp;如果想学习羽云十六进制编辑器的使用以及如何开发该软件的插件,请到 [该链接](https://wing-summer.github.io/WingHexExplorer2/docs/zh/credits.html) ,同时也欢迎大家指出 Wiki 的错误以及贡献优质内容。
## 插件库

View File

@ -115,8 +115,7 @@ For plugin development, the corresponding open source agreements are different.
1. The source code of this software shall not be used for closed-source commercial purposes unless you open source it completely (as required by the GPL agreement). If you want to commercially close the code of the software warehouse, you must contact me to purchase a commercial license and sign a contract. Please contact me for the price. Please do not disturb me if you are not serious.
2. This software was written in my spare time. Please forgive me for not being able to fix bugs or provide technical support in time.
3. I am not a computer major, and there are inevitably bugs in the program I write. Welcome to submit PR.
4. Whenever you have questions, please read the Wiki in detail.
3. Whenever you have questions, please read the Wiki in detail.
### For Packagers
@ -128,7 +127,7 @@ First of all, I would like to express my sincere tanks for your enthusiastic hel
4. It is best to contain the checksum of the main program in the package, but it is not mandatory.
5. Regardless of the packaging format, its open source agreement and free attributes must be stated, and it is not allowed to hide and charge for it. Although I don't mind charging, the download server still needs money to maintain, but I hate charging by taking advantage of the information gap.
6. Regardless of the purpose, if you do not obtain the authorization of me, you may not modify any network links pointed to and the ABOUT contents in the program, such as sponsorship and content about the software.
7. You may not insert any plug-in containing commercial promotion in the installation program, and strictly abide by the content of the second article.
7. You may not insert any plug-in containing commercial promotion in the installation program.
### Issue
@ -175,7 +174,7 @@ Of course, there are other repositories as mirror for Chinese users (which will
## WIKI
&emsp;&emsp;If you want to learn how to use WingHexEditor and how to develop plug-ins for the software, please go to this link: `Not available yet`. At the same time, you are also welcome to point out errors in the Wiki and contribute high-quality content.
&emsp;&emsp;If you want to learn how to use WingHexEditor and how to develop plug-ins for the software, please go to [this link](https://wing-summer.github.io/WingHexExplorer2/docs/zh/credits.html) (Chinese only). At the same time, you are also welcome to point out errors in the Wiki and contribute high-quality content.
## Plugins

View File

@ -61,8 +61,6 @@ add_library(
ctltestform.h
ctltestform.cpp
ctltestform.ui
testtablemodel.h
testtablemodel.cpp
testsettingpage.h
testsettingpage.cpp
testwingeditorviewwidget.h
@ -91,4 +89,5 @@ if(TEST_MODE)
${WINGHEX_PLUGIN_PATH})
endif()
target_link_libraries(TestPlugin PRIVATE Qt${QT_VERSION_MAJOR}::Widgets WingPlugin)
target_link_libraries(TestPlugin PRIVATE Qt${QT_VERSION_MAJOR}::Widgets
WingPlugin)

View File

@ -4,7 +4,7 @@
# You can redistribute this file and/or modify it under the terms of the BSD
# 3-Clause.
#
# THIS FILE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND
# THIS FILE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE

View File

@ -185,12 +185,12 @@
<translation></translation>
</message>
<message>
<location filename="../testform.ui" line="1184"/>
<location filename="../testform.ui" line="1055"/>
<source>Others</source>
<translation></translation>
</message>
<message>
<location filename="../testform.ui" line="1245"/>
<location filename="../testform.ui" line="1116"/>
<source>Other APIs should be Test With Script</source>
<translation> API </translation>
</message>
@ -231,7 +231,6 @@
<location filename="../testform.ui" line="311"/>
<location filename="../testform.ui" line="707"/>
<location filename="../testform.ui" line="881"/>
<location filename="../testform.ui" line="1070"/>
<source>Type</source>
<translation></translation>
</message>
@ -266,32 +265,6 @@
<source>Color</source>
<translation></translation>
</message>
<message>
<location filename="../testform.ui" line="1055"/>
<source>DataVisual</source>
<translation></translation>
</message>
<message>
<location filename="../testform.cpp" line="340"/>
<location filename="../testform.cpp" line="349"/>
<source>UpdateTextTreeError</source>
<translation></translation>
</message>
<message>
<location filename="../testform.cpp" line="364"/>
<source>UpdateTextListByModelError</source>
<translation></translation>
</message>
<message>
<location filename="../testform.cpp" line="374"/>
<source>UpdateTextTableByModelError</source>
<translation></translation>
</message>
<message>
<location filename="../testform.cpp" line="385"/>
<source>UpdateTextTreeByModelError</source>
<translation></translation>
</message>
</context>
<context>
<name>TestPlugin</name>

View File

@ -24,7 +24,6 @@
#include "ctltestform.h"
#include "readertestform.h"
#include "testtablemodel.h"
#include <QAbstractTableModel>
#include <QFileSystemModel>
@ -37,8 +36,6 @@ TestForm::TestForm(WingHex::IWingPlugin *plg, QWidget *parent)
: WingHex::WingPluginWidget(plg, parent), ui(new Ui::TestForm) {
ui->setupUi(this);
ui->teDataVisual->setAcceptRichText(false);
ui->saReader->widget()->layout()->addWidget(
new ReaderTestForm(plg, ui->tbReaderLogger, this));
ui->saCtl->widget()->layout()->addWidget(
@ -56,10 +53,6 @@ TestForm::TestForm(WingHex::IWingPlugin *plg, QWidget *parent)
ui->lblauthor->setPixmap(
WingHex::HOSTRESPIMG(QStringLiteral("author"), QStringLiteral(".jpg")));
_click = std::bind(&TestForm::onDVClicked, this, std::placeholders::_1);
_dblclick =
std::bind(&TestForm::onDVDoubleClicked, this, std::placeholders::_1);
}
TestForm::~TestForm() { delete ui; }
@ -153,16 +146,6 @@ QFileDialog::Options TestForm::getFileDialogOptions() const {
return options;
}
void TestForm::onDVClicked(const QModelIndex &index) {
logWarn(QStringLiteral("[Test - Click] ") +
index.model()->data(index).toString());
}
void TestForm::onDVDoubleClicked(const QModelIndex &index) {
msgWarning(this, QStringLiteral("Test - DoubleClick"),
index.model()->data(index).toString());
}
void TestForm::on_btnSendLog_clicked() {
auto txt = ui->leLogText->text();
switch (Level(ui->cbLogLevel->currentIndex())) {
@ -323,69 +306,6 @@ void TestForm::on_btnGetColor_clicked() {
}
}
void TestForm::on_btnText_2_clicked() {
dataVisualText(ui->teDataVisual->toPlainText(), QStringLiteral("TestForm"));
}
void TestForm::on_btnTextList_clicked() {
auto txts = ui->teDataVisual->toPlainText().split('\n');
dataVisualTextList(txts, QStringLiteral("TestForm"), _click, _dblclick);
}
void TestForm::on_btnTextTree_clicked() {
auto ret =
dataVisualTextTree(ui->teDataVisual->toPlainText(),
QStringLiteral("TestForm"), _click, _dblclick);
if (!ret) {
msgCritical(this, QStringLiteral("Test"), tr("UpdateTextTreeError"));
}
}
void TestForm::on_btnTextTable_clicked() {
auto ret = dataVisualTextTable(
ui->teDataVisual->toPlainText(), {"wingsummer", "wingsummer"}, {},
QStringLiteral("TestForm"), _click, _dblclick);
if (!ret) {
msgCritical(this, QStringLiteral("Test"), tr("UpdateTextTreeError"));
}
}
void TestForm::on_btnTextListByModel_clicked() {
auto model = new QStringListModel;
QStringList buffer;
for (int i = 0; i < WingHex::SDKVERSION; ++i) {
buffer.append("wingsummer" % QString::number(i));
}
model->setStringList(buffer);
auto ret = dataVisualTextListByModel(model, QStringLiteral("TestForm"),
_click, _dblclick);
if (!ret) {
msgCritical(this, QStringLiteral("Test"),
tr("UpdateTextListByModelError"));
}
}
void TestForm::on_btnTextTableByModel_clicked() {
auto model = new TestTableModel;
auto ret = dataVisualTextTableByModel(model, QStringLiteral("TestForm"),
_click, _dblclick);
if (!ret) {
msgCritical(this, QStringLiteral("Test"),
tr("UpdateTextTableByModelError"));
}
}
void TestForm::on_btnTextTreeByModel_clicked() {
auto model = new QFileSystemModel;
model->setRootPath(QDir::currentPath());
auto ret = dataVisualTextTreeByModel(model, QStringLiteral("TestForm"),
_click, _dblclick);
if (!ret) {
msgCritical(this, QStringLiteral("Test"),
tr("UpdateTextTreeByModelError"));
}
}
void TestForm::on_btnStatusVisible_clicked() {
if (ui->rbLockedFile->isChecked()) {
Q_UNUSED(setLockedFile(true));

View File

@ -85,20 +85,6 @@ private slots:
void on_btnGetColor_clicked();
void on_btnText_2_clicked();
void on_btnTextList_clicked();
void on_btnTextTree_clicked();
void on_btnTextTable_clicked();
void on_btnTextListByModel_clicked();
void on_btnTextTableByModel_clicked();
void on_btnTextTreeByModel_clicked();
void on_btnStatusVisible_clicked();
void on_btnStatusInvisible_clicked();
@ -121,15 +107,8 @@ private:
QFileDialog::Options getFileDialogOptions() const;
void onDVClicked(const QModelIndex &index);
void onDVDoubleClicked(const QModelIndex &index);
private:
Ui::TestForm *ui;
WingHex::ClickedCallBack _click;
WingHex::ClickedCallBack _dblclick;
};
#endif // TESTFORM_H

View File

@ -1050,135 +1050,6 @@
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_8">
<attribute name="title">
<string>DataVisual</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_20">
<item>
<widget class="QTextEdit" name="teDataVisual"/>
</item>
<item>
<widget class="QGroupBox" name="groupBox_6">
<property name="minimumSize">
<size>
<width>0</width>
<height>150</height>
</size>
</property>
<property name="title">
<string>Type</string>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<property name="sizeConstraint">
<enum>QLayout::SizeConstraint::SetMinAndMaxSize</enum>
</property>
<item row="0" column="1">
<widget class="QPushButton" name="btnTextList">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string notr="true">updateTextList (LineByLine)</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QPushButton" name="btnTextTree">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string notr="true">updateTextTree (Json)</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QPushButton" name="btnTextTable">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string notr="true">updateTextTable (Json)</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QPushButton" name="btnTextListByModel">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string notr="true">updateTextListByModel</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QPushButton" name="btnText_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string notr="true">updateText</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QPushButton" name="btnTextTableByModel">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string notr="true">updateTextTableByModel</string>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QPushButton" name="btnTextTreeByModel">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string notr="true">updateTextTreeByModel</string>
</property>
</widget>
</item>
<item row="1" column="1" rowspan="2">
<widget class="QLabel" name="label_13">
<property name="text">
<string notr="true">WingSummer</string>
</property>
<property name="alignment">
<set>Qt::AlignmentFlag::AlignCenter</set>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_9">
<attribute name="title">
<string>Others</string>
@ -1278,18 +1149,18 @@
<resources/>
<connections>
<connection>
<sender>btnClearCtl</sender>
<sender>btnClearFile</sender>
<signal>clicked()</signal>
<receiver>tbCtlLogger</receiver>
<receiver>tbFileLogger</receiver>
<slot>clear()</slot>
<hints>
<hint type="sourcelabel">
<x>271</x>
<y>387</y>
<x>239</x>
<y>388</y>
</hint>
<hint type="destinationlabel">
<x>318</x>
<y>264</y>
<x>290</x>
<y>328</y>
</hint>
</hints>
</connection>
@ -1310,34 +1181,34 @@
</hints>
</connection>
<connection>
<sender>btnClearInput</sender>
<sender>btnClearCtl</sender>
<signal>clicked()</signal>
<receiver>tbInputLogger</receiver>
<receiver>tbCtlLogger</receiver>
<slot>clear()</slot>
<hints>
<hint type="sourcelabel">
<x>210</x>
<y>388</y>
<x>271</x>
<y>387</y>
</hint>
<hint type="destinationlabel">
<x>236</x>
<y>332</y>
<x>318</x>
<y>264</y>
</hint>
</hints>
</connection>
<connection>
<sender>btnClearFile</sender>
<sender>btnClearText</sender>
<signal>clicked()</signal>
<receiver>tbFileLogger</receiver>
<receiver>leToastText</receiver>
<slot>clear()</slot>
<hints>
<hint type="sourcelabel">
<x>239</x>
<y>388</y>
<x>569</x>
<y>271</y>
</hint>
<hint type="destinationlabel">
<x>290</x>
<y>328</y>
<x>468</x>
<y>229</y>
</hint>
</hints>
</connection>
@ -1358,18 +1229,18 @@
</hints>
</connection>
<connection>
<sender>btnClearText</sender>
<sender>btnClearInput</sender>
<signal>clicked()</signal>
<receiver>leToastText</receiver>
<receiver>tbInputLogger</receiver>
<slot>clear()</slot>
<hints>
<hint type="sourcelabel">
<x>569</x>
<y>271</y>
<x>210</x>
<y>388</y>
</hint>
<hint type="destinationlabel">
<x>468</x>
<y>229</y>
<x>236</x>
<y>332</y>
</hint>
</hints>
</connection>

View File

@ -1,55 +0,0 @@
/*==============================================================================
** Copyright (C) 2024-2027 WingSummer
**
** Permission is hereby granted, free of charge, to any person obtaining a copy
** of this software and associated documentation files (the "Software"), to deal
** in the Software without restriction, including without limitation the rights
** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
** copies of the Software, and to permit persons to whom the Software is
** furnished to do so.
**
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
** THE SOFTWARE.
** =============================================================================
*/
#include "testtablemodel.h"
#include "WingPlugin/iwingpluginbase.h"
TestTableModel::TestTableModel(QObject *parent) : QAbstractTableModel(parent) {}
int TestTableModel::rowCount(const QModelIndex &parent) const {
Q_UNUSED(parent);
return WingHex::SDKVERSION;
}
int TestTableModel::columnCount(const QModelIndex &parent) const {
Q_UNUSED(parent);
return 5;
}
QVariant TestTableModel::data(const QModelIndex &index, int role) const {
switch (role) {
case Qt::DisplayRole:
case Qt::ToolTipRole: {
return QStringLiteral("(%1, %2)").arg(index.row()).arg(index.column());
}
case Qt::TextAlignmentRole:
return Qt::AlignCenter;
}
return QVariant();
}
QVariant TestTableModel::headerData(int section, Qt::Orientation orientation,
int role) const {
Q_UNUSED(orientation);
if (role == Qt::DisplayRole) {
return section + 1;
}
return QVariant();
}

View File

@ -1,41 +0,0 @@
/*==============================================================================
** Copyright (C) 2024-2027 WingSummer
**
** Permission is hereby granted, free of charge, to any person obtaining a copy
** of this software and associated documentation files (the "Software"), to deal
** in the Software without restriction, including without limitation the rights
** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
** copies of the Software, and to permit persons to whom the Software is
** furnished to do so.
**
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
** THE SOFTWARE.
** =============================================================================
*/
#ifndef TESTTABLEMODEL_H
#define TESTTABLEMODEL_H
#include <QAbstractTableModel>
class TestTableModel : public QAbstractTableModel {
Q_OBJECT
public:
explicit TestTableModel(QObject *parent = nullptr);
// QAbstractItemModel interface
public:
virtual int rowCount(const QModelIndex &parent) const override;
virtual int columnCount(const QModelIndex &parent) const override;
virtual QVariant data(const QModelIndex &index, int role) const override;
virtual QVariant headerData(int section, Qt::Orientation orientation,
int role) const override;
};
#endif // TESTTABLEMODEL_H

@ -1 +1 @@
Subproject commit 95b3250011d0bf76ebd77f9905dc77fd04a46ce7
Subproject commit 1f53c308498e529e5cfbed16c9f7f68c77a3e8e5

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -41,7 +41,7 @@ def run_command_interactive(command):
def main():
parser = argparse.ArgumentParser(
prog="mkdeb.py", description="A deb installer maker for WingHexExplorer2")
prog="mkinnopak.py", description="A InnoSetup installer maker for WingHexExplorer2")
parser.add_argument(
"folder", help="A folder that has contained the binary build", type=str
@ -275,7 +275,7 @@ Source: {#MyAppExePath}; DestDir: "{app}"; Flags: ignoreversion
iss_content += r'Root: HKCR; Subkey: "WingHexExplorer2\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\{#MyAppExeName},1"; Flags: noerror uninsdeletevalue; ' + '\n'
iss_content += r'Root: HKCR; Subkey: "WingHexExplorer2\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#MyAppExeName}"" ""%1""" ;Flags: noerror uninsdeletevalue; ' + '\n'
iss_content += """
iss_content += r"""
[Icons]
Name: "{group}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"
Name: "{group}\{cm:UninstallProgram,{#MyAppName}}"; Filename: "{uninstallexe}"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 MiB

After

Width:  |  Height:  |  Size: 1.5 MiB

View File

@ -55,6 +55,7 @@
<item>typedef</item>
<item>while</item>
<item>xor</item>
<item>co_await</item>
</list>
<list name="types">
<item>void</item>
@ -80,7 +81,7 @@
<HlCOct attribute="Octal" context="#stay"/>
<HlCHex attribute="Hex" context="#stay"/>
<Int attribute="Decimal" context="Int Suffixes"/>
<HlCChar attribute="Char" context="#stay"/>
<DetectChar attribute="Char" context="String" char="&apos;"/>
<StringDetect attribute="Text Block" context="TextBlock" String="&quot;&quot;&quot;"/>
<DetectChar attribute="String" context="String" char="&quot;"/>
<Detect2Chars attribute="Comment" context="MatchComment" char="/" char1="/" lookAhead="true"/>
@ -105,6 +106,7 @@
<context attribute="String" lineEndContext="#pop" name="String">
<DetectChar context="StringEscapedChar" char="\" lookAhead="1"/>
<DetectChar attribute="String" context="#pop" char="&quot;"/>
<DetectChar attribute="Char" context="#pop" char="&apos;"/>
</context>
<context attribute="String" lineEndContext="#pop" name="StringEscapedChar">
<HlCStringChar attribute="String Char" context="#pop"/>
@ -144,7 +146,7 @@
<itemData name="Octal" defStyleNum="dsBaseN"/>
<itemData name="Hex" defStyleNum="dsBaseN"/>
<itemData name="Float" defStyleNum="dsFloat"/>
<itemData name="Char" defStyleNum="dsChar"/>
<itemData name="Char" defStyleNum="dsString"/>
<itemData name="String" defStyleNum="dsString"/>
<itemData name="Text Block" defStyleNum="dsString"/>
<itemData name="String Char" defStyleNum="dsSpecialChar"/>

View File

@ -1,7 +1,7 @@
{
"Id": "WingAngelAPI",
"Author": "wingsummer",
"Version": "2.1.0",
"Version": "2.2.2",
"Vendor": "WingCloudStudio",
"License": "AGPL-3.0",
"Url": "https://github.com/Wing-summer/WingHexExplorer2"

View File

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

View File

@ -25,43 +25,49 @@
AngelObjString::AngelObjString() {}
QString AngelObjString::stringToString(void *obj, asDebugger *dbg) {
QString AngelObjString::stringToString(void *obj, asDebugger *dbg, asUINT tag) {
Q_UNUSED(dbg);
// We know the received object is a string
QString val = *reinterpret_cast<QString *>(obj);
if (tag == 1) {
val.prepend('"').append('"');
}
return val;
}
QString AngelObjString::arrayToString(void *obj, asDebugger *dbg) {
QString AngelObjString::arrayToString(void *obj, asDebugger *dbg, asUINT tag) {
CScriptArray *arr = reinterpret_cast<CScriptArray *>(obj);
QString str;
QTextStream s(&str);
s << tr("(len=") << arr->GetSize() << QStringLiteral(")");
s << QStringLiteral(" [");
s << QStringLiteral("{");
for (asUINT n = 0; n < arr->GetSize(); n++) {
s << dbg->toString(arr->At(n), arr->GetElementTypeId(),
arr->GetArrayObjectType()->GetEngine());
arr->GetArrayObjectType()->GetEngine(), 1);
if (n < arr->GetSize() - 1)
s << ", ";
}
s << QStringLiteral("]");
s << QStringLiteral("}");
return str;
}
QString AngelObjString::charToString(void *obj, asDebugger *dbg) {
QString AngelObjString::charToString(void *obj, asDebugger *dbg, asUINT tag) {
Q_UNUSED(dbg);
// We know the received object is a char
QChar *val = reinterpret_cast<QChar *>(obj);
return QString(*val);
auto ret = QString(*val);
if (tag == 1) {
ret.prepend('\'').append('\'');
}
return ret;
}
QString AngelObjString::dictionaryToString(void *obj, asDebugger *dbg) {
QString AngelObjString::dictionaryToString(void *obj, asDebugger *dbg,
asUINT tag) {
CScriptDictionary *dic = reinterpret_cast<CScriptDictionary *>(obj);
QString str;
@ -69,11 +75,11 @@ QString AngelObjString::dictionaryToString(void *obj, asDebugger *dbg) {
auto engine = dic->GetEngine();
s << " [";
s << QStringLiteral("{");
asUINT n = 0;
for (CScriptDictionary::CIterator it = dic->begin(); it != dic->end();
it++, n++) {
s << "[" << it.GetKey() << "] = ";
s << QStringLiteral("[") << it.GetKey() << QStringLiteral("] = ");
// Get the type and address of the value
const void *val = it.GetAddressOfValue();
@ -83,17 +89,17 @@ QString AngelObjString::dictionaryToString(void *obj, asDebugger *dbg) {
// active, the debugger will use the engine held inside it by
// default, but in an environment where there multiple engines this
// might not be the correct instance).
s << dbg->toString(const_cast<void *>(val), typeId, engine);
s << dbg->toString(const_cast<void *>(val), typeId, engine, 1);
if (n < dic->GetSize() - 1)
s << ", ";
s << QStringLiteral(", ");
}
s << "]";
s << QStringLiteral("}");
return str;
}
QString AngelObjString::colorToString(void *obj, asDebugger *dbg) {
QString AngelObjString::colorToString(void *obj, asDebugger *dbg, asUINT tag) {
Q_UNUSED(dbg);
auto color = reinterpret_cast<QColor *>(obj);

View File

@ -28,16 +28,15 @@ class AngelObjString : public QObject {
Q_OBJECT
public:
// for debugger use
static QString stringToString(void *obj, asDebugger *dbg);
static QString stringToString(void *obj, asDebugger *dbg, asUINT tag);
static QString arrayToString(void *obj, asDebugger *dbg);
static QString arrayToString(void *obj, asDebugger *dbg, asUINT tag);
static QString charToString(void *obj, asDebugger *dbg);
static QString charToString(void *obj, asDebugger *dbg, asUINT tag);
static QString dictionaryToString(void *obj, asDebugger *dbg);
static QString dictionaryToString(void *obj, asDebugger *dbg, asUINT tag);
static QString colorToString(void *obj, asDebugger *dbg);
static QString colorToString(void *obj, asDebugger *dbg, asUINT tag);
public:
// ==================================================

View File

@ -45,6 +45,8 @@ AppManager::AppManager(int &argc, char *argv[])
: QtSingleApplication(argc, argv) {
ASSERT_SINGLETON;
_instance = this;
LanguageManager::instance();
InspectQtLogHelper::instance().init();
CrashHandler::instance().init();
@ -102,6 +104,8 @@ AppManager::AppManager(int &argc, char *argv[])
his.add(cmd);
}
_timer.start();
_w = new MainWindow(splash);
setActivationWindow(_w);
@ -133,18 +137,18 @@ AppManager::AppManager(int &argc, char *argv[])
connect(_w, &MainWindow::closed, this,
[]() { AppManager::instance()->exit(); });
_instance = this;
if (splash)
splash->close();
}
AppManager::~AppManager() {
InspectQtLogHelper::instance().destory();
ClangFormatManager::instance().save();
// CommandHistoryManager::save(QConsoleWidget::history().strings_);
ScriptMachine::instance().deleteLater();
InspectQtLogHelper::instance().destory();
CommandHistoryManager::save(QConsoleWidget::history().strings_);
_w->deleteLater();
delete _w;
_w = nullptr;
}
@ -152,6 +156,8 @@ AppManager *AppManager::instance() { return _instance; }
MainWindow *AppManager::mainWindow() const { return _w; }
quint64 AppManager::currentMSecsSinceEpoch() { return _timer.elapsed(); }
void AppManager::openFile(const QString &file, bool autoDetect) {
EditorView *editor = nullptr;
Q_ASSERT(_w);

View File

@ -21,6 +21,8 @@
#include "dialog/mainwindow.h"
#include "qtsingleapplication/src/qtsingleapplication.h"
#include <QElapsedTimer>
class AppManager : public QtSingleApplication {
Q_OBJECT
public:
@ -36,6 +38,8 @@ public:
QApplication::tr("WingCloudStudio");
}
quint64 currentMSecsSinceEpoch();
public slots:
void openFile(const QString &file, bool autoDetect = true);
void openRawFile(const QString &file);
@ -43,6 +47,8 @@ public slots:
private:
MainWindow *_w = nullptr;
QElapsedTimer _timer;
static AppManager *_instance;
};

View File

@ -24,36 +24,17 @@
asBuilder::asBuilder(asIScriptEngine *engine) : AsPreprocesser(engine) {}
asBuilder::~asBuilder() {
if (module) {
module->Discard();
}
}
int asBuilder::StartNewModule(const char *moduleName) {
if (module) {
module->Discard();
}
module = engine->GetModule(moduleName, asGM_ALWAYS_CREATE);
if (module == nullptr)
return -1;
ClearAll();
return 0;
}
asIScriptModule *asBuilder::GetModule() { return module; }
int asBuilder::Build() {
int asBuilder::build(asIScriptModule *module) {
Q_ASSERT(module);
if (module == nullptr) {
return -1;
}
module->ResetGlobalVars();
for (auto &mod : modifiedScripts) {
module->AddScriptSection(mod.section.toUtf8(), mod.script.data(),
mod.script.size(), mod.lineOffset);
mod.script.size());
}
int r = module->Build();

View File

@ -27,19 +27,10 @@
class asBuilder : public AsPreprocesser {
public:
explicit asBuilder(asIScriptEngine *engine);
virtual ~asBuilder();
// Start a new module
virtual int StartNewModule(const char *moduleName);
// Build the added script sections
virtual int Build();
// Returns the current module
asIScriptModule *GetModule();
protected:
asIScriptModule *module = nullptr;
public:
// build the added script sections
int build(asIScriptModule *module);
};
#endif // ASBUILDER_H

View File

@ -18,10 +18,16 @@
#include "ascompletion.h"
#include "asdatabase.h"
#include "class/aspreprocesser.h"
#include "class/qascodeparser.h"
#include "class/scriptmachine.h"
#include "control/scripteditor.h"
#include "model/codecompletionmodel.h"
#include "utilities.h"
#include "wingcodeedit.h"
#include <QAbstractItemView>
#include <QApplication>
#include <QByteArray>
#include <QDir>
#include <QEvent>
@ -36,17 +42,17 @@ Q_GLOBAL_STATIC_WITH_ARGS(QByteArray, DOT_TRIGGER, ("."))
Q_GLOBAL_STATIC_WITH_ARGS(QByteArray, DBL_COLON_TRIGGER, ("::"))
Q_GLOBAL_STATIC_WITH_ARGS(QByteArray, LEFT_PARE_TRIGGER, ("("))
Q_GLOBAL_STATIC_WITH_ARGS(QByteArray, SEMI_COLON_TRIGGER, (";"))
Q_GLOBAL_STATIC_WITH_ARGS(QByteArray, SHARP_TRIGGER, ("#"))
AsCompletion::AsCompletion(asIScriptEngine *engine, WingCodeEdit *p)
: WingCompleter(p), parser(engine), _engine(engine),
m_parseDocument(false) {
Q_ASSERT(engine);
AsCompletion::AsCompletion(WingCodeEdit *p)
: WingCompleter(p), parser(ScriptMachine::instance().engine()) {
setTriggerList({*DOT_TRIGGER, *DBL_COLON_TRIGGER,
// unleash the power of call tips
*LEFT_PARE_TRIGGER,
// clear the tips
*SEMI_COLON_TRIGGER});
*SEMI_COLON_TRIGGER,
// for marcos
*SHARP_TRIGGER});
setTriggerAmount(3);
connect(this, QOverload<const QModelIndex &>::of(&AsCompletion::activated),
@ -63,8 +69,10 @@ AsCompletion::AsCompletion(asIScriptEngine *engine, WingCodeEdit *p)
AsCompletion::~AsCompletion() {}
void AsCompletion::applyEmptyNsNode(QList<CodeInfoTip> &nodes) {
void AsCompletion::applyEmptyNsNode(QList<CodeInfoTip> &nodes,
const QList<CodeInfoTip> &docNodes) {
static QList<CodeInfoTip> emptyNsNodes;
if (emptyNsNodes.isEmpty()) {
auto &hn = parser.headerNodes();
for (auto p = hn.constKeyValueBegin(); p != hn.constKeyValueEnd();
@ -80,7 +88,34 @@ void AsCompletion::applyEmptyNsNode(QList<CodeInfoTip> &nodes) {
}
emptyNsNodes.append(parser.keywordNodes());
}
nodes = emptyNsNodes;
nodes.clear();
for (auto &p : docNodes) {
if (p.nameSpace.isEmpty()) {
nodes.append(p);
} else {
if (p.dontAddGlobal) {
continue;
}
CodeInfoTip tip;
tip.type = CodeInfoTip::Type::Group;
tip.name = p.nameSpace;
nodes.append(tip);
}
if (p.type == CodeInfoTip::Type::Class) {
for (auto &c : p.children) {
if (c.type == CodeInfoTip::Type::ClsFunction ||
c.type == CodeInfoTip::Type::Property) {
nodes.append(c);
}
}
}
}
nodes.append(emptyNsNodes);
}
void AsCompletion::applyClassNodes(QList<CodeInfoTip> &nodes) {
@ -91,7 +126,7 @@ void AsCompletion::applyClassNodes(QList<CodeInfoTip> &nodes) {
for (auto &item : n) {
if (item.type == CodeInfoTip::Type::Class) {
for (auto &c : item.children) {
if (c.type == CodeInfoTip::Type::Function) {
if (c.type == CodeInfoTip::Type::ClsFunction) {
if (!c.addinfo.contains(CodeInfoTip::RetType)) {
continue;
}
@ -105,43 +140,112 @@ void AsCompletion::applyClassNodes(QList<CodeInfoTip> &nodes) {
nodes = clsNodes;
}
bool AsCompletion::parseDocument() const { return m_parseDocument; }
int AsCompletion::includeCallBack(const QString &include, bool quotedInclude,
const QString &from, AsPreprocesser *builder,
void *userParam) {
auto p = reinterpret_cast<AsCompletion *>(userParam);
void AsCompletion::setParseDocument(bool newParseDocument) {
m_parseDocument = newParseDocument;
QFileInfo info(include);
bool isAbsolute = info.isAbsolute();
bool hasNoExt = info.suffix().isEmpty();
QString inc;
if (quotedInclude) {
if (isAbsolute) {
inc = include;
} else {
auto editor = qobject_cast<ScriptEditor *>(p->widget()->parent());
Q_ASSERT(editor);
auto pwd = QFileInfo(editor->fileName()).absoluteDir();
inc = pwd.absoluteFilePath(include);
}
} else {
// absolute include is not allowed in #include<>
if (isAbsolute) {
// ignored in code completion
return asSUCCESS;
}
QDir dir(qApp->applicationDirPath());
if (!dir.cd(QStringLiteral("aslib"))) {
// someone crash the software, ignored
return asSUCCESS;
}
inc = dir.absoluteFilePath(include);
}
if (hasNoExt) {
inc += QStringLiteral(".as");
}
builder->loadSectionFromFile(inc);
return asSUCCESS;
}
void AsCompletion::pushCompleteDBData(const QString &fileName,
const QList<CodeInfoTip> &data) {
if (!QFile::exists(fileName)) {
return;
}
CompleteDB c;
c.data = data;
c.md5 = Utilities::getMd5(fileName);
c.time = 3;
comdb.insert(fileName, c);
}
void AsCompletion::remoteCompleteDBData(const QString &fileName) {
comdb.remove(fileName);
}
std::optional<AsCompletion::CompleteDB>
AsCompletion::getCompleteDBData(const QString &fileName) {
if (comdb.contains(fileName)) {
auto ret = comdb[fileName];
ret.time++;
return ret;
}
return std::nullopt;
}
void AsCompletion::clearCompleteDBUnused() {
for (auto &c : comdb) {
c.time--;
}
comdb.removeIf(
[](const QPair<QString, CompleteDB> &c) { return c.second.time == 0; });
}
void AsCompletion::clearFunctionTip() { emit onFunctionTip({}); }
QString AsCompletion::wordSeperators() const {
static QString eow(QStringLiteral("~!@#$%^&*()_+{}|\"<>?,/;'[]\\-="));
static QString eow(QStringLiteral("~!@$%^&*()_+{}|\"<>?,/;'[]\\-="));
return eow;
}
void AsCompletion::processTrigger(const QString &trigger,
bool AsCompletion::processTrigger(const QString &trigger,
const QString &content) {
QList<CodeInfoTip> nodes;
if (trigger == *SHARP_TRIGGER) {
setModel(new CodeCompletionModel(parseMarcos(), this));
setCompletionPrefix({});
return true;
}
if (content.isEmpty()) {
return;
return false;
}
if (trigger == *SEMI_COLON_TRIGGER) {
clearFunctionTip();
return;
return false;
}
auto len = content.length();
auto code = content.toUtf8();
QList<CodeInfoTip> nodes;
// TODO: PRs are welcomed !!!
// If this software is well-known or brings me lots of
// financial support, I will implement it myself.
// PARSING THE DOCUMENT
if (m_parseDocument) {
nodes.append(parseDocument());
}
if (!trigger.isEmpty() && trigger != *DOT_TRIGGER) {
clearFunctionTip();
}
@ -155,12 +259,14 @@ void AsCompletion::processTrigger(const QString &trigger,
QByteArray content;
};
auto engine = ScriptMachine::instance().engine();
// parse the tokens
QVector<Token> tokens;
qsizetype pos = 0;
for (; p < end;) {
asUINT tokenLen = 0;
auto tt = _engine->ParseToken(p, len, &tokenLen);
auto tt = engine->ParseToken(p, len, &tokenLen);
if (tt == asTC_WHITESPACE) {
p += tokenLen;
pos += tokenLen;
@ -203,22 +309,45 @@ void AsCompletion::processTrigger(const QString &trigger,
QByteArray fn;
if (tokens.isEmpty()) {
popup()->hide();
return;
return false;
}
QString prefix;
auto etoken = tokens.back();
// it can not be any trigger, so take the last as prefix
QString prefix = etoken.content;
if (etoken.type == asTC_VALUE || etoken.type == asTC_COMMENT ||
etoken.type == asTC_UNKNOWN) {
popup()->hide();
return false;
}
if (trigger.isEmpty() && popup()->isVisible()) {
setCompletionPrefix(prefix);
return true;
}
if (trigger.isEmpty() && tokens.length() > 1) {
auto t = std::next(tokens.rbegin());
if (t->content == "#") {
setModel(new CodeCompletionModel(parseMarcos(), this));
setCompletionPrefix(prefix);
return true;
}
}
QList<CodeInfoTip> docNodes = parseDocument();
// if trigger is empty, it's making editing
if (trigger.isEmpty()) {
// it can not be any trigger, so take the last as prefix
prefix = etoken.content;
tokens.removeLast();
if (tokens.isEmpty()) {
applyEmptyNsNode(nodes);
applyEmptyNsNode(nodes, docNodes);
} else {
etoken = tokens.back(); // checking later
}
} else {
prefix.clear();
}
if (nodes.isEmpty()) {
@ -227,21 +356,82 @@ void AsCompletion::processTrigger(const QString &trigger,
if (etoken.content == *DBL_COLON_TRIGGER) {
processTrigger(*DBL_COLON_TRIGGER, content.left(etoken.pos));
setCompletionPrefix(prefix);
return;
return true;
} else if (etoken.content == *DOT_TRIGGER) {
processTrigger(*DOT_TRIGGER, content.left(etoken.pos));
setCompletionPrefix(prefix);
return;
return true;
} else if (etoken.content == QByteArrayLiteral(")")) {
// ignore
} else {
applyEmptyNsNode(nodes);
applyEmptyNsNode(nodes, docNodes);
}
} else if (etoken.type != asTC_IDENTIFIER) {
popup()->hide();
return;
return false;
}
if (trigger == *DOT_TRIGGER) {
if (etoken.type == asTC_IDENTIFIER) {
// member type guessing ? basic match is enough. (>n<)
auto isBasicType = [](const QByteArray &type) {
static QByteArrayList basicType{
"int", "int8", "int16", "int32", "int64",
"uint", "uint8", "uint16", "uint32", "uint64",
"float", "double", "byte"};
return basicType.contains(type);
};
auto clsNodes = parser.classNodes();
// filter the type we can use to auto-complete in docNodes
for (auto &item : docNodes) {
if (item.type == CodeInfoTip::Type::Class) {
auto name = item.nameSpace;
if (name.isEmpty()) {
name = item.name;
} else {
name += QStringLiteral("::") + item.name;
}
clsNodes.insert(name, item.children);
}
// a typedef can only be used to define an alias
// for primitive types, so NO NEED for auto-completing
}
tokens.removeLast();
auto ns = getNamespace(tokens);
for (auto &item : docNodes) {
if (etoken.content == item.name && ns == item.nameSpace) {
auto retType = item.addinfo.value(CodeInfoTip::RetType);
auto decl = engine->GetTypeInfoByDecl(retType.toUtf8());
if (decl) {
QByteArray type = decl->GetNamespace();
if (type.isEmpty()) {
type = decl->GetName();
} else {
type +=
(QByteArrayLiteral("::") + decl->GetName());
}
// auto type inference is not supported.
// PRs will be welcomed !!!
if (isBasicType(type)) {
popup()->hide();
return false;
}
nodes.append(clsNodes.value(type));
break;
}
}
}
}
if (nodes.isEmpty()) {
applyClassNodes(nodes);
}
} else if (etoken.content.length() >= triggerAmount()) {
// completion for a.b.c or a::b.c or a::b::c.d or ::a::b.c
if (trigger == *DBL_COLON_TRIGGER) {
@ -250,12 +440,19 @@ void AsCompletion::processTrigger(const QString &trigger,
if (idx >= 0) {
if (tokens.at(idx).content == *DOT_TRIGGER) {
popup()->hide();
return;
return false;
}
}
nodes = parser.headerNodes().value(ns);
nodes = parser.headerNodes().value(ns) +
parser.enumsNodes().value(ns);
for (auto &n : docNodes) {
if (n.nameSpace == ns) {
nodes.append(n);
}
}
if (nodes.isEmpty()) {
return;
return true;
}
} else if (trigger == *LEFT_PARE_TRIGGER) {
// the first is function name, an identifier
@ -271,12 +468,14 @@ void AsCompletion::processTrigger(const QString &trigger,
if (idx >= 0 && idx < tokens.length()) {
if (tokens.at(idx).content == *DOT_TRIGGER) {
popup()->hide();
return;
return false;
}
}
nodes = parser.headerNodes().value(ns);
if (nodes.isEmpty()) {
applyEmptyNsNode(nodes);
applyEmptyNsNode(nodes, docNodes);
}
}
}
@ -284,9 +483,184 @@ void AsCompletion::processTrigger(const QString &trigger,
setModel(new CodeCompletionModel(nodes, this));
setCompletionPrefix(prefix);
return true;
}
QList<CodeInfoTip> AsCompletion::parseDocument() { return {}; }
QList<CodeInfoTip> AsCompletion::parseDocument() {
auto editor = qobject_cast<WingCodeEdit *>(widget());
if (editor == nullptr) {
return {};
}
auto code = editor->toPlainText();
auto engine = ScriptMachine::instance().engine();
// first preprocess the code
AsPreprocesser prepc(engine);
prepc.setIsCodeCompleteMode(true);
prepc.setIncludeCallback(&AsCompletion::includeCallBack, this);
auto r = prepc.loadSectionFromMemory(QStringLiteral("ASCOMPLETION"),
code.toUtf8());
if (r <= 0) {
return {};
}
auto data = prepc.scriptData();
QList<CodeInfoTip> ret;
auto marcos = prepc.definedMacros();
for (auto pkey = marcos.keyBegin(); pkey != marcos.keyEnd(); pkey++) {
CodeInfoTip tip;
tip.type = CodeInfoTip::Type::KeyWord;
tip.dontAddGlobal = true;
tip.name = *pkey;
ret.append(tip);
}
for (auto &d : data) {
qsizetype offset = -1;
QList<CodeInfoTip> dd;
if (d.section == QStringLiteral("ASCOMPLETION")) {
offset = editor->textCursor().position();
} else {
auto r = getCompleteDBData(d.section);
if (r) {
auto md5 = r->md5;
if (Utilities::getMd5(d.section) == md5) {
dd = r->data;
} else {
remoteCompleteDBData(d.section);
}
}
}
if (dd.isEmpty()) {
dd = parseScriptData(offset, d.script);
if (offset < 0) {
pushCompleteDBData(d.section, dd);
}
}
ret.append(dd);
}
return ret;
}
QList<CodeInfoTip> AsCompletion::parseMarcos() {
static QList<CodeInfoTip> marcos;
if (marcos.isEmpty()) {
QStringList m{"define", "undef", "if", "else", "endif",
"ifdef", "ifndef", "include", "pragma"};
for (auto &i : m) {
CodeInfoTip tip;
tip.name = i;
tip.dontAddGlobal = true;
tip.type = CodeInfoTip::Type::KeyWord;
marcos.append(tip);
}
}
return marcos;
}
QList<CodeInfoTip> AsCompletion::parseScriptData(qsizetype offset,
const QByteArray &code) {
QList<CodeInfoTip> ret;
auto engine = ScriptMachine::instance().engine();
QAsCodeParser parser(engine);
auto syms = parser.parseAndIntell(offset, code);
for (auto &sym : syms) {
CodeInfoTip tip;
tip.name = sym.name;
tip.nameSpace = QString::fromUtf8(sym.scope.join("::"));
switch (sym.symtype) {
case QAsCodeParser::SymbolType::Function:
case QAsCodeParser::SymbolType::FnDef:
tip.type = CodeInfoTip::Type::Function;
tip.addinfo.insert(CodeInfoTip::RetType,
QString::fromUtf8(sym.type));
tip.addinfo.insert(CodeInfoTip::Args,
QString::fromUtf8(sym.additonalInfo));
for (auto &var : sym.children) {
CodeInfoTip va;
va.dontAddGlobal = true;
va.name = var.name;
va.nameSpace = QString::fromUtf8(var.scope.join("::"));
va.addinfo.insert(CodeInfoTip::RetType, var.type);
va.type = CodeInfoTip::Type::Variable;
ret.append(va);
}
break;
case QAsCodeParser::SymbolType::Enum:
tip.type = CodeInfoTip::Type::Enum;
for (auto &e : sym.children) {
CodeInfoTip en;
en.dontAddGlobal = true;
en.name = e.name;
en.nameSpace = QString::fromUtf8(e.scope.join("::"));
en.type = CodeInfoTip::Type::Enumerater;
if (!e.additonalInfo.isEmpty()) {
en.addinfo.insert(CodeInfoTip::Comment,
en.name + QStringLiteral(" = ") +
e.additonalInfo);
}
ret.append(en);
}
break;
case QAsCodeParser::SymbolType::TypeDef:
tip.type = CodeInfoTip::Type::TypeDef;
break;
case QAsCodeParser::SymbolType::Variable:
tip.addinfo.insert(CodeInfoTip::RetType, sym.type);
tip.type = CodeInfoTip::Type::Variable;
break;
case QAsCodeParser::SymbolType::Class:
case QAsCodeParser::SymbolType::Interface:
for (auto &mem : sym.children) {
if (mem.vis != QAsCodeParser::Visiblity::Public) {
continue;
}
CodeInfoTip ctip;
ctip.name = mem.name;
ctip.nameSpace = QString::fromUtf8(mem.scope.join("::"));
if (mem.symtype == QAsCodeParser::SymbolType::Function) {
ctip.type = CodeInfoTip::Type::Function;
ctip.addinfo.insert(CodeInfoTip::RetType,
QString::fromUtf8(mem.type));
ctip.addinfo.insert(CodeInfoTip::Args,
QString::fromUtf8(mem.additonalInfo));
for (auto &var : mem.children) {
CodeInfoTip va;
va.dontAddGlobal = true;
va.name = var.name;
va.nameSpace = QString::fromUtf8(var.scope.join("::"));
va.addinfo.insert(CodeInfoTip::RetType, var.type);
va.type = CodeInfoTip::Type::Variable;
tip.children.append(va);
}
tip.children.append(ctip);
} else if (mem.symtype == QAsCodeParser::SymbolType::Variable) {
ctip.addinfo.insert(CodeInfoTip::RetType, mem.type);
ctip.type = CodeInfoTip::Type::Variable;
tip.children.append(ctip);
}
}
tip.type = CodeInfoTip::Type::Class;
break;
case QAsCodeParser::SymbolType::Invalid:
case QAsCodeParser::SymbolType::Import:
continue;
}
ret.append(tip);
}
return ret;
}
bool AsCompletion::eventFilter(QObject *watched, QEvent *event) {
if (event->type() == QEvent::KeyPress) {

View File

@ -21,10 +21,12 @@
#include "WingCodeEdit/wingcompleter.h"
#include "class/asdatabase.h"
class AsPreprocesser;
class AsCompletion : public WingCompleter {
Q_OBJECT
public:
explicit AsCompletion(asIScriptEngine *engine, WingCodeEdit *p);
explicit AsCompletion(WingCodeEdit *p);
virtual ~AsCompletion();
@ -32,17 +34,19 @@ public:
public:
virtual QString wordSeperators() const override;
bool parseDocument() const;
void setParseDocument(bool newParseDocument);
void clearFunctionTip();
protected:
virtual void processTrigger(const QString &trigger,
virtual bool processTrigger(const QString &trigger,
const QString &content) override;
virtual QList<CodeInfoTip> parseDocument();
virtual QList<CodeInfoTip> parseMarcos();
QList<CodeInfoTip> parseScriptData(qsizetype offset,
const QByteArray &code);
signals:
void onFunctionTip(const QString &content);
@ -51,13 +55,32 @@ public:
virtual bool eventFilter(QObject *watched, QEvent *event) override;
private:
void applyEmptyNsNode(QList<CodeInfoTip> &nodes);
void applyEmptyNsNode(QList<CodeInfoTip> &nodes,
const QList<CodeInfoTip> &docNodes);
void applyClassNodes(QList<CodeInfoTip> &nodes);
private:
static int includeCallBack(const QString &include, bool quotedInclude,
const QString &from, AsPreprocesser *builder,
void *userParam);
private:
struct CompleteDB {
QList<CodeInfoTip> data;
QByteArray md5;
uint time = 0;
};
QHash<QString, CompleteDB> comdb;
void pushCompleteDBData(const QString &fileName,
const QList<CodeInfoTip> &data);
std::optional<CompleteDB> getCompleteDBData(const QString &fileName);
void remoteCompleteDBData(const QString &fileName);
void clearCompleteDBUnused();
private:
ASDataBase parser;
asIScriptEngine *_engine;
bool m_parseDocument;
};
#endif // _CPP_COMPLETION_H_

View File

@ -1,12 +1,108 @@
/*==============================================================================
** Copyright (C) 2024-2027 WingSummer
**
** This program is free software: you can redistribute it and/or modify it under
** the terms of the GNU Affero General Public License as published by the Free
** Software Foundation, version 3.
**
** This program is distributed in the hope that it will be useful, but WITHOUT
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
** FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
** details.
**
** You should have received a copy of the GNU Affero General Public License
** along with this program. If not, see <https://www.gnu.org/licenses/>.
** =============================================================================
*/
#include "asconsolecompletion.h"
#include "AngelScript/sdk/angelscript/source/as_module.h"
#include "class/qascodeparser.h"
#include "control/scriptingconsole.h"
AsConsoleCompletion::AsConsoleCompletion(asIScriptEngine *engine,
ScriptingConsole *p)
: AsCompletion(engine, p), _console(p) {}
AsConsoleCompletion::AsConsoleCompletion(ScriptingConsole *p)
: AsCompletion(p), _console(p) {}
QList<CodeInfoTip> AsConsoleCompletion::parseDocument() {
// TODO
auto editor = _console;
if (editor == nullptr) {
return {};
}
auto code = editor->currentCodes();
auto engine = ScriptMachine::instance().engine();
QAsCodeParser parser(engine);
auto seg = parser.parse(code.toUtf8());
if (std::find_if(seg.begin(), seg.end(),
[](const QAsCodeParser::CodeSegment &seg) {
return seg.isValid();
}) == seg.end()) {
// wrap it to let code-completion work
code.prepend("void f(){").append('}');
}
// first preprocess the code
AsPreprocesser prepc(engine);
// including is not supported in console
auto r = prepc.loadSectionFromMemory(QStringLiteral("ASConCOMPLETION"),
code.toUtf8());
if (r <= 0) {
return {};
}
QList<CodeInfoTip> citips;
// first, global variables should be added into
auto rmod = ScriptMachine::instance().module(ScriptMachine::Interactive);
// I think hijack the internal will do the better
auto mod = dynamic_cast<asCModule *>(rmod);
if (mod) {
auto it = mod->m_scriptGlobals.List();
while (it) {
// lamda expression is great!
auto CString2ByteArray = [](const asCString &str) -> QByteArray {
return QByteArray(str.AddressOf(), str.GetLength());
};
CodeInfoTip tip;
tip.type = CodeInfoTip::Type::Variable;
tip.addinfo.insert(
CodeInfoTip::RetType,
CString2ByteArray(it->type.Format(mod->m_defaultNamespace)));
tip.nameSpace = CString2ByteArray(it->nameSpace->name);
tip.name = CString2ByteArray(it->name);
citips.append(tip);
it++;
}
}
auto data = prepc.scriptData();
if (data.size() == 1) {
auto d = data[0];
return citips + parseScriptData(d.script.length() - 1, d.script);
} else {
return citips;
}
}
QList<CodeInfoTip> AsConsoleCompletion::parseMarcos() {
static QList<CodeInfoTip> marcos;
if (marcos.isEmpty()) {
QStringList m{
"ls",
"del",
"cls",
};
for (auto &i : m) {
CodeInfoTip tip;
tip.name = i;
tip.dontAddGlobal = true;
tip.type = CodeInfoTip::Type::KeyWord;
marcos.append(tip);
}
}
return marcos;
}

View File

@ -1,3 +1,20 @@
/*==============================================================================
** Copyright (C) 2024-2027 WingSummer
**
** This program is free software: you can redistribute it and/or modify it under
** the terms of the GNU Affero General Public License as published by the Free
** Software Foundation, version 3.
**
** This program is distributed in the hope that it will be useful, but WITHOUT
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
** FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
** details.
**
** You should have received a copy of the GNU Affero General Public License
** along with this program. If not, see <https://www.gnu.org/licenses/>.
** =============================================================================
*/
#ifndef ASCONSOLECOMPLETION_H
#define ASCONSOLECOMPLETION_H
@ -8,12 +25,12 @@ class ScriptingConsole;
class AsConsoleCompletion : public AsCompletion {
Q_OBJECT
public:
explicit AsConsoleCompletion(asIScriptEngine *engine, ScriptingConsole *p);
explicit AsConsoleCompletion(ScriptingConsole *p);
virtual ~AsConsoleCompletion() = default;
// AsCompletion interface
protected:
virtual QList<CodeInfoTip> parseDocument() override;
virtual QList<CodeInfoTip> parseMarcos() override;
private:
ScriptingConsole *_console;

View File

@ -1,22 +0,0 @@
/*==============================================================================
** Copyright (C) 2024-2027 WingSummer
**
** This program is free software: you can redistribute it and/or modify it under
** the terms of the GNU Affero General Public License as published by the Free
** Software Foundation, version 3.
**
** This program is distributed in the hope that it will be useful, but WITHOUT
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
** FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
** details.
**
** You should have received a copy of the GNU Affero General Public License
** along with this program. If not, see <https://www.gnu.org/licenses/>.
** =============================================================================
*/
#include "ascontextmgr.h"
asContextMgr::asContextMgr() : CContextMgr() {}
bool asContextMgr::isRunning() const { return !m_threads.empty(); }

View File

@ -40,7 +40,8 @@ ASDataBase::ASDataBase(asIScriptEngine *engine) {
"function", "interface", "shared", "this", "explicit", "override",
"namespace", "get", "set", "super", "mixin", "false",
"true", "null", "typename", "return", "typedef", "funcdef",
"from", "import", "not", "xor", "or", "is"};
"from", "import", "not", "xor", "or", "is",
"co_await"};
for (auto &k : kws) {
CodeInfoTip t;
t.type = CodeInfoTip::Type::KeyWord;
@ -177,6 +178,10 @@ const QHash<QString, QList<CodeInfoTip>> &ASDataBase::headerNodes() const {
return _headerNodes;
}
const QHash<QString, QList<CodeInfoTip>> &ASDataBase::enumsNodes() const {
return _enumsNodes;
}
void ASDataBase::addGlobalFunctionCompletion(asIScriptEngine *engine) {
Q_ASSERT(engine);
@ -210,6 +215,13 @@ void ASDataBase::addEnumCompletion(asIScriptEngine *engine) {
einfo.name = etype->GetName();
einfo.type = CodeInfoTip::Type::Enum;
auto ens = einfo.nameSpace;
if (ens.isEmpty()) {
ens = einfo.name;
} else {
ens += QStringLiteral("::") + einfo.name;
}
for (asUINT i = 0; i < etype->GetEnumValueCount(); ++i) {
int v;
auto e = etype->GetEnumValueByIndex(i, &v);
@ -221,6 +233,7 @@ void ASDataBase::addEnumCompletion(asIScriptEngine *engine) {
QStringLiteral(" = ") +
QString::number(v));
einfo.children.append(en);
_enumsNodes[ens].append(en);
}
etype->Release();
@ -313,6 +326,42 @@ void ASDataBase::addClassCompletion(asIScriptEngine *engine) {
obj->Release();
_headerNodes[ns].append(cls);
auto cat = cls.nameSpace;
if (cat.isEmpty()) {
cat = cls.name;
} else {
cat += QStringLiteral("::") + cls.name;
}
auto data = cls.children;
data.removeIf([](const CodeInfoTip &tip) {
static QStringList opList{
"opNeg", "opCom", "opPreInc", "opPreDec",
"opPostInc", "opPostDec", "opEquals", "opCmp",
"opEquals", "opAssign", "opAddAssign", "opSubAssign",
"opMulAssign", "opDivAssign", "opModAssign", "opPowAssign",
"opAndAssign", "opOrAssign", "opXorAssign", "opShlAssign",
"opShrAssign", "opUShrAssign", "opAdd", "opAdd_r",
"opSub", "opSub_r", "opMul", "opMul_r",
"opDiv", "opDiv_r", "opMod", "opMod_r",
"opPow", "opPow_r", "opAnd", "opAnd_r",
"opOr", "opOr_r", "opXor", "opXor_r",
"opShl", "opShl_r", "opShr", "opShr_r",
"opUShr", "opUShr_r", "opIndex", "opCall",
"opConv", "opImplConv", "opCast", "opImplCast"};
return tip.addinfo.value(CodeInfoTip::RetType).isEmpty() ||
opList.contains(tip.name);
});
for (auto &d : data) {
d.addinfo.insert(
CodeInfoTip::Comment,
d.addinfo.value(CodeInfoTip::RetType) + QStringLiteral(" ") +
d.name + QStringLiteral("(") +
d.addinfo.value(CodeInfoTip::Args) + QStringLiteral(") ") +
d.addinfo.value(CodeInfoTip::SuffixQualifier));
}
_classNodes[cat].append(data);
}
}
@ -323,6 +372,10 @@ QString ASDataBase::getSuffixQualifier(asIScriptFunction *fn) {
return {};
}
const QHash<QString, QList<CodeInfoTip>> &ASDataBase::classNodes() const {
return _classNodes;
}
const QList<CodeInfoTip> &ASDataBase::keywordNodes() const {
return _keywordNode;
}

View File

@ -42,6 +42,8 @@ private:
public:
const QHash<QString, QList<CodeInfoTip>> &headerNodes() const;
const QHash<QString, QList<CodeInfoTip>> &enumsNodes() const;
const QHash<QString, QList<CodeInfoTip>> &classNodes() const;
const QList<CodeInfoTip> &keywordNodes() const;
@ -55,6 +57,8 @@ private:
private:
// <namespace, content>
QHash<QString, QList<CodeInfoTip>> _headerNodes;
QHash<QString, QList<CodeInfoTip>> _enumsNodes;
QHash<QString, QList<CodeInfoTip>> _classNodes;
QList<CodeInfoTip> _keywordNode;
};

View File

@ -16,6 +16,7 @@
*/
#include "asdebugger.h"
#include "class/appmanager.h"
#include "define.h"
#include <QApplication>
@ -71,10 +72,57 @@ void asDebugger::takeCommands(asIScriptContext *ctx) {
void asDebugger::lineCallback(asIScriptContext *ctx) {
Q_ASSERT(ctx);
// prevent UI freezing
qApp->processEvents();
// This should never happen, but it doesn't hurt to validate it
if (ctx == nullptr)
return;
// 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 (ctx->GetState() != asEXECUTION_ACTIVE)
return;
auto now = AppManager::instance()->currentMSecsSinceEpoch();
auto timer = reinterpret_cast<asPWORD>(
ctx->GetUserData(AsUserDataType::UserData_Timer));
auto timeOutTime = reinterpret_cast<asPWORD>(
ctx->GetUserData(AsUserDataType::UserData_TimeOut));
auto mode = reinterpret_cast<asPWORD>(
ctx->GetUserData(AsUserDataType::UserData_ContextMode));
bool timeOut = false;
if (timer < 0) {
timeOut = true;
} else {
if (mode == 0) {
timeOut = (now - timer) > 3000; // 3 s
} else {
if (timeOutTime) {
timeOut = (now - timer) > timeOutTime; // 10 min
}
}
}
if (timeOut && mode) {
auto timeOut = tr("ScriptTimedOut");
ScriptMachine::MessageInfo info;
info.message = timeOut;
info.mode = ScriptMachine::ConsoleMode(mode);
info.type = ScriptMachine::MessageType::Error;
ScriptMachine::instance().outputMessage(info);
ctx->Abort();
return;
}
auto isDbg = reinterpret_cast<asPWORD>(
ctx->GetUserData(AsUserDataType::UserData_isDbg));
if (!isDbg) {
return;
}
const char *file = 0;
int col = 0;
int lineNbr = ctx->GetLineNumber(0, &col, &file);
@ -98,12 +146,6 @@ 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 (ctx->GetState() != asEXECUTION_ACTIVE)
return;
auto dbgContext = reinterpret_cast<ContextDbgInfo *>(ctx->GetUserData());
Q_ASSERT(dbgContext);
@ -359,7 +401,7 @@ bool asDebugger::checkBreakPoint(asIScriptContext *ctx) {
}
QString asDebugger::toString(void *value, asUINT typeId,
asIScriptEngine *engine) {
asIScriptEngine *engine, asUINT tag) {
if (value == nullptr)
return QStringLiteral("<null>");
@ -436,7 +478,8 @@ QString asDebugger::toString(void *value, asUINT typeId,
s << name /*type->GetPropertyDeclaration(n)*/
<< QStringLiteral(" = ")
<< toString(obj->GetAddressOfProperty(n),
obj->GetPropertyTypeId(n), type->GetEngine());
obj->GetPropertyTypeId(n), type->GetEngine(),
1);
}
}
}
@ -473,7 +516,7 @@ QString asDebugger::toString(void *value, asUINT typeId,
// Invoke the callback to get the string representation of
// this type
s << it.value()(value, this);
s << it.value()(value, this, tag);
} else {
// Unknown type: type + address
s << type->GetName() << '(' << value << ')';
@ -500,6 +543,8 @@ asDebugger::GCStatistic asDebugger::gcStatistics() {
void asDebugger::runDebugAction(DebugAction action) { m_action = action; }
void asDebugger::resetState() { m_action = CONTINUE; }
void asDebugger::deleteDbgContextInfo(void *info) {
if (info) {
delete reinterpret_cast<ContextDbgInfo *>(info);

View File

@ -84,7 +84,7 @@ public:
virtual ~asDebugger();
// Register callbacks to handle to-string conversions of application types
typedef QString (*ToStringCallback)(void *obj, asDebugger *dbg);
typedef QString (*ToStringCallback)(void *obj, asDebugger *dbg, asUINT tag);
void registerToStringCallback(const asITypeInfo *ti,
ToStringCallback callback);
@ -108,14 +108,16 @@ public:
// Line callback invoked by context
void lineCallback(asIScriptContext *ctx);
// tag = 1 : string should be printed with quotes
QString toString(void *value, asUINT typeId,
asIScriptEngine *engine = nullptr);
asIScriptEngine *engine = nullptr, asUINT tag = 0);
GCStatistic gcStatistics();
void runDebugAction(DebugAction action);
DebugAction currentState() const;
void resetState();
static void deleteDbgContextInfo(void *info);

View File

@ -16,16 +16,29 @@
*/
#include "aspreprocesser.h"
#include "class/languagemanager.h"
#include "class/qascodeparser.h"
#include "class/scriptmachine.h"
#include "class/skinmanager.h"
#include "scriptaddon/aspromise.hpp"
#include <QDir>
#include <QFileInfo>
#include <QLibraryInfo>
#include <QMetaEnum>
#include <QStack>
#include <QVersionNumber>
Q_GLOBAL_STATIC_WITH_ARGS(
QStringList, DEFAULT_MARCO,
({"__AS_ARRAY__", "__AS_ANY__", "__AS_GRID__", "__AS_HANDLE__",
QByteArrayList, DEFAULT_MARCO,
({// special marcos
"__LINE__", "__SECTION__", "__SECTION_BASE__",
// functions
"__AS_ARRAY__", "__AS_ANY__", "__AS_GRID__", "__AS_HANDLE__",
"__AS_MATH__", "__AS_WEAKREF__", "__AS_COROUTINE__", "__WING_FILE__",
"__WING_STRING__", "__WING_COLOR__", "__WING_JSON__", "__WING_REGEX__",
"__WING_DICTIONARY__"}));
"__WING_DICTIONARY__", "__WING_PRINT_VAR__", "__WING_PRINT_LN__",
"__AS_PROMISE__", "__WING_CLIPBOARD__"}));
AsPreprocesser::AsPreprocesser(asIScriptEngine *engine) : engine(engine) {
Q_ASSERT(engine);
@ -36,19 +49,70 @@ AsPreprocesser::AsPreprocesser(asIScriptEngine *engine) : engine(engine) {
pragmaCallback = nullptr;
pragmaParam = nullptr;
definedWords = *DEFAULT_MARCO;
for (auto &m : *DEFAULT_MARCO) {
definedWords.insert(m, {});
}
static QHash<QString, QByteArray> addInfos;
if (addInfos.isEmpty()) {
// software infos
auto ver = QVersionNumber::fromString(WINGHEX_VERSION);
addInfos.insert(QStringLiteral("__WING_VERSION__"),
"\"" WINGHEX_VERSION "\"");
addInfos.insert(QStringLiteral("__WING_VERSION_MAJOR__"),
QByteArray::number(ver.majorVersion()));
addInfos.insert(QStringLiteral("__WING_VERSION_MINOR__"),
QByteArray::number(ver.minorVersion()));
addInfos.insert(QStringLiteral("__WING_VERSION_PATCH__"),
QByteArray::number(ver.microVersion()));
addInfos.insert(QStringLiteral("__ANGELSCRIPT_VERSION__"),
"\"" ANGELSCRIPT_VERSION_STRING "\"");
addInfos.insert(QStringLiteral("__ANGELSCRIPT_VERSION_MAJOR__"),
QByteArray::number(ANGELSCRIPT_VERSION / 10000));
addInfos.insert(QStringLiteral("__ANGELSCRIPT_VERSION_MINOR__"),
QByteArray::number((ANGELSCRIPT_VERSION / 100) % 100));
addInfos.insert(QStringLiteral("__ANGELSCRIPT_VERSION_PATCH__"),
QByteArray::number(ANGELSCRIPT_VERSION % 100));
addInfos.insert(QStringLiteral("__WINGHEX_APPNAME__"),
"\"" APP_NAME "\"");
addInfos.insert(QStringLiteral("__WINGHEX_AUTHOR__"), "\"wingsummer\"");
ver = QLibraryInfo::version();
addInfos.insert(QStringLiteral("__QT_VERSION__"),
ver.toString().toUtf8().prepend('"').append('"'));
addInfos.insert(QStringLiteral("__QT_VERSION_MAJOR__"),
QByteArray::number(ver.majorVersion()));
addInfos.insert(QStringLiteral("__QT_VERSION_MINOR__"),
QByteArray::number(ver.minorVersion()));
addInfos.insert(QStringLiteral("__QT_VERSION_PATCH__"),
QByteArray::number(ver.microVersion()));
// UI
addInfos.insert(QStringLiteral("__LANG__"), LanguageManager::instance()
.defaultLocale()
.name()
.toUtf8()
.prepend('"')
.append('"'));
auto theme = SkinManager::instance().currentTheme();
auto te = QMetaEnum::fromType<SkinManager::Theme>();
addInfos.insert(
QStringLiteral("__THEME__"),
QByteArray(te.valueToKey(int(theme))).prepend('"').append('"'));
}
definedWords.insert(addInfos);
}
AsPreprocesser::~AsPreprocesser() { void ClearAll(); }
int AsPreprocesser::AddSectionFromFile(const QString &filename) {
int AsPreprocesser::loadSectionFromFile(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 (includeIfNotAlreadyIncluded(fullpath)) {
int r = loadScriptSection(fullpath);
if (r < 0)
return r;
else
@ -58,62 +122,73 @@ int AsPreprocesser::AddSectionFromFile(const QString &filename) {
return 0;
}
QList<AsPreprocesser::ScriptData> AsPreprocesser::GetScriptData() const {
int AsPreprocesser::loadSectionFromMemory(const QString &section,
const QByteArray &code) {
int r = processScriptSection(code, section);
if (r < 0)
return r;
else
return 1;
}
QList<AsPreprocesser::ScriptData> AsPreprocesser::scriptData() const {
return modifiedScripts;
}
asIScriptEngine *AsPreprocesser::GetEngine() { return engine; }
asIScriptEngine *AsPreprocesser::getEngine() { return engine; }
void AsPreprocesser::SetIncludeCallback(INCLUDECALLBACK_t callback,
void AsPreprocesser::setIncludeCallback(INCLUDECALLBACK_t callback,
void *userParam) {
includeCallback = callback;
includeParam = userParam;
}
void AsPreprocesser::SetPragmaCallback(PRAGMACALLBACK_t callback,
void AsPreprocesser::setPragmaCallback(PRAGMACALLBACK_t callback,
void *userParam) {
pragmaCallback = callback;
pragmaParam = userParam;
}
void AsPreprocesser::DefineWord(const QString &word) {
if (!definedWords.contains(word)) {
definedWords.append(word);
bool AsPreprocesser::defineWord(const QString &word, const QByteArray &value) {
// try to modify system marco is not allowed
if (DEFAULT_MARCO->contains(word)) {
return false;
}
definedWords.insert(word, value);
return true;
}
unsigned int AsPreprocesser::GetSectionCount() const {
unsigned int AsPreprocesser::sectionCount() const {
return (unsigned int)(includedScripts.size());
}
QString AsPreprocesser::GetSectionName(unsigned int idx) const {
QString AsPreprocesser::sectionName(unsigned int idx) const {
if (qsizetype(idx) >= qsizetype(includedScripts.size()))
return {};
return includedScripts.at(idx);
}
void AsPreprocesser::ClearAll() { includedScripts.clear(); }
void AsPreprocesser::clearAll() { includedScripts.clear(); }
int AsPreprocesser::processScriptSection(const QByteArray &script,
const QString &sectionname) {
// #define DBG_CODEP // debug macro for pre-process
int AsPreprocesser::ProcessScriptSection(const QByteArray &script, int length,
const QString &sectionname,
int lineOffset) {
QVector<QPair<QString, bool>> includes;
QByteArray modifiedScript;
// Perform a superficial parsing of the script first to store the metadata
if (length)
modifiedScript = script.left(length);
else
modifiedScript = script;
QByteArray modifiedScript = script;
// First perform the checks for #if directives to exclude code that
// shouldn't be compiled
QByteArray::size_type pos = 0;
int nested = 0;
QStack<std::optional<bool>> m_condtionStack;
while (pos < modifiedScript.size()) {
auto SECTION = sectionname.toUtf8();
asUINT len = 0;
asETokenClass t = engine->ParseToken(modifiedScript.data() + pos,
modifiedScript.size() - pos, &len);
@ -126,45 +201,398 @@ int AsPreprocesser::ProcessScriptSection(const QByteArray &script, int length,
modifiedScript.size() - pos, &len);
Q_UNUSED(t);
QByteArray token = modifiedScript.mid(pos, len);
QByteArray token = modifiedScript.sliced(pos, len);
pos += len;
if (token == "if") {
bool isIf = token == QStringLiteral("if");
bool isIfDef = token == QStringLiteral("ifdef");
bool isIfnDef = token == QStringLiteral("ifndef");
bool isDef = token == QStringLiteral("define");
bool isUnDef = token == QStringLiteral("undef");
if (isIf || isIfDef || isIfnDef || isDef || isUnDef) {
t = engine->ParseToken(modifiedScript.data() + pos,
modifiedScript.size() - pos, &len);
if (t == asTC_WHITESPACE) {
if (std::any_of(modifiedScript.data() + pos,
modifiedScript.data() + pos + len,
[](char ch) { return ch == '\n'; })) {
if (_isCodeCompleteMode) {
pos = modifiedScript.indexOf('\n', pos);
continue;
}
auto str = QObject::tr("IfDefNoWord");
engine->WriteMessage(SECTION,
getLineCount(modifiedScript, pos),
1, asMSGTYPE_ERROR, str.toUtf8());
return asERROR;
}
pos += len;
t = engine->ParseToken(modifiedScript.data() + pos,
modifiedScript.size() - pos, &len);
}
if (isIfDef || isIfnDef) {
if (_isCodeCompleteMode) {
auto pos = modifiedScript.indexOf('\n', start);
if (pos < 0) {
overwriteCode(modifiedScript, start,
modifiedScript.size() - start - 1);
} else {
overwriteCode(modifiedScript, start, pos - start);
}
continue;
}
if (t == asTC_IDENTIFIER) {
QByteArray word = modifiedScript.mid(pos, len);
QByteArray word = modifiedScript.sliced(pos, len);
// Overwrite the #if directive with space characters to
// Overwrite the directive with space characters to
// avoid compiler error
pos += len;
OverwriteCode(modifiedScript, start, pos - start);
overwriteCode(modifiedScript, 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(modifiedScript, pos);
// Has this identifier been defined by the application
// or not?
if ((isIfDef && !definedWords.contains(word)) ||
(isIfnDef && definedWords.contains(word))) {
// Exclude all the code until and including the
// #endif
pos = excludeIfCode(modifiedScript, pos);
m_condtionStack.push(false);
} else {
nested++;
m_condtionStack.push(true);
}
#ifdef DBG_CODEP
qDebug().noquote() << modifiedScript;
#endif
} else {
auto str = QObject::tr("IfDefInvalidWord");
engine->WriteMessage(SECTION,
getLineCount(modifiedScript, pos),
1, asMSGTYPE_ERROR, str.toUtf8());
return asERROR;
}
} else if (isIf) {
if (_isCodeCompleteMode) {
auto pos = modifiedScript.indexOf('\n', start);
if (pos < 0) {
overwriteCode(modifiedScript, start,
modifiedScript.size() - start - 1);
} else {
overwriteCode(modifiedScript, start, pos - start);
}
continue;
}
// evalutate the string
auto npos = modifiedScript.indexOf('\n', pos);
QByteArray codes;
if (npos >= 0) {
codes = modifiedScript.sliced(pos, npos - pos);
} else {
codes = modifiedScript.sliced(pos);
}
overwriteCode(modifiedScript, start, npos - start);
auto &sm = ScriptMachine::instance();
bool ok = false;
// if expression should processed the marcos
auto ret = sm.evaluateDefine(
processIfExpression(
codes, getLineCount(modifiedScript, pos), SECTION),
ok);
if (ret < 0) {
auto str = QObject::tr("CalIfFailed");
engine->WriteMessage(SECTION,
getLineCount(modifiedScript, pos),
1, asMSGTYPE_ERROR, str.toUtf8());
return asERROR;
} else {
if (ok) {
m_condtionStack.push(true);
} else {
pos = excludeIfCode(modifiedScript, npos);
m_condtionStack.push(false);
}
}
#ifdef DBG_CODEP
qDebug().noquote() << modifiedScript;
#endif
} else if (isDef) {
if (t == asTC_IDENTIFIER) {
QByteArray word = modifiedScript.sliced(pos, len);
pos += len;
if (_isCodeCompleteMode) {
defineWord(word, {});
auto pos = modifiedScript.indexOf('\n', start);
if (pos < 0) {
overwriteCode(modifiedScript, start,
modifiedScript.size() - start -
1);
} else {
overwriteCode(modifiedScript, start,
pos - start);
}
continue;
}
t = engine->ParseToken(modifiedScript.data() + pos,
modifiedScript.size() - pos,
&len);
if (t == asTC_WHITESPACE) {
// line break?
if (std::any_of(
modifiedScript.data() + pos,
modifiedScript.data() + pos + len,
[](char ch) { return ch == '\n'; })) {
defineWord(word, {});
} else {
pos += len;
size_t total = 0;
auto v = QAsCodeParser::getToken(
engine, modifiedScript.data() + pos,
modifiedScript.size() - pos, &total);
// only support these things
switch (v) {
case ttIdentifier:
case ttIntConstant:
case ttFloatConstant:
case ttDoubleConstant:
case ttBitsConstant:
case ttStringConstant: {
auto v = modifiedScript.sliced(pos, total);
defineWord(word, v);
} break;
default:
auto str = QObject::tr("UnexceptedToken");
engine->WriteMessage(
SECTION,
getLineCount(modifiedScript, pos), 1,
asMSGTYPE_ERROR, str.toUtf8());
return asERROR;
}
pos += total;
}
overwriteCode(modifiedScript, start, pos - start);
}
// ensure end line
if (endLinePassFailed(modifiedScript, pos)) {
auto str = QObject::tr("UnexceptedToken");
engine->WriteMessage(
SECTION, getLineCount(modifiedScript, pos), 1,
asMSGTYPE_ERROR, str.toUtf8());
return asERROR;
}
} else {
if (_isCodeCompleteMode) {
auto pos = modifiedScript.indexOf('\n', start);
if (pos < 0) {
overwriteCode(modifiedScript, start,
modifiedScript.size() - start -
1);
} else {
overwriteCode(modifiedScript, start,
pos - start);
}
continue;
}
auto str = QObject::tr("InvalidDef");
engine->WriteMessage(SECTION,
getLineCount(modifiedScript, pos),
1, asMSGTYPE_ERROR, str.toUtf8());
return asERROR;
}
} else if (isUnDef) {
if (_isCodeCompleteMode) {
auto pos = modifiedScript.indexOf('\n', start);
if (pos < 0) {
overwriteCode(modifiedScript, start,
modifiedScript.size() - start - 1);
} else {
overwriteCode(modifiedScript, start, pos - start);
}
continue;
}
if (t == asTC_IDENTIFIER) {
QByteArray word = modifiedScript.sliced(pos, len);
// Overwrite the directive with space characters to
// avoid compiler error
pos += len;
overwriteCode(modifiedScript, start, pos - start);
constexpr auto PREFIX = "__";
if (word.startsWith(PREFIX) && word.endsWith(PREFIX)) {
// Warning
auto str = QObject::tr("ReservedMarcoType");
engine->WriteMessage(
SECTION, getLineCount(modifiedScript, pos), 1,
asMSGTYPE_WARNING, str.toUtf8());
} else {
if (!definedWords.remove(word)) {
auto str = QObject::tr("MarcoNotFound:") + word;
engine->WriteMessage(
SECTION, getLineCount(modifiedScript, pos),
1, asMSGTYPE_WARNING, str.toUtf8());
}
}
// ensure end line
if (endLinePassFailed(modifiedScript, pos)) {
auto str = QObject::tr("UnexceptedToken");
engine->WriteMessage(
SECTION, getLineCount(modifiedScript, pos), 1,
asMSGTYPE_ERROR, str.toUtf8());
return asERROR;
}
} else {
auto str = QObject::tr("InvalidDef");
engine->WriteMessage(SECTION,
getLineCount(modifiedScript, pos),
1, asMSGTYPE_ERROR, str.toUtf8());
return asERROR;
}
}
} else if (token == "else") {
if (_isCodeCompleteMode) {
overwriteCode(modifiedScript, start, pos - start);
continue;
}
if (m_condtionStack.isEmpty()) {
auto str = QObject::tr("NoMatchingIf");
engine->WriteMessage(SECTION,
getLineCount(modifiedScript, pos), 1,
asMSGTYPE_ERROR, str.toUtf8());
return asERROR;
} else {
overwriteCode(modifiedScript, start, pos - start);
auto opBool = m_condtionStack.top();
if (opBool) {
if (opBool.value()) {
pos = excludeElseCode(modifiedScript, pos);
m_condtionStack.top().reset();
}
// ensure end line
if (endLinePassFailed(modifiedScript, pos - 1)) {
auto str = QObject::tr("UnexceptedToken");
engine->WriteMessage(
SECTION, getLineCount(modifiedScript, pos), 1,
asMSGTYPE_ERROR, str.toUtf8());
return asERROR;
}
} else {
auto str = QObject::tr("DupElseDef");
engine->WriteMessage(SECTION,
getLineCount(modifiedScript, pos),
1, asMSGTYPE_ERROR, str.toUtf8());
return asERROR;
}
}
#ifdef DBG_CODEP
qDebug().noquote() << modifiedScript;
#endif
} else if (token == "endif") {
if (_isCodeCompleteMode) {
overwriteCode(modifiedScript, start, pos - start);
continue;
}
// Only remove the #endif if there was a matching #if
if (nested > 0) {
OverwriteCode(modifiedScript, start, pos - start);
nested--;
if (m_condtionStack.isEmpty()) {
auto str = QObject::tr("NoMatchingIf");
engine->WriteMessage(SECTION,
getLineCount(modifiedScript, pos), 1,
asMSGTYPE_ERROR, str.toUtf8());
return asERROR;
} else {
overwriteCode(modifiedScript, start, pos - start);
m_condtionStack.pop();
}
// ensure end line
if (endLinePassFailed(modifiedScript, pos)) {
auto str = QObject::tr("UnexceptedToken");
engine->WriteMessage(SECTION,
getLineCount(modifiedScript, pos), 1,
asMSGTYPE_ERROR, str.toUtf8());
return asERROR;
}
#ifdef DBG_CODEP
qDebug().noquote() << modifiedScript;
#endif
}
} else {
if (!_isCodeCompleteMode) {
if (t == asTC_IDENTIFIER) {
// define replace
auto word = modifiedScript.sliced(pos, len);
if (word == PROMISE_AWAIT) {
auto npos = pos + len;
asUINT total = 0;
auto t = engine->ParseToken(
modifiedScript.data() + npos,
modifiedScript.size() - npos, &total);
if (t == asTC_WHITESPACE) {
npos += total;
t = engine->ParseToken(modifiedScript.data() + npos,
modifiedScript.size() - npos,
&total);
if (t == asTC_IDENTIFIER) {
// ok
auto word = modifiedScript.sliced(npos, total);
auto data = "(" + word +
")." PROMISE_YIELD
"()." PROMISE_UNWRAP "()";
auto oldLen = npos - pos + word.length();
modifiedScript.replace(pos, oldLen, data);
pos = npos;
pos += data.length() - oldLen + word.length();
continue;
}
}
auto str = QObject::tr("UnexceptedToken");
engine->WriteMessage(SECTION,
getLineCount(modifiedScript, pos),
1, asMSGTYPE_ERROR, str.toUtf8());
return asERROR;
} else if (word == "__LINE__") {
auto data = QByteArray::number(
getLineCount(modifiedScript, pos));
modifiedScript.replace(pos, len, data);
pos += data.length();
continue;
} else if (word == "__SECTION__") {
auto data = SECTION;
data.prepend('"').append('"');
modifiedScript.replace(pos, len, data);
pos += data.length();
continue;
} else if (word == "__SECTION_BASE__") {
auto data = QFileInfo(SECTION).baseName().toUtf8();
data.prepend('"').append('"');
modifiedScript.replace(pos, len, data);
pos += data.length();
continue;
} else {
auto rword = findReplaceResult(word);
if (word != rword) {
modifiedScript.replace(pos, len, rword);
len = rword.length();
}
}
}
}
} else
pos += len;
}
}
#ifdef DBG_CODEP
qDebug().noquote() << modifiedScript;
#endif
// Then check for pre-processor directives
pos = 0;
@ -176,7 +604,7 @@ int AsPreprocesser::ProcessScriptSection(const QByteArray &script, int length,
pos += len;
continue;
}
QString token = modifiedScript.mid(pos, len);
QString token = modifiedScript.sliced(pos, len);
// Skip possible decorators before class and interface declarations
if (token == "shared" || token == "abstract" || token == "mixin" ||
@ -192,7 +620,7 @@ int AsPreprocesser::ProcessScriptSection(const QByteArray &script, int length,
t = engine->ParseToken(modifiedScript.data() + pos,
modifiedScript.size() - pos, &len);
if (t == asTC_IDENTIFIER) {
token = modifiedScript.mid(pos, len);
token = modifiedScript.sliced(pos, len);
if (token == "include") {
pos += len;
t = engine->ParseToken(modifiedScript.data() + pos,
@ -209,21 +637,21 @@ int AsPreprocesser::ProcessScriptSection(const QByteArray &script, int length,
modifiedScript[pos] == '\'')) {
// Get the include file
QString includefile =
modifiedScript.mid(pos + 1, len - 2);
modifiedScript.sliced(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 =
QObject::tr("Invalid file name for #include; "
"it contains a line-break: ") +
QStringLiteral("'") + includefile.left(p) +
QStringLiteral("'");
engine->WriteMessage(sectionname.toUtf8(), 0, 0,
engine->WriteMessage(
sectionname.toUtf8(),
getLineCount(modifiedScript, pos), 1,
asMSGTYPE_ERROR, str.toUtf8());
} else {
// Store it for later processing
@ -231,11 +659,10 @@ int AsPreprocesser::ProcessScriptSection(const QByteArray &script, int length,
// Overwrite the include directive with space
// characters to avoid compiler error
OverwriteCode(modifiedScript, start, pos - start);
overwriteCode(modifiedScript, start, pos - start);
}
}
if (t == asTC_KEYWORD && modifiedScript[pos] == '<') {
} else if (t == asTC_KEYWORD &&
modifiedScript[pos] == '<') {
pos += len;
// find the next '>'
@ -253,7 +680,8 @@ int AsPreprocesser::ProcessScriptSection(const QByteArray &script, int length,
if (found) {
QString includefile =
modifiedScript.mid(pos, rpos - pos).trimmed();
modifiedScript.sliced(pos, rpos - pos)
.trimmed();
pos = rpos + 1;
@ -262,24 +690,23 @@ int AsPreprocesser::ProcessScriptSection(const QByteArray &script, int length,
auto p = includefile.indexOf('\n');
auto ws = includefile.indexOf(' ');
if (!includefile.isEmpty() && p >= 0 && ws >= 0) {
// TODO: Show the correct line number for
// the error
auto str =
QObject::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());
engine->WriteMessage(
sectionname.toUtf8(),
getLineCount(modifiedScript, pos), 1,
asMSGTYPE_ERROR, str.toUtf8());
} else {
// Store it for later processing
includes.append({includefile, false});
// Overwrite the include directive with
// space characters to avoid compiler error
OverwriteCode(modifiedScript, start,
overwriteCode(modifiedScript, start,
pos - start);
}
} else {
@ -301,37 +728,41 @@ int AsPreprocesser::ProcessScriptSection(const QByteArray &script, int length,
// Call the pragma callback
auto pragmaText =
modifiedScript.mid(start + 7, pos - start - 7);
modifiedScript.sliced(start + 7, pos - start - 7);
// Overwrite the pragma directive with space characters
// to avoid compiler error
OverwriteCode(modifiedScript, start, pos - start);
overwriteCode(modifiedScript, start, pos - start);
if (!_isCodeCompleteMode) {
int r = pragmaCallback
? pragmaCallback(pragmaText, this, sectionname,
pragmaParam)
? pragmaCallback(pragmaText, this,
sectionname, pragmaParam)
: -1;
if (r < 0) {
// TODO: Report the correct line number
engine->WriteMessage(
sectionname.toUtf8(), 0, 0, asMSGTYPE_ERROR,
QObject::tr("Invalid #pragma directive").toUtf8());
sectionname.toUtf8(),
getLineCount(modifiedScript, pos), 1,
asMSGTYPE_ERROR,
QObject::tr("Invalid #pragma directive")
.toUtf8());
return r;
}
}
}
}
}
// Don't search for includes within statement blocks or
// between tokens in statements
else {
pos = SkipStatement(modifiedScript, pos);
pos = skipStatement(modifiedScript, pos);
}
}
// Build the actual script
engine->SetEngineProperty(asEP_COPY_SCRIPT_SECTIONS, true);
AddScriptSection(sectionname, modifiedScript, lineOffset);
addScriptSection(sectionname, modifiedScript);
if (includes.size() > 0) {
// If the callback has been set, then call it for each included file
@ -362,7 +793,7 @@ int AsPreprocesser::ProcessScriptSection(const QByteArray &script, int length,
}
// Include the script section
int r = AddSectionFromFile(includes[n].first);
int r = loadSectionFromFile(includes[n].first);
if (r < 0)
return r;
}
@ -372,7 +803,7 @@ int AsPreprocesser::ProcessScriptSection(const QByteArray &script, int length,
return 0;
}
int AsPreprocesser::LoadScriptSection(const QString &filename) {
int AsPreprocesser::loadScriptSection(const QString &filename) {
// Open the script file
QFile f(filename);
@ -384,9 +815,6 @@ int AsPreprocesser::LoadScriptSection(const QString &filename) {
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;
}
@ -396,10 +824,10 @@ int AsPreprocesser::LoadScriptSection(const QString &filename) {
// Process the script section even if it is zero length so that the name is
// registered
return ProcessScriptSection(code, code.length(), filename, 0);
return processScriptSection(code, filename);
}
bool AsPreprocesser::IncludeIfNotAlreadyIncluded(const QString &filename) {
bool AsPreprocesser::includeIfNotAlreadyIncluded(const QString &filename) {
if (includedScripts.contains(filename)) {
// Already included
return false;
@ -410,7 +838,7 @@ bool AsPreprocesser::IncludeIfNotAlreadyIncluded(const QString &filename) {
return true;
}
int AsPreprocesser::SkipStatement(const QByteArray &modifiedScript, int pos) {
int AsPreprocesser::skipStatement(QByteArray &modifiedScript, int pos) {
asUINT len = 0;
// Skip until ; or { whichever comes first
@ -436,41 +864,42 @@ int AsPreprocesser::SkipStatement(const QByteArray &modifiedScript, int pos) {
else if (modifiedScript[pos] == '}')
level--;
}
pos += len;
}
} else
} else {
pos += 1;
}
return pos;
}
int AsPreprocesser::ExcludeCode(QByteArray &modifiedScript, int pos) {
int AsPreprocesser::excludeIfCode(QByteArray &modifiedScript, 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] = ' ';
auto sharpPos = 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(modifiedScript, pos, len);
QString token = modifiedScript.sliced(pos, len);
if (token == "if") {
if (token == "if" || token == "ifdef" || token == "ifndef") {
nested++;
} else if (token == "endif") {
} else if (token == "else") {
if (nested-- == 0) {
pos += len;
pos = sharpPos;
break;
}
}
modifiedScript[sharpPos] = ' ';
overwriteCode(modifiedScript, pos, len);
} else if (modifiedScript[pos] != '\n') {
OverwriteCode(modifiedScript, pos, len);
overwriteCode(modifiedScript, pos, len);
}
pos += len;
}
@ -478,7 +907,41 @@ int AsPreprocesser::ExcludeCode(QByteArray &modifiedScript, int pos) {
return pos;
}
void AsPreprocesser::OverwriteCode(QByteArray &modifiedScript, int start,
int AsPreprocesser::excludeElseCode(QByteArray &modifiedScript, 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] == '#') {
auto sharpPos = pos;
pos++;
// Is it an #if or #endif directive?
engine->ParseToken(modifiedScript.data() + pos,
modifiedScript.size() - pos, &len);
QString token = modifiedScript.sliced(pos, len);
if (token == "if" || token == "ifdef" || token == "ifndef") {
nested++;
} else if (token == "endif") {
if (nested-- == 0) {
pos = sharpPos;
break;
}
}
modifiedScript[sharpPos] = ' ';
overwriteCode(modifiedScript, pos, len);
} else if (modifiedScript[pos] != '\n') {
overwriteCode(modifiedScript, pos, len);
}
pos += len;
}
return pos;
}
void AsPreprocesser::overwriteCode(QByteArray &modifiedScript, int start,
int len) {
auto code = modifiedScript.data() + start;
for (int n = 0; n < len; n++) {
@ -488,11 +951,91 @@ void AsPreprocesser::OverwriteCode(QByteArray &modifiedScript, int start,
}
}
void AsPreprocesser::AddScriptSection(const QString &section,
const QByteArray &code, int lineOffset) {
int AsPreprocesser::getLineCount(const QByteArray &modifiedScript,
int pos) const {
pos = qBound(0, pos, int(modifiedScript.size()));
return std::count_if(modifiedScript.begin(),
std::next(modifiedScript.begin(), pos),
[](char ch) -> bool { return ch == '\n'; }) +
1;
}
bool AsPreprocesser::endLinePassFailed(const QByteArray &modifiedScript,
int pos) { // ensure '\n' end line
bool endError = false;
asUINT len = 0;
auto t = engine->ParseToken(modifiedScript.data() + pos,
modifiedScript.size() - pos, &len);
if (t == asTC_WHITESPACE) {
if (!std::any_of(modifiedScript.data() + pos,
modifiedScript.data() + pos + len,
[](char ch) { return ch == '\n'; })) {
endError = true;
}
} else {
if (len != 0) {
endError = true;
}
}
return endError;
}
QByteArray
AsPreprocesser::processIfExpression(const QByteArray &codes, int currentLine,
const QByteArray &currentSection) {
QByteArray ret;
int pos = 0;
auto total = codes.length();
while (pos < total) {
asUINT len = 0;
auto t =
engine->ParseToken(codes.data() + pos, codes.size() - pos, &len);
auto word = codes.sliced(pos, len);
if (t == asTC_IDENTIFIER) {
if (word == "__LINE__") {
ret.append(currentLine);
} else if (word == "__SECTION__") {
auto s = currentSection;
s.prepend('"').append('"');
ret.append(s);
} else {
ret.append(findReplaceResult(word));
}
} else {
ret.append(word);
}
pos += len;
}
return ret;
}
QByteArray AsPreprocesser::findReplaceResult(const QByteArray &v) {
QByteArray r = v;
while (definedWords.contains(r)) {
r = definedWords.value(r);
if (r.isEmpty()) {
break;
}
}
return r;
}
bool AsPreprocesser::isCodeCompleteMode() const { return _isCodeCompleteMode; }
void AsPreprocesser::setIsCodeCompleteMode(bool newIsCodeCompleteMode) {
_isCodeCompleteMode = newIsCodeCompleteMode;
}
QHash<QString, QByteArray> AsPreprocesser::definedMacros() const {
return definedWords;
}
void AsPreprocesser::addScriptSection(const QString &section,
const QByteArray &code) {
ScriptData data;
data.section = section;
data.lineOffset = lineOffset;
data.script = code;
modifiedScripts.append(data);
}

View File

@ -35,6 +35,7 @@
#pragma warning(disable : 4786)
#endif
#include <QEventLoop>
#include <QMap>
#include <QSet>
#include <QVector>
@ -62,6 +63,16 @@ typedef int (*PRAGMACALLBACK_t)(const QByteArray &pragmaText,
// Helper class for loading and pre-processing script files to
// support include directives declarations
/** for macros, we support:
* * #if <conditions>
* * #else
* * #endif
* * #define <word>
* * #define <word> <string|int64|double>
* * #undef <word>
* * #ifdef <word>
* * #ifndef <word>
*/
class AsPreprocesser {
public:
explicit AsPreprocesser(asIScriptEngine *engine);
@ -70,7 +81,6 @@ public:
public:
struct ScriptData {
QString section;
int lineOffset = -1;
QByteArray script;
};
@ -79,41 +89,59 @@ public:
// Returns 1 if the file was included
// 0 if the file had already been included before
// <0 on error
int AddSectionFromFile(const QString &filename);
int loadSectionFromFile(const QString &filename);
int loadSectionFromMemory(const QString &section, const QByteArray &code);
QList<ScriptData> GetScriptData() const;
void addScriptSection(const QString &section, const QByteArray &code);
QList<ScriptData> scriptData() const;
// Returns the engine
asIScriptEngine *GetEngine();
asIScriptEngine *getEngine();
// Register the callback for resolving include directive
void SetIncludeCallback(INCLUDECALLBACK_t callback, void *userParam);
void setIncludeCallback(INCLUDECALLBACK_t callback, void *userParam);
// Register the callback for resolving pragma directive
void SetPragmaCallback(PRAGMACALLBACK_t callback, void *userParam);
void setPragmaCallback(PRAGMACALLBACK_t callback, void *userParam);
// Add a pre-processor define for conditional compilation
void DefineWord(const QString &word);
bool defineWord(const QString &word, const QByteArray &value = {});
// Enumerate included script sections
unsigned int GetSectionCount() const;
unsigned int sectionCount() const;
QString GetSectionName(unsigned int idx) const;
QString sectionName(unsigned int idx) const;
bool isCodeCompleteMode() const;
void setIsCodeCompleteMode(bool newIsCodeCompleteMode);
QHash<QString, QByteArray> definedMacros() const;
protected:
void ClearAll();
int ProcessScriptSection(const QByteArray &script, int length,
const QString &sectionname, int lineOffset);
int LoadScriptSection(const QString &filename);
bool IncludeIfNotAlreadyIncluded(const QString &filename);
QString translate(const char *str);
int SkipStatement(const QByteArray &modifiedScript, int pos);
protected:
void clearAll();
int ExcludeCode(QByteArray &modifiedScript, int pos);
void OverwriteCode(QByteArray &modifiedScript, int start, int len);
int processScriptSection(const QByteArray &script,
const QString &sectionname);
int loadScriptSection(const QString &filename);
bool includeIfNotAlreadyIncluded(const QString &filename);
void AddScriptSection(const QString &section, const QByteArray &code,
int lineOffset);
int skipStatement(QByteArray &modifiedScript, int pos);
int excludeIfCode(QByteArray &modifiedScript, int pos);
int excludeElseCode(QByteArray &modifiedScript, int pos);
void overwriteCode(QByteArray &modifiedScript, int start, int len);
int getLineCount(const QByteArray &modifiedScript, int pos) const;
bool endLinePassFailed(const QByteArray &modifiedScript, int pos);
QByteArray processIfExpression(const QByteArray &codes, int currentLine,
const QByteArray &currentSection);
QByteArray findReplaceResult(const QByteArray &v);
asIScriptEngine *engine;
QList<ScriptData> modifiedScripts;
@ -126,7 +154,10 @@ protected:
QStringList includedScripts;
QStringList definedWords;
QHash<QString, QByteArray> definedWords;
private:
bool _isCodeCompleteMode = false;
};
#endif // ASPREPROCESSER_H

View File

@ -133,7 +133,7 @@ QString ClangFormatManager::formatCode(const QString &codes, bool &ok) {
.arg(m_clangStyle)
.arg(m_identWidth);
auto ret = runClangFormat(
{style, QStringLiteral("--assume-filename=wing.cpp")}, codes, ok);
{style, QStringLiteral("--assume-filename=wing.cs")}, codes, ok);
return ret;
} else {
return runClangFormat({}, codes, ok);

View File

@ -1,84 +0,0 @@
/*==============================================================================
** Copyright (C) 2024-2027 WingSummer
**
** This program is free software: you can redistribute it and/or modify it under
** the terms of the GNU Affero General Public License as published by the Free
** Software Foundation, version 3.
**
** This program is distributed in the hope that it will be useful, but WITHOUT
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
** FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
** details.
**
** You should have received a copy of the GNU Affero General Public License
** along with this program. If not, see <https://www.gnu.org/licenses/>.
** =============================================================================
*/
#ifndef CLICKCALLBACK_H
#define CLICKCALLBACK_H
#include "WingPlugin/iwingplugin.h"
#include "angelscript.h"
class ClickCallBack {
public:
ClickCallBack() {}
ClickCallBack(const WingHex::ClickedCallBack &b) : _call(b) {}
explicit ClickCallBack(asIScriptEngine *engine, asIScriptFunction *func)
: _engine(engine), _func(func) {
if (_engine && _func) {
_func->AddRef();
}
}
~ClickCallBack() {
if (_func) {
_func->Release();
}
}
public:
ClickCallBack &operator=(const WingHex::ClickedCallBack &_Right) {
*this = ClickCallBack(_Right);
return *this;
}
ClickCallBack &operator=(WingHex::ClickedCallBack &&_Right) {
*this = ClickCallBack(_Right);
return *this;
}
public:
explicit operator bool() const noexcept {
return _call || (_engine && _func);
}
void operator()(const QModelIndex &index) {
if (_call) {
_call(index);
} else {
if (_engine && _func) {
auto ctx = _engine->CreateContext();
if (ctx) {
auto r = ctx->Prepare(_func);
if (r >= 0) {
auto idx = index;
ctx->SetArgObject(0, &idx);
ctx->Execute();
}
ctx->Release();
}
}
}
}
private:
WingHex::ClickedCallBack _call;
asIScriptEngine *_engine = nullptr;
asIScriptFunction *_func = nullptr;
};
#endif // CLICKCALLBACK_H

View File

@ -36,6 +36,7 @@ QIcon CodeInfoTip::getDisplayIcon(Type type, CodeInfoVisibility vis) {
case Type::Group:
return icon(ICON_NAMESPACE);
case Type::Class:
case Type::TypeDef:
return icon(ICON_CLASS);
case Type::ClsFunction:
case Type::Function:

View File

@ -36,6 +36,7 @@ public:
Variable,
Property = Variable,
Enumerater,
TypeDef
};
enum CacheIndex {
@ -78,6 +79,7 @@ public:
public:
QString name;
bool dontAddGlobal = false;
Type type = Type::Unknown;
QString nameSpace;
CodeInfoVisibility visualpo = CodeInfoVisibility::VISIBILITY_DEFAULT;

View File

@ -151,6 +151,9 @@ void CTypeParser::initialize() {
ADD_TYPE(longlong, QMetaType::LongLong);
ADD_TYPE_U(ulonglong, QMetaType::ULongLong);
using uint = unsigned int;
ADD_TYPE_U(uint, QMetaType::UInt);
using BOOL = bool;
using BYTE = byte;
using WORD = uint16;
@ -242,6 +245,8 @@ void CTypeParser::initialize() {
#undef ADD_TYPE
#undef ADD_TYPE_S
base_types_ = type_maps_.keys();
}
void CTypeParser::setIncludePaths(const QStringList &paths) {
@ -451,7 +456,7 @@ void CTypeParser::stripComments(QStringList &lines) const {
QStringLiteral("]");
// search comment start
while ((pos = line.indexOf(kSlash, pos)) != -1) {
while ((pos = line.indexOf(char(kSlash), pos)) != -1) {
if (line.length() <= pos + 1)
break; // the 1st '/' is at the end of line, so not a comment
@ -627,7 +632,7 @@ TokenTypes CTypeParser::getTokenType(const QString &token) const {
return keywords_.value(token);
} else if (qualifiers_.contains(token)) {
return kQualifier;
} else if (type_maps_.contains(token)) {
} else if (base_types_.contains(token)) {
return kBasicDataType;
} else if (struct_defs_.contains(token)) {
return kStructName;
@ -1065,7 +1070,7 @@ bool CTypeParser::parseDeclaration(const QString &line,
if (line.isEmpty()) {
return false;
}
if (line[line.length() - 1] != kSemicolon)
if (line[line.length() - 1] != char(kSemicolon))
return false;
QStringList tokens;
@ -1085,7 +1090,7 @@ bool CTypeParser::parseDeclaration(const QString &line,
return false;
}
if (tokens[++index].at(0) == kAsterisk) {
if (tokens[++index].at(0) == char(kAsterisk)) {
decl.is_pointer = true;
// size of a pointer is 4 types on a 32-bit system
length =
@ -1143,14 +1148,14 @@ bool CTypeParser::parseEnumDeclaration(const QString &line, int &last_value,
break;
case 2:
if (kComma != tokens[1].at(0)) {
if (char(kComma) != tokens[1].at(0)) {
return false;
}
decl.second = ++last_value;
break;
case 3:
if (kEqual != tokens[1].at(0)) {
if (char(kEqual) != tokens[1].at(0)) {
return false;
}
@ -1166,7 +1171,8 @@ bool CTypeParser::parseEnumDeclaration(const QString &line, int &last_value,
break;
case 4:
if (!(kEqual == tokens[1].at(0) && kComma == tokens[3].at(0))) {
if (!(char(kEqual) == tokens[1].at(0) &&
char(kComma) == tokens[3].at(0))) {
return false;
}
@ -1203,8 +1209,8 @@ bool CTypeParser::parseAssignExpression(const QString &line) {
// only 4 tokens for an assignment expression: var = number;
if (4 == splitLineIntoTokens(line, tokens) &&
kEqual == tokens[1].at(tokens[1].length() - 1) &&
kSemicolon == tokens[3].at(tokens[3].length() - 1) &&
char(kEqual) == tokens[1].at(tokens[1].length() - 1) &&
char(kSemicolon) == tokens[3].at(tokens[3].length() - 1) &&
isNumericToken(tokens[2], number)) {
const_defs_.insert(tokens[0], number);
return true;
@ -1239,7 +1245,7 @@ bool CTypeParser::parsePreProcDirective(const QString &src, qsizetype &pos) {
}
// only handle header file included with ""
if (kQuotation == token[token.length() - 1]) {
if (char(kQuotation) == token[token.length() - 1]) {
// get included header file name
if (!getNextToken(src, pos, token, false)) {
return false;
@ -1366,7 +1372,7 @@ bool CTypeParser::parseStructUnion(const bool is_struct, const bool is_typedef,
if (is_typedef) {
// format 1
if (!(getNextToken(src, pos, next_token) &&
kSemicolon == next_token.at(0))) {
char(kSemicolon) == next_token.at(0))) {
return false;
}
@ -1387,7 +1393,7 @@ bool CTypeParser::parseStructUnion(const bool is_struct, const bool is_typedef,
type_maps_[type_name] = type_maps_[type_alias];
}
} else { // non-typedef
if (kSemicolon == token.at(0)) {
if (char(kSemicolon) == token.at(0)) {
// format 2
is_decl = false;
if (type_name.isEmpty()) {
@ -1542,7 +1548,7 @@ bool CTypeParser::parseEnum(const bool is_typedef, const QString &src,
if (is_typedef) {
// format 1
if (!(getNextToken(src, pos, next_token) &&
kSemicolon == next_token.at(0))) {
char(kSemicolon) == next_token.at(0))) {
return false;
}
@ -1559,7 +1565,7 @@ bool CTypeParser::parseEnum(const bool is_typedef, const QString &src,
;
}
} else { // non-typedef
if (kSemicolon == token.at(0)) {
if (char(kSemicolon) == token.at(0)) {
// format 2
is_decl = false;
if (type_name.isEmpty()) {

View File

@ -211,6 +211,9 @@ private:
/// @note All enum types have fixed size, so they're not stored
QHash<QString, QPair<QMetaType::Type, qsizetype>> type_maps_;
/// basic types
QStringList base_types_;
/// unsigned types
QStringList unsigned_types_;

View File

@ -1,6 +1,8 @@
#include "inspectqtloghelper.h"
#include "utilities.h"
#include <QMenu>
#include <iostream>
#define INFOLOG(msg) "<font color=\"green\">" + msg + "</font>"

View File

@ -17,10 +17,10 @@
#include "pluginsystem.h"
#include "AngelScript/sdk/angelscript/source/as_builder.h"
#include "QJsonModel/include/QJsonModel.hpp"
#include "Qt-Advanced-Docking-System/src/DockAreaWidget.h"
#include "class/languagemanager.h"
#include "class/logger.h"
#include "class/scriptmachine.h"
#include "class/settingmanager.h"
#include "class/skinmanager.h"
#include "class/wingcstruct.h"
@ -32,10 +32,11 @@
#include "dialog/colorpickerdialog.h"
#include "dialog/framelessdialogbase.h"
#include "dialog/mainwindow.h"
#include "model/qjsontablemodel.h"
#include <QDir>
#include <QFileInfoList>
#include <QJsonArray>
#include <QJsonDocument>
#include <QMessageBox>
#include <QPluginLoader>
#include <QScopeGuard>
@ -1786,99 +1787,6 @@ bool PluginSystem::closeAllFiles(QObject *sender) {
return true;
}
bool PluginSystem::dataVisualText(QObject *sender, const QString &data,
const QString &title) {
auto plg = checkPluginAndReport(sender, __func__);
if (plg == nullptr || !checkThreadAff()) {
return false;
}
_win->m_infotxt->setProperty("__TITLE__", title);
_win->m_infotxt->setText(data);
return true;
}
bool PluginSystem::dataVisualTextList(QObject *sender, const QStringList &data,
const QString &title,
ClickedCallBack clicked,
ClickedCallBack dblClicked) {
auto plg = checkPluginAndReport(sender, __func__);
if (plg == nullptr || !checkThreadAff()) {
return false;
}
return updateTextList_API(data, title, clicked, dblClicked);
}
bool PluginSystem::dataVisualTextTree(QObject *sender, const QString &json,
const QString &title,
ClickedCallBack clicked,
ClickedCallBack dblClicked) {
auto plg = checkPluginAndReport(sender, __func__);
if (plg == nullptr || !checkThreadAff()) {
return false;
}
return updateTextTree_API(json, title, clicked, dblClicked);
}
bool PluginSystem::dataVisualTextTable(QObject *sender, const QString &json,
const QStringList &headers,
const QStringList &headerNames,
const QString &title,
ClickedCallBack clicked,
ClickedCallBack dblClicked) {
auto plg = checkPluginAndReport(sender, __func__);
if (plg == nullptr || !checkThreadAff()) {
return false;
}
return updateTextTable_API(json, headers, headerNames, title, clicked,
dblClicked);
}
bool PluginSystem::dataVisualTextListByModel(QObject *sender,
QAbstractItemModel *model,
const QString &title,
ClickedCallBack clicked,
ClickedCallBack dblClicked) {
auto plg = checkPluginAndReport(sender, __func__);
if (plg == nullptr || !checkThreadAff()) {
return false;
}
if (model) {
auto oldmodel = _win->m_infolist->model();
if (oldmodel) {
oldmodel->deleteLater();
}
_win->m_infolist->setProperty("__TITLE__", title);
_win->m_infolist->setModel(model);
_win->m_infoclickfn = clicked;
_win->m_infodblclickfn = dblClicked;
return true;
}
return false;
}
bool PluginSystem::dataVisualTextTreeByModel(QObject *sender,
QAbstractItemModel *model,
const QString &title,
ClickedCallBack clicked,
ClickedCallBack dblClicked) {
auto plg = checkPluginAndReport(sender, __func__);
if (plg == nullptr || !checkThreadAff()) {
return false;
}
if (model) {
auto oldmodel = _win->m_infotree->model();
if (oldmodel) {
oldmodel->deleteLater();
}
_win->m_infotree->setProperty("__TITLE__", title);
_win->m_infotree->setModel(model);
_win->m_infotreeclickfn = clicked;
_win->m_infotreedblclickfn = dblClicked;
return true;
}
return false;
}
bool PluginSystem::checkErrAllAllowAndReport(QObject *sender,
const char *func) {
auto p = qobject_cast<WingHex::IWingPlugin *>(sender);
@ -1903,29 +1811,6 @@ IWingPlugin *PluginSystem::checkPluginAndReport(QObject *sender,
return p;
}
bool PluginSystem::dataVisualTextTableByModel(QObject *sender,
QAbstractItemModel *model,
const QString &title,
ClickedCallBack clicked,
ClickedCallBack dblClicked) {
auto plg = checkPluginAndReport(sender, __func__);
if (plg == nullptr || !checkThreadAff()) {
return false;
}
if (model) {
auto oldmodel = _win->m_infotable->model();
if (oldmodel) {
oldmodel->deleteLater();
}
_win->m_infotable->setProperty("__TITLE__", title);
_win->m_infotable->setModel(model);
_win->m_infotableclickfn = clicked;
_win->m_infotabledblclickfn = dblClicked;
return true;
}
return false;
}
ErrFile PluginSystem::saveAsCurrent(QObject *sender, const QString &savename) {
auto plg = checkPluginAndReport(sender, __func__);
if (plg == nullptr) {
@ -2955,6 +2840,10 @@ void PluginSystem::checkDirRootSafe(const QDir &dir) {
}
void PluginSystem::registerFns(IWingPlugin *plg) {
if (_angelplg == nullptr) {
return;
}
Q_ASSERT(plg);
auto fns = plg->registeredScriptFns();
if (fns.isEmpty()) {
@ -3027,6 +2916,10 @@ void PluginSystem::registerFns(IWingPlugin *plg) {
}
void PluginSystem::registerUnSafeFns(IWingPlugin *plg) {
if (_angelplg == nullptr) {
return;
}
Q_ASSERT(plg);
auto fns = plg->registeredScriptUnsafeFns();
@ -3233,8 +3126,8 @@ void PluginSystem::applyFunctionTables(IWingPluginBase *plg,
plg->setProperty("__CALL_POINTER__", quintptr(this));
}
QString PluginSystem::type2AngelScriptString(IWingPlugin::MetaType type,
bool isArg, bool noModifier) {
QString PluginSystem::type2AngelScriptString(uint type, bool isArg,
bool noModifier) {
auto isArray = !!(type & WingHex::IWingPlugin::Array);
auto isList = !!(type & WingHex::IWingPlugin::List);
auto isContainer = isArray || isList;
@ -3715,77 +3608,6 @@ void PluginSystem::registerMarcoDevice(IWingDevice *plg) {
_scriptMarcos.append(sep + id + sep);
}
bool PluginSystem::updateTextList_API(const QStringList &data,
const QString &title,
const ClickCallBack &click,
const ClickCallBack &dblclick) {
auto oldmodel = _win->m_infolist->model();
if (oldmodel) {
oldmodel->deleteLater();
}
_win->m_infolist->setProperty("__TITLE__", title);
auto model = new QStringListModel(data);
_win->m_infolist->setModel(model);
_win->m_infoclickfn = click;
_win->m_infodblclickfn = dblclick;
return true;
}
bool PluginSystem::updateTextTree_API(const QString &json, const QString &title,
const ClickCallBack &click,
const ClickCallBack &dblclick) {
auto oldmodel = _win->m_infotree->model();
if (oldmodel) {
oldmodel->deleteLater();
}
_win->m_infotree->setProperty("__TITLE__", title);
auto model = new QJsonModel;
if (model->loadJson(json.toUtf8())) {
_win->m_infotree->setModel(model);
_win->m_infotreeclickfn = click;
_win->m_infotreedblclickfn = dblclick;
return true;
}
return false;
}
bool PluginSystem::updateTextTable_API(const QString &json,
const QStringList &headers,
const QStringList &headerNames,
const QString &title,
const ClickCallBack &click,
const ClickCallBack &dblclick) {
auto oldmodel = _win->m_infotable->model();
if (oldmodel) {
oldmodel->deleteLater();
}
QJsonTableModel::Header header;
if (headers.size() > headerNames.size()) {
for (auto &name : headers) {
QJsonTableModel::Heading heading;
heading["index"] = name;
heading["title"] = name;
header.append(heading);
}
} else {
auto np = headerNames.cbegin();
for (auto p = headers.cbegin(); p != headers.cend(); ++p, ++np) {
QJsonTableModel::Heading heading;
heading["index"] = *p;
heading["title"] = *np;
header.append(heading);
}
}
_win->m_infotable->setProperty("__TITLE__", title);
auto model = new QJsonTableModel(header);
model->setJson(QJsonDocument::fromJson(json.toUtf8()));
_win->m_infotable->setModel(model);
_win->m_infotableclickfn = click;
_win->m_infotabledblclickfn = dblclick;
return true;
}
bool PluginSystem::checkThreadAff() {
if (QThread::currentThread() != qApp->thread()) {
Logger::warning(tr("Not allowed operation in non-UI thread"));
@ -3886,6 +3708,8 @@ void PluginSystem::loadAllPlugin() {
}
}
Logger::newLine();
// loading finished, delete the checking engine
finalizeCheckingEngine();
}

View File

@ -32,7 +32,6 @@
#include <QVariant>
#include "WingPlugin/iwingdevice.h"
#include "class/clickcallback.h"
#include "class/wingangelapi.h"
#include "control/editorview.h"
@ -176,8 +175,8 @@ public:
static QString getPUID(IWingPluginBase *p);
static QString type2AngelScriptString(IWingPlugin::MetaType type,
bool isArg, bool noModifier = false);
static QString type2AngelScriptString(uint type, bool isArg,
bool noModifier = false);
private:
void loadExtPlugin();
@ -258,18 +257,6 @@ private:
void registerPluginDockWidgets(IWingPluginBase *p);
void registerPluginPages(IWingPluginBase *p);
public:
bool updateTextList_API(const QStringList &data, const QString &title,
const ClickCallBack &click,
const ClickCallBack &dblclick);
bool updateTextTree_API(const QString &json, const QString &title,
const ClickCallBack &click,
const ClickCallBack &dblclick);
bool updateTextTable_API(const QString &json, const QStringList &headers,
const QStringList &headerNames,
const QString &title, const ClickCallBack &click,
const ClickCallBack &dblclick);
public:
// fpr crash checking
QString currentLoadingPlugin() const;
@ -735,41 +722,6 @@ public:
// extension
WING_SERVICE bool closeAllFiles(QObject *sender);
public:
WING_SERVICE bool dataVisualText(QObject *sender, const QString &data,
const QString &title);
WING_SERVICE bool dataVisualTextList(QObject *sender,
const QStringList &data,
const QString &title,
WingHex::ClickedCallBack clicked,
WingHex::ClickedCallBack dblClicked);
WING_SERVICE bool dataVisualTextTree(QObject *sender, const QString &json,
const QString &title,
WingHex::ClickedCallBack clicked,
WingHex::ClickedCallBack dblClicked);
WING_SERVICE bool dataVisualTextTable(QObject *sender, const QString &json,
const QStringList &headers,
const QStringList &headerNames,
const QString &title,
WingHex::ClickedCallBack clicked,
WingHex::ClickedCallBack dblClicked);
// API for Qt Plugin Only
WING_SERVICE bool dataVisualTextListByModel(
QObject *sender, QAbstractItemModel *model, const QString &title,
WingHex::ClickedCallBack clicked, WingHex::ClickedCallBack dblClicked);
WING_SERVICE bool dataVisualTextTableByModel(
QObject *sender, QAbstractItemModel *model, const QString &title,
WingHex::ClickedCallBack clicked, WingHex::ClickedCallBack dblClicked);
WING_SERVICE bool dataVisualTextTreeByModel(
QObject *sender, QAbstractItemModel *model, const QString &title,
WingHex::ClickedCallBack clicked, WingHex::ClickedCallBack dblClicked);
private:
WingHex::IWingPlugin *checkPluginAndReport(QObject *sender,
const char *func);

File diff suppressed because it is too large Load Diff

View File

@ -24,11 +24,10 @@
#include <QMap>
#include <QString>
// This class is the modification of as_parser.
// This class comes from as_parser.h .
// You can modified it to support more features.
/** It's a complex thing to fully support AngelScript code intellisense.
** I just support basic code completion like local or global
* variables/functions.
** I just support basic code completion.
** If you are interested in implement a well-featured intellisense like
* Qt creator or Visual Studio, PRs are welcomed !!!
*/
@ -42,195 +41,192 @@ public:
public:
enum class SymbolType {
Invalid,
FnDecl,
Import,
Value, // a common value
Variable, // a variable
Variable, // variable or property in class
Enum, // an enum
Class, // a class type
Function, // a function
TypeDef, // a typedef
FnDef, // a funcdef
Property, // a property
Interface, // an interface
Import, // import but not supported
};
enum class Visiblity { Public, Private, Protected };
struct Symbol;
/**
* @brief The CodeSegment class
*/
struct CodeSegment {
bool valid = false;
// QByteArrayList ns;
QByteArray ret;
QByteArrayList scope;
QByteArray name;
qsizetype nameInSrc = -1;
QList<Symbol> args;
QByteArray code;
SymbolType type = QAsCodeParser::SymbolType::Invalid;
qsizetype offset = -1;
QByteArray codes;
bool isValid() const { return valid; }
QByteArrayList additonalInfos; // for other additonal infos
public:
inline bool isValid() const {
return offset >= 0 && type != SymbolType::Invalid;
}
inline qsizetype end() const {
if (offset < 0) {
return -1;
}
return offset + codes.length();
}
inline qsizetype length() const { return codes.length(); }
inline bool hasCodes() const { return !codes.isEmpty(); }
};
struct Symbol {
SymbolType type = SymbolType::Invalid;
QString name;
qsizetype nameInSrc = -1;
QString typeStr;
Visiblity vis = Visiblity::Public;
QList<Symbol> content;
SymbolType symtype = QAsCodeParser::SymbolType::Invalid;
QByteArrayList scope;
QByteArray name;
QByteArray type;
int offset = -1;
Visiblity vis = QAsCodeParser::Visiblity::Public;
QByteArray additonalInfo; // for other additonal info
// QByteArrayList ns; // namespaces
QMap<qsizetype, CodeSegment> codesegs; // used in class
// size_t scope = 0; // 0 for all
bool isValid() const { return type != SymbolType::Invalid; }
QByteArrayList inherit;
QList<Symbol> children;
};
using SymbolTable = QMap<size_t, Symbol>;
public:
// First, we should parse and split the code into segments
QList<QAsCodeParser::CodeSegment> parse(const QByteArray &codes);
private:
// QMap< offset , CodeSegment>
QMap<qsizetype, CodeSegment> m_segs; // global functions
// Then, we can deep parsing for code completion
QList<Symbol> parseIntell(qsizetype offset,
const QList<QAsCodeParser::CodeSegment> &segs);
// so, a helper function?
QList<Symbol> parseAndIntell(qsizetype offset, const QByteArray &codes);
public:
SymbolTable parse(const QByteArray &codes);
SymbolTable parseIntell(qsizetype offset, const QByteArray &codes);
static eTokenType getToken(asIScriptEngine *engine, const char *string,
size_t stringLength, size_t *tokenLength);
private:
void ParseScript(bool inBlock);
QList<QAsCodeParser::CodeSegment> parseScript(bool inBlock);
void skipCodeBlock();
QByteArray getSymbolString(const sToken &t);
QByteArrayList getRealNamespace(const QByteArrayList &ns);
// parse tokens
sToken ParseIdentifier();
sToken ParseToken(int token);
sToken ParseRealType();
sToken ParseDataType(bool allowVariableType = false,
bool allowAuto = false);
sToken ParseOneOf(int *tokens, int count);
sToken ParseType(bool allowConst, bool allowVariableType = false,
bool allowAuto = false);
// Statements
sToken SuperficiallyParseVarInit();
void superficiallyParseVarInit();
sToken superficiallyParseStatementBlock();
void superficiallyParseExpression();
void superficiallyParseTemplateList();
// parse and get symbols
Symbol ParseImport();
void ParseEnumeration();
void ParseTypedef();
void ParseClass();
void ParseMixin();
void ParseInterface();
Symbol ParseFuncDef();
void ParseNamespace();
void ParseReturn();
void ParseBreak();
void ParseContinue();
void ParseTryCatch();
void ParseIf();
void ParseLambda();
void ParseStatement();
CodeSegment ParseFunction(bool isMethod = false);
void ParseExpressionStatement();
void ParseListPattern();
void ParseStatementBlock();
void ParseAssignment();
void ParseAssignOperator();
void ParseSwitch();
void ParseCase();
void ParseFor();
void ParseWhile();
void ParseDoWhile();
void ParseCondition();
void ParseExpression();
void ParseExprTerm();
void ParseExprOperator();
void ParseExprPreOp();
void ParseExprPostOp();
void ParseExprValue();
QByteArrayList ParseOptionalScope();
Symbol ParseVirtualPropertyDecl(bool isMethod, bool isInterface);
QList<Symbol> ParseParameterList();
// parse but not get symbols
void ParseTypeMod(bool isParam);
void ParseFunctionCall();
void SuperficiallyParseStatementBlock();
void ParseInitList();
void ParseCast();
void ParseVariableAccess();
void ParseConstructCall();
void ParseConstant();
void parseMethodAttributes();
private:
void Reset();
bool IsVarDecl();
bool IsVirtualPropertyDecl();
bool IsFuncDecl(bool isMethod);
bool IsLambda();
bool IsFunctionCall();
void GetToken(sToken *token);
void RewindTo(const sToken *token);
void RewindErrorTo(sToken *token);
void SetPos(size_t pos);
bool IsRealType(int tokenType);
bool IdentifierIs(const sToken &t, const char *str);
bool IsDataType(const sToken &token);
CodeSegment parseEnumeration();
CodeSegment parseTypedef();
CodeSegment parseClass();
CodeSegment parseMixin();
CodeSegment parseInterface();
CodeSegment parseFuncDef();
CodeSegment parseFunction();
CodeSegment parseFunctionMethod(Visiblity &vis);
private:
bool CheckTemplateType(const sToken &t);
bool ParseTemplTypeList(bool required = true);
void ParseMethodAttributes();
// parse tokens
sToken parseIdentifier();
sToken parseToken(int token);
sToken parseRealType();
sToken parseDataType(bool allowVariableType = false,
bool allowAuto = false);
sToken parseOneOf(int *tokens, int count);
void ParseArgList(bool withParenthesis = true);
void SuperficiallyParseExpression();
void parseNamespace();
private:
bool FindTokenAfterType(sToken &nextToken);
bool FindIdentifierAfterScope(sToken &nextToken);
bool IsConstant(int tokenType);
bool IsOperator(int tokenType);
bool IsPreOperator(int tokenType);
bool IsPostOperator(int tokenType);
bool IsAssignOperator(int tokenType);
// deep parsing
QList<Symbol> parseEnumerationContent(const QByteArrayList &ns,
const QByteArray &name,
const QByteArray &code);
Symbol parseTypedefContent(const QByteArrayList &ns,
const QByteArray &code);
bool DoesTypeExist(const QString &t);
QList<Symbol> parseGlobalVarDecls(const QByteArrayList &ns,
const QByteArray &code);
Symbol ParseFunctionDefinition();
QByteArrayList parseOptionalScope();
QList<Symbol> ParseDeclaration(bool isClassProp = false,
QList<Symbol> parseDeclaration(qsizetype end, const QByteArrayList &ns,
bool isClassProp = false,
bool isGlobalVar = false);
Symbol ParseInterfaceMethod();
QByteArray parseType(bool allowConst, bool allowVariableType = false,
bool allowAuto = false);
void ParseStringConstant();
Symbol parseFuncDefContent(const QByteArrayList &ns,
const QByteArray &code);
Symbol parseFuncDefContent(const QByteArrayList &ns);
QPair<QByteArrayList, QList<Symbol>>
parseClassContent(qsizetype offset, const QByteArrayList &ns,
const QByteArray &code);
QPair<QByteArrayList, QList<Symbol>>
parseInterfaceContent(qsizetype offset, const QByteArrayList &ns,
const QByteArray &code);
QList<Symbol> parseStatementBlock(const QByteArrayList &ns,
const QByteArray &code, qsizetype end);
private:
bool errorWhileParsing;
bool isSyntaxError;
bool checkValidTypes;
bool isParsingAppInterface;
Symbol parseVirtualPropertyDecl(bool isMethod, bool isInterface);
QList<Symbol> parseParameterListContent();
// parse but not get symbols
void parseTypeMod(bool isParam);
private:
void reset();
bool isVarDecl();
bool isVirtualPropertyDecl();
bool isFuncDecl(bool isMethod);
void getToken(sToken *token);
void rewindTo(const sToken *token);
void rewindErrorTo(sToken *token);
void setPos(size_t pos);
bool isPrimType(int tokenType);
bool identifierIs(const sToken &t, const char *str);
bool isDataType(const sToken &token);
private:
bool checkTemplateType(const sToken &t);
private:
bool findTokenAfterType(sToken &nextToken);
bool typeExist(const QString &t);
Symbol parseInterfaceMethod();
private:
bool _errorWhileParsing;
bool _isSyntaxError;
bool _checkValidTypes;
bool _isParsingAppInterface;
asCScriptEngine *engine;
// size_t _curscope = 0;
SymbolTable _symtable;
QByteArray _code;
QByteArray code;
QByteArray tempString; // Used for reduzing amount of dynamic allocations
sToken _lastToken;
size_t _sourcePos;
sToken lastToken;
size_t sourcePos;
QByteArrayList currentNs;
};
#endif // QASCODEPARSER_H

View File

@ -1,157 +0,0 @@
/*==============================================================================
** Copyright (C) 2024-2027 WingSummer
**
** This program is free software: you can redistribute it and/or modify it under
** the terms of the GNU Affero General Public License as published by the Free
** Software Foundation, version 3.
**
** This program is distributed in the hope that it will be useful, but WITHOUT
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
** FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
** details.
**
** You should have received a copy of the GNU Affero General Public License
** along with this program. If not, see <https://www.gnu.org/licenses/>.
** =============================================================================
*/
#include "scriptconsolemachine.h"
#include <QRegularExpression>
#include <QTextStream>
ScriptConsoleMachine::ScriptConsoleMachine(
std::function<QString(void)> &getInputFn, QObject *parent)
: ScriptMachine(getInputFn, parent) {
if (!ScriptConsoleMachine::configureEngine(engine())) {
destoryMachine();
}
if (engine()) {
_model = new ScriptObjModel(engine(), debugger(), this);
}
}
ScriptConsoleMachine::~ScriptConsoleMachine() {}
bool ScriptConsoleMachine::executeCode(const QString &code) {
return execString(engine(), code);
}
ScriptObjModel *ScriptConsoleMachine::model() const { return _model; }
bool ScriptConsoleMachine::configureEngine(asIScriptEngine *engine) {
_clsfn = std::bind(&ScriptConsoleMachine::onClearConsole, this);
auto r = engine->RegisterGlobalFunction(
"void clear()", asMETHOD(decltype(_clsfn), operator()),
asCALL_THISCALL_ASGLOBAL, &_clsfn);
Q_ASSERT(r >= 0);
return r >= 0;
}
bool ScriptConsoleMachine::execString(asIScriptEngine *engine,
const QString &code) {
auto mod = engine->GetModule("Console", asGM_CREATE_IF_NOT_EXISTS);
if (code.startsWith(QStringLiteral("addvar "))) {
auto varcmd = code.mid(7) + QStringLiteral(";");
if (varcmd.length() == 1) {
MessageInfo info;
info.message = tr("EmptyDecl");
emit onOutput(MessageType::Error, info);
return false;
}
auto ret = mod->CompileGlobalVar("addvar", varcmd.toUtf8(), 0) >= 0;
_model->updateData();
return ret;
} else if (code.startsWith(QStringLiteral("rmvar "))) {
if (mod == nullptr || mod->GetGlobalVarCount() == 0) {
return false;
}
// remove the tailing semi-colons
static QRegularExpression re(QStringLiteral(";+$"));
auto varcmd = code.mid(6).remove(re);
for (auto &var : varcmd.split(' ', Qt::SkipEmptyParts)) {
int index = mod->GetGlobalVarIndexByName(var.toUtf8());
if (index >= 0) {
mod->RemoveGlobalVar(index);
}
}
_model->updateData();
return true;
} else if (code.startsWith(QStringLiteral("lsfn"))) {
if (code.trimmed().length() != 4) {
return false;
}
QString str;
QTextStream s(&str);
asUINT n;
// List the application registered functions
s << tr("Application functions:") << Qt::endl;
for (n = 0; n < engine->GetGlobalFunctionCount(); n++) {
asIScriptFunction *func = engine->GetGlobalFunctionByIndex(n);
// Skip the functions that start with _ as these are not meant to be
// called explicitly by the user
if (func->GetName()[0] != '_')
s << QStringLiteral(" ") << func->GetDeclaration() << Qt::endl;
}
// List the user functions in the module
if (mod) {
s << Qt::endl;
s << tr("User functions:") << Qt::endl;
for (n = 0; n < mod->GetFunctionCount(); n++) {
asIScriptFunction *func = mod->GetFunctionByIndex(n);
s << QStringLiteral(" ") << func->GetDeclaration() << Qt::endl;
}
}
MessageInfo info;
info.message = str;
emit onOutput(MessageType::Info, info);
return true;
} else if (code.startsWith(QStringLiteral("lsvar"))) {
if (code.trimmed().length() != 5) {
return false;
}
QString str;
QTextStream s(&str);
asUINT n;
// List the application registered variables
s << tr("Application variables:") << Qt::endl;
for (n = 0; n < engine->GetGlobalPropertyCount(); n++) {
const char *name;
int typeId;
bool isConst;
engine->GetGlobalPropertyByIndex(n, &name, 0, &typeId, &isConst);
auto decl =
isConst ? QStringLiteral(" const ") : QStringLiteral(" ");
decl += engine->GetTypeDeclaration(typeId);
decl += QStringLiteral(" ");
decl += name;
s << decl << Qt::endl;
}
// List the user variables in the module
if (mod) {
s << Qt::endl;
s << tr("User variables:") << Qt::endl;
for (n = 0; n < (asUINT)mod->GetGlobalVarCount(); n++) {
s << QStringLiteral(" ") << mod->GetGlobalVarDeclaration(n)
<< Qt::endl;
}
}
MessageInfo info;
info.message = str;
emit onOutput(MessageType::Info, info);
return true;
} else {
return ScriptMachine::executeCode(code);
}
}

View File

@ -1,50 +0,0 @@
/*==============================================================================
** Copyright (C) 2024-2027 WingSummer
**
** This program is free software: you can redistribute it and/or modify it under
** the terms of the GNU Affero General Public License as published by the Free
** Software Foundation, version 3.
**
** This program is distributed in the hope that it will be useful, but WITHOUT
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
** FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
** details.
**
** You should have received a copy of the GNU Affero General Public License
** along with this program. If not, see <https://www.gnu.org/licenses/>.
** =============================================================================
*/
#ifndef SCRIPTCONSOLEMACHINE_H
#define SCRIPTCONSOLEMACHINE_H
#include "model/scriptobjmodel.h"
#include "scriptmachine.h"
class ScriptConsoleMachine : public ScriptMachine {
Q_OBJECT
public:
explicit ScriptConsoleMachine(std::function<QString()> &getInputFn,
QObject *parent = nullptr);
virtual ~ScriptConsoleMachine();
virtual bool executeCode(const QString &code) override;
ScriptObjModel *model() const;
signals:
void onClearConsole();
protected:
virtual bool configureEngine(asIScriptEngine *engine) override;
private:
bool execString(asIScriptEngine *engine, const QString &code);
private:
ScriptObjModel *_model = nullptr;
std::function<void(void)> _clsfn;
};
#endif // SCRIPTCONSOLEMACHINE_H

File diff suppressed because it is too large Load Diff

View File

@ -18,27 +18,40 @@
#ifndef SCRIPTMACHINE_H
#define SCRIPTMACHINE_H
#include "AngelScript/sdk/add_on/contextmgr/contextmgr.h"
#include "AngelScript/sdk/angelscript/include/angelscript.h"
#include "class/aspreprocesser.h"
#include "asdebugger.h"
#include "class/ascontextmgr.h"
#include <QObject>
#include <QQueue>
class CScriptArray;
class ScriptMachine : public QObject {
Q_OBJECT
private:
using TranslateFunc = std::function<QString(const QStringList &)>;
public:
// we have three console modes
enum ConsoleMode {
Interactive = 1, // in a shell
Scripting, // in scripting dialog
Background, // run codes from other way
};
public:
enum class MessageType { Info, Warn, Error, Print };
struct MessageInfo {
ConsoleMode mode = ConsoleMode::Background;
QString section;
int row = -1;
int col = -1;
asEMsgType type = asEMsgType::asMSGTYPE_INFORMATION;
MessageType type = MessageType::Info;
QString message;
};
@ -78,29 +91,54 @@ public:
Q_ENUM(asEContextState)
public:
explicit ScriptMachine(const std::function<QString()> &getInputFn,
QObject *parent = nullptr);
struct RegCallBacks {
std::function<QString()> getInputFn;
std::function<void()> clearFn;
std::function<void(const ScriptMachine::MessageInfo &)> printMsgFn;
};
virtual ~ScriptMachine();
private:
explicit ScriptMachine();
Q_DISABLE_COPY_MOVE(ScriptMachine)
bool inited();
private:
asIScriptModule *createModule(ConsoleMode mode);
asIScriptModule *createModuleIfNotExist(ConsoleMode mode);
bool isModuleExists(ConsoleMode mode);
bool isRunning() const;
public:
asIScriptModule *module(ConsoleMode mode);
static ScriptMachine &instance();
void destoryMachine();
public:
bool init();
bool isInited() const;
bool isRunning(ConsoleMode mode) const;
static void registerEngineAddon(asIScriptEngine *engine);
static void registerEngineAssert(asIScriptEngine *engine);
static void registerEngineClipboard(asIScriptEngine *engine);
void registerCallBack(ConsoleMode mode, const RegCallBacks &callbacks);
public:
asDebugger *debugger() const;
asIScriptEngine *engine() const;
bool insteadFoundDisabled() const;
void setInsteadFoundDisabled(bool newInsteadFoundDisabled);
static void registerEngineAddon(asIScriptEngine *engine);
static void registerEngineAssert(asIScriptEngine *engine);
void outputMessage(const MessageInfo &info);
public:
static void scriptAssert(bool b);
static void scriptAssert_X(bool b, const QString &msg);
static void clip_setText(const QString &text);
static void clip_setBinary(const CScriptArray &array);
static QString clip_getText();
static CScriptArray *clip_getBinary();
static void scriptThrow(const QString &msg);
static QString scriptGetExceptionInfo();
@ -109,27 +147,28 @@ public:
public:
// debug or release?
bool isDebugMode() const;
void setDebugMode(bool isDbg);
bool isDebugMode(ConsoleMode mode = Scripting);
public slots:
virtual bool executeCode(const QString &code);
virtual bool executeScript(const QString &script, bool isInDebug = false);
bool executeCode(ConsoleMode mode, const QString &code);
// only scripting mode can be debugged
bool executeScript(ConsoleMode mode, const QString &script,
bool isInDebug = false, int *retCode = nullptr);
int evaluateDefine(const QString &code, bool &result);
void abortDbgScript();
void abortScript(ConsoleMode mode);
void abortScript();
private:
bool execute(const QString &code, bool isInDebug = false);
protected:
virtual bool configureEngine(asIScriptEngine *engine);
bool configureEngine();
QString getCallStack(asIScriptContext *context);
void destoryMachine();
private:
void print(void *ref, int typeId);
static void print(asIScriptGeneric *args);
static void println(asIScriptGeneric *args);
QString getInput();
@ -138,6 +177,10 @@ private:
static int execSystemCmd(QString &out, const QString &exe,
const QString &params, int timeout);
static QString beautify(const QString &str, uint indent);
QString stringify(void *ref, int typeId);
private:
static void messageCallback(const asSMessageInfo *msg, void *param);
@ -166,21 +209,19 @@ private:
Q_DECL_UNUSED void translation();
signals:
void onOutput(MessageType type, const MessageInfo &message);
void onDebugFinished();
private:
asIScriptEngine *_engine = nullptr;
asDebugger *_debugger = nullptr;
asContextMgr *_ctxMgr = nullptr;
QVector<asIScriptContext *> _ctxPool;
std::function<void(void *ref, int typeId)> _printFn;
CContextMgr *_ctxMgr = nullptr;
QQueue<asIScriptContext *> _ctxPool;
QVector<asITypeInfo *> _rtypes;
std::function<QString(void)> _getInputFn;
bool m_insteadFoundDisabled = false;
QMap<ConsoleMode, RegCallBacks> _regcalls;
QMap<ConsoleMode, asIScriptContext *> _ctx;
ConsoleMode _curMsgMode = ConsoleMode::Background;
};
Q_DECLARE_METATYPE(ScriptMachine::MessageInfo)

View File

@ -25,6 +25,7 @@
#include <QJsonObject>
#include <QMenu>
#include "scriptmachine.h"
#include "settingmanager.h"
#include "utilities.h"
#include "wingmessagebox.h"
@ -50,7 +51,7 @@ ScriptManager::ScriptManager() : QObject() {
refresh();
}
ScriptManager::~ScriptManager() { detach(); }
ScriptManager::~ScriptManager() {}
QStringList ScriptManager::getScriptFileNames(const QDir &dir) const {
if (!dir.exists()) {
@ -72,6 +73,11 @@ QString ScriptManager::readJsonObjString(const QJsonObject &jobj,
return {};
}
bool ScriptManager::readJsonObjBool(const QJsonObject &jobj,
const QString &key) {
return jobj.value(key).toBool();
}
QMenu *ScriptManager::buildUpScriptDirMenu(QWidget *parent,
const QStringList &files,
bool isSys) {
@ -166,6 +172,7 @@ ScriptManager::ensureDirMeta(const QFileInfo &info) {
jobj.insert(QStringLiteral("license"), QLatin1String());
jobj.insert(QStringLiteral("homepage"), QLatin1String());
jobj.insert(QStringLiteral("comment"), QLatin1String());
jobj.insert(QStringLiteral("hexmenu"), false);
QFile f(base.absoluteFilePath(QStringLiteral(".wingasmeta")));
if (f.open(QFile::WriteOnly | QFile::Text)) {
QJsonDocument jdoc(jobj);
@ -194,6 +201,8 @@ ScriptManager::ensureDirMeta(const QFileInfo &info) {
readJsonObjString(jobj, QStringLiteral("homepage"));
meta.comment =
readJsonObjString(jobj, QStringLiteral("comment"));
meta.isContextMenu =
readJsonObjBool(jobj, QStringLiteral("hexmenu"));
} else {
meta.name = info.fileName();
}
@ -206,14 +215,6 @@ ScriptManager::ensureDirMeta(const QFileInfo &info) {
return meta;
}
void ScriptManager::attach(ScriptingConsole *console) {
if (console) {
_console = console;
}
}
void ScriptManager::detach() { _console = nullptr; }
ScriptManager::ScriptDirMeta
ScriptManager::usrDirMeta(const QString &cat) const {
return _usrDirMetas.value(cat);
@ -224,9 +225,10 @@ ScriptManager::sysDirMeta(const QString &cat) const {
return _sysDirMetas.value(cat);
}
ScriptManager::ScriptActionMaps
ScriptManager::buildUpRibbonScriptRunner(RibbonButtonGroup *group) {
ScriptActionMaps maps;
QList<QMenu *>
ScriptManager::buildUpScriptRunnerContext(RibbonButtonGroup *group,
QWidget *parent) {
QList<QMenu *> maps;
auto &sm = ScriptManager::instance();
auto &stm = SettingManager::instance();
@ -236,10 +238,20 @@ ScriptManager::buildUpRibbonScriptRunner(RibbonButtonGroup *group) {
if (hideCats.contains(cat)) {
continue;
}
maps.sysList << addPannelAction(
group, ICONRES(QStringLiteral("scriptfolder")),
sm.sysDirMeta(cat).name,
auto meta = sm.sysDirMeta(cat);
addPannelAction(
group, ICONRES(QStringLiteral("scriptfolder")), meta.name,
buildUpScriptDirMenu(group, sm.getSysScriptFileNames(cat), true));
if (meta.isContextMenu) {
auto m = buildUpScriptDirMenu(parent, sm.getSysScriptFileNames(cat),
true);
m->setTitle(meta.name);
m->setIcon(ICONRES(QStringLiteral("scriptfolder")));
maps << m;
}
}
hideCats = stm.usrHideCats();
@ -247,23 +259,37 @@ ScriptManager::buildUpRibbonScriptRunner(RibbonButtonGroup *group) {
if (hideCats.contains(cat)) {
continue;
}
maps.usrList << addPannelAction(
group, ICONRES(QStringLiteral("scriptfolderusr")),
sm.usrDirMeta(cat).name,
auto meta = sm.usrDirMeta(cat);
addPannelAction(
group, ICONRES(QStringLiteral("scriptfolderusr")), meta.name,
buildUpScriptDirMenu(group, sm.getUsrScriptFileNames(cat), false));
if (meta.isContextMenu) {
auto m = buildUpScriptDirMenu(parent, sm.getSysScriptFileNames(cat),
true);
m->setTitle(meta.name);
m->setIcon(ICONRES(QStringLiteral("scriptfolderusr")));
maps << m;
}
}
return maps;
}
void ScriptManager::runScript(const QString &filename) {
Q_ASSERT(_console);
_console->setMode(ScriptingConsole::Output);
_console->stdWarn(tr("Excuting:") + filename);
_console->newLine();
_console->machine()->executeScript(filename);
_console->appendCommandPrompt();
_console->setMode(ScriptingConsole::Input);
auto &ins = ScriptMachine::instance();
if (ins.isRunning(ScriptMachine::Background)) {
auto ret = QMessageBox::question(nullptr, tr("ScriptRunning"),
tr("ScriptRunningRequestLastStop?"));
if (ret == QMessageBox::Yes) {
ins.abortScript(ScriptMachine::Background);
} else {
return;
}
}
ins.executeScript(ScriptMachine::Background, filename);
}
QStringList ScriptManager::usrScriptsDbCats() const {

View File

@ -24,7 +24,6 @@
#include <QObject>
#include "QWingRibbon/ribbonbuttongroup.h"
#include "control/scriptingconsole.h"
#include "utilities.h"
class ScriptManager : public QObject {
@ -38,14 +37,10 @@ public:
QString license;
QString homepage;
QString comment;
bool isContextMenu = false;
bool isSys; // a flag
};
struct ScriptActionMaps {
QList<QToolButton *> sysList;
QList<QToolButton *> usrList;
};
public:
static ScriptManager &instance();
@ -65,13 +60,11 @@ public:
void refreshUsrScriptsDbCats();
void refreshSysScriptsDbCats();
void attach(ScriptingConsole *console);
void detach();
ScriptDirMeta usrDirMeta(const QString &cat) const;
ScriptDirMeta sysDirMeta(const QString &cat) const;
static ScriptActionMaps buildUpRibbonScriptRunner(RibbonButtonGroup *group);
static QList<QMenu *> buildUpScriptRunnerContext(RibbonButtonGroup *group,
QWidget *parent);
private:
static QToolButton *addPannelAction(RibbonButtonGroup *pannel,
@ -122,6 +115,8 @@ private:
QString readJsonObjString(const QJsonObject &jobj, const QString &key);
bool readJsonObjBool(const QJsonObject &jobj, const QString &key);
static QMenu *buildUpScriptDirMenu(QWidget *parent,
const QStringList &files, bool isSys);
@ -133,8 +128,6 @@ private:
QHash<QString, ScriptDirMeta> _usrDirMetas;
QHash<QString, ScriptDirMeta> _sysDirMetas;
ScriptingConsole *_console = nullptr;
};
Q_DECLARE_METATYPE(ScriptManager::ScriptDirMeta)

View File

@ -33,12 +33,24 @@
_setUnsavedEditor.setFlag(flag, false); \
}
#define WRITE_CONFIG_EDITOR_RESET(config, flag, dvalue) \
do { \
WRITE_CONFIG(config, dvalue); \
_setUnsavedEditor.setFlag(flag, false); \
} while (0);
#define WRITE_CONFIG_CONSOLE_SET(config, flag, dvalue) \
if (this->_setUnsavedConsole.testFlag(flag)) { \
WRITE_CONFIG(config, dvalue); \
_setUnsavedConsole.setFlag(flag, false); \
}
#define WRITE_CONFIG_CONSOLE_RESET(config, flag, dvalue) \
do { \
WRITE_CONFIG(config, dvalue); \
_setUnsavedConsole.setFlag(flag, false); \
} while (0);
Q_GLOBAL_STATIC_WITH_ARGS(QString, CODEEDIT_FONT, ("codeedit.font"))
Q_GLOBAL_STATIC_WITH_ARGS(QString, CODEEDIT_FONT_SIZE, ("codeedit.fontsize"))
Q_GLOBAL_STATIC_WITH_ARGS(QString, CODEEDIT_THEME, ("codeedit.theme"))
@ -200,56 +212,59 @@ void ScriptSettings::save(SETTINGS cat) {
}
void ScriptSettings::reset(SETTINGS cat) {
__reset(cat);
load();
}
void ScriptSettings::__reset(SETTINGS cat) {
HANDLE_CONFIG;
if (cat.testFlag(SETTING::EDITOR)) {
WRITE_CONFIG_EDITOR_SET(CODEEDIT_FONT, SETTING_ITEM::FONT,
WRITE_CONFIG_EDITOR_RESET(CODEEDIT_FONT, SETTING_ITEM::FONT,
_defaultFont.defaultFamily());
WRITE_CONFIG_EDITOR_SET(CODEEDIT_FONT_SIZE, SETTING_ITEM::FONT_SIZE,
WRITE_CONFIG_EDITOR_RESET(CODEEDIT_FONT_SIZE, SETTING_ITEM::FONT_SIZE,
10);
WRITE_CONFIG_EDITOR_SET(CODEEDIT_THEME, SETTING_ITEM::THEME, {});
WRITE_CONFIG_EDITOR_SET(CODEEDIT_TABS_WIDTH, SETTING_ITEM::TAB_WIDTH,
WRITE_CONFIG_EDITOR_RESET(CODEEDIT_THEME, SETTING_ITEM::THEME, {});
WRITE_CONFIG_EDITOR_RESET(CODEEDIT_TABS_WIDTH, SETTING_ITEM::TAB_WIDTH,
4);
WRITE_CONFIG_EDITOR_SET(CODEEDIT_INDENTATION, SETTING_ITEM::INDENTATION,
0);
WRITE_CONFIG_EDITOR_SET(CODEEDIT_MATCH_BRACES,
WRITE_CONFIG_EDITOR_RESET(CODEEDIT_INDENTATION,
SETTING_ITEM::INDENTATION, 0);
WRITE_CONFIG_EDITOR_RESET(CODEEDIT_MATCH_BRACES,
SETTING_ITEM::MATCH_BRACES, true);
WRITE_CONFIG_EDITOR_SET(CODEEDIT_WORD_WRAP, SETTING_ITEM::WORD_WRAP,
WRITE_CONFIG_EDITOR_RESET(CODEEDIT_WORD_WRAP, SETTING_ITEM::WORD_WRAP,
false);
WRITE_CONFIG_EDITOR_SET(CODEEDIT_SHOW_LINENUMBER,
WRITE_CONFIG_EDITOR_RESET(CODEEDIT_SHOW_LINENUMBER,
SETTING_ITEM::SHOW_LINENUMBER, true);
WRITE_CONFIG_EDITOR_SET(CODEEDIT_SHOW_FOLDING,
WRITE_CONFIG_EDITOR_RESET(CODEEDIT_SHOW_FOLDING,
SETTING_ITEM::SHOW_FOLDING, true);
WRITE_CONFIG_EDITOR_SET(CODEEDIT_SHOW_INDENTGUIDES,
WRITE_CONFIG_EDITOR_RESET(CODEEDIT_SHOW_INDENTGUIDES,
SETTING_ITEM::SHOW_INDENTGUIDES, true);
WRITE_CONFIG_EDITOR_SET(CODEEDIT_SHOW_LONGLINEEDGE,
WRITE_CONFIG_EDITOR_RESET(CODEEDIT_SHOW_LONGLINEEDGE,
SETTING_ITEM::SHOW_LONGLINEEDGE, false);
WRITE_CONFIG_EDITOR_SET(CODEEDIT_SHOW_WHITESPACE,
WRITE_CONFIG_EDITOR_RESET(CODEEDIT_SHOW_WHITESPACE,
SETTING_ITEM::SHOW_WHITESPACE, false);
WRITE_CONFIG_EDITOR_SET(CODEEDIT_AUTO_CLOSE_CHAR,
WRITE_CONFIG_EDITOR_RESET(CODEEDIT_AUTO_CLOSE_CHAR,
SETTING_ITEM::AUTO_CLOSE_CHAR, true);
WRITE_CONFIG_EDITOR_SET(CODEEDIT_AUTO_IDEN, SETTING_ITEM::AUTO_IDEN,
WRITE_CONFIG_EDITOR_RESET(CODEEDIT_AUTO_IDEN, SETTING_ITEM::AUTO_IDEN,
true);
}
if (cat.testFlag(SETTING::CONSOLE)) {
WRITE_CONFIG_CONSOLE_SET(CONSOLE_FONT, SETTING_ITEM::FONT,
WRITE_CONFIG_CONSOLE_RESET(CONSOLE_FONT, SETTING_ITEM::FONT,
_defaultFont.defaultFamily());
WRITE_CONFIG_CONSOLE_SET(CONSOLE_FONT_SIZE, SETTING_ITEM::FONT_SIZE,
WRITE_CONFIG_CONSOLE_RESET(CONSOLE_FONT_SIZE, SETTING_ITEM::FONT_SIZE,
10);
WRITE_CONFIG_CONSOLE_SET(CONSOLE_THEME, SETTING_ITEM::THEME, {});
WRITE_CONFIG_CONSOLE_SET(CONSOLE_TABS_WIDTH, SETTING_ITEM::TAB_WIDTH,
WRITE_CONFIG_CONSOLE_RESET(CONSOLE_THEME, SETTING_ITEM::THEME, {});
WRITE_CONFIG_CONSOLE_RESET(CONSOLE_TABS_WIDTH, SETTING_ITEM::TAB_WIDTH,
4);
WRITE_CONFIG_CONSOLE_SET(CONSOLE_INDENTATION, SETTING_ITEM::INDENTATION,
0);
WRITE_CONFIG_CONSOLE_SET(CONSOLE_MATCH_BRACES,
WRITE_CONFIG_CONSOLE_RESET(CONSOLE_INDENTATION,
SETTING_ITEM::INDENTATION, 0);
WRITE_CONFIG_CONSOLE_RESET(CONSOLE_MATCH_BRACES,
SETTING_ITEM::MATCH_BRACES, true);
WRITE_CONFIG_CONSOLE_SET(CONSOLE_SHOW_WHITESPACE,
WRITE_CONFIG_CONSOLE_RESET(CONSOLE_SHOW_WHITESPACE,
SETTING_ITEM::SHOW_WHITESPACE, false);
WRITE_CONFIG_CONSOLE_SET(CONSOLE_AUTO_CLOSE_CHAR,
WRITE_CONFIG_CONSOLE_RESET(CONSOLE_AUTO_CLOSE_CHAR,
SETTING_ITEM::AUTO_CLOSE_CHAR, true);
}
load();
}
ScriptSettings::ScriptSettings() : QObject() {

View File

@ -57,6 +57,8 @@ public:
void save(SETTINGS cat = SETTING::ALL);
void reset(SETTINGS cat = SETTING::ALL);
void __reset(SETTINGS cat);
public:
QString editorFontFamily() const;
void setEditorFontFamily(const QString &newEditorFontFamily);

View File

@ -31,6 +31,12 @@
_setUnsaved.setFlag(SettingManager::SETTING_ITEM::config, false); \
}
#define WRITE_CONFIG_RESET(config, dvalue) \
do { \
WRITE_CONFIG(config, dvalue); \
_setUnsaved.setFlag(SettingManager::SETTING_ITEM::config, false); \
} while (0);
Q_GLOBAL_STATIC_WITH_ARGS(QString, DOCK_LAYOUT, ("dock.layout"))
Q_GLOBAL_STATIC_WITH_ARGS(QString, SCRIPT_DOCK_LAYOUT, ("script.layout"))
Q_GLOBAL_STATIC_WITH_ARGS(QString, APP_LASTUSED_PATH, ("app.lastusedpath"))
@ -59,6 +65,7 @@ Q_GLOBAL_STATIC_WITH_ARGS(QString, SCRIPT_RECENTFILES, ("script.recentfiles"))
Q_GLOBAL_STATIC_WITH_ARGS(QString, SCRIPT_ALLOW_USRSCRIPT_INROOT,
("script.allowUsrScriptRoot"))
Q_GLOBAL_STATIC_WITH_ARGS(QString, SCRIPT_ENABLE, ("script.enable"))
Q_GLOBAL_STATIC_WITH_ARGS(QString, SCRIPT_TIMEOUT, ("script.timeout"))
Q_GLOBAL_STATIC_WITH_ARGS(QString, SCRIPT_USRHIDECATS, ("script.usrHideCats"))
Q_GLOBAL_STATIC_WITH_ARGS(QString, SCRIPT_SYSHIDECATS, ("script.sysHideCats"))
Q_GLOBAL_STATIC_WITH_ARGS(QString, OTHER_USESYS_FILEDIALOG,
@ -113,6 +120,7 @@ void SettingManager::load() {
READ_CONFIG_BOOL(m_enablePlgInRoot, PLUGIN_ENABLE_ROOT, false);
READ_CONFIG_INT_POSITIVE(m_editorfontSize, EDITOR_FONTSIZE,
defaultFontSize);
m_editorfontSize = qBound(5, m_editorfontSize, 25);
READ_CONFIG_BOOL(m_editorShowHeader, EDITOR_SHOW_ADDR, true);
READ_CONFIG_BOOL(m_editorShowcol, EDITOR_SHOW_COL, true);
READ_CONFIG_BOOL(m_editorShowtext, EDITOR_SHOW_TEXT, true);
@ -146,6 +154,8 @@ void SettingManager::load() {
READ_CONFIG_BOOL(m_scriptEnabled, SCRIPT_ENABLE, true);
READ_CONFIG_BOOL(m_allowUsrScriptInRoot, SCRIPT_ALLOW_USRSCRIPT_INROOT,
false);
READ_CONFIG_INT(m_scriptTimeout, SCRIPT_TIMEOUT, 10);
m_scriptTimeout = qBound(0, m_scriptTimeout, 312480);
m_usrHideCats =
READ_CONFIG(SCRIPT_USRHIDECATS, QStringList()).toStringList();
m_sysHideCats =
@ -185,6 +195,16 @@ QVariantList SettingManager::getVarList(
return varlist;
}
int SettingManager::scriptTimeout() const { return m_scriptTimeout; }
void SettingManager::setScriptTimeout(int newScriptTimeout) {
newScriptTimeout = qBound(0, newScriptTimeout, 312480);
if (m_scriptTimeout != newScriptTimeout) {
m_scriptTimeout = newScriptTimeout;
_setUnsaved.setFlag(SETTING_ITEM::SCRIPT_TIMEOUT);
}
}
qsizetype SettingManager::logCount() const { return m_logCount; }
void SettingManager::setLogCount(qsizetype newLogCount) {
@ -392,6 +412,7 @@ void SettingManager::save(SETTINGS cat) {
}
if (cat.testFlag(SETTING::SCRIPT)) {
WRITE_CONFIG_SET(SCRIPT_ENABLE, m_scriptEnabled);
WRITE_CONFIG_SET(SCRIPT_TIMEOUT, m_scriptTimeout);
WRITE_CONFIG_SET(SCRIPT_ALLOW_USRSCRIPT_INROOT, m_allowUsrScriptInRoot);
WRITE_CONFIG_SET(SCRIPT_USRHIDECATS, m_usrHideCats);
WRITE_CONFIG_SET(SCRIPT_SYSHIDECATS, m_sysHideCats);
@ -409,44 +430,49 @@ void SettingManager::save(SETTINGS cat) {
}
void SettingManager::reset(SETTINGS cat) {
__reset(cat);
load();
}
void SettingManager::__reset(SETTINGS cat) {
HANDLE_CONFIG;
if (cat.testFlag(SETTING::APP)) {
WRITE_CONFIG_SET(SKIN_THEME, 0);
WRITE_CONFIG_SET(APP_LANGUAGE, QString());
WRITE_CONFIG_SET(APP_FONTFAMILY, _defaultFont.family());
WRITE_CONFIG_SET(APP_FONTSIZE, _defaultFont.pointSize());
WRITE_CONFIG_SET(APP_WINDOWSIZE, Qt::WindowMaximized);
WRITE_CONFIG_RESET(SKIN_THEME, 0);
WRITE_CONFIG_RESET(APP_LANGUAGE, QString());
WRITE_CONFIG_RESET(APP_FONTFAMILY, _defaultFont.family());
WRITE_CONFIG_RESET(APP_FONTSIZE, _defaultFont.pointSize());
WRITE_CONFIG_RESET(APP_WINDOWSIZE, Qt::WindowMaximized);
}
if (cat.testFlag(SETTING::PLUGIN)) {
WRITE_CONFIG_SET(PLUGIN_ENABLE, true);
WRITE_CONFIG_SET(PLUGIN_ENABLE_ROOT, false);
WRITE_CONFIG_RESET(PLUGIN_ENABLE, true);
WRITE_CONFIG_RESET(PLUGIN_ENABLE_ROOT, false);
}
if (cat.testFlag(SETTING::EDITOR)) {
WRITE_CONFIG_SET(EDITOR_FONTSIZE, _defaultFont.pointSize());
WRITE_CONFIG_SET(EDITOR_SHOW_ADDR, true);
WRITE_CONFIG_SET(EDITOR_SHOW_COL, true);
WRITE_CONFIG_SET(EDITOR_SHOW_TEXT, true);
WRITE_CONFIG_SET(EDITOR_FIND_MAXCOUNT, 100);
WRITE_CONFIG_SET(EDITOR_COPY_LIMIT, 100);
WRITE_CONFIG_SET(EDITOR_DECSTRLIMIT, 10);
WRITE_CONFIG_RESET(EDITOR_FONTSIZE, _defaultFont.pointSize());
WRITE_CONFIG_RESET(EDITOR_SHOW_ADDR, true);
WRITE_CONFIG_RESET(EDITOR_SHOW_COL, true);
WRITE_CONFIG_RESET(EDITOR_SHOW_TEXT, true);
WRITE_CONFIG_RESET(EDITOR_FIND_MAXCOUNT, 100);
WRITE_CONFIG_RESET(EDITOR_COPY_LIMIT, 100);
WRITE_CONFIG_RESET(EDITOR_DECSTRLIMIT, 10);
}
if (cat.testFlag(SETTING::SCRIPT)) {
WRITE_CONFIG_SET(SCRIPT_ENABLE, true);
WRITE_CONFIG_SET(SCRIPT_ALLOW_USRSCRIPT_INROOT, false);
WRITE_CONFIG_SET(SCRIPT_USRHIDECATS, QStringList());
WRITE_CONFIG_SET(SCRIPT_SYSHIDECATS, QStringList());
WRITE_CONFIG_RESET(SCRIPT_ENABLE, true);
WRITE_CONFIG_RESET(SCRIPT_TIMEOUT, 10);
WRITE_CONFIG_RESET(SCRIPT_ALLOW_USRSCRIPT_INROOT, false);
WRITE_CONFIG_RESET(SCRIPT_USRHIDECATS, QStringList());
WRITE_CONFIG_RESET(SCRIPT_SYSHIDECATS, QStringList());
}
if (cat.testFlag(SETTING::OTHER)) {
WRITE_CONFIG_SET(OTHER_USESYS_FILEDIALOG, true);
WRITE_CONFIG_RESET(OTHER_USESYS_FILEDIALOG, true);
#ifdef WINGHEX_USE_FRAMELESS
WRITE_CONFIG_SET(OTHER_USE_NATIVE_TITLEBAR, false);
WRITE_CONFIG_RESET(OTHER_USE_NATIVE_TITLEBAR, false);
#endif
WRITE_CONFIG_SET(OTHER_DONT_USE_SPLASH, false);
WRITE_CONFIG_SET(OTHER_CHECK_UPDATE, false);
WRITE_CONFIG_SET(OTHER_LOG_LEVEL, Logger::defaultLevel());
WRITE_CONFIG_SET(OTHER_LOG_COUNT, 20);
WRITE_CONFIG_RESET(OTHER_DONT_USE_SPLASH, false);
WRITE_CONFIG_RESET(OTHER_CHECK_UPDATE, false);
WRITE_CONFIG_RESET(OTHER_LOG_LEVEL, Logger::defaultLevel());
WRITE_CONFIG_RESET(OTHER_LOG_COUNT, 20);
}
load();
}
qsizetype SettingManager::decodeStrlimit() const { return m_decodeStrlimit; }

View File

@ -59,7 +59,7 @@ private:
EDITOR_SHOW_ADDR = 1u << 11,
EDITOR_SHOW_COL = 1u << 12,
EDITOR_SHOW_TEXT = 1u << 13,
// EDITOR_ENCODING = 1u << 14, // Reserved
SCRIPT_TIMEOUT = 1u << 14,
EDITOR_FIND_MAXCOUNT = 1u << 15,
EDITOR_COPY_LIMIT = 1u << 16,
EDITOR_DECSTRLIMIT = 1u << 17,
@ -119,6 +119,8 @@ public:
void save(SETTINGS cat = SETTING::ALL);
void reset(SETTINGS cat);
void __reset(SETTINGS cat);
QList<RecentFileManager::RecentInfo> recentHexFiles() const;
void
setRecentFiles(const QList<RecentFileManager::RecentInfo> &newRecentFiles);
@ -172,6 +174,9 @@ public:
qsizetype logCount() const;
void setLogCount(qsizetype newLogCount);
int scriptTimeout() const;
void setScriptTimeout(int newScriptTimeout);
public:
void checkWriteableAndWarn();
@ -229,6 +234,8 @@ private:
int m_logLevel = 0;
qsizetype m_logCount = 20;
int m_scriptTimeout = 60; // min
private:
QFont _defaultFont;
SETTING_ITEMS _setUnsaved;

File diff suppressed because it is too large Load Diff

View File

@ -23,9 +23,7 @@
#include <QFileDialog>
#include <any>
#include <functional>
#include <vector>
class asIScriptEngine;
class ScriptMachine;
@ -73,9 +71,6 @@ public:
void installAPI(ScriptMachine *machine);
void installBasicTypes(asIScriptEngine *engine);
void setBindingConsole(ScriptingConsole *console);
void unBindConsole();
static QString qvariantCastASString(const QMetaType::Type &id);
static int qvariantCastASID(asIScriptEngine *engine,
@ -100,25 +95,13 @@ private:
void installHexBaseType(asIScriptEngine *engine);
void installHexReaderAPI(asIScriptEngine *engine);
void installHexControllerAPI(asIScriptEngine *engine);
void installDataVisualAPI(asIScriptEngine *engine);
void installScriptFns(asIScriptEngine *engine);
void installScriptUnSafeFns(asIScriptEngine *engine);
void installScriptEnums(asIScriptEngine *engine);
private:
template <class T>
void registerAPI(asIScriptEngine *engine, const std::function<T> &fn,
const char *sig) {
_fnbuffer.push_back(fn);
auto r = engine->RegisterGlobalFunction(
sig, asMETHOD(std::function<T>, operator()),
asCALL_THISCALL_ASGLOBAL,
std::any_cast<std::function<T>>(&_fnbuffer.back()));
Q_ASSERT(r >= 0);
Q_UNUSED(r);
}
using WrapperFn = std::function<void(asIScriptGeneric *)>;
void registerAPI(asIScriptEngine *engine, const asSFuncPtr &fn,
const char *sig);
private:
QStringList cArray2QStringList(const CScriptArray &array, int stringID,
@ -203,22 +186,9 @@ private:
// =========================================================
QString getSenderHeader(const WingHex::SenderInfo &sender);
void cleanUpHandles(const QVector<int> &handles);
private:
QString _InputBox_getItem(const QString &title, const QString &label,
const CScriptArray &items, int current,
bool editable, bool *ok,
Qt::InputMethodHints inputMethodHints);
CScriptArray *_FileDialog_getOpenFileNames(const QString &caption,
const QString &dir,
const QString &filter,
QString *selectedFilter,
QFileDialog::Options options);
CScriptArray *_HexReader_selectedBytes(qsizetype index);
CScriptArray *_HexReader_selectionBytes();
@ -235,34 +205,92 @@ private:
bool _HexController_appendBytes(const CScriptArray &ba);
private:
bool _DataVisual_updateTextList(const CScriptArray &data,
const QString &title,
asIScriptFunction *click,
asIScriptFunction *dblclick);
void _UI_Toast(const QString &message, const QString &icon);
bool _DataVisual_updateTextTree(const QString &json, const QString &title,
asIScriptFunction *click,
asIScriptFunction *dblclick);
bool _DataVisual_updateTextTable(const QString &json,
const CScriptArray &headers,
const CScriptArray &headerNames,
const QString &title,
asIScriptFunction *click,
asIScriptFunction *dblclick);
QColor _Color_get(const QString &caption);
private:
std::vector<std::any> _fnbuffer;
void _MSG_AboutQt(const QString &title);
QMessageBox::StandardButton
_MSG_Information(const QString &title, const QString &text,
QMessageBox::StandardButtons buttons,
QMessageBox::StandardButton defaultButton);
QMessageBox::StandardButton
_MSG_Question(const QString &title, const QString &text,
QMessageBox::StandardButtons buttons,
QMessageBox::StandardButton defaultButton);
QMessageBox::StandardButton
_MSG_Warning(const QString &title, const QString &text,
QMessageBox::StandardButtons buttons,
QMessageBox::StandardButton defaultButton);
QMessageBox::StandardButton
_MSG_Critical(const QString &title, const QString &text,
QMessageBox::StandardButtons buttons,
QMessageBox::StandardButton defaultButton);
void _MSG_About(const QString &title, const QString &text);
QMessageBox::StandardButton
_MSG_msgbox(QMessageBox::Icon icon, const QString &title,
const QString &text, QMessageBox::StandardButtons buttons,
QMessageBox::StandardButton defaultButton);
private:
QString _InputBox_GetText(const QString &title, const QString &label,
QLineEdit::EchoMode echo, const QString &text,
bool *ok, Qt::InputMethodHints inputMethodHints);
QString _InputBox_GetMultiLineText(const QString &title,
const QString &label,
const QString &text, bool *ok,
Qt::InputMethodHints inputMethodHints);
QString _InputBox_getItem(const QString &title, const QString &label,
const CScriptArray &items, int current,
bool editable, bool *ok,
Qt::InputMethodHints inputMethodHints);
int _InputBox_GetInt(const QString &title, const QString &label, int value,
int minValue, int maxValue, int step, bool *ok);
double _InputBox_GetDouble(const QString &title, const QString &label,
double value, double minValue, double maxValue,
int decimals, bool *ok, double step);
private:
QString _FileDialog_GetExistingDirectory(const QString &caption,
const QString &dir,
QFileDialog::Options options);
QString _FileDialog_GetOpenFileName(const QString &caption,
const QString &dir,
const QString &filter,
QString *selectedFilter,
QFileDialog::Options options);
CScriptArray *_FileDialog_getOpenFileNames(const QString &caption,
const QString &dir,
const QString &filter,
QString *selectedFilter,
QFileDialog::Options options);
QString _FileDialog_GetSaveFileName(const QString &caption,
const QString &dir,
const QString &filter,
QString *selectedFilter,
QFileDialog::Options options);
private:
QVector<IWingPlugin::ScriptFnInfo> _sfns;
QHash<QString, QHash<QString, qsizetype>> _rfns;
QVector<IWingPlugin::UNSAFE_SCFNPTR> _usfns;
QHash<QString, QHash<QString, qsizetype>> _urfns;
ScriptingConsole *_console = nullptr;
QHash<QString, QHash<QString, QList<QPair<QString, int>>>> _objs;
QVector<int> _handles;

View File

@ -1,6 +1,8 @@
#include "wingconsolehighligher.h"
#include <KSyntaxHighlighting/FoldingRegion>
#include <KSyntaxHighlighting/Format>
#include <KSyntaxHighlighting/Theme>
WingConsoleHighligher::WingConsoleHighligher(QTextDocument *document)
: WingSyntaxHighlighter(document) {}
@ -18,11 +20,15 @@ void WingConsoleHighligher::applyFormat(
auto off = offsetv.toInt(&b);
if (b) {
if (off < 0) {
// don't highlight
return;
if (off == -2) {
WingSyntaxHighlighter::applyFormat(
offset, length, KSyntaxHighlighting::Format());
}
} else {
if (offset <= off) {
auto div = off - offset;
WingSyntaxHighlighter::applyFormat(
offset, div, KSyntaxHighlighting::Format());
auto rest = length - div;
if (rest <= 0) {
return;

View File

@ -95,7 +95,7 @@ WingCStruct::WingCStruct() : WingHex::IWingPlugin() {
info.fn = std::bind(
QOverload<const QVariantList &>::of(&WingCStruct::structTypes),
this, std::placeholders::_1);
info.ret = MetaType(MetaType::String | MetaType::Array);
info.ret = MetaType::String | MetaType::Array;
_scriptInfo.insert(QStringLiteral("structTypes"), info);
}
@ -125,7 +125,7 @@ WingCStruct::WingCStruct() : WingHex::IWingPlugin() {
info.fn = std::bind(
QOverload<const QVariantList &>::of(&WingCStruct::constDefines),
this, std::placeholders::_1);
info.ret = MetaType(MetaType::String | MetaType::Array);
info.ret = MetaType::String | MetaType::Array;
_scriptInfo.insert(QStringLiteral("constDefines"), info);
}
@ -166,7 +166,7 @@ WingCStruct::WingCStruct() : WingHex::IWingPlugin() {
info.fn = std::bind(
QOverload<const QVariantList &>::of(&WingCStruct::readRaw), this,
std::placeholders::_1);
info.ret = MetaType(MetaType::Byte | MetaType::Array);
info.ret = MetaType::Byte | MetaType::Array;
info.params.append(
qMakePair(getqsizetypeMetaType(), QStringLiteral("offset")));
@ -542,7 +542,6 @@ CScriptDictionary *WingCStruct::convert2AsDictionary(const QVariantHash &hash) {
switch (type) {
case QMetaType::Bool:
case QMetaType::UChar:
case QMetaType::Char:
case QMetaType::Int:
case QMetaType::Long:
case QMetaType::LongLong:
@ -564,6 +563,13 @@ CScriptDictionary *WingCStruct::convert2AsDictionary(const QVariantHash &hash) {
case QMetaType::Float:
dic->Set(p->first, var.toDouble());
break;
case QMetaType::Char: {
auto v = var.value<char>();
auto ch = new QChar(v);
auto id = engine->GetTypeIdByDecl("char");
dic->Set(p->first, ch, id);
break;
}
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
case QMetaType::Char16: {
auto v = var.value<char16_t>();

View File

@ -21,6 +21,7 @@
#include "utilities.h"
#include <QApplication>
#include <QPushButton>
#include <QResizeEvent>
WingMessageBox::WingMessageBox() {}
@ -161,5 +162,10 @@ WingMessageBox::msgbox(QWidget *parent, QMessageBox::Icon icon,
QObject::connect(msgbox, &QMessageBox::finished, &d,
&FramelessDialogBase::done);
return static_cast<QMessageBox::StandardButton>(d.exec());
auto ret = d.exec();
if (ret == 0) {
return msgbox->standardButton(msgbox->defaultButton());
}
return static_cast<QMessageBox::StandardButton>(ret);
}

View File

@ -13,5 +13,4 @@ The following components are all third-party components used by the software. Th
* [cpptrace](https://github.com/jeremy-rifkin/cpptrace) (MIT)
* [QConsoleWidget](https://github.com/gapost/qconsolewidget) (MIT, **FORK** -> AGPL-3.0)
* [QColorPicker](https://github.com/arsdever/qcolorpicker) (MIT)
* [QtJsonModel](https://github.com/dridk/QJsonmodel) (MIT)
* [Qt](https://www.qt.io/) (LGPL)

View File

@ -104,12 +104,21 @@ void CodeEdit::onCompletion(const QModelIndex &index) {
if (selfdata.type == CodeInfoTip::Type::Function ||
selfdata.type == CodeInfoTip::Type::ClsFunction) {
auto args = selfdata.addinfo.value(CodeInfoTip::Args);
auto cur = textCursor();
cur.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor);
auto ch = cur.selectedText();
if (ch.isEmpty() || ch.front().isSpace()) {
auto cursor = textCursor();
cursor.insertText(QStringLiteral("()"));
if (!args.isEmpty()) {
cursor.movePosition(QTextCursor::Left);
setTextCursor(cursor);
}
} else {
auto cursor = textCursor();
cursor.insertText(QStringLiteral("("));
}
}
}
@ -180,6 +189,8 @@ void CodeEdit::applyEditorSetStyle() {
SearchReplaceWidget *CodeEdit::searchWidget() const { return m_searchWidget; }
void CodeEdit::setContentModified(bool b) { emit contentModified(b); }
void CodeEdit::resizeEvent(QResizeEvent *event) {
if (event)
WingCodeEdit::resizeEvent(event);

View File

@ -35,6 +35,9 @@ public:
SearchReplaceWidget *searchWidget() const;
public:
void setContentModified(bool b);
signals:
void contentModified(bool b);

View File

@ -36,7 +36,7 @@
#include <unistd.h>
#endif
constexpr qsizetype FILE_MAX_BUFFER = 0x6400000; // 100MB
constexpr qsizetype FILE_MAX_BUFFER = 0x32000000; // 800MB
constexpr auto CLONE_LIMIT = 3;
constexpr auto VIEW_PROPERTY = "__VIEW__";
@ -121,6 +121,9 @@ EditorView::EditorView(QWidget *parent)
m_metadata->setDocument(doc);
});
connect(&_watcher, &QFileSystemWatcher::fileChanged, this,
&EditorView::need2Reload);
applySettings();
// build up call tables
@ -224,7 +227,7 @@ EditorView::FindError EditorView::find(const FindDialog::Result &result) {
end = cur->selectionEnd(0).offset();
} break;
default: {
begin = -1;
begin = 0;
end = -1;
} break;
}
@ -239,8 +242,12 @@ EditorView::FindError EditorView::find(const FindDialog::Result &result) {
contextLen = raw.length();
m_findResults->setEncoding(result.encoding);
d->findAllBytes(begin, end, raw, results);
m_findResults->lastFindData() = qMakePair(data, contextLen);
} else {
contextLen = d->findAllBytesExt(begin, end, result.str, results);
// assuming the find pattern is 'xx xx xx xx'
contextLen = std::count(data.begin(), data.end(), ' ') + 1;
d->findAllBytesExt(begin, end, result.str, results);
m_findResults->lastFindData() = qMakePair(data, contextLen);
}
m_findResults->beginUpdate();
@ -254,12 +261,9 @@ EditorView::FindError EditorView::find(const FindDialog::Result &result) {
r.col = r.offset % lineWidth;
m_findResults->results().append(r);
m_findResults->findData().append(
readContextFinding(ritem, contextLen, FIND_CONTEXT_SIZE,
FIND_MAX_DISPLAY_FIND_CHARS));
readContextFinding(ritem, contextLen));
}
m_findResults->lastFindData() = data;
m_findResults->endUpdate();
if (m_findResults->size() >= QHEXVIEW_FIND_LIMIT) {
@ -284,6 +288,8 @@ ErrFile EditorView::newFile(size_t index) {
if (isCloneFile()) {
return ErrFile::ClonedFile;
}
removeMonitorPaths();
auto istr = QString::number(index);
m_fileName = tr("Untitled") + istr;
this->setWindowTitle(m_fileName);
@ -320,6 +326,8 @@ ErrFile EditorView::openFile(const QString &filename) {
return ErrFile::Permission;
}
removeMonitorPaths();
m_hex->setDocument(QSharedPointer<QHexDocument>(p));
m_hex->setLockedFile(readonly);
m_hex->setKeepSize(true);
@ -335,6 +343,8 @@ ErrFile EditorView::openFile(const QString &filename) {
auto tab = this->tabWidget();
tab->setIcon(Utilities::getIconFromFile(style(), m_fileName));
tab->setToolTip(m_fileName);
addMonitorPath();
}
return ErrFile::Success;
@ -372,6 +382,8 @@ ErrFile EditorView::openExtFile(const QString &ext, const QString &file) {
return ErrFile::Error;
}
removeMonitorPaths();
m_hex->setDocument(QSharedPointer<QHexDocument>(p));
m_hex->setLockedFile(readonly);
m_hex->setKeepSize(true);
@ -558,6 +570,8 @@ ErrFile EditorView::save(const QString &workSpaceName, const QString &path,
return ErrFile::Permission;
}
removeMonitorPaths();
if (doc->saveTo(&file, !isExport)) {
file.close();
@ -567,6 +581,9 @@ ErrFile EditorView::save(const QString &workSpaceName, const QString &path,
m_docType = DocumentType::File;
doc->setDocSaved();
}
addMonitorPath();
#ifdef Q_OS_LINUX
adjustPermission();
#endif
@ -717,6 +734,16 @@ void EditorView::connectDocSavedFlag(EditorView *editor) {
});
}
void EditorView::removeMonitorPaths() {
auto files = _watcher.files();
if (files.isEmpty()) {
return;
}
_watcher.removePaths(files);
}
void EditorView::addMonitorPath() { _watcher.addPath(m_fileName); }
BookMarksModel *EditorView::bookmarksModel() const { return m_bookmarks; }
MetaDataModel *EditorView::metadataModel() const { return m_metadata; }
@ -784,34 +811,50 @@ bool EditorView::checkHasUnsavedState() const {
}
FindResultModel::FindInfo EditorView::readContextFinding(qsizetype offset,
qsizetype findSize,
int contextSize,
int maxDisplayBytes) {
qsizetype findSize) {
constexpr long DISPLAY_SIZE = 16;
constexpr long FIND_CONTENXT_LEN = 10;
constexpr long HT_SIZE = (DISPLAY_SIZE - FIND_CONTENXT_LEN) / 2;
auto doc = m_hex->document();
if (findSize <= FIND_CONTENXT_LEN) {
long leftsize = FIND_CONTENXT_LEN - findSize;
auto rs = std::div(leftsize, 2l);
qsizetype halfSize = maxDisplayBytes / 2;
auto header = doc->read(offset, qMin(findSize, halfSize));
QByteArray tailer;
if (header.size() < findSize) {
auto len = qMin(findSize, qsizetype(maxDisplayBytes) - halfSize);
tailer = doc->read(offset + findSize - len, len);
}
auto headerlen = HT_SIZE + rs.quot + rs.rem;
auto taillen = HT_SIZE + rs.quot;
auto left = qsizetype(maxDisplayBytes) - header.size() - tailer.size();
auto begin = qMax(offset - headerlen, 0);
auto end = qMin(offset + findSize + taillen, doc->length());
auto boff = offset - begin;
// append to contextSize
contextSize += (left / 2);
auto cheader = doc->read(offset - contextSize, contextSize);
auto ctailer = doc->read(offset + findSize, contextSize);
auto buffer = doc->read(begin, end - begin + 1);
FindResultModel::FindInfo info;
info.cheader = cheader;
info.hbuffer = header;
info.tbuffer = tailer;
info.ctailer = ctailer;
info.cheader = buffer.left(boff);
info.hbuffer = buffer.sliced(boff, findSize);
info.tbuffer = {};
info.ctailer = buffer.sliced(boff + findSize);
return info;
} else {
constexpr long FIND_CONTENXT_HALF = FIND_CONTENXT_LEN / 2;
auto hbegin = qMax(offset - HT_SIZE, 0);
auto hlen = offset - hbegin + FIND_CONTENXT_HALF;
auto header = doc->read(hbegin, hlen);
auto toff = offset - hbegin;
auto tbegin = offset + findSize - FIND_CONTENXT_HALF;
auto tlen = HT_SIZE + FIND_CONTENXT_HALF;
auto tailer = doc->read(tbegin, tlen);
FindResultModel::FindInfo info;
info.cheader = header.left(toff);
info.hbuffer = header.sliced(toff);
info.tbuffer = tailer.left(FIND_CONTENXT_HALF);
info.ctailer = tailer.sliced(FIND_CONTENXT_HALF);
return info;
}
}
void EditorView::applyFunctionTables(WingEditorViewWidget *view,
@ -2033,6 +2076,19 @@ EditorView *EditorView::clone() {
this->m_cloneChildren[this->m_cloneChildren.indexOf(ev)] = nullptr;
});
connect(ev, &EditorView::sigOnCutFile, this, &EditorView::sigOnCutFile);
connect(ev, &EditorView::sigOnCutHex, this, &EditorView::sigOnCutHex);
connect(ev, &EditorView::sigOnCopyFile, this, &EditorView::sigOnCopyFile);
connect(ev, &EditorView::sigOnCopyHex, this, &EditorView::sigOnCopyHex);
connect(ev, &EditorView::sigOnPasteFile, this, &EditorView::sigOnPasteFile);
connect(ev, &EditorView::sigOnPasteHex, this, &EditorView::sigOnPasteHex);
connect(ev, &EditorView::sigOnDelete, this, &EditorView::sigOnDelete);
connect(ev, &EditorView::sigOnFindFile, this, &EditorView::sigOnFindFile);
connect(ev, &EditorView::sigOnGoToLine, this, &EditorView::sigOnGoToLine);
connect(ev, &EditorView::sigOnFill, this, &EditorView::sigOnFill);
connect(ev, &EditorView::sigOnMetadata, this, &EditorView::sigOnMetadata);
connect(ev, &EditorView::sigOnBookMark, this, &EditorView::sigOnBookMark);
auto doc = this->m_hex->document();
ev->m_cloneParent = this;

View File

@ -18,6 +18,7 @@
#ifndef EDITORVIEW_H
#define EDITORVIEW_H
#include <QFileSystemWatcher>
#include <QReadWriteLock>
#include <QStackedWidget>
@ -224,9 +225,7 @@ private:
bool checkHasUnsavedState() const;
FindResultModel::FindInfo readContextFinding(qsizetype offset,
qsizetype findSize,
int contextSize,
int maxDisplayBytes);
qsizetype findSize);
void applyFunctionTables(WingHex::WingEditorViewWidget *view,
const CallTable &fns);
@ -537,11 +536,12 @@ private:
parent->addAction(a);
}
private:
void connectDocSavedFlag(EditorView *editor);
void removeMonitorPaths();
void addMonitorPath();
signals:
void sigFileSaved(QString filename, QString oldFileName);
void sigOnCutFile();
void sigOnCutHex();
void sigOnCopyFile();
@ -555,6 +555,8 @@ signals:
void sigOnMetadata();
void sigOnBookMark();
void need2Reload();
protected:
bool eventFilter(QObject *watched, QEvent *event) override;
@ -591,6 +593,7 @@ private:
QReadWriteLock _rwlock;
CallTable _viewFns;
QFileSystemWatcher _watcher;
};
#endif // EDITORVIEW_H

View File

@ -36,6 +36,7 @@ GotoLineWidget::GotoLineWidget(QWidget *parent)
m_sbline = new QSpinBox(this);
m_sbline->setRange(1, 1);
m_sbline->setContextMenuPolicy(Qt::NoContextMenu);
m_sbline->setMinimumWidth(120);
connect(m_sbline, &QSpinBox::valueChanged, this,
&GotoLineWidget::onGotoLine);

View File

@ -57,7 +57,7 @@ void GotoWidget::handleLineChanged() {
ui->lineEdit->setStyleSheet(QString());
emit jumpToLine(p, isline);
} else {
ui->lineEdit->setStyleSheet(QStringLiteral("color: red;"));
ui->lineEdit->setStyleSheet(QStringLiteral("QLineEdit{color: red}"));
}
}

View File

@ -6,7 +6,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<width>412</width>
<height>50</height>
</rect>
</property>
@ -44,7 +44,7 @@
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
@ -60,7 +60,7 @@
<string>GoTo:</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
<set>Qt::AlignmentFlag::AlignCenter</set>
</property>
</widget>
</item>
@ -78,6 +78,9 @@
<height>16777215</height>
</size>
</property>
<property name="contextMenuPolicy">
<enum>Qt::ContextMenuPolicy::NoContextMenu</enum>
</property>
</widget>
</item>
<item>

View File

@ -102,3 +102,17 @@ bool QHexTextEdit::isHexMode() const { return m_isHexMode; }
void QHexTextEdit::setIsHexMode(bool newIsHexMode) {
m_isHexMode = newIsHexMode;
}
void QHexTextEdit::setFindText(const QString &text) {
mText = text;
mText = mText
.removeIf([](const QChar &ch) {
return !std::isalnum(ch.unicode()) && ch != '?';
})
.toUpper();
setText(text);
auto cur = this->textCursor();
cur.movePosition(QTextCursor::End);
setTextCursor(cur);
mCurserPositionPre = cur.position();
}

View File

@ -28,6 +28,10 @@ public:
bool isHexMode() const;
void setIsHexMode(bool newIsHexMode);
public:
// must be 'xx xx xx' style
void setFindText(const QString &text);
protected:
void keyPressEvent(QKeyEvent *event) override;
void mousePressEvent(QMouseEvent *event) override;

View File

@ -1,257 +0,0 @@
/*==============================================================================
** Copyright (C) 2024-2027 WingSummer
**
** This program is free software: you can redistribute it and/or modify it under
** the terms of the GNU Affero General Public License as published by the Free
** Software Foundation, version 3.
**
** This program is distributed in the hope that it will be useful, but WITHOUT
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
** FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
** details.
**
** You should have received a copy of the GNU Affero General Public License
** along with this program. If not, see <https://www.gnu.org/licenses/>.
** =============================================================================
*/
#include "qtlonglongspinbox.h"
#include <QEvent>
#include <QKeyEvent>
#include <QLineEdit>
#include <limits>
QtLongLongSpinBox::QtLongLongSpinBox(QWidget *parent)
: QAbstractSpinBox(parent) {
m_minimum = std::numeric_limits<qlonglong>::min();
m_maximum = std::numeric_limits<qlonglong>::max();
m_value = 0;
m_singleStep = 1;
m_base = 10;
setValue(m_value);
}
qlonglong QtLongLongSpinBox::value() const { return m_value; }
void QtLongLongSpinBox::setValue(qlonglong expectedNewValue) {
const qlonglong newValue = qBound(m_minimum, expectedNewValue, m_maximum);
const QString newValueString = QString::number(newValue, m_base);
lineEdit()->setText(m_prefix + newValueString + m_suffix);
if (m_value != newValue) {
m_value = newValue;
emit valueChanged(newValue);
}
}
QString QtLongLongSpinBox::prefix() const { return m_prefix; }
void QtLongLongSpinBox::setPrefix(const QString &prefix) {
m_prefix = prefix;
setValue(m_value);
}
QString QtLongLongSpinBox::suffix() const { return m_suffix; }
void QtLongLongSpinBox::setSuffix(const QString &suffix) {
m_suffix = suffix;
setValue(m_value);
}
QString QtLongLongSpinBox::cleanText() const {
return QString::number(m_value);
}
qlonglong QtLongLongSpinBox::singleStep() const { return m_singleStep; }
void QtLongLongSpinBox::setSingleStep(qlonglong step) { m_singleStep = step; }
qlonglong QtLongLongSpinBox::minimum() const { return m_minimum; }
void QtLongLongSpinBox::setMinimum(qlonglong min) {
m_minimum = min;
if (m_maximum < m_minimum) {
m_maximum = m_minimum;
}
setValue(m_value);
}
qlonglong QtLongLongSpinBox::maximum() const { return m_maximum; }
void QtLongLongSpinBox::setMaximum(qlonglong max) {
m_maximum = max;
if (m_maximum < m_minimum) {
m_maximum = m_minimum;
}
setValue(m_value);
}
void QtLongLongSpinBox::setRange(qlonglong min, qlonglong max) {
if (min < max) {
m_minimum = min;
m_maximum = max;
} else {
m_minimum = max;
m_maximum = min;
}
setValue(m_value);
}
void QtLongLongSpinBox::setDisplayIntegerBase(int base) {
if (m_base != base) {
m_base = base;
setValue(m_value);
}
}
int QtLongLongSpinBox::displayIntegerBase() const { return m_base; }
void QtLongLongSpinBox::keyPressEvent(QKeyEvent *event) {
switch (event->key()) {
case Qt::Key_Enter:
case Qt::Key_Return:
selectCleanText();
lineEditEditingFinalize();
}
QAbstractSpinBox::keyPressEvent(event);
}
void QtLongLongSpinBox::focusOutEvent(QFocusEvent *event) {
lineEditEditingFinalize();
QAbstractSpinBox::focusOutEvent(event);
}
QAbstractSpinBox::StepEnabled QtLongLongSpinBox::stepEnabled() const {
if (isReadOnly()) {
return StepNone;
}
StepEnabled se = StepNone;
if (wrapping() || m_value < m_maximum) {
se |= StepUpEnabled;
}
if (wrapping() || m_value > m_minimum) {
se |= StepDownEnabled;
}
return se;
}
void QtLongLongSpinBox::stepBy(int steps) {
if (isReadOnly()) {
return;
}
if (m_prefix + QString::number(m_value) + m_suffix != lineEdit()->text()) {
lineEditEditingFinalize();
}
qlonglong newValue = m_value + (steps * m_singleStep);
if (wrapping()) {
// emulating the behavior of QSpinBox
if (newValue > m_maximum) {
if (m_value == m_maximum) {
newValue = m_minimum;
} else {
newValue = m_maximum;
}
} else if (newValue < m_minimum) {
if (m_value == m_minimum) {
newValue = m_maximum;
} else {
newValue = m_minimum;
}
}
} else {
newValue = qBound(m_minimum, newValue, m_maximum);
}
setValue(newValue);
selectCleanText();
}
QValidator::State QtLongLongSpinBox::validate(QString &input, int &pos) const {
// first, we try to interpret as a number without prefixes
bool ok;
const qlonglong value = input.toLongLong(&ok);
if (input.isEmpty() || (ok && value <= m_maximum)) {
input = m_prefix + input + m_suffix;
pos += m_prefix.length();
return QValidator::Acceptable;
}
// if string of text editor aren't simple number, try to interpret it
// as a number with prefix and suffix
bool valid = true;
if (!m_prefix.isEmpty() && !input.startsWith(m_prefix)) {
valid = false;
}
if (!m_suffix.isEmpty() && !input.endsWith(m_suffix)) {
valid = false;
}
if (valid) {
const int start = m_prefix.length();
const int length = input.length() - start - m_suffix.length();
bool ok;
const QString number = input.mid(start, length);
const qlonglong value = number.toLongLong(&ok);
if (number.isEmpty() || (ok && value <= m_maximum)) {
return QValidator::Acceptable;
}
}
// otherwise not acceptable
return QValidator::Invalid;
}
void QtLongLongSpinBox::lineEditEditingFinalize() {
const QString text = lineEdit()->text();
// first, we try to read as a number without prefixes
bool ok;
qlonglong value = text.toLongLong(&ok);
if (ok) {
setValue(value);
return;
}
// if string of text editor aren't simple number, try to interpret it
// as a number with prefix and suffix
bool valid = true;
if (!m_prefix.isEmpty() && !text.startsWith(m_prefix)) {
valid = false;
} else if (!m_suffix.isEmpty() && !text.endsWith(m_suffix)) {
valid = false;
}
if (valid) {
const int start = m_prefix.length();
const int length = text.length() - start - m_suffix.length();
bool ok;
const qlonglong value = text.mid(start, length).toLongLong(&ok);
if (ok) {
setValue(value);
return;
}
}
// otherwise set old value
setValue(m_value);
}
void QtLongLongSpinBox::selectCleanText() {
lineEdit()->setSelection(m_prefix.length(), lineEdit()->text().length() -
m_prefix.length() -
m_suffix.length());
}

View File

@ -1,85 +0,0 @@
/*==============================================================================
** Copyright (C) 2024-2027 WingSummer
**
** This program is free software: you can redistribute it and/or modify it under
** the terms of the GNU Affero General Public License as published by the Free
** Software Foundation, version 3.
**
** This program is distributed in the hope that it will be useful, but WITHOUT
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
** FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
** details.
**
** You should have received a copy of the GNU Affero General Public License
** along with this program. If not, see <https://www.gnu.org/licenses/>.
** =============================================================================
*/
#ifndef QTLONGLONGSPINBOX_H
#define QTLONGLONGSPINBOX_H
#include <QAbstractSpinBox>
#include <QtGlobal>
class QtLongLongSpinBox : public QAbstractSpinBox {
Q_OBJECT
public:
explicit QtLongLongSpinBox(QWidget *parent = 0);
qlonglong value() const;
QString prefix() const;
void setPrefix(const QString &prefix);
QString suffix() const;
void setSuffix(const QString &suffix);
QString cleanText() const;
qlonglong singleStep() const;
void setSingleStep(qlonglong val);
qlonglong minimum() const;
void setMinimum(qlonglong min);
qlonglong maximum() const;
void setMaximum(qlonglong max);
void setRange(qlonglong min, qlonglong max);
void setDisplayIntegerBase(int base);
int displayIntegerBase() const;
public slots:
void setValue(qlonglong value);
signals:
void valueChanged(qlonglong i);
protected:
virtual void keyPressEvent(QKeyEvent *event);
virtual void focusOutEvent(QFocusEvent *event);
virtual void stepBy(int steps);
virtual StepEnabled stepEnabled() const;
virtual QValidator::State validate(QString &input, int &pos) const;
private:
void lineEditEditingFinalize();
void selectCleanText();
private:
QString m_prefix;
QString m_suffix;
qlonglong m_singleStep;
qlonglong m_minimum;
qlonglong m_maximum;
qlonglong m_value;
int m_base;
private:
Q_DISABLE_COPY(QtLongLongSpinBox)
};
#endif // QTLONGLONGSPINBOX_H

View File

@ -35,7 +35,7 @@
#include "class/ascompletion.h"
#include "class/clangformatmanager.h"
ScriptEditor::ScriptEditor(asIScriptEngine *engine, QWidget *parent)
ScriptEditor::ScriptEditor(QWidget *parent)
: ads::CDockWidget(nullptr, QString(), parent) {
this->setFeatures(
CDockWidget::DockWidgetFocusable | CDockWidget::DockWidgetMovable |
@ -48,7 +48,7 @@ ScriptEditor::ScriptEditor(asIScriptEngine *engine, QWidget *parent)
m_editor->setSyntax(
m_editor->syntaxRepo().definitionForName("AngelScript"));
auto cm = new AsCompletion(engine, m_editor);
auto cm = new AsCompletion(m_editor);
connect(cm, &AsCompletion::onFunctionTip, this,
&ScriptEditor::onFunctionTip);
@ -57,6 +57,9 @@ ScriptEditor::ScriptEditor(asIScriptEngine *engine, QWidget *parent)
connect(m_editor, &CodeEdit::contentModified, this,
[this]() { processTitle(); });
connect(&_watcher, &QFileSystemWatcher::fileChanged, this,
&ScriptEditor::need2Reload);
this->setWidget(m_editor);
}
@ -75,12 +78,30 @@ bool ScriptEditor::openFile(const QString &filename) {
}
m_editor->setPlainText(QString::fromUtf8(f.readAll()));
f.close();
if (!m_fileName.isEmpty()) {
_watcher.removePath(m_fileName);
}
m_fileName = filename;
_watcher.addPath(m_fileName);
processTitle();
return true;
}
bool ScriptEditor::save(const QString &path) {
if (!m_fileName.isEmpty()) {
_watcher.removePath(m_fileName);
}
QScopeGuard guard([this, path]() {
if (path.isEmpty()) {
_watcher.addPath(m_fileName);
} else {
_watcher.addPath(path);
}
});
#ifdef Q_OS_LINUX
auto needAdjustFile = !QFile::exists(path);
#endif

View File

@ -21,13 +21,15 @@
#include "Qt-Advanced-Docking-System/src/DockWidget.h"
#include "control/codeedit.h"
#include <QFileSystemWatcher>
class asIScriptEngine;
class ScriptEditor : public ads::CDockWidget {
Q_OBJECT
public:
explicit ScriptEditor(asIScriptEngine *engine, QWidget *parent = nullptr);
explicit ScriptEditor(QWidget *parent = nullptr);
virtual ~ScriptEditor();
QString fileName() const;
@ -40,6 +42,8 @@ signals:
void onToggleMark(int line);
void onFunctionTip(const QString &tip);
void need2Reload();
public slots:
void setReadOnly(bool b);
bool openFile(const QString &filename);
@ -57,6 +61,8 @@ private:
private:
CodeEdit *m_editor = nullptr;
QString m_fileName;
QFileSystemWatcher _watcher;
};
#endif // SCRIPTEDITOR_H

View File

@ -17,17 +17,20 @@
#include "scriptingconsole.h"
#include "QConsoleWidget/QConsoleIODevice.h"
#include "class/logger.h"
#include "class/scriptconsolemachine.h"
#include "class/scriptmachine.h"
#include "class/scriptsettings.h"
#include "class/skinmanager.h"
#include "class/wingmessagebox.h"
#include "model/codecompletionmodel.h"
#include "utilities.h"
#include <QApplication>
#include <QClipboard>
#include <QColor>
#include <QIcon>
#include <QKeyEvent>
#include <QMenu>
#include <QMimeData>
#include <QRegularExpression>
#include <QTextBlock>
@ -68,7 +71,7 @@ void ScriptingConsole::handleReturnKey() {
// start new block
appendPlainText(QString());
dontHighlightLastLine();
dontHighlightLastLine(true);
setMode(Output);
QTextCursor textCursor = this->textCursor();
@ -84,33 +87,68 @@ void ScriptingConsole::handleReturnKey() {
if (iodevice_->isOpen())
iodevice_->consoleWidgetInput(code);
if (!_isWaitingRead) {
emit consoleCommand(code);
}
}
}
void ScriptingConsole::init() {
_getInputFn = std::bind(&ScriptingConsole::getInput, this);
_sp = new ScriptConsoleMachine(_getInputFn, this);
connect(_sp, &ScriptConsoleMachine::onClearConsole, this,
&ScriptingConsole::clear);
connect(this, &ScriptingConsole::abortEvaluation, _sp,
&ScriptConsoleMachine::abortScript);
connect(this, &QConsoleWidget::consoleCommand, this,
&ScriptingConsole::runConsoleCommand);
connect(
_sp, &ScriptConsoleMachine::onOutput, this,
[=](ScriptConsoleMachine::MessageType type,
const ScriptConsoleMachine::MessageInfo &message) {
auto cm = new AsConsoleCompletion(this);
connect(cm, &AsCompletion::onFunctionTip, this,
&ScriptingConsole::onFunctionTip);
}
void ScriptingConsole::clearConsole() {
setMode(Output);
auto cur = this->textCursor();
auto off = cur.position() - this->currentHeaderPos();
auto lastCmd = this->currentCommandLine();
auto dis = lastCmd.length() - off;
clear();
if (lastCommandPrompt()) {
auto lines = _codes.split('\n');
auto pl = lines.begin();
appendCommandPrompt(false);
writeStdOut(*pl);
pl++;
for (; pl != lines.end(); pl++) {
appendCommandPrompt(true);
writeStdOut(*pl);
}
appendCommandPrompt(true);
} else {
appendCommandPrompt(false);
}
setMode(Input);
replaceCommandLine(lastCmd);
cur = this->textCursor();
cur.movePosition(QTextCursor::EndOfBlock);
cur.movePosition(QTextCursor::Left, QTextCursor::MoveAnchor, dis);
setTextCursor(cur);
}
void ScriptingConsole::processKeyEvent(QKeyEvent *e) { keyPressEvent(e); }
void ScriptingConsole::onOutput(const ScriptMachine::MessageInfo &message) {
// <type, <row, col>>
static QPair<ScriptConsoleMachine::MessageType, QPair<int, int>>
lastInfo{ScriptConsoleMachine::MessageType::Print, {-1, -1}};
static QPair<ScriptMachine::MessageType, QPair<int, int>> lastInfo{
ScriptMachine::MessageType::Print, {-1, -1}};
auto doc = this->document();
auto lastLine = doc->lastBlock();
auto isNotBlockStart = !lastLine.text().isEmpty();
auto fmtMsg = [](const ScriptConsoleMachine::MessageInfo &message)
-> QString {
auto fmtMsg = [](const ScriptMachine::MessageInfo &message) -> QString {
if (message.row <= 0 || message.col <= 0) {
return message.message;
} else {
@ -120,20 +158,18 @@ void ScriptingConsole::init() {
}
};
auto isMatchLast =
[](ScriptConsoleMachine::MessageType type,
const ScriptConsoleMachine::MessageInfo &message) -> bool {
auto isMatchLast = [](const ScriptMachine::MessageInfo &message) -> bool {
if (message.row < 0 || message.col < 0) {
return false;
}
return lastInfo.first == type &&
return lastInfo.first == message.type &&
lastInfo.second.first == message.row &&
lastInfo.second.second == message.col;
};
switch (type) {
switch (message.type) {
case ScriptMachine::MessageType::Info:
if (isMatchLast(type, message)) {
if (isMatchLast(message)) {
stdOut(message.message);
} else {
if (isNotBlockStart) {
@ -144,7 +180,7 @@ void ScriptingConsole::init() {
flush();
break;
case ScriptMachine::MessageType::Warn:
if (isMatchLast(type, message)) {
if (isMatchLast(message)) {
stdWarn(message.message);
} else {
if (isNotBlockStart) {
@ -155,7 +191,7 @@ void ScriptingConsole::init() {
flush();
break;
case ScriptMachine::MessageType::Error:
if (isMatchLast(type, message)) {
if (isMatchLast(message)) {
stdErr(message.message);
} else {
if (isNotBlockStart) {
@ -166,47 +202,30 @@ void ScriptingConsole::init() {
flush();
break;
case ScriptMachine::MessageType::Print:
if (lastInfo.first != type) {
if (lastInfo.first != message.type && isNotBlockStart) {
newLine();
}
// If running ouput in the console,
// otherwise logging.
if (_sp->isRunning()) {
stdOut(message.message);
} else {
Logger::logPrint(Logger::packDebugStr(
packUpLoggingStr(message.message)));
}
break;
}
lastInfo.first = type;
lastInfo.first = message.type;
lastInfo.second = qMakePair(message.row, message.col);
});
connect(this, &QConsoleWidget::consoleCommand, this,
&ScriptingConsole::runConsoleCommand);
auto cm = new AsConsoleCompletion(_sp->engine(), this);
connect(cm, &AsCompletion::onFunctionTip, this,
&ScriptingConsole::onFunctionTip);
}
void ScriptingConsole::clearConsole() {
void ScriptingConsole::abortCurrentCode() {
setMode(Output);
clear();
appendCommandPrompt(lastCommandPrompt());
_codes.clear();
appendCommandPrompt();
setMode(Input);
}
void ScriptingConsole::processKeyEvent(QKeyEvent *e) { keyPressEvent(e); }
void ScriptingConsole::applyScriptSettings() {
auto &set = ScriptSettings::instance();
auto dfont = QFont(set.consoleFontFamily());
dfont.setPointSize(set.consoleFontSize());
auto thname = set.editorTheme();
auto thname = set.consoleTheme();
if (thname.isEmpty()) {
switch (SkinManager::instance().currentTheme()) {
case SkinManager::Theme::Dark:
@ -232,8 +251,94 @@ void ScriptingConsole::applyScriptSettings() {
void ScriptingConsole::runConsoleCommand(const QString &code) {
auto exec = code.trimmed();
if (exec.endsWith('\\')) {
if (exec == QStringLiteral("#ls")) {
auto &ins = ScriptMachine::instance();
auto mod = ins.module(ScriptMachine::Interactive);
if (mod) {
auto total = mod->GetGlobalVarCount();
setMode(Output);
if (total == 0) {
stdOut("<none>");
} else {
auto &sm = ScriptMachine::instance();
for (asUINT i = 0; i < total; ++i) {
const char *name;
int typeID;
auto decl = mod->GetGlobalVarDeclaration(i);
if (decl && mod->GetGlobalVar(i, &name, nullptr, &typeID) ==
asSUCCESS) {
auto value = sm.debugger()->toString(
mod->GetAddressOfGlobalVar(i), typeID, sm.engine(),
1);
stdOut(decl + QStringLiteral(" = ") + value);
}
}
}
_codes.clear();
appendCommandPrompt();
setMode(Input);
}
} else if (exec.startsWith(QStringLiteral("#del"))) {
// this is special command
auto &ins = ScriptMachine::instance();
auto mod = ins.module(ScriptMachine::Interactive);
if (mod) {
// first check whether contains \n
auto idx = exec.indexOf('\n');
if (idx >= 0) {
setMode(Output);
stdErr(tr("InvalidDelCmd"));
} else {
// ok, then tokens should be devided by the space
exec.remove(0, 4);
auto vars = exec.split(' ', Qt::SkipEmptyParts);
QList<asUINT> indices;
// then check
setMode(Output);
for (auto &v : vars) {
auto idx = mod->GetGlobalVarIndexByName(v.toUtf8());
if (idx >= 0) {
indices.append(idx);
} else {
stdWarn(tr("NotFoundIgnore:") + v);
}
}
std::sort(indices.begin(), indices.end(), std::greater<int>());
// ok, remove
for (auto &i : indices) {
mod->RemoveGlobalVar(i);
}
}
}
_codes.clear();
appendCommandPrompt();
setMode(Input);
} else if (exec == QStringLiteral("#cls")) {
auto &ins = ScriptMachine::instance();
auto mod = ins.module(ScriptMachine::Interactive);
if (mod) {
auto total = mod->GetGlobalVarCount();
if (total) {
asUINT i = total;
do {
--i;
mod->RemoveGlobalVar(i);
} while (i);
}
}
_codes.clear();
appendCommandPrompt();
setMode(Input);
} else if (exec.endsWith('\\')) {
static QRegularExpression ex(QStringLiteral("[\\\\\\s]+$"));
_codes.append('\n');
_codes += exec.remove(ex);
setMode(Output);
appendCommandPrompt(true);
@ -241,8 +346,8 @@ void ScriptingConsole::runConsoleCommand(const QString &code) {
} else {
setMode(Output);
_codes += exec;
if (!_sp->executeCode(_codes)) {
}
ScriptMachine::instance().executeCode(ScriptMachine::Interactive,
_codes);
_codes.clear();
appendCommandPrompt();
setMode(Input);
@ -250,11 +355,18 @@ void ScriptingConsole::runConsoleCommand(const QString &code) {
}
QString ScriptingConsole::getInput() {
auto &s = consoleStream();
appendCommandPrompt(true);
setMode(Input);
consoleStream().device()->waitForReadyRead(-1);
s.status();
auto d = s.device();
auto ba = d->bytesAvailable();
d->skip(ba);
_isWaitingRead = true;
d->waitForReadyRead(-1);
QString instr;
consoleStream() >> instr;
s >> instr;
_isWaitingRead = false;
setMode(Output);
return instr;
}
@ -277,35 +389,115 @@ void ScriptingConsole::onCompletion(const QModelIndex &index) {
if (selfdata.type == CodeInfoTip::Type::Function ||
selfdata.type == CodeInfoTip::Type::ClsFunction) {
auto args = selfdata.addinfo.value(CodeInfoTip::Args);
auto cur = textCursor();
cur.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor);
auto ch = cur.selectedText();
if (ch.isEmpty() || ch.front().isSpace()) {
auto cursor = textCursor();
cursor.insertText(QStringLiteral("()"));
if (!args.isEmpty()) {
cursor.movePosition(QTextCursor::Left);
setTextCursor(cursor);
}
} else {
auto cursor = textCursor();
cursor.insertText(QStringLiteral("("));
}
}
}
void ScriptingConsole::paste() {
if (ScriptMachine::instance().isRunning(ScriptMachine::Interactive)) {
return;
}
const QMimeData *const clipboard = QApplication::clipboard()->mimeData();
const QString text = clipboard->text();
if (!text.isEmpty()) {
if (text.indexOf('\n') < 0) {
if (isCursorInEditZone()) {
auto cursor = this->textCursor();
cursor.insertText(text);
} else {
replaceCommandLine(text);
}
} else {
auto ret = WingMessageBox::question(
nullptr, tr("MultiCodeCanNotUndo"), text);
if (ret == QMessageBox::No) {
return;
}
auto lines = text.split('\n');
if (lines.isEmpty()) {
return;
}
setMode(Output);
auto pl = lines.begin();
auto pend = std::prev(lines.end());
writeStdOut(*pl);
pl++;
for (; pl != pend; pl++) {
appendCommandPrompt(true);
writeStdOut(*pl);
}
appendCommandPrompt(true);
setMode(Input);
replaceCommandLine(*pl);
lines.removeLast();
_codes = lines.join('\n');
}
}
}
bool ScriptingConsole::isTerminal() const { return _isTerminal; }
void ScriptingConsole::setIsTerminal(bool newIsTerminal) {
_isTerminal = newIsTerminal;
}
QString ScriptingConsole::currentCodes() const {
return _codes + currentCommandLine();
QTextCursor textCursor = this->textCursor();
textCursor.setPosition(inpos_, QTextCursor::KeepAnchor);
return _codes + textCursor.selectedText();
}
ScriptMachine *ScriptingConsole::machine() const { return _sp; }
ScriptConsoleMachine *ScriptingConsole::consoleMachine() const { return _sp; }
void ScriptingConsole::contextMenuEvent(QContextMenuEvent *event) {
QMenu menu(this);
menu.addAction(QIcon(QStringLiteral(":/qeditor/copy.png")), tr("Copy"),
QKeySequence(QKeySequence::Copy), this,
auto a = menu.addAction(QIcon(QStringLiteral(":/qeditor/copy.png")),
tr("Copy"), QKeySequence(QKeySequence::Copy), this,
&ScriptingConsole::copy);
menu.addAction(QIcon(QStringLiteral(":/qeditor/cut.png")), tr("Cut"),
a->setShortcutContext(Qt::WidgetShortcut);
a = menu.addAction(QIcon(QStringLiteral(":/qeditor/cut.png")), tr("Cut"),
QKeySequence(QKeySequence::Cut), this,
&ScriptingConsole::cut);
menu.addAction(QIcon(QStringLiteral(":/qeditor/paste.png")), tr("Paste"),
QKeySequence(QKeySequence::Paste), this,
a->setShortcutContext(Qt::WidgetShortcut);
a = menu.addAction(QIcon(QStringLiteral(":/qeditor/paste.png")),
tr("Paste"), QKeySequence(QKeySequence::Paste), this,
&ScriptingConsole::paste);
a->setShortcutContext(Qt::WidgetShortcut);
if (_isTerminal) {
a = menu.addAction(ICONRES(QStringLiteral("del")), tr("Clear"),
QKeySequence(Qt::ControlModifier | Qt::Key_L), this,
&ScriptingConsole::clearConsole);
a->setShortcutContext(Qt::WidgetShortcut);
menu.addSeparator();
a = menu.addAction(ICONRES(QStringLiteral("dbgstop")),
tr("AbortScript"),
QKeySequence(Qt::ControlModifier | Qt::Key_Q), []() {
ScriptMachine::instance().abortScript(
ScriptMachine::Background);
});
a->setShortcutContext(Qt::WidgetShortcut);
} else {
a = menu.addAction(ICONRES(QStringLiteral("del")), tr("Clear"),
QKeySequence(Qt::ControlModifier | Qt::Key_L), this,
&ScriptingConsole::clear);
a->setShortcutContext(Qt::WidgetShortcut);
}
menu.exec(event->globalPos());
}

Some files were not shown because too many files have changed in this diff Show More