zeed/editor.cpp

313 lines
6.9 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, false) << '\n';
}
// 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) {
if(s.size())
s.pop_back();
}
else
s += ch;
print_text(cur.r, prompt+": " + s);
ch = getch();
}
print_current_line();
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) {
string inp = get_input(prompt, [](char ch) { return ch >= '0' && ch <= '9'; });
if(inp.size() > 0)
return stoi(inp);
else
return 0;
}
// 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
find_result 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, 1).find(text, start);
if(pos != string::npos)
return {true, {r, pos}};
}
return {false, position()};
}
void Editor::replace(position p, count_type length, string text) {
string line_text = file.get_line(p.r);
line_text.erase(p.c, length);
line_text.insert(p.c, text);
file.update(p.r, line_text);
}
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 'u':
if(file.version() > 0) {
jump({file.undo(), 0});
print_file(file_offset);
}
break;
case 'i':
mode = INSERT;
current_insert = file.get_line(file_offset + cur.r, false);
break;
case 'v':
mode = SELECT;
selection.pos = cur + file_offset;
break;
case 'f':
last_find = get_string("to find");
case 'n':
{
auto result = find(last_find);
if(!result.found)
break;
jump(result.pos);
}
print_file(file_offset);
break;
case 's':
last_find = get_string("to find");
last_replace = get_string("to replace");
case 'r':
{
auto result = find(last_find);
if(!result.found)
break;
jump(result.pos);
replace(cur + file_offset, last_find.size(), last_replace);
}
print_file(file_offset);
break;
case 'p':
file.compress_versions(selection.paste_selection(cur + file_offset));
print_file(file_offset);
break;
default:
//print_text(cur.r, "Unknown operation.");
break;
}
break;
case INSERT:
switch(ch) {
case ESC:
mode = NORMAL;
file.update(cur.r, current_insert);
print_text(cur.r, substitute_tabs(current_insert));
current_insert = "";
break;
case BS:
if(cur.c > 0) {
count_type new_c = cur.c;
do new_c--;
while(new_c > 0 && get_tab_offset(new_c, current_insert) > get_tab_offset(new_c-1, current_insert));
current_insert.erase(cur.c-1 - get_tab_offset(cur.c-1, current_insert), 1);
cur.c = new_c;
}
print_text(cur.r, substitute_tabs(current_insert));
break;
case ENTER:
file.update(cur.r, current_insert);
file.split_line(cur + file_offset);
cur.r++; cur.c = 0; move(cur);
current_insert = file.get_line(cur.r);
print_file(file_offset);
break;
case '\t':
current_insert.insert(cur.c - get_tab_offset(cur.c, current_insert), "\t");
cur.c += get_tab_length(cur.c);
print_text(cur.r, substitute_tabs(current_insert));
break;
default:
current_insert.insert(cur.c - get_tab_offset(cur.c, current_insert), string(1, ch));
cur.c += 1;
print_text(cur.r, substitute_tabs(current_insert));
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 'G':
jump(file.size());
print_file(file_offset);
break;
case 'v':
selection.copy_selection(cur + file_offset);
mode = NORMAL;
break;
case 'x':
file.compress_versions(selection.remove_selection(cur + file_offset)+1);
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;
}