Files
crib/include/syntax/line_map.h

137 lines
3.4 KiB
C++

#ifndef LINE_MAP_H
#define LINE_MAP_H
#include "syntax/decl.h"
struct LineKey {
uint32_t line;
uint32_t version;
bool operator==(const LineKey &other) const {
return line == other.line && version == other.version;
}
};
struct LineKeyHash {
size_t operator()(const LineKey &key) const {
uint64_t combined = (uint64_t(key.line) << 32) | key.version;
return std::hash<uint64_t>()(combined);
}
};
struct EditDelta {
uint32_t start_line;
int64_t delta;
};
struct LineMap {
LineMap() : current_version(0) { edit_log.push_back({0, 0}); }
LineData *at(uint32_t line) {
auto key_opt = resolve_line(line);
if (!key_opt)
return nullptr;
LineKey key = *key_opt;
if (key.version == current_version)
return lines[key].get();
auto data_ptr = std::move(lines[key]);
lines.erase(key);
key = {line, current_version};
lines[key] = std::move(data_ptr);
return lines[key].get();
}
LineData *create_at(uint32_t line) {
auto key_opt = resolve_line(line);
LineKey key;
std::unique_ptr<LineData> data_ptr;
if (key_opt) {
key = *key_opt;
if (key.version == current_version)
return lines[key].get();
data_ptr = std::move(lines[key]);
lines.erase(key);
} else {
data_ptr = std::make_unique<LineData>();
}
key = {line, current_version};
lines[key] = std::move(data_ptr);
return lines[key].get();
}
void apply_edit(uint32_t start, int64_t delta) {
if (delta < 0)
batch_remove(start, -delta);
current_version++;
edit_log.push_back({start, delta});
}
private:
std::unordered_map<LineKey, std::unique_ptr<LineData>, LineKeyHash> lines;
std::vector<EditDelta> edit_log;
uint32_t current_version;
void batch_remove(uint32_t line, uint32_t count) {
std::vector<uint32_t> lines_t;
lines_t.reserve(count);
for (uint32_t i = 0; i < count; i++)
lines_t.push_back(line + i);
for (int64_t v = current_version; v >= 0; v--) {
for (auto &l : lines_t) {
if (l == UINT32_MAX)
continue;
LineKey key = {l, (uint32_t)v};
if (lines.find(key) != lines.end()) {
lines.erase(key);
l = UINT32_MAX;
}
}
bool all_removed = true;
const auto &edit = edit_log[v];
for (auto &l : lines_t) {
if (l == UINT32_MAX)
continue;
all_removed = false;
if (edit.delta > 0) {
if (l >= edit.start_line) {
if (l < edit.start_line + edit.delta)
l = UINT32_MAX;
else
l -= edit.delta;
}
} else {
if (l >= edit.start_line)
l -= edit.delta;
}
}
if (all_removed)
break;
}
return;
}
std::optional<LineKey> resolve_line(uint32_t line) {
uint32_t current_line = line;
for (int64_t v = current_version; v >= 0; v--) {
LineKey key = {current_line, (uint32_t)v};
if (lines.find(key) != lines.end())
return key;
const auto &edit = edit_log[v];
if (edit.delta > 0) {
if (current_line >= edit.start_line) {
if (current_line < edit.start_line + edit.delta)
return std::nullopt;
current_line -= edit.delta;
}
} else {
if (current_line >= edit.start_line)
current_line -= edit.delta;
}
}
return std::nullopt;
}
};
#endif