zeed/editor.cpp

295 lines
6.2 KiB
C++

#include "everything.hpp"
// Save file from buffer
void Editor::save() {
std::ofstream outfile(filename);
if(!outfile.good()) {
std::cerr << "Bad filename";
return;
}
for(count_type i = 0; i < file.size(); ++i)
outfile << file.get_line(i, 0) << std::endl;
}
// Clear line
void Editor::clear_line(count_type i) {
::move(i, 0);
clrtoeol();
}
// Print any text
void Editor::print_text(count_type i, string text) {
clear_line(i);
::move(i, 0);
adds(text);
}
// Print file content
void Editor::print_file(count_type start) {
auto lines = file.bulk_get_line(start, LINES);
for(count_type i = 0; i < LINES; i++)
if(i < lines.size())
print_text(i, lines[i]);
else
clear_line(i);
}
// Generic input taking
template <typename F>
string Editor::get_input(string prompt, F func) {
print_text(cur.r, prompt+": ");
string s = "";
char ch = getch();
while((ch != ENTER && func(ch)) || ch == BS) {
if(ch == BS)
s.pop_back();
else
s += ch;
print_text(cur.r, prompt+": " + s);
ch = getch();
}
return s;
}
// Taking user input - string
string Editor::get_string(string prompt) {
return get_input(prompt, [](char ch) { return true; });
}
// Taking user input - number
count_type Editor::get_number(string prompt) {
return stoi(get_input(prompt, [](char ch) { return ch >= '0' && ch <= '9'; }));
}
// TODO undo
// Jump to end of line
void Editor::jump_line_end() {
count_type line_size = file.get_line(file_offset+cur.r).size();
if(cur.c > line_size)
cur.c = line_size;
}
// Cursor movement
void Editor::move_cursor(char ch) {
switch(ch) {
case 'h':
if(cur.c > 0) {
cur.c -= 1;
move(cur);
}
break;
case 'j':
if(file_offset + cur.r >= file.size()-1) break;
if(cur.r < LINES-1) {
cur.r += 1;
move(cur);
jump_line_end();
}
else if(cur.r == LINES-1) {
file_offset++;
jump_line_end();
print_file(file_offset);
}
break;
case 'k':
if(cur.r > 0) {
cur.r -= 1;
move(cur);
jump_line_end();
}
else if(file_offset > 0) {
file_offset--;
jump_line_end();
print_file(file_offset);
}
break;
case 'l':
if(cur.c < file.get_line(file_offset+cur.r).size()) {
cur.c += 1;
move(cur);
}
break;
}
}
// Jump to line
void Editor::jump(count_type r) {
if(r >= file.size()) r = file.size()-1;
else if(r < 0) r = 0;
if(r < file_offset)
file_offset = r;
else if(r >= file_offset + LINES)
file_offset = r - LINES+1;
cur.r = r - file_offset;
jump_line_end();
move(cur);
}
// Find next string appearance
std::pair<bool, position> Editor::find(string text) {
count_type len = text.size();
count_type file_size = file.size();
for(count_type _r = 0; _r < file_size; ++_r) {
count_type r = (file_offset + cur.r + _r) % file_size;
count_type start = ((_r == 0) ? (cur.c+1) : 0);
count_type pos = file.get_line(r, 0).find(text, start);
if(pos != string::npos)
return {true,{r, pos}};
}
return {false, position()};
}
bool Editor::take_action() {
move(cur);
char ch = getch();
switch(mode) {
case NORMAL:
print_current_line();
switch(ch) {
case 'h': case 'j': case 'k': case 'l':
move_cursor(ch);
break;
case 'g':
jump(get_number("line number"));
print_file(file_offset);
break;
case 'G':
jump(file.size());
print_file(file_offset);
break;
case 'd':
file.remove(file_offset + cur.r);
if(file_offset + cur.r >= file.size())
jump(cur + file_offset);
print_file(file_offset);
break;
case 'x':
file.remove(cur + file_offset);
print_current_line();
break;
case 'o':
file.insert(file_offset + cur.r, "");
jump_line_end();
print_file(file_offset);
break;
case 'q':
return false;
break;
case 'w':
save();
break;
case 'i':
mode = INSERT;
break;
case 'v':
mode = SELECT;
selection.pos = cur + file_offset;
break;
case 'f':
last_find = get_string("to find");
print_current_line();
case 'n':
{
auto result = find(last_find);
if(!result.first) {
print_current_line();
break;
}
jump(result.second);
}
print_file(file_offset);
break;
case 's':
last_find = get_string("to find");
last_replace = get_string("to replace");
print_current_line();
case 'r':
{
auto result = find(last_find);
if(!result.first) {
print_current_line();
break;
}
jump(result.second);
replace(cur + file_offset, last_find.size(), last_replace);
}
print_file(file_offset);
break;
case 'p':
selection.paste_selection(cur + file_offset);
print_file(file_offset);
break;
default:
break;
}
break;
case INSERT:
// TODO remake insert mode for better undo
switch(ch) {
case ESC:
mode = NORMAL;
// Change current row into current_insert TODO
print_current_line();
break;
case BS:
// Modify current_insert TODO
if(cur.c > 0) {
cur.c -= 1;
file.remove(cur + file_offset);
}
print_current_line();
break;
case ENTER:
// Modify current_insert and update TODO
file.split_line(cur + file_offset);
print_file(file_offset);
cur.r++;
jump_line_end();
break;
case '\t':
// Modify current_insert TODO
file.insert(cur + file_offset, "\t");
cur.c += TAB_SIZE - (cur.c % TAB_SIZE);
print_current_line();
break;
default:
// Modify current_insert TODO
file.insert(cur + file_offset, string(1, ch));
cur.c += 1;
print_current_line();
break;
}
break;
case SELECT:
print_current_line();
switch(ch) {
case 'h': case 'j': case 'k': case 'l':
move_cursor(ch);
break;
case 'g':
jump(get_number("line number"));
print_file(file_offset);
break;
case 'v':
selection.copy_selection(cur + file_offset);
mode = NORMAL;
break;
case 'x':
selection.remove_selection(cur + file_offset);
if(file_offset + cur.r >= file.size())
jump(cur + file_offset);
print_file(file_offset);
mode = NORMAL;
break;
default:
mode = NORMAL;
break;
}
break;
}
return true;
}