From c2e922344d9bad5c3ae4b50fb8c85c886664cd43 Mon Sep 17 00:00:00 2001 From: Michael Ochmann Date: Thu, 9 Mar 2017 13:16:38 +0100 Subject: [PATCH] added history (undo/redo) --- CHANGELOG.md | 8 ++++++- main.cpp | 25 ++++++++++++++++++++++ src/Editor.cpp | 3 ++- src/Editor.hpp | 16 ++++++++++---- src/Highlighter.cpp | 6 +++--- src/History.hpp | 42 +++++++++++++++++++++--------------- src/modes/Edit.cpp | 52 +++++++++++++++++++++++++++++++++++++++------ 7 files changed, 120 insertions(+), 32 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 19ff12a..254b5d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,10 @@ -# 0.0.1 +# v0.0.4 +Major improvements: + +* Undo/redo capability in `edit` mode +* Linemodes: `numbers`, `relativenumbers`, `nonumbers` + +# v0.0.1 Simple editor working. The editor is capable of * Loading files diff --git a/main.cpp b/main.cpp index 623e8a1..ad2d4b9 100644 --- a/main.cpp +++ b/main.cpp @@ -1,3 +1,28 @@ +/** + * Groove Editor + * + * Version 0.0.4 + * + * Copyright (c) 2017 Michael Ochmann + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + #include #include #include diff --git a/src/Editor.cpp b/src/Editor.cpp index 8811386..faa6482 100644 --- a/src/Editor.cpp +++ b/src/Editor.cpp @@ -8,7 +8,8 @@ namespace groove { Editor::Editor(std::string filename) : x(0), y(0), buffer(std::make_unique()), mode_(Mode::EDIT), - filename(filename), offset(0), lineMode(LineMode::NONE) { + filename(filename), offset(0), voffset(0), lineMode(LineMode::RELATIVE), + history(History()) { this->modes.emplace(Mode::INSERT, std::make_unique(*this)); this->modes.emplace(Mode::EDIT, std::make_unique(*this)); this->modes.emplace(Mode::QUIT, std::make_unique(*this)); diff --git a/src/Editor.hpp b/src/Editor.hpp index 7fc98ec..29dbd7e 100644 --- a/src/Editor.hpp +++ b/src/Editor.hpp @@ -1,13 +1,14 @@ #pragma once #include +#include #include #include #include #include #include #include -#include +#include namespace groove { @@ -31,11 +32,14 @@ namespace groove { friend class modes::Edit; friend class modes::Quit; friend class modes::Save; + friend class History; private: long x, y; int offset; + int voffset; long vspace; char lastChar = 0; + History history; LineMode lineMode; std::unique_ptr buffer; Mode mode_; @@ -65,10 +69,14 @@ namespace groove { } } void right() { - if (this->x + 1 <= COLS && this->x + 1 <= this->buffer->linebuffer().at(this->y).length()) - this->x++; + if (this->x + 1 <= this->buffer->linebuffer().at(this->y).length()) { + if (this->x + 1 <= COLS) + this->x++; + else + this->voffset++; + } else { - if (this->y + 1 <= this->buffer->size()) { + if (this->y + 1 < this->buffer->size()) { this->y++; this->x = this->buffer->at(this->y).size(); } diff --git a/src/Highlighter.cpp b/src/Highlighter.cpp index 09c52fb..7ad1020 100644 --- a/src/Highlighter.cpp +++ b/src/Highlighter.cpp @@ -5,16 +5,16 @@ namespace groove { std::vector> Highlighter::list = { + make_pair("(#[a-z]+ ?)(.*)", ncurses::Colors::ORANGE), make_pair("([+-.<>,;=!:])", ncurses::Colors::CYAN), make_pair("([\\{\\}\\[\\]\\(\\)])", ncurses::Colors::GREEN), - make_pair("(while|if|try|catch|void|this|namespace|private|public|protected|class|char|bool|unsigned|long|short|int|return)\\*?", ncurses::Colors::MAGENTA), + make_pair("(while|if|try|catch|void|this|else|namespace|private|public|protected|class|char|bool|unsigned|long|short|int|return)\\*?", ncurses::Colors::MAGENTA), make_pair("([a-zA-Z_][a-zA-Z_0-9]+)::", ncurses::Colors::GREEN), make_pair("::([a-zA-Z_][a-zA-Z_0-9]+)", ncurses::Colors::CYAN), make_pair("\\.([a-zA-Z_][a-zA-Z_0-9]+)", ncurses::Colors::CYAN), make_pair("\\\".*\\\"", ncurses::Colors::ORANGE), make_pair("/\\*.*\\*//*", ncurses::Colors::ORANGE), - make_pair("(//.*)", ncurses::Colors::ORANGE), - make_pair("(#.*<.*>$)", ncurses::Colors::ORANGE) + make_pair("(//.*)", ncurses::Colors::ORANGE) }; std::unordered_map> groove::Highlighter::get() { diff --git a/src/History.hpp b/src/History.hpp index f871609..802c75b 100644 --- a/src/History.hpp +++ b/src/History.hpp @@ -2,40 +2,48 @@ #include #include +#include namespace groove { struct HistoryNode { - std::shared_ptr last; - std::shared_ptr next; std::function undo, redo; - HistoryNode(std::function undo, std::function redo, - std::shared_ptr last = nullptr, - std::shared_ptr next = nullptr) : - undo(undo), redo(redo), last(last), next(next) {} + HistoryNode(std::function undo, std::function redo) : + undo(std::move(undo)), redo(std::move(redo)) {} }; class History { private: - std::shared_ptr begin; - std::shared_ptr end; + std::unique_ptr> history; + unsigned long pointer; + + void cutTail() { + if (this->history->begin() == this->history->end() || this->pointer >= this->history->size()) + return; + this->history->erase(this->history->begin() + this->pointer + 1, this->history->end()); + } public: - History() : begin(nullptr), end(nullptr) {} - void push(std::function undo, std::function redo) { - this->end->next = std::make_shared(undo, redo, this->end); + History() : history(std::make_unique>()), pointer(0) { + this->push(HistoryNode([](){}, [](){})); } - - void pop() { - this->end = this->end->last; + void push(HistoryNode node) { + this->cutTail(); + this->history->emplace_back(std::move(node)); + this->pointer = this->history->size() - 1; } void undo() { - this->end->undo(); - this->end = this->end->last; + if (this->pointer <= 0) + return; + this->history->at(this->pointer).undo(); + this->pointer--; } void redo() { - this->end->redo(); + if (this->pointer + 1 >= this->history->size()) + return; + this->history->at(this->pointer + 1).redo(); + this->pointer++; } }; diff --git a/src/modes/Edit.cpp b/src/modes/Edit.cpp index cb0e04f..e1a5559 100644 --- a/src/modes/Edit.cpp +++ b/src/modes/Edit.cpp @@ -25,16 +25,51 @@ namespace groove { this->editor.clipboard = this->editor.buffer->at(this->editor.y); break; case 'x': - this->editor.clipboard = this->editor.buffer->at(this->editor.y); - this->editor.buffer->remove(this->editor.y); + { + long y = this->editor.y; + std::string line = this->editor.buffer->at(y); + this->editor.history.push( + HistoryNode([this, y, line](){ + this->editor.buffer->insert(line, y); + }, [this, y](){ + this->editor.buffer->remove(y); + }) + ); + this->editor.clipboard = this->editor.buffer->at(y); + this->editor.buffer->remove(y); + } break; case 'v': - this->editor.buffer->insert(this->editor.clipboard, this->editor.y); - this->editor.y++; - this->editor.x = this->editor.buffer->at(this->editor.y).size(); + { + std::string clipboard = this->editor.clipboard; + long y = this->editor.y; + this->editor.history.push( + HistoryNode([this, y](){ + this->editor.buffer->remove(y); + },[this, y, clipboard](){ + this->editor.buffer->insert(clipboard, y); + this->editor.y = y; + this->editor.x = this->editor.buffer->at(y).size(); + }) + ); + this->editor.buffer->insert(clipboard, y); + this->editor.y++; + this->editor.x = this->editor.buffer->at(y).size(); + } break; case 'd': - this->editor.buffer->remove(this->editor.y); + { + long y = this->editor.y; + std::string line = this->editor.buffer->at(y); + this->editor.history.push( + HistoryNode([this, y, line](){ + this->editor.buffer->insert(line, y); + }, [this, y, line](){ + this->editor.buffer->remove(y); + }) + ); + this->editor.buffer->remove(this->editor.y); + } break; case 'l': switch (this->editor.lineMode) { @@ -49,6 +84,11 @@ namespace groove { break; } break; + case 'u': + this->editor.history.undo(); + break; + case 'r': + this->editor.history.redo(); } } }