Encapsulate editor actions
This commit is contained in:
parent
77b9b35901
commit
e1646c6098
6 changed files with 517 additions and 496 deletions
310
editor.cpp
Normal file
310
editor.cpp
Normal file
|
@ -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<bool, position> 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;
|
||||
}
|
94
editor.hpp
94
editor.hpp
|
@ -1,94 +0,0 @@
|
|||
#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);
|
||||
};
|
129
everything.hpp
Normal file
129
everything.hpp
Normal file
|
@ -0,0 +1,129 @@
|
|||
#include <ncurses.h>
|
||||
#include <curses.h>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
#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<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); }
|
||||
|
||||
// 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 <typename F, typename G>
|
||||
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<bool, position> 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();
|
||||
};
|
395
main.cpp
395
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<bool, position> 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();
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#include "editor.hpp"
|
||||
#include "everything.hpp"
|
||||
|
||||
// Selection manipulation
|
||||
template <typename F, typename G>
|
||||
|
@ -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));
|
||||
}
|
||||
|
|
54
treap.cpp
54
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);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue