commit
b1db1bcc8a
8 changed files with 480 additions and 0 deletions
@ -0,0 +1,2 @@ |
|||||||
|
.idea |
||||||
|
build |
@ -0,0 +1,26 @@ |
|||||||
|
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) |
@ -0,0 +1,25 @@ |
|||||||
|
#include <iostream> |
||||||
|
#include <ncurses/ncurses.hpp> |
||||||
|
#include <Editor.hpp> |
||||||
|
|
||||||
|
int main(int argc, char* argv[]) { |
||||||
|
groove::Editor editor; |
||||||
|
if (argc > 1) |
||||||
|
editor = groove::Editor(argv[1]); |
||||||
|
else |
||||||
|
editor = 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; |
||||||
|
} |
@ -0,0 +1,9 @@ |
|||||||
|
//
|
||||||
|
// Created by miko on 04.03.17.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <Buffer.hpp> |
||||||
|
|
||||||
|
namespace groove { |
||||||
|
|
||||||
|
} |
@ -0,0 +1,53 @@ |
|||||||
|
#pragma once |
||||||
|
|
||||||
|
#include <vector> |
||||||
|
#include <string> |
||||||
|
#include <memory> |
||||||
|
|
||||||
|
namespace groove { |
||||||
|
|
||||||
|
class Buffer { |
||||||
|
private: |
||||||
|
std::vector<std::string> lines; |
||||||
|
void removeTabs(std::string& line) { |
||||||
|
ulong tab = line.find("\t"); |
||||||
|
if(tab != line.npos) |
||||||
|
this->removeTabs(line.replace(tab, 1, " ")); |
||||||
|
} |
||||||
|
public: |
||||||
|
Buffer() : lines(std::vector<std::string>()) {} |
||||||
|
std::vector<std::string>& linebuffer() { |
||||||
|
return this->lines; |
||||||
|
} |
||||||
|
|
||||||
|
void insert(std::string str, int line) { |
||||||
|
this->lines.insert(this->lines.begin() + line, str); |
||||||
|
this->removeTabs(this->lines.at(line)); |
||||||
|
} |
||||||
|
|
||||||
|
void insert(std::string line) { |
||||||
|
this->removeTabs(line); |
||||||
|
this->lines.emplace_back(line); |
||||||
|
} |
||||||
|
void insert(int after) { |
||||||
|
this->lines.insert(this->lines.begin() + after + 1, ""); |
||||||
|
} |
||||||
|
|
||||||
|
void remove(int line) { |
||||||
|
this->lines.erase(this->lines.begin() + line); |
||||||
|
} |
||||||
|
|
||||||
|
void remove(int line, int car) { |
||||||
|
this->lines.at(line).erase(this->lines.at(line).begin() + car); |
||||||
|
} |
||||||
|
|
||||||
|
void deleteChar(int line, int car) { |
||||||
|
this->lines.at(line).erase(this->lines.at(line).begin() + car); |
||||||
|
} |
||||||
|
|
||||||
|
std::string& at(int line) { |
||||||
|
return this->lines.at(line); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
} |
@ -0,0 +1,234 @@ |
|||||||
|
#include <Editor.hpp> |
||||||
|
#include <fstream> |
||||||
|
#include <iostream> |
||||||
|
|
||||||
|
namespace groove { |
||||||
|
|
||||||
|
Editor::Editor(std::string filename) : x(0), y(0), buffer(std::make_unique<Buffer>()), |
||||||
|
mode_(Mode::EDIT), filename(filename), offset(0) { |
||||||
|
|
||||||
|
if (!this->load()) { |
||||||
|
std::cerr << "Could not open file: '" << this->filename << "', creating it on save.\n"; |
||||||
|
this->buffer->insert(""); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
bool Editor::load() { |
||||||
|
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); |
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
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 car) { |
||||||
|
if (this->mode_ != Mode::SAVE) { |
||||||
|
switch (car) { |
||||||
|
case KEY_LEFT: |
||||||
|
this->left(); |
||||||
|
return; |
||||||
|
case KEY_RIGHT: |
||||||
|
this->right(); |
||||||
|
return; |
||||||
|
case KEY_UP: |
||||||
|
this->up(); |
||||||
|
return; |
||||||
|
case KEY_DOWN: |
||||||
|
this->down(); |
||||||
|
return; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
switch (this->mode_) { |
||||||
|
case Mode::EDIT: |
||||||
|
switch (car) { |
||||||
|
case 'q': |
||||||
|
this->mode_ = Mode::QUIT; |
||||||
|
break; |
||||||
|
case 'i': |
||||||
|
this->mode_ = Mode::INSERT; |
||||||
|
break; |
||||||
|
case 'a': |
||||||
|
this->x = static_cast<int>(this->buffer->at(this->y).length()); |
||||||
|
this->mode_ = Mode::INSERT; |
||||||
|
break; |
||||||
|
case 's': |
||||||
|
this->mode_ = Mode::SAVE; |
||||||
|
break; |
||||||
|
case 'c': |
||||||
|
this->clipboard = this->buffer->at(this->y); |
||||||
|
break; |
||||||
|
case 'x': |
||||||
|
this->clipboard = this->buffer->at(this->y); |
||||||
|
this->buffer->remove(this->y); |
||||||
|
break; |
||||||
|
case 'p': |
||||||
|
this->buffer->insert(this->clipboard, this->y); |
||||||
|
this->y++; |
||||||
|
this->x = this->buffer->at(this->y).size(); |
||||||
|
break; |
||||||
|
case 'd': |
||||||
|
this->buffer->remove(this->y); |
||||||
|
break; |
||||||
|
} |
||||||
|
break; |
||||||
|
case Mode::INSERT: |
||||||
|
switch (car) { |
||||||
|
case 27: |
||||||
|
this->mode_ = Mode::EDIT; |
||||||
|
break; |
||||||
|
case KEY_ENTER: |
||||||
|
case 10: |
||||||
|
{ |
||||||
|
std::string appendix; |
||||||
|
appendix = ""; |
||||||
|
if (this->buffer->at(this->y).size() > this->x) { |
||||||
|
appendix = this->buffer->at(this->y).substr(this->x, this->buffer->at(this->y).size()); |
||||||
|
this->buffer->at(this->y) = this->buffer->at(this->y).substr(0, this->x); |
||||||
|
} |
||||||
|
this->buffer->insert(this->y); |
||||||
|
this->y++; |
||||||
|
this->buffer->linebuffer().at(this->y) += appendix; |
||||||
|
this->x = 0; |
||||||
|
} |
||||||
|
break; |
||||||
|
case KEY_BACKSPACE: |
||||||
|
if (this->x - 1 >= 0) { |
||||||
|
this->x--; |
||||||
|
this->buffer->remove(this->y, this->x); |
||||||
|
} |
||||||
|
else { |
||||||
|
if (this->buffer->at(this->y).size() > 0 && this->y > 0) { |
||||||
|
int oldLength = static_cast<int>(this->buffer->at(this->y - 1).size()); |
||||||
|
this->buffer->at(this->y - 1) += this->buffer->at(this->y); |
||||||
|
this->buffer->remove(this->y); |
||||||
|
this->y--; |
||||||
|
this->x = oldLength; |
||||||
|
} |
||||||
|
else { |
||||||
|
if (this->y - 1 >= 0) { |
||||||
|
this->buffer->remove(this->y); |
||||||
|
this->y--; |
||||||
|
this->x = static_cast<int>(this->buffer->at(this->y).length()); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
break; |
||||||
|
case KEY_DC: |
||||||
|
if (this->buffer->at(this->y).length() > this->x) |
||||||
|
this->buffer->deleteChar(this->y, this->x); |
||||||
|
else { |
||||||
|
if (this->buffer->at(this->y).empty() && this->buffer->linebuffer().size() - 1 > this->y) |
||||||
|
this->buffer->remove(this->y); |
||||||
|
else if (this->buffer->linebuffer().size() - 1 > this->y) { |
||||||
|
this->buffer->at(this->y) += this->buffer->at(this->y + 1); |
||||||
|
this->buffer->remove(this->y + 1); |
||||||
|
} |
||||||
|
} |
||||||
|
break; |
||||||
|
case KEY_BTAB: |
||||||
|
case KEY_CTAB: |
||||||
|
case KEY_STAB: |
||||||
|
case KEY_CATAB: |
||||||
|
case 9: |
||||||
|
this->buffer->at(this->y).insert(this->x, 4, ' '); |
||||||
|
this->x += 4; |
||||||
|
break; |
||||||
|
default: |
||||||
|
{ |
||||||
|
std::ofstream log("log"); |
||||||
|
log << car << std::endl; |
||||||
|
this->buffer->at(this->y).insert(this->x, 1, char(car)); |
||||||
|
if (this->lastChar != 195 && this->lastChar != 182) |
||||||
|
this->x++; |
||||||
|
this->lastChar = char(car); |
||||||
|
} |
||||||
|
break; |
||||||
|
} |
||||||
|
break; |
||||||
|
case Mode::QUIT: |
||||||
|
break; |
||||||
|
case Mode::SAVE: |
||||||
|
switch (car) { |
||||||
|
case 'n': |
||||||
|
case 27: |
||||||
|
this->mode_ = Mode::EDIT; |
||||||
|
break; |
||||||
|
case 'y': |
||||||
|
if (!this->save()) |
||||||
|
std::cerr << "Could not write to file: '" << this->filename << "'\n"; |
||||||
|
this->mode_ = Mode::EDIT; |
||||||
|
break; |
||||||
|
} |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void Editor::render() { |
||||||
|
clear(); |
||||||
|
for (int i = 0; i < LINES - 1 + this->offset; i++) { |
||||||
|
if(i >= this->buffer->linebuffer().size()) { |
||||||
|
move(i - this->offset, 0); |
||||||
|
clrtoeol(); |
||||||
|
} |
||||||
|
else { |
||||||
|
std::string line = /*std::to_string(i) + " | " +*/ this->buffer->at(i); |
||||||
|
mvprintw(i - this->offset, 0, line.c_str()); |
||||||
|
} |
||||||
|
clrtoeol(); |
||||||
|
} |
||||||
|
this->status(); |
||||||
|
move(this->y - this->offset, this->x); |
||||||
|
} |
||||||
|
|
||||||
|
void Editor::status() { |
||||||
|
std::string status; |
||||||
|
switch(this->mode_) |
||||||
|
{ |
||||||
|
case Mode::EDIT: |
||||||
|
status = "[E]"; |
||||||
|
break; |
||||||
|
case Mode::INSERT: |
||||||
|
status = "[I]"; |
||||||
|
break; |
||||||
|
case Mode::QUIT: |
||||||
|
status = "[QUIT]"; |
||||||
|
break; |
||||||
|
} |
||||||
|
status += "\t\t" + |
||||||
|
std::to_string(this->y) + ", " + |
||||||
|
std::to_string(this->x) + |
||||||
|
"\t\t\tbuffer: " + std::to_string(this->buffer->linebuffer().size()) + |
||||||
|
"\toffset: " + std::to_string(this->offset); |
||||||
|
|
||||||
|
if (this->mode_ == Mode::SAVE) |
||||||
|
status = "Save File '" + this->filename + "'? [Y/N]: "; |
||||||
|
|
||||||
|
status += std::string(COLS - status.length(), ' '); |
||||||
|
|
||||||
|
attron(COLOR_PAIR(2)); |
||||||
|
mvprintw(LINES-1, 0, status.c_str()); |
||||||
|
|
||||||
|
//clrtoeol();
|
||||||
|
attroff(COLOR_PAIR(2)); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,82 @@ |
|||||||
|
#pragma once |
||||||
|
|
||||||
|
#include <memory> |
||||||
|
#include <Buffer.hpp> |
||||||
|
#include <ncurses.h> |
||||||
|
|
||||||
|
namespace groove { |
||||||
|
|
||||||
|
enum class Mode { |
||||||
|
INSERT, |
||||||
|
EDIT, |
||||||
|
SAVE, |
||||||
|
QUIT |
||||||
|
}; |
||||||
|
|
||||||
|
class Editor { |
||||||
|
private: |
||||||
|
int x, y; |
||||||
|
int offset; |
||||||
|
char lastChar = 0; |
||||||
|
std::unique_ptr<Buffer> buffer; |
||||||
|
Mode mode_; |
||||||
|
std::string clipboard = ""; |
||||||
|
std::string filename; |
||||||
|
|
||||||
|
bool load(); |
||||||
|
bool save(); |
||||||
|
void status(); |
||||||
|
void scrollUp() { |
||||||
|
if (this->y < this->offset && this->offset > 0) |
||||||
|
this->offset--; |
||||||
|
} |
||||||
|
void scrollDown() { |
||||||
|
if (this->y >= LINES - 1) |
||||||
|
this->offset++; |
||||||
|
} |
||||||
|
void left() { |
||||||
|
if (this->x - 1 >= 0) |
||||||
|
this->x--; |
||||||
|
} |
||||||
|
void right() { |
||||||
|
if (this->x + 1 <= COLS && this->x + 1 <= this->buffer->linebuffer().at(this->y).length()) |
||||||
|
this->x++; |
||||||
|
} |
||||||
|
void up() { |
||||||
|
if (this->y - 1 >= 0) |
||||||
|
this->y--; |
||||||
|
else { |
||||||
|
return; |
||||||
|
} |
||||||
|
if (this->x >= this->buffer->linebuffer().at(this->y).length()) { |
||||||
|
int 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()) { |
||||||
|
int length = this->buffer->linebuffer().at(this->y).length(); |
||||||
|
this->x = length > 0 ? length - 1 : 0; |
||||||
|
} |
||||||
|
this->scrollDown(); |
||||||
|
} |
||||||
|
public: |
||||||
|
Editor() : x(0), y(0), buffer(std::make_unique<Buffer>()), mode_(Mode::EDIT), filename("unbenannt"), |
||||||
|
offset(0) { |
||||||
|
this->buffer->insert(""); |
||||||
|
} |
||||||
|
Editor(std::string file); |
||||||
|
Mode mode() { |
||||||
|
return this->mode_; |
||||||
|
} |
||||||
|
void input(int c); |
||||||
|
void render(); |
||||||
|
}; |
||||||
|
|
||||||
|
} |
@ -0,0 +1,49 @@ |
|||||||
|
#pragma once |
||||||
|
|
||||||
|
#include <ncurses.h> |
||||||
|
|
||||||
|
namespace groove { |
||||||
|
namespace ncurses { |
||||||
|
|
||||||
|
struct Color { |
||||||
|
short r, g, b; |
||||||
|
Color(short r, short g, short b) : |
||||||
|
r(static_cast<short>(r * 0.255)), |
||||||
|
g(static_cast<short>(g * 0.255)), |
||||||
|
b(static_cast<short>(b * 0.255)) {} |
||||||
|
}; |
||||||
|
|
||||||
|
class ncurses { |
||||||
|
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(); |
||||||
|
init_pair(1, COLOR_WHITE, COLOR_BLACK); |
||||||
|
init_pair(2, COLOR_WHITE, COLOR_MAGENTA); |
||||||
|
if (can_change_color()) { |
||||||
|
Color grey(39, 40, 34); |
||||||
|
Color pink(255, 0, 103); |
||||||
|
|
||||||
|
init_color(COLOR_BLACK, grey.r, grey.g, grey.b); |
||||||
|
init_color(COLOR_MAGENTA, pink.r, pink.g, pink.b); |
||||||
|
} |
||||||
|
attron(COLOR_PAIR(1)); |
||||||
|
} |
||||||
|
|
||||||
|
void flush() { |
||||||
|
refresh(); |
||||||
|
} |
||||||
|
|
||||||
|
void quit() { |
||||||
|
endwin(); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue