Switch to OOP style code

This commit is contained in:
2026-02-04 00:38:11 +00:00
parent e3fc5323df
commit a62d4a18a8
50 changed files with 3011 additions and 3078 deletions

View File

@@ -19,10 +19,11 @@ CFLAGS_DEBUG :=\
-std=c++20 -Wall -Wextra \
-O0 -fno-inline -gsplit-dwarf \
-g -fno-omit-frame-pointer \
-fsanitize=address \
-Wno-unused-command-line-argument \
-I./include -I./libs -I/home/syed/main/crib/libs/mruby/include
# C_SANITIZER := -fsanitize=address
CFLAGS_RELEASE :=\
-static --target=x86_64-linux-musl \
-std=c++20 -O3 -march=x86-64 -mtune=generic \
@@ -78,7 +79,7 @@ $(PCH_RELEASE): $(INCLUDE_DIR)/pch.h
$(TARGET_DEBUG): $(PCH_DEBUG) $(OBJ_DEBUG) $(UNICODE_OBJ_DEBUG)
mkdir -p $(BIN_DIR)
$(CXX) $(CFLAGS_DEBUG) -o $@ $(OBJ_DEBUG) $(UNICODE_OBJ_DEBUG) $(LIBS_DEBUG)
$(CXX) $(CFLAGS_DEBUG) $(C_SANITIZER) -o $@ $(OBJ_DEBUG) $(UNICODE_OBJ_DEBUG) $(LIBS_DEBUG)
$(TARGET_RELEASE): $(PCH_RELEASE) $(OBJ_RELEASE) $(UNICODE_OBJ_RELEASE)
mkdir -p $(BIN_DIR)

12
TODO.md
View File

@@ -10,19 +10,19 @@ Copyright 2025 Syed Daanish
```
* Next few super long boring things to do
* redo lsp threads such that no mutex needed for any rope stuff
* redo lsp threads such that no mutex needed for any rope stuff (necessary rn)
- Also make the classes own the methods in lsp
- This will mean that parsers/renderers and keystrokes will not need to be individually locked
- And so it will be much faster
- At which point the main thread can also be blocked on user input or lsp responses and still be fast
* Add a superclass for editor called Window (which can be popup or tiled)
* Add a recursive tiling class for windows
* Handled by a single renderer that calls and renders each window
- And a bg if no window open
* Make editor's functions into its own methods (classify it)
* Add a superclass for editor called Window (which can be popup or tiled) (done)
* Add a recursive tiling class for windows (done)
* Handled by a single renderer that calls and renders each window (done)
* Make editor's functions into its own methods (classify it) (done)
- While at it
- Seperate system functions into a class that branches to support local / ssh / server modes.
- Even lsp shouldnt be directly controlled because it can branch on local and server modes
- check wolfSSH stuff for remote editing
- Redo hooks as a engine of its own.
- And factorize renderer into its own class (and make it just return an array of the render without knowing teh x,y)
- which is just managed by the renderer

View File

@@ -23,6 +23,7 @@ struct CompletionItem {
};
struct CompletionSession {
struct Editor *editor;
std::shared_mutex mtx;
bool active = false;
Coord hook;
@@ -40,7 +41,14 @@ struct CompletionSession {
std::atomic<bool> hover_dirty = false;
int version;
CompletionSession() : box(this) {}
CompletionSession(Editor *editor) : editor(editor), box(this) {}
void resolve_doc();
void accept();
void next();
void prev();
void choose(uint8_t index);
void handle(KeyEvent event);
};
#endif

View File

@@ -1,16 +1,14 @@
#ifndef EDITOR_H
#define EDITOR_H
#include "editor/completions.h"
#include "editor/indents.h"
#include "io/knot.h"
#include "io/sysio.h"
#include "syntax/extras.h"
#include "syntax/parser.h"
#include "ui/completionbox.h"
#include "ui/diagnostics.h"
#include "ui/hover.h"
#include "utils/utils.h"
#include "windows/decl.h"
#define CHAR 0
#define WORD 1
@@ -19,105 +17,106 @@
#define EXTRA_META 2
#define INDENT_WIDTH 2
struct Editor {
std::string filename;
std::string uri;
Knot *root;
std::shared_mutex knot_mtx;
Coord cursor;
uint32_t cursor_preffered;
Coord selection;
bool selection_active;
bool unix_eol;
int selection_type;
Coord position;
Coord size;
Coord scroll;
Language lang;
uint32_t hooks[94];
bool jumper_set;
std::shared_mutex v_mtx;
std::vector<VWarn> warnings;
bool warnings_dirty;
VAI ai;
struct Editor : Window {
std::string filename = "";
std::string uri = "";
Knot *root = nullptr;
Coord cursor = {0, 0};
uint32_t cursor_preffered = 0;
Coord selection = {0, 0};
bool selection_active = false;
bool unix_eol = true;
int selection_type = 0;
Coord size = {0, 0};
Coord scroll = {0, 0};
Language lang = {};
uint32_t hooks[94] = {0};
bool jumper_set = false;
std::vector<VWarn> warnings = {};
bool warnings_dirty = false;
VAI ai = {};
std::shared_mutex lsp_mtx;
std::shared_ptr<struct LSPInstance> lsp;
bool hover_active;
HoverBox hover;
bool diagnostics_active;
DiagnosticBox diagnostics;
std::atomic<struct LSPInstance *> lsp = nullptr;
bool hover_active = false;
bool diagnostics_active = false;
std::atomic<int> lsp_version = 1;
CompletionSession completion;
IndentationEngine indents;
Parser *parser;
ExtraHighlighter extra_hl;
bool is_css_color;
};
IndentationEngine indents = {};
Parser *parser = nullptr;
ExtraHighlighter extra_hl = {};
bool is_css_color = false;
Editor *new_editor(const char *filename_arg, Coord position, Coord size,
uint8_t eol);
void save_file(Editor *editor);
void free_editor(Editor *editor);
void render_editor(Editor *editor);
void cursor_up(Editor *editor, uint32_t number);
void cursor_down(Editor *editor, uint32_t number);
Coord move_left(Editor *editor, Coord cursor, uint32_t number);
Coord move_right(Editor *editor, Coord cursor, uint32_t number);
void cursor_left(Editor *editor, uint32_t number);
void cursor_right(Editor *editor, uint32_t number);
void scroll_up(Editor *editor, int32_t number);
void scroll_down(Editor *editor, uint32_t number);
void ensure_cursor(Editor *editor);
void ensure_scroll(Editor *editor);
void handle_editor_event(Editor *editor, KeyEvent event);
void edit_erase(Editor *editor, Coord pos, int64_t len);
void edit_insert(Editor *editor, Coord pos, char *data, uint32_t len);
void edit_replace(Editor *editor, Coord start, Coord end, const char *text,
uint32_t len);
Coord editor_hit_test(Editor *editor, uint32_t x, uint32_t y);
char *get_selection(Editor *editor, uint32_t *out_len, Coord *out_start);
void selection_bounds(Editor *editor, Coord *out_start, Coord *out_end);
void editor_worker(Editor *editor);
void move_line_down(Editor *editor);
void move_line_up(Editor *editor);
void word_boundaries(Editor *editor, Coord coord, uint32_t *prev_col,
uint32_t *next_col, uint32_t *prev_clusters,
uint32_t *next_clusters);
void word_boundaries_exclusive(Editor *editor, Coord coord, uint32_t *prev_col,
Editor(const char *filename_arg, uint8_t eol);
~Editor();
void render(std::vector<ScreenCell> &buffer, Coord size, Coord pos) override;
void handle_event(KeyEvent event) override;
void handle_click(KeyEvent event, Coord size) override;
void handle_command(std::string &command) override;
void work() override;
std::array<std::string, 5> bar_info() override { return {}; };
void save();
void cursor_up(uint32_t number);
void cursor_down(uint32_t number);
void cursor_left(uint32_t number);
void cursor_right(uint32_t number);
void move_line_down();
void move_line_up();
void scroll_up(uint32_t number);
void scroll_down(uint32_t number);
void ensure_cursor();
void ensure_scroll();
void edit_erase(Coord pos, int64_t len);
void edit_insert(Coord pos, char *data, uint32_t len);
void edit_replace(Coord start, Coord end, const char *text, uint32_t len);
Coord click_coord(uint32_t x, uint32_t y);
char *get_selection(uint32_t *out_len, Coord *out_start);
void selection_bounds(Coord *out_start, Coord *out_end);
void insert_str(char *c, uint32_t len);
void insert_char(char c);
void normal_mode();
void backspace_edit();
void delete_prev_word();
void delete_next_word();
void clear_hooks_at_line(uint32_t line);
void cursor_prev_word();
void cursor_next_word();
void select_all();
void fetch_lsp_hover();
void indent_current_line();
void dedent_current_line();
void indent_selection();
void dedent_selection();
void paste();
void copy();
void cut();
void lsp_handle(json msg);
void apply_lsp_edits(std::vector<TextEdit> edits, bool move);
Coord move_left(Coord cursor, uint32_t number);
Coord move_right(Coord cursor, uint32_t number);
void word_boundaries(Coord coord, uint32_t *prev_col, uint32_t *next_col,
uint32_t *prev_clusters, uint32_t *next_clusters);
void word_boundaries_exclusive(Coord coord, uint32_t *prev_col,
uint32_t *next_col);
void editor_lsp_handle(Editor *editor, json msg);
void apply_lsp_edits(Editor *editor, std::vector<TextEdit> edits, bool move);
void completion_resolve_doc(Editor *editor);
void complete_accept(Editor *editor);
void complete_next(Editor *editor);
void complete_prev(Editor *editor);
void complete_select(Editor *editor, uint8_t index);
void handle_completion(Editor *editor, KeyEvent event);
inline void apply_hook_insertion(Editor *editor, uint32_t line, uint32_t rows) {
for (auto &hook : editor->hooks)
if (hook > line)
hook += rows;
}
inline void apply_hook_deletion(Editor *editor, uint32_t removal_start,
uint32_t removal_end) {
for (auto &hook : editor->hooks)
if (hook > removal_start)
hook -= removal_end - removal_start + 1;
}
inline static void utf8_normalize_edit(Editor *editor, TextEdit *edit) {
std::shared_lock lock(editor->knot_mtx);
if (edit->start.row > editor->root->line_count) {
edit->start.row = editor->root->line_count;
void utf8_normalize_edit(TextEdit *edit) {
if (edit->start.row > this->root->line_count) {
edit->start.row = this->root->line_count;
edit->start.col = UINT32_MAX;
}
if (edit->end.row > editor->root->line_count) {
edit->end.row = editor->root->line_count;
if (edit->end.row > this->root->line_count) {
edit->end.row = this->root->line_count;
edit->end.col = UINT32_MAX;
}
LineIterator *it = begin_l_iter(editor->root, edit->start.row);
LineIterator *it = begin_l_iter(this->root, edit->start.row);
if (!it)
return;
uint32_t len;
@@ -142,7 +141,7 @@ inline static void utf8_normalize_edit(Editor *editor, TextEdit *edit) {
}
free(it->buffer);
free(it);
it = begin_l_iter(editor->root, edit->end.row);
it = begin_l_iter(this->root, edit->end.row);
if (!it)
return;
line = next_line(it, &len);
@@ -157,6 +156,20 @@ inline static void utf8_normalize_edit(Editor *editor, TextEdit *edit) {
edit->end.col = len;
free(it->buffer);
free(it);
}
}
inline void apply_hook_insertion(uint32_t line, uint32_t rows) {
for (auto &hook : this->hooks)
if (hook > line)
hook += rows;
}
inline void apply_hook_deletion(uint32_t removal_start,
uint32_t removal_end) {
for (auto &hook : this->hooks)
if (hook > removal_start)
hook -= removal_end - removal_start + 1;
}
};
#endif

View File

@@ -3,24 +3,4 @@
#include "editor/editor.h"
void insert_str(Editor *editor, char *c, uint32_t len);
void insert_char(Editor *editor, char c);
void normal_mode(Editor *editor);
void backspace_edit(Editor *editor);
void delete_prev_word(Editor *editor);
void delete_next_word(Editor *editor);
void clear_hooks_at_line(Editor *editor, uint32_t line);
void cursor_prev_word(Editor *editor);
void cursor_next_word(Editor *editor);
void select_all(Editor *editor);
void fetch_lsp_hover(Editor *editor);
void handle_mouse(Editor *editor, KeyEvent event);
void indent_current_line(Editor *editor);
void dedent_current_line(Editor *editor);
void indent_selection(Editor *editor);
void dedent_selection(Editor *editor);
void paste(Editor *editor);
void copy(Editor *editor);
void cut(Editor *editor);
#endif

View File

@@ -0,0 +1,31 @@
#ifndef EXTENTION_HOVER_H
#define EXTENTION_HOVER_H
#include "io/sysio.h"
#include "pch.h"
#include "utils/utils.h"
#include "windows/decl.h"
TileRoot *init_hover();
struct HoverBox : Window {
std::string text;
std::atomic<bool> is_markup;
uint32_t scroll_;
bool scroll_dirty;
HoverBox() : scroll_(0) { this->hidden = true; }
void clear() {
this->text = "";
this->hidden = true;
this->is_markup = false;
this->scroll_ = 0;
this->scroll_dirty = true;
}
void scroll(int32_t number);
void render(std::vector<ScreenCell> &buffer, Coord size, Coord pos) override;
void handle_click(KeyEvent, Coord) override { this->hidden = true; };
~HoverBox() {};
};
#endif

View File

@@ -96,19 +96,13 @@ inline bool is_empty_cell(const ScreenCell &c) {
return c.utf8.empty() || c.utf8 == " " || c.utf8 == "\x1b";
}
extern std::vector<ScreenCell> new_screen;
Coord start_screen();
void end_screen();
void update(uint32_t row, uint32_t col, std::string utf8, uint32_t fg,
uint32_t bg, uint8_t flags);
void update(uint32_t row, uint32_t col, const char *utf8, uint32_t fg,
uint32_t bg, uint8_t flags);
void update(uint32_t row, uint32_t col, std::string utf8, uint32_t fg,
uint32_t bg, uint8_t flags, uint32_t ul_color);
void update(uint32_t row, uint32_t col, const char *utf8, uint32_t fg,
uint32_t bg, uint8_t flags, uint32_t ul_color);
void set_cursor(uint8_t row, uint8_t col, uint32_t type,
bool show_cursor_param);
void render();
void io_render();
Coord get_size();
KeyEvent read_key();

View File

@@ -5,56 +5,20 @@
#include "pch.h"
#include "utils/utils.h"
struct LSPPending {
Editor *editor = nullptr;
std::function<void(Editor *, const json &)> callback;
};
#define LSP_TIMEOUT 3000
// TODO: Defer any editor mutation to main thread to get rid of
// all mutex locks on the editor rope.
// struct LSPPendingResponse {
// LSPPending *pending = nullptr;
// json message;
// };
struct LSPOpenRequest {
Language language;
Editor *editor;
};
struct LSPInstance {
std::shared_mutex mtx;
const LSP *lsp;
std::string root_dir;
int pid{-1};
int stdin_fd{-1};
int stdout_fd{-1};
std::atomic<bool> initialized = false;
std::atomic<bool> exited = false;
bool incremental_sync = false;
bool allow_hover = false;
bool allow_completion = false;
bool allow_resolve = false;
bool allow_formatting = false;
bool allow_formatting_on_type = false;
bool is_utf8 = false;
std::vector<char> format_chars;
std::vector<char> trigger_chars;
std::vector<char> end_chars;
uint32_t last_id = 0;
Queue<json> inbox;
Queue<json> outbox;
Queue<std::pair<Language, Editor *>> open_queue;
std::unordered_map<uint32_t, LSPPending *> pending;
std::vector<Editor *> editors;
};
extern std::shared_mutex active_lsps_mtx;
extern std::unordered_map<std::string, std::shared_ptr<LSPInstance>>
namespace lsp {
extern std::mutex lsp_mutex;
extern std::unordered_map<std::string, std::unique_ptr<LSPInstance>>
active_lsps;
extern Queue<LSPOpenRequest> lsp_open_queue;
extern Queue<std::string> need_opening;
extern std::unordered_set<std::string> opened;
extern std::vector<Editor *> new_editors;
} // namespace lsp
static json client_capabilities = {
void lsp_worker();
static const json client_capabilities = {
{"general", {{"positionEncodings", {"utf-16"}}}},
{"textDocument",
{{"publishDiagnostics", {{"relatedInformation", true}}},
@@ -78,20 +42,403 @@ static json client_capabilities = {
{"contextSupport", true},
{"insertTextMode", 1}}}}}};
void lsp_send(std::shared_ptr<LSPInstance> lsp, json message,
LSPPending *pending);
void lsp_worker();
struct LSPMessage {
Editor *editor = nullptr;
json message;
std::function<void(const LSPMessage &)> callback;
};
std::shared_ptr<LSPInstance> get_or_init_lsp(std::string lsp_id);
void clean_lsp(std::shared_ptr<LSPInstance> lsp, std::string lsp_id);
void close_lsp(std::string lsp_id);
std::optional<json> read_lsp_message(int fd);
struct LSPInstance {
const LSP *lsp_info;
std::string root_dir;
int pid{-1};
int stdin_fd{-1};
int stdout_fd{-1};
std::atomic<bool> initialized = false;
std::atomic<bool> exited = false;
bool incremental_sync = false;
bool allow_hover = false;
bool allow_completion = false;
bool allow_resolve = false;
bool allow_formatting = false;
bool allow_formatting_on_type = false;
bool is_utf8 = false;
std::vector<char> format_chars;
std::vector<char> trigger_chars;
std::vector<char> end_chars;
uint32_t last_id = 0;
Queue<json> inbox;
Queue<json> outbox;
std::unordered_map<uint32_t, std::unique_ptr<LSPMessage>> pending;
std::vector<std::unique_ptr<LSPMessage>> lsp_response_queue;
std::vector<Editor *> editors;
void open_editor(std::shared_ptr<LSPInstance> lsp,
std::pair<Language, Editor *> entry);
void request_add_to_lsp(Language language, Editor *editor);
void add_to_lsp(Language language, Editor *editor);
void remove_from_lsp(Editor *editor);
void lsp_handle(std::shared_ptr<LSPInstance> lsp, json message);
LSPInstance(std::string lsp_id) {
lsp_info = &lsps[lsp_id];
if (!init_process()) {
exited = true;
return;
}
json initialize_message = {
{"jsonrpc", "2.0"},
{"id", ++last_id},
{"method", "initialize"},
{"params",
{{"processId", getpid()},
{"rootUri", "file://" + percent_encode(path_abs("."))},
{"capabilities", client_capabilities}}}};
send_raw(initialize_message);
pollfd pfd{stdout_fd, POLLIN, 0};
poll(&pfd, 1, LSP_TIMEOUT);
if (!(pfd.revents & POLLIN)) {
exited = true;
return;
}
json response = *read_lsp_message();
log("Lsp response: %s", response.dump().c_str());
if (response.contains("result") &&
response["result"].contains("capabilities")) {
auto &caps = response["result"]["capabilities"];
// if (caps.contains("positionEncoding")) {
// std::string s = caps["positionEncoding"].get<std::string>();
// if (s == "utf-8")
// is_utf8 = true;
// log("Lsp name: %s, supports: %s", lsp->command.c_str(),
// s.c_str());
// }
if (caps.contains("textDocumentSync")) {
auto &sync = caps["textDocumentSync"];
if (sync.is_number()) {
int change_type = sync.get<int>();
incremental_sync = (change_type == 2);
} else if (sync.is_object() && sync.contains("change")) {
int change_type = sync["change"].get<int>();
incremental_sync = (change_type == 2);
}
}
allow_formatting = caps.value("documentFormattingProvider", false);
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()) {
if (fmt.contains("firstTriggerCharacter")) {
std::string s = fmt["firstTriggerCharacter"].get<std::string>();
if (s.size() == 1)
format_chars.push_back(s[0]);
}
if (fmt.contains("moreTriggerCharacter")) {
for (auto &c : fmt["moreTriggerCharacter"]) {
std::string s = c.get<std::string>();
if (s.size() == 1)
format_chars.push_back(s[0]);
}
}
allow_formatting_on_type = true;
} else if (fmt.is_boolean()) {
allow_formatting_on_type = fmt.get<bool>();
}
}
if (caps.contains("hoverProvider")) {
auto &hover = caps["hoverProvider"];
allow_hover =
hover.is_boolean() ? hover.get<bool>() : hover.is_object();
} else {
allow_hover = false;
}
if (caps.contains("completionProvider")) {
allow_completion = true;
if (caps["completionProvider"].contains("resolveProvider"))
allow_resolve =
caps["completionProvider"]["resolveProvider"].get<bool>();
if (caps["completionProvider"].contains("triggerCharacters")) {
auto &chars = caps["completionProvider"]["triggerCharacters"];
if (chars.is_array()) {
for (auto &c : chars) {
std::string str = c.get<std::string>();
if (str.size() != 1)
continue;
trigger_chars.push_back(str[0]);
}
}
}
if (caps["completionProvider"].contains("allCommitCharacters")) {
auto &chars = caps["completionProvider"]["allCommitCharacters"];
if (chars.is_array()) {
for (auto &c : chars) {
std::string str = c.get<std::string>();
if (str.size() != 1)
continue;
end_chars.push_back(str[0]);
}
}
}
}
}
initialized = true;
json initialized_message = {{"jsonrpc", "2.0"},
{"method", "initialized"},
{"params", json::object()}};
send_raw(initialized_message);
}
~LSPInstance() {
for (auto &ed : editors)
ed->lsp.store(nullptr);
initialized = false;
exited = true;
if (pid == -1)
return;
json shutdown = {{"id", ++last_id}, {"method", "shutdown"}};
send_raw(shutdown);
pollfd pfd{stdout_fd, POLLIN, 0};
poll(&pfd, 1, LSP_TIMEOUT);
json exit_msg = {{"method", "exit"}};
send_raw(exit_msg);
int waited = 0;
while (waited < LSP_TIMEOUT) {
int status;
pid_t res = waitpid(pid, &status, WNOHANG);
if (res == pid)
break;
std::this_thread::sleep_for(std::chrono::milliseconds(10));
waited += 10;
}
if (kill(pid, 0) == 0) {
kill(pid, SIGKILL);
waitpid(pid, nullptr, 0);
}
pid = -1;
close(stdin_fd);
close(stdout_fd);
}
bool init_process() {
int in_pipe[2];
int out_pipe[2];
if (pipe(in_pipe) == -1 || pipe(out_pipe) == -1) {
perror("pipe");
return false;
}
pid_t pid_tmp = fork();
if (pid_tmp == -1) {
perror("fork");
return false;
}
if (pid_tmp == 0) {
dup2(in_pipe[0], STDIN_FILENO);
dup2(out_pipe[1], STDOUT_FILENO);
int devnull = open("/dev/null", O_WRONLY);
if (devnull >= 0) {
dup2(devnull, STDERR_FILENO);
close(devnull);
}
close(in_pipe[0]);
close(in_pipe[1]);
close(out_pipe[0]);
close(out_pipe[1]);
std::vector<char *> argv;
argv.push_back(const_cast<char *>(lsp_info->command.c_str()));
for (auto &arg : lsp_info->args)
argv.push_back(const_cast<char *>(arg.c_str()));
argv.push_back(nullptr);
execvp(lsp_info->command.c_str(), argv.data());
perror("execvp");
_exit(127);
}
pid = pid_tmp;
stdin_fd = in_pipe[1];
stdout_fd = out_pipe[0];
close(in_pipe[0]);
close(out_pipe[1]);
return true;
}
void add(Editor *ed) {
std::unique_lock lock(lsp::lsp_mutex);
editors.push_back(ed);
lock.unlock();
ed->lsp.store(this);
char *buf = read(ed->root, 0, ed->root->char_count);
std::string text(buf);
free(buf);
auto message = std::make_unique<LSPMessage>();
message->message = {{"method", "textDocument/didOpen"},
{"params",
{{"textDocument",
{{"uri", ed->uri},
{"languageId", ed->lang.name},
{"version", 1},
{"text", text}}}}}};
send(std::move(message));
}
void remove(Editor *ed) {
std::unique_lock lock(lsp::lsp_mutex);
editors.erase(std::remove(editors.begin(), editors.end(), ed),
editors.end());
lock.unlock();
auto message = std::make_unique<LSPMessage>();
message->message = {{"method", "textDocument/didClose"},
{"params", {{"textDocument", {{"uri", ed->uri}}}}}};
send(std::move(message));
}
void work() {
if (exited)
return;
int status;
pid_t res = waitpid(pid, &status, WNOHANG);
if (res == pid) {
exited = true;
pid = -1;
return;
}
while (!outbox.empty()) {
json message = outbox.front();
std::string m = message.value("method", "");
outbox.pop();
send_raw(message);
}
pollfd pfd{stdout_fd, POLLIN | POLLHUP | POLLERR, 0};
int r = poll(&pfd, 1, 0);
if (r > 0 && pfd.revents & (POLLHUP | POLLERR)) {
exited = true;
pid = -1;
return;
}
while ((r = poll(&pfd, 1, 0)) > 0) {
if (pfd.revents & (POLLHUP | POLLERR)) {
exited = true;
pid = -1;
return;
}
auto msg = read_lsp_message();
if (!msg)
break;
if (msg->contains("id")) {
uint32_t id = msg->at("id").get<uint32_t>();
auto it = pending.find(id);
if (it != pending.end()) {
if (it->second->editor) {
it->second->message = *msg;
lsp_response_queue.push_back(std::move(it->second));
} else {
auto message = *std::move(it->second);
message.message = *msg;
message.callback(message);
}
pending.erase(it);
}
} else if (msg->contains("method")) {
std::string uri;
if (msg->contains("params")) {
auto &p = (*msg)["params"];
if (p.contains("textDocument") && p["textDocument"].contains("uri"))
uri = p["textDocument"]["uri"].get<std::string>();
else if (p.contains("uri"))
uri = p["uri"].get<std::string>();
}
Editor *ed = resolve_uri(uri);
auto response = std::make_unique<LSPMessage>();
response->editor = ed;
response->message = *msg;
response->callback = editor_handle_wrapper;
if (ed)
lsp_response_queue.push_back(std::move(response));
else
lsp_handle(*msg);
}
}
}
inline static void editor_handle_wrapper(const LSPMessage &message) {
message.editor->lsp_handle(message.message);
}
void callbacks() {
for (auto &message : lsp_response_queue)
message->callback(*message);
lsp_response_queue.clear();
}
inline void send_raw(const json &msg) {
std::string payload = msg.dump();
std::string header =
"Content-Length: " + std::to_string(payload.size()) + "\r\n\r\n";
std::string out = header + payload;
const char *ptr = out.data();
size_t remaining = out.size();
while (remaining > 0) {
ssize_t n = write(stdin_fd, ptr, remaining);
if (n <= 0) {
if (errno == EINTR)
continue;
break;
}
ptr += n;
remaining -= n;
}
};
inline std::optional<json> read_lsp_message() {
std::string header;
char c;
while (true) {
ssize_t n = read(stdout_fd, &c, 1);
if (n <= 0)
return std::nullopt;
header.push_back(c);
if (header.size() >= 4 && header.substr(header.size() - 4) == "\r\n\r\n")
break;
}
size_t pos = header.find("Content-Length:");
if (pos == std::string::npos)
return std::nullopt;
pos += strlen("Content-Length:");
while (pos < header.size() && std::isspace(header[pos]))
pos++;
size_t end = pos;
while (end < header.size() && std::isdigit(header[end]))
end++;
size_t len = std::stoul(header.substr(pos, end - pos));
std::string body(len, '\0');
size_t got = 0;
while (got < len) {
ssize_t n = read(stdout_fd, &body[got], len - got);
if (n <= 0)
return std::nullopt;
got += n;
}
return json::parse(body);
}
inline Editor *resolve_uri(std::string uri) {
if (uri.empty())
return nullptr;
for (auto &editor : editors)
if (editor->uri == uri)
return editor;
return nullptr;
}
inline void lsp_handle(json &message) {
std::string method = message.value("method", "");
if (method == "window/showMessage") {
if (message.contains("params")) {
auto &p = message["params"];
if (p.contains("message"))
log("%s\n", p["message"].get<std::string>().c_str());
}
} else if (method == "window/logMessage") {
if (message.contains("params")) {
auto &p = message["params"];
if (p.contains("message"))
log("%s\n", p["message"].get<std::string>().c_str());
}
}
}
void send(std::unique_ptr<LSPMessage> message) {
if (pid == -1)
return;
message->message["jsonrpc"] = "2.0";
if (message->callback)
message->message["id"] = ++last_id;
outbox.push(message->message);
if (!message->callback)
return;
std::lock_guard lock(lsp::lsp_mutex);
pending[last_id] = std::move(message);
}
};
#endif

View File

@@ -12,8 +12,11 @@
extern std::atomic<bool> running;
extern std::atomic<uint8_t> mode;
extern std::vector<struct Editor *> editors;
extern uint8_t current_editor;
extern fs::path pwd;
namespace ui {
extern Bar bar;
extern TileRoot *hover_popup;
} // namespace ui
#endif

View File

@@ -50,6 +50,7 @@ extern "C" {
#include <thread>
#include <unistd.h>
#include <unordered_map>
#include <unordered_set>
#include <vector>
using json = nlohmann::json;

View File

@@ -3,6 +3,7 @@
#include "syntax/decl.h"
#include "utils/utils.h"
#include "windows/decl.h"
namespace fs = std::filesystem;
@@ -45,9 +46,7 @@ bool custom_compare(mrb_value match_block, std::string state1,
std::string parse_custom(std::vector<Token> *tokens, mrb_value parser_block,
const char *line, uint32_t len, std::string state,
uint32_t c_line);
BarLine bar_contents(uint8_t mode, std::string lang_name, uint32_t warnings,
std::string lsp_name, std::string filename,
std::string foldername, uint32_t line, uint32_t max_line,
uint32_t width);
BarLine bar_contents(uint8_t mode, uint32_t width, std::string foldername,
Window *window);
#endif

View File

@@ -177,7 +177,7 @@ module C
color: 0x6e1516,
symbol: "",
extensions: ["erb"],
lsp: "ruby-lsp"
lsp: "emmet-language-server"
},
lua: {
color: 0x36a3d9,
@@ -331,7 +331,7 @@ module C
@b_startup = nil
@b_shutdown = nil
@b_bar = proc do |info|
# mode, lang_name, warnings, lsp_name, filename, foldername, line, max_line, width
# mode, width, data[5] : strings (any data about the focused window)
# puts info.inspect
mode_color = 0x82AAFF
mode_symbol = " "
@@ -352,11 +352,15 @@ module C
mode_color = 0xF29CC3
mode_symbol = ""
end
lang_info = C.languages[info[:lang_name]]
lang_info = C.languages[:default]
filename = ""
if info[:data][0] == "editor"
lang_info = C.languages[info[:data][2]] if info[:data][2] != ""
if lang_info.nil?
lang_info = C.languages[:default]
end
filename = File.basename(info[:filename])
filename = File.basename(info[:data][1]) if info[:data][1] != ""
end
starting = " #{mode_symbol} #{info[:mode].to_s.upcase}  #{lang_info[:symbol]} #{filename}"
highlights = []
highlights << { fg: 0x0b0e14, bg: mode_color, flags: 1 << 1, start: 0, length: 10 }

View File

@@ -1,8 +1,8 @@
#pragma once
constexpr unsigned char _tmp___crib_precompiled_mrb[] = {
0x52, 0x49, 0x54, 0x45, 0x30, 0x34, 0x30, 0x30, 0x00, 0x00, 0x2b, 0xbe,
0x52, 0x49, 0x54, 0x45, 0x30, 0x34, 0x30, 0x30, 0x00, 0x00, 0x2c, 0x1d,
0x4d, 0x41, 0x54, 0x5a, 0x30, 0x30, 0x30, 0x30, 0x49, 0x52, 0x45, 0x50,
0x00, 0x00, 0x2a, 0x2a, 0x30, 0x34, 0x30, 0x30, 0x00, 0x00, 0x00, 0xa2,
0x00, 0x00, 0x2a, 0x89, 0x30, 0x34, 0x30, 0x30, 0x00, 0x00, 0x00, 0xa2,
0x00, 0x01, 0x00, 0x03, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a,
0x11, 0x01, 0x68, 0x01, 0x00, 0x69, 0x01, 0x00, 0x6b, 0x01, 0x01, 0x01,
0x11, 0x01, 0x68, 0x01, 0x02, 0x69, 0x01, 0x02, 0x11, 0x01, 0x68, 0x01,
@@ -301,7 +301,7 @@ constexpr unsigned char _tmp___crib_precompiled_mrb[] = {
0x05, 0x5c, 0x21, 0x0f, 0x5e, 0x1a, 0x04, 0x10, 0x1b, 0x12, 0x10, 0x1c,
0x02, 0x0f, 0x1d, 0x00, 0x6e, 0x15, 0x16, 0x10, 0x1e, 0x03, 0x5c, 0x1f,
0x38, 0x10, 0x20, 0x04, 0x5c, 0x21, 0x42, 0x52, 0x21, 0x01, 0x10, 0x22,
0x05, 0x5c, 0x23, 0x08, 0x5e, 0x1c, 0x04, 0x10, 0x1d, 0x13, 0x10, 0x1e,
0x05, 0x5c, 0x23, 0x15, 0x5e, 0x1c, 0x04, 0x10, 0x1d, 0x13, 0x10, 0x1e,
0x02, 0x0f, 0x1f, 0x00, 0x36, 0xa3, 0xd9, 0x10, 0x20, 0x03, 0x5c, 0x21,
0x43, 0x10, 0x22, 0x04, 0x5c, 0x23, 0x44, 0x52, 0x23, 0x01, 0x10, 0x24,
0x05, 0x5c, 0x25, 0x17, 0x5e, 0x1e, 0x04, 0x10, 0x1f, 0x14, 0x10, 0x20,
@@ -576,8 +576,8 @@ constexpr unsigned char _tmp___crib_precompiled_mrb[] = {
0x63, 0x6f, 0x70, 0x79, 0x00, 0x00, 0x08, 0x40, 0x62, 0x5f, 0x70, 0x61,
0x73, 0x74, 0x65, 0x00, 0x00, 0x0e, 0x40, 0x62, 0x5f, 0x66, 0x69, 0x6c,
0x65, 0x5f, 0x64, 0x65, 0x74, 0x65, 0x63, 0x74, 0x00, 0x00, 0x00, 0x03,
0x55, 0x00, 0x09, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
0x1f, 0x39, 0x04, 0x00, 0x00, 0x0f, 0x03, 0x00, 0x82, 0xaa, 0xff, 0x5c,
0xb4, 0x00, 0x09, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
0x7b, 0x39, 0x04, 0x00, 0x00, 0x0f, 0x03, 0x00, 0x82, 0xaa, 0xff, 0x5c,
0x04, 0x00, 0x01, 0x09, 0x01, 0x10, 0x0a, 0x00, 0x23, 0x09, 0x10, 0x0a,
0x01, 0x01, 0x0b, 0x09, 0x32, 0x0a, 0x02, 0x01, 0x28, 0x0a, 0x00, 0x0c,
0x0f, 0x03, 0x00, 0x82, 0xaa, 0xff, 0x5c, 0x04, 0x01, 0x26, 0x00, 0x68,
@@ -590,263 +590,256 @@ constexpr unsigned char _tmp___crib_precompiled_mrb[] = {
0x5c, 0x04, 0x04, 0x26, 0x00, 0x1a, 0x10, 0x0a, 0x06, 0x01, 0x0b, 0x09,
0x32, 0x0a, 0x02, 0x01, 0x28, 0x0a, 0x00, 0x0c, 0x0f, 0x03, 0x00, 0xf2,
0x9c, 0xc3, 0x5c, 0x04, 0x05, 0x26, 0x00, 0x00, 0x1d, 0x09, 0x07, 0x33,
0x09, 0x08, 0x01, 0x0a, 0x01, 0x10, 0x0b, 0x09, 0x23, 0x0a, 0x23, 0x09,
0x01, 0x05, 0x09, 0x01, 0x09, 0x05, 0x29, 0x09, 0x00, 0x03, 0x26, 0x00,
0x0e, 0x1d, 0x09, 0x07, 0x33, 0x09, 0x08, 0x10, 0x0a, 0x0a, 0x23, 0x09,
0x01, 0x05, 0x09, 0x1d, 0x09, 0x0b, 0x01, 0x0a, 0x01, 0x10, 0x0b, 0x0c,
0x23, 0x0a, 0x32, 0x09, 0x0d, 0x01, 0x01, 0x06, 0x09, 0x5c, 0x09, 0x06,
0x01, 0x0a, 0x04, 0x5d, 0x09, 0x5c, 0x0a, 0x06, 0x5d, 0x09, 0x01, 0x0a,
0x01, 0x10, 0x0b, 0x00, 0x23, 0x0a, 0x33, 0x0a, 0x0e, 0x33, 0x0a, 0x0f,
0x5d, 0x09, 0x5c, 0x0a, 0x07, 0x5d, 0x09, 0x01, 0x0a, 0x05, 0x10, 0x0b,
0x10, 0x23, 0x0a, 0x5d, 0x09, 0x5c, 0x0a, 0x06, 0x5d, 0x09, 0x01, 0x0a,
0x06, 0x5d, 0x09, 0x5c, 0x0a, 0x08, 0x5d, 0x09, 0x01, 0x07, 0x09, 0x52,
0x08, 0x00, 0x01, 0x09, 0x08, 0x10, 0x0a, 0x11, 0x0f, 0x0b, 0x00, 0x0b,
0x0e, 0x14, 0x10, 0x0c, 0x12, 0x01, 0x0d, 0x03, 0x10, 0x0e, 0x13, 0x08,
0x0f, 0x10, 0x10, 0x14, 0x06, 0x11, 0x10, 0x12, 0x15, 0x03, 0x13, 0x0a,
0x5e, 0x0a, 0x05, 0x32, 0x09, 0x16, 0x01, 0x01, 0x09, 0x08, 0x10, 0x0a,
0x11, 0x01, 0x0b, 0x03, 0x10, 0x0c, 0x12, 0x0f, 0x0d, 0x00, 0x33, 0x36,
0x3c, 0x10, 0x0e, 0x14, 0x03, 0x0f, 0x0a, 0x10, 0x10, 0x15, 0x07, 0x11,
0x5e, 0x0a, 0x04, 0x32, 0x09, 0x16, 0x01, 0x01, 0x09, 0x08, 0x10, 0x0a,
0x11, 0x0f, 0x0b, 0x00, 0x33, 0x36, 0x3c, 0x10, 0x0c, 0x12, 0x0f, 0x0d,
0x00, 0x24, 0x27, 0x2d, 0x10, 0x0e, 0x14, 0x03, 0x0f, 0x0b, 0x10, 0x10,
0x15, 0x07, 0x11, 0x5e, 0x0a, 0x04, 0x32, 0x09, 0x16, 0x01, 0x01, 0x09,
0x08, 0x10, 0x0a, 0x11, 0x01, 0x0b, 0x05, 0x10, 0x0c, 0x17, 0x23, 0x0b,
0x10, 0x0c, 0x12, 0x0f, 0x0d, 0x00, 0x24, 0x27, 0x2d, 0x10, 0x0e, 0x14,
0x03, 0x0f, 0x0d, 0x10, 0x10, 0x15, 0x08, 0x11, 0x5e, 0x0a, 0x04, 0x32,
0x09, 0x08, 0x10, 0x0a, 0x09, 0x23, 0x09, 0x01, 0x05, 0x09, 0x5c, 0x06,
0x06, 0x01, 0x09, 0x01, 0x10, 0x0a, 0x0a, 0x23, 0x09, 0x06, 0x0a, 0x23,
0x09, 0x5c, 0x0a, 0x07, 0x4d, 0x09, 0x28, 0x09, 0x00, 0x73, 0x01, 0x09,
0x01, 0x10, 0x0a, 0x0a, 0x23, 0x09, 0x08, 0x0a, 0x23, 0x09, 0x5c, 0x0a,
0x06, 0x32, 0x09, 0x0b, 0x01, 0x28, 0x09, 0x00, 0x17, 0x1d, 0x09, 0x07,
0x33, 0x09, 0x08, 0x01, 0x0a, 0x01, 0x10, 0x0b, 0x0a, 0x23, 0x0a, 0x08,
0x0b, 0x23, 0x0a, 0x23, 0x09, 0x01, 0x05, 0x09, 0x01, 0x09, 0x05, 0x29,
0x09, 0x00, 0x03, 0x26, 0x00, 0x0e, 0x1d, 0x09, 0x07, 0x33, 0x09, 0x08,
0x10, 0x0a, 0x09, 0x23, 0x09, 0x01, 0x05, 0x09, 0x01, 0x09, 0x01, 0x10,
0x0a, 0x0a, 0x23, 0x09, 0x07, 0x0a, 0x23, 0x09, 0x5c, 0x0a, 0x06, 0x32,
0x09, 0x0b, 0x01, 0x28, 0x09, 0x00, 0x16, 0x1d, 0x09, 0x0c, 0x01, 0x0a,
0x01, 0x10, 0x0b, 0x0a, 0x23, 0x0a, 0x07, 0x0b, 0x23, 0x0a, 0x32, 0x09,
0x0d, 0x01, 0x01, 0x06, 0x09, 0x5c, 0x09, 0x08, 0x01, 0x0a, 0x04, 0x5d,
0x09, 0x5c, 0x0a, 0x08, 0x5d, 0x09, 0x01, 0x0a, 0x01, 0x10, 0x0b, 0x00,
0x23, 0x0a, 0x33, 0x0a, 0x0e, 0x33, 0x0a, 0x0f, 0x5d, 0x09, 0x5c, 0x0a,
0x09, 0x5d, 0x09, 0x01, 0x0a, 0x05, 0x10, 0x0b, 0x10, 0x23, 0x0a, 0x5d,
0x09, 0x5c, 0x0a, 0x08, 0x5d, 0x09, 0x01, 0x0a, 0x06, 0x5d, 0x09, 0x5c,
0x0a, 0x0a, 0x5d, 0x09, 0x01, 0x07, 0x09, 0x52, 0x08, 0x00, 0x01, 0x09,
0x08, 0x10, 0x0a, 0x11, 0x0f, 0x0b, 0x00, 0x0b, 0x0e, 0x14, 0x10, 0x0c,
0x12, 0x01, 0x0d, 0x03, 0x10, 0x0e, 0x13, 0x08, 0x0f, 0x10, 0x10, 0x14,
0x06, 0x11, 0x10, 0x12, 0x15, 0x03, 0x13, 0x0a, 0x5e, 0x0a, 0x05, 0x32,
0x09, 0x16, 0x01, 0x01, 0x09, 0x08, 0x10, 0x0a, 0x11, 0x01, 0x0b, 0x03,
0x10, 0x0c, 0x12, 0x0f, 0x0d, 0x00, 0x33, 0x36, 0x3c, 0x10, 0x0e, 0x14,
0x03, 0x0f, 0x0a, 0x10, 0x10, 0x15, 0x07, 0x11, 0x5e, 0x0a, 0x04, 0x32,
0x09, 0x16, 0x01, 0x01, 0x09, 0x08, 0x10, 0x0a, 0x11, 0x0f, 0x0b, 0x00,
0xce, 0xd4, 0xdf, 0x10, 0x0c, 0x12, 0x0f, 0x0d, 0x00, 0x24, 0x27, 0x2d,
0x10, 0x0e, 0x14, 0x03, 0x0f, 0x0f, 0x10, 0x10, 0x15, 0x01, 0x11, 0x06,
0x33, 0x11, 0x15, 0x46, 0x11, 0x01, 0x5e, 0x0a, 0x04, 0x32, 0x09, 0x16,
0x01, 0x01, 0x09, 0x08, 0x10, 0x0a, 0x11, 0x0f, 0x0b, 0x00, 0x24, 0x27,
0x2d, 0x10, 0x0c, 0x12, 0x06, 0x0d, 0x10, 0x0e, 0x14, 0x03, 0x0f, 0x0f,
0x01, 0x10, 0x06, 0x33, 0x10, 0x15, 0x45, 0x0f, 0x46, 0x0f, 0x01, 0x10,
0x10, 0x15, 0x07, 0x11, 0x5e, 0x0a, 0x04, 0x32, 0x09, 0x16, 0x01, 0x10,
0x09, 0x18, 0x01, 0x0a, 0x07, 0x10, 0x0b, 0x19, 0x01, 0x0c, 0x08, 0x5e,
0x09, 0x02, 0x3d, 0x09, 0x00, 0x09, 0x00, 0x00, 0x02, 0x20, 0x20, 0x00,
0x00, 0x00, 0x04, 0xee, 0x99, 0x8e, 0x20, 0x00, 0x00, 0x00, 0x05, 0xf3,
0xb1, 0x93, 0xa7, 0x20, 0x00, 0x00, 0x00, 0x05, 0xf3, 0xb1, 0xa9, 0xa7,
0x20, 0x00, 0x00, 0x00, 0x04, 0xef, 0x84, 0xa0, 0x20, 0x00, 0x00, 0x00,
0x04, 0xee, 0xba, 0xa2, 0x20, 0x00, 0x00, 0x00, 0x01, 0x20, 0x00, 0x00,
0x00, 0x08, 0x20, 0xee, 0x82, 0xb4, 0xee, 0x82, 0xb4, 0x20, 0x00, 0x00,
0x00, 0x03, 0xee, 0x82, 0xb4, 0x00, 0x00, 0x1a, 0x00, 0x04, 0x6d, 0x6f,
0x64, 0x65, 0x00, 0x00, 0x06, 0x6e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x00,
0x00, 0x03, 0x3d, 0x3d, 0x3d, 0x00, 0x00, 0x06, 0x69, 0x6e, 0x73, 0x65,
0x72, 0x74, 0x00, 0x00, 0x06, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x00,
0x00, 0x06, 0x72, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x00, 0x00, 0x06, 0x6a,
0x75, 0x6d, 0x70, 0x65, 0x72, 0x00, 0x00, 0x01, 0x43, 0x00, 0x00, 0x09,
0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x73, 0x00, 0x00, 0x09,
0x6c, 0x61, 0x6e, 0x67, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x00, 0x00, 0x07,
0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x00, 0x00, 0x04, 0x46, 0x69,
0x6c, 0x65, 0x00, 0x00, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d,
0x65, 0x00, 0x00, 0x08, 0x62, 0x61, 0x73, 0x65, 0x6e, 0x61, 0x6d, 0x65,
0x00, 0x00, 0x04, 0x74, 0x6f, 0x5f, 0x73, 0x00, 0x00, 0x06, 0x75, 0x70,
0x63, 0x61, 0x73, 0x65, 0x00, 0x00, 0x06, 0x73, 0x79, 0x6d, 0x62, 0x6f,
0x6c, 0x00, 0x00, 0x02, 0x66, 0x67, 0x00, 0x00, 0x02, 0x62, 0x67, 0x00,
0x00, 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x00, 0x00, 0x05, 0x73, 0x74,
0x61, 0x72, 0x74, 0x00, 0x00, 0x06, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68,
0x00, 0x00, 0x02, 0x3c, 0x3c, 0x00, 0x00, 0x05, 0x63, 0x6f, 0x6c, 0x6f,
0x72, 0x00, 0x00, 0x04, 0x74, 0x65, 0x78, 0x74, 0x00, 0x00, 0x0a, 0x68,
0x69, 0x67, 0x68, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x73, 0x00, 0x00, 0x00,
0x00, 0x37, 0x00, 0x03, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x10, 0x39, 0x04, 0x00, 0x00, 0x1d, 0x03, 0x00, 0x01, 0x04, 0x01,
0x32, 0x03, 0x01, 0x01, 0x3d, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x09,
0x43, 0x6c, 0x69, 0x70, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x00, 0x00, 0x04,
0x63, 0x6f, 0x70, 0x79, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x02, 0x00,
0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x39, 0x00, 0x00,
0x00, 0x1d, 0x02, 0x00, 0x33, 0x02, 0x01, 0x3d, 0x02, 0x00, 0x00, 0x00,
0x02, 0x00, 0x09, 0x43, 0x6c, 0x69, 0x70, 0x62, 0x6f, 0x61, 0x72, 0x64,
0x00, 0x00, 0x05, 0x70, 0x61, 0x73, 0x74, 0x65, 0x00, 0x00, 0x00, 0x03,
0xc8, 0x00, 0x07, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
0x3a, 0x39, 0x04, 0x00, 0x00, 0x10, 0x03, 0x00, 0x1d, 0x07, 0x01, 0x01,
0x08, 0x01, 0x32, 0x07, 0x02, 0x01, 0x27, 0x07, 0x00, 0x02, 0x3d, 0x03,
0x1d, 0x07, 0x01, 0x01, 0x08, 0x01, 0x10, 0x09, 0x03, 0x34, 0x07, 0x04,
0x01, 0x33, 0x07, 0x05, 0x01, 0x04, 0x07, 0x01, 0x07, 0x04, 0x5c, 0x08,
0x00, 0x32, 0x07, 0x06, 0x01, 0x28, 0x07, 0x00, 0xc7, 0x01, 0x07, 0x04,
0x08, 0x08, 0x11, 0x09, 0x64, 0x08, 0x23, 0x07, 0x33, 0x07, 0x07, 0x01,
0x05, 0x07, 0x01, 0x07, 0x05, 0x66, 0x08, 0x1f, 0x08, 0x08, 0x5c, 0x09,
0x01, 0x32, 0x08, 0x09, 0x01, 0x01, 0x09, 0x07, 0x32, 0x08, 0x0a, 0x01,
0x27, 0x08, 0x00, 0x17, 0x66, 0x08, 0x1f, 0x08, 0x08, 0x5c, 0x09, 0x02,
0x32, 0x08, 0x09, 0x01, 0x01, 0x09, 0x07, 0x32, 0x08, 0x0a, 0x01, 0x28,
0x08, 0x00, 0x06, 0x10, 0x08, 0x0b, 0x26, 0x00, 0x7a, 0x66, 0x08, 0x1f,
0x08, 0x08, 0x5c, 0x09, 0x03, 0x32, 0x08, 0x09, 0x01, 0x01, 0x09, 0x07,
0x32, 0x08, 0x0a, 0x01, 0x28, 0x08, 0x00, 0x06, 0x10, 0x08, 0x0c, 0x26,
0x00, 0x5d, 0x66, 0x08, 0x1f, 0x08, 0x08, 0x5c, 0x09, 0x04, 0x32, 0x08,
0x09, 0x01, 0x01, 0x09, 0x07, 0x32, 0x08, 0x0a, 0x01, 0x28, 0x08, 0x00,
0x06, 0x10, 0x08, 0x0d, 0x26, 0x00, 0x40, 0x66, 0x08, 0x1f, 0x08, 0x08,
0x5c, 0x09, 0x05, 0x32, 0x08, 0x09, 0x01, 0x01, 0x09, 0x07, 0x32, 0x08,
0x0a, 0x01, 0x28, 0x08, 0x00, 0x06, 0x10, 0x08, 0x0e, 0x26, 0x00, 0x23,
0x66, 0x08, 0x1f, 0x08, 0x08, 0x5c, 0x09, 0x06, 0x32, 0x08, 0x09, 0x01,
0x01, 0x09, 0x07, 0x32, 0x08, 0x0a, 0x01, 0x28, 0x08, 0x00, 0x06, 0x10,
0x08, 0x0f, 0x26, 0x00, 0x06, 0x10, 0x08, 0x00, 0x26, 0x00, 0x00, 0x01,
0x03, 0x08, 0x3d, 0x03, 0x10, 0x07, 0x10, 0x10, 0x08, 0x10, 0x32, 0x07,
0x11, 0x01, 0x27, 0x07, 0x00, 0x0a, 0x10, 0x07, 0x10, 0x10, 0x08, 0x12,
0x32, 0x07, 0x11, 0x01, 0x28, 0x07, 0x00, 0x02, 0x3d, 0x03, 0x5c, 0x08,
0x07, 0x2f, 0x07, 0x13, 0x01, 0x33, 0x07, 0x14, 0x28, 0x07, 0x00, 0x02,
0x3d, 0x03, 0x5c, 0x08, 0x08, 0x01, 0x09, 0x01, 0x5d, 0x08, 0x5c, 0x09,
0x09, 0x5d, 0x08, 0x2f, 0x07, 0x15, 0x01, 0x33, 0x07, 0x05, 0x01, 0x06,
0x07, 0x01, 0x07, 0x06, 0x66, 0x08, 0x1f, 0x08, 0x08, 0x5c, 0x09, 0x0a,
0x32, 0x08, 0x09, 0x01, 0x01, 0x09, 0x07, 0x32, 0x08, 0x0a, 0x01, 0x28,
0x08, 0x00, 0x06, 0x10, 0x08, 0x0b, 0x26, 0x00, 0xd1, 0x66, 0x08, 0x1f,
0x08, 0x08, 0x5c, 0x09, 0x05, 0x32, 0x08, 0x09, 0x01, 0x01, 0x09, 0x07,
0x32, 0x08, 0x0a, 0x01, 0x28, 0x08, 0x00, 0x06, 0x10, 0x08, 0x0e, 0x26,
0x00, 0xb4, 0x66, 0x08, 0x1f, 0x08, 0x08, 0x5c, 0x09, 0x0b, 0x32, 0x08,
0x09, 0x01, 0x01, 0x09, 0x07, 0x32, 0x08, 0x0a, 0x01, 0x28, 0x08, 0x00,
0x06, 0x10, 0x08, 0x16, 0x26, 0x00, 0x97, 0x66, 0x08, 0x1f, 0x08, 0x08,
0x5c, 0x09, 0x0c, 0x32, 0x08, 0x09, 0x01, 0x01, 0x09, 0x07, 0x32, 0x08,
0x0a, 0x01, 0x28, 0x08, 0x00, 0x06, 0x10, 0x08, 0x17, 0x26, 0x00, 0x7a,
0x66, 0x08, 0x1f, 0x08, 0x08, 0x5c, 0x09, 0x04, 0x32, 0x08, 0x09, 0x01,
0x01, 0x09, 0x07, 0x32, 0x08, 0x0a, 0x01, 0x28, 0x08, 0x00, 0x06, 0x10,
0x08, 0x0d, 0x26, 0x00, 0x5d, 0x66, 0x08, 0x1f, 0x08, 0x08, 0x5c, 0x09,
0x0d, 0x32, 0x08, 0x09, 0x01, 0x01, 0x09, 0x07, 0x32, 0x08, 0x0a, 0x01,
0x28, 0x08, 0x00, 0x06, 0x10, 0x08, 0x18, 0x26, 0x00, 0x40, 0x66, 0x08,
0x1f, 0x08, 0x08, 0x5c, 0x09, 0x0e, 0x32, 0x08, 0x09, 0x01, 0x01, 0x09,
0x07, 0x32, 0x08, 0x0a, 0x01, 0x28, 0x08, 0x00, 0x06, 0x10, 0x08, 0x19,
0x26, 0x00, 0x23, 0x66, 0x08, 0x1f, 0x08, 0x08, 0x5c, 0x09, 0x0f, 0x32,
0x33, 0x36, 0x3c, 0x10, 0x0c, 0x12, 0x0f, 0x0d, 0x00, 0x24, 0x27, 0x2d,
0x10, 0x0e, 0x14, 0x03, 0x0f, 0x0b, 0x10, 0x10, 0x15, 0x07, 0x11, 0x5e,
0x0a, 0x04, 0x32, 0x09, 0x16, 0x01, 0x01, 0x09, 0x08, 0x10, 0x0a, 0x11,
0x01, 0x0b, 0x05, 0x10, 0x0c, 0x17, 0x23, 0x0b, 0x10, 0x0c, 0x12, 0x0f,
0x0d, 0x00, 0x24, 0x27, 0x2d, 0x10, 0x0e, 0x14, 0x03, 0x0f, 0x0d, 0x10,
0x10, 0x15, 0x08, 0x11, 0x5e, 0x0a, 0x04, 0x32, 0x09, 0x16, 0x01, 0x01,
0x09, 0x08, 0x10, 0x0a, 0x11, 0x0f, 0x0b, 0x00, 0xce, 0xd4, 0xdf, 0x10,
0x0c, 0x12, 0x0f, 0x0d, 0x00, 0x24, 0x27, 0x2d, 0x10, 0x0e, 0x14, 0x03,
0x0f, 0x0f, 0x10, 0x10, 0x15, 0x01, 0x11, 0x06, 0x33, 0x11, 0x15, 0x46,
0x11, 0x01, 0x5e, 0x0a, 0x04, 0x32, 0x09, 0x16, 0x01, 0x01, 0x09, 0x08,
0x10, 0x0a, 0x11, 0x0f, 0x0b, 0x00, 0x24, 0x27, 0x2d, 0x10, 0x0c, 0x12,
0x06, 0x0d, 0x10, 0x0e, 0x14, 0x03, 0x0f, 0x0f, 0x01, 0x10, 0x06, 0x33,
0x10, 0x15, 0x45, 0x0f, 0x46, 0x0f, 0x01, 0x10, 0x10, 0x15, 0x07, 0x11,
0x5e, 0x0a, 0x04, 0x32, 0x09, 0x16, 0x01, 0x10, 0x09, 0x18, 0x01, 0x0a,
0x07, 0x10, 0x0b, 0x19, 0x01, 0x0c, 0x08, 0x5e, 0x09, 0x02, 0x3d, 0x09,
0x00, 0x0b, 0x00, 0x00, 0x02, 0x20, 0x20, 0x00, 0x00, 0x00, 0x04, 0xee,
0x99, 0x8e, 0x20, 0x00, 0x00, 0x00, 0x05, 0xf3, 0xb1, 0x93, 0xa7, 0x20,
0x00, 0x00, 0x00, 0x05, 0xf3, 0xb1, 0xa9, 0xa7, 0x20, 0x00, 0x00, 0x00,
0x04, 0xef, 0x84, 0xa0, 0x20, 0x00, 0x00, 0x00, 0x04, 0xee, 0xba, 0xa2,
0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x65, 0x64, 0x69,
0x74, 0x6f, 0x72, 0x00, 0x00, 0x00, 0x01, 0x20, 0x00, 0x00, 0x00, 0x08,
0x20, 0xee, 0x82, 0xb4, 0xee, 0x82, 0xb4, 0x20, 0x00, 0x00, 0x00, 0x03,
0xee, 0x82, 0xb4, 0x00, 0x00, 0x1a, 0x00, 0x04, 0x6d, 0x6f, 0x64, 0x65,
0x00, 0x00, 0x06, 0x6e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x00, 0x00, 0x03,
0x3d, 0x3d, 0x3d, 0x00, 0x00, 0x06, 0x69, 0x6e, 0x73, 0x65, 0x72, 0x74,
0x00, 0x00, 0x06, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x00, 0x00, 0x06,
0x72, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x00, 0x00, 0x06, 0x6a, 0x75, 0x6d,
0x70, 0x65, 0x72, 0x00, 0x00, 0x01, 0x43, 0x00, 0x00, 0x09, 0x6c, 0x61,
0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x73, 0x00, 0x00, 0x07, 0x64, 0x65,
0x66, 0x61, 0x75, 0x6c, 0x74, 0x00, 0x00, 0x04, 0x64, 0x61, 0x74, 0x61,
0x00, 0x00, 0x02, 0x21, 0x3d, 0x00, 0x00, 0x04, 0x46, 0x69, 0x6c, 0x65,
0x00, 0x00, 0x08, 0x62, 0x61, 0x73, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x00,
0x00, 0x04, 0x74, 0x6f, 0x5f, 0x73, 0x00, 0x00, 0x06, 0x75, 0x70, 0x63,
0x61, 0x73, 0x65, 0x00, 0x00, 0x06, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c,
0x00, 0x00, 0x02, 0x66, 0x67, 0x00, 0x00, 0x02, 0x62, 0x67, 0x00, 0x00,
0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x00, 0x00, 0x05, 0x73, 0x74, 0x61,
0x72, 0x74, 0x00, 0x00, 0x06, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x00,
0x00, 0x02, 0x3c, 0x3c, 0x00, 0x00, 0x05, 0x63, 0x6f, 0x6c, 0x6f, 0x72,
0x00, 0x00, 0x04, 0x74, 0x65, 0x78, 0x74, 0x00, 0x00, 0x0a, 0x68, 0x69,
0x67, 0x68, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x73, 0x00, 0x00, 0x00, 0x00,
0x37, 0x00, 0x03, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x10, 0x39, 0x04, 0x00, 0x00, 0x1d, 0x03, 0x00, 0x01, 0x04, 0x01, 0x32,
0x03, 0x01, 0x01, 0x3d, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x09, 0x43,
0x6c, 0x69, 0x70, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x00, 0x00, 0x04, 0x63,
0x6f, 0x70, 0x79, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x02, 0x00, 0x04,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x39, 0x00, 0x00, 0x00,
0x1d, 0x02, 0x00, 0x33, 0x02, 0x01, 0x3d, 0x02, 0x00, 0x00, 0x00, 0x02,
0x00, 0x09, 0x43, 0x6c, 0x69, 0x70, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x00,
0x00, 0x05, 0x70, 0x61, 0x73, 0x74, 0x65, 0x00, 0x00, 0x00, 0x03, 0xc8,
0x00, 0x07, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x3a,
0x39, 0x04, 0x00, 0x00, 0x10, 0x03, 0x00, 0x1d, 0x07, 0x01, 0x01, 0x08,
0x01, 0x32, 0x07, 0x02, 0x01, 0x27, 0x07, 0x00, 0x02, 0x3d, 0x03, 0x1d,
0x07, 0x01, 0x01, 0x08, 0x01, 0x10, 0x09, 0x03, 0x34, 0x07, 0x04, 0x01,
0x33, 0x07, 0x05, 0x01, 0x04, 0x07, 0x01, 0x07, 0x04, 0x5c, 0x08, 0x00,
0x32, 0x07, 0x06, 0x01, 0x28, 0x07, 0x00, 0xc7, 0x01, 0x07, 0x04, 0x08,
0x08, 0x11, 0x09, 0x64, 0x08, 0x23, 0x07, 0x33, 0x07, 0x07, 0x01, 0x05,
0x07, 0x01, 0x07, 0x05, 0x66, 0x08, 0x1f, 0x08, 0x08, 0x5c, 0x09, 0x01,
0x32, 0x08, 0x09, 0x01, 0x01, 0x09, 0x07, 0x32, 0x08, 0x0a, 0x01, 0x27,
0x08, 0x00, 0x17, 0x66, 0x08, 0x1f, 0x08, 0x08, 0x5c, 0x09, 0x02, 0x32,
0x08, 0x09, 0x01, 0x01, 0x09, 0x07, 0x32, 0x08, 0x0a, 0x01, 0x28, 0x08,
0x00, 0x06, 0x10, 0x08, 0x1a, 0x26, 0x00, 0x06, 0x10, 0x08, 0x00, 0x26,
0x00, 0x00, 0x01, 0x03, 0x08, 0x3d, 0x03, 0x00, 0x10, 0x00, 0x00, 0x02,
0x23, 0x21, 0x00, 0x00, 0x00, 0x04, 0x62, 0x61, 0x73, 0x68, 0x00, 0x00,
0x00, 0x02, 0x73, 0x68, 0x00, 0x00, 0x00, 0x04, 0x66, 0x69, 0x73, 0x68,
0x00, 0x00, 0x00, 0x06, 0x70, 0x79, 0x74, 0x68, 0x6f, 0x6e, 0x00, 0x00,
0x00, 0x04, 0x72, 0x75, 0x62, 0x79, 0x00, 0x00, 0x00, 0x03, 0x6c, 0x75,
0x61, 0x00, 0x00, 0x00, 0x04, 0x66, 0x69, 0x6c, 0x65, 0x00, 0x00, 0x00,
0x14, 0x66, 0x69, 0x6c, 0x65, 0x20, 0x2d, 0x2d, 0x6d, 0x69, 0x6d, 0x65,
0x2d, 0x74, 0x79, 0x70, 0x65, 0x20, 0x2d, 0x62, 0x20, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x0b, 0x73, 0x68, 0x65, 0x6c, 0x6c, 0x73, 0x63,
0x72, 0x69, 0x70, 0x74, 0x00, 0x00, 0x00, 0x04, 0x64, 0x69, 0x66, 0x66,
0x00, 0x00, 0x00, 0x04, 0x68, 0x74, 0x6d, 0x6c, 0x00, 0x00, 0x00, 0x0a,
0x6a, 0x61, 0x76, 0x61, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x00, 0x00,
0x00, 0x08, 0x6d, 0x61, 0x6b, 0x65, 0x66, 0x69, 0x6c, 0x65, 0x00, 0x00,
0x00, 0x03, 0x2d, 0x63, 0x24, 0x00, 0x00, 0x1b, 0x00, 0x07, 0x64, 0x65,
0x66, 0x61, 0x75, 0x6c, 0x74, 0x00, 0x00, 0x04, 0x46, 0x69, 0x6c, 0x65,
0x00, 0x00, 0x06, 0x65, 0x78, 0x69, 0x73, 0x74, 0x3f, 0x00, 0x00, 0x08,
0x72, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x00, 0x00, 0x04, 0x6f,
0x70, 0x65, 0x6e, 0x00, 0x00, 0x05, 0x63, 0x68, 0x6f, 0x6d, 0x70, 0x00,
0x00, 0x0b, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x77, 0x69, 0x74, 0x68,
0x3f, 0x00, 0x00, 0x08, 0x64, 0x6f, 0x77, 0x6e, 0x63, 0x61, 0x73, 0x65,
0x00, 0x00, 0x06, 0x52, 0x65, 0x67, 0x65, 0x78, 0x70, 0x00, 0x00, 0x07,
0x63, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x00, 0x00, 0x03, 0x3d, 0x3d,
0x3d, 0x00, 0x00, 0x04, 0x62, 0x61, 0x73, 0x68, 0x00, 0x00, 0x04, 0x66,
0x69, 0x73, 0x68, 0x00, 0x00, 0x06, 0x70, 0x79, 0x74, 0x68, 0x6f, 0x6e,
0x00, 0x00, 0x04, 0x72, 0x75, 0x62, 0x79, 0x00, 0x00, 0x03, 0x6c, 0x75,
0x61, 0x00, 0x00, 0x05, 0x6c, 0x69, 0x6e, 0x75, 0x78, 0x00, 0x00, 0x02,
0x21, 0x3d, 0x00, 0x00, 0x03, 0x6d, 0x61, 0x63, 0x00, 0x00, 0x0f, 0x63,
0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x5f, 0x65, 0x78, 0x69, 0x73, 0x74,
0x73, 0x3f, 0x00, 0x00, 0x01, 0x21, 0x00, 0x00, 0x01, 0x60, 0x00, 0x00,
0x04, 0x64, 0x69, 0x66, 0x66, 0x00, 0x00, 0x04, 0x68, 0x74, 0x6d, 0x6c,
0x00, 0x00, 0x0a, 0x6a, 0x61, 0x76, 0x61, 0x73, 0x63, 0x72, 0x69, 0x70,
0x74, 0x00, 0x00, 0x08, 0x6d, 0x61, 0x6b, 0x65, 0x66, 0x69, 0x6c, 0x65,
0x00, 0x00, 0x01, 0x63, 0x00, 0x00, 0x00, 0x01, 0x6b, 0x00, 0x01, 0x00,
0x0a, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x10, 0x02, 0x00,
0x10, 0x03, 0x01, 0x10, 0x04, 0x02, 0x10, 0x05, 0x03, 0x10, 0x06, 0x04,
0x2f, 0x01, 0x05, 0x05, 0x10, 0x02, 0x06, 0x10, 0x03, 0x07, 0x10, 0x04,
0x08, 0x10, 0x05, 0x09, 0x10, 0x06, 0x0a, 0x10, 0x07, 0x0b, 0x10, 0x08,
0x0c, 0x2f, 0x01, 0x0d, 0x07, 0x6b, 0x01, 0x0e, 0x00, 0x6b, 0x01, 0x0f,
0x01, 0x6b, 0x01, 0x10, 0x02, 0x6b, 0x01, 0x11, 0x03, 0x6b, 0x01, 0x12,
0x04, 0x6b, 0x01, 0x13, 0x05, 0x6b, 0x01, 0x14, 0x06, 0x6b, 0x01, 0x15,
0x07, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x05, 0x74, 0x68, 0x65,
0x6d, 0x65, 0x00, 0x00, 0x0a, 0x6c, 0x73, 0x70, 0x5f, 0x63, 0x6f, 0x6e,
0x66, 0x69, 0x67, 0x00, 0x00, 0x09, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61,
0x67, 0x65, 0x73, 0x00, 0x00, 0x0c, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x65,
0x6e, 0x64, 0x69, 0x6e, 0x67, 0x73, 0x00, 0x00, 0x0c, 0x68, 0x69, 0x67,
0x68, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x65, 0x72, 0x73, 0x00, 0x00, 0x0d,
0x61, 0x74, 0x74, 0x72, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x6f,
0x72, 0x00, 0x00, 0x09, 0x62, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x75,
0x70, 0x00, 0x00, 0x0a, 0x62, 0x5f, 0x73, 0x68, 0x75, 0x74, 0x64, 0x6f,
0x77, 0x6e, 0x00, 0x00, 0x12, 0x62, 0x5f, 0x65, 0x78, 0x74, 0x72, 0x61,
0x00, 0x06, 0x10, 0x08, 0x0b, 0x26, 0x00, 0x7a, 0x66, 0x08, 0x1f, 0x08,
0x08, 0x5c, 0x09, 0x03, 0x32, 0x08, 0x09, 0x01, 0x01, 0x09, 0x07, 0x32,
0x08, 0x0a, 0x01, 0x28, 0x08, 0x00, 0x06, 0x10, 0x08, 0x0c, 0x26, 0x00,
0x5d, 0x66, 0x08, 0x1f, 0x08, 0x08, 0x5c, 0x09, 0x04, 0x32, 0x08, 0x09,
0x01, 0x01, 0x09, 0x07, 0x32, 0x08, 0x0a, 0x01, 0x28, 0x08, 0x00, 0x06,
0x10, 0x08, 0x0d, 0x26, 0x00, 0x40, 0x66, 0x08, 0x1f, 0x08, 0x08, 0x5c,
0x09, 0x05, 0x32, 0x08, 0x09, 0x01, 0x01, 0x09, 0x07, 0x32, 0x08, 0x0a,
0x01, 0x28, 0x08, 0x00, 0x06, 0x10, 0x08, 0x0e, 0x26, 0x00, 0x23, 0x66,
0x08, 0x1f, 0x08, 0x08, 0x5c, 0x09, 0x06, 0x32, 0x08, 0x09, 0x01, 0x01,
0x09, 0x07, 0x32, 0x08, 0x0a, 0x01, 0x28, 0x08, 0x00, 0x06, 0x10, 0x08,
0x0f, 0x26, 0x00, 0x06, 0x10, 0x08, 0x00, 0x26, 0x00, 0x00, 0x01, 0x03,
0x08, 0x3d, 0x03, 0x10, 0x07, 0x10, 0x10, 0x08, 0x10, 0x32, 0x07, 0x11,
0x01, 0x27, 0x07, 0x00, 0x0a, 0x10, 0x07, 0x10, 0x10, 0x08, 0x12, 0x32,
0x07, 0x11, 0x01, 0x28, 0x07, 0x00, 0x02, 0x3d, 0x03, 0x5c, 0x08, 0x07,
0x2f, 0x07, 0x13, 0x01, 0x33, 0x07, 0x14, 0x28, 0x07, 0x00, 0x02, 0x3d,
0x03, 0x5c, 0x08, 0x08, 0x01, 0x09, 0x01, 0x5d, 0x08, 0x5c, 0x09, 0x09,
0x5d, 0x08, 0x2f, 0x07, 0x15, 0x01, 0x33, 0x07, 0x05, 0x01, 0x06, 0x07,
0x01, 0x07, 0x06, 0x66, 0x08, 0x1f, 0x08, 0x08, 0x5c, 0x09, 0x0a, 0x32,
0x08, 0x09, 0x01, 0x01, 0x09, 0x07, 0x32, 0x08, 0x0a, 0x01, 0x28, 0x08,
0x00, 0x06, 0x10, 0x08, 0x0b, 0x26, 0x00, 0xd1, 0x66, 0x08, 0x1f, 0x08,
0x08, 0x5c, 0x09, 0x05, 0x32, 0x08, 0x09, 0x01, 0x01, 0x09, 0x07, 0x32,
0x08, 0x0a, 0x01, 0x28, 0x08, 0x00, 0x06, 0x10, 0x08, 0x0e, 0x26, 0x00,
0xb4, 0x66, 0x08, 0x1f, 0x08, 0x08, 0x5c, 0x09, 0x0b, 0x32, 0x08, 0x09,
0x01, 0x01, 0x09, 0x07, 0x32, 0x08, 0x0a, 0x01, 0x28, 0x08, 0x00, 0x06,
0x10, 0x08, 0x16, 0x26, 0x00, 0x97, 0x66, 0x08, 0x1f, 0x08, 0x08, 0x5c,
0x09, 0x0c, 0x32, 0x08, 0x09, 0x01, 0x01, 0x09, 0x07, 0x32, 0x08, 0x0a,
0x01, 0x28, 0x08, 0x00, 0x06, 0x10, 0x08, 0x17, 0x26, 0x00, 0x7a, 0x66,
0x08, 0x1f, 0x08, 0x08, 0x5c, 0x09, 0x04, 0x32, 0x08, 0x09, 0x01, 0x01,
0x09, 0x07, 0x32, 0x08, 0x0a, 0x01, 0x28, 0x08, 0x00, 0x06, 0x10, 0x08,
0x0d, 0x26, 0x00, 0x5d, 0x66, 0x08, 0x1f, 0x08, 0x08, 0x5c, 0x09, 0x0d,
0x32, 0x08, 0x09, 0x01, 0x01, 0x09, 0x07, 0x32, 0x08, 0x0a, 0x01, 0x28,
0x08, 0x00, 0x06, 0x10, 0x08, 0x18, 0x26, 0x00, 0x40, 0x66, 0x08, 0x1f,
0x08, 0x08, 0x5c, 0x09, 0x0e, 0x32, 0x08, 0x09, 0x01, 0x01, 0x09, 0x07,
0x32, 0x08, 0x0a, 0x01, 0x28, 0x08, 0x00, 0x06, 0x10, 0x08, 0x19, 0x26,
0x00, 0x23, 0x66, 0x08, 0x1f, 0x08, 0x08, 0x5c, 0x09, 0x0f, 0x32, 0x08,
0x09, 0x01, 0x01, 0x09, 0x07, 0x32, 0x08, 0x0a, 0x01, 0x28, 0x08, 0x00,
0x06, 0x10, 0x08, 0x1a, 0x26, 0x00, 0x06, 0x10, 0x08, 0x00, 0x26, 0x00,
0x00, 0x01, 0x03, 0x08, 0x3d, 0x03, 0x00, 0x10, 0x00, 0x00, 0x02, 0x23,
0x21, 0x00, 0x00, 0x00, 0x04, 0x62, 0x61, 0x73, 0x68, 0x00, 0x00, 0x00,
0x02, 0x73, 0x68, 0x00, 0x00, 0x00, 0x04, 0x66, 0x69, 0x73, 0x68, 0x00,
0x00, 0x00, 0x06, 0x70, 0x79, 0x74, 0x68, 0x6f, 0x6e, 0x00, 0x00, 0x00,
0x04, 0x72, 0x75, 0x62, 0x79, 0x00, 0x00, 0x00, 0x03, 0x6c, 0x75, 0x61,
0x00, 0x00, 0x00, 0x04, 0x66, 0x69, 0x6c, 0x65, 0x00, 0x00, 0x00, 0x14,
0x66, 0x69, 0x6c, 0x65, 0x20, 0x2d, 0x2d, 0x6d, 0x69, 0x6d, 0x65, 0x2d,
0x74, 0x79, 0x70, 0x65, 0x20, 0x2d, 0x62, 0x20, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x0b, 0x73, 0x68, 0x65, 0x6c, 0x6c, 0x73, 0x63, 0x72,
0x69, 0x70, 0x74, 0x00, 0x00, 0x00, 0x04, 0x64, 0x69, 0x66, 0x66, 0x00,
0x00, 0x00, 0x04, 0x68, 0x74, 0x6d, 0x6c, 0x00, 0x00, 0x00, 0x0a, 0x6a,
0x61, 0x76, 0x61, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x00, 0x00, 0x00,
0x08, 0x6d, 0x61, 0x6b, 0x65, 0x66, 0x69, 0x6c, 0x65, 0x00, 0x00, 0x00,
0x03, 0x2d, 0x63, 0x24, 0x00, 0x00, 0x1b, 0x00, 0x07, 0x64, 0x65, 0x66,
0x61, 0x75, 0x6c, 0x74, 0x00, 0x00, 0x04, 0x46, 0x69, 0x6c, 0x65, 0x00,
0x00, 0x06, 0x65, 0x78, 0x69, 0x73, 0x74, 0x3f, 0x00, 0x00, 0x08, 0x72,
0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x00, 0x00, 0x04, 0x6f, 0x70,
0x65, 0x6e, 0x00, 0x00, 0x05, 0x63, 0x68, 0x6f, 0x6d, 0x70, 0x00, 0x00,
0x0b, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x77, 0x69, 0x74, 0x68, 0x3f,
0x00, 0x00, 0x08, 0x64, 0x6f, 0x77, 0x6e, 0x63, 0x61, 0x73, 0x65, 0x00,
0x00, 0x06, 0x52, 0x65, 0x67, 0x65, 0x78, 0x70, 0x00, 0x00, 0x07, 0x63,
0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x00, 0x00, 0x03, 0x3d, 0x3d, 0x3d,
0x00, 0x00, 0x04, 0x62, 0x61, 0x73, 0x68, 0x00, 0x00, 0x04, 0x66, 0x69,
0x73, 0x68, 0x00, 0x00, 0x06, 0x70, 0x79, 0x74, 0x68, 0x6f, 0x6e, 0x00,
0x00, 0x04, 0x72, 0x75, 0x62, 0x79, 0x00, 0x00, 0x03, 0x6c, 0x75, 0x61,
0x00, 0x00, 0x05, 0x6c, 0x69, 0x6e, 0x75, 0x78, 0x00, 0x00, 0x02, 0x21,
0x3d, 0x00, 0x00, 0x03, 0x6d, 0x61, 0x63, 0x00, 0x00, 0x0f, 0x63, 0x6f,
0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x5f, 0x65, 0x78, 0x69, 0x73, 0x74, 0x73,
0x3f, 0x00, 0x00, 0x01, 0x21, 0x00, 0x00, 0x01, 0x60, 0x00, 0x00, 0x04,
0x64, 0x69, 0x66, 0x66, 0x00, 0x00, 0x04, 0x68, 0x74, 0x6d, 0x6c, 0x00,
0x00, 0x0a, 0x6a, 0x61, 0x76, 0x61, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74,
0x00, 0x00, 0x08, 0x6d, 0x61, 0x6b, 0x65, 0x66, 0x69, 0x6c, 0x65, 0x00,
0x00, 0x01, 0x63, 0x00, 0x00, 0x00, 0x01, 0x6b, 0x00, 0x01, 0x00, 0x0a,
0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x10, 0x02, 0x00, 0x10,
0x03, 0x01, 0x10, 0x04, 0x02, 0x10, 0x05, 0x03, 0x10, 0x06, 0x04, 0x2f,
0x01, 0x05, 0x05, 0x10, 0x02, 0x06, 0x10, 0x03, 0x07, 0x10, 0x04, 0x08,
0x10, 0x05, 0x09, 0x10, 0x06, 0x0a, 0x10, 0x07, 0x0b, 0x10, 0x08, 0x0c,
0x2f, 0x01, 0x0d, 0x07, 0x6b, 0x01, 0x0e, 0x00, 0x6b, 0x01, 0x0f, 0x01,
0x6b, 0x01, 0x10, 0x02, 0x6b, 0x01, 0x11, 0x03, 0x6b, 0x01, 0x12, 0x04,
0x6b, 0x01, 0x13, 0x05, 0x6b, 0x01, 0x14, 0x06, 0x6b, 0x01, 0x15, 0x07,
0x3d, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x05, 0x74, 0x68, 0x65, 0x6d,
0x65, 0x00, 0x00, 0x0a, 0x6c, 0x73, 0x70, 0x5f, 0x63, 0x6f, 0x6e, 0x66,
0x69, 0x67, 0x00, 0x00, 0x09, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67,
0x65, 0x73, 0x00, 0x00, 0x0c, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x65, 0x6e,
0x64, 0x69, 0x6e, 0x67, 0x73, 0x00, 0x00, 0x0c, 0x68, 0x69, 0x67, 0x68,
0x6c, 0x69, 0x67, 0x68, 0x74, 0x65, 0x72, 0x73, 0x00, 0x00, 0x0d, 0x61,
0x74, 0x74, 0x72, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72,
0x00, 0x00, 0x09, 0x62, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70,
0x00, 0x00, 0x0a, 0x62, 0x5f, 0x73, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77,
0x6e, 0x00, 0x00, 0x12, 0x62, 0x5f, 0x65, 0x78, 0x74, 0x72, 0x61, 0x5f,
0x68, 0x69, 0x67, 0x68, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x73, 0x00, 0x00,
0x05, 0x62, 0x5f, 0x62, 0x61, 0x72, 0x00, 0x00, 0x06, 0x62, 0x5f, 0x63,
0x6f, 0x70, 0x79, 0x00, 0x00, 0x07, 0x62, 0x5f, 0x70, 0x61, 0x73, 0x74,
0x65, 0x00, 0x00, 0x0d, 0x62, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x64,
0x65, 0x74, 0x65, 0x63, 0x74, 0x00, 0x00, 0x0b, 0x61, 0x74, 0x74, 0x72,
0x5f, 0x72, 0x65, 0x61, 0x64, 0x65, 0x72, 0x00, 0x00, 0x04, 0x62, 0x61,
0x72, 0x3d, 0x00, 0x00, 0x07, 0x73, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70,
0x00, 0x00, 0x08, 0x73, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x00,
0x00, 0x04, 0x63, 0x6f, 0x70, 0x79, 0x00, 0x00, 0x05, 0x70, 0x61, 0x73,
0x74, 0x65, 0x00, 0x00, 0x0b, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x64, 0x65,
0x74, 0x65, 0x63, 0x74, 0x00, 0x00, 0x10, 0x65, 0x78, 0x74, 0x72, 0x61,
0x5f, 0x68, 0x69, 0x67, 0x68, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x73, 0x00,
0x00, 0x05, 0x62, 0x5f, 0x62, 0x61, 0x72, 0x00, 0x00, 0x06, 0x62, 0x5f,
0x63, 0x6f, 0x70, 0x79, 0x00, 0x00, 0x07, 0x62, 0x5f, 0x70, 0x61, 0x73,
0x74, 0x65, 0x00, 0x00, 0x0d, 0x62, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x5f,
0x64, 0x65, 0x74, 0x65, 0x63, 0x74, 0x00, 0x00, 0x0b, 0x61, 0x74, 0x74,
0x72, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x65, 0x72, 0x00, 0x00, 0x04, 0x62,
0x61, 0x72, 0x3d, 0x00, 0x00, 0x07, 0x73, 0x74, 0x61, 0x72, 0x74, 0x75,
0x70, 0x00, 0x00, 0x08, 0x73, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e,
0x00, 0x00, 0x04, 0x63, 0x6f, 0x70, 0x79, 0x00, 0x00, 0x05, 0x70, 0x61,
0x73, 0x74, 0x65, 0x00, 0x00, 0x0b, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x64,
0x65, 0x74, 0x65, 0x63, 0x74, 0x00, 0x00, 0x10, 0x65, 0x78, 0x74, 0x72,
0x61, 0x5f, 0x68, 0x69, 0x67, 0x68, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x73,
0x00, 0x00, 0x04, 0x62, 0x69, 0x6e, 0x64, 0x00, 0x00, 0x00, 0x00, 0x2c,
0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f,
0x39, 0x00, 0x00, 0x01, 0x01, 0x02, 0x01, 0x01, 0x03, 0x02, 0x1a, 0x03,
0x00, 0x3d, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x06, 0x40, 0x62, 0x5f,
0x62, 0x61, 0x72, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x03, 0x00, 0x04,
0x00, 0x04, 0x62, 0x69, 0x6e, 0x64, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00,
0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x39,
0x00, 0x00, 0x01, 0x01, 0x02, 0x01, 0x01, 0x03, 0x02, 0x1a, 0x03, 0x00,
0x3d, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x06, 0x40, 0x62, 0x5f, 0x62,
0x61, 0x72, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x03, 0x00, 0x04, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x39, 0x00, 0x00, 0x01, 0x01,
0x02, 0x01, 0x01, 0x03, 0x02, 0x1a, 0x03, 0x00, 0x3d, 0x03, 0x00, 0x00,
0x00, 0x01, 0x00, 0x0a, 0x40, 0x62, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74,
0x75, 0x70, 0x00, 0x00, 0x00, 0x00, 0x31, 0x00, 0x03, 0x00, 0x04, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x39, 0x00, 0x00, 0x01, 0x01,
0x02, 0x01, 0x01, 0x03, 0x02, 0x1a, 0x03, 0x00, 0x3d, 0x03, 0x00, 0x00,
0x00, 0x01, 0x00, 0x0b, 0x40, 0x62, 0x5f, 0x73, 0x68, 0x75, 0x74, 0x64,
0x6f, 0x77, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x03, 0x00, 0x04,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x39, 0x00, 0x00, 0x01,
0x01, 0x02, 0x01, 0x01, 0x03, 0x02, 0x1a, 0x03, 0x00, 0x3d, 0x03, 0x00,
0x00, 0x00, 0x01, 0x00, 0x0a, 0x40, 0x62, 0x5f, 0x73, 0x74, 0x61, 0x72,
0x74, 0x75, 0x70, 0x00, 0x00, 0x00, 0x00, 0x31, 0x00, 0x03, 0x00, 0x04,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x39, 0x00, 0x00, 0x01,
0x01, 0x02, 0x01, 0x01, 0x03, 0x02, 0x1a, 0x03, 0x00, 0x3d, 0x03, 0x00,
0x00, 0x00, 0x01, 0x00, 0x0b, 0x40, 0x62, 0x5f, 0x73, 0x68, 0x75, 0x74,
0x64, 0x6f, 0x77, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x03, 0x00,
0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x39, 0x00, 0x00,
0x01, 0x01, 0x02, 0x01, 0x01, 0x03, 0x02, 0x1a, 0x03, 0x00, 0x3d, 0x03,
0x00, 0x00, 0x00, 0x01, 0x00, 0x07, 0x40, 0x62, 0x5f, 0x63, 0x6f, 0x70,
0x79, 0x00, 0x00, 0x00, 0x00, 0x2e, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x39, 0x00, 0x00, 0x01, 0x01, 0x02,
0x01, 0x01, 0x03, 0x02, 0x1a, 0x03, 0x00, 0x3d, 0x03, 0x00, 0x00, 0x00,
0x01, 0x00, 0x08, 0x40, 0x62, 0x5f, 0x70, 0x61, 0x73, 0x74, 0x65, 0x00,
0x00, 0x00, 0x00, 0x34, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x0f, 0x39, 0x00, 0x00, 0x01, 0x01, 0x02, 0x01, 0x01,
0x03, 0x02, 0x1a, 0x03, 0x00, 0x3d, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00,
0x0e, 0x40, 0x62, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x64, 0x65, 0x74,
0x65, 0x63, 0x74, 0x00, 0x00, 0x00, 0x00, 0x39, 0x00, 0x03, 0x00, 0x04,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x39, 0x00, 0x00, 0x01,
0x01, 0x02, 0x01, 0x01, 0x03, 0x02, 0x1a, 0x03, 0x00, 0x3d, 0x03, 0x00,
0x00, 0x00, 0x01, 0x00, 0x13, 0x40, 0x62, 0x5f, 0x65, 0x78, 0x74, 0x72,
0x61, 0x5f, 0x68, 0x69, 0x67, 0x68, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x73,
0x00, 0x00, 0x00, 0x01, 0x43, 0x00, 0x08, 0x00, 0x0b, 0x00, 0x03, 0x00,
0x00, 0x00, 0x00, 0x00, 0xc1, 0x39, 0x04, 0x40, 0x01, 0x26, 0x00, 0x06,
0x26, 0x00, 0x05, 0x26, 0x00, 0x04, 0x11, 0x02, 0x11, 0x03, 0x01, 0x05,
0x04, 0x01, 0x08, 0x01, 0x1d, 0x09, 0x00, 0x32, 0x08, 0x01, 0x01, 0x27,
0x08, 0x00, 0x07, 0x01, 0x08, 0x01, 0x53, 0x01, 0x08, 0x01, 0x01, 0x08,
0x02, 0x29, 0x08, 0x00, 0x03, 0x26, 0x00, 0x31, 0x12, 0x06, 0x1d, 0x08,
0x02, 0x33, 0x08, 0x03, 0x01, 0x07, 0x08, 0x01, 0x08, 0x07, 0x10, 0x09,
0x04, 0x62, 0x0a, 0x00, 0x34, 0x08, 0x05, 0x01, 0x30, 0x08, 0x06, 0x28,
0x08, 0x00, 0x0d, 0x01, 0x08, 0x07, 0x01, 0x09, 0x05, 0x34, 0x08, 0x07,
0x00, 0x26, 0x00, 0x02, 0x11, 0x08, 0x26, 0x00, 0x5b, 0x30, 0x08, 0x06,
0x28, 0x08, 0x00, 0x22, 0x01, 0x08, 0x02, 0x1d, 0x09, 0x00, 0x32, 0x08,
0x01, 0x01, 0x27, 0x08, 0x00, 0x07, 0x01, 0x08, 0x02, 0x53, 0x02, 0x08,
0x01, 0x01, 0x08, 0x01, 0x62, 0x09, 0x01, 0x34, 0x08, 0x08, 0x00, 0x26,
0x00, 0x32, 0x01, 0x08, 0x03, 0x1d, 0x09, 0x09, 0x32, 0x08, 0x01, 0x01,
0x28, 0x08, 0x00, 0x22, 0x01, 0x08, 0x02, 0x1d, 0x09, 0x00, 0x32, 0x08,
0x01, 0x01, 0x27, 0x08, 0x00, 0x07, 0x01, 0x08, 0x02, 0x53, 0x02, 0x08,
0x01, 0x01, 0x08, 0x01, 0x62, 0x09, 0x02, 0x34, 0x08, 0x08, 0x00, 0x26,
0x00, 0x02, 0x11, 0x08, 0x3d, 0x08, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x05,
0x41, 0x72, 0x72, 0x61, 0x79, 0x00, 0x00, 0x05, 0x69, 0x73, 0x5f, 0x61,
0x3f, 0x00, 0x00, 0x06, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x00, 0x00,
0x03, 0x6e, 0x65, 0x77, 0x00, 0x00, 0x03, 0x73, 0x65, 0x74, 0x00, 0x00,
0x17, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x5f, 0x73, 0x69, 0x6e, 0x67,
0x6c, 0x65, 0x74, 0x6f, 0x6e, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64,
0x00, 0x00, 0x0c, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x67, 0x69, 0x76,
0x65, 0x6e, 0x3f, 0x00, 0x00, 0x0d, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e,
0x63, 0x65, 0x5f, 0x65, 0x78, 0x65, 0x63, 0x00, 0x00, 0x04, 0x65, 0x61,
0x63, 0x68, 0x00, 0x00, 0x06, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x00,
0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x26, 0x39, 0x04, 0x20, 0x01, 0x26, 0x00, 0x03, 0x26,
0x00, 0x02, 0x11, 0x02, 0x01, 0x04, 0x03, 0x21, 0x05, 0x06, 0x00, 0x21,
0x06, 0x01, 0x00, 0x01, 0x07, 0x01, 0x01, 0x08, 0x02, 0x01, 0x09, 0x04,
0x34, 0x05, 0x00, 0x03, 0x3d, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x04,
0x62, 0x69, 0x6e, 0x64, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x03, 0x00,
0x05, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x39, 0x04, 0x00,
0x00, 0x21, 0x03, 0x02, 0x00, 0x62, 0x04, 0x00, 0x34, 0x03, 0x00, 0x00,
0x3d, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x04, 0x65, 0x61, 0x63, 0x68,
0x00, 0x00, 0x00, 0x00, 0x87, 0x00, 0x03, 0x00, 0x08, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x5e, 0x39, 0x04, 0x00, 0x00, 0x19, 0x03, 0x00,
0x21, 0x04, 0x01, 0x00, 0x23, 0x03, 0x27, 0x03, 0x00, 0x0f, 0x5e, 0x03,
0x00, 0x19, 0x04, 0x00, 0x21, 0x05, 0x01, 0x00, 0x01, 0x06, 0x03, 0x25,
0x04, 0x19, 0x03, 0x00, 0x21, 0x04, 0x01, 0x00, 0x23, 0x03, 0x01, 0x04,
0x01, 0x23, 0x03, 0x27, 0x03, 0x00, 0x14, 0x52, 0x03, 0x00, 0x19, 0x04,
0x00, 0x21, 0x05, 0x01, 0x00, 0x23, 0x04, 0x01, 0x05, 0x01, 0x01, 0x06,
0x03, 0x25, 0x04, 0x19, 0x03, 0x00, 0x21, 0x04, 0x01, 0x00, 0x23, 0x03,
0x01, 0x04, 0x01, 0x23, 0x03, 0x21, 0x04, 0x05, 0x01, 0x32, 0x03, 0x01,
0x01, 0x3d, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x0d, 0x40, 0x6b, 0x65,
0x79, 0x5f, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x73, 0x00, 0x00,
0x02, 0x3c, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x03, 0x00, 0x05,
0x00, 0x00, 0x01, 0x00, 0x07, 0x40, 0x62, 0x5f, 0x63, 0x6f, 0x70, 0x79,
0x00, 0x00, 0x00, 0x00, 0x2e, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x0f, 0x39, 0x00, 0x00, 0x01, 0x01, 0x02, 0x01,
0x01, 0x03, 0x02, 0x1a, 0x03, 0x00, 0x3d, 0x03, 0x00, 0x00, 0x00, 0x01,
0x00, 0x08, 0x40, 0x62, 0x5f, 0x70, 0x61, 0x73, 0x74, 0x65, 0x00, 0x00,
0x00, 0x00, 0x34, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x0f, 0x39, 0x00, 0x00, 0x01, 0x01, 0x02, 0x01, 0x01, 0x03,
0x02, 0x1a, 0x03, 0x00, 0x3d, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x0e,
0x40, 0x62, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x64, 0x65, 0x74, 0x65,
0x63, 0x74, 0x00, 0x00, 0x00, 0x00, 0x39, 0x00, 0x03, 0x00, 0x04, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x39, 0x00, 0x00, 0x01, 0x01,
0x02, 0x01, 0x01, 0x03, 0x02, 0x1a, 0x03, 0x00, 0x3d, 0x03, 0x00, 0x00,
0x00, 0x01, 0x00, 0x13, 0x40, 0x62, 0x5f, 0x65, 0x78, 0x74, 0x72, 0x61,
0x5f, 0x68, 0x69, 0x67, 0x68, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x73, 0x00,
0x00, 0x00, 0x01, 0x43, 0x00, 0x08, 0x00, 0x0b, 0x00, 0x03, 0x00, 0x00,
0x00, 0x00, 0x00, 0xc1, 0x39, 0x04, 0x40, 0x01, 0x26, 0x00, 0x06, 0x26,
0x00, 0x05, 0x26, 0x00, 0x04, 0x11, 0x02, 0x11, 0x03, 0x01, 0x05, 0x04,
0x01, 0x08, 0x01, 0x1d, 0x09, 0x00, 0x32, 0x08, 0x01, 0x01, 0x27, 0x08,
0x00, 0x07, 0x01, 0x08, 0x01, 0x53, 0x01, 0x08, 0x01, 0x01, 0x08, 0x02,
0x29, 0x08, 0x00, 0x03, 0x26, 0x00, 0x31, 0x12, 0x06, 0x1d, 0x08, 0x02,
0x33, 0x08, 0x03, 0x01, 0x07, 0x08, 0x01, 0x08, 0x07, 0x10, 0x09, 0x04,
0x62, 0x0a, 0x00, 0x34, 0x08, 0x05, 0x01, 0x30, 0x08, 0x06, 0x28, 0x08,
0x00, 0x0d, 0x01, 0x08, 0x07, 0x01, 0x09, 0x05, 0x34, 0x08, 0x07, 0x00,
0x26, 0x00, 0x02, 0x11, 0x08, 0x26, 0x00, 0x5b, 0x30, 0x08, 0x06, 0x28,
0x08, 0x00, 0x22, 0x01, 0x08, 0x02, 0x1d, 0x09, 0x00, 0x32, 0x08, 0x01,
0x01, 0x27, 0x08, 0x00, 0x07, 0x01, 0x08, 0x02, 0x53, 0x02, 0x08, 0x01,
0x01, 0x08, 0x01, 0x62, 0x09, 0x01, 0x34, 0x08, 0x08, 0x00, 0x26, 0x00,
0x32, 0x01, 0x08, 0x03, 0x1d, 0x09, 0x09, 0x32, 0x08, 0x01, 0x01, 0x28,
0x08, 0x00, 0x22, 0x01, 0x08, 0x02, 0x1d, 0x09, 0x00, 0x32, 0x08, 0x01,
0x01, 0x27, 0x08, 0x00, 0x07, 0x01, 0x08, 0x02, 0x53, 0x02, 0x08, 0x01,
0x01, 0x08, 0x01, 0x62, 0x09, 0x02, 0x34, 0x08, 0x08, 0x00, 0x26, 0x00,
0x02, 0x11, 0x08, 0x3d, 0x08, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x05, 0x41,
0x72, 0x72, 0x61, 0x79, 0x00, 0x00, 0x05, 0x69, 0x73, 0x5f, 0x61, 0x3f,
0x00, 0x00, 0x06, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x00, 0x00, 0x03,
0x6e, 0x65, 0x77, 0x00, 0x00, 0x03, 0x73, 0x65, 0x74, 0x00, 0x00, 0x17,
0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x5f, 0x73, 0x69, 0x6e, 0x67, 0x6c,
0x65, 0x74, 0x6f, 0x6e, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x00,
0x00, 0x0c, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x67, 0x69, 0x76, 0x65,
0x6e, 0x3f, 0x00, 0x00, 0x0d, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63,
0x65, 0x5f, 0x65, 0x78, 0x65, 0x63, 0x00, 0x00, 0x04, 0x65, 0x61, 0x63,
0x68, 0x00, 0x00, 0x06, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x00, 0x00,
0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x26, 0x39, 0x04, 0x20, 0x01, 0x26, 0x00, 0x03, 0x26, 0x00,
0x02, 0x11, 0x02, 0x01, 0x04, 0x03, 0x21, 0x05, 0x06, 0x00, 0x21, 0x06,
0x01, 0x00, 0x01, 0x07, 0x01, 0x01, 0x08, 0x02, 0x01, 0x09, 0x04, 0x34,
0x05, 0x00, 0x03, 0x3d, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x04, 0x62,
0x69, 0x6e, 0x64, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x03, 0x00, 0x05,
0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x39, 0x04, 0x00, 0x00,
0x21, 0x03, 0x02, 0x00, 0x62, 0x04, 0x00, 0x34, 0x03, 0x00, 0x00, 0x3d,
0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x04, 0x65, 0x61, 0x63, 0x68, 0x00,
0x00, 0x00, 0x00, 0x84, 0x00, 0x03, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x87, 0x00, 0x03, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x5e, 0x39, 0x04, 0x00, 0x00, 0x19, 0x03, 0x00, 0x21,
0x04, 0x01, 0x00, 0x23, 0x03, 0x27, 0x03, 0x00, 0x0f, 0x5e, 0x03, 0x00,
0x19, 0x04, 0x00, 0x21, 0x05, 0x01, 0x00, 0x01, 0x06, 0x03, 0x25, 0x04,
@@ -854,85 +847,100 @@ constexpr unsigned char _tmp___crib_precompiled_mrb[] = {
0x23, 0x03, 0x27, 0x03, 0x00, 0x14, 0x52, 0x03, 0x00, 0x19, 0x04, 0x00,
0x21, 0x05, 0x01, 0x00, 0x23, 0x04, 0x01, 0x05, 0x01, 0x01, 0x06, 0x03,
0x25, 0x04, 0x19, 0x03, 0x00, 0x21, 0x04, 0x01, 0x00, 0x23, 0x03, 0x01,
0x04, 0x01, 0x23, 0x03, 0x21, 0x04, 0x03, 0x01, 0x32, 0x03, 0x01, 0x01,
0x3d, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x0a, 0x40, 0x6b, 0x65, 0x79,
0x5f, 0x62, 0x69, 0x6e, 0x64, 0x73, 0x00, 0x00, 0x02, 0x3c, 0x3c, 0x00,
0x00, 0x00, 0x00, 0x38, 0x00, 0x01, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00,
0x00, 0x00, 0x00, 0x0a, 0x6b, 0x01, 0x00, 0x00, 0x6b, 0x01, 0x01, 0x01,
0x3d, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x10, 0x72, 0x65, 0x71, 0x75,
0x69, 0x72, 0x65, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x76, 0x65,
0x00, 0x00, 0x04, 0x6c, 0x6f, 0x61, 0x64, 0x00, 0x00, 0x00, 0x01, 0x07,
0x00, 0x05, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7b,
0x39, 0x04, 0x20, 0x00, 0x26, 0x00, 0x03, 0x26, 0x00, 0x02, 0x11, 0x02,
0x01, 0x05, 0x01, 0x5c, 0x06, 0x00, 0x32, 0x05, 0x00, 0x01, 0x27, 0x05,
0x00, 0x0b, 0x01, 0x05, 0x01, 0x5c, 0x06, 0x00, 0x45, 0x05, 0x01, 0x01,
0x05, 0x1d, 0x05, 0x01, 0x01, 0x06, 0x01, 0x1d, 0x07, 0x01, 0x1d, 0x08,
0x02, 0x33, 0x08, 0x03, 0x32, 0x07, 0x04, 0x01, 0x32, 0x05, 0x05, 0x02,
0x01, 0x01, 0x05, 0x15, 0x05, 0x06, 0x01, 0x06, 0x01, 0x32, 0x05, 0x07,
0x01, 0x28, 0x05, 0x00, 0x01, 0x40, 0x15, 0x05, 0x06, 0x01, 0x06, 0x01,
0x32, 0x05, 0x08, 0x01, 0x1d, 0x05, 0x01, 0x01, 0x06, 0x01, 0x32, 0x05,
0x09, 0x01, 0x01, 0x04, 0x05, 0x01, 0x06, 0x04, 0x01, 0x07, 0x02, 0x27,
0x07, 0x00, 0x03, 0x15, 0x07, 0x0a, 0x01, 0x08, 0x01, 0x2f, 0x05, 0x0b,
0x03, 0x3d, 0x05, 0x00, 0x01, 0x00, 0x00, 0x03, 0x2e, 0x72, 0x62, 0x00,
0x00, 0x0c, 0x00, 0x09, 0x65, 0x6e, 0x64, 0x5f, 0x77, 0x69, 0x74, 0x68,
0x3f, 0x00, 0x00, 0x04, 0x46, 0x69, 0x6c, 0x65, 0x00, 0x00, 0x01, 0x43,
0x00, 0x00, 0x0b, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x66, 0x69,
0x6c, 0x65, 0x00, 0x00, 0x07, 0x64, 0x69, 0x72, 0x6e, 0x61, 0x6d, 0x65,
0x00, 0x00, 0x0b, 0x65, 0x78, 0x70, 0x61, 0x6e, 0x64, 0x5f, 0x70, 0x61,
0x74, 0x68, 0x00, 0x00, 0x07, 0x24, 0x4c, 0x4f, 0x41, 0x44, 0x45, 0x44,
0x00, 0x00, 0x08, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x3f, 0x00,
0x00, 0x02, 0x3c, 0x3c, 0x00, 0x00, 0x04, 0x72, 0x65, 0x61, 0x64, 0x00,
0x00, 0x09, 0x24, 0x42, 0x49, 0x4e, 0x44, 0x5f, 0x54, 0x4f, 0x50, 0x00,
0x00, 0x04, 0x65, 0x76, 0x61, 0x6c, 0x00, 0x00, 0x00, 0x00, 0xd3, 0x00,
0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x39,
0x04, 0x01, 0x23, 0x03, 0x21, 0x04, 0x05, 0x01, 0x32, 0x03, 0x01, 0x01,
0x3d, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x0d, 0x40, 0x6b, 0x65, 0x79,
0x5f, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x73, 0x00, 0x00, 0x02,
0x3c, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x03, 0x00, 0x05, 0x00,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x39, 0x04, 0x00, 0x00, 0x21,
0x03, 0x02, 0x00, 0x62, 0x04, 0x00, 0x34, 0x03, 0x00, 0x00, 0x3d, 0x03,
0x00, 0x00, 0x00, 0x01, 0x00, 0x04, 0x65, 0x61, 0x63, 0x68, 0x00, 0x00,
0x00, 0x00, 0x84, 0x00, 0x03, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x5e, 0x39, 0x04, 0x00, 0x00, 0x19, 0x03, 0x00, 0x21, 0x04,
0x01, 0x00, 0x23, 0x03, 0x27, 0x03, 0x00, 0x0f, 0x5e, 0x03, 0x00, 0x19,
0x04, 0x00, 0x21, 0x05, 0x01, 0x00, 0x01, 0x06, 0x03, 0x25, 0x04, 0x19,
0x03, 0x00, 0x21, 0x04, 0x01, 0x00, 0x23, 0x03, 0x01, 0x04, 0x01, 0x23,
0x03, 0x27, 0x03, 0x00, 0x14, 0x52, 0x03, 0x00, 0x19, 0x04, 0x00, 0x21,
0x05, 0x01, 0x00, 0x23, 0x04, 0x01, 0x05, 0x01, 0x01, 0x06, 0x03, 0x25,
0x04, 0x19, 0x03, 0x00, 0x21, 0x04, 0x01, 0x00, 0x23, 0x03, 0x01, 0x04,
0x01, 0x23, 0x03, 0x21, 0x04, 0x03, 0x01, 0x32, 0x03, 0x01, 0x01, 0x3d,
0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x0a, 0x40, 0x6b, 0x65, 0x79, 0x5f,
0x62, 0x69, 0x6e, 0x64, 0x73, 0x00, 0x00, 0x02, 0x3c, 0x3c, 0x00, 0x00,
0x00, 0x00, 0x38, 0x00, 0x01, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00,
0x00, 0x00, 0x0a, 0x6b, 0x01, 0x00, 0x00, 0x6b, 0x01, 0x01, 0x01, 0x3d,
0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x10, 0x72, 0x65, 0x71, 0x75, 0x69,
0x72, 0x65, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x76, 0x65, 0x00,
0x00, 0x04, 0x6c, 0x6f, 0x61, 0x64, 0x00, 0x00, 0x00, 0x01, 0x07, 0x00,
0x05, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7b, 0x39,
0x04, 0x20, 0x00, 0x26, 0x00, 0x03, 0x26, 0x00, 0x02, 0x11, 0x02, 0x01,
0x04, 0x01, 0x5c, 0x05, 0x00, 0x32, 0x04, 0x00, 0x01, 0x27, 0x04, 0x00,
0x0b, 0x01, 0x04, 0x01, 0x5c, 0x05, 0x00, 0x45, 0x04, 0x01, 0x01, 0x04,
0x1d, 0x04, 0x01, 0x01, 0x05, 0x01, 0x1d, 0x06, 0x01, 0x1d, 0x07, 0x02,
0x33, 0x07, 0x03, 0x32, 0x06, 0x04, 0x01, 0x32, 0x04, 0x05, 0x02, 0x01,
0x01, 0x04, 0x15, 0x04, 0x06, 0x01, 0x05, 0x01, 0x32, 0x04, 0x07, 0x01,
0x01, 0x05, 0x01, 0x01, 0x06, 0x02, 0x2f, 0x04, 0x08, 0x02, 0x3d, 0x04,
0x00, 0x01, 0x00, 0x00, 0x03, 0x2e, 0x72, 0x62, 0x00, 0x00, 0x09, 0x00,
0x09, 0x65, 0x6e, 0x64, 0x5f, 0x77, 0x69, 0x74, 0x68, 0x3f, 0x00, 0x00,
0x04, 0x46, 0x69, 0x6c, 0x65, 0x00, 0x00, 0x01, 0x43, 0x00, 0x00, 0x0b,
0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x00,
0x00, 0x07, 0x64, 0x69, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x00, 0x00, 0x0b,
0x65, 0x78, 0x70, 0x61, 0x6e, 0x64, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x00,
0x00, 0x07, 0x24, 0x4c, 0x4f, 0x41, 0x44, 0x45, 0x44, 0x00, 0x00, 0x06,
0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x00, 0x00, 0x10, 0x72, 0x65, 0x71,
0x75, 0x69, 0x72, 0x65, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x76,
0x65, 0x00, 0x4c, 0x56, 0x41, 0x52, 0x00, 0x00, 0x01, 0x78, 0x00, 0x00,
0x00, 0x1d, 0x00, 0x03, 0x63, 0x6d, 0x64, 0x00, 0x04, 0x74, 0x65, 0x78,
0x74, 0x00, 0x01, 0x66, 0x00, 0x07, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65,
0x64, 0x00, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x00, 0x0a, 0x6d, 0x6f, 0x64,
0x65, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x00, 0x0b, 0x6d, 0x6f, 0x64,
0x65, 0x5f, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x00, 0x09, 0x6c, 0x61,
0x6e, 0x67, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x00, 0x08, 0x66, 0x69, 0x6c,
0x65, 0x6e, 0x61, 0x6d, 0x65, 0x00, 0x08, 0x73, 0x74, 0x61, 0x72, 0x74,
0x69, 0x6e, 0x67, 0x00, 0x0a, 0x68, 0x69, 0x67, 0x68, 0x6c, 0x69, 0x67,
0x68, 0x74, 0x73, 0x00, 0x04, 0x74, 0x79, 0x70, 0x65, 0x00, 0x0a, 0x66,
0x69, 0x72, 0x73, 0x74, 0x5f, 0x6c, 0x69, 0x6e, 0x65, 0x00, 0x07, 0x73,
0x68, 0x65, 0x62, 0x61, 0x6e, 0x67, 0x00, 0x08, 0x6d, 0x69, 0x6d, 0x65,
0x74, 0x79, 0x70, 0x65, 0x00, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x00,
0x05, 0x6d, 0x6f, 0x64, 0x65, 0x73, 0x00, 0x04, 0x6b, 0x65, 0x79, 0x73,
0x00, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x03, 0x61, 0x70,
0x70, 0x00, 0x03, 0x64, 0x73, 0x6c, 0x00, 0x01, 0x6b, 0x00, 0x03, 0x61,
0x63, 0x74, 0x00, 0x03, 0x62, 0x6c, 0x6b, 0x00, 0x04, 0x6d, 0x6f, 0x64,
0x65, 0x00, 0x03, 0x6b, 0x65, 0x79, 0x00, 0x04, 0x70, 0x61, 0x74, 0x68,
0x00, 0x04, 0x62, 0x69, 0x6e, 0x64, 0x00, 0x04, 0x63, 0x6f, 0x64, 0x65,
0x00, 0x00, 0xff, 0xff, 0x00, 0x01, 0xff, 0xff, 0x00, 0x02, 0xff, 0xff,
0x00, 0x02, 0xff, 0xff, 0x00, 0x02, 0xff, 0xff, 0x00, 0x02, 0xff, 0xff,
0x00, 0x02, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0xff, 0xff, 0x00, 0x03,
0x00, 0x04, 0xff, 0xff, 0x00, 0x05, 0x00, 0x06, 0x00, 0x07, 0x00, 0x08,
0x00, 0x09, 0x00, 0x0a, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0x00, 0x08,
0xff, 0xff, 0x00, 0x0b, 0x00, 0x0c, 0x00, 0x0d, 0x00, 0x0e, 0xff, 0xff,
0x00, 0x0f, 0xff, 0xff, 0x00, 0x0f, 0xff, 0xff, 0x00, 0x0f, 0xff, 0xff,
0x00, 0x0f, 0xff, 0xff, 0x00, 0x0f, 0xff, 0xff, 0x00, 0x0f, 0xff, 0xff,
0x00, 0x0f, 0x00, 0x10, 0x00, 0x11, 0x00, 0x12, 0xff, 0xff, 0x00, 0x0f,
0x00, 0x13, 0x00, 0x14, 0x00, 0x15, 0x00, 0x16, 0xff, 0xff, 0x00, 0x17,
0x00, 0x18, 0xff, 0xff, 0x00, 0x19, 0xff, 0xff, 0x00, 0x18, 0xff, 0xff,
0x00, 0x19, 0xff, 0xff, 0x00, 0x1a, 0x00, 0x1b, 0xff, 0xff, 0x00, 0x1c,
0x00, 0x1a, 0x00, 0x1b, 0xff, 0xff, 0x45, 0x4e, 0x44, 0x00, 0x00, 0x00,
0x00, 0x08
0x05, 0x01, 0x5c, 0x06, 0x00, 0x32, 0x05, 0x00, 0x01, 0x27, 0x05, 0x00,
0x0b, 0x01, 0x05, 0x01, 0x5c, 0x06, 0x00, 0x45, 0x05, 0x01, 0x01, 0x05,
0x1d, 0x05, 0x01, 0x01, 0x06, 0x01, 0x1d, 0x07, 0x01, 0x1d, 0x08, 0x02,
0x33, 0x08, 0x03, 0x32, 0x07, 0x04, 0x01, 0x32, 0x05, 0x05, 0x02, 0x01,
0x01, 0x05, 0x15, 0x05, 0x06, 0x01, 0x06, 0x01, 0x32, 0x05, 0x07, 0x01,
0x28, 0x05, 0x00, 0x01, 0x40, 0x15, 0x05, 0x06, 0x01, 0x06, 0x01, 0x32,
0x05, 0x08, 0x01, 0x1d, 0x05, 0x01, 0x01, 0x06, 0x01, 0x32, 0x05, 0x09,
0x01, 0x01, 0x04, 0x05, 0x01, 0x06, 0x04, 0x01, 0x07, 0x02, 0x27, 0x07,
0x00, 0x03, 0x15, 0x07, 0x0a, 0x01, 0x08, 0x01, 0x2f, 0x05, 0x0b, 0x03,
0x3d, 0x05, 0x00, 0x01, 0x00, 0x00, 0x03, 0x2e, 0x72, 0x62, 0x00, 0x00,
0x0c, 0x00, 0x09, 0x65, 0x6e, 0x64, 0x5f, 0x77, 0x69, 0x74, 0x68, 0x3f,
0x00, 0x00, 0x04, 0x46, 0x69, 0x6c, 0x65, 0x00, 0x00, 0x01, 0x43, 0x00,
0x00, 0x0b, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x66, 0x69, 0x6c,
0x65, 0x00, 0x00, 0x07, 0x64, 0x69, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x00,
0x00, 0x0b, 0x65, 0x78, 0x70, 0x61, 0x6e, 0x64, 0x5f, 0x70, 0x61, 0x74,
0x68, 0x00, 0x00, 0x07, 0x24, 0x4c, 0x4f, 0x41, 0x44, 0x45, 0x44, 0x00,
0x00, 0x08, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x3f, 0x00, 0x00,
0x02, 0x3c, 0x3c, 0x00, 0x00, 0x04, 0x72, 0x65, 0x61, 0x64, 0x00, 0x00,
0x09, 0x24, 0x42, 0x49, 0x4e, 0x44, 0x5f, 0x54, 0x4f, 0x50, 0x00, 0x00,
0x04, 0x65, 0x76, 0x61, 0x6c, 0x00, 0x00, 0x00, 0x00, 0xd3, 0x00, 0x04,
0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x39, 0x04,
0x20, 0x00, 0x26, 0x00, 0x03, 0x26, 0x00, 0x02, 0x11, 0x02, 0x01, 0x04,
0x01, 0x5c, 0x05, 0x00, 0x32, 0x04, 0x00, 0x01, 0x27, 0x04, 0x00, 0x0b,
0x01, 0x04, 0x01, 0x5c, 0x05, 0x00, 0x45, 0x04, 0x01, 0x01, 0x04, 0x1d,
0x04, 0x01, 0x01, 0x05, 0x01, 0x1d, 0x06, 0x01, 0x1d, 0x07, 0x02, 0x33,
0x07, 0x03, 0x32, 0x06, 0x04, 0x01, 0x32, 0x04, 0x05, 0x02, 0x01, 0x01,
0x04, 0x15, 0x04, 0x06, 0x01, 0x05, 0x01, 0x32, 0x04, 0x07, 0x01, 0x01,
0x05, 0x01, 0x01, 0x06, 0x02, 0x2f, 0x04, 0x08, 0x02, 0x3d, 0x04, 0x00,
0x01, 0x00, 0x00, 0x03, 0x2e, 0x72, 0x62, 0x00, 0x00, 0x09, 0x00, 0x09,
0x65, 0x6e, 0x64, 0x5f, 0x77, 0x69, 0x74, 0x68, 0x3f, 0x00, 0x00, 0x04,
0x46, 0x69, 0x6c, 0x65, 0x00, 0x00, 0x01, 0x43, 0x00, 0x00, 0x0b, 0x63,
0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x00, 0x00,
0x07, 0x64, 0x69, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x00, 0x00, 0x0b, 0x65,
0x78, 0x70, 0x61, 0x6e, 0x64, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x00, 0x00,
0x07, 0x24, 0x4c, 0x4f, 0x41, 0x44, 0x45, 0x44, 0x00, 0x00, 0x06, 0x64,
0x65, 0x6c, 0x65, 0x74, 0x65, 0x00, 0x00, 0x10, 0x72, 0x65, 0x71, 0x75,
0x69, 0x72, 0x65, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x76, 0x65,
0x00, 0x4c, 0x56, 0x41, 0x52, 0x00, 0x00, 0x01, 0x78, 0x00, 0x00, 0x00,
0x1d, 0x00, 0x03, 0x63, 0x6d, 0x64, 0x00, 0x04, 0x74, 0x65, 0x78, 0x74,
0x00, 0x01, 0x66, 0x00, 0x07, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x64,
0x00, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x00, 0x0a, 0x6d, 0x6f, 0x64, 0x65,
0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x00, 0x0b, 0x6d, 0x6f, 0x64, 0x65,
0x5f, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x00, 0x09, 0x6c, 0x61, 0x6e,
0x67, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x00, 0x08, 0x66, 0x69, 0x6c, 0x65,
0x6e, 0x61, 0x6d, 0x65, 0x00, 0x08, 0x73, 0x74, 0x61, 0x72, 0x74, 0x69,
0x6e, 0x67, 0x00, 0x0a, 0x68, 0x69, 0x67, 0x68, 0x6c, 0x69, 0x67, 0x68,
0x74, 0x73, 0x00, 0x04, 0x74, 0x79, 0x70, 0x65, 0x00, 0x0a, 0x66, 0x69,
0x72, 0x73, 0x74, 0x5f, 0x6c, 0x69, 0x6e, 0x65, 0x00, 0x07, 0x73, 0x68,
0x65, 0x62, 0x61, 0x6e, 0x67, 0x00, 0x08, 0x6d, 0x69, 0x6d, 0x65, 0x74,
0x79, 0x70, 0x65, 0x00, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x00, 0x05,
0x6d, 0x6f, 0x64, 0x65, 0x73, 0x00, 0x04, 0x6b, 0x65, 0x79, 0x73, 0x00,
0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x03, 0x61, 0x70, 0x70,
0x00, 0x03, 0x64, 0x73, 0x6c, 0x00, 0x01, 0x6b, 0x00, 0x03, 0x61, 0x63,
0x74, 0x00, 0x03, 0x62, 0x6c, 0x6b, 0x00, 0x04, 0x6d, 0x6f, 0x64, 0x65,
0x00, 0x03, 0x6b, 0x65, 0x79, 0x00, 0x04, 0x70, 0x61, 0x74, 0x68, 0x00,
0x04, 0x62, 0x69, 0x6e, 0x64, 0x00, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x00,
0x00, 0xff, 0xff, 0x00, 0x01, 0xff, 0xff, 0x00, 0x02, 0xff, 0xff, 0x00,
0x02, 0xff, 0xff, 0x00, 0x02, 0xff, 0xff, 0x00, 0x02, 0xff, 0xff, 0x00,
0x02, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0xff, 0xff, 0x00, 0x03, 0x00,
0x04, 0xff, 0xff, 0x00, 0x05, 0x00, 0x06, 0x00, 0x07, 0x00, 0x08, 0x00,
0x09, 0x00, 0x0a, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0x00, 0x08, 0xff,
0xff, 0x00, 0x0b, 0x00, 0x0c, 0x00, 0x0d, 0x00, 0x0e, 0xff, 0xff, 0x00,
0x0f, 0xff, 0xff, 0x00, 0x0f, 0xff, 0xff, 0x00, 0x0f, 0xff, 0xff, 0x00,
0x0f, 0xff, 0xff, 0x00, 0x0f, 0xff, 0xff, 0x00, 0x0f, 0xff, 0xff, 0x00,
0x0f, 0x00, 0x10, 0x00, 0x11, 0x00, 0x12, 0xff, 0xff, 0x00, 0x0f, 0x00,
0x13, 0x00, 0x14, 0x00, 0x15, 0x00, 0x16, 0xff, 0xff, 0x00, 0x17, 0x00,
0x18, 0xff, 0xff, 0x00, 0x19, 0xff, 0xff, 0x00, 0x18, 0xff, 0xff, 0x00,
0x19, 0xff, 0xff, 0x00, 0x1a, 0x00, 0x1b, 0xff, 0xff, 0x00, 0x1c, 0x00,
0x1a, 0x00, 0x1b, 0xff, 0xff, 0x45, 0x4e, 0x44, 0x00, 0x00, 0x00, 0x00,
0x08
};
constexpr unsigned int _tmp___crib_precompiled_mrb_len = 11198;
constexpr unsigned int _tmp___crib_precompiled_mrb_len = 11293;

View File

@@ -1,10 +0,0 @@
#ifndef TILING_DECL_H
#define TILING_DECL_H
#include "utils/utils.h"
struct Window {};
struct TileBlock {};
#endif

View File

@@ -12,8 +12,9 @@ struct Bar {
uint32_t cursor = 0;
void init(Coord screen) { this->screen = screen; }
void render();
void handle(KeyEvent event);
void render(std::vector<ScreenCell> &buffer);
void handle_event(KeyEvent event);
void handle_command(std::string &command);
void log(std::string message);
};

View File

@@ -1,21 +1,21 @@
#ifndef UI_COMPLETIONBOX_H
#define UI_COMPLETIONBOX_H
#include "io/sysio.h"
#include "pch.h"
#include "utils/utils.h"
struct CompletionBox {
std::shared_mutex mtx;
struct CompletionSession *session;
bool hidden = true;
std::vector<ScreenCell> cells;
Coord size;
Coord position;
CompletionBox(CompletionSession *s) : session(s) {}
void render_update();
void render(Coord pos);
};
#endif
// #ifndef UI_COMPLETIONBOX_H
// #define UI_COMPLETIONBOX_H
//
// #include "io/sysio.h"
// #include "pch.h"
// #include "utils/utils.h"
//
// struct CompletionBox {
// std::shared_mutex mtx;
// struct CompletionSession *session;
// bool hidden = true;
// std::vector<ScreenCell> cells;
// Coord size;
// Coord position;
//
// CompletionBox(CompletionSession *s) : session(s) {}
// void render_update();
// void render(Coord pos);
// };
//
// #endif

View File

@@ -1,22 +0,0 @@
#ifndef UI_HOVER_H
#define UI_HOVER_H
#include "editor/decl.h"
#include "io/sysio.h"
#include "pch.h"
#include "utils/utils.h"
struct HoverBox {
std::string text;
std::atomic<bool> is_markup;
uint32_t scroll_;
std::vector<ScreenCell> cells;
Coord size;
void clear();
void scroll(int32_t number);
void render_first(bool scroll = false);
void render(Coord pos);
};
#endif

120
include/windows/decl.h Normal file
View File

@@ -0,0 +1,120 @@
#ifndef TILING_DECL_H
#define TILING_DECL_H
#include "io/sysio.h"
#include "utils/utils.h"
struct Tile {
bool hidden = false;
uint32_t weight = 100;
virtual void render(std::vector<ScreenCell> &buffer, Coord size,
Coord pos) = 0;
virtual void handle_click(KeyEvent event, Coord size) = 0;
virtual ~Tile() = default;
};
struct Window : Tile {
virtual ~Window() = default;
virtual void handle_event(KeyEvent){};
virtual void handle_command(std::string &) {};
virtual void work() {};
virtual std::array<std::string, 5> bar_info() { return {}; };
};
struct TileBlock : Tile {
bool vertical;
std::vector<std::unique_ptr<Tile>> tiles;
void render(std::vector<ScreenCell> &buffer, Coord size, Coord pos) override {
uint32_t total_weight = 0;
for (auto &t : tiles) {
if (!t->hidden)
total_weight += t->weight;
}
if (total_weight == 0)
return;
for (auto &t : tiles) {
if (t->hidden)
continue;
uint32_t proportion =
t->weight * (vertical ? size.row : size.col) / total_weight;
Coord tile_size = vertical ? Coord{.row = proportion, .col = size.col}
: Coord{.row = size.row, .col = proportion};
t->render(buffer, tile_size, pos);
if (vertical)
pos.row += tile_size.row;
else
pos.col += tile_size.col;
}
}
void handle_click(KeyEvent event, Coord size) override {
uint32_t total_weight = 0;
for (auto &t : tiles)
if (!t->hidden)
total_weight += t->weight;
if (total_weight == 0)
return;
uint32_t i = 0;
for (auto &t : tiles) {
if (t->hidden)
continue;
uint32_t proportion =
t->weight * (vertical ? size.row : size.col) / total_weight;
Coord tile_size = vertical ? Coord{.row = proportion, .col = size.col}
: Coord{.row = size.row, .col = proportion};
if (vertical) {
if (event.mouse_y < i + proportion) {
event.mouse_y -= i;
t->handle_click(event, tile_size);
return;
}
} else {
if (event.mouse_x < i + proportion) {
event.mouse_x -= i;
t->handle_click(event, tile_size);
return;
}
}
i += proportion;
}
}
};
struct TileRoot {
std::unique_ptr<Tile> tile;
Coord pos;
Coord size;
void render(std::vector<ScreenCell> &buffer) {
if (this->tile->hidden)
return;
this->tile->render(buffer, size, pos);
}
void handle_click(KeyEvent event) {
event.mouse_x -= this->pos.col;
event.mouse_y -= this->pos.row;
this->tile->handle_click(event, size);
}
bool inside(uint32_t x, uint32_t y) {
return x >= pos.col && x < pos.col + size.col && y >= pos.row &&
y < pos.row + size.row;
}
};
extern TileRoot root_tile;
extern std::vector<std::unique_ptr<TileRoot>> popups;
extern Window *focused_window;
inline void close_popup(TileRoot *handle) {
auto it = std::find_if(popups.begin(), popups.end(),
[handle](const auto &p) { return p.get() == handle; });
if (it != popups.end())
popups.erase(it);
}
void render();
void handle_click(KeyEvent event);
#endif

View File

@@ -25,6 +25,7 @@ colorize() {
handle_error() {
log ERROR "An error occurred on line $1"
}
trap 'handle_error $LINENO' ERR
# Multiline string test
read -r -d '' MULTI <<'CPP'

View File

@@ -1,25 +1,24 @@
#include "editor/editor.h"
void ensure_cursor(Editor *editor) {
std::shared_lock knot_lock(editor->knot_mtx);
if (editor->cursor < editor->scroll) {
editor->cursor.row = editor->scroll.row;
editor->cursor.col = editor->scroll.col;
editor->cursor_preffered = UINT32_MAX;
void Editor::ensure_cursor() {
if (this->cursor < this->scroll) {
this->cursor.row = this->scroll.row;
this->cursor.col = this->scroll.col;
this->cursor_preffered = UINT32_MAX;
return;
}
uint32_t numlen =
EXTRA_META + static_cast<int>(std::log10(editor->root->line_count + 1));
uint32_t render_width = editor->size.col - numlen;
EXTRA_META + static_cast<int>(std::log10(this->root->line_count + 1));
uint32_t render_width = this->size.col - numlen;
uint32_t visual_rows = 0;
uint32_t line_index = editor->scroll.row;
uint32_t line_index = this->scroll.row;
bool first_visual_line = true;
LineIterator *it = begin_l_iter(editor->root, line_index);
LineIterator *it = begin_l_iter(this->root, line_index);
if (!it)
return;
Coord last_visible = editor->scroll;
Coord last_visible = this->scroll;
while (true) {
if (visual_rows >= editor->size.row)
if (visual_rows >= this->size.row)
break;
uint32_t line_len;
char *line = next_line(it, &line_len);
@@ -27,13 +26,13 @@ void ensure_cursor(Editor *editor) {
break;
if (line_len > 0 && line[line_len - 1] == '\n')
line_len--;
uint32_t offset = first_visual_line ? editor->scroll.col : 0;
uint32_t offset = first_visual_line ? this->scroll.col : 0;
first_visual_line = false;
while (offset < line_len || (line_len == 0 && offset == 0)) {
Coord current = {line_index, offset};
last_visible = current;
visual_rows++;
if (visual_rows >= editor->size.row)
if (visual_rows >= this->size.row)
break;
uint32_t col = 0;
uint32_t advance = 0;
@@ -48,9 +47,9 @@ void ensure_cursor(Editor *editor) {
left -= g;
col += w;
}
if (line_index == editor->cursor.row) {
if (editor->cursor.col >= offset &&
editor->cursor.col <= offset + advance) {
if (line_index == this->cursor.row) {
if (this->cursor.col >= offset &&
this->cursor.col <= offset + advance) {
free(it->buffer);
free(it);
return;
@@ -64,20 +63,19 @@ void ensure_cursor(Editor *editor) {
}
line_index++;
}
editor->cursor.row = last_visible.row;
editor->cursor.col = last_visible.col;
editor->cursor_preffered = UINT32_MAX;
this->cursor.row = last_visible.row;
this->cursor.col = last_visible.col;
this->cursor_preffered = UINT32_MAX;
free(it->buffer);
free(it);
}
void ensure_scroll(Editor *editor) {
std::shared_lock knot_lock(editor->knot_mtx);
void Editor::ensure_scroll() {
uint32_t numlen =
EXTRA_META + static_cast<int>(std::log10(editor->root->line_count + 1));
uint32_t render_width = editor->size.col - numlen;
if (editor->cursor < editor->scroll) {
LineIterator *it = begin_l_iter(editor->root, editor->cursor.row);
EXTRA_META + static_cast<int>(std::log10(this->root->line_count + 1));
uint32_t render_width = this->size.col - numlen;
if (this->cursor < this->scroll) {
LineIterator *it = begin_l_iter(this->root, this->cursor.row);
if (!it)
return;
uint32_t len;
@@ -98,9 +96,9 @@ void ensure_scroll(Editor *editor) {
int width = display_width(line + offset, inc);
if (cols + width > render_width) {
cols = 0;
if (editor->cursor.col > old_offset && editor->cursor.col <= offset) {
editor->scroll.row = editor->cursor.row;
editor->scroll.col = old_offset;
if (this->cursor.col > old_offset && this->cursor.col <= offset) {
this->scroll.row = this->cursor.row;
this->scroll.col = old_offset;
free(it->buffer);
free(it);
return;
@@ -112,14 +110,14 @@ void ensure_scroll(Editor *editor) {
}
free(it->buffer);
free(it);
editor->scroll.row = editor->cursor.row;
editor->scroll.col = (editor->cursor.col == 0) ? 0 : old_offset;
} else if (editor->cursor.row - editor->scroll.row < editor->size.row * 2) {
uint32_t line_index = editor->scroll.row;
LineIterator *it = begin_l_iter(editor->root, line_index);
this->scroll.row = this->cursor.row;
this->scroll.col = (this->cursor.col == 0) ? 0 : old_offset;
} else if (this->cursor.row - this->scroll.row < this->size.row * 2) {
uint32_t line_index = this->scroll.row;
LineIterator *it = begin_l_iter(this->root, line_index);
if (!it)
return;
uint32_t max_visual_lines = editor->size.row;
uint32_t max_visual_lines = this->size.row;
Coord *scroll_queue = (Coord *)malloc(sizeof(Coord) * max_visual_lines);
uint32_t q_head = 0;
uint32_t q_size = 0;
@@ -133,7 +131,7 @@ void ensure_scroll(Editor *editor) {
line_len--;
uint32_t current_byte_offset = 0;
if (first_visual_line) {
current_byte_offset += editor->scroll.col;
current_byte_offset += this->scroll.col;
first_visual_line = false;
}
while (current_byte_offset < line_len ||
@@ -160,16 +158,16 @@ void ensure_scroll(Editor *editor) {
line_left -= cluster_len;
col += width;
}
if (line_index == editor->cursor.row) {
if (line_index == this->cursor.row) {
bool cursor_found = false;
if (editor->cursor.col >= current_byte_offset &&
editor->cursor.col < current_byte_offset + local_render_offset)
if (this->cursor.col >= current_byte_offset &&
this->cursor.col < current_byte_offset + local_render_offset)
cursor_found = true;
else if (editor->cursor.col == line_len &&
else if (this->cursor.col == line_len &&
current_byte_offset + local_render_offset == line_len)
cursor_found = true;
if (cursor_found) {
editor->scroll = scroll_queue[q_head];
this->scroll = scroll_queue[q_head];
free(scroll_queue);
free(it->buffer);
free(it);
@@ -186,10 +184,10 @@ void ensure_scroll(Editor *editor) {
free(it->buffer);
free(it);
} else {
editor->scroll.row = (editor->cursor.row > editor->size.row * 1.5)
? editor->cursor.row - editor->size.row * 1.5
this->scroll.row = (this->cursor.row > this->size.row * 1.5)
? this->cursor.row - this->size.row * 1.5
: 0;
editor->scroll.col = 0;
ensure_scroll(editor);
this->scroll.col = 0;
this->ensure_scroll();
}
}

View File

@@ -32,12 +32,9 @@ uint32_t scan_right(const char *line, uint32_t len, uint32_t off) {
return i;
}
void word_boundaries_exclusive(Editor *editor, Coord coord, uint32_t *prev_col,
void Editor::word_boundaries_exclusive(Coord coord, uint32_t *prev_col,
uint32_t *next_col) {
if (!editor)
return;
std::shared_lock lock(editor->knot_mtx);
LineIterator *it = begin_l_iter(editor->root, coord.row);
LineIterator *it = begin_l_iter(this->root, coord.row);
if (!it)
return;
uint32_t line_len;
@@ -85,13 +82,10 @@ uint32_t word_jump_left(const char *line, size_t len, uint32_t col) {
return static_cast<uint32_t>(last);
}
void word_boundaries(Editor *editor, Coord coord, uint32_t *prev_col,
void Editor::word_boundaries(Coord coord, uint32_t *prev_col,
uint32_t *next_col, uint32_t *prev_clusters,
uint32_t *next_clusters) {
if (!editor)
return;
std::shared_lock lock(editor->knot_mtx);
LineIterator *it = begin_l_iter(editor->root, coord.row);
LineIterator *it = begin_l_iter(this->root, coord.row);
if (!it)
return;
uint32_t line_len;

View File

@@ -1,23 +1,22 @@
#include "editor/editor.h"
#include "main.h"
Coord editor_hit_test(Editor *editor, uint32_t x, uint32_t y) {
Coord Editor::click_coord(uint32_t x, uint32_t y) {
if (mode == INSERT)
x++;
uint32_t numlen =
EXTRA_META + static_cast<int>(std::log10(editor->root->line_count + 1));
uint32_t render_width = editor->size.col - numlen;
EXTRA_META + static_cast<int>(std::log10(this->root->line_count + 1));
uint32_t render_width = this->size.col - numlen;
x = MAX(x, numlen) - numlen + 1;
uint32_t target_visual_row = y;
uint32_t visual_row = 0;
uint32_t line_index = editor->scroll.row;
uint32_t last_line_index = editor->scroll.row;
uint32_t last_col = editor->scroll.col;
uint32_t line_index = this->scroll.row;
uint32_t last_line_index = this->scroll.row;
uint32_t last_col = this->scroll.col;
bool first_visual_line = true;
std::shared_lock knot_lock(editor->knot_mtx);
LineIterator *it = begin_l_iter(editor->root, line_index);
LineIterator *it = begin_l_iter(this->root, line_index);
if (!it)
return editor->scroll;
return this->scroll;
while (visual_row <= target_visual_row) {
uint32_t line_len;
char *line = next_line(it, &line_len);
@@ -27,7 +26,7 @@ Coord editor_hit_test(Editor *editor, uint32_t x, uint32_t y) {
line_len--;
last_line_index = line_index;
last_col = line_len;
uint32_t offset = first_visual_line ? editor->scroll.col : 0;
uint32_t offset = first_visual_line ? this->scroll.col : 0;
first_visual_line = false;
while (offset < line_len || (line_len == 0 && offset == 0)) {
uint32_t col = 0;

13
src/editor/commands.cc Normal file
View File

@@ -0,0 +1,13 @@
#include "editor/editor.h"
#include "main.h"
void Editor::handle_command(std::string &command) {
if (command == "w") {
this->save();
}
if (command == "wq") {
this->save();
command = "q";
ui::bar.handle_command(command);
}
}

View File

@@ -1,459 +1,458 @@
#include "editor/decl.h"
#include "editor/editor.h"
#include "io/knot.h"
#include "io/sysio.h"
#include "lsp/lsp.h"
#include "main.h"
#include "utils/utils.h"
inline static std::string completion_prefix(Editor *editor) {
Coord hook = editor->completion.hook;
Coord cur = editor->cursor;
if (hook.row != cur.row || cur.col < hook.col)
return "";
LineIterator *it = begin_l_iter(editor->root, hook.row);
uint32_t line_len;
char *line = next_line(it, &line_len);
if (!line) {
free(it->buffer);
free(it);
return "";
}
std::string prefix(line + hook.col, cur.col - hook.col);
free(it->buffer);
free(it);
return prefix;
}
inline static void completion_adjust_scroll(CompletionSession &s) {
if (s.visible.empty())
return;
int vi = -1;
for (size_t i = 0; i < s.visible.size(); i++)
if (s.visible[i] == s.select) {
vi = (int)i;
break;
}
if (vi < 0)
return;
if ((uint32_t)vi < s.scroll)
s.scroll = vi;
else if ((uint32_t)vi >= s.scroll + 8)
s.scroll = vi - 7;
}
void completion_filter(Editor *editor) {
auto &session = editor->completion;
std::string prefix = completion_prefix(editor);
session.visible.clear();
for (size_t i = 0; i < session.items.size(); ++i) {
const auto &item = session.items[i];
const std::string &key = item.filter;
if (key.size() >= prefix.size() &&
key.compare(0, prefix.size(), prefix) == 0)
session.visible.push_back(i);
}
if (session.visible.empty()) {
session.box.hidden = true;
return;
}
if (std::find(session.visible.begin(), session.visible.end(),
session.select) == session.visible.end())
session.select = session.visible[0];
session.box.hidden = false;
session.scroll = 0;
completion_adjust_scroll(session);
session.box.render_update();
}
void completion_request(Editor *editor) {
Coord hook = editor->cursor;
word_boundaries(editor, editor->cursor, &hook.col, nullptr, nullptr, nullptr);
editor->completion.hook = hook;
editor->completion.complete = false;
editor->completion.active = false;
editor->completion.items.clear();
editor->completion.visible.clear();
editor->completion.select = 0;
editor->completion.version = editor->lsp_version;
LSPPending *pending = new LSPPending();
pending->editor = editor;
pending->callback = [](Editor *editor, const json &message) {
auto &session = editor->completion;
std::unique_lock lock(session.mtx);
std::vector<json> items_json;
std::vector<char> end_chars_def;
int insert_text_format = 1;
int insert_text_mode = 1;
if (message.contains("result")) {
auto &result = message["result"];
if (result.is_array()) {
items_json = result.get<std::vector<json>>();
session.complete = true;
if (items_json.empty())
return;
editor->completion.active = true;
} else if (result.is_object() && result.contains("items")) {
auto &list = result;
items_json = list["items"].get<std::vector<json>>();
if (items_json.empty())
return;
editor->completion.active = true;
session.complete = !list.value("isIncomplete", false);
if (list.contains("itemDefaults") && list["itemDefaults"].is_object()) {
auto &defs = list["itemDefaults"];
if (defs.contains("insertTextFormat") &&
defs["insertTextFormat"].is_number())
insert_text_format = defs["insertTextFormat"].get<int>();
if (defs.contains("insertTextMode") &&
defs["insertTextMode"].is_number())
insert_text_mode = defs["insertTextMode"].get<int>();
if (defs.contains("textEdit"))
if (defs["textEdit"].is_array())
for (auto &c : defs["textEdit"]) {
if (!c.is_string())
continue;
std::string str = c.get<std::string>();
if (str.size() != 1)
continue;
end_chars_def.push_back(str[0]);
}
}
}
}
session.items.reserve(items_json.size() + 1);
session.visible.reserve(items_json.size() + 1);
for (auto &item_json : items_json) {
CompletionItem item;
item.original = item_json;
item.label = item_json.value("label", "");
item.kind = item_json.value("kind", 0);
if (item_json.contains("detail") && item_json["detail"].is_string())
item.detail = item_json["detail"].get<std::string>();
if (item_json.contains("documentation")) {
if (item_json["documentation"].is_string()) {
item.documentation = item_json["documentation"].get<std::string>();
} else if (item_json["documentation"].contains("value") &&
item_json["documentation"]["value"].is_string()) {
item.is_markup =
item_json["documentation"]["kind"].get<std::string>() ==
"markdown";
std::string documentation =
item_json["documentation"]["value"].get<std::string>();
if (documentation.size() > 1024)
item.is_markup = false;
if (item.is_markup) {
item.documentation =
substitute_fence(documentation, editor->lang.name);
} else {
item.documentation = documentation;
}
}
}
if (item_json.contains("deprecated") &&
item_json["deprecated"].is_boolean())
item.deprecated = item_json["deprecated"].get<bool>();
auto tags = item_json.value("tags", std::vector<int>());
for (auto tag : tags)
if (tag == 1)
item.deprecated = true;
item.sort = item_json.value("sortText", item.label);
item.filter = item_json.value("filterText", item.label);
if (item_json.contains("preselect") &&
item_json["preselect"].is_boolean() &&
item_json["preselect"].get<bool>())
session.select = session.items.size() - 1;
TextEdit edit;
if (item_json.contains("textEdit")) {
auto &te = item_json["textEdit"];
if (te.contains("newText")) {
edit.text = te.value("newText", "");
if (te.contains("replace")) {
edit.start.row = te["replace"]["start"]["line"];
edit.start.col = te["replace"]["start"]["character"];
edit.end.row = te["replace"]["end"]["line"];
edit.end.col = te["replace"]["end"]["character"];
} else if (te.contains("insert")) {
edit.start.row = te["insert"]["start"]["line"];
edit.start.col = te["insert"]["start"]["character"];
edit.end.row = te["insert"]["end"]["line"];
edit.end.col = te["insert"]["end"]["character"];
} else if (te.contains("range")) {
edit.start.row = te["range"]["start"]["line"];
edit.start.col = te["range"]["start"]["character"];
edit.end.row = te["range"]["end"]["line"];
edit.end.col = te["range"]["end"]["character"];
} else {
edit.start = session.hook;
edit.end = editor->cursor;
}
}
} else if (item_json.contains("insertText") &&
item_json["insertText"].is_string()) {
edit.text = item_json["insertText"].get<std::string>();
edit.start = session.hook;
edit.end = editor->cursor;
} else {
edit.text = item.label;
edit.start = session.hook;
edit.end = editor->cursor;
}
utf8_normalize_edit(editor, &edit);
item.edits.push_back(edit);
if (item_json.contains("additionalTextEdits")) {
for (auto &te : item_json["additionalTextEdits"]) {
TextEdit edit;
edit.text = te.value("newText", "");
edit.start.row = te["range"]["start"]["line"];
edit.start.col = te["range"]["start"]["character"];
edit.end.row = te["range"]["end"]["line"];
edit.end.col = te["range"]["end"]["character"];
utf8_normalize_edit(editor, &edit);
item.edits.push_back(edit);
}
}
item.snippet = insert_text_format == 2;
if (item_json.contains("insertTextFormat"))
item.snippet = item_json["insertTextFormat"].get<int>() == 2;
if (item_json.contains("insertTextMode"))
item.asis = item_json["insertTextMode"].get<int>() == 1;
if (item_json.contains("commitCharacters"))
for (auto &c : item_json["commitCharacters"])
if (c.is_string() && c.get<std::string>().size() == 1)
item.end_chars.push_back(c.get<std::string>()[0]);
session.items.push_back(std::move(item));
session.visible.push_back(session.items.size() - 1);
}
session.box.hidden = false;
session.box.render_update();
};
std::shared_lock lock(editor->knot_mtx);
LineIterator *it = begin_l_iter(editor->root, hook.row);
uint32_t length;
char *line = next_line(it, &length);
if (!line) {
free(it->buffer);
free(it);
return;
}
uint32_t col = utf8_offset_to_utf16(line, length, editor->cursor.col);
free(it->buffer);
free(it);
lock.unlock();
json message = {
{"jsonrpc", "2.0"},
{"method", "textDocument/completion"},
{"params",
{{"textDocument", {{"uri", editor->uri}}},
{"position", {{"line", editor->cursor.row}, {"character", col}}}}}};
if (editor->completion.trigger > 0) {
json context = {{"triggerKind", editor->completion.trigger}};
if (editor->completion.trigger == 2 && editor->completion.trigger_char)
context["triggerCharacter"] =
std::string(1, *editor->completion.trigger_char);
message["params"]["context"] = context;
}
lsp_send(editor->lsp, message, pending);
}
void handle_completion(Editor *editor, KeyEvent event) {
if (!editor->lsp || !editor->lsp->allow_completion)
return;
if (mode != INSERT) {
editor->completion.active = false;
return;
}
std::unique_lock lock(editor->completion.mtx);
if (event.key_type == KEY_PASTE) {
editor->completion.active = false;
return;
} else if (event.key_type == KEY_CHAR) {
char ch = *event.c;
if (!editor->completion.active) {
for (char c : editor->lsp->trigger_chars)
if (c == ch) {
editor->completion.trigger = 2;
editor->completion.trigger_char = c;
completion_request(editor);
return;
}
} else {
if (!editor->completion.items.empty()) {
const auto &item = editor->completion.items[editor->completion.select];
const std::vector<char> &end_chars =
item.end_chars.empty() ? editor->lsp->end_chars : item.end_chars;
for (char c : end_chars)
if (c == ch) {
complete_accept(editor);
return;
}
}
}
if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') ||
(ch >= '0' && ch <= '9') || ch == '_') {
if (editor->completion.active) {
if (editor->completion.complete)
completion_filter(editor);
else
completion_request(editor);
} else {
editor->completion.trigger = 3;
completion_request(editor);
}
} else if (ch == CTRL('\\')) {
if (editor->completion.active && !editor->completion.visible.empty()) {
complete_accept(editor);
} else {
editor->completion.trigger = 1;
completion_request(editor);
}
} else if (ch == CTRL('p')) {
if (editor->completion.active)
complete_next(editor);
} else if (ch == CTRL('o')) {
if (editor->completion.active)
complete_prev(editor);
} else if (ch == 0x7F || ch == 0x08 || ch == CTRL('W')) {
if (editor->completion.active) {
if (editor->completion.complete) {
if (editor->cursor <= editor->completion.hook)
editor->completion.active = false;
else
completion_filter(editor);
} else {
if (editor->cursor <= editor->completion.hook)
editor->completion.active = false;
else
completion_request(editor);
}
}
} else {
editor->completion.active = false;
}
} else if (event.key_type == KEY_MOUSE && event.mouse_modifier == 0) {
// Prolly remove mouse support here
// auto &box = editor->completion.box;
// if (event.mouse_y >= box.position.row &&
// event.mouse_x >= box.position.col) {
// uint32_t row = event.mouse_y - box.position.row;
// uint32_t col = event.mouse_x - box.position.col;
// if (row < box.size.row && col < box.size.col) {
// uint8_t idx = 0;
// /* TODO: fix index relative to scroll */
// complete_select(editor, idx);
// }
// }
// if it is being implemented then stop main event handler from processing
// when click inside the box
editor->completion.active = false;
} else {
editor->completion.active = false;
}
}
void completion_resolve_doc(Editor *editor) {
auto &item = editor->completion.items[editor->completion.select];
if (item.documentation)
return;
item.documentation = "";
LSPPending *pending = new LSPPending();
pending->editor = editor;
pending->callback = [](Editor *editor, const json &message) {
std::unique_lock lock(editor->completion.mtx);
auto &item = editor->completion.items[editor->completion.select];
if (message["result"].contains("documentation")) {
if (message["result"]["documentation"].is_string()) {
item.documentation =
message["result"]["documentation"].get<std::string>();
} else if (message["result"]["documentation"].contains("value") &&
message["result"]["documentation"]["value"].is_string()) {
item.is_markup =
message["result"]["documentation"]["kind"].get<std::string>() ==
"markdown";
std::string documentation =
message["result"]["documentation"]["value"].get<std::string>();
if (documentation.size() > 1024)
item.is_markup = false;
if (item.is_markup) {
item.documentation =
substitute_fence(documentation, editor->lang.name);
} else {
item.documentation = documentation;
}
}
}
editor->completion.box.render_update();
};
json message = {{"jsonrpc", "2.0"},
{"method", "completionItem/resolve"},
{"params", item.original}};
lsp_send(editor->lsp, message, pending);
}
void complete_accept(Editor *editor) {
if (!editor->completion.active || editor->completion.box.hidden)
return;
auto &item = editor->completion.items[editor->completion.select];
// TODO: support snippets and asis here
// once indentation engine is implemented
if (editor->completion.version != editor->lsp_version) {
int delta_col = 0;
TextEdit &e = item.edits[0];
if (e.end.row == editor->cursor.row) {
delta_col = editor->cursor.col - e.end.col;
e.end.col = editor->cursor.col;
for (size_t i = 1; i < item.edits.size(); ++i) {
TextEdit &e = item.edits[i];
if (e.start.row == editor->cursor.row) {
e.start.col += delta_col;
if (e.end.row == editor->cursor.row)
e.end.col += delta_col;
}
}
}
}
apply_lsp_edits(editor, item.edits, true);
editor->completion.active = false;
}
inline static int visible_index(const CompletionSession &s) {
for (size_t i = 0; i < s.visible.size(); ++i)
if (s.visible[i] == s.select)
return (int)i;
return -1;
}
void complete_next(Editor *editor) {
auto &s = editor->completion;
if (!s.active || s.box.hidden || s.visible.empty())
return;
int vi = visible_index(s);
if (vi < 0)
vi = 0;
else
vi = (vi + 1) % s.visible.size();
s.select = s.visible[vi];
completion_resolve_doc(editor);
completion_adjust_scroll(editor->completion);
editor->completion.box.render_update();
}
void complete_prev(Editor *editor) {
auto &s = editor->completion;
if (!s.active || s.box.hidden || s.visible.empty())
return;
int vi = visible_index(s);
if (vi < 0)
vi = 0;
else
vi = (vi + s.visible.size() - 1) % s.visible.size();
s.select = s.visible[vi];
completion_resolve_doc(editor);
completion_adjust_scroll(editor->completion);
editor->completion.box.render_update();
}
void complete_select(Editor *editor, uint8_t index) {
editor->completion.select = index;
complete_accept(editor);
}
// #include "editor/decl.h"
// #include "editor/editor.h"
// #include "io/knot.h"
// #include "io/sysio.h"
// #include "lsp/lsp.h"
// #include "main.h"
// #include "ui/completionbox.h"
// #include "utils/utils.h"
//
// inline static std::string completion_prefix(Editor *editor) {
// Coord hook = editor->completion.hook;
// Coord cur = editor->cursor;
// if (hook.row != cur.row || cur.col < hook.col)
// return "";
// LineIterator *it = begin_l_iter(editor->root, hook.row);
// uint32_t line_len;
// char *line = next_line(it, &line_len);
// if (!line) {
// free(it->buffer);
// free(it);
// return "";
// }
// std::string prefix(line + hook.col, cur.col - hook.col);
// free(it->buffer);
// free(it);
// return prefix;
// }
//
// inline static void completion_adjust_scroll(CompletionSession &s) {
// if (s.visible.empty())
// return;
// int vi = -1;
// for (size_t i = 0; i < s.visible.size(); i++)
// if (s.visible[i] == s.select) {
// vi = (int)i;
// break;
// }
// if (vi < 0)
// return;
// if ((uint32_t)vi < s.scroll)
// s.scroll = vi;
// else if ((uint32_t)vi >= s.scroll + 8)
// s.scroll = vi - 7;
// }
//
// void completion_filter(Editor *editor) {
// auto &session = editor->completion;
// std::string prefix = completion_prefix(editor);
// session.visible.clear();
// for (size_t i = 0; i < session.items.size(); ++i) {
// const auto &item = session.items[i];
// const std::string &key = item.filter;
// if (key.size() >= prefix.size() &&
// key.compare(0, prefix.size(), prefix) == 0)
// session.visible.push_back(i);
// }
// if (session.visible.empty()) {
// session.box.hidden = true;
// return;
// }
// if (std::find(session.visible.begin(), session.visible.end(),
// session.select) == session.visible.end())
// session.select = session.visible[0];
// session.box.hidden = false;
// session.scroll = 0;
// completion_adjust_scroll(session);
// session.box.render_update();
// }
//
// void completion_request(Editor *editor) {
// Coord hook = editor->cursor;
// editor->word_boundaries(editor->cursor, &hook.col, nullptr, nullptr,
// nullptr); editor->completion.hook = hook; editor->completion.complete =
// false; editor->completion.active = false; editor->completion.items.clear();
// editor->completion.visible.clear();
// editor->completion.select = 0;
// editor->completion.version = editor->lsp_version;
// LSPPending *pending = new LSPPending();
// pending->editor = editor;
// pending->callback = [](Editor *editor, const json &message) {
// auto &session = editor->completion;
// std::unique_lock lock(session.mtx);
// std::vector<json> items_json;
// std::vector<char> end_chars_def;
// int insert_text_format = 1;
// int insert_text_mode = 1;
// if (message.contains("result")) {
// auto &result = message["result"];
// if (result.is_array()) {
// items_json = result.get<std::vector<json>>();
// session.complete = true;
// if (items_json.empty())
// return;
// editor->completion.active = true;
// } else if (result.is_object() && result.contains("items")) {
// auto &list = result;
// items_json = list["items"].get<std::vector<json>>();
// if (items_json.empty())
// return;
// editor->completion.active = true;
// session.complete = !list.value("isIncomplete", false);
// if (list.contains("itemDefaults") &&
// list["itemDefaults"].is_object()) {
// auto &defs = list["itemDefaults"];
// if (defs.contains("insertTextFormat") &&
// defs["insertTextFormat"].is_number())
// insert_text_format = defs["insertTextFormat"].get<int>();
// if (defs.contains("insertTextMode") &&
// defs["insertTextMode"].is_number())
// insert_text_mode = defs["insertTextMode"].get<int>();
// if (defs.contains("textEdit"))
// if (defs["textEdit"].is_array())
// for (auto &c : defs["textEdit"]) {
// if (!c.is_string())
// continue;
// std::string str = c.get<std::string>();
// if (str.size() != 1)
// continue;
// end_chars_def.push_back(str[0]);
// }
// }
// }
// }
// session.items.reserve(items_json.size() + 1);
// session.visible.reserve(items_json.size() + 1);
// for (auto &item_json : items_json) {
// CompletionItem item;
// item.original = item_json;
// item.label = item_json.value("label", "");
// item.kind = item_json.value("kind", 0);
// if (item_json.contains("detail") && item_json["detail"].is_string())
// item.detail = item_json["detail"].get<std::string>();
// if (item_json.contains("documentation")) {
// if (item_json["documentation"].is_string()) {
// item.documentation = item_json["documentation"].get<std::string>();
// } else if (item_json["documentation"].contains("value") &&
// item_json["documentation"]["value"].is_string()) {
// item.is_markup =
// item_json["documentation"]["kind"].get<std::string>() ==
// "markdown";
// std::string documentation =
// item_json["documentation"]["value"].get<std::string>();
// if (documentation.size() > 1024)
// item.is_markup = false;
// if (item.is_markup) {
// item.documentation =
// substitute_fence(documentation, editor->lang.name);
// } else {
// item.documentation = documentation;
// }
// }
// }
// if (item_json.contains("deprecated") &&
// item_json["deprecated"].is_boolean())
// item.deprecated = item_json["deprecated"].get<bool>();
// auto tags = item_json.value("tags", std::vector<int>());
// for (auto tag : tags)
// if (tag == 1)
// item.deprecated = true;
// item.sort = item_json.value("sortText", item.label);
// item.filter = item_json.value("filterText", item.label);
// if (item_json.contains("preselect") &&
// item_json["preselect"].is_boolean() &&
// item_json["preselect"].get<bool>())
// session.select = session.items.size() - 1;
// TextEdit edit;
// if (item_json.contains("textEdit")) {
// auto &te = item_json["textEdit"];
// if (te.contains("newText")) {
// edit.text = te.value("newText", "");
// if (te.contains("replace")) {
// edit.start.row = te["replace"]["start"]["line"];
// edit.start.col = te["replace"]["start"]["character"];
// edit.end.row = te["replace"]["end"]["line"];
// edit.end.col = te["replace"]["end"]["character"];
// } else if (te.contains("insert")) {
// edit.start.row = te["insert"]["start"]["line"];
// edit.start.col = te["insert"]["start"]["character"];
// edit.end.row = te["insert"]["end"]["line"];
// edit.end.col = te["insert"]["end"]["character"];
// } else if (te.contains("range")) {
// edit.start.row = te["range"]["start"]["line"];
// edit.start.col = te["range"]["start"]["character"];
// edit.end.row = te["range"]["end"]["line"];
// edit.end.col = te["range"]["end"]["character"];
// } else {
// edit.start = session.hook;
// edit.end = editor->cursor;
// }
// }
// } else if (item_json.contains("insertText") &&
// item_json["insertText"].is_string()) {
// edit.text = item_json["insertText"].get<std::string>();
// edit.start = session.hook;
// edit.end = editor->cursor;
// } else {
// edit.text = item.label;
// edit.start = session.hook;
// edit.end = editor->cursor;
// }
// editor->utf8_normalize_edit(&edit);
// item.edits.push_back(edit);
// if (item_json.contains("additionalTextEdits")) {
// for (auto &te : item_json["additionalTextEdits"]) {
// TextEdit edit;
// edit.text = te.value("newText", "");
// edit.start.row = te["range"]["start"]["line"];
// edit.start.col = te["range"]["start"]["character"];
// edit.end.row = te["range"]["end"]["line"];
// edit.end.col = te["range"]["end"]["character"];
// editor->utf8_normalize_edit(&edit);
// item.edits.push_back(edit);
// }
// }
// item.snippet = insert_text_format == 2;
// if (item_json.contains("insertTextFormat"))
// item.snippet = item_json["insertTextFormat"].get<int>() == 2;
// if (item_json.contains("insertTextMode"))
// item.asis = item_json["insertTextMode"].get<int>() == 1;
// if (item_json.contains("commitCharacters"))
// for (auto &c : item_json["commitCharacters"])
// if (c.is_string() && c.get<std::string>().size() == 1)
// item.end_chars.push_back(c.get<std::string>()[0]);
// session.items.push_back(std::move(item));
// session.visible.push_back(session.items.size() - 1);
// }
// session.box.hidden = false;
// session.box.render_update();
// };
// LineIterator *it = begin_l_iter(editor->root, hook.row);
// uint32_t length;
// char *line = next_line(it, &length);
// if (!line) {
// free(it->buffer);
// free(it);
// return;
// }
// uint32_t col = utf8_offset_to_utf16(line, length, editor->cursor.col);
// free(it->buffer);
// free(it);
// json message = {
// {"jsonrpc", "2.0"},
// {"method", "textDocument/completion"},
// {"params",
// {{"textDocument", {{"uri", editor->uri}}},
// {"position", {{"line", editor->cursor.row}, {"character", col}}}}}};
// if (editor->completion.trigger > 0) {
// json context = {{"triggerKind", editor->completion.trigger}};
// if (editor->completion.trigger == 2 && editor->completion.trigger_char)
// context["triggerCharacter"] =
// std::string(1, *editor->completion.trigger_char);
// message["params"]["context"] = context;
// }
// lsp_send(editor->lsp, message, pending);
// }
//
// // Move this into the box and merge the box and this guy
// void CompletionSession::handle(KeyEvent event) {
// if (!editor->lsp || !editor->lsp->allow_completion)
// return;
// if (mode != INSERT) {
// this->active = false;
// return;
// }
// std::unique_lock lock(this->mtx);
// if (event.key_type == KEY_PASTE) {
// this->active = false;
// return;
// } else if (event.key_type == KEY_CHAR) {
// char ch = *event.c;
// if (!this->active) {
// for (char c : editor->lsp->trigger_chars)
// if (c == ch) {
// this->trigger = 2;
// this->trigger_char = c;
// completion_request(editor);
// return;
// }
// } else {
// if (!editor->completion.items.empty()) {
// const auto &item =
// editor->completion.items[editor->completion.select]; const
// std::vector<char> &end_chars =
// item.end_chars.empty() ? editor->lsp->end_chars : item.end_chars;
// for (char c : end_chars)
// if (c == ch) {
// this->accept();
// return;
// }
// }
// }
// if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') ||
// (ch >= '0' && ch <= '9') || ch == '_') {
// if (this->active) {
// if (this->complete)
// completion_filter(editor);
// else
// completion_request(editor);
// } else {
// this->trigger = 3;
// completion_request(editor);
// }
// } else if (ch == CTRL('\\')) {
// if (this->active && !this->visible.empty()) {
// this->accept();
// } else {
// this->trigger = 1;
// completion_request(editor);
// }
// } else if (ch == CTRL('p')) {
// if (this->active)
// this->next();
// } else if (ch == CTRL('o')) {
// if (this->active)
// this->prev();
// } else if (ch == 0x7F || ch == 0x08 || ch == CTRL('W')) {
// if (this->active) {
// if (this->complete) {
// if (editor->cursor <= this->hook)
// this->active = false;
// else
// completion_filter(editor);
// } else {
// if (editor->cursor <= this->hook)
// this->active = false;
// else
// completion_request(editor);
// }
// }
// } else {
// this->active = false;
// }
// } else if (event.key_type == KEY_MOUSE && event.mouse_modifier == 0) {
// // Prolly add mouse support here
// // auto &box = this->box;
// // if (event.mouse_y >= box.position.row &&
// // event.mouse_x >= box.position.col) {
// // uint32_t row = event.mouse_y - box.position.row;
// // uint32_t col = event.mouse_x - box.position.col;
// // if (row < box.size.row && col < box.size.col) {
// // uint8_t idx = 0;
// // /* TODO: fix index relative to scroll */
// // complete_select(editor, idx);
// // }
// // }
// // if it is being implemented then stop main event handler from
// processing
// // when click inside the box
// this->active = false;
// } else {
// this->active = false;
// }
// }
//
// void CompletionSession::resolve_doc() {
// auto &item = this->items[this->select];
// if (item.documentation)
// return;
// item.documentation = "";
// LSPPending *pending = new LSPPending();
// pending->editor = editor;
// pending->callback = [](Editor *editor, const json &message) {
// std::unique_lock lock(editor->completion.mtx);
// auto &item = editor->completion.items[editor->completion.select];
// if (message["result"].contains("documentation")) {
// if (message["result"]["documentation"].is_string()) {
// item.documentation =
// message["result"]["documentation"].get<std::string>();
// } else if (message["result"]["documentation"].contains("value") &&
// message["result"]["documentation"]["value"].is_string()) {
// item.is_markup =
// message["result"]["documentation"]["kind"].get<std::string>() ==
// "markdown";
// std::string documentation =
// message["result"]["documentation"]["value"].get<std::string>();
// if (documentation.size() > 1024)
// item.is_markup = false;
// if (item.is_markup) {
// item.documentation =
// substitute_fence(documentation, editor->lang.name);
// } else {
// item.documentation = documentation;
// }
// }
// }
// editor->completion.box.render_update();
// };
// json message = {{"jsonrpc", "2.0"},
// {"method", "completionItem/resolve"},
// {"params", item.original}};
// lsp_send(editor->lsp, message, pending);
// }
//
// void CompletionSession::accept() {
// if (!this->active || this->box.hidden)
// return;
// auto &item = this->items[this->select];
// // TODO: support snippets and asis here
// // once indentation engine is implemented
// if (this->version != editor->lsp_version) {
// int delta_col = 0;
// TextEdit &e = item.edits[0];
// if (e.end.row == editor->cursor.row) {
// delta_col = editor->cursor.col - e.end.col;
// e.end.col = editor->cursor.col;
// for (size_t i = 1; i < item.edits.size(); ++i) {
// TextEdit &e = item.edits[i];
// if (e.start.row == editor->cursor.row) {
// e.start.col += delta_col;
// if (e.end.row == editor->cursor.row)
// e.end.col += delta_col;
// }
// }
// }
// }
// editor->apply_lsp_edits(item.edits, true);
// this->active = false;
// }
//
// inline static int visible_index(const CompletionSession &s) {
// for (size_t i = 0; i < s.visible.size(); ++i)
// if (s.visible[i] == s.select)
// return (int)i;
// return -1;
// }
//
// void CompletionSession::next() {
// if (!this->active || this->box.hidden || this->visible.empty())
// return;
// int vi = visible_index(*this);
// if (vi < 0)
// vi = 0;
// else
// vi = (vi + 1) % this->visible.size();
// this->select = this->visible[vi];
// this->resolve_doc();
// completion_adjust_scroll(*this);
// this->box.render_update();
// }
//
// void CompletionSession::prev() {
// if (!this->active || this->box.hidden || this->visible.empty())
// return;
// int vi = visible_index(*this);
// if (vi < 0)
// vi = 0;
// else
// vi = (vi + this->visible.size() - 1) % this->visible.size();
// this->select = this->visible[vi];
// this->resolve_doc();
// completion_adjust_scroll(*this);
// this->box.render_update();
// }
//
// void CompletionSession::choose(uint8_t index) {
// this->select = index;
// this->accept();
// }

View File

@@ -1,14 +1,14 @@
#include "editor/editor.h"
#include "utils/utils.h"
Coord move_right(Editor *editor, Coord cursor, uint32_t number) {
Coord Editor::move_right(Coord cursor, uint32_t number) {
Coord result = cursor;
if (!editor || !editor->root || number == 0)
if (!this->root || number == 0)
return result;
uint32_t row = result.row;
uint32_t col = result.col;
uint32_t line_len = 0;
LineIterator *it = begin_l_iter(editor->root, row);
LineIterator *it = begin_l_iter(this->root, row);
if (!it)
return result;
char *line = next_line(it, &line_len);
@@ -22,7 +22,7 @@ Coord move_right(Editor *editor, Coord cursor, uint32_t number) {
while (number > 0) {
if (col >= line_len) {
uint32_t next_row = row + 1;
if (next_row >= editor->root->line_count) {
if (next_row >= this->root->line_count) {
col = line_len;
break;
}
@@ -49,14 +49,16 @@ Coord move_right(Editor *editor, Coord cursor, uint32_t number) {
return result;
}
Coord move_left(Editor *editor, Coord cursor, uint32_t number) {
Coord Editor::move_left(Coord cursor, uint32_t number) {
Coord result = cursor;
if (!editor || !editor->root || number == 0)
if (!this->root || number == 0)
return result;
uint32_t row = result.row;
uint32_t col = result.col;
uint32_t len = 0;
LineIterator *it = begin_l_iter(editor->root, row);
LineIterator *it = begin_l_iter(this->root, row);
if (!it)
return result;
char *line = next_line(it, &len);
if (!line) {
free(it->buffer);
@@ -102,29 +104,28 @@ Coord move_left(Editor *editor, Coord cursor, uint32_t number) {
return result;
}
void cursor_down(Editor *editor, uint32_t number) {
if (!editor || !editor->root || number == 0)
void Editor::cursor_down(uint32_t number) {
if (!this->root || number == 0)
return;
uint32_t visual_col = editor->cursor_preffered;
uint32_t visual_col = this->cursor_preffered;
if (visual_col == UINT32_MAX) {
uint32_t len;
LineIterator *it = begin_l_iter(editor->root, editor->cursor.row);
LineIterator *it = begin_l_iter(this->root, this->cursor.row);
char *line = next_line(it, &len);
if (!line) {
free(it->buffer);
free(it);
return;
}
editor->cursor_preffered =
get_visual_col_from_bytes(line, len, editor->cursor.col);
visual_col = editor->cursor_preffered;
this->cursor_preffered =
get_visual_col_from_bytes(line, len, this->cursor.col);
visual_col = this->cursor_preffered;
free(it->buffer);
free(it);
}
editor->cursor.row =
MIN(editor->cursor.row + number, editor->root->line_count - 1);
this->cursor.row = MIN(this->cursor.row + number, this->root->line_count - 1);
uint32_t len;
LineIterator *it = begin_l_iter(editor->root, editor->cursor.row);
LineIterator *it = begin_l_iter(this->root, this->cursor.row);
char *line = next_line(it, &len);
if (!line) {
free(it->buffer);
@@ -133,26 +134,26 @@ void cursor_down(Editor *editor, uint32_t number) {
}
if (len > 0 && line[len - 1] == '\n')
--len;
editor->cursor.col = get_bytes_from_visual_col(line, len, visual_col);
this->cursor.col = get_bytes_from_visual_col(line, len, visual_col);
free(it->buffer);
free(it);
}
void cursor_up(Editor *editor, uint32_t number) {
if (!editor || !editor->root || number == 0 || editor->cursor.row == 0)
void Editor::cursor_up(uint32_t number) {
if (!this->root || number == 0 || this->cursor.row == 0)
return;
uint32_t len;
LineIterator *it = begin_l_iter(editor->root, editor->cursor.row);
LineIterator *it = begin_l_iter(this->root, this->cursor.row);
char *line_content = next_line(it, &len);
if (!line_content)
return;
if (editor->cursor_preffered == UINT32_MAX)
editor->cursor_preffered =
get_visual_col_from_bytes(line_content, len, editor->cursor.col);
uint32_t visual_col = editor->cursor_preffered;
if (this->cursor_preffered == UINT32_MAX)
this->cursor_preffered =
get_visual_col_from_bytes(line_content, len, this->cursor.col);
uint32_t visual_col = this->cursor_preffered;
free(it->buffer);
free(it);
uint32_t target_row = editor->cursor.row;
uint32_t target_row = this->cursor.row;
while (number > 0 && target_row > 0) {
target_row--;
if (target_row == 0) {
@@ -161,32 +162,31 @@ void cursor_up(Editor *editor, uint32_t number) {
}
number--;
}
it = begin_l_iter(editor->root, target_row);
it = begin_l_iter(this->root, target_row);
line_content = next_line(it, &len);
if (line_content) {
if (len > 0 && line_content[len - 1] == '\n')
--len;
editor->cursor.row = target_row;
editor->cursor.col =
get_bytes_from_visual_col(line_content, len, visual_col);
this->cursor.row = target_row;
this->cursor.col = get_bytes_from_visual_col(line_content, len, visual_col);
} else {
editor->cursor.row = 0;
editor->cursor.col = 0;
this->cursor.row = 0;
this->cursor.col = 0;
}
free(it->buffer);
free(it);
}
void cursor_right(Editor *editor, uint32_t number) {
if (!editor || !editor->root || number == 0)
void Editor::cursor_right(uint32_t number) {
if (!this->root || number == 0)
return;
editor->cursor = move_right(editor, editor->cursor, number);
editor->cursor_preffered = UINT32_MAX;
this->cursor = this->move_right(this->cursor, number);
this->cursor_preffered = UINT32_MAX;
}
void cursor_left(Editor *editor, uint32_t number) {
if (!editor || !editor->root || number == 0)
void Editor::cursor_left(uint32_t number) {
if (!this->root || number == 0)
return;
editor->cursor = move_left(editor, editor->cursor, number);
editor->cursor_preffered = UINT32_MAX;
this->cursor = this->move_left(this->cursor, number);
this->cursor_preffered = UINT32_MAX;
}

View File

@@ -2,20 +2,18 @@
#include "lsp/lsp.h"
#include "utils/utils.h"
void edit_erase(Editor *editor, Coord pos, int64_t len) {
void Editor::edit_erase(Coord pos, int64_t len) {
if (len == 0)
return;
if (len < 0) {
std::shared_lock lock_1(editor->knot_mtx);
uint32_t cursor_original =
line_to_byte(editor->root, editor->cursor.row, nullptr) +
editor->cursor.col;
uint32_t byte_pos = line_to_byte(editor->root, pos.row, nullptr) + pos.col;
Coord point = move_left(editor, pos, -len);
line_to_byte(this->root, this->cursor.row, nullptr) + this->cursor.col;
uint32_t byte_pos = line_to_byte(this->root, pos.row, nullptr) + pos.col;
Coord point = this->move_left(pos, -len);
json lsp_range;
bool do_lsp = (editor->lsp != nullptr);
bool do_lsp = (this->lsp != nullptr);
if (do_lsp) {
LineIterator *it = begin_l_iter(editor->root, point.row);
LineIterator *it = begin_l_iter(this->root, point.row);
uint32_t len;
char *line = next_line(it, &len);
int utf16_start = 0;
@@ -23,7 +21,7 @@ void edit_erase(Editor *editor, Coord pos, int64_t len) {
utf16_start = utf8_offset_to_utf16(line, len, point.col);
free(it->buffer);
free(it);
it = begin_l_iter(editor->root, pos.row);
it = begin_l_iter(this->root, pos.row);
line = next_line(it, &len);
int utf16_end = 0;
if (line)
@@ -33,62 +31,58 @@ void edit_erase(Editor *editor, Coord pos, int64_t len) {
lsp_range = {{"start", {{"line", point.row}, {"character", utf16_start}}},
{"end", {{"line", pos.row}, {"character", utf16_end}}}};
}
uint32_t start = line_to_byte(editor->root, point.row, nullptr) + point.col;
uint32_t start = line_to_byte(this->root, point.row, nullptr) + point.col;
if (cursor_original > start && cursor_original <= byte_pos) {
editor->cursor = point;
editor->cursor_preffered = UINT32_MAX;
this->cursor = point;
this->cursor_preffered = UINT32_MAX;
} else if (cursor_original > byte_pos) {
uint32_t cursor_new = cursor_original - (byte_pos - start);
uint32_t new_col;
uint32_t new_row = byte_to_line(editor->root, cursor_new, &new_col);
editor->cursor = {new_row, new_col};
editor->cursor_preffered = UINT32_MAX;
uint32_t new_row = byte_to_line(this->root, cursor_new, &new_col);
this->cursor = {new_row, new_col};
this->cursor_preffered = UINT32_MAX;
}
lock_1.unlock();
uint32_t start_row = point.row;
uint32_t end_row = pos.row;
apply_hook_deletion(editor, start_row + 1, end_row);
std::unique_lock lock_2(editor->knot_mtx);
editor->root = erase(editor->root, start, byte_pos - start);
lock_2.unlock();
if (editor->parser)
editor->parser->edit(start_row, end_row - start_row, 0);
this->apply_hook_deletion(start_row + 1, end_row);
this->root = erase(this->root, start, byte_pos - start);
if (this->parser)
this->parser->edit(start_row, end_row - start_row, 0);
if (do_lsp) {
if (editor->lsp->incremental_sync) {
json message = {
{"jsonrpc", "2.0"},
auto lsp = this->lsp.load();
if (lsp->incremental_sync) {
auto message = std::make_unique<LSPMessage>();
message->message = {
{"method", "textDocument/didChange"},
{"params",
{{"textDocument",
{{"uri", editor->uri}, {"version", ++editor->lsp_version}}},
{{"uri", this->uri}, {"version", ++this->lsp_version}}},
{"contentChanges",
json::array({{{"range", lsp_range}, {"text", ""}}})}}}};
lsp_send(editor->lsp, message, nullptr);
lsp->send(std::move(message));
} else {
char *buf = read(editor->root, 0, editor->root->char_count);
char *buf = read(this->root, 0, this->root->char_count);
std::string text(buf);
free(buf);
json message = {
{"jsonrpc", "2.0"},
auto message = std::make_unique<LSPMessage>();
message->message = {
{"method", "textDocument/didChange"},
{"params",
{{"textDocument",
{{"uri", editor->uri}, {"version", ++editor->lsp_version}}},
{{"uri", this->uri}, {"version", ++this->lsp_version}}},
{"contentChanges", json::array({{{"text", text}}})}}}};
lsp_send(editor->lsp, message, nullptr);
lsp->send(std::move(message));
}
}
} else {
std::shared_lock lock_1(editor->knot_mtx);
uint32_t cursor_original =
line_to_byte(editor->root, editor->cursor.row, nullptr) +
editor->cursor.col;
uint32_t byte_pos = line_to_byte(editor->root, pos.row, nullptr) + pos.col;
Coord point = move_right(editor, pos, len);
line_to_byte(this->root, this->cursor.row, nullptr) + this->cursor.col;
uint32_t byte_pos = line_to_byte(this->root, pos.row, nullptr) + pos.col;
Coord point = this->move_right(pos, len);
json lsp_range;
bool do_lsp = (editor->lsp != nullptr);
bool do_lsp = (this->lsp != nullptr);
if (do_lsp) {
LineIterator *it = begin_l_iter(editor->root, pos.row);
LineIterator *it = begin_l_iter(this->root, pos.row);
uint32_t line_len;
char *line = next_line(it, &line_len);
int utf16_start = 0;
@@ -96,7 +90,7 @@ void edit_erase(Editor *editor, Coord pos, int64_t len) {
utf16_start = utf8_offset_to_utf16(line, line_len, pos.col);
free(it->buffer);
free(it);
it = begin_l_iter(editor->root, point.row);
it = begin_l_iter(this->root, point.row);
line = next_line(it, &line_len);
int utf16_end = 0;
if (line)
@@ -106,67 +100,63 @@ void edit_erase(Editor *editor, Coord pos, int64_t len) {
lsp_range = {{"start", {{"line", pos.row}, {"character", utf16_start}}},
{"end", {{"line", point.row}, {"character", utf16_end}}}};
}
uint32_t end = line_to_byte(editor->root, point.row, nullptr) + point.col;
uint32_t end = line_to_byte(this->root, point.row, nullptr) + point.col;
if (cursor_original > byte_pos && cursor_original <= end) {
editor->cursor = pos;
editor->cursor_preffered = UINT32_MAX;
this->cursor = pos;
this->cursor_preffered = UINT32_MAX;
} else if (cursor_original > end) {
uint32_t cursor_new = cursor_original - (end - byte_pos);
uint32_t new_col;
uint32_t new_row = byte_to_line(editor->root, cursor_new, &new_col);
editor->cursor = {new_row, new_col};
editor->cursor_preffered = UINT32_MAX;
uint32_t new_row = byte_to_line(this->root, cursor_new, &new_col);
this->cursor = {new_row, new_col};
this->cursor_preffered = UINT32_MAX;
}
lock_1.unlock();
uint32_t start_row = pos.row;
uint32_t end_row = point.row;
apply_hook_deletion(editor, start_row + 1, end_row);
std::unique_lock lock_2(editor->knot_mtx);
editor->root = erase(editor->root, byte_pos, end - byte_pos);
lock_2.unlock();
if (editor->parser)
editor->parser->edit(start_row, end_row - start_row, 0);
this->apply_hook_deletion(start_row + 1, end_row);
this->root = erase(this->root, byte_pos, end - byte_pos);
if (this->parser)
this->parser->edit(start_row, end_row - start_row, 0);
if (do_lsp) {
if (editor->lsp->incremental_sync) {
json message = {
{"jsonrpc", "2.0"},
auto lsp = this->lsp.load();
if (lsp->incremental_sync) {
auto message = std::make_unique<LSPMessage>();
message->message = {
{"method", "textDocument/didChange"},
{"params",
{{"textDocument",
{{"uri", editor->uri}, {"version", ++editor->lsp_version}}},
{{"uri", this->uri}, {"version", ++this->lsp_version}}},
{"contentChanges",
json::array({{{"range", lsp_range}, {"text", ""}}})}}}};
lsp_send(editor->lsp, message, nullptr);
lsp->send(std::move(message));
} else {
char *buf = read(editor->root, 0, editor->root->char_count);
char *buf = read(this->root, 0, this->root->char_count);
std::string text(buf);
free(buf);
json message = {
{"jsonrpc", "2.0"},
auto message = std::make_unique<LSPMessage>();
message->message = {
{"method", "textDocument/didChange"},
{"params",
{{"textDocument",
{{"uri", editor->uri}, {"version", ++editor->lsp_version}}},
{{"uri", this->uri}, {"version", ++this->lsp_version}}},
{"contentChanges", json::array({{{"text", text}}})}}}};
lsp_send(editor->lsp, message, nullptr);
lsp->send(std::move(message));
}
}
}
}
void edit_insert(Editor *editor, Coord pos, char *data, uint32_t len) {
std::shared_lock lock_1(editor->knot_mtx);
void Editor::edit_insert(Coord pos, char *data, uint32_t len) {
uint32_t cursor_original =
line_to_byte(editor->root, editor->cursor.row, nullptr) +
editor->cursor.col;
uint32_t byte_pos = line_to_byte(editor->root, pos.row, nullptr) + pos.col;
line_to_byte(this->root, this->cursor.row, nullptr) + this->cursor.col;
uint32_t byte_pos = line_to_byte(this->root, pos.row, nullptr) + pos.col;
if (cursor_original > byte_pos) {
uint32_t cursor_new = cursor_original + len;
uint32_t new_col;
uint32_t new_row = byte_to_line(editor->root, cursor_new, &new_col);
editor->cursor = {new_row, new_col};
uint32_t new_row = byte_to_line(this->root, cursor_new, &new_col);
this->cursor = {new_row, new_col};
}
LineIterator *it = begin_l_iter(editor->root, pos.row);
LineIterator *it = begin_l_iter(this->root, pos.row);
uint32_t line_len;
char *line = next_line(it, &line_len);
int utf16_col = 0;
@@ -174,55 +164,52 @@ void edit_insert(Editor *editor, Coord pos, char *data, uint32_t len) {
utf16_col = utf8_offset_to_utf16(line, line_len, pos.col);
free(it->buffer);
free(it);
lock_1.unlock();
std::unique_lock lock_2(editor->knot_mtx);
editor->root = insert(editor->root, byte_pos, data, len);
this->root = insert(this->root, byte_pos, data, len);
uint32_t rows = 0;
for (uint32_t i = 0; i < len; i++)
if (data[i] == '\n')
rows++;
apply_hook_insertion(editor, pos.row, rows);
lock_2.unlock();
if (editor->parser)
editor->parser->edit(pos.row, 0, rows);
if (editor->lsp) {
if (editor->lsp->incremental_sync) {
json message = {
{"jsonrpc", "2.0"},
this->apply_hook_insertion(pos.row, rows);
if (this->parser)
this->parser->edit(pos.row, 0, rows);
auto lsp = this->lsp.load();
if (lsp) {
if (lsp->incremental_sync) {
auto message = std::make_unique<LSPMessage>();
message->message = {
{"method", "textDocument/didChange"},
{"params",
{{"textDocument",
{{"uri", editor->uri}, {"version", ++editor->lsp_version}}},
{{"uri", this->uri}, {"version", ++this->lsp_version}}},
{"contentChanges",
json::array(
{{{"range",
{{"start", {{"line", pos.row}, {"character", utf16_col}}},
{"end", {{"line", pos.row}, {"character", utf16_col}}}}},
{"text", std::string(data, len)}}})}}}};
lsp_send(editor->lsp, message, nullptr);
lsp->send(std::move(message));
} else {
char *buf = read(editor->root, 0, editor->root->char_count);
char *buf = read(this->root, 0, this->root->char_count);
std::string text(buf);
free(buf);
json message = {
{"jsonrpc", "2.0"},
auto message = std::make_unique<LSPMessage>();
message->message = {
{"method", "textDocument/didChange"},
{"params",
{{"textDocument",
{{"uri", editor->uri}, {"version", ++editor->lsp_version}}},
{{"uri", this->uri}, {"version", ++this->lsp_version}}},
{"contentChanges", json::array({{{"text", text}}})}}}};
lsp_send(editor->lsp, message, nullptr);
lsp->send(std::move(message));
}
}
}
void edit_replace(Editor *editor, Coord start, Coord end, const char *text,
void Editor::edit_replace(Coord start, Coord end, const char *text,
uint32_t len) {
std::unique_lock lock(editor->knot_mtx);
uint32_t start_byte =
line_to_byte(editor->root, start.row, nullptr) + start.col;
uint32_t end_byte = line_to_byte(editor->root, end.row, nullptr) + end.col;
LineIterator *it = begin_l_iter(editor->root, start.row);
line_to_byte(this->root, start.row, nullptr) + start.col;
uint32_t end_byte = line_to_byte(this->root, end.row, nullptr) + end.col;
LineIterator *it = begin_l_iter(this->root, start.row);
uint32_t line_len;
char *line = next_line(it, &line_len);
int utf16_start = 0;
@@ -230,7 +217,7 @@ void edit_replace(Editor *editor, Coord start, Coord end, const char *text,
utf16_start = utf8_offset_to_utf16(line, line_len, start.col);
free(it->buffer);
free(it);
it = begin_l_iter(editor->root, end.row);
it = begin_l_iter(this->root, end.row);
line = next_line(it, &line_len);
int utf16_end = 0;
if (line)
@@ -238,25 +225,26 @@ void edit_replace(Editor *editor, Coord start, Coord end, const char *text,
free(it->buffer);
free(it);
if (start_byte != end_byte)
editor->root = erase(editor->root, start_byte, end_byte - start_byte);
this->root = erase(this->root, start_byte, end_byte - start_byte);
if (len > 0)
editor->root = insert(editor->root, start_byte, (char *)text, len);
this->root = insert(this->root, start_byte, (char *)text, len);
uint32_t rows = 0;
for (uint32_t i = 0; i < len; i++)
if (text[i] == '\n')
rows++;
if (rows > 0)
rows--;
if (editor->parser)
editor->parser->edit(start.row, end.row - start.row, rows);
if (editor->lsp) {
if (editor->lsp->incremental_sync) {
json message = {
{"jsonrpc", "2.0"},
if (this->parser)
this->parser->edit(start.row, end.row - start.row, rows);
auto lsp = this->lsp.load();
if (lsp) {
if (lsp->incremental_sync) {
auto message = std::make_unique<LSPMessage>();
message->message = {
{"method", "textDocument/didChange"},
{"params",
{{"textDocument",
{{"uri", editor->uri}, {"version", ++editor->lsp_version}}},
{{"uri", this->uri}, {"version", ++this->lsp_version}}},
{"contentChanges",
json::array(
{{{"range",
@@ -264,19 +252,19 @@ void edit_replace(Editor *editor, Coord start, Coord end, const char *text,
{{"line", start.row}, {"character", utf16_start}}},
{"end", {{"line", end.row}, {"character", utf16_end}}}}},
{"text", std::string(text, len)}}})}}}};
lsp_send(editor->lsp, message, nullptr);
lsp->send(std::move(message));
} else {
char *buf = read(editor->root, 0, editor->root->char_count);
char *buf = read(this->root, 0, this->root->char_count);
std::string full_text(buf);
free(buf);
json message = {
{"jsonrpc", "2.0"},
auto message = std::make_unique<LSPMessage>();
message->message = {
{"method", "textDocument/didChange"},
{"params",
{{"textDocument",
{{"uri", editor->uri}, {"version", ++editor->lsp_version}}},
{{"uri", this->uri}, {"version", ++this->lsp_version}}},
{"contentChanges", json::array({{{"text", full_text}}})}}}};
lsp_send(editor->lsp, message, nullptr);
lsp->send(std::move(message));
}
}
}

View File

@@ -5,69 +5,66 @@
#include "syntax/langs.h"
#include "utils/utils.h"
Editor *new_editor(const char *filename_arg, Coord position, Coord size,
uint8_t eol) {
Editor *editor = new Editor();
if (!editor)
return nullptr;
Editor::Editor(const char *filename_arg, uint8_t eol) {
uint32_t len = 0;
std::string filename = path_abs(filename_arg);
editor->unix_eol = eol & 1;
char *str = load_file(filename.c_str(), &len, &editor->unix_eol);
this->unix_eol = eol & 1;
char *str = load_file(filename.c_str(), &len, &this->unix_eol);
if (!str) {
str = (char *)malloc(1);
*str = '\n';
len = 1;
}
if ((eol >> 1) & 1)
editor->unix_eol = eol & 1;
editor->filename = filename;
editor->uri = path_to_file_uri(filename);
editor->position = position;
editor->size = size;
editor->cursor_preffered = UINT32_MAX;
this->unix_eol = eol & 1;
this->filename = filename;
this->uri = path_to_file_uri(filename);
this->cursor_preffered = UINT32_MAX;
if (len == 0) {
free(str);
str = (char *)malloc(1);
*str = '\n';
len = 1;
}
editor->root = load(str, len, optimal_chunk_size(len));
this->scroll = {0, 0};
this->cursor = {0, 0};
this->size = {20, 20};
this->root = load(str, len, optimal_chunk_size(len));
free(str);
editor->lang = language_for_file(filename.c_str());
if (parsers.find(editor->lang.name) != parsers.end())
editor->parser = new Parser(editor, editor->lang.name, size.row + 5);
if (editor->lang.name == "css" || editor->lang.name == "html" ||
editor->lang.name == "javascript" || editor->lang.name == "markdown" ||
editor->lang.name == "typescript")
editor->is_css_color = true;
if (len <= (1024 * 28))
request_add_to_lsp(editor->lang, editor);
editor->indents.compute_indent(editor);
return editor;
this->lang = language_for_file(filename.c_str());
if (parsers.find(this->lang.name) != parsers.end())
this->parser = new Parser(this, this->lang.name, size.row + 5);
if (this->lang.name == "css" || this->lang.name == "html" ||
this->lang.name == "javascript" || this->lang.name == "markdown" ||
this->lang.name == "typescript")
this->is_css_color = true;
if (len <= (1024 * 28)) {
std::lock_guard lock(lsp::lsp_mutex);
lsp::new_editors.push_back(this);
}
this->indents.compute_indent(this);
}
void free_editor(Editor *editor) {
remove_from_lsp(editor);
if (editor->parser)
delete editor->parser;
editor->parser = nullptr;
free_rope(editor->root);
delete editor;
Editor::~Editor() {
auto lsp = this->lsp.load();
if (lsp)
lsp->remove(this);
if (this->parser)
delete this->parser;
this->parser = nullptr;
free_rope(this->root);
}
void save_file(Editor *editor) {
if (!editor || !editor->root)
void Editor::save() {
if (!this->root)
return;
std::shared_lock lock(editor->knot_mtx);
int version = editor->lsp_version;
uint32_t char_count = editor->root->char_count;
char *str = read(editor->root, 0, char_count);
int version = this->lsp_version;
uint32_t char_count = this->root->char_count;
char *str = read(this->root, 0, char_count);
if (!str)
return;
lock.unlock();
std::ofstream out(editor->filename);
if (!editor->unix_eol) {
std::ofstream out(this->filename);
if (!this->unix_eol) {
for (uint32_t i = 0; i < char_count; ++i) {
if (str[i] == '\n')
out.put('\r');
@@ -78,30 +75,33 @@ void save_file(Editor *editor) {
}
out.close();
free(str);
bar.log("Written " + std::to_string(char_count) + " bytes to " +
editor->filename);
if (editor->lsp) {
json save_msg = {{"jsonrpc", "2.0"},
{"method", "textDocument/didSave"},
{"params", {{"textDocument", {{"uri", editor->uri}}}}}};
lsp_send(editor->lsp, save_msg, nullptr);
if (editor->lsp->allow_formatting) {
json msg = {{"jsonrpc", "2.0"},
{"method", "textDocument/formatting"},
ui::bar.log("Written " + std::to_string(char_count) + " bytes to " +
this->filename);
auto lsp = this->lsp.load();
if (lsp) {
log("Saving %s", this->filename.c_str());
auto message = std::make_unique<LSPMessage>();
message->message = {{"method", "textDocument/didSave"},
{"params", {{"textDocument", {{"uri", this->uri}}}}}};
lsp->send(std::move(message));
if (lsp->allow_formatting) {
log("Formatting %s", this->filename.c_str());
json s_msg = {{"method", "textDocument/formatting"},
{"params",
{{"textDocument", {{"uri", editor->uri}}},
{{"textDocument", {{"uri", this->uri}}},
{"options",
{{"tabSize", 2},
{"insertSpaces", true},
{"trimTrailingWhitespace", true},
{"trimFinalNewlines", true}}}}}};
LSPPending *pending = new LSPPending();
pending->editor = editor;
pending->callback = [save_msg, version](Editor *editor,
const json &message) {
if (version != editor->lsp_version)
auto save_msg = std::make_unique<LSPMessage>();
save_msg->editor = this;
save_msg->message = s_msg;
save_msg->callback = [s_msg, version](const LSPMessage &msg) {
log("Formattin");
if (version != msg.editor->lsp_version)
return;
auto &edits = message["result"];
auto &edits = msg.message["result"];
if (edits.is_array()) {
std::vector<TextEdit> t_edits;
t_edits.reserve(edits.size());
@@ -112,19 +112,17 @@ void save_file(Editor *editor) {
t_edit.start.col = edit["range"]["start"]["character"];
t_edit.end.row = edit["range"]["end"]["line"];
t_edit.end.col = edit["range"]["end"]["character"];
utf8_normalize_edit(editor, &t_edit);
msg.editor->utf8_normalize_edit(&t_edit);
t_edits.push_back(t_edit);
}
apply_lsp_edits(editor, t_edits, false);
ensure_scroll(editor);
std::shared_lock lock(editor->knot_mtx);
uint32_t char_count = editor->root->char_count;
char *str = read(editor->root, 0, char_count);
msg.editor->apply_lsp_edits(t_edits, false);
msg.editor->ensure_cursor();
uint32_t char_count = msg.editor->root->char_count;
char *str = read(msg.editor->root, 0, char_count);
if (!str)
return;
lock.unlock();
std::ofstream out(editor->filename);
if (!editor->unix_eol) {
std::ofstream out(msg.editor->filename);
if (!msg.editor->unix_eol) {
for (uint32_t i = 0; i < char_count; ++i) {
if (str[i] == '\n')
out.put('\r');
@@ -135,10 +133,14 @@ void save_file(Editor *editor) {
}
out.close();
free(str);
lsp_send(editor->lsp, save_msg, nullptr);
auto save_msg = std::make_unique<LSPMessage>();
save_msg->editor = msg.editor;
save_msg->message = s_msg;
save_msg->callback = [](const LSPMessage &) {};
msg.editor->lsp.load()->send(std::move(save_msg));
}
};
lsp_send(editor->lsp, msg, pending);
lsp->send(std::move(save_msg));
}
}
}

View File

@@ -1,60 +1,59 @@
#include "editor/editor.h"
#include "editor/helpers.h"
#include "extentions/hover.h"
#include "io/sysio.h"
#include "main.h"
#include "utils/utils.h"
void handle_editor_event(Editor *editor, KeyEvent event) {
void Editor::handle_event(KeyEvent event) {
uint8_t old_mode = mode;
if (editor->hover_active)
editor->hover_active = false;
handle_mouse(editor, event);
if (this->hover_active)
this->hover_active = false;
if (event.key_type == KEY_SPECIAL) {
switch (event.special_modifier) {
case 0:
switch (event.special_key) {
case KEY_DOWN:
cursor_down(editor, 1);
this->cursor_down(1);
break;
case KEY_UP:
cursor_up(editor, 1);
this->cursor_up(1);
break;
case KEY_LEFT:
cursor_left(editor, 1);
this->cursor_left(1);
break;
case KEY_RIGHT:
cursor_right(editor, 1);
this->cursor_right(1);
break;
}
break;
case CNTRL:
switch (event.special_key) {
case KEY_DOWN:
cursor_down(editor, 5);
this->cursor_down(5);
break;
case KEY_UP:
cursor_up(editor, 5);
this->cursor_up(5);
break;
case KEY_LEFT:
cursor_prev_word(editor);
this->cursor_prev_word();
case KEY_RIGHT:
cursor_next_word(editor);
this->cursor_next_word();
break;
}
break;
case ALT:
switch (event.special_key) {
case KEY_DOWN:
move_line_down(editor);
this->move_line_down();
break;
case KEY_UP:
move_line_up(editor);
this->move_line_up();
break;
case KEY_LEFT:
cursor_left(editor, 8);
this->cursor_left(8);
break;
case KEY_RIGHT:
cursor_right(editor, 8);
this->cursor_right(8);
break;
}
break;
@@ -65,86 +64,86 @@ void handle_editor_event(Editor *editor, KeyEvent event) {
if (event.key_type == KEY_CHAR && event.len == 1) {
switch (event.c[0]) {
case 'u':
select_all(editor);
this->select_all();
break;
case CTRL('h'):
editor->hover.scroll(-1);
editor->hover_active = true;
static_cast<HoverBox *>(ui::hover_popup->tile.get())->scroll(-1);
this->hover_active = true;
break;
case CTRL('l'):
editor->hover.scroll(1);
editor->hover_active = true;
static_cast<HoverBox *>(ui::hover_popup->tile.get())->scroll(1);
this->hover_active = true;
break;
case 'h':
fetch_lsp_hover(editor);
this->fetch_lsp_hover();
break;
case 'a': {
mode = INSERT;
Coord start = editor->cursor;
cursor_right(editor, 1);
if (start.row != editor->cursor.row)
cursor_left(editor, 1);
Coord start = this->cursor;
this->cursor_right(1);
if (start.row != this->cursor.row)
this->cursor_left(1);
} break;
case 'i':
mode = INSERT;
break;
case 'n':
mode = JUMPER;
editor->jumper_set = true;
this->jumper_set = true;
break;
case 'm':
mode = JUMPER;
editor->jumper_set = false;
this->jumper_set = false;
break;
case 'N':
clear_hooks_at_line(editor, editor->cursor.row);
this->clear_hooks_at_line(this->cursor.row);
break;
case 's':
case 'v':
mode = SELECT;
editor->selection_active = true;
editor->selection = editor->cursor;
editor->selection_type = CHAR;
this->selection_active = true;
this->selection = this->cursor;
this->selection_type = CHAR;
break;
case ';':
case ':':
mode = RUNNER;
break;
case 0x7F:
cursor_left(editor, 1);
this->cursor_left(1);
break;
case ' ':
cursor_right(editor, 1);
this->cursor_right(1);
break;
case '\r':
case '\n':
cursor_down(editor, 1);
this->cursor_down(1);
break;
case '\\':
case '|':
cursor_up(editor, 1);
this->cursor_up(1);
break;
case CTRL('d'):
scroll_down(editor, 1);
ensure_cursor(editor);
this->scroll_down(1);
this->ensure_cursor();
break;
case CTRL('u'):
scroll_up(editor, 1);
ensure_cursor(editor);
this->scroll_up(1);
this->ensure_cursor();
break;
case '>':
case '.':
indent_current_line(editor);
this->indent_current_line();
break;
case '<':
case ',':
dedent_current_line(editor);
this->dedent_current_line();
break;
case CTRL('s'):
save_file(editor);
this->save();
break;
case 'p':
paste(editor);
this->paste();
break;
}
}
@@ -153,34 +152,34 @@ void handle_editor_event(Editor *editor, KeyEvent event) {
if (event.key_type == KEY_CHAR) {
if (event.len == 1) {
if (event.c[0] == '\t') {
editor->indents.insert_tab(editor->cursor);
this->indents.insert_tab(this->cursor);
} else if (event.c[0] == '\n' || event.c[0] == '\r') {
editor->indents.insert_new_line(editor->cursor);
this->indents.insert_new_line(this->cursor);
} else if (event.c[0] == CTRL('W')) {
delete_prev_word(editor);
this->delete_prev_word();
} else if (isprint((unsigned char)(event.c[0]))) {
insert_char(editor, event.c[0]);
this->insert_char(event.c[0]);
} else if (event.c[0] == 0x7F || event.c[0] == 0x08) {
backspace_edit(editor);
this->backspace_edit();
} else if (event.c[0] == 0x1B) {
normal_mode(editor);
this->normal_mode();
}
} else if (event.len > 1) {
edit_insert(editor, editor->cursor, event.c, event.len);
cursor_right(editor, 1);
this->edit_insert(this->cursor, event.c, event.len);
this->cursor_right(1);
}
} else if (event.key_type == KEY_SPECIAL &&
event.special_key == KEY_DELETE) {
switch (event.special_modifier) {
case 0:
edit_erase(editor, editor->cursor, 1);
this->edit_erase(this->cursor, 1);
break;
case CNTRL:
delete_next_word(editor);
this->delete_next_word();
break;
}
} else if (event.key_type == KEY_PASTE) {
insert_str(editor, event.c, event.len);
this->insert_str(event.c, event.len);
}
break;
case SELECT:
@@ -189,28 +188,28 @@ void handle_editor_event(Editor *editor, KeyEvent event) {
case 0x1B:
case 's':
case 'v':
editor->selection_active = false;
this->selection_active = false;
mode = NORMAL;
break;
case 'y':
copy(editor);
this->copy();
mode = NORMAL;
break;
case 'x':
cut(editor);
this->cut();
mode = NORMAL;
break;
case 'p':
paste(editor);
this->paste();
mode = NORMAL;
break;
case '<':
case ',':
dedent_selection(editor);
this->dedent_selection();
break;
case '>':
case '.':
indent_selection(editor);
this->indent_selection();
break;
}
}
@@ -218,25 +217,25 @@ void handle_editor_event(Editor *editor, KeyEvent event) {
case JUMPER:
if (event.key_type == KEY_CHAR && event.len == 1 &&
(event.c[0] >= '!' && event.c[0] <= '~')) {
if (editor->jumper_set) {
if (this->jumper_set) {
for (uint8_t i = 0; i < 94; i++)
if (editor->hooks[i] == editor->cursor.row + 1) {
editor->hooks[i] = 0;
if (this->hooks[i] == this->cursor.row + 1) {
this->hooks[i] = 0;
break;
}
editor->hooks[event.c[0] - '!'] = editor->cursor.row + 1;
this->hooks[event.c[0] - '!'] = this->cursor.row + 1;
} else {
uint32_t line = editor->hooks[event.c[0] - '!'] - 1;
uint32_t line = this->hooks[event.c[0] - '!'] - 1;
if (line > 0) {
editor->cursor = {line, 0};
editor->cursor_preffered = UINT32_MAX;
this->cursor = {line, 0};
this->cursor_preffered = UINT32_MAX;
}
}
}
mode = NORMAL;
break;
}
if (old_mode == mode || mode != INSERT)
handle_completion(editor, event);
ensure_scroll(editor);
// if (old_mode == mode || mode != INSERT)
// this->completion.handle(event);
this->ensure_scroll();
}

View File

@@ -1,86 +1,83 @@
#include "editor/helpers.h"
#include "editor/editor.h"
#include "extentions/hover.h"
#include "io/sysio.h"
#include "lsp/lsp.h"
#include "main.h"
#include "utils/utils.h"
#include <sys/types.h>
void cut(Editor *editor) {
if (ABS((int64_t)editor->cursor.row - (int64_t)editor->selection.row) >
1500) {
bar.log("Selection too large!");
void Editor::cut() {
if (ABS((int64_t)this->cursor.row - (int64_t)this->selection.row) > 1500) {
ui::bar.log("Selection too large!");
return;
}
if (mode != SELECT)
return;
Coord start;
uint32_t len;
char *text = get_selection(editor, &len, &start);
char *text = this->get_selection(&len, &start);
ruby_copy(text, len);
len = count_clusters(text, len, 0, len);
edit_erase(editor, start, len);
this->edit_erase(start, len);
free(text);
editor->selection_active = false;
this->selection_active = false;
}
void copy(Editor *editor) {
if (ABS((int64_t)editor->cursor.row - (int64_t)editor->selection.row) >
1500) {
bar.log("Selection too large!");
void Editor::copy() {
if (ABS((int64_t)this->cursor.row - (int64_t)this->selection.row) > 1500) {
ui::bar.log("Selection too large!");
return;
}
if (mode != SELECT)
return;
uint32_t len;
char *text = get_selection(editor, &len, nullptr);
char *text = this->get_selection(&len, nullptr);
ruby_copy(text, len);
free(text);
editor->selection_active = false;
this->selection_active = false;
}
void paste(Editor *editor) {
void Editor::paste() {
if (mode == NORMAL) {
std::string text = ruby_paste();
if (text.empty())
return;
insert_str(editor, (char *)text.c_str(), text.length());
this->insert_str((char *)text.c_str(), text.length());
} else if (mode == SELECT) {
std::string text = ruby_paste();
if (!text.empty()) {
Coord start, end;
selection_bounds(editor, &start, &end);
this->selection_bounds(&start, &end);
uint32_t start_byte =
line_to_byte(editor->root, start.row, nullptr) + start.col;
uint32_t end_byte =
line_to_byte(editor->root, end.row, nullptr) + end.col;
edit_erase(editor, start, end_byte - start_byte);
edit_insert(editor, editor->cursor, (char *)text.c_str(), text.length());
line_to_byte(this->root, start.row, nullptr) + start.col;
uint32_t end_byte = line_to_byte(this->root, end.row, nullptr) + end.col;
this->edit_erase(start, end_byte - start_byte);
this->edit_insert(this->cursor, (char *)text.c_str(), text.length());
}
editor->selection_active = false;
this->selection_active = false;
}
}
void insert_str(Editor *editor, char *c, uint32_t len) {
void Editor::insert_str(char *c, uint32_t len) {
if (c) {
edit_insert(editor, editor->cursor, c, len);
this->edit_insert(this->cursor, c, len);
uint32_t grapheme_len = count_clusters(c, len, 0, len);
cursor_right(editor, grapheme_len);
this->cursor_right(grapheme_len);
}
}
void indent_current_line(Editor *editor) {
Coord start = editor->cursor;
uint32_t delta = editor->indents.indent_line(editor->cursor.row);
editor->cursor.col = start.col + delta;
editor->cursor.row = start.row;
void Editor::indent_current_line() {
Coord start = this->cursor;
uint32_t delta = this->indents.indent_line(this->cursor.row);
this->cursor.col = start.col + delta;
this->cursor.row = start.row;
}
void dedent_current_line(Editor *editor) {
Coord start = editor->cursor;
uint32_t delta = editor->indents.dedent_line(editor->cursor.row);
editor->cursor.col = MAX((int64_t)start.col - delta, 0);
editor->cursor.row = start.row;
void Editor::dedent_current_line() {
Coord start = this->cursor;
uint32_t delta = this->indents.dedent_line(this->cursor.row);
this->cursor.col = MAX((int64_t)start.col - delta, 0);
this->cursor.row = start.row;
}
static void move_coord_by_delta(Coord &c, uint32_t row, int64_t delta) {
@@ -90,49 +87,49 @@ static void move_coord_by_delta(Coord &c, uint32_t row, int64_t delta) {
}
}
void indent_selection(Editor *editor) {
uint32_t top = MIN(editor->cursor.row, editor->selection.row);
uint32_t bot = MAX(editor->cursor.row, editor->selection.row);
void Editor::indent_selection() {
uint32_t top = MIN(this->cursor.row, this->selection.row);
uint32_t bot = MAX(this->cursor.row, this->selection.row);
if (bot - top > 1500) {
bar.log("Can't indent more than 1500 lines at once!");
ui::bar.log("Can't indent more than 1500 lines at once!");
return;
}
if (bot - top >= 2)
editor->indents.indent_block(top + 1, bot - 1);
uint32_t delta_top = editor->indents.indent_line(top);
this->indents.indent_block(top + 1, bot - 1);
uint32_t delta_top = this->indents.indent_line(top);
uint32_t delta_bot =
(bot == top) ? delta_top : editor->indents.indent_line(bot);
move_coord_by_delta(editor->cursor, top, delta_top);
move_coord_by_delta(editor->selection, top, delta_top);
(bot == top) ? delta_top : this->indents.indent_line(bot);
move_coord_by_delta(this->cursor, top, delta_top);
move_coord_by_delta(this->selection, top, delta_top);
if (bot != top) {
move_coord_by_delta(editor->cursor, bot, delta_bot);
move_coord_by_delta(editor->selection, bot, delta_bot);
move_coord_by_delta(this->cursor, bot, delta_bot);
move_coord_by_delta(this->selection, bot, delta_bot);
}
}
void dedent_selection(Editor *editor) {
uint32_t top = MIN(editor->cursor.row, editor->selection.row);
uint32_t bot = MAX(editor->cursor.row, editor->selection.row);
void Editor::dedent_selection() {
uint32_t top = MIN(this->cursor.row, this->selection.row);
uint32_t bot = MAX(this->cursor.row, this->selection.row);
if (bot - top > 1500) {
bar.log("Can't dedent more than 1500 lines at once!");
ui::bar.log("Can't dedent more than 1500 lines at once!");
return;
}
if (bot - top >= 2)
editor->indents.dedent_block(top + 1, bot - 1);
uint32_t delta_top = editor->indents.dedent_line(top);
this->indents.dedent_block(top + 1, bot - 1);
uint32_t delta_top = this->indents.dedent_line(top);
uint32_t delta_bot =
(bot == top) ? delta_top : editor->indents.dedent_line(bot);
move_coord_by_delta(editor->cursor, top, -(int64_t)delta_top);
move_coord_by_delta(editor->selection, top, -(int64_t)delta_top);
(bot == top) ? delta_top : this->indents.dedent_line(bot);
move_coord_by_delta(this->cursor, top, -(int64_t)delta_top);
move_coord_by_delta(this->selection, top, -(int64_t)delta_top);
if (bot != top) {
move_coord_by_delta(editor->cursor, bot, -(int64_t)delta_bot);
move_coord_by_delta(editor->selection, bot, -(int64_t)delta_bot);
move_coord_by_delta(this->cursor, bot, -(int64_t)delta_bot);
move_coord_by_delta(this->selection, bot, -(int64_t)delta_bot);
}
}
void insert_char(Editor *editor, char c) {
uint32_t col = editor->cursor.col;
LineIterator *it = begin_l_iter(editor->root, editor->cursor.row);
void Editor::insert_char(char c) {
uint32_t col = this->cursor.col;
LineIterator *it = begin_l_iter(this->root, this->cursor.row);
if (!it)
return;
uint32_t len;
@@ -148,7 +145,7 @@ void insert_char(Editor *editor, char c) {
if ((c == '}' && next == '}') || (c == ')' && next == ')') ||
(c == ']' && next == ']') || (c == '"' && next == '"') ||
(c == '\'' && next == '\'')) {
cursor_right(editor, 1);
this->cursor_right(1);
skip_insert = true;
}
}
@@ -175,16 +172,17 @@ void insert_char(Editor *editor, char c) {
}
if (closing) {
char pair[2] = {c, closing};
edit_insert(editor, editor->cursor, pair, 2);
cursor_right(editor, 1);
this->edit_insert(this->cursor, pair, 2);
this->cursor_right(1);
} else {
edit_insert(editor, editor->cursor, &c, 1);
cursor_right(editor, 1);
this->edit_insert(this->cursor, &c, 1);
this->cursor_right(1);
}
if (editor->lsp && editor->lsp->allow_formatting_on_type) {
for (char ch : editor->lsp->format_chars) {
auto lsp = this->lsp.load();
if (lsp && lsp->allow_formatting_on_type) {
for (char ch : lsp->format_chars) {
if (ch == c) {
LineIterator *it = begin_l_iter(editor->root, editor->cursor.row);
LineIterator *it = begin_l_iter(this->root, this->cursor.row);
if (!it)
return;
uint32_t len;
@@ -194,29 +192,27 @@ void insert_char(Editor *editor, char c) {
free(it);
return;
}
uint32_t col = utf8_offset_to_utf16(line, len, editor->cursor.col);
uint32_t col = utf8_offset_to_utf16(line, len, this->cursor.col);
free(it->buffer);
free(it);
int version = editor->lsp_version;
json message = {
{"jsonrpc", "2.0"},
int version = this->lsp_version;
auto message = std::make_unique<LSPMessage>();
message->message = {
{"method", "textDocument/onTypeFormatting"},
{"params",
{{"textDocument", {{"uri", editor->uri}}},
{"position",
{{"line", editor->cursor.row}, {"character", col}}},
{{"textDocument", {{"uri", this->uri}}},
{"position", {{"line", this->cursor.row}, {"character", col}}},
{"ch", std::string(1, c)},
{"options",
{{"tabSize", 2},
{"insertSpaces", true},
{"trimTrailingWhitespace", true},
{"trimFinalNewlines", true}}}}}};
LSPPending *pending = new LSPPending();
pending->editor = editor;
pending->callback = [version](Editor *editor, const json &message) {
if (version != editor->lsp_version)
message->editor = this;
message->callback = [version](const LSPMessage &message) {
if (version != message.editor->lsp_version)
return;
auto &edits = message["result"];
auto &edits = message.message["result"];
if (edits.is_array()) {
std::vector<TextEdit> t_edits;
t_edits.reserve(edits.size());
@@ -227,14 +223,14 @@ void insert_char(Editor *editor, char c) {
t_edit.start.col = edit["range"]["start"]["character"];
t_edit.end.row = edit["range"]["end"]["line"];
t_edit.end.col = edit["range"]["end"]["character"];
utf8_normalize_edit(editor, &t_edit);
message.editor->utf8_normalize_edit(&t_edit);
t_edits.push_back(t_edit);
}
apply_lsp_edits(editor, t_edits, false);
ensure_scroll(editor);
message.editor->apply_lsp_edits(t_edits, false);
message.editor->ensure_scroll();
}
};
lsp_send(editor->lsp, message, pending);
lsp->send(std::move(message));
break;
}
}
@@ -242,19 +238,19 @@ void insert_char(Editor *editor, char c) {
}
}
void normal_mode(Editor *editor) {
Coord prev_pos = editor->cursor;
void Editor::normal_mode() {
Coord prev_pos = this->cursor;
mode = NORMAL;
cursor_left(editor, 1);
if (prev_pos.row != editor->cursor.row)
cursor_right(editor, 1);
this->cursor_left(1);
if (prev_pos.row != this->cursor.row)
this->cursor_right(1);
}
void backspace_edit(Editor *editor) {
Coord prev_pos = editor->cursor;
void Editor::backspace_edit() {
Coord prev_pos = this->cursor;
if (prev_pos.col > 0)
prev_pos.col--;
LineIterator *it = begin_l_iter(editor->root, editor->cursor.row);
LineIterator *it = begin_l_iter(this->root, this->cursor.row);
if (!it)
return;
uint32_t len;
@@ -267,11 +263,11 @@ void backspace_edit(Editor *editor) {
if (len > 0 && line[len - 1] == '\n')
--len;
char prev_char = (prev_pos.col < len) ? line[prev_pos.col] : 0;
char next_char = (editor->cursor.col < len) ? line[editor->cursor.col] : 0;
char next_char = (this->cursor.col < len) ? line[this->cursor.col] : 0;
bool before_content = false;
if (editor->cursor.col > 0) {
if (this->cursor.col > 0) {
before_content = true;
for (uint32_t i = 0; i < editor->cursor.col; i++)
for (uint32_t i = 0; i < this->cursor.col; i++)
if (line[i] != ' ' && line[i] != '\t') {
before_content = false;
break;
@@ -280,7 +276,7 @@ void backspace_edit(Editor *editor) {
free(it->buffer);
free(it);
if (before_content) {
dedent_current_line(editor);
this->dedent_current_line();
return;
}
bool is_pair = (prev_char == '{' && next_char == '}') ||
@@ -289,65 +285,65 @@ void backspace_edit(Editor *editor) {
(prev_char == '"' && next_char == '"') ||
(prev_char == '\'' && next_char == '\'');
if (is_pair) {
edit_erase(editor, editor->cursor, 1);
edit_erase(editor, prev_pos, 1);
this->edit_erase(this->cursor, 1);
this->edit_erase(prev_pos, 1);
} else {
edit_erase(editor, editor->cursor, -1);
this->edit_erase(this->cursor, -1);
}
}
void delete_prev_word(Editor *editor) {
void Editor::delete_prev_word() {
uint32_t prev_col_byte, prev_col_cluster;
word_boundaries(editor, editor->cursor, &prev_col_byte, nullptr,
this->word_boundaries(this->cursor, &prev_col_byte, nullptr,
&prev_col_cluster, nullptr);
if (prev_col_byte == editor->cursor.col)
edit_erase(editor, editor->cursor, -1);
if (prev_col_byte == this->cursor.col)
this->edit_erase(this->cursor, -1);
else
edit_erase(editor, editor->cursor, -(int64_t)prev_col_cluster);
this->edit_erase(this->cursor, -(int64_t)prev_col_cluster);
}
void delete_next_word(Editor *editor) {
void Editor::delete_next_word() {
uint32_t next_col_byte, next_col_cluster;
word_boundaries(editor, editor->cursor, nullptr, &next_col_byte, nullptr,
this->word_boundaries(this->cursor, nullptr, &next_col_byte, nullptr,
&next_col_cluster);
if (next_col_byte == editor->cursor.col)
edit_erase(editor, editor->cursor, 1);
if (next_col_byte == this->cursor.col)
this->edit_erase(this->cursor, 1);
else
edit_erase(editor, editor->cursor, next_col_cluster);
this->edit_erase(this->cursor, next_col_cluster);
}
void clear_hooks_at_line(Editor *editor, uint32_t line) {
void Editor::clear_hooks_at_line(uint32_t line) {
for (uint8_t i = 0; i < 94; i++)
if (editor->hooks[i] == line + 1) {
editor->hooks[i] = 0;
if (this->hooks[i] == line + 1) {
this->hooks[i] = 0;
break;
}
}
void cursor_prev_word(Editor *editor) {
void Editor::cursor_prev_word() {
uint32_t prev_col;
word_boundaries(editor, editor->cursor, &prev_col, nullptr, nullptr, nullptr);
editor->cursor_preffered = UINT32_MAX;
if (prev_col == editor->cursor.col)
cursor_left(editor, 1);
word_boundaries(this->cursor, &prev_col, nullptr, nullptr, nullptr);
this->cursor_preffered = UINT32_MAX;
if (prev_col == this->cursor.col)
cursor_left(1);
else
editor->cursor = {editor->cursor.row, prev_col};
this->cursor = {this->cursor.row, prev_col};
}
void cursor_next_word(Editor *editor) {
void Editor::cursor_next_word() {
uint32_t next_col;
word_boundaries(editor, editor->cursor, nullptr, &next_col, nullptr, nullptr);
editor->cursor_preffered = UINT32_MAX;
if (next_col == editor->cursor.col)
cursor_right(editor, 1);
word_boundaries(this->cursor, nullptr, &next_col, nullptr, nullptr);
this->cursor_preffered = UINT32_MAX;
if (next_col == this->cursor.col)
this->cursor_right(1);
else
editor->cursor = {editor->cursor.row, next_col};
this->cursor = {this->cursor.row, next_col};
}
void select_all(Editor *editor) {
if (editor->root->line_count > 0) {
editor->cursor.row = editor->root->line_count - 1;
LineIterator *it = begin_l_iter(editor->root, editor->cursor.row);
void Editor::select_all() {
if (this->root->line_count > 0) {
this->cursor.row = this->root->line_count - 1;
LineIterator *it = begin_l_iter(this->root, this->cursor.row);
if (!it)
return;
uint32_t line_len;
@@ -359,18 +355,19 @@ void select_all(Editor *editor) {
line_len = count_clusters(line, line_len, 0, line_len);
free(it->buffer);
free(it);
editor->cursor.col = line_len;
editor->cursor_preffered = UINT32_MAX;
this->cursor.col = line_len;
this->cursor_preffered = UINT32_MAX;
mode = SELECT;
editor->selection_active = true;
editor->selection = {0, 0};
editor->selection_type = LINE;
this->selection_active = true;
this->selection = {0, 0};
this->selection_type = LINE;
}
}
void fetch_lsp_hover(Editor *editor) {
if (editor->lsp && editor->lsp->allow_hover) {
LineIterator *it = begin_l_iter(editor->root, editor->cursor.row);
void Editor::fetch_lsp_hover() {
auto lsp = this->lsp.load();
if (lsp && lsp->allow_hover) {
LineIterator *it = begin_l_iter(this->root, this->cursor.row);
uint32_t line_len;
char *line = next_line(it, &line_len);
if (!line) {
@@ -378,18 +375,18 @@ void fetch_lsp_hover(Editor *editor) {
free(it);
return;
}
uint32_t col = utf8_offset_to_utf16(line, line_len, editor->cursor.col);
uint32_t col = utf8_offset_to_utf16(line, line_len, this->cursor.col);
free(it->buffer);
free(it);
json hover_request = {
{"jsonrpc", "2.0"},
auto message = std::make_unique<LSPMessage>();
message->message = {
{"method", "textDocument/hover"},
{"params",
{{"textDocument", {{"uri", editor->uri}}},
{"position", {{"line", editor->cursor.row}, {"character", col}}}}}};
LSPPending *pending = new LSPPending();
pending->editor = editor;
pending->callback = [](Editor *editor, const json &hover) {
{{"textDocument", {{"uri", this->uri}}},
{"position", {{"line", this->cursor.row}, {"character", col}}}}}};
message->editor = this;
message->callback = [](const LSPMessage &message) {
auto &hover = message.message;
if (hover.contains("result") && !hover["result"].is_null()) {
auto &contents = hover["result"]["contents"];
std::string hover_text = "";
@@ -413,19 +410,21 @@ void fetch_lsp_hover(Editor *editor) {
hover_text += contents.get<std::string>();
}
if (!hover_text.empty()) {
editor->hover.clear();
editor->hover.text = clean_text(hover_text);
editor->hover.is_markup = is_markup;
editor->hover.render_first();
editor->hover_active = true;
auto hover_box = static_cast<HoverBox *>(ui::hover_popup->tile.get());
hover_box->clear();
hover_box->text = clean_text(hover_text);
hover_box->is_markup = is_markup;
message.editor->hover_active = true;
}
}
};
lsp_send(editor->lsp, hover_request, pending);
lsp->send(std::move(message));
}
}
void handle_mouse(Editor *editor, KeyEvent event) {
void Editor::handle_click(KeyEvent event, Coord size) {
focused_window = this;
this->size = size;
static std::chrono::steady_clock::time_point last_click_time =
std::chrono::steady_clock::now();
static uint32_t click_count = 0;
@@ -439,18 +438,18 @@ void handle_mouse(Editor *editor, KeyEvent event) {
case SCROLL:
switch (event.mouse_direction) {
case SCROLL_UP:
scroll_up(editor, 4);
ensure_cursor(editor);
this->scroll_up(4);
this->ensure_cursor();
break;
case SCROLL_DOWN:
scroll_down(editor, 4);
ensure_cursor(editor);
this->scroll_down(4);
this->ensure_cursor();
break;
case SCROLL_LEFT:
cursor_left(editor, 10);
this->cursor_left(10);
break;
case SCROLL_RIGHT:
cursor_right(editor, 10);
this->cursor_right(10);
break;
}
break;
@@ -463,35 +462,35 @@ void handle_mouse(Editor *editor, KeyEvent event) {
click_count = 1;
last_click_time = now;
last_click_pos = cur_pos;
Coord p = editor_hit_test(editor, event.mouse_x, event.mouse_y);
Coord p = this->click_coord(event.mouse_x, event.mouse_y);
if (p.row == UINT32_MAX && p.col == UINT32_MAX)
return;
editor->cursor_preffered = UINT32_MAX;
this->cursor_preffered = UINT32_MAX;
if (click_count == 1) {
editor->cursor = p;
editor->selection = p;
this->cursor = p;
this->selection = p;
if (mode == SELECT) {
mode = NORMAL;
editor->selection_active = false;
this->selection_active = false;
}
} else if (click_count == 2) {
uint32_t prev_col, next_col;
word_boundaries(editor, editor->cursor, &prev_col, &next_col, nullptr,
this->word_boundaries(this->cursor, &prev_col, &next_col, nullptr,
nullptr);
if (editor->cursor < editor->selection)
editor->cursor = {editor->cursor.row, prev_col};
if (this->cursor < this->selection)
this->cursor = {this->cursor.row, prev_col};
else
editor->cursor = {editor->cursor.row, next_col};
editor->cursor_preffered = UINT32_MAX;
editor->selection_type = WORD;
this->cursor = {this->cursor.row, next_col};
this->cursor_preffered = UINT32_MAX;
this->selection_type = WORD;
mode = SELECT;
editor->selection_active = true;
this->selection_active = true;
} else if (click_count >= 3) {
if (editor->cursor < editor->selection) {
editor->cursor = {p.row, 0};
if (this->cursor < this->selection) {
this->cursor = {p.row, 0};
} else {
uint32_t line_len;
LineIterator *it = begin_l_iter(editor->root, p.row);
LineIterator *it = begin_l_iter(this->root, p.row);
char *line = next_line(it, &line_len);
if (!line)
return;
@@ -499,44 +498,44 @@ void handle_mouse(Editor *editor, KeyEvent event) {
line_len--;
free(it->buffer);
free(it);
editor->cursor = {p.row, line_len};
this->cursor = {p.row, line_len};
}
editor->cursor_preffered = UINT32_MAX;
editor->selection_type = LINE;
this->cursor_preffered = UINT32_MAX;
this->selection_type = LINE;
mode = SELECT;
editor->selection_active = true;
this->selection_active = true;
click_count = 3;
}
}
break;
case DRAG:
if (event.mouse_button == LEFT_BTN) {
Coord p = editor_hit_test(editor, event.mouse_x, event.mouse_y);
Coord p = this->click_coord(event.mouse_x, event.mouse_y);
if (p.row == UINT32_MAX && p.col == UINT32_MAX)
return;
editor->cursor_preffered = UINT32_MAX;
this->cursor_preffered = UINT32_MAX;
mode = SELECT;
if (!editor->selection_active) {
editor->selection_active = true;
editor->selection_type = CHAR;
if (!this->selection_active) {
this->selection_active = true;
this->selection_type = CHAR;
}
uint32_t prev_col, next_col, line_len;
switch (editor->selection_type) {
switch (this->selection_type) {
case CHAR:
editor->cursor = p;
this->cursor = p;
break;
case WORD:
word_boundaries(editor, p, &prev_col, &next_col, nullptr, nullptr);
if (editor->cursor < editor->selection)
editor->cursor = {p.row, prev_col};
this->word_boundaries(p, &prev_col, &next_col, nullptr, nullptr);
if (this->cursor < this->selection)
this->cursor = {p.row, prev_col};
else
editor->cursor = {p.row, next_col};
this->cursor = {p.row, next_col};
break;
case LINE:
if (editor->cursor < editor->selection) {
editor->cursor = {p.row, 0};
if (this->cursor < this->selection) {
this->cursor = {p.row, 0};
} else {
LineIterator *it = begin_l_iter(editor->root, p.row);
LineIterator *it = begin_l_iter(this->root, p.row);
char *line = next_line(it, &line_len);
if (!line)
return;
@@ -544,7 +543,7 @@ void handle_mouse(Editor *editor, KeyEvent event) {
line_len--;
free(it->buffer);
free(it);
editor->cursor = {p.row, line_len};
this->cursor = {p.row, line_len};
}
break;
}
@@ -552,10 +551,10 @@ void handle_mouse(Editor *editor, KeyEvent event) {
break;
case RELEASE:
if (event.mouse_button == LEFT_BTN)
if (editor->cursor.row == editor->selection.row &&
editor->cursor.col == editor->selection.col) {
if (this->cursor.row == this->selection.row &&
this->cursor.col == this->selection.col) {
mode = NORMAL;
editor->selection_active = false;
this->selection_active = false;
}
break;
}

View File

@@ -68,7 +68,6 @@ uint32_t IndentationEngine::indent_real(char *line, uint32_t len) {
}
uint32_t IndentationEngine::indent_expected(uint32_t row) {
std::shared_lock lock(editor->knot_mtx);
uint32_t line_idx = row;
if (row == 0)
return 0;
@@ -109,7 +108,6 @@ uint32_t IndentationEngine::indent_expected(uint32_t row) {
}
uint32_t IndentationEngine::set_indent(uint32_t row, int64_t new_indent) {
std::shared_lock lock(editor->knot_mtx);
LineIterator *it = begin_l_iter(editor->root, row);
if (!it)
return 0;
@@ -122,7 +120,6 @@ uint32_t IndentationEngine::set_indent(uint32_t row, int64_t new_indent) {
}
if (len > 0 && line[len - 1] == '\n')
--len;
lock.unlock();
if (new_indent <= 0)
new_indent = 0;
uint32_t ws_len = 0;
@@ -135,14 +132,13 @@ uint32_t IndentationEngine::set_indent(uint32_t row, int64_t new_indent) {
new_ws.assign(new_indent * indent, ' ');
Coord start = {row, 0};
Coord end = {row, ws_len};
edit_replace(editor, start, end, new_ws.c_str(), new_ws.length());
editor->edit_replace(start, end, new_ws.c_str(), new_ws.length());
free(it->buffer);
free(it);
return len - ws_len + (new_indent * indent);
}
uint32_t IndentationEngine::indent_line(uint32_t row) {
std::shared_lock lock(editor->knot_mtx);
LineIterator *it = begin_l_iter(editor->root, row);
if (!it)
return 0;
@@ -153,7 +149,6 @@ uint32_t IndentationEngine::indent_line(uint32_t row) {
free(it);
return 0;
}
lock.unlock();
if (len > 0 && line[len - 1] == '\n')
--len;
uint32_t new_indent = indent_real(line, len) + 1;
@@ -165,7 +160,7 @@ uint32_t IndentationEngine::indent_line(uint32_t row) {
new_ws.assign(new_indent, '\t');
else
new_ws.assign(new_indent * indent, ' ');
edit_replace(editor, {row, 0}, {row, ws_len}, new_ws.c_str(),
editor->edit_replace({row, 0}, {row, ws_len}, new_ws.c_str(),
new_indent * indent);
free(it->buffer);
free(it);
@@ -173,7 +168,6 @@ uint32_t IndentationEngine::indent_line(uint32_t row) {
}
uint32_t IndentationEngine::dedent_line(uint32_t row) {
std::shared_lock lock(editor->knot_mtx);
LineIterator *it = begin_l_iter(editor->root, row);
if (!it)
return 0;
@@ -184,7 +178,6 @@ uint32_t IndentationEngine::dedent_line(uint32_t row) {
free(it);
return 0;
}
lock.unlock();
if (len > 0 && line[len - 1] == '\n')
--len;
int64_t new_indent = (int64_t)indent_real(line, len) - 1;
@@ -198,7 +191,7 @@ uint32_t IndentationEngine::dedent_line(uint32_t row) {
new_ws.assign(new_indent, '\t');
else
new_ws.assign(new_indent * indent, ' ');
edit_replace(editor, {row, 0}, {row, ws_len}, new_ws.c_str(),
editor->edit_replace({row, 0}, {row, ws_len}, new_ws.c_str(),
new_indent * indent);
free(it->buffer);
free(it);
@@ -262,12 +255,11 @@ void IndentationEngine::indent_block(uint32_t start_row, uint32_t end_row,
}
}
free(block);
edit_replace(editor, {start_row, 0}, {end_row, end_len}, out, out_len);
editor->edit_replace({start_row, 0}, {end_row, end_len}, out, out_len);
free(out);
}
void IndentationEngine::insert_tab(Coord cursor) {
std::shared_lock lock(editor->knot_mtx);
LineIterator *it = begin_l_iter(editor->root, cursor.row);
if (!it)
return;
@@ -278,7 +270,6 @@ void IndentationEngine::insert_tab(Coord cursor) {
free(it);
return;
}
lock.unlock();
if (len > 0 && line[len - 1] == '\n')
--len;
uint32_t ws_len = 0;
@@ -295,13 +286,12 @@ void IndentationEngine::insert_tab(Coord cursor) {
}
free(it->buffer);
free(it);
edit_insert(editor, cursor, (char *)insert.c_str(), insert.size());
editor->edit_insert(cursor, (char *)insert.c_str(), insert.size());
editor->cursor.col += insert.size();
}
void IndentationEngine::insert_new_line(Coord cursor) {
std::string formatted;
std::shared_lock lock(editor->knot_mtx);
LineIterator *it = begin_l_iter(editor->root, cursor.row);
if (!it)
return;
@@ -312,7 +302,6 @@ void IndentationEngine::insert_new_line(Coord cursor) {
free(it);
return;
}
lock.unlock();
if (len > 0 && line[len - 1] == '\n')
--len;
if (cursor.col >= len) {
@@ -332,7 +321,6 @@ void IndentationEngine::insert_new_line(Coord cursor) {
cursor.row, (int64_t)indent_expected(cursor.row) - (int64_t)1);
break;
}
lock.lock();
free(it->buffer);
free(it);
it = begin_l_iter(editor->root, cursor.row);
@@ -346,7 +334,6 @@ void IndentationEngine::insert_new_line(Coord cursor) {
}
if (len > 0 && line[len - 1] == '\n')
--len;
lock.unlock();
}
std::string ending = trim(std::string(line + cursor.col, len - cursor.col));
std::string before = trim(std::string(line, cursor.col));
@@ -407,15 +394,16 @@ void IndentationEngine::insert_new_line(Coord cursor) {
: std::string(c_indent * indent, ' ')) +
ending;
Coord new_cursor = {cursor.row + 1, (uint32_t)c_indent * indent};
edit_replace(editor, cursor, {cursor.row, len}, formatted.data(),
editor->edit_replace(cursor, {cursor.row, len}, formatted.data(),
formatted.size());
editor->cursor = new_cursor;
editor->cursor_preffered = UINT32_MAX;
free(it->buffer);
free(it);
if (!editor->lsp || !editor->lsp->allow_formatting_on_type)
auto lsp = editor->lsp.load();
if (!lsp || !lsp->allow_formatting_on_type)
return;
for (char ch : editor->lsp->format_chars) {
for (char ch : lsp->format_chars) {
if (ch == '\n') {
LineIterator *it = begin_l_iter(editor->root, editor->cursor.row);
if (!it)
@@ -431,7 +419,9 @@ void IndentationEngine::insert_new_line(Coord cursor) {
free(it->buffer);
free(it);
int version = editor->lsp_version;
json message = {
auto message = std::make_unique<LSPMessage>();
message->editor = editor;
message->message = {
{"jsonrpc", "2.0"},
{"method", "textDocument/onTypeFormatting"},
{"params",
@@ -443,12 +433,10 @@ void IndentationEngine::insert_new_line(Coord cursor) {
{"insertSpaces", true},
{"trimTrailingWhitespace", true},
{"trimFinalNewlines", true}}}}}};
LSPPending *pending = new LSPPending();
pending->editor = editor;
pending->callback = [version](Editor *editor, const json &message) {
if (version != editor->lsp_version)
message->callback = [version](const LSPMessage &message) {
if (version != message.editor->lsp_version)
return;
auto &edits = message["result"];
auto &edits = message.message["result"];
if (edits.is_array()) {
std::vector<TextEdit> t_edits;
t_edits.reserve(edits.size());
@@ -459,14 +447,14 @@ void IndentationEngine::insert_new_line(Coord cursor) {
t_edit.start.col = edit["range"]["start"]["character"];
t_edit.end.row = edit["range"]["end"]["line"];
t_edit.end.col = edit["range"]["end"]["character"];
utf8_normalize_edit(editor, &t_edit);
message.editor->utf8_normalize_edit(&t_edit);
t_edits.push_back(t_edit);
}
apply_lsp_edits(editor, t_edits, false);
ensure_scroll(editor);
message.editor->apply_lsp_edits(t_edits, false);
message.editor->ensure_scroll();
}
};
lsp_send(editor->lsp, message, pending);
lsp->send(std::move(message));
break;
}
}

View File

@@ -2,52 +2,47 @@
#include "editor/editor.h"
#include "utils/utils.h"
void apply_lsp_edits(Editor *editor, std::vector<TextEdit> edits, bool move) {
void Editor::apply_lsp_edits(std::vector<TextEdit> edits, bool move) {
if (!edits.size())
return;
TextEdit first = edits[0];
Coord cursor = editor->cursor;
Coord cursor = this->cursor;
std::sort(
edits.begin(), edits.end(),
[](const TextEdit &a, const TextEdit &b) { return a.start > b.start; });
for (const auto &edit : edits)
edit_replace(editor, edit.start, edit.end, edit.text.c_str(),
this->edit_replace(edit.start, edit.end, edit.text.c_str(),
edit.text.size());
if (move) {
std::shared_lock lock(editor->knot_mtx);
editor->cursor = first.start;
editor->cursor =
move_right(editor, editor->cursor,
count_clusters(first.text.c_str(), first.text.size(), 0,
this->cursor = first.start;
this->cursor = this->move_right(
this->cursor, count_clusters(first.text.c_str(), first.text.size(), 0,
first.text.size()));
} else {
if (cursor.row >= editor->root->line_count) {
editor->cursor.row = editor->root->line_count - 1;
editor->cursor.col = 0;
if (cursor.row >= this->root->line_count) {
this->cursor.row = this->root->line_count - 1;
this->cursor.col = 0;
} else {
std::shared_lock lock(editor->knot_mtx);
uint32_t len;
line_to_byte(editor->root, cursor.row, &len);
line_to_byte(this->root, cursor.row, &len);
len--;
editor->cursor.row = cursor.row;
editor->cursor.col = cursor.col < len ? cursor.col : len;
this->cursor.row = cursor.row;
this->cursor.col = cursor.col < len ? cursor.col : len;
}
}
}
void editor_lsp_handle(Editor *editor, json msg) {
void Editor::lsp_handle(json msg) {
if (msg.contains("method") &&
msg["method"] == "textDocument/publishDiagnostics") {
std::unique_lock lock(editor->v_mtx);
editor->warnings.clear();
this->warnings.clear();
json diagnostics = msg["params"]["diagnostics"];
for (size_t i = 0; i < diagnostics.size(); i++) {
json d = diagnostics[i];
VWarn w;
w.line = d["range"]["start"]["line"];
w.start = d["range"]["start"]["character"];
std::shared_lock lock(editor->knot_mtx);
LineIterator *it = begin_l_iter(editor->root, w.line);
LineIterator *it = begin_l_iter(this->root, w.line);
if (!it)
continue;
uint32_t len;
@@ -59,7 +54,6 @@ void editor_lsp_handle(Editor *editor, json msg) {
}
if (len > 0 && line[len - 1] == '\n')
--len;
lock.unlock();
w.start = utf16_offset_to_utf8(line, len, w.start);
uint32_t end = d["range"]["end"]["character"];
if (d["range"]["end"]["line"] == w.line)
@@ -102,9 +96,9 @@ void editor_lsp_handle(Editor *editor, json msg) {
w.type = 1;
if (d.contains("severity"))
w.type = d["severity"].get<int>();
editor->warnings.push_back(w);
this->warnings.push_back(w);
}
std::sort(editor->warnings.begin(), editor->warnings.end());
editor->warnings_dirty = true;
std::sort(this->warnings.begin(), this->warnings.end());
this->warnings_dirty = true;
}
}

View File

@@ -1,120 +1,108 @@
#include "editor/editor.h"
#include "main.h"
void move_line_up(Editor *editor) {
if (!editor || !editor->root || editor->cursor.row == 0)
void Editor::move_line_up() {
if (!this->root || this->cursor.row == 0)
return;
if (mode == NORMAL || mode == INSERT) {
uint32_t line_len, line_cluster_len;
std::shared_lock lock(editor->knot_mtx);
LineIterator *it = begin_l_iter(editor->root, editor->cursor.row);
LineIterator *it = begin_l_iter(this->root, this->cursor.row);
char *line = next_line(it, &line_len);
if (!line) {
lock.unlock();
if (!line)
return;
}
if (line_len > 0 && line[line_len - 1] == '\n')
line_len--;
line_cluster_len = count_clusters(line, line_len, 0, line_len);
uint32_t target_row = editor->cursor.row - 1;
uint32_t up_by = editor->cursor.row - target_row;
uint32_t target_row = this->cursor.row - 1;
uint32_t up_by = this->cursor.row - target_row;
if (up_by > 1)
up_by--;
lock.unlock();
Coord cursor = editor->cursor;
edit_erase(editor, {cursor.row, 0}, line_cluster_len);
edit_erase(editor, {cursor.row, 0}, -1);
edit_insert(editor, {cursor.row - up_by, 0}, (char *)"\n", 1);
edit_insert(editor, {cursor.row - up_by, 0}, line, line_len);
Coord cursor = this->cursor;
edit_erase({cursor.row, 0}, line_cluster_len);
edit_erase({cursor.row, 0}, -1);
edit_insert({cursor.row - up_by, 0}, (char *)"\n", 1);
edit_insert({cursor.row - up_by, 0}, line, line_len);
free(it->buffer);
free(it);
editor->cursor = {cursor.row - up_by, cursor.col};
this->cursor = {cursor.row - up_by, cursor.col};
} else if (mode == SELECT) {
uint32_t start_row = MIN(editor->cursor.row, editor->selection.row);
uint32_t end_row = MAX(editor->cursor.row, editor->selection.row);
uint32_t start_byte = line_to_byte(editor->root, start_row, nullptr);
uint32_t end_byte = line_to_byte(editor->root, end_row + 1, nullptr);
char *selected_text = read(editor->root, start_byte, end_byte - start_byte);
uint32_t start_row = MIN(this->cursor.row, this->selection.row);
uint32_t end_row = MAX(this->cursor.row, this->selection.row);
uint32_t start_byte = line_to_byte(this->root, start_row, nullptr);
uint32_t end_byte = line_to_byte(this->root, end_row + 1, nullptr);
char *selected_text = read(this->root, start_byte, end_byte - start_byte);
if (!selected_text)
return;
uint32_t selected_len = count_clusters(selected_text, end_byte - start_byte,
0, end_byte - start_byte);
Coord cursor = editor->cursor;
Coord selection = editor->selection;
edit_erase(editor, {start_row, 0}, selected_len);
edit_insert(editor, {start_row - 1, 0}, selected_text,
end_byte - start_byte);
Coord cursor = this->cursor;
Coord selection = this->selection;
edit_erase({start_row, 0}, selected_len);
edit_insert({start_row - 1, 0}, selected_text, end_byte - start_byte);
free(selected_text);
editor->cursor = {cursor.row - 1, cursor.col};
editor->selection = {selection.row - 1, selection.col};
this->cursor = {cursor.row - 1, cursor.col};
this->selection = {selection.row - 1, selection.col};
}
}
void move_line_down(Editor *editor) {
if (!editor || !editor->root)
void Editor::move_line_down() {
if (!this->root)
return;
if (mode == NORMAL || mode == INSERT) {
if (editor->cursor.row >= editor->root->line_count - 1)
if (this->cursor.row >= this->root->line_count - 1)
return;
uint32_t line_len, line_cluster_len;
std::shared_lock lock(editor->knot_mtx);
LineIterator *it = begin_l_iter(editor->root, editor->cursor.row);
LineIterator *it = begin_l_iter(this->root, this->cursor.row);
char *line = next_line(it, &line_len);
if (!line) {
lock.unlock();
if (!line)
return;
}
if (line_len && line[line_len - 1] == '\n')
line_len--;
line_cluster_len = count_clusters(line, line_len, 0, line_len);
uint32_t target_row = editor->cursor.row + 1;
if (target_row >= editor->root->line_count) {
free(line);
lock.unlock();
uint32_t target_row = this->cursor.row + 1;
if (target_row >= this->root->line_count) {
free(it->buffer);
free(it);
return;
}
uint32_t down_by = target_row - editor->cursor.row;
uint32_t down_by = target_row - this->cursor.row;
if (down_by > 1)
down_by--;
uint32_t ln;
line_to_byte(editor->root, editor->cursor.row + down_by - 1, &ln);
lock.unlock();
Coord cursor = editor->cursor;
edit_erase(editor, {cursor.row, 0}, line_cluster_len);
edit_erase(editor, {cursor.row, 0}, -1);
edit_insert(editor, {cursor.row + down_by, 0}, (char *)"\n", 1);
edit_insert(editor, {cursor.row + down_by, 0}, line, line_len);
line_to_byte(this->root, this->cursor.row + down_by - 1, &ln);
Coord cursor = this->cursor;
edit_erase({cursor.row, 0}, line_cluster_len);
edit_erase({cursor.row, 0}, -1);
edit_insert({cursor.row + down_by, 0}, (char *)"\n", 1);
edit_insert({cursor.row + down_by, 0}, line, line_len);
free(it->buffer);
free(it);
editor->cursor = {cursor.row + down_by, cursor.col};
this->cursor = {cursor.row + down_by, cursor.col};
} else if (mode == SELECT) {
if (editor->cursor.row >= editor->root->line_count - 1 ||
editor->selection.row >= editor->root->line_count - 1)
if (this->cursor.row >= this->root->line_count - 1 ||
this->selection.row >= this->root->line_count - 1)
return;
std::shared_lock lock(editor->knot_mtx);
uint32_t start_row = MIN(editor->cursor.row, editor->selection.row);
uint32_t end_row = MAX(editor->cursor.row, editor->selection.row);
uint32_t start_row = MIN(this->cursor.row, this->selection.row);
uint32_t end_row = MAX(this->cursor.row, this->selection.row);
uint32_t target_row = end_row + 1;
if (target_row >= editor->root->line_count)
if (target_row >= this->root->line_count)
return;
uint32_t down_by = target_row - end_row;
if (down_by > 1)
down_by--;
uint32_t start_byte = line_to_byte(editor->root, start_row, nullptr);
uint32_t end_byte = line_to_byte(editor->root, end_row + 1, nullptr);
char *selected_text = read(editor->root, start_byte, end_byte - start_byte);
lock.unlock();
uint32_t start_byte = line_to_byte(this->root, start_row, nullptr);
uint32_t end_byte = line_to_byte(this->root, end_row + 1, nullptr);
char *selected_text = read(this->root, start_byte, end_byte - start_byte);
if (!selected_text)
return;
uint32_t selected_len = count_clusters(selected_text, end_byte - start_byte,
0, end_byte - start_byte);
Coord cursor = editor->cursor;
Coord selection = editor->selection;
edit_erase(editor, {start_row, 0}, selected_len);
edit_insert(editor, {start_row + down_by, 0}, selected_text,
end_byte - start_byte);
Coord cursor = this->cursor;
Coord selection = this->selection;
edit_erase({start_row, 0}, selected_len);
edit_insert({start_row + down_by, 0}, selected_text, end_byte - start_byte);
free(selected_text);
editor->cursor = {cursor.row + down_by, cursor.col};
editor->selection = {selection.row + down_by, selection.col};
this->cursor = {cursor.row + down_by, cursor.col};
this->selection = {selection.row + down_by, selection.col};
}
}

View File

@@ -3,27 +3,24 @@
#include "main.h"
#include "syntax/decl.h"
#include "syntax/parser.h"
#include <cstdint>
void render_editor(Editor *editor) {
void Editor::render(std::vector<ScreenCell> &buffer, Coord size, Coord pos) {
this->size = size;
uint32_t sel_start = 0, sel_end = 0;
std::shared_lock knot_lock(editor->knot_mtx);
uint32_t numlen =
EXTRA_META + static_cast<int>(std::log10(editor->root->line_count + 1));
uint32_t render_width = editor->size.col - numlen;
uint32_t render_x = editor->position.col + numlen + 1;
EXTRA_META + static_cast<int>(std::log10(this->root->line_count + 1));
uint32_t render_width = size.col - numlen;
uint32_t render_x = pos.col + numlen + 1;
std::vector<std::pair<uint32_t, char>> v;
for (size_t i = 0; i < 94; ++i)
if (editor->hooks[i] != 0)
v.push_back({editor->hooks[i], '!' + i});
if (this->hooks[i] != 0)
v.push_back({this->hooks[i], '!' + i});
std::sort(v.begin(), v.end());
auto hook_it = v.begin();
while (hook_it != v.end() && hook_it->first <= editor->scroll.row)
while (hook_it != v.end() && hook_it->first <= this->scroll.row)
++hook_it;
std::unique_lock warn_lock(editor->v_mtx);
auto warn_it = editor->warnings.begin();
while (warn_it != editor->warnings.end() &&
warn_it->line < editor->scroll.row)
auto warn_it = this->warnings.begin();
while (warn_it != this->warnings.end() && warn_it->line < this->scroll.row)
++warn_it;
LineData *line_data = nullptr;
auto get_type = [&](uint32_t col) {
@@ -34,40 +31,54 @@ void render_editor(Editor *editor) {
return (int)token.type;
return 0;
};
if (editor->selection_active) {
Coord screen = get_size();
auto update = [&](uint32_t row, uint32_t col, std::string text, uint32_t fg,
uint32_t bg, uint8_t flags, uint32_t u_color,
uint32_t width) {
if (row >= screen.row || col >= screen.col)
return;
ScreenCell &c = buffer[row * screen.col + col];
c.utf8 = text;
c.width = width;
c.fg = fg;
c.bg = bg;
c.flags = flags;
c.ul_color = u_color;
};
if (this->selection_active) {
Coord start, end;
if (editor->cursor >= editor->selection) {
if (this->cursor >= this->selection) {
uint32_t prev_col, next_col;
switch (editor->selection_type) {
switch (this->selection_type) {
case CHAR:
start = editor->selection;
end = move_right(editor, editor->cursor, 1);
start = this->selection;
end = this->move_right(this->cursor, 1);
break;
case WORD:
word_boundaries(editor, editor->selection, &prev_col, &next_col,
nullptr, nullptr);
start = {editor->selection.row, prev_col};
end = editor->cursor;
this->word_boundaries(this->selection, &prev_col, &next_col, nullptr,
nullptr);
start = {this->selection.row, prev_col};
end = this->cursor;
break;
case LINE:
start = {editor->selection.row, 0};
end = editor->cursor;
start = {this->selection.row, 0};
end = this->cursor;
break;
}
} else {
start = editor->cursor;
start = this->cursor;
uint32_t prev_col, next_col, line_len;
switch (editor->selection_type) {
switch (this->selection_type) {
case CHAR:
end = move_right(editor, editor->selection, 1);
end = this->move_right(this->selection, 1);
break;
case WORD:
word_boundaries(editor, editor->selection, &prev_col, &next_col,
nullptr, nullptr);
end = {editor->selection.row, next_col};
this->word_boundaries(this->selection, &prev_col, &next_col, nullptr,
nullptr);
end = {this->selection.row, next_col};
break;
case LINE:
LineIterator *it = begin_l_iter(editor->root, editor->selection.row);
LineIterator *it = begin_l_iter(this->root, this->selection.row);
char *line = next_line(it, &line_len);
if (!line)
return;
@@ -75,40 +86,40 @@ void render_editor(Editor *editor) {
line_len--;
free(it->buffer);
free(it);
end = {editor->selection.row, line_len};
end = {this->selection.row, line_len};
break;
}
}
sel_start = line_to_byte(editor->root, start.row, nullptr) + start.col;
sel_end = line_to_byte(editor->root, end.row, nullptr) + end.col;
sel_start = line_to_byte(this->root, start.row, nullptr) + start.col;
sel_end = line_to_byte(this->root, end.row, nullptr) + end.col;
}
Coord cursor = {UINT32_MAX, UINT32_MAX};
uint32_t line_index = editor->scroll.row;
LineIterator *it = begin_l_iter(editor->root, line_index);
uint32_t line_index = this->scroll.row;
LineIterator *it = begin_l_iter(this->root, line_index);
if (!it)
return;
uint32_t prev_col, next_col;
std::string word;
word_boundaries_exclusive(editor, editor->cursor, &prev_col, &next_col);
this->word_boundaries_exclusive(this->cursor, &prev_col, &next_col);
if (next_col - prev_col > 0 && next_col - prev_col < 256 - 4) {
uint32_t offset = line_to_byte(editor->root, editor->cursor.row, nullptr);
char *word_ptr = read(editor->root, offset + prev_col, next_col - prev_col);
uint32_t offset = line_to_byte(this->root, this->cursor.row, nullptr);
char *word_ptr = read(this->root, offset + prev_col, next_col - prev_col);
if (word_ptr) {
word = std::string(word_ptr, next_col - prev_col);
free(word_ptr);
}
}
editor->extra_hl.render(editor->root, line_index, word, editor->is_css_color);
this->extra_hl.render(this->root, line_index, word, this->is_css_color);
uint32_t rendered_rows = 0;
uint32_t global_byte_offset = line_to_byte(editor->root, line_index, nullptr);
while (rendered_rows < editor->size.row) {
uint32_t global_byte_offset = line_to_byte(this->root, line_index, nullptr);
while (rendered_rows < this->size.row) {
uint32_t line_len;
char *line = next_line(it, &line_len);
if (editor->parser) {
if (this->parser) {
if (line_data)
line_data = editor->parser->line_tree.next();
line_data = this->parser->line_tree.next();
else
line_data = editor->parser->line_tree.start_iter(line_index);
line_data = this->parser->line_tree.start_iter(line_index);
}
if (!line)
break;
@@ -123,53 +134,50 @@ void render_editor(Editor *editor) {
(line[content_start] == ' ' || line[content_start] == '\t'))
content_start++;
std::vector<VWarn> line_warnings;
while (warn_it != editor->warnings.end() && warn_it->line == line_index) {
while (warn_it != this->warnings.end() && warn_it->line == line_index) {
line_warnings.push_back(*warn_it);
++warn_it;
}
uint32_t current_byte_offset = 0;
if (rendered_rows == 0)
current_byte_offset += editor->scroll.col;
while (current_byte_offset < line_len && rendered_rows < editor->size.row) {
uint32_t color = editor->cursor.row == line_index ? 0x222222 : 0;
current_byte_offset += this->scroll.col;
while (current_byte_offset < line_len && rendered_rows < this->size.row) {
uint32_t color = this->cursor.row == line_index ? 0x222222 : 0;
if (current_byte_offset == 0 || rendered_rows == 0) {
const char *hook = nullptr;
const char *hook = "";
char h[2] = {0, 0};
if (hook_it != v.end() && hook_it->first == line_index + 1) {
h[0] = hook_it->second;
hook = h;
hook_it++;
}
update(editor->position.row + rendered_rows, editor->position.col, hook,
0xAAAAAA, 0, 0);
update(pos.row + rendered_rows, pos.col, hook, 0xAAAAAA, 0, 0, 0, 1);
char buf[16];
int len = snprintf(buf, sizeof(buf), "%*u ", numlen, line_index + 1);
uint32_t num_color =
editor->cursor.row == line_index ? 0xFFFFFF : 0x555555;
this->cursor.row == line_index ? 0xFFFFFF : 0x555555;
for (int i = 0; i < len; i++)
update(editor->position.row + rendered_rows, editor->position.col + i,
(char[2]){buf[i], 0}, num_color, 0, 0);
update(pos.row + rendered_rows, pos.col + i, (char[2]){buf[i], 0},
num_color, 0, 0, 0, 1);
} else {
for (uint32_t i = 0; i < numlen + 1; i++)
update(editor->position.row + rendered_rows, editor->position.col + i,
" ", 0, 0, 0);
update(pos.row + rendered_rows, pos.col + i, " ", 0, 0, 0, 0, 1);
}
uint32_t col = 0;
uint32_t local_render_offset = 0;
uint32_t line_left = line_len - current_byte_offset;
while (line_left > 0 && col < render_width) {
if (line_index == editor->cursor.row &&
editor->cursor.col == (current_byte_offset + local_render_offset)) {
cursor.row = editor->position.row + rendered_rows;
if (line_index == this->cursor.row &&
this->cursor.col == (current_byte_offset + local_render_offset)) {
cursor.row = pos.row + rendered_rows;
cursor.col = render_x + col;
}
uint32_t absolute_byte_pos =
global_byte_offset + current_byte_offset + local_render_offset;
const Highlight *hl = nullptr;
if (editor->parser)
if (this->parser)
hl = &highlights[get_type(current_byte_offset + local_render_offset)];
std::optional<std::pair<uint32_t, uint32_t>> extra =
editor->extra_hl.get(
std::optional<std::pair<uint32_t, uint32_t>> extra = this->extra_hl.get(
{line_index, current_byte_offset + local_render_offset});
uint32_t fg = extra && extra->second != UINT32_MAX
? extra->first
@@ -181,7 +189,7 @@ void render_editor(Editor *editor) {
(hl ? hl->flags : 0) |
(extra ? (extra->second != UINT32_MAX ? CF_BOLD : CF_UNDERLINE)
: 0);
if (editor->selection_active && absolute_byte_pos >= sel_start &&
if (this->selection_active && absolute_byte_pos >= sel_start &&
absolute_byte_pos < sel_end)
bg = bg | 0x555555;
uint32_t u_color = 0;
@@ -217,40 +225,39 @@ void render_editor(Editor *editor) {
break;
if (current_byte_offset + local_render_offset >= content_start &&
current_byte_offset + local_render_offset < content_end) {
update(editor->position.row + rendered_rows, render_x + col,
cluster.c_str(), fg, bg | color, fl, u_color);
update(pos.row + rendered_rows, render_x + col, cluster.c_str(), fg,
bg | color, fl, u_color, width);
} else {
if (cluster[0] == ' ') {
update(editor->position.row + rendered_rows, render_x + col, "·",
0x282828, bg | color, fl, u_color);
update(pos.row + rendered_rows, render_x + col, "·", 0x282828,
bg | color, fl, u_color, 1);
} else {
update(editor->position.row + rendered_rows, render_x + col, "-> ",
0x282828, bg | color, (fl & ~CF_BOLD) | CF_ITALIC, u_color);
update(pos.row + rendered_rows, render_x + col, "-> ", 0x282828,
bg | color, (fl & ~CF_BOLD) | CF_ITALIC, u_color, 4);
}
}
local_render_offset += cluster_len;
line_left -= cluster_len;
col += width;
while (width-- > 1)
update(editor->position.row + rendered_rows, render_x + col - width,
"\x1b", fg, bg | color, fl);
update(pos.row + rendered_rows, render_x + col - width, "\x1b", fg,
bg | color, fl, u_color, 0);
}
if (line_index == editor->cursor.row &&
editor->cursor.col == (current_byte_offset + local_render_offset)) {
cursor.row = editor->position.row + rendered_rows;
if (line_index == this->cursor.row &&
this->cursor.col == (current_byte_offset + local_render_offset)) {
cursor.row = pos.row + rendered_rows;
cursor.col = render_x + col;
}
if (editor->selection_active &&
if (this->selection_active &&
global_byte_offset + line_len + 1 > sel_start &&
global_byte_offset + line_len + 1 <= sel_end && col < render_width) {
update(editor->position.row + rendered_rows, render_x + col, " ", 0,
0x555555 | color, 0);
update(pos.row + rendered_rows, render_x + col, " ", 0,
0x555555 | color, 0, 0, 1);
col++;
}
if (!line_warnings.empty() && line_left == 0) {
VWarn warn = line_warnings.front();
update(editor->position.row + rendered_rows, render_x + col, " ", 0,
color, 0);
update(pos.row + rendered_rows, render_x + col, " ", 0, color, 0, 0, 1);
col++;
for (size_t i = 0; i < line_warnings.size(); i++) {
if (line_warnings[i].type < warn.type)
@@ -276,18 +283,18 @@ void render_editor(Editor *editor) {
goto final;
final:
if (col < render_width) {
update(editor->position.row + rendered_rows, render_x + col,
err_sym, fg_color, color, 0);
update(pos.row + rendered_rows, render_x + col, err_sym, fg_color,
color, 0, 0, 1);
col++;
update(editor->position.row + rendered_rows, render_x + col, " ",
fg_color, color, 0);
update(pos.row + rendered_rows, render_x + col, " ", fg_color,
color, 0, 0, 1);
col++;
}
}
}
if (col < render_width) {
update(editor->position.row + rendered_rows, render_x + col, " ", 0,
0 | color, 0);
update(pos.row + rendered_rows, render_x + col, " ", 0, 0 | color, 0,
0, 1);
col++;
}
size_t warn_idx = 0;
@@ -313,19 +320,19 @@ void render_editor(Editor *editor) {
int width = display_width(cluster.c_str(), cluster_len);
if (col + width > render_width)
break;
update(editor->position.row + rendered_rows, render_x + col,
cluster.c_str(), fg_color, color, 0);
update(pos.row + rendered_rows, render_x + col, cluster.c_str(),
fg_color, color, 0, 0, width);
col += width;
warn_idx += cluster_len;
while (width-- > 1)
update(editor->position.row + rendered_rows, render_x + col - width,
"\x1b", fg_color, color, 0);
update(pos.row + rendered_rows, render_x + col - width, "\x1b",
fg_color, color, 0, 0, 0);
}
line_warnings.clear();
}
while (col < render_width) {
update(editor->position.row + rendered_rows, render_x + col, " ", 0,
0 | color, 0);
update(pos.row + rendered_rows, render_x + col, " ", 0, 0 | color, 0, 0,
1);
col++;
}
rendered_rows++;
@@ -333,39 +340,36 @@ void render_editor(Editor *editor) {
}
if (line_len == 0 ||
(current_byte_offset >= line_len && rendered_rows == 0)) {
uint32_t color = editor->cursor.row == line_index ? 0x222222 : 0;
const char *hook = nullptr;
uint32_t color = this->cursor.row == line_index ? 0x222222 : 0;
const char *hook = "";
char h[2] = {0, 0};
if (hook_it != v.end() && hook_it->first == line_index + 1) {
h[0] = hook_it->second;
hook = h;
hook_it++;
}
update(editor->position.row + rendered_rows, editor->position.col, hook,
0xAAAAAA, 0, 0);
update(pos.row + rendered_rows, pos.col, hook, 0xAAAAAA, 0, 0, 0, 1);
char buf[16];
int len = snprintf(buf, sizeof(buf), "%*u ", numlen, line_index + 1);
uint32_t num_color =
editor->cursor.row == line_index ? 0xFFFFFF : 0x555555;
uint32_t num_color = this->cursor.row == line_index ? 0xFFFFFF : 0x555555;
for (int i = 0; i < len; i++)
update(editor->position.row + rendered_rows, editor->position.col + i,
(char[2]){buf[i], 0}, num_color, 0, 0);
if (editor->cursor.row == line_index) {
cursor.row = editor->position.row + rendered_rows;
update(pos.row + rendered_rows, pos.col + i, (char[2]){buf[i], 0},
num_color, 0, 0, 0, 1);
if (this->cursor.row == line_index) {
cursor.row = pos.row + rendered_rows;
cursor.col = render_x;
}
uint32_t col = 0;
if (editor->selection_active &&
if (this->selection_active &&
global_byte_offset + line_len + 1 > sel_start &&
global_byte_offset + line_len + 1 <= sel_end) {
update(editor->position.row + rendered_rows, render_x + col, " ", 0,
0x555555 | color, 0);
update(pos.row + rendered_rows, render_x + col, " ", 0,
0x555555 | color, 0, 0, 1);
col++;
}
if (!line_warnings.empty()) {
VWarn warn = line_warnings.front();
update(editor->position.row + rendered_rows, render_x + col, " ", 0,
color, 0);
update(pos.row + rendered_rows, render_x + col, " ", 0, color, 0, 0, 1);
col++;
for (size_t i = 0; i < line_warnings.size(); i++) {
if (line_warnings[i].type < warn.type)
@@ -391,18 +395,18 @@ void render_editor(Editor *editor) {
goto final2;
final2:
if (col < render_width) {
update(editor->position.row + rendered_rows, render_x + col,
err_sym, fg_color, color, 0);
update(pos.row + rendered_rows, render_x + col, err_sym, fg_color,
color, 0, 0, 1);
col++;
update(editor->position.row + rendered_rows, render_x + col, " ",
fg_color, color, 0);
update(pos.row + rendered_rows, render_x + col, " ", fg_color,
color, 0, 0, 1);
col++;
}
}
}
if (col < render_width) {
update(editor->position.row + rendered_rows, render_x + col, " ", 0,
0 | color, 0);
update(pos.row + rendered_rows, render_x + col, " ", 0, 0 | color, 0,
0, 1);
col++;
}
size_t warn_idx = 0;
@@ -428,18 +432,18 @@ void render_editor(Editor *editor) {
int width = display_width(cluster.c_str(), cluster_len);
if (col + width > render_width)
break;
update(editor->position.row + rendered_rows, render_x + col,
cluster.c_str(), fg_color, color, 0);
update(pos.row + rendered_rows, render_x + col, cluster.c_str(),
fg_color, color, 0, 0, width);
col += width;
warn_idx += cluster_len;
while (width-- > 1)
update(editor->position.row + rendered_rows, render_x + col - width,
"\x1b", fg_color, color, 0);
update(pos.row + rendered_rows, render_x + col - width, "\x1b",
fg_color, color, 0, 0, 0);
}
}
while (col < render_width) {
update(editor->position.row + rendered_rows, render_x + col, " ", 0,
0 | color, 0);
update(pos.row + rendered_rows, render_x + col, " ", 0, 0 | color, 0, 0,
1);
col++;
}
rendered_rows++;
@@ -447,10 +451,9 @@ void render_editor(Editor *editor) {
global_byte_offset += line_len + 1;
line_index++;
}
while (rendered_rows < editor->size.row) {
for (uint32_t col = 0; col < editor->size.col; col++)
update(editor->position.row + rendered_rows, editor->position.col + col,
" ", 0xFFFFFF, 0, 0);
while (rendered_rows < this->size.row) {
for (uint32_t col = 0; col < this->size.col; col++)
update(pos.row + rendered_rows, pos.col + col, " ", 0xFFFFFF, 0, 0, 0, 1);
rendered_rows++;
}
if (cursor.row != UINT32_MAX && cursor.col != UINT32_MAX) {
@@ -468,15 +471,17 @@ void render_editor(Editor *editor) {
break;
}
set_cursor(cursor.row, cursor.col, type, true);
if (editor->completion.active && !editor->completion.box.hidden)
editor->completion.box.render(cursor);
else if (editor->hover_active)
editor->hover.render(cursor);
else if (editor->diagnostics_active)
editor->diagnostics.render(cursor);
// if (this->completion.active && !this->completion.box.hidden)
// this->completion.box.render(cursor);
// else if (this->hover_active)
// this->hover.render(cursor);
// else if (this->diagnostics_active)
// this->diagnostics.render(cursor);
if (this->hover_active)
ui::hover_popup->pos = cursor;
}
free(it->buffer);
free(it);
if (editor->parser)
editor->parser->scroll(line_index + 5);
if (this->parser)
this->parser->scroll(line_index + 5);
}

View File

@@ -1,13 +1,13 @@
#include "editor/editor.h"
void scroll_up(Editor *editor, int32_t number) {
if (!editor || number == 0)
void Editor::scroll_up(uint32_t number) {
if (number == 0)
return;
uint32_t numlen =
EXTRA_META + static_cast<int>(std::log10(editor->root->line_count + 1));
uint32_t render_width = editor->size.col - numlen;
uint32_t line_index = editor->scroll.row;
LineIterator *it = begin_l_iter(editor->root, line_index);
EXTRA_META + static_cast<int>(std::log10(this->root->line_count + 1));
uint32_t render_width = this->size.col - numlen;
uint32_t line_index = this->scroll.row;
LineIterator *it = begin_l_iter(this->root, line_index);
if (!it)
return;
uint32_t len;
@@ -23,10 +23,9 @@ void scroll_up(Editor *editor, int32_t number) {
uint32_t col = 0;
std::vector<uint32_t> segment_starts;
segment_starts.reserve(16);
if (current_byte_offset < editor->scroll.col)
if (current_byte_offset < this->scroll.col)
segment_starts.push_back(0);
while (current_byte_offset < editor->scroll.col &&
current_byte_offset < len) {
while (current_byte_offset < this->scroll.col && current_byte_offset < len) {
uint32_t cluster_len = grapheme_next_character_break_utf8(
line + current_byte_offset, len - current_byte_offset);
int width = display_width(line + current_byte_offset, cluster_len);
@@ -40,7 +39,7 @@ void scroll_up(Editor *editor, int32_t number) {
for (auto it_seg = segment_starts.rbegin(); it_seg != segment_starts.rend();
++it_seg) {
if (--number == 0) {
editor->scroll = {line_index, *it_seg};
this->scroll = {line_index, *it_seg};
free(it->buffer);
free(it);
return;
@@ -48,7 +47,7 @@ void scroll_up(Editor *editor, int32_t number) {
}
line = prev_line(it, &len);
if (!line) {
editor->scroll = {0, 0};
this->scroll = {0, 0};
free(it->buffer);
free(it);
return;
@@ -57,7 +56,7 @@ void scroll_up(Editor *editor, int32_t number) {
line_index--;
line = prev_line(it, &len);
if (!line) {
editor->scroll = {0, 0};
this->scroll = {0, 0};
free(it->buffer);
free(it);
return;
@@ -83,7 +82,7 @@ void scroll_up(Editor *editor, int32_t number) {
for (auto it_seg = segment_starts.rbegin(); it_seg != segment_starts.rend();
++it_seg) {
if (--number == 0) {
editor->scroll = {line_index, *it_seg};
this->scroll = {line_index, *it_seg};
free(it->buffer);
free(it);
return;
@@ -94,17 +93,17 @@ void scroll_up(Editor *editor, int32_t number) {
free(it);
}
void scroll_down(Editor *editor, uint32_t number) {
if (!editor || number == 0)
void Editor::scroll_down(uint32_t number) {
if (number == 0)
return;
uint32_t numlen =
EXTRA_META + static_cast<int>(std::log10(editor->root->line_count + 1));
uint32_t render_width = editor->size.col - numlen;
uint32_t line_index = editor->scroll.row;
LineIterator *it = begin_l_iter(editor->root, line_index);
EXTRA_META + static_cast<int>(std::log10(this->root->line_count + 1));
uint32_t render_width = this->size.col - numlen;
uint32_t line_index = this->scroll.row;
LineIterator *it = begin_l_iter(this->root, line_index);
if (!it)
return;
const uint32_t max_visual_lines = editor->size.row;
const uint32_t max_visual_lines = this->size.row;
Coord *scroll_queue = (Coord *)malloc(sizeof(Coord) * max_visual_lines);
uint32_t q_head = 0;
uint32_t q_size = 0;
@@ -119,7 +118,7 @@ void scroll_down(Editor *editor, uint32_t number) {
line_len--;
uint32_t current_byte_offset = 0;
if (first_visual_line) {
current_byte_offset += editor->scroll.col;
current_byte_offset += this->scroll.col;
first_visual_line = false;
}
while (current_byte_offset < line_len ||
@@ -134,7 +133,7 @@ void scroll_down(Editor *editor, uint32_t number) {
}
visual_seen++;
if (visual_seen >= number + max_visual_lines) {
editor->scroll = scroll_queue[q_head];
this->scroll = scroll_queue[q_head];
free(scroll_queue);
free(it->buffer);
free(it);
@@ -162,7 +161,7 @@ void scroll_down(Editor *editor, uint32_t number) {
}
if (q_size > 0) {
uint32_t advance = (q_size > number) ? number : (q_size - 1);
editor->scroll = scroll_queue[(q_head + advance) % max_visual_lines];
this->scroll = scroll_queue[(q_head + advance) % max_visual_lines];
}
free(it->buffer);
free(it);

View File

@@ -1,47 +1,46 @@
#include "editor/editor.h"
#include "utils/utils.h"
void selection_bounds(Editor *editor, Coord *out_start, Coord *out_end) {
std::shared_lock lock(editor->knot_mtx);
void Editor::selection_bounds(Coord *out_start, Coord *out_end) {
Coord start, end;
if (editor->cursor >= editor->selection) {
if (this->cursor >= this->selection) {
uint32_t prev_col;
switch (editor->selection_type) {
switch (this->selection_type) {
case CHAR:
start = editor->selection;
end = move_right(editor, editor->cursor, 1);
start = this->selection;
end = this->move_right(this->cursor, 1);
break;
case WORD:
word_boundaries(editor, editor->selection, &prev_col, nullptr, nullptr,
this->word_boundaries(this->selection, &prev_col, nullptr, nullptr,
nullptr);
start = {editor->selection.row, prev_col};
end = editor->cursor;
start = {this->selection.row, prev_col};
end = this->cursor;
break;
case LINE:
start = {editor->selection.row, 0};
end = editor->cursor;
start = {this->selection.row, 0};
end = this->cursor;
break;
}
} else {
start = editor->cursor;
start = this->cursor;
uint32_t next_col, line_len;
switch (editor->selection_type) {
switch (this->selection_type) {
case CHAR:
end = move_right(editor, editor->selection, 1);
end = this->move_right(this->selection, 1);
break;
case WORD:
word_boundaries(editor, editor->selection, nullptr, &next_col, nullptr,
this->word_boundaries(this->selection, nullptr, &next_col, nullptr,
nullptr);
end = {editor->selection.row, next_col};
end = {this->selection.row, next_col};
break;
case LINE:
LineIterator *it = begin_l_iter(editor->root, editor->selection.row);
LineIterator *it = begin_l_iter(this->root, this->selection.row);
char *line = next_line(it, &line_len);
if (!line)
return;
if (line_len > 0 && line[line_len - 1] == '\n')
line_len--;
end = {editor->selection.row, line_len};
end = {this->selection.row, line_len};
free(it->buffer);
free(it);
break;
@@ -53,47 +52,46 @@ void selection_bounds(Editor *editor, Coord *out_start, Coord *out_end) {
*out_end = end;
}
char *get_selection(Editor *editor, uint32_t *out_len, Coord *out_start) {
std::shared_lock lock(editor->knot_mtx);
char *Editor::get_selection(uint32_t *out_len, Coord *out_start) {
Coord start, end;
if (editor->cursor >= editor->selection) {
if (this->cursor >= this->selection) {
uint32_t prev_col;
switch (editor->selection_type) {
switch (this->selection_type) {
case CHAR:
start = editor->selection;
end = move_right(editor, editor->cursor, 1);
start = this->selection;
end = this->move_right(this->cursor, 1);
break;
case WORD:
word_boundaries(editor, editor->selection, &prev_col, nullptr, nullptr,
this->word_boundaries(this->selection, &prev_col, nullptr, nullptr,
nullptr);
start = {editor->selection.row, prev_col};
end = editor->cursor;
start = {this->selection.row, prev_col};
end = this->cursor;
break;
case LINE:
start = {editor->selection.row, 0};
end = editor->cursor;
start = {this->selection.row, 0};
end = this->cursor;
break;
}
} else {
start = editor->cursor;
start = this->cursor;
uint32_t next_col, line_len;
switch (editor->selection_type) {
switch (this->selection_type) {
case CHAR:
end = move_right(editor, editor->selection, 1);
end = this->move_right(this->selection, 1);
break;
case WORD:
word_boundaries(editor, editor->selection, nullptr, &next_col, nullptr,
this->word_boundaries(this->selection, nullptr, &next_col, nullptr,
nullptr);
end = {editor->selection.row, next_col};
end = {this->selection.row, next_col};
break;
case LINE:
LineIterator *it = begin_l_iter(editor->root, editor->selection.row);
LineIterator *it = begin_l_iter(this->root, this->selection.row);
char *line = next_line(it, &line_len);
if (!line)
return nullptr;
if (line_len > 0 && line[line_len - 1] == '\n')
line_len--;
end = {editor->selection.row, line_len};
end = {this->selection.row, line_len};
free(it->buffer);
free(it);
break;
@@ -102,9 +100,9 @@ char *get_selection(Editor *editor, uint32_t *out_len, Coord *out_start) {
if (out_start)
*out_start = start;
uint32_t start_byte =
line_to_byte(editor->root, start.row, nullptr) + start.col;
uint32_t end_byte = line_to_byte(editor->root, end.row, nullptr) + end.col;
char *text = read(editor->root, start_byte, end_byte - start_byte);
line_to_byte(this->root, start.row, nullptr) + start.col;
uint32_t end_byte = line_to_byte(this->root, end.row, nullptr) + end.col;
char *text = read(this->root, start_byte, end_byte - start_byte);
if (out_len)
*out_len = end_byte - start_byte;
return text;

View File

@@ -1,37 +1,38 @@
#include "editor/editor.h"
void hover_diagnostic(Editor *editor) {
std::shared_lock lock(editor->v_mtx);
static uint32_t last_line = UINT32_MAX;
if (last_line == editor->cursor.row && !editor->warnings_dirty)
return;
VWarn dummy;
dummy.line = editor->cursor.row;
editor->warnings_dirty = false;
last_line = editor->cursor.row;
auto first =
std::lower_bound(editor->warnings.begin(), editor->warnings.end(), dummy);
auto last =
std::upper_bound(editor->warnings.begin(), editor->warnings.end(), dummy);
std::vector<VWarn> warnings_at_line(first, last);
if (warnings_at_line.size() == 0) {
editor->diagnostics_active = false;
return;
}
editor->diagnostics.clear();
editor->diagnostics.warnings.swap(warnings_at_line);
editor->diagnostics.render_first();
editor->diagnostics_active = true;
}
// void hover_diagnostic(Editor *editor) {
// static uint32_t last_line = UINT32_MAX;
// if (last_line == editor->cursor.row && !editor->warnings_dirty)
// return;
// VWarn dummy;
// dummy.line = editor->cursor.row;
// editor->warnings_dirty = false;
// last_line = editor->cursor.row;
// auto first =
// std::lower_bound(editor->warnings.begin(), editor->warnings.end(),
// dummy);
// auto last =
// std::upper_bound(editor->warnings.begin(), editor->warnings.end(),
// dummy);
// std::vector<VWarn> warnings_at_line(first, last);
// if (warnings_at_line.size() == 0) {
// editor->diagnostics_active = false;
// return;
// }
// editor->diagnostics.clear();
// editor->diagnostics.warnings.swap(warnings_at_line);
// editor->diagnostics.render_first();
// editor->diagnostics_active = true;
// }
void editor_worker(Editor *editor) {
if (!editor || !editor->root)
void Editor::work() {
if (!this->root)
return;
if (editor->parser)
editor->parser->work();
hover_diagnostic(editor);
if (editor->completion.active && editor->completion.hover_dirty) {
editor->completion.hover.render_first();
editor->completion.hover_dirty = false;
}
if (this->parser)
this->parser->work();
// hover_diagnostic(this);
// if (this->completion.active && this->completion.hover_dirty) {
// this->completion.hover.render_first();
// this->completion.hover_dirty = false;
// }
}

View File

View File

@@ -1,12 +1,15 @@
#include "ui/hover.h"
#include "extentions/hover.h"
#include "syntax/decl.h"
#include "windows/decl.h"
void HoverBox::clear() {
text = "";
scroll_ = 0;
is_markup = false;
size = {0, 0};
cells.clear();
TileRoot *init_hover() {
auto root = std::make_unique<TileRoot>();
root->tile = std::make_unique<HoverBox>();
root->pos = {0, 0};
root->size = {1, 1};
root->tile->hidden = true;
popups.push_back(std::move(root));
return popups.back().get();
}
void HoverBox::scroll(int32_t number) {
@@ -19,13 +22,23 @@ void HoverBox::scroll(int32_t number) {
scroll_ = MAX((int32_t)scroll_ + number, 0);
if (scroll_ > line_count)
scroll_ = line_count;
render_first(true);
scroll_dirty = true;
}
void HoverBox::render_first(bool scroll) {
if (!scroll) {
void HoverBox::render(std::vector<ScreenCell> &buffer, Coord size, Coord pos) {
if (scroll_dirty) {
// TODO: call syntax highlighter here
}
// int32_t start_row = (int32_t)pos.row - (int32_t)size.row;
// if (start_row < 0)
// start_row = pos.row + 1;
// int32_t start_col = pos.col; // pos here is the cursor pos
Coord screen_size = get_size();
// if (start_col + size.col > screen_size.col) {
// start_col = screen_size.col - size.col;
// if (start_col < 0)
// start_col = 0;
// }
uint32_t longest_line = 0;
uint32_t current_width = 0;
for (size_t j = 0; j < text.length(); j++) {
@@ -36,7 +49,7 @@ void HoverBox::render_first(bool scroll) {
current_width += 1;
}
}
// HACK: the 1 is added so the longest line doesnt wrap which should be fixed
// HACK: the 1 is added so the longest line doesnt wrap which should befixed
// in the loop instead as it was never meant to wrap in the first place
longest_line = MAX(longest_line, current_width) + 1;
uint32_t content_width = MIN(longest_line, 130u);
@@ -50,10 +63,19 @@ void HoverBox::render_first(bool scroll) {
}
uint32_t border_fg = 0x82AAFF;
uint32_t base_bg = 0;
cells.assign(size.col * 26, ScreenCell{" ", 0, 0, 0, 0, 0});
auto set = [&](uint32_t r, uint32_t c, const char *text, uint32_t fg,
uint32_t bg, uint8_t flags) {
cells[r * size.col + c] = {std::string(text), 0, fg, bg, flags, 0};
uint32_t bg, uint8_t flags, uint32_t width) {
if (r < 0 || r >= size.row || c < 0 || c >= size.col)
return;
r += pos.row;
c += pos.col;
ScreenCell &cell = buffer[r * screen_size.col + c];
cell.utf8 = text;
cell.width = width;
cell.fg = fg;
cell.bg = bg;
cell.flags = flags;
cell.ul_color = 0;
};
uint32_t r = 0;
while (i < text.length() && r < 24) {
@@ -75,45 +97,27 @@ void HoverBox::render_first(bool scroll) {
uint32_t fg = hl ? hl->fg : 0xFFFFFF;
uint32_t bg = hl ? hl->bg : 0;
uint32_t flags = hl ? hl->flags : 0;
set(r + 1, c + 1, cluster.c_str(), fg, bg | base_bg, flags);
set(r + 1, c + 1, cluster.c_str(), fg, bg | base_bg, flags, width);
c += width;
i += cluster_len;
for (int w = 1; w < width; w++)
set(r + 1, c - w + 1, "\x1b", 0xFFFFFF, base_bg, 0);
set(r + 1, c - w + 1, "\x1b", 0xFFFFFF, base_bg, 0, 0);
}
r++;
}
if (!scroll)
if (scroll_dirty)
size.row = r + 2;
set(0, 0, "", border_fg, base_bg, 0);
set(0, 0, "", border_fg, base_bg, 0, 1);
for (uint32_t i = 1; i < size.col - 1; i++)
set(0, i, "", border_fg, base_bg, 0);
set(0, size.col - 1, "", border_fg, base_bg, 0);
set(0, i, "", border_fg, base_bg, 0, 1);
set(0, size.col - 1, "", border_fg, base_bg, 0, 1);
for (uint32_t r = 1; r < size.row - 1; r++) {
set(r, 0, "", border_fg, base_bg, 0);
set(r, size.col - 1, "", border_fg, base_bg, 0);
set(r, 0, "", border_fg, base_bg, 0, 1);
set(r, size.col - 1, "", border_fg, base_bg, 0, 1);
}
set(size.row - 1, 0, "", border_fg, base_bg, 0);
set(size.row - 1, 0, "", border_fg, base_bg, 0, 1);
for (uint32_t i = 1; i < size.col - 1; i++)
set(size.row - 1, i, "", border_fg, base_bg, 0);
set(size.row - 1, size.col - 1, "", border_fg, base_bg, 0);
cells.resize(size.col * size.row);
}
void HoverBox::render(Coord pos) {
int32_t start_row = (int32_t)pos.row - (int32_t)size.row;
if (start_row < 0)
start_row = pos.row + 1;
int32_t start_col = pos.col;
Coord screen_size = get_size();
if (start_col + size.col > screen_size.col) {
start_col = screen_size.col - size.col;
if (start_col < 0)
start_col = 0;
}
for (uint32_t r = 0; r < size.row; r++)
for (uint32_t c = 0; c < size.col; c++)
update(start_row + r, start_col + c, cells[r * size.col + c].utf8,
cells[r * size.col + c].fg, cells[r * size.col + c].bg,
cells[r * size.col + c].flags);
set(size.row - 1, i, "", border_fg, base_bg, 0, 1);
set(size.row - 1, size.col - 1, "", border_fg, base_bg, 0, 1);
scroll_dirty = false;
}

View File

@@ -1,10 +1,9 @@
#include "io/sysio.h"
std::vector<ScreenCell> new_screen;
static uint32_t rows, cols;
static bool show_cursor = 0;
static std::vector<ScreenCell> screen;
static std::vector<ScreenCell> old_screen;
static std::mutex screen_mutex;
static termios orig_termios;
void disable_raw_mode() {
@@ -24,7 +23,7 @@ void enable_raw_mode() {
raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
raw.c_oflag &= ~(OPOST);
raw.c_cflag |= (CS8);
raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
raw.c_lflag &= ~(ECHO | ICANON | IEXTEN); // | ISIG);
raw.c_cc[VMIN] = 0;
raw.c_cc[VTIME] = 0;
if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw) == -1)
@@ -39,7 +38,7 @@ Coord start_screen() {
ioctl(0, TIOCGWINSZ, &w);
rows = w.ws_row;
cols = w.ws_col;
screen.assign(rows * cols, {});
new_screen.assign(rows * cols, {});
old_screen.assign(rows * cols, {});
return {rows, cols};
}
@@ -48,71 +47,7 @@ void end_screen() { disable_raw_mode(); }
Coord get_size() { return {rows, cols}; }
void update(uint32_t row, uint32_t col, std::string utf8, uint32_t fg,
uint32_t bg, uint8_t flags) {
if (row >= rows || col >= cols)
return;
uint32_t idx = row * cols + col;
std::lock_guard<std::mutex> lock(screen_mutex);
screen[idx].utf8 = utf8 != "" ? utf8 : "";
if (utf8 == "")
return;
screen[idx].width = display_width(utf8.c_str(), utf8.size());
screen[idx].fg = fg;
screen[idx].bg = bg;
screen[idx].flags = flags;
screen[idx].ul_color = 0;
}
void update(uint32_t row, uint32_t col, const char *utf8, uint32_t fg,
uint32_t bg, uint8_t flags) {
if (row >= rows || col >= cols)
return;
uint32_t idx = row * cols + col;
std::lock_guard<std::mutex> lock(screen_mutex);
screen[idx].utf8 = utf8 ? utf8 : "";
if (utf8 == nullptr)
return;
screen[idx].width = display_width(utf8, strlen(utf8));
screen[idx].fg = fg;
screen[idx].bg = bg;
screen[idx].flags = flags;
screen[idx].ul_color = 0;
}
void update(uint32_t row, uint32_t col, std::string utf8, uint32_t fg,
uint32_t bg, uint8_t flags, uint32_t ul_color) {
if (row >= rows || col >= cols)
return;
uint32_t idx = row * cols + col;
std::lock_guard<std::mutex> lock(screen_mutex);
screen[idx].utf8 = utf8 != "" ? utf8 : "";
if (utf8 == "")
return;
screen[idx].width = display_width(utf8.c_str(), utf8.size());
screen[idx].fg = fg;
screen[idx].bg = bg;
screen[idx].flags = flags;
screen[idx].ul_color = ul_color;
}
void update(uint32_t row, uint32_t col, const char *utf8, uint32_t fg,
uint32_t bg, uint8_t flags, uint32_t ul_color) {
if (row >= rows || col >= cols)
return;
uint32_t idx = row * cols + col;
std::lock_guard<std::mutex> lock(screen_mutex);
screen[idx].utf8 = utf8 ? utf8 : "";
if (utf8 == nullptr)
return;
screen[idx].width = display_width(utf8, strlen(utf8));
screen[idx].fg = fg;
screen[idx].bg = bg;
screen[idx].flags = flags;
screen[idx].ul_color = ul_color;
}
void render() {
void io_render() {
static bool first_render = true;
uint32_t current_fg = 0;
uint32_t current_bg = 0;
@@ -121,7 +56,6 @@ void render() {
bool current_bold = false;
bool current_strikethrough = false;
bool current_underline = false;
std::lock_guard<std::mutex> lock(screen_mutex);
std::string out;
out.reserve(static_cast<size_t>(rows) * static_cast<size_t>(cols) * 4 + 256);
out += "\x1b[s\x1b[?25l";
@@ -135,7 +69,7 @@ void render() {
for (uint32_t col = 0; col < cols; ++col) {
uint32_t idx = row * cols + col;
ScreenCell &old_cell = old_screen[idx];
ScreenCell &new_cell = screen[idx];
ScreenCell &new_cell = new_screen[idx];
bool content_changed =
old_cell.utf8 != new_cell.utf8 || old_cell.fg != new_cell.fg ||
old_cell.bg != new_cell.bg || old_cell.flags != new_cell.flags ||
@@ -147,7 +81,7 @@ void render() {
for (int64_t back = 1; back <= 4 && first_change_col - back >= 0;
++back) {
ScreenCell &prev_cell =
screen[row * cols + (first_change_col - back)];
new_screen[row * cols + (first_change_col - back)];
if (prev_cell.width > 1) {
first_change_col -= back;
break;
@@ -165,7 +99,7 @@ void render() {
for (uint32_t col = first_change_col; col <= last_change_col; ++col) {
uint32_t idx = row * cols + col;
ScreenCell &old_cell = old_screen[idx];
ScreenCell &new_cell = screen[idx];
ScreenCell &new_cell = new_screen[idx];
uint32_t width = new_cell.width > 0 ? new_cell.width : 1;
bool overlap = false;
if (width > 1) {
@@ -173,7 +107,7 @@ void render() {
uint32_t next_col = col + i;
if (next_col >= cols)
break;
const ScreenCell &next = screen[row * cols + next_col];
const ScreenCell &next = new_screen[row * cols + next_col];
if (!is_empty_cell(next)) {
overlap = true;
break;
@@ -244,7 +178,7 @@ void render() {
uint32_t next_col = col + i;
if (next_col >= cols)
break;
const ScreenCell &next = screen[row * cols + next_col];
const ScreenCell &next = new_screen[row * cols + next_col];
if (!is_empty_cell(next))
break;
out.push_back(' ');
@@ -260,7 +194,7 @@ void render() {
ScreenCell *cell = &new_cell;
int back = 0;
while ((int)col - back >= 0 && cell->utf8[0] == '\x1b')
cell = &screen[row * cols + col - ++back];
cell = &new_screen[row * cols + col - ++back];
if (width >= cell->width)
out.append(" ");
}

View File

@@ -1,87 +0,0 @@
#include "lsp/lsp.h"
Queue<LSPOpenRequest> lsp_open_queue;
void request_add_to_lsp(Language language, Editor *editor) {
lsp_open_queue.push({language, editor});
}
void add_to_lsp(Language language, Editor *editor) {
std::shared_ptr<LSPInstance> lsp = get_or_init_lsp(language.lsp_name);
if (!lsp)
return;
std::unique_lock lock(lsp->mtx);
if (editor->lsp == lsp)
return;
lsp->editors.push_back(editor);
lsp->open_queue.push({language, editor});
lock.unlock();
}
void open_editor(std::shared_ptr<LSPInstance> lsp,
std::pair<Language, Editor *> entry) {
Language language = entry.first;
Editor *editor = entry.second;
if (editor->lsp == lsp)
return;
editor->lsp = lsp;
std::unique_lock lock3(editor->knot_mtx);
char *buf = read(editor->root, 0, editor->root->char_count);
std::string text(buf);
free(buf);
json message = {{"jsonrpc", "2.0"},
{"method", "textDocument/didOpen"},
{"params",
{{"textDocument",
{{"uri", editor->uri},
{"languageId", language.name},
{"version", 1},
{"text", text}}}}}};
lock3.unlock();
lsp_send(lsp, message, nullptr);
}
static std::string find_lsp_id(std::shared_ptr<LSPInstance> needle) {
for (const auto &[id, lsp] : active_lsps)
if (lsp == needle)
return id;
return "";
}
void remove_from_lsp(Editor *editor) {
auto lsp = editor->lsp;
if (!lsp)
return;
std::unique_lock lock1(lsp->mtx);
lsp->editors.erase(
std::remove(lsp->editors.begin(), lsp->editors.end(), editor),
lsp->editors.end());
lock1.unlock();
std::unique_lock lock2(editor->lsp_mtx);
editor->lsp = nullptr;
lock2.unlock();
json message = {{"jsonrpc", "2.0"},
{"method", "textDocument/didClose"},
{"params", {{"textDocument", {{"uri", editor->uri}}}}}};
lsp_send(lsp, message, nullptr);
std::string lsp_id = find_lsp_id(lsp);
if (!lsp_id.empty() && lsp->editors.empty())
close_lsp(lsp_id);
}
void lsp_handle(std::shared_ptr<LSPInstance>, json message) {
std::string method = message.value("method", "");
if (method == "window/showMessage") {
if (message.contains("params")) {
auto &p = message["params"];
if (p.contains("message"))
log("%s\n", p["message"].get<std::string>().c_str());
}
} else if (method == "window/logMessage") {
if (message.contains("params")) {
auto &p = message["params"];
if (p.contains("message"))
log("%s\n", p["message"].get<std::string>().c_str());
}
}
}

View File

@@ -1,242 +0,0 @@
#include "lsp/lsp.h"
static bool init_lsp(std::shared_ptr<LSPInstance> lsp) {
int in_pipe[2];
int out_pipe[2];
if (pipe(in_pipe) == -1 || pipe(out_pipe) == -1) {
perror("pipe");
return false;
}
pid_t pid = fork();
if (pid == -1) {
perror("fork");
return false;
}
if (pid == 0) {
dup2(in_pipe[0], STDIN_FILENO);
dup2(out_pipe[1], STDOUT_FILENO);
int devnull = open("/dev/null", O_WRONLY);
if (devnull >= 0) {
dup2(devnull, STDERR_FILENO);
close(devnull);
}
close(in_pipe[0]);
close(in_pipe[1]);
close(out_pipe[0]);
close(out_pipe[1]);
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);
}
lsp->pid = pid;
lsp->stdin_fd = in_pipe[1];
lsp->stdout_fd = out_pipe[0];
close(in_pipe[0]);
close(out_pipe[1]);
return true;
}
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 = 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;
if (!init_lsp(lsp))
return nullptr;
LSPPending *pending = new LSPPending();
pending->editor = nullptr;
pending->callback = [lsp, lsp_id](Editor *, const 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.c_str(),
// s.c_str());
// }
if (caps.contains("textDocumentSync")) {
auto &sync = caps["textDocumentSync"];
if (sync.is_number()) {
int change_type = sync.get<int>();
lsp->incremental_sync = (change_type == 2);
} else if (sync.is_object() && sync.contains("change")) {
int change_type = sync["change"].get<int>();
lsp->incremental_sync = (change_type == 2);
}
}
lsp->allow_formatting = caps.value("documentFormattingProvider", false);
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()) {
if (fmt.contains("firstTriggerCharacter")) {
std::string s = fmt["firstTriggerCharacter"].get<std::string>();
if (s.size() == 1)
lsp->format_chars.push_back(s[0]);
}
if (fmt.contains("moreTriggerCharacter")) {
for (auto &c : fmt["moreTriggerCharacter"]) {
std::string s = c.get<std::string>();
if (s.size() == 1)
lsp->format_chars.push_back(s[0]);
}
}
lsp->allow_formatting_on_type = true;
} else if (fmt.is_boolean()) {
lsp->allow_formatting_on_type = fmt.get<bool>();
}
}
if (caps.contains("hoverProvider")) {
auto &hover = caps["hoverProvider"];
lsp->allow_hover =
hover.is_boolean() ? hover.get<bool>() : hover.is_object();
} else {
lsp->allow_hover = false;
}
if (caps.contains("completionProvider")) {
lsp->allow_completion = true;
if (caps["completionProvider"].contains("resolveProvider"))
lsp->allow_resolve =
caps["completionProvider"]["resolveProvider"].get<bool>();
if (caps["completionProvider"].contains("triggerCharacters")) {
auto &chars = caps["completionProvider"]["triggerCharacters"];
if (chars.is_array()) {
for (auto &c : chars) {
std::string str = c.get<std::string>();
if (str.size() != 1)
continue;
lsp->trigger_chars.push_back(str[0]);
}
}
}
if (caps["completionProvider"].contains("allCommitCharacters")) {
auto &chars = caps["completionProvider"]["allCommitCharacters"];
if (chars.is_array()) {
for (auto &c : chars) {
std::string str = c.get<std::string>();
if (str.size() != 1)
continue;
lsp->end_chars.push_back(str[0]);
}
}
}
}
}
lsp->initialized = true;
json initialized = {{"jsonrpc", "2.0"},
{"method", "initialized"},
{"params", json::object()}};
lsp_send(lsp, initialized, nullptr);
};
json init_message = {
{"jsonrpc", "2.0"},
{"method", "initialize"},
{"params",
{{"processId", getpid()},
{"rootUri", "file://" + percent_encode(path_abs("."))},
{"capabilities", client_capabilities}}}};
lsp_send(lsp, init_message, pending);
active_lsps[lsp_id] = lsp;
return lsp;
}
return it->second;
}
void close_lsp(std::string lsp_id) {
std::shared_ptr<LSPInstance> lsp;
{
std::shared_lock lock(active_lsps_mtx);
auto it = active_lsps.find(lsp_id);
if (it == active_lsps.end())
return;
lsp = it->second;
}
if (!lsp || lsp->pid == -1 || lsp->exited)
return;
lsp->exited = true;
lsp->initialized = false;
auto send_raw = [&](const json &msg) {
std::string payload = msg.dump();
std::string header =
"Content-Length: " + std::to_string(payload.size()) + "\r\n\r\n";
std::string out = header + payload;
const char *ptr = out.data();
size_t remaining = out.size();
while (remaining > 0) {
ssize_t n = write(lsp->stdin_fd, ptr, remaining);
if (n <= 0) {
if (errno == EINTR)
continue;
break;
}
ptr += n;
remaining -= n;
}
};
json shutdown = {{"jsonrpc", "2.0"}, {"id", 1}, {"method", "shutdown"}};
send_raw(shutdown);
{
pollfd pfd{lsp->stdout_fd, POLLIN, 0};
int timeout_ms = 300;
if (poll(&pfd, 1, timeout_ms) > 0) {
auto msg = read_lsp_message(lsp->stdout_fd);
(void)msg;
}
}
json exit_msg = {{"jsonrpc", "2.0"}, {"method", "exit"}};
send_raw(exit_msg);
const int max_wait_ms = 500;
int waited = 0;
while (waited < max_wait_ms) {
int status;
pid_t res = waitpid(lsp->pid, &status, WNOHANG);
if (res == lsp->pid)
break;
std::this_thread::sleep_for(std::chrono::milliseconds(10));
waited += 10;
}
if (kill(lsp->pid, 0) == 0) {
kill(lsp->pid, SIGKILL);
waitpid(lsp->pid, nullptr, 0);
}
close(lsp->stdin_fd);
close(lsp->stdout_fd);
{
std::unique_lock lock(lsp->mtx);
for (auto &kv : lsp->pending)
delete kv.second;
lsp->pending.clear();
}
for (auto &editor : lsp->editors) {
std::unique_lock editor_lock(editor->lsp_mtx);
editor->lsp = nullptr;
}
{
std::unique_lock lock(active_lsps_mtx);
active_lsps.erase(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;
close(lsp->stdin_fd);
close(lsp->stdout_fd);
for (auto &editor : lsp->editors) {
std::unique_lock editor_lock(editor->lsp_mtx);
editor->lsp = nullptr;
}
active_lsps.erase(lsp_id);
}

44
src/lsp/worker.cc Normal file
View File

@@ -0,0 +1,44 @@
#include "lsp/lsp.h"
namespace lsp {
std::mutex lsp_mutex;
std::unordered_map<std::string, std::unique_ptr<LSPInstance>> active_lsps;
std::unordered_set<std::string> opened;
Queue<std::string> need_opening;
std::vector<Editor *> new_editors;
} // namespace lsp
void lsp_worker() {
std::lock_guard lock(lsp::lsp_mutex);
while (!lsp::need_opening.empty()) {
auto lsp_id = *lsp::need_opening.front();
if (!lsp::opened.contains(lsp_id)) {
lsp::active_lsps[lsp_id] = std::make_unique<LSPInstance>(lsp_id);
lsp::opened.insert(lsp_id);
}
lsp::need_opening.pop();
}
for (auto it = lsp::new_editors.begin(); it != lsp::new_editors.end();) {
auto ed = *it;
auto lsp_id = ed->lang.lsp_name;
if (lsp::opened.contains(lsp_id)) {
auto a_it = lsp::active_lsps.find(lsp_id);
if (a_it != lsp::active_lsps.end())
a_it->second->add(ed);
it = lsp::new_editors.erase(it);
} else {
lsp::need_opening.push(lsp_id);
it++;
}
}
for (auto it = lsp::active_lsps.begin(); it != lsp::active_lsps.end();) {
auto &lsp_inst = it->second;
if (!lsp_inst || lsp_inst->pid == -1 || lsp_inst->editors.empty() ||
lsp_inst->exited) {
it = lsp::active_lsps.erase(it);
continue;
}
lsp_inst->work();
++it;
}
}

View File

@@ -1,170 +0,0 @@
#include "lsp/lsp.h"
std::shared_mutex active_lsps_mtx;
std::unordered_map<std::string, std::shared_ptr<LSPInstance>> active_lsps;
void lsp_send(std::shared_ptr<LSPInstance> lsp, json message,
LSPPending *pending) {
if (!lsp || lsp->stdin_fd == -1)
return;
std::unique_lock lock(lsp->mtx);
if (pending) {
message["id"] = lsp->last_id++;
uint32_t id = message["id"].get<uint32_t>();
lsp->pending[id] = pending;
}
lsp->outbox.push(message);
}
std::optional<json> read_lsp_message(int fd) {
std::string header;
char c;
while (true) {
ssize_t n = read(fd, &c, 1);
if (n <= 0)
return std::nullopt;
header.push_back(c);
if (header.size() >= 4 && header.substr(header.size() - 4) == "\r\n\r\n")
break;
}
size_t pos = header.find("Content-Length:");
if (pos == std::string::npos)
return std::nullopt;
pos += strlen("Content-Length:");
while (pos < header.size() && std::isspace(header[pos]))
pos++;
size_t end = pos;
while (end < header.size() && std::isdigit(header[end]))
end++;
size_t len = std::stoul(header.substr(pos, end - pos));
std::string body(len, '\0');
size_t got = 0;
while (got < len) {
ssize_t n = read(fd, &body[got], len - got);
if (n <= 0)
return std::nullopt;
got += n;
}
return json::parse(body);
}
static Editor *editor_for_uri(std::shared_ptr<LSPInstance> lsp,
std::string uri) {
if (uri.empty())
return nullptr;
for (auto &editor : lsp->editors)
if (editor->uri == uri)
return editor;
return nullptr;
}
void lsp_worker() {
LSPOpenRequest request;
while (lsp_open_queue.pop(request))
add_to_lsp(request.language, request.editor);
std::unique_lock active_lsps_lock(active_lsps_mtx);
for (auto &kv : active_lsps) {
std::shared_ptr<LSPInstance> lsp = kv.second;
std::unique_lock lock(lsp->mtx);
int status;
pid_t res = waitpid(lsp->pid, &status, WNOHANG);
if (res == lsp->pid) {
clean_lsp(lsp, kv.first);
return;
}
if (lsp->initialized) {
std::pair<Language, Editor *> request;
while (lsp->open_queue.pop(request)) {
lock.unlock();
open_editor(lsp, request);
lock.lock();
}
}
while (!lsp->outbox.empty()) {
json message = lsp->outbox.front();
std::string m = message.value("method", "");
if (lsp->exited) {
if (m != "exit" && m != "shutdown") {
lsp->outbox.pop(message);
continue;
}
}
if (!lsp->initialized) {
if (m != "initialize" && m != "exit" && m != "shutdown")
break;
}
lsp->outbox.pop(message);
std::string payload = message.dump();
std::string header =
"Content-Length: " + std::to_string(payload.size()) + "\r\n\r\n";
std::string out = header + payload;
const char *ptr = out.data();
size_t remaining = out.size();
while (remaining > 0) {
int status;
pid_t res = waitpid(lsp->pid, &status, WNOHANG);
if (res == lsp->pid) {
clean_lsp(lsp, kv.first);
return;
}
ssize_t written = write(lsp->stdin_fd, ptr, remaining);
if (written == 0)
break;
else if (written == -1) {
if (errno == EINTR)
continue;
perror("write");
clean_lsp(lsp, kv.first);
return;
} else {
ptr += written;
remaining -= written;
}
}
}
pollfd pfd{lsp->stdout_fd, POLLIN | POLLHUP | POLLERR, 0};
int r = poll(&pfd, 1, 0);
if (r > 0 && pfd.revents & (POLLHUP | POLLERR)) {
clean_lsp(lsp, kv.first);
return;
}
while ((r = poll(&pfd, 1, 0) > 0)) {
if (r > 0 && pfd.revents & (POLLHUP | POLLERR)) {
clean_lsp(lsp, kv.first);
return;
}
auto msg = read_lsp_message(lsp->stdout_fd);
if (!msg)
break;
if (msg->contains("id")) {
uint32_t id = msg->at("id").get<uint32_t>();
auto it = lsp->pending.find(id);
if (it != lsp->pending.end()) {
LSPPending *pend = it->second;
lock.unlock();
if (pend->callback)
pend->callback(pend->editor, *msg);
delete pend;
lock.lock();
lsp->pending.erase(it);
}
} else if (msg->contains("method")) {
std::string uri;
if (msg->contains("params")) {
auto &p = (*msg)["params"];
if (p.contains("textDocument") && p["textDocument"].contains("uri"))
uri = p["textDocument"]["uri"].get<std::string>();
else if (p.contains("uri"))
uri = p["uri"].get<std::string>();
}
Editor *ed = editor_for_uri(lsp, uri);
lock.unlock();
if (ed)
editor_lsp_handle(ed, *msg);
else
lsp_handle(lsp, *msg);
lock.lock();
}
}
}
}

View File

@@ -1,42 +1,28 @@
#include "main.h"
#include "editor/editor.h"
#include "extentions/hover.h"
#include "io/sysio.h"
#include "lsp/lsp.h"
#include "ruby/decl.h"
#include "ui/bar.h"
#include "utils/utils.h"
#include "windows/decl.h"
std::atomic<bool> running{true};
Queue<KeyEvent> event_queue;
std::vector<Editor *> editors;
Bar bar;
uint8_t current_editor = 0;
fs::path pwd;
std::atomic<uint8_t> mode = NORMAL;
namespace ui {
Bar bar;
TileRoot *hover_popup = nullptr;
} // namespace ui
void background_lsp() {
while (running)
throttle(8ms, lsp_worker);
}
inline Editor *editor_at(uint8_t x, uint8_t y) {
for (Editor *ed : editors) {
Coord pos = ed->position;
Coord size = ed->size;
if (x >= pos.col && x < pos.col + size.col && y >= pos.row &&
y < pos.row + size.row)
return ed;
}
return nullptr;
}
inline uint8_t index_of(Editor *ed) {
for (uint8_t i = 0; i < editors.size(); i++)
if (editors[i] == ed)
return i;
return 0;
}
int main(int argc, char *argv[]) {
ruby_start();
load_theme();
@@ -46,25 +32,21 @@ int main(int argc, char *argv[]) {
Coord screen = start_screen();
const char *filename = (argc > 1) ? argv[1] : "";
uint8_t eol = read_line_endings();
Editor *editor =
new_editor(filename, {0, 0}, {screen.row - 2, screen.col}, eol);
bar.init(screen);
if (!editor) {
end_screen();
fprintf(stderr, "Failed to load editor\n");
return 1;
}
ui::hover_popup = init_hover();
editors.push_back(editor);
current_editor = editors.size() - 1;
root_tile.size = {screen.row - 2, screen.col - 1};
root_tile.pos = {0, 0};
root_tile.tile = std::make_unique<Editor>(filename, eol);
focused_window = static_cast<Editor *>(root_tile.tile.get());
ui::bar.init(screen);
std::thread lsp_thread(background_lsp);
while (running) {
KeyEvent event = throttle(1ms, read_key);
if (event.key_type == KEY_NONE)
goto render;
if (event.key_type != KEY_NONE) {
if (event.key_type == KEY_CHAR && event.len == 1 &&
event.c[0] == CTRL('q')) {
free(event.c);
@@ -73,27 +55,22 @@ int main(int argc, char *argv[]) {
}
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);
handle_click(event);
} else {
focused_window->handle_event(event);
}
} else {
handle_editor_event(editors[current_editor], event);
ui::bar.handle_event(event);
}
} else {
bar.handle(event);
}
if ((event.key_type == KEY_CHAR || event.key_type == KEY_PASTE) && event.c)
if ((event.key_type == KEY_CHAR || event.key_type == KEY_PASTE) &&
event.c)
free(event.c);
render:
throttle(4ms, editor_worker, editors[current_editor]);
bar.render();
render_editor(editors[current_editor]);
}
for (auto &lsp_inst : lsp::active_lsps)
lsp_inst.second->callbacks();
focused_window->work();
throttle(4ms, render);
throttle(4ms, io_render);
}
if (lsp_thread.joinable())
@@ -101,9 +78,6 @@ int main(int argc, char *argv[]) {
end_screen();
for (auto editor : editors)
free_editor(editor);
ruby_shutdown();
return 0;

View File

@@ -89,8 +89,8 @@ static mrb_value sym_flags;
static mrb_value sym_start;
static mrb_value sym_length;
static mrb_value sym_mode;
static mrb_value sym_lang_name;
static mrb_value sym_filename;
static mrb_value sym_data;
static mrb_value sym_foldername;
static mrb_value sym_width;
static mrb_value sym_normal;
static mrb_value sym_insert;
@@ -105,8 +105,8 @@ inline void initialize_symbols() {
sym_start = mrb_symbol_value(mrb_intern_cstr(mrb, "start"));
sym_length = mrb_symbol_value(mrb_intern_cstr(mrb, "length"));
sym_mode = mrb_symbol_value(mrb_intern_cstr(mrb, "mode"));
sym_lang_name = mrb_symbol_value(mrb_intern_cstr(mrb, "lang_name"));
sym_filename = mrb_symbol_value(mrb_intern_cstr(mrb, "filename"));
sym_data = mrb_symbol_value(mrb_intern_cstr(mrb, "data"));
sym_foldername = mrb_symbol_value(mrb_intern_cstr(mrb, "foldername"));
sym_width = mrb_symbol_value(mrb_intern_cstr(mrb, "width"));
sym_normal = mrb_symbol_value(mrb_intern_cstr(mrb, "normal"));
sym_insert = mrb_symbol_value(mrb_intern_cstr(mrb, "insert"));
@@ -146,10 +146,8 @@ convert_highlights(mrb_state *mrb, mrb_value highlights_val) {
return result;
}
BarLine bar_contents(uint8_t mode, std::string lang_name, uint32_t warnings,
std::string lsp_name, std::string filename,
std::string foldername, uint32_t line, uint32_t max_line,
uint32_t width) {
BarLine bar_contents(uint8_t mode, uint32_t width, std::string foldername,
Window *window) {
BarLine bar_line;
static bool initialed = false;
if (!initialed) {
@@ -158,7 +156,6 @@ BarLine bar_contents(uint8_t mode, std::string lang_name, uint32_t warnings,
}
int ai = mrb_gc_arena_save(mrb);
mrb_value info = mrb_hash_new(mrb);
mrb_value key_mode = sym_mode;
mrb_value val_mode;
switch (mode) {
case NORMAL:
@@ -177,18 +174,20 @@ BarLine bar_contents(uint8_t mode, std::string lang_name, uint32_t warnings,
val_mode = sym_jumper;
break;
}
mrb_hash_set(mrb, info, key_mode, val_mode);
mrb_value key_lang_name = sym_lang_name;
mrb_value val_lang_name =
mrb_symbol_value(mrb_intern_cstr(mrb, lang_name.c_str()));
mrb_hash_set(mrb, info, key_lang_name, val_lang_name);
mrb_value key_filename = sym_filename;
mrb_value val_filename =
mrb_str_new(mrb, filename.c_str(), filename.length());
mrb_hash_set(mrb, info, key_filename, val_filename);
mrb_value key_width = sym_width;
mrb_hash_set(mrb, info, sym_mode, val_mode);
mrb_value val_foldername =
mrb_str_new(mrb, foldername.c_str(), foldername.length());
mrb_hash_set(mrb, info, sym_foldername, val_foldername);
std::array<std::string, 5> arr = window->bar_info();
mrb_value ary = mrb_ary_new(mrb);
mrb_ary_push(mrb, ary, mrb_str_new(mrb, arr[0].c_str(), arr[0].length()));
mrb_ary_push(mrb, ary, mrb_str_new(mrb, arr[1].c_str(), arr[1].length()));
mrb_ary_push(mrb, ary, mrb_str_new(mrb, arr[2].c_str(), arr[2].length()));
mrb_ary_push(mrb, ary, mrb_str_new(mrb, arr[3].c_str(), arr[3].length()));
mrb_ary_push(mrb, ary, mrb_str_new(mrb, arr[4].c_str(), arr[4].length()));
mrb_hash_set(mrb, info, sym_data, ary);
mrb_value val_width = mrb_fixnum_value(width);
mrb_hash_set(mrb, info, key_width, val_width);
mrb_hash_set(mrb, info, sym_width, val_width);
mrb_value mod_val = mrb_obj_value(C_module);
mrb_value block = mrb_funcall(mrb, mod_val, "b_bar", 0);
mrb_value val_line = mrb_funcall(mrb, block, "call", 1, info);

View File

@@ -74,7 +74,6 @@ void Parser::work() {
line_count = line_tree.count();
if (c_line > 0 && c_line < line_count)
prev_state = line_tree.at(c_line - 1)->out_state;
std::shared_lock k_lock(editor->knot_mtx);
LineIterator *it = begin_l_iter(editor->root, c_line);
if (!it)
continue;

View File

@@ -3,17 +3,24 @@
#include "lsp/lsp.h"
#include "main.h"
#include "syntax/decl.h"
#include "windows/decl.h"
void Bar::log(std::string message) { log_line = message; }
void Bar::render() {
void Bar::render(std::vector<ScreenCell> &buffer) {
USING(LSPInstance);
Editor *editor = editors[current_editor];
BarLine bar_line =
bar_contents(mode, editor->lang.name, editor->warnings.size(),
editor->lsp ? editor->lsp->lsp->command : "",
editor->filename, editor->filename, editor->cursor.row + 1,
editor->root->line_count + 1, screen.col);
BarLine bar_line;
bar_line = bar_contents(mode, screen.col, pwd.string(), focused_window);
auto update = [&](uint32_t row, uint32_t col, std::string text, uint32_t fg,
uint32_t bg, uint8_t flags, uint32_t width) {
ScreenCell &c = buffer[row * screen.col + col];
c.utf8 = text;
c.width = width;
c.fg = fg;
c.bg = bg;
c.flags = flags;
c.ul_color = 0;
};
uint32_t row = screen.row - 2;
uint32_t width = screen.col;
std::string &line = bar_line.line;
@@ -26,43 +33,46 @@ void Bar::render() {
int width = display_width(cluster.c_str(), cluster_len);
Highlight highlight = bar_line.get_highlight(col);
update(row, col, cluster.c_str(), highlight.fg, highlight.bg,
highlight.flags);
highlight.flags, width);
col += width;
i += cluster_len;
for (int w = 1; w < width; w++)
update(row, col - w, "\x1b", highlight.fg, highlight.bg, highlight.flags);
update(row, col - w, "\x1b", highlight.fg, highlight.bg, highlight.flags,
0);
}
while (col < width)
update(row, col++, " ", 0, 0, 0);
update(row, col++, " ", 0, 0, 0, 1);
col = 0;
row++;
if (mode == RUNNER) {
update(row, col++, ":", 0xFFFFFF, 0, 0);
update(row, col++, ":", 0xFFFFFF, 0, 0, 1);
for (char c : command)
update(row, col++, (char[2]){c, 0}, 0xFFFFFF, 0, 0);
update(row, col++, (char[2]){c, 0}, 0xFFFFFF, 0, 0, 1);
} else {
for (char c : log_line)
update(row, col++, (char[2]){c, 0}, 0xFFFFFF, 0, 0);
update(row, col++, (char[2]){c, 0}, 0xFFFFFF, 0, 0, 1);
}
while (col < width)
update(row, col++, " ", 0, 0, 0);
update(row, col++, " ", 0, 0, 0, 1);
}
void Bar::handle(KeyEvent event) {
void Bar::handle_command(std::string &command) {
if (command == "q") {
running = false;
return;
}
if (focused_window)
focused_window->handle_command(command);
}
void Bar::handle_event(KeyEvent event) {
if (event.key_type == KEY_CHAR && event.len == 1) {
if (event.c[0] == 0x1B) {
command = "";
mode = NORMAL;
} else if (event.c[0] == '\n' || event.c[0] == '\r') {
command = trim(command);
if (command == "w") {
save_file(editors[current_editor]);
} else if (command == "q") {
running = false;
} else if (command == "wq") {
save_file(editors[current_editor]);
running = false;
}
handle_command(command);
mode = NORMAL;
command = "";
} else if (isprint((unsigned char)(event.c[0]))) {

View File

@@ -1,187 +1,187 @@
#include "ui/completionbox.h"
#include "editor/completions.h"
#include "io/sysio.h"
#include "utils/utils.h"
std::string item_kind_name(uint8_t kind) {
switch (kind) {
case 1:
return "Text";
case 2:
return "Method";
case 3:
return "Function";
case 4:
return "Constructor";
case 5:
return "Field";
case 6:
return "Variable";
case 7:
return "Class";
case 8:
return "Interface";
case 9:
return "Module";
case 10:
return "Property";
case 11:
return "Unit";
case 12:
return "Value";
case 13:
return "Enum";
case 14:
return "Keyword";
case 15:
return "Snippet";
case 16:
return "Color";
case 17:
return "File";
case 18:
return "Reference";
case 19:
return "Folder";
case 20:
return "EnumMember";
case 21:
return "Constant";
case 22:
return "Struct";
case 23:
return "Event";
case 24:
return "Operator";
case 25:
return "TypeParameter";
default:
return "Unknown";
}
}
const char *item_symbol(uint8_t kind) { return ""; }
uint32_t kind_color(uint8_t kind) { return 0x82AAFF; }
void CompletionBox::render_update() {
if (hidden || session->visible.empty())
return;
std::unique_lock lock(mtx);
uint32_t max_label_len = 0;
uint32_t max_detail_len = 0;
uint32_t max_kind_len = 0;
for (uint32_t x = session->scroll;
x < session->scroll + 8 && x < session->visible.size(); x++) {
uint32_t i = session->visible[x];
if (i >= session->items.size())
continue;
auto &item = session->items[i];
max_label_len = MAX(max_label_len, (uint32_t)item.label.size());
if (item.detail)
max_detail_len = MAX(max_detail_len, (uint32_t)item.detail->size());
max_kind_len =
MAX(max_kind_len, (uint32_t)item_kind_name(item.kind).size());
}
uint32_t total = session->visible.size();
uint32_t rows = MIN(total, 8);
size.row = rows + 2;
size.col = 2 + 2 + max_label_len + 1 + max_detail_len + 2 + max_kind_len + 1;
cells.assign(size.row * size.col, {" ", 0, 0, 0, 0, 0});
auto set = [&](uint32_t r, uint32_t c, const char *text, uint32_t fg,
uint32_t bg, uint8_t flags) {
if (r < size.row && c < size.col)
cells[r * size.col + c] = {std::string(text), 0, fg, bg, flags, 0};
};
uint32_t border_fg = 0x82AAFF;
uint32_t sel_bg = 0x174225;
set(0, 0, "", border_fg, 0, 0);
for (uint32_t c = 1; c < size.col - 1; c++)
set(0, c, "", border_fg, 0, 0);
set(0, size.col - 1, "", border_fg, 0, 0);
uint32_t start = session->scroll;
uint32_t end = MIN(start + 8, session->visible.size());
for (uint32_t row_idx = start; row_idx < end; row_idx++) {
uint32_t r = (row_idx - start) + 1;
auto &item = session->items[session->visible[row_idx]];
uint32_t bg = (session->visible[row_idx] == session->select) ? sel_bg : 1;
uint32_t fg = 0xFFFFFF;
set(r, 0, "", border_fg, 0, 0);
uint32_t c = 1;
const char *sym = item_symbol(item.kind);
set(r, c++, sym, kind_color(item.kind), bg, 0);
set(r, c++, " ", fg, bg, 0);
for (size_t i = 0; i < item.label.size(); i++)
set(r, c + i, (char[2]){item.label[i], 0}, fg, bg,
item.deprecated ? CF_STRIKETHROUGH : 0);
c += item.label.size();
set(r, c++, " ", fg, bg, 0);
uint32_t detail_fg = 0xAAAAAA;
if (item.detail) {
for (size_t i = 0; i < item.detail->size(); i++)
set(r, c + i, (char[2]){(*item.detail)[i], 0}, detail_fg, bg, 0);
c += item.detail->size();
}
uint32_t pad = size.col - 1 - c - max_kind_len;
for (uint32_t i = 0; i < pad; i++)
set(r, c + i, " ", fg, bg, 0);
c += pad;
std::string kind_name = item_kind_name(item.kind);
for (size_t i = 0; i < kind_name.size(); i++)
set(r, c + i, (char[2]){kind_name[i], 0}, kind_color(item.kind), bg, 0);
set(r, size.col - 1, "", border_fg, 0, 0);
}
uint32_t bottom = size.row - 1;
set(bottom, 0, "", border_fg, 0, 0);
for (uint32_t c = 1; c < size.col - 1; c++)
set(bottom, c, "", border_fg, 0, 0);
set(bottom, size.col - 1, "", border_fg, 0, 0);
if (session->visible.size() > 8) {
std::string info = std::to_string(start + 1) + "-" + std::to_string(end) +
"/" + std::to_string(session->visible.size());
for (size_t i = 0; i < info.size() && i < size.col - 2; i++)
set(bottom, 1 + i, (char[2]){info[i], 0}, border_fg, 0, 0);
}
}
void CompletionBox::render(Coord pos) {
if (hidden || session->visible.empty())
return;
std::shared_lock lock(mtx);
int32_t start_row = (int32_t)pos.row - (int32_t)size.row;
if (start_row < 0)
start_row = pos.row + 1;
int32_t start_col = pos.col;
Coord screen_size = get_size();
if (start_col + size.col > screen_size.col) {
start_col = screen_size.col - size.col;
if (start_col < 0)
start_col = 0;
}
position = {(uint32_t)start_row, (uint32_t)start_col};
for (uint32_t r = 0; r < size.row; r++)
for (uint32_t c = 0; c < size.col; c++)
update(start_row + r, start_col + c, cells[r * size.col + c].utf8,
cells[r * size.col + c].fg, cells[r * size.col + c].bg,
cells[r * size.col + c].flags);
if (session->items.size() > session->select &&
session->items[session->select].documentation &&
*session->items[session->select].documentation != "" &&
!session->hover_dirty) {
if (session->doc != session->select) {
session->doc = session->select;
session->hover.clear();
session->hover.text = *session->items[session->select].documentation;
session->hover.is_markup = true;
session->hover_dirty = true;
} else {
if ((int32_t)position.col - (int32_t)session->hover.size.col > 0) {
session->hover.render({position.row + session->hover.size.row,
position.col - session->hover.size.col});
} else {
session->hover.render(
{position.row + session->hover.size.row, position.col + size.col});
}
}
}
}
// #include "ui/completionbox.h"
// #include "editor/completions.h"
// #include "io/sysio.h"
// #include "utils/utils.h"
//
// std::string item_kind_name(uint8_t kind) {
// switch (kind) {
// case 1:
// return "Text";
// case 2:
// return "Method";
// case 3:
// return "Function";
// case 4:
// return "Constructor";
// case 5:
// return "Field";
// case 6:
// return "Variable";
// case 7:
// return "Class";
// case 8:
// return "Interface";
// case 9:
// return "Module";
// case 10:
// return "Property";
// case 11:
// return "Unit";
// case 12:
// return "Value";
// case 13:
// return "Enum";
// case 14:
// return "Keyword";
// case 15:
// return "Snippet";
// case 16:
// return "Color";
// case 17:
// return "File";
// case 18:
// return "Reference";
// case 19:
// return "Folder";
// case 20:
// return "EnumMember";
// case 21:
// return "Constant";
// case 22:
// return "Struct";
// case 23:
// return "Event";
// case 24:
// return "Operator";
// case 25:
// return "TypeParameter";
// default:
// return "Unknown";
// }
// }
//
// const char *item_symbol(uint8_t kind) { return "●"; }
//
// uint32_t kind_color(uint8_t kind) { return 0x82AAFF; }
//
// void CompletionBox::render_update() {
// if (hidden || session->visible.empty())
// return;
// std::unique_lock lock(mtx);
// uint32_t max_label_len = 0;
// uint32_t max_detail_len = 0;
// uint32_t max_kind_len = 0;
// for (uint32_t x = session->scroll;
// x < session->scroll + 8 && x < session->visible.size(); x++) {
// uint32_t i = session->visible[x];
// if (i >= session->items.size())
// continue;
// auto &item = session->items[i];
// max_label_len = MAX(max_label_len, (uint32_t)item.label.size());
// if (item.detail)
// max_detail_len = MAX(max_detail_len, (uint32_t)item.detail->size());
// max_kind_len =
// MAX(max_kind_len, (uint32_t)item_kind_name(item.kind).size());
// }
// uint32_t total = session->visible.size();
// uint32_t rows = MIN(total, 8);
// size.row = rows + 2;
// size.col = 2 + 2 + max_label_len + 1 + max_detail_len + 2 + max_kind_len +
// 1; cells.assign(size.row * size.col, {" ", 0, 0, 0, 0, 0}); auto set =
// [&](uint32_t r, uint32_t c, const char *text, uint32_t fg,
// uint32_t bg, uint8_t flags) {
// if (r < size.row && c < size.col)
// cells[r * size.col + c] = {std::string(text), 0, fg, bg, flags, 0};
// };
// uint32_t border_fg = 0x82AAFF;
// uint32_t sel_bg = 0x174225;
// set(0, 0, "┌", border_fg, 0, 0);
// for (uint32_t c = 1; c < size.col - 1; c++)
// set(0, c, "─", border_fg, 0, 0);
// set(0, size.col - 1, "┐", border_fg, 0, 0);
// uint32_t start = session->scroll;
// uint32_t end = MIN(start + 8, session->visible.size());
// for (uint32_t row_idx = start; row_idx < end; row_idx++) {
// uint32_t r = (row_idx - start) + 1;
// auto &item = session->items[session->visible[row_idx]];
// uint32_t bg = (session->visible[row_idx] == session->select) ? sel_bg :
// 1; uint32_t fg = 0xFFFFFF; set(r, 0, "│", border_fg, 0, 0); uint32_t c =
// 1; const char *sym = item_symbol(item.kind); set(r, c++, sym,
// kind_color(item.kind), bg, 0); set(r, c++, " ", fg, bg, 0); for (size_t i
// = 0; i < item.label.size(); i++)
// set(r, c + i, (char[2]){item.label[i], 0}, fg, bg,
// item.deprecated ? CF_STRIKETHROUGH : 0);
// c += item.label.size();
// set(r, c++, " ", fg, bg, 0);
// uint32_t detail_fg = 0xAAAAAA;
// if (item.detail) {
// for (size_t i = 0; i < item.detail->size(); i++)
// set(r, c + i, (char[2]){(*item.detail)[i], 0}, detail_fg, bg, 0);
// c += item.detail->size();
// }
// uint32_t pad = size.col - 1 - c - max_kind_len;
// for (uint32_t i = 0; i < pad; i++)
// set(r, c + i, " ", fg, bg, 0);
// c += pad;
// std::string kind_name = item_kind_name(item.kind);
// for (size_t i = 0; i < kind_name.size(); i++)
// set(r, c + i, (char[2]){kind_name[i], 0}, kind_color(item.kind), bg,
// 0);
// set(r, size.col - 1, "│", border_fg, 0, 0);
// }
// uint32_t bottom = size.row - 1;
// set(bottom, 0, "└", border_fg, 0, 0);
// for (uint32_t c = 1; c < size.col - 1; c++)
// set(bottom, c, "─", border_fg, 0, 0);
// set(bottom, size.col - 1, "┘", border_fg, 0, 0);
// if (session->visible.size() > 8) {
// std::string info = std::to_string(start + 1) + "-" + std::to_string(end)
// +
// "/" + std::to_string(session->visible.size());
// for (size_t i = 0; i < info.size() && i < size.col - 2; i++)
// set(bottom, 1 + i, (char[2]){info[i], 0}, border_fg, 0, 0);
// }
// }
//
// void CompletionBox::render(Coord pos) {
// if (hidden || session->visible.empty())
// return;
// std::shared_lock lock(mtx);
// int32_t start_row = (int32_t)pos.row - (int32_t)size.row;
// if (start_row < 0)
// start_row = pos.row + 1;
// int32_t start_col = pos.col;
// Coord screen_size = get_size();
// if (start_col + size.col > screen_size.col) {
// start_col = screen_size.col - size.col;
// if (start_col < 0)
// start_col = 0;
// }
// position = {(uint32_t)start_row, (uint32_t)start_col};
// for (uint32_t r = 0; r < size.row; r++)
// for (uint32_t c = 0; c < size.col; c++)
// update(start_row + r, start_col + c, cells[r * size.col + c].utf8,
// cells[r * size.col + c].fg, cells[r * size.col + c].bg,
// cells[r * size.col + c].flags);
// if (session->items.size() > session->select &&
// session->items[session->select].documentation &&
// *session->items[session->select].documentation != "" &&
// !session->hover_dirty) {
// if (session->doc != session->select) {
// session->doc = session->select;
// session->hover.clear();
// session->hover.text = *session->items[session->select].documentation;
// session->hover.is_markup = true;
// session->hover_dirty = true;
// } else {
// if ((int32_t)position.col - (int32_t)session->hover.size.col > 0) {
// session->hover.render({position.row + session->hover.size.row,
// position.col - session->hover.size.col});
// } else {
// session->hover.render(
// {position.row + session->hover.size.row, position.col +
// size.col});
// }
// }
// }
// }

View File

@@ -1,162 +1,164 @@
#include "ui/diagnostics.h"
void DiagnosticBox::clear() {
warnings.clear();
cells.clear();
size = {0, 0};
};
void DiagnosticBox::render_first() {
if (warnings.empty())
return;
uint32_t longest_line = 8 + warnings[0].source.length();
for (auto &warn : warnings) {
uint32_t longest = 0;
uint32_t cur = 0;
for (char ch : warn.text_full)
if (ch == '\n') {
longest = MAX(longest, cur);
cur = 0;
} else {
if (ch == '\t')
cur += 3;
++cur;
}
longest = MAX(longest, cur);
longest_line = MAX(longest_line, longest + 7);
longest_line = MAX(longest_line, (uint32_t)warn.code.length() + 4);
for (auto &see_also : warn.see_also)
longest_line = MAX(longest_line, (uint32_t)see_also.length() + 4);
}
uint32_t content_width = MIN(longest_line, 150u);
size.col = content_width + 2;
cells.assign(size.col * 25, {" ", 0, 0, 0, 0, 0});
auto set = [&](uint32_t r, uint32_t c, const char *text, uint32_t fg,
uint32_t bg, uint8_t flags) {
cells[r * size.col + c] = {std::string(text), 0, fg, bg, flags, 0};
};
uint32_t base_bg = 0;
uint32_t border_fg = 0x82AAFF;
uint32_t r = 0;
if (warnings[0].source != "") {
std::string src_txt = "Source: ";
for (uint32_t i = 0; i < src_txt.length() && i < content_width; i++)
set(1, i + 1, (char[2]){src_txt[i], 0}, 0x3EAAFF, base_bg, 0);
for (uint32_t i = 0; i < warnings[0].source.length() && i < content_width;
i++)
set(1, i + 1 + src_txt.length(), (char[2]){warnings[0].source[i], 0},
0xffffff, base_bg, 0);
r++;
}
int idx = 1;
for (auto &warn : warnings) {
char buf[4];
std::snprintf(buf, sizeof(buf), "%2d", idx % 100);
std::string line_txt = std::string(buf) + ". ";
for (uint32_t i = 0; i < line_txt.length(); i++)
set(r + 1, i + 1, (char[2]){line_txt[i], 0}, 0xffffff, base_bg, 0);
if (r >= 23)
break;
const char *err_sym = "";
uint32_t c_sym = 0xAAAAAA;
switch (warn.type) {
case 1:
err_sym = "";
c_sym = 0xFF0000;
break;
case 2:
err_sym = "";
c_sym = 0xFFFF00;
break;
case 3:
err_sym = "";
c_sym = 0xFF00FF;
break;
case 4:
err_sym = "";
c_sym = 0xAAAAAA;
break;
}
std::string text = warn.text_full + " " + err_sym;
uint32_t i = 0;
while (i < text.length() && r < 23) {
uint32_t c = 4;
while (c < content_width && i < text.length()) {
if (text[i] == '\n') {
while (i < text.length() && text[i] == '\n')
i++;
break;
}
uint32_t cluster_len = grapheme_next_character_break_utf8(
text.c_str() + i, text.length() - i);
std::string cluster = text.substr(i, cluster_len);
int width = display_width(cluster.c_str(), cluster_len);
if (c + width > content_width)
break;
set(r + 1, c + 1, cluster.c_str(), c_sym, base_bg, 0);
c += width;
i += cluster_len;
for (int w = 1; w < width; w++)
set(r + 1, c - w + 1, "\x1b", c_sym, base_bg, 0);
}
r++;
}
if (r >= 23)
break;
if (warn.code != "") {
for (uint32_t i = 0; i < warn.code.length() && i + 5 < content_width; i++)
set(r + 1, i + 5, (char[2]){warn.code[i], 0}, 0x81cdc6, base_bg, 0);
r++;
}
if (r >= 23)
break;
for (std::string &see_also : warn.see_also) {
uint32_t fg = 0xB55EFF;
uint8_t colon_count = 0;
for (uint32_t i = 0; i < see_also.length() && i + 5 < content_width;
i++) {
set(r + 1, i + 5, (char[2]){see_also[i], 0}, fg, base_bg, 0);
if (see_also[i] == ':')
colon_count++;
if (colon_count == 2)
fg = 0xFFFFFF;
}
r++;
if (r >= 23)
break;
};
idx++;
}
size.row = 2 + r;
set(0, 0, "", border_fg, base_bg, 0);
for (uint32_t i = 1; i < size.col - 1; i++)
set(0, i, "", border_fg, base_bg, 0);
set(0, size.col - 1, "", border_fg, base_bg, 0);
for (uint32_t r = 1; r < size.row - 1; r++) {
set(r, 0, "", border_fg, base_bg, 0);
set(r, size.col - 1, "", border_fg, base_bg, 0);
}
set(size.row - 1, 0, "", border_fg, base_bg, 0);
for (uint32_t i = 1; i < size.col - 1; i++)
set(size.row - 1, i, "", border_fg, base_bg, 0);
set(size.row - 1, size.col - 1, "", border_fg, base_bg, 0);
cells.resize(size.col * size.row);
}
void DiagnosticBox::render(Coord pos) {
int32_t start_row = (int32_t)pos.row - (int32_t)size.row;
if (start_row < 0)
start_row = pos.row + 1;
int32_t start_col = pos.col;
Coord screen_size = get_size();
if (start_col + size.col > screen_size.col) {
start_col = screen_size.col - size.col;
if (start_col < 0)
start_col = 0;
}
for (uint32_t r = 0; r < size.row; r++)
for (uint32_t c = 0; c < size.col; c++)
update(start_row + r, start_col + c, cells[r * size.col + c].utf8,
cells[r * size.col + c].fg, cells[r * size.col + c].bg,
cells[r * size.col + c].flags);
}
// #include "ui/diagnostics.h"
//
// void DiagnosticBox::clear() {
// warnings.clear();
// cells.clear();
// size = {0, 0};
// };
//
// void DiagnosticBox::render_first() {
// if (warnings.empty())
// return;
// uint32_t longest_line = 8 + warnings[0].source.length();
// for (auto &warn : warnings) {
// uint32_t longest = 0;
// uint32_t cur = 0;
// for (char ch : warn.text_full)
// if (ch == '\n') {
// longest = MAX(longest, cur);
// cur = 0;
// } else {
// if (ch == '\t')
// cur += 3;
// ++cur;
// }
// longest = MAX(longest, cur);
// longest_line = MAX(longest_line, longest + 7);
// longest_line = MAX(longest_line, (uint32_t)warn.code.length() + 4);
// for (auto &see_also : warn.see_also)
// longest_line = MAX(longest_line, (uint32_t)see_also.length() + 4);
// }
// uint32_t content_width = MIN(longest_line, 150u);
// size.col = content_width + 2;
// cells.assign(size.col * 25, {" ", 0, 0, 0, 0, 0});
// auto set = [&](uint32_t r, uint32_t c, const char *text, uint32_t fg,
// uint32_t bg, uint8_t flags) {
// cells[r * size.col + c] = {std::string(text), 0, fg, bg, flags, 0};
// };
// uint32_t base_bg = 0;
// uint32_t border_fg = 0x82AAFF;
// uint32_t r = 0;
// if (warnings[0].source != "") {
// std::string src_txt = "Source: ";
// for (uint32_t i = 0; i < src_txt.length() && i < content_width; i++)
// set(1, i + 1, (char[2]){src_txt[i], 0}, 0x3EAAFF, base_bg, 0);
// for (uint32_t i = 0; i < warnings[0].source.length() && i <
// content_width;
// i++)
// set(1, i + 1 + src_txt.length(), (char[2]){warnings[0].source[i], 0},
// 0xffffff, base_bg, 0);
// r++;
// }
// int idx = 1;
// for (auto &warn : warnings) {
// char buf[4];
// std::snprintf(buf, sizeof(buf), "%2d", idx % 100);
// std::string line_txt = std::string(buf) + ". ";
// for (uint32_t i = 0; i < line_txt.length(); i++)
// set(r + 1, i + 1, (char[2]){line_txt[i], 0}, 0xffffff, base_bg, 0);
// if (r >= 23)
// break;
// const char *err_sym = "";
// uint32_t c_sym = 0xAAAAAA;
// switch (warn.type) {
// case 1:
// err_sym = "";
// c_sym = 0xFF0000;
// break;
// case 2:
// err_sym = "";
// c_sym = 0xFFFF00;
// break;
// case 3:
// err_sym = "";
// c_sym = 0xFF00FF;
// break;
// case 4:
// err_sym = "";
// c_sym = 0xAAAAAA;
// break;
// }
// std::string text = warn.text_full + " " + err_sym;
// uint32_t i = 0;
// while (i < text.length() && r < 23) {
// uint32_t c = 4;
// while (c < content_width && i < text.length()) {
// if (text[i] == '\n') {
// while (i < text.length() && text[i] == '\n')
// i++;
// break;
// }
// uint32_t cluster_len = grapheme_next_character_break_utf8(
// text.c_str() + i, text.length() - i);
// std::string cluster = text.substr(i, cluster_len);
// int width = display_width(cluster.c_str(), cluster_len);
// if (c + width > content_width)
// break;
// set(r + 1, c + 1, cluster.c_str(), c_sym, base_bg, 0);
// c += width;
// i += cluster_len;
// for (int w = 1; w < width; w++)
// set(r + 1, c - w + 1, "\x1b", c_sym, base_bg, 0);
// }
// r++;
// }
// if (r >= 23)
// break;
// if (warn.code != "") {
// for (uint32_t i = 0; i < warn.code.length() && i + 5 < content_width;
// i++)
// set(r + 1, i + 5, (char[2]){warn.code[i], 0}, 0x81cdc6, base_bg, 0);
// r++;
// }
// if (r >= 23)
// break;
// for (std::string &see_also : warn.see_also) {
// uint32_t fg = 0xB55EFF;
// uint8_t colon_count = 0;
// for (uint32_t i = 0; i < see_also.length() && i + 5 < content_width;
// i++) {
// set(r + 1, i + 5, (char[2]){see_also[i], 0}, fg, base_bg, 0);
// if (see_also[i] == ':')
// colon_count++;
// if (colon_count == 2)
// fg = 0xFFFFFF;
// }
// r++;
// if (r >= 23)
// break;
// };
// idx++;
// }
// size.row = 2 + r;
// set(0, 0, "┌", border_fg, base_bg, 0);
// for (uint32_t i = 1; i < size.col - 1; i++)
// set(0, i, "─", border_fg, base_bg, 0);
// set(0, size.col - 1, "┐", border_fg, base_bg, 0);
// for (uint32_t r = 1; r < size.row - 1; r++) {
// set(r, 0, "│", border_fg, base_bg, 0);
// set(r, size.col - 1, "│", border_fg, base_bg, 0);
// }
// set(size.row - 1, 0, "└", border_fg, base_bg, 0);
// for (uint32_t i = 1; i < size.col - 1; i++)
// set(size.row - 1, i, "─", border_fg, base_bg, 0);
// set(size.row - 1, size.col - 1, "┘", border_fg, base_bg, 0);
// cells.resize(size.col * size.row);
// }
//
// void DiagnosticBox::render(Coord pos) {
// int32_t start_row = (int32_t)pos.row - (int32_t)size.row;
// if (start_row < 0)
// start_row = pos.row + 1;
// int32_t start_col = pos.col;
// Coord screen_size = get_size();
// if (start_col + size.col > screen_size.col) {
// start_col = screen_size.col - size.col;
// if (start_col < 0)
// start_col = 0;
// }
// for (uint32_t r = 0; r < size.row; r++)
// for (uint32_t c = 0; c < size.col; c++)
// update(start_row + r, start_col + c, cells[r * size.col + c].utf8,
// cells[r * size.col + c].fg, cells[r * size.col + c].bg,
// cells[r * size.col + c].flags);
// }

23
src/windows/renderer.cc Normal file
View File

@@ -0,0 +1,23 @@
#include "main.h"
#include "windows/decl.h"
TileRoot root_tile;
std::vector<std::unique_ptr<TileRoot>> popups;
Window *focused_window;
void render() {
ui::bar.render(new_screen);
root_tile.render(new_screen);
for (auto &popup : popups)
popup->render(new_screen);
}
void handle_click(KeyEvent event) {
for (auto &popup : popups) {
if (popup->inside(event.mouse_x, event.mouse_y)) {
popup->handle_click(event);
return;
}
}
root_tile.handle_click(event);
}