Compare commits
No commits in common. '2ec4f90bee7b23d253e2d5dc98dac2835bcb7af4' and '154289558a6e465d2d10d42871ad472664230742' have entirely different histories.
2ec4f90bee
...
154289558a
31 changed files with 1444 additions and 21 deletions
@ -0,0 +1,2 @@ |
||||
.idea |
||||
build |
@ -0,0 +1,14 @@ |
||||
# 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 |
@ -0,0 +1,28 @@ |
||||
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) |
@ -1,19 +0,0 @@ |
||||
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. |
@ -0,0 +1,21 @@ |
||||
# 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 |
||||
# Groove Editor |
||||
– a simple `ncurses`-based texteditor for the terminaly |
||||
|
||||
a simple ncurses-based texteditor for the terminal |
@ -0,0 +1,53 @@ |
||||
/**
|
||||
* 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; |
||||
} |
@ -0,0 +1,9 @@ |
||||
//
|
||||
// Created by miko on 04.03.17.
|
||||
//
|
||||
|
||||
#include <Buffer.hpp> |
||||
|
||||
namespace groove { |
||||
|
||||
} |
@ -0,0 +1,77 @@ |
||||
#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(); |
||||
} |
||||
}; |
||||
|
||||
} |
@ -0,0 +1,173 @@ |
||||
#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; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,157 @@ |
||||
#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); |
||||
}; |
||||
|
||||
} |
@ -0,0 +1,63 @@ |
||||
#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; |
||||
|
||||
} |
||||
|
||||
} |
||||
|
||||
|
@ -0,0 +1,32 @@ |
||||
#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(); |
||||
}; |
||||
|
||||
} |
@ -0,0 +1,52 @@ |
||||
#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++; |
||||
} |
||||
}; |
||||
|
||||
} |
@ -0,0 +1,72 @@ |
||||
#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; |
||||
} |
||||
|
||||
} |
||||
} |
@ -0,0 +1,81 @@ |
||||
#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); |
||||
} |
||||
}; |
||||
|
||||
} |
||||
} |
@ -0,0 +1,138 @@ |
||||
#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; |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,18 @@ |
||||
#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); |
||||
}; |
||||
|
||||
} |
||||
} |
@ -0,0 +1,34 @@ |
||||
#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; |
||||
} |
||||
} |
||||
|
||||
} |
||||
} |
@ -0,0 +1,18 @@ |
||||
#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); |
||||
}; |
||||
|
||||
} |
||||
} |
@ -0,0 +1,94 @@ |
||||
#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; |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,18 @@ |
||||
#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); |
||||
}; |
||||
|
||||
} |
||||
} |
@ -0,0 +1,9 @@ |
||||
#include <modes/Mode.hpp> |
||||
|
||||
namespace groove { |
||||
namespace modes { |
||||
|
||||
Mode::~Mode() {} |
||||
|
||||
} |
||||
} |
@ -0,0 +1,22 @@ |
||||
#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; |
||||
}; |
||||
|
||||
} |
||||
} |
@ -0,0 +1,12 @@ |
||||
#include <modes/Quit.hpp> |
||||
|
||||
|
||||
namespace groove { |
||||
namespace modes { |
||||
|
||||
void Quit::input(int c) { |
||||
|
||||
} |
||||
|
||||
} |
||||
} |
@ -0,0 +1,18 @@ |
||||
#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); |
||||
}; |
||||
|
||||
} |
||||
} |
@ -0,0 +1,29 @@ |
||||
#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_; |
||||
} |
||||
|
||||
} |
||||
} |
@ -0,0 +1,16 @@ |
||||
#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); |
||||
}; |
||||
|
||||
} |
||||
} |
@ -0,0 +1,70 @@ |
||||
#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
|
||||
} |
||||
} |
||||
} |
||||
|
||||
} |
||||
} |
@ -0,0 +1,24 @@ |
||||
#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); |
||||
}; |
||||
|
||||
} |
||||
} |
@ -0,0 +1,88 @@ |
||||
#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