Add marks and regex addressing systems
- Hook up parser to buffer edits. - Other minor fixes.
This commit is contained in:
@@ -29,11 +29,12 @@ Address::Address(std::string &cmd, uint64_t &i) {
|
||||
break;
|
||||
case '\'': {
|
||||
i++;
|
||||
if (i < cmd.size() && 'a' <= cmd[i] && cmd[i] <= 'z')
|
||||
if (i < cmd.size()
|
||||
&& (('a' <= cmd[i] && cmd[i] <= 'z') || ('A' <= cmd[i] && cmd[i] <= 'Z')))
|
||||
i++;
|
||||
else
|
||||
throw address_error("Invalid mark.");
|
||||
base = Mark(cmd[i]);
|
||||
base = Mark(cmd[i - 1]);
|
||||
} break;
|
||||
case '/': {
|
||||
i++;
|
||||
@@ -54,7 +55,9 @@ Address::Address(std::string &cmd, uint64_t &i) {
|
||||
else
|
||||
i++;
|
||||
}
|
||||
base = RegexLine(Direction::Forward, cmd.substr(start, i - start));
|
||||
base = Regex(Direction::Forward, cmd.substr(start, i - start));
|
||||
if (i < cmd.size())
|
||||
i++;
|
||||
} break;
|
||||
case '?': {
|
||||
i++;
|
||||
@@ -75,7 +78,9 @@ Address::Address(std::string &cmd, uint64_t &i) {
|
||||
else
|
||||
i++;
|
||||
}
|
||||
base = RegexLine(Direction::Backward, cmd.substr(start, i - start));
|
||||
base = Regex(Direction::Backward, cmd.substr(start, i - start));
|
||||
if (i < cmd.size())
|
||||
i++;
|
||||
} break;
|
||||
case '+': {
|
||||
base = Current();
|
||||
|
||||
@@ -17,6 +17,21 @@ uint64_t Address::resolve(BEd &ctx) {
|
||||
line = ctx.active->vase.lines();
|
||||
} else if constexpr (std::is_same_v<T, Number>) {
|
||||
line = addr.i;
|
||||
} else if constexpr (std::is_same_v<T, Mark>) {
|
||||
line = ctx.active->marks.get(addr.m);
|
||||
if (line == UINT64_MAX)
|
||||
throw address_error("Mark not set.");
|
||||
} else if constexpr (std::is_same_v<T, Regex>) {
|
||||
std::string_view re = addr.re;
|
||||
if (re.size() == 0)
|
||||
re = ctx.last_regex;
|
||||
if (re.size() == 0)
|
||||
throw address_error("No regex given.");
|
||||
if (addr.dir == Direction::Forward)
|
||||
line = ctx.active->vase.find_next(re, ctx.active->line - 1) + 1;
|
||||
else
|
||||
line = ctx.active->vase.find_prev(re, ctx.active->line - 1) + 1;
|
||||
ctx.last_regex = re;
|
||||
} else if constexpr (std::is_same_v<T, Block>) {
|
||||
uint64_t current_line = ctx.active->line;
|
||||
if (ctx.active->vase.lines() > 0 && current_line == 0)
|
||||
|
||||
@@ -64,8 +64,6 @@ void Buffer::jump(uint64_t n_line) {
|
||||
if (n_line > vase.lines())
|
||||
throw ed_error("Line number too high.");
|
||||
line = n_line;
|
||||
prev_range.start = line;
|
||||
prev_range.end = line;
|
||||
}
|
||||
|
||||
void Buffer::append(std::string text, uint64_t line) {
|
||||
@@ -82,6 +80,9 @@ void Buffer::append(std::string text, uint64_t line) {
|
||||
prev_range.start = p.row + 1;
|
||||
vase.insert(&p, text);
|
||||
prev_range.end = p.row + 1;
|
||||
marks.insert(prev_range.start, prev_range.end);
|
||||
if (parser)
|
||||
parser->insert(vase, prev_range.start, prev_range.end);
|
||||
modified = true;
|
||||
}
|
||||
|
||||
@@ -89,6 +90,9 @@ void Buffer::remove(uint64_t start_line, uint64_t end_line) {
|
||||
vase.erase({{start_line - 1, 0}, {end_line, 0}});
|
||||
prev_range.start = start_line;
|
||||
prev_range.end = start_line;
|
||||
marks.erase(start_line, end_line - start_line + 1);
|
||||
if (parser)
|
||||
parser->erase(vase, start_line, end_line - start_line + 1);
|
||||
modified = true;
|
||||
}
|
||||
|
||||
@@ -96,6 +100,9 @@ void Buffer::join(uint64_t start_line, uint64_t end_line) {
|
||||
vase.regex_search_replace(R"(\n)", {{start_line - 1, 0}, {end_line, 0}}, "", "g");
|
||||
prev_range.start = start_line;
|
||||
prev_range.end = start_line;
|
||||
marks.collapse(start_line, end_line - start_line);
|
||||
if (parser)
|
||||
parser->erase(vase, start_line, end_line - start_line);
|
||||
modified = true;
|
||||
}
|
||||
|
||||
@@ -132,6 +139,8 @@ inline void reset(std::ostream &out) {
|
||||
}
|
||||
|
||||
void Buffer::print(BEd &ctx, uint64_t start_line, uint64_t end_line) {
|
||||
prev_range.start = start_line;
|
||||
prev_range.end = end_line;
|
||||
if (parser) {
|
||||
std::optional<syntax::Parser::Iterator> it_o = parser->get_hl(vase, start_line - 1);
|
||||
auto &it = *it_o;
|
||||
@@ -169,8 +178,52 @@ void Buffer::print(BEd &ctx, uint64_t start_line, uint64_t end_line) {
|
||||
while (it.next() && start_line++ <= end_line)
|
||||
std::cout << it.line << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void Buffer::number_print(BEd &ctx, uint64_t start_line, uint64_t end_line) {
|
||||
prev_range.start = start_line;
|
||||
prev_range.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(vase, start_line - 1);
|
||||
auto &it = *it_o;
|
||||
while (start_line <= end_line) {
|
||||
it.next();
|
||||
std::cout << std::setw(width) << it.at << "\t";
|
||||
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())
|
||||
break;
|
||||
if (end > line.size())
|
||||
break;
|
||||
if (cursor < start) {
|
||||
std::cout.write(
|
||||
line.data() + cursor,
|
||||
start - cursor
|
||||
);
|
||||
}
|
||||
const auto highlight = ctx.theme.get(token);
|
||||
apply(std::cout, highlight);
|
||||
std::cout.write(line.data() + start, end - start);
|
||||
reset(std::cout);
|
||||
cursor = end;
|
||||
}
|
||||
if (cursor < line.size())
|
||||
std::cout.write(line.data() + cursor, line.size() - cursor);
|
||||
std::cout << std::endl;
|
||||
++start_line;
|
||||
}
|
||||
} else {
|
||||
vase::Iterator it = vase.iterate(start_line - 1, Direction::Forward);
|
||||
while (it.next() && start_line <= end_line)
|
||||
std::cout << std::setw(width) << start_line++ << "\t" << it.line << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
std::string Buffer::list_string(std::string_view s) {
|
||||
|
||||
@@ -9,6 +9,15 @@ void Suffix::register_suffixes(BEd &ctx) {
|
||||
.handle = [](BEd &ctx) {
|
||||
auto line = ctx.active->line;
|
||||
ctx.active->print(ctx, line, line);
|
||||
ctx.active->jump(line);
|
||||
}
|
||||
};
|
||||
ctx.suffixes['n' - 'a'] = Suffix{
|
||||
.desc = "Prints current line with line number.",
|
||||
.handle = [](BEd &ctx) {
|
||||
auto line = ctx.active->line;
|
||||
ctx.active->number_print(ctx, line, line);
|
||||
ctx.active->jump(line);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -55,6 +64,26 @@ void Command::register_posix(BEd &ctx) {
|
||||
}
|
||||
}
|
||||
);
|
||||
ctx.commands.insert(
|
||||
"j",
|
||||
Command{
|
||||
.address_mode = Command::AddressMode::Range,
|
||||
.suffix = Command::SuffixKind::Suffix,
|
||||
.desc = "Join a set of lines (default: .,.+1)",
|
||||
.accept_zero = true,
|
||||
.handle = [](BEd &ctx, std::span<const uint64_t> addresses, std::string_view) {
|
||||
std::cout << addresses.size() << "\n";
|
||||
uint64_t start_line = ctx.active->line;
|
||||
uint64_t end_line = ctx.active->line + 1;
|
||||
if (addresses.size() == 1)
|
||||
return;
|
||||
else if (addresses.size() == 2)
|
||||
start_line = addresses[0], end_line = addresses[1];
|
||||
ctx.active->join(start_line, end_line);
|
||||
ctx.active->jump(start_line);
|
||||
}
|
||||
}
|
||||
);
|
||||
ctx.commands.insert(
|
||||
"q",
|
||||
Command{
|
||||
@@ -92,27 +121,67 @@ void Command::register_posix(BEd &ctx) {
|
||||
.handle = [](BEd &ctx, std::span<const uint64_t> addresses, std::string_view) {
|
||||
uint64_t start_line = ctx.active->line;
|
||||
uint64_t end_line = ctx.active->line;
|
||||
if (!addresses.size())
|
||||
ctx.active->print(ctx, start_line, end_line);
|
||||
else if (addresses.size() == 1)
|
||||
ctx.active->print(ctx, addresses[0], addresses[0]);
|
||||
else
|
||||
ctx.active->print(ctx, addresses[0], addresses[1]);
|
||||
if (addresses.size() == 1)
|
||||
start_line = addresses[0], end_line = addresses[0];
|
||||
else if (addresses.size() == 2)
|
||||
start_line = addresses[0], end_line = addresses[1];
|
||||
ctx.active->print(ctx, start_line, end_line);
|
||||
ctx.active->jump(end_line);
|
||||
}
|
||||
}
|
||||
);
|
||||
ctx.commands.insert(
|
||||
"n",
|
||||
Command{
|
||||
.address_mode = Command::AddressMode::Range,
|
||||
.suffix = Command::SuffixKind::Suffix,
|
||||
.desc = "Print range with line numbers (default .,.)",
|
||||
.accept_zero = false,
|
||||
.handle = [](BEd &ctx, std::span<const uint64_t> addresses, std::string_view) {
|
||||
uint64_t start_line = ctx.active->line;
|
||||
uint64_t end_line = ctx.active->line;
|
||||
if (addresses.size() == 1)
|
||||
start_line = addresses[0], end_line = addresses[0];
|
||||
else if (addresses.size() == 2)
|
||||
start_line = addresses[0], end_line = addresses[1];
|
||||
ctx.active->number_print(ctx, start_line, end_line);
|
||||
ctx.active->jump(end_line);
|
||||
}
|
||||
}
|
||||
);
|
||||
ctx.commands.insert(
|
||||
"=",
|
||||
Command{
|
||||
.address_mode = Command::AddressMode::Single,
|
||||
.address_mode = Command::AddressMode::Range,
|
||||
.suffix = Command::SuffixKind::Suffix,
|
||||
.desc = "Print line number",
|
||||
.desc = "Print line number(s)",
|
||||
.accept_zero = true,
|
||||
.handle = [](BEd &ctx, std::span<const uint64_t> addresses, std::string_view) {
|
||||
if (!addresses.size())
|
||||
std::cout << ctx.active->vase.lines() << std::endl;
|
||||
else
|
||||
else if (addresses.size() == 1)
|
||||
std::cout << addresses[0] << std::endl;
|
||||
else
|
||||
std::cout << addresses[0] << "," << addresses[1] << std::endl;
|
||||
}
|
||||
}
|
||||
);
|
||||
ctx.commands.insert(
|
||||
"k",
|
||||
Command{
|
||||
.address_mode = Command::AddressMode::Single,
|
||||
.suffix = Command::SuffixKind::Continuation,
|
||||
.desc = "Mark a line.",
|
||||
.accept_zero = true,
|
||||
.handle = [](BEd &ctx, std::span<const uint64_t> addresses, std::string_view args) {
|
||||
if (args.size() < 1 || args.size() > 2)
|
||||
throw ed_error("Malformed mark command");
|
||||
if (addresses.size())
|
||||
ctx.active->marks.set(args[0], addresses[0]);
|
||||
else
|
||||
ctx.active->marks.set(args[0], ctx.active->line);
|
||||
if (args.size() > 1)
|
||||
ctx.suffix_handle(args[1]);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
@@ -243,15 +243,12 @@ void Parser::modify(vase::Vase &vase, uint64_t target, uint64_t count) {
|
||||
events.clear();
|
||||
lang.parse(&state, it.line, at == 0, &tokens, &events);
|
||||
for (const auto &ev : events) {
|
||||
const auto t = uint8_t(ev.ev_type);
|
||||
if (t > ParseEvent::Closing)
|
||||
continue;
|
||||
if (c.leaf->n == c.leaf->cap) {
|
||||
uint32_t cap = c.leaf->cap ? c.leaf->cap * 2 : 8;
|
||||
c.leaf->blocks = (uint16_t *)realloc(c.leaf->blocks, cap * sizeof(uint16_t));
|
||||
c.leaf->cap = cap;
|
||||
}
|
||||
c.leaf->blocks[c.leaf->n++] = t << 15 | (at - chunk_start);
|
||||
c.leaf->blocks[c.leaf->n++] = ev.closing << 15 | (at - chunk_start);
|
||||
}
|
||||
at++;
|
||||
}
|
||||
|
||||
@@ -225,7 +225,7 @@ bool handle_line_markers(RubyParser &p, std::vector<Token> *tokens, std::vector<
|
||||
if (p.len() == 6 && p.peek_str(6) == "=begin") {
|
||||
p.current().state = RubyState::RubyInternalState::COMMENT;
|
||||
p.current().flags &= ~RubyState::RubyInternalState::EXPECTING_EXPRESSION;
|
||||
events->push_back(ParseEvent::Opening);
|
||||
events->push_back(false);
|
||||
tokens->push_back({0, p.len(), Token::Comment});
|
||||
return true;
|
||||
}
|
||||
@@ -266,7 +266,7 @@ bool handle_syntax(RubyParser &p, std::vector<Token> *tokens, std::vector<ParseE
|
||||
case RubyState::RubyInternalState::CLASS_NAME: {
|
||||
if (p.peek() == '<' && p.peek(1) == '<') {
|
||||
p.advance(2);
|
||||
events->push_back(ParseEvent::Opening);
|
||||
events->push_back(false);
|
||||
p.current().flags = (p.current().flags & ~RubyState::RubyInternalState::NAME_MASK);
|
||||
p.current().flags |= RubyState::RubyInternalState::EXPECTING_EXPRESSION;
|
||||
return false;
|
||||
@@ -285,7 +285,7 @@ bool handle_syntax(RubyParser &p, std::vector<Token> *tokens, std::vector<ParseE
|
||||
return false;
|
||||
}
|
||||
}
|
||||
events->push_back(ParseEvent::Opening);
|
||||
events->push_back(false);
|
||||
p.current().flags = (p.current().flags & ~RubyState::RubyInternalState::NAME_MASK);
|
||||
return false;
|
||||
}
|
||||
@@ -311,7 +311,7 @@ bool handle_syntax(RubyParser &p, std::vector<Token> *tokens, std::vector<ParseE
|
||||
return false;
|
||||
}
|
||||
}
|
||||
events->push_back(ParseEvent::Opening);
|
||||
events->push_back(false);
|
||||
p.current().flags = (p.current().flags & ~RubyState::RubyInternalState::NAME_MASK);
|
||||
return false;
|
||||
}
|
||||
@@ -330,7 +330,7 @@ bool handle_syntax(RubyParser &p, std::vector<Token> *tokens, std::vector<ParseE
|
||||
return false;
|
||||
}
|
||||
}
|
||||
events->push_back(ParseEvent::Opening);
|
||||
events->push_back(false);
|
||||
p.current().flags = (p.current().flags & ~RubyState::RubyInternalState::NAME_MASK);
|
||||
return false;
|
||||
}
|
||||
@@ -899,21 +899,21 @@ bool handle_syntax(RubyParser &p, std::vector<Token> *tokens, std::vector<ParseE
|
||||
p.advance(j);
|
||||
return false;
|
||||
} else if (j == tries.expecting_end_keywords_trie.longest_match(p.peek_str(j))) {
|
||||
events->push_back(ParseEvent::Opening);
|
||||
events->push_back(false);
|
||||
p.current().flags |= RubyState::RubyInternalState::EXPECTING_EXPRESSION;
|
||||
tokens->push_back({p.i, p.i + j, Token::Keyword});
|
||||
p.advance(j);
|
||||
return false;
|
||||
} else if (j == tries.conditional_keywords_trie.longest_match(p.peek_str(j))) {
|
||||
if (p.current().flags & RubyState::RubyInternalState::NEWLINE || p.op_last)
|
||||
events->push_back(ParseEvent::Opening);
|
||||
events->push_back(false);
|
||||
p.current().flags |= RubyState::RubyInternalState::EXPECTING_EXPRESSION;
|
||||
tokens->push_back({p.i, p.i + j, Token::Keyword});
|
||||
p.advance(j);
|
||||
return false;
|
||||
} else if (j == tries.end_keywords_trie.longest_match(p.peek_str(j))) {
|
||||
p.current().flags |= RubyState::RubyInternalState::EXPECTING_EXPRESSION;
|
||||
events->push_back(ParseEvent::Opening);
|
||||
events->push_back(false);
|
||||
tokens->push_back({p.i, p.i + j, Token::Keyword});
|
||||
p.advance(j);
|
||||
return false;
|
||||
@@ -950,7 +950,7 @@ bool handle_syntax(RubyParser &p, std::vector<Token> *tokens, std::vector<ParseE
|
||||
return false;
|
||||
}
|
||||
if (j == 3 && p.peek_str(3) == "end") {
|
||||
events->push_back(ParseEvent::Closing);
|
||||
events->push_back(true);
|
||||
tokens->push_back({p.i, p.i + j, Token::Keyword});
|
||||
p.advance(3);
|
||||
return false;
|
||||
@@ -980,17 +980,18 @@ bool handle_syntax(RubyParser &p, std::vector<Token> *tokens, std::vector<ParseE
|
||||
return false;
|
||||
}
|
||||
uint32_t start = p.i;
|
||||
p.advance(j);
|
||||
if (p.peek() == ':') {
|
||||
if (p.peek(j) == ':') {
|
||||
p.advance(j);
|
||||
p.advance();
|
||||
tokens->push_back({start, p.i, Token::Label});
|
||||
return false;
|
||||
} else if (p.peek(-1) == '!' || p.peek(-1) == '?') {
|
||||
p.advance();
|
||||
} else if (p.peek(j - 1) == '!' || p.peek(j - 1) == '?') {
|
||||
p.advance(j);
|
||||
tokens->push_back({start, p.i, Token::Function});
|
||||
return false;
|
||||
} else {
|
||||
uint32_t j = 0;
|
||||
p.advance(j);
|
||||
j = 0;
|
||||
if (p.peek(j) == '(' || p.peek(j) == '{') {
|
||||
tokens->push_back({start, p.i, Token::Function});
|
||||
return false;
|
||||
@@ -1066,7 +1067,7 @@ void ruby_parse(
|
||||
tokens->push_back({p.i, p.len(), Token::Comment});
|
||||
if (p.i == 0 && p.peek_str(4) == "=end") {
|
||||
p.current().state = RubyState::RubyInternalState::NONE;
|
||||
events->push_back(ParseEvent::Closing);
|
||||
events->push_back(true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -156,4 +156,122 @@ std::vector<Range> Vase::regex_search(
|
||||
result.push_back({point_of(match.start), point_of(match.end)});
|
||||
return result;
|
||||
}
|
||||
|
||||
uint64_t Vase::find_next(std::string_view pattern, uint64_t start) {
|
||||
std::vector<RegexMatch> results;
|
||||
int errornumber;
|
||||
PCRE2_SIZE erroroffset;
|
||||
pcre2_code *re = pcre2_compile(
|
||||
(PCRE2_SPTR)pattern.data(),
|
||||
pattern.size(),
|
||||
PCRE2_UTF | PCRE2_EXTENDED,
|
||||
&errornumber,
|
||||
&erroroffset,
|
||||
NULL
|
||||
);
|
||||
if (re == NULL)
|
||||
throw ed_error("Can't compile regex.");
|
||||
pcre2_match_data *match_data = pcre2_match_data_create_from_pattern(re, nullptr);
|
||||
if (!match_data) {
|
||||
pcre2_code_free(re);
|
||||
throw ed_error("Can't create regex match data.");
|
||||
}
|
||||
uint64_t at = (start + 1) % lines();
|
||||
LineIterator it(root, at, Direction::Forward);
|
||||
std::string line;
|
||||
while (it.next(&line)) {
|
||||
int rc = pcre2_match(re, (PCRE2_SPTR)line.data(), line.size(), 0, 0, match_data, nullptr);
|
||||
if (rc >= 0) {
|
||||
pcre2_match_data_free(match_data);
|
||||
pcre2_code_free(re);
|
||||
return at;
|
||||
}
|
||||
if (rc != PCRE2_ERROR_NOMATCH) {
|
||||
pcre2_match_data_free(match_data);
|
||||
pcre2_code_free(re);
|
||||
throw ed_error("Regex matching failed.");
|
||||
}
|
||||
at++;
|
||||
}
|
||||
at = 0;
|
||||
LineIterator it2(root, at, Direction::Forward);
|
||||
while (it2.next(&line)) {
|
||||
if (at >= start)
|
||||
break;
|
||||
int rc = pcre2_match(re, (PCRE2_SPTR)line.data(), line.size(), 0, 0, match_data, nullptr);
|
||||
if (rc >= 0) {
|
||||
pcre2_match_data_free(match_data);
|
||||
pcre2_code_free(re);
|
||||
return at;
|
||||
}
|
||||
if (rc != PCRE2_ERROR_NOMATCH) {
|
||||
pcre2_match_data_free(match_data);
|
||||
pcre2_code_free(re);
|
||||
throw ed_error("Regex matching failed.");
|
||||
}
|
||||
at++;
|
||||
}
|
||||
pcre2_match_data_free(match_data);
|
||||
pcre2_code_free(re);
|
||||
throw ed_error("No line matched.");
|
||||
}
|
||||
|
||||
uint64_t Vase::find_prev(std::string_view pattern, uint64_t start) {
|
||||
std::vector<RegexMatch> results;
|
||||
int errornumber;
|
||||
PCRE2_SIZE erroroffset;
|
||||
pcre2_code *re = pcre2_compile(
|
||||
(PCRE2_SPTR)pattern.data(),
|
||||
pattern.size(),
|
||||
PCRE2_UTF | PCRE2_EXTENDED,
|
||||
&errornumber,
|
||||
&erroroffset,
|
||||
NULL
|
||||
);
|
||||
if (re == NULL)
|
||||
throw ed_error("Can't compile regex.");
|
||||
pcre2_match_data *match_data = pcre2_match_data_create_from_pattern(re, nullptr);
|
||||
if (!match_data) {
|
||||
pcre2_code_free(re);
|
||||
throw ed_error("Can't create regex match data.");
|
||||
}
|
||||
uint64_t at = (start == 0 ? lines() : start) - 1;
|
||||
LineIterator it(root, at, Direction::Backward);
|
||||
std::string line;
|
||||
while (it.next(&line)) {
|
||||
int rc = pcre2_match(re, (PCRE2_SPTR)line.data(), line.size(), 0, 0, match_data, nullptr);
|
||||
if (rc >= 0) {
|
||||
pcre2_match_data_free(match_data);
|
||||
pcre2_code_free(re);
|
||||
return at;
|
||||
}
|
||||
if (rc != PCRE2_ERROR_NOMATCH) {
|
||||
pcre2_match_data_free(match_data);
|
||||
pcre2_code_free(re);
|
||||
throw ed_error("Regex matching failed.");
|
||||
}
|
||||
at--;
|
||||
}
|
||||
at = lines();
|
||||
LineIterator it2(root, at, Direction::Backward);
|
||||
while (it2.next(&line)) {
|
||||
if (at <= start)
|
||||
break;
|
||||
int rc = pcre2_match(re, (PCRE2_SPTR)line.data(), line.size(), 0, 0, match_data, nullptr);
|
||||
if (rc >= 0) {
|
||||
pcre2_match_data_free(match_data);
|
||||
pcre2_code_free(re);
|
||||
return at;
|
||||
}
|
||||
if (rc != PCRE2_ERROR_NOMATCH) {
|
||||
pcre2_match_data_free(match_data);
|
||||
pcre2_code_free(re);
|
||||
throw ed_error("Regex matching failed.");
|
||||
}
|
||||
at--;
|
||||
}
|
||||
pcre2_match_data_free(match_data);
|
||||
pcre2_code_free(re);
|
||||
throw ed_error("No line matched.");
|
||||
}
|
||||
} // namespace bed::internal::vase
|
||||
|
||||
Reference in New Issue
Block a user