Add marks and regex addressing systems
- Hook up parser to buffer edits. - Other minor fixes.
This commit is contained in:
@@ -31,5 +31,6 @@ struct BEd {
|
|||||||
~BEd();
|
~BEd();
|
||||||
void handle(std::string_view cmd, bool eof);
|
void handle(std::string_view cmd, bool eof);
|
||||||
void run();
|
void run();
|
||||||
|
void suffix_handle(char s);
|
||||||
};
|
};
|
||||||
} // namespace bed
|
} // namespace bed
|
||||||
|
|||||||
@@ -41,17 +41,11 @@ struct Address {
|
|||||||
char m;
|
char m;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct RegexLine { // /re/ or ?re?
|
struct Regex { // /re/ or ?re?
|
||||||
internal::Direction dir;
|
internal::Direction dir;
|
||||||
std::string re;
|
std::string re;
|
||||||
};
|
};
|
||||||
|
|
||||||
// BEd Extended types of addressing.
|
|
||||||
struct Regex { // &/re/ or &?re?
|
|
||||||
internal::Direction dir; // returns next/previous line containing the regex
|
|
||||||
std::string re; // (not only if it matches full line)
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Diagnostic { // #n# for diagnostic number n. (From lsp.)
|
struct Diagnostic { // #n# for diagnostic number n. (From lsp.)
|
||||||
uint16_t n;
|
uint16_t n;
|
||||||
};
|
};
|
||||||
@@ -91,7 +85,6 @@ struct Address {
|
|||||||
Last,
|
Last,
|
||||||
Number,
|
Number,
|
||||||
Mark,
|
Mark,
|
||||||
RegexLine,
|
|
||||||
Regex,
|
Regex,
|
||||||
Diagnostic,
|
Diagnostic,
|
||||||
DiagnosticNext,
|
DiagnosticNext,
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ struct Buffer {
|
|||||||
void remove(uint64_t start_line, uint64_t end_line);
|
void remove(uint64_t start_line, uint64_t end_line);
|
||||||
void append(std::string text, uint64_t line);
|
void append(std::string text, uint64_t line);
|
||||||
void print(BEd &ctx, uint64_t start_line, uint64_t end_line);
|
void print(BEd &ctx, uint64_t start_line, uint64_t end_line);
|
||||||
|
void number_print(BEd &ctx, uint64_t start_line, uint64_t end_line);
|
||||||
std::string list_string(std::string_view s);
|
std::string list_string(std::string_view s);
|
||||||
};
|
};
|
||||||
} // namespace bed::internal::buffer
|
} // namespace bed::internal::buffer
|
||||||
|
|||||||
@@ -3,5 +3,55 @@
|
|||||||
#include "pch.h"
|
#include "pch.h"
|
||||||
|
|
||||||
namespace bed::internal::marks {
|
namespace bed::internal::marks {
|
||||||
struct MarksEngine {};
|
struct MarksEngine {
|
||||||
|
uint64_t marks[1 << UINT8_WIDTH]{UINT64_MAX};
|
||||||
|
|
||||||
|
MarksEngine() {
|
||||||
|
for (auto &m : marks)
|
||||||
|
m = UINT64_MAX;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t get(uint8_t m) {
|
||||||
|
return marks[m];
|
||||||
|
}
|
||||||
|
|
||||||
|
void set(uint8_t m, uint64_t line) {
|
||||||
|
marks[m] = line;
|
||||||
|
}
|
||||||
|
|
||||||
|
void insert(uint64_t start, uint64_t count) {
|
||||||
|
for (uint16_t i = 0; i <= UINT8_MAX; ++i) {
|
||||||
|
if (marks[i] == UINT64_MAX)
|
||||||
|
continue;
|
||||||
|
if (marks[i] >= start)
|
||||||
|
marks[i] += count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void erase(uint64_t start, uint64_t count) {
|
||||||
|
for (uint16_t i = 0; i <= UINT8_MAX; ++i) {
|
||||||
|
if (marks[i] == UINT64_MAX)
|
||||||
|
continue;
|
||||||
|
if (marks[i] >= start) {
|
||||||
|
if (marks[i] < start + count)
|
||||||
|
marks[i] = UINT64_MAX;
|
||||||
|
else
|
||||||
|
marks[i] -= count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void collapse(uint64_t start, uint64_t count) {
|
||||||
|
for (uint16_t i = 0; i <= UINT8_MAX; ++i) {
|
||||||
|
if (marks[i] == UINT64_MAX)
|
||||||
|
continue;
|
||||||
|
if (marks[i] >= start) {
|
||||||
|
if (marks[i] < start + count)
|
||||||
|
marks[i] = start;
|
||||||
|
else
|
||||||
|
marks[i] -= count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
} // namespace bed::internal::marks
|
} // namespace bed::internal::marks
|
||||||
|
|||||||
@@ -69,17 +69,8 @@ struct Token {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct ParseEvent {
|
struct ParseEvent {
|
||||||
std::string_view name;
|
uint8_t closing;
|
||||||
enum T : uint8_t {
|
ParseEvent(uint8_t t) : closing(t) {}
|
||||||
Opening,
|
|
||||||
Closing,
|
|
||||||
SymbolDef,
|
|
||||||
Symbol
|
|
||||||
} ev_type;
|
|
||||||
uint8_t type; // type.
|
|
||||||
|
|
||||||
ParseEvent(T t) : ev_type(t) {}
|
|
||||||
ParseEvent(T e_t, std::string_view s, uint8_t type) : name(s), ev_type(e_t), type(type) {}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Language {
|
struct Language {
|
||||||
@@ -90,16 +81,6 @@ struct Language {
|
|||||||
std::function<void(void *)> destroy;
|
std::function<void(void *)> destroy;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Symbol {
|
|
||||||
uint32_t definition;
|
|
||||||
uint16_t reference_count;
|
|
||||||
uint8_t type;
|
|
||||||
uint8_t len;
|
|
||||||
// first chars of count len, padded to 4 bytes.
|
|
||||||
// then a set of references. 32bit
|
|
||||||
// references later as modifying at end is faster than shifting the name.
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ParseState {
|
struct ParseState {
|
||||||
static constexpr uint64_t BRANCH_BIT = 1ull << 63;
|
static constexpr uint64_t BRANCH_BIT = 1ull << 63;
|
||||||
static constexpr uint64_t LINES_MASK = ~BRANCH_BIT;
|
static constexpr uint64_t LINES_MASK = ~BRANCH_BIT;
|
||||||
|
|||||||
@@ -5,15 +5,6 @@
|
|||||||
#include "tries.h"
|
#include "tries.h"
|
||||||
|
|
||||||
namespace bed::internal::syntax::ruby {
|
namespace bed::internal::syntax::ruby {
|
||||||
enum struct ScopeTypes : uint8_t {
|
|
||||||
None,
|
|
||||||
Comment,
|
|
||||||
Method,
|
|
||||||
Class,
|
|
||||||
Module,
|
|
||||||
Block
|
|
||||||
};
|
|
||||||
|
|
||||||
struct alignas(2) RubyState {
|
struct alignas(2) RubyState {
|
||||||
struct RubyInternalState {
|
struct RubyInternalState {
|
||||||
uint16_t brace_level;
|
uint16_t brace_level;
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#include "buffer/append.h"
|
#include "buffer/append.h"
|
||||||
#include "buffer/original.h"
|
#include "buffer/original.h"
|
||||||
#include "constants.h"
|
#include "constants.h"
|
||||||
|
#include "definitions.h"
|
||||||
#include "iterators/line.h"
|
#include "iterators/line.h"
|
||||||
#include "pch.h"
|
#include "pch.h"
|
||||||
#include "shard.h"
|
#include "shard.h"
|
||||||
@@ -92,6 +93,9 @@ struct Vase {
|
|||||||
std::string_view pattern, Range range, std::string_view options
|
std::string_view pattern, Range range, std::string_view options
|
||||||
);
|
);
|
||||||
|
|
||||||
|
uint64_t find_next(std::string_view pattern, uint64_t start);
|
||||||
|
uint64_t find_prev(std::string_view pattern, uint64_t start);
|
||||||
|
|
||||||
bool undo();
|
bool undo();
|
||||||
bool redo();
|
bool redo();
|
||||||
void snapshot();
|
void snapshot();
|
||||||
|
|||||||
@@ -94,4 +94,9 @@ void BEd::handle(std::string_view cmd_, bool eof) {
|
|||||||
suffix->handle(*this);
|
suffix->handle(*this);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BEd::suffix_handle(char s) {
|
||||||
|
if (suffixes[s - 'a'].has_value())
|
||||||
|
suffixes[s - 'a'].value().handle(*this);
|
||||||
|
}
|
||||||
} // namespace bed
|
} // namespace bed
|
||||||
|
|||||||
@@ -29,11 +29,12 @@ Address::Address(std::string &cmd, uint64_t &i) {
|
|||||||
break;
|
break;
|
||||||
case '\'': {
|
case '\'': {
|
||||||
i++;
|
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++;
|
i++;
|
||||||
else
|
else
|
||||||
throw address_error("Invalid mark.");
|
throw address_error("Invalid mark.");
|
||||||
base = Mark(cmd[i]);
|
base = Mark(cmd[i - 1]);
|
||||||
} break;
|
} break;
|
||||||
case '/': {
|
case '/': {
|
||||||
i++;
|
i++;
|
||||||
@@ -54,7 +55,9 @@ Address::Address(std::string &cmd, uint64_t &i) {
|
|||||||
else
|
else
|
||||||
i++;
|
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;
|
} break;
|
||||||
case '?': {
|
case '?': {
|
||||||
i++;
|
i++;
|
||||||
@@ -75,7 +78,9 @@ Address::Address(std::string &cmd, uint64_t &i) {
|
|||||||
else
|
else
|
||||||
i++;
|
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;
|
} break;
|
||||||
case '+': {
|
case '+': {
|
||||||
base = Current();
|
base = Current();
|
||||||
|
|||||||
@@ -17,6 +17,21 @@ uint64_t Address::resolve(BEd &ctx) {
|
|||||||
line = ctx.active->vase.lines();
|
line = ctx.active->vase.lines();
|
||||||
} else if constexpr (std::is_same_v<T, Number>) {
|
} else if constexpr (std::is_same_v<T, Number>) {
|
||||||
line = addr.i;
|
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>) {
|
} else if constexpr (std::is_same_v<T, Block>) {
|
||||||
uint64_t current_line = ctx.active->line;
|
uint64_t current_line = ctx.active->line;
|
||||||
if (ctx.active->vase.lines() > 0 && current_line == 0)
|
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())
|
if (n_line > vase.lines())
|
||||||
throw ed_error("Line number too high.");
|
throw ed_error("Line number too high.");
|
||||||
line = n_line;
|
line = n_line;
|
||||||
prev_range.start = line;
|
|
||||||
prev_range.end = line;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Buffer::append(std::string text, uint64_t 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;
|
prev_range.start = p.row + 1;
|
||||||
vase.insert(&p, text);
|
vase.insert(&p, text);
|
||||||
prev_range.end = p.row + 1;
|
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;
|
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}});
|
vase.erase({{start_line - 1, 0}, {end_line, 0}});
|
||||||
prev_range.start = start_line;
|
prev_range.start = start_line;
|
||||||
prev_range.end = 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;
|
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");
|
vase.regex_search_replace(R"(\n)", {{start_line - 1, 0}, {end_line, 0}}, "", "g");
|
||||||
prev_range.start = start_line;
|
prev_range.start = start_line;
|
||||||
prev_range.end = 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;
|
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) {
|
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) {
|
if (parser) {
|
||||||
std::optional<syntax::Parser::Iterator> it_o = parser->get_hl(vase, start_line - 1);
|
std::optional<syntax::Parser::Iterator> it_o = parser->get_hl(vase, start_line - 1);
|
||||||
auto &it = *it_o;
|
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)
|
while (it.next() && start_line++ <= end_line)
|
||||||
std::cout << it.line << std::endl;
|
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.start = start_line;
|
||||||
prev_range.end = end_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) {
|
std::string Buffer::list_string(std::string_view s) {
|
||||||
|
|||||||
@@ -9,6 +9,15 @@ void Suffix::register_suffixes(BEd &ctx) {
|
|||||||
.handle = [](BEd &ctx) {
|
.handle = [](BEd &ctx) {
|
||||||
auto line = ctx.active->line;
|
auto line = ctx.active->line;
|
||||||
ctx.active->print(ctx, line, 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(
|
ctx.commands.insert(
|
||||||
"q",
|
"q",
|
||||||
Command{
|
Command{
|
||||||
@@ -92,27 +121,67 @@ void Command::register_posix(BEd &ctx) {
|
|||||||
.handle = [](BEd &ctx, std::span<const uint64_t> addresses, std::string_view) {
|
.handle = [](BEd &ctx, std::span<const uint64_t> addresses, std::string_view) {
|
||||||
uint64_t start_line = ctx.active->line;
|
uint64_t start_line = ctx.active->line;
|
||||||
uint64_t end_line = ctx.active->line;
|
uint64_t end_line = ctx.active->line;
|
||||||
if (!addresses.size())
|
if (addresses.size() == 1)
|
||||||
ctx.active->print(ctx, start_line, end_line);
|
start_line = addresses[0], end_line = addresses[0];
|
||||||
else if (addresses.size() == 1)
|
else if (addresses.size() == 2)
|
||||||
ctx.active->print(ctx, addresses[0], addresses[0]);
|
start_line = addresses[0], end_line = addresses[1];
|
||||||
else
|
ctx.active->print(ctx, start_line, end_line);
|
||||||
ctx.active->print(ctx, addresses[0], addresses[1]);
|
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(
|
ctx.commands.insert(
|
||||||
"=",
|
"=",
|
||||||
Command{
|
Command{
|
||||||
.address_mode = Command::AddressMode::Single,
|
.address_mode = Command::AddressMode::Range,
|
||||||
.suffix = Command::SuffixKind::Suffix,
|
.suffix = Command::SuffixKind::Suffix,
|
||||||
.desc = "Print line number",
|
.desc = "Print line number(s)",
|
||||||
.accept_zero = true,
|
.accept_zero = true,
|
||||||
.handle = [](BEd &ctx, std::span<const uint64_t> addresses, std::string_view) {
|
.handle = [](BEd &ctx, std::span<const uint64_t> addresses, std::string_view) {
|
||||||
if (!addresses.size())
|
if (!addresses.size())
|
||||||
std::cout << ctx.active->vase.lines() << std::endl;
|
std::cout << ctx.active->vase.lines() << std::endl;
|
||||||
else
|
else if (addresses.size() == 1)
|
||||||
std::cout << addresses[0] << std::endl;
|
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();
|
events.clear();
|
||||||
lang.parse(&state, it.line, at == 0, &tokens, &events);
|
lang.parse(&state, it.line, at == 0, &tokens, &events);
|
||||||
for (const auto &ev : 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) {
|
if (c.leaf->n == c.leaf->cap) {
|
||||||
uint32_t cap = c.leaf->cap ? c.leaf->cap * 2 : 8;
|
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->blocks = (uint16_t *)realloc(c.leaf->blocks, cap * sizeof(uint16_t));
|
||||||
c.leaf->cap = cap;
|
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++;
|
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") {
|
if (p.len() == 6 && p.peek_str(6) == "=begin") {
|
||||||
p.current().state = RubyState::RubyInternalState::COMMENT;
|
p.current().state = RubyState::RubyInternalState::COMMENT;
|
||||||
p.current().flags &= ~RubyState::RubyInternalState::EXPECTING_EXPRESSION;
|
p.current().flags &= ~RubyState::RubyInternalState::EXPECTING_EXPRESSION;
|
||||||
events->push_back(ParseEvent::Opening);
|
events->push_back(false);
|
||||||
tokens->push_back({0, p.len(), Token::Comment});
|
tokens->push_back({0, p.len(), Token::Comment});
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -266,7 +266,7 @@ bool handle_syntax(RubyParser &p, std::vector<Token> *tokens, std::vector<ParseE
|
|||||||
case RubyState::RubyInternalState::CLASS_NAME: {
|
case RubyState::RubyInternalState::CLASS_NAME: {
|
||||||
if (p.peek() == '<' && p.peek(1) == '<') {
|
if (p.peek() == '<' && p.peek(1) == '<') {
|
||||||
p.advance(2);
|
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 = (p.current().flags & ~RubyState::RubyInternalState::NAME_MASK);
|
||||||
p.current().flags |= RubyState::RubyInternalState::EXPECTING_EXPRESSION;
|
p.current().flags |= RubyState::RubyInternalState::EXPECTING_EXPRESSION;
|
||||||
return false;
|
return false;
|
||||||
@@ -285,7 +285,7 @@ bool handle_syntax(RubyParser &p, std::vector<Token> *tokens, std::vector<ParseE
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
events->push_back(ParseEvent::Opening);
|
events->push_back(false);
|
||||||
p.current().flags = (p.current().flags & ~RubyState::RubyInternalState::NAME_MASK);
|
p.current().flags = (p.current().flags & ~RubyState::RubyInternalState::NAME_MASK);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -311,7 +311,7 @@ bool handle_syntax(RubyParser &p, std::vector<Token> *tokens, std::vector<ParseE
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
events->push_back(ParseEvent::Opening);
|
events->push_back(false);
|
||||||
p.current().flags = (p.current().flags & ~RubyState::RubyInternalState::NAME_MASK);
|
p.current().flags = (p.current().flags & ~RubyState::RubyInternalState::NAME_MASK);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -330,7 +330,7 @@ bool handle_syntax(RubyParser &p, std::vector<Token> *tokens, std::vector<ParseE
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
events->push_back(ParseEvent::Opening);
|
events->push_back(false);
|
||||||
p.current().flags = (p.current().flags & ~RubyState::RubyInternalState::NAME_MASK);
|
p.current().flags = (p.current().flags & ~RubyState::RubyInternalState::NAME_MASK);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -899,21 +899,21 @@ bool handle_syntax(RubyParser &p, std::vector<Token> *tokens, std::vector<ParseE
|
|||||||
p.advance(j);
|
p.advance(j);
|
||||||
return false;
|
return false;
|
||||||
} else if (j == tries.expecting_end_keywords_trie.longest_match(p.peek_str(j))) {
|
} 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;
|
p.current().flags |= RubyState::RubyInternalState::EXPECTING_EXPRESSION;
|
||||||
tokens->push_back({p.i, p.i + j, Token::Keyword});
|
tokens->push_back({p.i, p.i + j, Token::Keyword});
|
||||||
p.advance(j);
|
p.advance(j);
|
||||||
return false;
|
return false;
|
||||||
} else if (j == tries.conditional_keywords_trie.longest_match(p.peek_str(j))) {
|
} else if (j == tries.conditional_keywords_trie.longest_match(p.peek_str(j))) {
|
||||||
if (p.current().flags & RubyState::RubyInternalState::NEWLINE || p.op_last)
|
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;
|
p.current().flags |= RubyState::RubyInternalState::EXPECTING_EXPRESSION;
|
||||||
tokens->push_back({p.i, p.i + j, Token::Keyword});
|
tokens->push_back({p.i, p.i + j, Token::Keyword});
|
||||||
p.advance(j);
|
p.advance(j);
|
||||||
return false;
|
return false;
|
||||||
} else if (j == tries.end_keywords_trie.longest_match(p.peek_str(j))) {
|
} else if (j == tries.end_keywords_trie.longest_match(p.peek_str(j))) {
|
||||||
p.current().flags |= RubyState::RubyInternalState::EXPECTING_EXPRESSION;
|
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});
|
tokens->push_back({p.i, p.i + j, Token::Keyword});
|
||||||
p.advance(j);
|
p.advance(j);
|
||||||
return false;
|
return false;
|
||||||
@@ -950,7 +950,7 @@ bool handle_syntax(RubyParser &p, std::vector<Token> *tokens, std::vector<ParseE
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (j == 3 && p.peek_str(3) == "end") {
|
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});
|
tokens->push_back({p.i, p.i + j, Token::Keyword});
|
||||||
p.advance(3);
|
p.advance(3);
|
||||||
return false;
|
return false;
|
||||||
@@ -980,17 +980,18 @@ bool handle_syntax(RubyParser &p, std::vector<Token> *tokens, std::vector<ParseE
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
uint32_t start = p.i;
|
uint32_t start = p.i;
|
||||||
p.advance(j);
|
if (p.peek(j) == ':') {
|
||||||
if (p.peek() == ':') {
|
p.advance(j);
|
||||||
p.advance();
|
p.advance();
|
||||||
tokens->push_back({start, p.i, Token::Label});
|
tokens->push_back({start, p.i, Token::Label});
|
||||||
return false;
|
return false;
|
||||||
} else if (p.peek(-1) == '!' || p.peek(-1) == '?') {
|
} else if (p.peek(j - 1) == '!' || p.peek(j - 1) == '?') {
|
||||||
p.advance();
|
p.advance(j);
|
||||||
tokens->push_back({start, p.i, Token::Function});
|
tokens->push_back({start, p.i, Token::Function});
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
uint32_t j = 0;
|
p.advance(j);
|
||||||
|
j = 0;
|
||||||
if (p.peek(j) == '(' || p.peek(j) == '{') {
|
if (p.peek(j) == '(' || p.peek(j) == '{') {
|
||||||
tokens->push_back({start, p.i, Token::Function});
|
tokens->push_back({start, p.i, Token::Function});
|
||||||
return false;
|
return false;
|
||||||
@@ -1066,7 +1067,7 @@ void ruby_parse(
|
|||||||
tokens->push_back({p.i, p.len(), Token::Comment});
|
tokens->push_back({p.i, p.len(), Token::Comment});
|
||||||
if (p.i == 0 && p.peek_str(4) == "=end") {
|
if (p.i == 0 && p.peek_str(4) == "=end") {
|
||||||
p.current().state = RubyState::RubyInternalState::NONE;
|
p.current().state = RubyState::RubyInternalState::NONE;
|
||||||
events->push_back(ParseEvent::Closing);
|
events->push_back(true);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -156,4 +156,122 @@ std::vector<Range> Vase::regex_search(
|
|||||||
result.push_back({point_of(match.start), point_of(match.end)});
|
result.push_back({point_of(match.start), point_of(match.end)});
|
||||||
return result;
|
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
|
} // namespace bed::internal::vase
|
||||||
|
|||||||
Reference in New Issue
Block a user