Fix minor bugs and add language detection.

This commit is contained in:
2026-09-25 13:23:57 +01:00
parent 6711ecbaee
commit 8d6f7d323e
14 changed files with 230 additions and 90 deletions
+2 -1
View File
@@ -59,4 +59,5 @@ It should support:
### TODO: ### TODO:
- Make "g" command work. - Make "g" command work.
- properly handle escapes for %q ' etc in ruby parser (rn everything is escapable.) - Switch to proper posix style Shard objects (to distinguish 1 empty line from no lines.)
- Use proper ruby binding context to keep locals across <pre>`</pre> function calls.
+2
View File
@@ -20,6 +20,8 @@ struct BEd {
internal::functions::Function eof_op; internal::functions::Function eof_op;
std::array<std::optional<internal::functions::Suffix>, 26> suffixes; std::array<std::optional<internal::functions::Suffix>, 26> suffixes;
internal::theme::Theme theme; internal::theme::Theme theme;
std::unordered_map<std::string, std::string> extention_to_language;
std::unordered_map<std::string, std::string> filename_to_language;
std::unordered_map<std::string, internal::syntax::Language *> languages; std::unordered_map<std::string, internal::syntax::Language *> languages;
internal::io::IO io; internal::io::IO io;
internal::vase::AppendStorage append{"/tmp"}; internal::vase::AppendStorage append{"/tmp"};
+1 -1
View File
@@ -33,7 +33,7 @@ struct Buffer {
virtual bool waste() = 0; virtual bool waste() = 0;
virtual uint64_t lines() = 0; virtual uint64_t lines() = 0;
virtual uint64_t bytes() = 0; virtual uint64_t bytes() = 0;
virtual void set_filename(std::filesystem::path path) = 0; virtual void set_filename(BEd &ctx, std::filesystem::path path) = 0;
virtual std::filesystem::path filename() = 0; virtual std::filesystem::path filename() = 0;
virtual void saved_hook() = 0; virtual void saved_hook() = 0;
virtual void load(BEd &ctx, vase::Shard *text) = 0; virtual void load(BEd &ctx, vase::Shard *text) = 0;
+1 -1
View File
@@ -14,7 +14,7 @@ struct ClipBuffer : Buffer {
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;
void set_filename(std::filesystem::path path) override; void set_filename(BEd &ctx, std::filesystem::path path) override;
std::filesystem::path filename() override; std::filesystem::path filename() override;
vase::Shard *copy(uint64_t start_line, uint64_t end_line) override; vase::Shard *copy(uint64_t start_line, uint64_t end_line) override;
void substitute( void substitute(
+1 -1
View File
@@ -34,7 +34,7 @@ struct GenericBuffer : ShardBuffer {
void saved_hook() override; 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(BEd &ctx, std::filesystem::path path) override;
std::filesystem::path filename() override; std::filesystem::path filename() override;
void substitute( void substitute(
BEd &ctx, uint64_t start_line, uint64_t end_line, BEd &ctx, uint64_t start_line, uint64_t end_line,
+1 -1
View File
@@ -19,7 +19,7 @@ struct ReadonlyBuffer : ShardBuffer {
void load(BEd &, vase::Shard *) override { void load(BEd &, vase::Shard *) override {
throw ed_error("This buffer is read-only."); throw ed_error("This buffer is read-only.");
} }
void set_filename(std::filesystem::path) override {} void set_filename(BEd &, std::filesystem::path) override {}
std::filesystem::path filename() override { std::filesystem::path filename() override {
return {}; return {};
} }
+3
View File
@@ -6,6 +6,9 @@
#include "storage/storage.h" #include "storage/storage.h"
namespace bed::internal::vase { namespace bed::internal::vase {
// TODO: rewrite with invariant, text ends with a newline, as right now both 1 real and empty line and no text mean teh same thing,
// which causes a ton of issues.
struct Shard { struct Shard {
enum struct Kind : uint8_t { enum struct Kind : uint8_t {
Branch, Branch,
+7
View File
@@ -47,7 +47,14 @@ BEd::BEd(std::vector<std::string> args)
internal::functions::Suffix::register_suffixes(*this); internal::functions::Suffix::register_suffixes(*this);
internal::scripting::register_basic(*this); internal::scripting::register_basic(*this);
languages["ruby"] = new internal::syntax::Language(internal::syntax::ruby::lang_ruby()); languages["ruby"] = new internal::syntax::Language(internal::syntax::ruby::lang_ruby());
extention_to_language[".rb"] = "ruby";
languages["c"] = new internal::syntax::Language(internal::syntax::c::lang_c()); languages["c"] = new internal::syntax::Language(internal::syntax::c::lang_c());
extention_to_language[".h"] = "c";
extention_to_language[".hh"] = "c";
extention_to_language[".h++"] = "c";
extention_to_language[".c"] = "c";
extention_to_language[".cc"] = "c";
extention_to_language[".c++"] = "c";
buffers["clip"] = new internal::buffer::ClipBuffer("clip"); buffers["clip"] = new internal::buffer::ClipBuffer("clip");
current() = {"default", 0}; current() = {"default", 0};
try { try {
+8 -2
View File
@@ -249,11 +249,17 @@ void GenericBuffer::load(BEd &ctx, vase::Shard *text) {
ctx.prev().end = text->lines + 1; ctx.prev().end = text->lines + 1;
} }
ctx.current() = {name, lines()}; ctx.current() = {name, lines()};
syntax::Language *lang = parse.lang;
syntax::release(parse); syntax::release(parse);
parse = syntax::make_parser(root, lines(), nullptr); if (save_path.has_extension()) {
auto it = ctx.extention_to_language.find(save_path.extension().string());
if (it != ctx.extention_to_language.end())
lang = ctx.languages[it->second];
}
parse = syntax::make_parser(root, lines(), lang);
} }
void GenericBuffer::set_filename(std::filesystem::path path) { void GenericBuffer::set_filename(BEd &, std::filesystem::path path) {
save_path = path; save_path = path;
} }
+1 -1
View File
@@ -47,7 +47,7 @@ void ClipBuffer::load(BEd &ctx, vase::Shard *text) {
} }
} }
void ClipBuffer::set_filename(std::filesystem::path) {} void ClipBuffer::set_filename(BEd &, std::filesystem::path) {}
std::filesystem::path ClipBuffer::filename() { std::filesystem::path ClipBuffer::filename() {
return ""; return "";
+5 -5
View File
@@ -145,7 +145,7 @@ void Function::register_posix(BEd &ctx) {
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); s = vase::Shard::from_file(path);
buf.set_filename(path); buf.set_filename(ctx, 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());
@@ -191,7 +191,7 @@ void Function::register_posix(BEd &ctx) {
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); s = vase::Shard::from_file(path);
buf.set_filename(path); buf.set_filename(ctx, 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());
@@ -234,7 +234,7 @@ void Function::register_posix(BEd &ctx) {
auto &addr = std::get<std::string>(addr_); auto &addr = std::get<std::string>(addr_);
auto &buf = ctx.buffer(addr); auto &buf = ctx.buffer(addr);
if (std::holds_alternative<std::filesystem::path>(arg)) if (std::holds_alternative<std::filesystem::path>(arg))
buf.set_filename(std::get<std::filesystem::path>(arg)); buf.set_filename(ctx, std::get<std::filesystem::path>(arg));
else if (std::holds_alternative<ShellArg>(arg)) else if (std::holds_alternative<ShellArg>(arg))
throw ed_error("Can't save shell command as save path."); throw ed_error("Can't save shell command as save path.");
if (buf.filename().empty()) if (buf.filename().empty())
@@ -590,7 +590,7 @@ void Function::register_posix(BEd &ctx) {
auto path = std::get<std::filesystem::path>(arg); auto path = std::get<std::filesystem::path>(arg);
s = vase::Shard::from_file(path); s = vase::Shard::from_file(path);
if (buf.filename().empty()) if (buf.filename().empty())
buf.set_filename(path); buf.set_filename(ctx, 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());
@@ -740,7 +740,7 @@ void Function::register_posix(BEd &ctx) {
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);
vase::write_file(path, text); vase::write_file(path, text);
buf.set_filename(path); buf.set_filename(ctx, 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());
+193 -76
View File
@@ -106,6 +106,197 @@ void handle_string(CParser &p, std::vector<io::Token> *tokens, bool string) {
} }
} }
void handle_number(CParser &p, std::vector<io::Token> *tokens) {
bool integer = true;
uint32_t start = p.i;
if (p.peek() == '0') {
p.advance();
if (p.peek() == 'x' || p.peek() == 'X') {
p.advance();
if (!(is_hex(p.peek()) || p.peek() == '.'))
return;
while (is_hex(p.peek())) {
p.advance();
if (p.peek() == '\'') {
p.advance();
if (!(is_hex(p.peek())))
return;
}
}
bool need_p = false;
if (p.peek() == '.') {
need_p = true;
p.advance();
}
while (is_hex(p.peek())) {
p.advance();
if (p.peek() == '\'') {
p.advance();
if (!(is_hex(p.peek())))
return;
}
}
if (p.peek() == 'p' || p.peek() == 'P') {
integer = false;
p.advance();
if (p.peek() == '-' || p.peek() == '+')
p.advance();
if (!('0' <= p.peek() && p.peek() <= '9'))
return;
p.advance();
while ('0' <= p.peek() && p.peek() <= '9') {
p.advance();
if (p.peek() == '\'') {
p.advance();
if (!('0' <= p.peek() && p.peek() <= '9'))
return;
}
}
} else if (need_p) {
return;
}
} else if (p.peek() == 'b' || p.peek() == 'B') {
p.advance();
if (p.peek() != '0' && p.peek() != '1')
return;
p.advance();
while (p.peek() == '0' || p.peek() == '1') {
p.advance();
if (p.peek() == '\'') {
p.advance();
if (!(p.peek() == '0' || p.peek() == '1'))
return;
}
}
} else {
while ('0' <= p.peek() && p.peek() <= '7') {
p.advance();
if (p.peek() == '\'') {
p.advance();
if (!('0' <= p.peek() && p.peek() <= '7'))
return;
}
}
if (p.peek() == '.') {
integer = false;
p.advance();
}
while ('0' <= p.peek() && p.peek() <= '7') {
p.advance();
if (p.peek() == '\'') {
p.advance();
if (!('0' <= p.peek() && p.peek() <= '7'))
return;
}
}
if (p.peek() == 'e' || p.peek() == 'E') {
integer = false;
p.advance();
if (p.peek() == '-' || p.peek() == '+')
p.advance();
if (!('0' <= p.peek() && p.peek() <= '9'))
return;
p.advance();
while ('0' <= p.peek() && p.peek() <= '9') {
p.advance();
if (p.peek() == '\'') {
p.advance();
if (!('0' <= p.peek() && p.peek() <= '9'))
return;
}
}
}
}
} else if (p.peek() == '.') {
integer = false;
p.advance();
goto after_decimal;
} else {
p.advance();
while ('0' <= p.peek() && p.peek() <= '9') {
p.advance();
if (p.peek() == '\'') {
p.advance();
if (!('0' <= p.peek() && p.peek() <= '9'))
return;
}
}
if (p.peek() == '.') {
integer = false;
p.advance();
}
after_decimal:
while ('0' <= p.peek() && p.peek() <= '9') {
p.advance();
if (p.peek() == '\'') {
p.advance();
if (!('0' <= p.peek() && p.peek() <= '9'))
return;
}
}
if (p.peek() == 'e' || p.peek() == 'E') {
integer = false;
p.advance();
if (p.peek() == '-' || p.peek() == '+')
p.advance();
if (!('0' <= p.peek() && p.peek() <= '9'))
return;
p.advance();
while ('0' <= p.peek() && p.peek() <= '9') {
p.advance();
if (p.peek() == '\'') {
p.advance();
if (!('0' <= p.peek() && p.peek() <= '9'))
return;
}
}
}
}
if (integer) {
if (p.peek() == 'u' || p.peek() == 'U') {
p.advance();
if (p.peek() == 'w') {
p.advance();
if (p.peek() != 'b')
return;
p.advance();
}
if (p.peek() == 'W') {
p.advance();
if (p.peek() != 'B')
return;
p.advance();
}
if (p.peek() == 'l' || p.peek() == 'L')
p.advance();
if (p.peek() == 'l' || p.peek() == 'L')
p.advance();
} else if (p.peek() == 'l' || p.peek() == 'L') {
p.advance();
if (p.peek() == 'l' || p.peek() == 'L')
p.advance();
if (p.peek() == 'u' || p.peek() == 'U')
p.advance();
} else if (p.peek_str(2) == "wb" || p.peek_str(2) == "WB") {
p.advance(2);
} else if (p.peek() == 'f' || p.peek() == 'F' || p.peek() == 'l' || p.peek() == 'L') {
p.advance();
} else if ((p.peek() == 'd' || p.peek() == 'D')
&& (p.peek(1) == 'f' || p.peek(1) == 'F' || p.peek(1) == 'd' || p.peek(1) == 'D' || p.peek(1) == 'l' || p.peek(1) == 'L')) {
p.advance(2);
}
} else {
if (p.peek() == 'f' || p.peek() == 'F' || p.peek() == 'l' || p.peek() == 'L')
p.advance();
else if ((p.peek() == 'd' || p.peek() == 'D')
&& (p.peek(1) == 'f' || p.peek(1) == 'F' || p.peek(1) == 'd' || p.peek(1) == 'D' || p.peek(1) == 'l' || p.peek(1) == 'L'))
p.advance(2);
}
if (identifier_char(p.peek()) || p.peek() == '.')
return;
tokens->push_back({start, p.i, io::Token::Number});
}
void handle_syntax(CParser &p, std::vector<io::Token> *tokens, std::vector<ParseEvent> *events) { void handle_syntax(CParser &p, std::vector<io::Token> *tokens, std::vector<ParseEvent> *events) {
if (p.peek() == '/' && p.peek(1) == '/') { if (p.peek() == '/' && p.peek(1) == '/') {
p.current().state = CState::CInternalState::INLINE_COMMENT; p.current().state = CState::CInternalState::INLINE_COMMENT;
@@ -165,82 +356,8 @@ void handle_syntax(CParser &p, std::vector<io::Token> *tokens, std::vector<Parse
} }
default: default:
def: def:
if ('0' <= p.peek() && p.peek() <= '9') { if (('0' <= p.peek() && p.peek() <= '9') || p.peek() == '.') {
// TODO: add floats/doubles handle_number(p, tokens);
uint32_t start = p.i;
if (p.peek() == '0') {
p.advance();
if (p.peek() == 'x' || p.peek() == 'X') {
p.advance();
while (is_hex(p.peek())) {
p.advance();
if (p.peek() == '\'') {
p.advance();
if (!(is_hex(p.peek())))
return;
}
}
} else if (p.peek() == 'b' || p.peek() == 'B') {
p.advance();
while (p.peek() == '0' || p.peek() == '1') {
p.advance();
if (p.peek() == '\'') {
p.advance();
if (!(p.peek() == '0' || p.peek() == '1'))
return;
}
}
} else {
while ('0' <= p.peek() && p.peek() <= '7') {
p.advance();
if (p.peek() == '\'') {
p.advance();
if (!('0' <= p.peek() && p.peek() <= '7'))
return;
}
}
}
} else {
p.advance();
while ('0' <= p.peek() && p.peek() <= '9') {
p.advance();
if (p.peek() == '\'') {
p.advance();
if (!('0' <= p.peek() && p.peek() <= '9'))
return;
}
}
}
if (p.peek() == 'u' || p.peek() == 'U') {
p.advance();
if (p.peek() == 'w') {
p.advance();
if (p.peek() != 'b')
return;
p.advance();
}
if (p.peek() == 'W') {
p.advance();
if (p.peek() != 'B')
return;
p.advance();
}
if (p.peek() == 'l' || p.peek() == 'L')
p.advance();
if (p.peek() == 'l' || p.peek() == 'L')
p.advance();
} else if (p.peek() == 'l' || p.peek() == 'L') {
p.advance();
if (p.peek() == 'l' || p.peek() == 'L')
p.advance();
if (p.peek() == 'u' || p.peek() == 'U')
p.advance();
} else if (p.peek_str(2) == "wb" || p.peek_str(2) == "WB") {
p.advance(2);
} else if (identifier_char(p.peek())) {
return;
}
tokens->push_back({start, p.i, io::Token::Number});
return; return;
} }
p.advance(); p.advance();
+1
View File
@@ -1000,6 +1000,7 @@ bool handle_syntax(RubyParser &p, std::vector<io::Token> *tokens, std::vector<Pa
p.current().flags &= ~RubyState::RubyInternalState::EXPECTING_EXPRESSION; p.current().flags &= ~RubyState::RubyInternalState::EXPECTING_EXPRESSION;
tokens->push_back({p.i, p.i + j, io::Token::Function}); tokens->push_back({p.i, p.i + j, io::Token::Function});
p.advance(j); p.advance(j);
return false;
} }
uint32_t start = p.i; uint32_t start = p.i;
if (p.peek(j) == ':') { if (p.peek(j) == ':') {
+3
View File
@@ -179,7 +179,10 @@ Shard *insert(AppendStorage *ap, Shard *root, Shard *text, uint64_t line) {
if (!root) { if (!root) {
if (line != 0) if (line != 0)
throw ed_error("line out of range"); throw ed_error("line out of range");
if (text)
Shard::retain(text); Shard::retain(text);
else
text = newline(ap);
return text; return text;
} }
if (line > root->lines + 1) if (line > root->lines + 1)