Allow ruby based configs and custom syntax parsers
This commit is contained in:
@@ -4,7 +4,8 @@
|
||||
#include "syntax/langs.h"
|
||||
#include "utils/utils.h"
|
||||
|
||||
Editor *new_editor(const char *filename_arg, Coord position, Coord size) {
|
||||
Editor *new_editor(const char *filename_arg, Coord position, Coord size,
|
||||
bool unix_eol) {
|
||||
Editor *editor = new Editor();
|
||||
if (!editor)
|
||||
return nullptr;
|
||||
@@ -15,6 +16,7 @@ Editor *new_editor(const char *filename_arg, Coord position, Coord size) {
|
||||
free_editor(editor);
|
||||
return nullptr;
|
||||
}
|
||||
editor->unix_eol = unix_eol;
|
||||
editor->filename = filename;
|
||||
editor->uri = path_to_file_uri(filename);
|
||||
editor->position = position;
|
||||
@@ -61,7 +63,15 @@ void save_file(Editor *editor) {
|
||||
return;
|
||||
lock.unlock();
|
||||
std::ofstream out(editor->filename);
|
||||
out.write(str, char_count);
|
||||
if (!editor->unix_eol) {
|
||||
for (uint32_t i = 0; i < char_count; ++i) {
|
||||
if (str[i] == '\n')
|
||||
out.put('\r');
|
||||
out.put(str[i]);
|
||||
}
|
||||
} else {
|
||||
out.write(str, char_count);
|
||||
}
|
||||
out.close();
|
||||
free(str);
|
||||
if (editor->lsp) {
|
||||
@@ -109,7 +119,15 @@ void save_file(Editor *editor) {
|
||||
return;
|
||||
lock.unlock();
|
||||
std::ofstream out(editor->filename);
|
||||
out.write(str, char_count);
|
||||
if (!editor->unix_eol) {
|
||||
for (uint32_t i = 0; i < char_count; ++i) {
|
||||
if (str[i] == '\n')
|
||||
out.put('\r');
|
||||
out.put(str[i]);
|
||||
}
|
||||
} else {
|
||||
out.write(str, char_count);
|
||||
}
|
||||
out.close();
|
||||
free(str);
|
||||
lsp_send(editor->lsp, save_msg, nullptr);
|
||||
|
||||
@@ -186,8 +186,6 @@ void handle_editor_event(Editor *editor, KeyEvent event) {
|
||||
break;
|
||||
case SELECT:
|
||||
if (event.key_type == KEY_CHAR && event.len == 1) {
|
||||
uint32_t len;
|
||||
char *text;
|
||||
switch (event.c[0]) {
|
||||
case 0x1B:
|
||||
case 's':
|
||||
|
||||
@@ -357,7 +357,6 @@ void handle_mouse(Editor *editor, KeyEvent event) {
|
||||
std::chrono::steady_clock::now();
|
||||
static uint32_t click_count = 0;
|
||||
static Coord last_click_pos = {UINT32_MAX, UINT32_MAX};
|
||||
uint8_t old_mode = mode;
|
||||
if (event.key_type == KEY_MOUSE) {
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
|
||||
@@ -7,7 +7,7 @@ void request_add_to_lsp(Language language, Editor *editor) {
|
||||
}
|
||||
|
||||
void add_to_lsp(Language language, Editor *editor) {
|
||||
std::shared_ptr<LSPInstance> lsp = get_or_init_lsp(language.lsp_id);
|
||||
std::shared_ptr<LSPInstance> lsp = get_or_init_lsp(language.lsp_name);
|
||||
if (!lsp)
|
||||
return;
|
||||
std::unique_lock lock(lsp->mtx);
|
||||
@@ -41,11 +41,11 @@ void open_editor(std::shared_ptr<LSPInstance> lsp,
|
||||
lsp_send(lsp, message, nullptr);
|
||||
}
|
||||
|
||||
static uint8_t find_lsp_id(std::shared_ptr<LSPInstance> needle) {
|
||||
static std::string find_lsp_id(std::shared_ptr<LSPInstance> needle) {
|
||||
for (const auto &[id, lsp] : active_lsps)
|
||||
if (lsp == needle)
|
||||
return id;
|
||||
return 0;
|
||||
return "";
|
||||
}
|
||||
|
||||
void remove_from_lsp(Editor *editor) {
|
||||
@@ -64,8 +64,8 @@ void remove_from_lsp(Editor *editor) {
|
||||
{"method", "textDocument/didClose"},
|
||||
{"params", {{"textDocument", {{"uri", editor->uri}}}}}};
|
||||
lsp_send(lsp, message, nullptr);
|
||||
uint8_t lsp_id = find_lsp_id(lsp);
|
||||
if (lsp_id && lsp->editors.empty())
|
||||
std::string lsp_id = find_lsp_id(lsp);
|
||||
if (!lsp_id.empty() && lsp->editors.empty())
|
||||
close_lsp(lsp_id);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#include "config.h"
|
||||
#include "lsp/lsp.h"
|
||||
|
||||
static bool init_lsp(std::shared_ptr<LSPInstance> lsp) {
|
||||
@@ -25,7 +24,12 @@ static bool init_lsp(std::shared_ptr<LSPInstance> lsp) {
|
||||
close(in_pipe[1]);
|
||||
close(out_pipe[0]);
|
||||
close(out_pipe[1]);
|
||||
execvp(lsp->lsp->command, (char *const *)(lsp->lsp->args.data()));
|
||||
std::vector<char *> argv;
|
||||
argv.push_back(const_cast<char *>(lsp->lsp->command.c_str()));
|
||||
for (auto &arg : lsp->lsp->args)
|
||||
argv.push_back(const_cast<char *>(arg.c_str()));
|
||||
argv.push_back(nullptr);
|
||||
execvp(lsp->lsp->command.c_str(), argv.data());
|
||||
perror("execvp");
|
||||
_exit(127);
|
||||
}
|
||||
@@ -37,12 +41,12 @@ static bool init_lsp(std::shared_ptr<LSPInstance> lsp) {
|
||||
return true;
|
||||
}
|
||||
|
||||
std::shared_ptr<LSPInstance> get_or_init_lsp(uint8_t lsp_id) {
|
||||
std::shared_ptr<LSPInstance> get_or_init_lsp(std::string lsp_id) {
|
||||
std::unique_lock lock(active_lsps_mtx);
|
||||
auto it = active_lsps.find(lsp_id);
|
||||
if (it == active_lsps.end()) {
|
||||
auto map_it = kLsps.find(lsp_id);
|
||||
if (map_it == kLsps.end())
|
||||
auto map_it = lsps.find(lsp_id);
|
||||
if (map_it == lsps.end())
|
||||
return nullptr;
|
||||
std::shared_ptr<LSPInstance> lsp = std::make_shared<LSPInstance>();
|
||||
lsp->lsp = &map_it->second;
|
||||
@@ -54,12 +58,13 @@ std::shared_ptr<LSPInstance> get_or_init_lsp(uint8_t lsp_id) {
|
||||
pending->callback = [lsp, lsp_id](Editor *, std::string, json msg) {
|
||||
if (msg.contains("result") && msg["result"].contains("capabilities")) {
|
||||
auto &caps = msg["result"]["capabilities"];
|
||||
if (caps.contains("positionEncoding")) {
|
||||
std::string s = caps["positionEncoding"].get<std::string>();
|
||||
if (s == "utf-8")
|
||||
lsp->is_utf8 = true;
|
||||
log("Lsp name: %s, supports: %s", lsp->lsp->command, s.c_str());
|
||||
}
|
||||
// if (caps.contains("positionEncoding")) {
|
||||
// std::string s = caps["positionEncoding"].get<std::string>();
|
||||
// if (s == "utf-8")
|
||||
// lsp->is_utf8 = true;
|
||||
// log("Lsp name: %s, supports: %s", lsp->lsp->command.c_str(),
|
||||
// s.c_str());
|
||||
// }
|
||||
if (caps.contains("textDocumentSync")) {
|
||||
auto &sync = caps["textDocumentSync"];
|
||||
if (sync.is_number()) {
|
||||
@@ -71,8 +76,8 @@ std::shared_ptr<LSPInstance> get_or_init_lsp(uint8_t lsp_id) {
|
||||
}
|
||||
}
|
||||
lsp->allow_formatting = caps.value("documentFormattingProvider", false);
|
||||
if (lsp_id !=
|
||||
LUA_LS /* Lua ls gives terrible ontype formatting so disable */
|
||||
if (lsp_id != "lua-language-server" /* Lua ls gives terrible ontype
|
||||
formatting so disable */
|
||||
&& caps.contains("documentOnTypeFormattingProvider")) {
|
||||
auto &fmt = caps["documentOnTypeFormattingProvider"];
|
||||
if (fmt.is_object()) {
|
||||
@@ -149,7 +154,7 @@ std::shared_ptr<LSPInstance> get_or_init_lsp(uint8_t lsp_id) {
|
||||
return it->second;
|
||||
}
|
||||
|
||||
void close_lsp(uint8_t lsp_id) {
|
||||
void close_lsp(std::string lsp_id) {
|
||||
std::shared_lock active_lsps_lock(active_lsps_mtx);
|
||||
auto it = active_lsps.find(lsp_id);
|
||||
if (it == active_lsps.end())
|
||||
@@ -186,7 +191,7 @@ void close_lsp(uint8_t lsp_id) {
|
||||
t.detach();
|
||||
}
|
||||
|
||||
void clean_lsp(std::shared_ptr<LSPInstance> lsp, uint8_t lsp_id) {
|
||||
void clean_lsp(std::shared_ptr<LSPInstance> lsp, std::string lsp_id) {
|
||||
for (auto &kv : lsp->pending)
|
||||
delete kv.second;
|
||||
lsp->pid = -1;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "lsp/lsp.h"
|
||||
|
||||
std::shared_mutex active_lsps_mtx;
|
||||
std::unordered_map<uint8_t, std::shared_ptr<LSPInstance>> active_lsps;
|
||||
std::unordered_map<std::string, std::shared_ptr<LSPInstance>> active_lsps;
|
||||
|
||||
void lsp_send(std::shared_ptr<LSPInstance> lsp, json message,
|
||||
LSPPending *pending) {
|
||||
|
||||
128
src/main.cc
128
src/main.cc
@@ -2,6 +2,7 @@
|
||||
#include "editor/editor.h"
|
||||
#include "io/sysio.h"
|
||||
#include "lsp/lsp.h"
|
||||
#include "scripting/decl.h"
|
||||
#include "ui/bar.h"
|
||||
#include "utils/utils.h"
|
||||
|
||||
@@ -12,32 +13,12 @@ std::vector<Editor *> editors;
|
||||
uint8_t current_editor = 0;
|
||||
std::atomic<uint8_t> mode = NORMAL;
|
||||
|
||||
void background_worker() {
|
||||
while (running)
|
||||
throttle(16ms, editor_worker, editors[current_editor]);
|
||||
}
|
||||
|
||||
void background_lsp() {
|
||||
while (running)
|
||||
throttle(8ms, lsp_worker);
|
||||
}
|
||||
|
||||
void input_listener() {
|
||||
while (running) {
|
||||
KeyEvent event = throttle(1ms, read_key);
|
||||
if (event.key_type == KEY_NONE)
|
||||
continue;
|
||||
if (event.key_type == KEY_CHAR && event.len == 1 &&
|
||||
event.c[0] == CTRL('q')) {
|
||||
free(event.c);
|
||||
running = false;
|
||||
return;
|
||||
}
|
||||
event_queue.push(event);
|
||||
}
|
||||
}
|
||||
|
||||
Editor *editor_at(uint8_t x, uint8_t y) {
|
||||
inline Editor *editor_at(uint8_t x, uint8_t y) {
|
||||
for (Editor *ed : editors) {
|
||||
Coord pos = ed->position;
|
||||
Coord size = ed->size;
|
||||
@@ -48,30 +29,73 @@ Editor *editor_at(uint8_t x, uint8_t y) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uint8_t index_of(Editor *ed) {
|
||||
inline uint8_t index_of(Editor *ed) {
|
||||
for (uint8_t i = 0; i < editors.size(); i++)
|
||||
if (editors[i] == ed)
|
||||
return i;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void input_listener(Bar bar) {
|
||||
while (running) {
|
||||
KeyEvent event = throttle(1ms, read_key);
|
||||
if (event.key_type == KEY_NONE)
|
||||
goto render;
|
||||
if (event.key_type == KEY_CHAR && event.len == 1 &&
|
||||
event.c[0] == CTRL('q')) {
|
||||
free(event.c);
|
||||
running = false;
|
||||
break;
|
||||
}
|
||||
if (mode != RUNNER) {
|
||||
if (event.key_type == KEY_MOUSE) {
|
||||
Editor *target = editor_at(event.mouse_x, event.mouse_y);
|
||||
if (target) {
|
||||
if (event.mouse_state == PRESS)
|
||||
current_editor = index_of(target);
|
||||
|
||||
event.mouse_x -= target->position.col;
|
||||
event.mouse_y -= target->position.row;
|
||||
handle_editor_event(target, event);
|
||||
}
|
||||
} else {
|
||||
handle_editor_event(editors[current_editor], event);
|
||||
}
|
||||
} else {
|
||||
bar.handle(event);
|
||||
}
|
||||
render:
|
||||
render_editor(editors[current_editor]);
|
||||
bar.render();
|
||||
throttle(4ms, render);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
auto start = std::chrono::high_resolution_clock::now();
|
||||
|
||||
ruby_init();
|
||||
|
||||
ruby_start((get_exe_dir() + "/../config/main.rb").c_str());
|
||||
load_theme();
|
||||
load_languages_info();
|
||||
load_custom_highlighters();
|
||||
|
||||
Coord screen = start_screen();
|
||||
const char *filename = (argc > 1) ? argv[1] : "";
|
||||
|
||||
int state;
|
||||
VALUE result;
|
||||
result = rb_eval_string_protect("puts 'Hello, world!'", &state);
|
||||
bool unix_eol = read_line_endings();
|
||||
|
||||
if (state) {
|
||||
/* handle exception */
|
||||
}
|
||||
|
||||
load_theme();
|
||||
|
||||
Editor *editor = new_editor(filename, {0, 0}, {screen.row - 2, screen.col});
|
||||
Editor *editor =
|
||||
new_editor(filename, {0, 0}, {screen.row - 2, screen.col}, unix_eol);
|
||||
Bar bar(screen);
|
||||
|
||||
auto end = std::chrono::high_resolution_clock::now();
|
||||
auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(end - start)
|
||||
.count();
|
||||
ruby_log("[LOG] STARTUP_TIME: " + std::to_string(static_cast<long long>(ms)) +
|
||||
"ms");
|
||||
|
||||
if (!editor) {
|
||||
end_screen();
|
||||
fprintf(stderr, "Failed to load editor\n");
|
||||
@@ -81,48 +105,22 @@ int main(int argc, char *argv[]) {
|
||||
editors.push_back(editor);
|
||||
current_editor = editors.size() - 1;
|
||||
|
||||
std::thread input_thread(input_listener);
|
||||
std::thread work_thread(background_worker);
|
||||
std::thread input_thread(input_listener, bar);
|
||||
std::thread lsp_thread(background_lsp);
|
||||
|
||||
while (running) {
|
||||
KeyEvent event;
|
||||
while (event_queue.pop(event)) {
|
||||
if (mode != RUNNER) {
|
||||
if (event.key_type == KEY_MOUSE) {
|
||||
Editor *target = editor_at(event.mouse_x, event.mouse_y);
|
||||
if (!target)
|
||||
continue;
|
||||
if (event.mouse_state == PRESS)
|
||||
current_editor = index_of(target);
|
||||
event.mouse_x -= target->position.col;
|
||||
event.mouse_y -= target->position.row;
|
||||
handle_editor_event(target, event);
|
||||
} else {
|
||||
handle_editor_event(editors[current_editor], event);
|
||||
}
|
||||
} else {
|
||||
bar.handle(event);
|
||||
}
|
||||
}
|
||||
render_editor(editors[current_editor]);
|
||||
bar.render();
|
||||
throttle(4ms, render);
|
||||
}
|
||||
while (running)
|
||||
throttle(16ms, editor_worker, editors[current_editor]);
|
||||
|
||||
if (input_thread.joinable())
|
||||
input_thread.join();
|
||||
|
||||
if (work_thread.joinable())
|
||||
work_thread.join();
|
||||
|
||||
if (lsp_thread.joinable())
|
||||
lsp_thread.join();
|
||||
|
||||
system(("bash " + get_exe_dir() + "/../scripts/exit.sh").c_str());
|
||||
|
||||
end_screen();
|
||||
|
||||
ruby_shutdown();
|
||||
|
||||
for (auto editor : editors)
|
||||
free_editor(editor);
|
||||
|
||||
@@ -136,5 +134,7 @@ int main(int argc, char *argv[]) {
|
||||
throttle(16ms, lsp_worker);
|
||||
}
|
||||
|
||||
return 0;
|
||||
rb_gc_start();
|
||||
|
||||
return ruby_cleanup(0);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
#include "ruby/internal/gc.h"
|
||||
#include "ruby/internal/value.h"
|
||||
#include "scripting/decl.h"
|
||||
#include "syntax/decl.h"
|
||||
#include "utils/utils.h"
|
||||
|
||||
struct ThemeEntry {
|
||||
std::unordered_map<std::string, std::pair<VALUE, VALUE>> custom_highlighters;
|
||||
|
||||
struct R_ThemeEntry {
|
||||
std::string key;
|
||||
uint32_t fg = 0xFFFFFF;
|
||||
uint32_t bg = 0x000000;
|
||||
@@ -12,18 +15,29 @@ struct ThemeEntry {
|
||||
bool strikethrough = false;
|
||||
};
|
||||
|
||||
struct R_Language {
|
||||
std::string name;
|
||||
uint32_t color = 0xFFFFFF;
|
||||
std::string symbol;
|
||||
std::vector<std::string> extensions;
|
||||
std::vector<std::string> filenames;
|
||||
std::vector<std::string> mimetypes;
|
||||
std::string lsp_command; // link to LSP by name
|
||||
};
|
||||
|
||||
VALUE C_module = Qnil;
|
||||
std::mutex ruby_mutex;
|
||||
|
||||
void ruby_start(const char *main_file) {
|
||||
USING(Language);
|
||||
ruby_init();
|
||||
std::lock_guard lock(ruby_mutex);
|
||||
ruby_init_loadpath();
|
||||
int state = 0;
|
||||
rb_load_protect(rb_str_new_cstr(main_file), 0, &state);
|
||||
if (state) {
|
||||
VALUE err = rb_errinfo();
|
||||
rb_errinfo();
|
||||
rb_set_errinfo(Qnil);
|
||||
fprintf(stderr, "Failed to load Ruby file\n");
|
||||
fprintf(stderr, "%d: Failed to load Ruby file\n", state);
|
||||
return;
|
||||
}
|
||||
C_module = rb_const_get(rb_cObject, rb_intern("C"));
|
||||
if (C_module == Qnil)
|
||||
@@ -34,16 +48,86 @@ void ruby_start(const char *main_file) {
|
||||
}
|
||||
|
||||
void ruby_shutdown() {
|
||||
std::lock_guard lock(ruby_mutex);
|
||||
if (C_module == Qnil)
|
||||
return;
|
||||
VALUE block = rb_funcall(C_module, rb_intern("b_shutdown"), 0);
|
||||
if (block != Qnil)
|
||||
rb_funcall(block, rb_intern("call"), 0);
|
||||
ruby_finalize();
|
||||
}
|
||||
|
||||
static std::vector<ThemeEntry> read_theme() {
|
||||
std::vector<ThemeEntry> result;
|
||||
inline std::vector<std::string> ruby_array_to_vector(VALUE rb_array) {
|
||||
std::vector<std::string> result;
|
||||
if (NIL_P(rb_array) || !RB_TYPE_P(rb_array, T_ARRAY))
|
||||
return result;
|
||||
for (long i = 0; i < RARRAY_LEN(rb_array); ++i) {
|
||||
VALUE item = rb_ary_entry(rb_array, i);
|
||||
if (RB_TYPE_P(item, T_STRING))
|
||||
result.push_back(StringValueCStr(item));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void ruby_log(std::string msg) {
|
||||
std::lock_guard lock(ruby_mutex);
|
||||
VALUE str = rb_str_new(msg.c_str(), msg.size());
|
||||
rb_funcall(C_module, rb_intern("queue_log"), 1, str);
|
||||
}
|
||||
|
||||
void load_custom_highlighters() {
|
||||
std::lock_guard lock(ruby_mutex);
|
||||
if (C_module == Qnil)
|
||||
return;
|
||||
VALUE hashmap = rb_funcall(C_module, rb_intern("highlighters"), 0);
|
||||
if (NIL_P(hashmap))
|
||||
return;
|
||||
VALUE keys = rb_funcall(hashmap, rb_intern("keys"), 0);
|
||||
for (long i = 0; i < RARRAY_LEN(keys); ++i) {
|
||||
VALUE key_sym = rb_ary_entry(keys, i);
|
||||
std::string key = rb_id2name(SYM2ID(key_sym));
|
||||
VALUE val_hash = rb_hash_aref(hashmap, key_sym);
|
||||
if (NIL_P(val_hash))
|
||||
continue;
|
||||
VALUE parse_block = rb_hash_aref(val_hash, ID2SYM(rb_intern("parser")));
|
||||
VALUE match_block = rb_hash_aref(val_hash, ID2SYM(rb_intern("matcher")));
|
||||
rb_gc_register_address(&match_block);
|
||||
rb_gc_register_address(&parse_block);
|
||||
custom_highlighters[key] = {parse_block, match_block};
|
||||
}
|
||||
}
|
||||
|
||||
bool custom_compare(VALUE match_block, VALUE state1, VALUE state2) {
|
||||
std::lock_guard lock(ruby_mutex);
|
||||
return RTEST(rb_funcall(match_block, rb_intern("call"), 2, state1, state2));
|
||||
}
|
||||
|
||||
VALUE parse_custom(std::vector<Token> *tokens, VALUE parser_block,
|
||||
const char *line, uint32_t len, VALUE state) {
|
||||
std::lock_guard lock(ruby_mutex);
|
||||
tokens->clear();
|
||||
if (NIL_P(parser_block))
|
||||
return {};
|
||||
VALUE ruby_line = rb_str_new(line, len);
|
||||
VALUE tokens_and_state_hash =
|
||||
rb_funcall(parser_block, rb_intern("call"), 2, ruby_line, state);
|
||||
VALUE tokens_rb =
|
||||
rb_hash_aref(tokens_and_state_hash, ID2SYM(rb_intern("tokens")));
|
||||
for (long i = 0; i < RARRAY_LEN(tokens_rb); ++i) {
|
||||
VALUE token = rb_ary_entry(tokens_rb, i);
|
||||
Token tok;
|
||||
tok.type =
|
||||
(TokenKind)NUM2INT(rb_hash_aref(token, ID2SYM(rb_intern("type"))));
|
||||
tok.start = NUM2UINT(rb_hash_aref(token, ID2SYM(rb_intern("start"))));
|
||||
tok.end = NUM2UINT(rb_hash_aref(token, ID2SYM(rb_intern("end"))));
|
||||
if (tok.type < TokenKind::Count && tok.end > tok.start && tok.end <= len)
|
||||
tokens->push_back(tok);
|
||||
}
|
||||
return rb_hash_aref(tokens_and_state_hash, ID2SYM(rb_intern("state")));
|
||||
}
|
||||
|
||||
static std::vector<R_ThemeEntry> read_theme() {
|
||||
std::lock_guard lock(ruby_mutex);
|
||||
std::vector<R_ThemeEntry> result;
|
||||
if (C_module == Qnil)
|
||||
return result;
|
||||
VALUE theme_hash = rb_funcall(C_module, rb_intern("theme"), 0);
|
||||
@@ -56,7 +140,7 @@ static std::vector<ThemeEntry> read_theme() {
|
||||
VALUE val_hash = rb_hash_aref(theme_hash, key_sym);
|
||||
if (NIL_P(val_hash))
|
||||
continue;
|
||||
ThemeEntry entry;
|
||||
R_ThemeEntry entry;
|
||||
entry.key = key;
|
||||
VALUE fg = rb_hash_aref(val_hash, ID2SYM(rb_intern("fg")));
|
||||
VALUE bg = rb_hash_aref(val_hash, ID2SYM(rb_intern("bg")));
|
||||
@@ -83,7 +167,7 @@ static std::vector<ThemeEntry> read_theme() {
|
||||
}
|
||||
|
||||
void load_theme() {
|
||||
std::vector<ThemeEntry> entries = read_theme();
|
||||
std::vector<R_ThemeEntry> entries = read_theme();
|
||||
Highlight default_hl = {0xFFFFFF, 0, 0};
|
||||
for (auto &entry : entries) {
|
||||
if (entry.key == "default") {
|
||||
@@ -126,20 +210,89 @@ void load_theme() {
|
||||
}
|
||||
}
|
||||
|
||||
std::string read_line_endings() {
|
||||
std::vector<LSP> read_lsps() {
|
||||
std::vector<LSP> result;
|
||||
if (C_module == Qnil)
|
||||
return "";
|
||||
VALUE le = rb_funcall(C_module, rb_intern("line_endings"), 0);
|
||||
if (SYMBOL_P(le))
|
||||
return rb_id2name(SYM2ID(le));
|
||||
return "";
|
||||
return result;
|
||||
VALUE lsp_hash = rb_funcall(C_module, rb_intern("lsp_config"), 0);
|
||||
if (NIL_P(lsp_hash))
|
||||
return result;
|
||||
VALUE keys = rb_funcall(lsp_hash, rb_intern("keys"), 0);
|
||||
for (long i = 0; i < RARRAY_LEN(keys); ++i) {
|
||||
VALUE key = rb_ary_entry(keys, i);
|
||||
std::string cmd = StringValueCStr(key);
|
||||
VALUE args_array = rb_hash_aref(lsp_hash, key);
|
||||
std::vector<std::string> args = ruby_array_to_vector(args_array);
|
||||
result.push_back({cmd, args});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string read_utf_mode() {
|
||||
std::vector<R_Language> read_languages() {
|
||||
std::vector<R_Language> result;
|
||||
if (C_module == Qnil)
|
||||
return "";
|
||||
VALUE utf = rb_funcall(C_module, rb_intern("utf_mode"), 0);
|
||||
if (SYMBOL_P(utf))
|
||||
return rb_id2name(SYM2ID(utf));
|
||||
return "";
|
||||
return result;
|
||||
VALUE lang_hash = rb_funcall(C_module, rb_intern("languages"), 0);
|
||||
if (NIL_P(lang_hash))
|
||||
return result;
|
||||
VALUE keys = rb_funcall(lang_hash, rb_intern("keys"), 0);
|
||||
for (long i = 0; i < RARRAY_LEN(keys); ++i) {
|
||||
VALUE key = rb_ary_entry(keys, i);
|
||||
VALUE val_hash = rb_hash_aref(lang_hash, key);
|
||||
if (NIL_P(val_hash))
|
||||
continue;
|
||||
R_Language lang;
|
||||
lang.name = rb_id2name(SYM2ID(key));
|
||||
VALUE fg = rb_hash_aref(val_hash, ID2SYM(rb_intern("color")));
|
||||
VALUE symbol = rb_hash_aref(val_hash, ID2SYM(rb_intern("symbol")));
|
||||
VALUE extensions = rb_hash_aref(val_hash, ID2SYM(rb_intern("extensions")));
|
||||
VALUE filenames = rb_hash_aref(val_hash, ID2SYM(rb_intern("filenames")));
|
||||
VALUE mimetypes = rb_hash_aref(val_hash, ID2SYM(rb_intern("mimetypes")));
|
||||
VALUE lsp = rb_hash_aref(val_hash, ID2SYM(rb_intern("lsp")));
|
||||
if (!NIL_P(fg))
|
||||
lang.color = NUM2UINT(fg);
|
||||
if (!NIL_P(symbol))
|
||||
lang.symbol = StringValueCStr(symbol);
|
||||
lang.extensions = ruby_array_to_vector(extensions);
|
||||
lang.filenames = ruby_array_to_vector(filenames);
|
||||
lang.mimetypes = ruby_array_to_vector(mimetypes);
|
||||
if (!NIL_P(lsp))
|
||||
lang.lsp_command = StringValueCStr(lsp);
|
||||
result.push_back(lang);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void load_languages_info() {
|
||||
std::lock_guard lock(ruby_mutex);
|
||||
auto langs = read_languages();
|
||||
auto lsps_t = read_lsps();
|
||||
languages.clear();
|
||||
for (auto &lang : langs) {
|
||||
Language l;
|
||||
l.name = lang.name;
|
||||
l.color = lang.color;
|
||||
l.lsp_name = lang.lsp_command;
|
||||
l.symbol = lang.symbol;
|
||||
languages[lang.name] = l;
|
||||
for (auto &ext : lang.extensions)
|
||||
language_extensions[ext] = lang.name;
|
||||
// TODO: seperate extensions and filenames
|
||||
for (auto &filename : lang.filenames)
|
||||
language_extensions[filename] = lang.name;
|
||||
for (auto &mimetype : lang.mimetypes)
|
||||
language_mimetypes[mimetype] = lang.name;
|
||||
}
|
||||
for (auto &lsp : lsps_t)
|
||||
lsps[lsp.command] = lsp;
|
||||
}
|
||||
|
||||
bool read_line_endings() {
|
||||
std::lock_guard lock(ruby_mutex);
|
||||
if (C_module == Qnil)
|
||||
return true;
|
||||
VALUE le = rb_funcall(C_module, rb_intern("line_endings"), 0);
|
||||
if (SYMBOL_P(le))
|
||||
return std::string(rb_id2name(SYM2ID(le))) == "unix";
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -68,28 +68,28 @@ std::shared_ptr<void> bash_parse(std::vector<Token> *tokens,
|
||||
uint32_t start = i;
|
||||
while (i < len) {
|
||||
if (text[i] == state->full_state->lit.delim[0]) {
|
||||
tokens->push_back({start, i, TokenKind::String});
|
||||
tokens->push_back({start, i, TokenKind::K_STRING});
|
||||
state->full_state->in_state = BashFullState::NONE;
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
if (i == len)
|
||||
tokens->push_back({start, i, TokenKind::String});
|
||||
tokens->push_back({start, i, TokenKind::K_STRING});
|
||||
continue;
|
||||
}
|
||||
if (text[i] == '#') {
|
||||
if (i == 0 && len > 4 && text[i + 1] == '!') {
|
||||
tokens->push_back({0, len, TokenKind::Shebang});
|
||||
tokens->push_back({0, len, TokenKind::K_SHEBANG});
|
||||
return state;
|
||||
}
|
||||
tokens->push_back({i, len, TokenKind::Comment});
|
||||
tokens->push_back({i, len, TokenKind::K_COMMENT});
|
||||
return state;
|
||||
} else if (text[i] == '\'') {
|
||||
state->full_state->in_state = BashFullState::STRING;
|
||||
state->full_state->lit.delim = "'";
|
||||
state->full_state->lit.allow_interp = false;
|
||||
tokens->push_back({i, ++i, TokenKind::String});
|
||||
tokens->push_back({i, ++i, TokenKind::K_STRING});
|
||||
continue;
|
||||
}
|
||||
i++;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "editor/editor.h"
|
||||
#include "io/knot.h"
|
||||
#include "main.h"
|
||||
#include "ruby/internal/special_consts.h"
|
||||
#include "syntax/decl.h"
|
||||
#include "syntax/langs.h"
|
||||
|
||||
@@ -11,12 +12,20 @@ Parser::Parser(Editor *n_editor, std::string n_lang, uint32_t n_scroll_max) {
|
||||
editor = n_editor;
|
||||
scroll_max = n_scroll_max;
|
||||
lang = n_lang;
|
||||
auto pair = parsers.find(n_lang);
|
||||
if (pair != parsers.end()) {
|
||||
parse_func = std::get<0>(pair->second);
|
||||
state_match_func = std::get<1>(pair->second);
|
||||
auto custom_parser = custom_highlighters.find(n_lang);
|
||||
if (custom_parser != custom_highlighters.end()) {
|
||||
parser_block = custom_parser->second.first;
|
||||
match_block = custom_parser->second.second;
|
||||
is_custom = true;
|
||||
} else {
|
||||
assert("unknown lang should be checked by caller" && 0);
|
||||
auto pair = parsers.find(n_lang);
|
||||
if (pair != parsers.end()) {
|
||||
parse_func = std::get<0>(pair->second);
|
||||
state_match_func = std::get<1>(pair->second);
|
||||
is_custom = false;
|
||||
} else {
|
||||
assert("unknown lang should be checked by caller" && 0);
|
||||
}
|
||||
}
|
||||
edit(0, 0, editor->root->line_count);
|
||||
}
|
||||
@@ -29,9 +38,9 @@ void Parser::edit(uint32_t start_line, uint32_t old_end_line,
|
||||
if (inserted_rows > 0)
|
||||
line_tree.insert(start_line, inserted_rows);
|
||||
if (start_line > 0)
|
||||
dirty_lines.insert(start_line - 1);
|
||||
dirty_lines.insert(start_line);
|
||||
dirty_lines.insert(start_line + 1);
|
||||
dirty_lines.push(start_line - 1);
|
||||
dirty_lines.push(start_line);
|
||||
dirty_lines.push(start_line + 1);
|
||||
}
|
||||
|
||||
void Parser::work() {
|
||||
@@ -41,16 +50,18 @@ void Parser::work() {
|
||||
k_lock.unlock();
|
||||
uint32_t capacity = 256;
|
||||
char *text = (char *)calloc((capacity + 1), sizeof(char));
|
||||
std::set<uint32_t> tmp_dirty;
|
||||
std::unique_lock lock_data(data_mutex);
|
||||
tmp_dirty.swap(dirty_lines);
|
||||
lock_data.unlock();
|
||||
std::set<uint32_t> remaining_dirty;
|
||||
std::unique_lock lock(mutex);
|
||||
lock.unlock();
|
||||
for (uint32_t c_line : tmp_dirty) {
|
||||
uint32_t c_line;
|
||||
while (dirty_lines.pop(c_line)) {
|
||||
if (!running.load(std::memory_order_relaxed)) {
|
||||
free(text);
|
||||
return;
|
||||
}
|
||||
if (c_line > scroll_max + 40) {
|
||||
remaining_dirty.insert(c_line);
|
||||
dirty_lines.push(c_line);
|
||||
continue;
|
||||
}
|
||||
uint32_t line_count = line_tree.count();
|
||||
@@ -65,6 +76,10 @@ void Parser::work() {
|
||||
free(text);
|
||||
return;
|
||||
}
|
||||
if (scroll_dirty.exchange(false, std::memory_order_acq_rel)) {
|
||||
dirty_lines.push(c_line);
|
||||
c_line = scroll_max < 50 ? 0 : scroll_max - 50;
|
||||
}
|
||||
k_lock.lock();
|
||||
if (c_line > editor->root->line_count) {
|
||||
k_lock.unlock();
|
||||
@@ -95,8 +110,22 @@ void Parser::work() {
|
||||
lock.unlock();
|
||||
continue;
|
||||
}
|
||||
std::shared_ptr<void> new_state =
|
||||
parse_func(&line_data->tokens, prev_state, text, r_len);
|
||||
std::shared_ptr<void> new_state{nullptr};
|
||||
if (is_custom) {
|
||||
VALUE state = Qnil;
|
||||
if (prev_state) {
|
||||
std::shared_ptr<CustomState> state_ptr =
|
||||
std::static_pointer_cast<CustomState>(prev_state);
|
||||
state = state_ptr->state;
|
||||
}
|
||||
VALUE out_state =
|
||||
parse_custom(&line_data->tokens, parser_block, text, r_len, state);
|
||||
std::shared_ptr<CustomState> out_state_ptr =
|
||||
std::make_shared<CustomState>(out_state);
|
||||
new_state = out_state_ptr;
|
||||
} else {
|
||||
new_state = parse_func(&line_data->tokens, prev_state, text, r_len);
|
||||
}
|
||||
line_data->in_state = prev_state;
|
||||
line_data->out_state = new_state;
|
||||
if (!running.load(std::memory_order_relaxed)) {
|
||||
@@ -110,16 +139,32 @@ void Parser::work() {
|
||||
if (lock.owns_lock())
|
||||
lock.unlock();
|
||||
if (c_line > 0)
|
||||
remaining_dirty.insert(c_line - 1);
|
||||
remaining_dirty.insert(c_line);
|
||||
dirty_lines.push(c_line - 1);
|
||||
dirty_lines.push(c_line);
|
||||
break;
|
||||
}
|
||||
if (c_line < line_count && (line_data = line_tree.at(c_line)) &&
|
||||
state_match_func(prev_state, line_data->in_state)) {
|
||||
lock_data.unlock();
|
||||
if (lock.owns_lock())
|
||||
lock.unlock();
|
||||
break;
|
||||
if (c_line < line_count && (line_data = line_tree.at(c_line))) {
|
||||
bool done = false;
|
||||
if (is_custom) {
|
||||
VALUE in_state_v = Qnil;
|
||||
if (prev_state)
|
||||
in_state_v =
|
||||
std::static_pointer_cast<CustomState>(prev_state)->state;
|
||||
VALUE out_state_v = Qnil;
|
||||
if (line_data->in_state)
|
||||
out_state_v =
|
||||
std::static_pointer_cast<CustomState>(line_data->in_state)
|
||||
->state;
|
||||
done = custom_compare(match_block, in_state_v, out_state_v);
|
||||
} else {
|
||||
done = state_match_func(prev_state, line_data->in_state);
|
||||
}
|
||||
if (done) {
|
||||
lock_data.unlock();
|
||||
if (lock.owns_lock())
|
||||
lock.unlock();
|
||||
break;
|
||||
}
|
||||
}
|
||||
lock_data.unlock();
|
||||
if (lock.owns_lock())
|
||||
@@ -132,80 +177,19 @@ void Parser::work() {
|
||||
}
|
||||
free(text);
|
||||
lock_data.lock();
|
||||
dirty_lines = std::move(remaining_dirty);
|
||||
}
|
||||
|
||||
void Parser::scroll(uint32_t line) {
|
||||
if (line != scroll_max) {
|
||||
scroll_max = line;
|
||||
uint32_t c_line = line > 100 ? line - 100 : 0;
|
||||
if (line_tree.count() < c_line)
|
||||
uint32_t c_line = line > 50 ? line - 50 : 0;
|
||||
if (c_line >= line_tree.count())
|
||||
return;
|
||||
std::unique_lock lock_data(data_mutex);
|
||||
if (line_tree.at(c_line)->in_state || line_tree.at(c_line)->out_state)
|
||||
return;
|
||||
lock_data.unlock();
|
||||
std::shared_lock k_lock(editor->knot_mtx);
|
||||
k_lock.unlock();
|
||||
uint32_t capacity = 256;
|
||||
char *text = (char *)calloc((capacity + 1), sizeof(char));
|
||||
uint32_t line_count = line_tree.count();
|
||||
std::unique_lock lock(mutex);
|
||||
std::shared_ptr<void> prev_state =
|
||||
(c_line > 0) ? line_tree.at(c_line - 1)->out_state : nullptr;
|
||||
lock.unlock();
|
||||
while (c_line < line_count) {
|
||||
if (!running.load(std::memory_order_relaxed)) {
|
||||
free(text);
|
||||
return;
|
||||
}
|
||||
k_lock.lock();
|
||||
if (c_line > editor->root->line_count) {
|
||||
k_lock.unlock();
|
||||
continue;
|
||||
}
|
||||
uint32_t r_offset, r_len;
|
||||
r_offset = line_to_byte(editor->root, c_line, &r_len);
|
||||
if (r_len > capacity) {
|
||||
capacity = r_len;
|
||||
text = (char *)realloc(text, capacity + 1);
|
||||
memset(text, 0, capacity + 1);
|
||||
}
|
||||
read_into(editor->root, r_offset, r_len, text);
|
||||
k_lock.unlock();
|
||||
if (c_line < scroll_max &&
|
||||
((scroll_max > 100 && c_line > scroll_max - 100) || c_line < 100))
|
||||
lock.lock();
|
||||
if (line_tree.count() < c_line) {
|
||||
if (lock.owns_lock())
|
||||
lock.unlock();
|
||||
continue;
|
||||
}
|
||||
lock_data.lock();
|
||||
LineData *line_data = line_tree.at(c_line);
|
||||
if (!line_data) {
|
||||
lock_data.unlock();
|
||||
if (lock.owns_lock())
|
||||
lock.unlock();
|
||||
continue;
|
||||
}
|
||||
std::shared_ptr<void> new_state =
|
||||
parse_func(&line_data->tokens, prev_state, text, r_len);
|
||||
line_data->in_state = nullptr;
|
||||
line_data->out_state = new_state;
|
||||
lock_data.unlock();
|
||||
if (lock.owns_lock())
|
||||
lock.unlock();
|
||||
if (!running.load(std::memory_order_relaxed)) {
|
||||
free(text);
|
||||
return;
|
||||
}
|
||||
prev_state = new_state;
|
||||
c_line++;
|
||||
if (c_line < line_count && c_line > scroll_max + 50)
|
||||
break;
|
||||
}
|
||||
free(text);
|
||||
scroll_dirty = true;
|
||||
dirty_lines.push(c_line);
|
||||
} else {
|
||||
scroll_max = line;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
#include "config.h"
|
||||
#include "utils/utils.h"
|
||||
|
||||
std::unordered_map<std::string, Language> languages;
|
||||
std::unordered_map<std::string, std::string> language_extensions;
|
||||
std::unordered_map<std::string, std::string> language_mimetypes;
|
||||
std::unordered_map<std::string, LSP> lsps;
|
||||
|
||||
void log(const char *fmt, ...) {
|
||||
FILE *fp = fopen("/tmp/log.txt", "a");
|
||||
if (!fp)
|
||||
@@ -41,33 +45,39 @@ std::string get_exe_dir() {
|
||||
}
|
||||
|
||||
char *load_file(const char *path, uint32_t *out_len) {
|
||||
std::ifstream file(path, std::ios::in | std::ios::binary | std::ios::ate);
|
||||
std::ifstream file(path, std::ios::binary | std::ios::ate);
|
||||
if (!file.is_open())
|
||||
return nullptr;
|
||||
std::streamsize len = file.tellg();
|
||||
if (len < 0 || static_cast<uint32_t>(len) > 0xFFFFFFFF)
|
||||
if (len < 0 || static_cast<uint64_t>(len) > 0xFFFFFFFF)
|
||||
return nullptr;
|
||||
file.seekg(0, std::ios::beg);
|
||||
bool add_newline = false;
|
||||
if (len > 0) {
|
||||
file.seekg(-1, std::ios::end);
|
||||
char last_char;
|
||||
file.read(&last_char, 1);
|
||||
if (last_char != '\n')
|
||||
add_newline = true;
|
||||
}
|
||||
file.seekg(0, std::ios::beg);
|
||||
uint32_t alloc_size = static_cast<uint32_t>(len) + (add_newline ? 1 : 0);
|
||||
char *buf = (char *)malloc(alloc_size);
|
||||
unsigned char bom[3] = {0};
|
||||
file.read(reinterpret_cast<char *>(bom), 3);
|
||||
if ((bom[0] == 0xFF && bom[1] == 0xFE) || (bom[0] == 0xFE && bom[1] == 0xFF))
|
||||
return nullptr;
|
||||
bool has_utf8_bom = (bom[0] == 0xEF && bom[1] == 0xBB && bom[2] == 0xBF);
|
||||
uint32_t skip = has_utf8_bom ? 3 : 0;
|
||||
uint32_t data_len = static_cast<uint32_t>(len) - skip;
|
||||
file.seekg(skip, std::ios::beg);
|
||||
char *buf = (char *)malloc(data_len + 1);
|
||||
if (!buf)
|
||||
return nullptr;
|
||||
if (!file.read(buf, len)) {
|
||||
free(buf);
|
||||
return nullptr;
|
||||
file.read(buf, data_len);
|
||||
if (memchr(buf, '\r', data_len) == nullptr) {
|
||||
uint32_t write = data_len;
|
||||
if (write == 0 || buf[write - 1] != '\n')
|
||||
buf[write++] = '\n';
|
||||
*out_len = write;
|
||||
return buf;
|
||||
}
|
||||
if (add_newline)
|
||||
buf[len++] = '\n';
|
||||
*out_len = static_cast<uint32_t>(len);
|
||||
uint32_t write = 0;
|
||||
for (uint32_t i = 0; i < data_len; ++i)
|
||||
if (buf[i] != '\r')
|
||||
buf[write++] = buf[i];
|
||||
if (write == 0 || buf[write - 1] != '\n')
|
||||
buf[write++] = '\n';
|
||||
*out_len = write;
|
||||
return buf;
|
||||
}
|
||||
|
||||
@@ -108,17 +118,17 @@ Language language_for_file(const char *filename) {
|
||||
std::string ext = file_extension(filename);
|
||||
std::string lang_name;
|
||||
if (!ext.empty()) {
|
||||
auto it = kExtToLang.find(ext);
|
||||
if (it != kExtToLang.end())
|
||||
return kLanguages.find(it->second)->second;
|
||||
auto it = language_extensions.find(ext);
|
||||
if (it != language_extensions.end())
|
||||
return languages.find(it->second)->second;
|
||||
}
|
||||
char *mime = detect_file_type(filename);
|
||||
if (mime) {
|
||||
std::string mime_type(mime);
|
||||
free(mime);
|
||||
auto it = kMimeToLang.find(mime_type);
|
||||
if (it != kMimeToLang.end())
|
||||
return kLanguages.find(it->second)->second;
|
||||
auto it = language_mimetypes.find(mime_type);
|
||||
if (it != language_mimetypes.end())
|
||||
return languages.find(it->second)->second;
|
||||
}
|
||||
return Language{};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user