From e1646c60980f206004d5dfe45e1f698fa7216aba Mon Sep 17 00:00:00 2001 From: Matuush Date: Sun, 25 May 2025 19:10:55 +0200 Subject: [PATCH] Encapsulate editor actions --- editor.cpp | 310 ++++++++++++++++++++++++++++++++++++++ editor.hpp | 94 ------------ everything.hpp | 129 ++++++++++++++++ main.cpp | 395 ++----------------------------------------------- selection.cpp | 31 ++-- treap.cpp | 54 +++++-- 6 files changed, 517 insertions(+), 496 deletions(-) create mode 100644 editor.cpp delete mode 100644 editor.hpp create mode 100644 everything.hpp diff --git a/editor.cpp b/editor.cpp new file mode 100644 index 0000000..f945018 --- /dev/null +++ b/editor.cpp @@ -0,0 +1,310 @@ +#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 r) { + ::move(r - file_offset, 0); + clrtoeol(); +} +// Split line +void Editor::split_line(position p) { + string line = file.get_line(p.r); + string newline = line.substr(p.c, line.size()); + file.remove(p, line.size()-p.c); + file.insert(p.r+1, newline); +} +// Print line +void Editor::print_line(count_type r) { + clear_line(r); + ::move(r - file_offset, 0); + adds(file.get_line(r)); +} +// Print file content +void Editor::print_file(count_type start) { + for(count_type i = start; i < file_offset + LINES; i++) + if(i < file.size()) + print_line(i); + else + clear_line(i); +} +// Print input +void Editor::print_input(string text) { + clear_line(file_offset + cur.r); + ::move(cur.r, 0); + adds(text); +} +// Taking user input - number +count_type Editor::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 Editor::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 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(); + } + 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 < 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 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_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(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_line(file_offset + cur.r); + break; + case 'o': + file.insert(file_offset + cur.r, ""); + jump_line_end(); + print_file(file_offset + cur.r); + 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_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; + file.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': + file.insert(cur + file_offset, "\t"); + cur.c += TAB_SIZE; + print_line(file_offset + cur.r); + break; + default: + // TODO remake for better undo + file.insert(cur + file_offset, string(1, 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 >= file.size()) + jump(cur + file_offset); + print_file(); + mode = NORMAL; + break; + default: + mode = NORMAL; + break; + } + break; + } + return true; +} diff --git a/editor.hpp b/editor.hpp deleted file mode 100644 index 222b0b3..0000000 --- a/editor.hpp +++ /dev/null @@ -1,94 +0,0 @@ -#include -#include -#include -#include - -using std::string; - -typedef unsigned long long count_type; - -//// Treap -// Treap node representation of a line -struct line { - count_type priority, size; - string text; - line *left, *right; - - line(count_type _p, string _t) : priority(_p), text(_t), size(1), left(nullptr), right(nullptr) {} -}; -typedef std::pair two_lines; - -// Treap data structure -class treap { - line* root; - - count_type get_size(line* l); - two_lines split(line *l, count_type k); - - line* join(line *a, line *b); - line* join(two_lines two) { return join(two.first, two.second); } - line* find(line* l, count_type k); - -public: - treap() { srand(120); root = nullptr; } - - line* find(count_type k); - string get(count_type k) { return find(k)->text; } - void clear(); - void insert(count_type k, string s); - void append(string s) { insert(size(), s); } - void remove(count_type k); - void pop() { remove(size()); } - count_type size() { return get_size(root); } -}; - -// Position -struct position { - count_type r, c; - position operator+(count_type offset) { return {r + offset, c}; } -}; - - -//// Editor -// Editor macros -#define ESC 27 -#define ENTER 10 -#define BS 127 -#define adds(s) addstr(s.c_str()) - -// Treap access functions -string get_line(count_type r, bool substitute_tab = 1); -count_type get_tab_offset(position p); -void set(position p, char ch); -void new_line(count_type r, string text); -void insert(position p, string t); -void insert(position p, char ch); -void remove(position p, count_type len); -void remove(count_type r); -void append(string t); -count_type get_size(); - -// File operations -bool load(string filename); -bool save(string filename); - -// Edit operations -void clear_line(count_type r); -void print_line(count_type r); -void print_file(count_type start); -void split_line(position p); -void print_input(string text); -count_type get_number(string prompt); -string get_string(string prompt); - -// Selection -class Selection { - treap content; // selection - template - void apply_on_selection(position p, F func, G func2); -public: - position pos; // selection initial position - void remove_selection(position p); - void copy_selection(position p); - void paste_selection(position p); -}; diff --git a/everything.hpp b/everything.hpp new file mode 100644 index 0000000..ea71a90 --- /dev/null +++ b/everything.hpp @@ -0,0 +1,129 @@ +#include +#include +#include +#include + +#define TAB_SIZE 2 + +using std::string; +typedef unsigned long long count_type; + +// Position +struct position { + count_type r, c; + position operator+(count_type offset) { return {r + offset, c}; } +}; + +//// Treap +// Treap node representation of a line +struct line { + count_type priority, size; + string text; + line *left, *right; + + line(count_type _p, string _t) : priority(_p), text(_t), size(1), left(nullptr), right(nullptr) {} +}; +typedef std::pair two_lines; + +// Treap data structure +class Treap { + line* root; + + count_type get_size(line* l); + two_lines split(line *l, count_type k); + + line* join(line *a, line *b); + line* join(two_lines two) { return join(two.first, two.second); } + line* find(line* l, count_type k); + +public: + Treap() { srand(120); root = nullptr; } + + line* find(count_type k); + string get(count_type k) { return find(k)->text; } + void clear(); + void insert(count_type k, string s); + void append(string s) { insert(size(), s); } + void remove(count_type k); + void pop() { remove(size()); } + count_type size() { return get_size(root); } + + // Treap access functions + string get_line(count_type r, bool substitute_tab = 1); + count_type get_tab_offset(position p); + void set(position p, char ch); + void insert(position p, string t); + void remove(position p, count_type len = 1); +}; + +//// Selection +class Selection { + Treap *file; + Treap content; // selection + template + void apply_on_selection(position p, F func, G func2); +public: + Selection() {} + Selection(Treap *_file) { file = _file; } + position pos; // selection initial position + void remove_selection(position p); + void copy_selection(position p); + void paste_selection(position p); +}; + + +//// Editor +#define ESC 27 +#define ENTER 10 +#define BS 127 +#define adds(s) addstr(s.c_str()) +enum mode_type { INSERT, NORMAL, SELECT }; + +class Editor{ + mode_type mode; + Treap file; // file representation + string filename; + position cur; // cursor position + count_type file_offset; // terminal file offset + + // Selections + string last_find, last_replace; // last find/replace inputs + Selection selection; + + // Write to file + void save(); + // Printing + void clear_line(count_type r); + void print_line(count_type r); + void split_line(position p); + + // User input + void print_input(string text); + count_type get_number(string prompt); + string get_string(string prompt); + + // Movement + void jump_line_end(); + void move(position p) { ::move(p.r, p.c); } + void move_cursor(char ch); + void jump(count_type r); + void jump(position p) { jump(p.r); cur.c = p.c; } + + // Find and replace + std::pair find(string text); + void replace(position p, count_type length, string text) { file.remove(p, length); file.insert(p, text); } + +public: + Editor(string _filename, Treap &_file) { + mode = NORMAL; + filename = _filename; + file = _file; + cur = {0, 0}; + file_offset = 0; + selection = Selection(&file); + } + void print_file(count_type start = 0); + + // The big function + bool take_action(); +}; diff --git a/main.cpp b/main.cpp index 6eeaa8b..2de9694 100644 --- a/main.cpp +++ b/main.cpp @@ -1,404 +1,39 @@ -#include "editor.hpp" +#include "everything.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(file_offset + 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; - 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); -} -// 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 + // Check argument if(argc <= 1) { std::cerr << "No input filename\n"; return 1; } string filename = argv[1]; - if(load(filename)) { + + // Check existing file + std::ifstream infile(filename); + if(!infile.good()) { std::cerr << "Bad input filename\n"; return 1; } + // Load file + Treap file; + string line; + while (std::getline(infile, line)) + file.append(line); + // Init initscr(); refresh(); - print_file(); - // Variables - string last_find, last_replace; // last find/replace inputs - Selection selection; + Editor ed(filename, file); - mode_type mode = NORMAL; + ed.print_file(); // 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; - } + while(ed.take_action()) refresh(); - } // End endwin(); diff --git a/selection.cpp b/selection.cpp index c689f42..df1a9c2 100644 --- a/selection.cpp +++ b/selection.cpp @@ -1,4 +1,4 @@ -#include "editor.hpp" +#include "everything.hpp" // Selection manipulation template @@ -13,29 +13,29 @@ void Selection::apply_on_selection(position p, F func, G func2) { // Last line start if(start.r < end.r) - func2(end.r, 0, end.c+1, &content); + func2(end.r, 0, end.c+1, file, &content); // All complete lines in between for(count_type r = end.r-1; r > start.r; --r) - func(r, &content); + func(r, file, &content); // First line end - string start_line = get_line(start.r); + string start_line = file->get_line(start.r); count_type size = ((start.r == end.r) ? end.c+1 : start_line.size()) - start.c; - func2(start.r, start.c, size, &content); + func2(start.r, start.c, size, file, &content); } // Removing selection void Selection::remove_selection(position p) { apply_on_selection(p, - [](count_type r, treap* sel){ remove(r); }, - [](count_type r, count_type start_c, count_type size, treap* sel) { remove({r, start_c}, size); } + [](count_type r, Treap *file, Treap *sel){ file->remove(r); }, + [](count_type r, count_type start_c, count_type size, Treap *file, Treap *sel) { file->remove({r, start_c}, size); } ); } // Copying selection void Selection::copy_selection(position p) { apply_on_selection(p, - [](count_type r, treap* sel){ sel->insert(0, get_line(r)); }, - [](count_type r, count_type start_c, count_type size, treap* sel) { sel->insert(0, get_line(r).substr(start_c, size)); } + [](count_type r, Treap *file, Treap *sel){ sel->insert(0, file->get_line(r)); }, + [](count_type r, count_type start_c, count_type size, Treap *file, Treap *sel) { sel->insert(0, file->get_line(r).substr(start_c, size)); } ); } // Pasting selection @@ -43,15 +43,20 @@ void Selection::paste_selection(position p) { if(content.size() == 0) return; // Insert last line inside of this - insert(p, content.get(content.size()-1)); + file->insert(p, content.get(content.size()-1)); if(content.size() == 1) return; + // Split line + string line = file->get_line(p.r); + string newline = line.substr(p.c, line.size()); + file->remove(p, line.size()-p.c); + file->insert(p.r+1, newline); + // Insert first line in front and split - split_line(p); - insert(p, content.get(0)); + file->insert(p, content.get(0)); if(content.size() == 2) return; // Insert lines for(count_type i = content.size()-2; i > 0; --i) - new_line(p.r, content.get(i)); + file->insert(p.r, content.get(i)); } diff --git a/treap.cpp b/treap.cpp index b549282..331b977 100644 --- a/treap.cpp +++ b/treap.cpp @@ -1,16 +1,16 @@ -#include "editor.hpp" +#include "everything.hpp" // Treap representation of a file line* root; // Get size uf a subtreap -count_type treap::get_size(line* l) { +count_type Treap::get_size(line* l) { if(l == nullptr) return 0; return l->size; } // Split treap by k-th element -two_lines treap::split(line *l, count_type k) { +two_lines Treap::split(line *l, count_type k) { if(l == nullptr) return {nullptr, nullptr}; if(get_size(l->left) >= k) { @@ -30,7 +30,7 @@ two_lines treap::split(line *l, count_type k) { } // Join two treaps -line* treap::join(line *a, line *b) { +line* Treap::join(line *a, line *b) { if(a == nullptr) return b; if(b == nullptr) return a; @@ -47,7 +47,7 @@ line* treap::join(line *a, line *b) { } // Find k-th line of file -line* treap::find(line* l, count_type k) { +line* Treap::find(line* l, count_type k) { if(l == nullptr) return nullptr; if(k <= get_size(l->left)) @@ -59,13 +59,13 @@ line* treap::find(line* l, count_type k) { } // File access -line* treap::find(count_type k) { +line* Treap::find(count_type k) { if(k >= root->size) return nullptr; // Don't find index k, but k-th line -> +1 return find(root, k+1); } -void treap::clear() { +void Treap::clear() { while(size() > 0) { auto two = split(root, 1); root = two.second; @@ -74,7 +74,7 @@ void treap::clear() { } // Line insert -void treap::insert(count_type k, string s) { +void Treap::insert(count_type k, string s) { line *l = new line(rand(), s); if(root == nullptr) { @@ -87,10 +87,46 @@ void treap::insert(count_type k, string s) { root = join(two); } // Line removal -void treap::remove(count_type k) { +void Treap::remove(count_type k) { auto two = split(root, k+1); auto first_split = split(two.first, k); delete first_split.second; two.first = first_split.first; root = join(two); } + +// Accessing the file +string Treap::get_line(count_type r, bool substitute_tab) { + string line = 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 Treap::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 Treap::set(position p, char ch) { + find(p.r)->text[p.c-get_tab_offset(p)] = ch; +} +void Treap::insert(position p, string t) { + find(p.r)->text.insert(p.c-get_tab_offset(p), t); +} +void Treap::remove(position p, count_type len) { + find(p.r)->text.erase(p.c-get_tab_offset(p),len); +}