Compare commits
7
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8f67be87ee
|
||
|
|
88785436cf
|
||
|
|
d246559292
|
||
|
|
3346bc9f83
|
||
|
|
a8d51dc0b6
|
||
|
|
118934a393
|
||
|
|
f5ec5516fa
|
+149
@@ -0,0 +1,149 @@
|
|||||||
|
# Base.
|
||||||
|
|
||||||
|
### `PCH`
|
||||||
|
|
||||||
|
Brings in all the external and standard headers. <br/>
|
||||||
|
Apart from c++std stuff we have:
|
||||||
|
- `mruby` and almost all it's subheaders.
|
||||||
|
- `pcre2` (For regex.)
|
||||||
|
- `grapheme.h` - The suckless libgrapheme (to find utf8 boundaries)
|
||||||
|
- `unicode_width.h` - To find terminal width of unicode char's.
|
||||||
|
|
||||||
|
*Maybe try removing some of them ive probably stopped using.*
|
||||||
|
|
||||||
|
### `definitions.h`
|
||||||
|
|
||||||
|
- Defines the `bed` namespace
|
||||||
|
- Forward declaration of `BEd` type needed by all.
|
||||||
|
- `fatal_error` definition, a msg, code (quits application on being thrown)
|
||||||
|
- `ed_error`. just a message (stops the current cycle of the ed repl.)
|
||||||
|
|
||||||
|
### `bed.h`
|
||||||
|
|
||||||
|
**As this is based heavily on the internals, and so ill look into it at the end.**
|
||||||
|
|
||||||
|
# `Internal`
|
||||||
|
|
||||||
|
## `Vase`
|
||||||
|
|
||||||
|
- This module doesn't depend on any other.
|
||||||
|
- It's namespace is `bed::internal::vase`
|
||||||
|
- Vase is the module to do with the actual storage and handling of a disk backed immutable and reference counted avl piece tree.
|
||||||
|
- It is a O(log n) per edit text datatype.
|
||||||
|
|
||||||
|
### `constants.h`
|
||||||
|
|
||||||
|
Defines `PETAL_SIZE_MAX` as `32 * 1024`.
|
||||||
|
- `PETAL_SIZE_MAX` limits teh size of a petal which makes multiple O(n) in petal searches O(1) as it is now bounded.
|
||||||
|
|
||||||
|
### `storage/`
|
||||||
|
|
||||||
|
A storage is something that stores the actual text, the text is actually stored in storages which are referenced by the trees.
|
||||||
|
|
||||||
|
#### `storage.h`
|
||||||
|
|
||||||
|
Defines the virtual `Storage` class.
|
||||||
|
It has the methods:
|
||||||
|
- `const char *read(uint64_t pos)`: reads text from position `pos` and returns a pointer to it.
|
||||||
|
- `uint64_t length()`: returns the length of the storage.
|
||||||
|
- `void retain() / release()`: Methods to help with refcounted storages.
|
||||||
|
|
||||||
|
#### `original.h`
|
||||||
|
|
||||||
|
Defines the `Original` type of storage.
|
||||||
|
This is a refcounted storage.
|
||||||
|
|
||||||
|
It has the fields:
|
||||||
|
- `atomic_uint64_t refs{0}`: refcount
|
||||||
|
- `const char *buf`: pointer to the actual text.
|
||||||
|
- `uint64_t len`
|
||||||
|
- `int fd = -1`: the file descriptor of teh undelying disk storage.
|
||||||
|
|
||||||
|
It adds the method `initialize` over the base class.
|
||||||
|
|
||||||
|
`src/vase/storage/original.cc`.
|
||||||
|
|
||||||
|
The constructor uses `mkstemp` to make a temp file, then unlinks it to hide it from the fs but keeps the fd alive.
|
||||||
|
|
||||||
|
`initialize` then `mmap`s the fd into a non modifyable pointer managed by the kernel then closes the fd itself.
|
||||||
|
- This is what gives us a pointer to the file contents that the kernel can load and evict from memory as needed, and therefore if we have say a 500MB file loaded, but the cursor is arount 200MB and the user is only editing a couple lines around that, the rest of the file will not be using your memory, it might be loaded but if needed the kernel can evict it.
|
||||||
|
|
||||||
|
The call site fills the fd before calling initialze, it cannot be modified after that.
|
||||||
|
|
||||||
|
#### `append.h`
|
||||||
|
|
||||||
|
Defines the `Append` type of storage.
|
||||||
|
This is not actually refcounted, it is to be owned by the application just once and reused all throughout.
|
||||||
|
It contains the actively typed stuff.
|
||||||
|
|
||||||
|
It has the fields:
|
||||||
|
- `const char *buf`: pointer to the actual text.
|
||||||
|
- `uint64_t allocated_capacity`: the amount of bytees that can be written to it without needing to increase the size.
|
||||||
|
- `uint64_t current_size`: Stores the size of actual text stored in it. (the cursor)
|
||||||
|
- `int fd = -1`: the file descriptor of the underlying disk storage.
|
||||||
|
|
||||||
|
It has the methods `append` (with 2 overloads) and private `grow` over the base class.
|
||||||
|
`retain` and `release` do nothing.
|
||||||
|
|
||||||
|
`src/vase/storage/append.cc`.
|
||||||
|
|
||||||
|
The constructor uses `mkstemp` an `unlink` similar to the original storage.
|
||||||
|
It then `ftruncate`s the file to `2^30` bytes or 1GiB this makes writing 1gb to the file possible but does'nt neccasarily use up 1gb on the disk.
|
||||||
|
But when `mmap`ing we set `PROT_READ | PROT_WRITE` and `MAP_SHARED` to be able to write edits to disk.
|
||||||
|
|
||||||
|
`grow` works by doubling the capacity `ftruncat`ing the fd to the new capacity and then on `linux` it uses `mremap` to remap to the new file size, but otherwise we `munmap` and `mmap` into the new size.
|
||||||
|
|
||||||
|
The append methods append text into the buffer (either a single char or char * + len), they may grow the storage size. (the char*+len version uses memcpy).
|
||||||
|
|
||||||
|
### `shard.h`
|
||||||
|
|
||||||
|
Defines the actual peice tree.
|
||||||
|
|
||||||
|
The tree is a polymorhic set of classes.
|
||||||
|
|
||||||
|
The base class `Shard` has:
|
||||||
|
- `enum Kind : uint8_t kind`: Branch or Petal.
|
||||||
|
- `uint16_t height`: the tree height (for avl balancing.)
|
||||||
|
- `atomic_uint32_t refs`: the refcount.
|
||||||
|
- `uint64_t length`: the length in bytes of this (sub)tree.
|
||||||
|
- `uint64_t lines`: the number of `\n` in this subtree.
|
||||||
|
- It starts refcount at 1.
|
||||||
|
|
||||||
|
The `Branch` class adds:
|
||||||
|
- `Shard *left/right` the subtrees.
|
||||||
|
- its constructor takes l, r and sets length/lines `(l->length + r->length)`
|
||||||
|
- and height `1 + std::max(l->height, r->height)`
|
||||||
|
|
||||||
|
The `Petal` (leaf) class adds:
|
||||||
|
- `Storage *` a pointer to the storage used.
|
||||||
|
- `uint64_t pos` the position in the storage its text starts at.
|
||||||
|
- it takes `(uint64_t length, uint64_t lines, Storage *source, uint64_t pos)`
|
||||||
|
- it retains `source`. (usefull for refcounted sotrages.)
|
||||||
|
|
||||||
|
A `nullptr` `Shard*` is an empty peice of text (i.e. valid).
|
||||||
|
|
||||||
|
`src/internal/vase/shard.cc`: defines a set of static function on the Shard class. (All the applications of it.)
|
||||||
|
|
||||||
|
the operations methods are:
|
||||||
|
- `retain/release` retain/releases them, if freeing a leaf it also releases the storage and for branches it releases both subtrees. nullptr's are ignored.
|
||||||
|
- `balance/rotate_(right/left) and height/balance factor` are for doing simple avl balancing operations.
|
||||||
|
- `concat` borrows 2 pointers to `Shard` and returns an owned Shard*.
|
||||||
|
- `split` borrows a `Shard` and returns a `pair<Shard*, Shard*>` of 2 owned shards.
|
||||||
|
- `merge_leaves` works similar to concat but it tries to merge the middle bit if they have their physical representation in order. for example if i have a shard `hell` and i type the letter `o` and because i'd been typing in order the Append storage will have text `hello` we can merge them into a single petal.
|
||||||
|
- `append` similar to concat, but uses merge_leaves, useful for small appends like when typing.
|
||||||
|
- `build` takes a pointer to an array of `Shard*` and start and end then makes a single avl balanced `Shard*` out of it it is generally faster than calling concat on each peice when we have many.
|
||||||
|
|
||||||
|
Then for building a Shard* in the first place we have:
|
||||||
|
- `from_command(const char *cmd)` runs the \0 terminated command string and reads its STDOUT and returns an owned Shard*.
|
||||||
|
- `from_file(const std::filesystem::path &path)` reads the file at path.
|
||||||
|
- `from_string(const char *data, uint64_t len)` loads the string given into a new storage, (normally inserting text can work by inserting into the append storage but this function loads it into a `OriginalStorage` object.).
|
||||||
|
These methods all clip the final newline if it exists.
|
||||||
|
And they also create a new `OriginalStorage` object.
|
||||||
|
|
||||||
|
### `vase.h`
|
||||||
|
|
||||||
|
### `iterators/`
|
||||||
|
|
||||||
|
#### `line.h`
|
||||||
|
|
||||||
|
#### `petal.h`
|
||||||
@@ -56,5 +56,7 @@ It should support:
|
|||||||
- Error handling.
|
- Error handling.
|
||||||
- And more.
|
- And more.
|
||||||
|
|
||||||
Not done yet.
|
### TODO:
|
||||||
|
|
||||||
|
- Make "g" command work.
|
||||||
|
- properly handle escapes for %q ' etc in ruby parser (rn everything is escapable.)
|
||||||
|
|||||||
@@ -111,7 +111,6 @@
|
|||||||
|
|
||||||
export MRUBY_CFLAGS=-I${mruby}/include
|
export MRUBY_CFLAGS=-I${mruby}/include
|
||||||
export MRUBY_LIBS="-L${mruby}/lib -lmruby"
|
export MRUBY_LIBS="-L${mruby}/lib -lmruby"
|
||||||
export PATH="$PWD/result/bin:$PATH"
|
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
+1
-3
@@ -14,6 +14,7 @@
|
|||||||
|
|
||||||
namespace bed {
|
namespace bed {
|
||||||
struct BEd {
|
struct BEd {
|
||||||
|
internal::scripting::RubyState mrb;
|
||||||
internal::trie::Trie<internal::functions::Function> functions;
|
internal::trie::Trie<internal::functions::Function> functions;
|
||||||
internal::functions::Function no_op;
|
internal::functions::Function no_op;
|
||||||
internal::functions::Function eof_op;
|
internal::functions::Function eof_op;
|
||||||
@@ -24,9 +25,6 @@ struct BEd {
|
|||||||
internal::vase::AppendStorage append{"/tmp"};
|
internal::vase::AppendStorage append{"/tmp"};
|
||||||
std::unordered_map<std::string, internal::buffer::Buffer *> buffers;
|
std::unordered_map<std::string, internal::buffer::Buffer *> buffers;
|
||||||
|
|
||||||
mrb_state *mrb = nullptr;
|
|
||||||
int arena;
|
|
||||||
|
|
||||||
bool help_mode = false;
|
bool help_mode = false;
|
||||||
bool prompt_mode = true;
|
bool prompt_mode = true;
|
||||||
std::function<std::string(BEd &)> prompt = nullptr;
|
std::function<std::string(BEd &)> prompt = nullptr;
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ struct Buffer {
|
|||||||
virtual uint64_t bytes() = 0;
|
virtual uint64_t bytes() = 0;
|
||||||
virtual void set_filename(std::filesystem::path path) = 0;
|
virtual void set_filename(std::filesystem::path path) = 0;
|
||||||
virtual std::filesystem::path filename() = 0;
|
virtual std::filesystem::path filename() = 0;
|
||||||
|
virtual void saved_hook() = 0;
|
||||||
virtual void load(BEd &ctx, vase::Shard *text) = 0;
|
virtual void load(BEd &ctx, vase::Shard *text) = 0;
|
||||||
virtual vase::Shard *copy(uint64_t start_line, uint64_t end_line) = 0;
|
virtual vase::Shard *copy(uint64_t start_line, uint64_t end_line) = 0;
|
||||||
virtual void substitute(
|
virtual void substitute(
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ struct ClipBuffer : Buffer {
|
|||||||
|
|
||||||
void clip_write(vase::Shard *text);
|
void clip_write(vase::Shard *text);
|
||||||
bool waste() override;
|
bool waste() override;
|
||||||
|
void saved_hook() override;
|
||||||
uint64_t lines() override;
|
uint64_t lines() override;
|
||||||
uint64_t bytes() override;
|
uint64_t bytes() override;
|
||||||
void load(BEd &ctx, vase::Shard *text) override;
|
void load(BEd &ctx, vase::Shard *text) override;
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ struct GenericBuffer : ShardBuffer {
|
|||||||
bool undo(BEd &ctx);
|
bool undo(BEd &ctx);
|
||||||
bool redo(BEd &ctx);
|
bool redo(BEd &ctx);
|
||||||
uint64_t prune(int);
|
uint64_t prune(int);
|
||||||
|
void saved_hook() override;
|
||||||
bool waste() override;
|
bool waste() override;
|
||||||
void load(BEd &ctx, vase::Shard *text) override;
|
void load(BEd &ctx, vase::Shard *text) override;
|
||||||
void set_filename(std::filesystem::path path) override;
|
void set_filename(std::filesystem::path path) override;
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ struct ReadonlyBuffer : ShardBuffer {
|
|||||||
const syntax::ParserSnapshot &snapshot
|
const syntax::ParserSnapshot &snapshot
|
||||||
) : ShardBuffer(std::move(name), root, snapshot, Kind::History) {}
|
) : ShardBuffer(std::move(name), root, snapshot, Kind::History) {}
|
||||||
|
|
||||||
|
void saved_hook() override {}
|
||||||
bool waste() override {
|
bool waste() override {
|
||||||
return useless;
|
return useless;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,30 @@
|
|||||||
#include "pch.h"
|
#include "pch.h"
|
||||||
|
|
||||||
namespace bed::internal::scripting {
|
namespace bed::internal::scripting {
|
||||||
|
struct RubyState {
|
||||||
|
mrb_state *state = nullptr;
|
||||||
|
int arena = 0;
|
||||||
|
|
||||||
|
explicit RubyState(BEd *ctx) : state(mrb_open()) {
|
||||||
|
if (!state)
|
||||||
|
throw fatal_error("Failed to initialize mruby.", 1);
|
||||||
|
state->ud = ctx;
|
||||||
|
arena = mrb_gc_arena_save(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
~RubyState() {
|
||||||
|
state->ud = nullptr;
|
||||||
|
if (state) {
|
||||||
|
mrb_gc_arena_restore(state, arena);
|
||||||
|
mrb_full_gc(state);
|
||||||
|
mrb_close(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RubyState(const RubyState &) = delete;
|
||||||
|
RubyState &operator=(const RubyState &) = delete;
|
||||||
|
};
|
||||||
|
|
||||||
struct Block {
|
struct Block {
|
||||||
mrb_state *mrb = nullptr;
|
mrb_state *mrb = nullptr;
|
||||||
mrb_value proc = mrb_nil_value();
|
mrb_value proc = mrb_nil_value();
|
||||||
@@ -96,5 +120,5 @@ struct Block {
|
|||||||
};
|
};
|
||||||
|
|
||||||
void register_basic(BEd &ctx);
|
void register_basic(BEd &ctx);
|
||||||
void run(BEd &ctx, const std::string &str);
|
std::string run(BEd &ctx, const std::string &str);
|
||||||
}; // namespace bed::internal::scripting
|
}; // namespace bed::internal::scripting
|
||||||
|
|||||||
@@ -24,7 +24,8 @@ struct alignas(2) RubyState {
|
|||||||
DEF_NAME = 0b10,
|
DEF_NAME = 0b10,
|
||||||
MODULE_NAME = 0b11
|
MODULE_NAME = 0b11
|
||||||
};
|
};
|
||||||
static constexpr uint8_t NEWLINE = 1 << 5;
|
static constexpr uint8_t NEWLINE = 1 << 4;
|
||||||
|
static constexpr uint8_t ALLOW_ESCAPE = 1 << 5;
|
||||||
static constexpr uint8_t ALLOW_INTERPOLATION = 1 << 6;
|
static constexpr uint8_t ALLOW_INTERPOLATION = 1 << 6;
|
||||||
static constexpr uint8_t EXPECTING_EXPRESSION = 1 << 7;
|
static constexpr uint8_t EXPECTING_EXPRESSION = 1 << 7;
|
||||||
uint8_t flags = 0;
|
uint8_t flags = 0;
|
||||||
|
|||||||
@@ -24,9 +24,9 @@ struct Shard {
|
|||||||
static void retain(Shard *n);
|
static void retain(Shard *n);
|
||||||
static void release(Shard *n);
|
static void release(Shard *n);
|
||||||
|
|
||||||
static Shard *from_file(const std::filesystem::path &path, bool posix_ending);
|
static Shard *from_file(const std::filesystem::path &path);
|
||||||
static Shard *from_string(const char *data, uint64_t len, bool posix_ending);
|
static Shard *from_string(const char *data, uint64_t len);
|
||||||
static Shard *from_command(const char *cmd, bool posix_ending);
|
static Shard *from_command(const char *cmd);
|
||||||
|
|
||||||
static std::pair<Shard *, Shard *> split(Shard *n, uint64_t offset);
|
static std::pair<Shard *, Shard *> split(Shard *n, uint64_t offset);
|
||||||
static Shard *concat(Shard *a, Shard *b);
|
static Shard *concat(Shard *a, Shard *b);
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
register :happy, address: :none do
|
||||||
|
puts "be nice"
|
||||||
|
end
|
||||||
+1
-12
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
namespace bed {
|
namespace bed {
|
||||||
BEd::BEd(std::vector<std::string> args)
|
BEd::BEd(std::vector<std::string> args)
|
||||||
: theme(internal::theme::Theme::default_theme()), io(*this) {
|
: mrb(this), theme(internal::theme::Theme::default_theme()), io(*this) {
|
||||||
std::string prompt_ = "";
|
std::string prompt_ = "";
|
||||||
std::string file = "";
|
std::string file = "";
|
||||||
bool suppress = false;
|
bool suppress = false;
|
||||||
@@ -42,11 +42,6 @@ BEd::BEd(std::vector<std::string> args)
|
|||||||
&& strcmp(colorterm, "24bit") != 0)
|
&& strcmp(colorterm, "24bit") != 0)
|
||||||
color = false;
|
color = false;
|
||||||
color_mode = color;
|
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_posix(*this);
|
||||||
internal::functions::Function::register_extented(*this);
|
internal::functions::Function::register_extented(*this);
|
||||||
internal::functions::Suffix::register_suffixes(*this);
|
internal::functions::Suffix::register_suffixes(*this);
|
||||||
@@ -70,12 +65,6 @@ BEd::~BEd() {
|
|||||||
delete buffer;
|
delete buffer;
|
||||||
for (auto &[_, lang] : languages)
|
for (auto &[_, lang] : languages)
|
||||||
delete lang;
|
delete lang;
|
||||||
mrb->ud = nullptr;
|
|
||||||
if (mrb) {
|
|
||||||
mrb_gc_arena_restore(mrb, arena);
|
|
||||||
mrb_full_gc(mrb);
|
|
||||||
mrb_close(mrb);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BEd::print_help() {
|
void BEd::print_help() {
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ GenericBuffer::~GenericBuffer() {
|
|||||||
|
|
||||||
void GenericBuffer::list_history(BEd &ctx) {
|
void GenericBuffer::list_history(BEd &ctx) {
|
||||||
uint64_t current = base_version + undo_stack.size();
|
uint64_t current = base_version + undo_stack.size();
|
||||||
|
uint64_t max_version = current + redo_stack.size();
|
||||||
|
size_t width = std::to_string(max_version).size();
|
||||||
for (size_t i = 0; i < undo_stack.size(); ++i) {
|
for (size_t i = 0; i < undo_stack.size(); ++i) {
|
||||||
auto &item = undo_stack[i];
|
auto &item = undo_stack[i];
|
||||||
uint64_t version = base_version + i;
|
uint64_t version = base_version + i;
|
||||||
@@ -22,8 +24,9 @@ void GenericBuffer::list_history(BEd &ctx) {
|
|||||||
std::tm tm = *std::localtime(&time);
|
std::tm tm = *std::localtime(&time);
|
||||||
ctx.io.write_line(
|
ctx.io.write_line(
|
||||||
std::format(
|
std::format(
|
||||||
" {} {:04}-{:02}-{:02} {:02}:{:02}:{:02} {}",
|
" {:>{}} {:04}-{:02}-{:02} {:02}:{:02}:{:02} {}",
|
||||||
version,
|
version,
|
||||||
|
width,
|
||||||
tm.tm_year + 1900,
|
tm.tm_year + 1900,
|
||||||
tm.tm_mon + 1,
|
tm.tm_mon + 1,
|
||||||
tm.tm_mday,
|
tm.tm_mday,
|
||||||
@@ -39,8 +42,9 @@ void GenericBuffer::list_history(BEd &ctx) {
|
|||||||
std::tm tm = *std::localtime(&time);
|
std::tm tm = *std::localtime(&time);
|
||||||
ctx.io.write_line(
|
ctx.io.write_line(
|
||||||
std::format(
|
std::format(
|
||||||
"* {} {:04}-{:02}-{:02} {:02}:{:02}:{:02} {}",
|
"* {:>{}} {:04}-{:02}-{:02} {:02}:{:02}:{:02} {}",
|
||||||
current,
|
current,
|
||||||
|
width,
|
||||||
tm.tm_year + 1900,
|
tm.tm_year + 1900,
|
||||||
tm.tm_mon + 1,
|
tm.tm_mon + 1,
|
||||||
tm.tm_mday,
|
tm.tm_mday,
|
||||||
@@ -58,8 +62,9 @@ void GenericBuffer::list_history(BEd &ctx) {
|
|||||||
std::tm tm = *std::localtime(&time);
|
std::tm tm = *std::localtime(&time);
|
||||||
ctx.io.write_line(
|
ctx.io.write_line(
|
||||||
std::format(
|
std::format(
|
||||||
" {} {:04}-{:02}-{:02} {:02}:{:02}:{:02} {}",
|
" {:>{}} {:04}-{:02}-{:02} {:02}:{:02}:{:02} {}",
|
||||||
version,
|
version,
|
||||||
|
width,
|
||||||
tm.tm_year + 1900,
|
tm.tm_year + 1900,
|
||||||
tm.tm_mon + 1,
|
tm.tm_mon + 1,
|
||||||
tm.tm_mday,
|
tm.tm_mday,
|
||||||
@@ -207,6 +212,10 @@ bool GenericBuffer::waste() {
|
|||||||
&& parse.lang == nullptr;
|
&& parse.lang == nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GenericBuffer::saved_hook() {
|
||||||
|
state = buffer::GenericBuffer::Unmodified;
|
||||||
|
}
|
||||||
|
|
||||||
void GenericBuffer::language(BEd &ctx, std::string name) {
|
void GenericBuffer::language(BEd &ctx, std::string name) {
|
||||||
syntax::Language *lang = nullptr;
|
syntax::Language *lang = nullptr;
|
||||||
if (name.size()) {
|
if (name.size()) {
|
||||||
@@ -253,17 +262,15 @@ std::filesystem::path GenericBuffer::filename() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
void GenericBuffer::append(BEd &ctx, vase::Shard *text, uint64_t line) {
|
void GenericBuffer::append(BEd &ctx, vase::Shard *text, uint64_t line) {
|
||||||
if (!text)
|
snapshot(std::format("Insert {} lines after line {}", (text ? text->lines + 1 : 1), line));
|
||||||
return;
|
|
||||||
snapshot(std::format("Insert {} lines after line {}", text->lines + 1, line));
|
|
||||||
ctx.prev().buffername = name;
|
ctx.prev().buffername = name;
|
||||||
ctx.prev().start = line + 1;
|
ctx.prev().start = line + 1;
|
||||||
ctx.prev().end = line + text->lines + 1;
|
ctx.prev().end = line + (text ? text->lines + 1 : 1);
|
||||||
ctx.current() = {name, line + text->lines + 1};
|
ctx.current() = {name, line + (text ? text->lines + 1 : 1)};
|
||||||
root = vase::insert(&ctx.append, root, text, line);
|
root = vase::insert(&ctx.append, root, text, line);
|
||||||
ctx.marks.insert(name, line, text->lines + 1);
|
ctx.marks.insert(name, line, (text ? text->lines + 1 : 1));
|
||||||
if (parse.lang)
|
if (parse.lang)
|
||||||
syntax::insert(parse, root, line, text->lines + 1);
|
syntax::insert(parse, root, line, (text ? text->lines + 1 : 1));
|
||||||
state = Modified;
|
state = Modified;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,15 +8,17 @@ bool ClipBuffer::waste() {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ClipBuffer::saved_hook() {}
|
||||||
|
|
||||||
uint64_t ClipBuffer::lines() {
|
uint64_t ClipBuffer::lines() {
|
||||||
auto s = vase::Shard::from_command("xclip -selection clipboard -o", true);
|
auto s = vase::Shard::from_command("xclip -selection clipboard -o");
|
||||||
uint64_t lines = s ? s->lines + 1 : 0;
|
uint64_t lines = s ? s->lines + 1 : 0;
|
||||||
vase::Shard::release(s);
|
vase::Shard::release(s);
|
||||||
return lines;
|
return lines;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t ClipBuffer::bytes() {
|
uint64_t ClipBuffer::bytes() {
|
||||||
auto s = vase::Shard::from_command("xclip -selection clipboard -o", true);
|
auto s = vase::Shard::from_command("xclip -selection clipboard -o");
|
||||||
uint64_t length = s ? s->length + 1 : 0;
|
uint64_t length = s ? s->length + 1 : 0;
|
||||||
vase::Shard::release(s);
|
vase::Shard::release(s);
|
||||||
return length;
|
return length;
|
||||||
@@ -55,7 +57,7 @@ void ClipBuffer::append(BEd &ctx, vase::Shard *text, uint64_t line) {
|
|||||||
ctx.prev().buffername = name;
|
ctx.prev().buffername = name;
|
||||||
ctx.prev().start = line + 1;
|
ctx.prev().start = line + 1;
|
||||||
ctx.prev().end = line + (text ? text->lines + 1 : 0);
|
ctx.prev().end = line + (text ? text->lines + 1 : 0);
|
||||||
auto s = vase::Shard::from_command("xclip -selection clipboard -o", true);
|
auto s = vase::Shard::from_command("xclip -selection clipboard -o");
|
||||||
s = vase::insert(&ctx.append, s, text, line);
|
s = vase::insert(&ctx.append, s, text, line);
|
||||||
clip_write(s);
|
clip_write(s);
|
||||||
vase::Shard::release(s);
|
vase::Shard::release(s);
|
||||||
@@ -63,7 +65,7 @@ void ClipBuffer::append(BEd &ctx, vase::Shard *text, uint64_t line) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ClipBuffer::remove(BEd &ctx, uint64_t start_line, uint64_t end_line) {
|
void ClipBuffer::remove(BEd &ctx, uint64_t start_line, uint64_t end_line) {
|
||||||
auto s = vase::Shard::from_command("xclip -selection clipboard -o", true);
|
auto s = vase::Shard::from_command("xclip -selection clipboard -o");
|
||||||
s = vase::erase(s, start_line, end_line);
|
s = vase::erase(s, start_line, end_line);
|
||||||
clip_write(s);
|
clip_write(s);
|
||||||
ctx.prev().buffername = name;
|
ctx.prev().buffername = name;
|
||||||
@@ -83,7 +85,7 @@ void ClipBuffer::replace(BEd &ctx, vase::Shard *text, uint64_t start_line, uint6
|
|||||||
ctx.prev().end = start_line + text->lines;
|
ctx.prev().end = start_line + text->lines;
|
||||||
uint64_t new_count = text->lines + 1;
|
uint64_t new_count = text->lines + 1;
|
||||||
uint64_t old_count = end_line - start_line + 1;
|
uint64_t old_count = end_line - start_line + 1;
|
||||||
auto s = vase::Shard::from_command("xclip -selection clipboard -o", true);
|
auto s = vase::Shard::from_command("xclip -selection clipboard -o");
|
||||||
s = vase::replace(s, text, start_line, end_line);
|
s = vase::replace(s, text, start_line, end_line);
|
||||||
clip_write(s);
|
clip_write(s);
|
||||||
vase::Shard::release(s);
|
vase::Shard::release(s);
|
||||||
@@ -97,7 +99,7 @@ void ClipBuffer::replace(BEd &ctx, vase::Shard *text, uint64_t start_line, uint6
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ClipBuffer::join(BEd &ctx, uint64_t start_line, uint64_t end_line) {
|
void ClipBuffer::join(BEd &ctx, uint64_t start_line, uint64_t end_line) {
|
||||||
auto s = vase::Shard::from_command("xclip -selection clipboard -o", true);
|
auto s = vase::Shard::from_command("xclip -selection clipboard -o");
|
||||||
s = vase::join(s, start_line, end_line);
|
s = vase::join(s, start_line, end_line);
|
||||||
clip_write(s);
|
clip_write(s);
|
||||||
vase::Shard::release(s);
|
vase::Shard::release(s);
|
||||||
@@ -114,7 +116,7 @@ void ClipBuffer::substitute(
|
|||||||
ctx.prev().buffername = name;
|
ctx.prev().buffername = name;
|
||||||
ctx.prev().start = start_line;
|
ctx.prev().start = start_line;
|
||||||
ctx.prev().end = end_line;
|
ctx.prev().end = end_line;
|
||||||
auto s = vase::Shard::from_command("xclip -selection clipboard -o", true);
|
auto s = vase::Shard::from_command("xclip -selection clipboard -o");
|
||||||
s = vase::substitute(
|
s = vase::substitute(
|
||||||
&ctx.append,
|
&ctx.append,
|
||||||
s,
|
s,
|
||||||
@@ -136,21 +138,21 @@ void ClipBuffer::substitute(
|
|||||||
}
|
}
|
||||||
|
|
||||||
vase::Shard *ClipBuffer::copy(uint64_t start_line, uint64_t end_line) {
|
vase::Shard *ClipBuffer::copy(uint64_t start_line, uint64_t end_line) {
|
||||||
auto s = vase::Shard::from_command("xclip -selection clipboard -o", true);
|
auto s = vase::Shard::from_command("xclip -selection clipboard -o");
|
||||||
vase::Shard *o = vase::copy(s, start_line, end_line);
|
vase::Shard *o = vase::copy(s, start_line, end_line);
|
||||||
vase::Shard::release(s);
|
vase::Shard::release(s);
|
||||||
return o;
|
return o;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t ClipBuffer::find_next(std::string_view pattern, uint64_t start) {
|
uint64_t ClipBuffer::find_next(std::string_view pattern, uint64_t start) {
|
||||||
auto s = vase::Shard::from_command("xclip -selection clipboard -o", true);
|
auto s = vase::Shard::from_command("xclip -selection clipboard -o");
|
||||||
uint64_t line = vase::find_next(s, pattern, start);
|
uint64_t line = vase::find_next(s, pattern, start);
|
||||||
vase::Shard::release(s);
|
vase::Shard::release(s);
|
||||||
return line;
|
return line;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t ClipBuffer::find_prev(std::string_view pattern, uint64_t start) {
|
uint64_t ClipBuffer::find_prev(std::string_view pattern, uint64_t start) {
|
||||||
auto s = vase::Shard::from_command("xclip -selection clipboard -o", true);
|
auto s = vase::Shard::from_command("xclip -selection clipboard -o");
|
||||||
uint64_t line = vase::find_prev(s, pattern, start);
|
uint64_t line = vase::find_prev(s, pattern, start);
|
||||||
vase::Shard::release(s);
|
vase::Shard::release(s);
|
||||||
return line;
|
return line;
|
||||||
@@ -174,7 +176,7 @@ void ClipBuffer::print(BEd &ctx, uint64_t start_line, uint64_t end_line) {
|
|||||||
ctx.prev().buffername = name;
|
ctx.prev().buffername = name;
|
||||||
ctx.prev().start = start_line;
|
ctx.prev().start = start_line;
|
||||||
ctx.prev().end = end_line;
|
ctx.prev().end = end_line;
|
||||||
auto s = vase::Shard::from_command("xclip -selection clipboard -o", true);
|
auto s = vase::Shard::from_command("xclip -selection clipboard -o");
|
||||||
vase::Iterator it(s, start_line - 1, Direction::Forward);
|
vase::Iterator it(s, start_line - 1, Direction::Forward);
|
||||||
while (it.next() && start_line++ <= end_line)
|
while (it.next() && start_line++ <= end_line)
|
||||||
ctx.io.write_line(it.line);
|
ctx.io.write_line(it.line);
|
||||||
@@ -188,7 +190,7 @@ void ClipBuffer::number_print(BEd &ctx, uint64_t start_line, uint64_t end_line)
|
|||||||
uint8_t width = 1;
|
uint8_t width = 1;
|
||||||
for (uint64_t n = end_line; n >= 10; n /= 10)
|
for (uint64_t n = end_line; n >= 10; n /= 10)
|
||||||
++width;
|
++width;
|
||||||
auto s = vase::Shard::from_command("xclip -selection clipboard -o", true);
|
auto s = vase::Shard::from_command("xclip -selection clipboard -o");
|
||||||
vase::Iterator it(s, start_line - 1, Direction::Forward);
|
vase::Iterator it(s, start_line - 1, Direction::Forward);
|
||||||
while (it.next() && start_line <= end_line)
|
while (it.next() && start_line <= end_line)
|
||||||
ctx.io.write_line(std::format("{:>{}}\t{}", start_line++, width, it.line));
|
ctx.io.write_line(std::format("{:>{}}\t{}", start_line++, width, it.line));
|
||||||
@@ -199,7 +201,7 @@ void ClipBuffer::list_print(BEd &ctx, uint64_t start_line, uint64_t end_line) {
|
|||||||
ctx.prev().buffername = name;
|
ctx.prev().buffername = name;
|
||||||
ctx.prev().start = start_line;
|
ctx.prev().start = start_line;
|
||||||
ctx.prev().end = end_line;
|
ctx.prev().end = end_line;
|
||||||
auto s = vase::Shard::from_command("xclip -selection clipboard -o", true);
|
auto s = vase::Shard::from_command("xclip -selection clipboard -o");
|
||||||
vase::Iterator it(s, start_line - 1, Direction::Forward);
|
vase::Iterator it(s, start_line - 1, Direction::Forward);
|
||||||
while (it.next() && start_line++ <= end_line)
|
while (it.next() && start_line++ <= end_line)
|
||||||
ctx.io.write_line(list_string(it.line));
|
ctx.io.write_line(list_string(it.line));
|
||||||
|
|||||||
@@ -176,8 +176,8 @@ void Function::register_extented(BEd &ctx) {
|
|||||||
.address_kind = Function::AddressKind::Range,
|
.address_kind = Function::AddressKind::Range,
|
||||||
.argument_kind = Function::ArgumentKind::Ruby,
|
.argument_kind = Function::ArgumentKind::Ruby,
|
||||||
.input_mode = Function::InputMode::None,
|
.input_mode = Function::InputMode::None,
|
||||||
.desc = "Execute some ruby code",
|
.desc = "Execute given ruby code.",
|
||||||
.default_address = "",
|
.default_address = "0,0",
|
||||||
.accept_zero = true,
|
.accept_zero = true,
|
||||||
.pre_text_mode = nullptr,
|
.pre_text_mode = nullptr,
|
||||||
.handle = [](
|
.handle = [](
|
||||||
@@ -193,7 +193,30 @@ void Function::register_extented(BEd &ctx) {
|
|||||||
+ ";$END=" + std::to_string(addr.end)
|
+ ";$END=" + std::to_string(addr.end)
|
||||||
+ ";$BUFNAME=\"" + addr.buffername + "\"";
|
+ ";$BUFNAME=\"" + addr.buffername + "\"";
|
||||||
scripting::run(ctx, pre_code);
|
scripting::run(ctx, pre_code);
|
||||||
scripting::run(ctx, arg.cmd);
|
auto line = scripting::run(ctx, arg.cmd);
|
||||||
|
ctx.io.write("=> ", 3);
|
||||||
|
auto parser = syntax::MiniParser(
|
||||||
|
*ctx.languages["ruby"],
|
||||||
|
vase::Shard::from_string(line.data(), line.size()),
|
||||||
|
nullptr
|
||||||
|
);
|
||||||
|
const auto &tokens = parser.lines[0].second;
|
||||||
|
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() || end > line.size())
|
||||||
|
break;
|
||||||
|
if (cursor < start)
|
||||||
|
ctx.io.write(line.data() + cursor, start - cursor);
|
||||||
|
ctx.io.apply(token.type);
|
||||||
|
ctx.io.write(line.data() + start, end - start);
|
||||||
|
ctx.io.reset();
|
||||||
|
cursor = end;
|
||||||
|
}
|
||||||
|
if (cursor < line.size())
|
||||||
|
ctx.io.write(line.data() + cursor, line.size() - cursor);
|
||||||
|
ctx.io.write_line("");
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -204,7 +227,7 @@ void Function::register_extented(BEd &ctx) {
|
|||||||
.argument_kind = Function::ArgumentKind::None,
|
.argument_kind = Function::ArgumentKind::None,
|
||||||
.input_mode = Function::InputMode::None,
|
.input_mode = Function::InputMode::None,
|
||||||
.desc = "Execute addressed lines as ruby code.",
|
.desc = "Execute addressed lines as ruby code.",
|
||||||
.default_address = ".,.",
|
.default_address = "1,$",
|
||||||
.accept_zero = false,
|
.accept_zero = false,
|
||||||
.pre_text_mode = nullptr,
|
.pre_text_mode = nullptr,
|
||||||
.handle = [](
|
.handle = [](
|
||||||
@@ -230,38 +253,6 @@ void Function::register_extented(BEd &ctx) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
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(
|
ctx.functions.insert(
|
||||||
"echo",
|
"echo",
|
||||||
Function{
|
Function{
|
||||||
@@ -296,7 +287,7 @@ void Function::register_extented(BEd &ctx) {
|
|||||||
str[i - 1] = '\n';
|
str[i - 1] = '\n';
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (str[i] == '$' && i < str.size() && '1' <= str[i + 1] && str[i + 1] <= '4') {
|
if (str[i] == '$' && i + 1 < str.size() && '1' <= str[i + 1] && str[i + 1] <= '4') {
|
||||||
char c = str[i + 1];
|
char c = str[i + 1];
|
||||||
str.erase(i, 2);
|
str.erase(i, 2);
|
||||||
switch (c) {
|
switch (c) {
|
||||||
|
|||||||
@@ -144,17 +144,17 @@ void Function::register_posix(BEd &ctx) {
|
|||||||
vase::Shard *s = nullptr;
|
vase::Shard *s = nullptr;
|
||||||
if (std::holds_alternative<std::filesystem::path>(arg)) {
|
if (std::holds_alternative<std::filesystem::path>(arg)) {
|
||||||
auto path = std::get<std::filesystem::path>(arg);
|
auto path = std::get<std::filesystem::path>(arg);
|
||||||
s = vase::Shard::from_file(path, true);
|
s = vase::Shard::from_file(path);
|
||||||
buf.set_filename(path);
|
buf.set_filename(path);
|
||||||
} else if (std::holds_alternative<ShellArg>(arg)) {
|
} else if (std::holds_alternative<ShellArg>(arg)) {
|
||||||
auto cmd = std::get<ShellArg>(arg).cmd;
|
auto cmd = std::get<ShellArg>(arg).cmd;
|
||||||
ctx.escape_command(cmd, buf.filename().string());
|
ctx.escape_command(cmd, buf.filename().string());
|
||||||
s = vase::Shard::from_command(cmd.c_str(), true);
|
s = vase::Shard::from_command(cmd.c_str());
|
||||||
} else {
|
} else {
|
||||||
auto path = buf.filename();
|
auto path = buf.filename();
|
||||||
if (path.empty())
|
if (path.empty())
|
||||||
throw ed_error("Need filename.");
|
throw ed_error("Need filename.");
|
||||||
s = vase::Shard::from_file(path, true);
|
s = vase::Shard::from_file(path);
|
||||||
};
|
};
|
||||||
try {
|
try {
|
||||||
buf.load(ctx, s);
|
buf.load(ctx, s);
|
||||||
@@ -190,17 +190,17 @@ void Function::register_posix(BEd &ctx) {
|
|||||||
vase::Shard *s = nullptr;
|
vase::Shard *s = nullptr;
|
||||||
if (std::holds_alternative<std::filesystem::path>(arg)) {
|
if (std::holds_alternative<std::filesystem::path>(arg)) {
|
||||||
auto path = std::get<std::filesystem::path>(arg);
|
auto path = std::get<std::filesystem::path>(arg);
|
||||||
s = vase::Shard::from_file(path, true);
|
s = vase::Shard::from_file(path);
|
||||||
buf.set_filename(path);
|
buf.set_filename(path);
|
||||||
} else if (std::holds_alternative<ShellArg>(arg)) {
|
} else if (std::holds_alternative<ShellArg>(arg)) {
|
||||||
auto cmd = std::get<ShellArg>(arg).cmd;
|
auto cmd = std::get<ShellArg>(arg).cmd;
|
||||||
ctx.escape_command(cmd, buf.filename().string());
|
ctx.escape_command(cmd, buf.filename().string());
|
||||||
s = vase::Shard::from_command(cmd.c_str(), true);
|
s = vase::Shard::from_command(cmd.c_str());
|
||||||
} else {
|
} else {
|
||||||
auto path = buf.filename();
|
auto path = buf.filename();
|
||||||
if (path.empty())
|
if (path.empty())
|
||||||
throw ed_error("Need filename.");
|
throw ed_error("Need filename.");
|
||||||
s = vase::Shard::from_file(path, true);
|
s = vase::Shard::from_file(path);
|
||||||
};
|
};
|
||||||
try {
|
try {
|
||||||
buf.load(ctx, s);
|
buf.load(ctx, s);
|
||||||
@@ -416,8 +416,7 @@ void Function::register_posix(BEd &ctx) {
|
|||||||
auto addr = std::get<buffer::Range>(addr_);
|
auto addr = std::get<buffer::Range>(addr_);
|
||||||
auto arg = std::get<buffer::Line>(arg_);
|
auto arg = std::get<buffer::Line>(arg_);
|
||||||
if (arg.buffername == addr.buffername
|
if (arg.buffername == addr.buffername
|
||||||
&& addr.start <= arg.number
|
&& arg.number >= addr.start && arg.number < addr.end)
|
||||||
&& addr.end < arg.number)
|
|
||||||
throw ed_error("Can't move lines within themselves.");
|
throw ed_error("Can't move lines within themselves.");
|
||||||
auto text = ctx.buffer(addr.buffername).copy(addr.start, addr.end);
|
auto text = ctx.buffer(addr.buffername).copy(addr.start, addr.end);
|
||||||
ctx.mark(252, arg);
|
ctx.mark(252, arg);
|
||||||
@@ -589,18 +588,18 @@ void Function::register_posix(BEd &ctx) {
|
|||||||
vase::Shard *s = nullptr;
|
vase::Shard *s = nullptr;
|
||||||
if (std::holds_alternative<std::filesystem::path>(arg)) {
|
if (std::holds_alternative<std::filesystem::path>(arg)) {
|
||||||
auto path = std::get<std::filesystem::path>(arg);
|
auto path = std::get<std::filesystem::path>(arg);
|
||||||
s = vase::Shard::from_file(path, true);
|
s = vase::Shard::from_file(path);
|
||||||
if (buf.filename().empty())
|
if (buf.filename().empty())
|
||||||
buf.set_filename(path);
|
buf.set_filename(path);
|
||||||
} else if (std::holds_alternative<ShellArg>(arg)) {
|
} else if (std::holds_alternative<ShellArg>(arg)) {
|
||||||
auto cmd = std::get<ShellArg>(arg).cmd;
|
auto cmd = std::get<ShellArg>(arg).cmd;
|
||||||
ctx.escape_command(cmd, buf.filename().string());
|
ctx.escape_command(cmd, buf.filename().string());
|
||||||
s = vase::Shard::from_command(cmd.c_str(), true);
|
s = vase::Shard::from_command(cmd.c_str());
|
||||||
} else {
|
} else {
|
||||||
auto path = buf.filename();
|
auto path = buf.filename();
|
||||||
if (path.empty())
|
if (path.empty())
|
||||||
throw ed_error("Need filename.");
|
throw ed_error("Need filename.");
|
||||||
s = vase::Shard::from_file(path, true);
|
s = vase::Shard::from_file(path);
|
||||||
};
|
};
|
||||||
try {
|
try {
|
||||||
buf.append(ctx, s, addr.number);
|
buf.append(ctx, s, addr.number);
|
||||||
@@ -757,6 +756,7 @@ void Function::register_posix(BEd &ctx) {
|
|||||||
vase::Shard::release(text);
|
vase::Shard::release(text);
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
buf.saved_hook();
|
||||||
if (!ctx.suppress_mode)
|
if (!ctx.suppress_mode)
|
||||||
ctx.io.write(std::format("{}\n", text ? text->length + 1 : 0));
|
ctx.io.write(std::format("{}\n", text ? text->length + 1 : 0));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -105,20 +105,24 @@ static mrb_value mrb_bed_unregister(mrb_state *mrb, mrb_value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void register_basic(BEd &ctx) {
|
void register_basic(BEd &ctx) {
|
||||||
|
auto mrb = ctx.mrb.state;
|
||||||
auto *bed_error =
|
auto *bed_error =
|
||||||
mrb_define_class(ctx.mrb, "EdError", mrb_exc_get_id(ctx.mrb, MRB_ERROR_SYM(RuntimeError)));
|
mrb_define_class(mrb, "EdError", mrb_exc_get_id(mrb, MRB_ERROR_SYM(RuntimeError)));
|
||||||
mrb_define_class(ctx.mrb, "FatalError", bed_error);
|
mrb_define_class(mrb, "FatalError", bed_error);
|
||||||
mrb_define_method(ctx.mrb, ctx.mrb->kernel_module, "exit", mrb_bed_exit, MRB_ARGS_NONE());
|
mrb_define_method(mrb, 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));
|
mrb_define_method(mrb, mrb->kernel_module, "handle", mrb_bed_handle, MRB_ARGS_REQ(1));
|
||||||
mrb_define_method(ctx.mrb, ctx.mrb->kernel_module, "register", mrb_bed_register, MRB_ARGS_REQ(1) | MRB_ARGS_OPT(1) | MRB_ARGS_BLOCK());
|
mrb_define_method(mrb, mrb->kernel_module, "register", mrb_bed_register, MRB_ARGS_REQ(1) | MRB_ARGS_OPT(1) | MRB_ARGS_BLOCK());
|
||||||
mrb_define_method(ctx.mrb, ctx.mrb->kernel_module, "unregister", mrb_bed_unregister, MRB_ARGS_REQ(1));
|
mrb_define_method(mrb, mrb->kernel_module, "unregister", mrb_bed_unregister, MRB_ARGS_REQ(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
void run(BEd &ctx, const std::string &str) {
|
std::string run(BEd &ctx, const std::string &str) {
|
||||||
mrb_state *mrb = ctx.mrb;
|
mrb_state *mrb = ctx.mrb.state;
|
||||||
mrb_load_nstring(mrb, str.data(), str.size());
|
mrb_value result = mrb_load_nstring(mrb, str.data(), str.size());
|
||||||
if (!mrb->exc)
|
if (!mrb->exc) {
|
||||||
return;
|
mrb_value inspected = mrb_funcall(mrb, result, "inspect", 0);
|
||||||
|
std::string output(RSTRING_PTR(inspected), RSTRING_LEN(inspected));
|
||||||
|
return output;
|
||||||
|
}
|
||||||
mrb_value exc = mrb_obj_value(mrb->exc);
|
mrb_value exc = mrb_obj_value(mrb->exc);
|
||||||
mrb_value msg = mrb_funcall(mrb, exc, "message", 0);
|
mrb_value msg = mrb_funcall(mrb, exc, "message", 0);
|
||||||
std::string error;
|
std::string error;
|
||||||
|
|||||||
@@ -35,7 +35,21 @@ inline uint8_t utf8_codepoint_width(unsigned char c) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool handle_escapes(RubyParser &p, std::vector<io::Token> *tokens, uint32_t &start, bool string = true) {
|
bool handle_escapes(RubyParser &p, std::vector<io::Token> *tokens, uint32_t &start, bool string = true) {
|
||||||
if (p.peek() == '\\') {
|
if (p.peek() != '\\')
|
||||||
|
return false;
|
||||||
|
if (!(p.current().flags & RubyState::RubyInternalState::ALLOW_ESCAPE)) {
|
||||||
|
p.advance();
|
||||||
|
if (p.peek() != '\'' && p.peek() != '\\')
|
||||||
|
return false;
|
||||||
|
if (string)
|
||||||
|
tokens->push_back({start, p.i - 1, io::Token::String});
|
||||||
|
else
|
||||||
|
tokens->push_back({start, p.i - 1, io::Token::Regexp});
|
||||||
|
p.advance();
|
||||||
|
tokens->push_back({p.i - 2, p.i, io::Token::Escape});
|
||||||
|
start = p.i;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
if (string)
|
if (string)
|
||||||
tokens->push_back({start, p.i, io::Token::String});
|
tokens->push_back({start, p.i, io::Token::String});
|
||||||
else
|
else
|
||||||
@@ -98,8 +112,6 @@ bool handle_escapes(RubyParser &p, std::vector<io::Token> *tokens, uint32_t &sta
|
|||||||
tokens->push_back({start, p.i, io::Token::Escape});
|
tokens->push_back({start, p.i, io::Token::Escape});
|
||||||
start = p.i;
|
start = p.i;
|
||||||
return true;
|
return true;
|
||||||
}
|
|
||||||
return false;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
bool handle_heredoc(RubyParser &p, std::vector<io::Token> *tokens, std::vector<ParseEvent> *events) {
|
bool handle_heredoc(RubyParser &p, std::vector<io::Token> *tokens, std::vector<ParseEvent> *events) {
|
||||||
@@ -668,6 +680,7 @@ bool handle_syntax(RubyParser &p, std::vector<io::Token> *tokens, std::vector<Pa
|
|||||||
p.current().delim_start = '\'';
|
p.current().delim_start = '\'';
|
||||||
p.current().delim_end = '\'';
|
p.current().delim_end = '\'';
|
||||||
p.current().flags &= ~RubyState::RubyInternalState::ALLOW_INTERPOLATION;
|
p.current().flags &= ~RubyState::RubyInternalState::ALLOW_INTERPOLATION;
|
||||||
|
p.current().flags &= ~RubyState::RubyInternalState::ALLOW_ESCAPE;
|
||||||
p.advance();
|
p.advance();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -678,6 +691,7 @@ bool handle_syntax(RubyParser &p, std::vector<io::Token> *tokens, std::vector<Pa
|
|||||||
p.current().delim_start = '"';
|
p.current().delim_start = '"';
|
||||||
p.current().delim_end = '"';
|
p.current().delim_end = '"';
|
||||||
p.current().flags |= RubyState::RubyInternalState::ALLOW_INTERPOLATION;
|
p.current().flags |= RubyState::RubyInternalState::ALLOW_INTERPOLATION;
|
||||||
|
p.current().flags |= RubyState::RubyInternalState::ALLOW_ESCAPE;
|
||||||
p.advance();
|
p.advance();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -703,6 +717,7 @@ bool handle_syntax(RubyParser &p, std::vector<io::Token> *tokens, std::vector<Pa
|
|||||||
char delim_start = '\0';
|
char delim_start = '\0';
|
||||||
char delim_end = '\0';
|
char delim_end = '\0';
|
||||||
bool allow_interp = true;
|
bool allow_interp = true;
|
||||||
|
bool allow_escape = true;
|
||||||
int prefix_len = 1;
|
int prefix_len = 1;
|
||||||
bool is_regexp = false;
|
bool is_regexp = false;
|
||||||
switch (type) {
|
switch (type) {
|
||||||
@@ -716,6 +731,7 @@ bool handle_syntax(RubyParser &p, std::vector<io::Token> *tokens, std::vector<Pa
|
|||||||
case 'I':
|
case 'I':
|
||||||
case 'W':
|
case 'W':
|
||||||
allow_interp = true;
|
allow_interp = true;
|
||||||
|
allow_escape = true;
|
||||||
prefix_len = 2;
|
prefix_len = 2;
|
||||||
break;
|
break;
|
||||||
case 'w':
|
case 'w':
|
||||||
@@ -723,10 +739,12 @@ bool handle_syntax(RubyParser &p, std::vector<io::Token> *tokens, std::vector<Pa
|
|||||||
case 'i':
|
case 'i':
|
||||||
case 's':
|
case 's':
|
||||||
allow_interp = false;
|
allow_interp = false;
|
||||||
|
allow_escape = false;
|
||||||
prefix_len = 2;
|
prefix_len = 2;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
allow_interp = true;
|
allow_interp = true;
|
||||||
|
allow_escape = true;
|
||||||
prefix_len = 1;
|
prefix_len = 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -768,6 +786,8 @@ bool handle_syntax(RubyParser &p, std::vector<io::Token> *tokens, std::vector<Pa
|
|||||||
p.current().delim_end = delim_end;
|
p.current().delim_end = delim_end;
|
||||||
if (allow_interp)
|
if (allow_interp)
|
||||||
p.current().flags |= RubyState::RubyInternalState::ALLOW_INTERPOLATION;
|
p.current().flags |= RubyState::RubyInternalState::ALLOW_INTERPOLATION;
|
||||||
|
if (allow_escape)
|
||||||
|
p.current().flags |= RubyState::RubyInternalState::ALLOW_ESCAPE;
|
||||||
p.current().lit_brace_level = 1;
|
p.current().lit_brace_level = 1;
|
||||||
p.advance(prefix_len + 1);
|
p.advance(prefix_len + 1);
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -202,7 +202,7 @@ Shard *Shard::build(Shard **pieces, uint64_t lo, uint64_t hi) {
|
|||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
Shard *Shard::from_command(const char *cmd, bool posix_ending) {
|
Shard *Shard::from_command(const char *cmd) {
|
||||||
auto o = new OriginalStorage("/tmp");
|
auto o = new OriginalStorage("/tmp");
|
||||||
int dest_fd = o->fd;
|
int dest_fd = o->fd;
|
||||||
if (dest_fd == -1) {
|
if (dest_fd == -1) {
|
||||||
@@ -278,7 +278,6 @@ Shard *Shard::from_command(const char *cmd, bool posix_ending) {
|
|||||||
io::IO::enable_raw();
|
io::IO::enable_raw();
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
if (posix_ending) {
|
|
||||||
if (ending[1] == '\n') {
|
if (ending[1] == '\n') {
|
||||||
Petal *last = (Petal *)pieces.back();
|
Petal *last = (Petal *)pieces.back();
|
||||||
last->lines--;
|
last->lines--;
|
||||||
@@ -295,7 +294,6 @@ Shard *Shard::from_command(const char *cmd, bool posix_ending) {
|
|||||||
if (last && ending[0] == '\r')
|
if (last && ending[0] == '\r')
|
||||||
last->length--;
|
last->length--;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
o->initialize();
|
o->initialize();
|
||||||
io::IO::enable_raw();
|
io::IO::enable_raw();
|
||||||
if (pieces.size() == 1)
|
if (pieces.size() == 1)
|
||||||
@@ -303,7 +301,7 @@ Shard *Shard::from_command(const char *cmd, bool posix_ending) {
|
|||||||
return build(pieces.data(), 0, pieces.size());
|
return build(pieces.data(), 0, pieces.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
Shard *Shard::from_file(const std::filesystem::path &path, bool posix_ending) {
|
Shard *Shard::from_file(const std::filesystem::path &path) {
|
||||||
auto o = new OriginalStorage("/tmp");
|
auto o = new OriginalStorage("/tmp");
|
||||||
int dest_fd = o->fd;
|
int dest_fd = o->fd;
|
||||||
if (dest_fd == -1) {
|
if (dest_fd == -1) {
|
||||||
@@ -313,10 +311,10 @@ Shard *Shard::from_file(const std::filesystem::path &path, bool posix_ending) {
|
|||||||
int src_fd = open(path.c_str(), O_RDONLY);
|
int src_fd = open(path.c_str(), O_RDONLY);
|
||||||
if (src_fd == -1) {
|
if (src_fd == -1) {
|
||||||
delete o;
|
delete o;
|
||||||
return nullptr;
|
throw ed_error("Couldn't open file.");
|
||||||
}
|
}
|
||||||
uint64_t total = std::filesystem::file_size(path);
|
uint64_t total = std::filesystem::file_size(path);
|
||||||
if (posix_ending && total > 0) {
|
if (total > 0) {
|
||||||
char last;
|
char last;
|
||||||
if (pread(src_fd, &last, 1, (off_t)(total - 1)) != 1) {
|
if (pread(src_fd, &last, 1, (off_t)(total - 1)) != 1) {
|
||||||
delete o;
|
delete o;
|
||||||
@@ -381,7 +379,7 @@ Shard *Shard::from_file(const std::filesystem::path &path, bool posix_ending) {
|
|||||||
return build(pieces.data(), 0, pieces.size());
|
return build(pieces.data(), 0, pieces.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
Shard *Shard::from_string(const char *data, uint64_t len, bool posix_ending) {
|
Shard *Shard::from_string(const char *data, uint64_t len) {
|
||||||
auto o = new OriginalStorage("/tmp");
|
auto o = new OriginalStorage("/tmp");
|
||||||
int dest_fd = o->fd;
|
int dest_fd = o->fd;
|
||||||
if (dest_fd == -1 || data == nullptr) {
|
if (dest_fd == -1 || data == nullptr) {
|
||||||
@@ -389,7 +387,7 @@ Shard *Shard::from_string(const char *data, uint64_t len, bool posix_ending) {
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
uint64_t total = len;
|
uint64_t total = len;
|
||||||
if (posix_ending && total > 0) {
|
if (total > 0) {
|
||||||
if (data[total - 1] == '\n') {
|
if (data[total - 1] == '\n') {
|
||||||
total--;
|
total--;
|
||||||
if (total > 0 && data[total - 1] == '\r')
|
if (total > 0 && data[total - 1] == '\r')
|
||||||
|
|||||||
@@ -182,8 +182,6 @@ Shard *insert(AppendStorage *ap, Shard *root, Shard *text, uint64_t line) {
|
|||||||
Shard::retain(text);
|
Shard::retain(text);
|
||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
if (!text)
|
|
||||||
return root;
|
|
||||||
if (line > root->lines + 1)
|
if (line > root->lines + 1)
|
||||||
throw ed_error("line out of range");
|
throw ed_error("line out of range");
|
||||||
Shard::retain(text);
|
Shard::retain(text);
|
||||||
|
|||||||
Reference in New Issue
Block a user