Make substitutions work and other minor fixes.
This commit is contained in:
@@ -10,6 +10,10 @@ struct Parser {
|
|||||||
|
|
||||||
ParseState *root;
|
ParseState *root;
|
||||||
Language lang;
|
Language lang;
|
||||||
|
bool in_edit = false;
|
||||||
|
bool dirty = false;
|
||||||
|
uint64_t dirty_start = 0;
|
||||||
|
uint64_t dirty_end = 0;
|
||||||
|
|
||||||
Parser(vase::Shard *, uint64_t, Language);
|
Parser(vase::Shard *, uint64_t, Language);
|
||||||
~Parser();
|
~Parser();
|
||||||
@@ -21,6 +25,12 @@ struct Parser {
|
|||||||
void insert(vase::Shard *, uint64_t, uint64_t);
|
void insert(vase::Shard *, uint64_t, uint64_t);
|
||||||
void modify(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 next_closing(uint64_t line);
|
||||||
uint64_t prev_opening(uint64_t line);
|
uint64_t prev_opening(uint64_t line);
|
||||||
|
|
||||||
|
|||||||
@@ -20,8 +20,8 @@ struct Range {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct RegexGroup {
|
struct RegexGroup {
|
||||||
uint64_t start{0};
|
uint64_t start{UINT64_MAX};
|
||||||
uint64_t end{0};
|
uint64_t end{UINT64_MAX};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct RegexMatch {
|
struct RegexMatch {
|
||||||
@@ -57,7 +57,10 @@ uint64_t find_prev(Shard *root, std::string_view pattern, uint64_t start);
|
|||||||
Shard *substitute(
|
Shard *substitute(
|
||||||
AppendStorage *ap, Shard *root,
|
AppendStorage *ap, Shard *root,
|
||||||
std::string_view pattern, uint64_t start, uint64_t end,
|
std::string_view pattern, uint64_t start, uint64_t end,
|
||||||
std::string_view replace, std::string_view options
|
std::string_view replace, std::string_view options,
|
||||||
|
const std::function<void(
|
||||||
|
uint64_t line, uint64_t old_lines, uint64_t new_lines
|
||||||
|
)> &on_edit = nullptr
|
||||||
);
|
);
|
||||||
|
|
||||||
// internal
|
// internal
|
||||||
|
|||||||
@@ -22,11 +22,20 @@ uint64_t Buffer::bytes() {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Buffer::load(BEd &, vase::Shard *text) {
|
void Buffer::load(BEd &ctx, vase::Shard *text) {
|
||||||
try {
|
try {
|
||||||
vase::Shard::release(root);
|
vase::Shard::release(root);
|
||||||
root = text;
|
root = text;
|
||||||
state = buffer::Buffer::Unmodified;
|
state = buffer::Buffer::Unmodified;
|
||||||
|
if (!text) {
|
||||||
|
ctx.prev().buffername = name;
|
||||||
|
ctx.prev().start = 0;
|
||||||
|
ctx.prev().end = 0;
|
||||||
|
} else {
|
||||||
|
ctx.prev().buffername = name;
|
||||||
|
ctx.prev().start = 1;
|
||||||
|
ctx.prev().end = text->lines + 1;
|
||||||
|
}
|
||||||
parser.emplace(root, lines(), syntax::ruby::lang_ruby());
|
parser.emplace(root, lines(), syntax::ruby::lang_ruby());
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
vase::Shard::release(text);
|
vase::Shard::release(text);
|
||||||
@@ -71,13 +80,35 @@ void Buffer::substitute(
|
|||||||
BEd &ctx, uint64_t start_line, uint64_t end_line,
|
BEd &ctx, uint64_t start_line, uint64_t end_line,
|
||||||
std::string ®ex, std::string &replacement, std::string &options
|
std::string ®ex, std::string &replacement, std::string &options
|
||||||
) {
|
) {
|
||||||
// TODO: make substitue return a list of modifications made.
|
ctx.prev().buffername = name;
|
||||||
root = vase::substitute(&ctx.append, root, regex, start_line, end_line, replacement, options);
|
ctx.prev().start = start_line;
|
||||||
/*prev_range.start = start_line;
|
ctx.prev().end = end_line;
|
||||||
prev_range.end = start_line;
|
|
||||||
marks.collapse(start_line, end_line - start_line);
|
|
||||||
if (parser)
|
if (parser)
|
||||||
parser->erase(vase, start_line, end_line - start_line);*/
|
parser->begin_edit();
|
||||||
|
root = vase::substitute(
|
||||||
|
&ctx.append,
|
||||||
|
root,
|
||||||
|
regex,
|
||||||
|
start_line,
|
||||||
|
end_line,
|
||||||
|
replacement,
|
||||||
|
options,
|
||||||
|
[&](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 (new_lines) {
|
||||||
|
ctx.marks.insert(name, line, line + new_lines - 1);
|
||||||
|
if (parser)
|
||||||
|
parser->insert(line, line + new_lines - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
if (parser)
|
||||||
|
parser->end_edit(root);
|
||||||
|
ctx.prev().buffername = name;
|
||||||
state = Modified;
|
state = Modified;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ void Function::register_posix(BEd &ctx) {
|
|||||||
.input_mode = Function::InputMode::None,
|
.input_mode = Function::InputMode::None,
|
||||||
.desc = "Delete set of lines.",
|
.desc = "Delete set of lines.",
|
||||||
.default_address = ".,.",
|
.default_address = ".,.",
|
||||||
.accept_zero = true,
|
.accept_zero = false,
|
||||||
.pre_text_mode = nullptr,
|
.pre_text_mode = nullptr,
|
||||||
.handle = [](BEd &ctx, const buffer::Address &addr_, vase::Shard *, const Argument &, std::vector<buffer::Line> *) {
|
.handle = [](BEd &ctx, const buffer::Address &addr_, vase::Shard *, const Argument &, std::vector<buffer::Line> *) {
|
||||||
auto addr = std::get<buffer::Range>(addr_);
|
auto addr = std::get<buffer::Range>(addr_);
|
||||||
@@ -88,6 +88,25 @@ void Function::register_posix(BEd &ctx) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
ctx.functions.insert(
|
||||||
|
"c",
|
||||||
|
Function{
|
||||||
|
.address_kind = Function::AddressKind::Range,
|
||||||
|
.argument_kind = Function::ArgumentKind::None,
|
||||||
|
.input_mode = Function::InputMode::Text,
|
||||||
|
.desc = "Change set of lines.",
|
||||||
|
.default_address = ".,.",
|
||||||
|
.accept_zero = false,
|
||||||
|
.pre_text_mode = nullptr,
|
||||||
|
.handle = [](BEd &ctx, const buffer::Address &addr_, vase::Shard *text, const Argument &, std::vector<buffer::Line> *) {
|
||||||
|
auto addr = std::get<buffer::Range>(addr_);
|
||||||
|
ctx.buffer(addr.buffername).remove(ctx, addr.start, addr.end);
|
||||||
|
ctx.buffer(addr.buffername).append(ctx, text, addr.start - 1);
|
||||||
|
ctx.current() = {ctx.prev().buffername, ctx.prev().end};
|
||||||
|
vase::Shard::release(text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
ctx.functions.insert(
|
ctx.functions.insert(
|
||||||
"j",
|
"j",
|
||||||
Function{
|
Function{
|
||||||
@@ -186,7 +205,7 @@ void Function::register_posix(BEd &ctx) {
|
|||||||
.accept_zero = true,
|
.accept_zero = true,
|
||||||
.pre_text_mode = nullptr,
|
.pre_text_mode = nullptr,
|
||||||
.handle = [](BEd &ctx, const buffer::Address &addr_, vase::Shard *, const Argument &, std::vector<buffer::Line> *) {
|
.handle = [](BEd &ctx, const buffer::Address &addr_, vase::Shard *, const Argument &, std::vector<buffer::Line> *) {
|
||||||
auto addr = std::get<buffer::Range>(addr_);
|
auto &addr = std::get<buffer::Range>(addr_);
|
||||||
if (addr.start == addr.end)
|
if (addr.start == addr.end)
|
||||||
std::cout << ':' << addr.buffername << ':' << addr.start << "\n";
|
std::cout << ':' << addr.buffername << ':' << addr.start << "\n";
|
||||||
else
|
else
|
||||||
@@ -206,11 +225,44 @@ void Function::register_posix(BEd &ctx) {
|
|||||||
.accept_zero = false,
|
.accept_zero = false,
|
||||||
.pre_text_mode = nullptr,
|
.pre_text_mode = nullptr,
|
||||||
.handle = [](BEd &ctx, const buffer::Address &addr_, vase::Shard *, const Argument &arg, std::vector<buffer::Line> *) {
|
.handle = [](BEd &ctx, const buffer::Address &addr_, vase::Shard *, const Argument &arg, std::vector<buffer::Line> *) {
|
||||||
auto addr = std::get<buffer::Line>(addr_);
|
auto &addr = std::get<buffer::Line>(addr_);
|
||||||
ctx.mark(std::get<char>(arg), addr);
|
ctx.mark(std::get<char>(arg), addr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
ctx.functions.insert(
|
||||||
|
"s",
|
||||||
|
Function{
|
||||||
|
.address_kind = Function::AddressKind::Range,
|
||||||
|
.argument_kind = Function::ArgumentKind::Regex,
|
||||||
|
.input_mode = Function::InputMode::None,
|
||||||
|
.desc = "Substitute regex.",
|
||||||
|
.default_address = ".,.",
|
||||||
|
.accept_zero = false,
|
||||||
|
.pre_text_mode = nullptr,
|
||||||
|
.handle = [](BEd &ctx, const buffer::Address &addr_, vase::Shard *, const Argument &arg_, std::vector<buffer::Line> *) {
|
||||||
|
auto &addr = std::get<buffer::Range>(addr_);
|
||||||
|
auto arg = std::get<RegexArg>(arg_);
|
||||||
|
if (arg.expression == "")
|
||||||
|
arg.expression = ctx.last_regex;
|
||||||
|
else
|
||||||
|
ctx.last_regex = arg.expression;
|
||||||
|
if (arg.replacement == "%")
|
||||||
|
arg.replacement = ctx.last_replacement;
|
||||||
|
else
|
||||||
|
ctx.last_replacement = arg.replacement;
|
||||||
|
ctx.buffer(addr.buffername)
|
||||||
|
.substitute(
|
||||||
|
ctx,
|
||||||
|
addr.start,
|
||||||
|
addr.end,
|
||||||
|
arg.expression,
|
||||||
|
arg.replacement,
|
||||||
|
arg.options
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
ctx.functions.insert(
|
ctx.functions.insert(
|
||||||
"e",
|
"e",
|
||||||
Function{
|
Function{
|
||||||
|
|||||||
@@ -132,7 +132,7 @@ void Parser::operation() {
|
|||||||
if (delim == '\0')
|
if (delim == '\0')
|
||||||
throw ed_error("regex expected");
|
throw ed_error("regex expected");
|
||||||
advance();
|
advance();
|
||||||
uint64_t j = 0;
|
uint16_t j = 0;
|
||||||
while (true) {
|
while (true) {
|
||||||
if (peek(j) == '\0')
|
if (peek(j) == '\0')
|
||||||
throw ed_error("Unterminated regex");
|
throw ed_error("Unterminated regex");
|
||||||
@@ -141,7 +141,7 @@ void Parser::operation() {
|
|||||||
else if (peek(j) == '\\')
|
else if (peek(j) == '\\')
|
||||||
j += 2;
|
j += 2;
|
||||||
else if (peek(j) == '[')
|
else if (peek(j) == '[')
|
||||||
while (peek(j) != '\0' && cmd[i] != ']')
|
while (peek(j) != '\0' && peek(j) != ']')
|
||||||
j++;
|
j++;
|
||||||
else
|
else
|
||||||
j++;
|
j++;
|
||||||
@@ -157,15 +157,16 @@ void Parser::operation() {
|
|||||||
else if (peek(j) == '\\')
|
else if (peek(j) == '\\')
|
||||||
j += 2;
|
j += 2;
|
||||||
else if (peek(j) == '[')
|
else if (peek(j) == '[')
|
||||||
while (peek(j) != '\0' && cmd[i] != ']')
|
while (peek(j) != '\0' && peek(j) != ']')
|
||||||
j++;
|
j++;
|
||||||
else
|
else
|
||||||
j++;
|
j++;
|
||||||
}
|
}
|
||||||
std::string replacement;
|
std::string replacement(peek_str(j));
|
||||||
if (peek(j) != '\0') {
|
if (peek(j) != '\0') {
|
||||||
replacement = peek_str(j);
|
|
||||||
advance(j + 1);
|
advance(j + 1);
|
||||||
|
} else {
|
||||||
|
advance(j);
|
||||||
}
|
}
|
||||||
std::string options;
|
std::string options;
|
||||||
if (peek() != '\0') {
|
if (peek() != '\0') {
|
||||||
|
|||||||
@@ -123,36 +123,15 @@ ParseState *Parser::join_tree(ParseState *a, ParseState *b) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Parser::erase(vase::Shard *vase, uint64_t start, uint64_t count) {
|
void Parser::erase(vase::Shard *vase, uint64_t start, uint64_t count) {
|
||||||
if (count == 0 || !root)
|
begin_edit();
|
||||||
return;
|
erase(start, count);
|
||||||
auto [a, remaining] = split_tree(root, start);
|
end_edit(vase);
|
||||||
auto [waste, b] = split_tree(remaining, count);
|
|
||||||
destroy_tree(waste, lang);
|
|
||||||
root = join_tree(a, b);
|
|
||||||
modify(vase, start, 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Parser::insert(vase::Shard *vase, uint64_t start, uint64_t count) {
|
void Parser::insert(vase::Shard *vase, uint64_t start, uint64_t count) {
|
||||||
if (count == 0)
|
begin_edit();
|
||||||
return;
|
insert(start, count);
|
||||||
std::vector<ParseStateLeaf *> leaves;
|
end_edit(vase);
|
||||||
leaves.reserve((count + MAX_CHUNK - 1) / MAX_CHUNK);
|
|
||||||
uint64_t consumed = 0;
|
|
||||||
while (consumed < count) {
|
|
||||||
auto *leaf = (ParseStateLeaf *)malloc(sizeof(ParseStateLeaf));
|
|
||||||
leaf->state = nullptr;
|
|
||||||
leaf->blocks = nullptr;
|
|
||||||
leaf->n = 0;
|
|
||||||
leaf->cap = 0;
|
|
||||||
uint64_t chunk = std::min(MAX_CHUNK, count - consumed);
|
|
||||||
leaf->header = chunk;
|
|
||||||
consumed += chunk;
|
|
||||||
leaves.push_back(leaf);
|
|
||||||
}
|
|
||||||
ParseState *subtree = build_tree(leaves, 0, leaves.size());
|
|
||||||
auto [left, right] = split_tree(root, start);
|
|
||||||
root = join_tree(join_tree(left, subtree), right);
|
|
||||||
modify(vase, start, count);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Parser::modify(vase::Shard *vase, uint64_t target, uint64_t count) {
|
void Parser::modify(vase::Shard *vase, uint64_t target, uint64_t count) {
|
||||||
@@ -219,6 +198,63 @@ void Parser::modify(vase::Shard *vase, uint64_t target, uint64_t count) {
|
|||||||
lang.destroy(state);
|
lang.destroy(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Parser::begin_edit() {
|
||||||
|
in_edit = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Parser::mark_dirty(uint64_t start, uint64_t end) {
|
||||||
|
if (!dirty) {
|
||||||
|
dirty_start = start;
|
||||||
|
dirty_end = end;
|
||||||
|
dirty = true;
|
||||||
|
} else {
|
||||||
|
dirty_start = std::min(dirty_start, start);
|
||||||
|
dirty_end = std::max(dirty_end, end);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Parser::erase(uint64_t start, uint64_t count) {
|
||||||
|
if (count == 0 || !root)
|
||||||
|
return;
|
||||||
|
auto [a, remaining] = split_tree(root, start);
|
||||||
|
auto [waste, b] = split_tree(remaining, count);
|
||||||
|
destroy_tree(waste, lang);
|
||||||
|
root = join_tree(a, b);
|
||||||
|
mark_dirty(start, start + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Parser::insert(uint64_t start, uint64_t count) {
|
||||||
|
if (count == 0)
|
||||||
|
return;
|
||||||
|
std::vector<ParseStateLeaf *> leaves;
|
||||||
|
leaves.reserve((count + MAX_CHUNK - 1) / MAX_CHUNK);
|
||||||
|
uint64_t consumed = 0;
|
||||||
|
while (consumed < count) {
|
||||||
|
auto *leaf = (ParseStateLeaf *)malloc(sizeof(ParseStateLeaf));
|
||||||
|
leaf->state = nullptr;
|
||||||
|
leaf->blocks = nullptr;
|
||||||
|
leaf->n = 0;
|
||||||
|
leaf->cap = 0;
|
||||||
|
uint64_t chunk = std::min(MAX_CHUNK, count - consumed);
|
||||||
|
leaf->header = chunk;
|
||||||
|
consumed += chunk;
|
||||||
|
leaves.push_back(leaf);
|
||||||
|
}
|
||||||
|
ParseState *subtree = build_tree(leaves, 0, leaves.size());
|
||||||
|
auto [left, right] = split_tree(root, start);
|
||||||
|
root = join_tree(join_tree(left, subtree), right);
|
||||||
|
mark_dirty(start, start + count);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Parser::end_edit(vase::Shard *vase) {
|
||||||
|
in_edit = false;
|
||||||
|
if (!dirty)
|
||||||
|
return;
|
||||||
|
uint64_t count = dirty_end > dirty_start ? dirty_end - dirty_start : 1;
|
||||||
|
modify(vase, dirty_start, count);
|
||||||
|
dirty = false;
|
||||||
|
}
|
||||||
|
|
||||||
uint64_t Parser::next_closing(uint64_t line) {
|
uint64_t Parser::next_closing(uint64_t line) {
|
||||||
if (!root)
|
if (!root)
|
||||||
return UINT64_MAX;
|
return UINT64_MAX;
|
||||||
|
|||||||
@@ -25,12 +25,7 @@ std::vector<ReplacePart> parse_replace(AppendStorage *ap, std::string_view s) {
|
|||||||
};
|
};
|
||||||
for (size_t i = 0; i < s.size(); ++i) {
|
for (size_t i = 0; i < s.size(); ++i) {
|
||||||
char c = s[i];
|
char c = s[i];
|
||||||
if (c == '\\' && i + 1 < s.size() && s[i + 1] == '$') {
|
if (c == '\\' && i + 1 < s.size()) {
|
||||||
constant.push_back('$');
|
|
||||||
++i;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (c == '$' && i + 1 < s.size()) {
|
|
||||||
char next = s[i + 1];
|
char next = s[i + 1];
|
||||||
if (next == '0') {
|
if (next == '0') {
|
||||||
flush_constant();
|
flush_constant();
|
||||||
@@ -40,10 +35,7 @@ std::vector<ReplacePart> parse_replace(AppendStorage *ap, std::string_view s) {
|
|||||||
.value = (uint8_t)0
|
.value = (uint8_t)0
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
++i;
|
} else if (next >= '1' && next <= '9') {
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (next >= '1' && next <= '9') {
|
|
||||||
flush_constant();
|
flush_constant();
|
||||||
parts.push_back(
|
parts.push_back(
|
||||||
ReplacePart{
|
ReplacePart{
|
||||||
@@ -51,9 +43,22 @@ std::vector<ReplacePart> parse_replace(AppendStorage *ap, std::string_view s) {
|
|||||||
.value = (uint8_t)(next - '0')
|
.value = (uint8_t)(next - '0')
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
++i;
|
} else if (next == 'n') {
|
||||||
continue;
|
constant.push_back('\n');
|
||||||
|
} else {
|
||||||
|
constant.push_back(next);
|
||||||
}
|
}
|
||||||
|
++i;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (c == '&') {
|
||||||
|
flush_constant();
|
||||||
|
parts.push_back(
|
||||||
|
ReplacePart{
|
||||||
|
.type = ReplacePart::PartType::FullMatch,
|
||||||
|
.value = (uint8_t)0
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
constant.push_back(c);
|
constant.push_back(c);
|
||||||
}
|
}
|
||||||
@@ -64,7 +69,8 @@ std::vector<ReplacePart> parse_replace(AppendStorage *ap, std::string_view s) {
|
|||||||
Shard *substitute(
|
Shard *substitute(
|
||||||
AppendStorage *ap, Shard *root,
|
AppendStorage *ap, Shard *root,
|
||||||
std::string_view pattern, uint64_t start, uint64_t end,
|
std::string_view pattern, uint64_t start, uint64_t end,
|
||||||
std::string_view replace, std::string_view options
|
std::string_view replace, std::string_view options,
|
||||||
|
const std::function<void(uint64_t line, uint64_t old_lines, uint64_t new_lines)> &on_edit
|
||||||
) {
|
) {
|
||||||
if (!start || !end)
|
if (!start || !end)
|
||||||
throw ed_error("Invalid range.");
|
throw ed_error("Invalid range.");
|
||||||
@@ -79,11 +85,18 @@ Shard *substitute(
|
|||||||
uint64_t end_offset =
|
uint64_t end_offset =
|
||||||
(end + 1 == line_count)
|
(end + 1 == line_count)
|
||||||
? root->length
|
? root->length
|
||||||
: offset_of(root, end + 1);
|
: offset_of(root, end + 1) - 1;
|
||||||
std::vector<RegexMatch> matches = _regex_search(root, pattern, start_offset, end_offset, options);
|
std::vector<RegexMatch> matches = _regex_search(root, pattern, start_offset, end_offset, options);
|
||||||
if (matches.empty())
|
if (matches.empty())
|
||||||
return root;
|
return root;
|
||||||
std::vector<ReplacePart> replace_parts = parse_replace(ap, replace);
|
std::vector<ReplacePart> replace_parts = parse_replace(ap, replace);
|
||||||
|
struct Edit {
|
||||||
|
uint64_t line;
|
||||||
|
uint64_t old_lines;
|
||||||
|
uint64_t new_lines;
|
||||||
|
};
|
||||||
|
uint64_t orig_line = start;
|
||||||
|
int64_t line_delta = 0;
|
||||||
std::vector<Shard *> pieces;
|
std::vector<Shard *> pieces;
|
||||||
pieces.reserve(matches.size() * 2 + 1);
|
pieces.reserve(matches.size() * 2 + 1);
|
||||||
Shard *remaining = root;
|
Shard *remaining = root;
|
||||||
@@ -96,26 +109,33 @@ Shard *substitute(
|
|||||||
Shard::release(remaining);
|
Shard::release(remaining);
|
||||||
pieces.push_back(keep);
|
pieces.push_back(keep);
|
||||||
remaining = rest;
|
remaining = rest;
|
||||||
|
orig_line += keep ? keep->lines : 0;
|
||||||
}
|
}
|
||||||
auto [dropped, rest2] = Shard::split(remaining, match.end - match.start);
|
auto [dropped, rest2] = Shard::split(remaining, match.end - match.start);
|
||||||
Shard::release(remaining);
|
Shard::release(remaining);
|
||||||
remaining = rest2;
|
remaining = rest2;
|
||||||
|
uint64_t old_lines = dropped ? dropped->lines : 0;
|
||||||
|
uint64_t new_lines = 0;
|
||||||
for (size_t i = 0; i < replace_parts.size(); ++i) {
|
for (size_t i = 0; i < replace_parts.size(); ++i) {
|
||||||
const ReplacePart &part = replace_parts[i];
|
const ReplacePart &part = replace_parts[i];
|
||||||
switch (part.type) {
|
switch (part.type) {
|
||||||
case ReplacePart::PartType::Constant:
|
case ReplacePart::PartType::Constant: {
|
||||||
Shard::retain(std::get<Shard *>(part.value));
|
Shard *c = std::get<Shard *>(part.value);
|
||||||
pieces.push_back(std::get<Shard *>(part.value));
|
Shard::retain(c);
|
||||||
|
pieces.push_back(c);
|
||||||
|
new_lines += c ? c->lines : 0;
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case ReplacePart::PartType::FullMatch:
|
case ReplacePart::PartType::FullMatch:
|
||||||
Shard::retain(dropped);
|
Shard::retain(dropped);
|
||||||
pieces.push_back(dropped);
|
pieces.push_back(dropped);
|
||||||
|
new_lines += dropped ? dropped->lines : 0;
|
||||||
break;
|
break;
|
||||||
case ReplacePart::PartType::CaptureGroup: {
|
case ReplacePart::PartType::CaptureGroup: {
|
||||||
uint8_t idx = std::get<uint8_t>(part.value);
|
uint8_t idx = std::get<uint8_t>(part.value);
|
||||||
if (idx <= 9) {
|
if (idx <= 9) {
|
||||||
const RegexGroup &group = match.groups[idx - 1];
|
const RegexGroup &group = match.groups[idx - 1];
|
||||||
if (group.start > group.end) {
|
if (group.start != UINT64_MAX) {
|
||||||
uint64_t ls = group.start - match.start;
|
uint64_t ls = group.start - match.start;
|
||||||
uint64_t le = group.end - match.start;
|
uint64_t le = group.end - match.start;
|
||||||
auto [a, b] = Shard::split(dropped, ls);
|
auto [a, b] = Shard::split(dropped, ls);
|
||||||
@@ -124,6 +144,7 @@ Shard *substitute(
|
|||||||
Shard::release(b);
|
Shard::release(b);
|
||||||
Shard::release(c);
|
Shard::release(c);
|
||||||
pieces.push_back(g);
|
pieces.push_back(g);
|
||||||
|
new_lines += g ? g->lines : 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -131,6 +152,13 @@ Shard *substitute(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Shard::release(dropped);
|
Shard::release(dropped);
|
||||||
|
if (old_lines || new_lines) {
|
||||||
|
uint64_t report_line = (uint64_t)((int64_t)orig_line + line_delta);
|
||||||
|
if (on_edit)
|
||||||
|
on_edit(report_line, old_lines, new_lines);
|
||||||
|
line_delta += (int64_t)new_lines - (int64_t)old_lines;
|
||||||
|
}
|
||||||
|
orig_line += old_lines;
|
||||||
cursor = match.end;
|
cursor = match.end;
|
||||||
}
|
}
|
||||||
pieces.push_back(remaining);
|
pieces.push_back(remaining);
|
||||||
@@ -151,7 +179,7 @@ Shard *substitute(
|
|||||||
}
|
}
|
||||||
|
|
||||||
uint64_t find_next(Shard *root, std::string_view pattern, uint64_t start) {
|
uint64_t find_next(Shard *root, std::string_view pattern, uint64_t start) {
|
||||||
if (start == 0 || start > root->lines)
|
if (start == 0 || start > root->lines + 1)
|
||||||
throw ed_error("Invalid line number.");
|
throw ed_error("Invalid line number.");
|
||||||
start--;
|
start--;
|
||||||
std::vector<RegexMatch> results;
|
std::vector<RegexMatch> results;
|
||||||
@@ -180,7 +208,7 @@ uint64_t find_next(Shard *root, std::string_view pattern, uint64_t start) {
|
|||||||
if (rc >= 0) {
|
if (rc >= 0) {
|
||||||
pcre2_match_data_free(match_data);
|
pcre2_match_data_free(match_data);
|
||||||
pcre2_code_free(re);
|
pcre2_code_free(re);
|
||||||
return at;
|
return at + 1;
|
||||||
}
|
}
|
||||||
if (rc != PCRE2_ERROR_NOMATCH) {
|
if (rc != PCRE2_ERROR_NOMATCH) {
|
||||||
pcre2_match_data_free(match_data);
|
pcre2_match_data_free(match_data);
|
||||||
@@ -198,7 +226,7 @@ uint64_t find_next(Shard *root, std::string_view pattern, uint64_t start) {
|
|||||||
if (rc >= 0) {
|
if (rc >= 0) {
|
||||||
pcre2_match_data_free(match_data);
|
pcre2_match_data_free(match_data);
|
||||||
pcre2_code_free(re);
|
pcre2_code_free(re);
|
||||||
return at;
|
return at + 1;
|
||||||
}
|
}
|
||||||
if (rc != PCRE2_ERROR_NOMATCH) {
|
if (rc != PCRE2_ERROR_NOMATCH) {
|
||||||
pcre2_match_data_free(match_data);
|
pcre2_match_data_free(match_data);
|
||||||
@@ -213,7 +241,7 @@ uint64_t find_next(Shard *root, std::string_view pattern, uint64_t start) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
uint64_t find_prev(Shard *root, std::string_view pattern, uint64_t start) {
|
uint64_t find_prev(Shard *root, std::string_view pattern, uint64_t start) {
|
||||||
if (start == 0 || start > root->lines)
|
if (start == 0 || start > root->lines + 1)
|
||||||
throw ed_error("Invalid line number.");
|
throw ed_error("Invalid line number.");
|
||||||
start--;
|
start--;
|
||||||
std::vector<RegexMatch> results;
|
std::vector<RegexMatch> results;
|
||||||
@@ -242,7 +270,7 @@ uint64_t find_prev(Shard *root, std::string_view pattern, uint64_t start) {
|
|||||||
if (rc >= 0) {
|
if (rc >= 0) {
|
||||||
pcre2_match_data_free(match_data);
|
pcre2_match_data_free(match_data);
|
||||||
pcre2_code_free(re);
|
pcre2_code_free(re);
|
||||||
return at;
|
return at + 1;
|
||||||
}
|
}
|
||||||
if (rc != PCRE2_ERROR_NOMATCH) {
|
if (rc != PCRE2_ERROR_NOMATCH) {
|
||||||
pcre2_match_data_free(match_data);
|
pcre2_match_data_free(match_data);
|
||||||
@@ -260,7 +288,7 @@ uint64_t find_prev(Shard *root, std::string_view pattern, uint64_t start) {
|
|||||||
if (rc >= 0) {
|
if (rc >= 0) {
|
||||||
pcre2_match_data_free(match_data);
|
pcre2_match_data_free(match_data);
|
||||||
pcre2_code_free(re);
|
pcre2_code_free(re);
|
||||||
return at;
|
return at + 1;
|
||||||
}
|
}
|
||||||
if (rc != PCRE2_ERROR_NOMATCH) {
|
if (rc != PCRE2_ERROR_NOMATCH) {
|
||||||
pcre2_match_data_free(match_data);
|
pcre2_match_data_free(match_data);
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ std::vector<RegexMatch> _regex_search(
|
|||||||
uint64_t offset = UINT64_MAX;
|
uint64_t offset = UINT64_MAX;
|
||||||
|
|
||||||
auto record_match = [&](int rc, PCRE2_SIZE *ovector) {
|
auto record_match = [&](int rc, PCRE2_SIZE *ovector) {
|
||||||
if (global_offset + (uint64_t)ovector[1] > end_offset || ovector[0] == ovector[1])
|
if (global_offset + (uint64_t)ovector[1] > end_offset)
|
||||||
return;
|
return;
|
||||||
RegexMatch match{
|
RegexMatch match{
|
||||||
.start = global_offset + (uint64_t)ovector[0],
|
.start = global_offset + (uint64_t)ovector[0],
|
||||||
@@ -74,8 +74,8 @@ std::vector<RegexMatch> _regex_search(
|
|||||||
PCRE2_SIZE s = ovector[2 * i];
|
PCRE2_SIZE s = ovector[2 * i];
|
||||||
PCRE2_SIZE e = ovector[2 * i + 1];
|
PCRE2_SIZE e = ovector[2 * i + 1];
|
||||||
if (s != PCRE2_UNSET) {
|
if (s != PCRE2_UNSET) {
|
||||||
match.groups[i].start = global_offset + (uint64_t)s;
|
match.groups[i - 1].start = global_offset + (uint64_t)s;
|
||||||
match.groups[i].end = global_offset + (uint64_t)e;
|
match.groups[i - 1].end = global_offset + (uint64_t)e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
results.push_back(std::move(match));
|
results.push_back(std::move(match));
|
||||||
|
|||||||
@@ -232,7 +232,6 @@ Shard *join(Shard *root, uint64_t start, uint64_t end) {
|
|||||||
uint64_t line_count = root->lines + 1;
|
uint64_t line_count = root->lines + 1;
|
||||||
if (start >= end || end >= line_count)
|
if (start >= end || end >= line_count)
|
||||||
throw ed_error("line range out of bounds");
|
throw ed_error("line range out of bounds");
|
||||||
|
|
||||||
uint64_t offset = offset_of(root, start);
|
uint64_t offset = offset_of(root, start);
|
||||||
std::vector<Shard *> pieces;
|
std::vector<Shard *> pieces;
|
||||||
pieces.reserve(end - start + 3);
|
pieces.reserve(end - start + 3);
|
||||||
|
|||||||
Reference in New Issue
Block a user