Add history support, and fix parsing system.
This commit is contained in:
+214
-182
@@ -3,7 +3,192 @@
|
||||
|
||||
namespace bed::internal::buffer {
|
||||
GenericBuffer::~GenericBuffer() {
|
||||
for (auto &item : undo_stack) {
|
||||
syntax::release(item.parse_state);
|
||||
vase::Shard::release(item.text);
|
||||
}
|
||||
for (auto &item : redo_stack) {
|
||||
syntax::release(item.parse_state);
|
||||
vase::Shard::release(item.text);
|
||||
}
|
||||
}
|
||||
|
||||
void GenericBuffer::list_history(BEd &ctx) {
|
||||
uint64_t current = base_version + undo_stack.size();
|
||||
for (size_t i = 0; i < undo_stack.size(); ++i) {
|
||||
auto &item = undo_stack[i];
|
||||
uint64_t version = base_version + i;
|
||||
auto time = std::chrono::system_clock::to_time_t(item.timestamp);
|
||||
std::tm tm = *std::localtime(&time);
|
||||
ctx.io.write_line(
|
||||
std::format(
|
||||
" {} {:04}-{:02}-{:02} {:02}:{:02}:{:02} {}",
|
||||
version,
|
||||
tm.tm_year + 1900,
|
||||
tm.tm_mon + 1,
|
||||
tm.tm_mday,
|
||||
tm.tm_hour,
|
||||
tm.tm_min,
|
||||
tm.tm_sec,
|
||||
item.summary
|
||||
)
|
||||
);
|
||||
}
|
||||
{
|
||||
auto time = std::chrono::system_clock::to_time_t(timestamp);
|
||||
std::tm tm = *std::localtime(&time);
|
||||
ctx.io.write_line(
|
||||
std::format(
|
||||
"X {} {:04}-{:02}-{:02} {:02}:{:02}:{:02} {}",
|
||||
current,
|
||||
tm.tm_year + 1900,
|
||||
tm.tm_mon + 1,
|
||||
tm.tm_mday,
|
||||
tm.tm_hour,
|
||||
tm.tm_min,
|
||||
tm.tm_sec,
|
||||
action
|
||||
)
|
||||
);
|
||||
}
|
||||
for (size_t i = 0; i < redo_stack.size(); ++i) {
|
||||
auto &item = redo_stack[redo_stack.size() - 1 - i];
|
||||
uint64_t version = current + i + 1;
|
||||
auto time = std::chrono::system_clock::to_time_t(item.timestamp);
|
||||
std::tm tm = *std::localtime(&time);
|
||||
ctx.io.write_line(
|
||||
std::format(
|
||||
" {} {:04}-{:02}-{:02} {:02}:{:02}:{:02}",
|
||||
version,
|
||||
tm.tm_year + 1900,
|
||||
tm.tm_mon + 1,
|
||||
tm.tm_mday,
|
||||
tm.tm_hour,
|
||||
tm.tm_min,
|
||||
tm.tm_sec,
|
||||
item.summary
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
HistoryBuffer *GenericBuffer::get_history(uint64_t version) {
|
||||
uint64_t current = base_version + undo_stack.size();
|
||||
if (version < base_version)
|
||||
throw ed_error("History version has been pruned.");
|
||||
if (version < current) {
|
||||
auto &item = undo_stack[version - base_version];
|
||||
return new HistoryBuffer(name, item.text, item.parse_state);
|
||||
}
|
||||
if (version == current)
|
||||
return new HistoryBuffer(name, root, parse);
|
||||
uint64_t redo_offset = version - current - 1;
|
||||
if (redo_offset >= redo_stack.size())
|
||||
throw ed_error("No such history version.");
|
||||
auto &item = redo_stack[redo_stack.size() - 1 - redo_offset];
|
||||
return new HistoryBuffer(name, item.text, item.parse_state);
|
||||
}
|
||||
|
||||
void GenericBuffer::snapshot(std::string action_) {
|
||||
vase::Shard::retain(root);
|
||||
undo_stack.push_back(
|
||||
HistoryItem{
|
||||
syntax::retain(parse),
|
||||
root,
|
||||
timestamp,
|
||||
action
|
||||
}
|
||||
);
|
||||
for (auto &item : redo_stack) {
|
||||
syntax::release(item.parse_state);
|
||||
vase::Shard::release(item.text);
|
||||
}
|
||||
redo_stack.clear();
|
||||
timestamp = std::chrono::system_clock::now();
|
||||
action = action_;
|
||||
}
|
||||
|
||||
bool GenericBuffer::undo(BEd &ctx) {
|
||||
if (undo_stack.empty())
|
||||
return false;
|
||||
HistoryItem prev = undo_stack.back();
|
||||
undo_stack.pop_back();
|
||||
vase::Shard::retain(root);
|
||||
redo_stack.push_back(
|
||||
HistoryItem{
|
||||
syntax::retain(parse),
|
||||
root,
|
||||
timestamp,
|
||||
action
|
||||
}
|
||||
);
|
||||
vase::Shard::release(root);
|
||||
root = prev.text;
|
||||
syntax::release(parse);
|
||||
parse = prev.parse_state;
|
||||
state = Modified;
|
||||
if (!root) {
|
||||
ctx.prev().buffername = name;
|
||||
ctx.prev().start = 0;
|
||||
ctx.prev().end = 0;
|
||||
} else {
|
||||
ctx.prev().buffername = name;
|
||||
ctx.prev().start = 1;
|
||||
ctx.prev().end = root->lines + 1;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GenericBuffer::redo(BEd &ctx) {
|
||||
if (redo_stack.empty())
|
||||
return false;
|
||||
HistoryItem next = redo_stack.back();
|
||||
redo_stack.pop_back();
|
||||
vase::Shard::retain(root);
|
||||
undo_stack.push_back(
|
||||
HistoryItem{
|
||||
syntax::retain(parse),
|
||||
root,
|
||||
timestamp,
|
||||
action
|
||||
}
|
||||
);
|
||||
vase::Shard::release(root);
|
||||
root = next.text;
|
||||
syntax::release(parse);
|
||||
parse = next.parse_state;
|
||||
state = Modified;
|
||||
if (!root) {
|
||||
ctx.prev().buffername = name;
|
||||
ctx.prev().start = 0;
|
||||
ctx.prev().end = 0;
|
||||
} else {
|
||||
ctx.prev().buffername = name;
|
||||
ctx.prev().start = 1;
|
||||
ctx.prev().end = root->lines + 1;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void GenericBuffer::prune(int keep) {
|
||||
size_t drop =
|
||||
undo_stack.size() > (size_t)keep
|
||||
? undo_stack.size() - keep
|
||||
: 0;
|
||||
for (size_t i = 0; i < drop; ++i) {
|
||||
syntax::release(undo_stack[i].parse_state);
|
||||
vase::Shard::release(undo_stack[i].text);
|
||||
}
|
||||
undo_stack.erase(
|
||||
undo_stack.begin(),
|
||||
undo_stack.begin() + drop
|
||||
);
|
||||
base_version += drop;
|
||||
for (auto &item : redo_stack) {
|
||||
syntax::release(item.parse_state);
|
||||
vase::Shard::release(item.text);
|
||||
}
|
||||
redo_stack.clear();
|
||||
}
|
||||
|
||||
bool GenericBuffer::waste() {
|
||||
@@ -11,19 +196,8 @@ bool GenericBuffer::waste() {
|
||||
&& root == nullptr;
|
||||
}
|
||||
|
||||
uint64_t GenericBuffer::lines() {
|
||||
if (root)
|
||||
return root->lines + 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t GenericBuffer::bytes() {
|
||||
if (root)
|
||||
return root->length + 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void GenericBuffer::load(BEd &ctx, vase::Shard *text) {
|
||||
snapshot("Load file.");
|
||||
if (lines())
|
||||
ctx.marks.erase(name, 1, lines());
|
||||
vase::Shard::release(root);
|
||||
@@ -39,7 +213,8 @@ void GenericBuffer::load(BEd &ctx, vase::Shard *text) {
|
||||
ctx.prev().start = 1;
|
||||
ctx.prev().end = text->lines + 1;
|
||||
}
|
||||
parser.emplace(root, lines(), syntax::ruby::lang_ruby());
|
||||
syntax::release(parse);
|
||||
parse = syntax::make_parser(root, lines(), ctx.languages["ruby"]);
|
||||
}
|
||||
|
||||
void GenericBuffer::set_filename(std::filesystem::path path) {
|
||||
@@ -53,24 +228,26 @@ std::filesystem::path GenericBuffer::filename() {
|
||||
void GenericBuffer::append(BEd &ctx, vase::Shard *text, uint64_t line) {
|
||||
if (!text)
|
||||
return;
|
||||
snapshot(std::format("Insert {} lines after line {}", text->lines + 1, line));
|
||||
ctx.prev().buffername = name;
|
||||
ctx.prev().start = line + 1;
|
||||
ctx.prev().end = line + text->lines + 1;
|
||||
root = vase::insert(&ctx.append, root, text, line);
|
||||
ctx.marks.insert(name, line, text->lines + 1);
|
||||
if (parser)
|
||||
parser->insert(root, line, text->lines + 1);
|
||||
if (parse.lang)
|
||||
syntax::insert(parse, root, line, text->lines + 1);
|
||||
state = Modified;
|
||||
}
|
||||
|
||||
void GenericBuffer::remove(BEd &ctx, uint64_t start_line, uint64_t end_line) {
|
||||
snapshot(std::format("Remove lines {} to {}", start_line, end_line));
|
||||
root = vase::erase(root, start_line, end_line);
|
||||
ctx.prev().buffername = name;
|
||||
ctx.prev().start = std::min(start_line, lines());
|
||||
ctx.prev().end = std::min(start_line, lines());
|
||||
ctx.marks.erase(name, start_line, end_line - start_line + 1);
|
||||
if (parser)
|
||||
parser->erase(root, start_line, end_line - start_line + 1);
|
||||
if (parse.lang)
|
||||
syntax::erase(parse, root, start_line, end_line - start_line + 1);
|
||||
state = Modified;
|
||||
}
|
||||
|
||||
@@ -79,17 +256,18 @@ void GenericBuffer::replace(BEd &ctx, vase::Shard *text, uint64_t start_line, ui
|
||||
remove(ctx, start_line, end_line);
|
||||
return;
|
||||
}
|
||||
snapshot(std::format("Replace lines {} to {} with {} lines", start_line, end_line, text->lines));
|
||||
ctx.prev().buffername = name;
|
||||
ctx.prev().start = start_line;
|
||||
ctx.prev().end = start_line + text->lines;
|
||||
uint64_t new_count = text->lines + 1;
|
||||
uint64_t old_count = end_line - start_line + 1;
|
||||
root = vase::replace(root, text, start_line, end_line);
|
||||
if (parser) {
|
||||
parser->begin_edit();
|
||||
parser->erase(start_line, old_count);
|
||||
parser->insert(start_line, new_count);
|
||||
parser->end_edit(root);
|
||||
if (parse.lang) {
|
||||
syntax::Edit edit(parse);
|
||||
edit.erase(start_line, old_count);
|
||||
edit.insert(start_line, new_count);
|
||||
edit.commit(root);
|
||||
}
|
||||
if (new_count > old_count) {
|
||||
uint64_t diff = new_count - old_count;
|
||||
@@ -102,13 +280,14 @@ void GenericBuffer::replace(BEd &ctx, vase::Shard *text, uint64_t start_line, ui
|
||||
}
|
||||
|
||||
void GenericBuffer::join(BEd &ctx, uint64_t start_line, uint64_t end_line) {
|
||||
snapshot(std::format("Join lines {} to {}", start_line, end_line));
|
||||
root = vase::join(root, start_line, end_line);
|
||||
ctx.prev().buffername = name;
|
||||
ctx.prev().start = start_line;
|
||||
ctx.prev().end = start_line;
|
||||
ctx.marks.collapse(name, start_line, end_line - start_line);
|
||||
if (parser)
|
||||
parser->erase(root, start_line, end_line - start_line);
|
||||
if (parse.lang)
|
||||
syntax::erase(parse, root, start_line, end_line - start_line);
|
||||
state = Modified;
|
||||
}
|
||||
|
||||
@@ -116,11 +295,13 @@ void GenericBuffer::substitute(
|
||||
BEd &ctx, uint64_t start_line, uint64_t end_line,
|
||||
std::string ®ex, std::string &replacement, std::string &options
|
||||
) {
|
||||
snapshot(std::format("Substitute /{}/ with /{}/ in lines {} to {}", regex, replacement, start_line, end_line));
|
||||
ctx.prev().buffername = name;
|
||||
ctx.prev().start = start_line;
|
||||
ctx.prev().end = end_line;
|
||||
if (parser)
|
||||
parser->begin_edit();
|
||||
std::optional<syntax::Edit> edit;
|
||||
if (parse.lang)
|
||||
edit.emplace(parse);
|
||||
root = vase::substitute(
|
||||
&ctx.append,
|
||||
root,
|
||||
@@ -132,168 +313,19 @@ void GenericBuffer::substitute(
|
||||
[&](uint64_t line, uint64_t old_lines, uint64_t new_lines) {
|
||||
if (old_lines) {
|
||||
ctx.marks.erase(name, line, old_lines);
|
||||
if (parser)
|
||||
parser->erase(line, old_lines);
|
||||
if (edit)
|
||||
edit->erase(line, old_lines);
|
||||
}
|
||||
if (new_lines) {
|
||||
ctx.marks.insert(name, line, line + new_lines);
|
||||
if (parser)
|
||||
parser->insert(line, new_lines);
|
||||
ctx.marks.insert(name, line, new_lines);
|
||||
if (edit)
|
||||
edit->insert(line, new_lines);
|
||||
}
|
||||
}
|
||||
);
|
||||
if (parser)
|
||||
parser->end_edit(root);
|
||||
if (edit)
|
||||
edit->commit(root);
|
||||
ctx.prev().buffername = name;
|
||||
state = Modified;
|
||||
}
|
||||
|
||||
vase::Shard *GenericBuffer::copy(uint64_t start_line, uint64_t end_line) {
|
||||
return vase::copy(root, start_line, end_line);
|
||||
}
|
||||
|
||||
uint64_t GenericBuffer::find_next(std::string_view pattern, uint64_t start) {
|
||||
return vase::find_next(root, pattern, start);
|
||||
}
|
||||
|
||||
uint64_t GenericBuffer::find_prev(std::string_view pattern, uint64_t start) {
|
||||
return vase::find_prev(root, pattern, start);
|
||||
}
|
||||
|
||||
uint64_t GenericBuffer::next_closing(uint64_t start) {
|
||||
if (parser.has_value()) {
|
||||
uint64_t closing = parser->next_closing(start - 1);
|
||||
if (closing == UINT64_MAX)
|
||||
return lines();
|
||||
return closing + 1;
|
||||
} else {
|
||||
start += 10;
|
||||
if (start > lines())
|
||||
return lines();
|
||||
return start;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t GenericBuffer::prev_closing(uint64_t start) {
|
||||
if (parser.has_value()) {
|
||||
return parser->prev_opening(start - 1) + 1;
|
||||
} else {
|
||||
if (start > 10)
|
||||
return start - 10;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
inline void apply(io::IO &io, const Highlight &hl) {
|
||||
io.write("\x1b[0m");
|
||||
const uint8_t r = (hl.fg >> 16) & 0xff;
|
||||
const uint8_t g = (hl.fg >> 8) & 0xff;
|
||||
const uint8_t b = hl.fg & 0xff;
|
||||
io.write(std::format("\x1b[38;2;{};{};{}m", r, g, b));
|
||||
if (hl.bg != 0) {
|
||||
const uint8_t br = (hl.bg >> 16) & 0xff;
|
||||
const uint8_t bg = (hl.bg >> 8) & 0xff;
|
||||
const uint8_t bb = hl.bg & 0xff;
|
||||
io.write(std::format("\x1b[48;2;{};{};{}m", br, bg, bb));
|
||||
}
|
||||
if (hl.flags & Highlight::Bold)
|
||||
io.write("\x1b[1m");
|
||||
if (hl.flags & Highlight::Italic)
|
||||
io.write("\x1b[3m");
|
||||
if (hl.flags & Highlight::Underline)
|
||||
io.write("\x1b[4m");
|
||||
if (hl.flags & Highlight::Strikethrough)
|
||||
io.write("\x1b[9m");
|
||||
}
|
||||
|
||||
inline void reset(io::IO &io) {
|
||||
io.write("\x1b[0m");
|
||||
}
|
||||
|
||||
void GenericBuffer::print(BEd &ctx, uint64_t start_line, uint64_t end_line) {
|
||||
ctx.prev().buffername = name;
|
||||
ctx.prev().start = start_line;
|
||||
ctx.prev().end = end_line;
|
||||
if (parser) {
|
||||
auto it_o = parser->get_hl(root, start_line - 1);
|
||||
auto &it = *it_o;
|
||||
while (start_line <= end_line) {
|
||||
it.next();
|
||||
const std::string &line = it.it->line;
|
||||
const auto &tokens = it.tokens;
|
||||
uint32_t cursor = 0;
|
||||
for (const auto &token : tokens) {
|
||||
const uint32_t start = token.start;
|
||||
const uint32_t end = token.end;
|
||||
if (start > line.size() || end > line.size())
|
||||
break;
|
||||
if (cursor < start)
|
||||
ctx.io.write(line.data() + cursor, start - cursor);
|
||||
const auto highlight = ctx.theme.get(token);
|
||||
apply(ctx.io, highlight);
|
||||
ctx.io.write(line.data() + start, end - start);
|
||||
reset(ctx.io);
|
||||
cursor = end;
|
||||
}
|
||||
if (cursor < line.size())
|
||||
ctx.io.write(line.data() + cursor, line.size() - cursor);
|
||||
ctx.io.write_line("");
|
||||
++start_line;
|
||||
}
|
||||
} else {
|
||||
vase::Iterator it(root, start_line - 1, Direction::Forward);
|
||||
while (it.next() && start_line++ <= end_line)
|
||||
ctx.io.write_line(it.line);
|
||||
}
|
||||
}
|
||||
|
||||
void GenericBuffer::number_print(BEd &ctx, uint64_t start_line, uint64_t end_line) {
|
||||
ctx.prev().buffername = name;
|
||||
ctx.prev().start = start_line;
|
||||
ctx.prev().end = end_line;
|
||||
uint8_t width = 1;
|
||||
for (uint64_t n = end_line; n >= 10; n /= 10)
|
||||
++width;
|
||||
if (parser) {
|
||||
std::optional<syntax::Parser::Iterator> it_o = parser->get_hl(root, start_line - 1);
|
||||
auto &it = *it_o;
|
||||
while (start_line <= end_line) {
|
||||
it.next();
|
||||
ctx.io.write(std::format("{:>{}}\t", start_line, width));
|
||||
const std::string &line = it.it->line;
|
||||
const auto &tokens = it.tokens;
|
||||
uint32_t cursor = 0;
|
||||
for (const auto &token : tokens) {
|
||||
const uint32_t start = token.start;
|
||||
const uint32_t end = token.end;
|
||||
if (start > line.size() || end > line.size())
|
||||
break;
|
||||
if (cursor < start)
|
||||
ctx.io.write(line.data() + cursor, start - cursor);
|
||||
const auto highlight = ctx.theme.get(token);
|
||||
apply(ctx.io, highlight);
|
||||
ctx.io.write(line.data() + start, end - start);
|
||||
reset(ctx.io);
|
||||
cursor = end;
|
||||
}
|
||||
if (cursor < line.size())
|
||||
ctx.io.write(line.data() + cursor, line.size() - cursor);
|
||||
ctx.io.write_line("");
|
||||
++start_line;
|
||||
}
|
||||
} else {
|
||||
vase::Iterator it(root, start_line - 1, Direction::Forward);
|
||||
while (it.next() && start_line <= end_line)
|
||||
ctx.io.write_line(std::format("{:>{}}\t{}", start_line++, width, it.line));
|
||||
}
|
||||
}
|
||||
|
||||
void GenericBuffer::list_print(BEd &ctx, uint64_t start_line, uint64_t end_line) {
|
||||
ctx.prev().buffername = name;
|
||||
ctx.prev().start = start_line;
|
||||
ctx.prev().end = end_line;
|
||||
vase::Iterator it(root, start_line - 1, Direction::Forward);
|
||||
while (it.next() && start_line++ <= end_line)
|
||||
ctx.io.write_line(list_string(it.line));
|
||||
}
|
||||
} // namespace bed::internal::buffer
|
||||
|
||||
@@ -0,0 +1,171 @@
|
||||
#include "bed.h"
|
||||
#include "internal/buffer/buffer.h"
|
||||
|
||||
namespace bed::internal::buffer {
|
||||
uint64_t ShardBuffer::lines() {
|
||||
if (root)
|
||||
return root->lines + 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t ShardBuffer::bytes() {
|
||||
if (root)
|
||||
return root->length + 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
vase::Shard *ShardBuffer::copy(uint64_t start_line, uint64_t end_line) {
|
||||
return vase::copy(root, start_line, end_line);
|
||||
}
|
||||
|
||||
uint64_t ShardBuffer::find_next(std::string_view pattern, uint64_t start) {
|
||||
return vase::find_next(root, pattern, start);
|
||||
}
|
||||
|
||||
uint64_t ShardBuffer::find_prev(std::string_view pattern, uint64_t start) {
|
||||
return vase::find_prev(root, pattern, start);
|
||||
}
|
||||
|
||||
uint64_t ShardBuffer::next_closing(uint64_t start) {
|
||||
if (parse.lang) {
|
||||
uint64_t closing = syntax::next_closing(parse, start - 1);
|
||||
if (closing == UINT64_MAX)
|
||||
return lines();
|
||||
return closing + 1;
|
||||
} else {
|
||||
start += 10;
|
||||
if (start > lines())
|
||||
return lines();
|
||||
return start;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t ShardBuffer::prev_closing(uint64_t start) {
|
||||
if (parse.lang) {
|
||||
return syntax::prev_opening(parse, start - 1) + 1;
|
||||
} else {
|
||||
if (start > 10)
|
||||
return start - 10;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
inline void apply(io::IO &io, const Highlight &hl) {
|
||||
io.write("\x1b[0m");
|
||||
const uint8_t r = (hl.fg >> 16) & 0xff;
|
||||
const uint8_t g = (hl.fg >> 8) & 0xff;
|
||||
const uint8_t b = hl.fg & 0xff;
|
||||
io.write(std::format("\x1b[38;2;{};{};{}m", r, g, b));
|
||||
if (hl.bg != 0) {
|
||||
const uint8_t br = (hl.bg >> 16) & 0xff;
|
||||
const uint8_t bg = (hl.bg >> 8) & 0xff;
|
||||
const uint8_t bb = hl.bg & 0xff;
|
||||
io.write(std::format("\x1b[48;2;{};{};{}m", br, bg, bb));
|
||||
}
|
||||
if (hl.flags & Highlight::Bold)
|
||||
io.write("\x1b[1m");
|
||||
if (hl.flags & Highlight::Italic)
|
||||
io.write("\x1b[3m");
|
||||
if (hl.flags & Highlight::Underline)
|
||||
io.write("\x1b[4m");
|
||||
if (hl.flags & Highlight::Strikethrough)
|
||||
io.write("\x1b[9m");
|
||||
}
|
||||
|
||||
inline void reset(io::IO &io) {
|
||||
io.write("\x1b[0m");
|
||||
}
|
||||
|
||||
void ShardBuffer::print(BEd &ctx, uint64_t start_line, uint64_t end_line) {
|
||||
ctx.prev().buffername = name;
|
||||
ctx.prev().start = start_line;
|
||||
ctx.prev().end = end_line;
|
||||
if (parse.lang) {
|
||||
auto it_o = syntax::get_hl(parse, root, start_line - 1);
|
||||
if (!it_o)
|
||||
goto h;
|
||||
auto &it = *it_o;
|
||||
while (start_line <= end_line) {
|
||||
it.next();
|
||||
const std::string &line = it.it->line;
|
||||
const auto &tokens = it.tokens;
|
||||
uint32_t cursor = 0;
|
||||
for (const auto &token : tokens) {
|
||||
const uint32_t start = token.start;
|
||||
const uint32_t end = token.end;
|
||||
if (start > line.size() || end > line.size())
|
||||
break;
|
||||
if (cursor < start)
|
||||
ctx.io.write(line.data() + cursor, start - cursor);
|
||||
const auto highlight = ctx.theme.get(token);
|
||||
apply(ctx.io, highlight);
|
||||
ctx.io.write(line.data() + start, end - start);
|
||||
reset(ctx.io);
|
||||
cursor = end;
|
||||
}
|
||||
if (cursor < line.size())
|
||||
ctx.io.write(line.data() + cursor, line.size() - cursor);
|
||||
ctx.io.write_line("");
|
||||
++start_line;
|
||||
}
|
||||
} else {
|
||||
h:
|
||||
vase::Iterator it(root, start_line - 1, Direction::Forward);
|
||||
while (it.next() && start_line++ <= end_line)
|
||||
ctx.io.write_line(it.line);
|
||||
}
|
||||
}
|
||||
|
||||
void ShardBuffer::number_print(BEd &ctx, uint64_t start_line, uint64_t end_line) {
|
||||
ctx.prev().buffername = name;
|
||||
ctx.prev().start = start_line;
|
||||
ctx.prev().end = end_line;
|
||||
uint8_t width = 1;
|
||||
for (uint64_t n = end_line; n >= 10; n /= 10)
|
||||
++width;
|
||||
if (parse.lang) {
|
||||
std::optional<syntax::Iterator> it_o = syntax::get_hl(parse, root, start_line - 1);
|
||||
if (!it_o)
|
||||
goto h;
|
||||
auto &it = *it_o;
|
||||
while (start_line <= end_line) {
|
||||
it.next();
|
||||
ctx.io.write(std::format("{:>{}}\t", start_line, width));
|
||||
const std::string &line = it.it->line;
|
||||
const auto &tokens = it.tokens;
|
||||
uint32_t cursor = 0;
|
||||
for (const auto &token : tokens) {
|
||||
const uint32_t start = token.start;
|
||||
const uint32_t end = token.end;
|
||||
if (start > line.size() || end > line.size())
|
||||
break;
|
||||
if (cursor < start)
|
||||
ctx.io.write(line.data() + cursor, start - cursor);
|
||||
const auto highlight = ctx.theme.get(token);
|
||||
apply(ctx.io, highlight);
|
||||
ctx.io.write(line.data() + start, end - start);
|
||||
reset(ctx.io);
|
||||
cursor = end;
|
||||
}
|
||||
if (cursor < line.size())
|
||||
ctx.io.write(line.data() + cursor, line.size() - cursor);
|
||||
ctx.io.write_line("");
|
||||
++start_line;
|
||||
}
|
||||
} else {
|
||||
h:
|
||||
vase::Iterator it(root, start_line - 1, Direction::Forward);
|
||||
while (it.next() && start_line <= end_line)
|
||||
ctx.io.write_line(std::format("{:>{}}\t{}", start_line++, width, it.line));
|
||||
}
|
||||
}
|
||||
|
||||
void ShardBuffer::list_print(BEd &ctx, uint64_t start_line, uint64_t end_line) {
|
||||
ctx.prev().buffername = name;
|
||||
ctx.prev().start = start_line;
|
||||
ctx.prev().end = end_line;
|
||||
vase::Iterator it(root, start_line - 1, Direction::Forward);
|
||||
while (it.next() && start_line++ <= end_line)
|
||||
ctx.io.write_line(list_string(it.line));
|
||||
}
|
||||
} // namespace bed::internal::buffer
|
||||
@@ -128,7 +128,7 @@ void ClipBuffer::substitute(
|
||||
if (old_lines)
|
||||
ctx.marks.erase(name, line, old_lines);
|
||||
if (new_lines)
|
||||
ctx.marks.insert(name, line, line + new_lines - 1);
|
||||
ctx.marks.insert(name, line, new_lines - 1);
|
||||
}
|
||||
);
|
||||
clip_write(s);
|
||||
|
||||
Reference in New Issue
Block a user