Encapsulate selection and fragment code better
This commit is contained in:
parent
bce4f0141e
commit
aa065cd89f
5 changed files with 257 additions and 221 deletions
94
editor.hpp
Normal file
94
editor.hpp
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
#include <ncurses.h>
|
||||||
|
#include <curses.h>
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
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<line*, line*> 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 <typename F, typename G>
|
||||||
|
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);
|
||||||
|
};
|
97
main.cpp
97
main.cpp
|
@ -1,39 +1,16 @@
|
||||||
#include <ncurses.h>
|
#include "editor.hpp"
|
||||||
#include <curses.h>
|
|
||||||
#include <string>
|
|
||||||
#include <fstream>
|
|
||||||
|
|
||||||
#include "treap.hpp"
|
|
||||||
|
|
||||||
using std::string;
|
|
||||||
|
|
||||||
#define TAB_SIZE 2
|
#define TAB_SIZE 2
|
||||||
|
|
||||||
#define ESC 27
|
|
||||||
#define ENTER 10
|
|
||||||
#define BS 127
|
|
||||||
#define adds(s) addstr(s.c_str())
|
|
||||||
enum mode_type { INSERT, NORMAL, SELECT };
|
enum mode_type { INSERT, NORMAL, SELECT };
|
||||||
|
|
||||||
struct position {
|
|
||||||
count_type r, c;
|
|
||||||
position operator+(count_type offset) { return {r + offset, c}; }
|
|
||||||
};
|
|
||||||
|
|
||||||
// Utilities
|
|
||||||
count_type min(count_type a, count_type b) { return (a <= b) ? a : b; }
|
|
||||||
count_type max(count_type a, count_type b) { return (a >= b) ? a : b; }
|
|
||||||
|
|
||||||
// Global variables
|
// Global variables
|
||||||
treap file; // file representation
|
treap file; // file representation
|
||||||
position cur = {0, 0}; // cursor position
|
position cur = {0, 0}; // cursor position
|
||||||
count_type file_offset = 0; // terminal file offset
|
count_type file_offset = 0; // terminal file offset
|
||||||
position clp; // selection initial position
|
|
||||||
treap selection; // selection
|
|
||||||
string last_find, last_replace; // last find/replace inputs
|
|
||||||
|
|
||||||
// Accessing the file
|
// Accessing the file
|
||||||
string get_line(count_type r, bool substitute_tab = true) {
|
string get_line(count_type r, bool substitute_tab) {
|
||||||
string line = file.get(r);
|
string line = file.get(r);
|
||||||
if(!substitute_tab)
|
if(!substitute_tab)
|
||||||
return line;
|
return line;
|
||||||
|
@ -157,64 +134,6 @@ string get_string(string prompt) {
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Selection manipulation
|
|
||||||
template <typename F, typename G>
|
|
||||||
void apply_on_selection(position p, F func, G func2) {
|
|
||||||
// Determine first and last selected position
|
|
||||||
position start = p, end = clp;
|
|
||||||
if(clp.r < cur.r || (clp.r == cur.r && clp.c < cur.c))
|
|
||||||
start = clp, end = p;
|
|
||||||
|
|
||||||
// Clear previous selection
|
|
||||||
selection.clear();
|
|
||||||
|
|
||||||
// Last line start
|
|
||||||
if(start.r < end.r)
|
|
||||||
func2(end.r, 0, end.c+1);
|
|
||||||
// selection.append(get_line(end.r).substr(0, end.c+1));
|
|
||||||
|
|
||||||
// All complete lines in between
|
|
||||||
for(count_type r = end.r-1; r > start.r; --r)
|
|
||||||
func(r);
|
|
||||||
//selection.append(get_line(r));
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
func2(start.r, start.c, size); // append
|
|
||||||
//selection.insert(0, start_line.substr(start.c, size));
|
|
||||||
}
|
|
||||||
// Removing selection
|
|
||||||
void remove_selection(position p = cur + file_offset) {
|
|
||||||
apply_on_selection(p,
|
|
||||||
[](count_type r){ remove(r); },
|
|
||||||
[](count_type r, count_type start_c, count_type size) { remove({r, start_c}, size); }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
// Copying selection
|
|
||||||
void copy_selection(position p = cur + file_offset) {
|
|
||||||
apply_on_selection(p,
|
|
||||||
[](count_type r){ selection.insert(0, get_line(r)); },
|
|
||||||
[](count_type r, count_type start_c, count_type size) { selection.insert(0, get_line(r).substr(start_c, size)); }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
// Pasting selection
|
|
||||||
void paste_selection(position p = cur + file_offset) {
|
|
||||||
if(selection.size() == 0) return;
|
|
||||||
|
|
||||||
// Insert last line inside of this
|
|
||||||
insert(p, selection.get(selection.size()-1));
|
|
||||||
if(selection.size() == 1) return;
|
|
||||||
|
|
||||||
// Insert first line in front and split
|
|
||||||
split_line(p);
|
|
||||||
insert(p, selection.get(0));
|
|
||||||
if(selection.size() == 2) return;
|
|
||||||
|
|
||||||
// Insert lines
|
|
||||||
for(count_type i = selection.size()-2; i > 0; --i)
|
|
||||||
new_line(p.r, selection.get(i));
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO undo
|
// TODO undo
|
||||||
|
|
||||||
|
@ -330,6 +249,10 @@ int main(int argc, char* argv[]) {
|
||||||
refresh();
|
refresh();
|
||||||
print_file();
|
print_file();
|
||||||
|
|
||||||
|
// Variables
|
||||||
|
string last_find, last_replace; // last find/replace inputs
|
||||||
|
Selection selection;
|
||||||
|
|
||||||
mode_type mode = NORMAL;
|
mode_type mode = NORMAL;
|
||||||
|
|
||||||
// Main loop
|
// Main loop
|
||||||
|
@ -378,7 +301,7 @@ int main(int argc, char* argv[]) {
|
||||||
break;
|
break;
|
||||||
case 'v':
|
case 'v':
|
||||||
mode = SELECT;
|
mode = SELECT;
|
||||||
clp = cur + file_offset;
|
selection.pos = cur + file_offset;
|
||||||
break;
|
break;
|
||||||
case 'f':
|
case 'f':
|
||||||
last_find = get_string("to find");
|
last_find = get_string("to find");
|
||||||
|
@ -411,7 +334,7 @@ int main(int argc, char* argv[]) {
|
||||||
print_file(file_offset + cur.r);
|
print_file(file_offset + cur.r);
|
||||||
break;
|
break;
|
||||||
case 'p':
|
case 'p':
|
||||||
paste_selection(cur + file_offset);
|
selection.paste_selection(cur + file_offset);
|
||||||
print_file(file_offset + cur.r);
|
print_file(file_offset + cur.r);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -459,11 +382,11 @@ int main(int argc, char* argv[]) {
|
||||||
print_file(file_offset);
|
print_file(file_offset);
|
||||||
break;
|
break;
|
||||||
case 'v':
|
case 'v':
|
||||||
copy_selection(cur + file_offset);
|
selection.copy_selection(cur + file_offset);
|
||||||
mode = NORMAL;
|
mode = NORMAL;
|
||||||
break;
|
break;
|
||||||
case 'x':
|
case 'x':
|
||||||
remove_selection(cur + file_offset);
|
selection.remove_selection(cur + file_offset);
|
||||||
if(file_offset + cur.r >= get_size())
|
if(file_offset + cur.r >= get_size())
|
||||||
jump(cur + file_offset);
|
jump(cur + file_offset);
|
||||||
print_file();
|
print_file();
|
||||||
|
|
57
selection.cpp
Normal file
57
selection.cpp
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
#include "editor.hpp"
|
||||||
|
|
||||||
|
// Selection manipulation
|
||||||
|
template <typename F, typename G>
|
||||||
|
void Selection::apply_on_selection(position p, F func, G func2) {
|
||||||
|
// Determine first and last selected position
|
||||||
|
position start = p, end = pos;
|
||||||
|
if(pos.r < p.r || (pos.r == p.r && pos.c < p.c))
|
||||||
|
start = pos, end = p;
|
||||||
|
|
||||||
|
// Clear previous selection
|
||||||
|
content.clear();
|
||||||
|
|
||||||
|
// Last line start
|
||||||
|
if(start.r < end.r)
|
||||||
|
func2(end.r, 0, end.c+1, &content);
|
||||||
|
|
||||||
|
// All complete lines in between
|
||||||
|
for(count_type r = end.r-1; r > start.r; --r)
|
||||||
|
func(r, &content);
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
func2(start.r, start.c, size, &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); }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// 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)); }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// Pasting selection
|
||||||
|
void Selection::paste_selection(position p) {
|
||||||
|
if(content.size() == 0) return;
|
||||||
|
|
||||||
|
// Insert last line inside of this
|
||||||
|
insert(p, content.get(content.size()-1));
|
||||||
|
if(content.size() == 1) return;
|
||||||
|
|
||||||
|
// Insert first line in front and split
|
||||||
|
split_line(p);
|
||||||
|
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));
|
||||||
|
}
|
96
treap.cpp
Normal file
96
treap.cpp
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
#include "editor.hpp"
|
||||||
|
|
||||||
|
// Treap representation of a file
|
||||||
|
line* root;
|
||||||
|
|
||||||
|
// Get size uf a subtreap
|
||||||
|
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) {
|
||||||
|
if(l == nullptr) return {nullptr, nullptr};
|
||||||
|
|
||||||
|
if(get_size(l->left) >= k) {
|
||||||
|
// In the left subtree
|
||||||
|
auto two = split(l->left, k);
|
||||||
|
l->left = two.second;
|
||||||
|
l->size -= get_size(two.first);
|
||||||
|
return {two.first, l};
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// In the right subtree
|
||||||
|
auto two = split(l->right, k - (1+get_size(l->left)));
|
||||||
|
l->right = two.first;
|
||||||
|
l->size -= get_size(two.second);
|
||||||
|
return {l, two.second};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Join two treaps
|
||||||
|
line* treap::join(line *a, line *b) {
|
||||||
|
if(a == nullptr) return b;
|
||||||
|
if(b == nullptr) return a;
|
||||||
|
|
||||||
|
if(a->priority < b->priority) {
|
||||||
|
a->size += get_size(b);
|
||||||
|
a->right = join(a->right, b);
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
b->size += get_size(a);
|
||||||
|
b->left = join(a, b->left);
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find k-th line of file
|
||||||
|
line* treap::find(line* l, count_type k) {
|
||||||
|
if(l == nullptr) return nullptr;
|
||||||
|
|
||||||
|
if(k <= get_size(l->left))
|
||||||
|
return find(l->left, k);
|
||||||
|
else if(k == get_size(l->left)+1)
|
||||||
|
return l;
|
||||||
|
else
|
||||||
|
return find(l->right, k - (1+get_size(l->left)) );
|
||||||
|
}
|
||||||
|
|
||||||
|
// File access
|
||||||
|
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() {
|
||||||
|
while(size() > 0) {
|
||||||
|
auto two = split(root, 1);
|
||||||
|
root = two.second;
|
||||||
|
delete two.first;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Line insert
|
||||||
|
void treap::insert(count_type k, string s) {
|
||||||
|
line *l = new line(rand(), s);
|
||||||
|
|
||||||
|
if(root == nullptr) {
|
||||||
|
root = l;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto two = split(root, k);
|
||||||
|
two.first = join(two.first, l);
|
||||||
|
root = join(two);
|
||||||
|
}
|
||||||
|
// Line removal
|
||||||
|
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);
|
||||||
|
}
|
134
treap.hpp
134
treap.hpp
|
@ -1,134 +0,0 @@
|
||||||
#include <string>
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <utility>
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
using std::string;
|
|
||||||
typedef unsigned long long count_type;
|
|
||||||
|
|
||||||
// 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<line*, line*> two_lines;
|
|
||||||
|
|
||||||
// Treap representation of a file
|
|
||||||
class treap {
|
|
||||||
line* root;
|
|
||||||
|
|
||||||
// Get size uf a subtreap
|
|
||||||
count_type get_size(line* l) {
|
|
||||||
if(l == nullptr) return 0;
|
|
||||||
return l->size;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Split treap by k-th element
|
|
||||||
// Hopefully with sizes
|
|
||||||
two_lines split(line *l, count_type k) {
|
|
||||||
if(l == nullptr) return {nullptr, nullptr};
|
|
||||||
|
|
||||||
if(get_size(l->left) >= k) {
|
|
||||||
// In the left subtree
|
|
||||||
auto two = split(l->left, k);
|
|
||||||
l->left = two.second;
|
|
||||||
l->size -= get_size(two.first);
|
|
||||||
return {two.first, l};
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// In the right subtree
|
|
||||||
auto two = split(l->right, k - (1+get_size(l->left)));
|
|
||||||
l->right = two.first;
|
|
||||||
l->size -= get_size(two.second);
|
|
||||||
return {l, two.second};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Join two treaps
|
|
||||||
// Hopefully with working sizes
|
|
||||||
line* join(line *a, line *b) {
|
|
||||||
if(a == nullptr) return b;
|
|
||||||
if(b == nullptr) return a;
|
|
||||||
|
|
||||||
if(a->priority < b->priority) {
|
|
||||||
a->size += get_size(b);
|
|
||||||
a->right = join(a->right, b);
|
|
||||||
return a;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
b->size += get_size(a);
|
|
||||||
b->left = join(a, b->left);
|
|
||||||
return b;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
line* join(two_lines two) { return join(two.first, two.second); }
|
|
||||||
|
|
||||||
// Find k-th line of file
|
|
||||||
line* find(line* l, count_type k) {
|
|
||||||
if(l == nullptr) return nullptr;
|
|
||||||
|
|
||||||
if(k <= get_size(l->left))
|
|
||||||
return find(l->left, k);
|
|
||||||
else if(k == get_size(l->left)+1)
|
|
||||||
return l;
|
|
||||||
else
|
|
||||||
return find(l->right, k - (1+get_size(l->left)) );
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
treap() {
|
|
||||||
srand(120);
|
|
||||||
root = nullptr;
|
|
||||||
}
|
|
||||||
// File access
|
|
||||||
line* 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);
|
|
||||||
}
|
|
||||||
string get(count_type k) {
|
|
||||||
return find(k)->text;
|
|
||||||
}
|
|
||||||
void clear() {
|
|
||||||
while(size() > 0) {
|
|
||||||
auto two = split(root, 1);
|
|
||||||
root = two.second;
|
|
||||||
delete two.first;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Line insert
|
|
||||||
void insert(count_type k, string s) {
|
|
||||||
line *l = new line(rand(), s);
|
|
||||||
|
|
||||||
if(root == nullptr) {
|
|
||||||
root = l;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto two = split(root, k);
|
|
||||||
two.first = join(two.first, l);
|
|
||||||
root = join(two);
|
|
||||||
}
|
|
||||||
void append(string s) {
|
|
||||||
insert(size(), s);
|
|
||||||
}
|
|
||||||
// Line removal
|
|
||||||
void 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);
|
|
||||||
}
|
|
||||||
void pop() {
|
|
||||||
remove(size());
|
|
||||||
}
|
|
||||||
count_type size() {
|
|
||||||
return get_size(root);
|
|
||||||
}
|
|
||||||
};
|
|
Loading…
Reference in a new issue