Add history support, and fix parsing system.
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "clip.h"
|
||||
#include "./types/clip.h"
|
||||
#include "./types/generic.h"
|
||||
#include "decl.h"
|
||||
#include "generic.h"
|
||||
#include "pch.h"
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace bed::internal::buffer {
|
||||
struct Buffer {
|
||||
enum struct Kind : uint8_t {
|
||||
Generic,
|
||||
Null,
|
||||
History,
|
||||
Clip,
|
||||
Cancel,
|
||||
Shell,
|
||||
@@ -26,7 +26,7 @@ struct Buffer {
|
||||
std::string name;
|
||||
|
||||
explicit Buffer(std::string name, Kind kind)
|
||||
: kind(kind), state(Unmodified), name(name) {};
|
||||
: kind(kind), state(Unmodified), name(std::move(name)) {};
|
||||
virtual ~Buffer() = default;
|
||||
|
||||
virtual bool waste() = 0;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "decl.h"
|
||||
#include "pch.h"
|
||||
#include "../decl.h"
|
||||
|
||||
namespace bed::internal::buffer {
|
||||
struct ClipBuffer : Buffer {
|
||||
@@ -1,26 +1,41 @@
|
||||
#pragma once
|
||||
|
||||
#include "decl.h"
|
||||
#include "pch.h"
|
||||
#include "../decl.h"
|
||||
#include "history.h"
|
||||
#include "shard.h"
|
||||
|
||||
namespace bed::internal::buffer {
|
||||
struct GenericBuffer : Buffer {
|
||||
vase::Shard *root;
|
||||
struct HistoryItem {
|
||||
syntax::ParserSnapshot parse_state;
|
||||
vase::Shard *text;
|
||||
std::chrono::system_clock::time_point timestamp;
|
||||
std::string summary;
|
||||
};
|
||||
|
||||
struct GenericBuffer : ShardBuffer {
|
||||
uint64_t base_version{0};
|
||||
std::chrono::system_clock::time_point timestamp{
|
||||
std::chrono::system_clock::now()
|
||||
};
|
||||
std::string action{"created"};
|
||||
std::filesystem::path save_path{};
|
||||
std::string language{};
|
||||
std::optional<syntax::Parser> parser{};
|
||||
std::vector<HistoryItem> undo_stack;
|
||||
std::vector<HistoryItem> redo_stack;
|
||||
|
||||
GenericBuffer(std::string name)
|
||||
: Buffer(name, Kind::Generic), root(nullptr) {};
|
||||
: ShardBuffer(name, nullptr, nullptr, Kind::Generic) {}
|
||||
~GenericBuffer();
|
||||
|
||||
void list_history(BEd &ctx);
|
||||
HistoryBuffer *get_history(uint64_t version);
|
||||
void snapshot(std::string action);
|
||||
bool undo(BEd &ctx);
|
||||
bool redo(BEd &ctx);
|
||||
void prune(int = 0);
|
||||
bool waste() override;
|
||||
uint64_t lines() override;
|
||||
uint64_t bytes() override;
|
||||
void load(BEd &ctx, vase::Shard *text) override;
|
||||
void set_filename(std::filesystem::path path) override;
|
||||
std::filesystem::path filename() override;
|
||||
vase::Shard *copy(uint64_t start_line, uint64_t end_line) override;
|
||||
void substitute(
|
||||
BEd &ctx, uint64_t start_line, uint64_t end_line,
|
||||
std::string ®ex, std::string &replacement, std::string &options
|
||||
@@ -29,12 +44,5 @@ struct GenericBuffer : Buffer {
|
||||
void remove(BEd &ctx, uint64_t start_line, uint64_t end_line) override;
|
||||
void append(BEd &ctx, vase::Shard *text, uint64_t line) override;
|
||||
void replace(BEd &ctx, vase::Shard *text, uint64_t start_line, uint64_t end_line) override;
|
||||
void print(BEd &ctx, uint64_t start_line, uint64_t end_line) override;
|
||||
void number_print(BEd &ctx, uint64_t start_line, uint64_t end_line) override;
|
||||
void list_print(BEd &ctx, uint64_t start_line, uint64_t end_line) override;
|
||||
uint64_t next_closing(uint64_t start) override;
|
||||
uint64_t prev_closing(uint64_t start) override;
|
||||
uint64_t find_next(std::string_view pattern, uint64_t start) override;
|
||||
uint64_t find_prev(std::string_view pattern, uint64_t start) override;
|
||||
};
|
||||
} // namespace bed::internal::buffer
|
||||
@@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
|
||||
#include "../decl.h"
|
||||
#include "shard.h"
|
||||
|
||||
namespace bed::internal::buffer {
|
||||
struct HistoryBuffer : ShardBuffer {
|
||||
HistoryBuffer(
|
||||
std::string name, vase::Shard *root,
|
||||
const syntax::ParserSnapshot &snapshot
|
||||
) : ShardBuffer(std::move(name), root, snapshot, Kind::History) {}
|
||||
|
||||
bool waste() override {
|
||||
return true;
|
||||
}
|
||||
void load(BEd &, vase::Shard *) override {
|
||||
throw ed_error("History buffers are read-only.");
|
||||
}
|
||||
void set_filename(std::filesystem::path) override {}
|
||||
std::filesystem::path filename() override {
|
||||
return {};
|
||||
}
|
||||
void substitute(BEd &, uint64_t, uint64_t, std::string &, std::string &, std::string &) override {
|
||||
throw ed_error("History buffers are read-only.");
|
||||
}
|
||||
void join(BEd &, uint64_t, uint64_t) override {
|
||||
throw ed_error("History buffers are read-only.");
|
||||
}
|
||||
void remove(BEd &, uint64_t, uint64_t) override {
|
||||
throw ed_error("History buffers are read-only.");
|
||||
}
|
||||
void append(BEd &, vase::Shard *, uint64_t) override {
|
||||
throw ed_error("History buffers are read-only.");
|
||||
}
|
||||
void replace(BEd &, vase::Shard *, uint64_t, uint64_t) override {
|
||||
throw ed_error("History buffers are read-only.");
|
||||
}
|
||||
};
|
||||
} // namespace bed::internal::buffer
|
||||
@@ -0,0 +1,42 @@
|
||||
#pragma once
|
||||
|
||||
#include "../decl.h"
|
||||
|
||||
namespace bed::internal::buffer {
|
||||
struct ShardBuffer : Buffer {
|
||||
vase::Shard *root;
|
||||
syntax::ParserSnapshot parse{};
|
||||
|
||||
ShardBuffer(std::string name, vase::Shard *root, syntax::Language *language, Kind kind)
|
||||
: Buffer(std::move(name), kind), root(root) {
|
||||
vase::Shard::retain(root);
|
||||
if (language)
|
||||
parse = syntax::make_parser(root, lines(), language);
|
||||
}
|
||||
ShardBuffer(
|
||||
std::string name,
|
||||
vase::Shard *root,
|
||||
const syntax::ParserSnapshot &snapshot,
|
||||
Kind kind
|
||||
) : Buffer(std::move(name), kind),
|
||||
root(root),
|
||||
parse(syntax::retain(snapshot)) {
|
||||
vase::Shard::retain(root);
|
||||
}
|
||||
~ShardBuffer() {
|
||||
vase::Shard::release(root);
|
||||
syntax::release(parse);
|
||||
}
|
||||
|
||||
uint64_t lines() override;
|
||||
uint64_t bytes() override;
|
||||
vase::Shard *copy(uint64_t start_line, uint64_t end_line) override;
|
||||
void print(BEd &ctx, uint64_t start_line, uint64_t end_line) override;
|
||||
void number_print(BEd &ctx, uint64_t start_line, uint64_t end_line) override;
|
||||
void list_print(BEd &ctx, uint64_t start_line, uint64_t end_line) override;
|
||||
uint64_t next_closing(uint64_t start) override;
|
||||
uint64_t prev_closing(uint64_t start) override;
|
||||
uint64_t find_next(std::string_view pattern, uint64_t start) override;
|
||||
uint64_t find_prev(std::string_view pattern, uint64_t start) override;
|
||||
};
|
||||
} // namespace bed::internal::buffer
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "internal/trie/trie.h"
|
||||
#include "internal/vase/vase.h"
|
||||
#include "pch.h"
|
||||
|
||||
namespace bed::internal::syntax {
|
||||
@@ -75,7 +76,11 @@ struct ParseEvent {
|
||||
|
||||
struct Language {
|
||||
std::function<void *()> none_state;
|
||||
std::function<void(void **, std::string_view, bool, std::vector<Token> *, std::vector<ParseEvent> *)> parse;
|
||||
std::function<void(
|
||||
void **, std::string_view,
|
||||
bool, std::vector<Token> *, std::vector<ParseEvent> *
|
||||
)>
|
||||
parse;
|
||||
std::function<void *(void *)> copy;
|
||||
std::function<bool(void *, void *)> equal;
|
||||
std::function<void(void *)> destroy;
|
||||
@@ -85,35 +90,125 @@ struct ParseState {
|
||||
static constexpr uint64_t BRANCH_BIT = 1ull << 63;
|
||||
static constexpr uint64_t LINES_MASK = ~BRANCH_BIT;
|
||||
uint64_t header;
|
||||
uint16_t height;
|
||||
std::atomic_uint16_t refs;
|
||||
bool is_branch() const {
|
||||
return header & BRANCH_BIT;
|
||||
}
|
||||
uint64_t lines() const {
|
||||
return header & LINES_MASK;
|
||||
}
|
||||
explicit ParseState(bool branch, uint64_t lines, uint16_t height)
|
||||
: height(height), refs(1) {
|
||||
header = lines;
|
||||
header |= branch * BRANCH_BIT;
|
||||
};
|
||||
static void retain(ParseState *node);
|
||||
static void release(Language &lang, ParseState *node);
|
||||
static ParseState *build(Language &lang, ParseState **pieces, uint64_t lo, uint64_t hi);
|
||||
static ParseState *splice(
|
||||
Language &lang, ParseState *node, vase::Shard *vase,
|
||||
uint64_t line, uint64_t original, uint64_t final
|
||||
);
|
||||
static ParseState *concat(Language &lang, ParseState *a, ParseState *b);
|
||||
};
|
||||
|
||||
struct ParseStateBranch : ParseState {
|
||||
ParseState *left;
|
||||
ParseState *right;
|
||||
ParseStateBranch(ParseState *l, ParseState *r)
|
||||
: ParseState(
|
||||
true, l->lines() + r->lines(),
|
||||
1 + std::max(l->height, r->height)
|
||||
),
|
||||
left(l), right(r) {
|
||||
retain(l);
|
||||
retain(r);
|
||||
}
|
||||
};
|
||||
|
||||
struct ParseStateLeaf : ParseState {
|
||||
void *state;
|
||||
uint32_t n;
|
||||
uint32_t cap;
|
||||
uint16_t *blocks;
|
||||
static constexpr uint64_t MAX_CHUNK = 512;
|
||||
uint32_t n{0};
|
||||
static constexpr uint16_t IS_CLOSING = 0x8000;
|
||||
static constexpr uint16_t LINE_MASK = 0x7fff;
|
||||
uint16_t *blocks{nullptr};
|
||||
void *state;
|
||||
ParseStateLeaf(void *state, uint64_t lines, uint32_t n, uint16_t *blocks_)
|
||||
: ParseState(false, lines, 1), n(n), state(state) {
|
||||
blocks = (uint16_t *)malloc(sizeof(uint16_t) * n);
|
||||
memcpy(blocks, blocks_, sizeof(uint16_t) * n);
|
||||
};
|
||||
};
|
||||
|
||||
struct ParsePieceBuilder {
|
||||
Language ⟨
|
||||
std::vector<ParseState *> pieces;
|
||||
std::vector<uint16_t> blocks;
|
||||
void *piece_state{nullptr};
|
||||
uint64_t chunk_start{0};
|
||||
uint64_t chunk_lines{0};
|
||||
ParsePieceBuilder(Language &lang, uint64_t first_line)
|
||||
: lang(lang), chunk_start(first_line) {}
|
||||
void add(
|
||||
void *state,
|
||||
uint64_t line,
|
||||
const std::vector<ParseEvent> &events
|
||||
) {
|
||||
if (chunk_lines == 0) {
|
||||
chunk_start = line;
|
||||
piece_state = lang.copy(state);
|
||||
}
|
||||
for (const auto &ev : events) {
|
||||
blocks.push_back(
|
||||
(ev.closing ? ParseStateLeaf::IS_CLOSING : 0)
|
||||
| (line - chunk_start)
|
||||
);
|
||||
}
|
||||
++chunk_lines;
|
||||
if (chunk_lines == ParseStateLeaf::MAX_CHUNK)
|
||||
flush();
|
||||
}
|
||||
void flush() {
|
||||
if (chunk_lines == 0)
|
||||
return;
|
||||
pieces.push_back(new ParseStateLeaf(
|
||||
piece_state,
|
||||
chunk_lines,
|
||||
blocks.size(),
|
||||
blocks.data()
|
||||
));
|
||||
piece_state = nullptr;
|
||||
blocks.clear();
|
||||
chunk_lines = 0;
|
||||
}
|
||||
ParseState *finish() {
|
||||
flush();
|
||||
if (pieces.empty())
|
||||
return nullptr;
|
||||
ParseState *root = ParseState::build(lang, pieces.data(), 0, pieces.size());
|
||||
pieces.clear();
|
||||
return root;
|
||||
}
|
||||
};
|
||||
|
||||
struct TreeCursor {
|
||||
Language ⟨
|
||||
ParseState *root;
|
||||
ParseStateLeaf *leaf = nullptr;
|
||||
ParseStateBranch *stack[64];
|
||||
uint8_t depth = 0;
|
||||
bool went_left[64];
|
||||
TreeCursor(ParseState *root, uint64_t target_line, uint64_t *relative);
|
||||
TreeCursor(
|
||||
Language &lang, ParseState *root,
|
||||
uint64_t target_line, uint64_t *relative
|
||||
);
|
||||
~TreeCursor();
|
||||
TreeCursor(const TreeCursor &) = delete;
|
||||
TreeCursor &operator=(const TreeCursor &) = delete;
|
||||
void next();
|
||||
void prev();
|
||||
ParseState *prefix();
|
||||
ParseState *suffix();
|
||||
};
|
||||
} // namespace bed::internal::syntax
|
||||
|
||||
@@ -1,57 +1,54 @@
|
||||
#pragma once
|
||||
|
||||
#include "decl.h"
|
||||
#include "internal/vase/vase.h"
|
||||
#include "pch.h"
|
||||
|
||||
namespace bed::internal::syntax {
|
||||
struct Parser {
|
||||
static constexpr uint64_t MAX_CHUNK = 512;
|
||||
struct ParserSnapshot {
|
||||
ParseState *root = nullptr;
|
||||
Language *lang = nullptr;
|
||||
};
|
||||
|
||||
ParseState *root;
|
||||
Language lang;
|
||||
bool in_edit = false;
|
||||
ParserSnapshot make_parser(vase::Shard *vase, uint64_t lines, Language *lang);
|
||||
ParserSnapshot retain(const ParserSnapshot &snap);
|
||||
void release(ParserSnapshot &snap);
|
||||
|
||||
uint64_t next_closing(const ParserSnapshot &snap, uint64_t line);
|
||||
uint64_t prev_opening(const ParserSnapshot &snap, uint64_t line);
|
||||
|
||||
struct Iterator {
|
||||
ParserSnapshot snap;
|
||||
std::optional<vase::Iterator> it;
|
||||
void *state;
|
||||
uint64_t at;
|
||||
std::vector<Token> tokens;
|
||||
std::vector<ParseEvent> events;
|
||||
Iterator(uint64_t, ParserSnapshot, vase::Shard *);
|
||||
~Iterator();
|
||||
Iterator(const Iterator &) = delete;
|
||||
Iterator &operator=(const Iterator &) = delete;
|
||||
Iterator(Iterator &&other);
|
||||
Iterator &operator=(Iterator &&other);
|
||||
void next();
|
||||
};
|
||||
|
||||
std::optional<Iterator> get_hl(const ParserSnapshot &snap, vase::Shard *vase, uint64_t target);
|
||||
|
||||
void insert(ParserSnapshot &snap, vase::Shard *vase, uint64_t start, uint64_t count);
|
||||
void erase(ParserSnapshot &snap, vase::Shard *vase, uint64_t start, uint64_t count);
|
||||
|
||||
struct Edit {
|
||||
ParserSnapshot *target;
|
||||
bool dirty = false;
|
||||
uint64_t dirty_start = 0;
|
||||
uint64_t dirty_end = 0;
|
||||
int64_t edit_delta = 0;
|
||||
|
||||
Parser(vase::Shard *, uint64_t, Language);
|
||||
~Parser();
|
||||
Parser(const Parser &) = delete;
|
||||
Parser &operator=(const Parser &) = delete;
|
||||
explicit Edit(ParserSnapshot &snap) : target(&snap) {}
|
||||
|
||||
void reset(vase::Shard *, uint64_t, Language);
|
||||
void erase(vase::Shard *, uint64_t, uint64_t);
|
||||
void insert(vase::Shard *, uint64_t, uint64_t);
|
||||
void modify(vase::Shard *, uint64_t, uint64_t);
|
||||
|
||||
void begin_edit();
|
||||
void erase(uint64_t start, uint64_t count);
|
||||
void insert(uint64_t start, uint64_t count);
|
||||
void end_edit(vase::Shard *vase);
|
||||
void mark_dirty(uint64_t start, uint64_t end);
|
||||
|
||||
uint64_t next_closing(uint64_t line);
|
||||
uint64_t prev_opening(uint64_t line);
|
||||
|
||||
std::pair<ParseState *, ParseState *> split_tree(ParseState *node, uint64_t line);
|
||||
ParseState *join_tree(ParseState *a, ParseState *b);
|
||||
|
||||
struct Iterator {
|
||||
Parser *p;
|
||||
std::optional<vase::Iterator> it;
|
||||
void *state;
|
||||
uint64_t at;
|
||||
std::vector<Token> tokens;
|
||||
std::vector<ParseEvent> events;
|
||||
Iterator(uint64_t, Parser *, vase::Shard *);
|
||||
~Iterator();
|
||||
Iterator(const Iterator &) = delete;
|
||||
Iterator &operator=(const Iterator &) = delete;
|
||||
Iterator(Iterator &&other);
|
||||
Iterator &operator=(Iterator &&other);
|
||||
void next();
|
||||
};
|
||||
std::optional<Iterator> get_hl(vase::Shard *, uint64_t);
|
||||
void insert(uint64_t start, uint64_t count);
|
||||
void erase(uint64_t start, uint64_t count);
|
||||
void commit(vase::Shard *vase);
|
||||
};
|
||||
} // namespace bed::internal::syntax
|
||||
|
||||
@@ -12,13 +12,13 @@ struct Shard {
|
||||
Petal
|
||||
} kind;
|
||||
|
||||
uint8_t height;
|
||||
uint16_t height;
|
||||
std::atomic_uint32_t refs;
|
||||
|
||||
uint64_t length;
|
||||
uint64_t lines;
|
||||
|
||||
Shard(Kind kind, uint64_t length, uint64_t lines, uint8_t height)
|
||||
Shard(Kind kind, uint64_t length, uint64_t lines, uint16_t height)
|
||||
: kind(kind), height(height), refs(1), length(length), lines(lines) {};
|
||||
|
||||
static void retain(Shard *n);
|
||||
@@ -62,5 +62,4 @@ struct Petal : Shard {
|
||||
source->retain();
|
||||
};
|
||||
};
|
||||
|
||||
} // namespace bed::internal::vase
|
||||
|
||||
Reference in New Issue
Block a user