diff --git a/Makefile b/Makefile index 141851b..eab5045 100644 --- a/Makefile +++ b/Makefile @@ -36,7 +36,7 @@ CFLAGS_RELEASE :=\ -fomit-frame-pointer -DNDEBUG -s \ -I./include -I./libs/unicode_width -CFLAGS_DEBUG += $(PCRE_CFLAGS) $(LIBGRAPHEME_CFLAGS) $(MRUBY_CFLAGS) $(C_SANITIZER) +CFLAGS_DEBUG += $(PCRE_CFLAGS) $(LIBGRAPHEME_CFLAGS) $(MRUBY_CFLAGS) CFLAGS_RELEASE += $(PCRE_CFLAGS) $(LIBGRAPHEME_CFLAGS) $(MRUBY_CFLAGS) UNICODE_SRC := $(wildcard libs/unicode_width/*.c) diff --git a/include/bed.h b/include/bed.h index e479d3c..49b54eb 100644 --- a/include/bed.h +++ b/include/bed.h @@ -6,6 +6,7 @@ #include "internal/functions/suffixes.h" #include "internal/io/io.h" #include "internal/marks/marks.h" +#include "internal/scripting/ruby.h" #include "internal/theme/theme.h" #include "internal/ui/command.h" #include "internal/ui/text_mode.h" @@ -21,6 +22,8 @@ struct BEd { std::unordered_map languages; internal::io::IO io; internal::vase::AppendStorage append{"/tmp"}; + mrb_state *mrb = nullptr; + int arena; bool help_mode = false; bool prompt_mode = true; diff --git a/include/internal/buffer/types/generic.h b/include/internal/buffer/types/generic.h index f8c2c38..9ad0f49 100644 --- a/include/internal/buffer/types/generic.h +++ b/include/internal/buffer/types/generic.h @@ -14,8 +14,8 @@ struct HistoryItem { struct GenericBuffer : ShardBuffer { uint64_t base_version{0}; - std::chrono::system_clock::time_point timestamp; - std::string action; + std::chrono::system_clock::time_point timestamp{std::chrono::system_clock::now()}; + std::string action{"Created buffer."}; std::filesystem::path save_path{}; std::vector undo_stack; std::vector redo_stack; @@ -24,6 +24,7 @@ struct GenericBuffer : ShardBuffer { : ShardBuffer(name, nullptr, nullptr, Kind::Generic) {} ~GenericBuffer(); + void language(BEd &ctx, std::string name); void list_history(BEd &ctx); ReadonlyBuffer *get_history(uint64_t version); void snapshot(std::string action); diff --git a/include/internal/scripting/ruby.h b/include/internal/scripting/ruby.h new file mode 100644 index 0000000..7625702 --- /dev/null +++ b/include/internal/scripting/ruby.h @@ -0,0 +1,43 @@ +#pragma once + +#include "definitions.h" +#include "pch.h" + +namespace bed::internal::scripting { +struct Block { + mrb_value proc; + mrb_state *mrb; + + Block(mrb_state *mrb, mrb_value proc) + : proc(proc), mrb(mrb) { + mrb_gc_register(mrb, proc); + } + + Block() : proc(mrb_nil_value()) {} + + ~Block() { + if (!mrb_nil_p(proc)) + mrb_gc_unregister(mrb, proc); + } + + void set_proc(mrb_value new_proc) { + if (!mrb_nil_p(proc)) + mrb_gc_unregister(mrb, proc); + proc = new_proc; + mrb_gc_register(mrb, proc); + } + + Block(const Block &) = delete; + Block &operator=(const Block &) = delete; + + mrb_value call(int argc = 0, mrb_value *argv = nullptr) const { + if (mrb_nil_p(proc)) + return mrb_nil_value(); + mrb_value result = mrb_funcall_argv(mrb, proc, mrb_intern_cstr(mrb, "call"), argc, argv); + return result; + } +}; + +void register_basic(BEd &ctx); +void run(BEd &ctx, const std::string &str); +}; // namespace bed::internal::scripting diff --git a/include/internal/syntax/decl.h b/include/internal/syntax/decl.h index 96a4d7a..4cd4f3f 100644 --- a/include/internal/syntax/decl.h +++ b/include/internal/syntax/decl.h @@ -48,6 +48,7 @@ struct ParseState { uint64_t line, uint64_t original, uint64_t final ); static ParseState *concat(Language &lang, ParseState *a, ParseState *b); + static void *state_before(Language &lang, ParseState *root, vase::Shard *vase, uint64_t line); }; struct ParseStateBranch : ParseState { @@ -83,18 +84,25 @@ struct ParsePieceBuilder { std::vector pieces; std::vector blocks; void *piece_state{nullptr}; + void *prev_state{nullptr}; uint64_t chunk_start{0}; uint64_t chunk_lines{0}; - ParsePieceBuilder(Language &lang, uint64_t first_line) - : lang(lang), chunk_start(first_line) {} - void add( - void *state, - uint64_t line, - const std::vector &events - ) { + ParsePieceBuilder(Language &lang, uint64_t first_line, void *entry_state) + : lang(lang), chunk_start(first_line) { + prev_state = lang.copy(entry_state); + } + ~ParsePieceBuilder() { + if (prev_state) + lang.destroy(prev_state); + if (piece_state) + lang.destroy(piece_state); + } + ParsePieceBuilder(const ParsePieceBuilder &) = delete; + ParsePieceBuilder &operator=(const ParsePieceBuilder &) = delete; + void add(void *state, uint64_t line, const std::vector &events) { if (chunk_lines == 0) { chunk_start = line; - piece_state = lang.copy(state); + piece_state = lang.copy(prev_state); } for (const auto &ev : events) { blocks.push_back( @@ -103,6 +111,9 @@ struct ParsePieceBuilder { ); } ++chunk_lines; + if (prev_state) + lang.destroy(prev_state); + prev_state = lang.copy(state); if (chunk_lines == ParseStateLeaf::MAX_CHUNK) flush(); } diff --git a/include/internal/syntax/parser.h b/include/internal/syntax/parser.h index 7875397..c5e6006 100644 --- a/include/internal/syntax/parser.h +++ b/include/internal/syntax/parser.h @@ -13,6 +13,7 @@ ParserSnapshot make_parser(vase::Shard *vase, uint64_t lines, Language *lang); ParserSnapshot retain(const ParserSnapshot &snap); void release(ParserSnapshot &snap); +void *state_before(const ParserSnapshot &snap, vase::Shard *vase, uint64_t line); uint64_t next_closing(const ParserSnapshot &snap, uint64_t line); uint64_t prev_opening(const ParserSnapshot &snap, uint64_t line); diff --git a/include/pch.h b/include/pch.h index 4c15ebf..133b88b 100644 --- a/include/pch.h +++ b/include/pch.h @@ -4,10 +4,29 @@ #include #include +#include +#include +#include #include +#include +#include +#include +#include #include +#include #include +#include +#include +#include +#include +#include +#include +#include #include +#include +#include +#include +#include #include extern "C" { #include diff --git a/src/bed/bed.cc b/src/bed/bed.cc index b247d01..37f660e 100644 --- a/src/bed/bed.cc +++ b/src/bed/bed.cc @@ -1,5 +1,4 @@ #include "bed.h" -#include "internal/parser/parser.h" namespace bed { BEd::BEd(std::vector args) @@ -43,9 +42,15 @@ BEd::BEd(std::vector args) && strcmp(colorterm, "24bit") != 0) color = false; color_mode = color; + mrb = mrb_open(); + if (!mrb) + throw fatal_error("Failed to initialize mruby.", 1); + mrb->ud = this; + arena = mrb_gc_arena_save(mrb); internal::functions::Function::register_posix(*this); internal::functions::Function::register_extented(*this); internal::functions::Suffix::register_suffixes(*this); + internal::scripting::register_basic(*this); languages["ruby"] = new internal::syntax::Language(internal::syntax::ruby::lang_ruby()); buffers["clip"] = new internal::buffer::ClipBuffer("clip"); current() = {"default", 0}; @@ -65,6 +70,12 @@ BEd::~BEd() { delete buffer; for (auto &[_, lang] : languages) delete lang; + mrb->ud = nullptr; + if (mrb) { + mrb_gc_arena_restore(mrb, arena); + mrb_full_gc(mrb); + mrb_close(mrb); + } } void BEd::print_help() { diff --git a/src/bed/handle.cc b/src/bed/handle.cc index d4652c3..72bde77 100644 --- a/src/bed/handle.cc +++ b/src/bed/handle.cc @@ -2,23 +2,6 @@ #include "internal/parser/parser.h" namespace bed { -void BEd::run() { - while (true) { - internal::ui::CommandIO command(*this); - auto [cmd, eof] = command.run(); - try { - handle(cmd, eof); - } catch (ed_error &e) { - io.apply(internal::io::Token::Warning); - io.write_line("?"); - if (help_mode) - io.write_line(e.what()); - io.reset(); - last_help = e.what(); - } - } -} - void BEd::handle(std::string_view cmd, bool eof) { if (eof && cmd.empty()) { eof_op.handle(*this, "", nullptr, std::monostate(), nullptr); diff --git a/src/bed/run.cc b/src/bed/run.cc index b423962..c229747 100644 --- a/src/bed/run.cc +++ b/src/bed/run.cc @@ -8,7 +8,7 @@ void BEd::run() { auto [cmd, eof] = command.run(); try { handle(cmd, eof); - } catch (ed_error &e) { + } catch (const ed_error &e) { io.apply(internal::io::Token::Warning); io.write_line("?"); if (help_mode) diff --git a/src/internal/buffer/generic.cc b/src/internal/buffer/generic.cc index 5d37fc3..37644e6 100644 --- a/src/internal/buffer/generic.cc +++ b/src/internal/buffer/generic.cc @@ -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) { diff --git a/src/internal/functions/extended.cc b/src/internal/functions/extended.cc index e76ba9d..25d4025 100644 --- a/src/internal/functions/extended.cc +++ b/src/internal/functions/extended.cc @@ -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 * + ) { + auto &addr = std::get(addr_); + auto &arg = std::get(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 * ) { - // TODO: connect up to mruby. + auto &addr = std::get(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 { + return {nullptr, ctx.languages["ruby"], nullptr}; + }, + .handle = []( + BEd &ctx, + const buffer::Address &addr_, + vase::Shard *code, + const Argument &, + std::vector * + ) { + auto &addr = std::get(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 * + ) { + auto &addr = std::get(addr_); + auto name = std::get(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 diff --git a/src/internal/functions/posix.cc b/src/internal/functions/posix.cc index c606e2a..d30fc9b 100644 --- a/src/internal/functions/posix.cc +++ b/src/internal/functions/posix.cc @@ -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 { + const auto &addr = std::get(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(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 { + auto addr = std::get(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( diff --git a/src/internal/scripting/ruby.cc b/src/internal/scripting/ruby.cc new file mode 100644 index 0000000..cce35bc --- /dev/null +++ b/src/internal/scripting/ruby.cc @@ -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 diff --git a/src/internal/syntax/iterator.cc b/src/internal/syntax/iterator.cc index 296e2aa..5bc6778 100644 --- a/src/internal/syntax/iterator.cc +++ b/src/internal/syntax/iterator.cc @@ -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(); diff --git a/src/internal/syntax/miniparser.cc b/src/internal/syntax/miniparser.cc index 954499a..5913d0c 100644 --- a/src/internal/syntax/miniparser.cc +++ b/src/internal/syntax/miniparser.cc @@ -8,7 +8,7 @@ MiniParser::MiniParser(Language &lang, vase::Shard *vase, void *initial_state) void *state = lang.copy(start_state); std::vector tokens; std::vector 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(); diff --git a/src/internal/syntax/parser.cc b/src/internal/syntax/parser.cc index 43caea3..4704917 100644 --- a/src/internal/syntax/parser.cc +++ b/src/internal/syntax/parser.cc @@ -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; diff --git a/src/internal/syntax/parser_state.cc b/src/internal/syntax/parser_state.cc index cd50bc1..a56fff2 100644 --- a/src/internal/syntax/parser_state.cc +++ b/src/internal/syntax/parser_state.cc @@ -108,7 +108,7 @@ ParseState *ParseState::splice( void *state = lang.none_state(); std::vector tokens; std::vector 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 tokens; + std::vector 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 diff --git a/src/internal/syntax/ruby/parse.cc b/src/internal/syntax/ruby/parse.cc index 0694353..b1f3cc9 100644 --- a/src/internal/syntax/ruby/parse.cc +++ b/src/internal/syntax/ruby/parse.cc @@ -782,11 +782,6 @@ bool handle_syntax(RubyParser &p, std::vector *tokens, std::vector *tokens, std::vector 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 *tokens, std::vector') + || 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 *tokens, std::vector *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) diff --git a/src/internal/ui/text_mode/text_mode.cc b/src/internal/ui/text_mode/text_mode.cc index f93be6a..cf3bfeb 100644 --- a/src/internal/ui/text_mode/text_mode.cc +++ b/src/internal/ui/text_mode/text_mode.cc @@ -206,11 +206,13 @@ std::pair 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 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 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 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; }