#include #include #include #include #include "treap.hpp" using std::string; #define ESC 27 #define ENTER 10 #define BS 127 #define adds(s) addstr(s.c_str()) enum mode_type { INSERT, NORMAL, SELECT }; struct position { count_type r, c; }; // Global variables treap file; position cur = {0, 0}; count_type file_offset = 0; position clp; treap selection; // Accessing the file string get_line(count_type r, bool clean = true) { string line = (*file.find(file_offset+r)).text; if(!clean) return line; for(count_type i = 0; i < line.size(); ++i) if(line[i] == '\t') line[i] = ' '; return line; } char get(position p) { return get_line(file_offset+p.r)[p.c]; } void set(position p, char ch) { file.find(file_offset+p.r)->text[p.c] = ch; } void new_line(count_type r, string text = "") { file.insert(file_offset+r, text); } void insert(position p, string t) { file.find(file_offset+p.r)->text.insert(p.c, t); } void insert(position p, char ch) { insert(p, string{ch}); } void append(string t) { file.append(t); } void remove(position p, count_type len=1) { file.find(file_offset+p.r)->text.erase(p.c,len); } void remove(count_type r) { file.remove(file_offset+r); } count_type get_number() { char ch = '0'; string s = ""; while(ch >= '0' && ch <= '9') { s += ch; ch = getch(); } return stoi(s); } // Load file to buffer bool load(string filename) { std::ifstream infile(filename); if(!infile.good()) return 1; string line; while (std::getline(infile, line)) append(line); return 0; } // Save file from buffer bool save(string filename) { std::ofstream outfile(filename); if(!outfile.good()) return 1; for(count_type i = 0; i < file.size(); ++i) outfile << get_line(i-file_offset, 0) << std::endl; return 0; } // Clear line void clear_line(count_type r) { move(r, 0); clrtoeol(); } // Print line void print_line(count_type r) { clear_line(r); move(r, 0); adds(get_line(r)); } // Print file content void print_file(count_type start = 0) { for(count_type i = start; i < LINES; i++) if(file_offset+i < file.size()) print_line(i); else clear_line(i); } // Splitting lines void split_line(position p) { string line = get_line(p.r); string newline = line.substr(p.c, line.size()); remove(p, line.size()-p.c); new_line(p.r+1, newline); } // Copying void copy_selection() { // Determine first and last selected position position start = cur, end = clp; if(clp.r < cur.r || (clp.r == cur.r && clp.c < cur.c)) start = clp, end = cur; // Clear previous selection selection.clear(); // Add all complete lines in between for(count_type r = start.r+1; r < end.r; ++r) selection.append(get_line(r)); // Add first line end string start_line = get_line(start.r); count_type size = ((start.r == end.r) ? end.c+1 : start_line.size()) - start.c; string start_text = start_line.substr(start.c, size); selection.insert(0, start_text); // Add last line start if(start.r < end.r) { string end_line = get_line(end.r); string end_text = end_line.substr(0, end.c+1); selection.append(end_text); } } // Pasting void paste_selection(position p = cur) { if(selection.size() == 0) return; // Insert last line inside of this insert(p, selection.find(selection.size()-1)->text); if(selection.size() == 1) return; // Insert first line in front and split split_line(p); insert(p, selection.find(0)->text); if(selection.size() == 2) return; // Insert lines for(count_type i = selection.size()-2; i > 0; --i) new_line(p.r, selection.find(i)->text); } // TODO hledání a nahrazování // TODO undo // Jump to end of line void jump_line_end() { count_type line_size = get_line(cur.r).size(); if(cur.c > line_size) cur.c = line_size; } // Move cursor within window void move(position p) { move(p.r, p.c); } // Cursor movement void move_cursor(char ch) { switch(ch) { case 'h': if(cur.c > 0) move(cur.r, --cur.c); 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(); } 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(); } break; case 'l': if(cur.c < get_line(cur.r).size()) { cur.c += 1; move(cur); } break; } } // Jump to line void jump(count_type r) { count_type last_start = file.size()-LINES; if(last_start < 0) last_start = 0; cur.r = 0; file_offset = r; if(file_offset > last_start) { file_offset = last_start; cur.r = r - file_offset; if(cur.r >= LINES) cur.r = LINES-1; } jump_line_end(); } // Jump to position void jump(position p) { jump(p.r); cur.c = p.c; } int main(int argc, char* argv[]) { // Check valid filename and load if(argc <= 1) return 1; string filename = argv[1]; if(load(filename)) return 1; // Init initscr(); refresh(); print_file(); mode_type mode = NORMAL; // Main loop bool run = true; while(run) { move(cur); char ch = getch(); switch(mode) { case NORMAL: print_line(cur.r); switch(ch) { case 'h': case 'j': case 'k': case 'l': move_cursor(ch); break; case 'g': jump(get_number()); print_file(); break; case 'd': remove(cur.r); print_file(cur.r); break; case 'x': remove(cur); print_line(cur.r); break; case 'o': new_line(cur.r); print_file(cur.r); break; case 'q': run = false; break; case 'w': save(filename); break; case 'i': mode = INSERT; break; case 'v': mode = SELECT; clp = cur; break; case 'p': paste_selection(); print_file(cur.r); break; default: break; } break; case INSERT: switch(ch) { case ESC: mode = NORMAL; print_line(cur.r); break; case BS: if(cur.c > 0) { cur.c -= 1; remove(cur); } print_line(cur.r); break; case ENTER: split_line(cur); print_file(cur.r++); jump_line_end(); break; default: insert(cur, ch); cur.c += 1; print_line(cur.r); break; } break; case SELECT: print_line(cur.r); switch(ch) { case 'h': case 'j': case 'k': case 'l': move_cursor(ch); break; case 'g': jump(get_number()); print_file(); break; case 'v': copy_selection(); mode = NORMAL; break; default: addch('d'); mode = NORMAL; break; } break; } refresh(); } // End endwin(); return 0; }