From 4330b7ddaad7f89def86f9782b98111c75d02c88 Mon Sep 17 00:00:00 2001 From: Properrr Date: Fri, 15 Dec 2017 02:52:47 -0800 Subject: [PATCH] Implemented syntax highlighter (#220) * Implemented syntaxHighLighter * removed PseudoWidget.ui * buildfix * reverted r2-submodule * Added nowrap to the pseudocodeWidget * changed color of functions in the pseudocode highlighter --- src/cutter.pro | 3 +- src/utils/SyntaxHighlighter.cpp | 102 +++++++++++++++++++++++++++++++ src/utils/SyntaxHighlighter.h | 34 +++++++++++ src/widgets/PseudocodeWidget.cpp | 33 +++++----- src/widgets/PseudocodeWidget.h | 24 ++++---- src/widgets/PseudocodeWidget.ui | 27 -------- 6 files changed, 168 insertions(+), 55 deletions(-) create mode 100644 src/utils/SyntaxHighlighter.cpp create mode 100644 src/utils/SyntaxHighlighter.h delete mode 100644 src/widgets/PseudocodeWidget.ui diff --git a/src/cutter.pro b/src/cutter.pro index 4e256e3f..1670b977 100644 --- a/src/cutter.pro +++ b/src/cutter.pro @@ -78,6 +78,7 @@ SOURCES += \ dialogs/SaveProjectDialog.cpp \ utils/TempConfig.cpp \ utils/SvgIconEngine.cpp \ + utils/SyntaxHighlighter.cpp \ widgets/PseudocodeWidget.cpp \ widgets/VisualNavbar.cpp \ widgets/GraphView.cpp \ @@ -131,6 +132,7 @@ HEADERS += \ dialogs/SaveProjectDialog.h \ utils/TempConfig.h \ utils/SvgIconEngine.h \ + utils/SyntaxHighlighter.h \ widgets/PseudocodeWidget.h \ widgets/VisualNavbar.h \ widgets/GraphView.h \ @@ -165,7 +167,6 @@ FORMS += \ widgets/SidebarWidget.ui \ widgets/HexdumpWidget.ui \ dialogs/SaveProjectDialog.ui \ - widgets/PseudocodeWidget.ui \ dialogs/preferences/PreferencesDialog.ui \ dialogs/preferences/GeneralOptionsWidget.ui diff --git a/src/utils/SyntaxHighlighter.cpp b/src/utils/SyntaxHighlighter.cpp new file mode 100644 index 00000000..51996e98 --- /dev/null +++ b/src/utils/SyntaxHighlighter.cpp @@ -0,0 +1,102 @@ +#include "SyntaxHighlighter.h" + +SyntaxHighlighter::SyntaxHighlighter(QTextDocument *parent) + : QSyntaxHighlighter(parent) + , commentStartExpression("/\\*") + , commentEndExpression("\\*/") +{ + HighlightingRule rule; + QStringList keywordPatterns; + + //C language keywords + keywordPatterns << "\\bauto\\b" << "\\bdouble\\b" << "\\bint\\b" + << "\\bstruct\\b" << "\\bbreak\\b" << "\\belse\\b" + << "\\blong\\b" << "\\switch\\b" << "\\bcase\\b" + << "\\benum\\b" << "\\bregister\\b" << "\\btypedef\\b" + << "\\bchar\\b" << "\\bextern\\b" << "\\breturn\\b" + << "\\bunion\\b" << "\\bconst\\b" << "\\bfloat\\b" + << "\\bshort\\b" << "\\bunsigned\\b" << "\\bcontinue\\b" + << "\\bfor\\b" << "\\bsigned\\b" << "\\bvoid\\b" + << "\\bdefault\\b" << "\\bgoto\\b" << "\\bsizeof\\b" + << "\\bvolatile\\b" << "\\bdo\\b" << "\\bif\\b" + << "\\static\\b" << "\\while\\b"; + //Special words + keywordPatterns << "\\bloc_*\\b" << "\\bsym.*\\b"; + + QTextCharFormat keywordFormat; + keywordFormat.setForeground(Qt::red); + keywordFormat.setFontWeight(QFont::Bold); + + for( const auto& pattern : keywordPatterns ) + { + rule.pattern.setPattern(pattern); + rule.format = keywordFormat; + highlightingRules.append(rule); + } + + //Functions + rule.pattern.setPattern("\\b[A-Za-z0-9_]+(?=\\()"); + rule.format.clearBackground(); + rule.format.clearForeground(); + rule.format.setFontItalic(true); + rule.format.setForeground(Qt::darkCyan); + highlightingRules.append(rule); + + //single-line comment + rule.pattern.setPattern("//[^\n]*"); + rule.format.clearBackground(); + rule.format.clearForeground(); + rule.format.setForeground(Qt::gray); + highlightingRules.append(rule); + + //quotation + rule.pattern.setPattern("\".*\""); + rule.format.clearBackground(); + rule.format.clearForeground(); + rule.format.setForeground(Qt::darkGreen); + highlightingRules.append(rule); + + multiLineCommentFormat.setForeground(Qt::gray); +} + +void SyntaxHighlighter::highlightBlock(const QString&text) +{ + for( const auto& it : highlightingRules ) + { + auto matchIterator = it.pattern.globalMatch(text); + while (matchIterator.hasNext()) + { + const auto match = matchIterator.next(); + setFormat(match.capturedStart(), match.capturedLength(), it.format); + } + } + + setCurrentBlockState(0); + + int startIndex = 0; + if (previousBlockState() != 1) + { + startIndex = text.indexOf(commentStartExpression); + } + + while (startIndex >= 0) + { + const auto match = commentEndExpression.match(text, startIndex); + const int endIndex = match.capturedStart(); + int commentLength = 0; + + if (endIndex == -1) + { + setCurrentBlockState(1); + commentLength = text.length() - startIndex; + } + else + { + commentLength = endIndex - startIndex + + match.capturedLength(); + } + + setFormat(startIndex, commentLength, multiLineCommentFormat); + startIndex = text.indexOf(commentStartExpression, startIndex + commentLength); + } +} diff --git a/src/utils/SyntaxHighlighter.h b/src/utils/SyntaxHighlighter.h new file mode 100644 index 00000000..c2530d71 --- /dev/null +++ b/src/utils/SyntaxHighlighter.h @@ -0,0 +1,34 @@ +#pragma once + +#include +#include +#include +#include +#include + + +class SyntaxHighlighter : public QSyntaxHighlighter +{ + Q_OBJECT + +public: + SyntaxHighlighter(QTextDocument *parent = nullptr); + virtual ~SyntaxHighlighter() = default; + +protected: + void highlightBlock(const QString &text) override; + +private: + struct HighlightingRule + { + QRegularExpression pattern; + QTextCharFormat format; + }; + + QVector highlightingRules; + + QRegularExpression commentStartExpression; + QRegularExpression commentEndExpression; + + QTextCharFormat multiLineCommentFormat; +}; diff --git a/src/widgets/PseudocodeWidget.cpp b/src/widgets/PseudocodeWidget.cpp index 4d84c327..61731711 100644 --- a/src/widgets/PseudocodeWidget.cpp +++ b/src/widgets/PseudocodeWidget.cpp @@ -1,18 +1,23 @@ #include "PseudocodeWidget.h" -#include "ui_PseudocodeWidget.h" + +#include #include "utils/Configuration.h" #include "utils/Helpers.h" +#include "utils/SyntaxHighlighter.h" #include "utils/TempConfig.h" -PseudocodeWidget::PseudocodeWidget(QWidget *parent, Qt::WindowFlags flags) : - QDockWidget(parent, flags), - ui(new Ui::PseudocodeWidget) +PseudocodeWidget::PseudocodeWidget(QWidget *parent, Qt::WindowFlags flags) + : QDockWidget(parent, flags) + , textEditWidget(new QTextEdit(this)) + , syntaxHighLighter( new SyntaxHighlighter(textEditWidget->document())) { - ui->setupUi(this); - + textEditWidget->setParent(this); + setWidget(textEditWidget); setupFonts(); colorsUpdatedSlot(); + textEditWidget->setReadOnly(true); + textEditWidget->setLineWrapMode(QTextEdit::NoWrap); connect(Config(), SIGNAL(fontsUpdated()), this, SLOT(fontsUpdated())); connect(Config(), SIGNAL(colorsUpdated()), this, SLOT(colorsUpdatedSlot())); @@ -27,8 +32,6 @@ PseudocodeWidget::PseudocodeWidget(QWidget *parent, Qt::WindowFlags flags) : }); - - connect(Core(), SIGNAL(seekChanged(RVA)), this, SLOT(on_seekChanged(RVA))); connect(Core(), SIGNAL(raisePrioritizedMemoryWidget(CutterCore::MemoryWidgetType)), this, SLOT(raisePrioritizedMemoryWidget(CutterCore::MemoryWidgetType))); connect(this, &QDockWidget::visibilityChanged, this, [](bool visibility) { @@ -62,12 +65,13 @@ void PseudocodeWidget::on_seekChanged(RVA addr) void PseudocodeWidget::refresh(RVA addr) { - QString decompiledCode = Core()->getDecompiledCode(addr); + const QString& decompiledCode = Core()->getDecompiledCode(addr); if (decompiledCode.length() == 0) { - decompiledCode = tr("Cannot decompile at") + " " + RAddressString(addr) + " " + tr("(Not a function?)"); + textEditWidget->setText(tr("Cannot decompile at") + " " + RAddressString(addr) + " " + tr("(Not a function?)")); + return; } - ui->pseudocodeTextBrowser->setText(decompiledCode); + textEditWidget->setText(decompiledCode); } void PseudocodeWidget::refreshPseudocode() @@ -86,7 +90,7 @@ void PseudocodeWidget::raisePrioritizedMemoryWidget(CutterCore::MemoryWidgetType void PseudocodeWidget::setupFonts() { QFont font = Config()->getFont(); - ui->pseudocodeTextBrowser->setFont(font); + textEditWidget->setFont(font); } void PseudocodeWidget::fontsUpdated() @@ -96,8 +100,9 @@ void PseudocodeWidget::fontsUpdated() void PseudocodeWidget::colorsUpdatedSlot() { - QString styleSheet = QString("QTextBrowser { background-color: %1; color: %2; }") + const QString textEditClassName(textEditWidget->metaObject()->className()); + QString styleSheet = QString(textEditClassName + " { background-color: %1; color: %2; }") .arg(ConfigColor("gui.background").name()) .arg(ConfigColor("btext").name()); - ui->pseudocodeTextBrowser->setStyleSheet(styleSheet); + textEditWidget->setStyleSheet(styleSheet); } diff --git a/src/widgets/PseudocodeWidget.h b/src/widgets/PseudocodeWidget.h index 8b7434b0..79141373 100644 --- a/src/widgets/PseudocodeWidget.h +++ b/src/widgets/PseudocodeWidget.h @@ -4,13 +4,11 @@ #include #include -#include "ui_PseudocodeWidget.h" #include "cutter.h" -namespace Ui -{ - class PseudocodeWidget; -} + +class QTextEdit; +class SyntaxHighlighter; class PseudocodeWidget : public QDockWidget { @@ -21,20 +19,20 @@ public: explicit PseudocodeWidget(QWidget *parent = nullptr, Qt::WindowFlags flags = 0); ~PseudocodeWidget(); -private: - std::unique_ptr ui; - void refresh(RVA addr); - void setupFonts(); - -signals: - -public slots: private slots: void on_seekChanged(RVA addr); void raisePrioritizedMemoryWidget(CutterCore::MemoryWidgetType type); void fontsUpdated(); void colorsUpdatedSlot(); void refreshPseudocode(); + +private: + void refresh(RVA addr); + void setupFonts(); + +private: + QTextEdit* textEditWidget; + SyntaxHighlighter* syntaxHighLighter; }; #endif // PSEUDOCODEWIDGET_H diff --git a/src/widgets/PseudocodeWidget.ui b/src/widgets/PseudocodeWidget.ui deleted file mode 100644 index 6dc1c10b..00000000 --- a/src/widgets/PseudocodeWidget.ui +++ /dev/null @@ -1,27 +0,0 @@ - - - PseudocodeWidget - - - - 0 - 0 - 686 - 646 - - - - 0 - - - Form - - - - QTextEdit::NoWrap - - - - - -