// Copyright 2023 Dolphin Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include "DolphinQt/Debugger/GekkoSyntaxHighlight.h" #include "Common/Assembler/GekkoParser.h" #include #include namespace { using namespace Common::GekkoAssembler; using namespace Common::GekkoAssembler::detail; class HighlightParsePlugin : public ParsePlugin { public: virtual ~HighlightParsePlugin() = default; std::vector>&& MoveParens() { return std::move(m_matched_parens); } std::vector>&& MoveFormatting() { return std::move(m_formatting); } void OnDirectivePre(GekkoDirective) override { HighlightCurToken(HighlightFormat::Directive); } void OnInstructionPre(const ParseInfo&, bool) override { HighlightCurToken(HighlightFormat::Mnemonic); } void OnTerminal(Terminal type, const AssemblerToken& val) override { switch (type) { case Terminal::Id: HighlightCurToken(HighlightFormat::Symbol); break; case Terminal::Hex: case Terminal::Dec: case Terminal::Oct: case Terminal::Bin: case Terminal::Flt: HighlightCurToken(HighlightFormat::Immediate); break; case Terminal::GPR: HighlightCurToken(HighlightFormat::GPR); break; case Terminal::FPR: HighlightCurToken(HighlightFormat::GPR); break; case Terminal::SPR: HighlightCurToken(HighlightFormat::SPR); break; case Terminal::CRField: HighlightCurToken(HighlightFormat::CRField); break; case Terminal::Lt: case Terminal::Gt: case Terminal::Eq: case Terminal::So: HighlightCurToken(HighlightFormat::CRFlag); break; case Terminal::Str: HighlightCurToken(HighlightFormat::Str); break; default: break; } } void OnHiaddr(std::string_view) override { HighlightCurToken(HighlightFormat::Symbol); auto&& [ha_pos, ha_tok] = m_owner->lexer.LookaheadTagRef(2); m_formatting.emplace_back(static_cast(ha_pos.col), static_cast(ha_tok.token_val.length()), HighlightFormat::HaLa); } void OnLoaddr(std::string_view id) override { OnHiaddr(id); } void OnOpenParen(ParenType type) override { m_paren_stack.push_back(static_cast(m_owner->lexer.ColNumber())); } void OnCloseParen(ParenType type) override { if (m_paren_stack.empty()) { return; } m_matched_parens.emplace_back(m_paren_stack.back(), static_cast(m_owner->lexer.ColNumber())); m_paren_stack.pop_back(); } void OnError() override { m_formatting.emplace_back(static_cast(m_owner->error->col), static_cast(m_owner->error->len), HighlightFormat::Error); } void OnLabelDecl(std::string_view name) override { const int len = static_cast(m_owner->lexer.LookaheadRef().token_val.length()); const int off = static_cast(m_owner->lexer.ColNumber()); m_formatting.emplace_back(len, off, HighlightFormat::Symbol); } void OnVarDecl(std::string_view name) override { OnLabelDecl(name); } private: std::vector m_paren_stack; std::vector> m_matched_parens; std::vector> m_formatting; void HighlightCurToken(HighlightFormat format) { const int len = static_cast(m_owner->lexer.LookaheadRef().token_val.length()); const int off = static_cast(m_owner->lexer.ColNumber()); m_formatting.emplace_back(off, len, format); } }; } // namespace void GekkoSyntaxHighlight::highlightBlock(const QString& text) { BlockInfo* info = static_cast(currentBlockUserData()); if (info == nullptr) { info = new BlockInfo; setCurrentBlockUserData(info); } qsizetype comment_idx = text.indexOf(QLatin1Char('#')); if (comment_idx != -1) { HighlightSubstr(comment_idx, text.length() - comment_idx, HighlightFormat::Comment); } if (m_mode == 0) { HighlightParsePlugin plugin; ParseWithPlugin(&plugin, text.toStdString()); info->block_format = plugin.MoveFormatting(); info->parens = plugin.MoveParens(); info->error = std::move(plugin.Error()); info->error_at_eol = info->error && info->error->len == 0; } else if (m_mode == 1) { auto paren_it = std::find_if(info->parens.begin(), info->parens.end(), [this](const std::pair& p) { return p.first == m_cursor_loc || p.second == m_cursor_loc; }); if (paren_it != info->parens.end()) { HighlightSubstr(paren_it->first, 1, HighlightFormat::Paren); HighlightSubstr(paren_it->second, 1, HighlightFormat::Paren); } } for (auto&& [off, len, format] : info->block_format) { HighlightSubstr(off, len, format); } } GekkoSyntaxHighlight::GekkoSyntaxHighlight(QTextDocument* document, QTextCharFormat base_format, bool dark_scheme) : QSyntaxHighlighter(document), m_base_format(base_format) { QPalette base_scheme; m_theme_idx = dark_scheme ? 1 : 0; } void GekkoSyntaxHighlight::HighlightSubstr(int start, int len, HighlightFormat format) { QTextCharFormat hl_format = m_base_format; const QColor DIRECTIVE_COLOR[2] = {QColor(0x9d, 0x00, 0x06), QColor(0xfb, 0x49, 0x34)}; // Gruvbox darkred const QColor MNEMONIC_COLOR[2] = {QColor(0x79, 0x74, 0x0e), QColor(0xb8, 0xbb, 0x26)}; // Gruvbox darkgreen const QColor IMM_COLOR[2] = {QColor(0xb5, 0x76, 0x14), QColor(0xfa, 0xbd, 0x2f)}; // Gruvbox darkyellow const QColor BUILTIN_COLOR[2] = {QColor(0x07, 0x66, 0x78), QColor(0x83, 0xa5, 0x98)}; // Gruvbox darkblue const QColor HA_LA_COLOR[2] = {QColor(0xaf, 0x3a, 0x03), QColor(0xfe, 0x80, 0x19)}; // Gruvbox darkorange const QColor HOVER_BG_COLOR[2] = {QColor(0xd5, 0xc4, 0xa1), QColor(0x50, 0x49, 0x45)}; // Gruvbox bg2 const QColor STRING_COLOR[2] = {QColor(0x98, 0x97, 0x1a), QColor(0x98, 0x97, 0x1a)}; // Gruvbox green const QColor COMMENT_COLOR[2] = {QColor(0x68, 0x9d, 0x6a), QColor(0x68, 0x9d, 0x6a)}; // Gruvbox aqua switch (format) { case HighlightFormat::Directive: hl_format.setForeground(DIRECTIVE_COLOR[m_theme_idx]); break; case HighlightFormat::Mnemonic: hl_format.setForeground(MNEMONIC_COLOR[m_theme_idx]); break; case HighlightFormat::Symbol: break; case HighlightFormat::Immediate: hl_format.setForeground(IMM_COLOR[m_theme_idx]); break; case HighlightFormat::GPR: hl_format.setForeground(BUILTIN_COLOR[m_theme_idx]); break; case HighlightFormat::FPR: hl_format.setForeground(BUILTIN_COLOR[m_theme_idx]); break; case HighlightFormat::SPR: hl_format.setForeground(BUILTIN_COLOR[m_theme_idx]); break; case HighlightFormat::CRField: hl_format.setForeground(BUILTIN_COLOR[m_theme_idx]); break; case HighlightFormat::CRFlag: hl_format.setForeground(BUILTIN_COLOR[m_theme_idx]); break; case HighlightFormat::Str: hl_format.setForeground(STRING_COLOR[m_theme_idx]); break; case HighlightFormat::HaLa: hl_format.setForeground(HA_LA_COLOR[m_theme_idx]); break; case HighlightFormat::Paren: hl_format.setBackground(HOVER_BG_COLOR[m_theme_idx]); break; case HighlightFormat::Default: hl_format.clearForeground(); hl_format.clearBackground(); break; case HighlightFormat::Comment: hl_format.setForeground(COMMENT_COLOR[m_theme_idx]); break; case HighlightFormat::Error: hl_format.setUnderlineColor(Qt::red); hl_format.setUnderlineStyle(QTextCharFormat::WaveUnderline); break; } setFormat(start, len, hl_format); }