#include "editor.hpp" #define TAB_SIZE 2 enum mode_type { INSERT, NORMAL, SELECT }; // Global variables treap file; // file representation position cur = {0, 0}; // cursor position count_type file_offset = 0; // terminal file offset // Accessing the file string get_line(count_type r, bool substitute_tab) { string line = file.get(r); if(!substitute_tab) return line; string ret = ""; for(count_type i = 0; i < line.size(); ++i) { if(line[i] == '\t') for(int j = 0; j < TAB_SIZE; j++) ret += ' '; else ret += line[i]; } return ret; } count_type get_tab_offset(position p) { count_type tab_offset = 0; string line = get_line(p.r, false); for(count_type i = 0; i + tab_offset < p.c; ++i) { if(line[i] == '\t') tab_offset += TAB_SIZE - 1; if(i + tab_offset > p.c) tab_offset -= i + tab_offset - p.c; } return tab_offset; } void set(position p, char ch) { file.find(p.r)->text[p.c-get_tab_offset(p)] = ch; } void new_line(count_type r, string text = "") { file.insert(r, text); } void insert(position p, string t) { file.find(p.r)->text.insert(p.c-get_tab_offset(p), t); } void insert(position p, char ch) { insert(p, string{ch}); } void remove(position p, count_type len=1) { file.find(p.r)->text.erase(p.c-get_tab_offset(p),len); } void remove(count_type r) { file.remove(r); } void append(string t) { file.append(t); } count_type get_size() { return file.size(); } // 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 < get_size(); ++i) outfile << get_line(i, 0) << std::endl; return 0; } // Clear line void clear_line(count_type r) { move(r - file_offset, 0); clrtoeol(); } // Print line void print_line(count_type r) { clear_line(r); move(r - file_offset, 0); adds(get_line(r)); } // Print file content void print_file(count_type start = 0) { for(count_type i = start; i < file_offset + LINES; i++) if(i < get_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); } // Print input void print_input(string text) { clear_line(cur.r); move(cur.r, 0); adds(text); } // Taking user input - number count_type get_number(string prompt) { print_input(prompt+": "); char ch = '0'; string s = ""; ch = getch(); while(ch >= '0' && ch <= '9') { s += ch; print_input(prompt+": " + s); ch = getch(); } if(s.size() == 0) return file_offset + cur.r; return stoi(s); } // Taking user input - string string get_string(string prompt) { print_input(prompt+": "); char ch; string s = ""; ch = getch(); while(ch != ENTER) { if(ch == BS) s.pop_back(); else s += ch; print_input(prompt+": " + s); ch = getch(); } return s; } // TODO undo // Jump to end of line void jump_line_end() { count_type line_size = get_line(file_offset+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 >= get_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(file_offset+cur.r).size()) { cur.c += 1; move(cur); } break; } } // Jump to line void jump(count_type r) { if(r >= get_size()) r = get_size()-1; if(r - file_offset > LINES || r < file_offset) file_offset = max(0, min(get_size()-LINES, r)); else if(file_offset + LINES > get_size() && get_size() > LINES) file_offset = get_size() - LINES; cur.r = max(0, r - file_offset); jump_line_end(); } // Jump to position void jump(position p) { jump(p.r); cur.c = p.c; } // Find next string appearance std::pair find(string text) { count_type len = text.size(); count_type file_size = get_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 = get_line(r, 0).find(text, start); if(pos != string::npos) return {true,{r, pos}}; } return {false, position()}; } // Replace string appearance void replace(position p, count_type length, string text) { remove(p, length); insert(p, text); } int main(int argc, char* argv[]) { // Check valid filename and load if(argc <= 1) { std::cerr << "No input filename\n"; return 1; } string filename = argv[1]; if(load(filename)) { std::cerr << "Bad input filename\n"; return 1; } // Init initscr(); refresh(); print_file(); // Variables string last_find, last_replace; // last find/replace inputs Selection selection; mode_type mode = NORMAL; // Main loop bool run = true; while(run) { move(cur); char ch = getch(); switch(mode) { case NORMAL: print_line(file_offset + cur.r); 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(get_size()); print_file(file_offset); break; case 'd': remove(file_offset + cur.r); if(file_offset + cur.r >= get_size()) jump(cur + file_offset); print_file(file_offset); break; case 'x': remove(cur + file_offset); print_line(file_offset + cur.r); break; case 'o': new_line(file_offset + cur.r); jump_line_end(); print_file(file_offset + cur.r); break; case 'q': run = false; break; case 'w': save(filename); 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_line(file_offset + cur.r); case 'n': { auto result = find(last_find); if(!result.first) { print_line(file_offset + cur.r); break; } jump(result.second); } print_file(file_offset + cur.r); break; case 's': last_find = get_string("to find"); last_replace = get_string("to replace"); print_line(file_offset + cur.r); case 'r': { auto result = find(last_find); if(!result.first) { print_line(file_offset + cur.r); break; } jump(result.second); replace(cur + file_offset, last_find.size(), last_replace); } print_file(file_offset + cur.r); break; case 'p': selection.paste_selection(cur + file_offset); print_file(file_offset + cur.r); break; default: break; } break; case INSERT: switch(ch) { case ESC: mode = NORMAL; print_line(file_offset + cur.r); break; case BS: if(cur.c > 0) { cur.c -= 1; remove(cur + file_offset); } print_line(file_offset + cur.r); break; case ENTER: split_line(cur + file_offset); print_file(file_offset + cur.r++); jump_line_end(); break; case '\t': insert(cur + file_offset, ch); cur.c += TAB_SIZE; print_line(file_offset + cur.r); break; default: insert(cur + file_offset, ch); cur.c += 1; print_line(file_offset + cur.r); break; } break; case SELECT: print_line(file_offset + cur.r); 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 >= get_size()) jump(cur + file_offset); print_file(); mode = NORMAL; break; default: mode = NORMAL; break; } break; } refresh(); } // End endwin(); return 0; }