Compare commits
No commits in common. '154289558a6e465d2d10d42871ad472664230742' and '2ec4f90bee7b23d253e2d5dc98dac2835bcb7af4' have entirely different histories.
154289558a
...
2ec4f90bee
31 changed files with 21 additions and 1444 deletions
@ -1,2 +0,0 @@ |
|||||||
.idea |
|
||||||
build |
|
@ -1,14 +0,0 @@ |
|||||||
# 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 |
|
||||||
* Saving files |
|
||||||
* Editing files |
|
||||||
* Cut, copy and pasting lines |
|
||||||
* Scrolling |
|
@ -1,28 +0,0 @@ |
|||||||
cmake_minimum_required(VERSION 3.7) |
|
||||||
project(groove C CXX) |
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 14) |
|
||||||
|
|
||||||
include_directories(src) |
|
||||||
|
|
||||||
#set(SOURCE_FILES main.cpp) |
|
||||||
file( |
|
||||||
GLOB_RECURSE SOURCE_FILES |
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp |
|
||||||
) |
|
||||||
|
|
||||||
file( |
|
||||||
GLOB_RECURSE HEADER_FILES |
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/*.h |
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/*.hpp |
|
||||||
) |
|
||||||
|
|
||||||
include_directories( |
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src |
|
||||||
) |
|
||||||
|
|
||||||
add_executable(groove main.cpp ${SOURCE_FILES} ${HEADER_FILES}) |
|
||||||
|
|
||||||
target_link_libraries(groove ncursesw) |
|
||||||
|
|
||||||
install(TARGETS groove DESTINATION /usr/bin) |
|
@ -0,0 +1,19 @@ |
|||||||
|
MIT License Copyright (c) <year> <copyright holders> |
||||||
|
|
||||||
|
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 (including the next |
||||||
|
paragraph) 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. |
@ -1,21 +0,0 @@ |
|||||||
# The MIT License (MIT) |
|
||||||
|
|
||||||
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. |
|
@ -1,3 +1,3 @@ |
|||||||
# Groove Editor |
# groove |
||||||
– a simple `ncurses`-based texteditor for the terminaly |
|
||||||
|
|
||||||
|
a simple ncurses-based texteditor for the terminal |
@ -1,53 +0,0 @@ |
|||||||
/**
|
|
||||||
* 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 <iostream> |
|
||||||
#include <ncurses/ncurses.hpp> |
|
||||||
#include <Editor.hpp> |
|
||||||
#include <Highlighter.hpp> |
|
||||||
|
|
||||||
int main(int argc, char* argv[]) { |
|
||||||
std::unique_ptr<groove::Editor> editor; |
|
||||||
|
|
||||||
if (argc > 1) |
|
||||||
editor = std::make_unique<groove::Editor>(argv[1]); |
|
||||||
else |
|
||||||
editor = std::make_unique<groove::Editor>(); |
|
||||||
|
|
||||||
groove::ncurses::ncurses curses; |
|
||||||
curses.init(); |
|
||||||
curses.flush(); |
|
||||||
|
|
||||||
while(editor->mode() != groove::Mode::QUIT) |
|
||||||
{ |
|
||||||
editor->render(); |
|
||||||
int input = getch(); // Blocking until input
|
|
||||||
editor->input(input); |
|
||||||
} |
|
||||||
|
|
||||||
curses.quit(); |
|
||||||
|
|
||||||
return 0; |
|
||||||
} |
|
@ -1,9 +0,0 @@ |
|||||||
//
|
|
||||||
// Created by miko on 04.03.17.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include <Buffer.hpp> |
|
||||||
|
|
||||||
namespace groove { |
|
||||||
|
|
||||||
} |
|
@ -1,77 +0,0 @@ |
|||||||
#pragma once |
|
||||||
|
|
||||||
#include <vector> |
|
||||||
#include <string> |
|
||||||
#include <memory> |
|
||||||
#include <stack> |
|
||||||
|
|
||||||
namespace groove { |
|
||||||
|
|
||||||
class Buffer { |
|
||||||
private: |
|
||||||
std::vector<std::string> lines; |
|
||||||
std::vector<std::string> reverted; |
|
||||||
std::stack<std::vector<std::string>> history; |
|
||||||
void removeTabs(std::string& line) { |
|
||||||
ulong tab = line.find("\t"); |
|
||||||
if(tab != line.npos) |
|
||||||
this->removeTabs(line.replace(tab, 1, " ")); |
|
||||||
} |
|
||||||
public: |
|
||||||
bool changed; |
|
||||||
Buffer() : lines(std::vector<std::string>()), reverted(std::vector<std::string>()), changed(false) {} |
|
||||||
std::vector<std::string>& linebuffer() { |
|
||||||
return this->lines; |
|
||||||
} |
|
||||||
|
|
||||||
void insert(std::string str, unsigned long line) { |
|
||||||
this->lines.insert(this->lines.begin() + line, str); |
|
||||||
this->removeTabs(this->lines.at(line)); |
|
||||||
this->changed = true; |
|
||||||
} |
|
||||||
|
|
||||||
void insert(std::string line) { |
|
||||||
this->removeTabs(line); |
|
||||||
this->lines.emplace_back(line); |
|
||||||
this->changed = true; |
|
||||||
} |
|
||||||
void insert(unsigned long after) { |
|
||||||
this->lines.insert(this->lines.begin() + after + 1, ""); |
|
||||||
this->changed = true; |
|
||||||
} |
|
||||||
|
|
||||||
void remove(unsigned long line) { |
|
||||||
this->lines.erase(this->lines.begin() + line); |
|
||||||
this->changed = true; |
|
||||||
} |
|
||||||
|
|
||||||
void remove(unsigned long line, int car) { |
|
||||||
this->lines.at(line).erase(this->lines.at(line).begin() + car); |
|
||||||
this->changed = true; |
|
||||||
} |
|
||||||
|
|
||||||
void deleteChar(unsigned long line, int car) { |
|
||||||
this->lines.at(line).erase(this->lines.at(line).begin() + car); |
|
||||||
this->changed = true; |
|
||||||
} |
|
||||||
|
|
||||||
std::string& at(unsigned long line) { |
|
||||||
return this->lines.at(line); |
|
||||||
} |
|
||||||
|
|
||||||
void swap(std::vector<std::string> buffer) { |
|
||||||
this->history.push(this->lines); |
|
||||||
this->lines = buffer; |
|
||||||
} |
|
||||||
|
|
||||||
void revert() { |
|
||||||
this->lines = this->history.top(); |
|
||||||
this->history.pop(); |
|
||||||
} |
|
||||||
|
|
||||||
unsigned long size() { |
|
||||||
return this->lines.size(); |
|
||||||
} |
|
||||||
}; |
|
||||||
|
|
||||||
} |
|
@ -1,173 +0,0 @@ |
|||||||
#include <Editor.hpp> |
|
||||||
#include <Highlighter.hpp> |
|
||||||
#include <fstream> |
|
||||||
#include <math.h> |
|
||||||
|
|
||||||
namespace groove { |
|
||||||
|
|
||||||
Editor::Editor(std::string filename) : x(0), y(0), |
|
||||||
buffer(std::make_unique<Buffer>()), mode_(Mode::EDIT), |
|
||||||
filename(filename), offset(0), voffset(0), |
|
||||||
history(History()), inComment(false) { |
|
||||||
|
|
||||||
std::string ending; |
|
||||||
if (filename.find_last_of('.') != std::string::npos) |
|
||||||
ending = std::string(filename.begin() + filename.find_last_of('.'), filename.end()); |
|
||||||
else |
|
||||||
ending = ""; |
|
||||||
|
|
||||||
if (ending == ".cpp" || ending == ".hpp" || ending == ".h" || ending == ".c") |
|
||||||
groove::Highlighter::list = groove::Highlighter::Syntaxes.at(groove::Syntax::CPP); |
|
||||||
else |
|
||||||
groove::Highlighter::list = groove::Highlighter::Syntaxes.at(groove::Syntax::DEFAULT); |
|
||||||
|
|
||||||
this->modes.emplace(Mode::INSERT, std::make_unique<modes::Insert>(*this)); |
|
||||||
this->modes.emplace(Mode::EDIT, std::make_unique<modes::Edit>(*this)); |
|
||||||
this->modes.emplace(Mode::QUIT, std::make_unique<modes::Quit>(*this)); |
|
||||||
this->modes.emplace(Mode::SAVE, std::make_unique<modes::Save>(*this)); |
|
||||||
this->modes.emplace(Mode::EXIT, std::make_unique<modes::Exit>(*this)); |
|
||||||
this->modes.emplace(Mode::SEARCH, std::make_unique<modes::Search>(*this)); |
|
||||||
|
|
||||||
this->lineMode = this->config.get<std::string>("linenumbers") == "relative" ? LineMode::RELATIVE : |
|
||||||
(this->config.get<std::string>("linenumbers") == "none" ? LineMode::NONE : LineMode::NUMBERS); |
|
||||||
|
|
||||||
this->overlay = newwin(LINES * 0.9, COLS * 0.9, (LINES * 0.1f) / 2.0f, (COLS * 0.1f) / 2.0f); |
|
||||||
box(this->overlay, 0, 0); |
|
||||||
|
|
||||||
if (!this->load()) { |
|
||||||
std::cerr << "Could not open file: '" << this->filename << "', creating it on save.\n"; |
|
||||||
this->buffer->insert(""); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
bool Editor::load() { |
|
||||||
if (this->filename == "") { |
|
||||||
this->filename = "unbenannt"; |
|
||||||
|
|
||||||
return false; |
|
||||||
} |
|
||||||
std::ifstream file(this->filename.c_str()); |
|
||||||
if(file.is_open()) { |
|
||||||
while(!file.eof()) { |
|
||||||
std::string line; |
|
||||||
std::getline(file, line); |
|
||||||
this->buffer->insert(line); |
|
||||||
} |
|
||||||
this->buffer->changed = false; |
|
||||||
|
|
||||||
return true; |
|
||||||
} |
|
||||||
this->buffer->changed = false; |
|
||||||
|
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
bool Editor::save() { |
|
||||||
std::ofstream file(this->filename.c_str()); |
|
||||||
if(file.is_open()) { |
|
||||||
for (auto& line : this->buffer->linebuffer()) { |
|
||||||
file << line << std::endl; |
|
||||||
} |
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
void groove::Editor::input(int c) { |
|
||||||
this->modes.at(this->mode_)->input(c); |
|
||||||
} |
|
||||||
|
|
||||||
void Editor::render() { |
|
||||||
long linenumber = this->offset; |
|
||||||
this->vspace = this->lineMode == LineMode::RELATIVE || this->lineMode == LineMode::NUMBERS ? |
|
||||||
Editor::Digits(this->buffer->linebuffer().size()) + 2 : 0; |
|
||||||
clear(); |
|
||||||
for (int i = this->offset; i < LINES - 1 + this->offset; i++) { |
|
||||||
long ln; |
|
||||||
switch (this->lineMode) { |
|
||||||
case LineMode::RELATIVE: |
|
||||||
ln = static_cast<long>(std::sqrt(std::pow(static_cast<double>(this->y - linenumber), 2))); |
|
||||||
ln = ln == 0 ? linenumber : ln; |
|
||||||
break; |
|
||||||
case LineMode::NUMBERS: |
|
||||||
ln = linenumber; |
|
||||||
break; |
|
||||||
} |
|
||||||
|
|
||||||
if(i >= this->buffer->linebuffer().size()) { |
|
||||||
move(i - this->offset, 0); |
|
||||||
clrtoeol(); |
|
||||||
} |
|
||||||
else { |
|
||||||
std::string line = this->buffer->at(i); |
|
||||||
Highlighter highlighter(line); |
|
||||||
std::unordered_map<long, std::pair<long, ncurses::Colors>> hilist = highlighter.get(); |
|
||||||
long x = this->vspace; |
|
||||||
long found = -1; |
|
||||||
long len = -1; |
|
||||||
|
|
||||||
if (this->lineMode != LineMode::NONE) { |
|
||||||
std::string label = std::string(x - Editor::Digits(ln) - 1, ' '); |
|
||||||
label += std::to_string(ln) + ' '; |
|
||||||
if (linenumber != this->y) |
|
||||||
attron(COLOR_PAIR(ncurses::Colors::LINENUMBERS)); |
|
||||||
mvprintw(i - offset, 0, label.c_str()); |
|
||||||
if (linenumber != this->y) |
|
||||||
attroff(COLOR_PAIR(ncurses::Colors::LINENUMBERS)); |
|
||||||
} |
|
||||||
|
|
||||||
for (auto& car : line) { |
|
||||||
if (car == '*' && this->lastChar == '/') |
|
||||||
this->inComment = true; |
|
||||||
if (car == '/' && this->lastChar == '*') |
|
||||||
this->inComment = false; |
|
||||||
if (hilist.find(x - this->vspace) != hilist.end()) { |
|
||||||
found = x - this->vspace; |
|
||||||
len = hilist.at(x - this->vspace).first; |
|
||||||
} |
|
||||||
if (found >= 0 && len >= 0) { |
|
||||||
if (x - this->vspace >= found && x - this->vspace <= found + len - 1) |
|
||||||
attron(COLOR_PAIR(hilist.at(found).second)); |
|
||||||
else if (x - this->vspace > found && x - this->vspace >= found + len) { |
|
||||||
attron(COLOR_PAIR(ncurses::Colors::MAIN)); |
|
||||||
found = -1; |
|
||||||
len = -1; |
|
||||||
} |
|
||||||
} |
|
||||||
if (inComment) |
|
||||||
attron(COLOR_PAIR(ncurses::Colors::COMMENTS)); |
|
||||||
mvaddch(i - this->offset, x , car); |
|
||||||
attron(COLOR_PAIR(ncurses::Colors::MAIN)); |
|
||||||
this->lastChar = car; |
|
||||||
x++; |
|
||||||
} |
|
||||||
//mvprintw(i - this->offset, 0, line.c_str());
|
|
||||||
} |
|
||||||
clrtoeol(); |
|
||||||
linenumber++; |
|
||||||
} |
|
||||||
this->status(); |
|
||||||
move(static_cast<int>(this->y - this->offset), static_cast<int>(this->x + this->vspace)); |
|
||||||
this->renderOverlay(); |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
void Editor::status() { |
|
||||||
std::string status; |
|
||||||
|
|
||||||
std::string position = std::to_string(this->y) + ", " + std::to_string(this->x) + " "; |
|
||||||
status = this->modes.at(this->mode_)->status(); |
|
||||||
status += std::string(COLS - status.length() - position.length(), ' '); |
|
||||||
status += position; |
|
||||||
|
|
||||||
attron(COLOR_PAIR(ncurses::Colors::STATUSBAR)); |
|
||||||
mvprintw(LINES-1, 0, status.c_str()); |
|
||||||
attroff(COLOR_PAIR(ncurses::Colors::STATUSBAR)); |
|
||||||
} |
|
||||||
|
|
||||||
long Editor::Digits(long number) { |
|
||||||
return number > 0 ? static_cast<long>(log10 ((double) number) + 1) : 1; |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -1,157 +0,0 @@ |
|||||||
#pragma once |
|
||||||
|
|
||||||
#include <memory> |
|
||||||
#include <unordered_map> |
|
||||||
#include <Buffer.hpp> |
|
||||||
#include <ncurses/ncurses.hpp> |
|
||||||
#include <modes/Insert.hpp> |
|
||||||
#include <modes/Edit.hpp> |
|
||||||
#include <modes/Quit.hpp> |
|
||||||
#include <modes/Save.hpp> |
|
||||||
#include <modes/Search.hpp> |
|
||||||
#include <History.hpp> |
|
||||||
#include <config/ConfigParser.hpp> |
|
||||||
#include <modes/Exit.hpp> |
|
||||||
|
|
||||||
namespace groove { |
|
||||||
|
|
||||||
enum class Mode { |
|
||||||
INSERT, |
|
||||||
EDIT, |
|
||||||
SAVE, |
|
||||||
QUIT, |
|
||||||
EXIT, |
|
||||||
SEARCH |
|
||||||
}; |
|
||||||
|
|
||||||
enum LineMode { |
|
||||||
NUMBERS, |
|
||||||
RELATIVE, |
|
||||||
NONE, |
|
||||||
COUNT |
|
||||||
}; |
|
||||||
|
|
||||||
class Editor { |
|
||||||
friend class modes::Mode; |
|
||||||
friend class modes::Insert; |
|
||||||
friend class modes::Edit; |
|
||||||
friend class modes::Quit; |
|
||||||
friend class modes::Save; |
|
||||||
friend class modes::Exit; |
|
||||||
friend class modes::Search; |
|
||||||
friend class History; |
|
||||||
private: |
|
||||||
long x, y; |
|
||||||
int offset; |
|
||||||
int voffset; |
|
||||||
long vspace; |
|
||||||
bool inComment; |
|
||||||
char lastChar = 0; |
|
||||||
WINDOW* overlay; |
|
||||||
History history; |
|
||||||
LineMode lineMode; |
|
||||||
std::unique_ptr<Buffer> buffer; |
|
||||||
Mode mode_; |
|
||||||
std::string clipboard = ""; |
|
||||||
std::string filename; |
|
||||||
std::unordered_map<Mode, std::unique_ptr<modes::Mode>> modes; |
|
||||||
config::ConfigParser config; |
|
||||||
|
|
||||||
bool load(); |
|
||||||
bool save(); |
|
||||||
void status(); |
|
||||||
void scrollUp() { |
|
||||||
if (this->y < this->offset && this->offset > 0) |
|
||||||
this->offset--; |
|
||||||
} |
|
||||||
void scrollDown() { |
|
||||||
if (this->y - this->offset >= LINES - 1 && this->y < this->buffer->size()) |
|
||||||
this->offset++; |
|
||||||
} |
|
||||||
void left() { |
|
||||||
if (this->x - 1 >= 0) |
|
||||||
this->x--; |
|
||||||
else { |
|
||||||
if (this->y - 1 >= 0) { |
|
||||||
this->y--; |
|
||||||
this->x = this->buffer->at(this->y).length(); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
void right() { |
|
||||||
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()) { |
|
||||||
this->y++; |
|
||||||
this->x = this->buffer->at(this->y).size(); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
void up() { |
|
||||||
if (this->y - 1 >= 0) |
|
||||||
this->y--; |
|
||||||
else { |
|
||||||
return; |
|
||||||
} |
|
||||||
if (this->x >= this->buffer->linebuffer().at(this->y).length()) { |
|
||||||
long length = this->buffer->linebuffer().at(this->y).length(); |
|
||||||
this->x = length > 0 ? length - 1 : 0; |
|
||||||
} |
|
||||||
this->scrollUp(); |
|
||||||
} |
|
||||||
void down() { |
|
||||||
if (this->y + 1 < this->buffer->linebuffer().size() ) |
|
||||||
this->y++; |
|
||||||
else { |
|
||||||
return; |
|
||||||
} |
|
||||||
if (this->x >= this->buffer->linebuffer().at(this->y).length()) { |
|
||||||
long length = this->buffer->linebuffer().at(this->y).length(); |
|
||||||
this->x = length > 0 ? length - 1 : 0; |
|
||||||
} |
|
||||||
this->scrollDown(); |
|
||||||
} |
|
||||||
|
|
||||||
bool movement(int c) { |
|
||||||
switch (c) { |
|
||||||
case KEY_LEFT: |
|
||||||
this->left(); |
|
||||||
return true; |
|
||||||
case KEY_RIGHT: |
|
||||||
this->right(); |
|
||||||
return true; |
|
||||||
case KEY_UP: |
|
||||||
this->up(); |
|
||||||
return true; |
|
||||||
case KEY_DOWN: |
|
||||||
this->down(); |
|
||||||
return true; |
|
||||||
default: |
|
||||||
return false; |
|
||||||
} |
|
||||||
} |
|
||||||
void renderOverlay() { |
|
||||||
wbkgd(this->overlay, COLOR_PAIR(ncurses::Colors::STATUSBAR)); |
|
||||||
mvwaddstr(this->overlay, 3, 3, "Hallo win"); |
|
||||||
refresh(); |
|
||||||
wrefresh(this->overlay); |
|
||||||
} |
|
||||||
public: |
|
||||||
Editor(std::string file = "ubenannt"); |
|
||||||
Mode mode() { |
|
||||||
return this->mode_; |
|
||||||
} |
|
||||||
~Editor() { |
|
||||||
delete this->overlay; |
|
||||||
} |
|
||||||
void input(int c); |
|
||||||
void render(); |
|
||||||
static long Digits(long number); |
|
||||||
}; |
|
||||||
|
|
||||||
} |
|
@ -1,63 +0,0 @@ |
|||||||
#include <regex> |
|
||||||
#include <Highlighter.hpp> |
|
||||||
#include <iostream> |
|
||||||
|
|
||||||
namespace groove { |
|
||||||
std::unordered_map<Syntax, RulesList> Highlighter::Syntaxes = { |
|
||||||
std::make_pair(Syntax::CPP, |
|
||||||
RulesList { |
|
||||||
make_pair("([+-.<>,;=!:&*])", ncurses::Colors::CYAN), |
|
||||||
make_pair("([\\{\\}\\[\\]\\(\\)])", ncurses::Colors::GREEN), |
|
||||||
make_pair("(^|\\s)(while|if|try|catch|void|this|else|using|namespace|private|public|protected|friend|class|char|bool|unsigned|long|short|int|return):?(\\s+|$)\\*?", 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("([_a-zA-Z][a-zA-Z0-9_-]+)\\(", ncurses::Colors::CYAN), |
|
||||||
make_pair("\\\".*\\\"", ncurses::Colors::ORANGE), |
|
||||||
make_pair("/\\*.*\\*//*", ncurses::Colors::COMMENTS), |
|
||||||
make_pair("(//.*)", ncurses::Colors::ORANGE) |
|
||||||
}), |
|
||||||
std::make_pair(Syntax::DEFAULT, |
|
||||||
RulesList { |
|
||||||
make_pair("([.,;!\\*])", ncurses::Colors::MAGENTA), |
|
||||||
make_pair("([\\{\\}\\[\\]\\(\\)])", ncurses::Colors::GREEN), |
|
||||||
make_pair("\\\".*\\\"", ncurses::Colors::ORANGE), |
|
||||||
make_pair("/\\*.*\\*//*", ncurses::Colors::COMMENTS), |
|
||||||
}) |
|
||||||
}; |
|
||||||
|
|
||||||
|
|
||||||
RulesList Highlighter::list = RulesList(); |
|
||||||
|
|
||||||
std::unordered_map<long, std::pair<long, ncurses::Colors>> groove::Highlighter::get() { |
|
||||||
std::unordered_map<long, std::pair<long, ncurses::Colors>> list; |
|
||||||
|
|
||||||
if (this->line.size() > 0 && this->line.at(0) == '#') { |
|
||||||
list.emplace(0, std::make_pair(this->line.size(), ncurses::Colors::COMMENTS)); |
|
||||||
|
|
||||||
return list; |
|
||||||
} |
|
||||||
|
|
||||||
for (auto &keyword : Highlighter::list) { |
|
||||||
try { |
|
||||||
std::sregex_iterator next(this->line.begin(), this->line.end(), keyword.first); |
|
||||||
std::sregex_iterator end; |
|
||||||
while (next != end) { |
|
||||||
std::smatch match = *next; |
|
||||||
for (unsigned i = 0; i < match.size(); ++i) { |
|
||||||
list.emplace(match.position(i), std::make_pair(match.length(i), keyword.second)); |
|
||||||
} |
|
||||||
next++; |
|
||||||
} |
|
||||||
} catch (std::regex_error &e) { |
|
||||||
// Syntax error in the regular expression
|
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
return list; |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
|
|
@ -1,32 +0,0 @@ |
|||||||
#pragma once |
|
||||||
|
|
||||||
#include <string> |
|
||||||
#include <ncurses/ncurses.hpp> |
|
||||||
#include <vector> |
|
||||||
#include <unordered_map> |
|
||||||
#include <regex> |
|
||||||
|
|
||||||
namespace groove { |
|
||||||
|
|
||||||
typedef std::vector<std::pair<std::regex, ncurses::Colors>> RulesList; |
|
||||||
|
|
||||||
inline std::pair<std::regex, ncurses::Colors> make_pair(std::string regex, ncurses::Colors color) { |
|
||||||
return std::make_pair(std::regex(regex), color); |
|
||||||
}; |
|
||||||
|
|
||||||
enum class Syntax { |
|
||||||
DEFAULT, |
|
||||||
CPP |
|
||||||
}; |
|
||||||
|
|
||||||
class Highlighter { |
|
||||||
private: |
|
||||||
std::string line; |
|
||||||
public: |
|
||||||
static std::unordered_map<Syntax, RulesList> Syntaxes; |
|
||||||
static RulesList list; |
|
||||||
Highlighter(std::string line) : line(line) {} |
|
||||||
std::unordered_map<long, std::pair<long, ncurses::Colors>> get(); |
|
||||||
}; |
|
||||||
|
|
||||||
} |
|
@ -1,52 +0,0 @@ |
|||||||
#pragma once |
|
||||||
|
|
||||||
#include <functional> |
|
||||||
#include <memory> |
|
||||||
#include <vector> |
|
||||||
|
|
||||||
namespace groove { |
|
||||||
|
|
||||||
typedef std::function<void()> lambda; |
|
||||||
|
|
||||||
struct HistoryNode { |
|
||||||
lambda undo, redo; |
|
||||||
HistoryNode(lambda undo, lambda redo) : |
|
||||||
undo(std::move(undo)), redo(std::move(redo)) {} |
|
||||||
}; |
|
||||||
|
|
||||||
class History { |
|
||||||
private: |
|
||||||
std::unique_ptr<std::vector<HistoryNode>> 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() : history(std::make_unique<std::vector<HistoryNode>>()), pointer(0) { |
|
||||||
this->push(HistoryNode([](){}, [](){})); |
|
||||||
} |
|
||||||
void push(HistoryNode node) { |
|
||||||
this->cutTail(); |
|
||||||
this->history->emplace_back(std::move(node)); |
|
||||||
this->pointer = this->history->size() - 1; |
|
||||||
} |
|
||||||
|
|
||||||
void undo() { |
|
||||||
if (this->pointer <= 0) |
|
||||||
return; |
|
||||||
this->history->at(this->pointer).undo(); |
|
||||||
this->pointer--; |
|
||||||
} |
|
||||||
|
|
||||||
void redo() { |
|
||||||
if (this->pointer + 1 >= this->history->size()) |
|
||||||
return; |
|
||||||
this->history->at(this->pointer + 1).redo(); |
|
||||||
this->pointer++; |
|
||||||
} |
|
||||||
}; |
|
||||||
|
|
||||||
} |
|
@ -1,72 +0,0 @@ |
|||||||
#include <config/ConfigParser.hpp> |
|
||||||
#include <fstream> |
|
||||||
|
|
||||||
namespace groove { |
|
||||||
namespace config { |
|
||||||
|
|
||||||
void ConfigValue::set(std::string &value) { |
|
||||||
strVal = value; |
|
||||||
boolVal = value == "true"; |
|
||||||
try { |
|
||||||
intVal = std::stoi(value); |
|
||||||
} catch (std::invalid_argument) { |
|
||||||
intVal = 0; |
|
||||||
} |
|
||||||
try { |
|
||||||
floatVal = std::stof(value); |
|
||||||
} catch (std::invalid_argument) { |
|
||||||
floatVal = 0.0f; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
void ConfigParser::parse() { |
|
||||||
std::ifstream file(this->filename.c_str()); |
|
||||||
if(file.is_open()) { |
|
||||||
while(!file.eof()) { |
|
||||||
std::string line; |
|
||||||
std::getline(file, line); |
|
||||||
if (line == "" || line.at(0) == '#') |
|
||||||
continue; |
|
||||||
std::vector<std::string> pair = std::explode(line,'='); |
|
||||||
std::string key = pair.at(0); |
|
||||||
std::string value = pair.at(1); |
|
||||||
std::trim(key); |
|
||||||
std::trim(value); |
|
||||||
this->params->emplace(key, ConfigValue(value)); |
|
||||||
} |
|
||||||
} |
|
||||||
else { |
|
||||||
std::cerr << "Could not load config file." << std::endl; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
template<> |
|
||||||
std::string ConfigParser::get<std::string>(const std::string& key) { |
|
||||||
if (this->params->find(key) == this->params->end()) |
|
||||||
return ""; |
|
||||||
return this->params->at(key).strVal; |
|
||||||
} |
|
||||||
|
|
||||||
template<> |
|
||||||
int ConfigParser::get<int>(const std::string& key) { |
|
||||||
if (this->params->find(key) == this->params->end()) |
|
||||||
return 0; |
|
||||||
return this->params->at(key).intVal; |
|
||||||
} |
|
||||||
|
|
||||||
template<> |
|
||||||
float ConfigParser::get<float>(const std::string& key) { |
|
||||||
if (this->params->find(key) == this->params->end()) |
|
||||||
return 0.0f; |
|
||||||
return this->params->at(key).floatVal; |
|
||||||
} |
|
||||||
|
|
||||||
template<> |
|
||||||
bool ConfigParser::get<bool>(const std::string& key) { |
|
||||||
if (this->params->find(key) == this->params->end()) |
|
||||||
return false; |
|
||||||
return this->params->at(key).boolVal; |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
||||||
} |
|
@ -1,81 +0,0 @@ |
|||||||
#pragma once |
|
||||||
|
|
||||||
#include <string> |
|
||||||
#include <unordered_map> |
|
||||||
#include <vector> |
|
||||||
#include <sstream> |
|
||||||
#include <algorithm> |
|
||||||
#include <iostream> |
|
||||||
#include <pwd.h> |
|
||||||
#include <unistd.h> |
|
||||||
#include <sys/types.h> |
|
||||||
#include <memory> |
|
||||||
|
|
||||||
namespace std { |
|
||||||
static inline std::vector<std::string> explode(const std::string &str, char delimiter) { |
|
||||||
std::vector<std::string> tokens; |
|
||||||
std::stringstream inStream(str); |
|
||||||
std::string tempString; |
|
||||||
|
|
||||||
while (std::getline(inStream, tempString, delimiter)) |
|
||||||
tokens.push_back(tempString); |
|
||||||
|
|
||||||
return tokens; |
|
||||||
} |
|
||||||
|
|
||||||
static inline void ltrim(std::string &string) { |
|
||||||
string.erase(string.begin(), std::find_if(string.begin(), string.end(), [](auto c){return !std::isspace(c);})); |
|
||||||
} |
|
||||||
|
|
||||||
static inline void rtrim(std::string &string) { |
|
||||||
string.erase(std::find_if(string.rbegin(), string.rend(), [](auto c){return !std::isspace(c);}).base(), string.end()); |
|
||||||
} |
|
||||||
|
|
||||||
static inline void trim(std::string &string) { |
|
||||||
rtrim(string); |
|
||||||
ltrim(string); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
namespace groove { |
|
||||||
namespace config { |
|
||||||
|
|
||||||
struct ConfigValue { |
|
||||||
std::string strVal; |
|
||||||
int intVal; |
|
||||||
float floatVal; |
|
||||||
bool boolVal; |
|
||||||
|
|
||||||
ConfigValue(std::string &value) { |
|
||||||
set(value); |
|
||||||
} |
|
||||||
|
|
||||||
void set(std::string &value); |
|
||||||
}; |
|
||||||
|
|
||||||
class ConfigParser { |
|
||||||
private: |
|
||||||
std::unique_ptr<std::unordered_map<std::string, ConfigValue>> params; |
|
||||||
std::string filename; |
|
||||||
|
|
||||||
void parse(); |
|
||||||
public: |
|
||||||
ConfigParser() : params(std::make_unique<std::unordered_map<std::string, ConfigValue>>()) { |
|
||||||
this->filename = getenv("HOME"); |
|
||||||
this->filename += "/.grooverc"; |
|
||||||
this->parse(); |
|
||||||
} |
|
||||||
ConfigParser(const ConfigParser&) = delete; |
|
||||||
|
|
||||||
template <typename T> |
|
||||||
T get(const std::string& key); |
|
||||||
|
|
||||||
template<typename T> |
|
||||||
void set(std::string key, T value) { |
|
||||||
std::string newValue = std::to_string(value); |
|
||||||
this->params->at(key).set(newValue); |
|
||||||
} |
|
||||||
}; |
|
||||||
|
|
||||||
} |
|
||||||
} |
|
@ -1,138 +0,0 @@ |
|||||||
#include <modes/Edit.hpp> |
|
||||||
#include <Editor.hpp> |
|
||||||
|
|
||||||
namespace groove { |
|
||||||
namespace modes { |
|
||||||
|
|
||||||
void Edit::input(int c) { |
|
||||||
this->status_ = this->editor.buffer->changed ? "*" : ""; |
|
||||||
if (this->editor.movement(c)) |
|
||||||
return; |
|
||||||
switch (c) { |
|
||||||
case 'q': |
|
||||||
this->editor.mode_ = groove::Mode::EXIT; |
|
||||||
this->editor.input(' '); |
|
||||||
break; |
|
||||||
case 'i': |
|
||||||
{ |
|
||||||
std::vector<std::string>& oldBuffer = this->editor.buffer->linebuffer(); |
|
||||||
this->editor.history.push(HistoryNode( |
|
||||||
[this, oldBuffer](){ |
|
||||||
this->editor.buffer->swap(oldBuffer); |
|
||||||
}, [this](){ |
|
||||||
this->editor.buffer->revert(); |
|
||||||
}) |
|
||||||
); |
|
||||||
this->editor.mode_ = groove::Mode::INSERT; |
|
||||||
} |
|
||||||
break; |
|
||||||
case 'a': |
|
||||||
this->editor.x = static_cast<int>(this->editor.buffer->at(this->editor.y).length()); |
|
||||||
this->editor.mode_ = groove::Mode::INSERT; |
|
||||||
break; |
|
||||||
case 's': |
|
||||||
this->editor.mode_ = groove::Mode::SAVE; |
|
||||||
break; |
|
||||||
case 'c': |
|
||||||
this->editor.clipboard = this->editor.buffer->at(this->editor.y); |
|
||||||
break; |
|
||||||
case 'x': |
|
||||||
{ |
|
||||||
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': |
|
||||||
{ |
|
||||||
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': |
|
||||||
{ |
|
||||||
if (this->editor.y >= this->editor.buffer->size() - 1) |
|
||||||
break; |
|
||||||
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) { |
|
||||||
case LineMode::NUMBERS: |
|
||||||
this->editor.lineMode = LineMode::RELATIVE; |
|
||||||
break; |
|
||||||
case LineMode::RELATIVE: |
|
||||||
this->editor.lineMode = LineMode::NONE; |
|
||||||
break; |
|
||||||
case LineMode::NONE: |
|
||||||
this->editor.lineMode = LineMode::NUMBERS; |
|
||||||
break; |
|
||||||
} |
|
||||||
break; |
|
||||||
case 'f': |
|
||||||
this->editor.mode_ = groove::Mode::SEARCH; |
|
||||||
this->editor.input(' '); |
|
||||||
break; |
|
||||||
case 'u': |
|
||||||
this->editor.history.undo(); |
|
||||||
break; |
|
||||||
case 'r': |
|
||||||
this->editor.history.redo(); |
|
||||||
break; |
|
||||||
case '^': |
|
||||||
this->editor.x = 0; |
|
||||||
break; |
|
||||||
case '$': |
|
||||||
this->editor.x = this->editor.buffer->at(this->editor.y).size(); |
|
||||||
break; |
|
||||||
case 'g': |
|
||||||
this->editor.y = 0; |
|
||||||
this->editor.offset = 0; |
|
||||||
break; |
|
||||||
case 'G': |
|
||||||
this->editor.y = this->editor.buffer->size() - 1; |
|
||||||
this->editor.offset = this->editor.buffer->size() - LINES + 1; |
|
||||||
break; |
|
||||||
case KEY_NPAGE: |
|
||||||
for (int i = 0; i < LINES; i++) { |
|
||||||
this->editor.down(); |
|
||||||
} |
|
||||||
break; |
|
||||||
case KEY_PPAGE: |
|
||||||
for (int i = 0; i < LINES; i++) { |
|
||||||
this->editor.up(); |
|
||||||
} |
|
||||||
break; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -1,18 +0,0 @@ |
|||||||
#pragma once |
|
||||||
|
|
||||||
#include <modes/Mode.hpp> |
|
||||||
|
|
||||||
namespace groove { |
|
||||||
namespace modes { |
|
||||||
|
|
||||||
class Edit : public Mode { |
|
||||||
public: |
|
||||||
using Mode::Mode; |
|
||||||
std::string status() { |
|
||||||
return this->status_; |
|
||||||
} |
|
||||||
void input(int c); |
|
||||||
}; |
|
||||||
|
|
||||||
} |
|
||||||
} |
|
@ -1,34 +0,0 @@ |
|||||||
#include <modes/Exit.hpp> |
|
||||||
#include <Editor.hpp> |
|
||||||
|
|
||||||
|
|
||||||
namespace groove { |
|
||||||
namespace modes { |
|
||||||
|
|
||||||
void Exit::input(int c) { |
|
||||||
if (!this->editor.buffer->changed) { |
|
||||||
this->editor.mode_ = groove::Mode::QUIT; |
|
||||||
|
|
||||||
return; |
|
||||||
} |
|
||||||
|
|
||||||
this->status_ = "Save file '" + this->editor.filename + "'? [Y/N]:"; |
|
||||||
switch (c) { |
|
||||||
case 27: |
|
||||||
this->status_ = ""; |
|
||||||
this->editor.mode_ = groove::Mode::EDIT; |
|
||||||
this->editor.input(' '); |
|
||||||
break; |
|
||||||
case 'n': |
|
||||||
this->editor.mode_ = groove::Mode::QUIT; |
|
||||||
break; |
|
||||||
case 'y': |
|
||||||
if (!this->editor.save()) |
|
||||||
std::cerr << "Could not write to file: '" << this->editor.filename << "'\n"; |
|
||||||
this->editor.mode_ = groove::Mode::QUIT; |
|
||||||
break; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
||||||
} |
|
@ -1,18 +0,0 @@ |
|||||||
#pragma once |
|
||||||
|
|
||||||
#include <modes/Mode.hpp> |
|
||||||
|
|
||||||
namespace groove { |
|
||||||
namespace modes { |
|
||||||
|
|
||||||
class Exit : public Mode { |
|
||||||
public: |
|
||||||
using Mode::Mode; |
|
||||||
std::string status() { |
|
||||||
return this->status_; |
|
||||||
} |
|
||||||
void input(int c); |
|
||||||
}; |
|
||||||
|
|
||||||
} |
|
||||||
} |
|
@ -1,94 +0,0 @@ |
|||||||
#include <modes/Insert.hpp> |
|
||||||
#include <Editor.hpp> |
|
||||||
#include <iostream> |
|
||||||
|
|
||||||
namespace groove { |
|
||||||
namespace modes { |
|
||||||
|
|
||||||
void Insert::input(int c) { |
|
||||||
if (this->editor.movement(c)) |
|
||||||
return; |
|
||||||
switch (c) { |
|
||||||
case 27: |
|
||||||
this->editor.mode_ = groove::Mode::EDIT; |
|
||||||
break; |
|
||||||
case KEY_ENTER: |
|
||||||
case 10: |
|
||||||
{ |
|
||||||
std::string appendix; |
|
||||||
appendix = ""; |
|
||||||
if (this->editor.buffer->at(this->editor.y).size() > this->editor.x) { |
|
||||||
appendix = this->editor.buffer->at(this->editor.y).substr(this->editor.x, this->editor.buffer->at(this->editor.y).size()); |
|
||||||
this->editor.buffer->at(this->editor.y) = this->editor.buffer->at(this->editor.y).substr(0, this->editor.x); |
|
||||||
} |
|
||||||
this->editor.buffer->insert(this->editor.y); |
|
||||||
this->editor.y++; |
|
||||||
this->editor.buffer->linebuffer().at(this->editor.y) += appendix; |
|
||||||
this->editor.x = 0; |
|
||||||
this->editor.scrollDown(); |
|
||||||
} |
|
||||||
break; |
|
||||||
case KEY_BACKSPACE: |
|
||||||
if (this->editor.x - 1 >= 0) { |
|
||||||
this->editor.x--; |
|
||||||
this->editor.buffer->remove(this->editor.y, this->editor.x); |
|
||||||
} |
|
||||||
else { |
|
||||||
if (this->editor.buffer->at(this->editor.y).size() > 0 && this->editor.y > 0) { |
|
||||||
int oldLength = static_cast<int>(this->editor.buffer->at(this->editor.y - 1).size()); |
|
||||||
this->editor.buffer->at(this->editor.y - 1) += this->editor.buffer->at(this->editor.y); |
|
||||||
this->editor.buffer->remove(this->editor.y); |
|
||||||
this->editor.y--; |
|
||||||
this->editor.x = oldLength; |
|
||||||
} |
|
||||||
else { |
|
||||||
if (this->editor.y - 1 >= 0) { |
|
||||||
this->editor.buffer->remove(this->editor.y); |
|
||||||
this->editor.y--; |
|
||||||
this->editor.x = static_cast<int>(this->editor.buffer->at(this->editor.y).length()); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
this->editor.scrollUp(); |
|
||||||
break; |
|
||||||
case KEY_DC: |
|
||||||
if (this->editor.buffer->at(this->editor.y).length() > this->editor.x) |
|
||||||
this->editor.buffer->deleteChar(this->editor.y, this->editor.x); |
|
||||||
else { |
|
||||||
if (this->editor.buffer->at(this->editor.y).empty() && this->editor.buffer->linebuffer().size() - 1 > this->editor.y) |
|
||||||
this->editor.buffer->remove(this->editor.y); |
|
||||||
else if (this->editor.buffer->linebuffer().size() - 1 > this->editor.y) { |
|
||||||
this->editor.buffer->at(this->editor.y) += this->editor.buffer->at(this->editor.y + 1); |
|
||||||
this->editor.buffer->remove(this->editor.y + 1); |
|
||||||
} |
|
||||||
} |
|
||||||
break; |
|
||||||
case KEY_BTAB: |
|
||||||
case KEY_CTAB: |
|
||||||
case KEY_STAB: |
|
||||||
case KEY_CATAB: |
|
||||||
case 9: |
|
||||||
this->editor.buffer->at(this->editor.y).insert(this->editor.x, 4, ' '); |
|
||||||
this->editor.x += 4; |
|
||||||
break; |
|
||||||
default: |
|
||||||
{ |
|
||||||
switch (c) { |
|
||||||
case '{': |
|
||||||
case '[': |
|
||||||
this->editor.buffer->at(this->editor.y).insert(this->editor.x, 1, c + 2); |
|
||||||
break; |
|
||||||
case '(': |
|
||||||
this->editor.buffer->at(this->editor.y).insert(this->editor.x, 1, c + 1); |
|
||||||
break; |
|
||||||
} |
|
||||||
this->editor.buffer->at(this->editor.y).insert(this->editor.x, 1, char(c)); |
|
||||||
if (this->editor.lastChar != 195 && this->editor.lastChar != 182) |
|
||||||
this->editor.x++; |
|
||||||
this->editor.lastChar = char(c); |
|
||||||
} |
|
||||||
break; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -1,18 +0,0 @@ |
|||||||
#pragma once |
|
||||||
|
|
||||||
#include <modes/Mode.hpp> |
|
||||||
|
|
||||||
namespace groove { |
|
||||||
namespace modes { |
|
||||||
|
|
||||||
class Insert : public Mode { |
|
||||||
public: |
|
||||||
using Mode::Mode; |
|
||||||
std::string status() { |
|
||||||
return " -- INSERT --" + this->status_; |
|
||||||
} |
|
||||||
void input(int c); |
|
||||||
}; |
|
||||||
|
|
||||||
} |
|
||||||
} |
|
@ -1,9 +0,0 @@ |
|||||||
#include <modes/Mode.hpp> |
|
||||||
|
|
||||||
namespace groove { |
|
||||||
namespace modes { |
|
||||||
|
|
||||||
Mode::~Mode() {} |
|
||||||
|
|
||||||
} |
|
||||||
} |
|
@ -1,22 +0,0 @@ |
|||||||
#pragma once |
|
||||||
|
|
||||||
#include <string> |
|
||||||
|
|
||||||
namespace groove { |
|
||||||
class Editor; |
|
||||||
|
|
||||||
namespace modes { |
|
||||||
|
|
||||||
class Mode { |
|
||||||
protected: |
|
||||||
groove::Editor& editor; |
|
||||||
std::string status_; |
|
||||||
public: |
|
||||||
Mode(groove::Editor& editor) : editor(editor) , status_("") {} |
|
||||||
virtual ~Mode() = 0; |
|
||||||
virtual std::string status() = 0; |
|
||||||
virtual void input(int c) = 0; |
|
||||||
}; |
|
||||||
|
|
||||||
} |
|
||||||
} |
|
@ -1,12 +0,0 @@ |
|||||||
#include <modes/Quit.hpp> |
|
||||||
|
|
||||||
|
|
||||||
namespace groove { |
|
||||||
namespace modes { |
|
||||||
|
|
||||||
void Quit::input(int c) { |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
} |
|
||||||
} |
|
@ -1,18 +0,0 @@ |
|||||||
#pragma once |
|
||||||
|
|
||||||
#include <modes/Mode.hpp> |
|
||||||
|
|
||||||
namespace groove { |
|
||||||
namespace modes { |
|
||||||
|
|
||||||
class Quit : public Mode { |
|
||||||
public: |
|
||||||
using Mode::Mode; |
|
||||||
std::string status() { |
|
||||||
return this->status_; |
|
||||||
} |
|
||||||
void input(int c); |
|
||||||
}; |
|
||||||
|
|
||||||
} |
|
||||||
} |
|
@ -1,29 +0,0 @@ |
|||||||
#include <modes/Save.hpp> |
|
||||||
#include <Editor.hpp> |
|
||||||
#include <iostream> |
|
||||||
|
|
||||||
namespace groove { |
|
||||||
namespace modes { |
|
||||||
|
|
||||||
void Save::input(int c) { |
|
||||||
switch (c) { |
|
||||||
case 'n': |
|
||||||
case 27: |
|
||||||
this->editor.mode_ = groove::Mode::EDIT; |
|
||||||
break; |
|
||||||
case 'y': |
|
||||||
if (!this->editor.save()) |
|
||||||
std::cerr << "Could not write to file: '" << this->editor.filename << "'\n"; |
|
||||||
this->editor.buffer->changed = false; |
|
||||||
this->editor.mode_ = groove::Mode::EDIT; |
|
||||||
break; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
std::string Save::status() { |
|
||||||
this->status_ = "Save file '" + this->editor.filename + "'? [Y/N]:"; |
|
||||||
return this->status_; |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
||||||
} |
|
@ -1,16 +0,0 @@ |
|||||||
#pragma once |
|
||||||
|
|
||||||
#include <modes/Mode.hpp> |
|
||||||
|
|
||||||
namespace groove { |
|
||||||
namespace modes { |
|
||||||
|
|
||||||
class Save : public Mode { |
|
||||||
public: |
|
||||||
using Mode::Mode; |
|
||||||
std::string status(); |
|
||||||
void input(int c); |
|
||||||
}; |
|
||||||
|
|
||||||
} |
|
||||||
} |
|
@ -1,70 +0,0 @@ |
|||||||
#include <modes/Search.hpp> |
|
||||||
#include <Editor.hpp> |
|
||||||
#include <regex> |
|
||||||
|
|
||||||
|
|
||||||
namespace groove { |
|
||||||
namespace modes { |
|
||||||
|
|
||||||
void Search::input(int c) { |
|
||||||
this->editor.movement(c); |
|
||||||
switch (c) { |
|
||||||
case 27: |
|
||||||
this->editor.mode_ = groove::Mode::EDIT; |
|
||||||
this->editor.input(' '); |
|
||||||
break; |
|
||||||
case KEY_BACKSPACE: |
|
||||||
if (this->keyword.size() > 0) |
|
||||||
this->keyword.pop_back(); |
|
||||||
break; |
|
||||||
case KEY_ENTER: |
|
||||||
case 10: |
|
||||||
this->search(); |
|
||||||
break; |
|
||||||
default: |
|
||||||
{ |
|
||||||
std::regex regex("[ -~]"); |
|
||||||
std::string character(1, c); |
|
||||||
if (!std::regex_match(character, regex)) |
|
||||||
break; |
|
||||||
this->keyword += c; |
|
||||||
} |
|
||||||
break; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
void Search::search() { |
|
||||||
if (this->keyword == "") |
|
||||||
return; |
|
||||||
std::regex regex(this->keyword); |
|
||||||
int pos = this->match == 0 ? this->position + 1 : this->position; |
|
||||||
for (long i = pos; i < this->editor.buffer->size(); i++) { |
|
||||||
if (i >= this->editor.buffer->size() - 1) |
|
||||||
this->position = 0; |
|
||||||
std::string line = this->editor.buffer->at(i); |
|
||||||
try { |
|
||||||
std::sregex_iterator next(line.begin(), line.end(), regex); |
|
||||||
std::sregex_iterator end; |
|
||||||
if (next == end) { |
|
||||||
this->match = 0; |
|
||||||
} |
|
||||||
while (next != end) { |
|
||||||
std::smatch match = *next; |
|
||||||
for (unsigned n = this->match; n < match.size(); ++n) { |
|
||||||
this->editor.y = i; |
|
||||||
this->editor.offset = i - LINES / 2; |
|
||||||
this->editor.x = match.position(n); |
|
||||||
this->position = i; |
|
||||||
this->match++; |
|
||||||
return; |
|
||||||
} |
|
||||||
next++; |
|
||||||
} |
|
||||||
} catch (std::regex_error& e) { |
|
||||||
// Syntax error in the regular expression
|
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
||||||
} |
|
@ -1,24 +0,0 @@ |
|||||||
#pragma once |
|
||||||
|
|
||||||
#include <modes/Mode.hpp> |
|
||||||
|
|
||||||
namespace groove { |
|
||||||
namespace modes { |
|
||||||
|
|
||||||
class Search : public Mode { |
|
||||||
private: |
|
||||||
std::string keyword; |
|
||||||
long position; |
|
||||||
int match; |
|
||||||
|
|
||||||
void search(); |
|
||||||
public: |
|
||||||
Search(groove::Editor& editor) : Mode(editor), keyword(""), position(0), match(0) {}; |
|
||||||
std::string status() { |
|
||||||
return "\tFind: " + this->keyword + ' ' + this->status_; |
|
||||||
} |
|
||||||
void input(int c); |
|
||||||
}; |
|
||||||
|
|
||||||
} |
|
||||||
} |
|
@ -1,88 +0,0 @@ |
|||||||
#pragma once |
|
||||||
|
|
||||||
#include <ncurses.h> |
|
||||||
#include <iostream> |
|
||||||
|
|
||||||
#define CURSES_MAX_COLOR 1000.0f |
|
||||||
|
|
||||||
namespace groove { |
|
||||||
namespace ncurses { |
|
||||||
|
|
||||||
struct Color { |
|
||||||
short r, g, b; |
|
||||||
Color(short r, short g, short b) : |
|
||||||
r(static_cast<short>(r / 255.0f * CURSES_MAX_COLOR)), |
|
||||||
g(static_cast<short>(g / 255.0f * CURSES_MAX_COLOR)), |
|
||||||
b(static_cast<short>(b / 255.0f * CURSES_MAX_COLOR)) {} |
|
||||||
}; |
|
||||||
|
|
||||||
enum Colors { |
|
||||||
MAIN = 1, |
|
||||||
STATUSBAR, |
|
||||||
MAGENTA, |
|
||||||
GREEN, |
|
||||||
PURPLE, |
|
||||||
ORANGE, |
|
||||||
CYAN, |
|
||||||
GREY, |
|
||||||
LINENUMBERS, |
|
||||||
COMMENTS |
|
||||||
}; |
|
||||||
|
|
||||||
class ncurses { |
|
||||||
private: |
|
||||||
WINDOW* overlay; |
|
||||||
public: |
|
||||||
ncurses() { |
|
||||||
}; |
|
||||||
void init() { |
|
||||||
setlocale(LC_ALL, ""); |
|
||||||
initscr(); // Start ncurses mode
|
|
||||||
noecho(); // Don't echo keystrokes
|
|
||||||
cbreak(); // Disable line buffering
|
|
||||||
keypad(stdscr, true); // Enable special keys to be recorde
|
|
||||||
|
|
||||||
start_color(); |
|
||||||
|
|
||||||
if (can_change_color()) { |
|
||||||
Color grey(39, 40, 34); |
|
||||||
Color pink(255, 0, 103); |
|
||||||
Color cyan(102 , 217, 239); |
|
||||||
Color green(162, 217, 43); |
|
||||||
Color lightgray(60, 61, 55); |
|
||||||
Color lighterGray(144, 144, 138); |
|
||||||
Color yellow(230, 219, 116); |
|
||||||
|
|
||||||
init_color(COLOR_BLACK, grey.r, grey.g, grey.b); |
|
||||||
init_color(COLOR_MAGENTA, pink.r, pink.g, pink.b); |
|
||||||
init_color(COLOR_CYAN, cyan.r, cyan.g, cyan.b); |
|
||||||
init_color(COLOR_BLUE, lightgray.r, lightgray.g, lightgray.b); |
|
||||||
init_color(COLOR_YELLOW, yellow.r, yellow.g, yellow.b); |
|
||||||
init_color(COLOR_GREEN, green.r, green.g, green.b); |
|
||||||
init_color(COLOR_RED, lighterGray.r, lighterGray.g, lighterGray.b); |
|
||||||
} |
|
||||||
|
|
||||||
init_pair(Colors::LINENUMBERS, COLOR_WHITE, COLOR_BLUE); |
|
||||||
init_pair(Colors::COMMENTS, COLOR_RED, COLOR_BLACK); |
|
||||||
init_pair(Colors::MAIN, COLOR_WHITE, COLOR_BLACK); |
|
||||||
init_pair(Colors::STATUSBAR, COLOR_WHITE, COLOR_MAGENTA); |
|
||||||
init_pair(Colors::MAGENTA, COLOR_MAGENTA, COLOR_BLACK); |
|
||||||
init_pair(Colors::GREEN, COLOR_GREEN, COLOR_BLACK); |
|
||||||
init_pair(Colors::GREY, COLOR_YELLOW, COLOR_BLACK); |
|
||||||
init_pair(Colors::CYAN, COLOR_CYAN, COLOR_BLACK); |
|
||||||
init_pair(Colors::ORANGE, COLOR_YELLOW, COLOR_BLACK); |
|
||||||
|
|
||||||
attron(COLOR_PAIR(1)); |
|
||||||
} |
|
||||||
|
|
||||||
void flush() { |
|
||||||
refresh(); |
|
||||||
} |
|
||||||
|
|
||||||
void quit() { |
|
||||||
endwin(); |
|
||||||
} |
|
||||||
}; |
|
||||||
|
|
||||||
} |
|
||||||
} |
|
Loading…
Reference in new issue