Add basic scripting support.
- Fix certain bugs in ruby parser - And a lot more cleanup/minor fixes.
This commit is contained in:
@@ -203,7 +203,23 @@ uint64_t GenericBuffer::prune(int keep) {
|
||||
bool GenericBuffer::waste() {
|
||||
return save_path.empty()
|
||||
&& root == nullptr
|
||||
&& undo_stack.empty();
|
||||
&& undo_stack.empty()
|
||||
&& parse.lang == nullptr;
|
||||
}
|
||||
|
||||
void GenericBuffer::language(BEd &ctx, std::string name) {
|
||||
syntax::Language *lang = nullptr;
|
||||
if (name.size()) {
|
||||
auto it = ctx.languages.find(name);
|
||||
if (it == ctx.languages.end())
|
||||
throw ed_error("Language not found.");
|
||||
lang = it->second;
|
||||
}
|
||||
if (lang == parse.lang)
|
||||
return;
|
||||
snapshot("Change buffer language.");
|
||||
syntax::release(parse);
|
||||
parse = syntax::make_parser(root, lines(), lang);
|
||||
}
|
||||
|
||||
void GenericBuffer::load(BEd &ctx, vase::Shard *text) {
|
||||
@@ -225,7 +241,7 @@ void GenericBuffer::load(BEd &ctx, vase::Shard *text) {
|
||||
}
|
||||
ctx.current() = {name, lines()};
|
||||
syntax::release(parse);
|
||||
parse = syntax::make_parser(root, lines(), ctx.languages["ruby"]);
|
||||
parse = syntax::make_parser(root, lines(), nullptr);
|
||||
}
|
||||
|
||||
void GenericBuffer::set_filename(std::filesystem::path path) {
|
||||
|
||||
@@ -173,24 +173,95 @@ void Function::register_extented(BEd &ctx) {
|
||||
ctx.functions.insert(
|
||||
"`",
|
||||
Function{
|
||||
.address_kind = Function::AddressKind::None,
|
||||
.address_kind = Function::AddressKind::Range,
|
||||
.argument_kind = Function::ArgumentKind::Ruby,
|
||||
.input_mode = Function::InputMode::None,
|
||||
.desc = "Execute some ruby code",
|
||||
.default_address = "",
|
||||
.accept_zero = true,
|
||||
.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<RubyArg>(arg_);
|
||||
auto pre_code = "$START=" + std::to_string(addr.start)
|
||||
+ ";$END=" + std::to_string(addr.end)
|
||||
+ ";$BUFNAME=\"" + addr.buffername + "\"";
|
||||
scripting::run(ctx, pre_code);
|
||||
scripting::run(ctx, arg.cmd);
|
||||
},
|
||||
}
|
||||
);
|
||||
ctx.functions.insert(
|
||||
"``",
|
||||
Function{
|
||||
.address_kind = Function::AddressKind::Range,
|
||||
.argument_kind = Function::ArgumentKind::None,
|
||||
.input_mode = Function::InputMode::None,
|
||||
.desc = "Execute addressed lines as ruby code.",
|
||||
.default_address = ".,.",
|
||||
.accept_zero = false,
|
||||
.pre_text_mode = nullptr,
|
||||
.handle = [](
|
||||
BEd &,
|
||||
const buffer::Address &,
|
||||
BEd &ctx,
|
||||
const buffer::Address &addr_,
|
||||
vase::Shard *,
|
||||
const Argument &,
|
||||
std::vector<buffer::Line> *
|
||||
) {
|
||||
// TODO: connect up to mruby.
|
||||
auto &addr = std::get<buffer::Range>(addr_);
|
||||
auto &buf = ctx.buffer(addr.buffername);
|
||||
auto code = buf.copy(addr.start, addr.end);
|
||||
ctx.prev().buffername = addr.buffername;
|
||||
ctx.prev().start = addr.start;
|
||||
ctx.prev().end = addr.end;
|
||||
ctx.current() = {addr.buffername, addr.end};
|
||||
auto pre_code = "$START=" + std::to_string(addr.start)
|
||||
+ ";$END=" + std::to_string(addr.end)
|
||||
+ ";$BUFNAME=\"" + addr.buffername + "\"";
|
||||
scripting::run(ctx, pre_code);
|
||||
scripting::run(ctx, vase::to_string(code));
|
||||
vase::Shard::release(code);
|
||||
},
|
||||
}
|
||||
);
|
||||
ctx.functions.insert(
|
||||
"```",
|
||||
Function{
|
||||
.address_kind = Function::AddressKind::Range,
|
||||
.argument_kind = Function::ArgumentKind::None,
|
||||
.input_mode = Function::InputMode::Text,
|
||||
.desc = "Execute some ruby code (taken from text mode)",
|
||||
.default_address = "",
|
||||
.accept_zero = true,
|
||||
.pre_text_mode = [](
|
||||
BEd &ctx,
|
||||
const buffer::Address &,
|
||||
const Argument &
|
||||
) -> std::tuple<vase::Shard *, syntax::Language *, void *> {
|
||||
return {nullptr, ctx.languages["ruby"], nullptr};
|
||||
},
|
||||
.handle = [](
|
||||
BEd &ctx,
|
||||
const buffer::Address &addr_,
|
||||
vase::Shard *code,
|
||||
const Argument &,
|
||||
std::vector<buffer::Line> *
|
||||
) {
|
||||
auto &addr = std::get<buffer::Range>(addr_);
|
||||
auto pre_code = "$START=" + std::to_string(addr.start)
|
||||
+ ";$END=" + std::to_string(addr.end)
|
||||
+ ";$BUFNAME=\"" + addr.buffername + "\"";
|
||||
scripting::run(ctx, pre_code);
|
||||
scripting::run(ctx, vase::to_string(code));
|
||||
vase::Shard::release(code); },
|
||||
}
|
||||
);
|
||||
ctx.functions.insert(
|
||||
"echo",
|
||||
Function{
|
||||
@@ -432,5 +503,38 @@ void Function::register_extented(BEd &ctx) {
|
||||
}
|
||||
}
|
||||
);
|
||||
ctx.functions.insert(
|
||||
"lang",
|
||||
Function{
|
||||
.address_kind = Function::AddressKind::None,
|
||||
.argument_kind = Function::ArgumentKind::Any,
|
||||
.input_mode = Function::InputMode::None,
|
||||
.desc = "Set buffer language",
|
||||
.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<std::string>(addr_);
|
||||
auto name = std::get<std::string>(arg_);
|
||||
const auto first = name.find_first_not_of(" \t");
|
||||
const auto last = name.find_last_not_of(" \t");
|
||||
if (first == std::string::npos)
|
||||
name.clear();
|
||||
else
|
||||
name = name.substr(first, last - first + 1);
|
||||
auto &buf_ = ctx.buffer(addr);
|
||||
if (buf_.kind != buffer::Buffer::Kind::Generic)
|
||||
throw ed_error("Can't set language to buffer.");
|
||||
auto &buf = *(buffer::GenericBuffer *)&buf_;
|
||||
buf.language(ctx, name);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
} // namespace bed::internal::functions
|
||||
|
||||
@@ -37,7 +37,19 @@ void Function::register_posix(BEd &ctx) {
|
||||
.desc = "Append text to a line",
|
||||
.default_address = ".",
|
||||
.accept_zero = true,
|
||||
.pre_text_mode = nullptr,
|
||||
.pre_text_mode = [](
|
||||
BEd &ctx,
|
||||
const buffer::Address &addr_,
|
||||
const Argument &
|
||||
) -> std::tuple<vase::Shard *, syntax::Language *, void *> {
|
||||
const auto &addr = std::get<buffer::Line>(addr_);
|
||||
auto &buf_ = ctx.buffer(addr.buffername);
|
||||
if (buf_.kind != buffer::Buffer::Kind::Generic)
|
||||
return {nullptr, nullptr, nullptr};
|
||||
auto &buf = *(buffer::GenericBuffer *)&buf_;
|
||||
void *state = syntax::state_before(buf.parse, buf.root, addr.number);
|
||||
return {nullptr, buf.parse.lang, state};
|
||||
},
|
||||
.handle = [](
|
||||
BEd &ctx,
|
||||
const buffer::Address &addr_,
|
||||
@@ -47,8 +59,7 @@ void Function::register_posix(BEd &ctx) {
|
||||
) {
|
||||
auto addr = std::get<buffer::Line>(addr_);
|
||||
ctx.buffer(addr.buffername).append(ctx, text, addr.number);
|
||||
vase::Shard::release(text);
|
||||
}
|
||||
vase::Shard::release(text); }
|
||||
}
|
||||
);
|
||||
ctx.functions.insert(
|
||||
@@ -70,9 +81,7 @@ void Function::register_posix(BEd &ctx) {
|
||||
if (buf_.kind != buffer::Buffer::Kind::Generic)
|
||||
return {buf_.copy(addr.start, addr.end), nullptr, nullptr};
|
||||
auto &buf = *(buffer::GenericBuffer *)&buf_;
|
||||
if (!buf.parse.lang)
|
||||
return {buf.copy(addr.start, addr.end), nullptr, nullptr};
|
||||
void *state = nullptr; // TODO: buf.parse.root.get_at(addr.start);
|
||||
void *state = syntax::state_before(buf.parse, buf.root, addr.start - 1);
|
||||
return {buf.copy(addr.start, addr.end), buf.parse.lang, state};
|
||||
},
|
||||
.handle = [](
|
||||
@@ -291,7 +300,21 @@ void Function::register_posix(BEd &ctx) {
|
||||
.desc = "Insert text before a line",
|
||||
.default_address = ".",
|
||||
.accept_zero = true,
|
||||
.pre_text_mode = nullptr,
|
||||
.pre_text_mode = [](
|
||||
BEd &ctx,
|
||||
const buffer::Address &addr_,
|
||||
const Argument &
|
||||
) -> std::tuple<vase::Shard *, syntax::Language *, void *> {
|
||||
auto addr = std::get<buffer::Line>(addr_);
|
||||
if (addr.number)
|
||||
addr.number--;
|
||||
auto &buf_ = ctx.buffer(addr.buffername);
|
||||
if (buf_.kind != buffer::Buffer::Kind::Generic)
|
||||
return {nullptr, nullptr, nullptr};
|
||||
auto &buf = *(buffer::GenericBuffer *)&buf_;
|
||||
void *state = syntax::state_before(buf.parse, buf.root, addr.number);
|
||||
return {nullptr, buf.parse.lang, state};
|
||||
},
|
||||
.handle = [](
|
||||
BEd &ctx,
|
||||
const buffer::Address &addr_,
|
||||
@@ -303,8 +326,7 @@ void Function::register_posix(BEd &ctx) {
|
||||
if (addr.number)
|
||||
addr.number--;
|
||||
ctx.buffer(addr.buffername).append(ctx, text, addr.number);
|
||||
vase::Shard::release(text);
|
||||
}
|
||||
vase::Shard::release(text); }
|
||||
}
|
||||
);
|
||||
ctx.functions.insert(
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
#include "internal/scripting/ruby.h"
|
||||
#include "bed.h"
|
||||
|
||||
namespace bed::internal::scripting {
|
||||
static mrb_value mrb_bed_exit(mrb_state *mrb, mrb_value) {
|
||||
mrb_raise(mrb, E_RUNTIME_ERROR, "Use `handle(\"q\")` to quit.");
|
||||
return mrb_nil_value();
|
||||
}
|
||||
|
||||
static void
|
||||
raise_fatal(mrb_state *mrb, const fatal_error &e) {
|
||||
struct RClass *klass = mrb_class_get(mrb, "FatalError");
|
||||
mrb_value exc = mrb_exc_new_str(mrb, klass, mrb_str_new_cstr(mrb, e.what()));
|
||||
mrb_iv_set(mrb, exc, mrb_intern_lit(mrb, "@code"), mrb_fixnum_value(e.code));
|
||||
mrb_exc_raise(mrb, exc);
|
||||
}
|
||||
|
||||
static mrb_value mrb_bed_handle(mrb_state *mrb, mrb_value) {
|
||||
auto &ctx = *(BEd *)mrb->ud;
|
||||
const char *command;
|
||||
mrb_int len;
|
||||
mrb_get_args(mrb, "s", &command, &len);
|
||||
std::string_view cmd(command, len);
|
||||
try {
|
||||
ctx.handle(cmd, false);
|
||||
} catch (const ed_error &e) {
|
||||
mrb_raise(mrb, mrb_class_get(mrb, "EdError"), e.what());
|
||||
} catch (const fatal_error &f) {
|
||||
raise_fatal(mrb, f);
|
||||
} catch (...) {
|
||||
mrb_raise(mrb, E_RUNTIME_ERROR, "Unexpected error.");
|
||||
}
|
||||
return mrb_nil_value();
|
||||
}
|
||||
|
||||
void register_basic(BEd &ctx) {
|
||||
auto *bed_error =
|
||||
mrb_define_class(ctx.mrb, "EdError", mrb_exc_get_id(ctx.mrb, MRB_ERROR_SYM(RuntimeError)));
|
||||
mrb_define_class(ctx.mrb, "FatalError", bed_error);
|
||||
|
||||
mrb_define_method(ctx.mrb, ctx.mrb->kernel_module, "exit", mrb_bed_exit, MRB_ARGS_NONE());
|
||||
mrb_define_method(ctx.mrb, ctx.mrb->kernel_module, "handle", mrb_bed_handle, MRB_ARGS_REQ(1));
|
||||
}
|
||||
|
||||
void run(BEd &ctx, const std::string &str) {
|
||||
mrb_state *mrb = ctx.mrb;
|
||||
mrb_load_nstring(mrb, str.data(), str.size());
|
||||
if (!mrb->exc)
|
||||
return;
|
||||
mrb_value exc = mrb_obj_value(mrb->exc);
|
||||
mrb_value msg = mrb_funcall(mrb, exc, "message", 0);
|
||||
std::string error;
|
||||
if (mrb_string_p(msg))
|
||||
error.assign(RSTRING_PTR(msg), RSTRING_LEN(msg));
|
||||
auto *fatal_class = mrb_class_get(mrb, "FatalError");
|
||||
if (mrb_obj_is_kind_of(mrb, exc, fatal_class)) {
|
||||
mrb_value code =
|
||||
mrb_iv_get(mrb, exc, mrb_intern_lit(mrb, "@code"));
|
||||
mrb->exc = nullptr;
|
||||
int c = 1;
|
||||
if (mrb_fixnum_p(code))
|
||||
c = mrb_fixnum(code);
|
||||
throw fatal_error(error, c);
|
||||
}
|
||||
auto *ed_class = mrb_class_get(mrb, "EdError");
|
||||
if (mrb_obj_is_kind_of(mrb, exc, ed_class)) {
|
||||
mrb->exc = nullptr;
|
||||
throw ed_error(error);
|
||||
}
|
||||
mrb->exc = nullptr;
|
||||
throw ed_error("Ruby Exception: " + error);
|
||||
}
|
||||
} // namespace bed::internal::scripting
|
||||
@@ -2,19 +2,11 @@
|
||||
|
||||
namespace bed::internal::syntax {
|
||||
Iterator::Iterator(uint64_t target, ParserSnapshot p, vase::Shard *vase)
|
||||
: snap(p) {
|
||||
uint64_t offset;
|
||||
TreeCursor c = TreeCursor(*p.lang, p.root, target, &offset);
|
||||
at = target - offset;
|
||||
state = p.lang->copy(c.leaf->state);
|
||||
: snap(p), at(target) {
|
||||
at = target;
|
||||
if (snap.lang)
|
||||
state = ParseState::state_before(*snap.lang, snap.root, vase, at);
|
||||
it = vase::Iterator(vase, at, Direction::Forward);
|
||||
while (at < target) {
|
||||
it->next();
|
||||
tokens.clear();
|
||||
events.clear();
|
||||
p.lang->parse(&state, it->line, at == 0, &tokens, &events);
|
||||
at++;
|
||||
}
|
||||
}
|
||||
|
||||
Iterator::~Iterator() {
|
||||
@@ -46,6 +38,8 @@ Iterator &Iterator::operator=(Iterator &&other) {
|
||||
}
|
||||
|
||||
void Iterator::next() {
|
||||
if (!state)
|
||||
return;
|
||||
it->next();
|
||||
tokens.clear();
|
||||
events.clear();
|
||||
|
||||
@@ -8,7 +8,7 @@ MiniParser::MiniParser(Language &lang, vase::Shard *vase, void *initial_state)
|
||||
void *state = lang.copy(start_state);
|
||||
std::vector<io::Token> tokens;
|
||||
std::vector<ParseEvent> events;
|
||||
const uint64_t count = vase ? vase->lines + 1 : 0;
|
||||
const uint64_t count = vase ? vase->lines + 1 : 1;
|
||||
lines.reserve(count);
|
||||
for (uint64_t i = 0; i < count; ++i) {
|
||||
it.next();
|
||||
|
||||
@@ -22,6 +22,12 @@ void release(ParserSnapshot &snap) {
|
||||
snap.root = nullptr;
|
||||
}
|
||||
|
||||
void *state_before(const ParserSnapshot &snap, vase::Shard *vase, uint64_t line) {
|
||||
if (!snap.lang)
|
||||
return nullptr;
|
||||
return ParseState::state_before(*snap.lang, snap.root, vase, line);
|
||||
}
|
||||
|
||||
uint64_t next_closing(const ParserSnapshot &snap, uint64_t line) {
|
||||
if (!snap.root || !snap.lang)
|
||||
return line + 10;
|
||||
|
||||
@@ -108,7 +108,7 @@ ParseState *ParseState::splice(
|
||||
void *state = lang.none_state();
|
||||
std::vector<io::Token> tokens;
|
||||
std::vector<ParseEvent> events;
|
||||
ParsePieceBuilder builder(lang, 0);
|
||||
ParsePieceBuilder builder(lang, 0, state);
|
||||
for (uint64_t at = 0; at < final; ++at) {
|
||||
it.next();
|
||||
tokens.clear();
|
||||
@@ -136,7 +136,7 @@ ParseState *ParseState::splice(
|
||||
uint64_t end_extra;
|
||||
TreeCursor c = TreeCursor(lang, root, end_in_tree, &end_extra);
|
||||
uint64_t end_in_vase = line + final;
|
||||
ParsePieceBuilder builder(lang, at);
|
||||
ParsePieceBuilder builder(lang, at, state);
|
||||
while (at < end_in_vase + end_extra) {
|
||||
it.next();
|
||||
tokens.clear();
|
||||
@@ -192,4 +192,28 @@ ParseState *ParseState::concat(Language &lang, ParseState *a, ParseState *b) {
|
||||
}
|
||||
return balance(lang, new ParseStateBranch(a, b));
|
||||
}
|
||||
|
||||
void *ParseState::state_before(Language &lang, ParseState *root, vase::Shard *vase, uint64_t line) {
|
||||
uint64_t offset;
|
||||
uint64_t at;
|
||||
void *state;
|
||||
if (!root) {
|
||||
at = 0;
|
||||
state = lang.none_state();
|
||||
} else {
|
||||
TreeCursor c = TreeCursor(lang, root, line, &offset);
|
||||
at = line - offset;
|
||||
state = lang.copy(c.leaf->state);
|
||||
}
|
||||
vase::Iterator it(vase, at, Direction::Forward);
|
||||
std::vector<io::Token> tokens;
|
||||
std::vector<ParseEvent> events;
|
||||
for (; at < line; ++at) {
|
||||
it.next();
|
||||
tokens.clear();
|
||||
events.clear();
|
||||
lang.parse(&state, it.line, at == 0, &tokens, &events);
|
||||
}
|
||||
return state;
|
||||
}
|
||||
} // namespace bed::internal::syntax
|
||||
|
||||
@@ -782,11 +782,6 @@ bool handle_syntax(RubyParser &p, std::vector<io::Token> *tokens, std::vector<Pa
|
||||
p.ending = false;
|
||||
p.advance();
|
||||
return false;
|
||||
case '\0':
|
||||
if (p.ending)
|
||||
p.current().flags |= RubyState::RubyInternalState::EXPECTING_EXPRESSION;
|
||||
p.advance();
|
||||
return false;
|
||||
default:
|
||||
if ('0' <= p.peek() && p.peek() <= '9') {
|
||||
p.current().flags &= ~RubyState::RubyInternalState::EXPECTING_EXPRESSION;
|
||||
@@ -981,6 +976,11 @@ bool handle_syntax(RubyParser &p, std::vector<io::Token> *tokens, std::vector<Pa
|
||||
| RubyState::RubyInternalState::DEF_NAME;
|
||||
return false;
|
||||
}
|
||||
if (j > 3 && p.peek_str(3) == "to_") {
|
||||
p.current().flags &= ~RubyState::RubyInternalState::EXPECTING_EXPRESSION;
|
||||
tokens->push_back({p.i, p.i + j, io::Token::Function});
|
||||
p.advance(j);
|
||||
}
|
||||
uint32_t start = p.i;
|
||||
if (p.peek(j) == ':') {
|
||||
p.advance(j);
|
||||
@@ -1008,8 +1008,9 @@ bool handle_syntax(RubyParser &p, std::vector<io::Token> *tokens, std::vector<Pa
|
||||
return false;
|
||||
if (p.peek(j) == '&'
|
||||
|| p.peek(j) == '%'
|
||||
|| p.peek(j) == ':') {
|
||||
if (p.peek(j + 1) == ' ' || p.peek(j + 1) == '>')
|
||||
|| p.peek(j) == ':'
|
||||
|| p.peek(j) == '?') {
|
||||
if (p.peek(j + 1) == ' ' || p.peek(j + 1) == '\0')
|
||||
return false;
|
||||
} else if (p.peek(j) == '-') {
|
||||
if (p.peek(j + 1) != '>')
|
||||
@@ -1025,7 +1026,6 @@ bool handle_syntax(RubyParser &p, std::vector<io::Token> *tokens, std::vector<Pa
|
||||
|| p.peek(j) == '*'
|
||||
|| p.peek(j) == '/'
|
||||
|| p.peek(j) == '='
|
||||
|| p.peek(j) == '?'
|
||||
|| p.peek(j) == '|'
|
||||
|| p.peek(j) == '^'
|
||||
|| p.peek(j) == '<'
|
||||
@@ -1061,7 +1061,7 @@ void ruby_parse(
|
||||
std::vector<ParseEvent> *events
|
||||
) {
|
||||
RubyParser p(v_state, line);
|
||||
while (p.i <= p.len()) {
|
||||
while (p.i < p.len()) {
|
||||
p.op_last = p.set_op_last;
|
||||
p.set_op_last = false;
|
||||
if (p.current().state == RubyState::RubyInternalState::END)
|
||||
@@ -1099,6 +1099,13 @@ void ruby_parse(
|
||||
return;
|
||||
if (!handle_syntax(p, tokens, events))
|
||||
p.current().flags &= ~RubyState::RubyInternalState::NEWLINE;
|
||||
if (p.peek() == '\0') {
|
||||
if (p.ending)
|
||||
p.current().flags |= RubyState::RubyInternalState::EXPECTING_EXPRESSION;
|
||||
p.advance();
|
||||
p.current().flags &= ~RubyState::RubyInternalState::NEWLINE;
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (p.ending)
|
||||
|
||||
@@ -206,11 +206,13 @@ std::pair<vase::Shard *, bool> TextMode::run_terminal() {
|
||||
}
|
||||
vase::Range r = {last, cursor};
|
||||
vase = vase::erase(vase, r);
|
||||
if (cursor.row == last.row) {
|
||||
parser->dirty(vase, cursor.row, 1);
|
||||
} else {
|
||||
parser->erase(vase, cursor.row, 1);
|
||||
parser->dirty(vase, last.row, 1);
|
||||
if (parser) {
|
||||
if (cursor.row == last.row) {
|
||||
parser->dirty(vase, cursor.row, 1);
|
||||
} else {
|
||||
parser->erase(vase, cursor.row, 1);
|
||||
parser->dirty(vase, last.row, 1);
|
||||
}
|
||||
}
|
||||
cursor = last;
|
||||
}
|
||||
@@ -224,10 +226,12 @@ std::pair<vase::Shard *, bool> TextMode::run_terminal() {
|
||||
}
|
||||
}
|
||||
vase = vase::insert(&bed.append, vase, &cursor, '\n');
|
||||
parser->insert(vase, cursor.row - 1, 1);
|
||||
if (parser)
|
||||
parser->insert(vase, cursor.row - 1, 1);
|
||||
} else {
|
||||
vase = vase::insert(&bed.append, vase, &cursor, res.text.data(), res.text.size());
|
||||
parser->dirty(vase, cursor.row, 1);
|
||||
if (parser)
|
||||
parser->dirty(vase, cursor.row, 1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -235,11 +239,13 @@ std::pair<vase::Shard *, bool> TextMode::run_terminal() {
|
||||
case io::KeyEvent::KeyType::PASTE: {
|
||||
auto lines = std::count(res.text.begin(), res.text.end(), '\n');
|
||||
vase = vase::insert(&bed.append, vase, &cursor, res.text.data(), res.text.size());
|
||||
if (lines) {
|
||||
parser->insert(vase, cursor.row - lines, lines);
|
||||
parser->dirty(vase, cursor.row - lines, 1);
|
||||
} else {
|
||||
parser->dirty(vase, cursor.row, 1);
|
||||
if (parser) {
|
||||
if (lines) {
|
||||
parser->insert(vase, cursor.row - lines, lines);
|
||||
parser->dirty(vase, cursor.row - lines, 1);
|
||||
} else {
|
||||
parser->dirty(vase, cursor.row, 1);
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case io::KeyEvent::KeyType::SPECIAL:
|
||||
@@ -304,9 +310,11 @@ std::pair<vase::Shard *, bool> TextMode::run_terminal() {
|
||||
}
|
||||
vase::Range r = {cursor, next};
|
||||
vase = vase::erase(vase, r);
|
||||
if (cursor.row != next.row)
|
||||
parser->erase(vase, cursor.row + 1, 1);
|
||||
parser->dirty(vase, cursor.row, 1);
|
||||
if (parser) {
|
||||
if (cursor.row != next.row)
|
||||
parser->erase(vase, cursor.row + 1, 1);
|
||||
parser->dirty(vase, cursor.row, 1);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user