Compare commits

..

9 Commits

Author SHA1 Message Date
syedm 10287e1750 Fix file permissions 2026-07-01 18:53:49 +01:00
syedm 013bffcad9 Cleanup 2026-04-13 19:45:58 +01:00
syedm 037f884050 Move folder 2026-04-13 10:32:46 +01:00
syedm c683754d49 Cleanup / optimizations 2026-02-12 01:01:52 +00:00
syedm e9d164d769 Rewrite diagnostics popup with new api 2026-02-11 19:51:45 +00:00
syedm 5b66f503e4 Improve highlighting structure
- switched to a sparse delta based map
- true lazy-loading to avoid any unneccessary allocations
- fixed windows management api
2026-02-11 18:18:28 +00:00
syedm d79ea4e75a Fix parser bug on newlines 2026-02-05 14:23:52 +00:00
syedm dd474cc38d Fix deadlock 2026-02-05 13:03:41 +00:00
syedm a62d4a18a8 Switch to OOP style code 2026-02-04 00:38:11 +00:00
71 changed files with 3270 additions and 3138 deletions
+5 -4
View File
@@ -19,9 +19,10 @@ CFLAGS_DEBUG :=\
-std=c++20 -Wall -Wextra \ -std=c++20 -Wall -Wextra \
-O0 -fno-inline -gsplit-dwarf \ -O0 -fno-inline -gsplit-dwarf \
-g -fno-omit-frame-pointer \ -g -fno-omit-frame-pointer \
-fsanitize=address \
-Wno-unused-command-line-argument \ -Wno-unused-command-line-argument \
-I./include -I./libs -I/home/syed/main/crib/libs/mruby/include -I./include -I./libs -I./libs/mruby/include
# C_SANITIZER := -fsanitize=address
CFLAGS_RELEASE :=\ CFLAGS_RELEASE :=\
-static --target=x86_64-linux-musl \ -static --target=x86_64-linux-musl \
@@ -32,7 +33,7 @@ CFLAGS_RELEASE :=\
-fomit-frame-pointer -DNDEBUG -s \ -fomit-frame-pointer -DNDEBUG -s \
-mllvm -vectorize-loops \ -mllvm -vectorize-loops \
-Wno-unused-command-line-argument \ -Wno-unused-command-line-argument \
-I./include -I./libs -I/home/syed/main/crib/libs/mruby/include -I./include -I./libs -I./libs/mruby/include
PCH_CFLAGS_DEBUG := $(CFLAGS_DEBUG) -x c++-header PCH_CFLAGS_DEBUG := $(CFLAGS_DEBUG) -x c++-header
PCH_CFLAGS_RELEASE := $(CFLAGS_RELEASE) -x c++-header PCH_CFLAGS_RELEASE := $(CFLAGS_RELEASE) -x c++-header
@@ -78,7 +79,7 @@ $(PCH_RELEASE): $(INCLUDE_DIR)/pch.h
$(TARGET_DEBUG): $(PCH_DEBUG) $(OBJ_DEBUG) $(UNICODE_OBJ_DEBUG) $(TARGET_DEBUG): $(PCH_DEBUG) $(OBJ_DEBUG) $(UNICODE_OBJ_DEBUG)
mkdir -p $(BIN_DIR) 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) $(TARGET_RELEASE): $(PCH_RELEASE) $(OBJ_RELEASE) $(UNICODE_OBJ_RELEASE)
mkdir -p $(BIN_DIR) mkdir -p $(BIN_DIR)
+2 -1
View File
@@ -21,7 +21,7 @@ curl https://syedm.dev/crib | sh
``` ```
Currently only for Linux.<br> Currently only for Linux.<br>
*Tested with arch linux and ubuntu and void*<br> *Tested with arch linux, ubuntu and void*<br>
## Building ## Building
@@ -134,6 +134,7 @@ crib ./filename.ext
``` ```
*If `filename.ext` does not exist, it will be created*<br> *If `filename.ext` does not exist, it will be created*<br>
*memory usage is average case 10MB + 2x size of file*<br>
## Keybindings ## Keybindings
+152 -75
View File
@@ -2,45 +2,61 @@ Copyright 2025 Syed Daanish
# TODO # TODO
# memory usage for debug build (release build will be smaller by about 25%) - Fix bug where scroll up at top of screen first line jumps to bottom
```
8K -> 13.2M - Make completions work with new system
128K -> 13.2M (expected worst case 16.6M) - And factorize renderer into its own class
128M -> 412.0M (expected worst case 2.3G) - which is just managed by the renderer
``` - and adjusment.cc will call it
- which is reused by scrollers/ensurers too (for knowing screen wrapped end)
- this will then allow inlay hints to be possible
- and also make VAI easier to implement (probably as inlay hints)
- and also allow code lens to work too
- and an api to allow line skipping / virtual text which allows for folding logic as extention
- Also time extentions and disable if they take too long / for larger files with options to force
* Next few super long boring things to do
* redo lsp threads such that no mutex needed for any rope stuff
- 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)
- 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
- 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
- which is reused by scrollers/ensurers too
- this will then allow inlay hints to be possible
- and also make VAI easier to implement
* Allow keybinds to be set in ruby * Allow keybinds to be set in ruby
* then the fun part: * ruby file for project specific runs alongside any system wide ones
- 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 libssh2 or child process stuff for remote editing (remote mode)
- Thick and thin mode (server mode) (through ssh or port) (for website only port + webhook)
- thin mode only shares screen diffing over the network - uses server settings / themes / shell
- only one user connects at a time
- provides data as a websocket with tui or gui/online modes where tui shares diffing as is (can be connected to by gui or website)
- and gui/online modes try to (if not then render onto a canvas) forward window.render_gui() and dedicated windowing system (i.e windowing is html divs)
- and window.render_gui() returns a markup code (custom markup)
- and for events i could either leave tui as-is and for markup have special event handling or i
- implement the widget system in both and have the same event handling then
- actually for true cross platform i need to make a ui lib with widgets etc. so it is workable on all platforms (so they all need only a single ui lib)
- thick only shared fileio and lsp data with local used for all else - uses local settings / themes
- multiple users connect at once but maybe either make them not be allowed to open same file or make vscode like multiediting (unsure how exactly)
- they all share same lsp instances
- they all have the same shell access
- allow having an instance that forwards thick mode to thin mode (to connect online mode to a thick server for code collaboration)
* Then allow ruby code to create and handle windows as extentions * Then allow ruby code to create and handle windows as extentions
* Then 3 inbuilt extentions being file manager, theme picker, tab selector * Then 3 inbuilt extentions being file manager, theme picker, tab selector
* make another tile type called tile tabbed that has a tab switcher and shows only one of its children at once
* it takes focus and takes some keybinds for switching and forwards the rest (it also forwards the info and adds one for itself - the selected/total tabs)
* also implement info strings for all the stuff that take focus
* Mode is a feild on a editor and there is no global "mode system" except in editors (enxtentions do their own thing)
- make mode normal and not atomic if it is truly unused across threads
- this means keybinds can be set to use the editor as insert mode only and so behave like vscode
* split lsp stuff into multiple files (not a monolithic class as it is now)
* Extentions can also be used as file openers (for like databases . diffing . images . audio etc) * Extentions can also be used as file openers (for like databases . diffing . images . audio etc)
* Local cache for state management (like undo history, cursor positions etc) (location can be set in config) * Local cache for state management (like undo history, cursor positions etc) (location can be set in config)
* make sure to write inbuilt extentions in cpp and not ruby * make sure to write inbuilt extentions in cpp and not ruby
* also move default bar and clipboard back into cpp * also move default bar and clipboard back into cpp
* all lsp popups are no longer their own classes but instead windows (extention like) in popup mode * all lsp popups are no longer their own classes but instead windows (extention like) in popup mode
* also **fix click handling to send mouse unclick to the same focus window as the click And also for drag to be same as the starter**
* skip opening binary files * skip opening binary files
* apply themeing in bg log bar lsp popus etc. to keep visual consistency * apply themeing in bg log bar lsp popus etc. to keep visual consistency
* searching/replace/Multi-Cursor (for just one lsp command for all) with pcre2 with regex (started by a slash) (disabled for large files) * searching/replace/Multi-Cursor (for just one lsp command for all) with pcre2 with regex (started by a slash) (disabled for large files)
@@ -52,10 +68,10 @@ Copyright 2025 Syed Daanish
* Add a file picker suggestion while typing a path (including stuff like ~ and .. and $HOME etc) * Add a file picker suggestion while typing a path (including stuff like ~ and .. and $HOME etc)
* allow opening directory after filemanger is sorted out. * allow opening directory after filemanger is sorted out.
* commands to: * commands to:
change pwd change pwd
load a rb file load a rb file
run a ruby command run a ruby command
close a window etc. close a window etc.
* [ ] Add mgems for most common things and a ruby library to allow combining true ruby with mruby * [ ] Add mgems for most common things and a ruby library to allow combining true ruby with mruby
* add command to set and use a file type at runtime * add command to set and use a file type at runtime
* [ ] color alpha in ini files * [ ] color alpha in ini files
@@ -63,12 +79,12 @@ Copyright 2025 Syed Daanish
* [ ] **LSP Bug:** Check why `fish-lsp` is behaving so off with completions filtering. * [ ] **LSP Bug:** Check why `fish-lsp` is behaving so off with completions filtering.
* [ ] **Line move:** fix the move line functions to work without the calculations from folds as folds are removed. * [ ] **Line move:** fix the move line functions to work without the calculations from folds as folds are removed.
* [ ] **Editor Indentation Fix:** - Main : merger indentation with the parser for more accurate results. * [ ] **Editor Indentation Fix:** - Main : merger indentation with the parser for more accurate results.
* [ ] Ignore comments/strings from parser when auto-indenting. - [ ] Ignore comments/strings from parser when auto-indenting.
* [ ] Support for stuff like bash \ and math operators in other languages and comma and line starting with a dot (like in ruby) - [ ] Support for stuff like bash \ and math operators in other languages and comma and line starting with a dot (like in ruby)
etc. etc.
* [ ] **Readme:** Update readme to show ruby based config in detail. * [ ] **Readme:** Update readme to show ruby based config in detail.
* [ ] **UI Refinement:** * [ ] **UI Refinement:**
* [ ] Finish autocomplete box style functions. - [ ] Finish autocomplete box style functions.
* [ ] **Documentation UI:** Capture `Ctrl+h` / `Ctrl+l` for scrolling documentation windows. * [ ] **Documentation UI:** Capture `Ctrl+h` / `Ctrl+l` for scrolling documentation windows.
* Cap line_tree data limit for large files and just store about a thousand previous lines maybe? (lazily loaded) * Cap line_tree data limit for large files and just store about a thousand previous lines maybe? (lazily loaded)
@@ -81,17 +97,17 @@ Copyright 2025 Syed Daanish
* allow flushing functions in ruby to tell c++ to refresh keybinds/themes etc. * allow flushing functions in ruby to tell c++ to refresh keybinds/themes etc.
* [ ] **LSP:** * [ ] **LSP:**
support snippets in completion properly support snippets in completion properly
check:: check::
pull diagnostics for ruby-lsp pull diagnostics for ruby-lsp
lsp selection range - use to highlight start / end of range maybe? lsp selection range - use to highlight start / end of range maybe?
goto definiton goto definiton
signature help signature help
document symbol for the top bar maybe? (or workspace symbol) document symbol for the top bar maybe? (or workspace symbol)
also setup workspaces also setup workspaces
Semantic highlighting Semantic highlighting
Quick fixes Quick fixes
Rename symbols Rename symbols
* Allow ruby to config lsp capabilities * Allow ruby to config lsp capabilities
@@ -113,47 +129,108 @@ Copyright 2025 Syed Daanish
* [ ] **Auto brace selection:** Add support for auto brace selection. * [ ] **Auto brace selection:** Add support for auto brace selection.
* [ ] **Tree-sitter Indent:** Attempt to allow Tree-sitter to handle indentation if possible.
### UX ### UX
* [ ] **Completion Filtering:** - [ ] **Completion Filtering:**
* [ ] Stop filtering case-sensitive. - [ ] Stop filtering case-sensitive.
* [ ] Normalize completion edits if local filtering is used. - [ ] Normalize completion edits if local filtering is used.
* [ ] **Basic Autocomplete:** Keep a list of words in the current buffer for non-LSP fallback. - [ ] **Basic Autocomplete:** Keep a list of words in the current buffer for non-LSP fallback.
### Major Features ### Features
* [ ] **Search & Replace:** - [ ] **Search & Replace:**
* [ ] Add Search/Replace UI. - [ ] Add Search/Replace UI.
* [ ] Support capture groups (`$1`, `$2`) or allow Perl regex directly. - [ ] Support capture groups (`$1`, `$2`) or allow Perl regex directly.
* [ ] Ensure virtual cursors are included in search positions. - [ ] Ensure virtual cursors are included in search positions.
* [ ] **Multi-Cursor:** - [ ] **Multi-Cursor:**
* [ ] Add virtual cursor support (edits apply to all locations). - [ ] Add virtual cursor support (edits apply to all locations).
* [ ] Add `Alt+Click` to set multiple cursors. - [ ] Add `Alt+Click` to set multiple cursors.
* [ ] Allow search and place cursor at all matches. - [ ] Allow search and place cursor at all matches.
* [ ] **Block Selection:** - [ ] **Block Selection:**
* [ ] Double-clicking a bracket selects the whole block and sets mode to `WORD`. - [ ] Double-clicking a bracket selects the whole block and sets mode to `WORD`.
### Visuals, UI & Extensions? ### Visuals, UI & Extensions?
* [ ] Add color picker/palette (as a floating extention). - [ ] Add color picker/palette (as a floating extention).
* [ ] **Git:** Add Git integration (status, diffs). - [ ] **Git:** Add Git integration (status, diffs).
* [ ] **AI/Snippets:** - [ ] **AI/Snippets:**
* [ ] Add snippets support (LuaSnip/VSnip style). - [ ] Add snippets support (LuaSnip/VSnip style).
* [ ] Add Codeium/Copilot support (using VAI virtual text) as a test phase. - [ ] Add Codeium/Copilot support (using VAI virtual text) as a test phase.
* [ ] **SQL:** Add SQL support (Viewer and Basic Editor) (as ruby extension).
* [ ] **Prolly?:** Add Splash Screen / Minigame.
- [ ] **SQL:** Add SQL support (Viewer and Basic Editor) (as ruby extension).
- [ ] **Prolly?:** Add Splash Screen / Minigame.
### Unimportant optimizations ### Unimportant optimizations
* [ ] **Performance:** - [ ] **Performance:**
* [ ] Switch JSON parser to `RapidJSON` (or similar high-performance lib). - [ ] Switch JSON parser to `RapidJSON` (or similar high-performance lib).
* [ ] Decrease usage of `std::string` in UI, LSP, warnings etc. - [ ] Decrease usage of `std::string` in UI, LSP, warnings etc.
* [ ] Also for vectors into managed memory especially for completions/lsp-stuff. - [ ] Also for vectors into managed memory especially for completions/lsp-stuff.
<!-- The pits of hell: -->
<!-- * Replace perfectly working superfast rope with a structure that stores for small files everything -->
<!-- - but for large ones it stores only what the user viewed / edited (by line number) -->
<!-- - the rest is from the disk and not entirely loaded into memory (keep as option maybe they want everything in memory?) -->
<!-- - also for the line mode still go through entire file to get and store line offsets -->
<!-- - also it stores everything prolly still as rope tho (rope of lines) (or sparse map) -->
<!-- - it stores the line text as-is which is only unicode / crlf normalized during usage -->
whyy is visible returnint x1,y2 and x2, 2y2 instead of x1,y1 and x2,y2
Render H: 1, Cursor: 0,0, Visible: 0,26 - 0,52
Render H: 2, Cursor: 0,0, Visible: 1,0 - 1,0
Render H: 3, Cursor: 0,0, Visible: 2,30 - 2,60
Render H: 4, Cursor: 0,0, Visible: 3,36 - 3,72
Render H: 5, Cursor: 0,0, Visible: 4,32 - 4,64
Render H: 6, Cursor: 0,0, Visible: 5,40 - 5,80
Render H: 7, Cursor: 0,0, Visible: 6,11 - 6,22
Render H: 8, Cursor: 0,0, Visible: 7,3 - 7,6
Render H: 9, Cursor: 0,0, Visible: 8,19 - 8,38
Render H: 10, Cursor: 0,0, Visible: 9,76 - 9,152
Render H: 11, Cursor: 0,0, Visible: 10,50 - 10,100
Render H: 12, Cursor: 0,0, Visible: 11,29 - 11,58
Render H: 13, Cursor: 0,0, Visible: 12,49 - 12,98
Render H: 14, Cursor: 0,0, Visible: 13,32 - 13,64
Render H: 15, Cursor: 0,0, Visible: 14,30 - 14,60
Render H: 16, Cursor: 0,0, Visible: 15,56 - 15,112
Render H: 17, Cursor: 0,0, Visible: 16,42 - 16,84
Render H: 18, Cursor: 0,0, Visible: 17,20 - 17,40
Render H: 19, Cursor: 0,0, Visible: 18,31 - 18,62
Render H: 20, Cursor: 0,0, Visible: 19,77 - 19,154
Render H: 21, Cursor: 0,0, Visible: 20,77 - 20,154
Render H: 22, Cursor: 0,0, Visible: 21,44 - 21,88
Render H: 23, Cursor: 0,0, Visible: 22,56 - 22,112
Render H: 24, Cursor: 0,0, Visible: 23,13 - 23,26
Render H: 25, Cursor: 0,0, Visible: 24,3 - 24,6
Render H: 26, Cursor: 0,0, Visible: 25,24 - 25,48
Render H: 27, Cursor: 0,0, Visible: 26,32 - 26,64
Render H: 28, Cursor: 0,0, Visible: 27,38 - 27,76
Render H: 29, Cursor: 0,0, Visible: 28,1 - 28,2
Render H: 30, Cursor: 0,0, Visible: 29,0 - 29,0
Render H: 31, Cursor: 0,0, Visible: 30,30 - 30,60
Render H: 32, Cursor: 0,0, Visible: 31,19 - 31,38
Render H: 33, Cursor: 0,0, Visible: 32,76 - 32,152
Render H: 34, Cursor: 0,0, Visible: 33,50 - 33,100
Render H: 35, Cursor: 0,0, Visible: 34,36 - 34,72
Render H: 36, Cursor: 0,0, Visible: 35,66 - 35,132
Render H: 37, Cursor: 0,0, Visible: 36,12 - 36,24
Render H: 38, Cursor: 0,0, Visible: 37,13 - 37,26
Render H: 39, Cursor: 0,0, Visible: 38,17 - 38,34
Render H: 40, Cursor: 0,0, Visible: 39,37 - 39,74
Render H: 41, Cursor: 0,0, Visible: 40,16 - 40,32
Render H: 42, Cursor: 0,0, Visible: 41,23 - 41,46
Render H: 43, Cursor: 0,0, Visible: 42,15 - 42,30
Render H: 44, Cursor: 0,0, Visible: 43,13 - 43,26
Render H: 45, Cursor: 0,0, Visible: 44,5 - 44,10
Render H: 46, Cursor: 0,0, Visible: 45,41 - 45,82
Render H: 47, Cursor: 0,0, Visible: 46,12 - 46,24
Render H: 48, Cursor: 0,0, Visible: 47,22 - 47,44
Render H: 49, Cursor: 0,0, Visible: 48,24 - 48,48
Render H: 50, Cursor: 0,0, Visible: 49,28 - 49,56
Render H: 51, Cursor: 0,0, Visible: 50,26 - 50,52
Render H: 52, Cursor: 0,0, Visible: 51,20 - 51,40
Render H: 53, Cursor: 0,0, Visible: 52,74 - 52,148
-46
View File
@@ -1,46 +0,0 @@
#ifndef EDITOR_COMPLETIONS_H
#define EDITOR_COMPLETIONS_H
#include "pch.h"
#include "ui/completionbox.h"
#include "ui/hover.h"
#include "utils/utils.h"
struct CompletionItem {
std::string label;
uint8_t kind;
std::optional<std::string> detail;
std::optional<std::string> documentation;
bool is_markup = false;
bool deprecated = false;
bool asis = true;
std::string sort;
std::string filter;
bool snippet = false;
std::vector<TextEdit> edits;
json original;
std::vector<char> end_chars;
};
struct CompletionSession {
std::shared_mutex mtx;
bool active = false;
Coord hook;
std::optional<std::string> prefix;
uint32_t select = 0;
uint32_t scroll = 0;
std::vector<CompletionItem> items;
std::vector<uint32_t> visible;
bool complete = true;
std::optional<char> trigger_char;
uint8_t trigger = 0;
CompletionBox box;
HoverBox hover;
uint32_t doc = UINT32_MAX;
std::atomic<bool> hover_dirty = false;
int version;
CompletionSession() : box(this) {}
};
#endif
+135 -128
View File
@@ -1,16 +1,18 @@
#ifndef EDITOR_H #ifndef EDITOR_H
#define EDITOR_H #define EDITOR_H
#include "editor/completions.h" #include "editor/decl.h"
#include "editor/hooks.h"
#include "editor/indents.h" #include "editor/indents.h"
#include "editor/visual.h"
#include "extentions/diagnostics.h"
#include "extentions/hover.h"
#include "io/knot.h" #include "io/knot.h"
#include "io/sysio.h" #include "io/sysio.h"
#include "syntax/extras.h" #include "syntax/extras.h"
#include "syntax/parser.h" #include "syntax/parser.h"
#include "ui/completionbox.h"
#include "ui/diagnostics.h"
#include "ui/hover.h"
#include "utils/utils.h" #include "utils/utils.h"
#include "windows/decl.h"
#define CHAR 0 #define CHAR 0
#define WORD 1 #define WORD 1
@@ -19,144 +21,149 @@
#define EXTRA_META 2 #define EXTRA_META 2
#define INDENT_WIDTH 2 #define INDENT_WIDTH 2
struct Editor { struct Editor : Window {
std::string filename; std::string filename = "";
std::string uri; std::string uri = "";
Knot *root; Knot *root = nullptr;
std::shared_mutex knot_mtx; Coord cursor = {0, 0};
Coord cursor; uint32_t cursor_preffered = 0;
uint32_t cursor_preffered; Coord selection = {0, 0};
Coord selection; bool selection_active = false;
bool selection_active; bool unix_eol = true;
bool unix_eol; int selection_type = 0;
int selection_type; Coord size = {0, 0};
Coord position; Coord scroll = {0, 0};
Coord size; Language lang = {};
Coord scroll; bool jumper_set = false;
Language lang; std::vector<VWarn> warnings = {};
uint32_t hooks[94]; bool warnings_dirty = false;
bool jumper_set; VAI ai = {};
std::shared_mutex v_mtx;
std::vector<VWarn> warnings;
bool warnings_dirty;
VAI ai;
std::shared_mutex lsp_mtx; std::shared_mutex lsp_mtx;
std::shared_ptr<struct LSPInstance> lsp; std::atomic<struct LSPInstance *> lsp = nullptr;
bool hover_active; HoverBox *hover_popup = init_hover();
HoverBox hover; DiagnosticBox *diagnostic_popup = init_diagnostic();
bool diagnostics_active;
DiagnosticBox diagnostics;
std::atomic<int> lsp_version = 1; std::atomic<int> lsp_version = 1;
CompletionSession completion; IndentationEngine indents = {};
IndentationEngine indents; HookEngine hooks = {};
Parser *parser; Parser *parser = nullptr;
ExtraHighlighter extra_hl; ExtraHighlighter extra_hl = {};
bool is_css_color; bool is_css_color = false;
};
Editor *new_editor(const char *filename_arg, Coord position, Coord size, Editor(const char *filename_arg, uint8_t eol);
uint8_t eol); ~Editor();
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,
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) { void render(std::vector<ScreenCell> &buffer, Coord size, Coord pos) override;
for (auto &hook : editor->hooks) void handle_event(KeyEvent event) override;
if (hook > line) void handle_click(KeyEvent event, Coord size) override;
hook += rows; void handle_command(std::string &command) override;
} void work() override;
std::array<std::string, 5> bar_info() override { return {}; };
inline void apply_hook_deletion(Editor *editor, uint32_t removal_start, void save();
uint32_t removal_end) { void cursor_up(uint32_t number);
for (auto &hook : editor->hooks) void cursor_down(uint32_t number);
if (hook > removal_start) void cursor_left(uint32_t number);
hook -= removal_end - removal_start + 1; void cursor_right(uint32_t number);
} void move_line_down();
void move_line_up();
inline static void utf8_normalize_edit(Editor *editor, TextEdit *edit) { void scroll_up(uint32_t number);
std::shared_lock lock(editor->knot_mtx); void scroll_down(uint32_t number);
if (edit->start.row > editor->root->line_count) { void ensure_cursor();
edit->start.row = editor->root->line_count; void ensure_scroll();
edit->start.col = UINT32_MAX;
} void edit_erase(Coord pos, int64_t len);
if (edit->end.row > editor->root->line_count) { void edit_insert(Coord pos, char *data, uint32_t len);
edit->end.row = editor->root->line_count; void edit_replace(Coord start, Coord end, const char *text, uint32_t len);
edit->end.col = UINT32_MAX;
} Coord click_coord(uint32_t x, uint32_t y);
LineIterator *it = begin_l_iter(editor->root, edit->start.row);
if (!it) char *get_selection(uint32_t *out_len, Coord *out_start);
return; void selection_bounds(Coord *out_start, Coord *out_end);
uint32_t len;
char *line = next_line(it, &len); void insert_str(char *c, uint32_t len);
if (!line) { void insert_char(char c);
void normal_mode();
void backspace_edit();
void delete_prev_word();
void delete_next_word();
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);
// Not used by core editor, but can be used by
// extentions (due to cursor movement complexity)
void format_full(int version);
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 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 > this->root->line_count) {
edit->end.row = this->root->line_count;
edit->end.col = UINT32_MAX;
}
LineIterator *it = begin_l_iter(this->root, edit->start.row);
if (!it)
return;
uint32_t len;
char *line = next_line(it, &len);
if (!line) {
free(it->buffer);
free(it);
return;
}
if (edit->start.col < len)
edit->start.col = utf16_offset_to_utf8(line, len, edit->start.col);
else
edit->start.col = len;
if (edit->end.row == edit->start.row) {
if (edit->end.col < len)
edit->end.col = utf16_offset_to_utf8(line, len, edit->end.col);
else
edit->end.col = len;
free(it->buffer);
free(it);
return;
}
free(it->buffer); free(it->buffer);
free(it); free(it);
return; it = begin_l_iter(this->root, edit->end.row);
} if (!it)
if (edit->start.col < len) return;
edit->start.col = utf16_offset_to_utf8(line, len, edit->start.col); line = next_line(it, &len);
else if (!line) {
edit->start.col = len; free(it->buffer);
if (edit->end.row == edit->start.row) { free(it);
return;
}
if (edit->end.col < len) if (edit->end.col < len)
edit->end.col = utf16_offset_to_utf8(line, len, edit->end.col); edit->end.col = utf16_offset_to_utf8(line, len, edit->end.col);
else else
edit->end.col = len; edit->end.col = len;
free(it->buffer); free(it->buffer);
free(it); free(it);
return;
} }
free(it->buffer); };
free(it);
it = begin_l_iter(editor->root, edit->end.row);
if (!it)
return;
line = next_line(it, &len);
if (!line) {
free(it->buffer);
free(it);
return;
}
if (edit->end.col < len)
edit->end.col = utf16_offset_to_utf8(line, len, edit->end.col);
else
edit->end.col = len;
free(it->buffer);
free(it);
}
#endif #endif
-20
View File
@@ -3,24 +3,4 @@
#include "editor/editor.h" #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 #endif
+91
View File
@@ -0,0 +1,91 @@
#ifndef EDITOR_HOOKS_H
#define EDITOR_HOOKS_H
#include "utils/utils.h"
struct HookEngine {
uint32_t hooks[94];
std::vector<std::pair<uint32_t, char>> v;
std::vector<std::pair<uint32_t, char>>::iterator it;
HookEngine() {
for (int i = 0; i < 94; i++)
hooks[i] = UINT32_MAX;
v.reserve(94);
}
inline void edit(uint32_t line, uint32_t removed_rows,
uint32_t inserted_rows) {
int64_t delta = (int64_t)inserted_rows - (int64_t)removed_rows;
for (int i = 0; i < 94; i++) {
uint32_t &h = hooks[i];
if (h == UINT32_MAX)
continue;
if (h < line)
continue;
if (removed_rows > 0 && h < line + removed_rows) {
h = UINT32_MAX;
continue;
}
h = (uint32_t)((int64_t)h + delta);
}
}
inline void set(char c, uint32_t line) {
if (c < '!' || c > '~')
return;
int idx = c - '!';
for (int i = 0; i < 94; i++) {
if (hooks[i] == line) {
hooks[i] = UINT32_MAX;
break;
}
}
hooks[idx] = line;
}
inline bool get(char c, uint32_t *out_line) const {
if (c < '!' || c > '~')
return false;
uint32_t h = hooks[c - '!'];
if (h == UINT32_MAX)
return false;
*out_line = h;
return true;
}
inline void clear_line(uint32_t line) {
for (int i = 0; i < 94; i++)
if (hooks[i] == line)
hooks[i] = UINT32_MAX;
}
inline void clear(char c) {
if (c < '!' || c > '~')
return;
hooks[c - '!'] = UINT32_MAX;
}
void start_iter(uint32_t scroll_line) {
for (int i = 0; i < 94; i++)
if (hooks[i] != UINT32_MAX)
v.push_back({hooks[i], (char)('!' + i)});
std::sort(v.begin(), v.end(),
[](const auto &a, const auto &b) { return a.first < b.first; });
it = std::lower_bound(
v.begin(), v.end(), scroll_line,
[](const auto &p, uint32_t line) { return p.first < line; });
}
char next(uint32_t line_index) {
if (it != v.end() && it->first == line_index) {
char c = it->second;
it++;
return c;
}
return '\0';
}
};
#endif
+113
View File
@@ -0,0 +1,113 @@
#include "editor/decl.h"
#include "io/knot.h"
#include "io/sysio.h"
#include "utils/utils.h"
struct GraphemeIterator {
char *line{nullptr};
uint32_t len{0};
GraphemeIterator(char *line, uint32_t len) : line(line), len(len) {}
char *next(uint32_t *o_len, uint32_t *o_width) {
if (!line || len == 0)
return nullptr;
char *cur = line;
uint32_t g = grapheme_next_character_break_utf8(cur, len);
if (o_width)
*o_width = display_width(cur, g);
if (o_len)
*o_len = g;
line += g;
len -= g;
return cur;
}
};
struct VisualIterator {
LineIterator *it{nullptr};
uint32_t line_index{UINT32_MAX};
uint32_t offset{0};
uint32_t len{0};
bool first{true};
char *line{nullptr};
uint32_t render_width{io::cols};
std::stack<std::pair<Coord, Coord>> prev_stack;
VisualIterator(Knot *root, Coord pos, uint32_t width)
: it(begin_l_iter(root, pos.row)), line_index(pos.row - 1),
offset(pos.col), render_width(width) {}
std::pair<Coord, Coord> prev() {
if (!it)
return {{UINT32_MAX, UINT32_MAX}, {UINT32_MAX, UINT32_MAX}};
if (prev_stack.empty())
return _prev();
auto ret = prev_stack.top();
offset = ret.first.col;
prev_stack.pop();
return ret;
}
std::pair<Coord, Coord> _prev() {
if (!it)
return {{UINT32_MAX, UINT32_MAX}, {UINT32_MAX, UINT32_MAX}};
line_index--;
line = prev_line(it, &len);
if (!line)
return {{UINT32_MAX, UINT32_MAX}, {UINT32_MAX, UINT32_MAX}};
if (len > 0 && line[len - 1] == '\n')
len--;
offset = first ? offset : 0;
first = false;
GraphemeIterator g(line + offset, len - offset);
uint32_t o_len, o_width, rendered = 0, advance = 0;
while (g.next(&o_len, &o_width)) {
if (rendered + o_width > render_width) {
prev_stack.push({{line_index, offset}, {line_index, offset + advance}});
offset += advance;
rendered = 0;
advance = 0;
}
advance += o_len;
rendered += o_width;
}
return {{line_index, offset}, {line_index, offset + advance}};
}
std::pair<Coord, Coord> next() {
if (!it)
return {{UINT32_MAX, UINT32_MAX}, {UINT32_MAX, UINT32_MAX}};
if (!line) {
line_index++;
line = next_line(it, &len);
if (!line)
return {{UINT32_MAX, UINT32_MAX}, {UINT32_MAX, UINT32_MAX}};
if (len > 0 && line[len - 1] == '\n')
len--;
offset = first ? offset : 0;
first = false;
}
GraphemeIterator g(line + offset, len - offset);
uint32_t o_len, o_width, rendered = 0, advance = 0;
while (g.next(&o_len, &o_width)) {
if (rendered + o_width > render_width) {
offset += advance;
return {{line_index, offset - advance}, {line_index, offset}};
}
advance += o_len;
rendered += o_width;
}
offset += advance;
if (offset >= len)
line = nullptr;
return {{line_index, offset}, {line_index, offset + advance}};
}
~VisualIterator() {
if (!it)
return;
free(it->buffer);
free(it);
}
};
+64
View File
@@ -0,0 +1,64 @@
#ifndef EXTENTION_COMPLETION_H
#define EXTENTION_COMPLETION_H
#include "editor/decl.h"
#include "io/sysio.h"
#include "pch.h"
#include "utils/utils.h"
#include "windows/decl.h"
struct CompletionItem {
std::string label;
uint8_t kind;
std::optional<std::string> detail;
std::optional<std::string> documentation;
bool is_markup = false;
bool deprecated = false;
bool asis = true;
std::string sort;
std::string filter;
bool snippet = false;
std::vector<TextEdit> edits;
json original;
std::vector<char> end_chars;
};
struct CompletionBox : Popup {
struct CompletionSession *session;
CompletionBox() { this->hidden = true; }
void render(std::vector<ScreenCell> &buffer, Coord size, Coord pos) override;
void handle_click(KeyEvent, Coord) override { this->hidden = true; }
~CompletionBox() {}
};
struct CompletionSession {
struct Editor *editor;
std::shared_mutex mtx;
bool active = false;
Coord hook;
uint32_t select = 0;
uint32_t scroll = 0;
std::vector<CompletionItem> items;
std::vector<uint32_t> visible;
bool complete = true;
std::optional<char> trigger_char;
uint8_t trigger = 0;
CompletionBox *box = nullptr;
uint32_t doc = UINT32_MAX;
std::atomic<bool> hover_dirty = false;
int version;
CompletionSession(Editor *editor) : editor(editor) {}
void resolve_doc();
void accept();
void next();
void prev();
void choose(uint8_t index);
void handle(KeyEvent event);
};
CompletionBox *init_completion_box(CompletionSession *session);
void completion_request(Editor *editor);
void completion_filter(Editor *editor);
#endif
+22
View File
@@ -0,0 +1,22 @@
#ifndef EXTENTION_DIAGNOSTICS_H
#define EXTENTION_DIAGNOSTICS_H
#include "editor/decl.h"
#include "io/sysio.h"
#include "pch.h"
#include "utils/utils.h"
#include "windows/decl.h"
struct DiagnosticBox : Popup {
std::vector<VWarn> warnings;
DiagnosticBox() { this->hidden = true; }
void clear() { this->warnings.clear(); }
void render(std::vector<ScreenCell> &buffer, Coord size, Coord pos) override;
void handle_click(KeyEvent, Coord) override { this->hidden = true; };
~DiagnosticBox() {};
};
DiagnosticBox *init_diagnostic();
#endif
+37
View File
@@ -0,0 +1,37 @@
#ifndef EXTENTION_HOVER_H
#define EXTENTION_HOVER_H
#include "io/sysio.h"
#include "pch.h"
#include "utils/utils.h"
#include "windows/decl.h"
struct HoverBox : Popup {
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 ev, Coord) override {
if (ev.mouse_button == SCROLL_BTN && ev.mouse_state == SCROLL) {
this->scroll(ev.mouse_direction == SCROLL_UP ? -1 : 1);
return;
}
this->hidden = true;
};
~HoverBox() {};
};
HoverBox *init_hover();
#endif
+9 -10
View File
@@ -96,20 +96,19 @@ inline bool is_empty_cell(const ScreenCell &c) {
return c.utf8.empty() || c.utf8 == " " || c.utf8 == "\x1b"; return c.utf8.empty() || c.utf8 == " " || c.utf8 == "\x1b";
} }
namespace io {
extern std::vector<ScreenCell> new_screen;
extern uint32_t rows, cols;
extern bool show_cursor;
extern std::vector<ScreenCell> old_screen;
extern termios orig_termios;
} // namespace io
Coord start_screen(); Coord start_screen();
void end_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, void set_cursor(uint8_t row, uint8_t col, uint32_t type,
bool show_cursor_param); bool show_cursor_param);
void render(); void io_render();
Coord get_size();
KeyEvent read_key(); KeyEvent read_key();
+416 -61
View File
@@ -2,59 +2,19 @@
#define LSP_H #define LSP_H
#include "editor/editor.h" #include "editor/editor.h"
#include "main.h"
#include "pch.h" #include "pch.h"
#include "utils/utils.h" #include "utils/utils.h"
#include <cstdint>
#include <sys/types.h>
struct LSPPending { #define LSP_TIMEOUT_START 3000
Editor *editor = nullptr; #define LSP_TIMEOUT_QUIT_NORMAL 300
std::function<void(Editor *, const json &)> callback; #define LSP_TIMEOUT_QUIT_FORCE 80
};
// TODO: Defer any editor mutation to main thread to get rid of void lsp_worker();
// all mutex locks on the editor rope.
// struct LSPPendingResponse {
// LSPPending *pending = nullptr;
// json message;
// };
struct LSPOpenRequest { static const json client_capabilities = {
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>>
active_lsps;
extern Queue<LSPOpenRequest> lsp_open_queue;
static json client_capabilities = {
{"general", {{"positionEncodings", {"utf-16"}}}}, {"general", {{"positionEncodings", {"utf-16"}}}},
{"textDocument", {"textDocument",
{{"publishDiagnostics", {{"relatedInformation", true}}}, {{"publishDiagnostics", {{"relatedInformation", true}}},
@@ -78,20 +38,415 @@ static json client_capabilities = {
{"contextSupport", true}, {"contextSupport", true},
{"insertTextMode", 1}}}}}}; {"insertTextMode", 1}}}}}};
void lsp_send(std::shared_ptr<LSPInstance> lsp, json message, struct LSPMessage {
LSPPending *pending); Editor *editor = nullptr;
void lsp_worker(); json message;
std::function<void(const LSPMessage &)> callback;
};
std::shared_ptr<LSPInstance> get_or_init_lsp(std::string lsp_id); namespace lsp {
void clean_lsp(std::shared_ptr<LSPInstance> lsp, std::string lsp_id); extern std::mutex lsp_mutex;
void close_lsp(std::string lsp_id); extern std::unordered_map<std::string, std::unique_ptr<LSPInstance>>
std::optional<json> read_lsp_message(int fd); active_lsps;
extern Queue<std::string> need_opening;
extern std::unordered_set<std::string> opened;
extern std::vector<Editor *> new_editors;
extern Queue<std::unique_ptr<LSPMessage>> response_queue;
} // namespace lsp
void open_editor(std::shared_ptr<LSPInstance> lsp, struct LSPInstance {
std::pair<Language, Editor *> entry); const LSP *lsp_info;
void request_add_to_lsp(Language language, Editor *editor); std::string root_dir;
void add_to_lsp(Language language, Editor *editor); int pid{-1};
void remove_from_lsp(Editor *editor); int stdin_fd{-1};
void lsp_handle(std::shared_ptr<LSPInstance> lsp, json message); 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<Editor *> editors;
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};
uint32_t waited = 0;
while (waited < LSP_TIMEOUT_START) {
poll(&pfd, 1, 50);
if (pfd.revents & POLLIN)
break;
if (!running)
return;
waited += 50;
}
if (!(pfd.revents & POLLIN)) {
exited = true;
return;
}
json response = *read_lsp_message();
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 (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() {
uint32_t timeout =
running ? LSP_TIMEOUT_QUIT_NORMAL : LSP_TIMEOUT_QUIT_FORCE;
for (auto &ed : editors)
ed->lsp.store(nullptr);
initialized = false;
exited = true;
if (pid == -1)
return;
json shutdown = {
{"jsonrpc", "2.0"}, {"id", ++last_id}, {"method", "shutdown"}};
send_raw(shutdown);
pollfd pfd{stdout_fd, POLLIN, 0};
poll(&pfd, 1, timeout);
if (pfd.revents & POLLIN) {
json exit_msg = {{"jsonrpc", "2.0"}, {"method", "exit"}};
send_raw(exit_msg);
uint32_t waited = 0;
while (waited < timeout) {
int status;
pid_t res = waitpid(pid, &status, WNOHANG);
if (res == pid)
break;
std::this_thread::sleep_for(10ms);
waited += 10;
}
}
close(stdin_fd);
close(stdout_fd);
if (kill(pid, 0) == 0) {
kill(pid, SIGKILL);
waitpid(pid, nullptr, 0);
}
pid = -1;
}
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) {
editors.push_back(ed);
ed->lsp.store(this);
char *buf = read(ed->root, 0, ed->root->char_count);
std::string text(buf);
free(buf);
json message = {{"jsonrpc", "2.0"},
{"method", "textDocument/didOpen"},
{"params",
{{"textDocument",
{{"uri", ed->uri},
{"languageId", ed->lang.name},
{"version", 1},
{"text", text}}}}}};
send_raw(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(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(std::move(response));
else
lsp_handle(*msg);
}
}
}
inline static void editor_handle_wrapper(const LSPMessage &message) {
message.editor->lsp_handle(message.message);
}
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 #endif
+4 -2
View File
@@ -12,8 +12,10 @@
extern std::atomic<bool> running; extern std::atomic<bool> running;
extern std::atomic<uint8_t> mode; extern std::atomic<uint8_t> mode;
extern std::vector<struct Editor *> editors; extern fs::path pwd;
extern uint8_t current_editor;
namespace ui {
extern Bar bar; extern Bar bar;
} // namespace ui
#endif #endif
+2
View File
@@ -31,6 +31,7 @@ extern "C" {
#include <filesystem> #include <filesystem>
#include <fstream> #include <fstream>
#include <functional> #include <functional>
#include <immintrin.h>
#include <limits.h> #include <limits.h>
#include <map> #include <map>
#include <mutex> #include <mutex>
@@ -50,6 +51,7 @@ extern "C" {
#include <thread> #include <thread>
#include <unistd.h> #include <unistd.h>
#include <unordered_map> #include <unordered_map>
#include <unordered_set>
#include <vector> #include <vector>
using json = nlohmann::json; using json = nlohmann::json;
+3 -4
View File
@@ -3,6 +3,7 @@
#include "syntax/decl.h" #include "syntax/decl.h"
#include "utils/utils.h" #include "utils/utils.h"
#include "windows/decl.h"
namespace fs = std::filesystem; 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, std::string parse_custom(std::vector<Token> *tokens, mrb_value parser_block,
const char *line, uint32_t len, std::string state, const char *line, uint32_t len, std::string state,
uint32_t c_line); uint32_t c_line);
BarLine bar_contents(uint8_t mode, std::string lang_name, uint32_t warnings, BarLine bar_contents(uint8_t mode, uint32_t width, std::string foldername,
std::string lsp_name, std::string filename, Window *window);
std::string foldername, uint32_t line, uint32_t max_line,
uint32_t width);
#endif #endif
+10 -6
View File
@@ -177,7 +177,7 @@ module C
color: 0x6e1516, color: 0x6e1516,
symbol: "", symbol: "",
extensions: ["erb"], extensions: ["erb"],
lsp: "ruby-lsp" lsp: "emmet-language-server"
}, },
lua: { lua: {
color: 0x36a3d9, color: 0x36a3d9,
@@ -331,7 +331,7 @@ module C
@b_startup = nil @b_startup = nil
@b_shutdown = nil @b_shutdown = nil
@b_bar = proc do |info| @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 # puts info.inspect
mode_color = 0x82AAFF mode_color = 0x82AAFF
mode_symbol = " " mode_symbol = " "
@@ -352,11 +352,15 @@ module C
mode_color = 0xF29CC3 mode_color = 0xF29CC3
mode_symbol = "" mode_symbol = ""
end end
lang_info = C.languages[info[:lang_name]] lang_info = C.languages[:default]
if lang_info.nil? filename = ""
lang_info = C.languages[:default] 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[:data][1]) if info[:data][1] != ""
end end
filename = File.basename(info[:filename])
starting = " #{mode_symbol} #{info[:mode].to_s.upcase}  #{lang_info[:symbol]} #{filename}" starting = " #{mode_symbol} #{info[:mode].to_s.upcase}  #{lang_info[:symbol]} #{filename}"
highlights = [] highlights = []
highlights << { fg: 0x0b0e14, bg: mode_color, flags: 1 << 1, start: 0, length: 10 } highlights << { fg: 0x0b0e14, bg: mode_color, flags: 1 << 1, start: 0, length: 10 }
+342 -334
View File
@@ -1,8 +1,8 @@
#pragma once #pragma once
constexpr unsigned char _tmp___crib_precompiled_mrb[] = { 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, 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, 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, 0x00, 0x69, 0x01, 0x00, 0x6b, 0x01, 0x01, 0x01,
0x11, 0x01, 0x68, 0x01, 0x02, 0x69, 0x01, 0x02, 0x11, 0x01, 0x68, 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, 0x05, 0x5c, 0x21, 0x0f, 0x5e, 0x1a, 0x04, 0x10, 0x1b, 0x12, 0x10, 0x1c,
0x02, 0x0f, 0x1d, 0x00, 0x6e, 0x15, 0x16, 0x10, 0x1e, 0x03, 0x5c, 0x1f, 0x02, 0x0f, 0x1d, 0x00, 0x6e, 0x15, 0x16, 0x10, 0x1e, 0x03, 0x5c, 0x1f,
0x38, 0x10, 0x20, 0x04, 0x5c, 0x21, 0x42, 0x52, 0x21, 0x01, 0x10, 0x22, 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, 0x02, 0x0f, 0x1f, 0x00, 0x36, 0xa3, 0xd9, 0x10, 0x20, 0x03, 0x5c, 0x21,
0x43, 0x10, 0x22, 0x04, 0x5c, 0x23, 0x44, 0x52, 0x23, 0x01, 0x10, 0x24, 0x43, 0x10, 0x22, 0x04, 0x5c, 0x23, 0x44, 0x52, 0x23, 0x01, 0x10, 0x24,
0x05, 0x5c, 0x25, 0x17, 0x5e, 0x1e, 0x04, 0x10, 0x1f, 0x14, 0x10, 0x20, 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, 0x63, 0x6f, 0x70, 0x79, 0x00, 0x00, 0x08, 0x40, 0x62, 0x5f, 0x70, 0x61,
0x73, 0x74, 0x65, 0x00, 0x00, 0x0e, 0x40, 0x62, 0x5f, 0x66, 0x69, 0x6c, 0x73, 0x74, 0x65, 0x00, 0x00, 0x0e, 0x40, 0x62, 0x5f, 0x66, 0x69, 0x6c,
0x65, 0x5f, 0x64, 0x65, 0x74, 0x65, 0x63, 0x74, 0x00, 0x00, 0x00, 0x03, 0x65, 0x5f, 0x64, 0x65, 0x74, 0x65, 0x63, 0x74, 0x00, 0x00, 0x00, 0x03,
0x55, 0x00, 0x09, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xb4, 0x00, 0x09, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
0x1f, 0x39, 0x04, 0x00, 0x00, 0x0f, 0x03, 0x00, 0x82, 0xaa, 0xff, 0x5c, 0x7b, 0x39, 0x04, 0x00, 0x00, 0x0f, 0x03, 0x00, 0x82, 0xaa, 0xff, 0x5c,
0x04, 0x00, 0x01, 0x09, 0x01, 0x10, 0x0a, 0x00, 0x23, 0x09, 0x10, 0x0a, 0x04, 0x00, 0x01, 0x09, 0x01, 0x10, 0x0a, 0x00, 0x23, 0x09, 0x10, 0x0a,
0x01, 0x01, 0x0b, 0x09, 0x32, 0x0a, 0x02, 0x01, 0x28, 0x0a, 0x00, 0x0c, 0x01, 0x01, 0x0b, 0x09, 0x32, 0x0a, 0x02, 0x01, 0x28, 0x0a, 0x00, 0x0c,
0x0f, 0x03, 0x00, 0x82, 0xaa, 0xff, 0x5c, 0x04, 0x01, 0x26, 0x00, 0x68, 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, 0x5c, 0x04, 0x04, 0x26, 0x00, 0x1a, 0x10, 0x0a, 0x06, 0x01, 0x0b, 0x09,
0x32, 0x0a, 0x02, 0x01, 0x28, 0x0a, 0x00, 0x0c, 0x0f, 0x03, 0x00, 0xf2, 0x32, 0x0a, 0x02, 0x01, 0x28, 0x0a, 0x00, 0x0c, 0x0f, 0x03, 0x00, 0xf2,
0x9c, 0xc3, 0x5c, 0x04, 0x05, 0x26, 0x00, 0x00, 0x1d, 0x09, 0x07, 0x33, 0x9c, 0xc3, 0x5c, 0x04, 0x05, 0x26, 0x00, 0x00, 0x1d, 0x09, 0x07, 0x33,
0x09, 0x08, 0x01, 0x0a, 0x01, 0x10, 0x0b, 0x09, 0x23, 0x0a, 0x23, 0x09, 0x09, 0x08, 0x10, 0x0a, 0x09, 0x23, 0x09, 0x01, 0x05, 0x09, 0x5c, 0x06,
0x01, 0x05, 0x09, 0x01, 0x09, 0x05, 0x29, 0x09, 0x00, 0x03, 0x26, 0x00, 0x06, 0x01, 0x09, 0x01, 0x10, 0x0a, 0x0a, 0x23, 0x09, 0x06, 0x0a, 0x23,
0x0e, 0x1d, 0x09, 0x07, 0x33, 0x09, 0x08, 0x10, 0x0a, 0x0a, 0x23, 0x09, 0x09, 0x5c, 0x0a, 0x07, 0x4d, 0x09, 0x28, 0x09, 0x00, 0x73, 0x01, 0x09,
0x01, 0x05, 0x09, 0x1d, 0x09, 0x0b, 0x01, 0x0a, 0x01, 0x10, 0x0b, 0x0c, 0x01, 0x10, 0x0a, 0x0a, 0x23, 0x09, 0x08, 0x0a, 0x23, 0x09, 0x5c, 0x0a,
0x23, 0x0a, 0x32, 0x09, 0x0d, 0x01, 0x01, 0x06, 0x09, 0x5c, 0x09, 0x06, 0x06, 0x32, 0x09, 0x0b, 0x01, 0x28, 0x09, 0x00, 0x17, 0x1d, 0x09, 0x07,
0x01, 0x0a, 0x04, 0x5d, 0x09, 0x5c, 0x0a, 0x06, 0x5d, 0x09, 0x01, 0x0a, 0x33, 0x09, 0x08, 0x01, 0x0a, 0x01, 0x10, 0x0b, 0x0a, 0x23, 0x0a, 0x08,
0x01, 0x10, 0x0b, 0x00, 0x23, 0x0a, 0x33, 0x0a, 0x0e, 0x33, 0x0a, 0x0f, 0x0b, 0x23, 0x0a, 0x23, 0x09, 0x01, 0x05, 0x09, 0x01, 0x09, 0x05, 0x29,
0x5d, 0x09, 0x5c, 0x0a, 0x07, 0x5d, 0x09, 0x01, 0x0a, 0x05, 0x10, 0x0b, 0x09, 0x00, 0x03, 0x26, 0x00, 0x0e, 0x1d, 0x09, 0x07, 0x33, 0x09, 0x08,
0x10, 0x23, 0x0a, 0x5d, 0x09, 0x5c, 0x0a, 0x06, 0x5d, 0x09, 0x01, 0x0a, 0x10, 0x0a, 0x09, 0x23, 0x09, 0x01, 0x05, 0x09, 0x01, 0x09, 0x01, 0x10,
0x06, 0x5d, 0x09, 0x5c, 0x0a, 0x08, 0x5d, 0x09, 0x01, 0x07, 0x09, 0x52, 0x0a, 0x0a, 0x23, 0x09, 0x07, 0x0a, 0x23, 0x09, 0x5c, 0x0a, 0x06, 0x32,
0x08, 0x00, 0x01, 0x09, 0x08, 0x10, 0x0a, 0x11, 0x0f, 0x0b, 0x00, 0x0b, 0x09, 0x0b, 0x01, 0x28, 0x09, 0x00, 0x16, 0x1d, 0x09, 0x0c, 0x01, 0x0a,
0x0e, 0x14, 0x10, 0x0c, 0x12, 0x01, 0x0d, 0x03, 0x10, 0x0e, 0x13, 0x08, 0x01, 0x10, 0x0b, 0x0a, 0x23, 0x0a, 0x07, 0x0b, 0x23, 0x0a, 0x32, 0x09,
0x0f, 0x10, 0x10, 0x14, 0x06, 0x11, 0x10, 0x12, 0x15, 0x03, 0x13, 0x0a, 0x0d, 0x01, 0x01, 0x06, 0x09, 0x5c, 0x09, 0x08, 0x01, 0x0a, 0x04, 0x5d,
0x5e, 0x0a, 0x05, 0x32, 0x09, 0x16, 0x01, 0x01, 0x09, 0x08, 0x10, 0x0a, 0x09, 0x5c, 0x0a, 0x08, 0x5d, 0x09, 0x01, 0x0a, 0x01, 0x10, 0x0b, 0x00,
0x11, 0x01, 0x0b, 0x03, 0x10, 0x0c, 0x12, 0x0f, 0x0d, 0x00, 0x33, 0x36, 0x23, 0x0a, 0x33, 0x0a, 0x0e, 0x33, 0x0a, 0x0f, 0x5d, 0x09, 0x5c, 0x0a,
0x3c, 0x10, 0x0e, 0x14, 0x03, 0x0f, 0x0a, 0x10, 0x10, 0x15, 0x07, 0x11, 0x09, 0x5d, 0x09, 0x01, 0x0a, 0x05, 0x10, 0x0b, 0x10, 0x23, 0x0a, 0x5d,
0x5e, 0x0a, 0x04, 0x32, 0x09, 0x16, 0x01, 0x01, 0x09, 0x08, 0x10, 0x0a, 0x09, 0x5c, 0x0a, 0x08, 0x5d, 0x09, 0x01, 0x0a, 0x06, 0x5d, 0x09, 0x5c,
0x11, 0x0f, 0x0b, 0x00, 0x33, 0x36, 0x3c, 0x10, 0x0c, 0x12, 0x0f, 0x0d, 0x0a, 0x0a, 0x5d, 0x09, 0x01, 0x07, 0x09, 0x52, 0x08, 0x00, 0x01, 0x09,
0x00, 0x24, 0x27, 0x2d, 0x10, 0x0e, 0x14, 0x03, 0x0f, 0x0b, 0x10, 0x10, 0x08, 0x10, 0x0a, 0x11, 0x0f, 0x0b, 0x00, 0x0b, 0x0e, 0x14, 0x10, 0x0c,
0x15, 0x07, 0x11, 0x5e, 0x0a, 0x04, 0x32, 0x09, 0x16, 0x01, 0x01, 0x09, 0x12, 0x01, 0x0d, 0x03, 0x10, 0x0e, 0x13, 0x08, 0x0f, 0x10, 0x10, 0x14,
0x08, 0x10, 0x0a, 0x11, 0x01, 0x0b, 0x05, 0x10, 0x0c, 0x17, 0x23, 0x0b, 0x06, 0x11, 0x10, 0x12, 0x15, 0x03, 0x13, 0x0a, 0x5e, 0x0a, 0x05, 0x32,
0x10, 0x0c, 0x12, 0x0f, 0x0d, 0x00, 0x24, 0x27, 0x2d, 0x10, 0x0e, 0x14, 0x09, 0x16, 0x01, 0x01, 0x09, 0x08, 0x10, 0x0a, 0x11, 0x01, 0x0b, 0x03,
0x03, 0x0f, 0x0d, 0x10, 0x10, 0x15, 0x08, 0x11, 0x5e, 0x0a, 0x04, 0x32, 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, 0x09, 0x16, 0x01, 0x01, 0x09, 0x08, 0x10, 0x0a, 0x11, 0x0f, 0x0b, 0x00,
0xce, 0xd4, 0xdf, 0x10, 0x0c, 0x12, 0x0f, 0x0d, 0x00, 0x24, 0x27, 0x2d, 0x33, 0x36, 0x3c, 0x10, 0x0c, 0x12, 0x0f, 0x0d, 0x00, 0x24, 0x27, 0x2d,
0x10, 0x0e, 0x14, 0x03, 0x0f, 0x0f, 0x10, 0x10, 0x15, 0x01, 0x11, 0x06, 0x10, 0x0e, 0x14, 0x03, 0x0f, 0x0b, 0x10, 0x10, 0x15, 0x07, 0x11, 0x5e,
0x33, 0x11, 0x15, 0x46, 0x11, 0x01, 0x5e, 0x0a, 0x04, 0x32, 0x09, 0x16, 0x0a, 0x04, 0x32, 0x09, 0x16, 0x01, 0x01, 0x09, 0x08, 0x10, 0x0a, 0x11,
0x01, 0x01, 0x09, 0x08, 0x10, 0x0a, 0x11, 0x0f, 0x0b, 0x00, 0x24, 0x27, 0x01, 0x0b, 0x05, 0x10, 0x0c, 0x17, 0x23, 0x0b, 0x10, 0x0c, 0x12, 0x0f,
0x2d, 0x10, 0x0c, 0x12, 0x06, 0x0d, 0x10, 0x0e, 0x14, 0x03, 0x0f, 0x0f, 0x0d, 0x00, 0x24, 0x27, 0x2d, 0x10, 0x0e, 0x14, 0x03, 0x0f, 0x0d, 0x10,
0x01, 0x10, 0x06, 0x33, 0x10, 0x15, 0x45, 0x0f, 0x46, 0x0f, 0x01, 0x10, 0x10, 0x15, 0x08, 0x11, 0x5e, 0x0a, 0x04, 0x32, 0x09, 0x16, 0x01, 0x01,
0x10, 0x15, 0x07, 0x11, 0x5e, 0x0a, 0x04, 0x32, 0x09, 0x16, 0x01, 0x10, 0x09, 0x08, 0x10, 0x0a, 0x11, 0x0f, 0x0b, 0x00, 0xce, 0xd4, 0xdf, 0x10,
0x09, 0x18, 0x01, 0x0a, 0x07, 0x10, 0x0b, 0x19, 0x01, 0x0c, 0x08, 0x5e, 0x0c, 0x12, 0x0f, 0x0d, 0x00, 0x24, 0x27, 0x2d, 0x10, 0x0e, 0x14, 0x03,
0x09, 0x02, 0x3d, 0x09, 0x00, 0x09, 0x00, 0x00, 0x02, 0x20, 0x20, 0x00, 0x0f, 0x0f, 0x10, 0x10, 0x15, 0x01, 0x11, 0x06, 0x33, 0x11, 0x15, 0x46,
0x00, 0x00, 0x04, 0xee, 0x99, 0x8e, 0x20, 0x00, 0x00, 0x00, 0x05, 0xf3, 0x11, 0x01, 0x5e, 0x0a, 0x04, 0x32, 0x09, 0x16, 0x01, 0x01, 0x09, 0x08,
0xb1, 0x93, 0xa7, 0x20, 0x00, 0x00, 0x00, 0x05, 0xf3, 0xb1, 0xa9, 0xa7, 0x10, 0x0a, 0x11, 0x0f, 0x0b, 0x00, 0x24, 0x27, 0x2d, 0x10, 0x0c, 0x12,
0x20, 0x00, 0x00, 0x00, 0x04, 0xef, 0x84, 0xa0, 0x20, 0x00, 0x00, 0x00, 0x06, 0x0d, 0x10, 0x0e, 0x14, 0x03, 0x0f, 0x0f, 0x01, 0x10, 0x06, 0x33,
0x04, 0xee, 0xba, 0xa2, 0x20, 0x00, 0x00, 0x00, 0x01, 0x20, 0x00, 0x00, 0x10, 0x15, 0x45, 0x0f, 0x46, 0x0f, 0x01, 0x10, 0x10, 0x15, 0x07, 0x11,
0x00, 0x08, 0x20, 0xee, 0x82, 0xb4, 0xee, 0x82, 0xb4, 0x20, 0x00, 0x00, 0x5e, 0x0a, 0x04, 0x32, 0x09, 0x16, 0x01, 0x10, 0x09, 0x18, 0x01, 0x0a,
0x00, 0x03, 0xee, 0x82, 0xb4, 0x00, 0x00, 0x1a, 0x00, 0x04, 0x6d, 0x6f, 0x07, 0x10, 0x0b, 0x19, 0x01, 0x0c, 0x08, 0x5e, 0x09, 0x02, 0x3d, 0x09,
0x64, 0x65, 0x00, 0x00, 0x06, 0x6e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x02, 0x20, 0x20, 0x00, 0x00, 0x00, 0x04, 0xee,
0x00, 0x03, 0x3d, 0x3d, 0x3d, 0x00, 0x00, 0x06, 0x69, 0x6e, 0x73, 0x65, 0x99, 0x8e, 0x20, 0x00, 0x00, 0x00, 0x05, 0xf3, 0xb1, 0x93, 0xa7, 0x20,
0x72, 0x74, 0x00, 0x00, 0x06, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x00, 0x00, 0x00, 0x00, 0x05, 0xf3, 0xb1, 0xa9, 0xa7, 0x20, 0x00, 0x00, 0x00,
0x00, 0x06, 0x72, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x00, 0x00, 0x06, 0x6a, 0x04, 0xef, 0x84, 0xa0, 0x20, 0x00, 0x00, 0x00, 0x04, 0xee, 0xba, 0xa2,
0x75, 0x6d, 0x70, 0x65, 0x72, 0x00, 0x00, 0x01, 0x43, 0x00, 0x00, 0x09, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x65, 0x64, 0x69,
0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x73, 0x00, 0x00, 0x09, 0x74, 0x6f, 0x72, 0x00, 0x00, 0x00, 0x01, 0x20, 0x00, 0x00, 0x00, 0x08,
0x6c, 0x61, 0x6e, 0x67, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x00, 0x00, 0x07, 0x20, 0xee, 0x82, 0xb4, 0xee, 0x82, 0xb4, 0x20, 0x00, 0x00, 0x00, 0x03,
0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x00, 0x00, 0x04, 0x46, 0x69, 0xee, 0x82, 0xb4, 0x00, 0x00, 0x1a, 0x00, 0x04, 0x6d, 0x6f, 0x64, 0x65,
0x6c, 0x65, 0x00, 0x00, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x00, 0x00, 0x06, 0x6e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x00, 0x00, 0x03,
0x65, 0x00, 0x00, 0x08, 0x62, 0x61, 0x73, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x3d, 0x3d, 0x00, 0x00, 0x06, 0x69, 0x6e, 0x73, 0x65, 0x72, 0x74,
0x00, 0x00, 0x04, 0x74, 0x6f, 0x5f, 0x73, 0x00, 0x00, 0x06, 0x75, 0x70, 0x00, 0x00, 0x06, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x00, 0x00, 0x06,
0x63, 0x61, 0x73, 0x65, 0x00, 0x00, 0x06, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x72, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x00, 0x00, 0x06, 0x6a, 0x75, 0x6d,
0x6c, 0x00, 0x00, 0x02, 0x66, 0x67, 0x00, 0x00, 0x02, 0x62, 0x67, 0x00, 0x70, 0x65, 0x72, 0x00, 0x00, 0x01, 0x43, 0x00, 0x00, 0x09, 0x6c, 0x61,
0x00, 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x00, 0x00, 0x05, 0x73, 0x74, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x73, 0x00, 0x00, 0x07, 0x64, 0x65,
0x61, 0x72, 0x74, 0x00, 0x00, 0x06, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x00, 0x00, 0x04, 0x64, 0x61, 0x74, 0x61,
0x00, 0x00, 0x02, 0x3c, 0x3c, 0x00, 0x00, 0x05, 0x63, 0x6f, 0x6c, 0x6f, 0x00, 0x00, 0x02, 0x21, 0x3d, 0x00, 0x00, 0x04, 0x46, 0x69, 0x6c, 0x65,
0x72, 0x00, 0x00, 0x04, 0x74, 0x65, 0x78, 0x74, 0x00, 0x00, 0x0a, 0x68, 0x00, 0x00, 0x08, 0x62, 0x61, 0x73, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x00,
0x69, 0x67, 0x68, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x73, 0x00, 0x00, 0x00, 0x00, 0x04, 0x74, 0x6f, 0x5f, 0x73, 0x00, 0x00, 0x06, 0x75, 0x70, 0x63,
0x00, 0x37, 0x00, 0x03, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x73, 0x65, 0x00, 0x00, 0x06, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c,
0x00, 0x10, 0x39, 0x04, 0x00, 0x00, 0x1d, 0x03, 0x00, 0x01, 0x04, 0x01, 0x00, 0x00, 0x02, 0x66, 0x67, 0x00, 0x00, 0x02, 0x62, 0x67, 0x00, 0x00,
0x32, 0x03, 0x01, 0x01, 0x3d, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x09, 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x00, 0x00, 0x05, 0x73, 0x74, 0x61,
0x43, 0x6c, 0x69, 0x70, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x00, 0x00, 0x04, 0x72, 0x74, 0x00, 0x00, 0x06, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x00,
0x63, 0x6f, 0x70, 0x79, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x02, 0x00, 0x00, 0x02, 0x3c, 0x3c, 0x00, 0x00, 0x05, 0x63, 0x6f, 0x6c, 0x6f, 0x72,
0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x39, 0x00, 0x00, 0x00, 0x00, 0x04, 0x74, 0x65, 0x78, 0x74, 0x00, 0x00, 0x0a, 0x68, 0x69,
0x00, 0x1d, 0x02, 0x00, 0x33, 0x02, 0x01, 0x3d, 0x02, 0x00, 0x00, 0x00, 0x67, 0x68, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x73, 0x00, 0x00, 0x00, 0x00,
0x02, 0x00, 0x09, 0x43, 0x6c, 0x69, 0x70, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x37, 0x00, 0x03, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x05, 0x70, 0x61, 0x73, 0x74, 0x65, 0x00, 0x00, 0x00, 0x03, 0x10, 0x39, 0x04, 0x00, 0x00, 0x1d, 0x03, 0x00, 0x01, 0x04, 0x01, 0x32,
0xc8, 0x00, 0x07, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x01, 0x01, 0x3d, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x09, 0x43,
0x3a, 0x39, 0x04, 0x00, 0x00, 0x10, 0x03, 0x00, 0x1d, 0x07, 0x01, 0x01, 0x6c, 0x69, 0x70, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x00, 0x00, 0x04, 0x63,
0x08, 0x01, 0x32, 0x07, 0x02, 0x01, 0x27, 0x07, 0x00, 0x02, 0x3d, 0x03, 0x6f, 0x70, 0x79, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x02, 0x00, 0x04,
0x1d, 0x07, 0x01, 0x01, 0x08, 0x01, 0x10, 0x09, 0x03, 0x34, 0x07, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x39, 0x00, 0x00, 0x00,
0x01, 0x33, 0x07, 0x05, 0x01, 0x04, 0x07, 0x01, 0x07, 0x04, 0x5c, 0x08, 0x1d, 0x02, 0x00, 0x33, 0x02, 0x01, 0x3d, 0x02, 0x00, 0x00, 0x00, 0x02,
0x00, 0x32, 0x07, 0x06, 0x01, 0x28, 0x07, 0x00, 0xc7, 0x01, 0x07, 0x04, 0x00, 0x09, 0x43, 0x6c, 0x69, 0x70, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x00,
0x08, 0x08, 0x11, 0x09, 0x64, 0x08, 0x23, 0x07, 0x33, 0x07, 0x07, 0x01, 0x00, 0x05, 0x70, 0x61, 0x73, 0x74, 0x65, 0x00, 0x00, 0x00, 0x03, 0xc8,
0x05, 0x07, 0x01, 0x07, 0x05, 0x66, 0x08, 0x1f, 0x08, 0x08, 0x5c, 0x09, 0x00, 0x07, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x3a,
0x01, 0x32, 0x08, 0x09, 0x01, 0x01, 0x09, 0x07, 0x32, 0x08, 0x0a, 0x01, 0x39, 0x04, 0x00, 0x00, 0x10, 0x03, 0x00, 0x1d, 0x07, 0x01, 0x01, 0x08,
0x27, 0x08, 0x00, 0x17, 0x66, 0x08, 0x1f, 0x08, 0x08, 0x5c, 0x09, 0x02, 0x01, 0x32, 0x07, 0x02, 0x01, 0x27, 0x07, 0x00, 0x02, 0x3d, 0x03, 0x1d,
0x32, 0x08, 0x09, 0x01, 0x01, 0x09, 0x07, 0x32, 0x08, 0x0a, 0x01, 0x28, 0x07, 0x01, 0x01, 0x08, 0x01, 0x10, 0x09, 0x03, 0x34, 0x07, 0x04, 0x01,
0x08, 0x00, 0x06, 0x10, 0x08, 0x0b, 0x26, 0x00, 0x7a, 0x66, 0x08, 0x1f, 0x33, 0x07, 0x05, 0x01, 0x04, 0x07, 0x01, 0x07, 0x04, 0x5c, 0x08, 0x00,
0x08, 0x08, 0x5c, 0x09, 0x03, 0x32, 0x08, 0x09, 0x01, 0x01, 0x09, 0x07, 0x32, 0x07, 0x06, 0x01, 0x28, 0x07, 0x00, 0xc7, 0x01, 0x07, 0x04, 0x08,
0x32, 0x08, 0x0a, 0x01, 0x28, 0x08, 0x00, 0x06, 0x10, 0x08, 0x0c, 0x26, 0x08, 0x11, 0x09, 0x64, 0x08, 0x23, 0x07, 0x33, 0x07, 0x07, 0x01, 0x05,
0x00, 0x5d, 0x66, 0x08, 0x1f, 0x08, 0x08, 0x5c, 0x09, 0x04, 0x32, 0x08, 0x07, 0x01, 0x07, 0x05, 0x66, 0x08, 0x1f, 0x08, 0x08, 0x5c, 0x09, 0x01,
0x09, 0x01, 0x01, 0x09, 0x07, 0x32, 0x08, 0x0a, 0x01, 0x28, 0x08, 0x00, 0x32, 0x08, 0x09, 0x01, 0x01, 0x09, 0x07, 0x32, 0x08, 0x0a, 0x01, 0x27,
0x06, 0x10, 0x08, 0x0d, 0x26, 0x00, 0x40, 0x66, 0x08, 0x1f, 0x08, 0x08, 0x08, 0x00, 0x17, 0x66, 0x08, 0x1f, 0x08, 0x08, 0x5c, 0x09, 0x02, 0x32,
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, 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, 0x06, 0x10, 0x08, 0x0b, 0x26, 0x00, 0x7a, 0x66, 0x08, 0x1f, 0x08,
0x00, 0x00, 0x01, 0x03, 0x08, 0x3d, 0x03, 0x00, 0x10, 0x00, 0x00, 0x02, 0x08, 0x5c, 0x09, 0x03, 0x32, 0x08, 0x09, 0x01, 0x01, 0x09, 0x07, 0x32,
0x23, 0x21, 0x00, 0x00, 0x00, 0x04, 0x62, 0x61, 0x73, 0x68, 0x00, 0x00, 0x08, 0x0a, 0x01, 0x28, 0x08, 0x00, 0x06, 0x10, 0x08, 0x0c, 0x26, 0x00,
0x00, 0x02, 0x73, 0x68, 0x00, 0x00, 0x00, 0x04, 0x66, 0x69, 0x73, 0x68, 0x5d, 0x66, 0x08, 0x1f, 0x08, 0x08, 0x5c, 0x09, 0x04, 0x32, 0x08, 0x09,
0x00, 0x00, 0x00, 0x06, 0x70, 0x79, 0x74, 0x68, 0x6f, 0x6e, 0x00, 0x00, 0x01, 0x01, 0x09, 0x07, 0x32, 0x08, 0x0a, 0x01, 0x28, 0x08, 0x00, 0x06,
0x00, 0x04, 0x72, 0x75, 0x62, 0x79, 0x00, 0x00, 0x00, 0x03, 0x6c, 0x75, 0x10, 0x08, 0x0d, 0x26, 0x00, 0x40, 0x66, 0x08, 0x1f, 0x08, 0x08, 0x5c,
0x61, 0x00, 0x00, 0x00, 0x04, 0x66, 0x69, 0x6c, 0x65, 0x00, 0x00, 0x00, 0x09, 0x05, 0x32, 0x08, 0x09, 0x01, 0x01, 0x09, 0x07, 0x32, 0x08, 0x0a,
0x14, 0x66, 0x69, 0x6c, 0x65, 0x20, 0x2d, 0x2d, 0x6d, 0x69, 0x6d, 0x65, 0x01, 0x28, 0x08, 0x00, 0x06, 0x10, 0x08, 0x0e, 0x26, 0x00, 0x23, 0x66,
0x2d, 0x74, 0x79, 0x70, 0x65, 0x20, 0x2d, 0x62, 0x20, 0x00, 0x00, 0x00, 0x08, 0x1f, 0x08, 0x08, 0x5c, 0x09, 0x06, 0x32, 0x08, 0x09, 0x01, 0x01,
0x00, 0x00, 0x00, 0x00, 0x0b, 0x73, 0x68, 0x65, 0x6c, 0x6c, 0x73, 0x63, 0x09, 0x07, 0x32, 0x08, 0x0a, 0x01, 0x28, 0x08, 0x00, 0x06, 0x10, 0x08,
0x72, 0x69, 0x70, 0x74, 0x00, 0x00, 0x00, 0x04, 0x64, 0x69, 0x66, 0x66, 0x0f, 0x26, 0x00, 0x06, 0x10, 0x08, 0x00, 0x26, 0x00, 0x00, 0x01, 0x03,
0x00, 0x00, 0x00, 0x04, 0x68, 0x74, 0x6d, 0x6c, 0x00, 0x00, 0x00, 0x0a, 0x08, 0x3d, 0x03, 0x10, 0x07, 0x10, 0x10, 0x08, 0x10, 0x32, 0x07, 0x11,
0x6a, 0x61, 0x76, 0x61, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x00, 0x00, 0x01, 0x27, 0x07, 0x00, 0x0a, 0x10, 0x07, 0x10, 0x10, 0x08, 0x12, 0x32,
0x00, 0x08, 0x6d, 0x61, 0x6b, 0x65, 0x66, 0x69, 0x6c, 0x65, 0x00, 0x00, 0x07, 0x11, 0x01, 0x28, 0x07, 0x00, 0x02, 0x3d, 0x03, 0x5c, 0x08, 0x07,
0x00, 0x03, 0x2d, 0x63, 0x24, 0x00, 0x00, 0x1b, 0x00, 0x07, 0x64, 0x65, 0x2f, 0x07, 0x13, 0x01, 0x33, 0x07, 0x14, 0x28, 0x07, 0x00, 0x02, 0x3d,
0x66, 0x61, 0x75, 0x6c, 0x74, 0x00, 0x00, 0x04, 0x46, 0x69, 0x6c, 0x65, 0x03, 0x5c, 0x08, 0x08, 0x01, 0x09, 0x01, 0x5d, 0x08, 0x5c, 0x09, 0x09,
0x00, 0x00, 0x06, 0x65, 0x78, 0x69, 0x73, 0x74, 0x3f, 0x00, 0x00, 0x08, 0x5d, 0x08, 0x2f, 0x07, 0x15, 0x01, 0x33, 0x07, 0x05, 0x01, 0x06, 0x07,
0x72, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x00, 0x00, 0x04, 0x6f, 0x01, 0x07, 0x06, 0x66, 0x08, 0x1f, 0x08, 0x08, 0x5c, 0x09, 0x0a, 0x32,
0x70, 0x65, 0x6e, 0x00, 0x00, 0x05, 0x63, 0x68, 0x6f, 0x6d, 0x70, 0x00, 0x08, 0x09, 0x01, 0x01, 0x09, 0x07, 0x32, 0x08, 0x0a, 0x01, 0x28, 0x08,
0x00, 0x0b, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x77, 0x69, 0x74, 0x68, 0x00, 0x06, 0x10, 0x08, 0x0b, 0x26, 0x00, 0xd1, 0x66, 0x08, 0x1f, 0x08,
0x3f, 0x00, 0x00, 0x08, 0x64, 0x6f, 0x77, 0x6e, 0x63, 0x61, 0x73, 0x65, 0x08, 0x5c, 0x09, 0x05, 0x32, 0x08, 0x09, 0x01, 0x01, 0x09, 0x07, 0x32,
0x00, 0x00, 0x06, 0x52, 0x65, 0x67, 0x65, 0x78, 0x70, 0x00, 0x00, 0x07, 0x08, 0x0a, 0x01, 0x28, 0x08, 0x00, 0x06, 0x10, 0x08, 0x0e, 0x26, 0x00,
0x63, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x00, 0x00, 0x03, 0x3d, 0x3d, 0xb4, 0x66, 0x08, 0x1f, 0x08, 0x08, 0x5c, 0x09, 0x0b, 0x32, 0x08, 0x09,
0x3d, 0x00, 0x00, 0x04, 0x62, 0x61, 0x73, 0x68, 0x00, 0x00, 0x04, 0x66, 0x01, 0x01, 0x09, 0x07, 0x32, 0x08, 0x0a, 0x01, 0x28, 0x08, 0x00, 0x06,
0x69, 0x73, 0x68, 0x00, 0x00, 0x06, 0x70, 0x79, 0x74, 0x68, 0x6f, 0x6e, 0x10, 0x08, 0x16, 0x26, 0x00, 0x97, 0x66, 0x08, 0x1f, 0x08, 0x08, 0x5c,
0x00, 0x00, 0x04, 0x72, 0x75, 0x62, 0x79, 0x00, 0x00, 0x03, 0x6c, 0x75, 0x09, 0x0c, 0x32, 0x08, 0x09, 0x01, 0x01, 0x09, 0x07, 0x32, 0x08, 0x0a,
0x61, 0x00, 0x00, 0x05, 0x6c, 0x69, 0x6e, 0x75, 0x78, 0x00, 0x00, 0x02, 0x01, 0x28, 0x08, 0x00, 0x06, 0x10, 0x08, 0x17, 0x26, 0x00, 0x7a, 0x66,
0x21, 0x3d, 0x00, 0x00, 0x03, 0x6d, 0x61, 0x63, 0x00, 0x00, 0x0f, 0x63, 0x08, 0x1f, 0x08, 0x08, 0x5c, 0x09, 0x04, 0x32, 0x08, 0x09, 0x01, 0x01,
0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x5f, 0x65, 0x78, 0x69, 0x73, 0x74, 0x09, 0x07, 0x32, 0x08, 0x0a, 0x01, 0x28, 0x08, 0x00, 0x06, 0x10, 0x08,
0x73, 0x3f, 0x00, 0x00, 0x01, 0x21, 0x00, 0x00, 0x01, 0x60, 0x00, 0x00, 0x0d, 0x26, 0x00, 0x5d, 0x66, 0x08, 0x1f, 0x08, 0x08, 0x5c, 0x09, 0x0d,
0x04, 0x64, 0x69, 0x66, 0x66, 0x00, 0x00, 0x04, 0x68, 0x74, 0x6d, 0x6c, 0x32, 0x08, 0x09, 0x01, 0x01, 0x09, 0x07, 0x32, 0x08, 0x0a, 0x01, 0x28,
0x00, 0x00, 0x0a, 0x6a, 0x61, 0x76, 0x61, 0x73, 0x63, 0x72, 0x69, 0x70, 0x08, 0x00, 0x06, 0x10, 0x08, 0x18, 0x26, 0x00, 0x40, 0x66, 0x08, 0x1f,
0x74, 0x00, 0x00, 0x08, 0x6d, 0x61, 0x6b, 0x65, 0x66, 0x69, 0x6c, 0x65, 0x08, 0x08, 0x5c, 0x09, 0x0e, 0x32, 0x08, 0x09, 0x01, 0x01, 0x09, 0x07,
0x00, 0x00, 0x01, 0x63, 0x00, 0x00, 0x00, 0x01, 0x6b, 0x00, 0x01, 0x00, 0x32, 0x08, 0x0a, 0x01, 0x28, 0x08, 0x00, 0x06, 0x10, 0x08, 0x19, 0x26,
0x0a, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x10, 0x02, 0x00, 0x00, 0x23, 0x66, 0x08, 0x1f, 0x08, 0x08, 0x5c, 0x09, 0x0f, 0x32, 0x08,
0x10, 0x03, 0x01, 0x10, 0x04, 0x02, 0x10, 0x05, 0x03, 0x10, 0x06, 0x04, 0x09, 0x01, 0x01, 0x09, 0x07, 0x32, 0x08, 0x0a, 0x01, 0x28, 0x08, 0x00,
0x2f, 0x01, 0x05, 0x05, 0x10, 0x02, 0x06, 0x10, 0x03, 0x07, 0x10, 0x04, 0x06, 0x10, 0x08, 0x1a, 0x26, 0x00, 0x06, 0x10, 0x08, 0x00, 0x26, 0x00,
0x08, 0x10, 0x05, 0x09, 0x10, 0x06, 0x0a, 0x10, 0x07, 0x0b, 0x10, 0x08, 0x00, 0x01, 0x03, 0x08, 0x3d, 0x03, 0x00, 0x10, 0x00, 0x00, 0x02, 0x23,
0x0c, 0x2f, 0x01, 0x0d, 0x07, 0x6b, 0x01, 0x0e, 0x00, 0x6b, 0x01, 0x0f, 0x21, 0x00, 0x00, 0x00, 0x04, 0x62, 0x61, 0x73, 0x68, 0x00, 0x00, 0x00,
0x01, 0x6b, 0x01, 0x10, 0x02, 0x6b, 0x01, 0x11, 0x03, 0x6b, 0x01, 0x12, 0x02, 0x73, 0x68, 0x00, 0x00, 0x00, 0x04, 0x66, 0x69, 0x73, 0x68, 0x00,
0x04, 0x6b, 0x01, 0x13, 0x05, 0x6b, 0x01, 0x14, 0x06, 0x6b, 0x01, 0x15, 0x00, 0x00, 0x06, 0x70, 0x79, 0x74, 0x68, 0x6f, 0x6e, 0x00, 0x00, 0x00,
0x07, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x05, 0x74, 0x68, 0x65, 0x04, 0x72, 0x75, 0x62, 0x79, 0x00, 0x00, 0x00, 0x03, 0x6c, 0x75, 0x61,
0x6d, 0x65, 0x00, 0x00, 0x0a, 0x6c, 0x73, 0x70, 0x5f, 0x63, 0x6f, 0x6e, 0x00, 0x00, 0x00, 0x04, 0x66, 0x69, 0x6c, 0x65, 0x00, 0x00, 0x00, 0x14,
0x66, 0x69, 0x67, 0x00, 0x00, 0x09, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x66, 0x69, 0x6c, 0x65, 0x20, 0x2d, 0x2d, 0x6d, 0x69, 0x6d, 0x65, 0x2d,
0x67, 0x65, 0x73, 0x00, 0x00, 0x0c, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x65, 0x74, 0x79, 0x70, 0x65, 0x20, 0x2d, 0x62, 0x20, 0x00, 0x00, 0x00, 0x00,
0x6e, 0x64, 0x69, 0x6e, 0x67, 0x73, 0x00, 0x00, 0x0c, 0x68, 0x69, 0x67, 0x00, 0x00, 0x00, 0x0b, 0x73, 0x68, 0x65, 0x6c, 0x6c, 0x73, 0x63, 0x72,
0x68, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x65, 0x72, 0x73, 0x00, 0x00, 0x0d, 0x69, 0x70, 0x74, 0x00, 0x00, 0x00, 0x04, 0x64, 0x69, 0x66, 0x66, 0x00,
0x61, 0x74, 0x74, 0x72, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x00, 0x00, 0x04, 0x68, 0x74, 0x6d, 0x6c, 0x00, 0x00, 0x00, 0x0a, 0x6a,
0x72, 0x00, 0x00, 0x09, 0x62, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x75, 0x61, 0x76, 0x61, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x00, 0x00, 0x00,
0x70, 0x00, 0x00, 0x0a, 0x62, 0x5f, 0x73, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x08, 0x6d, 0x61, 0x6b, 0x65, 0x66, 0x69, 0x6c, 0x65, 0x00, 0x00, 0x00,
0x77, 0x6e, 0x00, 0x00, 0x12, 0x62, 0x5f, 0x65, 0x78, 0x74, 0x72, 0x61, 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, 0x5f, 0x68, 0x69, 0x67, 0x68, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x73, 0x00,
0x00, 0x05, 0x62, 0x5f, 0x62, 0x61, 0x72, 0x00, 0x00, 0x06, 0x62, 0x5f, 0x00, 0x04, 0x62, 0x69, 0x6e, 0x64, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00,
0x63, 0x6f, 0x70, 0x79, 0x00, 0x00, 0x07, 0x62, 0x5f, 0x70, 0x61, 0x73, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x39,
0x74, 0x65, 0x00, 0x00, 0x0d, 0x62, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x00, 0x00, 0x01, 0x01, 0x02, 0x01, 0x01, 0x03, 0x02, 0x1a, 0x03, 0x00,
0x64, 0x65, 0x74, 0x65, 0x63, 0x74, 0x00, 0x00, 0x0b, 0x61, 0x74, 0x74, 0x3d, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x06, 0x40, 0x62, 0x5f, 0x62,
0x72, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x65, 0x72, 0x00, 0x00, 0x04, 0x62, 0x61, 0x72, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x03, 0x00, 0x04, 0x00,
0x61, 0x72, 0x3d, 0x00, 0x00, 0x07, 0x73, 0x74, 0x61, 0x72, 0x74, 0x75, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x39, 0x00, 0x00, 0x01, 0x01,
0x70, 0x00, 0x00, 0x08, 0x73, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x02, 0x01, 0x01, 0x03, 0x02, 0x1a, 0x03, 0x00, 0x3d, 0x03, 0x00, 0x00,
0x00, 0x00, 0x04, 0x63, 0x6f, 0x70, 0x79, 0x00, 0x00, 0x05, 0x70, 0x61, 0x00, 0x01, 0x00, 0x0a, 0x40, 0x62, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74,
0x73, 0x74, 0x65, 0x00, 0x00, 0x0b, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x64, 0x75, 0x70, 0x00, 0x00, 0x00, 0x00, 0x31, 0x00, 0x03, 0x00, 0x04, 0x00,
0x65, 0x74, 0x65, 0x63, 0x74, 0x00, 0x00, 0x10, 0x65, 0x78, 0x74, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x39, 0x00, 0x00, 0x01, 0x01,
0x61, 0x5f, 0x68, 0x69, 0x67, 0x68, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x73, 0x02, 0x01, 0x01, 0x03, 0x02, 0x1a, 0x03, 0x00, 0x3d, 0x03, 0x00, 0x00,
0x00, 0x00, 0x04, 0x62, 0x69, 0x6e, 0x64, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x01, 0x00, 0x0b, 0x40, 0x62, 0x5f, 0x73, 0x68, 0x75, 0x74, 0x64,
0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x6f, 0x77, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x03, 0x00, 0x04,
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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x39, 0x00, 0x00, 0x01,
0x01, 0x02, 0x01, 0x01, 0x03, 0x02, 0x1a, 0x03, 0x00, 0x3d, 0x03, 0x00, 0x01, 0x02, 0x01, 0x01, 0x03, 0x02, 0x1a, 0x03, 0x00, 0x3d, 0x03, 0x00,
0x00, 0x00, 0x01, 0x00, 0x0a, 0x40, 0x62, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x00, 0x00, 0x01, 0x00, 0x07, 0x40, 0x62, 0x5f, 0x63, 0x6f, 0x70, 0x79,
0x74, 0x75, 0x70, 0x00, 0x00, 0x00, 0x00, 0x31, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x2e, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x39, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x39, 0x00, 0x00, 0x01, 0x01, 0x02, 0x01,
0x01, 0x02, 0x01, 0x01, 0x03, 0x02, 0x1a, 0x03, 0x00, 0x3d, 0x03, 0x00, 0x01, 0x03, 0x02, 0x1a, 0x03, 0x00, 0x3d, 0x03, 0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x01, 0x00, 0x0b, 0x40, 0x62, 0x5f, 0x73, 0x68, 0x75, 0x74, 0x00, 0x08, 0x40, 0x62, 0x5f, 0x70, 0x61, 0x73, 0x74, 0x65, 0x00, 0x00,
0x64, 0x6f, 0x77, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x03, 0x00, 0x00, 0x00, 0x34, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x39, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x39, 0x00, 0x00, 0x01, 0x01, 0x02, 0x01, 0x01, 0x03,
0x01, 0x01, 0x02, 0x01, 0x01, 0x03, 0x02, 0x1a, 0x03, 0x00, 0x3d, 0x03, 0x02, 0x1a, 0x03, 0x00, 0x3d, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x0e,
0x00, 0x00, 0x00, 0x01, 0x00, 0x07, 0x40, 0x62, 0x5f, 0x63, 0x6f, 0x70, 0x40, 0x62, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x64, 0x65, 0x74, 0x65,
0x79, 0x00, 0x00, 0x00, 0x00, 0x2e, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x63, 0x74, 0x00, 0x00, 0x00, 0x00, 0x39, 0x00, 0x03, 0x00, 0x04, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x39, 0x00, 0x00, 0x01, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x39, 0x00, 0x00, 0x01, 0x01,
0x01, 0x01, 0x03, 0x02, 0x1a, 0x03, 0x00, 0x3d, 0x03, 0x00, 0x00, 0x00, 0x02, 0x01, 0x01, 0x03, 0x02, 0x1a, 0x03, 0x00, 0x3d, 0x03, 0x00, 0x00,
0x01, 0x00, 0x08, 0x40, 0x62, 0x5f, 0x70, 0x61, 0x73, 0x74, 0x65, 0x00, 0x00, 0x01, 0x00, 0x13, 0x40, 0x62, 0x5f, 0x65, 0x78, 0x74, 0x72, 0x61,
0x00, 0x00, 0x00, 0x34, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x68, 0x69, 0x67, 0x68, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x73, 0x00,
0x00, 0x00, 0x00, 0x0f, 0x39, 0x00, 0x00, 0x01, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0x01, 0x43, 0x00, 0x08, 0x00, 0x0b, 0x00, 0x03, 0x00, 0x00,
0x03, 0x02, 0x1a, 0x03, 0x00, 0x3d, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xc1, 0x39, 0x04, 0x40, 0x01, 0x26, 0x00, 0x06, 0x26,
0x0e, 0x40, 0x62, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x64, 0x65, 0x74, 0x00, 0x05, 0x26, 0x00, 0x04, 0x11, 0x02, 0x11, 0x03, 0x01, 0x05, 0x04,
0x65, 0x63, 0x74, 0x00, 0x00, 0x00, 0x00, 0x39, 0x00, 0x03, 0x00, 0x04, 0x01, 0x08, 0x01, 0x1d, 0x09, 0x00, 0x32, 0x08, 0x01, 0x01, 0x27, 0x08,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x39, 0x00, 0x00, 0x01, 0x00, 0x07, 0x01, 0x08, 0x01, 0x53, 0x01, 0x08, 0x01, 0x01, 0x08, 0x02,
0x01, 0x02, 0x01, 0x01, 0x03, 0x02, 0x1a, 0x03, 0x00, 0x3d, 0x03, 0x00, 0x29, 0x08, 0x00, 0x03, 0x26, 0x00, 0x31, 0x12, 0x06, 0x1d, 0x08, 0x02,
0x00, 0x00, 0x01, 0x00, 0x13, 0x40, 0x62, 0x5f, 0x65, 0x78, 0x74, 0x72, 0x33, 0x08, 0x03, 0x01, 0x07, 0x08, 0x01, 0x08, 0x07, 0x10, 0x09, 0x04,
0x61, 0x5f, 0x68, 0x69, 0x67, 0x68, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x73, 0x62, 0x0a, 0x00, 0x34, 0x08, 0x05, 0x01, 0x30, 0x08, 0x06, 0x28, 0x08,
0x00, 0x00, 0x00, 0x01, 0x43, 0x00, 0x08, 0x00, 0x0b, 0x00, 0x03, 0x00, 0x00, 0x0d, 0x01, 0x08, 0x07, 0x01, 0x09, 0x05, 0x34, 0x08, 0x07, 0x00,
0x00, 0x00, 0x00, 0x00, 0xc1, 0x39, 0x04, 0x40, 0x01, 0x26, 0x00, 0x06, 0x26, 0x00, 0x02, 0x11, 0x08, 0x26, 0x00, 0x5b, 0x30, 0x08, 0x06, 0x28,
0x26, 0x00, 0x05, 0x26, 0x00, 0x04, 0x11, 0x02, 0x11, 0x03, 0x01, 0x05, 0x08, 0x00, 0x22, 0x01, 0x08, 0x02, 0x1d, 0x09, 0x00, 0x32, 0x08, 0x01,
0x04, 0x01, 0x08, 0x01, 0x1d, 0x09, 0x00, 0x32, 0x08, 0x01, 0x01, 0x27, 0x01, 0x27, 0x08, 0x00, 0x07, 0x01, 0x08, 0x02, 0x53, 0x02, 0x08, 0x01,
0x08, 0x00, 0x07, 0x01, 0x08, 0x01, 0x53, 0x01, 0x08, 0x01, 0x01, 0x08, 0x01, 0x08, 0x01, 0x62, 0x09, 0x01, 0x34, 0x08, 0x08, 0x00, 0x26, 0x00,
0x02, 0x29, 0x08, 0x00, 0x03, 0x26, 0x00, 0x31, 0x12, 0x06, 0x1d, 0x08, 0x32, 0x01, 0x08, 0x03, 0x1d, 0x09, 0x09, 0x32, 0x08, 0x01, 0x01, 0x28,
0x02, 0x33, 0x08, 0x03, 0x01, 0x07, 0x08, 0x01, 0x08, 0x07, 0x10, 0x09, 0x08, 0x00, 0x22, 0x01, 0x08, 0x02, 0x1d, 0x09, 0x00, 0x32, 0x08, 0x01,
0x04, 0x62, 0x0a, 0x00, 0x34, 0x08, 0x05, 0x01, 0x30, 0x08, 0x06, 0x28, 0x01, 0x27, 0x08, 0x00, 0x07, 0x01, 0x08, 0x02, 0x53, 0x02, 0x08, 0x01,
0x08, 0x00, 0x0d, 0x01, 0x08, 0x07, 0x01, 0x09, 0x05, 0x34, 0x08, 0x07, 0x01, 0x08, 0x01, 0x62, 0x09, 0x02, 0x34, 0x08, 0x08, 0x00, 0x26, 0x00,
0x00, 0x26, 0x00, 0x02, 0x11, 0x08, 0x26, 0x00, 0x5b, 0x30, 0x08, 0x06, 0x02, 0x11, 0x08, 0x3d, 0x08, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x05, 0x41,
0x28, 0x08, 0x00, 0x22, 0x01, 0x08, 0x02, 0x1d, 0x09, 0x00, 0x32, 0x08, 0x72, 0x72, 0x61, 0x79, 0x00, 0x00, 0x05, 0x69, 0x73, 0x5f, 0x61, 0x3f,
0x01, 0x01, 0x27, 0x08, 0x00, 0x07, 0x01, 0x08, 0x02, 0x53, 0x02, 0x08, 0x00, 0x00, 0x06, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x00, 0x00, 0x03,
0x01, 0x01, 0x08, 0x01, 0x62, 0x09, 0x01, 0x34, 0x08, 0x08, 0x00, 0x26, 0x6e, 0x65, 0x77, 0x00, 0x00, 0x03, 0x73, 0x65, 0x74, 0x00, 0x00, 0x17,
0x00, 0x32, 0x01, 0x08, 0x03, 0x1d, 0x09, 0x09, 0x32, 0x08, 0x01, 0x01, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x5f, 0x73, 0x69, 0x6e, 0x67, 0x6c,
0x28, 0x08, 0x00, 0x22, 0x01, 0x08, 0x02, 0x1d, 0x09, 0x00, 0x32, 0x08, 0x65, 0x74, 0x6f, 0x6e, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x00,
0x01, 0x01, 0x27, 0x08, 0x00, 0x07, 0x01, 0x08, 0x02, 0x53, 0x02, 0x08, 0x00, 0x0c, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x67, 0x69, 0x76, 0x65,
0x01, 0x01, 0x08, 0x01, 0x62, 0x09, 0x02, 0x34, 0x08, 0x08, 0x00, 0x26, 0x6e, 0x3f, 0x00, 0x00, 0x0d, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63,
0x00, 0x02, 0x11, 0x08, 0x3d, 0x08, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x05, 0x65, 0x5f, 0x65, 0x78, 0x65, 0x63, 0x00, 0x00, 0x04, 0x65, 0x61, 0x63,
0x41, 0x72, 0x72, 0x61, 0x79, 0x00, 0x00, 0x05, 0x69, 0x73, 0x5f, 0x61, 0x68, 0x00, 0x00, 0x06, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x00, 0x00,
0x3f, 0x00, 0x00, 0x06, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x00, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00,
0x03, 0x6e, 0x65, 0x77, 0x00, 0x00, 0x03, 0x73, 0x65, 0x74, 0x00, 0x00, 0x00, 0x00, 0x26, 0x39, 0x04, 0x20, 0x01, 0x26, 0x00, 0x03, 0x26, 0x00,
0x17, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x5f, 0x73, 0x69, 0x6e, 0x67, 0x02, 0x11, 0x02, 0x01, 0x04, 0x03, 0x21, 0x05, 0x06, 0x00, 0x21, 0x06,
0x6c, 0x65, 0x74, 0x6f, 0x6e, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x01, 0x00, 0x01, 0x07, 0x01, 0x01, 0x08, 0x02, 0x01, 0x09, 0x04, 0x34,
0x00, 0x00, 0x0c, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x67, 0x69, 0x76, 0x05, 0x00, 0x03, 0x3d, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x04, 0x62,
0x65, 0x6e, 0x3f, 0x00, 0x00, 0x0d, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x69, 0x6e, 0x64, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x03, 0x00, 0x05,
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, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x39, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x39, 0x04, 0x00, 0x00,
0x21, 0x03, 0x02, 0x00, 0x62, 0x04, 0x00, 0x34, 0x03, 0x00, 0x00, 0x3d, 0x21, 0x03, 0x02, 0x00, 0x62, 0x04, 0x00, 0x34, 0x03, 0x00, 0x00, 0x3d,
0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x04, 0x65, 0x61, 0x63, 0x68, 0x00, 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, 0x00, 0x00, 0x00, 0x5e, 0x39, 0x04, 0x00, 0x00, 0x19, 0x03, 0x00, 0x21,
0x04, 0x01, 0x00, 0x23, 0x03, 0x27, 0x03, 0x00, 0x0f, 0x5e, 0x03, 0x00, 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, 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, 0x23, 0x03, 0x27, 0x03, 0x00, 0x14, 0x52, 0x03, 0x00, 0x19, 0x04, 0x00,
0x21, 0x05, 0x01, 0x00, 0x23, 0x04, 0x01, 0x05, 0x01, 0x01, 0x06, 0x03, 0x21, 0x05, 0x01, 0x00, 0x23, 0x04, 0x01, 0x05, 0x01, 0x01, 0x06, 0x03,
0x25, 0x04, 0x19, 0x03, 0x00, 0x21, 0x04, 0x01, 0x00, 0x23, 0x03, 0x01, 0x25, 0x04, 0x19, 0x03, 0x00, 0x21, 0x04, 0x01, 0x00, 0x23, 0x03, 0x01,
0x04, 0x01, 0x23, 0x03, 0x21, 0x04, 0x03, 0x01, 0x32, 0x03, 0x01, 0x01, 0x04, 0x01, 0x23, 0x03, 0x21, 0x04, 0x05, 0x01, 0x32, 0x03, 0x01, 0x01,
0x3d, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x0a, 0x40, 0x6b, 0x65, 0x79, 0x3d, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x0d, 0x40, 0x6b, 0x65, 0x79,
0x5f, 0x62, 0x69, 0x6e, 0x64, 0x73, 0x00, 0x00, 0x02, 0x3c, 0x3c, 0x00, 0x5f, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x73, 0x00, 0x00, 0x02,
0x00, 0x00, 0x00, 0x38, 0x00, 0x01, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x3c, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x03, 0x00, 0x05, 0x00,
0x00, 0x00, 0x00, 0x0a, 0x6b, 0x01, 0x00, 0x00, 0x6b, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x39, 0x04, 0x00, 0x00, 0x21,
0x3d, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x10, 0x72, 0x65, 0x71, 0x75, 0x03, 0x02, 0x00, 0x62, 0x04, 0x00, 0x34, 0x03, 0x00, 0x00, 0x3d, 0x03,
0x69, 0x72, 0x65, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x76, 0x65, 0x00, 0x00, 0x00, 0x01, 0x00, 0x04, 0x65, 0x61, 0x63, 0x68, 0x00, 0x00,
0x00, 0x00, 0x04, 0x6c, 0x6f, 0x61, 0x64, 0x00, 0x00, 0x00, 0x01, 0x07, 0x00, 0x00, 0x84, 0x00, 0x03, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x05, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x5e, 0x39, 0x04, 0x00, 0x00, 0x19, 0x03, 0x00, 0x21, 0x04,
0x39, 0x04, 0x20, 0x00, 0x26, 0x00, 0x03, 0x26, 0x00, 0x02, 0x11, 0x02, 0x01, 0x00, 0x23, 0x03, 0x27, 0x03, 0x00, 0x0f, 0x5e, 0x03, 0x00, 0x19,
0x01, 0x05, 0x01, 0x5c, 0x06, 0x00, 0x32, 0x05, 0x00, 0x01, 0x27, 0x05, 0x04, 0x00, 0x21, 0x05, 0x01, 0x00, 0x01, 0x06, 0x03, 0x25, 0x04, 0x19,
0x00, 0x0b, 0x01, 0x05, 0x01, 0x5c, 0x06, 0x00, 0x45, 0x05, 0x01, 0x01, 0x03, 0x00, 0x21, 0x04, 0x01, 0x00, 0x23, 0x03, 0x01, 0x04, 0x01, 0x23,
0x05, 0x1d, 0x05, 0x01, 0x01, 0x06, 0x01, 0x1d, 0x07, 0x01, 0x1d, 0x08, 0x03, 0x27, 0x03, 0x00, 0x14, 0x52, 0x03, 0x00, 0x19, 0x04, 0x00, 0x21,
0x02, 0x33, 0x08, 0x03, 0x32, 0x07, 0x04, 0x01, 0x32, 0x05, 0x05, 0x02, 0x05, 0x01, 0x00, 0x23, 0x04, 0x01, 0x05, 0x01, 0x01, 0x06, 0x03, 0x25,
0x01, 0x01, 0x05, 0x15, 0x05, 0x06, 0x01, 0x06, 0x01, 0x32, 0x05, 0x07, 0x04, 0x19, 0x03, 0x00, 0x21, 0x04, 0x01, 0x00, 0x23, 0x03, 0x01, 0x04,
0x01, 0x28, 0x05, 0x00, 0x01, 0x40, 0x15, 0x05, 0x06, 0x01, 0x06, 0x01, 0x01, 0x23, 0x03, 0x21, 0x04, 0x03, 0x01, 0x32, 0x03, 0x01, 0x01, 0x3d,
0x32, 0x05, 0x08, 0x01, 0x1d, 0x05, 0x01, 0x01, 0x06, 0x01, 0x32, 0x05, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x0a, 0x40, 0x6b, 0x65, 0x79, 0x5f,
0x09, 0x01, 0x01, 0x04, 0x05, 0x01, 0x06, 0x04, 0x01, 0x07, 0x02, 0x27, 0x62, 0x69, 0x6e, 0x64, 0x73, 0x00, 0x00, 0x02, 0x3c, 0x3c, 0x00, 0x00,
0x07, 0x00, 0x03, 0x15, 0x07, 0x0a, 0x01, 0x08, 0x01, 0x2f, 0x05, 0x0b, 0x00, 0x00, 0x38, 0x00, 0x01, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00,
0x03, 0x3d, 0x05, 0x00, 0x01, 0x00, 0x00, 0x03, 0x2e, 0x72, 0x62, 0x00, 0x00, 0x00, 0x0a, 0x6b, 0x01, 0x00, 0x00, 0x6b, 0x01, 0x01, 0x01, 0x3d,
0x00, 0x0c, 0x00, 0x09, 0x65, 0x6e, 0x64, 0x5f, 0x77, 0x69, 0x74, 0x68, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x10, 0x72, 0x65, 0x71, 0x75, 0x69,
0x3f, 0x00, 0x00, 0x04, 0x46, 0x69, 0x6c, 0x65, 0x00, 0x00, 0x01, 0x43, 0x72, 0x65, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x76, 0x65, 0x00,
0x00, 0x00, 0x0b, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x66, 0x69, 0x00, 0x04, 0x6c, 0x6f, 0x61, 0x64, 0x00, 0x00, 0x00, 0x01, 0x07, 0x00,
0x6c, 0x65, 0x00, 0x00, 0x07, 0x64, 0x69, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x05, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7b, 0x39,
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, 0x20, 0x00, 0x26, 0x00, 0x03, 0x26, 0x00, 0x02, 0x11, 0x02, 0x01,
0x04, 0x01, 0x5c, 0x05, 0x00, 0x32, 0x04, 0x00, 0x01, 0x27, 0x04, 0x00, 0x05, 0x01, 0x5c, 0x06, 0x00, 0x32, 0x05, 0x00, 0x01, 0x27, 0x05, 0x00,
0x0b, 0x01, 0x04, 0x01, 0x5c, 0x05, 0x00, 0x45, 0x04, 0x01, 0x01, 0x04, 0x0b, 0x01, 0x05, 0x01, 0x5c, 0x06, 0x00, 0x45, 0x05, 0x01, 0x01, 0x05,
0x1d, 0x04, 0x01, 0x01, 0x05, 0x01, 0x1d, 0x06, 0x01, 0x1d, 0x07, 0x02, 0x1d, 0x05, 0x01, 0x01, 0x06, 0x01, 0x1d, 0x07, 0x01, 0x1d, 0x08, 0x02,
0x33, 0x07, 0x03, 0x32, 0x06, 0x04, 0x01, 0x32, 0x04, 0x05, 0x02, 0x01, 0x33, 0x08, 0x03, 0x32, 0x07, 0x04, 0x01, 0x32, 0x05, 0x05, 0x02, 0x01,
0x01, 0x04, 0x15, 0x04, 0x06, 0x01, 0x05, 0x01, 0x32, 0x04, 0x07, 0x01, 0x01, 0x05, 0x15, 0x05, 0x06, 0x01, 0x06, 0x01, 0x32, 0x05, 0x07, 0x01,
0x01, 0x05, 0x01, 0x01, 0x06, 0x02, 0x2f, 0x04, 0x08, 0x02, 0x3d, 0x04, 0x28, 0x05, 0x00, 0x01, 0x40, 0x15, 0x05, 0x06, 0x01, 0x06, 0x01, 0x32,
0x00, 0x01, 0x00, 0x00, 0x03, 0x2e, 0x72, 0x62, 0x00, 0x00, 0x09, 0x00, 0x05, 0x08, 0x01, 0x1d, 0x05, 0x01, 0x01, 0x06, 0x01, 0x32, 0x05, 0x09,
0x09, 0x65, 0x6e, 0x64, 0x5f, 0x77, 0x69, 0x74, 0x68, 0x3f, 0x00, 0x00, 0x01, 0x01, 0x04, 0x05, 0x01, 0x06, 0x04, 0x01, 0x07, 0x02, 0x27, 0x07,
0x04, 0x46, 0x69, 0x6c, 0x65, 0x00, 0x00, 0x01, 0x43, 0x00, 0x00, 0x0b, 0x00, 0x03, 0x15, 0x07, 0x0a, 0x01, 0x08, 0x01, 0x2f, 0x05, 0x0b, 0x03,
0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x00, 0x3d, 0x05, 0x00, 0x01, 0x00, 0x00, 0x03, 0x2e, 0x72, 0x62, 0x00, 0x00,
0x00, 0x07, 0x64, 0x69, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x00, 0x00, 0x0b, 0x0c, 0x00, 0x09, 0x65, 0x6e, 0x64, 0x5f, 0x77, 0x69, 0x74, 0x68, 0x3f,
0x65, 0x78, 0x70, 0x61, 0x6e, 0x64, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x00, 0x00, 0x00, 0x04, 0x46, 0x69, 0x6c, 0x65, 0x00, 0x00, 0x01, 0x43, 0x00,
0x00, 0x07, 0x24, 0x4c, 0x4f, 0x41, 0x44, 0x45, 0x44, 0x00, 0x00, 0x06, 0x00, 0x0b, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x66, 0x69, 0x6c,
0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x00, 0x00, 0x10, 0x72, 0x65, 0x71, 0x65, 0x00, 0x00, 0x07, 0x64, 0x69, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x00,
0x75, 0x69, 0x72, 0x65, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x76, 0x00, 0x0b, 0x65, 0x78, 0x70, 0x61, 0x6e, 0x64, 0x5f, 0x70, 0x61, 0x74,
0x65, 0x00, 0x4c, 0x56, 0x41, 0x52, 0x00, 0x00, 0x01, 0x78, 0x00, 0x00, 0x68, 0x00, 0x00, 0x07, 0x24, 0x4c, 0x4f, 0x41, 0x44, 0x45, 0x44, 0x00,
0x00, 0x1d, 0x00, 0x03, 0x63, 0x6d, 0x64, 0x00, 0x04, 0x74, 0x65, 0x78, 0x00, 0x08, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x3f, 0x00, 0x00,
0x74, 0x00, 0x01, 0x66, 0x00, 0x07, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x02, 0x3c, 0x3c, 0x00, 0x00, 0x04, 0x72, 0x65, 0x61, 0x64, 0x00, 0x00,
0x64, 0x00, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x00, 0x0a, 0x6d, 0x6f, 0x64, 0x09, 0x24, 0x42, 0x49, 0x4e, 0x44, 0x5f, 0x54, 0x4f, 0x50, 0x00, 0x00,
0x65, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x00, 0x0b, 0x6d, 0x6f, 0x64, 0x04, 0x65, 0x76, 0x61, 0x6c, 0x00, 0x00, 0x00, 0x00, 0xd3, 0x00, 0x04,
0x65, 0x5f, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x00, 0x09, 0x6c, 0x61, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x39, 0x04,
0x6e, 0x67, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x00, 0x08, 0x66, 0x69, 0x6c, 0x20, 0x00, 0x26, 0x00, 0x03, 0x26, 0x00, 0x02, 0x11, 0x02, 0x01, 0x04,
0x65, 0x6e, 0x61, 0x6d, 0x65, 0x00, 0x08, 0x73, 0x74, 0x61, 0x72, 0x74, 0x01, 0x5c, 0x05, 0x00, 0x32, 0x04, 0x00, 0x01, 0x27, 0x04, 0x00, 0x0b,
0x69, 0x6e, 0x67, 0x00, 0x0a, 0x68, 0x69, 0x67, 0x68, 0x6c, 0x69, 0x67, 0x01, 0x04, 0x01, 0x5c, 0x05, 0x00, 0x45, 0x04, 0x01, 0x01, 0x04, 0x1d,
0x68, 0x74, 0x73, 0x00, 0x04, 0x74, 0x79, 0x70, 0x65, 0x00, 0x0a, 0x66, 0x04, 0x01, 0x01, 0x05, 0x01, 0x1d, 0x06, 0x01, 0x1d, 0x07, 0x02, 0x33,
0x69, 0x72, 0x73, 0x74, 0x5f, 0x6c, 0x69, 0x6e, 0x65, 0x00, 0x07, 0x73, 0x07, 0x03, 0x32, 0x06, 0x04, 0x01, 0x32, 0x04, 0x05, 0x02, 0x01, 0x01,
0x68, 0x65, 0x62, 0x61, 0x6e, 0x67, 0x00, 0x08, 0x6d, 0x69, 0x6d, 0x65, 0x04, 0x15, 0x04, 0x06, 0x01, 0x05, 0x01, 0x32, 0x04, 0x07, 0x01, 0x01,
0x74, 0x79, 0x70, 0x65, 0x00, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x00, 0x05, 0x01, 0x01, 0x06, 0x02, 0x2f, 0x04, 0x08, 0x02, 0x3d, 0x04, 0x00,
0x05, 0x6d, 0x6f, 0x64, 0x65, 0x73, 0x00, 0x04, 0x6b, 0x65, 0x79, 0x73, 0x01, 0x00, 0x00, 0x03, 0x2e, 0x72, 0x62, 0x00, 0x00, 0x09, 0x00, 0x09,
0x00, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x03, 0x61, 0x70, 0x65, 0x6e, 0x64, 0x5f, 0x77, 0x69, 0x74, 0x68, 0x3f, 0x00, 0x00, 0x04,
0x70, 0x00, 0x03, 0x64, 0x73, 0x6c, 0x00, 0x01, 0x6b, 0x00, 0x03, 0x61, 0x46, 0x69, 0x6c, 0x65, 0x00, 0x00, 0x01, 0x43, 0x00, 0x00, 0x0b, 0x63,
0x63, 0x74, 0x00, 0x03, 0x62, 0x6c, 0x6b, 0x00, 0x04, 0x6d, 0x6f, 0x64, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x00, 0x00,
0x65, 0x00, 0x03, 0x6b, 0x65, 0x79, 0x00, 0x04, 0x70, 0x61, 0x74, 0x68, 0x07, 0x64, 0x69, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x00, 0x00, 0x0b, 0x65,
0x00, 0x04, 0x62, 0x69, 0x6e, 0x64, 0x00, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x78, 0x70, 0x61, 0x6e, 0x64, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x00, 0x00,
0x00, 0x00, 0xff, 0xff, 0x00, 0x01, 0xff, 0xff, 0x00, 0x02, 0xff, 0xff, 0x07, 0x24, 0x4c, 0x4f, 0x41, 0x44, 0x45, 0x44, 0x00, 0x00, 0x06, 0x64,
0x00, 0x02, 0xff, 0xff, 0x00, 0x02, 0xff, 0xff, 0x00, 0x02, 0xff, 0xff, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x00, 0x00, 0x10, 0x72, 0x65, 0x71, 0x75,
0x00, 0x02, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0xff, 0xff, 0x00, 0x03, 0x69, 0x72, 0x65, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x76, 0x65,
0x00, 0x04, 0xff, 0xff, 0x00, 0x05, 0x00, 0x06, 0x00, 0x07, 0x00, 0x08, 0x00, 0x4c, 0x56, 0x41, 0x52, 0x00, 0x00, 0x01, 0x78, 0x00, 0x00, 0x00,
0x00, 0x09, 0x00, 0x0a, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0x00, 0x08, 0x1d, 0x00, 0x03, 0x63, 0x6d, 0x64, 0x00, 0x04, 0x74, 0x65, 0x78, 0x74,
0xff, 0xff, 0x00, 0x0b, 0x00, 0x0c, 0x00, 0x0d, 0x00, 0x0e, 0xff, 0xff, 0x00, 0x01, 0x66, 0x00, 0x07, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x64,
0x00, 0x0f, 0xff, 0xff, 0x00, 0x0f, 0xff, 0xff, 0x00, 0x0f, 0xff, 0xff, 0x00, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x00, 0x0a, 0x6d, 0x6f, 0x64, 0x65,
0x00, 0x0f, 0xff, 0xff, 0x00, 0x0f, 0xff, 0xff, 0x00, 0x0f, 0xff, 0xff, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x00, 0x0b, 0x6d, 0x6f, 0x64, 0x65,
0x00, 0x0f, 0x00, 0x10, 0x00, 0x11, 0x00, 0x12, 0xff, 0xff, 0x00, 0x0f, 0x5f, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x00, 0x09, 0x6c, 0x61, 0x6e,
0x00, 0x13, 0x00, 0x14, 0x00, 0x15, 0x00, 0x16, 0xff, 0xff, 0x00, 0x17, 0x67, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x00, 0x08, 0x66, 0x69, 0x6c, 0x65,
0x00, 0x18, 0xff, 0xff, 0x00, 0x19, 0xff, 0xff, 0x00, 0x18, 0xff, 0xff, 0x6e, 0x61, 0x6d, 0x65, 0x00, 0x08, 0x73, 0x74, 0x61, 0x72, 0x74, 0x69,
0x00, 0x19, 0xff, 0xff, 0x00, 0x1a, 0x00, 0x1b, 0xff, 0xff, 0x00, 0x1c, 0x6e, 0x67, 0x00, 0x0a, 0x68, 0x69, 0x67, 0x68, 0x6c, 0x69, 0x67, 0x68,
0x00, 0x1a, 0x00, 0x1b, 0xff, 0xff, 0x45, 0x4e, 0x44, 0x00, 0x00, 0x00, 0x74, 0x73, 0x00, 0x04, 0x74, 0x79, 0x70, 0x65, 0x00, 0x0a, 0x66, 0x69,
0x00, 0x08 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;
+17 -2
View File
@@ -6,6 +6,8 @@
#include "pch.h" #include "pch.h"
#include "syntax/trie.h" #include "syntax/trie.h"
#define MAX_LINES_LOOKAROUND 512
struct Highlight { struct Highlight {
uint32_t fg{0xFFFFFF}; uint32_t fg{0xFFFFFF};
uint32_t bg{0x000000}; uint32_t bg{0x000000};
@@ -35,10 +37,23 @@ struct Token {
TokenKind type; TokenKind type;
}; };
struct StateBase {
virtual ~StateBase() = default;
virtual std::unique_ptr<StateBase> clone() const = 0;
};
struct CustomState : StateBase {
std::string value;
explicit CustomState(std::string v) : value(std::move(v)) {}
std::unique_ptr<StateBase> clone() const override {
return std::make_unique<CustomState>(value);
}
};
struct LineData { struct LineData {
std::shared_ptr<void> in_state{nullptr}; std::unique_ptr<StateBase> out_state;
std::vector<Token> tokens; std::vector<Token> tokens;
std::shared_ptr<void> out_state{nullptr}; std::unique_ptr<StateBase> in_state;
}; };
#endif #endif
+10 -21
View File
@@ -4,29 +4,19 @@
#include "syntax/decl.h" #include "syntax/decl.h"
#define DEF_LANG(name) \ #define DEF_LANG(name) \
std::shared_ptr<void> name##_parse( \ std::unique_ptr<StateBase> name##_parse( \
std::vector<Token> *tokens, std::shared_ptr<void> in_state, \ std::vector<Token> *tokens, StateBase *in_state, const char *text, \
const char *text, uint32_t len, uint32_t line_num); \ uint32_t len, uint32_t line_num); \
bool name##_state_match(std::shared_ptr<void> state_1, \ bool name##_state_match(StateBase *state_1, StateBase *state_2);
std::shared_ptr<void> state_2);
#define LANG_A(name) \ #define LANG_A(name) \
{ \ { \
#name, { name##_parse, name##_state_match } \ #name, { name##_parse, name##_state_match } \
} }
template <typename T> template <typename B, typename A>
inline std::shared_ptr<T> ensure_state(std::shared_ptr<T> state) { std::unique_ptr<B> static_unique_ptr_cast(std::unique_ptr<A> &&p) {
using U = typename T::full_state_type; return std::unique_ptr<B>(static_cast<B *>(p.release()));
if (!state)
state = std::make_shared<T>();
if (!state.unique())
state = std::make_shared<T>(*state);
if (!state->full_state)
state->full_state = std::make_shared<U>();
else if (!state->full_state.unique())
state->full_state = std::make_shared<U>(*state->full_state);
return state;
} }
DEF_LANG(ruby); DEF_LANG(ruby);
@@ -34,11 +24,10 @@ DEF_LANG(bash);
inline static const std::unordered_map< inline static const std::unordered_map<
std::string, std::string,
std::tuple<std::shared_ptr<void> (*)( std::tuple<std::unique_ptr<StateBase> (*)(
std::vector<Token> *tokens, std::shared_ptr<void> in_state, std::vector<Token> *tokens, StateBase *in_state,
const char *text, uint32_t len, uint32_t line_num), const char *text, uint32_t len, uint32_t line_num),
bool (*)(std::shared_ptr<void> state_1, bool (*)(StateBase *state_1, StateBase *state_2)>>
std::shared_ptr<void> state_2)>>
parsers = { parsers = {
LANG_A(ruby), LANG_A(ruby),
LANG_A(bash), LANG_A(bash),
+136
View File
@@ -0,0 +1,136 @@
#ifndef LINE_MAP_H
#define LINE_MAP_H
#include "syntax/decl.h"
struct LineKey {
uint32_t line;
uint32_t version;
bool operator==(const LineKey &other) const {
return line == other.line && version == other.version;
}
};
struct LineKeyHash {
size_t operator()(const LineKey &key) const {
uint64_t combined = (uint64_t(key.line) << 32) | key.version;
return std::hash<uint64_t>()(combined);
}
};
struct EditDelta {
uint32_t start_line;
int64_t delta;
};
struct LineMap {
LineMap() : current_version(0) { edit_log.push_back({0, 0}); }
LineData *at(uint32_t line) {
auto key_opt = resolve_line(line);
if (!key_opt)
return nullptr;
LineKey key = *key_opt;
if (key.version == current_version)
return lines[key].get();
auto data_ptr = std::move(lines[key]);
lines.erase(key);
key = {line, current_version};
lines[key] = std::move(data_ptr);
return lines[key].get();
}
LineData *create_at(uint32_t line) {
auto key_opt = resolve_line(line);
LineKey key;
std::unique_ptr<LineData> data_ptr;
if (key_opt) {
key = *key_opt;
if (key.version == current_version)
return lines[key].get();
data_ptr = std::move(lines[key]);
lines.erase(key);
} else {
data_ptr = std::make_unique<LineData>();
}
key = {line, current_version};
lines[key] = std::move(data_ptr);
return lines[key].get();
}
void apply_edit(uint32_t start, int64_t delta) {
if (delta < 0)
batch_remove(start, -delta);
current_version++;
edit_log.push_back({start, delta});
}
private:
std::unordered_map<LineKey, std::unique_ptr<LineData>, LineKeyHash> lines;
std::vector<EditDelta> edit_log;
uint32_t current_version;
void batch_remove(uint32_t line, uint32_t count) {
std::vector<uint32_t> lines_t;
lines_t.reserve(count);
for (uint32_t i = 0; i < count; i++)
lines_t.push_back(line + i);
for (int64_t v = current_version; v >= 0; v--) {
for (auto &l : lines_t) {
if (l == UINT32_MAX)
continue;
LineKey key = {l, (uint32_t)v};
if (lines.find(key) != lines.end()) {
lines.erase(key);
l = UINT32_MAX;
}
}
bool all_removed = true;
const auto &edit = edit_log[v];
for (auto &l : lines_t) {
if (l == UINT32_MAX)
continue;
all_removed = false;
if (edit.delta > 0) {
if (l >= edit.start_line) {
if (l < edit.start_line + edit.delta)
l = UINT32_MAX;
else
l -= edit.delta;
}
} else {
if (l >= edit.start_line)
l -= edit.delta;
}
}
if (all_removed)
break;
}
return;
}
std::optional<LineKey> resolve_line(uint32_t line) {
uint32_t current_line = line;
for (int64_t v = current_version; v >= 0; v--) {
LineKey key = {current_line, (uint32_t)v};
if (lines.find(key) != lines.end())
return key;
const auto &edit = edit_log[v];
if (edit.delta > 0) {
if (current_line >= edit.start_line) {
if (current_line < edit.start_line + edit.delta)
return std::nullopt;
current_line -= edit.delta;
}
} else {
if (current_line >= edit.start_line)
current_line -= edit.delta;
}
}
return std::nullopt;
}
};
#endif
-232
View File
@@ -1,232 +0,0 @@
#ifndef LINE_TREE_H
#define LINE_TREE_H
#include "syntax/decl.h"
struct LineTree {
void clear() {
clear_node(root);
root = nullptr;
stack_size = 0;
}
void build(uint32_t x) { root = build_node(x); }
LineData *at(uint32_t x) {
LineNode *n = root;
while (n) {
uint32_t left_size = n->left ? n->left->size : 0;
if (x < left_size) {
n = n->left;
} else if (x < left_size + n->data.size()) {
return &n->data[x - left_size];
} else {
x -= left_size + n->data.size();
n = n->right;
}
}
return nullptr;
}
LineData *start_iter(uint32_t x) {
stack_size = 0;
LineNode *n = root;
while (n) {
uint32_t left_size = n->left ? n->left->size : 0;
if (x < left_size) {
push(n, 0);
n = n->left;
} else if (x < left_size + n->data.size()) {
push(n, x - left_size + 1);
return &n->data[x - left_size];
} else {
x -= left_size + n->data.size();
push(n, UINT32_MAX);
n = n->right;
}
}
return nullptr;
}
void end_iter() { stack_size = 0; }
LineData *next() {
while (stack_size) {
auto &f = stack[stack_size - 1];
LineNode *n = f.node;
if (f.index < n->data.size())
return &n->data[f.index++];
stack_size--;
if (n->right) {
n = n->right;
while (n) {
push(n, 0);
if (!n->left)
break;
n = n->left;
}
return &stack[stack_size - 1].node->data[0];
}
}
return nullptr;
}
void insert(uint32_t x, uint32_t y) {
if (x > subtree_size(root))
x = subtree_size(root);
root = insert_node(root, x, y);
}
void erase(uint32_t x, uint32_t y) {
if (x + y > subtree_size(root))
x = subtree_size(root) - y;
root = erase_node(root, x, y);
}
uint32_t count() { return subtree_size(root); }
~LineTree() { clear(); }
private:
struct LineNode {
LineNode *left = nullptr;
LineNode *right = nullptr;
uint8_t depth = 1;
uint32_t size = 0;
std::vector<LineData> data;
};
struct Frame {
LineNode *node;
uint32_t index;
};
void push(LineNode *n, uint32_t x) {
stack[stack_size].node = n;
stack[stack_size].index = x;
stack_size++;
}
static void clear_node(LineNode *n) {
if (!n)
return;
clear_node(n->left);
clear_node(n->right);
delete n;
}
LineNode *root = nullptr;
Frame stack[32];
std::atomic<uint8_t> stack_size = 0;
static constexpr uint32_t LEAF_TARGET = 256;
LineTree::LineNode *erase_node(LineNode *n, uint32_t x, uint32_t y) {
if (!n || y == 0)
return n;
uint32_t left_sz = subtree_size(n->left);
uint32_t mid_sz = n->data.size();
if (x < left_sz) {
uint32_t len = std::min(y, left_sz - x);
n->left = erase_node(n->left, x, len);
y -= len;
x = left_sz;
}
if (y > 0 && x < left_sz + mid_sz) {
uint32_t mid_x = x - left_sz;
uint32_t len = std::min(y, mid_sz - mid_x);
n->data.erase(n->data.begin() + mid_x, n->data.begin() + mid_x + len);
y -= len;
x += len;
}
if (y > 0) {
n->right = erase_node(n->right, x - left_sz - n->data.size(), y);
}
if (n->left && n->right &&
subtree_size(n->left) + subtree_size(n->right) < 256) {
return merge(n->left, n->right);
}
return rebalance(n);
}
LineTree::LineNode *insert_node(LineNode *n, uint32_t x, uint32_t y) {
if (!n) {
auto *leaf = new LineNode();
leaf->data.resize(y);
leaf->size = y;
return leaf;
}
if (!n->left && !n->right) {
n->data.insert(n->data.begin() + x, y, LineData());
fix(n);
if (n->data.size() > 512)
return split_leaf(n);
return n;
}
uint32_t left_size = subtree_size(n->left);
if (x <= left_size)
n->left = insert_node(n->left, x, y);
else
n->right = insert_node(n->right, x - left_size - n->data.size(), y);
return rebalance(n);
}
LineNode *build_node(uint32_t count) {
if (count <= LEAF_TARGET) {
auto *n = new LineNode();
n->data.resize(count);
n->size = count;
return n;
}
uint32_t left_count = count / 2;
uint32_t right_count = count - left_count;
auto *n = new LineNode();
n->left = build_node(left_count);
n->right = build_node(right_count);
fix(n);
return n;
}
static LineNode *split_leaf(LineNode *n) {
auto *right = new LineNode();
size_t mid = n->data.size() / 2;
right->data.assign(n->data.begin() + mid, n->data.end());
n->data.resize(mid);
fix(n);
fix(right);
auto *parent = new LineNode();
parent->left = n;
parent->right = right;
fix(parent);
return parent;
}
static LineNode *merge(LineNode *a, LineNode *b) {
a->data.insert(a->data.end(), b->data.begin(), b->data.end());
delete b;
fix(a);
return a;
}
static void fix(LineNode *n) {
n->depth = 1 + MAX(height(n->left), height(n->right));
n->size = subtree_size(n->left) + n->data.size() + subtree_size(n->right);
}
static LineNode *rotate_right(LineNode *y) {
LineNode *x = y->left;
LineNode *T2 = x->right;
x->right = y;
y->left = T2;
fix(y);
fix(x);
return x;
}
static LineNode *rotate_left(LineNode *x) {
LineNode *y = x->right;
LineNode *T2 = y->left;
y->left = x;
x->right = T2;
fix(x);
fix(y);
return y;
}
static LineNode *rebalance(LineNode *n) {
fix(n);
int balance = int(height(n->left)) - int(height(n->right));
if (balance > 1) {
if (height(n->left->left) < height(n->left->right))
n->left = rotate_left(n->left);
return rotate_right(n);
}
if (balance < -1) {
if (height(n->right->right) < height(n->right->left))
n->right = rotate_right(n->right);
return rotate_left(n);
}
return n;
}
static uint8_t height(LineNode *n) { return n ? n->depth : 0; }
static uint32_t subtree_size(LineNode *n) { return n ? n->size : 0; }
};
#endif
+7 -9
View File
@@ -3,24 +3,22 @@
#include "ruby/decl.h" #include "ruby/decl.h"
#include "syntax/decl.h" #include "syntax/decl.h"
#include "syntax/line_tree.h" #include "syntax/line_map.h"
struct Parser { struct Parser {
struct Editor *editor = nullptr; struct Editor *editor = nullptr;
std::string lang; std::string lang;
std::shared_ptr<void> (*parse_func)(std::vector<Token> *tokens, std::unique_ptr<StateBase> (*parse_func)(std::vector<Token> *tokens,
std::shared_ptr<void> in_state, StateBase *in_state,
const char *text, uint32_t len, const char *text, uint32_t len,
uint32_t line_num); uint32_t line_num);
bool (*state_match_func)(std::shared_ptr<void> state_1, bool (*state_match_func)(StateBase *state_1, StateBase *state_2);
std::shared_ptr<void> state_2);
mrb_value parser_block = mrb_nil_value(); mrb_value parser_block = mrb_nil_value();
mrb_value match_block = mrb_nil_value(); mrb_value match_block = mrb_nil_value();
bool is_custom{false}; bool is_custom{false};
std::atomic<uint32_t> scroll_max{0}; std::atomic<uint32_t> scroll_max{0};
std::atomic<bool> scroll_dirty{false}; std::atomic<bool> scroll_dirty{false};
LineTree line_tree; LineMap line_map = LineMap();
UniqueQueue<uint32_t> dirty_lines;
Parser(Editor *editor, std::string n_lang, uint32_t n_scroll_max); Parser(Editor *editor, std::string n_lang, uint32_t n_scroll_max);
void edit(uint32_t start_line, uint32_t removed_rows, uint32_t inserted_rows); void edit(uint32_t start_line, uint32_t removed_rows, uint32_t inserted_rows);
-10
View File
@@ -1,10 +0,0 @@
#ifndef TILING_DECL_H
#define TILING_DECL_H
#include "utils/utils.h"
struct Window {};
struct TileBlock {};
#endif
+3 -2
View File
@@ -12,8 +12,9 @@ struct Bar {
uint32_t cursor = 0; uint32_t cursor = 0;
void init(Coord screen) { this->screen = screen; } void init(Coord screen) { this->screen = screen; }
void render(); void render(std::vector<ScreenCell> &buffer);
void handle(KeyEvent event); void handle_event(KeyEvent event);
void handle_command(std::string &command);
void log(std::string message); void log(std::string message);
}; };
-21
View File
@@ -1,21 +0,0 @@
#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
-19
View File
@@ -1,19 +0,0 @@
#ifndef UI_DIAGNOSTICS_H
#define UI_DIAGNOSTICS_H
#include "editor/decl.h"
#include "io/sysio.h"
#include "pch.h"
#include "utils/utils.h"
struct DiagnosticBox {
std::vector<VWarn> warnings;
std::vector<ScreenCell> cells;
Coord size;
void clear();
void render_first();
void render(Coord pos);
};
#endif
-22
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
+10 -3
View File
@@ -6,18 +6,19 @@
template <typename T> struct Queue { template <typename T> struct Queue {
void push(T val) { void push(T val) {
std::lock_guard<std::mutex> lock(m); std::lock_guard<std::mutex> lock(m);
q.push(val); q.push(std::move(val));
} }
std::optional<T> front() { std::optional<T> front() {
std::lock_guard<std::mutex> lock(m);
if (q.empty()) if (q.empty())
return std::nullopt; return std::nullopt;
return q.front(); return std::move(q.front());
} }
bool pop(T &val) { bool pop(T &val) {
std::lock_guard<std::mutex> lock(m); std::lock_guard<std::mutex> lock(m);
if (q.empty()) if (q.empty())
return false; return false;
val = q.front(); val = std::move(q.front());
q.pop(); q.pop();
return true; return true;
} }
@@ -92,6 +93,11 @@ struct Coord {
bool operator>=(const Coord &other) const { return !(*this < other); } bool operator>=(const Coord &other) const { return !(*this < other); }
}; };
static inline bool inside(Coord c, Coord pos, Coord size) {
return c.row >= pos.row && c.col >= pos.col && c.row - pos.row < size.row &&
c.col - pos.col < size.col;
}
struct Match { struct Match {
size_t start; size_t start;
size_t end; size_t end;
@@ -132,6 +138,7 @@ std::string percent_encode(const std::string &s);
std::string percent_decode(const std::string &s); std::string percent_decode(const std::string &s);
uint32_t count_clusters(const char *line, size_t len, size_t from, size_t to); uint32_t count_clusters(const char *line, size_t len, size_t from, size_t to);
std::string trim(const std::string &s); std::string trim(const std::string &s);
std::string strip_newlines(const std::string &s);
std::string substitute_fence(const std::string &documentation, std::string substitute_fence(const std::string &documentation,
const std::string &lang); const std::string &lang);
+119
View File
@@ -0,0 +1,119 @@
#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 Popup : Window {
Coord pos;
Coord size;
virtual ~Popup() = default;
};
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 TileBase {
std::unique_ptr<Tile> tile;
Coord pos;
Coord size;
inline bool hidden() { return this->tile->hidden; }
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);
}
};
namespace layout {
extern TileBase root_tile;
extern Window *focused_window;
extern std::vector<std::unique_ptr<Popup>> popups;
extern std::vector<std::unique_ptr<TileBase>> floating_tiles;
} // namespace layout
void render();
void handle_click(KeyEvent event);
#endif
Regular → Executable
View File
+1
View File
@@ -25,6 +25,7 @@ colorize() {
handle_error() { handle_error() {
log ERROR "An error occurred on line $1" log ERROR "An error occurred on line $1"
} }
trap 'handle_error $LINENO' ERR trap 'handle_error $LINENO' ERR
# Multiline string test # Multiline string test
read -r -d '' MULTI <<'CPP' read -r -d '' MULTI <<'CPP'
-1
View File
@@ -339,6 +339,5 @@ puts 'Ruby syntax highlighting test complete.'
__END__ __END__
Anything here should be ignored >><< Anything here should be ignored >><<
{{{}}}[[[]]](((000))) {{{}}}[[[]]](((000)))
+47 -91
View File
@@ -1,83 +1,39 @@
#include "editor/editor.h" #include "editor/editor.h"
void ensure_cursor(Editor *editor) { void Editor::ensure_cursor() {
std::shared_lock knot_lock(editor->knot_mtx); if (this->cursor < this->scroll) {
if (editor->cursor < editor->scroll) { this->cursor = this->scroll;
editor->cursor.row = editor->scroll.row; this->cursor_preffered = UINT32_MAX;
editor->cursor.col = editor->scroll.col;
editor->cursor_preffered = UINT32_MAX;
return; return;
} }
uint32_t numlen = uint32_t numlen =
EXTRA_META + static_cast<int>(std::log10(editor->root->line_count + 1)); EXTRA_META + static_cast<int>(std::log10(this->root->line_count + 1));
uint32_t render_width = editor->size.col - numlen; uint32_t render_width = this->size.col - numlen;
uint32_t visual_rows = 0; uint32_t render_height = 0;
uint32_t line_index = editor->scroll.row; VisualIterator vis(root, scroll, render_width);
bool first_visual_line = true; std::pair<Coord, Coord> vline;
LineIterator *it = begin_l_iter(editor->root, line_index); Coord last_visible = scroll;
if (!it) while ((vline = vis.next()).first.row != UINT32_MAX &&
return; render_height < this->size.row) {
Coord last_visible = editor->scroll; render_height++;
while (true) { last_visible = vline.first;
if (visual_rows >= editor->size.row) log("Render H: %d, Cursor: %d,%d, Visible: %d,%d - %d,%d", render_height,
break; this->cursor.row, this->cursor.col, vline.first.row, vline.first.col,
uint32_t line_len; vline.second.row, vline.second.col);
char *line = next_line(it, &line_len); if (cursor >= vline.first && cursor <= vline.second)
if (!line) return;
break;
if (line_len > 0 && line[line_len - 1] == '\n')
line_len--;
uint32_t offset = first_visual_line ? editor->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)
break;
uint32_t col = 0;
uint32_t advance = 0;
uint32_t left = line_len - offset;
while (left > 0 && col < render_width) {
uint32_t g =
grapheme_next_character_break_utf8(line + offset + advance, left);
int w = display_width(line + offset + advance, g);
if (col + w > render_width)
break;
advance += g;
left -= g;
col += w;
}
if (line_index == editor->cursor.row) {
if (editor->cursor.col >= offset &&
editor->cursor.col <= offset + advance) {
free(it->buffer);
free(it);
return;
}
}
if (advance == 0)
break;
offset += advance;
if (line_len == 0)
break;
}
line_index++;
} }
editor->cursor.row = last_visible.row; cursor = last_visible;
editor->cursor.col = last_visible.col; cursor_preffered = UINT32_MAX;
editor->cursor_preffered = UINT32_MAX; this->cursor_preffered = UINT32_MAX;
free(it->buffer);
free(it);
} }
void ensure_scroll(Editor *editor) { void Editor::ensure_scroll() {
std::shared_lock knot_lock(editor->knot_mtx);
uint32_t numlen = uint32_t numlen =
EXTRA_META + static_cast<int>(std::log10(editor->root->line_count + 1)); EXTRA_META + static_cast<int>(std::log10(this->root->line_count + 1));
uint32_t render_width = editor->size.col - numlen; uint32_t render_width = this->size.col - numlen;
if (editor->cursor < editor->scroll) { if (this->cursor < this->scroll) {
LineIterator *it = begin_l_iter(editor->root, editor->cursor.row); LineIterator *it = begin_l_iter(this->root, this->cursor.row);
if (!it) if (!it)
return; return;
uint32_t len; uint32_t len;
@@ -98,9 +54,9 @@ void ensure_scroll(Editor *editor) {
int width = display_width(line + offset, inc); int width = display_width(line + offset, inc);
if (cols + width > render_width) { if (cols + width > render_width) {
cols = 0; cols = 0;
if (editor->cursor.col > old_offset && editor->cursor.col <= offset) { if (this->cursor.col > old_offset && this->cursor.col <= offset) {
editor->scroll.row = editor->cursor.row; this->scroll.row = this->cursor.row;
editor->scroll.col = old_offset; this->scroll.col = old_offset;
free(it->buffer); free(it->buffer);
free(it); free(it);
return; return;
@@ -112,14 +68,14 @@ void ensure_scroll(Editor *editor) {
} }
free(it->buffer); free(it->buffer);
free(it); free(it);
editor->scroll.row = editor->cursor.row; this->scroll.row = this->cursor.row;
editor->scroll.col = (editor->cursor.col == 0) ? 0 : old_offset; this->scroll.col = (this->cursor.col == 0) ? 0 : old_offset;
} else if (editor->cursor.row - editor->scroll.row < editor->size.row * 2) { } else if (this->cursor.row - this->scroll.row < this->size.row * 2) {
uint32_t line_index = editor->scroll.row; uint32_t line_index = this->scroll.row;
LineIterator *it = begin_l_iter(editor->root, line_index); LineIterator *it = begin_l_iter(this->root, line_index);
if (!it) if (!it)
return; 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); Coord *scroll_queue = (Coord *)malloc(sizeof(Coord) * max_visual_lines);
uint32_t q_head = 0; uint32_t q_head = 0;
uint32_t q_size = 0; uint32_t q_size = 0;
@@ -133,7 +89,7 @@ void ensure_scroll(Editor *editor) {
line_len--; line_len--;
uint32_t current_byte_offset = 0; uint32_t current_byte_offset = 0;
if (first_visual_line) { if (first_visual_line) {
current_byte_offset += editor->scroll.col; current_byte_offset += this->scroll.col;
first_visual_line = false; first_visual_line = false;
} }
while (current_byte_offset < line_len || while (current_byte_offset < line_len ||
@@ -160,16 +116,16 @@ void ensure_scroll(Editor *editor) {
line_left -= cluster_len; line_left -= cluster_len;
col += width; col += width;
} }
if (line_index == editor->cursor.row) { if (line_index == this->cursor.row) {
bool cursor_found = false; bool cursor_found = false;
if (editor->cursor.col >= current_byte_offset && if (this->cursor.col >= current_byte_offset &&
editor->cursor.col < current_byte_offset + local_render_offset) this->cursor.col < current_byte_offset + local_render_offset)
cursor_found = true; 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) current_byte_offset + local_render_offset == line_len)
cursor_found = true; cursor_found = true;
if (cursor_found) { if (cursor_found) {
editor->scroll = scroll_queue[q_head]; this->scroll = scroll_queue[q_head];
free(scroll_queue); free(scroll_queue);
free(it->buffer); free(it->buffer);
free(it); free(it);
@@ -186,10 +142,10 @@ void ensure_scroll(Editor *editor) {
free(it->buffer); free(it->buffer);
free(it); free(it);
} else { } else {
editor->scroll.row = (editor->cursor.row > editor->size.row * 1.5) this->scroll.row = (this->cursor.row > this->size.row * 1.5)
? editor->cursor.row - editor->size.row * 1.5 ? this->cursor.row - this->size.row * 1.5
: 0; : 0;
editor->scroll.col = 0; this->scroll.col = 0;
ensure_scroll(editor); this->ensure_scroll();
} }
} }
+7 -13
View File
@@ -32,12 +32,9 @@ uint32_t scan_right(const char *line, uint32_t len, uint32_t off) {
return i; 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) { uint32_t *next_col) {
if (!editor) LineIterator *it = begin_l_iter(this->root, coord.row);
return;
std::shared_lock lock(editor->knot_mtx);
LineIterator *it = begin_l_iter(editor->root, coord.row);
if (!it) if (!it)
return; return;
uint32_t line_len; 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); 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_col, uint32_t *prev_clusters,
uint32_t *next_clusters) { uint32_t *next_clusters) {
if (!editor) LineIterator *it = begin_l_iter(this->root, coord.row);
return;
std::shared_lock lock(editor->knot_mtx);
LineIterator *it = begin_l_iter(editor->root, coord.row);
if (!it) if (!it)
return; return;
uint32_t line_len; uint32_t line_len;
+10 -14
View File
@@ -1,23 +1,19 @@
#include "editor/editor.h" #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 = uint32_t numlen =
EXTRA_META + static_cast<int>(std::log10(editor->root->line_count + 1)); EXTRA_META + static_cast<int>(std::log10(this->root->line_count + 1));
uint32_t render_width = editor->size.col - numlen; uint32_t render_width = this->size.col - numlen;
x = MAX(x, numlen) - numlen + 1; x = MAX(x, numlen) - numlen - 1;
uint32_t target_visual_row = y; uint32_t target_visual_row = y;
uint32_t visual_row = 0; uint32_t visual_row = 0;
uint32_t line_index = editor->scroll.row; uint32_t line_index = this->scroll.row;
uint32_t last_line_index = editor->scroll.row; uint32_t last_line_index = this->scroll.row;
uint32_t last_col = editor->scroll.col; uint32_t last_col = this->scroll.col;
bool first_visual_line = true; bool first_visual_line = true;
std::shared_lock knot_lock(editor->knot_mtx); LineIterator *it = begin_l_iter(this->root, line_index);
LineIterator *it = begin_l_iter(editor->root, line_index);
if (!it) if (!it)
return editor->scroll; return this->scroll;
while (visual_row <= target_visual_row) { while (visual_row <= target_visual_row) {
uint32_t line_len; uint32_t line_len;
char *line = next_line(it, &line_len); char *line = next_line(it, &line_len);
@@ -27,7 +23,7 @@ Coord editor_hit_test(Editor *editor, uint32_t x, uint32_t y) {
line_len--; line_len--;
last_line_index = line_index; last_line_index = line_index;
last_col = line_len; 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; first_visual_line = false;
while (offset < line_len || (line_len == 0 && offset == 0)) { while (offset < line_len || (line_len == 0 && offset == 0)) {
uint32_t col = 0; uint32_t col = 0;
+13
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);
}
}
+84 -87
View File
@@ -1,9 +1,8 @@
#include "editor/decl.h" /* #include "editor/editor.h"
#include "editor/editor.h"
#include "io/knot.h" #include "io/knot.h"
#include "io/sysio.h" #include "io/sysio.h"
#include "lsp/lsp.h"
#include "main.h" #include "main.h"
#include "ui/completionbox.h"
#include "utils/utils.h" #include "utils/utils.h"
inline static std::string completion_prefix(Editor *editor) { inline static std::string completion_prefix(Editor *editor) {
@@ -68,11 +67,9 @@ void completion_filter(Editor *editor) {
void completion_request(Editor *editor) { void completion_request(Editor *editor) {
Coord hook = editor->cursor; Coord hook = editor->cursor;
word_boundaries(editor, editor->cursor, &hook.col, nullptr, nullptr, nullptr); editor->word_boundaries(editor->cursor, &hook.col, nullptr, nullptr,
editor->completion.hook = hook; nullptr); editor->completion.hook = hook; editor->completion.complete =
editor->completion.complete = false; false; editor->completion.active = false; editor->completion.items.clear();
editor->completion.active = false;
editor->completion.items.clear();
editor->completion.visible.clear(); editor->completion.visible.clear();
editor->completion.select = 0; editor->completion.select = 0;
editor->completion.version = editor->lsp_version; editor->completion.version = editor->lsp_version;
@@ -100,7 +97,8 @@ void completion_request(Editor *editor) {
return; return;
editor->completion.active = true; editor->completion.active = true;
session.complete = !list.value("isIncomplete", false); session.complete = !list.value("isIncomplete", false);
if (list.contains("itemDefaults") && list["itemDefaults"].is_object()) { if (list.contains("itemDefaults") &&
list["itemDefaults"].is_object()) {
auto &defs = list["itemDefaults"]; auto &defs = list["itemDefaults"];
if (defs.contains("insertTextFormat") && if (defs.contains("insertTextFormat") &&
defs["insertTextFormat"].is_number()) defs["insertTextFormat"].is_number())
@@ -198,7 +196,7 @@ void completion_request(Editor *editor) {
edit.start = session.hook; edit.start = session.hook;
edit.end = editor->cursor; edit.end = editor->cursor;
} }
utf8_normalize_edit(editor, &edit); editor->utf8_normalize_edit(&edit);
item.edits.push_back(edit); item.edits.push_back(edit);
if (item_json.contains("additionalTextEdits")) { if (item_json.contains("additionalTextEdits")) {
for (auto &te : item_json["additionalTextEdits"]) { for (auto &te : item_json["additionalTextEdits"]) {
@@ -208,7 +206,7 @@ void completion_request(Editor *editor) {
edit.start.col = te["range"]["start"]["character"]; edit.start.col = te["range"]["start"]["character"];
edit.end.row = te["range"]["end"]["line"]; edit.end.row = te["range"]["end"]["line"];
edit.end.col = te["range"]["end"]["character"]; edit.end.col = te["range"]["end"]["character"];
utf8_normalize_edit(editor, &edit); editor->utf8_normalize_edit(&edit);
item.edits.push_back(edit); item.edits.push_back(edit);
} }
} }
@@ -227,7 +225,6 @@ void completion_request(Editor *editor) {
session.box.hidden = false; session.box.hidden = false;
session.box.render_update(); session.box.render_update();
}; };
std::shared_lock lock(editor->knot_mtx);
LineIterator *it = begin_l_iter(editor->root, hook.row); LineIterator *it = begin_l_iter(editor->root, hook.row);
uint32_t length; uint32_t length;
char *line = next_line(it, &length); char *line = next_line(it, &length);
@@ -239,7 +236,6 @@ void completion_request(Editor *editor) {
uint32_t col = utf8_offset_to_utf16(line, length, editor->cursor.col); uint32_t col = utf8_offset_to_utf16(line, length, editor->cursor.col);
free(it->buffer); free(it->buffer);
free(it); free(it);
lock.unlock();
json message = { json message = {
{"jsonrpc", "2.0"}, {"jsonrpc", "2.0"},
{"method", "textDocument/completion"}, {"method", "textDocument/completion"},
@@ -256,103 +252,105 @@ void completion_request(Editor *editor) {
lsp_send(editor->lsp, message, pending); lsp_send(editor->lsp, message, pending);
} }
void handle_completion(Editor *editor, KeyEvent event) { Move this into the box and merge the box and this guy
void CompletionSession::handle(KeyEvent event) {
if (!editor->lsp || !editor->lsp->allow_completion) if (!editor->lsp || !editor->lsp->allow_completion)
return; return;
if (mode != INSERT) { if (mode != INSERT) {
editor->completion.active = false; this->active = false;
return; return;
} }
std::unique_lock lock(editor->completion.mtx); std::unique_lock lock(this->mtx);
if (event.key_type == KEY_PASTE) { if (event.key_type == KEY_PASTE) {
editor->completion.active = false; this->active = false;
return; return;
} else if (event.key_type == KEY_CHAR) { } else if (event.key_type == KEY_CHAR) {
char ch = *event.c; char ch = *event.c;
if (!editor->completion.active) { if (!this->active) {
for (char c : editor->lsp->trigger_chars) for (char c : editor->lsp->trigger_chars)
if (c == ch) { if (c == ch) {
editor->completion.trigger = 2; this->trigger = 2;
editor->completion.trigger_char = c; this->trigger_char = c;
completion_request(editor); completion_request(editor);
return; return;
} }
} else { } else {
if (!editor->completion.items.empty()) { if (!editor->completion.items.empty()) {
const auto &item = editor->completion.items[editor->completion.select]; const auto &item =
const std::vector<char> &end_chars = editor->completion.items[editor->completion.select]; const
std::vector<char> &end_chars =
item.end_chars.empty() ? editor->lsp->end_chars : item.end_chars; item.end_chars.empty() ? editor->lsp->end_chars : item.end_chars;
for (char c : end_chars) for (char c : end_chars)
if (c == ch) { if (c == ch) {
complete_accept(editor); this->accept();
return; return;
} }
} }
} }
if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') ||
(ch >= '0' && ch <= '9') || ch == '_') { (ch >= '0' && ch <= '9') || ch == '_') {
if (editor->completion.active) { if (this->active) {
if (editor->completion.complete) if (this->complete)
completion_filter(editor); completion_filter(editor);
else else
completion_request(editor); completion_request(editor);
} else { } else {
editor->completion.trigger = 3; this->trigger = 3;
completion_request(editor); completion_request(editor);
} }
} else if (ch == CTRL('\\')) { } else if (ch == CTRL('\\')) {
if (editor->completion.active && !editor->completion.visible.empty()) { if (this->active && !this->visible.empty()) {
complete_accept(editor); this->accept();
} else { } else {
editor->completion.trigger = 1; this->trigger = 1;
completion_request(editor); completion_request(editor);
} }
} else if (ch == CTRL('p')) { } else if (ch == CTRL('p')) {
if (editor->completion.active) if (this->active)
complete_next(editor); this->next();
} else if (ch == CTRL('o')) { } else if (ch == CTRL('o')) {
if (editor->completion.active) if (this->active)
complete_prev(editor); this->prev();
} else if (ch == 0x7F || ch == 0x08 || ch == CTRL('W')) { } else if (ch == 0x7F || ch == 0x08 || ch == CTRL('W')) {
if (editor->completion.active) { if (this->active) {
if (editor->completion.complete) { if (this->complete) {
if (editor->cursor <= editor->completion.hook) if (editor->cursor <= this->hook)
editor->completion.active = false; this->active = false;
else else
completion_filter(editor); completion_filter(editor);
} else { } else {
if (editor->cursor <= editor->completion.hook) if (editor->cursor <= this->hook)
editor->completion.active = false; this->active = false;
else else
completion_request(editor); completion_request(editor);
} }
} }
} else { } else {
editor->completion.active = false; this->active = false;
} }
} else if (event.key_type == KEY_MOUSE && event.mouse_modifier == 0) { } else if (event.key_type == KEY_MOUSE && event.mouse_modifier == 0) {
// Prolly remove mouse support here Prolly add mouse support here
// auto &box = editor->completion.box; auto &box = this->box;
// if (event.mouse_y >= box.position.row && if (event.mouse_y >= box.position.row &&
// event.mouse_x >= box.position.col) { event.mouse_x >= box.position.col) {
// uint32_t row = event.mouse_y - box.position.row; uint32_t row = event.mouse_y - box.position.row;
// uint32_t col = event.mouse_x - box.position.col; uint32_t col = event.mouse_x - box.position.col;
// if (row < box.size.row && col < box.size.col) { if (row < box.size.row && col < box.size.col) {
// uint8_t idx = 0; uint8_t idx = 0;
// /* TODO: fix index relative to scroll */ complete_select(editor, idx);
// complete_select(editor, idx); }
// } }
// } if it is being implemented then stop main event handler from
// if it is being implemented then stop main event handler from processing processing
// when click inside the box when click inside the box
editor->completion.active = false; this->active = false;
} else { } else {
editor->completion.active = false; this->active = false;
} }
} }
void completion_resolve_doc(Editor *editor) { void CompletionSession::resolve_doc() {
auto &item = editor->completion.items[editor->completion.select]; auto &item = this->items[this->select];
if (item.documentation) if (item.documentation)
return; return;
item.documentation = ""; item.documentation = "";
@@ -390,13 +388,13 @@ void completion_resolve_doc(Editor *editor) {
lsp_send(editor->lsp, message, pending); lsp_send(editor->lsp, message, pending);
} }
void complete_accept(Editor *editor) { void CompletionSession::accept() {
if (!editor->completion.active || editor->completion.box.hidden) if (!this->active || this->box.hidden)
return; return;
auto &item = editor->completion.items[editor->completion.select]; auto &item = this->items[this->select];
// TODO: support snippets and asis here TODO: support snippets and asis here
// once indentation engine is implemented once indentation engine is implemented
if (editor->completion.version != editor->lsp_version) { if (this->version != editor->lsp_version) {
int delta_col = 0; int delta_col = 0;
TextEdit &e = item.edits[0]; TextEdit &e = item.edits[0];
if (e.end.row == editor->cursor.row) { if (e.end.row == editor->cursor.row) {
@@ -412,8 +410,8 @@ void complete_accept(Editor *editor) {
} }
} }
} }
apply_lsp_edits(editor, item.edits, true); editor->apply_lsp_edits(item.edits, true);
editor->completion.active = false; this->active = false;
} }
inline static int visible_index(const CompletionSession &s) { inline static int visible_index(const CompletionSession &s) {
@@ -423,37 +421,36 @@ inline static int visible_index(const CompletionSession &s) {
return -1; return -1;
} }
void complete_next(Editor *editor) { void CompletionSession::next() {
auto &s = editor->completion; if (!this->active || this->box.hidden || this->visible.empty())
if (!s.active || s.box.hidden || s.visible.empty())
return; return;
int vi = visible_index(s); int vi = visible_index(*this);
if (vi < 0) if (vi < 0)
vi = 0; vi = 0;
else else
vi = (vi + 1) % s.visible.size(); vi = (vi + 1) % this->visible.size();
s.select = s.visible[vi]; this->select = this->visible[vi];
completion_resolve_doc(editor); this->resolve_doc();
completion_adjust_scroll(editor->completion); completion_adjust_scroll(*this);
editor->completion.box.render_update(); this->box.render_update();
} }
void complete_prev(Editor *editor) { void CompletionSession::prev() {
auto &s = editor->completion; if (!this->active || this->box.hidden || this->visible.empty())
if (!s.active || s.box.hidden || s.visible.empty())
return; return;
int vi = visible_index(s); int vi = visible_index(*this);
if (vi < 0) if (vi < 0)
vi = 0; vi = 0;
else else
vi = (vi + s.visible.size() - 1) % s.visible.size(); vi = (vi + this->visible.size() - 1) % this->visible.size();
s.select = s.visible[vi]; this->select = this->visible[vi];
completion_resolve_doc(editor); this->resolve_doc();
completion_adjust_scroll(editor->completion); completion_adjust_scroll(*this);
editor->completion.box.render_update(); this->box.render_update();
} }
void complete_select(Editor *editor, uint8_t index) { void CompletionSession::choose(uint8_t index) {
editor->completion.select = index; this->select = index;
complete_accept(editor); this->accept();
} }
*/
+40 -40
View File
@@ -1,14 +1,14 @@
#include "editor/editor.h" #include "editor/editor.h"
#include "utils/utils.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; Coord result = cursor;
if (!editor || !editor->root || number == 0) if (!this->root || number == 0)
return result; return result;
uint32_t row = result.row; uint32_t row = result.row;
uint32_t col = result.col; uint32_t col = result.col;
uint32_t line_len = 0; uint32_t line_len = 0;
LineIterator *it = begin_l_iter(editor->root, row); LineIterator *it = begin_l_iter(this->root, row);
if (!it) if (!it)
return result; return result;
char *line = next_line(it, &line_len); char *line = next_line(it, &line_len);
@@ -22,7 +22,7 @@ Coord move_right(Editor *editor, Coord cursor, uint32_t number) {
while (number > 0) { while (number > 0) {
if (col >= line_len) { if (col >= line_len) {
uint32_t next_row = row + 1; uint32_t next_row = row + 1;
if (next_row >= editor->root->line_count) { if (next_row >= this->root->line_count) {
col = line_len; col = line_len;
break; break;
} }
@@ -49,14 +49,16 @@ Coord move_right(Editor *editor, Coord cursor, uint32_t number) {
return result; return result;
} }
Coord move_left(Editor *editor, Coord cursor, uint32_t number) { Coord Editor::move_left(Coord cursor, uint32_t number) {
Coord result = cursor; Coord result = cursor;
if (!editor || !editor->root || number == 0) if (!this->root || number == 0)
return result; return result;
uint32_t row = result.row; uint32_t row = result.row;
uint32_t col = result.col; uint32_t col = result.col;
uint32_t len = 0; 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); char *line = next_line(it, &len);
if (!line) { if (!line) {
free(it->buffer); free(it->buffer);
@@ -102,29 +104,28 @@ Coord move_left(Editor *editor, Coord cursor, uint32_t number) {
return result; return result;
} }
void cursor_down(Editor *editor, uint32_t number) { void Editor::cursor_down(uint32_t number) {
if (!editor || !editor->root || number == 0) if (!this->root || number == 0)
return; return;
uint32_t visual_col = editor->cursor_preffered; uint32_t visual_col = this->cursor_preffered;
if (visual_col == UINT32_MAX) { if (visual_col == UINT32_MAX) {
uint32_t len; 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); char *line = next_line(it, &len);
if (!line) { if (!line) {
free(it->buffer); free(it->buffer);
free(it); free(it);
return; return;
} }
editor->cursor_preffered = this->cursor_preffered =
get_visual_col_from_bytes(line, len, editor->cursor.col); get_visual_col_from_bytes(line, len, this->cursor.col);
visual_col = editor->cursor_preffered; visual_col = this->cursor_preffered;
free(it->buffer); free(it->buffer);
free(it); free(it);
} }
editor->cursor.row = this->cursor.row = MIN(this->cursor.row + number, this->root->line_count - 1);
MIN(editor->cursor.row + number, editor->root->line_count - 1);
uint32_t len; 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); char *line = next_line(it, &len);
if (!line) { if (!line) {
free(it->buffer); free(it->buffer);
@@ -133,26 +134,26 @@ void cursor_down(Editor *editor, uint32_t number) {
} }
if (len > 0 && line[len - 1] == '\n') if (len > 0 && line[len - 1] == '\n')
--len; --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->buffer);
free(it); free(it);
} }
void cursor_up(Editor *editor, uint32_t number) { void Editor::cursor_up(uint32_t number) {
if (!editor || !editor->root || number == 0 || editor->cursor.row == 0) if (!this->root || number == 0 || this->cursor.row == 0)
return; return;
uint32_t len; 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); char *line_content = next_line(it, &len);
if (!line_content) if (!line_content)
return; return;
if (editor->cursor_preffered == UINT32_MAX) if (this->cursor_preffered == UINT32_MAX)
editor->cursor_preffered = this->cursor_preffered =
get_visual_col_from_bytes(line_content, len, editor->cursor.col); get_visual_col_from_bytes(line_content, len, this->cursor.col);
uint32_t visual_col = editor->cursor_preffered; uint32_t visual_col = this->cursor_preffered;
free(it->buffer); free(it->buffer);
free(it); free(it);
uint32_t target_row = editor->cursor.row; uint32_t target_row = this->cursor.row;
while (number > 0 && target_row > 0) { while (number > 0 && target_row > 0) {
target_row--; target_row--;
if (target_row == 0) { if (target_row == 0) {
@@ -161,32 +162,31 @@ void cursor_up(Editor *editor, uint32_t number) {
} }
number--; number--;
} }
it = begin_l_iter(editor->root, target_row); it = begin_l_iter(this->root, target_row);
line_content = next_line(it, &len); line_content = next_line(it, &len);
if (line_content) { if (line_content) {
if (len > 0 && line_content[len - 1] == '\n') if (len > 0 && line_content[len - 1] == '\n')
--len; --len;
editor->cursor.row = target_row; this->cursor.row = target_row;
editor->cursor.col = this->cursor.col = get_bytes_from_visual_col(line_content, len, visual_col);
get_bytes_from_visual_col(line_content, len, visual_col);
} else { } else {
editor->cursor.row = 0; this->cursor.row = 0;
editor->cursor.col = 0; this->cursor.col = 0;
} }
free(it->buffer); free(it->buffer);
free(it); free(it);
} }
void cursor_right(Editor *editor, uint32_t number) { void Editor::cursor_right(uint32_t number) {
if (!editor || !editor->root || number == 0) if (!this->root || number == 0)
return; return;
editor->cursor = move_right(editor, editor->cursor, number); this->cursor = this->move_right(this->cursor, number);
editor->cursor_preffered = UINT32_MAX; this->cursor_preffered = UINT32_MAX;
} }
void cursor_left(Editor *editor, uint32_t number) { void Editor::cursor_left(uint32_t number) {
if (!editor || !editor->root || number == 0) if (!this->root || number == 0)
return; return;
editor->cursor = move_left(editor, editor->cursor, number); this->cursor = this->move_left(this->cursor, number);
editor->cursor_preffered = UINT32_MAX; this->cursor_preffered = UINT32_MAX;
} }
+100 -113
View File
@@ -2,20 +2,18 @@
#include "lsp/lsp.h" #include "lsp/lsp.h"
#include "utils/utils.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) if (len == 0)
return; return;
if (len < 0) { if (len < 0) {
std::shared_lock lock_1(editor->knot_mtx);
uint32_t cursor_original = uint32_t cursor_original =
line_to_byte(editor->root, editor->cursor.row, nullptr) + line_to_byte(this->root, this->cursor.row, nullptr) + this->cursor.col;
editor->cursor.col; uint32_t byte_pos = line_to_byte(this->root, pos.row, nullptr) + pos.col;
uint32_t byte_pos = line_to_byte(editor->root, pos.row, nullptr) + pos.col; Coord point = this->move_left(pos, -len);
Coord point = move_left(editor, pos, -len);
json lsp_range; json lsp_range;
bool do_lsp = (editor->lsp != nullptr); bool do_lsp = (this->lsp != nullptr);
if (do_lsp) { if (do_lsp) {
LineIterator *it = begin_l_iter(editor->root, point.row); LineIterator *it = begin_l_iter(this->root, point.row);
uint32_t len; uint32_t len;
char *line = next_line(it, &len); char *line = next_line(it, &len);
int utf16_start = 0; 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); utf16_start = utf8_offset_to_utf16(line, len, point.col);
free(it->buffer); free(it->buffer);
free(it); free(it);
it = begin_l_iter(editor->root, pos.row); it = begin_l_iter(this->root, pos.row);
line = next_line(it, &len); line = next_line(it, &len);
int utf16_end = 0; int utf16_end = 0;
if (line) 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}}}, lsp_range = {{"start", {{"line", point.row}, {"character", utf16_start}}},
{"end", {{"line", pos.row}, {"character", utf16_end}}}}; {"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) { if (cursor_original > start && cursor_original <= byte_pos) {
editor->cursor = point; this->cursor = point;
editor->cursor_preffered = UINT32_MAX; this->cursor_preffered = UINT32_MAX;
} else if (cursor_original > byte_pos) { } else if (cursor_original > byte_pos) {
uint32_t cursor_new = cursor_original - (byte_pos - start); uint32_t cursor_new = cursor_original - (byte_pos - start);
uint32_t new_col; uint32_t new_col;
uint32_t new_row = byte_to_line(editor->root, cursor_new, &new_col); uint32_t new_row = byte_to_line(this->root, cursor_new, &new_col);
editor->cursor = {new_row, new_col}; this->cursor = {new_row, new_col};
editor->cursor_preffered = UINT32_MAX; this->cursor_preffered = UINT32_MAX;
} }
lock_1.unlock();
uint32_t start_row = point.row; uint32_t start_row = point.row;
uint32_t end_row = pos.row; uint32_t end_row = pos.row;
apply_hook_deletion(editor, start_row + 1, end_row); this->root = erase(this->root, start, byte_pos - start);
std::unique_lock lock_2(editor->knot_mtx); if (this->parser)
editor->root = erase(editor->root, start, byte_pos - start); this->parser->edit(start_row, end_row - start_row, 0);
lock_2.unlock(); this->hooks.edit(start_row, end_row - start_row, 0);
if (editor->parser)
editor->parser->edit(start_row, end_row - start_row, 0);
if (do_lsp) { if (do_lsp) {
if (editor->lsp->incremental_sync) { auto lsp = this->lsp.load();
json message = { if (lsp->incremental_sync) {
{"jsonrpc", "2.0"}, auto message = std::make_unique<LSPMessage>();
message->message = {
{"method", "textDocument/didChange"}, {"method", "textDocument/didChange"},
{"params", {"params",
{{"textDocument", {{"textDocument",
{{"uri", editor->uri}, {"version", ++editor->lsp_version}}}, {{"uri", this->uri}, {"version", ++this->lsp_version}}},
{"contentChanges", {"contentChanges",
json::array({{{"range", lsp_range}, {"text", ""}}})}}}}; json::array({{{"range", lsp_range}, {"text", ""}}})}}}};
lsp_send(editor->lsp, message, nullptr); lsp->send(std::move(message));
} else { } 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); std::string text(buf);
free(buf); free(buf);
json message = { auto message = std::make_unique<LSPMessage>();
{"jsonrpc", "2.0"}, message->message = {
{"method", "textDocument/didChange"}, {"method", "textDocument/didChange"},
{"params", {"params",
{{"textDocument", {{"textDocument",
{{"uri", editor->uri}, {"version", ++editor->lsp_version}}}, {{"uri", this->uri}, {"version", ++this->lsp_version}}},
{"contentChanges", json::array({{{"text", text}}})}}}}; {"contentChanges", json::array({{{"text", text}}})}}}};
lsp_send(editor->lsp, message, nullptr); lsp->send(std::move(message));
} }
} }
} else { } else {
std::shared_lock lock_1(editor->knot_mtx);
uint32_t cursor_original = uint32_t cursor_original =
line_to_byte(editor->root, editor->cursor.row, nullptr) + line_to_byte(this->root, this->cursor.row, nullptr) + this->cursor.col;
editor->cursor.col; uint32_t byte_pos = line_to_byte(this->root, pos.row, nullptr) + pos.col;
uint32_t byte_pos = line_to_byte(editor->root, pos.row, nullptr) + pos.col; Coord point = this->move_right(pos, len);
Coord point = move_right(editor, pos, len);
json lsp_range; json lsp_range;
bool do_lsp = (editor->lsp != nullptr); bool do_lsp = (this->lsp != nullptr);
if (do_lsp) { 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; uint32_t line_len;
char *line = next_line(it, &line_len); char *line = next_line(it, &line_len);
int utf16_start = 0; 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); utf16_start = utf8_offset_to_utf16(line, line_len, pos.col);
free(it->buffer); free(it->buffer);
free(it); free(it);
it = begin_l_iter(editor->root, point.row); it = begin_l_iter(this->root, point.row);
line = next_line(it, &line_len); line = next_line(it, &line_len);
int utf16_end = 0; int utf16_end = 0;
if (line) 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}}}, lsp_range = {{"start", {{"line", pos.row}, {"character", utf16_start}}},
{"end", {{"line", point.row}, {"character", utf16_end}}}}; {"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) { if (cursor_original > byte_pos && cursor_original <= end) {
editor->cursor = pos; this->cursor = pos;
editor->cursor_preffered = UINT32_MAX; this->cursor_preffered = UINT32_MAX;
} else if (cursor_original > end) { } else if (cursor_original > end) {
uint32_t cursor_new = cursor_original - (end - byte_pos); uint32_t cursor_new = cursor_original - (end - byte_pos);
uint32_t new_col; uint32_t new_col;
uint32_t new_row = byte_to_line(editor->root, cursor_new, &new_col); uint32_t new_row = byte_to_line(this->root, cursor_new, &new_col);
editor->cursor = {new_row, new_col}; this->cursor = {new_row, new_col};
editor->cursor_preffered = UINT32_MAX; this->cursor_preffered = UINT32_MAX;
} }
lock_1.unlock();
uint32_t start_row = pos.row; uint32_t start_row = pos.row;
uint32_t end_row = point.row; uint32_t end_row = point.row;
apply_hook_deletion(editor, start_row + 1, end_row); this->root = erase(this->root, byte_pos, end - byte_pos);
std::unique_lock lock_2(editor->knot_mtx); if (this->parser)
editor->root = erase(editor->root, byte_pos, end - byte_pos); this->parser->edit(start_row, end_row - start_row, 0);
lock_2.unlock(); this->hooks.edit(start_row, end_row - start_row, 0);
if (editor->parser)
editor->parser->edit(start_row, end_row - start_row, 0);
if (do_lsp) { if (do_lsp) {
if (editor->lsp->incremental_sync) { auto lsp = this->lsp.load();
json message = { if (lsp->incremental_sync) {
{"jsonrpc", "2.0"}, auto message = std::make_unique<LSPMessage>();
message->message = {
{"method", "textDocument/didChange"}, {"method", "textDocument/didChange"},
{"params", {"params",
{{"textDocument", {{"textDocument",
{{"uri", editor->uri}, {"version", ++editor->lsp_version}}}, {{"uri", this->uri}, {"version", ++this->lsp_version}}},
{"contentChanges", {"contentChanges",
json::array({{{"range", lsp_range}, {"text", ""}}})}}}}; json::array({{{"range", lsp_range}, {"text", ""}}})}}}};
lsp_send(editor->lsp, message, nullptr); lsp->send(std::move(message));
} else { } 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); std::string text(buf);
free(buf); free(buf);
json message = { auto message = std::make_unique<LSPMessage>();
{"jsonrpc", "2.0"}, message->message = {
{"method", "textDocument/didChange"}, {"method", "textDocument/didChange"},
{"params", {"params",
{{"textDocument", {{"textDocument",
{{"uri", editor->uri}, {"version", ++editor->lsp_version}}}, {{"uri", this->uri}, {"version", ++this->lsp_version}}},
{"contentChanges", json::array({{{"text", text}}})}}}}; {"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) { void Editor::edit_insert(Coord pos, char *data, uint32_t len) {
std::shared_lock lock_1(editor->knot_mtx);
uint32_t cursor_original = uint32_t cursor_original =
line_to_byte(editor->root, editor->cursor.row, nullptr) + line_to_byte(this->root, this->cursor.row, nullptr) + this->cursor.col;
editor->cursor.col; uint32_t byte_pos = line_to_byte(this->root, pos.row, nullptr) + pos.col;
uint32_t byte_pos = line_to_byte(editor->root, pos.row, nullptr) + pos.col;
if (cursor_original > byte_pos) { if (cursor_original > byte_pos) {
uint32_t cursor_new = cursor_original + len; uint32_t cursor_new = cursor_original + len;
uint32_t new_col; uint32_t new_col;
uint32_t new_row = byte_to_line(editor->root, cursor_new, &new_col); uint32_t new_row = byte_to_line(this->root, cursor_new, &new_col);
editor->cursor = {new_row, 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; uint32_t line_len;
char *line = next_line(it, &line_len); char *line = next_line(it, &line_len);
int utf16_col = 0; 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); utf16_col = utf8_offset_to_utf16(line, line_len, pos.col);
free(it->buffer); free(it->buffer);
free(it); free(it);
lock_1.unlock(); this->root = insert(this->root, byte_pos, data, len);
std::unique_lock lock_2(editor->knot_mtx);
editor->root = insert(editor->root, byte_pos, data, len);
uint32_t rows = 0; uint32_t rows = 0;
for (uint32_t i = 0; i < len; i++) for (uint32_t i = 0; i < len; i++)
if (data[i] == '\n') if (data[i] == '\n')
rows++; rows++;
apply_hook_insertion(editor, pos.row, rows); if (this->parser)
lock_2.unlock(); this->parser->edit(pos.row, 0, rows);
if (editor->parser) this->hooks.edit(pos.row, 0, rows);
editor->parser->edit(pos.row, 0, rows); auto lsp = this->lsp.load();
if (editor->lsp) { if (lsp) {
if (editor->lsp->incremental_sync) { if (lsp->incremental_sync) {
json message = { auto message = std::make_unique<LSPMessage>();
{"jsonrpc", "2.0"}, message->message = {
{"method", "textDocument/didChange"}, {"method", "textDocument/didChange"},
{"params", {"params",
{{"textDocument", {{"textDocument",
{{"uri", editor->uri}, {"version", ++editor->lsp_version}}}, {{"uri", this->uri}, {"version", ++this->lsp_version}}},
{"contentChanges", {"contentChanges",
json::array( json::array(
{{{"range", {{{"range",
{{"start", {{"line", pos.row}, {"character", utf16_col}}}, {{"start", {{"line", pos.row}, {"character", utf16_col}}},
{"end", {{"line", pos.row}, {"character", utf16_col}}}}}, {"end", {{"line", pos.row}, {"character", utf16_col}}}}},
{"text", std::string(data, len)}}})}}}}; {"text", std::string(data, len)}}})}}}};
lsp_send(editor->lsp, message, nullptr); lsp->send(std::move(message));
} else { } 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); std::string text(buf);
free(buf); free(buf);
json message = { auto message = std::make_unique<LSPMessage>();
{"jsonrpc", "2.0"}, message->message = {
{"method", "textDocument/didChange"}, {"method", "textDocument/didChange"},
{"params", {"params",
{{"textDocument", {{"textDocument",
{{"uri", editor->uri}, {"version", ++editor->lsp_version}}}, {{"uri", this->uri}, {"version", ++this->lsp_version}}},
{"contentChanges", json::array({{{"text", text}}})}}}}; {"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) { uint32_t len) {
std::unique_lock lock(editor->knot_mtx);
uint32_t start_byte = uint32_t start_byte =
line_to_byte(editor->root, start.row, nullptr) + start.col; line_to_byte(this->root, start.row, nullptr) + start.col;
uint32_t end_byte = line_to_byte(editor->root, end.row, nullptr) + end.col; uint32_t end_byte = line_to_byte(this->root, end.row, nullptr) + end.col;
LineIterator *it = begin_l_iter(editor->root, start.row); LineIterator *it = begin_l_iter(this->root, start.row);
uint32_t line_len; uint32_t line_len;
char *line = next_line(it, &line_len); char *line = next_line(it, &line_len);
int utf16_start = 0; 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); utf16_start = utf8_offset_to_utf16(line, line_len, start.col);
free(it->buffer); free(it->buffer);
free(it); free(it);
it = begin_l_iter(editor->root, end.row); it = begin_l_iter(this->root, end.row);
line = next_line(it, &line_len); line = next_line(it, &line_len);
int utf16_end = 0; int utf16_end = 0;
if (line) if (line)
@@ -238,25 +225,25 @@ void edit_replace(Editor *editor, Coord start, Coord end, const char *text,
free(it->buffer); free(it->buffer);
free(it); free(it);
if (start_byte != end_byte) 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) 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; uint32_t rows = 0;
for (uint32_t i = 0; i < len; i++) for (uint32_t i = 0; i < len; i++)
if (text[i] == '\n') if (text[i] == '\n')
rows++; rows++;
if (rows > 0) if (this->parser)
rows--; this->parser->edit(start.row, end.row - start.row, rows);
if (editor->parser) this->hooks.edit(start.row, end.row - start.row, rows);
editor->parser->edit(start.row, end.row - start.row, rows); auto lsp = this->lsp.load();
if (editor->lsp) { if (lsp) {
if (editor->lsp->incremental_sync) { if (lsp->incremental_sync) {
json message = { auto message = std::make_unique<LSPMessage>();
{"jsonrpc", "2.0"}, message->message = {
{"method", "textDocument/didChange"}, {"method", "textDocument/didChange"},
{"params", {"params",
{{"textDocument", {{"textDocument",
{{"uri", editor->uri}, {"version", ++editor->lsp_version}}}, {{"uri", this->uri}, {"version", ++this->lsp_version}}},
{"contentChanges", {"contentChanges",
json::array( json::array(
{{{"range", {{{"range",
@@ -264,19 +251,19 @@ void edit_replace(Editor *editor, Coord start, Coord end, const char *text,
{{"line", start.row}, {"character", utf16_start}}}, {{"line", start.row}, {"character", utf16_start}}},
{"end", {{"line", end.row}, {"character", utf16_end}}}}}, {"end", {{"line", end.row}, {"character", utf16_end}}}}},
{"text", std::string(text, len)}}})}}}}; {"text", std::string(text, len)}}})}}}};
lsp_send(editor->lsp, message, nullptr); lsp->send(std::move(message));
} else { } 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); std::string full_text(buf);
free(buf); free(buf);
json message = { auto message = std::make_unique<LSPMessage>();
{"jsonrpc", "2.0"}, message->message = {
{"method", "textDocument/didChange"}, {"method", "textDocument/didChange"},
{"params", {"params",
{{"textDocument", {{"textDocument",
{{"uri", editor->uri}, {"version", ++editor->lsp_version}}}, {{"uri", this->uri}, {"version", ++this->lsp_version}}},
{"contentChanges", json::array({{{"text", full_text}}})}}}}; {"contentChanges", json::array({{{"text", full_text}}})}}}};
lsp_send(editor->lsp, message, nullptr); lsp->send(std::move(message));
} }
} }
} }
+39 -105
View File
@@ -1,73 +1,68 @@
#include "editor/editor.h" #include "editor/editor.h"
#include "editor/decl.h"
#include "lsp/lsp.h" #include "lsp/lsp.h"
#include "main.h" #include "main.h"
#include "syntax/langs.h" #include "syntax/langs.h"
#include "utils/utils.h" #include "utils/utils.h"
Editor *new_editor(const char *filename_arg, Coord position, Coord size, Editor::Editor(const char *filename_arg, uint8_t eol) {
uint8_t eol) {
Editor *editor = new Editor();
if (!editor)
return nullptr;
uint32_t len = 0; uint32_t len = 0;
std::string filename = path_abs(filename_arg); std::string filename = path_abs(filename_arg);
editor->unix_eol = eol & 1; this->unix_eol = eol & 1;
char *str = load_file(filename.c_str(), &len, &editor->unix_eol); char *str = load_file(filename.c_str(), &len, &this->unix_eol);
if (!str) { if (!str) {
str = (char *)malloc(1); str = (char *)malloc(1);
*str = '\n'; *str = '\n';
len = 1; len = 1;
} }
if ((eol >> 1) & 1) if ((eol >> 1) & 1)
editor->unix_eol = eol & 1; this->unix_eol = eol & 1;
editor->filename = filename; this->filename = filename;
editor->uri = path_to_file_uri(filename); this->uri = path_to_file_uri(filename);
editor->position = position; this->cursor_preffered = UINT32_MAX;
editor->size = size;
editor->cursor_preffered = UINT32_MAX;
if (len == 0) { if (len == 0) {
free(str); free(str);
str = (char *)malloc(1); str = (char *)malloc(1);
*str = '\n'; *str = '\n';
len = 1; 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); free(str);
editor->lang = language_for_file(filename.c_str()); this->lang = language_for_file(filename.c_str());
if (parsers.find(editor->lang.name) != parsers.end()) if (parsers.find(this->lang.name) != parsers.end())
editor->parser = new Parser(editor, editor->lang.name, size.row + 5); this->parser = new Parser(this, this->lang.name, size.row + 5);
if (editor->lang.name == "css" || editor->lang.name == "html" || if (this->lang.name == "css" || this->lang.name == "html" ||
editor->lang.name == "javascript" || editor->lang.name == "markdown" || this->lang.name == "javascript" || this->lang.name == "markdown" ||
editor->lang.name == "typescript") this->lang.name == "typescript")
editor->is_css_color = true; this->is_css_color = true;
if (len <= (1024 * 28)) if (len <= (1024 * 28)) {
request_add_to_lsp(editor->lang, editor); std::lock_guard lock(lsp::lsp_mutex);
editor->indents.compute_indent(editor); lsp::new_editors.push_back(this);
return editor; }
this->indents.compute_indent(this);
} }
void free_editor(Editor *editor) { Editor::~Editor() {
remove_from_lsp(editor); auto lsp = this->lsp.load();
if (editor->parser) if (lsp)
delete editor->parser; lsp->remove(this);
editor->parser = nullptr; if (this->parser)
free_rope(editor->root); delete this->parser;
delete editor; this->parser = nullptr;
free_rope(this->root);
} }
void save_file(Editor *editor) { void Editor::save() {
if (!editor || !editor->root) if (!this->root)
return; return;
std::shared_lock lock(editor->knot_mtx); uint32_t char_count = this->root->char_count;
int version = editor->lsp_version; char *str = read(this->root, 0, char_count);
uint32_t char_count = editor->root->char_count;
char *str = read(editor->root, 0, char_count);
if (!str) if (!str)
return; return;
lock.unlock(); std::ofstream out(this->filename);
std::ofstream out(editor->filename); if (!this->unix_eol) {
if (!editor->unix_eol) {
for (uint32_t i = 0; i < char_count; ++i) { for (uint32_t i = 0; i < char_count; ++i) {
if (str[i] == '\n') if (str[i] == '\n')
out.put('\r'); out.put('\r');
@@ -78,67 +73,6 @@ void save_file(Editor *editor) {
} }
out.close(); out.close();
free(str); free(str);
bar.log("Written " + std::to_string(char_count) + " bytes to " + ui::bar.log("Written " + std::to_string(char_count) + " bytes to " +
editor->filename); this->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"},
{"params",
{{"textDocument", {{"uri", editor->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)
return;
auto &edits = message["result"];
if (edits.is_array()) {
std::vector<TextEdit> t_edits;
t_edits.reserve(edits.size());
for (auto &edit : edits) {
TextEdit t_edit;
t_edit.text = edit.value("newText", "");
t_edit.start.row = edit["range"]["start"]["line"];
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);
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);
if (!str)
return;
lock.unlock();
std::ofstream out(editor->filename);
if (!editor->unix_eol) {
for (uint32_t i = 0; i < char_count; ++i) {
if (str[i] == '\n')
out.put('\r');
out.put(str[i]);
}
} else {
out.write(str, char_count);
}
out.close();
free(str);
lsp_send(editor->lsp, save_msg, nullptr);
}
};
lsp_send(editor->lsp, msg, pending);
}
}
} }
+69 -77
View File
@@ -1,60 +1,59 @@
#include "editor/editor.h" #include "editor/editor.h"
#include "editor/helpers.h" #include "extentions/hover.h"
#include "io/sysio.h" #include "io/sysio.h"
#include "main.h" #include "main.h"
#include "utils/utils.h" #include "utils/utils.h"
void handle_editor_event(Editor *editor, KeyEvent event) { void Editor::handle_event(KeyEvent event) {
uint8_t old_mode = mode; uint8_t old_mode = mode;
if (editor->hover_active) if (!this->hover_popup->hidden)
editor->hover_active = false; this->hover_popup->hidden = true;
handle_mouse(editor, event);
if (event.key_type == KEY_SPECIAL) { if (event.key_type == KEY_SPECIAL) {
switch (event.special_modifier) { switch (event.special_modifier) {
case 0: case 0:
switch (event.special_key) { switch (event.special_key) {
case KEY_DOWN: case KEY_DOWN:
cursor_down(editor, 1); this->cursor_down(1);
break; break;
case KEY_UP: case KEY_UP:
cursor_up(editor, 1); this->cursor_up(1);
break; break;
case KEY_LEFT: case KEY_LEFT:
cursor_left(editor, 1); this->cursor_left(1);
break; break;
case KEY_RIGHT: case KEY_RIGHT:
cursor_right(editor, 1); this->cursor_right(1);
break; break;
} }
break; break;
case CNTRL: case CNTRL:
switch (event.special_key) { switch (event.special_key) {
case KEY_DOWN: case KEY_DOWN:
cursor_down(editor, 5); this->cursor_down(5);
break; break;
case KEY_UP: case KEY_UP:
cursor_up(editor, 5); this->cursor_up(5);
break; break;
case KEY_LEFT: case KEY_LEFT:
cursor_prev_word(editor); this->cursor_prev_word();
case KEY_RIGHT: case KEY_RIGHT:
cursor_next_word(editor); this->cursor_next_word();
break; break;
} }
break; break;
case ALT: case ALT:
switch (event.special_key) { switch (event.special_key) {
case KEY_DOWN: case KEY_DOWN:
move_line_down(editor); this->move_line_down();
break; break;
case KEY_UP: case KEY_UP:
move_line_up(editor); this->move_line_up();
break; break;
case KEY_LEFT: case KEY_LEFT:
cursor_left(editor, 8); this->cursor_left(8);
break; break;
case KEY_RIGHT: case KEY_RIGHT:
cursor_right(editor, 8); this->cursor_right(8);
break; break;
} }
break; break;
@@ -65,86 +64,86 @@ void handle_editor_event(Editor *editor, KeyEvent event) {
if (event.key_type == KEY_CHAR && event.len == 1) { if (event.key_type == KEY_CHAR && event.len == 1) {
switch (event.c[0]) { switch (event.c[0]) {
case 'u': case 'u':
select_all(editor); this->select_all();
break; break;
case CTRL('h'): case CTRL('h'):
editor->hover.scroll(-1); this->hover_popup->scroll(-1);
editor->hover_active = true; this->hover_popup->hidden = false;
break; break;
case CTRL('l'): case CTRL('l'):
editor->hover.scroll(1); this->hover_popup->scroll(1);
editor->hover_active = true; this->hover_popup->hidden = false;
break; break;
case 'h': case 'h':
fetch_lsp_hover(editor); this->fetch_lsp_hover();
break; break;
case 'a': { case 'a': {
mode = INSERT; mode = INSERT;
Coord start = editor->cursor; Coord start = this->cursor;
cursor_right(editor, 1); this->cursor_right(1);
if (start.row != editor->cursor.row) if (start.row != this->cursor.row)
cursor_left(editor, 1); this->cursor_left(1);
} break; } break;
case 'i': case 'i':
mode = INSERT; mode = INSERT;
break; break;
case 'n': case 'n':
mode = JUMPER; mode = JUMPER;
editor->jumper_set = true; this->jumper_set = true;
break; break;
case 'm': case 'm':
mode = JUMPER; mode = JUMPER;
editor->jumper_set = false; this->jumper_set = false;
break; break;
case 'N': case 'N':
clear_hooks_at_line(editor, editor->cursor.row); this->hooks.clear_line(this->cursor.row);
break; break;
case 's': case 's':
case 'v': case 'v':
mode = SELECT; mode = SELECT;
editor->selection_active = true; this->selection_active = true;
editor->selection = editor->cursor; this->selection = this->cursor;
editor->selection_type = CHAR; this->selection_type = CHAR;
break; break;
case ';': case ';':
case ':': case ':':
mode = RUNNER; mode = RUNNER;
break; break;
case 0x7F: case 0x7F:
cursor_left(editor, 1); this->cursor_left(1);
break; break;
case ' ': case ' ':
cursor_right(editor, 1); this->cursor_right(1);
break; break;
case '\r': case '\r':
case '\n': case '\n':
cursor_down(editor, 1); this->cursor_down(1);
break; break;
case '\\': case '\\':
case '|': case '|':
cursor_up(editor, 1); this->cursor_up(1);
break; break;
case CTRL('d'): case CTRL('d'):
scroll_down(editor, 1); this->scroll_down(1);
ensure_cursor(editor); this->ensure_cursor();
break; break;
case CTRL('u'): case CTRL('u'):
scroll_up(editor, 1); this->scroll_up(1);
ensure_cursor(editor); this->ensure_cursor();
break; break;
case '>': case '>':
case '.': case '.':
indent_current_line(editor); this->indent_current_line();
break; break;
case '<': case '<':
case ',': case ',':
dedent_current_line(editor); this->dedent_current_line();
break; break;
case CTRL('s'): case CTRL('s'):
save_file(editor); this->save();
break; break;
case 'p': case 'p':
paste(editor); this->paste();
break; break;
} }
} }
@@ -153,34 +152,34 @@ void handle_editor_event(Editor *editor, KeyEvent event) {
if (event.key_type == KEY_CHAR) { if (event.key_type == KEY_CHAR) {
if (event.len == 1) { if (event.len == 1) {
if (event.c[0] == '\t') { 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') { } 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')) { } else if (event.c[0] == CTRL('W')) {
delete_prev_word(editor); this->delete_prev_word();
} else if (isprint((unsigned char)(event.c[0]))) { } 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) { } else if (event.c[0] == 0x7F || event.c[0] == 0x08) {
backspace_edit(editor); this->backspace_edit();
} else if (event.c[0] == 0x1B) { } else if (event.c[0] == 0x1B) {
normal_mode(editor); this->normal_mode();
} }
} else if (event.len > 1) { } else if (event.len > 1) {
edit_insert(editor, editor->cursor, event.c, event.len); this->edit_insert(this->cursor, event.c, event.len);
cursor_right(editor, 1); this->cursor_right(1);
} }
} else if (event.key_type == KEY_SPECIAL && } else if (event.key_type == KEY_SPECIAL &&
event.special_key == KEY_DELETE) { event.special_key == KEY_DELETE) {
switch (event.special_modifier) { switch (event.special_modifier) {
case 0: case 0:
edit_erase(editor, editor->cursor, 1); this->edit_erase(this->cursor, 1);
break; break;
case CNTRL: case CNTRL:
delete_next_word(editor); this->delete_next_word();
break; break;
} }
} else if (event.key_type == KEY_PASTE) { } else if (event.key_type == KEY_PASTE) {
insert_str(editor, event.c, event.len); this->insert_str(event.c, event.len);
} }
break; break;
case SELECT: case SELECT:
@@ -189,28 +188,28 @@ void handle_editor_event(Editor *editor, KeyEvent event) {
case 0x1B: case 0x1B:
case 's': case 's':
case 'v': case 'v':
editor->selection_active = false; this->selection_active = false;
mode = NORMAL; mode = NORMAL;
break; break;
case 'y': case 'y':
copy(editor); this->copy();
mode = NORMAL; mode = NORMAL;
break; break;
case 'x': case 'x':
cut(editor); this->cut();
mode = NORMAL; mode = NORMAL;
break; break;
case 'p': case 'p':
paste(editor); this->paste();
mode = NORMAL; mode = NORMAL;
break; break;
case '<': case '<':
case ',': case ',':
dedent_selection(editor); this->dedent_selection();
break; break;
case '>': case '>':
case '.': case '.':
indent_selection(editor); this->indent_selection();
break; break;
} }
} }
@@ -218,25 +217,18 @@ void handle_editor_event(Editor *editor, KeyEvent event) {
case JUMPER: case JUMPER:
if (event.key_type == KEY_CHAR && event.len == 1 && if (event.key_type == KEY_CHAR && event.len == 1 &&
(event.c[0] >= '!' && event.c[0] <= '~')) { (event.c[0] >= '!' && event.c[0] <= '~')) {
if (editor->jumper_set) { if (this->jumper_set) {
for (uint8_t i = 0; i < 94; i++) this->hooks.set(event.c[0], this->cursor.row);
if (editor->hooks[i] == editor->cursor.row + 1) {
editor->hooks[i] = 0;
break;
}
editor->hooks[event.c[0] - '!'] = editor->cursor.row + 1;
} else { } else {
uint32_t line = editor->hooks[event.c[0] - '!'] - 1; uint32_t line;
if (line > 0) { if (this->hooks.get(event.c[0], &line))
editor->cursor = {line, 0}; this->cursor = {line, 0};
editor->cursor_preffered = UINT32_MAX;
}
} }
} }
mode = NORMAL; mode = NORMAL;
break; break;
} }
if (old_mode == mode || mode != INSERT) // if (old_mode == mode || mode != INSERT)
handle_completion(editor, event); // this->completion.handle(event);
ensure_scroll(editor); this->ensure_scroll();
} }
View File
+261 -208
View File
@@ -1,86 +1,83 @@
#include "editor/helpers.h" #include "editor/helpers.h"
#include "editor/editor.h" #include "editor/editor.h"
#include "extentions/hover.h"
#include "io/sysio.h" #include "io/sysio.h"
#include "lsp/lsp.h" #include "lsp/lsp.h"
#include "main.h" #include "main.h"
#include "utils/utils.h" #include "utils/utils.h"
#include <sys/types.h>
void cut(Editor *editor) { void Editor::cut() {
if (ABS((int64_t)editor->cursor.row - (int64_t)editor->selection.row) > if (ABS((int64_t)this->cursor.row - (int64_t)this->selection.row) > 1500) {
1500) { ui::bar.log("Selection too large!");
bar.log("Selection too large!");
return; return;
} }
if (mode != SELECT) if (mode != SELECT)
return; return;
Coord start; Coord start;
uint32_t len; uint32_t len;
char *text = get_selection(editor, &len, &start); char *text = this->get_selection(&len, &start);
ruby_copy(text, len); ruby_copy(text, len);
len = count_clusters(text, len, 0, len); len = count_clusters(text, len, 0, len);
edit_erase(editor, start, len); this->edit_erase(start, len);
free(text); free(text);
editor->selection_active = false; this->selection_active = false;
} }
void copy(Editor *editor) { void Editor::copy() {
if (ABS((int64_t)editor->cursor.row - (int64_t)editor->selection.row) > if (ABS((int64_t)this->cursor.row - (int64_t)this->selection.row) > 1500) {
1500) { ui::bar.log("Selection too large!");
bar.log("Selection too large!");
return; return;
} }
if (mode != SELECT) if (mode != SELECT)
return; return;
uint32_t len; uint32_t len;
char *text = get_selection(editor, &len, nullptr); char *text = this->get_selection(&len, nullptr);
ruby_copy(text, len); ruby_copy(text, len);
free(text); free(text);
editor->selection_active = false; this->selection_active = false;
} }
void paste(Editor *editor) { void Editor::paste() {
if (mode == NORMAL) { if (mode == NORMAL) {
std::string text = ruby_paste(); std::string text = ruby_paste();
if (text.empty()) if (text.empty())
return; return;
insert_str(editor, (char *)text.c_str(), text.length()); this->insert_str((char *)text.c_str(), text.length());
} else if (mode == SELECT) { } else if (mode == SELECT) {
std::string text = ruby_paste(); std::string text = ruby_paste();
if (!text.empty()) { if (!text.empty()) {
Coord start, end; Coord start, end;
selection_bounds(editor, &start, &end); this->selection_bounds(&start, &end);
uint32_t start_byte = uint32_t start_byte =
line_to_byte(editor->root, start.row, nullptr) + start.col; line_to_byte(this->root, start.row, nullptr) + start.col;
uint32_t end_byte = uint32_t end_byte = line_to_byte(this->root, end.row, nullptr) + end.col;
line_to_byte(editor->root, end.row, nullptr) + end.col; this->edit_erase(start, end_byte - start_byte);
edit_erase(editor, start, end_byte - start_byte); this->edit_insert(this->cursor, (char *)text.c_str(), text.length());
edit_insert(editor, editor->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) { 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); 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) { void Editor::indent_current_line() {
Coord start = editor->cursor; Coord start = this->cursor;
uint32_t delta = editor->indents.indent_line(editor->cursor.row); uint32_t delta = this->indents.indent_line(this->cursor.row);
editor->cursor.col = start.col + delta; this->cursor.col = start.col + delta;
editor->cursor.row = start.row; this->cursor.row = start.row;
} }
void dedent_current_line(Editor *editor) { void Editor::dedent_current_line() {
Coord start = editor->cursor; Coord start = this->cursor;
uint32_t delta = editor->indents.dedent_line(editor->cursor.row); uint32_t delta = this->indents.dedent_line(this->cursor.row);
editor->cursor.col = MAX((int64_t)start.col - delta, 0); this->cursor.col = MAX((int64_t)start.col - delta, 0);
editor->cursor.row = start.row; this->cursor.row = start.row;
} }
static void move_coord_by_delta(Coord &c, uint32_t row, int64_t delta) { 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) { void Editor::indent_selection() {
uint32_t top = MIN(editor->cursor.row, editor->selection.row); uint32_t top = MIN(this->cursor.row, this->selection.row);
uint32_t bot = MAX(editor->cursor.row, editor->selection.row); uint32_t bot = MAX(this->cursor.row, this->selection.row);
if (bot - top > 1500) { 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; return;
} }
if (bot - top >= 2) if (bot - top >= 2)
editor->indents.indent_block(top + 1, bot - 1); this->indents.indent_block(top + 1, bot - 1);
uint32_t delta_top = editor->indents.indent_line(top); uint32_t delta_top = this->indents.indent_line(top);
uint32_t delta_bot = uint32_t delta_bot =
(bot == top) ? delta_top : editor->indents.indent_line(bot); (bot == top) ? delta_top : this->indents.indent_line(bot);
move_coord_by_delta(editor->cursor, top, delta_top); move_coord_by_delta(this->cursor, top, delta_top);
move_coord_by_delta(editor->selection, top, delta_top); move_coord_by_delta(this->selection, top, delta_top);
if (bot != top) { if (bot != top) {
move_coord_by_delta(editor->cursor, bot, delta_bot); move_coord_by_delta(this->cursor, bot, delta_bot);
move_coord_by_delta(editor->selection, bot, delta_bot); move_coord_by_delta(this->selection, bot, delta_bot);
} }
} }
void dedent_selection(Editor *editor) { void Editor::dedent_selection() {
uint32_t top = MIN(editor->cursor.row, editor->selection.row); uint32_t top = MIN(this->cursor.row, this->selection.row);
uint32_t bot = MAX(editor->cursor.row, editor->selection.row); uint32_t bot = MAX(this->cursor.row, this->selection.row);
if (bot - top > 1500) { 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; return;
} }
if (bot - top >= 2) if (bot - top >= 2)
editor->indents.dedent_block(top + 1, bot - 1); this->indents.dedent_block(top + 1, bot - 1);
uint32_t delta_top = editor->indents.dedent_line(top); uint32_t delta_top = this->indents.dedent_line(top);
uint32_t delta_bot = uint32_t delta_bot =
(bot == top) ? delta_top : editor->indents.dedent_line(bot); (bot == top) ? delta_top : this->indents.dedent_line(bot);
move_coord_by_delta(editor->cursor, top, -(int64_t)delta_top); move_coord_by_delta(this->cursor, top, -(int64_t)delta_top);
move_coord_by_delta(editor->selection, top, -(int64_t)delta_top); move_coord_by_delta(this->selection, top, -(int64_t)delta_top);
if (bot != top) { if (bot != top) {
move_coord_by_delta(editor->cursor, bot, -(int64_t)delta_bot); move_coord_by_delta(this->cursor, bot, -(int64_t)delta_bot);
move_coord_by_delta(editor->selection, bot, -(int64_t)delta_bot); move_coord_by_delta(this->selection, bot, -(int64_t)delta_bot);
} }
} }
void insert_char(Editor *editor, char c) { void Editor::insert_char(char c) {
uint32_t col = editor->cursor.col; uint32_t col = this->cursor.col;
LineIterator *it = begin_l_iter(editor->root, editor->cursor.row); LineIterator *it = begin_l_iter(this->root, this->cursor.row);
if (!it) if (!it)
return; return;
uint32_t len; uint32_t len;
@@ -148,7 +145,7 @@ void insert_char(Editor *editor, char c) {
if ((c == '}' && next == '}') || (c == ')' && next == ')') || if ((c == '}' && next == '}') || (c == ')' && next == ')') ||
(c == ']' && next == ']') || (c == '"' && next == '"') || (c == ']' && next == ']') || (c == '"' && next == '"') ||
(c == '\'' && next == '\'')) { (c == '\'' && next == '\'')) {
cursor_right(editor, 1); this->cursor_right(1);
skip_insert = true; skip_insert = true;
} }
} }
@@ -175,16 +172,17 @@ void insert_char(Editor *editor, char c) {
} }
if (closing) { if (closing) {
char pair[2] = {c, closing}; char pair[2] = {c, closing};
edit_insert(editor, editor->cursor, pair, 2); this->edit_insert(this->cursor, pair, 2);
cursor_right(editor, 1); this->cursor_right(1);
} else { } else {
edit_insert(editor, editor->cursor, &c, 1); this->edit_insert(this->cursor, &c, 1);
cursor_right(editor, 1); this->cursor_right(1);
} }
if (editor->lsp && editor->lsp->allow_formatting_on_type) { auto lsp = this->lsp.load();
for (char ch : editor->lsp->format_chars) { if (lsp && lsp->allow_formatting_on_type) {
for (char ch : lsp->format_chars) {
if (ch == c) { 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) if (!it)
return; return;
uint32_t len; uint32_t len;
@@ -194,29 +192,27 @@ void insert_char(Editor *editor, char c) {
free(it); free(it);
return; 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->buffer);
free(it); free(it);
int version = editor->lsp_version; int version = this->lsp_version;
json message = { auto message = std::make_unique<LSPMessage>();
{"jsonrpc", "2.0"}, message->message = {
{"method", "textDocument/onTypeFormatting"}, {"method", "textDocument/onTypeFormatting"},
{"params", {"params",
{{"textDocument", {{"uri", editor->uri}}}, {{"textDocument", {{"uri", this->uri}}},
{"position", {"position", {{"line", this->cursor.row}, {"character", col}}},
{{"line", editor->cursor.row}, {"character", col}}},
{"ch", std::string(1, c)}, {"ch", std::string(1, c)},
{"options", {"options",
{{"tabSize", 2}, {{"tabSize", 2},
{"insertSpaces", true}, {"insertSpaces", true},
{"trimTrailingWhitespace", true}, {"trimTrailingWhitespace", true},
{"trimFinalNewlines", true}}}}}}; {"trimFinalNewlines", true}}}}}};
LSPPending *pending = new LSPPending(); message->editor = this;
pending->editor = editor; message->callback = [version](const LSPMessage &message) {
pending->callback = [version](Editor *editor, const json &message) { if (version != message.editor->lsp_version)
if (version != editor->lsp_version)
return; return;
auto &edits = message["result"]; auto &edits = message.message["result"];
if (edits.is_array()) { if (edits.is_array()) {
std::vector<TextEdit> t_edits; std::vector<TextEdit> t_edits;
t_edits.reserve(edits.size()); 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.start.col = edit["range"]["start"]["character"];
t_edit.end.row = edit["range"]["end"]["line"]; t_edit.end.row = edit["range"]["end"]["line"];
t_edit.end.col = edit["range"]["end"]["character"]; 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); t_edits.push_back(t_edit);
} }
apply_lsp_edits(editor, t_edits, false); message.editor->apply_lsp_edits(t_edits, false);
ensure_scroll(editor); message.editor->ensure_scroll();
} }
}; };
lsp_send(editor->lsp, message, pending); lsp->send(std::move(message));
break; break;
} }
} }
@@ -242,19 +238,86 @@ void insert_char(Editor *editor, char c) {
} }
} }
void normal_mode(Editor *editor) { void Editor::format_full(int version) {
Coord prev_pos = editor->cursor; auto lsp = this->lsp.load();
mode = NORMAL; if (lsp) {
cursor_left(editor, 1); log("Saving %s", this->filename.c_str());
if (prev_pos.row != editor->cursor.row) auto message = std::make_unique<LSPMessage>();
cursor_right(editor, 1); message->message = {{"method", "textDocument/didSave"},
{"params", {{"textDocument", {{"uri", this->uri}}}}}};
lsp->send(std::move(message));
if (lsp->allow_formatting) {
json s_msg = {{"method", "textDocument/formatting"},
{"params",
{{"textDocument", {{"uri", this->uri}}},
{"options",
{{"tabSize", 2},
{"insertSpaces", true},
{"trimTrailingWhitespace", true},
{"trimFinalNewlines", true}}}}}};
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) {
if (version != msg.editor->lsp_version)
return;
auto &edits = msg.message["result"];
if (edits.is_array()) {
std::vector<TextEdit> t_edits;
t_edits.reserve(edits.size());
for (auto &edit : edits) {
TextEdit t_edit;
t_edit.text = edit.value("newText", "");
t_edit.start.row = edit["range"]["start"]["line"];
t_edit.start.col = edit["range"]["start"]["character"];
t_edit.end.row = edit["range"]["end"]["line"];
t_edit.end.col = edit["range"]["end"]["character"];
msg.editor->utf8_normalize_edit(&t_edit);
t_edits.push_back(t_edit);
}
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;
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');
out.put(str[i]);
}
} else {
out.write(str, char_count);
}
out.close();
free(str);
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(std::move(save_msg));
}
}
} }
void backspace_edit(Editor *editor) { void Editor::normal_mode() {
Coord prev_pos = editor->cursor; Coord prev_pos = this->cursor;
mode = NORMAL;
this->cursor_left(1);
if (prev_pos.row != this->cursor.row)
this->cursor_right(1);
}
void Editor::backspace_edit() {
Coord prev_pos = this->cursor;
if (prev_pos.col > 0) if (prev_pos.col > 0)
prev_pos.col--; 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) if (!it)
return; return;
uint32_t len; uint32_t len;
@@ -267,11 +330,11 @@ void backspace_edit(Editor *editor) {
if (len > 0 && line[len - 1] == '\n') if (len > 0 && line[len - 1] == '\n')
--len; --len;
char prev_char = (prev_pos.col < len) ? line[prev_pos.col] : 0; 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; bool before_content = false;
if (editor->cursor.col > 0) { if (this->cursor.col > 0) {
before_content = true; 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') { if (line[i] != ' ' && line[i] != '\t') {
before_content = false; before_content = false;
break; break;
@@ -280,7 +343,7 @@ void backspace_edit(Editor *editor) {
free(it->buffer); free(it->buffer);
free(it); free(it);
if (before_content) { if (before_content) {
dedent_current_line(editor); this->dedent_current_line();
return; return;
} }
bool is_pair = (prev_char == '{' && next_char == '}') || bool is_pair = (prev_char == '{' && next_char == '}') ||
@@ -289,65 +352,57 @@ void backspace_edit(Editor *editor) {
(prev_char == '"' && next_char == '"') || (prev_char == '"' && next_char == '"') ||
(prev_char == '\'' && next_char == '\''); (prev_char == '\'' && next_char == '\'');
if (is_pair) { if (is_pair) {
edit_erase(editor, editor->cursor, 1); this->edit_erase(this->cursor, 1);
edit_erase(editor, prev_pos, 1); this->edit_erase(prev_pos, 1);
} else { } 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; 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); &prev_col_cluster, nullptr);
if (prev_col_byte == editor->cursor.col) if (prev_col_byte == this->cursor.col)
edit_erase(editor, editor->cursor, -1); this->edit_erase(this->cursor, -1);
else 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; 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); &next_col_cluster);
if (next_col_byte == editor->cursor.col) if (next_col_byte == this->cursor.col)
edit_erase(editor, editor->cursor, 1); this->edit_erase(this->cursor, 1);
else 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::cursor_prev_word() {
for (uint8_t i = 0; i < 94; i++)
if (editor->hooks[i] == line + 1) {
editor->hooks[i] = 0;
break;
}
}
void cursor_prev_word(Editor *editor) {
uint32_t prev_col; uint32_t prev_col;
word_boundaries(editor, editor->cursor, &prev_col, nullptr, nullptr, nullptr); word_boundaries(this->cursor, &prev_col, nullptr, nullptr, nullptr);
editor->cursor_preffered = UINT32_MAX; this->cursor_preffered = UINT32_MAX;
if (prev_col == editor->cursor.col) if (prev_col == this->cursor.col)
cursor_left(editor, 1); cursor_left(1);
else 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; uint32_t next_col;
word_boundaries(editor, editor->cursor, nullptr, &next_col, nullptr, nullptr); word_boundaries(this->cursor, nullptr, &next_col, nullptr, nullptr);
editor->cursor_preffered = UINT32_MAX; this->cursor_preffered = UINT32_MAX;
if (next_col == editor->cursor.col) if (next_col == this->cursor.col)
cursor_right(editor, 1); this->cursor_right(1);
else else
editor->cursor = {editor->cursor.row, next_col}; this->cursor = {this->cursor.row, next_col};
} }
void select_all(Editor *editor) { void Editor::select_all() {
if (editor->root->line_count > 0) { if (this->root->line_count > 0) {
editor->cursor.row = editor->root->line_count - 1; this->cursor.row = this->root->line_count - 1;
LineIterator *it = begin_l_iter(editor->root, editor->cursor.row); LineIterator *it = begin_l_iter(this->root, this->cursor.row);
if (!it) if (!it)
return; return;
uint32_t line_len; uint32_t line_len;
@@ -359,18 +414,19 @@ void select_all(Editor *editor) {
line_len = count_clusters(line, line_len, 0, line_len); line_len = count_clusters(line, line_len, 0, line_len);
free(it->buffer); free(it->buffer);
free(it); free(it);
editor->cursor.col = line_len; this->cursor.col = line_len;
editor->cursor_preffered = UINT32_MAX; this->cursor_preffered = UINT32_MAX;
mode = SELECT; mode = SELECT;
editor->selection_active = true; this->selection_active = true;
editor->selection = {0, 0}; this->selection = {0, 0};
editor->selection_type = LINE; this->selection_type = LINE;
} }
} }
void fetch_lsp_hover(Editor *editor) { void Editor::fetch_lsp_hover() {
if (editor->lsp && editor->lsp->allow_hover) { auto lsp = this->lsp.load();
LineIterator *it = begin_l_iter(editor->root, editor->cursor.row); if (lsp && lsp->allow_hover) {
LineIterator *it = begin_l_iter(this->root, this->cursor.row);
uint32_t line_len; uint32_t line_len;
char *line = next_line(it, &line_len); char *line = next_line(it, &line_len);
if (!line) { if (!line) {
@@ -378,18 +434,18 @@ void fetch_lsp_hover(Editor *editor) {
free(it); free(it);
return; 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->buffer);
free(it); free(it);
json hover_request = { auto message = std::make_unique<LSPMessage>();
{"jsonrpc", "2.0"}, message->message = {
{"method", "textDocument/hover"}, {"method", "textDocument/hover"},
{"params", {"params",
{{"textDocument", {{"uri", editor->uri}}}, {{"textDocument", {{"uri", this->uri}}},
{"position", {{"line", editor->cursor.row}, {"character", col}}}}}}; {"position", {{"line", this->cursor.row}, {"character", col}}}}}};
LSPPending *pending = new LSPPending(); message->editor = this;
pending->editor = editor; message->callback = [](const LSPMessage &message) {
pending->callback = [](Editor *editor, const json &hover) { auto &hover = message.message;
if (hover.contains("result") && !hover["result"].is_null()) { if (hover.contains("result") && !hover["result"].is_null()) {
auto &contents = hover["result"]["contents"]; auto &contents = hover["result"]["contents"];
std::string hover_text = ""; std::string hover_text = "";
@@ -413,23 +469,26 @@ void fetch_lsp_hover(Editor *editor) {
hover_text += contents.get<std::string>(); hover_text += contents.get<std::string>();
} }
if (!hover_text.empty()) { if (!hover_text.empty()) {
editor->hover.clear(); message.editor->hover_popup->clear();
editor->hover.text = clean_text(hover_text); message.editor->hover_popup->text = clean_text(hover_text);
editor->hover.is_markup = is_markup; message.editor->hover_popup->is_markup = is_markup;
editor->hover.render_first(); message.editor->hover_popup->hidden = false;
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) {
layout::focused_window = this;
this->size = size;
static std::chrono::steady_clock::time_point last_click_time = static std::chrono::steady_clock::time_point last_click_time =
std::chrono::steady_clock::now(); std::chrono::steady_clock::now();
static uint32_t click_count = 0; static uint32_t click_count = 0;
static Coord last_click_pos = {UINT32_MAX, UINT32_MAX}; static Coord last_click_pos = {UINT32_MAX, UINT32_MAX};
if (!this->hover_popup->hidden)
this->hover_popup->hidden = true;
if (event.key_type == KEY_MOUSE) { if (event.key_type == KEY_MOUSE) {
auto now = std::chrono::steady_clock::now(); auto now = std::chrono::steady_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>( auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(
@@ -439,18 +498,12 @@ void handle_mouse(Editor *editor, KeyEvent event) {
case SCROLL: case SCROLL:
switch (event.mouse_direction) { switch (event.mouse_direction) {
case SCROLL_UP: case SCROLL_UP:
scroll_up(editor, 4); this->scroll_up(1);
ensure_cursor(editor); this->ensure_cursor();
break; break;
case SCROLL_DOWN: case SCROLL_DOWN:
scroll_down(editor, 4); this->scroll_down(1);
ensure_cursor(editor); this->ensure_cursor();
break;
case SCROLL_LEFT:
cursor_left(editor, 10);
break;
case SCROLL_RIGHT:
cursor_right(editor, 10);
break; break;
} }
break; break;
@@ -463,35 +516,35 @@ void handle_mouse(Editor *editor, KeyEvent event) {
click_count = 1; click_count = 1;
last_click_time = now; last_click_time = now;
last_click_pos = cur_pos; 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) if (p.row == UINT32_MAX && p.col == UINT32_MAX)
return; return;
editor->cursor_preffered = UINT32_MAX; this->cursor_preffered = UINT32_MAX;
if (click_count == 1) { if (click_count == 1) {
editor->cursor = p; this->cursor = p;
editor->selection = p; this->selection = p;
if (mode == SELECT) { if (mode == SELECT) {
mode = NORMAL; mode = NORMAL;
editor->selection_active = false; this->selection_active = false;
} }
} else if (click_count == 2) { } else if (click_count == 2) {
uint32_t prev_col, next_col; 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); nullptr);
if (editor->cursor < editor->selection) if (this->cursor < this->selection)
editor->cursor = {editor->cursor.row, prev_col}; this->cursor = {this->cursor.row, prev_col};
else else
editor->cursor = {editor->cursor.row, next_col}; this->cursor = {this->cursor.row, next_col};
editor->cursor_preffered = UINT32_MAX; this->cursor_preffered = UINT32_MAX;
editor->selection_type = WORD; this->selection_type = WORD;
mode = SELECT; mode = SELECT;
editor->selection_active = true; this->selection_active = true;
} else if (click_count >= 3) { } else if (click_count >= 3) {
if (editor->cursor < editor->selection) { if (this->cursor < this->selection) {
editor->cursor = {p.row, 0}; this->cursor = {p.row, 0};
} else { } else {
uint32_t line_len; 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); char *line = next_line(it, &line_len);
if (!line) if (!line)
return; return;
@@ -499,44 +552,44 @@ void handle_mouse(Editor *editor, KeyEvent event) {
line_len--; line_len--;
free(it->buffer); free(it->buffer);
free(it); free(it);
editor->cursor = {p.row, line_len}; this->cursor = {p.row, line_len};
} }
editor->cursor_preffered = UINT32_MAX; this->cursor_preffered = UINT32_MAX;
editor->selection_type = LINE; this->selection_type = LINE;
mode = SELECT; mode = SELECT;
editor->selection_active = true; this->selection_active = true;
click_count = 3; click_count = 3;
} }
} }
break; break;
case DRAG: case DRAG:
if (event.mouse_button == LEFT_BTN) { 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) if (p.row == UINT32_MAX && p.col == UINT32_MAX)
return; return;
editor->cursor_preffered = UINT32_MAX; this->cursor_preffered = UINT32_MAX;
mode = SELECT; mode = SELECT;
if (!editor->selection_active) { if (!this->selection_active) {
editor->selection_active = true; this->selection_active = true;
editor->selection_type = CHAR; this->selection_type = CHAR;
} }
uint32_t prev_col, next_col, line_len; uint32_t prev_col, next_col, line_len;
switch (editor->selection_type) { switch (this->selection_type) {
case CHAR: case CHAR:
editor->cursor = p; this->cursor = p;
break; break;
case WORD: case WORD:
word_boundaries(editor, p, &prev_col, &next_col, nullptr, nullptr); this->word_boundaries(p, &prev_col, &next_col, nullptr, nullptr);
if (editor->cursor < editor->selection) if (this->cursor < this->selection)
editor->cursor = {p.row, prev_col}; this->cursor = {p.row, prev_col};
else else
editor->cursor = {p.row, next_col}; this->cursor = {p.row, next_col};
break; break;
case LINE: case LINE:
if (editor->cursor < editor->selection) { if (this->cursor < this->selection) {
editor->cursor = {p.row, 0}; this->cursor = {p.row, 0};
} else { } 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); char *line = next_line(it, &line_len);
if (!line) if (!line)
return; return;
@@ -544,7 +597,7 @@ void handle_mouse(Editor *editor, KeyEvent event) {
line_len--; line_len--;
free(it->buffer); free(it->buffer);
free(it); free(it);
editor->cursor = {p.row, line_len}; this->cursor = {p.row, line_len};
} }
break; break;
} }
@@ -552,10 +605,10 @@ void handle_mouse(Editor *editor, KeyEvent event) {
break; break;
case RELEASE: case RELEASE:
if (event.mouse_button == LEFT_BTN) if (event.mouse_button == LEFT_BTN)
if (editor->cursor.row == editor->selection.row && if (this->cursor.row == this->selection.row &&
editor->cursor.col == editor->selection.col) { this->cursor.col == this->selection.col) {
mode = NORMAL; mode = NORMAL;
editor->selection_active = false; this->selection_active = false;
} }
break; break;
} }
+22 -34
View File
@@ -68,7 +68,6 @@ uint32_t IndentationEngine::indent_real(char *line, uint32_t len) {
} }
uint32_t IndentationEngine::indent_expected(uint32_t row) { uint32_t IndentationEngine::indent_expected(uint32_t row) {
std::shared_lock lock(editor->knot_mtx);
uint32_t line_idx = row; uint32_t line_idx = row;
if (row == 0) if (row == 0)
return 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) { 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); LineIterator *it = begin_l_iter(editor->root, row);
if (!it) if (!it)
return 0; 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') if (len > 0 && line[len - 1] == '\n')
--len; --len;
lock.unlock();
if (new_indent <= 0) if (new_indent <= 0)
new_indent = 0; new_indent = 0;
uint32_t ws_len = 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, ' '); new_ws.assign(new_indent * indent, ' ');
Coord start = {row, 0}; Coord start = {row, 0};
Coord end = {row, ws_len}; 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->buffer);
free(it); free(it);
return len - ws_len + (new_indent * indent); return len - ws_len + (new_indent * indent);
} }
uint32_t IndentationEngine::indent_line(uint32_t row) { uint32_t IndentationEngine::indent_line(uint32_t row) {
std::shared_lock lock(editor->knot_mtx);
LineIterator *it = begin_l_iter(editor->root, row); LineIterator *it = begin_l_iter(editor->root, row);
if (!it) if (!it)
return 0; return 0;
@@ -153,7 +149,6 @@ uint32_t IndentationEngine::indent_line(uint32_t row) {
free(it); free(it);
return 0; return 0;
} }
lock.unlock();
if (len > 0 && line[len - 1] == '\n') if (len > 0 && line[len - 1] == '\n')
--len; --len;
uint32_t new_indent = indent_real(line, len) + 1; uint32_t new_indent = indent_real(line, len) + 1;
@@ -165,15 +160,14 @@ uint32_t IndentationEngine::indent_line(uint32_t row) {
new_ws.assign(new_indent, '\t'); new_ws.assign(new_indent, '\t');
else else
new_ws.assign(new_indent * indent, ' '); 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); new_indent * indent);
free(it->buffer); free(it->buffer);
free(it); free(it);
return (uint32_t)ABS((int64_t)ws_len - (new_indent * indent)); return (uint32_t)ABS((int64_t)ws_len - (new_indent * indent));
} }
uint32_t IndentationEngine::dedent_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); LineIterator *it = begin_l_iter(editor->root, row);
if (!it) if (!it)
return 0; return 0;
@@ -184,7 +178,6 @@ uint32_t IndentationEngine::dedent_line(uint32_t row) {
free(it); free(it);
return 0; return 0;
} }
lock.unlock();
if (len > 0 && line[len - 1] == '\n') if (len > 0 && line[len - 1] == '\n')
--len; --len;
int64_t new_indent = (int64_t)indent_real(line, len) - 1; int64_t new_indent = (int64_t)indent_real(line, len) - 1;
@@ -198,8 +191,8 @@ uint32_t IndentationEngine::dedent_line(uint32_t row) {
new_ws.assign(new_indent, '\t'); new_ws.assign(new_indent, '\t');
else else
new_ws.assign(new_indent * indent, ' '); 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); new_indent * indent);
free(it->buffer); free(it->buffer);
free(it); free(it);
return (uint32_t)ABS((int64_t)ws_len - (new_indent * indent)); return (uint32_t)ABS((int64_t)ws_len - (new_indent * indent));
@@ -262,12 +255,11 @@ void IndentationEngine::indent_block(uint32_t start_row, uint32_t end_row,
} }
} }
free(block); 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); free(out);
} }
void IndentationEngine::insert_tab(Coord cursor) { void IndentationEngine::insert_tab(Coord cursor) {
std::shared_lock lock(editor->knot_mtx);
LineIterator *it = begin_l_iter(editor->root, cursor.row); LineIterator *it = begin_l_iter(editor->root, cursor.row);
if (!it) if (!it)
return; return;
@@ -278,7 +270,6 @@ void IndentationEngine::insert_tab(Coord cursor) {
free(it); free(it);
return; return;
} }
lock.unlock();
if (len > 0 && line[len - 1] == '\n') if (len > 0 && line[len - 1] == '\n')
--len; --len;
uint32_t ws_len = 0; uint32_t ws_len = 0;
@@ -295,13 +286,12 @@ void IndentationEngine::insert_tab(Coord cursor) {
} }
free(it->buffer); free(it->buffer);
free(it); 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(); editor->cursor.col += insert.size();
} }
void IndentationEngine::insert_new_line(Coord cursor) { void IndentationEngine::insert_new_line(Coord cursor) {
std::string formatted; std::string formatted;
std::shared_lock lock(editor->knot_mtx);
LineIterator *it = begin_l_iter(editor->root, cursor.row); LineIterator *it = begin_l_iter(editor->root, cursor.row);
if (!it) if (!it)
return; return;
@@ -312,7 +302,6 @@ void IndentationEngine::insert_new_line(Coord cursor) {
free(it); free(it);
return; return;
} }
lock.unlock();
if (len > 0 && line[len - 1] == '\n') if (len > 0 && line[len - 1] == '\n')
--len; --len;
if (cursor.col >= 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); cursor.row, (int64_t)indent_expected(cursor.row) - (int64_t)1);
break; break;
} }
lock.lock();
free(it->buffer); free(it->buffer);
free(it); free(it);
it = begin_l_iter(editor->root, cursor.row); 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') if (len > 0 && line[len - 1] == '\n')
--len; --len;
lock.unlock();
} }
std::string ending = trim(std::string(line + cursor.col, len - cursor.col)); std::string ending = trim(std::string(line + cursor.col, len - cursor.col));
std::string before = trim(std::string(line, 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, ' ')) + : std::string(c_indent * indent, ' ')) +
ending; ending;
Coord new_cursor = {cursor.row + 1, (uint32_t)c_indent * indent}; 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()); formatted.size());
editor->cursor = new_cursor; editor->cursor = new_cursor;
editor->cursor_preffered = UINT32_MAX; editor->cursor_preffered = UINT32_MAX;
free(it->buffer); free(it->buffer);
free(it); free(it);
if (!editor->lsp || !editor->lsp->allow_formatting_on_type) auto lsp = editor->lsp.load();
if (!lsp || !lsp->allow_formatting_on_type)
return; return;
for (char ch : editor->lsp->format_chars) { for (char ch : lsp->format_chars) {
if (ch == '\n') { if (ch == '\n') {
LineIterator *it = begin_l_iter(editor->root, editor->cursor.row); LineIterator *it = begin_l_iter(editor->root, editor->cursor.row);
if (!it) if (!it)
@@ -431,7 +419,9 @@ void IndentationEngine::insert_new_line(Coord cursor) {
free(it->buffer); free(it->buffer);
free(it); free(it);
int version = editor->lsp_version; int version = editor->lsp_version;
json message = { auto message = std::make_unique<LSPMessage>();
message->editor = editor;
message->message = {
{"jsonrpc", "2.0"}, {"jsonrpc", "2.0"},
{"method", "textDocument/onTypeFormatting"}, {"method", "textDocument/onTypeFormatting"},
{"params", {"params",
@@ -443,12 +433,10 @@ void IndentationEngine::insert_new_line(Coord cursor) {
{"insertSpaces", true}, {"insertSpaces", true},
{"trimTrailingWhitespace", true}, {"trimTrailingWhitespace", true},
{"trimFinalNewlines", true}}}}}}; {"trimFinalNewlines", true}}}}}};
LSPPending *pending = new LSPPending(); message->callback = [version](const LSPMessage &message) {
pending->editor = editor; if (version != message.editor->lsp_version)
pending->callback = [version](Editor *editor, const json &message) {
if (version != editor->lsp_version)
return; return;
auto &edits = message["result"]; auto &edits = message.message["result"];
if (edits.is_array()) { if (edits.is_array()) {
std::vector<TextEdit> t_edits; std::vector<TextEdit> t_edits;
t_edits.reserve(edits.size()); 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.start.col = edit["range"]["start"]["character"];
t_edit.end.row = edit["range"]["end"]["line"]; t_edit.end.row = edit["range"]["end"]["line"];
t_edit.end.col = edit["range"]["end"]["character"]; 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); t_edits.push_back(t_edit);
} }
apply_lsp_edits(editor, t_edits, false); message.editor->apply_lsp_edits(t_edits, false);
ensure_scroll(editor); message.editor->ensure_scroll();
} }
}; };
lsp_send(editor->lsp, message, pending); lsp->send(std::move(message));
break; break;
} }
} }
+20 -26
View File
@@ -2,52 +2,47 @@
#include "editor/editor.h" #include "editor/editor.h"
#include "utils/utils.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()) if (!edits.size())
return; return;
TextEdit first = edits[0]; TextEdit first = edits[0];
Coord cursor = editor->cursor; Coord cursor = this->cursor;
std::sort( std::sort(
edits.begin(), edits.end(), edits.begin(), edits.end(),
[](const TextEdit &a, const TextEdit &b) { return a.start > b.start; }); [](const TextEdit &a, const TextEdit &b) { return a.start > b.start; });
for (const auto &edit : edits) 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()); edit.text.size());
if (move) { if (move) {
std::shared_lock lock(editor->knot_mtx); this->cursor = first.start;
editor->cursor = first.start; this->cursor = this->move_right(
editor->cursor = this->cursor, count_clusters(first.text.c_str(), first.text.size(), 0,
move_right(editor, editor->cursor, first.text.size()));
count_clusters(first.text.c_str(), first.text.size(), 0,
first.text.size()));
} else { } else {
if (cursor.row >= editor->root->line_count) { if (cursor.row >= this->root->line_count) {
editor->cursor.row = editor->root->line_count - 1; this->cursor.row = this->root->line_count - 1;
editor->cursor.col = 0; this->cursor.col = 0;
} else { } else {
std::shared_lock lock(editor->knot_mtx);
uint32_t len; uint32_t len;
line_to_byte(editor->root, cursor.row, &len); line_to_byte(this->root, cursor.row, &len);
len--; len--;
editor->cursor.row = cursor.row; this->cursor.row = cursor.row;
editor->cursor.col = cursor.col < len ? cursor.col : len; 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") && if (msg.contains("method") &&
msg["method"] == "textDocument/publishDiagnostics") { msg["method"] == "textDocument/publishDiagnostics") {
std::unique_lock lock(editor->v_mtx); this->warnings.clear();
editor->warnings.clear();
json diagnostics = msg["params"]["diagnostics"]; json diagnostics = msg["params"]["diagnostics"];
for (size_t i = 0; i < diagnostics.size(); i++) { for (size_t i = 0; i < diagnostics.size(); i++) {
json d = diagnostics[i]; json d = diagnostics[i];
VWarn w; VWarn w;
w.line = d["range"]["start"]["line"]; w.line = d["range"]["start"]["line"];
w.start = d["range"]["start"]["character"]; w.start = d["range"]["start"]["character"];
std::shared_lock lock(editor->knot_mtx); LineIterator *it = begin_l_iter(this->root, w.line);
LineIterator *it = begin_l_iter(editor->root, w.line);
if (!it) if (!it)
continue; continue;
uint32_t len; uint32_t len;
@@ -59,7 +54,6 @@ void editor_lsp_handle(Editor *editor, json msg) {
} }
if (len > 0 && line[len - 1] == '\n') if (len > 0 && line[len - 1] == '\n')
--len; --len;
lock.unlock();
w.start = utf16_offset_to_utf8(line, len, w.start); w.start = utf16_offset_to_utf8(line, len, w.start);
uint32_t end = d["range"]["end"]["character"]; uint32_t end = d["range"]["end"]["character"];
if (d["range"]["end"]["line"] == w.line) if (d["range"]["end"]["line"] == w.line)
@@ -102,9 +96,9 @@ void editor_lsp_handle(Editor *editor, json msg) {
w.type = 1; w.type = 1;
if (d.contains("severity")) if (d.contains("severity"))
w.type = d["severity"].get<int>(); w.type = d["severity"].get<int>();
editor->warnings.push_back(w); this->warnings.push_back(w);
} }
std::sort(editor->warnings.begin(), editor->warnings.end()); std::sort(this->warnings.begin(), this->warnings.end());
editor->warnings_dirty = true; this->warnings_dirty = true;
} }
} }
+54 -66
View File
@@ -1,120 +1,108 @@
#include "editor/editor.h" #include "editor/editor.h"
#include "main.h" #include "main.h"
void move_line_up(Editor *editor) { void Editor::move_line_up() {
if (!editor || !editor->root || editor->cursor.row == 0) if (!this->root || this->cursor.row == 0)
return; return;
if (mode == NORMAL || mode == INSERT) { if (mode == NORMAL || mode == INSERT) {
uint32_t line_len, line_cluster_len; uint32_t line_len, line_cluster_len;
std::shared_lock lock(editor->knot_mtx); LineIterator *it = begin_l_iter(this->root, this->cursor.row);
LineIterator *it = begin_l_iter(editor->root, editor->cursor.row);
char *line = next_line(it, &line_len); char *line = next_line(it, &line_len);
if (!line) { if (!line)
lock.unlock();
return; return;
}
if (line_len > 0 && line[line_len - 1] == '\n') if (line_len > 0 && line[line_len - 1] == '\n')
line_len--; line_len--;
line_cluster_len = count_clusters(line, line_len, 0, line_len); line_cluster_len = count_clusters(line, line_len, 0, line_len);
uint32_t target_row = editor->cursor.row - 1; uint32_t target_row = this->cursor.row - 1;
uint32_t up_by = editor->cursor.row - target_row; uint32_t up_by = this->cursor.row - target_row;
if (up_by > 1) if (up_by > 1)
up_by--; up_by--;
lock.unlock(); Coord cursor = this->cursor;
Coord cursor = editor->cursor; edit_erase({cursor.row, 0}, line_cluster_len);
edit_erase(editor, {cursor.row, 0}, line_cluster_len); edit_erase({cursor.row, 0}, -1);
edit_erase(editor, {cursor.row, 0}, -1); edit_insert({cursor.row - up_by, 0}, (char *)"\n", 1);
edit_insert(editor, {cursor.row - up_by, 0}, (char *)"\n", 1); edit_insert({cursor.row - up_by, 0}, line, line_len);
edit_insert(editor, {cursor.row - up_by, 0}, line, line_len);
free(it->buffer); free(it->buffer);
free(it); free(it);
editor->cursor = {cursor.row - up_by, cursor.col}; this->cursor = {cursor.row - up_by, cursor.col};
} else if (mode == SELECT) { } else if (mode == SELECT) {
uint32_t start_row = MIN(editor->cursor.row, editor->selection.row); uint32_t start_row = MIN(this->cursor.row, this->selection.row);
uint32_t end_row = MAX(editor->cursor.row, editor->selection.row); uint32_t end_row = MAX(this->cursor.row, this->selection.row);
uint32_t start_byte = line_to_byte(editor->root, start_row, nullptr); uint32_t start_byte = line_to_byte(this->root, start_row, nullptr);
uint32_t end_byte = line_to_byte(editor->root, end_row + 1, nullptr); uint32_t end_byte = line_to_byte(this->root, end_row + 1, nullptr);
char *selected_text = read(editor->root, start_byte, end_byte - start_byte); char *selected_text = read(this->root, start_byte, end_byte - start_byte);
if (!selected_text) if (!selected_text)
return; return;
uint32_t selected_len = count_clusters(selected_text, end_byte - start_byte, uint32_t selected_len = count_clusters(selected_text, end_byte - start_byte,
0, end_byte - start_byte); 0, end_byte - start_byte);
Coord cursor = editor->cursor; Coord cursor = this->cursor;
Coord selection = editor->selection; Coord selection = this->selection;
edit_erase(editor, {start_row, 0}, selected_len); edit_erase({start_row, 0}, selected_len);
edit_insert(editor, {start_row - 1, 0}, selected_text, edit_insert({start_row - 1, 0}, selected_text, end_byte - start_byte);
end_byte - start_byte);
free(selected_text); free(selected_text);
editor->cursor = {cursor.row - 1, cursor.col}; this->cursor = {cursor.row - 1, cursor.col};
editor->selection = {selection.row - 1, selection.col}; this->selection = {selection.row - 1, selection.col};
} }
} }
void move_line_down(Editor *editor) { void Editor::move_line_down() {
if (!editor || !editor->root) if (!this->root)
return; return;
if (mode == NORMAL || mode == INSERT) { if (mode == NORMAL || mode == INSERT) {
if (editor->cursor.row >= editor->root->line_count - 1) if (this->cursor.row >= this->root->line_count - 1)
return; return;
uint32_t line_len, line_cluster_len; uint32_t line_len, line_cluster_len;
std::shared_lock lock(editor->knot_mtx); LineIterator *it = begin_l_iter(this->root, this->cursor.row);
LineIterator *it = begin_l_iter(editor->root, editor->cursor.row);
char *line = next_line(it, &line_len); char *line = next_line(it, &line_len);
if (!line) { if (!line)
lock.unlock();
return; return;
}
if (line_len && line[line_len - 1] == '\n') if (line_len && line[line_len - 1] == '\n')
line_len--; line_len--;
line_cluster_len = count_clusters(line, line_len, 0, line_len); line_cluster_len = count_clusters(line, line_len, 0, line_len);
uint32_t target_row = editor->cursor.row + 1; uint32_t target_row = this->cursor.row + 1;
if (target_row >= editor->root->line_count) { if (target_row >= this->root->line_count) {
free(line); free(it->buffer);
lock.unlock(); free(it);
return; return;
} }
uint32_t down_by = target_row - editor->cursor.row; uint32_t down_by = target_row - this->cursor.row;
if (down_by > 1) if (down_by > 1)
down_by--; down_by--;
uint32_t ln; uint32_t ln;
line_to_byte(editor->root, editor->cursor.row + down_by - 1, &ln); line_to_byte(this->root, this->cursor.row + down_by - 1, &ln);
lock.unlock(); Coord cursor = this->cursor;
Coord cursor = editor->cursor; edit_erase({cursor.row, 0}, line_cluster_len);
edit_erase(editor, {cursor.row, 0}, line_cluster_len); edit_erase({cursor.row, 0}, -1);
edit_erase(editor, {cursor.row, 0}, -1); edit_insert({cursor.row + down_by, 0}, (char *)"\n", 1);
edit_insert(editor, {cursor.row + down_by, 0}, (char *)"\n", 1); edit_insert({cursor.row + down_by, 0}, line, line_len);
edit_insert(editor, {cursor.row + down_by, 0}, line, line_len);
free(it->buffer); free(it->buffer);
free(it); free(it);
editor->cursor = {cursor.row + down_by, cursor.col}; this->cursor = {cursor.row + down_by, cursor.col};
} else if (mode == SELECT) { } else if (mode == SELECT) {
if (editor->cursor.row >= editor->root->line_count - 1 || if (this->cursor.row >= this->root->line_count - 1 ||
editor->selection.row >= editor->root->line_count - 1) this->selection.row >= this->root->line_count - 1)
return; return;
std::shared_lock lock(editor->knot_mtx); uint32_t start_row = MIN(this->cursor.row, this->selection.row);
uint32_t start_row = MIN(editor->cursor.row, editor->selection.row); uint32_t end_row = MAX(this->cursor.row, this->selection.row);
uint32_t end_row = MAX(editor->cursor.row, editor->selection.row);
uint32_t target_row = end_row + 1; uint32_t target_row = end_row + 1;
if (target_row >= editor->root->line_count) if (target_row >= this->root->line_count)
return; return;
uint32_t down_by = target_row - end_row; uint32_t down_by = target_row - end_row;
if (down_by > 1) if (down_by > 1)
down_by--; down_by--;
uint32_t start_byte = line_to_byte(editor->root, start_row, nullptr); uint32_t start_byte = line_to_byte(this->root, start_row, nullptr);
uint32_t end_byte = line_to_byte(editor->root, end_row + 1, nullptr); uint32_t end_byte = line_to_byte(this->root, end_row + 1, nullptr);
char *selected_text = read(editor->root, start_byte, end_byte - start_byte); char *selected_text = read(this->root, start_byte, end_byte - start_byte);
lock.unlock();
if (!selected_text) if (!selected_text)
return; return;
uint32_t selected_len = count_clusters(selected_text, end_byte - start_byte, uint32_t selected_len = count_clusters(selected_text, end_byte - start_byte,
0, end_byte - start_byte); 0, end_byte - start_byte);
Coord cursor = editor->cursor; Coord cursor = this->cursor;
Coord selection = editor->selection; Coord selection = this->selection;
edit_erase(editor, {start_row, 0}, selected_len); edit_erase({start_row, 0}, selected_len);
edit_insert(editor, {start_row + down_by, 0}, selected_text, edit_insert({start_row + down_by, 0}, selected_text, end_byte - start_byte);
end_byte - start_byte);
free(selected_text); free(selected_text);
editor->cursor = {cursor.row + down_by, cursor.col}; this->cursor = {cursor.row + down_by, cursor.col};
editor->selection = {selection.row + down_by, selection.col}; this->selection = {selection.row + down_by, selection.col};
} }
} }
+131 -152
View File
@@ -3,27 +3,17 @@
#include "main.h" #include "main.h"
#include "syntax/decl.h" #include "syntax/decl.h"
#include "syntax/parser.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; uint32_t sel_start = 0, sel_end = 0;
std::shared_lock knot_lock(editor->knot_mtx);
uint32_t numlen = uint32_t numlen =
EXTRA_META + static_cast<int>(std::log10(editor->root->line_count + 1)); EXTRA_META + static_cast<int>(std::log10(this->root->line_count + 1));
uint32_t render_width = editor->size.col - numlen; uint32_t render_width = size.col - numlen - 1;
uint32_t render_x = editor->position.col + numlen + 1; uint32_t render_x = pos.col + numlen + 1;
std::vector<std::pair<uint32_t, char>> v; this->hooks.start_iter(this->scroll.row);
for (size_t i = 0; i < 94; ++i) auto warn_it = this->warnings.begin();
if (editor->hooks[i] != 0) while (warn_it != this->warnings.end() && warn_it->line < this->scroll.row)
v.push_back({editor->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)
++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)
++warn_it; ++warn_it;
LineData *line_data = nullptr; LineData *line_data = nullptr;
auto get_type = [&](uint32_t col) { auto get_type = [&](uint32_t col) {
@@ -34,40 +24,54 @@ void render_editor(Editor *editor) {
return (int)token.type; return (int)token.type;
return 0; return 0;
}; };
if (editor->selection_active) { Coord screen = {io::rows, io::cols};
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; Coord start, end;
if (editor->cursor >= editor->selection) { if (this->cursor >= this->selection) {
uint32_t prev_col, next_col; uint32_t prev_col, next_col;
switch (editor->selection_type) { switch (this->selection_type) {
case CHAR: case CHAR:
start = editor->selection; start = this->selection;
end = move_right(editor, editor->cursor, 1); end = this->move_right(this->cursor, 1);
break; break;
case WORD: case WORD:
word_boundaries(editor, editor->selection, &prev_col, &next_col, this->word_boundaries(this->selection, &prev_col, &next_col, nullptr,
nullptr, nullptr); nullptr);
start = {editor->selection.row, prev_col}; start = {this->selection.row, prev_col};
end = editor->cursor; end = this->cursor;
break; break;
case LINE: case LINE:
start = {editor->selection.row, 0}; start = {this->selection.row, 0};
end = editor->cursor; end = this->cursor;
break; break;
} }
} else { } else {
start = editor->cursor; start = this->cursor;
uint32_t prev_col, next_col, line_len; uint32_t prev_col, next_col, line_len;
switch (editor->selection_type) { switch (this->selection_type) {
case CHAR: case CHAR:
end = move_right(editor, editor->selection, 1); end = this->move_right(this->selection, 1);
break; break;
case WORD: case WORD:
word_boundaries(editor, editor->selection, &prev_col, &next_col, this->word_boundaries(this->selection, &prev_col, &next_col, nullptr,
nullptr, nullptr); nullptr);
end = {editor->selection.row, next_col}; end = {this->selection.row, next_col};
break; break;
case LINE: 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); char *line = next_line(it, &line_len);
if (!line) if (!line)
return; return;
@@ -75,41 +79,37 @@ void render_editor(Editor *editor) {
line_len--; line_len--;
free(it->buffer); free(it->buffer);
free(it); free(it);
end = {editor->selection.row, line_len}; end = {this->selection.row, line_len};
break; break;
} }
} }
sel_start = line_to_byte(editor->root, start.row, nullptr) + start.col; sel_start = line_to_byte(this->root, start.row, nullptr) + start.col;
sel_end = line_to_byte(editor->root, end.row, nullptr) + end.col; sel_end = line_to_byte(this->root, end.row, nullptr) + end.col;
} }
Coord cursor = {UINT32_MAX, UINT32_MAX}; Coord cursor = {UINT32_MAX, UINT32_MAX};
uint32_t line_index = editor->scroll.row; uint32_t line_index = this->scroll.row;
LineIterator *it = begin_l_iter(editor->root, line_index); LineIterator *it = begin_l_iter(this->root, line_index);
if (!it) if (!it)
return; return;
uint32_t prev_col, next_col; uint32_t prev_col, next_col;
std::string word; 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) { if (next_col - prev_col > 0 && next_col - prev_col < 256 - 4) {
uint32_t offset = line_to_byte(editor->root, editor->cursor.row, nullptr); uint32_t offset = line_to_byte(this->root, this->cursor.row, nullptr);
char *word_ptr = read(editor->root, offset + prev_col, next_col - prev_col); char *word_ptr = read(this->root, offset + prev_col, next_col - prev_col);
if (word_ptr) { if (word_ptr) {
word = std::string(word_ptr, next_col - prev_col); word = std::string(word_ptr, next_col - prev_col);
free(word_ptr); 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 rendered_rows = 0;
uint32_t global_byte_offset = line_to_byte(editor->root, line_index, nullptr); uint32_t global_byte_offset = line_to_byte(this->root, line_index, nullptr);
while (rendered_rows < editor->size.row) { while (rendered_rows < this->size.row) {
uint32_t line_len; uint32_t line_len;
char *line = next_line(it, &line_len); char *line = next_line(it, &line_len);
if (editor->parser) { if (this->parser)
if (line_data) line_data = this->parser->line_map.at(line_index);
line_data = editor->parser->line_tree.next();
else
line_data = editor->parser->line_tree.start_iter(line_index);
}
if (!line) if (!line)
break; break;
if (line_len > 0 && line[line_len - 1] == '\n') if (line_len > 0 && line[line_len - 1] == '\n')
@@ -123,54 +123,46 @@ void render_editor(Editor *editor) {
(line[content_start] == ' ' || line[content_start] == '\t')) (line[content_start] == ' ' || line[content_start] == '\t'))
content_start++; content_start++;
std::vector<VWarn> line_warnings; 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); line_warnings.push_back(*warn_it);
++warn_it; ++warn_it;
} }
uint32_t current_byte_offset = 0; uint32_t current_byte_offset = 0;
if (rendered_rows == 0) if (rendered_rows == 0)
current_byte_offset += editor->scroll.col; current_byte_offset += this->scroll.col;
while (current_byte_offset < line_len && rendered_rows < editor->size.row) { while (current_byte_offset < line_len && rendered_rows < this->size.row) {
uint32_t color = editor->cursor.row == line_index ? 0x222222 : 0; uint32_t color = this->cursor.row == line_index ? 0x222222 : 0;
if (current_byte_offset == 0 || rendered_rows == 0) { if (current_byte_offset == 0 || rendered_rows == 0) {
const char *hook = nullptr; char hook = this->hooks.next(line_index);
char h[2] = {0, 0}; update(pos.row + rendered_rows, pos.col, {hook, 0}, 0xAAAAAA, 0, 0, 0,
if (hook_it != v.end() && hook_it->first == line_index + 1) { 1);
h[0] = hook_it->second;
hook = h;
hook_it++;
}
update(editor->position.row + rendered_rows, editor->position.col, hook,
0xAAAAAA, 0, 0);
char buf[16]; char buf[16];
int len = snprintf(buf, sizeof(buf), "%*u ", numlen, line_index + 1); int len = snprintf(buf, sizeof(buf), "%*u ", numlen, line_index + 1);
uint32_t num_color = 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++) for (int i = 0; i < len; i++)
update(editor->position.row + rendered_rows, editor->position.col + i, update(pos.row + rendered_rows, pos.col + i, (char[2]){buf[i], 0},
(char[2]){buf[i], 0}, num_color, 0, 0); num_color, 0, 0, 0, 1);
} else { } else {
for (uint32_t i = 0; i < numlen + 1; i++) for (uint32_t i = 0; i < numlen + 1; i++)
update(editor->position.row + rendered_rows, editor->position.col + i, update(pos.row + rendered_rows, pos.col + i, " ", 0, 0, 0, 0, 1);
" ", 0, 0, 0);
} }
uint32_t col = 0; uint32_t col = 0;
uint32_t local_render_offset = 0; uint32_t local_render_offset = 0;
uint32_t line_left = line_len - current_byte_offset; uint32_t line_left = line_len - current_byte_offset;
while (line_left > 0 && col < render_width) { while (line_left > 0 && col < render_width) {
if (line_index == editor->cursor.row && if (line_index == this->cursor.row &&
editor->cursor.col == (current_byte_offset + local_render_offset)) { this->cursor.col == (current_byte_offset + local_render_offset)) {
cursor.row = editor->position.row + rendered_rows; cursor.row = pos.row + rendered_rows;
cursor.col = render_x + col; cursor.col = render_x + col;
} }
uint32_t absolute_byte_pos = uint32_t absolute_byte_pos =
global_byte_offset + current_byte_offset + local_render_offset; global_byte_offset + current_byte_offset + local_render_offset;
const Highlight *hl = nullptr; const Highlight *hl = nullptr;
if (editor->parser) if (this->parser)
hl = &highlights[get_type(current_byte_offset + local_render_offset)]; hl = &highlights[get_type(current_byte_offset + local_render_offset)];
std::optional<std::pair<uint32_t, uint32_t>> extra = std::optional<std::pair<uint32_t, uint32_t>> extra = this->extra_hl.get(
editor->extra_hl.get( {line_index, current_byte_offset + local_render_offset});
{line_index, current_byte_offset + local_render_offset});
uint32_t fg = extra && extra->second != UINT32_MAX uint32_t fg = extra && extra->second != UINT32_MAX
? extra->first ? extra->first
: (hl ? hl->fg : 0xFFFFFF); : (hl ? hl->fg : 0xFFFFFF);
@@ -181,7 +173,7 @@ void render_editor(Editor *editor) {
(hl ? hl->flags : 0) | (hl ? hl->flags : 0) |
(extra ? (extra->second != UINT32_MAX ? CF_BOLD : CF_UNDERLINE) (extra ? (extra->second != UINT32_MAX ? CF_BOLD : CF_UNDERLINE)
: 0); : 0);
if (editor->selection_active && absolute_byte_pos >= sel_start && if (this->selection_active && absolute_byte_pos >= sel_start &&
absolute_byte_pos < sel_end) absolute_byte_pos < sel_end)
bg = bg | 0x555555; bg = bg | 0x555555;
uint32_t u_color = 0; uint32_t u_color = 0;
@@ -217,40 +209,39 @@ void render_editor(Editor *editor) {
break; break;
if (current_byte_offset + local_render_offset >= content_start && if (current_byte_offset + local_render_offset >= content_start &&
current_byte_offset + local_render_offset < content_end) { current_byte_offset + local_render_offset < content_end) {
update(editor->position.row + rendered_rows, render_x + col, update(pos.row + rendered_rows, render_x + col, cluster.c_str(), fg,
cluster.c_str(), fg, bg | color, fl, u_color); bg | color, fl, u_color, width);
} else { } else {
if (cluster[0] == ' ') { if (cluster[0] == ' ') {
update(editor->position.row + rendered_rows, render_x + col, "·", update(pos.row + rendered_rows, render_x + col, "·", 0x282828,
0x282828, bg | color, fl, u_color); bg | color, fl, u_color, 1);
} else { } else {
update(editor->position.row + rendered_rows, render_x + col, "-> ", update(pos.row + rendered_rows, render_x + col, "-> ", 0x282828,
0x282828, bg | color, (fl & ~CF_BOLD) | CF_ITALIC, u_color); bg | color, (fl & ~CF_BOLD) | CF_ITALIC, u_color, 4);
} }
} }
local_render_offset += cluster_len; local_render_offset += cluster_len;
line_left -= cluster_len; line_left -= cluster_len;
col += width; col += width;
while (width-- > 1) while (width-- > 1)
update(editor->position.row + rendered_rows, render_x + col - width, update(pos.row + rendered_rows, render_x + col - width, "\x1b", fg,
"\x1b", fg, bg | color, fl); bg | color, fl, u_color, 0);
} }
if (line_index == editor->cursor.row && if (line_index == this->cursor.row &&
editor->cursor.col == (current_byte_offset + local_render_offset)) { this->cursor.col == (current_byte_offset + local_render_offset)) {
cursor.row = editor->position.row + rendered_rows; cursor.row = pos.row + rendered_rows;
cursor.col = render_x + col; 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_start &&
global_byte_offset + line_len + 1 <= sel_end && col < render_width) { global_byte_offset + line_len + 1 <= sel_end && col < render_width) {
update(editor->position.row + rendered_rows, render_x + col, " ", 0, update(pos.row + rendered_rows, render_x + col, " ", 0,
0x555555 | color, 0); 0x555555 | color, 0, 0, 1);
col++; col++;
} }
if (!line_warnings.empty() && line_left == 0) { if (!line_warnings.empty() && line_left == 0) {
VWarn warn = line_warnings.front(); VWarn warn = line_warnings.front();
update(editor->position.row + rendered_rows, render_x + col, " ", 0, update(pos.row + rendered_rows, render_x + col, " ", 0, color, 0, 0, 1);
color, 0);
col++; col++;
for (size_t i = 0; i < line_warnings.size(); i++) { for (size_t i = 0; i < line_warnings.size(); i++) {
if (line_warnings[i].type < warn.type) if (line_warnings[i].type < warn.type)
@@ -276,18 +267,18 @@ void render_editor(Editor *editor) {
goto final; goto final;
final: final:
if (col < render_width) { if (col < render_width) {
update(editor->position.row + rendered_rows, render_x + col, update(pos.row + rendered_rows, render_x + col, err_sym, fg_color,
err_sym, fg_color, color, 0); color, 0, 0, 1);
col++; col++;
update(editor->position.row + rendered_rows, render_x + col, " ", update(pos.row + rendered_rows, render_x + col, " ", fg_color,
fg_color, color, 0); color, 0, 0, 1);
col++; col++;
} }
} }
} }
if (col < render_width) { if (col < render_width) {
update(editor->position.row + rendered_rows, render_x + col, " ", 0, update(pos.row + rendered_rows, render_x + col, " ", 0, 0 | color, 0,
0 | color, 0); 0, 1);
col++; col++;
} }
size_t warn_idx = 0; size_t warn_idx = 0;
@@ -313,19 +304,19 @@ void render_editor(Editor *editor) {
int width = display_width(cluster.c_str(), cluster_len); int width = display_width(cluster.c_str(), cluster_len);
if (col + width > render_width) if (col + width > render_width)
break; break;
update(editor->position.row + rendered_rows, render_x + col, update(pos.row + rendered_rows, render_x + col, cluster.c_str(),
cluster.c_str(), fg_color, color, 0); fg_color, color, 0, 0, width);
col += width; col += width;
warn_idx += cluster_len; warn_idx += cluster_len;
while (width-- > 1) while (width-- > 1)
update(editor->position.row + rendered_rows, render_x + col - width, update(pos.row + rendered_rows, render_x + col - width, "\x1b",
"\x1b", fg_color, color, 0); fg_color, color, 0, 0, 0);
} }
line_warnings.clear(); line_warnings.clear();
} }
while (col < render_width) { while (col < render_width) {
update(editor->position.row + rendered_rows, render_x + col, " ", 0, update(pos.row + rendered_rows, render_x + col, " ", 0, 0 | color, 0, 0,
0 | color, 0); 1);
col++; col++;
} }
rendered_rows++; rendered_rows++;
@@ -333,39 +324,30 @@ void render_editor(Editor *editor) {
} }
if (line_len == 0 || if (line_len == 0 ||
(current_byte_offset >= line_len && rendered_rows == 0)) { (current_byte_offset >= line_len && rendered_rows == 0)) {
uint32_t color = editor->cursor.row == line_index ? 0x222222 : 0; uint32_t color = this->cursor.row == line_index ? 0x222222 : 0;
const char *hook = nullptr; char hook = this->hooks.next(line_index);
char h[2] = {0, 0}; update(pos.row + rendered_rows, pos.col, {hook, 0}, 0xAAAAAA, 0, 0, 0, 1);
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);
char buf[16]; char buf[16];
int len = snprintf(buf, sizeof(buf), "%*u ", numlen, line_index + 1); int len = snprintf(buf, sizeof(buf), "%*u ", numlen, line_index + 1);
uint32_t num_color = uint32_t num_color = this->cursor.row == line_index ? 0xFFFFFF : 0x555555;
editor->cursor.row == line_index ? 0xFFFFFF : 0x555555;
for (int i = 0; i < len; i++) for (int i = 0; i < len; i++)
update(editor->position.row + rendered_rows, editor->position.col + i, update(pos.row + rendered_rows, pos.col + i, (char[2]){buf[i], 0},
(char[2]){buf[i], 0}, num_color, 0, 0); num_color, 0, 0, 0, 1);
if (editor->cursor.row == line_index) { if (this->cursor.row == line_index) {
cursor.row = editor->position.row + rendered_rows; cursor.row = pos.row + rendered_rows;
cursor.col = render_x; cursor.col = render_x;
} }
uint32_t col = 0; 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_start &&
global_byte_offset + line_len + 1 <= sel_end) { global_byte_offset + line_len + 1 <= sel_end) {
update(editor->position.row + rendered_rows, render_x + col, " ", 0, update(pos.row + rendered_rows, render_x + col, " ", 0,
0x555555 | color, 0); 0x555555 | color, 0, 0, 1);
col++; col++;
} }
if (!line_warnings.empty()) { if (!line_warnings.empty()) {
VWarn warn = line_warnings.front(); VWarn warn = line_warnings.front();
update(editor->position.row + rendered_rows, render_x + col, " ", 0, update(pos.row + rendered_rows, render_x + col, " ", 0, color, 0, 0, 1);
color, 0);
col++; col++;
for (size_t i = 0; i < line_warnings.size(); i++) { for (size_t i = 0; i < line_warnings.size(); i++) {
if (line_warnings[i].type < warn.type) if (line_warnings[i].type < warn.type)
@@ -391,18 +373,18 @@ void render_editor(Editor *editor) {
goto final2; goto final2;
final2: final2:
if (col < render_width) { if (col < render_width) {
update(editor->position.row + rendered_rows, render_x + col, update(pos.row + rendered_rows, render_x + col, err_sym, fg_color,
err_sym, fg_color, color, 0); color, 0, 0, 1);
col++; col++;
update(editor->position.row + rendered_rows, render_x + col, " ", update(pos.row + rendered_rows, render_x + col, " ", fg_color,
fg_color, color, 0); color, 0, 0, 1);
col++; col++;
} }
} }
} }
if (col < render_width) { if (col < render_width) {
update(editor->position.row + rendered_rows, render_x + col, " ", 0, update(pos.row + rendered_rows, render_x + col, " ", 0, 0 | color, 0,
0 | color, 0); 0, 1);
col++; col++;
} }
size_t warn_idx = 0; size_t warn_idx = 0;
@@ -428,18 +410,18 @@ void render_editor(Editor *editor) {
int width = display_width(cluster.c_str(), cluster_len); int width = display_width(cluster.c_str(), cluster_len);
if (col + width > render_width) if (col + width > render_width)
break; break;
update(editor->position.row + rendered_rows, render_x + col, update(pos.row + rendered_rows, render_x + col, cluster.c_str(),
cluster.c_str(), fg_color, color, 0); fg_color, color, 0, 0, width);
col += width; col += width;
warn_idx += cluster_len; warn_idx += cluster_len;
while (width-- > 1) while (width-- > 1)
update(editor->position.row + rendered_rows, render_x + col - width, update(pos.row + rendered_rows, render_x + col - width, "\x1b",
"\x1b", fg_color, color, 0); fg_color, color, 0, 0, 0);
} }
} }
while (col < render_width) { while (col < render_width) {
update(editor->position.row + rendered_rows, render_x + col, " ", 0, update(pos.row + rendered_rows, render_x + col, " ", 0, 0 | color, 0, 0,
0 | color, 0); 1);
col++; col++;
} }
rendered_rows++; rendered_rows++;
@@ -447,10 +429,9 @@ void render_editor(Editor *editor) {
global_byte_offset += line_len + 1; global_byte_offset += line_len + 1;
line_index++; line_index++;
} }
while (rendered_rows < editor->size.row) { while (rendered_rows < this->size.row) {
for (uint32_t col = 0; col < editor->size.col; col++) for (uint32_t col = 0; col < this->size.col; col++)
update(editor->position.row + rendered_rows, editor->position.col + col, update(pos.row + rendered_rows, pos.col + col, " ", 0xFFFFFF, 0, 0, 0, 1);
" ", 0xFFFFFF, 0, 0);
rendered_rows++; rendered_rows++;
} }
if (cursor.row != UINT32_MAX && cursor.col != UINT32_MAX) { if (cursor.row != UINT32_MAX && cursor.col != UINT32_MAX) {
@@ -468,15 +449,13 @@ void render_editor(Editor *editor) {
break; break;
} }
set_cursor(cursor.row, cursor.col, type, true); set_cursor(cursor.row, cursor.col, type, true);
if (editor->completion.active && !editor->completion.box.hidden) if (!this->hover_popup->hidden)
editor->completion.box.render(cursor); this->hover_popup->pos = cursor;
else if (editor->hover_active) if (!this->diagnostic_popup->hidden)
editor->hover.render(cursor); this->diagnostic_popup->pos = cursor;
else if (editor->diagnostics_active)
editor->diagnostics.render(cursor);
} }
free(it->buffer); free(it->buffer);
free(it); free(it);
if (editor->parser) if (this->parser)
editor->parser->scroll(line_index + 5); this->parser->scroll(line_index + 5);
} }
+22 -23
View File
@@ -1,13 +1,13 @@
#include "editor/editor.h" #include "editor/editor.h"
void scroll_up(Editor *editor, int32_t number) { void Editor::scroll_up(uint32_t number) {
if (!editor || number == 0) if (number == 0)
return; return;
uint32_t numlen = uint32_t numlen =
EXTRA_META + static_cast<int>(std::log10(editor->root->line_count + 1)); EXTRA_META + static_cast<int>(std::log10(this->root->line_count + 1));
uint32_t render_width = editor->size.col - numlen; uint32_t render_width = this->size.col - numlen;
uint32_t line_index = editor->scroll.row; uint32_t line_index = this->scroll.row;
LineIterator *it = begin_l_iter(editor->root, line_index); LineIterator *it = begin_l_iter(this->root, line_index);
if (!it) if (!it)
return; return;
uint32_t len; uint32_t len;
@@ -23,10 +23,9 @@ void scroll_up(Editor *editor, int32_t number) {
uint32_t col = 0; uint32_t col = 0;
std::vector<uint32_t> segment_starts; std::vector<uint32_t> segment_starts;
segment_starts.reserve(16); segment_starts.reserve(16);
if (current_byte_offset < editor->scroll.col) if (current_byte_offset < this->scroll.col)
segment_starts.push_back(0); segment_starts.push_back(0);
while (current_byte_offset < editor->scroll.col && while (current_byte_offset < this->scroll.col && current_byte_offset < len) {
current_byte_offset < len) {
uint32_t cluster_len = grapheme_next_character_break_utf8( uint32_t cluster_len = grapheme_next_character_break_utf8(
line + current_byte_offset, len - current_byte_offset); line + current_byte_offset, len - current_byte_offset);
int width = display_width(line + current_byte_offset, cluster_len); 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(); for (auto it_seg = segment_starts.rbegin(); it_seg != segment_starts.rend();
++it_seg) { ++it_seg) {
if (--number == 0) { if (--number == 0) {
editor->scroll = {line_index, *it_seg}; this->scroll = {line_index, *it_seg};
free(it->buffer); free(it->buffer);
free(it); free(it);
return; return;
@@ -48,7 +47,7 @@ void scroll_up(Editor *editor, int32_t number) {
} }
line = prev_line(it, &len); line = prev_line(it, &len);
if (!line) { if (!line) {
editor->scroll = {0, 0}; this->scroll = {0, 0};
free(it->buffer); free(it->buffer);
free(it); free(it);
return; return;
@@ -57,7 +56,7 @@ void scroll_up(Editor *editor, int32_t number) {
line_index--; line_index--;
line = prev_line(it, &len); line = prev_line(it, &len);
if (!line) { if (!line) {
editor->scroll = {0, 0}; this->scroll = {0, 0};
free(it->buffer); free(it->buffer);
free(it); free(it);
return; 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(); for (auto it_seg = segment_starts.rbegin(); it_seg != segment_starts.rend();
++it_seg) { ++it_seg) {
if (--number == 0) { if (--number == 0) {
editor->scroll = {line_index, *it_seg}; this->scroll = {line_index, *it_seg};
free(it->buffer); free(it->buffer);
free(it); free(it);
return; return;
@@ -94,17 +93,17 @@ void scroll_up(Editor *editor, int32_t number) {
free(it); free(it);
} }
void scroll_down(Editor *editor, uint32_t number) { void Editor::scroll_down(uint32_t number) {
if (!editor || number == 0) if (number == 0)
return; return;
uint32_t numlen = uint32_t numlen =
EXTRA_META + static_cast<int>(std::log10(editor->root->line_count + 1)); EXTRA_META + static_cast<int>(std::log10(this->root->line_count + 1));
uint32_t render_width = editor->size.col - numlen; uint32_t render_width = this->size.col - numlen;
uint32_t line_index = editor->scroll.row; uint32_t line_index = this->scroll.row;
LineIterator *it = begin_l_iter(editor->root, line_index); LineIterator *it = begin_l_iter(this->root, line_index);
if (!it) if (!it)
return; 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); Coord *scroll_queue = (Coord *)malloc(sizeof(Coord) * max_visual_lines);
uint32_t q_head = 0; uint32_t q_head = 0;
uint32_t q_size = 0; uint32_t q_size = 0;
@@ -119,7 +118,7 @@ void scroll_down(Editor *editor, uint32_t number) {
line_len--; line_len--;
uint32_t current_byte_offset = 0; uint32_t current_byte_offset = 0;
if (first_visual_line) { if (first_visual_line) {
current_byte_offset += editor->scroll.col; current_byte_offset += this->scroll.col;
first_visual_line = false; first_visual_line = false;
} }
while (current_byte_offset < line_len || while (current_byte_offset < line_len ||
@@ -134,7 +133,7 @@ void scroll_down(Editor *editor, uint32_t number) {
} }
visual_seen++; visual_seen++;
if (visual_seen >= number + max_visual_lines) { if (visual_seen >= number + max_visual_lines) {
editor->scroll = scroll_queue[q_head]; this->scroll = scroll_queue[q_head];
free(scroll_queue); free(scroll_queue);
free(it->buffer); free(it->buffer);
free(it); free(it);
@@ -162,7 +161,7 @@ void scroll_down(Editor *editor, uint32_t number) {
} }
if (q_size > 0) { if (q_size > 0) {
uint32_t advance = (q_size > number) ? number : (q_size - 1); 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->buffer);
free(it); free(it);
+41 -43
View File
@@ -1,47 +1,46 @@
#include "editor/editor.h" #include "editor/editor.h"
#include "utils/utils.h" #include "utils/utils.h"
void selection_bounds(Editor *editor, Coord *out_start, Coord *out_end) { void Editor::selection_bounds(Coord *out_start, Coord *out_end) {
std::shared_lock lock(editor->knot_mtx);
Coord start, end; Coord start, end;
if (editor->cursor >= editor->selection) { if (this->cursor >= this->selection) {
uint32_t prev_col; uint32_t prev_col;
switch (editor->selection_type) { switch (this->selection_type) {
case CHAR: case CHAR:
start = editor->selection; start = this->selection;
end = move_right(editor, editor->cursor, 1); end = this->move_right(this->cursor, 1);
break; break;
case WORD: case WORD:
word_boundaries(editor, editor->selection, &prev_col, nullptr, nullptr, this->word_boundaries(this->selection, &prev_col, nullptr, nullptr,
nullptr); nullptr);
start = {editor->selection.row, prev_col}; start = {this->selection.row, prev_col};
end = editor->cursor; end = this->cursor;
break; break;
case LINE: case LINE:
start = {editor->selection.row, 0}; start = {this->selection.row, 0};
end = editor->cursor; end = this->cursor;
break; break;
} }
} else { } else {
start = editor->cursor; start = this->cursor;
uint32_t next_col, line_len; uint32_t next_col, line_len;
switch (editor->selection_type) { switch (this->selection_type) {
case CHAR: case CHAR:
end = move_right(editor, editor->selection, 1); end = this->move_right(this->selection, 1);
break; break;
case WORD: case WORD:
word_boundaries(editor, editor->selection, nullptr, &next_col, nullptr, this->word_boundaries(this->selection, nullptr, &next_col, nullptr,
nullptr); nullptr);
end = {editor->selection.row, next_col}; end = {this->selection.row, next_col};
break; break;
case LINE: 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); char *line = next_line(it, &line_len);
if (!line) if (!line)
return; return;
if (line_len > 0 && line[line_len - 1] == '\n') if (line_len > 0 && line[line_len - 1] == '\n')
line_len--; line_len--;
end = {editor->selection.row, line_len}; end = {this->selection.row, line_len};
free(it->buffer); free(it->buffer);
free(it); free(it);
break; break;
@@ -53,47 +52,46 @@ void selection_bounds(Editor *editor, Coord *out_start, Coord *out_end) {
*out_end = end; *out_end = end;
} }
char *get_selection(Editor *editor, uint32_t *out_len, Coord *out_start) { char *Editor::get_selection(uint32_t *out_len, Coord *out_start) {
std::shared_lock lock(editor->knot_mtx);
Coord start, end; Coord start, end;
if (editor->cursor >= editor->selection) { if (this->cursor >= this->selection) {
uint32_t prev_col; uint32_t prev_col;
switch (editor->selection_type) { switch (this->selection_type) {
case CHAR: case CHAR:
start = editor->selection; start = this->selection;
end = move_right(editor, editor->cursor, 1); end = this->move_right(this->cursor, 1);
break; break;
case WORD: case WORD:
word_boundaries(editor, editor->selection, &prev_col, nullptr, nullptr, this->word_boundaries(this->selection, &prev_col, nullptr, nullptr,
nullptr); nullptr);
start = {editor->selection.row, prev_col}; start = {this->selection.row, prev_col};
end = editor->cursor; end = this->cursor;
break; break;
case LINE: case LINE:
start = {editor->selection.row, 0}; start = {this->selection.row, 0};
end = editor->cursor; end = this->cursor;
break; break;
} }
} else { } else {
start = editor->cursor; start = this->cursor;
uint32_t next_col, line_len; uint32_t next_col, line_len;
switch (editor->selection_type) { switch (this->selection_type) {
case CHAR: case CHAR:
end = move_right(editor, editor->selection, 1); end = this->move_right(this->selection, 1);
break; break;
case WORD: case WORD:
word_boundaries(editor, editor->selection, nullptr, &next_col, nullptr, this->word_boundaries(this->selection, nullptr, &next_col, nullptr,
nullptr); nullptr);
end = {editor->selection.row, next_col}; end = {this->selection.row, next_col};
break; break;
case LINE: 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); char *line = next_line(it, &line_len);
if (!line) if (!line)
return nullptr; return nullptr;
if (line_len > 0 && line[line_len - 1] == '\n') if (line_len > 0 && line[line_len - 1] == '\n')
line_len--; line_len--;
end = {editor->selection.row, line_len}; end = {this->selection.row, line_len};
free(it->buffer); free(it->buffer);
free(it); free(it);
break; break;
@@ -102,9 +100,9 @@ char *get_selection(Editor *editor, uint32_t *out_len, Coord *out_start) {
if (out_start) if (out_start)
*out_start = start; *out_start = start;
uint32_t start_byte = uint32_t start_byte =
line_to_byte(editor->root, start.row, nullptr) + start.col; line_to_byte(this->root, start.row, nullptr) + start.col;
uint32_t end_byte = line_to_byte(editor->root, end.row, nullptr) + end.col; uint32_t end_byte = line_to_byte(this->root, end.row, nullptr) + end.col;
char *text = read(editor->root, start_byte, end_byte - start_byte); char *text = read(this->root, start_byte, end_byte - start_byte);
if (out_len) if (out_len)
*out_len = end_byte - start_byte; *out_len = end_byte - start_byte;
return text; return text;
+13 -15
View File
@@ -1,7 +1,6 @@
#include "editor/editor.h" #include "editor/editor.h"
void hover_diagnostic(Editor *editor) { void hover_diagnostic(Editor *editor) {
std::shared_lock lock(editor->v_mtx);
static uint32_t last_line = UINT32_MAX; static uint32_t last_line = UINT32_MAX;
if (last_line == editor->cursor.row && !editor->warnings_dirty) if (last_line == editor->cursor.row && !editor->warnings_dirty)
return; return;
@@ -15,23 +14,22 @@ void hover_diagnostic(Editor *editor) {
std::upper_bound(editor->warnings.begin(), editor->warnings.end(), dummy); std::upper_bound(editor->warnings.begin(), editor->warnings.end(), dummy);
std::vector<VWarn> warnings_at_line(first, last); std::vector<VWarn> warnings_at_line(first, last);
if (warnings_at_line.size() == 0) { if (warnings_at_line.size() == 0) {
editor->diagnostics_active = false; editor->diagnostic_popup->hidden = true;
return; return;
} }
editor->diagnostics.clear(); editor->diagnostic_popup->clear();
editor->diagnostics.warnings.swap(warnings_at_line); editor->diagnostic_popup->warnings.swap(warnings_at_line);
editor->diagnostics.render_first(); editor->diagnostic_popup->hidden = false;
editor->diagnostics_active = true;
} }
void editor_worker(Editor *editor) { void Editor::work() {
if (!editor || !editor->root) if (!this->root)
return; return;
if (editor->parser) if (this->parser)
editor->parser->work(); this->parser->work();
hover_diagnostic(editor); hover_diagnostic(this);
if (editor->completion.active && editor->completion.hover_dirty) { // if (this->completion.active && this->completion.hover_dirty) {
editor->completion.hover.render_first(); // this->completion.hover.render_first();
editor->completion.hover_dirty = false; // this->completion.hover_dirty = false;
} // }
} }
View File
@@ -1,12 +1,33 @@
#include "ui/diagnostics.h" #include "extentions/diagnostics.h"
void DiagnosticBox::clear() { DiagnosticBox *init_diagnostic() {
warnings.clear(); auto diagnostic = std::make_unique<DiagnosticBox>();
cells.clear(); diagnostic->pos = {0, 0};
size = {0, 0}; diagnostic->size = {1, 1};
}; diagnostic->hidden = true;
DiagnosticBox *ptr = diagnostic.get();
layout::popups.push_back(std::move(diagnostic));
return ptr;
}
void DiagnosticBox::render_first() { // TODO: Make this code more readable, It works fine tho.
void DiagnosticBox::render(std::vector<ScreenCell> &buffer, Coord n_size,
Coord n_pos) {
pos = n_pos;
size = n_size;
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 = {io::rows, io::cols};
if (start_col + size.col > screen_size.col) {
start_col = screen_size.col - size.col;
if (start_col < 0)
start_col = 0;
}
pos.col = start_col;
pos.row = start_row;
if (warnings.empty()) if (warnings.empty())
return; return;
uint32_t longest_line = 8 + warnings[0].source.length(); uint32_t longest_line = 8 + warnings[0].source.length();
@@ -30,22 +51,35 @@ void DiagnosticBox::render_first() {
} }
uint32_t content_width = MIN(longest_line, 150u); uint32_t content_width = MIN(longest_line, 150u);
size.col = content_width + 2; size.col = content_width + 2;
cells.assign(size.col * 25, {" ", 0, 0, 0, 0, 0}); auto set = [&](uint32_t r, uint32_t c, std::string text, uint32_t fg,
auto set = [&](uint32_t r, uint32_t c, const char *text, uint32_t fg, uint32_t bg, uint8_t flags, uint32_t width) {
uint32_t bg, uint8_t flags) { if (r < 0 || r >= size.row || c < 0 || c >= size.col)
cells[r * size.col + c] = {std::string(text), 0, fg, bg, flags, 0}; return;
r += pos.row;
c += pos.col;
if (r < 0 || r >= screen_size.row || c < 0 || c >= screen_size.col)
return;
ScreenCell &cell = buffer[r * screen_size.col + c];
cell.utf8 = std::move(text);
cell.width = width;
cell.fg = fg;
cell.bg = bg;
cell.flags = flags;
cell.ul_color = 0;
}; };
uint32_t base_bg = 0; uint32_t base_bg = 0;
uint32_t border_fg = 0x82AAFF; uint32_t border_fg = 0x82AAFF;
uint32_t r = 0; uint32_t r = 0;
if (warnings[0].source != "") { if (warnings[0].source != "") {
std::string src_txt = "Source: "; std::string src_txt = "Source: ";
for (uint32_t i = 0; i < src_txt.length() && i < content_width; i++) uint32_t i = 0;
set(1, i + 1, (char[2]){src_txt[i], 0}, 0x3EAAFF, base_bg, 0); for (; i < 8 && i < content_width; i++)
for (uint32_t i = 0; i < warnings[0].source.length() && i < content_width; set(1, i + 1, (char[2]){src_txt[i], 0}, 0x3EAAFF, base_bg, 0, 1);
i++) for (; i < 8 + warnings[0].source.length() && i < content_width; i++)
set(1, i + 1 + src_txt.length(), (char[2]){warnings[0].source[i], 0}, set(1, i + 1, (char[2]){warnings[0].source[i - 8], 0}, 0xffffff, base_bg,
0xffffff, base_bg, 0); 0, 1);
while (i < content_width)
set(1, ++i, " ", 0xffffff, base_bg, 0, 1);
r++; r++;
} }
int idx = 1; int idx = 1;
@@ -54,7 +88,7 @@ void DiagnosticBox::render_first() {
std::snprintf(buf, sizeof(buf), "%2d", idx % 100); std::snprintf(buf, sizeof(buf), "%2d", idx % 100);
std::string line_txt = std::string(buf) + ". "; std::string line_txt = std::string(buf) + ". ";
for (uint32_t i = 0; i < line_txt.length(); i++) 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); set(r + 1, i + 1, (char[2]){line_txt[i], 0}, 0xffffff, base_bg, 0, 1);
if (r >= 23) if (r >= 23)
break; break;
const char *err_sym = ""; const char *err_sym = "";
@@ -93,19 +127,26 @@ void DiagnosticBox::render_first() {
int width = display_width(cluster.c_str(), cluster_len); int width = display_width(cluster.c_str(), cluster_len);
if (c + width > content_width) if (c + width > content_width)
break; break;
set(r + 1, c + 1, cluster.c_str(), c_sym, base_bg, 0); set(r + 1, c + 1, cluster.c_str(), c_sym, base_bg, 0, width);
c += width; c += width;
i += cluster_len; i += cluster_len;
for (int w = 1; w < width; w++) for (int w = 1; w < width; w++)
set(r + 1, c - w + 1, "\x1b", c_sym, base_bg, 0); set(r + 1, c - w + 1, "\x1b", c_sym, base_bg, 0, 0);
} }
while (c < content_width)
set(r + 1, ++c, " ", 0xffffff, base_bg, 0, 1);
r++; r++;
} }
if (r >= 23) if (r >= 23)
break; break;
if (warn.code != "") { if (warn.code != "") {
for (uint32_t i = 0; i < warn.code.length() && i + 5 < content_width; i++) uint32_t i = 0;
set(r + 1, i + 5, (char[2]){warn.code[i], 0}, 0x81cdc6, base_bg, 0); for (; i < 5 && i < content_width; i++)
set(r + 1, i, " ", 0x81cdc6, base_bg, 0, 1);
for (; i < warn.code.length() + 5 && i < content_width; i++)
set(r + 1, i, (char[2]){warn.code[i - 5], 0}, 0x81cdc6, base_bg, 0, 1);
while (i <= content_width)
set(r + 1, i++, " ", 0x81cdc6, base_bg, 0, 1);
r++; r++;
} }
if (r >= 23) if (r >= 23)
@@ -113,14 +154,18 @@ void DiagnosticBox::render_first() {
for (std::string &see_also : warn.see_also) { for (std::string &see_also : warn.see_also) {
uint32_t fg = 0xB55EFF; uint32_t fg = 0xB55EFF;
uint8_t colon_count = 0; uint8_t colon_count = 0;
for (uint32_t i = 0; i < see_also.length() && i + 5 < content_width; uint32_t i = 0;
i++) { for (; i < 5 && i < content_width; i++)
set(r + 1, i + 5, (char[2]){see_also[i], 0}, fg, base_bg, 0); set(r + 1, i, " ", 0x81cdc6, base_bg, 0, 1);
if (see_also[i] == ':') for (; i < see_also.length() + 5 && i < content_width; i++) {
set(r + 1, i, (char[2]){see_also[i - 5], 0}, fg, base_bg, 0, 1);
if (see_also[i - 5] == ':')
colon_count++; colon_count++;
if (colon_count == 2) if (colon_count == 2)
fg = 0xFFFFFF; fg = 0xFFFFFF;
} }
while (i <= content_width)
set(r + 1, i++, " ", fg, base_bg, 0, 1);
r++; r++;
if (r >= 23) if (r >= 23)
break; break;
@@ -128,35 +173,16 @@ void DiagnosticBox::render_first() {
idx++; idx++;
} }
size.row = 2 + r; size.row = 2 + r;
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++) for (uint32_t i = 1; i < size.col - 1; i++)
set(0, i, "", border_fg, base_bg, 0); set(0, i, "", border_fg, base_bg, 0, 1);
set(0, size.col - 1, "", border_fg, base_bg, 0); set(0, size.col - 1, "", border_fg, base_bg, 0, 1);
for (uint32_t r = 1; r < size.row - 1; r++) { for (uint32_t r = 1; r < size.row - 1; r++) {
set(r, 0, "", border_fg, base_bg, 0); set(r, 0, "", border_fg, base_bg, 0, 1);
set(r, size.col - 1, "", border_fg, base_bg, 0); 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++) for (uint32_t i = 1; i < size.col - 1; i++)
set(size.row - 1, i, "", border_fg, base_bg, 0); set(size.row - 1, i, "", border_fg, base_bg, 0, 1);
set(size.row - 1, size.col - 1, "", border_fg, base_bg, 0); set(size.row - 1, size.col - 1, "", border_fg, base_bg, 0, 1);
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);
} }
+58 -44
View File
@@ -1,12 +1,16 @@
#include "ui/hover.h" #include "extentions/hover.h"
#include "io/sysio.h"
#include "syntax/decl.h" #include "syntax/decl.h"
#include "windows/decl.h"
void HoverBox::clear() { HoverBox *init_hover() {
text = ""; auto hover = std::make_unique<HoverBox>();
scroll_ = 0; hover->pos = {0, 0};
is_markup = false; hover->size = {1, 1};
size = {0, 0}; hover->hidden = true;
cells.clear(); HoverBox *ptr = hover.get();
layout::popups.push_back(std::move(hover));
return ptr;
} }
void HoverBox::scroll(int32_t number) { void HoverBox::scroll(int32_t number) {
@@ -19,13 +23,28 @@ void HoverBox::scroll(int32_t number) {
scroll_ = MAX((int32_t)scroll_ + number, 0); scroll_ = MAX((int32_t)scroll_ + number, 0);
if (scroll_ > line_count) if (scroll_ > line_count)
scroll_ = line_count; scroll_ = line_count;
render_first(true); scroll_dirty = true;
} }
void HoverBox::render_first(bool scroll) { void HoverBox::render(std::vector<ScreenCell> &buffer, Coord n_size,
if (!scroll) { Coord n_pos) {
pos = n_pos;
size = n_size;
if (scroll_dirty) {
// TODO: call syntax highlighter here // 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;
Coord screen_size = {io::rows, io::cols};
if (start_col + size.col > screen_size.col) {
start_col = screen_size.col - size.col;
if (start_col < 0)
start_col = 0;
}
pos.col = start_col;
pos.row = start_row;
uint32_t longest_line = 0; uint32_t longest_line = 0;
uint32_t current_width = 0; uint32_t current_width = 0;
for (size_t j = 0; j < text.length(); j++) { for (size_t j = 0; j < text.length(); j++) {
@@ -36,7 +55,7 @@ void HoverBox::render_first(bool scroll) {
current_width += 1; 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 // in the loop instead as it was never meant to wrap in the first place
longest_line = MAX(longest_line, current_width) + 1; longest_line = MAX(longest_line, current_width) + 1;
uint32_t content_width = MIN(longest_line, 130u); uint32_t content_width = MIN(longest_line, 130u);
@@ -50,10 +69,21 @@ void HoverBox::render_first(bool scroll) {
} }
uint32_t border_fg = 0x82AAFF; uint32_t border_fg = 0x82AAFF;
uint32_t base_bg = 0; 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, auto set = [&](uint32_t r, uint32_t c, const char *text, uint32_t fg,
uint32_t bg, uint8_t flags) { uint32_t bg, uint8_t flags, uint32_t width) {
cells[r * size.col + c] = {std::string(text), 0, fg, bg, flags, 0}; if (r < 0 || r >= size.row || c < 0 || c >= size.col)
return;
r += pos.row;
c += pos.col;
if (r < 0 || r >= screen_size.row || c < 0 || c >= screen_size.col)
return;
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; uint32_t r = 0;
while (i < text.length() && r < 24) { while (i < text.length() && r < 24) {
@@ -75,45 +105,29 @@ void HoverBox::render_first(bool scroll) {
uint32_t fg = hl ? hl->fg : 0xFFFFFF; uint32_t fg = hl ? hl->fg : 0xFFFFFF;
uint32_t bg = hl ? hl->bg : 0; uint32_t bg = hl ? hl->bg : 0;
uint32_t flags = hl ? hl->flags : 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; c += width;
i += cluster_len; i += cluster_len;
for (int w = 1; w < width; w++) 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);
} }
while (c < content_width)
set(r + 1, ++c, " ", 0xFFFFFF, base_bg, 0, 0);
r++; r++;
} }
if (!scroll) if (scroll_dirty)
size.row = r + 2; 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++) for (uint32_t i = 1; i < size.col - 1; i++)
set(0, i, "", border_fg, base_bg, 0); set(0, i, "", border_fg, base_bg, 0, 1);
set(0, size.col - 1, "", border_fg, base_bg, 0); set(0, size.col - 1, "", border_fg, base_bg, 0, 1);
for (uint32_t r = 1; r < size.row - 1; r++) { for (uint32_t r = 1; r < size.row - 1; r++) {
set(r, 0, "", border_fg, base_bg, 0); set(r, 0, "", border_fg, base_bg, 0, 1);
set(r, size.col - 1, "", border_fg, base_bg, 0); 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++) for (uint32_t i = 1; i < size.col - 1; i++)
set(size.row - 1, i, "", border_fg, base_bg, 0); set(size.row - 1, i, "", border_fg, base_bg, 0, 1);
set(size.row - 1, size.col - 1, "", border_fg, base_bg, 0); set(size.row - 1, size.col - 1, "", border_fg, base_bg, 0, 1);
cells.resize(size.col * size.row); scroll_dirty = false;
}
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);
} }
+19 -83
View File
@@ -1,16 +1,19 @@
#include "io/sysio.h" #include "io/sysio.h"
static uint32_t rows, cols; namespace io {
static bool show_cursor = 0; std::vector<ScreenCell> new_screen;
static std::vector<ScreenCell> screen; uint32_t rows, cols;
static std::vector<ScreenCell> old_screen; bool show_cursor = 0;
static std::mutex screen_mutex; std::vector<ScreenCell> old_screen;
static termios orig_termios; termios orig_termios;
} // namespace io
using namespace io;
void disable_raw_mode() { void disable_raw_mode() {
std::string os = "\x1b[?1049l\x1b[2 q\x1b[?1002l\x1b[?25h\x1b[?2004l"; std::string os = "\x1b[?1049l\x1b[2 q\x1b[?1002l\x1b[?25h\x1b[?2004l";
write(STDOUT_FILENO, os.c_str(), os.size()); write(STDOUT_FILENO, os.c_str(), os.size());
if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &orig_termios) == -1) { if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &io::orig_termios) == -1) {
perror("tcsetattr"); perror("tcsetattr");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
@@ -24,7 +27,7 @@ void enable_raw_mode() {
raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON); raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
raw.c_oflag &= ~(OPOST); raw.c_oflag &= ~(OPOST);
raw.c_cflag |= (CS8); 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[VMIN] = 0;
raw.c_cc[VTIME] = 0; raw.c_cc[VTIME] = 0;
if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw) == -1) if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw) == -1)
@@ -39,80 +42,14 @@ Coord start_screen() {
ioctl(0, TIOCGWINSZ, &w); ioctl(0, TIOCGWINSZ, &w);
rows = w.ws_row; rows = w.ws_row;
cols = w.ws_col; cols = w.ws_col;
screen.assign(rows * cols, {}); new_screen.assign(rows * cols, {});
old_screen.assign(rows * cols, {}); old_screen.assign(rows * cols, {});
return {rows, cols}; return {rows, cols};
} }
void end_screen() { disable_raw_mode(); } void end_screen() { disable_raw_mode(); }
Coord get_size() { return {rows, cols}; } void io_render() {
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() {
static bool first_render = true; static bool first_render = true;
uint32_t current_fg = 0; uint32_t current_fg = 0;
uint32_t current_bg = 0; uint32_t current_bg = 0;
@@ -121,7 +58,6 @@ void render() {
bool current_bold = false; bool current_bold = false;
bool current_strikethrough = false; bool current_strikethrough = false;
bool current_underline = false; bool current_underline = false;
std::lock_guard<std::mutex> lock(screen_mutex);
std::string out; std::string out;
out.reserve(static_cast<size_t>(rows) * static_cast<size_t>(cols) * 4 + 256); out.reserve(static_cast<size_t>(rows) * static_cast<size_t>(cols) * 4 + 256);
out += "\x1b[s\x1b[?25l"; out += "\x1b[s\x1b[?25l";
@@ -135,7 +71,7 @@ void render() {
for (uint32_t col = 0; col < cols; ++col) { for (uint32_t col = 0; col < cols; ++col) {
uint32_t idx = row * cols + col; uint32_t idx = row * cols + col;
ScreenCell &old_cell = old_screen[idx]; ScreenCell &old_cell = old_screen[idx];
ScreenCell &new_cell = screen[idx]; ScreenCell &new_cell = new_screen[idx];
bool content_changed = bool content_changed =
old_cell.utf8 != new_cell.utf8 || old_cell.fg != new_cell.fg || old_cell.utf8 != new_cell.utf8 || old_cell.fg != new_cell.fg ||
old_cell.bg != new_cell.bg || old_cell.flags != new_cell.flags || old_cell.bg != new_cell.bg || old_cell.flags != new_cell.flags ||
@@ -147,7 +83,7 @@ void render() {
for (int64_t back = 1; back <= 4 && first_change_col - back >= 0; for (int64_t back = 1; back <= 4 && first_change_col - back >= 0;
++back) { ++back) {
ScreenCell &prev_cell = ScreenCell &prev_cell =
screen[row * cols + (first_change_col - back)]; new_screen[row * cols + (first_change_col - back)];
if (prev_cell.width > 1) { if (prev_cell.width > 1) {
first_change_col -= back; first_change_col -= back;
break; break;
@@ -165,7 +101,7 @@ void render() {
for (uint32_t col = first_change_col; col <= last_change_col; ++col) { for (uint32_t col = first_change_col; col <= last_change_col; ++col) {
uint32_t idx = row * cols + col; uint32_t idx = row * cols + col;
ScreenCell &old_cell = old_screen[idx]; 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; uint32_t width = new_cell.width > 0 ? new_cell.width : 1;
bool overlap = false; bool overlap = false;
if (width > 1) { if (width > 1) {
@@ -173,7 +109,7 @@ void render() {
uint32_t next_col = col + i; uint32_t next_col = col + i;
if (next_col >= cols) if (next_col >= cols)
break; break;
const ScreenCell &next = screen[row * cols + next_col]; const ScreenCell &next = new_screen[row * cols + next_col];
if (!is_empty_cell(next)) { if (!is_empty_cell(next)) {
overlap = true; overlap = true;
break; break;
@@ -244,7 +180,7 @@ void render() {
uint32_t next_col = col + i; uint32_t next_col = col + i;
if (next_col >= cols) if (next_col >= cols)
break; break;
const ScreenCell &next = screen[row * cols + next_col]; const ScreenCell &next = new_screen[row * cols + next_col];
if (!is_empty_cell(next)) if (!is_empty_cell(next))
break; break;
out.push_back(' '); out.push_back(' ');
@@ -260,7 +196,7 @@ void render() {
ScreenCell *cell = &new_cell; ScreenCell *cell = &new_cell;
int back = 0; int back = 0;
while ((int)col - back >= 0 && cell->utf8[0] == '\x1b') 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) if (width >= cell->width)
out.append(" "); out.append(" ");
} }
-87
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());
}
}
}
-242
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);
}
+49
View File
@@ -0,0 +1,49 @@
#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;
Queue<std::unique_ptr<LSPMessage>> response_queue;
}
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_id.empty()) {
it = lsp::new_editors.erase(it);
continue;
}
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;
}
}
-170
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();
}
}
}
}
+49 -67
View File
@@ -5,36 +5,19 @@
#include "ruby/decl.h" #include "ruby/decl.h"
#include "ui/bar.h" #include "ui/bar.h"
#include "utils/utils.h" #include "utils/utils.h"
#include "windows/decl.h"
std::atomic<bool> running{true}; std::atomic<bool> running{true};
Queue<KeyEvent> event_queue;
std::vector<Editor *> editors;
Bar bar;
uint8_t current_editor = 0;
std::atomic<uint8_t> mode = NORMAL; std::atomic<uint8_t> mode = NORMAL;
fs::path pwd;
namespace ui {
Bar bar;
} // namespace ui
void background_lsp() { void background_lsp() {
while (running) while (running)
throttle(8ms, lsp_worker); throttle(16ms, 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[]) { int main(int argc, char *argv[]) {
@@ -46,64 +29,63 @@ int main(int argc, char *argv[]) {
Coord screen = start_screen(); Coord screen = start_screen();
const char *filename = (argc > 1) ? argv[1] : ""; const char *filename = (argc > 1) ? argv[1] : "";
uint8_t eol = read_line_endings(); uint8_t eol = read_line_endings();
Editor *editor =
new_editor(filename, {0, 0}, {screen.row - 2, screen.col}, eol);
bar.init(screen);
if (!editor) { layout::root_tile.pos = {0, 0};
end_screen(); layout::root_tile.size = {screen.row - 2, screen.col - 1};
fprintf(stderr, "Failed to load editor\n"); auto editor = std::make_unique<Editor>(filename, eol);
return 1; layout::focused_window = editor.get();
} auto block = std::make_unique<TileBlock>();
block->tiles.push_back(std::move(editor));
layout::root_tile.tile = std::move(block);
editors.push_back(editor); ui::bar.init(screen);
current_editor = editors.size() - 1;
std::thread lsp_thread(background_lsp); std::thread lsp_thread(background_lsp);
while (running) { while (running) {
KeyEvent event = throttle(1ms, read_key); layout::focused_window->work();
if (event.key_type == KEY_NONE) render();
goto render; throttle(16ms, io_render);
if (event.key_type == KEY_CHAR && event.len == 1 && KeyEvent event;
event.c[0] == CTRL('q')) { while (running && lsp::response_queue.empty() &&
free(event.c); (event = read_key()).key_type == KEY_NONE)
running = false; std::this_thread::sleep_for(1ms);
break; uint8_t consumed = 0;
} do {
if (mode != RUNNER) { if (event.key_type != KEY_NONE) {
if (event.key_type == KEY_MOUSE) { if (event.key_type == KEY_CHAR && event.len == 1 &&
Editor *target = editor_at(event.mouse_x, event.mouse_y); event.c[0] == CTRL('q')) {
if (target) { free(event.c);
if (event.mouse_state == PRESS) running = false;
current_editor = index_of(target); goto quit;
event.mouse_x -= target->position.col;
event.mouse_y -= target->position.row;
handle_editor_event(target, event);
} }
} else { if (mode != RUNNER) {
handle_editor_event(editors[current_editor], event); if (event.key_type == KEY_MOUSE) {
handle_click(event);
} else {
layout::focused_window->handle_event(event);
}
} else {
ui::bar.handle_event(event);
}
if ((event.key_type == KEY_CHAR || event.key_type == KEY_PASTE) &&
event.c)
free(event.c);
} }
} else { } while (++consumed < 32 && (event = read_key()).key_type != KEY_NONE);
bar.handle(event); std::unique_ptr<LSPMessage> msg;
} while (lsp::response_queue.pop(msg)) {
if ((event.key_type == KEY_CHAR || event.key_type == KEY_PASTE) && event.c) msg->callback(*msg);
free(event.c); };
render:
throttle(4ms, editor_worker, editors[current_editor]);
bar.render();
render_editor(editors[current_editor]);
throttle(4ms, render);
} }
quit:
if (lsp_thread.joinable()) if (lsp_thread.joinable())
lsp_thread.join(); lsp_thread.join();
end_screen(); end_screen();
for (auto editor : editors)
free_editor(editor);
ruby_shutdown(); ruby_shutdown();
return 0; return 0;
+1
View File
@@ -1,6 +1,7 @@
#include "ruby/decl.h" #include "ruby/decl.h"
mrb_value get_config_file(mrb_state *mrb, mrb_value self) { mrb_value get_config_file(mrb_state *mrb, mrb_value self) {
UNUSED(self);
return mrb_str_new_cstr(mrb, ruby_config_path.string().c_str()); return mrb_str_new_cstr(mrb, ruby_config_path.string().c_str());
} }
+19 -20
View File
@@ -89,8 +89,8 @@ static mrb_value sym_flags;
static mrb_value sym_start; static mrb_value sym_start;
static mrb_value sym_length; static mrb_value sym_length;
static mrb_value sym_mode; static mrb_value sym_mode;
static mrb_value sym_lang_name; static mrb_value sym_data;
static mrb_value sym_filename; static mrb_value sym_foldername;
static mrb_value sym_width; static mrb_value sym_width;
static mrb_value sym_normal; static mrb_value sym_normal;
static mrb_value sym_insert; static mrb_value sym_insert;
@@ -105,8 +105,8 @@ inline void initialize_symbols() {
sym_start = mrb_symbol_value(mrb_intern_cstr(mrb, "start")); sym_start = mrb_symbol_value(mrb_intern_cstr(mrb, "start"));
sym_length = mrb_symbol_value(mrb_intern_cstr(mrb, "length")); sym_length = mrb_symbol_value(mrb_intern_cstr(mrb, "length"));
sym_mode = mrb_symbol_value(mrb_intern_cstr(mrb, "mode")); sym_mode = mrb_symbol_value(mrb_intern_cstr(mrb, "mode"));
sym_lang_name = mrb_symbol_value(mrb_intern_cstr(mrb, "lang_name")); sym_data = mrb_symbol_value(mrb_intern_cstr(mrb, "data"));
sym_filename = mrb_symbol_value(mrb_intern_cstr(mrb, "filename")); sym_foldername = mrb_symbol_value(mrb_intern_cstr(mrb, "foldername"));
sym_width = mrb_symbol_value(mrb_intern_cstr(mrb, "width")); sym_width = mrb_symbol_value(mrb_intern_cstr(mrb, "width"));
sym_normal = mrb_symbol_value(mrb_intern_cstr(mrb, "normal")); sym_normal = mrb_symbol_value(mrb_intern_cstr(mrb, "normal"));
sym_insert = mrb_symbol_value(mrb_intern_cstr(mrb, "insert")); 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; return result;
} }
BarLine bar_contents(uint8_t mode, std::string lang_name, uint32_t warnings, BarLine bar_contents(uint8_t mode, uint32_t width, std::string foldername,
std::string lsp_name, std::string filename, Window *window) {
std::string foldername, uint32_t line, uint32_t max_line,
uint32_t width) {
BarLine bar_line; BarLine bar_line;
static bool initialed = false; static bool initialed = false;
if (!initialed) { 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); int ai = mrb_gc_arena_save(mrb);
mrb_value info = mrb_hash_new(mrb); mrb_value info = mrb_hash_new(mrb);
mrb_value key_mode = sym_mode;
mrb_value val_mode; mrb_value val_mode;
switch (mode) { switch (mode) {
case NORMAL: case NORMAL:
@@ -177,18 +174,20 @@ BarLine bar_contents(uint8_t mode, std::string lang_name, uint32_t warnings,
val_mode = sym_jumper; val_mode = sym_jumper;
break; break;
} }
mrb_hash_set(mrb, info, key_mode, val_mode); mrb_hash_set(mrb, info, sym_mode, val_mode);
mrb_value key_lang_name = sym_lang_name; mrb_value val_foldername =
mrb_value val_lang_name = mrb_str_new(mrb, foldername.c_str(), foldername.length());
mrb_symbol_value(mrb_intern_cstr(mrb, lang_name.c_str())); mrb_hash_set(mrb, info, sym_foldername, val_foldername);
mrb_hash_set(mrb, info, key_lang_name, val_lang_name); std::array<std::string, 5> arr = window->bar_info();
mrb_value key_filename = sym_filename; mrb_value ary = mrb_ary_new(mrb);
mrb_value val_filename = mrb_ary_push(mrb, ary, mrb_str_new(mrb, arr[0].c_str(), arr[0].length()));
mrb_str_new(mrb, filename.c_str(), filename.length()); mrb_ary_push(mrb, ary, mrb_str_new(mrb, arr[1].c_str(), arr[1].length()));
mrb_hash_set(mrb, info, key_filename, val_filename); mrb_ary_push(mrb, ary, mrb_str_new(mrb, arr[2].c_str(), arr[2].length()));
mrb_value key_width = sym_width; 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_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 mod_val = mrb_obj_value(C_module);
mrb_value block = mrb_funcall(mrb, mod_val, "b_bar", 0); mrb_value block = mrb_funcall(mrb, mod_val, "b_bar", 0);
mrb_value val_line = mrb_funcall(mrb, block, "call", 1, info); mrb_value val_line = mrb_funcall(mrb, block, "call", 1, info);
Executable → Regular
View File
+17 -11
View File
@@ -10,7 +10,7 @@ struct BashFullState {
bool line_cont = false; bool line_cont = false;
struct Lit { struct Lit {
std::string delim = ""; // Only 1 wide for strings std::string delim = "";
bool allow_interp = false; bool allow_interp = false;
bool operator==(const BashFullState::Lit &other) const { bool operator==(const BashFullState::Lit &other) const {
@@ -24,7 +24,7 @@ struct BashFullState {
} }
}; };
struct BashState { struct BashState : StateBase {
using full_state_type = BashFullState; using full_state_type = BashFullState;
int interp_level = 0; int interp_level = 0;
@@ -37,26 +37,32 @@ struct BashState {
((full_state && other.full_state && ((full_state && other.full_state &&
*full_state == *other.full_state)); *full_state == *other.full_state));
} }
std::unique_ptr<StateBase> clone() const override {
return std::make_unique<BashState>(*this);
}
}; };
bool bash_state_match(std::shared_ptr<void> state_1, bool bash_state_match(StateBase *state_1, StateBase *state_2) {
std::shared_ptr<void> state_2) {
if (!state_1 || !state_2) if (!state_1 || !state_2)
return false; return false;
return *std::static_pointer_cast<BashState>(state_1) == return *static_cast<BashState *>(state_1) ==
*std::static_pointer_cast<BashState>(state_2); *static_cast<BashState *>(state_2);
} }
std::shared_ptr<void> bash_parse(std::vector<Token> *tokens, std::unique_ptr<StateBase> bash_parse(std::vector<Token> *tokens,
std::shared_ptr<void> in_state, StateBase *in_state, const char *text,
const char *text, uint32_t len, uint32_t len, uint32_t line_num) {
uint32_t line_num) {
static bool keywords_trie_init = false; static bool keywords_trie_init = false;
if (!keywords_trie_init) { if (!keywords_trie_init) {
keywords_trie_init = true; keywords_trie_init = true;
} }
tokens->clear(); tokens->clear();
auto state = ensure_state(std::static_pointer_cast<BashState>(in_state)); std::unique_ptr<BashState> state;
if (in_state)
state = static_unique_ptr_cast<BashState>(in_state->clone());
else
state = std::make_unique<BashState>();
uint32_t i = 0; uint32_t i = 0;
while (len > 0 && (text[len - 1] == '\n' || text[len - 1] == '\r' || while (len > 0 && (text[len - 1] == '\n' || text[len - 1] == '\r' ||
text[len - 1] == '\t' || text[len - 1] == ' ')) text[len - 1] == '\t' || text[len - 1] == ' '))
+62 -61
View File
@@ -26,55 +26,55 @@ Parser::Parser(Editor *n_editor, std::string n_lang, uint32_t n_scroll_max) {
assert("unknown lang should be checked by caller" && 0); assert("unknown lang should be checked by caller" && 0);
} }
} }
edit(0, 0, editor->root->line_count + 1); line_map.apply_edit(0, editor->root->line_count + 1);
} }
void Parser::edit(uint32_t start_line, uint32_t removed_rows, void Parser::edit(uint32_t start_line, uint32_t removed_rows,
uint32_t inserted_rows) { uint32_t inserted_rows) {
int64_t delta = (int64_t)inserted_rows - (int64_t)removed_rows; int64_t delta = (int64_t)inserted_rows - (int64_t)removed_rows;
if (delta < 0) line_map.apply_edit(start_line, delta);
line_tree.erase(start_line, (uint32_t)(-delta));
else if (delta > 0)
line_tree.insert(start_line, (uint32_t)delta);
uint32_t span = MAX(removed_rows, inserted_rows); uint32_t span = MAX(removed_rows, inserted_rows);
uint32_t begin = (start_line > 0) ? start_line - 1 : 0; uint32_t begin = (start_line > 0) ? start_line - 1 : 0;
uint32_t end = start_line + span; uint32_t end = start_line + span;
for (uint32_t line = begin; line <= end + 1; ++line) for (uint32_t line = begin; line <= end + 1; ++line)
dirty_lines.push(line); if (LineData *ld = line_map.at(line))
ld->out_state = nullptr;
} }
void Parser::work() { void Parser::work() {
if (!editor || !editor->root) if (!editor || !editor->root)
return; return;
std::vector<uint32_t> batch; std::vector<uint32_t> batch;
uint32_t c_line; uint32_t line_count = editor->root->line_count + 1;
while (dirty_lines.pop(c_line)) uint32_t min_line =
batch.push_back(c_line); scroll_max > MAX_LINES_LOOKAROUND ? scroll_max - MAX_LINES_LOOKAROUND : 0;
uint32_t i = MAX(0, (int64_t)scroll_max - 60); uint32_t max_line = MIN(scroll_max + MAX_LINES_LOOKAROUND, line_count - 1);
LineData *l_iter = line_tree.start_iter(i); bool sequential = false;
while (l_iter && i < scroll_max + 10) { for (uint32_t i = min_line; i <= max_line; ++i) {
if (!l_iter->out_state) LineData *ld = line_map.at(i);
if ((!ld || !ld->in_state || !ld->out_state) && !sequential) {
batch.push_back(i); batch.push_back(i);
i++; sequential = true;
l_iter = line_tree.next(); continue;
}
sequential = false;
} }
line_tree.end_iter();
for (uint32_t c_line : batch) { for (uint32_t c_line : batch) {
if (!running.load(std::memory_order_relaxed)) if (!running.load(std::memory_order_relaxed))
break; break;
uint32_t min_line = scroll_max > 60 ? scroll_max - 60 : 0; uint32_t min_line = scroll_max > MAX_LINES_LOOKAROUND
uint32_t max_line = scroll_max + 10; ? scroll_max - MAX_LINES_LOOKAROUND
if (c_line < min_line || c_line > max_line) { : 0;
dirty_lines.push(c_line); uint32_t max_line = scroll_max + MAX_LINES_LOOKAROUND;
if (c_line < min_line || c_line > max_line)
continue; continue;
}
uint32_t scroll_snapshot = scroll_max; uint32_t scroll_snapshot = scroll_max;
std::shared_ptr<void> prev_state = nullptr; std::unique_ptr<StateBase> prev_state = nullptr;
uint32_t line_count; if (c_line > 0 && c_line < line_count) {
line_count = line_tree.count(); auto lm = line_map.at(c_line - 1);
if (c_line > 0 && c_line < line_count) if (lm && lm->out_state)
prev_state = line_tree.at(c_line - 1)->out_state; prev_state = lm->out_state ? lm->out_state->clone() : nullptr;
std::shared_lock k_lock(editor->knot_mtx); }
LineIterator *it = begin_l_iter(editor->root, c_line); LineIterator *it = begin_l_iter(editor->root, c_line);
if (!it) if (!it)
continue; continue;
@@ -82,60 +82,61 @@ void Parser::work() {
while (cur_line < line_count) { while (cur_line < line_count) {
if (!running.load(std::memory_order_relaxed)) if (!running.load(std::memory_order_relaxed))
break; break;
if (scroll_snapshot != scroll_max) { if (scroll_snapshot != scroll_max)
LineData *line_data = line_tree.at(cur_line);
if (line_data && !line_data->out_state)
dirty_lines.push(cur_line);
break; break;
}
if (cur_line < min_line || cur_line > max_line) { if (cur_line < min_line || cur_line > max_line) {
dirty_lines.push(cur_line); LineData *line_data = line_map.at(cur_line);
if (line_data)
line_data->out_state = nullptr;
break; break;
} }
uint32_t len; uint32_t len;
char *line = next_line(it, &len); char *line = next_line(it, &len);
if (!line) if (!line)
break; break;
LineData *line_data = line_tree.at(cur_line); LineData *line_data = line_map.create_at(cur_line);
if (!line_data) { std::unique_ptr<StateBase> new_state;
cur_line++;
continue;
}
std::shared_ptr<void> new_state;
if (is_custom) { if (is_custom) {
std::string state = ""; std::string prev_value = "";
if (prev_state) if (prev_state) {
state = std::static_pointer_cast<std::string>(prev_state)->c_str(); CustomState *prev_custom =
std::string out_state = parse_custom(&line_data->tokens, parser_block, static_cast<CustomState *>(prev_state.get());
line, len, state, cur_line); prev_value = prev_custom->value;
new_state = std::make_shared<std::string>(out_state); }
std::string out_value = parse_custom(&line_data->tokens, parser_block,
line, len, prev_value, cur_line);
new_state = std::make_unique<CustomState>(out_value);
} else { } else {
new_state = new_state = parse_func(&line_data->tokens, prev_state.get(), line, len,
parse_func(&line_data->tokens, prev_state, line, len, cur_line); cur_line);
} }
line_data->in_state = prev_state; line_data->in_state = std::move(prev_state);
line_data->out_state = new_state; line_data->out_state = std::move(new_state);
bool done = false; bool done = false;
if (cur_line + 1 < line_count) { if (cur_line + 1 < line_count) {
LineData *next_line_data = line_tree.at(cur_line + 1); LineData *next_line_data = line_map.at(cur_line + 1);
if (next_line_data) { if (next_line_data) {
if (is_custom) { if (is_custom) {
std::string a = std::string a = "";
prev_state if (new_state) {
? std::static_pointer_cast<std::string>(new_state)->c_str() CustomState *cs = static_cast<CustomState *>(new_state.get());
: ""; a = cs->value;
std::string b = next_line_data->in_state }
? std::static_pointer_cast<std::string>( std::string b = "";
next_line_data->in_state) if (next_line_data->in_state) {
->c_str() CustomState *cs =
: ""; static_cast<CustomState *>(next_line_data->in_state.get());
b = cs->value;
}
done = custom_compare(match_block, a, b); done = custom_compare(match_block, a, b);
} else { } else {
done = state_match_func(new_state, next_line_data->in_state); done = state_match_func(new_state.get(),
next_line_data->in_state.get());
} }
} }
} }
prev_state = new_state; prev_state =
line_data->out_state ? line_data->out_state->clone() : nullptr;
cur_line++; cur_line++;
if (done) if (done)
break; break;
+122 -118
View File
@@ -241,20 +241,22 @@ struct RubyFullState {
} }
}; };
struct RubyState { struct RubyState : StateBase {
using full_state_type = RubyFullState; using full_state_type = RubyFullState;
int interp_level = 0; int interp_level = 0;
std::stack<std::shared_ptr<RubyFullState>> interp_stack; std::stack<RubyFullState> interp_stack;
std::shared_ptr<RubyFullState> full_state; RubyFullState full_state;
std::deque<HeredocInfo> heredocs; std::deque<HeredocInfo> heredocs;
bool operator==(const RubyState &other) const { bool operator==(const RubyState &other) const {
return interp_level == other.interp_level && return interp_level == other.interp_level &&
interp_stack == other.interp_stack && interp_stack == other.interp_stack &&
((full_state && other.full_state && full_state == other.full_state && heredocs == other.heredocs;
*full_state == *other.full_state)) && }
heredocs == other.heredocs;
std::unique_ptr<StateBase> clone() const override {
return std::make_unique<RubyState>(*this);
} }
}; };
@@ -278,18 +280,16 @@ inline static uint32_t get_next_word(const char *text, uint32_t i,
return width; return width;
} }
bool ruby_state_match(std::shared_ptr<void> state_1, bool ruby_state_match(StateBase *state_1, StateBase *state_2) {
std::shared_ptr<void> state_2) {
if (!state_1 || !state_2) if (!state_1 || !state_2)
return false; return false;
return *std::static_pointer_cast<RubyState>(state_1) == return *static_cast<RubyState *>(state_1) ==
*std::static_pointer_cast<RubyState>(state_2); *static_cast<RubyState *>(state_2);
} }
std::shared_ptr<void> ruby_parse(std::vector<Token> *tokens, std::unique_ptr<StateBase> ruby_parse(std::vector<Token> *tokens,
std::shared_ptr<void> in_state, StateBase *in_state, const char *text,
const char *text, uint32_t len, uint32_t len, uint32_t line_num) {
uint32_t line_num) {
static bool keywords_trie_init = false; static bool keywords_trie_init = false;
static Trie<void> base_keywords_trie; static Trie<void> base_keywords_trie;
static Trie<void> expecting_keywords_trie; static Trie<void> expecting_keywords_trie;
@@ -313,7 +313,11 @@ std::shared_ptr<void> ruby_parse(std::vector<Token> *tokens,
keywords_trie_init = true; keywords_trie_init = true;
} }
tokens->clear(); tokens->clear();
auto state = ensure_state(std::static_pointer_cast<RubyState>(in_state)); std::unique_ptr<RubyState> state;
if (in_state)
state = static_unique_ptr_cast<RubyState>(in_state->clone());
else
state = std::make_unique<RubyState>();
uint32_t i = 0; uint32_t i = 0;
while (len > 0 && (text[len - 1] == '\n' || text[len - 1] == '\r' || while (len > 0 && (text[len - 1] == '\n' || text[len - 1] == '\r' ||
text[len - 1] == '\t' || text[len - 1] == ' ')) text[len - 1] == '\t' || text[len - 1] == ' '))
@@ -322,18 +326,18 @@ std::shared_ptr<void> ruby_parse(std::vector<Token> *tokens,
return state; return state;
bool heredoc_first = false; bool heredoc_first = false;
while (i < len) { while (i < len) {
if (state->full_state->in_state == RubyFullState::END) if (state->full_state.in_state == RubyFullState::END)
return state; return state;
if (state->full_state->in_state == RubyFullState::COMMENT) { if (state->full_state.in_state == RubyFullState::COMMENT) {
tokens->push_back({i, len, TokenKind::K_COMMENT}); tokens->push_back({i, len, TokenKind::K_COMMENT});
if (i == 0 && len == 4 && text[i] == '=' && text[i + 1] == 'e' && if (i == 0 && len == 4 && text[i] == '=' && text[i + 1] == 'e' &&
text[i + 2] == 'n' && text[i + 3] == 'd') { text[i + 2] == 'n' && text[i + 3] == 'd') {
state->full_state->in_state = RubyFullState::NONE; state->full_state.in_state = RubyFullState::NONE;
} }
return state; return state;
} }
if (!heredoc_first && if (!heredoc_first &&
state->full_state->in_state == RubyFullState::HEREDOC) { state->full_state.in_state == RubyFullState::HEREDOC) {
if (i == 0) { if (i == 0) {
uint32_t start = 0; uint32_t start = 0;
if (state->heredocs.front().allow_indentation) if (state->heredocs.front().allow_indentation)
@@ -344,7 +348,7 @@ std::shared_ptr<void> ruby_parse(std::vector<Token> *tokens,
state->heredocs.front().delim.length())) { state->heredocs.front().delim.length())) {
state->heredocs.pop_front(); state->heredocs.pop_front();
if (state->heredocs.empty()) if (state->heredocs.empty())
state->full_state->in_state = RubyFullState::NONE; state->full_state.in_state = RubyFullState::NONE;
tokens->push_back({i, len, TokenKind::K_ANNOTATION}); tokens->push_back({i, len, TokenKind::K_ANNOTATION});
return state; return state;
} }
@@ -421,7 +425,7 @@ std::shared_ptr<void> ruby_parse(std::vector<Token> *tokens,
tokens->push_back({i, i + 2, TokenKind::K_INTERPOLATION}); tokens->push_back({i, i + 2, TokenKind::K_INTERPOLATION});
i += 2; i += 2;
state->interp_stack.push(state->full_state); state->interp_stack.push(state->full_state);
state->full_state = std::make_shared<RubyFullState>(); state->full_state = RubyFullState();
state->interp_level = 1; state->interp_level = 1;
break; break;
} }
@@ -432,7 +436,7 @@ std::shared_ptr<void> ruby_parse(std::vector<Token> *tokens,
continue; continue;
} }
} }
if (state->full_state->in_state == RubyFullState::STRING) { if (state->full_state.in_state == RubyFullState::STRING) {
uint32_t start = i; uint32_t start = i;
while (i < len) { while (i < len) {
if (text[i] == '\\') { if (text[i] == '\\') {
@@ -496,36 +500,36 @@ std::shared_ptr<void> ruby_parse(std::vector<Token> *tokens,
tokens->push_back({start, i, TokenKind::K_ESCAPE}); tokens->push_back({start, i, TokenKind::K_ESCAPE});
continue; continue;
} }
if (state->full_state->lit.allow_interp && text[i] == '#' && if (state->full_state.lit.allow_interp && text[i] == '#' &&
i + 1 < len && text[i + 1] == '{') { i + 1 < len && text[i + 1] == '{') {
tokens->push_back({start, i, TokenKind::K_STRING}); tokens->push_back({start, i, TokenKind::K_STRING});
tokens->push_back({i, i + 2, TokenKind::K_INTERPOLATION}); tokens->push_back({i, i + 2, TokenKind::K_INTERPOLATION});
i += 2; i += 2;
state->interp_stack.push(state->full_state); state->interp_stack.push(state->full_state);
state->full_state = std::make_shared<RubyFullState>(); state->full_state = RubyFullState();
state->interp_level = 1; state->interp_level = 1;
break; break;
} }
if (text[i] == state->full_state->lit.delim_start && if (text[i] == state->full_state.lit.delim_start &&
state->full_state->lit.delim_start != state->full_state.lit.delim_start !=
state->full_state->lit.delim_end) { state->full_state.lit.delim_end) {
state->full_state->lit.brace_level++; state->full_state.lit.brace_level++;
} }
if (text[i] == state->full_state->lit.delim_end) { if (text[i] == state->full_state.lit.delim_end) {
if (state->full_state->lit.delim_start == if (state->full_state.lit.delim_start ==
state->full_state->lit.delim_end) { state->full_state.lit.delim_end) {
i++; i++;
tokens->push_back({start, i, TokenKind::K_STRING}); tokens->push_back({start, i, TokenKind::K_STRING});
state->full_state->in_state = RubyFullState::NONE; state->full_state.in_state = RubyFullState::NONE;
state->full_state->expecting_expr = false; state->full_state.expecting_expr = false;
break; break;
} else { } else {
state->full_state->lit.brace_level--; state->full_state.lit.brace_level--;
if (state->full_state->lit.brace_level == 0) { if (state->full_state.lit.brace_level == 0) {
i++; i++;
tokens->push_back({start, i, TokenKind::K_STRING}); tokens->push_back({start, i, TokenKind::K_STRING});
state->full_state->in_state = RubyFullState::NONE; state->full_state.in_state = RubyFullState::NONE;
state->full_state->expecting_expr = false; state->full_state.expecting_expr = false;
break; break;
} }
} }
@@ -536,7 +540,7 @@ std::shared_ptr<void> ruby_parse(std::vector<Token> *tokens,
tokens->push_back({start, len, TokenKind::K_STRING}); tokens->push_back({start, len, TokenKind::K_STRING});
continue; continue;
} }
if (state->full_state->in_state == RubyFullState::REGEXP) { if (state->full_state.in_state == RubyFullState::REGEXP) {
uint32_t start = i; uint32_t start = i;
while (i < len) { while (i < len) {
if (text[i] == '\\') { if (text[i] == '\\') {
@@ -605,30 +609,30 @@ std::shared_ptr<void> ruby_parse(std::vector<Token> *tokens,
tokens->push_back({i, i + 2, TokenKind::K_INTERPOLATION}); tokens->push_back({i, i + 2, TokenKind::K_INTERPOLATION});
i += 2; i += 2;
state->interp_stack.push(state->full_state); state->interp_stack.push(state->full_state);
state->full_state = std::make_shared<RubyFullState>(); state->full_state = RubyFullState();
state->interp_level = 1; state->interp_level = 1;
break; break;
} }
if (text[i] == state->full_state->lit.delim_start && if (text[i] == state->full_state.lit.delim_start &&
state->full_state->lit.delim_start != state->full_state.lit.delim_start !=
state->full_state->lit.delim_end) { state->full_state.lit.delim_end) {
state->full_state->lit.brace_level++; state->full_state.lit.brace_level++;
} }
if (text[i] == state->full_state->lit.delim_end) { if (text[i] == state->full_state.lit.delim_end) {
if (state->full_state->lit.delim_start == if (state->full_state.lit.delim_start ==
state->full_state->lit.delim_end) { state->full_state.lit.delim_end) {
i += 1; i += 1;
tokens->push_back({start, i, TokenKind::K_REGEXP}); tokens->push_back({start, i, TokenKind::K_REGEXP});
state->full_state->in_state = RubyFullState::NONE; state->full_state.in_state = RubyFullState::NONE;
state->full_state->expecting_expr = false; state->full_state.expecting_expr = false;
break; break;
} else { } else {
state->full_state->lit.brace_level--; state->full_state.lit.brace_level--;
if (state->full_state->lit.brace_level == 0) { if (state->full_state.lit.brace_level == 0) {
i += 1; i += 1;
tokens->push_back({start, i, TokenKind::K_REGEXP}); tokens->push_back({start, i, TokenKind::K_REGEXP});
state->full_state->in_state = RubyFullState::NONE; state->full_state.in_state = RubyFullState::NONE;
state->full_state->expecting_expr = false; state->full_state.expecting_expr = false;
break; break;
} }
} }
@@ -642,8 +646,8 @@ std::shared_ptr<void> ruby_parse(std::vector<Token> *tokens,
if (i == 0 && len == 6) { if (i == 0 && len == 6) {
if (text[i] == '=' && text[i + 1] == 'b' && text[i + 2] == 'e' && if (text[i] == '=' && text[i + 1] == 'b' && text[i + 2] == 'e' &&
text[i + 3] == 'g' && text[i + 4] == 'i' && text[i + 5] == 'n') { text[i + 3] == 'g' && text[i + 4] == 'i' && text[i + 5] == 'n') {
state->full_state->in_state = RubyFullState::COMMENT; state->full_state.in_state = RubyFullState::COMMENT;
state->full_state->expecting_expr = false; state->full_state.expecting_expr = false;
tokens->push_back({0, len, TokenKind::K_COMMENT}); tokens->push_back({0, len, TokenKind::K_COMMENT});
return state; return state;
} }
@@ -653,8 +657,8 @@ std::shared_ptr<void> ruby_parse(std::vector<Token> *tokens,
text[i + 3] == 'N' && text[i + 4] == 'D' && text[i + 5] == '_' && text[i + 3] == 'N' && text[i + 4] == 'D' && text[i + 5] == '_' &&
text[i + 6] == '_') { text[i + 6] == '_') {
tokens->clear(); tokens->clear();
state->full_state->in_state = RubyFullState::END; state->full_state.in_state = RubyFullState::END;
state->full_state->expecting_expr = false; state->full_state.expecting_expr = false;
return state; return state;
} }
} }
@@ -684,33 +688,33 @@ std::shared_ptr<void> ruby_parse(std::vector<Token> *tokens,
delim += text[j++]; delim += text[j++];
} }
} }
state->full_state->expecting_expr = false; state->full_state.expecting_expr = false;
if (!delim.empty()) { if (!delim.empty()) {
tokens->push_back({s, j, TokenKind::K_ANNOTATION}); tokens->push_back({s, j, TokenKind::K_ANNOTATION});
state->heredocs.push_back({delim, interpolation, indented}); state->heredocs.push_back({delim, interpolation, indented});
state->full_state->in_state = RubyFullState::HEREDOC; state->full_state.in_state = RubyFullState::HEREDOC;
heredoc_first = true; heredoc_first = true;
} }
i = j; i = j;
continue; continue;
} }
if (text[i] == '/' && state->full_state->expecting_expr) { if (text[i] == '/' && state->full_state.expecting_expr) {
tokens->push_back({i, i + 1, TokenKind::K_REGEXP}); tokens->push_back({i, i + 1, TokenKind::K_REGEXP});
state->full_state->in_state = RubyFullState::REGEXP; state->full_state.in_state = RubyFullState::REGEXP;
state->full_state->expecting_expr = false; state->full_state.expecting_expr = false;
state->full_state->lit.delim_start = '/'; state->full_state.lit.delim_start = '/';
state->full_state->lit.delim_end = '/'; state->full_state.lit.delim_end = '/';
state->full_state->lit.allow_interp = true; state->full_state.lit.allow_interp = true;
i++; i++;
continue; continue;
} else if (text[i] == '#') { } else if (text[i] == '#') {
if (line_num == 0 && i == 0 && len > 4 && text[i + 1] == '!') { if (line_num == 0 && i == 0 && len > 4 && text[i + 1] == '!') {
state->full_state->expecting_expr = false; state->full_state.expecting_expr = false;
tokens->push_back({0, len, TokenKind::K_SHEBANG}); tokens->push_back({0, len, TokenKind::K_SHEBANG});
return state; return state;
} }
tokens->push_back({i, len, TokenKind::K_COMMENT}); tokens->push_back({i, len, TokenKind::K_COMMENT});
state->full_state->expecting_expr = false; state->full_state.expecting_expr = false;
return state; return state;
} else if (text[i] == '.') { } else if (text[i] == '.') {
uint32_t start = i; uint32_t start = i;
@@ -722,15 +726,15 @@ std::shared_ptr<void> ruby_parse(std::vector<Token> *tokens,
} }
} }
tokens->push_back({start, i, TokenKind::K_OPERATOR}); tokens->push_back({start, i, TokenKind::K_OPERATOR});
state->full_state->expecting_expr = false; state->full_state.expecting_expr = false;
continue; continue;
} else if (text[i] == ':') { } else if (text[i] == ':') {
state->full_state->expecting_expr = false; state->full_state.expecting_expr = false;
uint32_t start = i; uint32_t start = i;
i++; i++;
if (i >= len) { if (i >= len) {
tokens->push_back({start, i, TokenKind::K_OPERATOR}); tokens->push_back({start, i, TokenKind::K_OPERATOR});
state->full_state->expecting_expr = true; state->full_state.expecting_expr = true;
continue; continue;
} }
if (text[i] == ':') { if (text[i] == ':') {
@@ -739,7 +743,7 @@ std::shared_ptr<void> ruby_parse(std::vector<Token> *tokens,
} }
if (text[i] == '\'' || text[i] == '"') { if (text[i] == '\'' || text[i] == '"') {
tokens->push_back({start, i, TokenKind::K_LABEL}); tokens->push_back({start, i, TokenKind::K_LABEL});
state->full_state->expecting_expr = true; state->full_state.expecting_expr = true;
continue; continue;
} }
if (text[i] == '$' || text[i] == '@') { if (text[i] == '$' || text[i] == '@') {
@@ -767,7 +771,7 @@ std::shared_ptr<void> ruby_parse(std::vector<Token> *tokens,
tokens->push_back({start, i, TokenKind::K_OPERATOR}); tokens->push_back({start, i, TokenKind::K_OPERATOR});
continue; continue;
} else if (text[i] == '@') { } else if (text[i] == '@') {
state->full_state->expecting_expr = false; state->full_state.expecting_expr = false;
uint32_t start = i; uint32_t start = i;
i++; i++;
if (i >= len) if (i >= len)
@@ -783,7 +787,7 @@ std::shared_ptr<void> ruby_parse(std::vector<Token> *tokens,
tokens->push_back({start, i, TokenKind::K_VARIABLEINSTANCE}); tokens->push_back({start, i, TokenKind::K_VARIABLEINSTANCE});
continue; continue;
} else if (text[i] == '$') { } else if (text[i] == '$') {
state->full_state->expecting_expr = false; state->full_state.expecting_expr = false;
uint32_t start = i; uint32_t start = i;
i++; i++;
if (i >= len) if (i >= len)
@@ -806,7 +810,7 @@ std::shared_ptr<void> ruby_parse(std::vector<Token> *tokens,
tokens->push_back({start, i, TokenKind::K_VARIABLEGLOBAL}); tokens->push_back({start, i, TokenKind::K_VARIABLEGLOBAL});
continue; continue;
} else if (text[i] == '?') { } else if (text[i] == '?') {
state->full_state->expecting_expr = false; state->full_state.expecting_expr = false;
uint32_t start = i; uint32_t start = i;
i++; i++;
if (i < len && text[i] == '\\') { if (i < len && text[i] == '\\') {
@@ -851,95 +855,95 @@ std::shared_ptr<void> ruby_parse(std::vector<Token> *tokens,
tokens->push_back({start, i, TokenKind::K_CHAR}); tokens->push_back({start, i, TokenKind::K_CHAR});
continue; continue;
} else { } else {
state->full_state->expecting_expr = true; state->full_state.expecting_expr = true;
tokens->push_back({start, i, TokenKind::K_OPERATOR}); tokens->push_back({start, i, TokenKind::K_OPERATOR});
continue; continue;
} }
} else if (text[i] == '{') { } else if (text[i] == '{') {
state->full_state->expecting_expr = true; state->full_state.expecting_expr = true;
uint8_t brace_color = uint8_t brace_color =
(uint8_t)TokenKind::K_BRACE1 + (state->full_state->brace_level % 5); (uint8_t)TokenKind::K_BRACE1 + (state->full_state.brace_level % 5);
tokens->push_back({i, i + 1, (TokenKind)brace_color}); tokens->push_back({i, i + 1, (TokenKind)brace_color});
state->interp_level++; state->interp_level++;
state->full_state->brace_level++; state->full_state.brace_level++;
i++; i++;
continue; continue;
} else if (text[i] == '}') { } else if (text[i] == '}') {
state->full_state->expecting_expr = false; state->full_state.expecting_expr = false;
state->interp_level--; state->interp_level--;
if (state->interp_level == 0 && !state->interp_stack.empty()) { if (state->interp_level == 0 && !state->interp_stack.empty()) {
state->full_state = state->interp_stack.top(); state->full_state = state->interp_stack.top();
state->interp_stack.pop(); state->interp_stack.pop();
tokens->push_back({i, i + 1, TokenKind::K_INTERPOLATION}); tokens->push_back({i, i + 1, TokenKind::K_INTERPOLATION});
} else { } else {
state->full_state->brace_level--; state->full_state.brace_level--;
uint8_t brace_color = uint8_t brace_color =
(uint8_t)TokenKind::K_BRACE1 + (state->full_state->brace_level % 5); (uint8_t)TokenKind::K_BRACE1 + (state->full_state.brace_level % 5);
tokens->push_back({i, i + 1, (TokenKind)brace_color}); tokens->push_back({i, i + 1, (TokenKind)brace_color});
} }
i++; i++;
continue; continue;
} else if (text[i] == '(') { } else if (text[i] == '(') {
state->full_state->expecting_expr = true; state->full_state.expecting_expr = true;
uint8_t brace_color = uint8_t brace_color =
(uint8_t)TokenKind::K_BRACE1 + (state->full_state->brace_level % 5); (uint8_t)TokenKind::K_BRACE1 + (state->full_state.brace_level % 5);
tokens->push_back({i, i + 1, (TokenKind)brace_color}); tokens->push_back({i, i + 1, (TokenKind)brace_color});
state->full_state->brace_level++; state->full_state.brace_level++;
i++; i++;
continue; continue;
} else if (text[i] == ')') { } else if (text[i] == ')') {
state->full_state->expecting_expr = false; state->full_state.expecting_expr = false;
state->full_state->brace_level--; state->full_state.brace_level--;
uint8_t brace_color = uint8_t brace_color =
(uint8_t)TokenKind::K_BRACE1 + (state->full_state->brace_level % 5); (uint8_t)TokenKind::K_BRACE1 + (state->full_state.brace_level % 5);
tokens->push_back({i, i + 1, (TokenKind)brace_color}); tokens->push_back({i, i + 1, (TokenKind)brace_color});
i++; i++;
continue; continue;
} else if (text[i] == '[') { } else if (text[i] == '[') {
state->full_state->expecting_expr = true; state->full_state.expecting_expr = true;
uint8_t brace_color = uint8_t brace_color =
(uint8_t)TokenKind::K_BRACE1 + (state->full_state->brace_level % 5); (uint8_t)TokenKind::K_BRACE1 + (state->full_state.brace_level % 5);
tokens->push_back({i, i + 1, (TokenKind)brace_color}); tokens->push_back({i, i + 1, (TokenKind)brace_color});
state->full_state->brace_level++; state->full_state.brace_level++;
i++; i++;
continue; continue;
} else if (text[i] == ']') { } else if (text[i] == ']') {
state->full_state->expecting_expr = false; state->full_state.expecting_expr = false;
state->full_state->brace_level--; state->full_state.brace_level--;
uint8_t brace_color = uint8_t brace_color =
(uint8_t)TokenKind::K_BRACE1 + (state->full_state->brace_level % 5); (uint8_t)TokenKind::K_BRACE1 + (state->full_state.brace_level % 5);
tokens->push_back({i, i + 1, (TokenKind)brace_color}); tokens->push_back({i, i + 1, (TokenKind)brace_color});
i++; i++;
continue; continue;
} else if (text[i] == '\'') { } else if (text[i] == '\'') {
state->full_state->expecting_expr = false; state->full_state.expecting_expr = false;
tokens->push_back({i, i + 1, TokenKind::K_STRING}); tokens->push_back({i, i + 1, TokenKind::K_STRING});
state->full_state->in_state = RubyFullState::STRING; state->full_state.in_state = RubyFullState::STRING;
state->full_state->lit.delim_start = '\''; state->full_state.lit.delim_start = '\'';
state->full_state->lit.delim_end = '\''; state->full_state.lit.delim_end = '\'';
state->full_state->lit.allow_interp = false; state->full_state.lit.allow_interp = false;
i++; i++;
continue; continue;
} else if (text[i] == '"') { } else if (text[i] == '"') {
state->full_state->expecting_expr = false; state->full_state.expecting_expr = false;
tokens->push_back({i, i + 1, TokenKind::K_STRING}); tokens->push_back({i, i + 1, TokenKind::K_STRING});
state->full_state->in_state = RubyFullState::STRING; state->full_state.in_state = RubyFullState::STRING;
state->full_state->lit.delim_start = '"'; state->full_state.lit.delim_start = '"';
state->full_state->lit.delim_end = '"'; state->full_state.lit.delim_end = '"';
state->full_state->lit.allow_interp = true; state->full_state.lit.allow_interp = true;
i++; i++;
continue; continue;
} else if (text[i] == '`') { } else if (text[i] == '`') {
state->full_state->expecting_expr = false; state->full_state.expecting_expr = false;
tokens->push_back({i, i + 1, TokenKind::K_STRING}); tokens->push_back({i, i + 1, TokenKind::K_STRING});
state->full_state->in_state = RubyFullState::STRING; state->full_state.in_state = RubyFullState::STRING;
state->full_state->lit.delim_start = '`'; state->full_state.lit.delim_start = '`';
state->full_state->lit.delim_end = '`'; state->full_state.lit.delim_end = '`';
state->full_state->lit.allow_interp = true; state->full_state.lit.allow_interp = true;
i++; i++;
continue; continue;
} else if (text[i] == '%') { } else if (text[i] == '%') {
state->full_state->expecting_expr = false; state->full_state.expecting_expr = false;
if (i + 1 >= len) { if (i + 1 >= len) {
i++; i++;
continue; continue;
@@ -1005,16 +1009,16 @@ std::shared_ptr<void> ruby_parse(std::vector<Token> *tokens,
tokens->push_back( tokens->push_back(
{i, i + prefix_len + 1, {i, i + prefix_len + 1,
(is_regexp ? TokenKind::K_REGEXP : TokenKind::K_STRING)}); (is_regexp ? TokenKind::K_REGEXP : TokenKind::K_STRING)});
state->full_state->in_state = state->full_state.in_state =
is_regexp ? RubyFullState::REGEXP : RubyFullState::STRING; is_regexp ? RubyFullState::REGEXP : RubyFullState::STRING;
state->full_state->lit.delim_start = delim_start; state->full_state.lit.delim_start = delim_start;
state->full_state->lit.delim_end = delim_end; state->full_state.lit.delim_end = delim_end;
state->full_state->lit.allow_interp = allow_interp; state->full_state.lit.allow_interp = allow_interp;
state->full_state->lit.brace_level = 1; state->full_state.lit.brace_level = 1;
i += prefix_len + 1; i += prefix_len + 1;
continue; continue;
} else if (isdigit(text[i])) { } else if (isdigit(text[i])) {
state->full_state->expecting_expr = false; state->full_state.expecting_expr = false;
uint32_t start = i; uint32_t start = i;
if (text[i] == '0') { if (text[i] == '0') {
i++; i++;
@@ -1115,7 +1119,7 @@ std::shared_ptr<void> ruby_parse(std::vector<Token> *tokens,
tokens->push_back({start, i, TokenKind::K_NUMBER}); tokens->push_back({start, i, TokenKind::K_NUMBER});
continue; continue;
} else if (identifier_start_char(text[i])) { } else if (identifier_start_char(text[i])) {
state->full_state->expecting_expr = false; state->full_state.expecting_expr = false;
uint32_t length; uint32_t length;
if ((length = base_keywords_trie.match(text, i, len, identifier_char))) { if ((length = base_keywords_trie.match(text, i, len, identifier_char))) {
tokens->push_back({i, i + length, TokenKind::K_KEYWORD}); tokens->push_back({i, i + length, TokenKind::K_KEYWORD});
@@ -1123,7 +1127,7 @@ std::shared_ptr<void> ruby_parse(std::vector<Token> *tokens,
continue; continue;
} else if ((length = expecting_keywords_trie.match(text, i, len, } else if ((length = expecting_keywords_trie.match(text, i, len,
identifier_char))) { identifier_char))) {
state->full_state->expecting_expr = true; state->full_state.expecting_expr = true;
tokens->push_back({i, i + length, TokenKind::K_KEYWORD}); tokens->push_back({i, i + length, TokenKind::K_KEYWORD});
i += length; i += length;
continue; continue;
@@ -1134,7 +1138,7 @@ std::shared_ptr<void> ruby_parse(std::vector<Token> *tokens,
continue; continue;
} else if ((length = expecting_operators_trie.match( } else if ((length = expecting_operators_trie.match(
text, i, len, identifier_char)) > 0) { text, i, len, identifier_char)) > 0) {
state->full_state->expecting_expr = true; state->full_state.expecting_expr = true;
tokens->push_back({i, i + length, TokenKind::K_KEYWORDOPERATOR}); tokens->push_back({i, i + length, TokenKind::K_KEYWORDOPERATOR});
i += length; i += length;
continue; continue;
@@ -1256,7 +1260,7 @@ std::shared_ptr<void> ruby_parse(std::vector<Token> *tokens,
operator_trie.match(text, i, len, [](char) { return false; }))) { operator_trie.match(text, i, len, [](char) { return false; }))) {
tokens->push_back({i, i + op_len, TokenKind::K_OPERATOR}); tokens->push_back({i, i + op_len, TokenKind::K_OPERATOR});
i += op_len; i += op_len;
state->full_state->expecting_expr = true; state->full_state.expecting_expr = true;
continue; continue;
} else { } else {
i += utf8_codepoint_width(text[i]); i += utf8_codepoint_width(text[i]);
+35 -24
View File
@@ -3,17 +3,25 @@
#include "lsp/lsp.h" #include "lsp/lsp.h"
#include "main.h" #include "main.h"
#include "syntax/decl.h" #include "syntax/decl.h"
#include "windows/decl.h"
void Bar::log(std::string message) { log_line = message; } void Bar::log(std::string message) { log_line = strip_newlines(message); }
void Bar::render() { void Bar::render(std::vector<ScreenCell> &buffer) {
USING(LSPInstance); USING(LSPInstance);
Editor *editor = editors[current_editor]; BarLine bar_line;
BarLine bar_line = bar_line =
bar_contents(mode, editor->lang.name, editor->warnings.size(), bar_contents(mode, screen.col, pwd.string(), layout::focused_window);
editor->lsp ? editor->lsp->lsp->command : "", auto update = [&](uint32_t row, uint32_t col, std::string text, uint32_t fg,
editor->filename, editor->filename, editor->cursor.row + 1, uint32_t bg, uint8_t flags, uint32_t width) {
editor->root->line_count + 1, screen.col); 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 row = screen.row - 2;
uint32_t width = screen.col; uint32_t width = screen.col;
std::string &line = bar_line.line; std::string &line = bar_line.line;
@@ -26,43 +34,46 @@ void Bar::render() {
int width = display_width(cluster.c_str(), cluster_len); int width = display_width(cluster.c_str(), cluster_len);
Highlight highlight = bar_line.get_highlight(col); Highlight highlight = bar_line.get_highlight(col);
update(row, col, cluster.c_str(), highlight.fg, highlight.bg, update(row, col, cluster.c_str(), highlight.fg, highlight.bg,
highlight.flags); highlight.flags, width);
col += width; col += width;
i += cluster_len; i += cluster_len;
for (int w = 1; w < width; w++) 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) while (col < width)
update(row, col++, " ", 0, 0, 0); update(row, col++, " ", 0, 0, 0, 1);
col = 0; col = 0;
row++; row++;
if (mode == RUNNER) { if (mode == RUNNER) {
update(row, col++, ":", 0xFFFFFF, 0, 0); update(row, col++, ":", 0xFFFFFF, 0, 0, 1);
for (char c : command) 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 { } else {
for (char c : log_line) 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) 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 (layout::focused_window)
layout::focused_window->handle_command(command);
}
void Bar::handle_event(KeyEvent event) {
if (event.key_type == KEY_CHAR && event.len == 1) { if (event.key_type == KEY_CHAR && event.len == 1) {
if (event.c[0] == 0x1B) { if (event.c[0] == 0x1B) {
command = ""; command = "";
mode = NORMAL; mode = NORMAL;
} else if (event.c[0] == '\n' || event.c[0] == '\r') { } else if (event.c[0] == '\n' || event.c[0] == '\r') {
command = trim(command); command = trim(command);
if (command == "w") { handle_command(command);
save_file(editors[current_editor]);
} else if (command == "q") {
running = false;
} else if (command == "wq") {
save_file(editors[current_editor]);
running = false;
}
mode = NORMAL; mode = NORMAL;
command = ""; command = "";
} else if (isprint((unsigned char)(event.c[0]))) { } else if (isprint((unsigned char)(event.c[0]))) {
+18 -18
View File
@@ -1,4 +1,4 @@
#include "ui/completionbox.h" /* #include "ui/completionbox.h"
#include "editor/completions.h" #include "editor/completions.h"
#include "io/sysio.h" #include "io/sysio.h"
#include "utils/utils.h" #include "utils/utils.h"
@@ -86,9 +86,9 @@ void CompletionBox::render_update() {
uint32_t total = session->visible.size(); uint32_t total = session->visible.size();
uint32_t rows = MIN(total, 8); uint32_t rows = MIN(total, 8);
size.row = rows + 2; size.row = rows + 2;
size.col = 2 + 2 + max_label_len + 1 + max_detail_len + 2 + max_kind_len + 1; size.col = 2 + 2 + max_label_len + 1 + max_detail_len + 2 + max_kind_len +
cells.assign(size.row * size.col, {" ", 0, 0, 0, 0, 0}); 1; cells.assign(size.row * size.col, {" ", 0, 0, 0, 0, 0}); auto set =
auto set = [&](uint32_t r, uint32_t c, const char *text, uint32_t fg, [&](uint32_t r, uint32_t c, const char *text, uint32_t fg,
uint32_t bg, uint8_t flags) { uint32_t bg, uint8_t flags) {
if (r < size.row && c < size.col) if (r < size.row && c < size.col)
cells[r * size.col + c] = {std::string(text), 0, fg, bg, flags, 0}; cells[r * size.col + c] = {std::string(text), 0, fg, bg, flags, 0};
@@ -104,14 +104,11 @@ void CompletionBox::render_update() {
for (uint32_t row_idx = start; row_idx < end; row_idx++) { for (uint32_t row_idx = start; row_idx < end; row_idx++) {
uint32_t r = (row_idx - start) + 1; uint32_t r = (row_idx - start) + 1;
auto &item = session->items[session->visible[row_idx]]; auto &item = session->items[session->visible[row_idx]];
uint32_t bg = (session->visible[row_idx] == session->select) ? sel_bg : 1; uint32_t bg = (session->visible[row_idx] == session->select) ? sel_bg :
uint32_t fg = 0xFFFFFF; 1; uint32_t fg = 0xFFFFFF; set(r, 0, "│", border_fg, 0, 0); uint32_t c =
set(r, 0, "", border_fg, 0, 0); 1; const char *sym = item_symbol(item.kind); set(r, c++, sym,
uint32_t c = 1; kind_color(item.kind), bg, 0); set(r, c++, " ", fg, bg, 0); for (size_t i
const char *sym = item_symbol(item.kind); = 0; i < item.label.size(); i++)
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, set(r, c + i, (char[2]){item.label[i], 0}, fg, bg,
item.deprecated ? CF_STRIKETHROUGH : 0); item.deprecated ? CF_STRIKETHROUGH : 0);
c += item.label.size(); c += item.label.size();
@@ -128,7 +125,8 @@ void CompletionBox::render_update() {
c += pad; c += pad;
std::string kind_name = item_kind_name(item.kind); std::string kind_name = item_kind_name(item.kind);
for (size_t i = 0; i < kind_name.size(); i++) 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, c + i, (char[2]){kind_name[i], 0}, kind_color(item.kind), bg,
0);
set(r, size.col - 1, "│", border_fg, 0, 0); set(r, size.col - 1, "│", border_fg, 0, 0);
} }
uint32_t bottom = size.row - 1; uint32_t bottom = size.row - 1;
@@ -137,7 +135,8 @@ void CompletionBox::render_update() {
set(bottom, c, "─", border_fg, 0, 0); set(bottom, c, "─", border_fg, 0, 0);
set(bottom, size.col - 1, "┘", border_fg, 0, 0); set(bottom, size.col - 1, "┘", border_fg, 0, 0);
if (session->visible.size() > 8) { if (session->visible.size() > 8) {
std::string info = std::to_string(start + 1) + "-" + std::to_string(end) + std::string info = std::to_string(start + 1) + "-" + std::to_string(end)
+
"/" + std::to_string(session->visible.size()); "/" + std::to_string(session->visible.size());
for (size_t i = 0; i < info.size() && i < size.col - 2; i++) 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); set(bottom, 1 + i, (char[2]){info[i], 0}, border_fg, 0, 0);
@@ -152,9 +151,8 @@ void CompletionBox::render(Coord pos) {
if (start_row < 0) if (start_row < 0)
start_row = pos.row + 1; start_row = pos.row + 1;
int32_t start_col = pos.col; int32_t start_col = pos.col;
Coord screen_size = get_size(); if (start_col + size.col > io::cols) {
if (start_col + size.col > screen_size.col) { start_col = io::cols - size.col;
start_col = screen_size.col - size.col;
if (start_col < 0) if (start_col < 0)
start_col = 0; start_col = 0;
} }
@@ -180,8 +178,10 @@ void CompletionBox::render(Coord pos) {
position.col - session->hover.size.col}); position.col - session->hover.size.col});
} else { } else {
session->hover.render( session->hover.render(
{position.row + session->hover.size.row, position.col + size.col}); {position.row + session->hover.size.row, position.col +
size.col});
} }
} }
} }
} }
*/
+1
View File
@@ -59,6 +59,7 @@ char *load_file(const char *path, uint32_t *out_len, bool *out_eol) {
bool has_utf8_bom = (bom[0] == 0xEF && bom[1] == 0xBB && bom[2] == 0xBF); bool has_utf8_bom = (bom[0] == 0xEF && bom[1] == 0xBB && bom[2] == 0xBF);
uint32_t skip = has_utf8_bom ? 3 : 0; uint32_t skip = has_utf8_bom ? 3 : 0;
uint32_t data_len = static_cast<uint32_t>(len) - skip; uint32_t data_len = static_cast<uint32_t>(len) - skip;
file.clear();
file.seekg(skip, std::ios::beg); file.seekg(skip, std::ios::beg);
char *buf = (char *)malloc(data_len + 1); char *buf = (char *)malloc(data_len + 1);
if (!buf) if (!buf)
+9
View File
@@ -91,6 +91,15 @@ std::string trim(const std::string &s) {
return s.substr(start, end - start + 1); return s.substr(start, end - start + 1);
} }
std::string strip_newlines(const std::string &s) {
std::string out;
out.reserve(s.size());
for (char c : s)
if (c != '\n' && c != '\r')
out.push_back(c);
return out;
}
std::string clean_text(const std::string &input) { std::string clean_text(const std::string &input) {
std::string result = input; std::string result = input;
static const std::unordered_map<std::string, std::string> entities = { static const std::unordered_map<std::string, std::string> entities = {
+49
View File
@@ -0,0 +1,49 @@
#include "io/sysio.h"
#include "main.h"
#include "windows/decl.h"
namespace layout {
TileBase root_tile;
Window *focused_window;
std::vector<std::unique_ptr<Popup>> popups;
std::vector<std::unique_ptr<TileBase>> floating_tiles;
} // namespace layout
void render() {
ui::bar.render(io::new_screen);
layout::root_tile.render(io::new_screen);
for (auto &tile : layout::floating_tiles) {
if (tile->hidden())
continue;
tile->render(io::new_screen);
}
for (auto &popup : layout::popups) {
if (popup->hidden)
continue;
popup->render(io::new_screen, popup->size, popup->pos);
}
}
void handle_click(KeyEvent event) {
for (auto &popup : layout::popups) {
if (!popup->hidden &&
inside({event.mouse_y, event.mouse_x}, popup->pos, popup->size)) {
event.mouse_x -= popup->pos.col;
event.mouse_y -= popup->pos.row;
popup->handle_click(event, popup->size);
return;
}
}
for (auto &tile : layout::floating_tiles) {
if (!tile->hidden() &&
inside({event.mouse_y, event.mouse_x}, tile->pos, tile->size)) {
event.mouse_x -= tile->pos.col;
event.mouse_y -= tile->pos.row;
tile->handle_click(event);
return;
}
}
if (inside({event.mouse_y, event.mouse_x}, layout::root_tile.pos,
layout::root_tile.size))
layout::root_tile.handle_click(event);
}