feat: 更好的代码填充;自动重载文件相关;

This commit is contained in:
寂静的羽夏 2025-04-24 21:10:54 +08:00
parent f59755e3f0
commit 2ee3051a7d
13 changed files with 838 additions and 633 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -69,7 +69,7 @@ QString AngelObjString::dictionaryToString(void *obj, asDebugger *dbg) {
auto engine = dic->GetEngine();
s << " [";
s << " {";
asUINT n = 0;
for (CScriptDictionary::CIterator it = dic->begin(); it != dic->end();
it++, n++) {
@ -88,7 +88,7 @@ QString AngelObjString::dictionaryToString(void *obj, asDebugger *dbg) {
if (n < dic->GetSize() - 1)
s << ", ";
}
s << "]";
s << "}";
return str;
}

View File

@ -154,11 +154,6 @@ bool AsCompletion::processTrigger(const QString &trigger,
auto code = content.toUtf8();
QList<CodeInfoTip> nodes;
QList<CodeInfoTip> docNodes;
if (m_parseDocument) {
docNodes = parseDocument();
}
if (!trigger.isEmpty() && trigger != *DOT_TRIGGER) {
clearFunctionTip();
@ -226,24 +221,35 @@ bool AsCompletion::processTrigger(const QString &trigger,
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;
}
QList<CodeInfoTip> docNodes;
if (m_parseDocument) {
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, docNodes);
} else {
etoken = tokens.back(); // checking later
}
} else {
prefix.clear();
}
if (nodes.isEmpty()) {
@ -266,8 +272,52 @@ bool AsCompletion::processTrigger(const QString &trigger,
}
if (trigger == *DOT_TRIGGER) {
// member guessing ?
applyClassNodes(nodes);
// member type guessing ? basic match is enough. (>n<)
auto isBasicType = [](const QString &type) {
static QStringList basicType{
"int", "int8", "int16", "int32", "int64",
"uint", "uint8", "uint16", "uint32", "uint64",
"float", "double", "byte"};
return basicType.contains(type);
};
auto clsNodes = parser.headerNodes();
// 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;
}
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 type inference is not supported.
// PRs will be welcomed !!!
if (isBasicType(retType)) {
popup()->hide();
return false;
}
nodes.append(clsNodes.value(retType));
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) {
@ -368,6 +418,7 @@ QList<CodeInfoTip> AsCompletion::parseDocument() {
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);
}
@ -389,17 +440,47 @@ QList<CodeInfoTip> AsCompletion::parseDocument() {
}
break;
case QAsCodeParser::SymbolType::TypeDef:
tip.type = CodeInfoTip::Type::KeyWord;
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:
tip.type = CodeInfoTip::Type::Class;
for (auto &mem : sym.children) {
// TODO
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:

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 {

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

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

@ -119,12 +119,26 @@ QAsCodeParser::parseIntell(qsizetype offset,
continue;
}
break;
case SymbolType::Class:
sym.children = parseClassContent(offset, sym.scope, seg.codes);
break;
case SymbolType::Interface:
sym.children = parseInterfaceContent(offset, sym.scope, seg.codes);
break;
case SymbolType::Class: {
auto syms =
parseClassContent(offset - seg.offset, sym.scope, seg.codes);
// TODO: PRS, 'cause i have no need to code-complete a class
sym.inherit = syms.first;
sym.children = syms.second;
if (offset > seg.offset && offset < seg.end()) {
ret.append(syms.second);
}
} break;
case SymbolType::Interface: {
auto syms = parseInterfaceContent(offset - seg.offset, sym.scope,
seg.codes);
// TODO: PRS, 'cause i have no need to code-complete an interface
sym.inherit = syms.first;
sym.children = syms.second;
if (offset > seg.offset && offset < seg.end()) {
ret.append(syms.second);
}
} break;
case SymbolType::Invalid:
case SymbolType::Import:
continue;
@ -510,7 +524,8 @@ QAsCodeParser::parseStatementBlock(const QByteArrayList &ns,
QList<QAsCodeParser::Symbol> ret;
for (auto &symlist : syms) {
for (auto &sym : symlist) {
if (sym.symtype == SymbolType::Variable) {
if (sym.symtype == SymbolType::Variable &&
!sym.type.isEmpty()) {
auto var = sym.name;
auto n = std::find_if(
ret.begin(), ret.end(),
@ -940,13 +955,14 @@ QAsCodeParser::CodeSegment QAsCodeParser::parseFuncDef() {
auto begin = t1.pos;
skipCodeBlock();
getToken(&t1);
rewindTo(&t1);
auto end = t1.pos;
// seg.name is empty
seg.scope = currentNs;
seg.offset = begin;
seg.type = SymbolType::FnDef;
seg.codes = _code.sliced(begin, end - begin + 1);
seg.codes = _code.sliced(begin, end - begin);
return seg;
}
@ -1027,6 +1043,7 @@ void QAsCodeParser::superficiallyParseVarInit() {
} while (indentParan || indentBrace ||
(t.type != ttListSeparator && t.type != ttEndStatement &&
t.type != ttEndStatementBlock));
rewindTo(&t);
} else if (t.type == ttOpenParenthesis) {
sToken start = t;
@ -1308,6 +1325,7 @@ QAsCodeParser::CodeSegment QAsCodeParser::parseTypedef() {
auto begin = token.pos;
skipCodeBlock();
getToken(&token);
rewindTo(&token);
auto end = token.pos;
// seg.name is empty
@ -1423,22 +1441,27 @@ QAsCodeParser::parseGlobalVarDecls(const QByteArrayList &ns,
return parseDeclaration(ns, false, true);
}
QAsCodeParser::CodeSegment QAsCodeParser::parseFunctionMethod() {
QAsCodeParser::CodeSegment QAsCodeParser::parseFunctionMethod(Visiblity &vis) {
CodeSegment seg;
sToken t1;
getToken(&t1);
vis = Visiblity::Public;
// A class method can start with 'private' or 'protected'
if (t1.type == ttPrivate) {
rewindTo(&t1);
parseToken(ttPrivate);
getToken(&t1);
vis = Visiblity::Private;
} else if (t1.type == ttProtected) {
rewindTo(&t1);
parseToken(ttProtected);
getToken(&t1);
vis = Visiblity::Protected;
}
if (_isSyntaxError)
return seg;
@ -1530,12 +1553,13 @@ QAsCodeParser::parseFuncDefContent(const QByteArrayList &ns,
return parseFuncDefContent(ns);
}
QList<QAsCodeParser::Symbol>
QPair<QByteArrayList, QList<QAsCodeParser::Symbol>>
QAsCodeParser::parseClassContent(qsizetype offset, const QByteArrayList &ns,
const QByteArray &code) {
reset();
_code = code;
QByteArrayList inhertSyms;
QList<Symbol> syms;
sToken t;
@ -1544,32 +1568,30 @@ QAsCodeParser::parseClassContent(qsizetype offset, const QByteArrayList &ns,
// Optional list of interfaces that are being implemented and classes that
// are being inherited
if (t.type == ttColon) {
Symbol inhertSym;
QByteArray inhertSym;
// assuming it as an interface
inhertSym.symtype = SymbolType::Interface;
Symbol isym;
isym.scope = parseOptionalScope();
isym.name = getSymbolString(parseIdentifier());
isym.symtype = SymbolType::Class; // assuming it as a class
inhertSym.children.append(isym);
inhertSym = parseOptionalScope().join("::");
if (!inhertSym.isEmpty()) {
inhertSym += "::";
}
inhertSym += getSymbolString(parseIdentifier());
inhertSyms.append(inhertSym);
getToken(&t);
while (t.type == ttListSeparator) {
isym.scope = parseOptionalScope();
isym.name = getSymbolString(parseIdentifier());
inhertSym.children.append(isym);
inhertSym = parseOptionalScope().join("::");
if (!inhertSym.isEmpty()) {
inhertSym += "::";
}
inhertSym += getSymbolString(parseIdentifier());
inhertSyms.append(inhertSym);
getToken(&t);
}
syms.append(inhertSym);
}
if (t.type != ttStartStatementBlock) {
rewindErrorTo(&t);
return syms;
return {inhertSyms, syms};
}
// Parse properties
@ -1581,11 +1603,11 @@ QAsCodeParser::parseClassContent(qsizetype offset, const QByteArrayList &ns,
auto fndef = parseFuncDefContent(ns);
syms.append(fndef);
} else if (isFuncDecl(true)) {
auto fn = parseFunctionMethod();
Symbol sym;
auto fn = parseFunctionMethod(sym.vis);
// add function symbols
Symbol sym;
sym.symtype = fn.type;
sym.symtype = SymbolType::Function;
sym.scope = fn.scope;
sym.offset = fn.offset;
sym.name = fn.name;
@ -1595,7 +1617,8 @@ QAsCodeParser::parseClassContent(qsizetype offset, const QByteArrayList &ns,
// deep parsing
if (offset >= fn.offset && offset < fn.end()) {
auto ss = parseStatementBlock(fn.scope, fn.codes, offset);
auto ss =
parseStatementBlock(fn.scope, fn.codes, offset - fn.offset);
syms.append(ss);
}
} else if (isVirtualPropertyDecl()) {
@ -1609,11 +1632,11 @@ QAsCodeParser::parseClassContent(qsizetype offset, const QByteArrayList &ns,
getToken(&t);
else {
rewindErrorTo(&t);
return syms;
return {inhertSyms, syms};
}
if (_isSyntaxError)
return syms;
return {inhertSyms, syms};
getToken(&t);
rewindTo(&t);
@ -1622,15 +1645,16 @@ QAsCodeParser::parseClassContent(qsizetype offset, const QByteArrayList &ns,
getToken(&t);
if (t.type != ttEndStatementBlock) {
rewindErrorTo(&t);
return syms;
return {inhertSyms, syms};
}
return syms;
return {inhertSyms, syms};
}
QList<QAsCodeParser::Symbol>
QPair<QByteArrayList, QList<QAsCodeParser::Symbol>>
QAsCodeParser::parseInterfaceContent(qsizetype offset, const QByteArrayList &ns,
const QByteArray &code) {
QByteArrayList inhertSyms;
QList<Symbol> syms;
sToken t;
@ -1638,12 +1662,22 @@ QAsCodeParser::parseInterfaceContent(qsizetype offset, const QByteArrayList &ns,
getToken(&t);
// Can optionally have a list of interfaces that are inherited
if (t.type == ttColon) {
parseOptionalScope();
parseIdentifier();
QByteArray inhertSym;
inhertSym = parseOptionalScope().join("::");
if (!inhertSym.isEmpty()) {
inhertSym += "::";
}
inhertSym += getSymbolString(parseIdentifier());
inhertSyms.append(inhertSym);
getToken(&t);
while (t.type == ttListSeparator) {
parseOptionalScope();
parseIdentifier();
inhertSym = parseOptionalScope().join("::");
if (!inhertSym.isEmpty()) {
inhertSym += "::";
}
inhertSym += getSymbolString(parseIdentifier());
inhertSyms.append(inhertSym);
getToken(&t);
}
}
@ -1670,7 +1704,7 @@ QAsCodeParser::parseInterfaceContent(qsizetype offset, const QByteArrayList &ns,
}
if (_isSyntaxError)
return syms;
return {inhertSyms, syms};
getToken(&t);
rewindTo(&t);
@ -1679,10 +1713,10 @@ QAsCodeParser::parseInterfaceContent(qsizetype offset, const QByteArrayList &ns,
getToken(&t);
if (t.type != ttEndStatementBlock) {
rewindErrorTo(&t);
return syms;
return {inhertSyms, syms};
}
return syms;
return {inhertSyms, syms};
}
QAsCodeParser::Symbol QAsCodeParser::parseInterfaceMethod() {

View File

@ -90,6 +90,7 @@ public:
Visiblity vis = QAsCodeParser::Visiblity::Public;
QByteArray additonalInfo; // for other additonal info
QByteArrayList inherit;
QList<Symbol> children;
};
@ -134,7 +135,7 @@ private:
CodeSegment parseInterface();
CodeSegment parseFuncDef();
CodeSegment parseFunction();
CodeSegment parseFunctionMethod();
CodeSegment parseFunctionMethod(Visiblity &vis);
private:
// parse tokens
@ -172,12 +173,13 @@ private:
Symbol parseFuncDefContent(const QByteArrayList &ns);
QList<Symbol> parseClassContent(qsizetype offset, const QByteArrayList &ns,
const QByteArray &code);
QPair<QByteArrayList, QList<Symbol>>
parseClassContent(qsizetype offset, const QByteArrayList &ns,
const QByteArray &code);
QList<Symbol> parseInterfaceContent(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);

View File

@ -121,6 +121,9 @@ EditorView::EditorView(QWidget *parent)
m_metadata->setDocument(doc);
});
connect(&_watcher, &QFileSystemWatcher::fileChanged, this,
&EditorView::need2Reload);
applySettings();
// build up call tables
@ -284,6 +287,9 @@ ErrFile EditorView::newFile(size_t index) {
if (isCloneFile()) {
return ErrFile::ClonedFile;
}
if (!m_fileName.isEmpty()) {
_watcher.removePath(m_fileName);
}
auto istr = QString::number(index);
m_fileName = tr("Untitled") + istr;
this->setWindowTitle(m_fileName);
@ -320,6 +326,10 @@ ErrFile EditorView::openFile(const QString &filename) {
return ErrFile::Permission;
}
if (!m_fileName.isEmpty()) {
_watcher.removePath(m_fileName);
}
m_hex->setDocument(QSharedPointer<QHexDocument>(p));
m_hex->setLockedFile(readonly);
m_hex->setKeepSize(true);
@ -335,6 +345,8 @@ ErrFile EditorView::openFile(const QString &filename) {
auto tab = this->tabWidget();
tab->setIcon(Utilities::getIconFromFile(style(), m_fileName));
tab->setToolTip(m_fileName);
_watcher.addPath(m_fileName);
}
return ErrFile::Success;
@ -372,6 +384,10 @@ ErrFile EditorView::openExtFile(const QString &ext, const QString &file) {
return ErrFile::Error;
}
if (!m_fileName.isEmpty()) {
_watcher.removePath(m_fileName);
}
m_hex->setDocument(QSharedPointer<QHexDocument>(p));
m_hex->setLockedFile(readonly);
m_hex->setKeepSize(true);
@ -562,10 +578,14 @@ ErrFile EditorView::save(const QString &workSpaceName, const QString &path,
file.close();
if (!isExport) {
if (!m_fileName.isEmpty()) {
_watcher.removePath(m_fileName);
}
m_fileName = QFileInfo(fileName).absoluteFilePath();
m_isNewFile = false;
m_docType = DocumentType::File;
doc->setDocSaved();
_watcher.addPath(m_fileName);
}
#ifdef Q_OS_LINUX
adjustPermission();

View File

@ -18,6 +18,7 @@
#ifndef EDITORVIEW_H
#define EDITORVIEW_H
#include <QFileSystemWatcher>
#include <QReadWriteLock>
#include <QStackedWidget>
@ -553,6 +554,8 @@ signals:
void sigOnMetadata();
void sigOnBookMark();
void need2Reload();
protected:
bool eventFilter(QObject *watched, QEvent *event) override;
@ -589,6 +592,7 @@ private:
QReadWriteLock _rwlock;
CallTable _viewFns;
QFileSystemWatcher _watcher;
};
#endif // EDITORVIEW_H

View File

@ -245,7 +245,8 @@ MainWindow::MainWindow(SplashDialog *splash) : FramelessMainWindow() {
sm.registerCallBack(ScriptMachine::Interactive, callbacks);
callbacks.getInputFn = [this]() -> QString {
return WingInputDialog::getText(this, tr(""), tr(""));
return WingInputDialog::getText(this, tr("InputRequest"),
tr("PleaseInput"));
};
callbacks.clearFn = [this]() { m_bgScriptOutput->clear(); };
callbacks.printMsgFn =
@ -3162,6 +3163,22 @@ void MainWindow::connectEditorView(EditorView *editor) {
connect(editor, &EditorView::sigOnPasteHex, this, &MainWindow::on_pastehex);
connect(editor, &EditorView::sigOnPasteFile, this,
&MainWindow::on_pastefile);
editor->setProperty("__RELOAD__", false);
connect(editor, &EditorView::need2Reload, this, [editor, this]() {
if (editor->isBigFile()) {
editor->reload();
}
if (currentEditor() == editor) {
auto ret = WingMessageBox::question(this, tr("Reload"),
tr("ReloadNeededYesOrNo"));
if (ret == QMessageBox::Yes) {
editor->reload();
}
} else {
editor->setProperty("__RELOAD__", true);
}
});
}
void MainWindow::swapEditor(EditorView *old, EditorView *cur) {
@ -3188,6 +3205,15 @@ void MainWindow::swapEditor(EditorView *old, EditorView *cur) {
Q_ASSERT(cur);
auto hexeditor = cur->hexEditor();
auto needReload = cur->property("__RELOAD__").toBool();
if (needReload) {
auto ret = WingMessageBox::question(this, tr("Reload"),
tr("ReloadNeededYesOrNo"));
if (ret == QMessageBox::Yes) {
cur->reload();
}
cur->setProperty("__RELOAD__", false);
}
connect(hexeditor, &QHexView::cursorLocationChanged, this,
&MainWindow::on_locChanged);
connect(hexeditor, &QHexView::cursorSelectionChanged, this,