Files
kte/Buffer.h
Kyle Isom e4cd4877cc Introduce file picker and GUI configuration with enhancements.
- Add visual file picker for GUI with toggle support.
- Introduce `GUIConfig` class for loading GUI settings from configuration file.
- Refactor window initialization to support dynamic sizing based on configuration.
- Add macOS-specific handling for fullscreen behavior.
- Improve header inclusion order and minor code cleanup.
2025-11-30 18:35:12 -08:00

367 lines
6.0 KiB
C++

/*
* Buffer.h - editor buffer representing an open document
*/
#ifndef KTE_BUFFER_H
#define KTE_BUFFER_H
#include <cstddef>
#include <string>
#include <vector>
#include <string_view>
#include "AppendBuffer.h"
#include "UndoSystem.h"
class Buffer {
public:
Buffer();
Buffer(const Buffer &other);
Buffer &operator=(const Buffer &other);
Buffer(Buffer &&other) noexcept;
Buffer &operator=(Buffer &&other) noexcept;
explicit Buffer(const std::string &path);
// File operations
bool OpenFromFile(const std::string &path, std::string &err);
bool Save(std::string &err) const; // saves to existing filename; returns false if not file-backed
bool SaveAs(const std::string &path, std::string &err); // saves to path and makes buffer file-backed
// Accessors
[[nodiscard]] std::size_t Curx() const
{
return curx_;
}
[[nodiscard]] std::size_t Cury() const
{
return cury_;
}
[[nodiscard]] std::size_t Rx() const
{
return rx_;
}
[[nodiscard]] std::size_t Nrows() const
{
return nrows_;
}
[[nodiscard]] std::size_t Rowoffs() const
{
return rowoffs_;
}
[[nodiscard]] std::size_t Coloffs() const
{
return coloffs_;
}
// Line wrapper backed by AppendBuffer (GapBuffer/PieceTable)
class Line {
public:
Line() = default;
Line(const char *s)
{
assign_from(s ? std::string(s) : std::string());
}
Line(const std::string &s)
{
assign_from(s);
}
Line(const Line &other) = default;
Line &operator=(const Line &other) = default;
Line(Line &&other) noexcept = default;
Line &operator=(Line &&other) noexcept = default;
// capacity helpers
void Clear()
{
buf_.Clear();
}
// size/access
[[nodiscard]] std::size_t size() const
{
return buf_.Size();
}
[[nodiscard]] bool empty() const
{
return size() == 0;
}
// read-only raw view
[[nodiscard]] const char *Data() const
{
return buf_.Data();
}
[[nodiscard]] std::size_t Size() const
{
return buf_.Size();
}
// element access (read-only)
[[nodiscard]] char operator[](std::size_t i) const
{
const char *d = buf_.Data();
return (i < buf_.Size() && d) ? d[i] : '\0';
}
// conversions
operator std::string() const
{
return std::string(buf_.Data() ? buf_.Data() : "", buf_.Size());
}
// string-like API used by command/renderer layers (implemented via materialization for now)
std::string substr(std::size_t pos) const
{
const std::size_t n = buf_.Size();
if (pos >= n)
return std::string();
return std::string(buf_.Data() + pos, n - pos);
}
std::string substr(std::size_t pos, std::size_t len) const
{
const std::size_t n = buf_.Size();
if (pos >= n)
return std::string();
const std::size_t take = (pos + len > n) ? (n - pos) : len;
return std::string(buf_.Data() + pos, take);
}
void erase(std::size_t pos)
{
// erase to end
material_edit([&](std::string &s) {
if (pos < s.size())
s.erase(pos);
});
}
void erase(std::size_t pos, std::size_t len)
{
material_edit([&](std::string &s) {
if (pos < s.size())
s.erase(pos, len);
});
}
void insert(std::size_t pos, const std::string &seg)
{
material_edit([&](std::string &s) {
if (pos > s.size())
pos = s.size();
s.insert(pos, seg);
});
}
Line &operator+=(const Line &other)
{
buf_.Append(other.buf_.Data(), other.buf_.Size());
return *this;
}
Line &operator+=(const std::string &s)
{
buf_.Append(s.data(), s.size());
return *this;
}
Line &operator=(const std::string &s)
{
assign_from(s);
return *this;
}
private:
void assign_from(const std::string &s)
{
buf_.Clear();
if (!s.empty())
buf_.Append(s.data(), s.size());
}
template<typename F>
void material_edit(F fn)
{
std::string tmp = static_cast<std::string>(*this);
fn(tmp);
assign_from(tmp);
}
AppendBuffer buf_;
};
[[nodiscard]] const std::vector<Line> &Rows() const
{
return rows_;
}
[[nodiscard]] std::vector<Line> &Rows()
{
return rows_;
}
[[nodiscard]] const std::string &Filename() const
{
return filename_;
}
[[nodiscard]] bool IsFileBacked() const
{
return is_file_backed_;
}
[[nodiscard]] bool Dirty() const
{
return dirty_;
}
void SetCursor(std::size_t x, std::size_t y)
{
curx_ = x;
cury_ = y;
}
void SetRenderX(std::size_t rx)
{
rx_ = rx;
}
void SetOffsets(std::size_t row, std::size_t col)
{
rowoffs_ = row;
coloffs_ = col;
}
void SetDirty(bool d)
{
dirty_ = d;
}
// Mark support
void ClearMark()
{
mark_set_ = false;
}
void SetMark(std::size_t x, std::size_t y)
{
mark_set_ = true;
mark_curx_ = x;
mark_cury_ = y;
}
[[nodiscard]] bool MarkSet() const
{
return mark_set_;
}
[[nodiscard]] std::size_t MarkCurx() const
{
return mark_curx_;
}
[[nodiscard]] std::size_t MarkCury() const
{
return mark_cury_;
}
[[nodiscard]] std::string AsString() const;
// Raw, low-level editing APIs used by UndoSystem apply().
// These must NOT trigger undo recording. They also do not move the cursor.
void insert_text(int row, int col, std::string_view text);
void delete_text(int row, int col, std::size_t len);
void split_line(int row, int col);
void join_lines(int row);
void insert_row(int row, std::string_view text);
void delete_row(int row);
// Undo system accessors (created per-buffer)
UndoSystem *Undo();
const UndoSystem *Undo() const;
private:
// State mirroring original C struct (without undo_tree)
std::size_t curx_ = 0, cury_ = 0; // cursor position in characters
std::size_t rx_ = 0; // render x (tabs expanded)
std::size_t nrows_ = 0; // number of rows
std::size_t rowoffs_ = 0, coloffs_ = 0; // viewport offsets
std::vector<Line> rows_; // buffer rows (without trailing newlines)
std::string filename_;
bool is_file_backed_ = false;
bool dirty_ = false;
bool mark_set_ = false;
std::size_t mark_curx_ = 0, mark_cury_ = 0;
// Per-buffer undo state
std::unique_ptr<struct UndoTree> undo_tree_;
std::unique_ptr<UndoSystem> undo_sys_;
};
#endif // KTE_BUFFER_H