Setup ed the posix line editor.
This commit is contained in:
@@ -88,16 +88,24 @@ void PetalIterator::seek_line(uint64_t line) {
|
||||
} else {
|
||||
auto *b = (Branch *)curr;
|
||||
uint64_t left_lines = b->left->lines;
|
||||
if (line <= left_lines) {
|
||||
if (dir == Direction::Forward)
|
||||
if (dir == Direction::Forward) {
|
||||
if (line < left_lines) {
|
||||
stack.push_back(b->right);
|
||||
curr = b->left;
|
||||
curr = b->left;
|
||||
} else {
|
||||
line -= left_lines;
|
||||
global_offset += b->left->length;
|
||||
curr = b->right;
|
||||
}
|
||||
} else {
|
||||
line -= left_lines;
|
||||
if (dir == Direction::Backward)
|
||||
if (line <= left_lines) {
|
||||
curr = b->left;
|
||||
} else {
|
||||
line -= left_lines;
|
||||
stack.push_back(b->left);
|
||||
global_offset += b->left->length;
|
||||
curr = b->right;
|
||||
global_offset += b->left->length;
|
||||
curr = b->right;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,7 +91,7 @@ Shard *balance(Shard *node) {
|
||||
|
||||
Shard *Shard::merge(Shard *a, Shard *b) {
|
||||
if (!a)
|
||||
return b ? (Shard::retain(b), b) : nullptr;
|
||||
return (Shard::retain(b), b);
|
||||
if (!b)
|
||||
return (Shard::retain(a), a);
|
||||
|
||||
@@ -205,7 +205,7 @@ Shard *Shard::build(Shard **pieces, uint64_t lo, uint64_t hi) {
|
||||
return node;
|
||||
}
|
||||
|
||||
Shard *Shard::from_file(std::filesystem::path path, OriginalBuffer *o) {
|
||||
Shard *Shard::from_file(std::filesystem::path path, OriginalBuffer *o, bool posix_ending) {
|
||||
int dest_fd = o->fd;
|
||||
if (dest_fd == -1)
|
||||
return nullptr;
|
||||
@@ -214,6 +214,19 @@ Shard *Shard::from_file(std::filesystem::path path, OriginalBuffer *o) {
|
||||
return nullptr;
|
||||
|
||||
uint64_t total = std::filesystem::file_size(path);
|
||||
if (posix_ending && total > 0) {
|
||||
char last;
|
||||
char s_last;
|
||||
if (pread(src_fd, &last, 1, (off_t)(total - 1)) != 1)
|
||||
return nullptr;
|
||||
if (pread(src_fd, &s_last, 1, (off_t)(total - 2)) != 1)
|
||||
return nullptr;
|
||||
if (last == '\n') {
|
||||
total--;
|
||||
if (s_last == '\r')
|
||||
total--;
|
||||
}
|
||||
}
|
||||
std::vector<Shard *> pieces;
|
||||
uint64_t pos = 0;
|
||||
pieces.reserve((total + PETAL_SIZE_MAX - 1) / PETAL_SIZE_MAX);
|
||||
@@ -257,4 +270,54 @@ Shard *Shard::from_file(std::filesystem::path path, OriginalBuffer *o) {
|
||||
return pieces[0];
|
||||
return build(pieces.data(), 0, pieces.size());
|
||||
}
|
||||
|
||||
void Shard::dump(Shard *node, int depth) {
|
||||
if (!node) {
|
||||
std::cout << std::string(depth * 2, ' ') << "<null>\n";
|
||||
return;
|
||||
}
|
||||
std::string indent(depth * 2, ' ');
|
||||
std::cout << indent
|
||||
<< "Shard@" << node
|
||||
<< " kind=";
|
||||
switch (node->kind) {
|
||||
case Shard::Kind::Branch:
|
||||
std::cout << "Branch";
|
||||
break;
|
||||
case Shard::Kind::Petal:
|
||||
std::cout << "Petal";
|
||||
break;
|
||||
}
|
||||
std::cout
|
||||
<< " height=" << unsigned(node->height)
|
||||
<< " length=" << node->length
|
||||
<< " lines=" << node->lines
|
||||
<< " refs=" << node->refs.load()
|
||||
<< "\n";
|
||||
if (node->kind == Shard::Kind::Branch) {
|
||||
auto *branch = (Branch *)node;
|
||||
std::cout << indent << " left:\n";
|
||||
dump(branch->left, depth + 2);
|
||||
std::cout << indent << " right:\n";
|
||||
dump(branch->right, depth + 2);
|
||||
} else {
|
||||
auto *petal = static_cast<Petal *>(node);
|
||||
constexpr auto clean = [](const std::string &text) {
|
||||
std::string result = text;
|
||||
size_t pos = 0;
|
||||
while ((pos = result.find('\n', pos)) != std::string::npos) {
|
||||
result.replace(pos, 1, "\\n");
|
||||
pos += 2;
|
||||
}
|
||||
return result;
|
||||
};
|
||||
std::cout
|
||||
<< indent << " source=" << petal->source
|
||||
<< " pos=" << petal->pos
|
||||
<< " length=" << petal->length
|
||||
<< " lines=" << petal->lines
|
||||
<< " text=\"" << clean(std::string(petal->source->read(petal->pos), petal->length))
|
||||
<< "\"\n";
|
||||
}
|
||||
}
|
||||
} // namespace crib::internal::vase
|
||||
|
||||
@@ -8,12 +8,12 @@ Vase::Vase(std::filesystem::path path, std::filesystem::path swapdir)
|
||||
append = new AppendBuffer(swapdir);
|
||||
original = new OriginalBuffer(swapdir);
|
||||
if (std::filesystem::is_regular_file(path))
|
||||
root = Shard::from_file(path, original);
|
||||
root = Shard::from_file(path, original, posix_ending);
|
||||
else
|
||||
root = nullptr;
|
||||
history_top = 0;
|
||||
history.push_back(root);
|
||||
Shard::retain(root);
|
||||
history_top = 0;
|
||||
}
|
||||
|
||||
Vase::~Vase() {
|
||||
@@ -25,22 +25,38 @@ Vase::~Vase() {
|
||||
}
|
||||
|
||||
uint64_t Vase::length() {
|
||||
if (!root)
|
||||
return 0;
|
||||
return root->length;
|
||||
}
|
||||
|
||||
uint64_t Vase::lines() {
|
||||
if (!root)
|
||||
return 0;
|
||||
return root->lines + 1;
|
||||
}
|
||||
|
||||
std::string Vase::to_string() {
|
||||
std::string out;
|
||||
if (!root)
|
||||
return out;
|
||||
PetalIterator it(root, Direction::Forward);
|
||||
it.seek_offset(0);
|
||||
const char *data;
|
||||
uint64_t len;
|
||||
while (it.next(&data, &len))
|
||||
out.append(data, len);
|
||||
if (posix_ending)
|
||||
out.append("\n");
|
||||
return out;
|
||||
}
|
||||
|
||||
std::string Vase::to_string(Range range) {
|
||||
clamp(&range.start);
|
||||
clamp(&range.end);
|
||||
std::string out;
|
||||
if (!root)
|
||||
return out;
|
||||
PetalIterator it(root, Direction::Forward);
|
||||
uint64_t start = offset_of(range.start);
|
||||
it.seek_offset(start);
|
||||
@@ -55,8 +71,8 @@ std::string Vase::to_string(Range range) {
|
||||
return out;
|
||||
}
|
||||
|
||||
Iterator Vase::iterate(uint64_t line) {
|
||||
return Iterator(root, line);
|
||||
Iterator Vase::iterate(uint64_t line, Direction dir) {
|
||||
return Iterator(root, line, dir);
|
||||
}
|
||||
|
||||
bool Vase::undo() {
|
||||
@@ -100,6 +116,8 @@ void Vase::prune_history(uint64_t n) {
|
||||
}
|
||||
|
||||
bool Vase::save() {
|
||||
if (!root)
|
||||
return true;
|
||||
std::ofstream file(path, std::ios::binary);
|
||||
if (!file)
|
||||
return false;
|
||||
@@ -112,6 +130,10 @@ bool Vase::save() {
|
||||
if (!file)
|
||||
return false;
|
||||
}
|
||||
if (posix_ending)
|
||||
file.write("\n", 1);
|
||||
if (!file)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -137,6 +159,10 @@ void Vase::insert(Point *point, char key) {
|
||||
point->col++;
|
||||
}
|
||||
|
||||
void Vase::insert(Point *point, std::string_view str) {
|
||||
insert(point, str.data(), str.size());
|
||||
}
|
||||
|
||||
void Vase::insert(Point *point, const char *data, uint64_t len) {
|
||||
while (len) {
|
||||
uint64_t chunk_size = std::min<uint64_t>(len, PETAL_SIZE_MAX);
|
||||
@@ -229,12 +255,17 @@ void Vase::erase(Range range) {
|
||||
root = new_root;
|
||||
}
|
||||
|
||||
void Vase::replace(Range range, std::string_view str) {
|
||||
replace(range, str.data(), str.size());
|
||||
}
|
||||
|
||||
void Vase::replace(Range range, const char *data, uint64_t len) {
|
||||
erase(range);
|
||||
insert(&range.start, data, len);
|
||||
}
|
||||
|
||||
uint64_t Vase::offset_of(Point point) {
|
||||
clamp(&point);
|
||||
LineIterator it(root, point.row, Direction::Forward);
|
||||
std::string line;
|
||||
uint64_t offset = 0;
|
||||
@@ -296,6 +327,7 @@ Point Vase::point_of(uint64_t offset) {
|
||||
ptr += next_len;
|
||||
p.col++;
|
||||
}
|
||||
clamp(&p);
|
||||
return p;
|
||||
}
|
||||
|
||||
@@ -363,11 +395,19 @@ void Vase::move_clusters(Point *point, uint64_t amount, Direction dir) {
|
||||
}
|
||||
}
|
||||
}
|
||||
clamp(point);
|
||||
}
|
||||
|
||||
void Vase::clamp(Point *point) {
|
||||
if (point->row > root->lines)
|
||||
if (!root) {
|
||||
point->row = 0;
|
||||
point->col = 0;
|
||||
return;
|
||||
}
|
||||
if (point->row > root->lines) {
|
||||
point->row = root->lines;
|
||||
point->col = UINT64_MAX;
|
||||
}
|
||||
LineIterator it(root, point->row, Direction::Forward);
|
||||
std::string line;
|
||||
uint64_t clusters = 0;
|
||||
|
||||
Reference in New Issue
Block a user