Files
kte/PieceTable.cc
Kyle Isom 57bfab633d Lots of updates:
1. Scrolling and click to set the cursor works.
2. GUI now uses Brass Mono as the font.
3. A lot of stability and other updates.
2025-11-29 20:22:24 -08:00

228 lines
4.2 KiB
C++

#include <algorithm>
#include <utility>
#include "PieceTable.h"
PieceTable::PieceTable() = default;
PieceTable::PieceTable(const std::size_t initialCapacity)
{
add_.reserve(initialCapacity);
materialized_.reserve(initialCapacity);
}
PieceTable::PieceTable(const PieceTable &other)
: original_(other.original_),
add_(other.add_),
pieces_(other.pieces_),
materialized_(other.materialized_),
dirty_(other.dirty_),
total_size_(other.total_size_) {}
PieceTable &
PieceTable::operator=(const PieceTable &other)
{
if (this == &other)
return *this;
original_ = other.original_;
add_ = other.add_;
pieces_ = other.pieces_;
materialized_ = other.materialized_;
dirty_ = other.dirty_;
total_size_ = other.total_size_;
return *this;
}
PieceTable::PieceTable(PieceTable &&other) noexcept
: original_(std::move(other.original_)),
add_(std::move(other.add_)),
pieces_(std::move(other.pieces_)),
materialized_(std::move(other.materialized_)),
dirty_(other.dirty_),
total_size_(other.total_size_)
{
other.dirty_ = true;
other.total_size_ = 0;
}
PieceTable &
PieceTable::operator=(PieceTable &&other) noexcept
{
if (this == &other)
return *this;
original_ = std::move(other.original_);
add_ = std::move(other.add_);
pieces_ = std::move(other.pieces_);
materialized_ = std::move(other.materialized_);
dirty_ = other.dirty_;
total_size_ = other.total_size_;
other.dirty_ = true;
other.total_size_ = 0;
return *this;
}
PieceTable::~PieceTable() = default;
void
PieceTable::Reserve(const std::size_t newCapacity)
{
add_.reserve(newCapacity);
materialized_.reserve(newCapacity);
}
void
PieceTable::AppendChar(char c)
{
const std::size_t start = add_.size();
add_.push_back(c);
addPieceBack(Source::Add, start, 1);
}
void
PieceTable::Append(const char *s, const std::size_t len)
{
if (len == 0) {
return;
}
const std::size_t start = add_.size();
add_.append(s, len);
addPieceBack(Source::Add, start, len);
}
void
PieceTable::Append(const PieceTable &other)
{
// Simpler and safe: materialize "other" and append bytes
const char *d = other.Data();
Append(d, other.Size());
}
void
PieceTable::PrependChar(const char c)
{
const std::size_t start = add_.size();
add_.push_back(c);
addPieceFront(Source::Add, start, 1);
}
void
PieceTable::Prepend(const char *s, const std::size_t len)
{
if (len == 0) {
return;
}
const std::size_t start = add_.size();
add_.append(s, len);
addPieceFront(Source::Add, start, len);
}
void
PieceTable::Prepend(const PieceTable &other)
{
const char *d = other.Data();
Prepend(d, other.Size());
}
void
PieceTable::Clear()
{
pieces_.clear();
add_.clear();
materialized_.clear();
total_size_ = 0;
dirty_ = true;
}
void
PieceTable::addPieceBack(const Source src, const std::size_t start, const std::size_t len)
{
if (len == 0) {
return;
}
// Attempt to coalesce with last piece if contiguous and same source
if (!pieces_.empty()) {
Piece &last = pieces_.back();
if (last.src == src) {
std::size_t expectStart = last.start + last.len;
if (expectStart == start) {
last.len += len;
total_size_ += len;
dirty_ = true;
return;
}
}
}
pieces_.push_back(Piece{src, start, len});
total_size_ += len;
dirty_ = true;
}
void
PieceTable::addPieceFront(Source src, std::size_t start, std::size_t len)
{
if (len == 0) {
return;
}
// Attempt to coalesce with first piece if contiguous and same source
if (!pieces_.empty()) {
Piece &first = pieces_.front();
if (first.src == src && start + len == first.start) {
first.start = start;
first.len += len;
total_size_ += len;
dirty_ = true;
return;
}
}
pieces_.insert(pieces_.begin(), Piece{src, start, len});
total_size_ += len;
dirty_ = true;
}
void
PieceTable::materialize() const
{
if (!dirty_) {
return;
}
materialized_.clear();
materialized_.reserve(total_size_ + 1);
for (const auto &p: pieces_) {
const std::string &src = p.src == Source::Original ? original_ : add_;
if (p.len == 0) {
continue;
}
materialized_.append(src.data() + static_cast<std::ptrdiff_t>(p.start), p.len);
}
// Ensure there is a null terminator present via std::string invariants
dirty_ = false;
}