mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2024-11-15 13:57:57 -07:00
100242a380
Same behavior, but without as much duplication.
254 lines
7.6 KiB
C++
254 lines
7.6 KiB
C++
// Copyright 2023 Dolphin Emulator Project
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
#include "DolphinQt/Debugger/GekkoSyntaxHighlight.h"
|
|
|
|
#include "Common/Assembler/GekkoParser.h"
|
|
|
|
#include <QLabel>
|
|
#include <QPalette>
|
|
|
|
namespace
|
|
{
|
|
using namespace Common::GekkoAssembler;
|
|
using namespace Common::GekkoAssembler::detail;
|
|
|
|
class HighlightParsePlugin : public ParsePlugin
|
|
{
|
|
public:
|
|
virtual ~HighlightParsePlugin() = default;
|
|
|
|
std::vector<std::pair<int, int>>&& MoveParens() { return std::move(m_matched_parens); }
|
|
std::vector<std::tuple<int, int, HighlightFormat>>&& 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::FPR);
|
|
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<int>(ha_pos.col),
|
|
static_cast<int>(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<int>(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<int>(m_owner->lexer.ColNumber()));
|
|
m_paren_stack.pop_back();
|
|
}
|
|
|
|
void OnError() override
|
|
{
|
|
m_formatting.emplace_back(static_cast<int>(m_owner->error->col),
|
|
static_cast<int>(m_owner->error->len), HighlightFormat::Error);
|
|
}
|
|
|
|
void OnLabelDecl(std::string_view name) override
|
|
{
|
|
const int len = static_cast<int>(m_owner->lexer.LookaheadRef().token_val.length());
|
|
const int off = static_cast<int>(m_owner->lexer.ColNumber());
|
|
m_formatting.emplace_back(len, off, HighlightFormat::Symbol);
|
|
}
|
|
|
|
void OnVarDecl(std::string_view name) override { OnLabelDecl(name); }
|
|
|
|
private:
|
|
std::vector<int> m_paren_stack;
|
|
std::vector<std::pair<int, int>> m_matched_parens;
|
|
std::vector<std::tuple<int, int, HighlightFormat>> m_formatting;
|
|
|
|
void HighlightCurToken(HighlightFormat format)
|
|
{
|
|
const int len = static_cast<int>(m_owner->lexer.LookaheadRef().token_val.length());
|
|
const int off = static_cast<int>(m_owner->lexer.ColNumber());
|
|
m_formatting.emplace_back(off, len, format);
|
|
}
|
|
};
|
|
} // namespace
|
|
|
|
void GekkoSyntaxHighlight::highlightBlock(const QString& text)
|
|
{
|
|
BlockInfo* info = static_cast<BlockInfo*>(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<int, int>& 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:
|
|
case HighlightFormat::FPR:
|
|
case HighlightFormat::SPR:
|
|
case HighlightFormat::CRField:
|
|
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);
|
|
}
|