From 55eba7800e2d1372431b4352104b11dd9f87c6c1 Mon Sep 17 00:00:00 2001 From: Sightem Date: Mon, 13 Oct 2025 11:28:23 -0700 Subject: [PATCH 1/2] Add equate autocomplete to disassembly view's goto dialog --- gui/qt/debugger.cpp | 53 +++++++++++++++++++++++++++++++++++++---- gui/qt/gotodialog.cpp | 17 +++++++++++++ gui/qt/gotodialog.h | 2 ++ gui/qt/mainwindow.h | 6 +++++ gui/qt/memorywidget.cpp | 7 +++--- 5 files changed, 78 insertions(+), 7 deletions(-) diff --git a/gui/qt/debugger.cpp b/gui/qt/debugger.cpp index 3f90a203..dcfd6a15 100644 --- a/gui/qt/debugger.cpp +++ b/gui/qt/debugger.cpp @@ -26,6 +26,7 @@ #include #include +#include #include #include #include @@ -41,6 +42,7 @@ #include #include #include +#include #ifdef _MSC_VER #include @@ -222,6 +224,7 @@ void MainWindow::debugImportFile(const QString &file) { disasm.map.clear(); disasm.reverse.clear(); + markDisasmGotoCompletionsDirty(); for (QString &equFile : m_equateFiles) { equatesAddFile(equFile); } @@ -1887,6 +1890,7 @@ void MainWindow::updateLabels() { void MainWindow::equatesRefresh() { disasm.map.clear(); disasm.reverse.clear(); + markDisasmGotoCompletionsDirty(); for (QString &file : m_equateFiles) { equatesAddFile(file); } @@ -1984,11 +1988,14 @@ void MainWindow::equatesAddEquate(const QString &name, uint32_t address) { if (!equatesAddEquateInternal(name, address)) { return; } + markDisasmGotoCompletionsDirty(); uint8_t *ptr = static_cast(phys_mem_ptr(address - 4, 9)); if (ptr && ptr[4] == 0xC3 && (ptr[0] == 0xC3 || ptr[8] == 0xC3)) { // jump table? uint32_t address2 = ptr[5] | ptr[6] << 8 | ptr[7] << 16; if (phys_mem_ptr(address2, 1)) { - equatesAddEquateInternal(QStringLiteral("_") + name, address2); + if (equatesAddEquateInternal(QStringLiteral("_") + name, address2)) { + markDisasmGotoCompletionsDirty(); + } } } } @@ -2148,14 +2155,15 @@ void MainWindow::gotoPressed() { m_gotoAddr = m_disasm->getSelectedAddr(); } - GotoDialog dlg(m_gotoAddr, m_disasmGotoHistory, this); + GotoDialog dlg(m_gotoAddr, m_disasmGotoHistory, disasmGotoCompletions(), this); if (dlg.exec() == QDialog::Accepted) { QString typed = dlg.text().trimmed(); bool ok = false; QString resolved = resolveAddressOrEquate(typed, &ok); if (ok) { m_gotoAddr = typed; - disasmUpdateAddr(hex2int(resolved), false); + // changes are routed through here to make sure history is updated for fwd and back + gotoDisasmAddr(static_cast(hex2int(resolved))); auto &hist = m_disasmGotoHistory; std::erase_if(hist, [&](const QString &s){ return s.compare(typed, Qt::CaseInsensitive) == 0; }); @@ -2226,6 +2234,43 @@ QAction *MainWindow::gotoMemAction(QMenu *menu, bool vat) const { return gotoMem; } +const QStringList &MainWindow::disasmGotoCompletions() { + if (!m_disasmGotoCompletionsDirty) { + return m_disasmGotoCompletions; + } + + m_disasmGotoCompletionsDirty = false; + + QStringList completions; + static constexpr std::array kRegisterAliases = { + "AF"_L1, "HL"_L1, "DE"_L1, "BC"_L1, "IX"_L1, "IY"_L1, + "AF'"_L1, "HL'"_L1, "DE'"_L1, "BC'"_L1, "SPL"_L1, "SPS"_L1, "PC"_L1 + }; + + completions.reserve(static_cast(disasm.reverse.size() + kRegisterAliases.size())); + + for (const auto &name : disasm.reverse | std::views::keys) { + completions.append(QString::fromStdString(name)); + } + + for (const auto alias : kRegisterAliases) { + if (!completions.contains(alias)) { + completions.append(QString(alias)); + } + } + + std::ranges::sort(completions, [](const QString &lhs, const QString &rhs) { + return lhs.localeAwareCompare(rhs) < 0; + }); + + m_disasmGotoCompletions = std::move(completions); + return m_disasmGotoCompletions; +} + +void MainWindow::markDisasmGotoCompletionsDirty() { + m_disasmGotoCompletionsDirty = true; +} + void MainWindow::handleCtrlClickText(QPlainTextEdit *edit) { if (QApplication::keyboardModifiers().testFlag(Qt::ControlModifier)) { bool ok = true; @@ -2352,7 +2397,7 @@ bool MainWindow::eventFilter(QObject *obj, QEvent *e) { return true; } } - + // Mouse back/forward in Disassembly view if (obj == m_disasm && e->type() == QEvent::MouseButtonPress) { auto *me = static_cast(e); diff --git a/gui/qt/gotodialog.cpp b/gui/qt/gotodialog.cpp index bfd488fa..d65c80a0 100644 --- a/gui/qt/gotodialog.cpp +++ b/gui/qt/gotodialog.cpp @@ -1,13 +1,17 @@ #include "gotodialog.h" #include +#include #include #include #include #include +#include +#include GotoDialog::GotoDialog(const QString &seed, const std::vector &history, + const QStringList &completions, QWidget *parent) : QDialog(parent) { setWindowTitle(tr("Goto")); @@ -18,6 +22,19 @@ GotoDialog::GotoDialog(const QString &seed, m_combo = new QComboBox(this); // NOLINT: Qt parent-ownership handles deletion m_combo->setEditable(true); + m_combo->setInsertPolicy(QComboBox::NoInsert); + + if (!completions.isEmpty()) { + auto *completer = new QCompleter(completions, m_combo); // NOLINT: Qt parent-ownership handles deletion + completer->setCaseSensitivity(Qt::CaseInsensitive); + completer->setFilterMode(Qt::MatchContains); + completer->setCompletionMode(QCompleter::PopupCompletion); + if (auto *lineEdit = m_combo->lineEdit()) { + lineEdit->setCompleter(completer); + } else { + m_combo->setCompleter(completer); + } + } for (const QString &h : history) { m_combo->addItem(h); diff --git a/gui/qt/gotodialog.h b/gui/qt/gotodialog.h index 89dad6d7..b38d4287 100644 --- a/gui/qt/gotodialog.h +++ b/gui/qt/gotodialog.h @@ -3,6 +3,7 @@ #include #include +#include #include QT_BEGIN_NAMESPACE @@ -14,6 +15,7 @@ class GotoDialog : public QDialog { public: explicit GotoDialog(const QString &seed, const std::vector &history, + const QStringList &completions = {}, QWidget *parent = nullptr); [[nodiscard]] QString text() const; diff --git a/gui/qt/mainwindow.h b/gui/qt/mainwindow.h index 81623a71..dc13adf4 100644 --- a/gui/qt/mainwindow.h +++ b/gui/qt/mainwindow.h @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -366,6 +367,8 @@ private slots: void gotoMemAddr(uint32_t addr); HexWidget *gotoMemAddrNoRaise(uint32_t addr); QAction *gotoMemAction(QMenu *menu, bool vat = false) const; + const QStringList &disasmGotoCompletions(); + void markDisasmGotoCompletionsDirty(); void handleCtrlClickText(QPlainTextEdit *edit); void handleCtrlClickLine(QLineEdit *edit); @@ -736,6 +739,9 @@ private slots: std::vector m_memGotoHistory; + QStringList m_disasmGotoCompletions; + bool m_disasmGotoCompletionsDirty = true; + QString m_pathConfig; QMenu *m_menuDocks; QMenu *m_menuDebug; diff --git a/gui/qt/memorywidget.cpp b/gui/qt/memorywidget.cpp index f0d8ea4d..f6f43e3a 100644 --- a/gui/qt/memorywidget.cpp +++ b/gui/qt/memorywidget.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -107,7 +108,7 @@ void MainWindow::memUpdateEdit(HexWidget *edit, bool force) { } void MainWindow::flashGotoPressed() { - if (GotoDialog dlg(m_flashGotoAddr, m_memGotoHistory, this); dlg.exec() == QDialog::Accepted) { + if (GotoDialog dlg(m_flashGotoAddr, m_memGotoHistory, QStringList(), this); dlg.exec() == QDialog::Accepted) { const QString typed = dlg.text().trimmed(); bool ok = false; const QString resolved = resolveAddressOrEquate(typed, &ok); @@ -127,7 +128,7 @@ void MainWindow::flashGotoPressed() { } void MainWindow::ramGotoPressed() { - GotoDialog dlg(m_RamGotoAddr, m_memGotoHistory, this); + GotoDialog dlg(m_RamGotoAddr, m_memGotoHistory, QStringList(), this); if (dlg.exec() == QDialog::Accepted) { const QString typed = dlg.text().trimmed(); bool ok = false; @@ -216,7 +217,7 @@ void MainWindow::memGotoEdit(HexWidget *edit) { return; } - GotoDialog dlg(m_memGotoAddr, m_memGotoHistory, this); + GotoDialog dlg(m_memGotoAddr, m_memGotoHistory, QStringList(), this); if (dlg.exec() == QDialog::Accepted) { QString typed = dlg.text().trimmed(); bool ok = false; From c4bd131566576a4bc8b43b5a32c6a19eecfc09e1 Mon Sep 17 00:00:00 2001 From: Adrien Bertrand Date: Tue, 14 Oct 2025 11:18:11 +0200 Subject: [PATCH 2/2] autocompletions: keep original case of equates. --- gui/qt/debugger.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/gui/qt/debugger.cpp b/gui/qt/debugger.cpp index dcfd6a15..7ded76e4 100644 --- a/gui/qt/debugger.cpp +++ b/gui/qt/debugger.cpp @@ -2247,10 +2247,13 @@ const QStringList &MainWindow::disasmGotoCompletions() { "AF'"_L1, "HL'"_L1, "DE'"_L1, "BC'"_L1, "SPL"_L1, "SPS"_L1, "PC"_L1 }; - completions.reserve(static_cast(disasm.reverse.size() + kRegisterAliases.size())); + completions.reserve(static_cast(disasm.map.size() + kRegisterAliases.size())); - for (const auto &name : disasm.reverse | std::views::keys) { - completions.append(QString::fromStdString(name)); + for (const auto &name : disasm.map | std::views::values) { + const auto qname = QString::fromStdString(name); + if (!completions.contains(qname, Qt::CaseInsensitive)) { + completions.append(qname); + } } for (const auto alias : kRegisterAliases) {