From 1d692a6770a532443c5ef234a038f1cd2f5bcace Mon Sep 17 00:00:00 2001 From: Garen Wang Date: Sat, 26 Jun 2021 22:52:25 +0800 Subject: [PATCH 1/4] add comment about rope data structure --- include/ui/rope.hpp | 65 ++++++++++++++++++++++++++++----------------- src/ui/rope.cpp | 27 ++++++++++++++++--- 2 files changed, 64 insertions(+), 28 deletions(-) mode change 100755 => 100644 include/ui/rope.hpp mode change 100755 => 100644 src/ui/rope.cpp diff --git a/include/ui/rope.hpp b/include/ui/rope.hpp old mode 100755 new mode 100644 index 8683439..53f2287 --- a/include/ui/rope.hpp +++ b/include/ui/rope.hpp @@ -38,7 +38,7 @@ struct BufferSlice { - +/* Node of rope */ class Node { public: /* shallow free */ @@ -57,8 +57,9 @@ class Node { virtual Node *insert(const Buffer *, int pos, int len, int k, Attr attr) = 0; virtual Node *remove(int pos, int len) = 0; virtual Node *setAttr(int pos, int len, Attr attr) = 0; - + /* Get an index for the position with a particular row and column */ virtual int seek_pos(int row, int col) const = 0; + /* Get an ordered pair, namely row and column for the position with a particular index */ virtual QPair get_pos(int idx) const = 0; virtual void query(QList &collector, int pos, int len) const = 0; RopeIter iterAt(int pos); @@ -67,11 +68,20 @@ class Node { virtual RopeIter iterAt(int pos, QList *left, QList *right) = 0; protected: + /* the total number of lines in the subtree of this node. */ int m_lines; + /* the index of the end position of the first row of content stored in the subtree of this node */ int m_first_row; + /* the index of the end position of the last row of content stored in the subtree of this node */ + /* [m_first_row] and [m_last_row] are used to maintain [m_max_col]. */ int m_last_row; + /* the maximum column in the subtree of this node + * used to support cursor movement */ int m_max_col; + /* the total number of leaf in the subtree of this node. + * used to keep balance. */ int m_size; + /* the length of characters stored in the subtree of this node. */ int m_length; friend class Leaf; @@ -80,6 +90,7 @@ friend class RopeIter; }; + class Leaf : public Node { public: Leaf(); @@ -101,7 +112,9 @@ class Leaf : public Node { private: const Buffer *m_buf; + /* [m_begin] represent the begining point of the content stored in this leaf on the buffer */ int m_begin; + /* [m_attr] represents the style of the content */ Attr m_attr; }; @@ -151,8 +164,11 @@ class RopeIter { void next(); private: + /* store the nodes along the path while getting rope iterator + * used to find prev or next */ QList *m_left, *m_right; BufferSlice m_slice; + /* [m_cursor] points to current character of the leaf.*/ int m_cursor; friend class Leaf; @@ -172,40 +188,40 @@ struct RopeOperation { class Rope { public: - Rope(); - ~Rope(); + Rope(); + ~Rope(); - int length() const; - int lines() const; - int maxCol() const; + int length() const; + int lines() const; + int maxCol() const; - void insert( - const Buffer *, - int pos, int len, - int k, Attr attr, - bool forget = false - ); - void insert(const QString &, int k, Attr attr); - void insert(QChar c, int pos, Attr attr); + void insert( + const Buffer *, + int pos, int len, + int k, Attr attr, + bool forget = false + ); + void insert(const QString &, int k, Attr attr); + void insert(QChar c, int pos, Attr attr); - void remove(int pos, int len, bool forget = false); + void remove(int pos, int len, bool forget = false); - void setAttr(int pos, int len, Attr attr); + void setAttr(int pos, int len, Attr attr); int seek_pos(int row, int col) const; QPair get_pos(int idx) const; RopeIter iterAt(int pos); - QList query(int pos, int len) const; + QList query(int pos, int len) const; - void changeAttr(Attr attr, int begin, int end); + void changeAttr(Attr attr, int begin, int end); - void maintainInterval(Interval *); - void forgetInterval(Interval *); + void maintainInterval(Interval *); + void forgetInterval(Interval *); - void undo(); - void redo(); - void save_operation(); + void undo(); + void redo(); + void save_operation(); private: void playOperation(const RopeOperation &op); @@ -221,6 +237,7 @@ class Rope { QList> m_undo; QList> m_redo; + /* used to store the operation after insert or remove operation */ QList m_pending; QList m_intervals; diff --git a/src/ui/rope.cpp b/src/ui/rope.cpp old mode 100755 new mode 100644 index c782dd5..9222f4f --- a/src/ui/rope.cpp +++ b/src/ui/rope.cpp @@ -1,5 +1,7 @@ #include "rope.hpp" +/* There exist three cases when inserting new characters, + * namely the insert position is before / in / behind the interval. */ void Interval::ins(int pos, int len) { if(pos <= begin) { begin += len; @@ -156,6 +158,8 @@ Node *Leaf::insert(const Buffer *buf, int pos, int len, int k, Attr attr) { Node *Leaf::remove(int pos, int len) { + /* If the interval to be deleted covers the content stored on the leaf, + * we will not delete this leaf immediately, instead, we set the length of this leaf to be 0 */ if (pos == 0) { m_begin += len; m_length = qMax(0, m_length - len); @@ -167,6 +171,9 @@ Node *Leaf::remove(int pos, int len) { this->updateRowInfo(); return this; } + /* If the content stored in the leaf covers the interval to be delete, + * we must split the leaf into two leaves, and create a new branch whose + * sub-nodes store the original content of left part and right part respectively */ else { Leaf *left = new Leaf(m_buf, m_begin, pos, m_attr); Leaf *right = new Leaf(m_buf, pos + len, m_length - pos - len, m_attr); @@ -213,7 +220,7 @@ Node *Leaf::setAttr(int pos, int len, Attr attr) { int Leaf::seek_pos(int row, int col) const { if (row > 0) return m_length; - + int last_pos = m_lines > 0 ? m_length - 2 : m_length - 1; return qMax(0, qMin(col, last_pos)); } @@ -287,6 +294,8 @@ void Branch::pushup() { m_last_row = m_left->length() + rlr; m_max_col = qMax(m_left->maxCol(), m_right->maxCol()); + /* The longest row may be stored in both two sub-nodes. + * In this case, we need to calculate its length based on [llr] and [rfr] */ int mid_col_len = (rfr == -1 ? m_length : m_left->length() + rfr) - llr - 1; @@ -316,9 +325,11 @@ void Branch::maintain() { } } +/* Since all the information is stored in the leaf, we need to continuously + * recurse to the sub-nodes until the leaf node. After inserting the buffer + * on the leaf, all nodes along the path need to be maintained. */ Node *Branch::insert(const Buffer *buf, int pos, int len, int k, Attr attr) { int left_len = m_left->length(); - if (k <= left_len) m_left = m_left->insert(buf, pos, len, k, attr); else @@ -329,6 +340,8 @@ Node *Branch::insert(const Buffer *buf, int pos, int len, int k, Attr attr) { return this; } + + Node *Branch::remove(int pos, int len) { int left_len = m_left->length(); if (pos < left_len) { @@ -338,7 +351,9 @@ Node *Branch::remove(int pos, int len) { int start = qMax(0, pos - left_len); m_right = m_right->remove(start, pos + len - start - left_len); } - + /* Rope is a complete binary tree, if the length of one of the leaves + * is equal to 0, which means it has been deleted, then another leaf + * will replace their father node. */ if (m_left->length() == 0) { delete m_left; auto r = m_right; @@ -379,13 +394,16 @@ int Branch::seek_pos(int row, int col) const { int ridx = m_right->seek_pos(row - m_left->lines(), col); return ridx + m_left->length(); } - + /* Case3: [row] == [m_left->lines] + * [row_s] is the beginning point of the finding row + * [row_e] is the endpoint of the finding row */ int row_s = m_left->lastRow() + 1; int row_e = m_right->firstRow(); row_e = row_e == -1 ? m_length : m_left->length() + row_e; return row_s + qMax(0, qMin(col, row_e - row_s - 1)); } + QPair Branch::get_pos(int idx) const { int left_len = m_left->length(); @@ -645,6 +663,7 @@ void Rope::redo() { } } + void Rope::save_operation() { if (m_pending.isEmpty()) return; From d194fef1c138f277641afd28d2da99ba49c8afdb Mon Sep 17 00:00:00 2001 From: Garen Wang Date: Sun, 27 Jun 2021 12:17:14 +0800 Subject: [PATCH 2/4] add comments --- include/pierc/pierc.h | 8 +- include/shl/builtin.h | 30 ++++-- include/ui/actions.hpp | 33 ++++++- include/ui/config.hpp | 12 +++ include/ui/libs.hpp | 1 + include/ui/modes.hpp | 11 +++ include/ui/rope.hpp | 0 include/ui/textArea.hpp | 41 +++++++- include/ui/window.hpp | 11 +++ src/shl/presets.cpp | 2 - src/ui/actions.cpp | 12 ++- src/ui/config.cpp | 1 + src/ui/main.cpp | 7 +- src/ui/modes.cpp | 29 ++++-- src/ui/rope.cpp | 0 src/ui/textArea.cpp | 205 +++++++++++++++++++++++++++++++++++----- src/ui/window.cpp | 8 ++ 17 files changed, 358 insertions(+), 53 deletions(-) mode change 100644 => 100755 include/ui/rope.hpp mode change 100644 => 100755 src/ui/rope.cpp diff --git a/include/pierc/pierc.h b/include/pierc/pierc.h index e5c9572..9f36eb9 100644 --- a/include/pierc/pierc.h +++ b/include/pierc/pierc.h @@ -5,16 +5,22 @@ #include namespace pierc { + /* a special type of parser, only used to parse .pierc */ typedef peg_parser::ParserGenerator ConfigParser; + /* store the name of the font */ extern std::string fontName; + + /* store the size of the font */ extern int fontSize; + /* configure the parser after initialization */ void config(ConfigParser &parser); - // can use only once at the beginning when pie starts execution + /* used only once at the beginning when pie starts its execution */ void gatherInfo(); + /* retrieve the font family in format of *QFont* */ QFont getFont(); }// namespace pierc diff --git a/include/shl/builtin.h b/include/shl/builtin.h index c1f500a..0ec9e7f 100644 --- a/include/shl/builtin.h +++ b/include/shl/builtin.h @@ -19,12 +19,14 @@ using Attr = Style; namespace shl { + /* types of languages under syntax highlighting support */ enum class LanguageType { CPP, JAVA, PYTHON, }; + /* a pair of syntax highlighting message, including indexes and corresponding style attributes */ class SyntaxHighlightInfo { public: mutable int idx = 0; @@ -33,51 +35,65 @@ namespace shl { explicit SyntaxHighlightInfo(int idx, Attr attr); }; + /* act as the return type of each semantic action */ typedef std::vector SyntaxHighlightInfos; using Parser = peg_parser::ParserGenerator; using ParserBuilder = peg_parser::ParserGenerator, Parser &>; class Colors { private: - // not wise to use *unordered_set* here, try *unordered_map* + /* map color names to corresponding QFont objects */ std::unordered_map colors; public: - // get expression for PEG parser grammar + // get expression in parsing expression grammar std::string getExpr(); - // hope all string in `colors` are lowercase + // all strings in `colors` are expected to be lowercase void append(const std::string &color, const QColor &_color); bool exist(const std::string &str); QColor get(const std::string &str); }; + /* return a ParserBuilder that have finished configuration */ ParserBuilder *generateParserBuilder(Colors &colors, Rope *); + /* check all colors under support */ Colors getPredefinedColors(); + /* receive path of shl file, return a parser generated according to the grammar of shl file */ std::pair generateParserFromSHL(const std::string &filename, Rope *rope); + /* the initial configuration to a new ParserBuilder */ void initParserBuilder(ParserBuilder &g, Colors &colors, Rope *, bool render = false); - extern std::vector indentDepth; - - extern std::vector> blockRecords; + /* debug messages when indent-aware feature is enable */ + extern std::vector indentDepth; // elements are the number of spaces as indent + extern std::vector> blockRecords; // record the beginning and the end line of an indent block + /* teach a ParserBuilder about the grammar of Syntax Highlighting Language */ void initSHLGrammar(ParserBuilder &g); + /* find out the positional interval in text, from which line to which line */ std::pair getLineNumber(std::string_view input, std::pair blockRecord); + /* return the real index described in shl language, given the size */ int getIdx(const std::string &str, int size); + /* check if all the letters in the string are lowercase */ bool isLowercase(const std::string &str); + /* generate the corresponding language parser */ + /* user can directly invoke this API to get the final parser */ std::pair generateLanguageParser(LanguageType languageType, Rope *); }// namespace shl +/* FilePath is designed to make file path experience consistent across Windows and Linux, + * without hardcoding the whole path */ class FilePath { private: std::string pathString; // stores the actual path on different system + /* get separator, determined in runtime */ const static char _separator = #if defined _WIN32 || defined __CYGWIN__ '\\'; @@ -86,10 +102,12 @@ class FilePath { #endif public: + /* allow multiple strings as arguments */ explicit FilePath(int n_args, ...); ~FilePath() = default; + /* reconstruct the path if the path needs to modifying */ static std::string constructFileString(int n_args, ...); std::string getPathString() const; diff --git a/include/ui/actions.hpp b/include/ui/actions.hpp index de06d8c..71762d3 100755 --- a/include/ui/actions.hpp +++ b/include/ui/actions.hpp @@ -3,24 +3,42 @@ #include "libs.hpp" #include "rope.hpp" +/* primitive text objects that indicate some part of certain text */ enum PrimitveObject { + /* current selected text */ Selection, + /* NextH(PrevH) the char on the right(left) of current cursor */ NextH, PrevH, + /* NextV(PrevV) the char on the bottom(top) of current cursor */ NextV, PrevV, + /* NextW(PrevW) the word on the right(left) of current cursor */ NextW, PrevW, + /* The whole line(s) containing current selection */ CurLine, + /* Current line, or the next one if current one is already + * fully selected */ Line, + /* Begin/End of the line containing current cursor */ LineBeg, LineEnd, + /* The region between a char and its matching one, + * for example '(' and ')', '[' and ']' */ Match }; +/* An object of some derived class of [ActionVisitor] is something + * capable to perform all allowed actions */ class ActionVisitor { public: + /* Return the interval corresponding to the text object */ virtual Interval getObject(PrimitveObject) = 0; + /* insert a character before current cursor position */ virtual void insertChar(QChar c) = 0; + /* set the selection to be the given interval */ virtual void setSelection(Interval) = 0; + /* expand the selection so that it contains the given interval */ virtual void expandSelection(Interval) = 0; + /* delete an interval from the text */ virtual void delInterval(Interval) = 0; virtual void pushCount(int digit) = 0; @@ -32,13 +50,16 @@ class ActionVisitor { }; +/* An abstract base class for all possible actions. + * Using the Visitor Model to implement */ class Action { public: virtual void accept(ActionVisitor *) = 0; - virtual ~Action() {}; + virtual ~Action() = default; }; +/* The action that modifies current selection */ class ModifySelection: public Action { public: ModifySelection(bool expand, PrimitveObject); @@ -51,7 +72,7 @@ class ModifySelection: public Action { PrimitveObject m_obj; }; - +/* The action that insert a single char */ class InsertChar : public Action { public: InsertChar(QChar c); @@ -62,21 +83,26 @@ class InsertChar : public Action { QChar m_char; }; +/* The action that deletes the char before current cursor */ class Backspace : public Action { public: void accept(ActionVisitor *) override; }; +/* The action that inserts a newline at the end of current line, + * and enter "insert" mode */ class InsertNewline : public Action { public: void accept(ActionVisitor *) override; }; +/* The action that deletes current selection */ class DelSelection : public Action { public: void accept(ActionVisitor *) override; }; +/* The action that deletes current selection and enters insert mode */ class ReplaceSelection : public Action { public: void accept(ActionVisitor *) override; @@ -91,16 +117,19 @@ class PushCountChar : public Action { int m_count; }; +/* The action that undo the last operation */ class Undo : public Action { public: void accept(ActionVisitor *) override; }; +/* The action that redo the last undone operation */ class Redo : public Action { public: void accept(ActionVisitor *) override; }; +/* The action that switch current mode */ class SetMode : public Action { public: SetMode(QString); diff --git a/include/ui/config.hpp b/include/ui/config.hpp index ae9ee5c..a27485c 100755 --- a/include/ui/config.hpp +++ b/include/ui/config.hpp @@ -1,16 +1,28 @@ #pragma once + +/* This file contains possible settings of pie */ #include "libs.hpp" +/* The [Style] type records how a character should be + * displayed. */ struct Style { + /* foreground and background color */ QColor fg, bg; + /* whether the character should be bold/italic */ bool bold, italic; + /* underline: a line is drawn on the bottom of the character + * strikethrough: a line is drawn on the middle of the character */ bool underline, strikethrough; bool operator==(const Style &) const; bool operator!=(const Style &) const; + /* Builtin styles used by pie internally */ + /* default style for characters */ static Style default_style; + /* the style for the cursor */ static Style cursor_style; + /* the style for selected chars other than the cursor */ static Style selection_style; }; diff --git a/include/ui/libs.hpp b/include/ui/libs.hpp index cc2bddb..8914fdf 100755 --- a/include/ui/libs.hpp +++ b/include/ui/libs.hpp @@ -1,5 +1,6 @@ #pragma once +/* This file contains the necessary external dependencies */ #include #include #include diff --git a/include/ui/modes.hpp b/include/ui/modes.hpp index ab0f23f..e19a781 100755 --- a/include/ui/modes.hpp +++ b/include/ui/modes.hpp @@ -3,6 +3,8 @@ #include "libs.hpp" #include "actions.hpp" +/* An object of [Mode] represents a text editing mode + * that has a series of key bindings */ class Mode { public: Mode(QString name); @@ -10,16 +12,25 @@ class Mode { const QString &name(); + /* Add a new key binding to a mode */ void addBinding(Qt::KeyboardModifiers mods, int key, Action *); + /* Accept a [QKeyEvent], and executes corresponding actions + * on the visitor if the event matches any of the mode's key bindings */ void acceptKeyEvent(QKeyEvent *, ActionVisitor *); public: + /* Find a mode (globally) by its name. + * Return [nullptr] if not found */ static Mode *findMode(const QString &); + /* Init default modes and their key bindings */ static void initDefaultModes(); + /* Builtin "normal" mode */ static Mode normal; + /* Builtin "insert" mode */ static Mode insert; private: + /* All defined modes */ static QMap all_modes; private: diff --git a/include/ui/rope.hpp b/include/ui/rope.hpp old mode 100644 new mode 100755 diff --git a/include/ui/textArea.hpp b/include/ui/textArea.hpp index e81b9a8..7d2242a 100755 --- a/include/ui/textArea.hpp +++ b/include/ui/textArea.hpp @@ -8,6 +8,8 @@ #include +/* The text area class is the widget that display content + * of a file */ class TextArea : public QWidget, public ActionVisitor { Q_OBJECT public: @@ -18,17 +20,22 @@ Q_OBJECT }; public: + /* Open a file */ TextArea(QWidget *parent, const QString &file); + /* Using an existing rope */ TextArea(QWidget *parent, Rope *content); + /* return file name of this text area */ const QString &file() const; + /* save the opened file */ void save() const; + /* save to an alternative file, with name [file] */ void save(const QString &file) const; - void resizeByChar(int n_row, int n_col); - public: + /* Implementation of methods in [ActionVisitor]. + * See [include/ui/actions.hpp] for more details. */ Interval getObject(PrimitveObject) override; void insertChar(QChar c) override; void setSelection(Interval) override; @@ -39,26 +46,52 @@ Q_OBJECT void redo() override; void setMode(const QString &) override; - // void setFont(const QFont &); - private: + /* resize the text area by number of characters. */ + void resizeByChar(int n_row, int n_col); + + /* re-run the parser on current content to update. + * Should be called when file content changes */ void reparse(); + /* setup pen of QPainter to make it draw characters + * using the specified style. */ static void setup_pen(QPainter &, const Style &); + + /* overriden event handlers from QWidget */ void keyPressEvent(QKeyEvent *) override; void paintEvent(QPaintEvent *) override; private: + /* file name. [""] if none is specified */ QString m_file; + /* Content of the text area. Owned by the TextArea object */ Rope *m_content; + + /* Current selection. + * Several invariants should be guaranteed: + * 1. [m_sel.interval.end - m_sel.interval.begin >= 1] + * 2. [m_sel.interval.begin + m_sel.cursor < m_sel.interval.end] + * 3. [m_sel.cursor > 0] + * 4. [0 <= m_sel.interval.begin < m_content.length()] + * 5. [0 < m_sel.interval.end <= m_content.length()] */ Selection m_sel; + /* Information about current font */ QFontMetrics *m_fm; + /* The size of a single-width character under current font */ QSize m_cell_size; + /* current mode. See [include/ui/modes.hpp] for more details */ Mode *m_mode; + /* current count, + * i.e. how many times the next command should be repeated */ int m_count; + /* If the file type of the text area is supported, + * [m_parser] should point to a syntax highlight parser for the file type. + * Otherwise, [m_parser] should be set to [nullptr]. + * See [include/shl/builtin.h] for more details */ shl::Parser *m_parser; }; diff --git a/include/ui/window.hpp b/include/ui/window.hpp index 86397a6..f1d513c 100755 --- a/include/ui/window.hpp +++ b/include/ui/window.hpp @@ -3,11 +3,13 @@ #include "libs.hpp" #include "textArea.hpp" +/* The class for the main window of pie */ class Window : public QMainWindow { Q_OBJECT public: explicit Window(QWidget *parent); + /* Add a new text area, opening the given file */ void addTextArea(TextArea *, const QString &); private: @@ -17,18 +19,27 @@ Q_OBJECT void exit(); private slots: + /* "open" menu activated */ void openFileMenu(bool); + /* actually open the file */ void doOpenFile(const QString &file); + /* "save" menu activated */ void saveFileMenu(bool); + /* "save as" menu activated */ void saveAsMenu(bool); + /* actually save the file */ void doSaveFile(const QString &file); void exitMenu(bool); + + /* "undo" menu activated */ void undo(bool); + /* "redo" menu activated */ void redo(bool); private: + /* All the menus */ QMenu *m_file_menu, *m_edit_menu; QAction *m_file_save_act, *m_file_saveas_act, *m_file_open_act, *m_exit_act; QAction *m_edit_undo_act, *m_edit_redo_act; diff --git a/src/shl/presets.cpp b/src/shl/presets.cpp index 6e94334..19d7bd4 100755 --- a/src/shl/presets.cpp +++ b/src/shl/presets.cpp @@ -463,8 +463,6 @@ namespace shl { std::pair generateLanguageParser(LanguageType languageType, Rope *rope) { switch (languageType) { case LanguageType::CPP: { - // std::cout << FilePath::constructFileString(3, ".", "shl", "cpp.shl") << std::endl; - // std::cout << FilePath::_separator << std::endl; auto temp = generateParserFromSHL( FilePath::constructFileString(3, ".", "shl", "cpp.shl"), rope); if (temp.first) { diff --git a/src/ui/actions.cpp b/src/ui/actions.cpp index 8ac9ec1..d34db3d 100755 --- a/src/ui/actions.cpp +++ b/src/ui/actions.cpp @@ -1,6 +1,7 @@ #include "actions.hpp" +/* ModifySelection */ ModifySelection::ModifySelection(bool expand, PrimitveObject obj) { m_expand = expand; m_obj = obj; @@ -15,7 +16,7 @@ void ModifySelection::accept(ActionVisitor *visitor) { } - +/* InsertChar */ InsertChar::InsertChar(QChar c) : m_char(c) {} void InsertChar::accept(ActionVisitor *visitor) { @@ -23,13 +24,14 @@ void InsertChar::accept(ActionVisitor *visitor) { } +/* Backspace */ void Backspace::accept(ActionVisitor *visitor) { auto i = visitor->getObject(PrevH); visitor->delInterval(i); } - +/* InsertNewline */ void InsertNewline::accept(ActionVisitor *visitor) { auto i = visitor->getObject(LineEnd); visitor->setSelection(i); @@ -38,11 +40,13 @@ void InsertNewline::accept(ActionVisitor *visitor) { } +/* DelSelection */ void DelSelection::accept(ActionVisitor *visitor) { visitor->delInterval(visitor->getObject(Selection)); } +/* PushCountChar */ PushCountChar::PushCountChar(int c) : m_count(c) {} void PushCountChar::accept(ActionVisitor *visitor) { @@ -50,22 +54,26 @@ void PushCountChar::accept(ActionVisitor *visitor) { } +/* ReplaceSelection */ void ReplaceSelection::accept(ActionVisitor *visitor) { visitor->delInterval(visitor->getObject(Selection)); visitor->setMode("insert"); } +/* Undo */ void Undo::accept(ActionVisitor *visitor) { visitor->undo(); } +/* Redo */ void Redo::accept(ActionVisitor *visitor) { visitor->redo(); } +/* SetMode */ SetMode::SetMode(QString mode) : m_mode(mode) {} void SetMode::accept(ActionVisitor *visitor) { diff --git a/src/ui/config.cpp b/src/ui/config.cpp index 73d2e6e..b01438d 100755 --- a/src/ui/config.cpp +++ b/src/ui/config.cpp @@ -14,6 +14,7 @@ bool Style::operator!=(const Style &other) const { return !(this->operator==(other)); } +/* A parchment-like light theme */ Style Style::default_style = { .fg = QColor(0 , 0 , 0 ), .bg = QColor(245, 222, 179), diff --git a/src/ui/main.cpp b/src/ui/main.cpp index 86e2c13..9f008fc 100755 --- a/src/ui/main.cpp +++ b/src/ui/main.cpp @@ -1,15 +1,14 @@ - #include "libs.hpp" #include "window.hpp" int main(int argc, char **argv) { Mode::initDefaultModes(); - QApplication app(argc, argv); - Window win(nullptr); + QApplication app(argc, argv); + Window win(NULL); QObject::connect(&win, SIGNAL (exit()), &app, SLOT (quit())); - win.show(); + return app.exec(); } diff --git a/src/ui/modes.cpp b/src/ui/modes.cpp index 657c9d9..674cca8 100755 --- a/src/ui/modes.cpp +++ b/src/ui/modes.cpp @@ -1,5 +1,6 @@ #include "modes.hpp" +/* Maintain [all_modes] in constructor and destructor */ Mode::Mode(QString name) : m_name(name) { all_modes.insert(name, this); } @@ -36,10 +37,12 @@ Mode *Mode::findMode(const QString &name) { } void Mode::initDefaultModes() { + /* ------ Keys for "normal" mode ------ */ static const int left_key = Qt::Key_J; static const int right_key = Qt::Key_L; static const int up_key = Qt::Key_I; static const int down_key = Qt::Key_K; + /* mode switching keys */ normal.addBinding( Qt::NoModifier, Qt::Key_U, new SetMode("insert") @@ -48,6 +51,8 @@ void Mode::initDefaultModes() { Qt::NoModifier, Qt::Key_O, new InsertNewline() ); + + /* directional navigation keys */ normal.addBinding( Qt::NoModifier, right_key, new ModifySelection(false, NextH) @@ -56,6 +61,7 @@ void Mode::initDefaultModes() { Qt::ShiftModifier, right_key, new ModifySelection(true, NextH) ); + normal.addBinding( Qt::NoModifier, left_key, new ModifySelection(false, PrevH) @@ -64,6 +70,7 @@ void Mode::initDefaultModes() { Qt::ShiftModifier, left_key, new ModifySelection(true, PrevH) ); + normal.addBinding( Qt::NoModifier, up_key, new ModifySelection(false, PrevV) @@ -72,14 +79,7 @@ void Mode::initDefaultModes() { Qt::ShiftModifier, up_key, new ModifySelection(true, PrevV) ); - normal.addBinding( - Qt::NoModifier, down_key, - new ModifySelection(false, NextV) - ); - normal.addBinding( - Qt::ShiftModifier, down_key, - new ModifySelection(true, NextV) - ); + normal.addBinding( Qt::NoModifier, down_key, new ModifySelection(false, NextV) @@ -89,6 +89,7 @@ void Mode::initDefaultModes() { new ModifySelection(true, NextV) ); + /* word-based navigation keys */ normal.addBinding( Qt::NoModifier, Qt::Key_W, new ModifySelection(false, NextW) @@ -107,6 +108,7 @@ void Mode::initDefaultModes() { new ModifySelection(true, PrevW) ); + /* line-based navigation keys */ normal.addBinding( Qt::NoModifier, Qt::Key_X, new ModifySelection(false, Line) @@ -116,6 +118,7 @@ void Mode::initDefaultModes() { new ModifySelection(true, Line) ); + /* match-based navigation keys */ normal.addBinding( Qt::NoModifier, Qt::Key_M, new ModifySelection(false, Match) @@ -125,6 +128,7 @@ void Mode::initDefaultModes() { new ModifySelection(true, Match) ); + /* selection modification keys */ normal.addBinding( Qt::NoModifier, Qt::Key_D, new DelSelection() @@ -143,6 +147,8 @@ void Mode::initDefaultModes() { new Redo() ); + /* ------ Keys for "insert" mode ------ */ + /* pass through all alphabetic and numeric keys */ for (char c = 'a'; c <= 'z'; ++c) { insert.addBinding( Qt::NoModifier, Qt::Key_A + c - 'a', @@ -153,13 +159,14 @@ void Mode::initDefaultModes() { new InsertChar(c - 'a' + 'A') ); } - for (char c = '0'; c <= 'z'; ++c) { + for (char c = '0'; c <= '9'; ++c) { insert.addBinding( Qt::NoModifier, Qt::Key_0 + c - '0', new InsertChar(c) ); } + /* ... as well as symbolic keys */ static const QVector> symb_keys = { { ' ', Qt::Key_Space }, { '\n', Qt::Key_Return }, @@ -181,7 +188,7 @@ void Mode::initDefaultModes() { new InsertChar(entry.first) ); } - + /* shifted symbol keys */ static const QVector> shift_keys = { { '~', Qt::Key_AsciiTilde }, { '!', Qt::Key_Exclam }, @@ -212,11 +219,13 @@ void Mode::initDefaultModes() { ); } + /* keys going back to "normal" */ insert.addBinding( Qt::NoModifier, Qt::Key_Escape, new SetMode("normal") ); + /* backspace */ insert.addBinding( Qt::NoModifier, Qt::Key_Backspace, new Backspace() diff --git a/src/ui/rope.cpp b/src/ui/rope.cpp old mode 100644 new mode 100755 diff --git a/src/ui/textArea.cpp b/src/ui/textArea.cpp index 9fcaeed..cb7d3de 100755 --- a/src/ui/textArea.cpp +++ b/src/ui/textArea.cpp @@ -2,7 +2,16 @@ #include "textArea.hpp" #include "pierc.h" - +/* This class is a connector class, + * used to feed the content of a rope to a parser. + * The interface is that of [peg_parser::Input], + * i.e. the overriden methods. + * + * The class is designed with the following things in mind: + * 1. zero copy + * 2. not dependent of the given row. + * That is, instances of [RopeParserInput] should still be + * valid after the given rope is modified */ class RopeParserInput : public peg_parser::Input { public: explicit RopeParserInput(const Rope *rope); @@ -18,18 +27,47 @@ class RopeParserInput : public peg_parser::Input { int getPosition() const override; void setPosition(int) override; + /* Return a copy of [this], with the same content. + * The return value is dynamically allocated, + * and the caller is responsible to free it. */ Input *copy() override; private: + /* Used internally to implement [copy] */ explicit RopeParserInput(const QList &content) : m_content(content) {} private: + /* The content of the rope. [Rope] provides zero copy API + * to obtain this list. */ const QList m_content; + /* Points to the slice in which the current position lies, + * or [m_content.constEnd()] if input reaches end. */ QList::const_iterator m_current; + /* Total length of input */ int m_len; + /* Offset from start of [*m_current] to current position */ int m_offset; + /* Current position, relative to start of [m_content] */ int m_pos; + /* The parser requires random seeking (setPosition(int)), + * but seeking a random position in the list is an O(n) operation. + * So here a cache is used to speed up random seeking, + * especially backward seeking, which is extensively used in the parser. + * + * The cache is structured as follows: + * key: + * - absolute position relative to content start + * value = QPair(pos, it): + * - [pos] is the same as key. Due to limitation of the QMap API + * it must also be stored in the value + * - [*it] is the slice whose start position in whole content + * is [pos] + */ using Cache = QMap::const_iterator>>; + /* The parser requires a [copy] function, + * and it is more desirable to share the cache between inputs + * with the same content. + * So here [m_cache] is wrapped in a shared pointer. */ std::shared_ptr m_cache; }; @@ -45,6 +83,7 @@ TextArea::TextArea(QWidget *parent, const QString &file) : QWidget(parent) { m_mode = &Mode::normal; m_count = 0; + /* So that the text area can get keyboard focus */ this->setFocusPolicy(Qt::StrongFocus); m_file = file; @@ -53,8 +92,15 @@ TextArea::TextArea(QWidget *parent, const QString &file) : QWidget(parent) { QFile f(m_file); if (f.open(QFile::ReadOnly)) { + /* Thanks to piece table, file content doesn't have to be + * split apart. This can improve locality. + * For very large files, perhaps lazy loading and splitting + * can speed up opening, though. */ Buffer *buf = new Buffer { std::move(f.readAll()) }; int len = buf->characters.size(); + /* A scan is necessary here, + * as [Rope] requires that only the last char of + * inserted buffer fragment can be newline. */ for (int i = 0, j = 0; i < len; ++i) { if (i == len - 1 || buf->characters[i] == '\n') { m_content->insert(buf, j, i + 1 - j, j, Style::default_style, true); @@ -63,11 +109,15 @@ TextArea::TextArea(QWidget *parent, const QString &file) : QWidget(parent) { } } f.close(); + /* Store a more compact relative path, if possible */ auto relative = QDir::current().relativeFilePath(m_file); if (relative.size() < m_file.size()) m_file = relative; + + /* Make the text area fit its content */ this->resizeByChar(m_content->lines() + 1, m_content->maxCol() + 1); + /* Selection is placed at start of content initially */ m_sel = { { 0, 1 }, 0 }; m_content->maintainInterval(&(m_sel.interval)); @@ -77,6 +127,7 @@ TextArea::TextArea(QWidget *parent, const QString &file) : QWidget(parent) { if (result.first) { qDebug() << "filetype: Cpp"; m_parser = new shl::Parser(std::move(result.second)); + /* Do the first parsing */ this->reparse(); } } @@ -151,11 +202,18 @@ void TextArea::save(const QString &file) const { } +/* ------------------------------------------------- + * Implementation of [ActionVisitor] methods + * ------------------------------------------------- */ + +/* Return the interval of a given text object */ Interval TextArea::getObject(PrimitveObject obj) { Interval i = m_sel.interval; switch (obj) { case PrimitveObject::Selection: break; + /* [NextH] and [PrevH] are all single-character + * text objects */ case NextH: i.begin = i.begin + m_sel.cursor + 1, i.end = i.begin + 1; @@ -171,6 +229,7 @@ Interval TextArea::getObject(PrimitveObject obj) { int offset = obj == NextV ? 1 : -1; auto cursor_pos = m_content->get_pos(i.begin + m_sel.cursor); int row = qMax(0, cursor_pos.first + offset); + /* Try to go to the same column. End of line if not possible */ i.begin = m_content->seek_pos(row, cursor_pos.second); i.end = i.begin + 1; break; @@ -192,6 +251,10 @@ Interval TextArea::getObject(PrimitveObject obj) { break; } } + /* [NextW] should always advance at least one character, + * so that the user won't get stuck in one place when + * repeatedly entering [w] in normal mode. + * Hence if we are not moving at all, move by one char. */ if (pos == begin + 1) { i.begin = pos; i.end = pos + 1; @@ -215,6 +278,7 @@ Interval TextArea::getObject(PrimitveObject obj) { } --pos; } + /* Similar to above */ if (pos == end - 1) { i.begin = end - 2; i.end = end - 1; @@ -224,13 +288,19 @@ Interval TextArea::getObject(PrimitveObject obj) { case CurLine: { + /* In case current selection expand multiple lines, + * the start postition and end position are seeked + * with respect to [m_sel.interval.begin] and + * [m_sel.interval.end]. */ auto pos1 = m_content->get_pos(i.begin); auto pos2 = m_content->get_pos(i.end - 1); i.begin = m_content->seek_pos(pos1.first, 0); + /* start of next row = position after end of current row. */ i.end = m_content->seek_pos(pos2.first + 1, 0); break; } + /* Select the next line */ case Line: { auto cursor_pos = m_content->get_pos(i.begin + m_sel.cursor); @@ -271,8 +341,14 @@ Interval TextArea::getObject(PrimitveObject obj) { int pos = i.begin + m_sel.cursor; auto it = std::move(m_content->iterAt(pos)); + /* The char we search for whose match */ + QChar paren_char = '\0'; + /* The match of [paren_char] */ QChar complement = '\0'; + /* Whether searching is backward, i.e. right to left */ bool search_backward = false; + + /* First search for the first char that has a match */ while (it.hasNext()) { QChar c = it.get().first; complement @@ -281,6 +357,7 @@ Interval TextArea::getObject(PrimitveObject obj) { : c == '{' ? '}' : '\0'; if (complement != '\0') { + paren_char = c; search_backward = false; break; } @@ -290,6 +367,7 @@ Interval TextArea::getObject(PrimitveObject obj) { : c == '}' ? '{' : '\0'; if (complement != '\0') { + paren_char = c; search_backward = true; break; } @@ -297,21 +375,39 @@ Interval TextArea::getObject(PrimitveObject obj) { it.next(); ++pos; } - - // search backward if (complement == '\0') break; + /* Now find the match of [paren_char] */ + + /* If we come up with [paren_char] again when searching + * for its match, than the next [complement] should matches + * the newly encountered [paren_char], instead of the one + * we are searching for. So we use [recursion_count] to + * capture the number of extra [paren_char]'es encountered + * along the way. */ + int recursion_count = 0; + + /* search backward */ if (search_backward) { int end = pos + 1; while (it.hasPrev()) { it.prev(); --pos; - if (it.get().first == complement) { - i.begin = pos; - i.end = end; - break; + QChar c = it.get().first; + if (c == complement) { + /* The real match */ + if (recursion_count == 0) { + i.begin = pos; + i.end = end; + break; + } + /* The match for some other [paren_char] */ + else + --recursion_count; } + else if (c == paren_char) + ++recursion_count; } } else { @@ -319,17 +415,25 @@ Interval TextArea::getObject(PrimitveObject obj) { while (it.hasNext()) { it.next(); ++pos; - if (it.get().first == complement) { - i.begin = begin; - i.end = pos + 1; - break; + QChar c = it.get().first; + if (c == complement) { + if (recursion_count == 0) { + i.begin = begin; + i.end = pos + 1; + break; + } + else + --recursion_count; } + else if (c == paren_char) + ++recursion_count; } } break; } } + /* Make sure the resulting interval fits into current content */ i.begin = qMax(0, qMin(i.begin, m_content->length() - 1)); i.end = qMax(1, qMin(i.end, m_content->length())); return i; @@ -346,6 +450,12 @@ void TextArea::insertChar(QChar c) { void TextArea::setSelection(Interval i) { int cursor = m_sel.interval.begin + m_sel.cursor; + /* Update cursor position based on the following heuristics: + * 1. if selection extends to the right compared to previous one, + * set cursor to the right of selection + * 2. otherwise, if selection extends to the left, set cursor to + * be the left of selection + * 3. otherwise, toggle cursor position */ if (cursor < i.end - 1) m_sel.cursor = i.end - i.begin - 1; else if (i.begin < cursor) @@ -361,6 +471,7 @@ void TextArea::setSelection(Interval i) { void TextArea::expandSelection(Interval i) { int cursor = m_sel.interval.begin + m_sel.cursor; + /* The union of current selection and the interval to extend */ i.begin = qMin(i.begin, m_sel.interval.begin); i.end = qMax(i.end, m_sel.interval.end); this->setSelection(i); @@ -369,6 +480,8 @@ void TextArea::expandSelection(Interval i) { void TextArea::delInterval(Interval i) { int len = i.end - i.begin; + /* Don't delete all of content and make it empty, + * leave a newline anyway */ if (len >= m_content->length()) { if (len == 1) return; @@ -385,6 +498,8 @@ void TextArea::delInterval(Interval i) { --(m_sel.interval.begin); m_sel.interval.end = m_sel.interval.begin + 1; } + /* Deletion is a complete operation w.r.t. the undo and redo + * system. So save current (unsaved) operations here. */ m_content->save_operation(); this->resizeByChar(m_content->lines() + 1, m_content->maxCol() + 1); this->reparse(); @@ -438,24 +553,40 @@ void TextArea::keyPressEvent(QKeyEvent *ev) { void TextArea::paintEvent(QPaintEvent *event) { QPainter p(this); + /* fill background */ p.fillRect(event->rect(), Style::default_style.bg); QPen pen = p.pen(); + /* so that underline and strikethrough are drawn with proper width */ pen.setWidth(m_fm->lineWidth()); + + /* offset from "ascent" position of characters + * to the place to draw underline */ int underline_offset = m_fm->ascent() + m_fm->underlinePos(); + /* similar */ int strikethrough_offset = m_fm->ascent() - m_fm->strikeOutPos(); + /* The rectangle to repaint. + * Here to improve performance, only the portion necessary + * to repaint are repainted. */ QRect rect = event->rect(); + /* Due to the existence of double width characters (i.e. CJK ones), + * content must be drawn in a per-line basis. + * So only find out the first row and last row covered by [rect] */ int row = qFloor((double)rect.y() / m_cell_size.height()); int row_e = qCeil((double)(rect.y() + rect.height()) / m_cell_size.height()); + /* Current position to draw things, w.r.t. [this] */ int x = 0; int y = row * m_cell_size.height(); + /* start and end index of characters that should be drawn + * in [m_content] */ int pos_s = m_content->seek_pos(row, 0); int pos_e = m_content->seek_pos(row_e + 1, 0); int pos = pos_s; + /* cursor position */ int cursor = m_sel.interval.begin + m_sel.cursor; RopeIter it = std::move(m_content->iterAt(pos)); @@ -465,6 +596,8 @@ void TextArea::paintEvent(QPaintEvent *event) { Style style = entry.second; it.next(); + /* Draw cursor and selection, no matter how they + * are highlighted by the parser */ if (pos == cursor) style = Style::cursor_style; else if (m_sel.interval.begin <= pos && pos < m_sel.interval.end) @@ -472,12 +605,14 @@ void TextArea::paintEvent(QPaintEvent *event) { ++pos; if (c == '\n') { + /* Draw something blank at the place of newline */ p.fillRect(QRect { x, y, m_cell_size.width(), m_cell_size.height() }, style.bg); ++row; + /* Advance drawing position to start ot next row */ y += m_cell_size.height(); x = 0; continue; @@ -485,24 +620,32 @@ void TextArea::paintEvent(QPaintEvent *event) { this->setup_pen(p, style); int cell_width = m_cell_size.width(); + /* Determine whether the character is double-width, + * directly using its size in the font */ if (m_fm->horizontalAdvance(c) > m_cell_size.width()) cell_width *= 2; + /* Rectangle of current character cell */ QRect rect( x, y, cell_width, m_cell_size.height() ); + /* draw background */ p.fillRect(rect, style.bg); + /* Draw underline. Builtin underline and strikethrough mechanism + * of Qt is error-prone with a composited font, + * e.g. a font with ASCII characters and CJK characters coming + * from different fonts */ if (style.underline) { p.drawLine( x, y + underline_offset, x + cell_width, y + underline_offset ); } - + /* Draw strikethrough */ if (style.strikethrough) { p.drawLine( x, y + strikethrough_offset, @@ -530,12 +673,16 @@ void TextArea::setup_pen(QPainter &p, const Style &style) { ft.setItalic(style.italic); else return; - - p.setFont(ft); } +/* ------------------------------------------------ + * Connector between Rope and parser + * ------------------------------------------------ */ + RopeParserInput::RopeParserInput(const Rope *rope) +/* copy constructor of [QList] is COW (Copy On Write), + * so no copy would actually happen here */ : m_content(rope->query(0, rope->length())) { m_len = rope->length(); @@ -563,14 +710,20 @@ char RopeParserInput::current() const { return c; } +/* Return the substring [begin, begin + len) of the whole content */ std::string RopeParserInput::slice(int begin, int len) { + /* Here we use [setPosition] to seek [begin], + * however [slice] should not affect the state of [this], + * so the current state is saved priorly and recovered later. */ auto current_it = m_current; int current_offset = m_offset; int current_pos = m_pos; - this->setPosition(begin); + int copied = 0; + /* Initialize result buffer */ std::string result(len, '\0'); + /* Copy characters into [result] until [len] characters in copied */ while (copied < len) { for (int i = 0; i < qMin(m_current->len - m_offset, len - copied); ++i) result[copied + i] = m_current->buf->characters[m_current->begin + m_offset + i].toLatin1(); @@ -578,47 +731,54 @@ std::string RopeParserInput::slice(int begin, int len) { ++m_current; m_offset = 0; } + + /* Recover state */ m_current = current_it; m_offset = current_offset; m_pos = current_pos; + return std::move(result); } void RopeParserInput::advance(int amount) { - // qDebug() << "pre adv"; if (m_current == m_content.constEnd()) return; + /* Advancement can be done in current slice */ if (m_offset + amount < m_current->len) { m_offset += amount; m_pos += amount; return; } + /* Cannot be done in current slice. + * Move to next slice, and advance recursively */ amount = m_offset + amount - m_current->len; - m_pos = m_pos + m_current->len - m_offset; + m_pos = m_pos + m_current->len - m_offset; m_offset = 0; ++m_current; - if (m_current == m_content.constEnd()) - return; + /* Cache on each advancement incrementally */ (*m_cache)[m_pos] = { m_pos, m_current }; this->advance(amount); - // qDebug() << "post adv"; } int RopeParserInput::getPosition() const { return m_pos; } void RopeParserInput::setPosition(int pos) { - // qDebug() << "pre set"; + /* [*ub] is the first slice whose start position + * is greater than [pos] */ auto ub = m_cache->upperBound(pos); + /* Now [*ub] is the first slice whose start position + * is smaller than [pos], i.e. closest to [pos] */ --ub; + /* seek to start of [*ub] */ m_pos = ub->first; m_current = ub->second; m_offset = 0; + /* Advance slowly, build up cache along the way */ this->advance(pos - m_pos); - // qDebug() << "post set"; } @@ -628,6 +788,7 @@ peg_parser::Input *RopeParserInput::copy() { result->m_len = m_len; result->m_offset = m_offset; result->m_pos = m_pos; + /* [m_cache] is shared */ result->m_cache = m_cache; return result; } diff --git a/src/ui/window.cpp b/src/ui/window.cpp index b48d4fa..68d2852 100755 --- a/src/ui/window.cpp +++ b/src/ui/window.cpp @@ -7,9 +7,12 @@ Window::Window(QWidget *parent) { this->resize(1000, 800); pierc::gatherInfo(); + /* The central part of the main window is a tabbed widget + * displaying different text areas. */ auto tabs = new QTabWidget(this); this->setCentralWidget(tabs); + /* Initialize menus */ m_file_menu = this->menuBar()->addMenu("File"); m_file_open_act = m_file_menu->addAction("Open"); @@ -61,20 +64,25 @@ Window::Window(QWidget *parent) void Window::addTextArea(TextArea *ta, const QString &label) { + /* Wrap the text area in a [QScrollArea] to provide + * proper scrolling support */ auto sa = new QScrollArea(this); sa->setWidget(ta); + /* Fill the background of the widget */ sa->setAutoFillBackground(true); QPalette pal = this->palette(); pal.setColor(QPalette::Window , Style::default_style.bg); sa->setPalette(pal); + /* Add the new text area to the tabs, and make it the focus */ auto tabs = (QTabWidget*)(this->centralWidget()); tabs->insertTab(tabs->currentIndex() + 1, sa, label); tabs->setCurrentIndex(tabs->currentIndex() + 1); ta->setFocus(); } +/* Helper function that return the focused text area */ TextArea *Window::currentTextArea() { auto tabs = static_cast(this->centralWidget()); if (! tabs) From 93ea2cac3049687cebfa8b42b854351aba0e75f2 Mon Sep 17 00:00:00 2001 From: Garen Wang Date: Sun, 27 Jun 2021 12:17:37 +0800 Subject: [PATCH 3/4] remove unused header files --- include/shl/parser_builder.h | 100 ----------------------------------- include/shl/presets.h | 33 ------------ 2 files changed, 133 deletions(-) delete mode 100644 include/shl/parser_builder.h delete mode 100644 include/shl/presets.h diff --git a/include/shl/parser_builder.h b/include/shl/parser_builder.h deleted file mode 100644 index 5d71ec0..0000000 --- a/include/shl/parser_builder.h +++ /dev/null @@ -1,100 +0,0 @@ -// -// Created by garen on 5/27/21. -// - -#ifndef PYTHON_PARSER_PARSER_BUILDER_H -#define PYTHON_PARSER_PARSER_BUILDER_H - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -class _Color { -private: - int r, g, b; - -public: - explicit _Color() : r(0), g(0), b(0) {} - explicit _Color(int r, int g, int b) : r(r), g(g), b(b) {} - - friend std::ostream& operator<<(std::ostream& out, const _Color& _color) { - return out << "(" << _color.r << ", " << _color.g << ", " << _color.b << ")"; - } -}; - -struct Style { - _Color fg, bg; - bool bold, italic; - bool underlined, strikethrough; - - explicit Style() - : fg(255, 255, 255), - bg(255, 255, 255), - bold(false), - italic(false), - underlined(false), - strikethrough(false) {} - explicit Style(_Color fg) - : fg(fg), - bg(255, 255, 255), - bold(false), - italic(false), - underlined(false), - strikethrough(false) {} - - friend std::ostream& operator<<(std::ostream& out, const Style& style) { return out << style.fg; } -}; - -using Attr = Style; - -namespace shl { - extern std::set identifiers; - - class SyntaxHighlightInfo { - public: - mutable int idx = 0; - Attr attr; - explicit SyntaxHighlightInfo(Attr attr); - explicit SyntaxHighlightInfo(int idx, Attr attr); - }; - - typedef std::vector SyntaxHighlightInfos; - typedef peg_parser::ParserGenerator Parser; - typedef peg_parser::ParserGenerator, Parser&> ParserBuilder; - - class Colors { - private: - // not wise to use *unordered_set* here, try *unordered_map* - std::unordered_map colors; - - public: - // get expression for PEG parser grammar - std::string getExpr(); - // hope all string in `colors` are lowercase - void append(const std::string& color, const _Color& _color); - bool exist(const std::string& str); - _Color get(const std::string& str); - }; - - void changeAttr(Attr attr, int begin, int end); - - ParserBuilder generateParserBuilder(Colors& colors); - - Colors getPredefinedColors(); - - std::pair generateParserFromSHL(const std::string& filename); - - bool renderSyntaxHighlightForSHL(const std::string& filename); - - void initParserBuilder(ParserBuilder& g, Colors& colors, bool render = false); -} // namespace shl - -#endif //PYTHON_PARSER_PARSER_BUILDER_H \ No newline at end of file diff --git a/include/shl/presets.h b/include/shl/presets.h deleted file mode 100644 index 41c6c38..0000000 --- a/include/shl/presets.h +++ /dev/null @@ -1,33 +0,0 @@ -// -// Created by garen on 5/29/21. -// - -#ifndef PYTHON_PARSER_PRESETS_H -#define PYTHON_PARSER_PRESETS_H - -#include "parser_builder.h" - -enum class LanguageType { - CPP, - JAVA, - PYTHON, - JSON, -}; - -namespace shl { - - extern std::vector indentDepth; - extern std::vector > blockRecords; - - void initSHLGrammar(ParserBuilder& g); - - std::pair getLineNumber(std::string_view input, std::pair blockRecord); - - int getIdx(const std::string& str, int size); - - bool isLowercase(const std::string& str); -} // namespace shl - -std::pair generateLanguageParser(LanguageType languageType); - -#endif //PYTHON_PARSER_PRESETS_H From 82031fd91f31c4d82ac3ea8119a2beec0134927d Mon Sep 17 00:00:00 2001 From: Garen Wang Date: Sun, 27 Jun 2021 12:27:39 +0800 Subject: [PATCH 4/4] update README.md --- README.md | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index ffbaf79..b9f61dc 100644 --- a/README.md +++ b/README.md @@ -6,13 +6,15 @@ Pie (abbr. Pie Is an Editor) is a cross-platform text editor, intended to be fas ## Features -- graphical user interface +- SOTA inner text representation -- modal editing +- Graphical user interface -- syntax highlighting +- Easy-to-use modal editing -- configurable +- Semantic syntax highlighting + +- Configurable ## Usage @@ -79,14 +81,15 @@ If you would like to change the system default font to your favorite one like Sa ### Windows -1. Download the pre-built version of Pie. +1. Download the pre-built Windows version of Pie. 2. Extract the archive. 3. Run `pie.exe`. ### Linux -1. Download the Appimage of Pie. -2. Run `pie-x86_64.Appimage`. +1. Download the pre-built Linux version of Pie. +2. Extract the archive. +3. Run `pie`. ### Build from Source @@ -95,6 +98,11 @@ If you would like to change the system default font to your favorite one like Sa 3. Copy `shl` and `test` to the source root of the build directory. 4. Run the executable. +## Collaborators + +- Qing Liu, a.k.a [guest0x0](https://github.com/guest0x0) +- Xiankai Miao + ## License The repository is under WTFPL.