Compare commits

..

20 Commits

Author SHA1 Message Date
syedm 8c437d2639 Make it work with flake.nix 2026-07-08 14:52:12 +01:00
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
syedm e3fc5323df Cleanup 2026-02-02 10:45:22 +00:00
syedm 15cef855d6 Update installer 2026-02-01 20:49:38 +00:00
syedm 59fe554259 Fix bar dead code 2026-02-01 20:47:41 +00:00
syedm 8b93b955e8 Fix parsing bugs and add better indentation support 2026-02-01 20:40:52 +00:00
syedm f77caf604f Post release cleanup 2026-02-01 17:06:27 +00:00
syedm 8b49ab6085 Update mruby api. 2026-02-01 17:01:57 +00:00
syedm 154e557339 Fix memory leaks 2026-01-31 23:23:08 +00:00
syedm 04cce4224e Embed mruby and better clipboard support 2026-01-31 17:19:08 +00:00
syedm 410222b82a Fix minor bugs 2026-01-31 10:54:03 +00:00
syedm f93afc0d14 Make binary portable and other fixes 2026-01-31 10:25:39 +00:00
83 changed files with 5001 additions and 3663 deletions
+1
View File
@@ -2,6 +2,7 @@ CompileFlags:
Add: [ Add: [
-I/home/syed/main/crib/include, -I/home/syed/main/crib/include,
-I/home/syed/main/crib/libs, -I/home/syed/main/crib/libs,
-I/home/syed/main/crib/libs/mruby/include,
-std=c++23 -std=c++23
] ]
Remove: [] Remove: []
+4
View File
@@ -17,4 +17,8 @@ bin
Gemfile* Gemfile*
.ruby-lsp/ .ruby-lsp/
include/scripting/ruby_compiled.h
__old__ __old__
.nix-bin/
+11 -3
View File
@@ -1,4 +1,12 @@
[submodule "libs/libgrapheme"] [submodule "libs/libgrapheme"]
path = libs/libgrapheme path = libs/libgrapheme
url = git://git.suckless.org/libgrapheme url = git://git.suckless.org/libgrapheme
ignore = dirty ignore = dirty
[submodule "libs/mruby"]
path = libs/mruby
url = https://github.com/mruby/mruby.git
ignore = dirty
[submodule "libs/json"]
path = libs/json
url = https://github.com/nlohmann/json
ignore = dirty
+29 -19
View File
@@ -12,27 +12,35 @@ PCH_RELEASE := $(OBJ_DIR)/release/pch.h.gch
GENERATED_HEADER := $(INCLUDE_DIR)/scripting/ruby_compiled.h GENERATED_HEADER := $(INCLUDE_DIR)/scripting/ruby_compiled.h
CCACHE := ccache CCACHE := ccache
CXX := $(CCACHE) clang++
CC := $(CCACHE) clang CXX ?= x86_64-unknown-linux-musl-g++
CC ?= x86_64-unknown-linux-musl-gcc
CXX := $(CCACHE) $(CXX)
CC := $(CCACHE) $(CC)
CFLAGS_DEBUG :=\ CFLAGS_DEBUG :=\
-std=c++20 -Wall -Wextra \ -std=c++20 -Wall -Wextra -static \
-O0 -fno-inline -gsplit-dwarf\ -O0 -fno-inline -gsplit-dwarf \
-g -fno-omit-frame-pointer\ -g -fno-omit-frame-pointer \
-Wno-unused-command-line-argument \ -I./include -I./libs -I./libs/mruby/include -I./libs/json/include -I./libs/mruby/build/host/include
-I./include -I./libs
# C_SANITIZER := -fsanitize=address
CFLAGS_RELEASE :=\ CFLAGS_RELEASE :=\
-static \ -static \
-std=c++20 -O3 -march=native \ -std=c++20 -O3 -march=x86-64 -mtune=generic -fno-rtti \
-fno-rtti -fstrict-aliasing \
-ffast-math -flto=thin \ -ffast-math -flto=thin \
-fvisibility=hidden \ -fvisibility=hidden \
-fomit-frame-pointer -DNDEBUG -s \ -fomit-frame-pointer -DNDEBUG -s \
-mllvm -vectorize-loops \ -mllvm -vectorize-loops \
-fno-unwind-tables -fno-asynchronous-unwind-tables\ -I./include -I./libs -I./libs/mruby/include -I./libs/json/include -I./libs/mruby/build/host/include
-Wno-unused-command-line-argument \
-I./include -I./libs PCRE_CFLAGS := $(shell pkg-config --cflags libpcre2-8)
PCRE_LIBS := $(shell pkg-config --static --libs libpcre2-8)
CFLAGS_DEBUG += $(PCRE_CFLAGS)
CFLAGS_RELEASE += $(PCRE_CFLAGS)
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
@@ -42,13 +50,15 @@ UNICODE_SRC := $(wildcard libs/unicode_width/*.c)
UNICODE_OBJ_DEBUG := $(patsubst libs/unicode_width/%.c,$(OBJ_DIR)/debug/unicode_width/%.o,$(UNICODE_SRC)) UNICODE_OBJ_DEBUG := $(patsubst libs/unicode_width/%.c,$(OBJ_DIR)/debug/unicode_width/%.o,$(UNICODE_SRC))
UNICODE_OBJ_RELEASE := $(patsubst libs/unicode_width/%.c,$(OBJ_DIR)/release/unicode_width/%.o,$(UNICODE_SRC)) UNICODE_OBJ_RELEASE := $(patsubst libs/unicode_width/%.c,$(OBJ_DIR)/release/unicode_width/%.o,$(UNICODE_SRC))
LIBS_RELEASE := \
libs/libgrapheme/libgrapheme.a \
-Wl,-Bstatic,--gc-sections -lpcre2-8 -lmruby
LIBS_DEBUG := \ LIBS_DEBUG := \
libs/libgrapheme/libgrapheme.a \ libs/libgrapheme/libgrapheme.a \
-Wl,-Bdynamic -lpcre2-8 -lmruby ./libs/mruby/build/host/lib/libmruby.a \
$(PCRE_LIBS)
LIBS_RELEASE := \
libs/libgrapheme/libgrapheme.a \
./libs/mruby/build/host/lib/libmruby.a \
$(PCRE_LIBS)
SRC := $(wildcard $(SRC_DIR)/**/*.cc) $(wildcard $(SRC_DIR)/*.cc) SRC := $(wildcard $(SRC_DIR)/**/*.cc) $(wildcard $(SRC_DIR)/*.cc)
OBJ_DEBUG := $(patsubst $(SRC_DIR)/%.cc,$(OBJ_DIR)/debug/%.o,$(SRC)) OBJ_DEBUG := $(patsubst $(SRC_DIR)/%.cc,$(OBJ_DIR)/debug/%.o,$(SRC))
@@ -65,7 +75,7 @@ test: $(TARGET_DEBUG)
release: $(TARGET_RELEASE) release: $(TARGET_RELEASE)
$(GENERATED_HEADER): $(INCLUDE_DIR)/syntax/tokens.def $(INCLUDE_DIR)/scripting/libcrib.rb src/ruby_compile.sh $(GENERATED_HEADER): $(INCLUDE_DIR)/syntax/tokens.def $(INCLUDE_DIR)/ruby/libcrib.rb src/ruby_compile.sh
src/ruby_compile.sh src/ruby_compile.sh
$(PCH_DEBUG): $(INCLUDE_DIR)/pch.h $(PCH_DEBUG): $(INCLUDE_DIR)/pch.h
@@ -78,7 +88,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)
+27 -11
View File
@@ -20,9 +20,8 @@ Binary can be installed with the following command:
curl https://syedm.dev/crib | sh curl https://syedm.dev/crib | sh
``` ```
It requires `libmagic` to be installed (most systems have it preinstalled).<br>
Currently only for Linux.<br> Currently only for Linux.<br>
*Tested with arch linux and ubuntu*<br> *Tested with arch linux, ubuntu and void*<br>
## Building ## Building
@@ -52,12 +51,6 @@ Make sure you have the following dependencies installed (apart from the standard
#include <pcre2.h> #include <pcre2.h>
``` ```
* **[mruby](https://github.com/mruby/mruby)**
Install it via your package manager. Once installed, the header should be available as:
```cpp
#include <mruby.h>
```
It also uses `xclip` at runtime for copying/pasting *(TODO: make it os portable)*. It also uses `xclip` at runtime for copying/pasting *(TODO: make it os portable)*.
And any modern terminal should work fine - preferably `kitty` or `wezterm`.<br> And any modern terminal should work fine - preferably `kitty` or `wezterm`.<br>
@@ -66,10 +59,32 @@ And any modern terminal should work fine - preferably `kitty` or `wezterm`.<br>
Some other dependancies are added as submodules or copied.<br> Some other dependancies are added as submodules or copied.<br>
- `unicode_width` is compiled by the makefile so nothing to do there.<br> - `unicode_width` is compiled by the makefile so nothing to do there.<br>
- `libgrapheme` needs to be compiled using `make` in it's folder.<br> - `libgrapheme` needs to be compiled using `make` in it's folder.<br>
- `mruby` needs to be compiled using `rake` in it's folder (needs ruby).<br>
- Need to add the following lines to the `build_config/default.rb` file (in the `MRuby::Build.new` block)
```ruby
conf.gem :mgem => 'pure-regexp'
conf.gem :mgem => 'env'
conf.gem :mgem => 'process'
conf.gem :mgem => 'marshal-c'
```
#### config file
The config file is a ruby file placed in one of these places (in order, first one found will be used):
- `$CRIB_CONFIG`
- `$CRIB_CONFIG_DIR/crib.rb`
- `$CRIB_CONFIG_DIR/main.rb`
- `./crib.rb`
- `$XDG_CONFIG_HOME/crib/crib.rb`
- `$XDG_CONFIG_HOME/crib/main.rb`
- `$XDG_CONFIG_HOME/crib.rb`
- `$HOME/.config/crib/crib.rb`
- `$HOME/.config/crib/main.rb`
- `$HOME/.config/crib.rb`
#### LSPs #### LSPs
Lsp's are defined in the `libcrib.rb` file and you can use your `config/main.rb` file to add more.<br> Lsp's are defined in the `libcrib.rb` file and you can use your config file to add more.<br>
The following lsp's are added by default and can be installed anywhere in your `$PATH`<br> The following lsp's are added by default and can be installed anywhere in your `$PATH`<br>
@@ -99,7 +114,7 @@ The following lsp's are added by default and can be installed anywhere in your `
#### Compiler #### Compiler
`clang++` should work fine but `c++23+` is required.<br> `g++` or `clang++` should work fine but `c++20+` is required.<br>
Can remove `ccache` if you want from the makefile.<br> Can remove `ccache` if you want from the makefile.<br>
#### Compliling #### Compliling
@@ -119,10 +134,11 @@ 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
TODO: add keybind information on how to set in `config/main.rb` TODO: add keybind information on how to set in config file
and default / unchangeable keybinds and default / unchangeable keybinds
## Features Implemented ## Features Implemented
+191 -78
View File
@@ -2,122 +2,235 @@ Copyright 2025 Syed Daanish
# TODO # TODO
##### BTW Check each lsp with each of the features implemented - Fix bug where scroll up at top of screen first line jumps to bottom
- Make completions work with new system
- And factorize renderer into its own class
- 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
* Allow keybinds to be set in ruby
* 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 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)
* 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
* 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
* 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
* 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)
* add links support in xterm (kitty like clickable links)
* And preprocess markdown in popups to be more like styled than just highlighted
* In the ruby libcrib populate toplevel binding and file and dir and rubybuild stuff and other similar constants
* in require_relative allow requiring without appending .rb if possible.
* Possibly in the future limit memory usage by parser for larger files
* Add a file picker suggestion while typing a path (including stuff like ~ and .. and $HOME etc)
* allow opening directory after filemanger is sorted out.
* commands to:
change pwd
load a rb file
run a ruby command
close a window etc.
* [ ] 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
* [ ] color alpha in ini files
* [ ] Make warning before ctrl+q for saving
* [ ] **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.
* [ ] **Critical Crash:** Fix bug where closing immediately while LSP is still loading hangs and then segfaults (especially on slow ones like fish-lsp where quick edits and exit can hang).
* [ ] **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.
* [ ] Fix bug where enter at start of line with ending type crashes - [ ] Ignore comments/strings from parser when auto-indenting.
* [ ] Keep cache of language maps in engine to reduce lookup time. - [ ] Support for stuff like bash \ and math operators in other languages and comma and line starting with a dot (like in ruby)
* [ ] In indents add function to support tab which indents if before any content and inserts a pure \t otherwise. etc.
* [ ] And backspace which undents if before any content. * [ ] **Readme:** Update readme to show ruby based config in detail.
* [ ] Add block indentation support.
* [ ] Ignore comments/strings from parser when auto-indenting.
* [ ] These will dedent when the block immediately after them is dedented
* [ ] Dont dedent if ending is valid starting is invalid but also empty
* [ ] Just leave asis if starting is empty
* [ ] **Readme:** Update readme to show ruby based config.
* [ ] **UI Refinement:** * [ ] **UI Refinement:**
* [ ] Allow completion list to be scrolled; show only `x` max items. - [ ] 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.
* [ ] Redo hooks as a struct. * Cap line_tree data limit for large files and just store about a thousand previous lines maybe? (lazily loaded)
* [ ] breakdown the render function into smaller functions.
* Try to make all functions better now that folds have been purged * add `:j<n>` command to jump to line \<n> in the current file
* Cleanup syntax and renderer files * and give warning for invalid commands
* and upon escape clear the current command
* allow multiline logging which captures the input entirely and y will copy the log and anything else will exit
* it will then collapse to being the first line from the log only
* Add a thing called view which is a rect with speacial type editor . but otherwise a ruby or c++ view
* can be used for stuff like file manager/git manager/theme picker.
* 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.
* allow keybinds to be set in ruby
check:: * [ ] **LSP:**
pull diagnostics for ruby-lsp support snippets in completion properly
lsp selection range - use to highlight start / end of range maybe? check::
goto definiton pull diagnostics for ruby-lsp
signature help lsp selection range - use to highlight start / end of range maybe?
document symbol for the top bar maybe? (or workspace symbol) goto definiton
also setup workspaces signature help
Semantic highlighting document symbol for the top bar maybe? (or workspace symbol)
Quick fixes also setup workspaces
Rename symbols Semantic highlighting
Quick fixes
Rename symbols
probably remove solargraph support and use ruby-lsp (or sorbet?) instead because it is a pain for utf stuff * Allow ruby to config lsp capabilities
ruby-lsp also supports erb so thats a plus
move lsp configs to json and also allow configs for windows-style vs unix-style line endings and utf-8 vs utf-16 * also try to fix why solargraph is the only one breaking on edits near emojis
* ruby-lsp also supports erb so thats a plus
* the ruby should have an api to be able to draw windows and add mappings to them * the ruby should have an api to be able to draw windows and add mappings to them
* **Syntax highlighting**
* ruby done!!
* finish bash then do all the directive-like ones like jsonc (first to help with theme files) / toml / yaml / ini / nginx * finish bash then do all the directive-like ones like jsonc (first to help with theme files) / toml / yaml / ini / nginx
* then [ch](++)? then gdscript and python then erb then php
* then markdown / html * then markdown / html
* then gitignore / gitattributes * then gitignore / gitattributes
* then fish then sql then css and [jt]sx? then python then lua (make with type annotations for lsp results) * then fish then sql then css and [jt]sx? then lua (make with type annotations for lsp results)
* then [ch](++)? then gdscript then erb then php
* then haskell then gomod then go then rust * then haskell then gomod then go then rust
* [ ] **Undo/Redo:** Add support for undo/redo history. * [ ] **Undo/Redo:** Add support for undo/redo history.
* [ ] **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.
* [ ] **Scrolling:** Add logic where selecting at the end of the screen scrolls down (and vice versa).
* *Implementation:* Update `main.cc` to send drag events to the selected editor even if coordinates are out of bounds.
### UX ### UX
* [ ] **Editor word highlighter:** Do not recompute word under cursor if not changed. - [ ] **Completion Filtering:**
- [ ] Stop filtering case-sensitive.
- [ ] Normalize completion edits if local filtering is used.
* [ ] **Completion Filtering:** - [ ] **Basic Autocomplete:** Keep a list of words in the current buffer for non-LSP fallback.
* [ ] Stop filtering case-sensitive.
* [ ] Normalize completion edits if local filtering is used.
* [ ] **LSP Features:** ### Features
* [ ] Add LSP jumping support (Go to Definition, Hover).
* [ ] Add LSP rename support.
* [ ] Handle snippets properly in autocomplete: use only the last word in signature when replacing and set cursor to the first one.
* [ ] **Basic Autocomplete:** Keep a list of words in the current buffer for non-LSP fallback. - [ ] **Search & Replace:**
- [ ] Add Search/Replace UI.
- [ ] Support capture groups (`$1`, `$2`) or allow Perl regex directly.
- [ ] Ensure virtual cursors are included in search positions.
### Major Features - [ ] **Multi-Cursor:**
- [ ] Add virtual cursor support (edits apply to all locations).
* [ ] **Search & Replace:** - [ ] Add `Alt+Click` to set multiple cursors.
* [ ] Add Search/Replace UI. - [ ] Allow search and place cursor at all matches.
* [ ] Support capture groups (`$1`, `$2`) or allow Perl regex directly.
* [ ] Ensure virtual cursors are included in search positions.
* [ ] **Multi-Cursor:**
* [ ] Add virtual cursor support (edits apply to all locations).
* [ ] Add `Alt+Click` to set multiple cursors.
* [ ] Allow search and place cursor at all matches.
* [ ] **Block Selection:**
* [ ] Double-clicking a bracket selects the whole block (first time only) and sets mode to `WORD`.
- [ ] **Block Selection:**
- [ ] Double-clicking a bracket selects the whole block and sets mode to `WORD`.
### Visuals, UI & Extensions? ### Visuals, UI & Extensions?
* [ ] **Status Bar:** Complete status bar and command runner. - [ ] Add color picker/palette (as a floating extention).
* [ ] Add color picker/palette. - [ ] **Git:** Add Git integration (status, diffs).
- [ ] **AI/Snippets:**
- [ ] Add snippets support (LuaSnip/VSnip style).
- [ ] Add Codeium/Copilot support (using VAI virtual text) as a test phase.
* [ ] **Git:** Add Git integration (status, diffs). - [ ] **SQL:** Add SQL support (Viewer and Basic Editor) (as ruby extension).
* [ ] **AI/Snippets:** - [ ] **Prolly?:** Add Splash Screen / Minigame.
* [ ] Add snippets support (LuaSnip/VSnip style).
* [ ] Add Codeium/Copilot support (using VAI virtual text) as a test phase.
* [ ] **SQL:** Add SQL support (Viewer and Basic Editor). ### Unimportant optimizations
* [ ] **Prolly?:** Add Splash Screen / Minigame.
- [ ] **Performance:**
- [ ] Switch JSON parser to `RapidJSON` (or similar high-performance lib).
- [ ] Decrease usage of `std::string` in UI, LSP, warnings etc.
- [ ] Also for vectors into managed memory especially for completions/lsp-stuff.
### Optimizations & Fluff <!-- 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 -->
* [ ] **Performance:** whyy is visible returnint x1,y2 and x2, 2y2 instead of x1,y1 and x2,y2
* [ ] Switch JSON parser to `RapidJSON` (or similar high-performance lib).
* [ ] Decrease usage of `std::string` in UI, LSP, and warnings.
* [ ] Also for vectors into managed memory especially for completions/lsp-stuff.
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
+8 -38
View File
@@ -1,3 +1,10 @@
# Files can be insluded using Kernel#require_relative
# but it can be called with binding as the second argument
# skipping it will call it with global binding which is usually fine
# Kernel#load can also be used
require_relative "theme"
# basic configuration # basic configuration
# This can also be used to do speacail configs for different projects. # This can also be used to do speacail configs for different projects.
# its ruby guys script whatever you want. # its ruby guys script whatever you want.
@@ -12,43 +19,6 @@ C.shutdown do
puts "Exiting crib..." puts "Exiting crib..."
end end
# this can be modified by the user during runtime through keybindings
# But i need to know how to ever read this value only when needed.
# maybe i can write a function that notifies if theme maybe changed then reload
# It can also be scripted to load different theme formats into a hash usable by crib
C.theme = {
:default => { fg: 0xEEEEEE },
:shebang => { fg: 0x7DCFFF },
:error => { fg: 0xEF5168 },
:comment => { fg: 0xAAAAAA, italic: true },
:string => { fg: 0xAAD94C },
:escape => { fg: 0x7DCFFF },
:interpolation => { fg: 0x7DCFFF },
:regexp => { fg: 0xD2A6FF },
:number => { fg: 0xE6C08A },
# rubocop:disable Lint/BooleanSymbol
:true => { fg: 0x7AE93C },
:false => { fg: 0xEF5168 },
# rubocop:enable Lint/BooleanSymbol
:char => { fg: 0xFFAF70 },
:keyword => { fg: 0xFF8F40 },
:keywordoperator => { fg: 0xF07178 },
:operator => { fg: 0xFFFFFF, italic: true },
:function => { fg: 0xFFAF70 },
:type => { fg: 0xF07178 },
:constant => { fg: 0x7DCFFF },
:variableinstance => { fg: 0x95E6CB },
:variableglobal => { fg: 0xF07178 },
:annotation => { fg: 0x7DCFFF },
:directive => { fg: 0xFF8F40 },
:label => { fg: 0xD2A6FF },
:brace1 => { fg: 0xD2A6FF },
:brace2 => { fg: 0xFFAFAF },
:brace3 => { fg: 0xFFFF00 },
:brace4 => { fg: 0x0FFF0F },
:brace5 => { fg: 0xFF0F0F }
}
# TODO: to be done once a proper api for binding and window drawing is made # TODO: to be done once a proper api for binding and window drawing is made
# The binds will be connected to either `editor` or windows where editor can # The binds will be connected to either `editor` or windows where editor can
# only use a preset set of stuff to bind while teh windows are purely custom # only use a preset set of stuff to bind while teh windows are purely custom
@@ -99,7 +69,7 @@ C.extra_highlights do |_line, _idx|
end end
# The highlighter will be aplied to the language as long as the langauge is defined in C.languages # The highlighter will be aplied to the language as long as the langauge is defined in C.languages
C.highlighters[:ruby_n] = { C.highlighters[:string] = {
parser: ->(line, state, line_idx) { parser: ->(line, state, line_idx) {
# the return value is a hash # the return value is a hash
# it contains the state and the highlights # it contains the state and the highlights
+36
View File
@@ -0,0 +1,36 @@
# this can be modified by the user during runtime through keybindings
# But i need to know how to ever read this value only when needed.
# maybe i can write a function that notifies if theme maybe changed then reload
# It can also be scripted to load different theme formats into a hash usable by crib
C.theme = {
:default => { fg: 0xEEEEEE },
:shebang => { fg: 0x7DCFFF },
:error => { fg: 0xEF5168 },
:comment => { fg: 0xAAAAAA, italic: true },
:string => { fg: 0xAAD94C },
:escape => { fg: 0x7DCFFF },
:interpolation => { fg: 0x7DCFFF },
:regexp => { fg: 0xD2A6FF },
:number => { fg: 0xE6C08A },
# rubocop:disable Lint/BooleanSymbol
:true => { fg: 0x7AE93C },
:false => { fg: 0xEF5168 },
# rubocop:enable Lint/BooleanSymbol
:char => { fg: 0xFFAF70 },
:keyword => { fg: 0xFF8F40 },
:keywordoperator => { fg: 0xF07178 },
:operator => { fg: 0xFFFFFF, italic: true },
:function => { fg: 0xFFAF70 },
:type => { fg: 0xF07178 },
:constant => { fg: 0x7DCFFF },
:variableinstance => { fg: 0x95E6CB },
:variableglobal => { fg: 0xF07178 },
:annotation => { fg: 0x7DCFFF },
:directive => { fg: 0xFF8F40 },
:label => { fg: 0xD2A6FF },
:brace1 => { fg: 0xD2A6FF },
:brace2 => { fg: 0xFFAFAF },
:brace3 => { fg: 0xFFFF00 },
:brace4 => { fg: 0x0FFF0F },
:brace5 => { fg: 0xFF0F0F }
}
Generated
+27
View File
@@ -0,0 +1,27 @@
{
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1783224372,
"narHash": "sha256-8i/87eeoqiGE4yOTjwSA3Eh/ziJRQEmd/unYU+K27sk=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "d407951447dcd00442e97087bf374aad70c04cea",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"nixpkgs": "nixpkgs"
}
}
},
"root": "root",
"version": 7
}
+45
View File
@@ -0,0 +1,45 @@
{
description = "crib development environment";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
};
outputs = { self, nixpkgs }:
let
system = "x86_64-linux";
pkgs = import nixpkgs {
inherit system;
};
static = pkgs.pkgsStatic;
in {
devShells.${system}.default = static.mkShell {
packages = [
pkgs.ccache
pkgs.gnumake
pkgs.bear
pkgs.ruby
pkgs.pkg-config
pkgs.xxd
pkgs.gdb
pkgs.binutils
static.pcre2
];
shellHook = ''
export PKG_CONFIG_PATH="${static.pcre2.dev}/lib/pkgconfig:$PKG_CONFIG_PATH"
echo "crib static musl dev shell"
export CC=x86_64-unknown-linux-musl-gcc
export CXX=x86_64-unknown-linux-musl-g++
export AR=x86_64-unknown-linux-musl-ar
'';
};
};
}
-45
View File
@@ -1,45 +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;
uint8_t select = 0;
std::vector<CompletionItem> items;
std::vector<uint8_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
+136 -132
View File
@@ -1,165 +1,169 @@
#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 <cstdint> #include "windows/decl.h"
#define CHAR 0 #define CHAR 0
#define WORD 1 #define WORD 1
#define LINE 2 #define LINE 2
#define EXTRA_META 4 #define EXTRA_META 2
#define INDENT_WIDTH 2 #define INDENT_WIDTH 2
// autocomplete lua// Bracket closing / tab on enter struct Editor : Window {
std::string filename = "";
struct Editor { std::string uri = "";
std::string filename; Knot *root = nullptr;
std::string uri; Coord cursor = {0, 0};
Knot *root; uint32_t cursor_preffered = 0;
std::shared_mutex knot_mtx; Coord selection = {0, 0};
Coord cursor; bool selection_active = false;
uint32_t cursor_preffered; bool unix_eol = true;
Coord selection; int selection_type = 0;
bool selection_active; Coord size = {0, 0};
bool unix_eol; // false for windows Coord scroll = {0, 0};
int selection_type; Language lang = {};
Coord position; bool jumper_set = false;
Coord size; std::vector<VWarn> warnings = {};
Coord scroll; bool warnings_dirty = false;
Language lang; VAI ai = {};
uint32_t hooks[94];
bool jumper_set;
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
-18
View File
@@ -3,22 +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 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
+6
View File
@@ -112,12 +112,18 @@ struct IndentationEngine {
uint32_t set_indent(uint32_t row, int64_t indent_level); uint32_t set_indent(uint32_t row, int64_t indent_level);
uint32_t indent_line(uint32_t row); uint32_t indent_line(uint32_t row);
uint32_t dedent_line(uint32_t row); uint32_t dedent_line(uint32_t row);
void indent_block(uint32_t start_row, uint32_t end_row, int delta);
void indent_block(uint32_t start, uint32_t end); void indent_block(uint32_t start, uint32_t end);
void dedent_block(uint32_t start, uint32_t end); void dedent_block(uint32_t start, uint32_t end);
// fixes a autocomplete block's indentation // fixes a autocomplete block's indentation
char *block_to_asis(Coord cursor, std::string source, uint32_t *out_len); char *block_to_asis(Coord cursor, std::string source, uint32_t *out_len);
private: private:
const std::vector<std::string> *start_end = nullptr;
const std::vector<std::string> *start_start = nullptr;
const std::vector<std::string> *end_full = nullptr;
const std::vector<std::string> *end_start = nullptr;
// TODO: Ignore comments/strings too // TODO: Ignore comments/strings too
// returns the indent level of the line itself or of the previous non-empty // returns the indent level of the line itself or of the previous non-empty
uint32_t indent_expected(uint32_t row); uint32_t indent_expected(uint32_t row);
+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
+2 -2
View File
@@ -115,8 +115,8 @@ LineIterator *begin_l_iter(Knot *root, uint32_t start_line);
// Each subsequent call returns the next line as a null terminated string // Each subsequent call returns the next line as a null terminated string
// `it` is the iterator returned from begin_l_iter // `it` is the iterator returned from begin_l_iter
// After getting the necessary lines free the iterator (no need to go upto // After getting the necessary lines free the iterator (no need to go upto
// the end) returns null if there are no more lines All return strings // the end) returns null if there are no more lines
// `must` be freed by the caller // The string must not be freed
char *next_line(LineIterator *it, uint32_t *out_len); char *next_line(LineIterator *it, uint32_t *out_len);
// Returns the previous line as a null terminated string // Returns the previous line as a null terminated string
+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 -55
View File
@@ -2,54 +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
std::string method; #define LSP_TIMEOUT_QUIT_NORMAL 300
Editor *editor = nullptr; #define LSP_TIMEOUT_QUIT_FORCE 80
std::function<void(Editor *, std::string, json)> callback; void lsp_worker();
};
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}}},
@@ -73,19 +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>>
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
+6 -2
View File
@@ -2,6 +2,7 @@
#define MAIN_H #define MAIN_H
#include "pch.h" #include "pch.h"
#include "ui/bar.h"
#define NORMAL 0 #define NORMAL 0
#define INSERT 1 #define INSERT 1
@@ -11,7 +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;
} // namespace ui
#endif #endif
+9 -7
View File
@@ -4,12 +4,12 @@
#define PCRE2_CODE_UNIT_WIDTH 8 #define PCRE2_CODE_UNIT_WIDTH 8
#define PCRE_WORKSPACE_SIZE 512 #define PCRE_WORKSPACE_SIZE 512
#include <mruby.h> #include "mruby.h"
#include <mruby/array.h> #include "mruby/array.h"
#include <mruby/compile.h> #include "mruby/compile.h"
#include <mruby/hash.h> #include "mruby/hash.h"
#include <mruby/irep.h> #include "mruby/irep.h"
#include <mruby/string.h> #include "mruby/string.h"
#include <nlohmann/json.hpp> #include <nlohmann/json.hpp>
#include <pcre2.h> #include <pcre2.h>
extern "C" { extern "C" {
@@ -31,10 +31,12 @@ 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>
#include <optional> #include <optional>
#include <poll.h>
#include <queue> #include <queue>
#include <set> #include <set>
#include <shared_mutex> #include <shared_mutex>
@@ -43,13 +45,13 @@ extern "C" {
#include <string.h> #include <string.h>
#include <string> #include <string>
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <sys/poll.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/wait.h> #include <sys/wait.h>
#include <termios.h> #include <termios.h>
#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;
+52
View File
@@ -0,0 +1,52 @@
#ifndef SCRIPTING_DECL_H
#define SCRIPTING_DECL_H
#include "syntax/decl.h"
#include "utils/utils.h"
#include "windows/decl.h"
namespace fs = std::filesystem;
extern std::unordered_map<std::string, std::pair<mrb_value, mrb_value>>
custom_highlighters;
extern mrb_state *mrb;
extern fs::path ruby_config_path;
struct BarLight {
uint32_t start;
uint32_t end;
Highlight highlight;
};
struct BarLine {
std::string line;
std::vector<BarLight> highlights;
Highlight get_highlight(uint32_t x) {
for (auto &hl : highlights) {
if (hl.start <= x && x <= hl.end)
return hl.highlight;
}
return {0xFFFFFF, 0, 0};
}
};
void setup_ruby_bindings(mrb_state *mrb, RClass *C_module);
void ruby_start();
void ruby_shutdown();
void ruby_copy(const char *text, size_t len);
std::string ruby_paste();
std::string ruby_file_detect(std::string filename);
void load_theme();
void load_languages_info();
uint8_t read_line_endings();
void load_custom_highlighters();
bool custom_compare(mrb_value match_block, std::string state1,
std::string state2);
std::string parse_custom(std::vector<Token> *tokens, mrb_value parser_block,
const char *line, uint32_t len, std::string state,
uint32_t c_line);
BarLine bar_contents(uint8_t mode, uint32_t width, std::string foldername,
Window *window);
#endif
@@ -1,3 +1,66 @@
def command_exists?(cmd)
system("command -v #{cmd} > /dev/null 2>&1")
end
module Clipboard
@clip = ""
@os = :os_name_placed_here
class << self
def copy(text)
if @os == :windows
IO.popen("clip", "w") { |f| f.write(text) }
elsif @os == :mac
IO.popen("pbcopy", "w") { |f| f.write(text) }
elsif @os == :linux
if ENV["XDG_SESSION_TYPE"]&.downcase == "wayland" || ENV["WAYLAND_DISPLAY"]
if command_exists?("wl-copy")
IO.popen("wl-copy", "w") { |f| f.write(text) }
else
osc52_copy(text)
end
elsif ENV["XDG_SESSION_TYPE"]&.downcase == "x11" || ENV["DISPLAY"]
if command_exists?("xsel")
IO.popen("xsel --clipboard --input", "w") { |f| f.write(text) }
elsif command_exists?("xclip")
IO.popen("xclip -selection clipboard", "w") { |f| f.write(text) }
else
osc52_copy(text)
end
end
end
@clip = text
end
def paste
if @os == :windows
return `powershell -NoProfile -Command Get-Clipboard`
elsif @os == :mac
return `pbpaste`
elsif @os == :linux
if ENV["XDG_SESSION_TYPE"]&.downcase == "wayland" || ENV["WAYLAND_DISPLAY"]
if command_exists?("wl-copy")
return `wl-paste`
end
elsif ENV["XDG_SESSION_TYPE"]&.downcase == "x11" || ENV["DISPLAY"]
if command_exists?("xsel")
return `xsel --clipboard --output`
elsif command_exists?("xclip")
return `xclip -selection clipboard -o`
else
return @clip
end
end
end
return ""
end
def osc52_copy(text)
encoded = [text].pack("m0")
print "\e]52;c;#{encoded}\a"
text
end
end
end
module C module C
@lsp_config = { @lsp_config = {
"clangd" => [ "clangd" => [
@@ -114,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,
@@ -224,6 +287,11 @@ module C
extensions: ["sh"], extensions: ["sh"],
filenames: ["bash_profile", "bashrc"], filenames: ["bash_profile", "bashrc"],
lsp: "bash-language-server" lsp: "bash-language-server"
},
default: {
color: 0x6d8086,
symbol: "󰈚 ",
extensions: []
} }
} }
@theme = { @theme = {
@@ -262,11 +330,98 @@ module C
@highlighters = {} @highlighters = {}
@b_startup = nil @b_startup = nil
@b_shutdown = nil @b_shutdown = nil
@b_bar = proc do |info|
# mode, width, data[5] : strings (any data about the focused window)
# puts info.inspect
mode_color = 0x82AAFF
mode_symbol = " "
case info[:mode]
when :normal
mode_color = 0x82AAFF
mode_symbol = ""
when :insert
mode_color = 0xFF8F40
mode_symbol = "󱓧 "
when :select
mode_color = 0x9ADE7A
mode_symbol = "󱩧 "
when :runner
mode_color = 0xFFD700
mode_symbol = ""
when :jumper
mode_color = 0xF29CC3
mode_symbol = ""
end
lang_info = C.languages[:default]
filename = ""
if info[:data][0] == "editor"
lang_info = C.languages[info[:data][2]] if info[:data][2] != ""
if lang_info.nil?
lang_info = C.languages[:default]
end
filename = File.basename(info[:data][1]) if info[:data][1] != ""
end
starting = " #{mode_symbol} #{info[:mode].to_s.upcase}  #{lang_info[:symbol]} #{filename}"
highlights = []
highlights << { fg: 0x0b0e14, bg: mode_color, flags: 1 << 1, start: 0, length: 10 }
highlights << { fg: mode_color, bg: 0x33363c, start: 10, length: 1 }
highlights << { fg: 0x33363c, bg: 0x24272d, start: 11, length: 1 }
highlights << { fg: lang_info[:color], bg: 0x24272d, start: 13, length: 2 }
highlights << { fg: 0xced4df, bg: 0x24272d, start: 15, length: filename.length + 1 }
highlights << { fg: 0x24272d, bg: 0x000000, start: 15 + filename.length + 1, length: 1 }
next {
text: starting,
highlights: highlights
}
end
@b_copy = proc do |text|
Clipboard.copy(text)
end
@b_paste = proc do
next Clipboard.paste
end
@b_file_detect = proc do |filename|
type = :default
next type unless File.exist?(filename)
first_line = File.open(filename, &:readline).chomp
if first_line.start_with?("#!")
shebang = first_line[2..].downcase
type = case shebang
when /bash/, /sh/ then :bash
when /fish/ then :fish
when /python/ then :python
when /ruby/ then :ruby
when /lua/ then :lua
else :default
end
next type
end
next type if :os_name_placed_here != :linux || :os_name_placed_here != :mac
next type if !command_exists?("file")
mimetype = `file --mime-type -b #{filename}`.chomp
type = case mimetype
when /shellscript/ then :bash
when /ruby/ then :ruby
when /diff/ then :diff
when /html/ then :html
when /python/ then :python
when /javascript/ then :javascript
when /makefile/ then :makefile
when /-c$/ then :c
else :default
end
next type
end
class << self class << self
attr_accessor :theme, :lsp_config, :languages, attr_accessor :theme, :lsp_config, :languages,
:line_endings, :highlighters :line_endings, :highlighters
attr_reader :b_startup, :b_shutdown, :b_extra_highlights attr_reader :b_startup, :b_shutdown, :b_extra_highlights,
:b_bar, :b_copy, :b_paste, :b_file_detect
def bar=(&block)
@b_bar = block
end
def startup(&block) def startup(&block)
@b_startup = block @b_startup = block
@@ -276,6 +431,18 @@ module C
@b_shutdown = block @b_shutdown = block
end end
def copy(&block)
@b_copy = block
end
def paste(&block)
@b_paste = block
end
def file_detect(&block)
@b_file_detect = block
end
def extra_highlights(&block) def extra_highlights(&block)
@b_extra_highlights = block @b_extra_highlights = block
end end
@@ -311,3 +478,23 @@ module C
end end
end end
end end
$LOADED ||= []
$BIND_TOP = binding
module Kernel
def require_relative(path, bind = nil)
path += ".rb" unless path.end_with?(".rb")
path = File.expand_path(path, File.dirname(C.config_file))
return if $LOADED.include?(path)
$LOADED << path
code = File.read(path)
eval(code, bind || $BIND_TOP, path)
end
def load(path, bind = nil)
path += ".rb" unless path.end_with?(".rb")
path = File.expand_path(path, File.dirname(C.config_file))
$LOADED.delete(path)
require_relative(path, bind)
end
end
+955
View File
@@ -0,0 +1,955 @@
#pragma once
constexpr unsigned char _tmp___crib_precompiled_mrb[] = {
0x52, 0x49, 0x54, 0x45, 0x30, 0x34, 0x30, 0x30, 0x00, 0x00, 0x2c, 0x93,
0x48, 0x53, 0x4d, 0x4b, 0x30, 0x30, 0x30, 0x30, 0x49, 0x52, 0x45, 0x50,
0x00, 0x00, 0x2a, 0xff, 0x30, 0x34, 0x30, 0x30, 0x00, 0x00, 0x00, 0xa2,
0x00, 0x01, 0x00, 0x04, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a,
0x11, 0x01, 0x68, 0x01, 0x00, 0x69, 0x01, 0x00, 0x6b, 0x01, 0x01, 0x01,
0x11, 0x01, 0x68, 0x01, 0x02, 0x69, 0x01, 0x02, 0x11, 0x01, 0x68, 0x01,
0x03, 0x69, 0x01, 0x03, 0x15, 0x01, 0x04, 0x27, 0x01, 0x00, 0x06, 0x52,
0x01, 0x00, 0x16, 0x01, 0x04, 0x30, 0x02, 0x05, 0x16, 0x02, 0x06, 0x11,
0x02, 0x68, 0x02, 0x07, 0x69, 0x02, 0x04, 0x3d, 0x02, 0x76, 0x00, 0x00,
0x00, 0x08, 0x00, 0x06, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x00, 0x00,
0x0f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x5f, 0x65, 0x78, 0x69,
0x73, 0x74, 0x73, 0x3f, 0x00, 0x00, 0x09, 0x43, 0x6c, 0x69, 0x70, 0x62,
0x6f, 0x61, 0x72, 0x64, 0x00, 0x00, 0x01, 0x43, 0x00, 0x00, 0x07, 0x24,
0x4c, 0x4f, 0x41, 0x44, 0x45, 0x44, 0x00, 0x00, 0x07, 0x62, 0x69, 0x6e,
0x64, 0x69, 0x6e, 0x67, 0x00, 0x00, 0x09, 0x24, 0x42, 0x49, 0x4e, 0x44,
0x5f, 0x54, 0x4f, 0x50, 0x00, 0x00, 0x06, 0x4b, 0x65, 0x72, 0x6e, 0x65,
0x6c, 0x00, 0x00, 0x00, 0x03, 0xfc, 0x00, 0x01, 0x00, 0x03, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0x3b, 0x06, 0x01, 0x1e, 0x01, 0x00, 0x07,
0x01, 0x1e, 0x01, 0x01, 0x08, 0x01, 0x1e, 0x01, 0x02, 0x09, 0x01, 0x1e,
0x01, 0x03, 0x0a, 0x01, 0x1e, 0x01, 0x04, 0x0b, 0x01, 0x1e, 0x01, 0x05,
0x0c, 0x01, 0x1e, 0x01, 0x06, 0x0d, 0x01, 0x1e, 0x01, 0x07, 0x03, 0x01,
0x08, 0x1e, 0x01, 0x08, 0x03, 0x01, 0x09, 0x1e, 0x01, 0x09, 0x03, 0x01,
0x0a, 0x1e, 0x01, 0x0a, 0x03, 0x01, 0x0b, 0x1e, 0x01, 0x0b, 0x03, 0x01,
0x0c, 0x1e, 0x01, 0x0c, 0x03, 0x01, 0x0d, 0x1e, 0x01, 0x0d, 0x03, 0x01,
0x0e, 0x1e, 0x01, 0x0e, 0x03, 0x01, 0x0f, 0x1e, 0x01, 0x0f, 0x03, 0x01,
0x10, 0x1e, 0x01, 0x10, 0x03, 0x01, 0x11, 0x1e, 0x01, 0x11, 0x03, 0x01,
0x12, 0x1e, 0x01, 0x12, 0x03, 0x01, 0x13, 0x1e, 0x01, 0x13, 0x03, 0x01,
0x14, 0x1e, 0x01, 0x14, 0x03, 0x01, 0x15, 0x1e, 0x01, 0x15, 0x03, 0x01,
0x16, 0x1e, 0x01, 0x16, 0x03, 0x01, 0x17, 0x1e, 0x01, 0x17, 0x03, 0x01,
0x18, 0x1e, 0x01, 0x18, 0x03, 0x01, 0x19, 0x1e, 0x01, 0x19, 0x03, 0x01,
0x1a, 0x1e, 0x01, 0x1a, 0x03, 0x01, 0x1b, 0x1e, 0x01, 0x1b, 0x03, 0x01,
0x1c, 0x1e, 0x01, 0x1c, 0x03, 0x01, 0x1d, 0x1e, 0x01, 0x1d, 0x03, 0x01,
0x1e, 0x1e, 0x01, 0x1e, 0x03, 0x01, 0x1f, 0x1e, 0x01, 0x1f, 0x03, 0x01,
0x20, 0x1e, 0x01, 0x20, 0x03, 0x01, 0x21, 0x1e, 0x01, 0x21, 0x03, 0x01,
0x22, 0x1e, 0x01, 0x22, 0x03, 0x01, 0x23, 0x1e, 0x01, 0x23, 0x03, 0x01,
0x24, 0x1e, 0x01, 0x24, 0x03, 0x01, 0x25, 0x1e, 0x01, 0x25, 0x03, 0x01,
0x26, 0x1e, 0x01, 0x26, 0x03, 0x01, 0x27, 0x1e, 0x01, 0x27, 0x03, 0x01,
0x28, 0x1e, 0x01, 0x28, 0x03, 0x01, 0x29, 0x1e, 0x01, 0x29, 0x03, 0x01,
0x2a, 0x1e, 0x01, 0x2a, 0x03, 0x01, 0x2b, 0x1e, 0x01, 0x2b, 0x03, 0x01,
0x2c, 0x1e, 0x01, 0x2c, 0x03, 0x01, 0x2d, 0x1e, 0x01, 0x2d, 0x03, 0x01,
0x2e, 0x1e, 0x01, 0x2e, 0x03, 0x01, 0x2f, 0x1e, 0x01, 0x2f, 0x03, 0x01,
0x30, 0x1e, 0x01, 0x30, 0x03, 0x01, 0x31, 0x1e, 0x01, 0x31, 0x03, 0x01,
0x32, 0x1e, 0x01, 0x32, 0x03, 0x01, 0x33, 0x1e, 0x01, 0x33, 0x03, 0x01,
0x34, 0x1e, 0x01, 0x34, 0x30, 0x01, 0x35, 0x3d, 0x01, 0x00, 0x00, 0x00,
0x36, 0x00, 0x06, 0x4b, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x00, 0x00, 0x09,
0x4b, 0x5f, 0x53, 0x48, 0x45, 0x42, 0x41, 0x4e, 0x47, 0x00, 0x00, 0x09,
0x4b, 0x5f, 0x43, 0x4f, 0x4d, 0x4d, 0x45, 0x4e, 0x54, 0x00, 0x00, 0x07,
0x4b, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x00, 0x00, 0x08, 0x4b, 0x5f,
0x53, 0x54, 0x52, 0x49, 0x4e, 0x47, 0x00, 0x00, 0x08, 0x4b, 0x5f, 0x45,
0x53, 0x43, 0x41, 0x50, 0x45, 0x00, 0x00, 0x0f, 0x4b, 0x5f, 0x49, 0x4e,
0x54, 0x45, 0x52, 0x50, 0x4f, 0x4c, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x00,
0x00, 0x08, 0x4b, 0x5f, 0x52, 0x45, 0x47, 0x45, 0x58, 0x50, 0x00, 0x00,
0x08, 0x4b, 0x5f, 0x4e, 0x55, 0x4d, 0x42, 0x45, 0x52, 0x00, 0x00, 0x06,
0x4b, 0x5f, 0x54, 0x52, 0x55, 0x45, 0x00, 0x00, 0x07, 0x4b, 0x5f, 0x46,
0x41, 0x4c, 0x53, 0x45, 0x00, 0x00, 0x06, 0x4b, 0x5f, 0x43, 0x48, 0x41,
0x52, 0x00, 0x00, 0x09, 0x4b, 0x5f, 0x4b, 0x45, 0x59, 0x57, 0x4f, 0x52,
0x44, 0x00, 0x00, 0x11, 0x4b, 0x5f, 0x4b, 0x45, 0x59, 0x57, 0x4f, 0x52,
0x44, 0x4f, 0x50, 0x45, 0x52, 0x41, 0x54, 0x4f, 0x52, 0x00, 0x00, 0x0a,
0x4b, 0x5f, 0x4f, 0x50, 0x45, 0x52, 0x41, 0x54, 0x4f, 0x52, 0x00, 0x00,
0x0a, 0x4b, 0x5f, 0x46, 0x55, 0x4e, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x00,
0x00, 0x06, 0x4b, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x00, 0x00, 0x0a, 0x4b,
0x5f, 0x43, 0x4f, 0x4e, 0x53, 0x54, 0x41, 0x4e, 0x54, 0x00, 0x00, 0x12,
0x4b, 0x5f, 0x56, 0x41, 0x52, 0x49, 0x41, 0x42, 0x4c, 0x45, 0x49, 0x4e,
0x53, 0x54, 0x41, 0x4e, 0x43, 0x45, 0x00, 0x00, 0x10, 0x4b, 0x5f, 0x56,
0x41, 0x52, 0x49, 0x41, 0x42, 0x4c, 0x45, 0x47, 0x4c, 0x4f, 0x42, 0x41,
0x4c, 0x00, 0x00, 0x0c, 0x4b, 0x5f, 0x41, 0x4e, 0x4e, 0x4f, 0x54, 0x41,
0x54, 0x49, 0x4f, 0x4e, 0x00, 0x00, 0x0b, 0x4b, 0x5f, 0x44, 0x49, 0x52,
0x45, 0x43, 0x54, 0x49, 0x56, 0x45, 0x00, 0x00, 0x07, 0x4b, 0x5f, 0x4c,
0x41, 0x42, 0x45, 0x4c, 0x00, 0x00, 0x08, 0x4b, 0x5f, 0x42, 0x52, 0x41,
0x43, 0x45, 0x31, 0x00, 0x00, 0x08, 0x4b, 0x5f, 0x42, 0x52, 0x41, 0x43,
0x45, 0x32, 0x00, 0x00, 0x08, 0x4b, 0x5f, 0x42, 0x52, 0x41, 0x43, 0x45,
0x33, 0x00, 0x00, 0x08, 0x4b, 0x5f, 0x42, 0x52, 0x41, 0x43, 0x45, 0x34,
0x00, 0x00, 0x08, 0x4b, 0x5f, 0x42, 0x52, 0x41, 0x43, 0x45, 0x35, 0x00,
0x00, 0x0a, 0x4b, 0x5f, 0x48, 0x45, 0x41, 0x44, 0x49, 0x4e, 0x47, 0x31,
0x00, 0x00, 0x0a, 0x4b, 0x5f, 0x48, 0x45, 0x41, 0x44, 0x49, 0x4e, 0x47,
0x32, 0x00, 0x00, 0x0a, 0x4b, 0x5f, 0x48, 0x45, 0x41, 0x44, 0x49, 0x4e,
0x47, 0x33, 0x00, 0x00, 0x0a, 0x4b, 0x5f, 0x48, 0x45, 0x41, 0x44, 0x49,
0x4e, 0x47, 0x34, 0x00, 0x00, 0x0a, 0x4b, 0x5f, 0x48, 0x45, 0x41, 0x44,
0x49, 0x4e, 0x47, 0x35, 0x00, 0x00, 0x0a, 0x4b, 0x5f, 0x48, 0x45, 0x41,
0x44, 0x49, 0x4e, 0x47, 0x36, 0x00, 0x00, 0x0c, 0x4b, 0x5f, 0x42, 0x4c,
0x4f, 0x43, 0x4b, 0x51, 0x55, 0x4f, 0x54, 0x45, 0x00, 0x00, 0x06, 0x4b,
0x5f, 0x4c, 0x49, 0x53, 0x54, 0x00, 0x00, 0x0a, 0x4b, 0x5f, 0x4c, 0x49,
0x53, 0x54, 0x49, 0x54, 0x45, 0x4d, 0x00, 0x00, 0x06, 0x4b, 0x5f, 0x43,
0x4f, 0x44, 0x45, 0x00, 0x00, 0x0e, 0x4b, 0x5f, 0x4c, 0x41, 0x4e, 0x47,
0x55, 0x41, 0x47, 0x45, 0x4e, 0x41, 0x4d, 0x45, 0x00, 0x00, 0x0b, 0x4b,
0x5f, 0x4c, 0x49, 0x4e, 0x4b, 0x4c, 0x41, 0x42, 0x45, 0x4c, 0x00, 0x00,
0x0c, 0x4b, 0x5f, 0x49, 0x4d, 0x41, 0x47, 0x45, 0x4c, 0x41, 0x42, 0x45,
0x4c, 0x00, 0x00, 0x06, 0x4b, 0x5f, 0x4c, 0x49, 0x4e, 0x4b, 0x00, 0x00,
0x07, 0x4b, 0x5f, 0x54, 0x41, 0x42, 0x4c, 0x45, 0x00, 0x00, 0x0d, 0x4b,
0x5f, 0x54, 0x41, 0x42, 0x4c, 0x45, 0x48, 0x45, 0x41, 0x44, 0x45, 0x52,
0x00, 0x00, 0x08, 0x4b, 0x5f, 0x49, 0x54, 0x41, 0x4c, 0x49, 0x43, 0x00,
0x00, 0x06, 0x4b, 0x5f, 0x42, 0x4f, 0x4c, 0x44, 0x00, 0x00, 0x0b, 0x4b,
0x5f, 0x55, 0x4e, 0x44, 0x45, 0x52, 0x4c, 0x49, 0x4e, 0x45, 0x00, 0x00,
0x0f, 0x4b, 0x5f, 0x53, 0x54, 0x52, 0x49, 0x4b, 0x45, 0x54, 0x48, 0x52,
0x4f, 0x55, 0x47, 0x48, 0x00, 0x00, 0x10, 0x4b, 0x5f, 0x48, 0x4f, 0x52,
0x49, 0x58, 0x4f, 0x4e, 0x54, 0x41, 0x4c, 0x52, 0x55, 0x4c, 0x45, 0x00,
0x00, 0x05, 0x4b, 0x5f, 0x54, 0x41, 0x47, 0x00, 0x00, 0x0b, 0x4b, 0x5f,
0x41, 0x54, 0x54, 0x52, 0x49, 0x42, 0x55, 0x54, 0x45, 0x00, 0x00, 0x0b,
0x4b, 0x5f, 0x43, 0x48, 0x45, 0x43, 0x4b, 0x44, 0x4f, 0x4e, 0x45, 0x00,
0x00, 0x0e, 0x4b, 0x5f, 0x43, 0x48, 0x45, 0x43, 0x4b, 0x4e, 0x4f, 0x54,
0x44, 0x4f, 0x4e, 0x45, 0x00, 0x00, 0x06, 0x66, 0x72, 0x65, 0x65, 0x7a,
0x65, 0x00, 0x00, 0x00, 0x00, 0x58, 0x00, 0x03, 0x00, 0x06, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x39, 0x04, 0x00, 0x00, 0x5c, 0x04,
0x00, 0x01, 0x05, 0x01, 0x5d, 0x04, 0x5c, 0x05, 0x01, 0x5d, 0x04, 0x2f,
0x03, 0x00, 0x01, 0x3d, 0x03, 0x00, 0x02, 0x00, 0x00, 0x0b, 0x63, 0x6f,
0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x20, 0x2d, 0x76, 0x20, 0x00, 0x00, 0x00,
0x11, 0x20, 0x3e, 0x20, 0x2f, 0x64, 0x65, 0x76, 0x2f, 0x6e, 0x75, 0x6c,
0x6c, 0x20, 0x32, 0x3e, 0x26, 0x31, 0x00, 0x00, 0x01, 0x00, 0x06, 0x73,
0x79, 0x73, 0x74, 0x65, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x43, 0x00, 0x01,
0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x5c, 0x01,
0x00, 0x1a, 0x01, 0x00, 0x10, 0x01, 0x01, 0x1a, 0x01, 0x02, 0x12, 0x01,
0x6f, 0x01, 0x69, 0x01, 0x00, 0x3d, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x03, 0x00, 0x05, 0x40, 0x63, 0x6c, 0x69, 0x70, 0x00, 0x00,
0x05, 0x6c, 0x69, 0x6e, 0x75, 0x78, 0x00, 0x00, 0x03, 0x40, 0x6f, 0x73,
0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00,
0x00, 0x00, 0x00, 0x00, 0x0e, 0x6b, 0x01, 0x00, 0x00, 0x6b, 0x01, 0x01,
0x01, 0x6b, 0x01, 0x02, 0x02, 0x3d, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00,
0x04, 0x63, 0x6f, 0x70, 0x79, 0x00, 0x00, 0x05, 0x70, 0x61, 0x73, 0x74,
0x65, 0x00, 0x00, 0x0a, 0x6f, 0x73, 0x63, 0x35, 0x32, 0x5f, 0x63, 0x6f,
0x70, 0x79, 0x00, 0x00, 0x00, 0x02, 0x37, 0x00, 0x03, 0x00, 0x07, 0x00,
0x05, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0f, 0x39, 0x04, 0x00, 0x00, 0x19,
0x03, 0x00, 0x10, 0x04, 0x01, 0x4d, 0x03, 0x28, 0x03, 0x00, 0x13, 0x1d,
0x03, 0x02, 0x5c, 0x04, 0x00, 0x5c, 0x05, 0x01, 0x62, 0x06, 0x00, 0x34,
0x03, 0x03, 0x02, 0x26, 0x00, 0xe4, 0x19, 0x03, 0x00, 0x10, 0x04, 0x04,
0x4d, 0x03, 0x28, 0x03, 0x00, 0x13, 0x1d, 0x03, 0x02, 0x5c, 0x04, 0x02,
0x5c, 0x05, 0x01, 0x62, 0x06, 0x01, 0x34, 0x03, 0x03, 0x02, 0x26, 0x00,
0xc5, 0x19, 0x03, 0x00, 0x10, 0x04, 0x05, 0x4d, 0x03, 0x28, 0x03, 0x00,
0xb9, 0x1d, 0x03, 0x06, 0x5c, 0x04, 0x03, 0x23, 0x03, 0x01, 0x04, 0x03,
0x29, 0x04, 0x00, 0x03, 0x33, 0x03, 0x07, 0x5c, 0x04, 0x04, 0x4d, 0x03,
0x27, 0x03, 0x00, 0x08, 0x1d, 0x03, 0x06, 0x5c, 0x04, 0x05, 0x23, 0x03,
0x28, 0x03, 0x00, 0x28, 0x5c, 0x04, 0x06, 0x2f, 0x03, 0x08, 0x01, 0x28,
0x03, 0x00, 0x13, 0x1d, 0x03, 0x02, 0x5c, 0x04, 0x06, 0x5c, 0x05, 0x01,
0x62, 0x06, 0x02, 0x34, 0x03, 0x03, 0x02, 0x26, 0x00, 0x07, 0x01, 0x04,
0x01, 0x2f, 0x03, 0x09, 0x01, 0x26, 0x00, 0x6a, 0x1d, 0x03, 0x06, 0x5c,
0x04, 0x03, 0x23, 0x03, 0x01, 0x04, 0x03, 0x29, 0x04, 0x00, 0x03, 0x33,
0x03, 0x07, 0x5c, 0x04, 0x07, 0x4d, 0x03, 0x27, 0x03, 0x00, 0x08, 0x1d,
0x03, 0x06, 0x5c, 0x04, 0x08, 0x23, 0x03, 0x28, 0x03, 0x00, 0x43, 0x5c,
0x04, 0x09, 0x2f, 0x03, 0x08, 0x01, 0x28, 0x03, 0x00, 0x13, 0x1d, 0x03,
0x02, 0x5c, 0x04, 0x0a, 0x5c, 0x05, 0x01, 0x62, 0x06, 0x03, 0x34, 0x03,
0x03, 0x02, 0x26, 0x00, 0x25, 0x5c, 0x04, 0x0b, 0x2f, 0x03, 0x08, 0x01,
0x28, 0x03, 0x00, 0x13, 0x1d, 0x03, 0x02, 0x5c, 0x04, 0x0c, 0x5c, 0x05,
0x01, 0x62, 0x06, 0x04, 0x34, 0x03, 0x03, 0x02, 0x26, 0x00, 0x07, 0x01,
0x04, 0x01, 0x2f, 0x03, 0x09, 0x01, 0x01, 0x03, 0x01, 0x1a, 0x03, 0x0a,
0x3d, 0x03, 0x00, 0x0d, 0x00, 0x00, 0x04, 0x63, 0x6c, 0x69, 0x70, 0x00,
0x00, 0x00, 0x01, 0x77, 0x00, 0x00, 0x00, 0x06, 0x70, 0x62, 0x63, 0x6f,
0x70, 0x79, 0x00, 0x00, 0x00, 0x10, 0x58, 0x44, 0x47, 0x5f, 0x53, 0x45,
0x53, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x00, 0x00,
0x00, 0x07, 0x77, 0x61, 0x79, 0x6c, 0x61, 0x6e, 0x64, 0x00, 0x00, 0x00,
0x0f, 0x57, 0x41, 0x59, 0x4c, 0x41, 0x4e, 0x44, 0x5f, 0x44, 0x49, 0x53,
0x50, 0x4c, 0x41, 0x59, 0x00, 0x00, 0x00, 0x07, 0x77, 0x6c, 0x2d, 0x63,
0x6f, 0x70, 0x79, 0x00, 0x00, 0x00, 0x03, 0x78, 0x31, 0x31, 0x00, 0x00,
0x00, 0x07, 0x44, 0x49, 0x53, 0x50, 0x4c, 0x41, 0x59, 0x00, 0x00, 0x00,
0x04, 0x78, 0x73, 0x65, 0x6c, 0x00, 0x00, 0x00, 0x18, 0x78, 0x73, 0x65,
0x6c, 0x20, 0x2d, 0x2d, 0x63, 0x6c, 0x69, 0x70, 0x62, 0x6f, 0x61, 0x72,
0x64, 0x20, 0x2d, 0x2d, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x00, 0x00, 0x00,
0x05, 0x78, 0x63, 0x6c, 0x69, 0x70, 0x00, 0x00, 0x00, 0x1a, 0x78, 0x63,
0x6c, 0x69, 0x70, 0x20, 0x2d, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69,
0x6f, 0x6e, 0x20, 0x63, 0x6c, 0x69, 0x70, 0x62, 0x6f, 0x61, 0x72, 0x64,
0x00, 0x00, 0x0b, 0x00, 0x03, 0x40, 0x6f, 0x73, 0x00, 0x00, 0x07, 0x77,
0x69, 0x6e, 0x64, 0x6f, 0x77, 0x73, 0x00, 0x00, 0x02, 0x49, 0x4f, 0x00,
0x00, 0x05, 0x70, 0x6f, 0x70, 0x65, 0x6e, 0x00, 0x00, 0x03, 0x6d, 0x61,
0x63, 0x00, 0x00, 0x05, 0x6c, 0x69, 0x6e, 0x75, 0x78, 0x00, 0x00, 0x03,
0x45, 0x4e, 0x56, 0x00, 0x00, 0x08, 0x64, 0x6f, 0x77, 0x6e, 0x63, 0x61,
0x73, 0x65, 0x00, 0x00, 0x0f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64,
0x5f, 0x65, 0x78, 0x69, 0x73, 0x74, 0x73, 0x3f, 0x00, 0x00, 0x0a, 0x6f,
0x73, 0x63, 0x35, 0x32, 0x5f, 0x63, 0x6f, 0x70, 0x79, 0x00, 0x00, 0x05,
0x40, 0x63, 0x6c, 0x69, 0x70, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x03,
0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x39, 0x04,
0x00, 0x00, 0x01, 0x03, 0x01, 0x21, 0x04, 0x01, 0x00, 0x32, 0x03, 0x00,
0x01, 0x3d, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x05, 0x77, 0x72, 0x69,
0x74, 0x65, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x03, 0x00, 0x06, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x39, 0x04, 0x00, 0x00, 0x01,
0x03, 0x01, 0x21, 0x04, 0x01, 0x00, 0x32, 0x03, 0x00, 0x01, 0x3d, 0x03,
0x00, 0x00, 0x00, 0x01, 0x00, 0x05, 0x77, 0x72, 0x69, 0x74, 0x65, 0x00,
0x00, 0x00, 0x00, 0x2d, 0x00, 0x03, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x11, 0x39, 0x04, 0x00, 0x00, 0x01, 0x03, 0x01, 0x21,
0x04, 0x01, 0x00, 0x32, 0x03, 0x00, 0x01, 0x3d, 0x03, 0x00, 0x00, 0x00,
0x01, 0x00, 0x05, 0x77, 0x72, 0x69, 0x74, 0x65, 0x00, 0x00, 0x00, 0x00,
0x2d, 0x00, 0x03, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x11, 0x39, 0x04, 0x00, 0x00, 0x01, 0x03, 0x01, 0x21, 0x04, 0x01, 0x00,
0x32, 0x03, 0x00, 0x01, 0x3d, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x05,
0x77, 0x72, 0x69, 0x74, 0x65, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x03,
0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x39, 0x04,
0x00, 0x00, 0x01, 0x03, 0x01, 0x21, 0x04, 0x01, 0x00, 0x32, 0x03, 0x00,
0x01, 0x3d, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x05, 0x77, 0x72, 0x69,
0x74, 0x65, 0x00, 0x00, 0x00, 0x02, 0x31, 0x00, 0x02, 0x00, 0x05, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe7, 0x39, 0x00, 0x00, 0x00, 0x19,
0x02, 0x00, 0x10, 0x03, 0x01, 0x4d, 0x02, 0x28, 0x02, 0x00, 0x0e, 0x12,
0x02, 0x5c, 0x03, 0x00, 0x2f, 0x02, 0x02, 0x01, 0x3d, 0x02, 0x26, 0x00,
0xc4, 0x19, 0x02, 0x00, 0x10, 0x03, 0x03, 0x4d, 0x02, 0x28, 0x02, 0x00,
0x0e, 0x12, 0x02, 0x5c, 0x03, 0x01, 0x2f, 0x02, 0x02, 0x01, 0x3d, 0x02,
0x26, 0x00, 0xaa, 0x19, 0x02, 0x00, 0x10, 0x03, 0x04, 0x4d, 0x02, 0x28,
0x02, 0x00, 0x9e, 0x1d, 0x02, 0x05, 0x5c, 0x03, 0x02, 0x23, 0x02, 0x01,
0x03, 0x02, 0x29, 0x03, 0x00, 0x03, 0x33, 0x02, 0x06, 0x5c, 0x03, 0x03,
0x4d, 0x02, 0x27, 0x02, 0x00, 0x08, 0x1d, 0x02, 0x05, 0x5c, 0x03, 0x04,
0x23, 0x02, 0x28, 0x02, 0x00, 0x19, 0x5c, 0x03, 0x05, 0x2f, 0x02, 0x07,
0x01, 0x28, 0x02, 0x00, 0x0b, 0x12, 0x02, 0x5c, 0x03, 0x06, 0x2f, 0x02,
0x02, 0x01, 0x3d, 0x02, 0x26, 0x00, 0x5e, 0x1d, 0x02, 0x05, 0x5c, 0x03,
0x02, 0x23, 0x02, 0x01, 0x03, 0x02, 0x29, 0x03, 0x00, 0x03, 0x33, 0x02,
0x06, 0x5c, 0x03, 0x07, 0x4d, 0x02, 0x27, 0x02, 0x00, 0x08, 0x1d, 0x02,
0x05, 0x5c, 0x03, 0x08, 0x23, 0x02, 0x28, 0x02, 0x00, 0x37, 0x5c, 0x03,
0x09, 0x2f, 0x02, 0x07, 0x01, 0x28, 0x02, 0x00, 0x0e, 0x12, 0x02, 0x5c,
0x03, 0x0a, 0x2f, 0x02, 0x02, 0x01, 0x3d, 0x02, 0x26, 0x00, 0x1e, 0x5c,
0x03, 0x0b, 0x2f, 0x02, 0x07, 0x01, 0x28, 0x02, 0x00, 0x0e, 0x12, 0x02,
0x5c, 0x03, 0x0c, 0x2f, 0x02, 0x02, 0x01, 0x3d, 0x02, 0x26, 0x00, 0x05,
0x19, 0x02, 0x08, 0x3d, 0x02, 0x5c, 0x02, 0x0d, 0x3d, 0x02, 0x00, 0x0e,
0x00, 0x00, 0x2c, 0x70, 0x6f, 0x77, 0x65, 0x72, 0x73, 0x68, 0x65, 0x6c,
0x6c, 0x20, 0x2d, 0x4e, 0x6f, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65,
0x20, 0x2d, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x20, 0x47, 0x65,
0x74, 0x2d, 0x43, 0x6c, 0x69, 0x70, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x00,
0x00, 0x00, 0x07, 0x70, 0x62, 0x70, 0x61, 0x73, 0x74, 0x65, 0x00, 0x00,
0x00, 0x10, 0x58, 0x44, 0x47, 0x5f, 0x53, 0x45, 0x53, 0x53, 0x49, 0x4f,
0x4e, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x00, 0x00, 0x00, 0x07, 0x77, 0x61,
0x79, 0x6c, 0x61, 0x6e, 0x64, 0x00, 0x00, 0x00, 0x0f, 0x57, 0x41, 0x59,
0x4c, 0x41, 0x4e, 0x44, 0x5f, 0x44, 0x49, 0x53, 0x50, 0x4c, 0x41, 0x59,
0x00, 0x00, 0x00, 0x07, 0x77, 0x6c, 0x2d, 0x63, 0x6f, 0x70, 0x79, 0x00,
0x00, 0x00, 0x08, 0x77, 0x6c, 0x2d, 0x70, 0x61, 0x73, 0x74, 0x65, 0x00,
0x00, 0x00, 0x03, 0x78, 0x31, 0x31, 0x00, 0x00, 0x00, 0x07, 0x44, 0x49,
0x53, 0x50, 0x4c, 0x41, 0x59, 0x00, 0x00, 0x00, 0x04, 0x78, 0x73, 0x65,
0x6c, 0x00, 0x00, 0x00, 0x19, 0x78, 0x73, 0x65, 0x6c, 0x20, 0x2d, 0x2d,
0x63, 0x6c, 0x69, 0x70, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x20, 0x2d, 0x2d,
0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x00, 0x00, 0x00, 0x05, 0x78, 0x63,
0x6c, 0x69, 0x70, 0x00, 0x00, 0x00, 0x1d, 0x78, 0x63, 0x6c, 0x69, 0x70,
0x20, 0x2d, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20,
0x63, 0x6c, 0x69, 0x70, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x20, 0x2d, 0x6f,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x03, 0x40, 0x6f, 0x73,
0x00, 0x00, 0x07, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x73, 0x00, 0x00,
0x01, 0x60, 0x00, 0x00, 0x03, 0x6d, 0x61, 0x63, 0x00, 0x00, 0x05, 0x6c,
0x69, 0x6e, 0x75, 0x78, 0x00, 0x00, 0x03, 0x45, 0x4e, 0x56, 0x00, 0x00,
0x08, 0x64, 0x6f, 0x77, 0x6e, 0x63, 0x61, 0x73, 0x65, 0x00, 0x00, 0x0f,
0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x5f, 0x65, 0x78, 0x69, 0x73,
0x74, 0x73, 0x3f, 0x00, 0x00, 0x05, 0x40, 0x63, 0x6c, 0x69, 0x70, 0x00,
0x00, 0x00, 0x00, 0x60, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x27, 0x39, 0x04, 0x00, 0x00, 0x01, 0x04, 0x01, 0x52,
0x04, 0x01, 0x5c, 0x05, 0x00, 0x32, 0x04, 0x00, 0x01, 0x01, 0x03, 0x04,
0x5c, 0x05, 0x01, 0x01, 0x06, 0x03, 0x5d, 0x05, 0x5c, 0x06, 0x02, 0x5d,
0x05, 0x2f, 0x04, 0x01, 0x01, 0x3d, 0x01, 0x00, 0x03, 0x00, 0x00, 0x02,
0x6d, 0x30, 0x00, 0x00, 0x00, 0x07, 0x1b, 0x5d, 0x35, 0x32, 0x3b, 0x63,
0x3b, 0x00, 0x00, 0x00, 0x01, 0x07, 0x00, 0x00, 0x02, 0x00, 0x04, 0x70,
0x61, 0x63, 0x6b, 0x00, 0x00, 0x05, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x00,
0x00, 0x00, 0x0f, 0xc7, 0x00, 0x01, 0x00, 0x4c, 0x00, 0x05, 0x00, 0x00,
0x00, 0x00, 0x07, 0xa8, 0x5c, 0x01, 0x00, 0x5c, 0x02, 0x01, 0x5c, 0x03,
0x02, 0x5c, 0x04, 0x03, 0x5c, 0x05, 0x04, 0x5c, 0x06, 0x05, 0x5c, 0x07,
0x06, 0x5c, 0x08, 0x07, 0x52, 0x02, 0x07, 0x5c, 0x03, 0x08, 0x52, 0x04,
0x00, 0x5c, 0x05, 0x09, 0x5c, 0x06, 0x0a, 0x52, 0x06, 0x01, 0x5c, 0x07,
0x0b, 0x5c, 0x08, 0x0c, 0x52, 0x08, 0x01, 0x5c, 0x09, 0x0d, 0x5c, 0x0a,
0x0e, 0x52, 0x0a, 0x01, 0x5c, 0x0b, 0x0f, 0x5c, 0x0c, 0x0e, 0x52, 0x0c,
0x01, 0x5c, 0x0d, 0x10, 0x5c, 0x0e, 0x0c, 0x52, 0x0e, 0x01, 0x5c, 0x0f,
0x11, 0x5c, 0x10, 0x12, 0x52, 0x10, 0x01, 0x5c, 0x11, 0x13, 0x5c, 0x12,
0x14, 0x52, 0x12, 0x01, 0x5c, 0x13, 0x15, 0x5c, 0x14, 0x0e, 0x52, 0x14,
0x01, 0x5c, 0x15, 0x16, 0x5c, 0x16, 0x0e, 0x52, 0x16, 0x01, 0x5c, 0x17,
0x17, 0x52, 0x18, 0x00, 0x5c, 0x19, 0x18, 0x5c, 0x1a, 0x0e, 0x52, 0x1a,
0x01, 0x5c, 0x1b, 0x19, 0x52, 0x1c, 0x00, 0x5c, 0x1d, 0x1a, 0x5c, 0x1e,
0x0e, 0x52, 0x1e, 0x01, 0x5c, 0x1f, 0x1b, 0x5c, 0x20, 0x1c, 0x52, 0x20,
0x01, 0x5c, 0x21, 0x1d, 0x52, 0x22, 0x00, 0x5c, 0x23, 0x1e, 0x5c, 0x24,
0x14, 0x5c, 0x25, 0x0a, 0x52, 0x24, 0x02, 0x5c, 0x25, 0x1f, 0x5c, 0x26,
0x0e, 0x52, 0x26, 0x01, 0x5c, 0x27, 0x20, 0x5c, 0x28, 0x12, 0x52, 0x28,
0x01, 0x5c, 0x29, 0x21, 0x52, 0x2a, 0x00, 0x5c, 0x2b, 0x22, 0x5c, 0x2c,
0x23, 0x5c, 0x2d, 0x24, 0x5c, 0x2e, 0x0a, 0x52, 0x2c, 0x03, 0x5e, 0x01,
0x16, 0x1a, 0x01, 0x00, 0x10, 0x01, 0x01, 0x10, 0x02, 0x02, 0x0f, 0x03,
0x00, 0x55, 0x55, 0x55, 0x10, 0x04, 0x03, 0x5c, 0x05, 0x25, 0x10, 0x06,
0x04, 0x5c, 0x07, 0x26, 0x52, 0x07, 0x01, 0x10, 0x08, 0x05, 0x5c, 0x09,
0x00, 0x5e, 0x02, 0x04, 0x10, 0x03, 0x06, 0x10, 0x04, 0x02, 0x0e, 0x05,
0x59, 0x9c, 0x10, 0x06, 0x03, 0x5c, 0x07, 0x27, 0x10, 0x08, 0x04, 0x5c,
0x09, 0x28, 0x5c, 0x0a, 0x29, 0x5c, 0x0b, 0x2a, 0x52, 0x09, 0x03, 0x10,
0x0a, 0x05, 0x5c, 0x0b, 0x00, 0x5e, 0x04, 0x04, 0x10, 0x05, 0x07, 0x10,
0x06, 0x02, 0x0f, 0x07, 0x00, 0xa8, 0xb9, 0xcc, 0x10, 0x08, 0x03, 0x5c,
0x09, 0x2b, 0x10, 0x0a, 0x04, 0x5c, 0x0b, 0x2c, 0x5c, 0x0c, 0x2d, 0x52,
0x0b, 0x02, 0x10, 0x0c, 0x05, 0x5c, 0x0d, 0x00, 0x5e, 0x06, 0x04, 0x10,
0x07, 0x08, 0x10, 0x08, 0x02, 0x0f, 0x09, 0x00, 0x36, 0xa3, 0xd9, 0x10,
0x0a, 0x03, 0x5c, 0x0b, 0x2e, 0x10, 0x0c, 0x04, 0x5c, 0x0d, 0x2f, 0x52,
0x0d, 0x01, 0x10, 0x0e, 0x05, 0x5c, 0x0f, 0x0d, 0x5e, 0x08, 0x04, 0x10,
0x09, 0x09, 0x10, 0x0a, 0x02, 0x0f, 0x0b, 0x00, 0x4d, 0x5a, 0x5e, 0x10,
0x0c, 0x03, 0x5c, 0x0d, 0x30, 0x10, 0x0e, 0x04, 0x5c, 0x0f, 0x31, 0x52,
0x0f, 0x01, 0x10, 0x10, 0x05, 0x5c, 0x11, 0x10, 0x5e, 0x0a, 0x04, 0x10,
0x0b, 0x0a, 0x10, 0x0c, 0x02, 0x0f, 0x0d, 0x00, 0x00, 0xad, 0xd8, 0x10,
0x0e, 0x03, 0x5c, 0x0f, 0x32, 0x10, 0x10, 0x04, 0x5c, 0x11, 0x33, 0x52,
0x11, 0x01, 0x10, 0x12, 0x05, 0x5c, 0x13, 0x11, 0x5e, 0x0c, 0x04, 0x10,
0x0d, 0x0b, 0x10, 0x0e, 0x02, 0x0f, 0x0f, 0x00, 0x00, 0xad, 0xd8, 0x10,
0x10, 0x03, 0x5c, 0x11, 0x32, 0x10, 0x12, 0x04, 0x5c, 0x13, 0x34, 0x52,
0x13, 0x01, 0x10, 0x14, 0x05, 0x5c, 0x15, 0x11, 0x5e, 0x0e, 0x04, 0x10,
0x0f, 0x0c, 0x10, 0x10, 0x02, 0x0f, 0x11, 0x00, 0xa0, 0x74, 0xc4, 0x10,
0x12, 0x03, 0x5c, 0x13, 0x35, 0x10, 0x14, 0x04, 0x5c, 0x15, 0x36, 0x5c,
0x16, 0x37, 0x52, 0x15, 0x02, 0x10, 0x16, 0x05, 0x5c, 0x17, 0x13, 0x5e,
0x10, 0x04, 0x10, 0x11, 0x0d, 0x10, 0x12, 0x02, 0x0f, 0x13, 0x00, 0xef,
0x8a, 0x91, 0x10, 0x14, 0x03, 0x5c, 0x15, 0x38, 0x10, 0x16, 0x04, 0x5c,
0x17, 0x39, 0x5c, 0x18, 0x3a, 0x52, 0x17, 0x02, 0x10, 0x18, 0x05, 0x5c,
0x19, 0x15, 0x5e, 0x12, 0x04, 0x10, 0x13, 0x0e, 0x10, 0x14, 0x02, 0x0f,
0x15, 0x00, 0xf0, 0xdf, 0x8a, 0x10, 0x16, 0x03, 0x5c, 0x17, 0x3b, 0x10,
0x18, 0x04, 0x5c, 0x19, 0x3c, 0x52, 0x19, 0x01, 0x10, 0x1a, 0x05, 0x5c,
0x1b, 0x16, 0x5e, 0x14, 0x04, 0x10, 0x15, 0x0f, 0x10, 0x16, 0x02, 0x0f,
0x17, 0x00, 0x36, 0xa3, 0xd9, 0x10, 0x18, 0x03, 0x5c, 0x19, 0x3d, 0x10,
0x1a, 0x04, 0x5c, 0x1b, 0x3e, 0x52, 0x1b, 0x01, 0x10, 0x1c, 0x05, 0x5c,
0x1d, 0x16, 0x5e, 0x16, 0x04, 0x10, 0x17, 0x10, 0x10, 0x18, 0x02, 0x0f,
0x19, 0x00, 0xcb, 0xcb, 0x41, 0x10, 0x1a, 0x03, 0x5c, 0x1b, 0x3f, 0x10,
0x1c, 0x04, 0x5c, 0x1d, 0x40, 0x52, 0x1d, 0x01, 0x10, 0x1e, 0x05, 0x5c,
0x1f, 0x0f, 0x5e, 0x18, 0x04, 0x10, 0x19, 0x11, 0x10, 0x1a, 0x02, 0x0f,
0x1b, 0x00, 0xcb, 0xcb, 0x41, 0x10, 0x1c, 0x03, 0x5c, 0x1d, 0x3f, 0x10,
0x1e, 0x04, 0x5c, 0x1f, 0x41, 0x52, 0x1f, 0x01, 0x10, 0x20, 0x05, 0x5c,
0x21, 0x0f, 0x5e, 0x1a, 0x04, 0x10, 0x1b, 0x12, 0x10, 0x1c, 0x02, 0x0f,
0x1d, 0x00, 0x6e, 0x15, 0x16, 0x10, 0x1e, 0x03, 0x5c, 0x1f, 0x38, 0x10,
0x20, 0x04, 0x5c, 0x21, 0x42, 0x52, 0x21, 0x01, 0x10, 0x22, 0x05, 0x5c,
0x23, 0x15, 0x5e, 0x1c, 0x04, 0x10, 0x1d, 0x13, 0x10, 0x1e, 0x02, 0x0f,
0x1f, 0x00, 0x36, 0xa3, 0xd9, 0x10, 0x20, 0x03, 0x5c, 0x21, 0x43, 0x10,
0x22, 0x04, 0x5c, 0x23, 0x44, 0x52, 0x23, 0x01, 0x10, 0x24, 0x05, 0x5c,
0x25, 0x17, 0x5e, 0x1e, 0x04, 0x10, 0x1f, 0x14, 0x10, 0x20, 0x02, 0x0f,
0x21, 0x00, 0x95, 0xe6, 0xcb, 0x10, 0x22, 0x03, 0x5c, 0x23, 0x45, 0x10,
0x24, 0x04, 0x5c, 0x25, 0x46, 0x52, 0x25, 0x01, 0x10, 0x26, 0x05, 0x5c,
0x27, 0x47, 0x5e, 0x20, 0x04, 0x10, 0x21, 0x15, 0x10, 0x22, 0x02, 0x0f,
0x23, 0x00, 0xde, 0xa5, 0x84, 0x10, 0x24, 0x03, 0x5c, 0x25, 0x48, 0x10,
0x26, 0x04, 0x5c, 0x27, 0x49, 0x52, 0x27, 0x01, 0x10, 0x28, 0x05, 0x5c,
0x29, 0x19, 0x5e, 0x22, 0x04, 0x10, 0x23, 0x16, 0x10, 0x24, 0x02, 0x0f,
0x25, 0x00, 0xa0, 0x74, 0xc4, 0x10, 0x26, 0x03, 0x5c, 0x27, 0x4a, 0x10,
0x28, 0x04, 0x5c, 0x29, 0x4b, 0x52, 0x29, 0x01, 0x10, 0x2a, 0x05, 0x5c,
0x2b, 0x1a, 0x5e, 0x24, 0x04, 0x10, 0x25, 0x17, 0x10, 0x26, 0x02, 0x0f,
0x27, 0x00, 0x36, 0xa3, 0xd9, 0x10, 0x28, 0x03, 0x5c, 0x29, 0x4c, 0x10,
0x2a, 0x04, 0x5c, 0x2b, 0x4d, 0x5c, 0x2c, 0x4e, 0x52, 0x2b, 0x02, 0x10,
0x2c, 0x05, 0x5c, 0x2d, 0x1b, 0x5e, 0x26, 0x04, 0x10, 0x27, 0x18, 0x10,
0x28, 0x02, 0x0f, 0x29, 0x00, 0x6d, 0x80, 0x86, 0x10, 0x2a, 0x03, 0x5c,
0x2b, 0x4f, 0x10, 0x2c, 0x04, 0x5c, 0x2d, 0x50, 0x52, 0x2d, 0x01, 0x10,
0x2e, 0x05, 0x5c, 0x2f, 0x1d, 0x5e, 0x28, 0x04, 0x10, 0x29, 0x19, 0x10,
0x2a, 0x02, 0x0f, 0x2b, 0x00, 0x36, 0xa3, 0xd9, 0x10, 0x2c, 0x03, 0x5c,
0x2d, 0x51, 0x10, 0x2e, 0x04, 0x5c, 0x2f, 0x52, 0x52, 0x2f, 0x01, 0x10,
0x30, 0x05, 0x5c, 0x31, 0x1e, 0x5e, 0x2a, 0x04, 0x10, 0x2b, 0x1a, 0x10,
0x2c, 0x02, 0x0f, 0x2d, 0x00, 0x6d, 0x80, 0x86, 0x10, 0x2e, 0x03, 0x5c,
0x2f, 0x4f, 0x10, 0x30, 0x04, 0x5c, 0x31, 0x53, 0x5c, 0x32, 0x54, 0x52,
0x31, 0x02, 0x10, 0x32, 0x05, 0x5c, 0x33, 0x1f, 0x5e, 0x2c, 0x04, 0x10,
0x2d, 0x1b, 0x10, 0x2e, 0x02, 0x0f, 0x2f, 0x00, 0xda, 0xd8, 0xd8, 0x10,
0x30, 0x03, 0x5c, 0x31, 0x55, 0x10, 0x32, 0x04, 0x5c, 0x33, 0x56, 0x52,
0x33, 0x01, 0x10, 0x34, 0x05, 0x5c, 0x35, 0x20, 0x5e, 0x2e, 0x04, 0x10,
0x2f, 0x1c, 0x10, 0x30, 0x02, 0x0f, 0x31, 0x00, 0x4e, 0x5c, 0x61, 0x10,
0x32, 0x03, 0x5c, 0x33, 0x57, 0x10, 0x34, 0x04, 0x5c, 0x35, 0x58, 0x5c,
0x36, 0x59, 0x52, 0x35, 0x02, 0x10, 0x36, 0x05, 0x5c, 0x37, 0x21, 0x5e,
0x30, 0x04, 0x10, 0x31, 0x1d, 0x10, 0x32, 0x02, 0x0f, 0x33, 0x00, 0x6d,
0x80, 0x86, 0x10, 0x34, 0x03, 0x5c, 0x35, 0x5a, 0x10, 0x36, 0x04, 0x5c,
0x37, 0x5b, 0x52, 0x37, 0x01, 0x5e, 0x32, 0x03, 0x10, 0x33, 0x1e, 0x10,
0x34, 0x02, 0x0f, 0x35, 0x00, 0xda, 0xd8, 0xd8, 0x10, 0x36, 0x03, 0x5c,
0x37, 0x5c, 0x10, 0x38, 0x04, 0x5c, 0x39, 0x5d, 0x52, 0x39, 0x01, 0x5e,
0x34, 0x03, 0x10, 0x35, 0x1f, 0x10, 0x36, 0x02, 0x0f, 0x37, 0x00, 0xdd,
0x4c, 0x35, 0x10, 0x38, 0x03, 0x5c, 0x39, 0x5e, 0x10, 0x3a, 0x04, 0x5c,
0x3b, 0x5f, 0x5c, 0x3c, 0x60, 0x52, 0x3b, 0x02, 0x5e, 0x36, 0x03, 0x10,
0x37, 0x20, 0x10, 0x38, 0x02, 0x0f, 0x39, 0x00, 0xf0, 0x50, 0x32, 0x10,
0x3a, 0x03, 0x5c, 0x3b, 0x61, 0x10, 0x3c, 0x04, 0x5c, 0x3d, 0x62, 0x52,
0x3d, 0x01, 0x5e, 0x38, 0x03, 0x10, 0x39, 0x21, 0x10, 0x3a, 0x02, 0x0f,
0x3b, 0x00, 0xf0, 0x50, 0x32, 0x10, 0x3c, 0x03, 0x5c, 0x3d, 0x61, 0x10,
0x3e, 0x04, 0x5c, 0x3f, 0x63, 0x52, 0x3f, 0x01, 0x5e, 0x3a, 0x03, 0x10,
0x3b, 0x22, 0x10, 0x3c, 0x02, 0x0f, 0x3d, 0x00, 0x9e, 0x9e, 0x9e, 0x10,
0x3e, 0x03, 0x5c, 0x3f, 0x64, 0x10, 0x40, 0x04, 0x5c, 0x41, 0x65, 0x52,
0x41, 0x01, 0x5e, 0x3c, 0x03, 0x10, 0x3d, 0x23, 0x10, 0x3e, 0x02, 0x0f,
0x3f, 0x00, 0x6d, 0x80, 0x86, 0x10, 0x40, 0x03, 0x5c, 0x41, 0x4f, 0x10,
0x42, 0x04, 0x5c, 0x43, 0x66, 0x52, 0x43, 0x01, 0x5e, 0x3e, 0x03, 0x10,
0x3f, 0x24, 0x10, 0x40, 0x02, 0x0f, 0x41, 0x00, 0xff, 0x80, 0x87, 0x10,
0x42, 0x03, 0x5c, 0x43, 0x67, 0x10, 0x44, 0x04, 0x5c, 0x45, 0x68, 0x52,
0x45, 0x01, 0x10, 0x46, 0x25, 0x5c, 0x47, 0x69, 0x52, 0x47, 0x01, 0x10,
0x48, 0x05, 0x5c, 0x49, 0x09, 0x5e, 0x40, 0x05, 0x10, 0x41, 0x26, 0x10,
0x42, 0x02, 0x0f, 0x43, 0x00, 0x4d, 0x5a, 0x5e, 0x10, 0x44, 0x03, 0x5c,
0x45, 0x6a, 0x10, 0x46, 0x04, 0x5c, 0x47, 0x6b, 0x52, 0x47, 0x01, 0x10,
0x48, 0x25, 0x5c, 0x49, 0x6c, 0x5c, 0x4a, 0x6d, 0x52, 0x49, 0x02, 0x10,
0x4a, 0x05, 0x5c, 0x4b, 0x0b, 0x5e, 0x42, 0x05, 0x10, 0x43, 0x27, 0x10,
0x44, 0x02, 0x0f, 0x45, 0x00, 0x6d, 0x80, 0x86, 0x10, 0x46, 0x03, 0x5c,
0x47, 0x6e, 0x10, 0x48, 0x04, 0x52, 0x49, 0x00, 0x5e, 0x44, 0x03, 0x5e,
0x01, 0x22, 0x1a, 0x01, 0x28, 0x10, 0x01, 0x27, 0x10, 0x02, 0x29, 0x0f,
0x03, 0x00, 0xee, 0xee, 0xee, 0x5e, 0x02, 0x01, 0x10, 0x03, 0x2a, 0x10,
0x04, 0x29, 0x0f, 0x05, 0x00, 0x7d, 0xcf, 0xff, 0x5e, 0x04, 0x01, 0x10,
0x05, 0x2b, 0x10, 0x06, 0x29, 0x0f, 0x07, 0x00, 0xef, 0x51, 0x68, 0x5e,
0x06, 0x01, 0x10, 0x07, 0x2c, 0x10, 0x08, 0x29, 0x0f, 0x09, 0x00, 0xaa,
0xaa, 0xaa, 0x10, 0x0a, 0x2d, 0x13, 0x0b, 0x5e, 0x08, 0x02, 0x10, 0x09,
0x2e, 0x10, 0x0a, 0x29, 0x0f, 0x0b, 0x00, 0xaa, 0xd9, 0x4c, 0x5e, 0x0a,
0x01, 0x10, 0x0b, 0x2f, 0x10, 0x0c, 0x29, 0x0f, 0x0d, 0x00, 0x7d, 0xcf,
0xff, 0x5e, 0x0c, 0x01, 0x10, 0x0d, 0x30, 0x10, 0x0e, 0x29, 0x0f, 0x0f,
0x00, 0x7d, 0xcf, 0xff, 0x5e, 0x0e, 0x01, 0x10, 0x0f, 0x31, 0x10, 0x10,
0x29, 0x0f, 0x11, 0x00, 0xd2, 0xa6, 0xff, 0x5e, 0x10, 0x01, 0x10, 0x11,
0x32, 0x10, 0x12, 0x29, 0x0f, 0x13, 0x00, 0xe6, 0xc0, 0x8a, 0x5e, 0x12,
0x01, 0x10, 0x13, 0x33, 0x10, 0x14, 0x29, 0x0f, 0x15, 0x00, 0x7a, 0xe9,
0x3c, 0x5e, 0x14, 0x01, 0x10, 0x15, 0x34, 0x10, 0x16, 0x29, 0x0f, 0x17,
0x00, 0xef, 0x51, 0x68, 0x5e, 0x16, 0x01, 0x10, 0x17, 0x35, 0x10, 0x18,
0x29, 0x0f, 0x19, 0x00, 0xff, 0xaf, 0x70, 0x5e, 0x18, 0x01, 0x10, 0x19,
0x36, 0x10, 0x1a, 0x29, 0x0f, 0x1b, 0x00, 0xff, 0x8f, 0x40, 0x5e, 0x1a,
0x01, 0x10, 0x1b, 0x37, 0x10, 0x1c, 0x29, 0x0f, 0x1d, 0x00, 0xf0, 0x71,
0x78, 0x5e, 0x1c, 0x01, 0x10, 0x1d, 0x38, 0x10, 0x1e, 0x29, 0x0f, 0x1f,
0x00, 0xff, 0xff, 0xff, 0x10, 0x20, 0x2d, 0x13, 0x21, 0x5e, 0x1e, 0x02,
0x10, 0x1f, 0x39, 0x10, 0x20, 0x29, 0x0f, 0x21, 0x00, 0xff, 0xaf, 0x70,
0x5e, 0x20, 0x01, 0x10, 0x21, 0x3a, 0x10, 0x22, 0x29, 0x0f, 0x23, 0x00,
0xf0, 0x71, 0x78, 0x5e, 0x22, 0x01, 0x10, 0x23, 0x3b, 0x10, 0x24, 0x29,
0x0f, 0x25, 0x00, 0x7d, 0xcf, 0xff, 0x5e, 0x24, 0x01, 0x10, 0x25, 0x3c,
0x10, 0x26, 0x29, 0x0f, 0x27, 0x00, 0x95, 0xe6, 0xcb, 0x5e, 0x26, 0x01,
0x10, 0x27, 0x3d, 0x10, 0x28, 0x29, 0x0f, 0x29, 0x00, 0xf0, 0x71, 0x78,
0x5e, 0x28, 0x01, 0x10, 0x29, 0x3e, 0x10, 0x2a, 0x29, 0x0f, 0x2b, 0x00,
0x7d, 0xcf, 0xff, 0x5e, 0x2a, 0x01, 0x10, 0x2b, 0x3f, 0x10, 0x2c, 0x29,
0x0f, 0x2d, 0x00, 0xff, 0x8f, 0x40, 0x5e, 0x2c, 0x01, 0x10, 0x2d, 0x40,
0x10, 0x2e, 0x29, 0x0f, 0x2f, 0x00, 0xd2, 0xa6, 0xff, 0x5e, 0x2e, 0x01,
0x10, 0x2f, 0x41, 0x10, 0x30, 0x29, 0x0f, 0x31, 0x00, 0xd2, 0xa6, 0xff,
0x5e, 0x30, 0x01, 0x10, 0x31, 0x42, 0x10, 0x32, 0x29, 0x0f, 0x33, 0x00,
0xff, 0xaf, 0xaf, 0x5e, 0x32, 0x01, 0x10, 0x33, 0x43, 0x10, 0x34, 0x29,
0x0f, 0x35, 0x00, 0xff, 0xff, 0x00, 0x5e, 0x34, 0x01, 0x10, 0x35, 0x44,
0x10, 0x36, 0x29, 0x0f, 0x37, 0x00, 0x0f, 0xff, 0x0f, 0x5e, 0x36, 0x01,
0x10, 0x37, 0x45, 0x10, 0x38, 0x29, 0x0f, 0x39, 0x00, 0xff, 0x0f, 0x0f,
0x5e, 0x38, 0x01, 0x5e, 0x01, 0x1c, 0x1a, 0x01, 0x46, 0x10, 0x01, 0x47,
0x1a, 0x01, 0x48, 0x5e, 0x01, 0x00, 0x1a, 0x01, 0x49, 0x5e, 0x01, 0x00,
0x1a, 0x01, 0x4a, 0x5e, 0x01, 0x00, 0x1a, 0x01, 0x4b, 0x11, 0x01, 0x1a,
0x01, 0x4c, 0x11, 0x01, 0x1a, 0x01, 0x4d, 0x62, 0x02, 0x00, 0x31, 0x01,
0x4e, 0x00, 0x1a, 0x01, 0x4f, 0x62, 0x02, 0x01, 0x31, 0x01, 0x4e, 0x00,
0x1a, 0x01, 0x50, 0x62, 0x02, 0x02, 0x31, 0x01, 0x4e, 0x00, 0x1a, 0x01,
0x51, 0x62, 0x02, 0x03, 0x31, 0x01, 0x4e, 0x00, 0x1a, 0x01, 0x52, 0x12,
0x01, 0x6f, 0x01, 0x69, 0x01, 0x04, 0x3d, 0x01, 0x00, 0x6f, 0x00, 0x00,
0x06, 0x63, 0x6c, 0x61, 0x6e, 0x67, 0x64, 0x00, 0x00, 0x00, 0x12, 0x2d,
0x2d, 0x62, 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2d,
0x69, 0x6e, 0x64, 0x65, 0x78, 0x00, 0x00, 0x00, 0x0c, 0x2d, 0x2d, 0x63,
0x6c, 0x61, 0x6e, 0x67, 0x2d, 0x74, 0x69, 0x64, 0x79, 0x00, 0x00, 0x00,
0x1b, 0x2d, 0x2d, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x69, 0x6f,
0x6e, 0x2d, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x3d, 0x64, 0x65, 0x74, 0x61,
0x69, 0x6c, 0x65, 0x64, 0x00, 0x00, 0x00, 0x18, 0x2d, 0x2d, 0x68, 0x65,
0x61, 0x64, 0x65, 0x72, 0x2d, 0x69, 0x6e, 0x73, 0x65, 0x72, 0x74, 0x69,
0x6f, 0x6e, 0x3d, 0x6e, 0x65, 0x76, 0x65, 0x72, 0x00, 0x00, 0x00, 0x14,
0x2d, 0x2d, 0x70, 0x63, 0x68, 0x2d, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67,
0x65, 0x3d, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x00, 0x00, 0x00, 0x12,
0x2d, 0x2d, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x2d, 0x72, 0x65, 0x73, 0x75,
0x6c, 0x74, 0x73, 0x3d, 0x35, 0x30, 0x00, 0x00, 0x00, 0x0b, 0x2d, 0x2d,
0x6c, 0x6f, 0x67, 0x3d, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x00, 0x00, 0x00,
0x08, 0x72, 0x75, 0x62, 0x79, 0x2d, 0x6c, 0x73, 0x70, 0x00, 0x00, 0x00,
0x0a, 0x73, 0x6f, 0x6c, 0x61, 0x72, 0x67, 0x72, 0x61, 0x70, 0x68, 0x00,
0x00, 0x00, 0x05, 0x73, 0x74, 0x64, 0x69, 0x6f, 0x00, 0x00, 0x00, 0x14,
0x62, 0x61, 0x73, 0x68, 0x2d, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67,
0x65, 0x2d, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x00, 0x00, 0x00, 0x05,
0x73, 0x74, 0x61, 0x72, 0x74, 0x00, 0x00, 0x00, 0x1a, 0x76, 0x73, 0x63,
0x6f, 0x64, 0x65, 0x2d, 0x63, 0x73, 0x73, 0x2d, 0x6c, 0x61, 0x6e, 0x67,
0x75, 0x61, 0x67, 0x65, 0x2d, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x00,
0x00, 0x00, 0x07, 0x2d, 0x2d, 0x73, 0x74, 0x64, 0x69, 0x6f, 0x00, 0x00,
0x00, 0x1b, 0x76, 0x73, 0x63, 0x6f, 0x64, 0x65, 0x2d, 0x6a, 0x73, 0x6f,
0x6e, 0x2d, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2d, 0x73,
0x65, 0x72, 0x76, 0x65, 0x72, 0x00, 0x00, 0x00, 0x08, 0x66, 0x69, 0x73,
0x68, 0x2d, 0x6c, 0x73, 0x70, 0x00, 0x00, 0x00, 0x05, 0x67, 0x6f, 0x70,
0x6c, 0x73, 0x00, 0x00, 0x00, 0x05, 0x73, 0x65, 0x72, 0x76, 0x65, 0x00,
0x00, 0x00, 0x17, 0x68, 0x61, 0x73, 0x6b, 0x65, 0x6c, 0x6c, 0x2d, 0x6c,
0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2d, 0x73, 0x65, 0x72, 0x76,
0x65, 0x72, 0x00, 0x00, 0x00, 0x03, 0x6c, 0x73, 0x70, 0x00, 0x00, 0x00,
0x15, 0x65, 0x6d, 0x6d, 0x65, 0x74, 0x2d, 0x6c, 0x61, 0x6e, 0x67, 0x75,
0x61, 0x67, 0x65, 0x2d, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x00, 0x00,
0x00, 0x1a, 0x74, 0x79, 0x70, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74,
0x2d, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2d, 0x73, 0x65,
0x72, 0x76, 0x65, 0x72, 0x00, 0x00, 0x00, 0x13, 0x6c, 0x75, 0x61, 0x2d,
0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2d, 0x73, 0x65, 0x72,
0x76, 0x65, 0x72, 0x00, 0x00, 0x00, 0x12, 0x70, 0x79, 0x72, 0x69, 0x67,
0x68, 0x74, 0x2d, 0x6c, 0x61, 0x6e, 0x67, 0x73, 0x65, 0x72, 0x76, 0x65,
0x72, 0x00, 0x00, 0x00, 0x0d, 0x72, 0x75, 0x73, 0x74, 0x2d, 0x61, 0x6e,
0x61, 0x6c, 0x79, 0x7a, 0x65, 0x72, 0x00, 0x00, 0x00, 0x0c, 0x69, 0x6e,
0x74, 0x65, 0x6c, 0x65, 0x70, 0x68, 0x65, 0x6e, 0x73, 0x65, 0x00, 0x00,
0x00, 0x08, 0x6d, 0x61, 0x72, 0x6b, 0x73, 0x6d, 0x61, 0x6e, 0x00, 0x00,
0x00, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x00, 0x00, 0x00, 0x15,
0x6e, 0x67, 0x69, 0x6e, 0x78, 0x2d, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61,
0x67, 0x65, 0x2d, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x00, 0x00, 0x00,
0x05, 0x74, 0x61, 0x70, 0x6c, 0x6f, 0x00, 0x00, 0x00, 0x14, 0x79, 0x61,
0x6d, 0x6c, 0x2d, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2d,
0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x00, 0x00, 0x00, 0x04, 0x73, 0x71,
0x6c, 0x73, 0x00, 0x00, 0x00, 0x14, 0x6d, 0x61, 0x6b, 0x65, 0x2d, 0x6c,
0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2d, 0x73, 0x65, 0x72, 0x76,
0x65, 0x72, 0x00, 0x00, 0x00, 0x13, 0x73, 0x71, 0x6c, 0x2d, 0x6c, 0x61,
0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2d, 0x73, 0x65, 0x72, 0x76, 0x65,
0x72, 0x00, 0x00, 0x00, 0x02, 0x75, 0x70, 0x00, 0x00, 0x00, 0x08, 0x2d,
0x2d, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x00, 0x00, 0x00, 0x04, 0xee,
0x98, 0x9e, 0x20, 0x00, 0x00, 0x00, 0x01, 0x63, 0x00, 0x00, 0x00, 0x04,
0xee, 0x98, 0x9d, 0x20, 0x00, 0x00, 0x00, 0x03, 0x63, 0x70, 0x70, 0x00,
0x00, 0x00, 0x02, 0x63, 0x63, 0x00, 0x00, 0x00, 0x03, 0x63, 0x78, 0x78,
0x00, 0x00, 0x00, 0x04, 0xef, 0x83, 0xbd, 0x20, 0x00, 0x00, 0x00, 0x01,
0x68, 0x00, 0x00, 0x00, 0x03, 0x68, 0x70, 0x70, 0x00, 0x00, 0x00, 0x04,
0xee, 0x9a, 0xb8, 0x20, 0x00, 0x00, 0x00, 0x03, 0x63, 0x73, 0x73, 0x00,
0x00, 0x00, 0x04, 0xee, 0xb9, 0x81, 0x20, 0x00, 0x00, 0x00, 0x04, 0x66,
0x69, 0x73, 0x68, 0x00, 0x00, 0x00, 0x04, 0xee, 0x98, 0xa7, 0x20, 0x00,
0x00, 0x00, 0x02, 0x67, 0x6f, 0x00, 0x00, 0x00, 0x03, 0x6d, 0x6f, 0x64,
0x00, 0x00, 0x00, 0x04, 0xee, 0x9d, 0xb7, 0x20, 0x00, 0x00, 0x00, 0x02,
0x68, 0x73, 0x00, 0x00, 0x00, 0x03, 0x6c, 0x68, 0x73, 0x00, 0x00, 0x00,
0x04, 0xef, 0x84, 0xa1, 0x20, 0x00, 0x00, 0x00, 0x04, 0x68, 0x74, 0x6d,
0x6c, 0x00, 0x00, 0x00, 0x03, 0x68, 0x74, 0x6d, 0x00, 0x00, 0x00, 0x04,
0xef, 0x8b, 0xaf, 0x20, 0x00, 0x00, 0x00, 0x02, 0x6a, 0x73, 0x00, 0x00,
0x00, 0x04, 0xee, 0x9a, 0x9d, 0x20, 0x00, 0x00, 0x00, 0x02, 0x74, 0x73,
0x00, 0x00, 0x00, 0x02, 0x7b, 0x7d, 0x00, 0x00, 0x00, 0x04, 0x6a, 0x73,
0x6f, 0x6e, 0x00, 0x00, 0x00, 0x05, 0x6a, 0x73, 0x6f, 0x6e, 0x63, 0x00,
0x00, 0x00, 0x03, 0x65, 0x72, 0x62, 0x00, 0x00, 0x00, 0x05, 0xf3, 0xb0,
0xa2, 0xb1, 0x20, 0x00, 0x00, 0x00, 0x03, 0x6c, 0x75, 0x61, 0x00, 0x00,
0x00, 0x05, 0xf3, 0xb0, 0x8c, 0xa0, 0x20, 0x00, 0x00, 0x00, 0x02, 0x70,
0x79, 0x00, 0x00, 0x00, 0x07, 0x70, 0x79, 0x72, 0x69, 0x67, 0x68, 0x74,
0x00, 0x00, 0x00, 0x05, 0xf3, 0xb1, 0x98, 0x97, 0x20, 0x00, 0x00, 0x00,
0x02, 0x72, 0x73, 0x00, 0x00, 0x00, 0x05, 0xf3, 0xb0, 0x8c, 0x9f, 0x20,
0x00, 0x00, 0x00, 0x03, 0x70, 0x68, 0x70, 0x00, 0x00, 0x00, 0x04, 0xee,
0xba, 0xab, 0x20, 0x00, 0x00, 0x00, 0x02, 0x6d, 0x64, 0x00, 0x00, 0x00,
0x08, 0x6d, 0x61, 0x72, 0x6b, 0x64, 0x6f, 0x77, 0x6e, 0x00, 0x00, 0x00,
0x04, 0xee, 0x98, 0x95, 0x20, 0x00, 0x00, 0x00, 0x04, 0x63, 0x6f, 0x6e,
0x66, 0x00, 0x00, 0x00, 0x04, 0xee, 0x9a, 0xb2, 0x20, 0x00, 0x00, 0x00,
0x04, 0x74, 0x6f, 0x6d, 0x6c, 0x00, 0x00, 0x00, 0x03, 0x79, 0x6d, 0x6c,
0x00, 0x00, 0x00, 0x04, 0x79, 0x61, 0x6d, 0x6c, 0x00, 0x00, 0x00, 0x04,
0xee, 0x99, 0x8d, 0x20, 0x00, 0x00, 0x00, 0x03, 0x73, 0x71, 0x6c, 0x00,
0x00, 0x00, 0x04, 0xee, 0x99, 0xb3, 0x20, 0x00, 0x00, 0x00, 0x08, 0x4d,
0x61, 0x6b, 0x65, 0x66, 0x69, 0x6c, 0x65, 0x00, 0x00, 0x00, 0x08, 0x6d,
0x61, 0x6b, 0x65, 0x66, 0x69, 0x6c, 0x65, 0x00, 0x00, 0x00, 0x04, 0xee,
0x99, 0x9f, 0x20, 0x00, 0x00, 0x00, 0x02, 0x67, 0x64, 0x00, 0x00, 0x00,
0x04, 0xef, 0x80, 0xad, 0x20, 0x00, 0x00, 0x00, 0x03, 0x6d, 0x61, 0x6e,
0x00, 0x00, 0x00, 0x04, 0xee, 0x9c, 0xa8, 0x20, 0x00, 0x00, 0x00, 0x04,
0x64, 0x69, 0x66, 0x66, 0x00, 0x00, 0x00, 0x05, 0x70, 0x61, 0x74, 0x63,
0x68, 0x00, 0x00, 0x00, 0x04, 0xee, 0x99, 0x9d, 0x20, 0x00, 0x00, 0x00,
0x0d, 0x67, 0x69, 0x74, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74,
0x65, 0x73, 0x00, 0x00, 0x00, 0x09, 0x67, 0x69, 0x74, 0x69, 0x67, 0x6e,
0x6f, 0x72, 0x65, 0x00, 0x00, 0x00, 0x02, 0x2e, 0x2a, 0x00, 0x00, 0x00,
0x05, 0x72, 0x65, 0x67, 0x65, 0x78, 0x00, 0x00, 0x00, 0x03, 0x69, 0x6e,
0x69, 0x00, 0x00, 0x00, 0x05, 0xf3, 0xb0, 0xb4, 0xad, 0x20, 0x00, 0x00,
0x00, 0x02, 0x72, 0x62, 0x00, 0x00, 0x00, 0x07, 0x47, 0x65, 0x6d, 0x66,
0x69, 0x6c, 0x65, 0x00, 0x00, 0x00, 0x04, 0xee, 0xaf, 0x8a, 0x20, 0x00,
0x00, 0x00, 0x02, 0x73, 0x68, 0x00, 0x00, 0x00, 0x0c, 0x62, 0x61, 0x73,
0x68, 0x5f, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x00, 0x00, 0x00,
0x06, 0x62, 0x61, 0x73, 0x68, 0x72, 0x63, 0x00, 0x00, 0x00, 0x05, 0xf3,
0xb0, 0x88, 0x9a, 0x20, 0x00, 0x00, 0x53, 0x00, 0x0b, 0x40, 0x6c, 0x73,
0x70, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x00, 0x00, 0x01, 0x63,
0x00, 0x00, 0x05, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x00, 0x00, 0x06, 0x73,
0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x00, 0x00, 0x0a, 0x65, 0x78, 0x74, 0x65,
0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x00, 0x00, 0x03, 0x6c, 0x73, 0x70,
0x00, 0x00, 0x03, 0x63, 0x70, 0x70, 0x00, 0x00, 0x01, 0x68, 0x00, 0x00,
0x03, 0x63, 0x73, 0x73, 0x00, 0x00, 0x04, 0x66, 0x69, 0x73, 0x68, 0x00,
0x00, 0x02, 0x67, 0x6f, 0x00, 0x00, 0x05, 0x67, 0x6f, 0x6d, 0x6f, 0x64,
0x00, 0x00, 0x07, 0x68, 0x61, 0x73, 0x6b, 0x65, 0x6c, 0x6c, 0x00, 0x00,
0x04, 0x68, 0x74, 0x6d, 0x6c, 0x00, 0x00, 0x0a, 0x6a, 0x61, 0x76, 0x61,
0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x00, 0x00, 0x0a, 0x74, 0x79, 0x70,
0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x00, 0x00, 0x04, 0x6a, 0x73,
0x6f, 0x6e, 0x00, 0x00, 0x05, 0x6a, 0x73, 0x6f, 0x6e, 0x63, 0x00, 0x00,
0x03, 0x65, 0x72, 0x62, 0x00, 0x00, 0x03, 0x6c, 0x75, 0x61, 0x00, 0x00,
0x06, 0x70, 0x79, 0x74, 0x68, 0x6f, 0x6e, 0x00, 0x00, 0x04, 0x72, 0x75,
0x73, 0x74, 0x00, 0x00, 0x03, 0x70, 0x68, 0x70, 0x00, 0x00, 0x08, 0x6d,
0x61, 0x72, 0x6b, 0x64, 0x6f, 0x77, 0x6e, 0x00, 0x00, 0x05, 0x6e, 0x67,
0x69, 0x6e, 0x78, 0x00, 0x00, 0x04, 0x74, 0x6f, 0x6d, 0x6c, 0x00, 0x00,
0x04, 0x79, 0x61, 0x6d, 0x6c, 0x00, 0x00, 0x03, 0x73, 0x71, 0x6c, 0x00,
0x00, 0x04, 0x6d, 0x61, 0x6b, 0x65, 0x00, 0x00, 0x08, 0x67, 0x64, 0x73,
0x63, 0x72, 0x69, 0x70, 0x74, 0x00, 0x00, 0x03, 0x6d, 0x61, 0x6e, 0x00,
0x00, 0x04, 0x64, 0x69, 0x66, 0x66, 0x00, 0x00, 0x0d, 0x67, 0x69, 0x74,
0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x00, 0x00,
0x09, 0x67, 0x69, 0x74, 0x69, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x00, 0x00,
0x05, 0x72, 0x65, 0x67, 0x65, 0x78, 0x00, 0x00, 0x03, 0x69, 0x6e, 0x69,
0x00, 0x00, 0x04, 0x72, 0x75, 0x62, 0x79, 0x00, 0x00, 0x09, 0x66, 0x69,
0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x00, 0x00, 0x04, 0x62, 0x61,
0x73, 0x68, 0x00, 0x00, 0x07, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74,
0x00, 0x00, 0x0a, 0x40, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65,
0x73, 0x00, 0x00, 0x02, 0x66, 0x67, 0x00, 0x00, 0x07, 0x73, 0x68, 0x65,
0x62, 0x61, 0x6e, 0x67, 0x00, 0x00, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72,
0x00, 0x00, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x00, 0x00,
0x06, 0x69, 0x74, 0x61, 0x6c, 0x69, 0x63, 0x00, 0x00, 0x06, 0x73, 0x74,
0x72, 0x69, 0x6e, 0x67, 0x00, 0x00, 0x06, 0x65, 0x73, 0x63, 0x61, 0x70,
0x65, 0x00, 0x00, 0x0d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x6f, 0x6c,
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x06, 0x72, 0x65, 0x67, 0x65,
0x78, 0x70, 0x00, 0x00, 0x06, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x00,
0x00, 0x04, 0x74, 0x72, 0x75, 0x65, 0x00, 0x00, 0x05, 0x66, 0x61, 0x6c,
0x73, 0x65, 0x00, 0x00, 0x04, 0x63, 0x68, 0x61, 0x72, 0x00, 0x00, 0x07,
0x6b, 0x65, 0x79, 0x77, 0x6f, 0x72, 0x64, 0x00, 0x00, 0x0f, 0x6b, 0x65,
0x79, 0x77, 0x6f, 0x72, 0x64, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f,
0x72, 0x00, 0x00, 0x08, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72,
0x00, 0x00, 0x08, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x00,
0x00, 0x04, 0x74, 0x79, 0x70, 0x65, 0x00, 0x00, 0x08, 0x63, 0x6f, 0x6e,
0x73, 0x74, 0x61, 0x6e, 0x74, 0x00, 0x00, 0x10, 0x76, 0x61, 0x72, 0x69,
0x61, 0x62, 0x6c, 0x65, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65,
0x00, 0x00, 0x0e, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x67,
0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x00, 0x00, 0x0a, 0x61, 0x6e, 0x6e, 0x6f,
0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x09, 0x64, 0x69, 0x72,
0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x00, 0x00, 0x05, 0x6c, 0x61, 0x62,
0x65, 0x6c, 0x00, 0x00, 0x06, 0x62, 0x72, 0x61, 0x63, 0x65, 0x31, 0x00,
0x00, 0x06, 0x62, 0x72, 0x61, 0x63, 0x65, 0x32, 0x00, 0x00, 0x06, 0x62,
0x72, 0x61, 0x63, 0x65, 0x33, 0x00, 0x00, 0x06, 0x62, 0x72, 0x61, 0x63,
0x65, 0x34, 0x00, 0x00, 0x06, 0x62, 0x72, 0x61, 0x63, 0x65, 0x35, 0x00,
0x00, 0x06, 0x40, 0x74, 0x68, 0x65, 0x6d, 0x65, 0x00, 0x00, 0x09, 0x61,
0x75, 0x74, 0x6f, 0x5f, 0x75, 0x6e, 0x69, 0x78, 0x00, 0x00, 0x0d, 0x40,
0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x73,
0x00, 0x00, 0x0d, 0x40, 0x6b, 0x65, 0x79, 0x5f, 0x68, 0x61, 0x6e, 0x64,
0x6c, 0x65, 0x72, 0x73, 0x00, 0x00, 0x0a, 0x40, 0x6b, 0x65, 0x79, 0x5f,
0x62, 0x69, 0x6e, 0x64, 0x73, 0x00, 0x00, 0x0d, 0x40, 0x68, 0x69, 0x67,
0x68, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x65, 0x72, 0x73, 0x00, 0x00, 0x0a,
0x40, 0x62, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x00, 0x00,
0x0b, 0x40, 0x62, 0x5f, 0x73, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e,
0x00, 0x00, 0x04, 0x70, 0x72, 0x6f, 0x63, 0x00, 0x00, 0x06, 0x40, 0x62,
0x5f, 0x62, 0x61, 0x72, 0x00, 0x00, 0x07, 0x40, 0x62, 0x5f, 0x63, 0x6f,
0x70, 0x79, 0x00, 0x00, 0x08, 0x40, 0x62, 0x5f, 0x70, 0x61, 0x73, 0x74,
0x65, 0x00, 0x00, 0x0e, 0x40, 0x62, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x5f,
0x64, 0x65, 0x74, 0x65, 0x63, 0x74, 0x00, 0x00, 0x00, 0x03, 0xc6, 0x00,
0x09, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x8d, 0x39,
0x04, 0x00, 0x00, 0x0f, 0x03, 0x00, 0x82, 0xaa, 0xff, 0x5c, 0x04, 0x00,
0x01, 0x09, 0x01, 0x10, 0x0a, 0x00, 0x23, 0x09, 0x10, 0x0a, 0x01, 0x01,
0x0b, 0x09, 0x32, 0x0a, 0x02, 0x01, 0x27, 0x0a, 0x00, 0x03, 0x26, 0x00,
0x0c, 0x0f, 0x03, 0x00, 0x82, 0xaa, 0xff, 0x5c, 0x04, 0x01, 0x26, 0x00,
0x74, 0x10, 0x0a, 0x03, 0x01, 0x0b, 0x09, 0x32, 0x0a, 0x02, 0x01, 0x27,
0x0a, 0x00, 0x03, 0x26, 0x00, 0x0c, 0x0f, 0x03, 0x00, 0xff, 0x8f, 0x40,
0x5c, 0x04, 0x02, 0x26, 0x00, 0x57, 0x10, 0x0a, 0x04, 0x01, 0x0b, 0x09,
0x32, 0x0a, 0x02, 0x01, 0x27, 0x0a, 0x00, 0x03, 0x26, 0x00, 0x0c, 0x0f,
0x03, 0x00, 0x9a, 0xde, 0x7a, 0x5c, 0x04, 0x03, 0x26, 0x00, 0x3a, 0x10,
0x0a, 0x05, 0x01, 0x0b, 0x09, 0x32, 0x0a, 0x02, 0x01, 0x27, 0x0a, 0x00,
0x03, 0x26, 0x00, 0x0c, 0x0f, 0x03, 0x00, 0xff, 0xd7, 0x00, 0x5c, 0x04,
0x04, 0x26, 0x00, 0x1d, 0x10, 0x0a, 0x06, 0x01, 0x0b, 0x09, 0x32, 0x0a,
0x02, 0x01, 0x27, 0x0a, 0x00, 0x03, 0x26, 0x00, 0x0c, 0x0f, 0x03, 0x00,
0xf2, 0x9c, 0xc3, 0x5c, 0x04, 0x05, 0x26, 0x00, 0x00, 0x1d, 0x09, 0x07,
0x33, 0x09, 0x08, 0x10, 0x0a, 0x09, 0x23, 0x09, 0x01, 0x05, 0x09, 0x5c,
0x06, 0x06, 0x01, 0x09, 0x01, 0x10, 0x0a, 0x0a, 0x23, 0x09, 0x06, 0x0a,
0x23, 0x09, 0x5c, 0x0a, 0x07, 0x4d, 0x09, 0x28, 0x09, 0x00, 0x73, 0x01,
0x09, 0x01, 0x10, 0x0a, 0x0a, 0x23, 0x09, 0x08, 0x0a, 0x23, 0x09, 0x5c,
0x0a, 0x06, 0x32, 0x09, 0x0b, 0x01, 0x28, 0x09, 0x00, 0x17, 0x1d, 0x09,
0x07, 0x33, 0x09, 0x08, 0x01, 0x0a, 0x01, 0x10, 0x0b, 0x0a, 0x23, 0x0a,
0x08, 0x0b, 0x23, 0x0a, 0x23, 0x09, 0x01, 0x05, 0x09, 0x01, 0x09, 0x05,
0x29, 0x09, 0x00, 0x03, 0x26, 0x00, 0x0e, 0x1d, 0x09, 0x07, 0x33, 0x09,
0x08, 0x10, 0x0a, 0x09, 0x23, 0x09, 0x01, 0x05, 0x09, 0x01, 0x09, 0x01,
0x10, 0x0a, 0x0a, 0x23, 0x09, 0x07, 0x0a, 0x23, 0x09, 0x5c, 0x0a, 0x06,
0x32, 0x09, 0x0b, 0x01, 0x28, 0x09, 0x00, 0x16, 0x1d, 0x09, 0x0c, 0x01,
0x0a, 0x01, 0x10, 0x0b, 0x0a, 0x23, 0x0a, 0x07, 0x0b, 0x23, 0x0a, 0x32,
0x09, 0x0d, 0x01, 0x01, 0x06, 0x09, 0x5c, 0x09, 0x08, 0x01, 0x0a, 0x04,
0x5d, 0x09, 0x5c, 0x0a, 0x08, 0x5d, 0x09, 0x01, 0x0a, 0x01, 0x10, 0x0b,
0x00, 0x23, 0x0a, 0x33, 0x0a, 0x0e, 0x33, 0x0a, 0x0f, 0x5d, 0x09, 0x5c,
0x0a, 0x09, 0x5d, 0x09, 0x01, 0x0a, 0x05, 0x10, 0x0b, 0x10, 0x23, 0x0a,
0x5d, 0x09, 0x5c, 0x0a, 0x08, 0x5d, 0x09, 0x01, 0x0a, 0x06, 0x5d, 0x09,
0x5c, 0x0a, 0x0a, 0x5d, 0x09, 0x01, 0x07, 0x09, 0x52, 0x08, 0x00, 0x01,
0x09, 0x08, 0x10, 0x0a, 0x11, 0x0f, 0x0b, 0x00, 0x0b, 0x0e, 0x14, 0x10,
0x0c, 0x12, 0x01, 0x0d, 0x03, 0x10, 0x0e, 0x13, 0x08, 0x0f, 0x10, 0x10,
0x14, 0x06, 0x11, 0x10, 0x12, 0x15, 0x03, 0x13, 0x0a, 0x5e, 0x0a, 0x05,
0x32, 0x09, 0x16, 0x01, 0x01, 0x09, 0x08, 0x10, 0x0a, 0x11, 0x01, 0x0b,
0x03, 0x10, 0x0c, 0x12, 0x0f, 0x0d, 0x00, 0x33, 0x36, 0x3c, 0x10, 0x0e,
0x14, 0x03, 0x0f, 0x0a, 0x10, 0x10, 0x15, 0x07, 0x11, 0x5e, 0x0a, 0x04,
0x32, 0x09, 0x16, 0x01, 0x01, 0x09, 0x08, 0x10, 0x0a, 0x11, 0x0f, 0x0b,
0x00, 0x33, 0x36, 0x3c, 0x10, 0x0c, 0x12, 0x0f, 0x0d, 0x00, 0x24, 0x27,
0x2d, 0x10, 0x0e, 0x14, 0x03, 0x0f, 0x0b, 0x10, 0x10, 0x15, 0x07, 0x11,
0x5e, 0x0a, 0x04, 0x32, 0x09, 0x16, 0x01, 0x01, 0x09, 0x08, 0x10, 0x0a,
0x11, 0x01, 0x0b, 0x05, 0x10, 0x0c, 0x17, 0x23, 0x0b, 0x10, 0x0c, 0x12,
0x0f, 0x0d, 0x00, 0x24, 0x27, 0x2d, 0x10, 0x0e, 0x14, 0x03, 0x0f, 0x0d,
0x10, 0x10, 0x15, 0x08, 0x11, 0x5e, 0x0a, 0x04, 0x32, 0x09, 0x16, 0x01,
0x01, 0x09, 0x08, 0x10, 0x0a, 0x11, 0x0f, 0x0b, 0x00, 0xce, 0xd4, 0xdf,
0x10, 0x0c, 0x12, 0x0f, 0x0d, 0x00, 0x24, 0x27, 0x2d, 0x10, 0x0e, 0x14,
0x03, 0x0f, 0x0f, 0x10, 0x10, 0x15, 0x01, 0x11, 0x06, 0x33, 0x11, 0x15,
0x46, 0x11, 0x01, 0x5e, 0x0a, 0x04, 0x32, 0x09, 0x16, 0x01, 0x01, 0x09,
0x08, 0x10, 0x0a, 0x11, 0x0f, 0x0b, 0x00, 0x24, 0x27, 0x2d, 0x10, 0x0c,
0x12, 0x06, 0x0d, 0x10, 0x0e, 0x14, 0x03, 0x0f, 0x0f, 0x01, 0x10, 0x06,
0x33, 0x10, 0x15, 0x45, 0x0f, 0x46, 0x0f, 0x01, 0x10, 0x10, 0x15, 0x07,
0x11, 0x5e, 0x0a, 0x04, 0x32, 0x09, 0x16, 0x01, 0x10, 0x09, 0x18, 0x01,
0x0a, 0x07, 0x10, 0x0b, 0x19, 0x01, 0x0c, 0x08, 0x5e, 0x09, 0x02, 0x52,
0x09, 0x01, 0x3d, 0x09, 0x00, 0x0b, 0x00, 0x00, 0x02, 0x20, 0x20, 0x00,
0x00, 0x00, 0x04, 0xee, 0x99, 0x8e, 0x20, 0x00, 0x00, 0x00, 0x05, 0xf3,
0xb1, 0x93, 0xa7, 0x20, 0x00, 0x00, 0x00, 0x05, 0xf3, 0xb1, 0xa9, 0xa7,
0x20, 0x00, 0x00, 0x00, 0x04, 0xef, 0x84, 0xa0, 0x20, 0x00, 0x00, 0x00,
0x04, 0xee, 0xba, 0xa2, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x06, 0x65, 0x64, 0x69, 0x74, 0x6f, 0x72, 0x00, 0x00, 0x00, 0x01, 0x20,
0x00, 0x00, 0x00, 0x08, 0x20, 0xee, 0x82, 0xb4, 0xee, 0x82, 0xb4, 0x20,
0x00, 0x00, 0x00, 0x03, 0xee, 0x82, 0xb4, 0x00, 0x00, 0x1a, 0x00, 0x04,
0x6d, 0x6f, 0x64, 0x65, 0x00, 0x00, 0x06, 0x6e, 0x6f, 0x72, 0x6d, 0x61,
0x6c, 0x00, 0x00, 0x03, 0x3d, 0x3d, 0x3d, 0x00, 0x00, 0x06, 0x69, 0x6e,
0x73, 0x65, 0x72, 0x74, 0x00, 0x00, 0x06, 0x73, 0x65, 0x6c, 0x65, 0x63,
0x74, 0x00, 0x00, 0x06, 0x72, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x00, 0x00,
0x06, 0x6a, 0x75, 0x6d, 0x70, 0x65, 0x72, 0x00, 0x00, 0x01, 0x43, 0x00,
0x00, 0x09, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x73, 0x00,
0x00, 0x07, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x00, 0x00, 0x04,
0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x02, 0x21, 0x3d, 0x00, 0x00, 0x04,
0x46, 0x69, 0x6c, 0x65, 0x00, 0x00, 0x08, 0x62, 0x61, 0x73, 0x65, 0x6e,
0x61, 0x6d, 0x65, 0x00, 0x00, 0x04, 0x74, 0x6f, 0x5f, 0x73, 0x00, 0x00,
0x06, 0x75, 0x70, 0x63, 0x61, 0x73, 0x65, 0x00, 0x00, 0x06, 0x73, 0x79,
0x6d, 0x62, 0x6f, 0x6c, 0x00, 0x00, 0x02, 0x66, 0x67, 0x00, 0x00, 0x02,
0x62, 0x67, 0x00, 0x00, 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x00, 0x00,
0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x00, 0x00, 0x06, 0x6c, 0x65, 0x6e,
0x67, 0x74, 0x68, 0x00, 0x00, 0x02, 0x3c, 0x3c, 0x00, 0x00, 0x05, 0x63,
0x6f, 0x6c, 0x6f, 0x72, 0x00, 0x00, 0x04, 0x74, 0x65, 0x78, 0x74, 0x00,
0x00, 0x0a, 0x68, 0x69, 0x67, 0x68, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x73,
0x00, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, 0x06, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x10, 0x39, 0x04, 0x00, 0x00, 0x1d, 0x03, 0x00,
0x01, 0x04, 0x01, 0x32, 0x03, 0x01, 0x01, 0x3d, 0x03, 0x00, 0x00, 0x00,
0x02, 0x00, 0x09, 0x43, 0x6c, 0x69, 0x70, 0x62, 0x6f, 0x61, 0x72, 0x64,
0x00, 0x00, 0x04, 0x63, 0x6f, 0x70, 0x79, 0x00, 0x00, 0x00, 0x00, 0x37,
0x00, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f,
0x39, 0x00, 0x00, 0x00, 0x1d, 0x02, 0x00, 0x33, 0x02, 0x01, 0x52, 0x02,
0x01, 0x3d, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x09, 0x43, 0x6c, 0x69,
0x70, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x00, 0x00, 0x05, 0x70, 0x61, 0x73,
0x74, 0x65, 0x00, 0x00, 0x00, 0x04, 0x13, 0x00, 0x07, 0x00, 0x0b, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x80, 0x39, 0x04, 0x00, 0x00, 0x10,
0x03, 0x00, 0x1d, 0x07, 0x01, 0x01, 0x08, 0x01, 0x32, 0x07, 0x02, 0x01,
0x27, 0x07, 0x00, 0x08, 0x01, 0x07, 0x03, 0x52, 0x07, 0x01, 0x3d, 0x07,
0x1d, 0x07, 0x01, 0x01, 0x08, 0x01, 0x10, 0x09, 0x03, 0x34, 0x07, 0x04,
0x01, 0x33, 0x07, 0x05, 0x01, 0x04, 0x07, 0x01, 0x07, 0x04, 0x5c, 0x08,
0x00, 0x32, 0x07, 0x06, 0x01, 0x28, 0x07, 0x00, 0xde, 0x01, 0x07, 0x04,
0x08, 0x08, 0x11, 0x09, 0x64, 0x08, 0x23, 0x07, 0x33, 0x07, 0x07, 0x01,
0x05, 0x07, 0x01, 0x07, 0x05, 0x66, 0x08, 0x1f, 0x08, 0x08, 0x5c, 0x09,
0x01, 0x32, 0x08, 0x09, 0x01, 0x01, 0x09, 0x07, 0x32, 0x08, 0x0a, 0x01,
0x27, 0x08, 0x00, 0x1a, 0x66, 0x08, 0x1f, 0x08, 0x08, 0x5c, 0x09, 0x02,
0x32, 0x08, 0x09, 0x01, 0x01, 0x09, 0x07, 0x32, 0x08, 0x0a, 0x01, 0x27,
0x08, 0x00, 0x03, 0x26, 0x00, 0x06, 0x10, 0x08, 0x0b, 0x26, 0x00, 0x88,
0x66, 0x08, 0x1f, 0x08, 0x08, 0x5c, 0x09, 0x03, 0x32, 0x08, 0x09, 0x01,
0x01, 0x09, 0x07, 0x32, 0x08, 0x0a, 0x01, 0x27, 0x08, 0x00, 0x03, 0x26,
0x00, 0x06, 0x10, 0x08, 0x0c, 0x26, 0x00, 0x68, 0x66, 0x08, 0x1f, 0x08,
0x08, 0x5c, 0x09, 0x04, 0x32, 0x08, 0x09, 0x01, 0x01, 0x09, 0x07, 0x32,
0x08, 0x0a, 0x01, 0x27, 0x08, 0x00, 0x03, 0x26, 0x00, 0x06, 0x10, 0x08,
0x0d, 0x26, 0x00, 0x48, 0x66, 0x08, 0x1f, 0x08, 0x08, 0x5c, 0x09, 0x05,
0x32, 0x08, 0x09, 0x01, 0x01, 0x09, 0x07, 0x32, 0x08, 0x0a, 0x01, 0x27,
0x08, 0x00, 0x03, 0x26, 0x00, 0x06, 0x10, 0x08, 0x0e, 0x26, 0x00, 0x28,
0x66, 0x08, 0x1f, 0x08, 0x08, 0x5c, 0x09, 0x06, 0x32, 0x08, 0x09, 0x01,
0x01, 0x09, 0x07, 0x32, 0x08, 0x0a, 0x01, 0x27, 0x08, 0x00, 0x03, 0x26,
0x00, 0x06, 0x10, 0x08, 0x0f, 0x26, 0x00, 0x08, 0x10, 0x08, 0x00, 0x26,
0x00, 0x02, 0x11, 0x08, 0x01, 0x03, 0x08, 0x01, 0x07, 0x03, 0x52, 0x07,
0x01, 0x3d, 0x07, 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, 0x08, 0x01, 0x07, 0x03, 0x52, 0x07,
0x01, 0x3d, 0x07, 0x5c, 0x08, 0x07, 0x2f, 0x07, 0x13, 0x01, 0x33, 0x07,
0x14, 0x28, 0x07, 0x00, 0x08, 0x01, 0x07, 0x03, 0x52, 0x07, 0x01, 0x3d,
0x07, 0x12, 0x07, 0x5c, 0x08, 0x08, 0x01, 0x09, 0x01, 0x5d, 0x08, 0x2f,
0x07, 0x16, 0x01, 0x33, 0x07, 0x05, 0x01, 0x06, 0x07, 0x01, 0x07, 0x06,
0x66, 0x08, 0x1f, 0x08, 0x08, 0x5c, 0x09, 0x09, 0x32, 0x08, 0x09, 0x01,
0x01, 0x09, 0x07, 0x32, 0x08, 0x0a, 0x01, 0x27, 0x08, 0x00, 0x03, 0x26,
0x00, 0x06, 0x10, 0x08, 0x0b, 0x26, 0x00, 0xe8, 0x66, 0x08, 0x1f, 0x08,
0x08, 0x5c, 0x09, 0x05, 0x32, 0x08, 0x09, 0x01, 0x01, 0x09, 0x07, 0x32,
0x08, 0x0a, 0x01, 0x27, 0x08, 0x00, 0x03, 0x26, 0x00, 0x06, 0x10, 0x08,
0x0e, 0x26, 0x00, 0xc8, 0x66, 0x08, 0x1f, 0x08, 0x08, 0x5c, 0x09, 0x0a,
0x32, 0x08, 0x09, 0x01, 0x01, 0x09, 0x07, 0x32, 0x08, 0x0a, 0x01, 0x27,
0x08, 0x00, 0x03, 0x26, 0x00, 0x06, 0x10, 0x08, 0x17, 0x26, 0x00, 0xa8,
0x66, 0x08, 0x1f, 0x08, 0x08, 0x5c, 0x09, 0x0b, 0x32, 0x08, 0x09, 0x01,
0x01, 0x09, 0x07, 0x32, 0x08, 0x0a, 0x01, 0x27, 0x08, 0x00, 0x03, 0x26,
0x00, 0x06, 0x10, 0x08, 0x18, 0x26, 0x00, 0x88, 0x66, 0x08, 0x1f, 0x08,
0x08, 0x5c, 0x09, 0x04, 0x32, 0x08, 0x09, 0x01, 0x01, 0x09, 0x07, 0x32,
0x08, 0x0a, 0x01, 0x27, 0x08, 0x00, 0x03, 0x26, 0x00, 0x06, 0x10, 0x08,
0x0d, 0x26, 0x00, 0x68, 0x66, 0x08, 0x1f, 0x08, 0x08, 0x5c, 0x09, 0x0c,
0x32, 0x08, 0x09, 0x01, 0x01, 0x09, 0x07, 0x32, 0x08, 0x0a, 0x01, 0x27,
0x08, 0x00, 0x03, 0x26, 0x00, 0x06, 0x10, 0x08, 0x19, 0x26, 0x00, 0x48,
0x66, 0x08, 0x1f, 0x08, 0x08, 0x5c, 0x09, 0x0d, 0x32, 0x08, 0x09, 0x01,
0x01, 0x09, 0x07, 0x32, 0x08, 0x0a, 0x01, 0x27, 0x08, 0x00, 0x03, 0x26,
0x00, 0x06, 0x10, 0x08, 0x1a, 0x26, 0x00, 0x28, 0x66, 0x08, 0x1f, 0x08,
0x08, 0x5c, 0x09, 0x0e, 0x32, 0x08, 0x09, 0x01, 0x01, 0x09, 0x07, 0x32,
0x08, 0x0a, 0x01, 0x27, 0x08, 0x00, 0x03, 0x26, 0x00, 0x06, 0x10, 0x08,
0x1b, 0x26, 0x00, 0x08, 0x10, 0x08, 0x00, 0x26, 0x00, 0x02, 0x11, 0x08,
0x01, 0x03, 0x08, 0x01, 0x07, 0x03, 0x52, 0x07, 0x01, 0x3d, 0x07, 0x00,
0x0f, 0x00, 0x00, 0x02, 0x23, 0x21, 0x00, 0x00, 0x00, 0x04, 0x62, 0x61,
0x73, 0x68, 0x00, 0x00, 0x00, 0x02, 0x73, 0x68, 0x00, 0x00, 0x00, 0x04,
0x66, 0x69, 0x73, 0x68, 0x00, 0x00, 0x00, 0x06, 0x70, 0x79, 0x74, 0x68,
0x6f, 0x6e, 0x00, 0x00, 0x00, 0x04, 0x72, 0x75, 0x62, 0x79, 0x00, 0x00,
0x00, 0x03, 0x6c, 0x75, 0x61, 0x00, 0x00, 0x00, 0x04, 0x66, 0x69, 0x6c,
0x65, 0x00, 0x00, 0x00, 0x14, 0x66, 0x69, 0x6c, 0x65, 0x20, 0x2d, 0x2d,
0x6d, 0x69, 0x6d, 0x65, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x20, 0x2d, 0x62,
0x20, 0x00, 0x00, 0x00, 0x0b, 0x73, 0x68, 0x65, 0x6c, 0x6c, 0x73, 0x63,
0x72, 0x69, 0x70, 0x74, 0x00, 0x00, 0x00, 0x04, 0x64, 0x69, 0x66, 0x66,
0x00, 0x00, 0x00, 0x04, 0x68, 0x74, 0x6d, 0x6c, 0x00, 0x00, 0x00, 0x0a,
0x6a, 0x61, 0x76, 0x61, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x00, 0x00,
0x00, 0x08, 0x6d, 0x61, 0x6b, 0x65, 0x66, 0x69, 0x6c, 0x65, 0x00, 0x00,
0x00, 0x03, 0x2d, 0x63, 0x24, 0x00, 0x00, 0x1c, 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, 0x06, 0x4b, 0x65, 0x72,
0x6e, 0x65, 0x6c, 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, 0x01,
0x00, 0x00, 0x00, 0x16, 0x00, 0x05, 0x74, 0x68, 0x65, 0x6d, 0x65, 0x00,
0x00, 0x0a, 0x6c, 0x73, 0x70, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67,
0x00, 0x00, 0x09, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x73,
0x00, 0x00, 0x0c, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x65, 0x6e, 0x64, 0x69,
0x6e, 0x67, 0x73, 0x00, 0x00, 0x0c, 0x68, 0x69, 0x67, 0x68, 0x6c, 0x69,
0x67, 0x68, 0x74, 0x65, 0x72, 0x73, 0x00, 0x00, 0x0d, 0x61, 0x74, 0x74,
0x72, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x00, 0x00,
0x09, 0x62, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x00, 0x00,
0x0a, 0x62, 0x5f, 0x73, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x00,
0x00, 0x12, 0x62, 0x5f, 0x65, 0x78, 0x74, 0x72, 0x61, 0x5f, 0x68, 0x69,
0x67, 0x68, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x73, 0x00, 0x00, 0x05, 0x62,
0x5f, 0x62, 0x61, 0x72, 0x00, 0x00, 0x06, 0x62, 0x5f, 0x63, 0x6f, 0x70,
0x79, 0x00, 0x00, 0x07, 0x62, 0x5f, 0x70, 0x61, 0x73, 0x74, 0x65, 0x00,
0x00, 0x0d, 0x62, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x64, 0x65, 0x74,
0x65, 0x63, 0x74, 0x00, 0x00, 0x0b, 0x61, 0x74, 0x74, 0x72, 0x5f, 0x72,
0x65, 0x61, 0x64, 0x65, 0x72, 0x00, 0x00, 0x04, 0x62, 0x61, 0x72, 0x3d,
0x00, 0x00, 0x07, 0x73, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x00, 0x00,
0x08, 0x73, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x00, 0x00, 0x04,
0x63, 0x6f, 0x70, 0x79, 0x00, 0x00, 0x05, 0x70, 0x61, 0x73, 0x74, 0x65,
0x00, 0x00, 0x0b, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x64, 0x65, 0x74, 0x65,
0x63, 0x74, 0x00, 0x00, 0x10, 0x65, 0x78, 0x74, 0x72, 0x61, 0x5f, 0x68,
0x69, 0x67, 0x68, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x73, 0x00, 0x00, 0x04,
0x62, 0x69, 0x6e, 0x64, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x03, 0x00,
0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x39, 0x00, 0x00,
0x01, 0x01, 0x02, 0x01, 0x01, 0x03, 0x02, 0x1a, 0x03, 0x00, 0x3d, 0x03,
0x00, 0x00, 0x00, 0x01, 0x00, 0x06, 0x40, 0x62, 0x5f, 0x62, 0x61, 0x72,
0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x0f, 0x39, 0x00, 0x00, 0x01, 0x01, 0x02, 0x01,
0x01, 0x03, 0x02, 0x1a, 0x03, 0x00, 0x3d, 0x03, 0x00, 0x00, 0x00, 0x01,
0x00, 0x0a, 0x40, 0x62, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70,
0x00, 0x00, 0x00, 0x00, 0x31, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x0f, 0x39, 0x00, 0x00, 0x01, 0x01, 0x02, 0x01,
0x01, 0x03, 0x02, 0x1a, 0x03, 0x00, 0x3d, 0x03, 0x00, 0x00, 0x00, 0x01,
0x00, 0x0b, 0x40, 0x62, 0x5f, 0x73, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77,
0x6e, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x39, 0x00, 0x00, 0x01, 0x01, 0x02,
0x01, 0x01, 0x03, 0x02, 0x1a, 0x03, 0x00, 0x3d, 0x03, 0x00, 0x00, 0x00,
0x01, 0x00, 0x07, 0x40, 0x62, 0x5f, 0x63, 0x6f, 0x70, 0x79, 0x00, 0x00,
0x00, 0x00, 0x2e, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x0f, 0x39, 0x00, 0x00, 0x01, 0x01, 0x02, 0x01, 0x01, 0x03,
0x02, 0x1a, 0x03, 0x00, 0x3d, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x08,
0x40, 0x62, 0x5f, 0x70, 0x61, 0x73, 0x74, 0x65, 0x00, 0x00, 0x00, 0x00,
0x34, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x0f, 0x39, 0x00, 0x00, 0x01, 0x01, 0x02, 0x01, 0x01, 0x03, 0x02, 0x1a,
0x03, 0x00, 0x3d, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x0e, 0x40, 0x62,
0x5f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x64, 0x65, 0x74, 0x65, 0x63, 0x74,
0x00, 0x00, 0x00, 0x00, 0x39, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x0f, 0x39, 0x00, 0x00, 0x01, 0x01, 0x02, 0x01,
0x01, 0x03, 0x02, 0x1a, 0x03, 0x00, 0x3d, 0x03, 0x00, 0x00, 0x00, 0x01,
0x00, 0x13, 0x40, 0x62, 0x5f, 0x65, 0x78, 0x74, 0x72, 0x61, 0x5f, 0x68,
0x69, 0x67, 0x68, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x73, 0x00, 0x00, 0x00,
0x01, 0x43, 0x00, 0x08, 0x00, 0x0b, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00,
0x00, 0xc1, 0x39, 0x04, 0x40, 0x01, 0x26, 0x00, 0x06, 0x26, 0x00, 0x05,
0x26, 0x00, 0x04, 0x11, 0x02, 0x11, 0x03, 0x01, 0x05, 0x04, 0x01, 0x08,
0x01, 0x1d, 0x09, 0x00, 0x32, 0x08, 0x01, 0x01, 0x27, 0x08, 0x00, 0x07,
0x01, 0x08, 0x01, 0x53, 0x01, 0x08, 0x01, 0x01, 0x08, 0x02, 0x29, 0x08,
0x00, 0x03, 0x26, 0x00, 0x31, 0x12, 0x06, 0x1d, 0x08, 0x02, 0x33, 0x08,
0x03, 0x01, 0x07, 0x08, 0x01, 0x08, 0x07, 0x10, 0x09, 0x04, 0x62, 0x0a,
0x00, 0x34, 0x08, 0x05, 0x01, 0x30, 0x08, 0x06, 0x28, 0x08, 0x00, 0x0d,
0x01, 0x08, 0x07, 0x01, 0x09, 0x05, 0x34, 0x08, 0x07, 0x00, 0x26, 0x00,
0x02, 0x11, 0x08, 0x26, 0x00, 0x5b, 0x30, 0x08, 0x06, 0x28, 0x08, 0x00,
0x22, 0x01, 0x08, 0x02, 0x1d, 0x09, 0x00, 0x32, 0x08, 0x01, 0x01, 0x27,
0x08, 0x00, 0x07, 0x01, 0x08, 0x02, 0x53, 0x02, 0x08, 0x01, 0x01, 0x08,
0x01, 0x62, 0x09, 0x01, 0x34, 0x08, 0x08, 0x00, 0x26, 0x00, 0x32, 0x01,
0x08, 0x03, 0x1d, 0x09, 0x09, 0x32, 0x08, 0x01, 0x01, 0x28, 0x08, 0x00,
0x22, 0x01, 0x08, 0x02, 0x1d, 0x09, 0x00, 0x32, 0x08, 0x01, 0x01, 0x27,
0x08, 0x00, 0x07, 0x01, 0x08, 0x02, 0x53, 0x02, 0x08, 0x01, 0x01, 0x08,
0x01, 0x62, 0x09, 0x02, 0x34, 0x08, 0x08, 0x00, 0x26, 0x00, 0x02, 0x11,
0x08, 0x3d, 0x08, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x05, 0x41, 0x72, 0x72,
0x61, 0x79, 0x00, 0x00, 0x05, 0x69, 0x73, 0x5f, 0x61, 0x3f, 0x00, 0x00,
0x06, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x00, 0x00, 0x03, 0x6e, 0x65,
0x77, 0x00, 0x00, 0x03, 0x73, 0x65, 0x74, 0x00, 0x00, 0x17, 0x64, 0x65,
0x66, 0x69, 0x6e, 0x65, 0x5f, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x74,
0x6f, 0x6e, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x00, 0x00, 0x0c,
0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x67, 0x69, 0x76, 0x65, 0x6e, 0x3f,
0x00, 0x00, 0x0d, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f,
0x65, 0x78, 0x65, 0x63, 0x00, 0x00, 0x04, 0x65, 0x61, 0x63, 0x68, 0x00,
0x00, 0x06, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x00,
0x41, 0x00, 0x05, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x26, 0x39, 0x04, 0x20, 0x01, 0x26, 0x00, 0x03, 0x26, 0x00, 0x02, 0x11,
0x02, 0x01, 0x04, 0x03, 0x21, 0x05, 0x06, 0x00, 0x21, 0x06, 0x01, 0x00,
0x01, 0x07, 0x01, 0x01, 0x08, 0x02, 0x01, 0x09, 0x04, 0x34, 0x05, 0x00,
0x03, 0x3d, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x04, 0x62, 0x69, 0x6e,
0x64, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x03, 0x00, 0x05, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x39, 0x04, 0x00, 0x00, 0x21, 0x03,
0x02, 0x00, 0x62, 0x04, 0x00, 0x34, 0x03, 0x00, 0x00, 0x3d, 0x03, 0x00,
0x00, 0x00, 0x01, 0x00, 0x04, 0x65, 0x61, 0x63, 0x68, 0x00, 0x00, 0x00,
0x00, 0x8d, 0x00, 0x03, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x59, 0x39, 0x04, 0x00, 0x00, 0x19, 0x03, 0x00, 0x21, 0x04, 0x01,
0x00, 0x01, 0x05, 0x03, 0x01, 0x06, 0x04, 0x32, 0x05, 0x01, 0x01, 0x27,
0x05, 0x00, 0x07, 0x5e, 0x05, 0x00, 0x32, 0x03, 0x02, 0x02, 0x19, 0x03,
0x00, 0x21, 0x04, 0x01, 0x00, 0x23, 0x03, 0x01, 0x04, 0x01, 0x01, 0x05,
0x03, 0x01, 0x06, 0x04, 0x32, 0x05, 0x01, 0x01, 0x27, 0x05, 0x00, 0x07,
0x52, 0x05, 0x00, 0x32, 0x03, 0x02, 0x02, 0x19, 0x03, 0x00, 0x21, 0x04,
0x01, 0x00, 0x23, 0x03, 0x01, 0x04, 0x01, 0x23, 0x03, 0x21, 0x04, 0x05,
0x01, 0x32, 0x03, 0x03, 0x01, 0x3d, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00,
0x0d, 0x40, 0x6b, 0x65, 0x79, 0x5f, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65,
0x72, 0x73, 0x00, 0x00, 0x02, 0x5b, 0x5d, 0x00, 0x00, 0x03, 0x5b, 0x5d,
0x3d, 0x00, 0x00, 0x02, 0x3c, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00,
0x03, 0x00, 0x05, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x39,
0x04, 0x00, 0x00, 0x21, 0x03, 0x02, 0x00, 0x62, 0x04, 0x00, 0x34, 0x03,
0x00, 0x00, 0x3d, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x04, 0x65, 0x61,
0x63, 0x68, 0x00, 0x00, 0x00, 0x00, 0x8a, 0x00, 0x03, 0x00, 0x08, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x59, 0x39, 0x04, 0x00, 0x00, 0x19,
0x03, 0x00, 0x21, 0x04, 0x01, 0x00, 0x01, 0x05, 0x03, 0x01, 0x06, 0x04,
0x32, 0x05, 0x01, 0x01, 0x27, 0x05, 0x00, 0x07, 0x5e, 0x05, 0x00, 0x32,
0x03, 0x02, 0x02, 0x19, 0x03, 0x00, 0x21, 0x04, 0x01, 0x00, 0x23, 0x03,
0x01, 0x04, 0x01, 0x01, 0x05, 0x03, 0x01, 0x06, 0x04, 0x32, 0x05, 0x01,
0x01, 0x27, 0x05, 0x00, 0x07, 0x52, 0x05, 0x00, 0x32, 0x03, 0x02, 0x02,
0x19, 0x03, 0x00, 0x21, 0x04, 0x01, 0x00, 0x23, 0x03, 0x01, 0x04, 0x01,
0x23, 0x03, 0x21, 0x04, 0x03, 0x01, 0x32, 0x03, 0x03, 0x01, 0x3d, 0x03,
0x00, 0x00, 0x00, 0x04, 0x00, 0x0a, 0x40, 0x6b, 0x65, 0x79, 0x5f, 0x62,
0x69, 0x6e, 0x64, 0x73, 0x00, 0x00, 0x02, 0x5b, 0x5d, 0x00, 0x00, 0x03,
0x5b, 0x5d, 0x3d, 0x00, 0x00, 0x02, 0x3c, 0x3c, 0x00, 0x00, 0x00, 0x00,
0x38, 0x00, 0x01, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
0x0a, 0x6b, 0x01, 0x00, 0x00, 0x6b, 0x01, 0x01, 0x01, 0x3d, 0x01, 0x00,
0x00, 0x00, 0x02, 0x00, 0x10, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65,
0x5f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x76, 0x65, 0x00, 0x00, 0x04,
0x6c, 0x6f, 0x61, 0x64, 0x00, 0x00, 0x00, 0x01, 0x07, 0x00, 0x05, 0x00,
0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7b, 0x39, 0x04, 0x20,
0x00, 0x26, 0x00, 0x03, 0x26, 0x00, 0x02, 0x11, 0x02, 0x01, 0x05, 0x01,
0x5c, 0x06, 0x00, 0x32, 0x05, 0x00, 0x01, 0x27, 0x05, 0x00, 0x0b, 0x01,
0x05, 0x01, 0x5c, 0x06, 0x00, 0x45, 0x05, 0x01, 0x01, 0x05, 0x1d, 0x05,
0x01, 0x01, 0x06, 0x01, 0x1d, 0x07, 0x01, 0x1d, 0x08, 0x02, 0x33, 0x08,
0x03, 0x32, 0x07, 0x04, 0x01, 0x32, 0x05, 0x05, 0x02, 0x01, 0x01, 0x05,
0x15, 0x05, 0x06, 0x01, 0x06, 0x01, 0x32, 0x05, 0x07, 0x01, 0x28, 0x05,
0x00, 0x01, 0x40, 0x15, 0x05, 0x06, 0x01, 0x06, 0x01, 0x32, 0x05, 0x08,
0x01, 0x1d, 0x05, 0x01, 0x01, 0x06, 0x01, 0x32, 0x05, 0x09, 0x01, 0x01,
0x04, 0x05, 0x01, 0x06, 0x04, 0x01, 0x07, 0x02, 0x27, 0x07, 0x00, 0x03,
0x15, 0x07, 0x0a, 0x01, 0x08, 0x01, 0x2f, 0x05, 0x0b, 0x03, 0x3d, 0x05,
0x00, 0x01, 0x00, 0x00, 0x03, 0x2e, 0x72, 0x62, 0x00, 0x00, 0x0c, 0x00,
0x09, 0x65, 0x6e, 0x64, 0x5f, 0x77, 0x69, 0x74, 0x68, 0x3f, 0x00, 0x00,
0x04, 0x46, 0x69, 0x6c, 0x65, 0x00, 0x00, 0x01, 0x43, 0x00, 0x00, 0x0b,
0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x00,
0x00, 0x07, 0x64, 0x69, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x00, 0x00, 0x0b,
0x65, 0x78, 0x70, 0x61, 0x6e, 0x64, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x00,
0x00, 0x07, 0x24, 0x4c, 0x4f, 0x41, 0x44, 0x45, 0x44, 0x00, 0x00, 0x08,
0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x3f, 0x00, 0x00, 0x02, 0x3c,
0x3c, 0x00, 0x00, 0x04, 0x72, 0x65, 0x61, 0x64, 0x00, 0x00, 0x09, 0x24,
0x42, 0x49, 0x4e, 0x44, 0x5f, 0x54, 0x4f, 0x50, 0x00, 0x00, 0x04, 0x65,
0x76, 0x61, 0x6c, 0x00, 0x00, 0x00, 0x00, 0xd3, 0x00, 0x04, 0x00, 0x09,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x39, 0x04, 0x20, 0x00,
0x26, 0x00, 0x03, 0x26, 0x00, 0x02, 0x11, 0x02, 0x01, 0x04, 0x01, 0x5c,
0x05, 0x00, 0x32, 0x04, 0x00, 0x01, 0x27, 0x04, 0x00, 0x0b, 0x01, 0x04,
0x01, 0x5c, 0x05, 0x00, 0x45, 0x04, 0x01, 0x01, 0x04, 0x1d, 0x04, 0x01,
0x01, 0x05, 0x01, 0x1d, 0x06, 0x01, 0x1d, 0x07, 0x02, 0x33, 0x07, 0x03,
0x32, 0x06, 0x04, 0x01, 0x32, 0x04, 0x05, 0x02, 0x01, 0x01, 0x04, 0x15,
0x04, 0x06, 0x01, 0x05, 0x01, 0x32, 0x04, 0x07, 0x01, 0x01, 0x05, 0x01,
0x01, 0x06, 0x02, 0x2f, 0x04, 0x08, 0x02, 0x3d, 0x04, 0x00, 0x01, 0x00,
0x00, 0x03, 0x2e, 0x72, 0x62, 0x00, 0x00, 0x09, 0x00, 0x09, 0x65, 0x6e,
0x64, 0x5f, 0x77, 0x69, 0x74, 0x68, 0x3f, 0x00, 0x00, 0x04, 0x46, 0x69,
0x6c, 0x65, 0x00, 0x00, 0x01, 0x43, 0x00, 0x00, 0x0b, 0x63, 0x6f, 0x6e,
0x66, 0x69, 0x67, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x00, 0x00, 0x07, 0x64,
0x69, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x00, 0x00, 0x0b, 0x65, 0x78, 0x70,
0x61, 0x6e, 0x64, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x00, 0x00, 0x07, 0x24,
0x4c, 0x4f, 0x41, 0x44, 0x45, 0x44, 0x00, 0x00, 0x06, 0x64, 0x65, 0x6c,
0x65, 0x74, 0x65, 0x00, 0x00, 0x10, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72,
0x65, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x76, 0x65, 0x00, 0x4c,
0x56, 0x41, 0x52, 0x00, 0x00, 0x01, 0x78, 0x00, 0x00, 0x00, 0x1d, 0x00,
0x03, 0x63, 0x6d, 0x64, 0x00, 0x04, 0x74, 0x65, 0x78, 0x74, 0x00, 0x01,
0x66, 0x00, 0x07, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x00, 0x04,
0x69, 0x6e, 0x66, 0x6f, 0x00, 0x0a, 0x6d, 0x6f, 0x64, 0x65, 0x5f, 0x63,
0x6f, 0x6c, 0x6f, 0x72, 0x00, 0x0b, 0x6d, 0x6f, 0x64, 0x65, 0x5f, 0x73,
0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x00, 0x09, 0x6c, 0x61, 0x6e, 0x67, 0x5f,
0x69, 0x6e, 0x66, 0x6f, 0x00, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61,
0x6d, 0x65, 0x00, 0x08, 0x73, 0x74, 0x61, 0x72, 0x74, 0x69, 0x6e, 0x67,
0x00, 0x0a, 0x68, 0x69, 0x67, 0x68, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x73,
0x00, 0x04, 0x74, 0x79, 0x70, 0x65, 0x00, 0x0a, 0x66, 0x69, 0x72, 0x73,
0x74, 0x5f, 0x6c, 0x69, 0x6e, 0x65, 0x00, 0x07, 0x73, 0x68, 0x65, 0x62,
0x61, 0x6e, 0x67, 0x00, 0x08, 0x6d, 0x69, 0x6d, 0x65, 0x74, 0x79, 0x70,
0x65, 0x00, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x00, 0x05, 0x6d, 0x6f,
0x64, 0x65, 0x73, 0x00, 0x04, 0x6b, 0x65, 0x79, 0x73, 0x00, 0x06, 0x61,
0x63, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x03, 0x61, 0x70, 0x70, 0x00, 0x03,
0x64, 0x73, 0x6c, 0x00, 0x01, 0x6b, 0x00, 0x03, 0x61, 0x63, 0x74, 0x00,
0x03, 0x62, 0x6c, 0x6b, 0x00, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x00, 0x03,
0x6b, 0x65, 0x79, 0x00, 0x04, 0x70, 0x61, 0x74, 0x68, 0x00, 0x04, 0x62,
0x69, 0x6e, 0x64, 0x00, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x00, 0x00, 0xff,
0xff, 0x00, 0x01, 0xff, 0xff, 0x00, 0x02, 0xff, 0xff, 0x00, 0x02, 0xff,
0xff, 0x00, 0x02, 0xff, 0xff, 0x00, 0x02, 0xff, 0xff, 0x00, 0x02, 0xff,
0xff, 0xff, 0xff, 0x00, 0x01, 0xff, 0xff, 0x00, 0x03, 0x00, 0x04, 0xff,
0xff, 0x00, 0x05, 0x00, 0x06, 0x00, 0x07, 0x00, 0x08, 0x00, 0x09, 0x00,
0x0a, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0x00, 0x08, 0xff, 0xff, 0x00,
0x0b, 0x00, 0x0c, 0x00, 0x0d, 0x00, 0x0e, 0xff, 0xff, 0x00, 0x0f, 0xff,
0xff, 0x00, 0x0f, 0xff, 0xff, 0x00, 0x0f, 0xff, 0xff, 0x00, 0x0f, 0xff,
0xff, 0x00, 0x0f, 0xff, 0xff, 0x00, 0x0f, 0xff, 0xff, 0x00, 0x0f, 0x00,
0x10, 0x00, 0x11, 0x00, 0x12, 0xff, 0xff, 0x00, 0x0f, 0x00, 0x13, 0x00,
0x14, 0x00, 0x15, 0x00, 0x16, 0xff, 0xff, 0x00, 0x17, 0x00, 0x18, 0xff,
0xff, 0x00, 0x19, 0xff, 0xff, 0x00, 0x18, 0xff, 0xff, 0x00, 0x19, 0xff,
0xff, 0x00, 0x1a, 0x00, 0x1b, 0xff, 0xff, 0x00, 0x1c, 0x00, 0x1a, 0x00,
0x1b, 0xff, 0xff, 0x45, 0x4e, 0x44, 0x00, 0x00, 0x00, 0x00, 0x08
};
constexpr unsigned int _tmp___crib_precompiled_mrb_len = 11411;
-21
View File
@@ -1,21 +0,0 @@
#ifndef SCRIPTING_DECL_H
#define SCRIPTING_DECL_H
#include "syntax/decl.h"
#include "utils/utils.h"
extern std::unordered_map<std::string, std::pair<mrb_value, mrb_value>>
custom_highlighters;
void ruby_start();
void ruby_shutdown();
void load_theme();
void load_languages_info();
uint8_t read_line_endings();
void load_custom_highlighters();
mrb_value parse_custom(std::vector<Token> *tokens, mrb_value parser_block,
const char *line, uint32_t len, mrb_value state,
uint32_t c_line);
bool custom_compare(mrb_value match_block, mrb_value state1, mrb_value state2);
#endif
-530
View File
@@ -1,530 +0,0 @@
#pragma once
constexpr unsigned char _tmp___crib_precompiled_mrb[] = {
0x52, 0x49, 0x54, 0x45, 0x30, 0x33, 0x30, 0x30, 0x00, 0x00, 0x18, 0xa1,
0x4d, 0x41, 0x54, 0x5a, 0x30, 0x30, 0x30, 0x30, 0x49, 0x52, 0x45, 0x50,
0x00, 0x00, 0x18, 0x13, 0x30, 0x33, 0x30, 0x30, 0x00, 0x00, 0x00, 0x34,
0x00, 0x01, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13,
0x11, 0x01, 0x5d, 0x01, 0x00, 0x5e, 0x01, 0x00, 0x11, 0x01, 0x5d, 0x01,
0x01, 0x5e, 0x01, 0x01, 0x38, 0x01, 0x69, 0x00, 0x00, 0x00, 0x02, 0x00,
0x06, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x00, 0x00, 0x01, 0x43, 0x00,
0x00, 0x00, 0x03, 0xfd, 0x00, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x01, 0x3c, 0x06, 0x01, 0x1e, 0x01, 0x00, 0x07, 0x01, 0x1e,
0x01, 0x01, 0x08, 0x01, 0x1e, 0x01, 0x02, 0x09, 0x01, 0x1e, 0x01, 0x03,
0x0a, 0x01, 0x1e, 0x01, 0x04, 0x0b, 0x01, 0x1e, 0x01, 0x05, 0x0c, 0x01,
0x1e, 0x01, 0x06, 0x0d, 0x01, 0x1e, 0x01, 0x07, 0x03, 0x01, 0x08, 0x1e,
0x01, 0x08, 0x03, 0x01, 0x09, 0x1e, 0x01, 0x09, 0x03, 0x01, 0x0a, 0x1e,
0x01, 0x0a, 0x03, 0x01, 0x0b, 0x1e, 0x01, 0x0b, 0x03, 0x01, 0x0c, 0x1e,
0x01, 0x0c, 0x03, 0x01, 0x0d, 0x1e, 0x01, 0x0d, 0x03, 0x01, 0x0e, 0x1e,
0x01, 0x0e, 0x03, 0x01, 0x0f, 0x1e, 0x01, 0x0f, 0x03, 0x01, 0x10, 0x1e,
0x01, 0x10, 0x03, 0x01, 0x11, 0x1e, 0x01, 0x11, 0x03, 0x01, 0x12, 0x1e,
0x01, 0x12, 0x03, 0x01, 0x13, 0x1e, 0x01, 0x13, 0x03, 0x01, 0x14, 0x1e,
0x01, 0x14, 0x03, 0x01, 0x15, 0x1e, 0x01, 0x15, 0x03, 0x01, 0x16, 0x1e,
0x01, 0x16, 0x03, 0x01, 0x17, 0x1e, 0x01, 0x17, 0x03, 0x01, 0x18, 0x1e,
0x01, 0x18, 0x03, 0x01, 0x19, 0x1e, 0x01, 0x19, 0x03, 0x01, 0x1a, 0x1e,
0x01, 0x1a, 0x03, 0x01, 0x1b, 0x1e, 0x01, 0x1b, 0x03, 0x01, 0x1c, 0x1e,
0x01, 0x1c, 0x03, 0x01, 0x1d, 0x1e, 0x01, 0x1d, 0x03, 0x01, 0x1e, 0x1e,
0x01, 0x1e, 0x03, 0x01, 0x1f, 0x1e, 0x01, 0x1f, 0x03, 0x01, 0x20, 0x1e,
0x01, 0x20, 0x03, 0x01, 0x21, 0x1e, 0x01, 0x21, 0x03, 0x01, 0x22, 0x1e,
0x01, 0x22, 0x03, 0x01, 0x23, 0x1e, 0x01, 0x23, 0x03, 0x01, 0x24, 0x1e,
0x01, 0x24, 0x03, 0x01, 0x25, 0x1e, 0x01, 0x25, 0x03, 0x01, 0x26, 0x1e,
0x01, 0x26, 0x03, 0x01, 0x27, 0x1e, 0x01, 0x27, 0x03, 0x01, 0x28, 0x1e,
0x01, 0x28, 0x03, 0x01, 0x29, 0x1e, 0x01, 0x29, 0x03, 0x01, 0x2a, 0x1e,
0x01, 0x2a, 0x03, 0x01, 0x2b, 0x1e, 0x01, 0x2b, 0x03, 0x01, 0x2c, 0x1e,
0x01, 0x2c, 0x03, 0x01, 0x2d, 0x1e, 0x01, 0x2d, 0x03, 0x01, 0x2e, 0x1e,
0x01, 0x2e, 0x03, 0x01, 0x2f, 0x1e, 0x01, 0x2f, 0x03, 0x01, 0x30, 0x1e,
0x01, 0x30, 0x03, 0x01, 0x31, 0x1e, 0x01, 0x31, 0x03, 0x01, 0x32, 0x1e,
0x01, 0x32, 0x03, 0x01, 0x33, 0x1e, 0x01, 0x33, 0x03, 0x01, 0x34, 0x1e,
0x01, 0x34, 0x2d, 0x01, 0x35, 0x00, 0x38, 0x01, 0x00, 0x00, 0x00, 0x36,
0x00, 0x06, 0x4b, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x00, 0x00, 0x09, 0x4b,
0x5f, 0x53, 0x48, 0x45, 0x42, 0x41, 0x4e, 0x47, 0x00, 0x00, 0x09, 0x4b,
0x5f, 0x43, 0x4f, 0x4d, 0x4d, 0x45, 0x4e, 0x54, 0x00, 0x00, 0x07, 0x4b,
0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x00, 0x00, 0x08, 0x4b, 0x5f, 0x53,
0x54, 0x52, 0x49, 0x4e, 0x47, 0x00, 0x00, 0x08, 0x4b, 0x5f, 0x45, 0x53,
0x43, 0x41, 0x50, 0x45, 0x00, 0x00, 0x0f, 0x4b, 0x5f, 0x49, 0x4e, 0x54,
0x45, 0x52, 0x50, 0x4f, 0x4c, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x00, 0x00,
0x08, 0x4b, 0x5f, 0x52, 0x45, 0x47, 0x45, 0x58, 0x50, 0x00, 0x00, 0x08,
0x4b, 0x5f, 0x4e, 0x55, 0x4d, 0x42, 0x45, 0x52, 0x00, 0x00, 0x06, 0x4b,
0x5f, 0x54, 0x52, 0x55, 0x45, 0x00, 0x00, 0x07, 0x4b, 0x5f, 0x46, 0x41,
0x4c, 0x53, 0x45, 0x00, 0x00, 0x06, 0x4b, 0x5f, 0x43, 0x48, 0x41, 0x52,
0x00, 0x00, 0x09, 0x4b, 0x5f, 0x4b, 0x45, 0x59, 0x57, 0x4f, 0x52, 0x44,
0x00, 0x00, 0x11, 0x4b, 0x5f, 0x4b, 0x45, 0x59, 0x57, 0x4f, 0x52, 0x44,
0x4f, 0x50, 0x45, 0x52, 0x41, 0x54, 0x4f, 0x52, 0x00, 0x00, 0x0a, 0x4b,
0x5f, 0x4f, 0x50, 0x45, 0x52, 0x41, 0x54, 0x4f, 0x52, 0x00, 0x00, 0x0a,
0x4b, 0x5f, 0x46, 0x55, 0x4e, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x00, 0x00,
0x06, 0x4b, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x00, 0x00, 0x0a, 0x4b, 0x5f,
0x43, 0x4f, 0x4e, 0x53, 0x54, 0x41, 0x4e, 0x54, 0x00, 0x00, 0x12, 0x4b,
0x5f, 0x56, 0x41, 0x52, 0x49, 0x41, 0x42, 0x4c, 0x45, 0x49, 0x4e, 0x53,
0x54, 0x41, 0x4e, 0x43, 0x45, 0x00, 0x00, 0x10, 0x4b, 0x5f, 0x56, 0x41,
0x52, 0x49, 0x41, 0x42, 0x4c, 0x45, 0x47, 0x4c, 0x4f, 0x42, 0x41, 0x4c,
0x00, 0x00, 0x0c, 0x4b, 0x5f, 0x41, 0x4e, 0x4e, 0x4f, 0x54, 0x41, 0x54,
0x49, 0x4f, 0x4e, 0x00, 0x00, 0x0b, 0x4b, 0x5f, 0x44, 0x49, 0x52, 0x45,
0x43, 0x54, 0x49, 0x56, 0x45, 0x00, 0x00, 0x07, 0x4b, 0x5f, 0x4c, 0x41,
0x42, 0x45, 0x4c, 0x00, 0x00, 0x08, 0x4b, 0x5f, 0x42, 0x52, 0x41, 0x43,
0x45, 0x31, 0x00, 0x00, 0x08, 0x4b, 0x5f, 0x42, 0x52, 0x41, 0x43, 0x45,
0x32, 0x00, 0x00, 0x08, 0x4b, 0x5f, 0x42, 0x52, 0x41, 0x43, 0x45, 0x33,
0x00, 0x00, 0x08, 0x4b, 0x5f, 0x42, 0x52, 0x41, 0x43, 0x45, 0x34, 0x00,
0x00, 0x08, 0x4b, 0x5f, 0x42, 0x52, 0x41, 0x43, 0x45, 0x35, 0x00, 0x00,
0x0a, 0x4b, 0x5f, 0x48, 0x45, 0x41, 0x44, 0x49, 0x4e, 0x47, 0x31, 0x00,
0x00, 0x0a, 0x4b, 0x5f, 0x48, 0x45, 0x41, 0x44, 0x49, 0x4e, 0x47, 0x32,
0x00, 0x00, 0x0a, 0x4b, 0x5f, 0x48, 0x45, 0x41, 0x44, 0x49, 0x4e, 0x47,
0x33, 0x00, 0x00, 0x0a, 0x4b, 0x5f, 0x48, 0x45, 0x41, 0x44, 0x49, 0x4e,
0x47, 0x34, 0x00, 0x00, 0x0a, 0x4b, 0x5f, 0x48, 0x45, 0x41, 0x44, 0x49,
0x4e, 0x47, 0x35, 0x00, 0x00, 0x0a, 0x4b, 0x5f, 0x48, 0x45, 0x41, 0x44,
0x49, 0x4e, 0x47, 0x36, 0x00, 0x00, 0x0c, 0x4b, 0x5f, 0x42, 0x4c, 0x4f,
0x43, 0x4b, 0x51, 0x55, 0x4f, 0x54, 0x45, 0x00, 0x00, 0x06, 0x4b, 0x5f,
0x4c, 0x49, 0x53, 0x54, 0x00, 0x00, 0x0a, 0x4b, 0x5f, 0x4c, 0x49, 0x53,
0x54, 0x49, 0x54, 0x45, 0x4d, 0x00, 0x00, 0x06, 0x4b, 0x5f, 0x43, 0x4f,
0x44, 0x45, 0x00, 0x00, 0x0e, 0x4b, 0x5f, 0x4c, 0x41, 0x4e, 0x47, 0x55,
0x41, 0x47, 0x45, 0x4e, 0x41, 0x4d, 0x45, 0x00, 0x00, 0x0b, 0x4b, 0x5f,
0x4c, 0x49, 0x4e, 0x4b, 0x4c, 0x41, 0x42, 0x45, 0x4c, 0x00, 0x00, 0x0c,
0x4b, 0x5f, 0x49, 0x4d, 0x41, 0x47, 0x45, 0x4c, 0x41, 0x42, 0x45, 0x4c,
0x00, 0x00, 0x06, 0x4b, 0x5f, 0x4c, 0x49, 0x4e, 0x4b, 0x00, 0x00, 0x07,
0x4b, 0x5f, 0x54, 0x41, 0x42, 0x4c, 0x45, 0x00, 0x00, 0x0d, 0x4b, 0x5f,
0x54, 0x41, 0x42, 0x4c, 0x45, 0x48, 0x45, 0x41, 0x44, 0x45, 0x52, 0x00,
0x00, 0x08, 0x4b, 0x5f, 0x49, 0x54, 0x41, 0x4c, 0x49, 0x43, 0x00, 0x00,
0x06, 0x4b, 0x5f, 0x42, 0x4f, 0x4c, 0x44, 0x00, 0x00, 0x0b, 0x4b, 0x5f,
0x55, 0x4e, 0x44, 0x45, 0x52, 0x4c, 0x49, 0x4e, 0x45, 0x00, 0x00, 0x0f,
0x4b, 0x5f, 0x53, 0x54, 0x52, 0x49, 0x4b, 0x45, 0x54, 0x48, 0x52, 0x4f,
0x55, 0x47, 0x48, 0x00, 0x00, 0x10, 0x4b, 0x5f, 0x48, 0x4f, 0x52, 0x49,
0x58, 0x4f, 0x4e, 0x54, 0x41, 0x4c, 0x52, 0x55, 0x4c, 0x45, 0x00, 0x00,
0x05, 0x4b, 0x5f, 0x54, 0x41, 0x47, 0x00, 0x00, 0x0b, 0x4b, 0x5f, 0x41,
0x54, 0x54, 0x52, 0x49, 0x42, 0x55, 0x54, 0x45, 0x00, 0x00, 0x0b, 0x4b,
0x5f, 0x43, 0x48, 0x45, 0x43, 0x4b, 0x44, 0x4f, 0x4e, 0x45, 0x00, 0x00,
0x0e, 0x4b, 0x5f, 0x43, 0x48, 0x45, 0x43, 0x4b, 0x4e, 0x4f, 0x54, 0x44,
0x4f, 0x4e, 0x45, 0x00, 0x00, 0x06, 0x66, 0x72, 0x65, 0x65, 0x7a, 0x65,
0x00, 0x00, 0x00, 0x0f, 0x45, 0x00, 0x01, 0x00, 0x4c, 0x00, 0x01, 0x00,
0x00, 0x00, 0x00, 0x07, 0x65, 0x51, 0x01, 0x00, 0x51, 0x02, 0x01, 0x51,
0x03, 0x02, 0x51, 0x04, 0x03, 0x51, 0x05, 0x04, 0x51, 0x06, 0x05, 0x51,
0x07, 0x06, 0x51, 0x08, 0x07, 0x47, 0x02, 0x07, 0x51, 0x03, 0x08, 0x47,
0x04, 0x00, 0x51, 0x05, 0x09, 0x51, 0x06, 0x0a, 0x47, 0x06, 0x01, 0x51,
0x07, 0x0b, 0x51, 0x08, 0x0c, 0x47, 0x08, 0x01, 0x51, 0x09, 0x0d, 0x51,
0x0a, 0x0e, 0x47, 0x0a, 0x01, 0x51, 0x0b, 0x0f, 0x51, 0x0c, 0x0e, 0x47,
0x0c, 0x01, 0x51, 0x0d, 0x10, 0x51, 0x0e, 0x0c, 0x47, 0x0e, 0x01, 0x51,
0x0f, 0x11, 0x51, 0x10, 0x12, 0x47, 0x10, 0x01, 0x51, 0x11, 0x13, 0x51,
0x12, 0x14, 0x47, 0x12, 0x01, 0x51, 0x13, 0x15, 0x51, 0x14, 0x0e, 0x47,
0x14, 0x01, 0x51, 0x15, 0x16, 0x51, 0x16, 0x0e, 0x47, 0x16, 0x01, 0x51,
0x17, 0x17, 0x47, 0x18, 0x00, 0x51, 0x19, 0x18, 0x51, 0x1a, 0x0e, 0x47,
0x1a, 0x01, 0x51, 0x1b, 0x19, 0x47, 0x1c, 0x00, 0x51, 0x1d, 0x1a, 0x51,
0x1e, 0x0e, 0x47, 0x1e, 0x01, 0x51, 0x1f, 0x1b, 0x51, 0x20, 0x1c, 0x47,
0x20, 0x01, 0x51, 0x21, 0x1d, 0x47, 0x22, 0x00, 0x51, 0x23, 0x1e, 0x51,
0x24, 0x14, 0x51, 0x25, 0x0a, 0x47, 0x24, 0x02, 0x51, 0x25, 0x1f, 0x51,
0x26, 0x0e, 0x47, 0x26, 0x01, 0x51, 0x27, 0x20, 0x51, 0x28, 0x12, 0x47,
0x28, 0x01, 0x51, 0x29, 0x21, 0x47, 0x2a, 0x00, 0x51, 0x2b, 0x22, 0x51,
0x2c, 0x23, 0x51, 0x2d, 0x24, 0x51, 0x2e, 0x0a, 0x47, 0x2c, 0x03, 0x53,
0x01, 0x16, 0x1a, 0x01, 0x00, 0x10, 0x01, 0x01, 0x10, 0x02, 0x02, 0x0f,
0x03, 0x00, 0x55, 0x55, 0x55, 0x10, 0x04, 0x03, 0x51, 0x05, 0x25, 0x10,
0x06, 0x04, 0x51, 0x07, 0x26, 0x47, 0x07, 0x01, 0x10, 0x08, 0x05, 0x51,
0x09, 0x00, 0x53, 0x02, 0x04, 0x10, 0x03, 0x06, 0x10, 0x04, 0x02, 0x0e,
0x05, 0x59, 0x9c, 0x10, 0x06, 0x03, 0x51, 0x07, 0x27, 0x10, 0x08, 0x04,
0x51, 0x09, 0x28, 0x51, 0x0a, 0x29, 0x51, 0x0b, 0x2a, 0x47, 0x09, 0x03,
0x10, 0x0a, 0x05, 0x51, 0x0b, 0x00, 0x53, 0x04, 0x04, 0x10, 0x05, 0x07,
0x10, 0x06, 0x02, 0x0f, 0x07, 0x00, 0xa8, 0xb9, 0xcc, 0x10, 0x08, 0x03,
0x51, 0x09, 0x2b, 0x10, 0x0a, 0x04, 0x51, 0x0b, 0x2c, 0x51, 0x0c, 0x2d,
0x47, 0x0b, 0x02, 0x10, 0x0c, 0x05, 0x51, 0x0d, 0x00, 0x53, 0x06, 0x04,
0x10, 0x07, 0x08, 0x10, 0x08, 0x02, 0x0f, 0x09, 0x00, 0x36, 0xa3, 0xd9,
0x10, 0x0a, 0x03, 0x51, 0x0b, 0x2e, 0x10, 0x0c, 0x04, 0x51, 0x0d, 0x2f,
0x47, 0x0d, 0x01, 0x10, 0x0e, 0x05, 0x51, 0x0f, 0x0d, 0x53, 0x08, 0x04,
0x10, 0x09, 0x09, 0x10, 0x0a, 0x02, 0x0f, 0x0b, 0x00, 0x4d, 0x5a, 0x5e,
0x10, 0x0c, 0x03, 0x51, 0x0d, 0x30, 0x10, 0x0e, 0x04, 0x51, 0x0f, 0x31,
0x47, 0x0f, 0x01, 0x10, 0x10, 0x05, 0x51, 0x11, 0x10, 0x53, 0x0a, 0x04,
0x10, 0x0b, 0x0a, 0x10, 0x0c, 0x02, 0x0f, 0x0d, 0x00, 0x00, 0xad, 0xd8,
0x10, 0x0e, 0x03, 0x51, 0x0f, 0x32, 0x10, 0x10, 0x04, 0x51, 0x11, 0x33,
0x47, 0x11, 0x01, 0x10, 0x12, 0x05, 0x51, 0x13, 0x11, 0x53, 0x0c, 0x04,
0x10, 0x0d, 0x0b, 0x10, 0x0e, 0x02, 0x0f, 0x0f, 0x00, 0x00, 0xad, 0xd8,
0x10, 0x10, 0x03, 0x51, 0x11, 0x32, 0x10, 0x12, 0x04, 0x51, 0x13, 0x34,
0x47, 0x13, 0x01, 0x10, 0x14, 0x05, 0x51, 0x15, 0x11, 0x53, 0x0e, 0x04,
0x10, 0x0f, 0x0c, 0x10, 0x10, 0x02, 0x0f, 0x11, 0x00, 0xa0, 0x74, 0xc4,
0x10, 0x12, 0x03, 0x51, 0x13, 0x35, 0x10, 0x14, 0x04, 0x51, 0x15, 0x36,
0x51, 0x16, 0x37, 0x47, 0x15, 0x02, 0x10, 0x16, 0x05, 0x51, 0x17, 0x13,
0x53, 0x10, 0x04, 0x10, 0x11, 0x0d, 0x10, 0x12, 0x02, 0x0f, 0x13, 0x00,
0xef, 0x8a, 0x91, 0x10, 0x14, 0x03, 0x51, 0x15, 0x38, 0x10, 0x16, 0x04,
0x51, 0x17, 0x39, 0x51, 0x18, 0x3a, 0x47, 0x17, 0x02, 0x10, 0x18, 0x05,
0x51, 0x19, 0x15, 0x53, 0x12, 0x04, 0x10, 0x13, 0x0e, 0x10, 0x14, 0x02,
0x0f, 0x15, 0x00, 0xf0, 0xdf, 0x8a, 0x10, 0x16, 0x03, 0x51, 0x17, 0x3b,
0x10, 0x18, 0x04, 0x51, 0x19, 0x3c, 0x47, 0x19, 0x01, 0x10, 0x1a, 0x05,
0x51, 0x1b, 0x16, 0x53, 0x14, 0x04, 0x10, 0x15, 0x0f, 0x10, 0x16, 0x02,
0x0f, 0x17, 0x00, 0x36, 0xa3, 0xd9, 0x10, 0x18, 0x03, 0x51, 0x19, 0x3d,
0x10, 0x1a, 0x04, 0x51, 0x1b, 0x3e, 0x47, 0x1b, 0x01, 0x10, 0x1c, 0x05,
0x51, 0x1d, 0x16, 0x53, 0x16, 0x04, 0x10, 0x17, 0x10, 0x10, 0x18, 0x02,
0x0f, 0x19, 0x00, 0xcb, 0xcb, 0x41, 0x10, 0x1a, 0x03, 0x51, 0x1b, 0x3f,
0x10, 0x1c, 0x04, 0x51, 0x1d, 0x40, 0x47, 0x1d, 0x01, 0x10, 0x1e, 0x05,
0x51, 0x1f, 0x0f, 0x53, 0x18, 0x04, 0x10, 0x19, 0x11, 0x10, 0x1a, 0x02,
0x0f, 0x1b, 0x00, 0xcb, 0xcb, 0x41, 0x10, 0x1c, 0x03, 0x51, 0x1d, 0x3f,
0x10, 0x1e, 0x04, 0x51, 0x1f, 0x41, 0x47, 0x1f, 0x01, 0x10, 0x20, 0x05,
0x51, 0x21, 0x0f, 0x53, 0x1a, 0x04, 0x10, 0x1b, 0x12, 0x10, 0x1c, 0x02,
0x0f, 0x1d, 0x00, 0x6e, 0x15, 0x16, 0x10, 0x1e, 0x03, 0x51, 0x1f, 0x38,
0x10, 0x20, 0x04, 0x51, 0x21, 0x42, 0x47, 0x21, 0x01, 0x10, 0x22, 0x05,
0x51, 0x23, 0x08, 0x53, 0x1c, 0x04, 0x10, 0x1d, 0x13, 0x10, 0x1e, 0x02,
0x0f, 0x1f, 0x00, 0x36, 0xa3, 0xd9, 0x10, 0x20, 0x03, 0x51, 0x21, 0x43,
0x10, 0x22, 0x04, 0x51, 0x23, 0x44, 0x47, 0x23, 0x01, 0x10, 0x24, 0x05,
0x51, 0x25, 0x17, 0x53, 0x1e, 0x04, 0x10, 0x1f, 0x14, 0x10, 0x20, 0x02,
0x0f, 0x21, 0x00, 0x95, 0xe6, 0xcb, 0x10, 0x22, 0x03, 0x51, 0x23, 0x45,
0x10, 0x24, 0x04, 0x51, 0x25, 0x46, 0x47, 0x25, 0x01, 0x10, 0x26, 0x05,
0x51, 0x27, 0x47, 0x53, 0x20, 0x04, 0x10, 0x21, 0x15, 0x10, 0x22, 0x02,
0x0f, 0x23, 0x00, 0xde, 0xa5, 0x84, 0x10, 0x24, 0x03, 0x51, 0x25, 0x48,
0x10, 0x26, 0x04, 0x51, 0x27, 0x49, 0x47, 0x27, 0x01, 0x10, 0x28, 0x05,
0x51, 0x29, 0x19, 0x53, 0x22, 0x04, 0x10, 0x23, 0x16, 0x10, 0x24, 0x02,
0x0f, 0x25, 0x00, 0xa0, 0x74, 0xc4, 0x10, 0x26, 0x03, 0x51, 0x27, 0x4a,
0x10, 0x28, 0x04, 0x51, 0x29, 0x4b, 0x47, 0x29, 0x01, 0x10, 0x2a, 0x05,
0x51, 0x2b, 0x1a, 0x53, 0x24, 0x04, 0x10, 0x25, 0x17, 0x10, 0x26, 0x02,
0x0f, 0x27, 0x00, 0x36, 0xa3, 0xd9, 0x10, 0x28, 0x03, 0x51, 0x29, 0x4c,
0x10, 0x2a, 0x04, 0x51, 0x2b, 0x4d, 0x51, 0x2c, 0x4e, 0x47, 0x2b, 0x02,
0x10, 0x2c, 0x05, 0x51, 0x2d, 0x1b, 0x53, 0x26, 0x04, 0x10, 0x27, 0x18,
0x10, 0x28, 0x02, 0x0f, 0x29, 0x00, 0x6d, 0x80, 0x86, 0x10, 0x2a, 0x03,
0x51, 0x2b, 0x4f, 0x10, 0x2c, 0x04, 0x51, 0x2d, 0x50, 0x47, 0x2d, 0x01,
0x10, 0x2e, 0x05, 0x51, 0x2f, 0x1d, 0x53, 0x28, 0x04, 0x10, 0x29, 0x19,
0x10, 0x2a, 0x02, 0x0f, 0x2b, 0x00, 0x36, 0xa3, 0xd9, 0x10, 0x2c, 0x03,
0x51, 0x2d, 0x51, 0x10, 0x2e, 0x04, 0x51, 0x2f, 0x52, 0x47, 0x2f, 0x01,
0x10, 0x30, 0x05, 0x51, 0x31, 0x1e, 0x53, 0x2a, 0x04, 0x10, 0x2b, 0x1a,
0x10, 0x2c, 0x02, 0x0f, 0x2d, 0x00, 0x6d, 0x80, 0x86, 0x10, 0x2e, 0x03,
0x51, 0x2f, 0x4f, 0x10, 0x30, 0x04, 0x51, 0x31, 0x53, 0x51, 0x32, 0x54,
0x47, 0x31, 0x02, 0x10, 0x32, 0x05, 0x51, 0x33, 0x1f, 0x53, 0x2c, 0x04,
0x10, 0x2d, 0x1b, 0x10, 0x2e, 0x02, 0x0f, 0x2f, 0x00, 0xda, 0xd8, 0xd8,
0x10, 0x30, 0x03, 0x51, 0x31, 0x55, 0x10, 0x32, 0x04, 0x51, 0x33, 0x56,
0x47, 0x33, 0x01, 0x10, 0x34, 0x05, 0x51, 0x35, 0x20, 0x53, 0x2e, 0x04,
0x10, 0x2f, 0x1c, 0x10, 0x30, 0x02, 0x0f, 0x31, 0x00, 0x4e, 0x5c, 0x61,
0x10, 0x32, 0x03, 0x51, 0x33, 0x57, 0x10, 0x34, 0x04, 0x51, 0x35, 0x58,
0x51, 0x36, 0x59, 0x47, 0x35, 0x02, 0x10, 0x36, 0x05, 0x51, 0x37, 0x21,
0x53, 0x30, 0x04, 0x10, 0x31, 0x1d, 0x10, 0x32, 0x02, 0x0f, 0x33, 0x00,
0x6d, 0x80, 0x86, 0x10, 0x34, 0x03, 0x51, 0x35, 0x5a, 0x10, 0x36, 0x04,
0x51, 0x37, 0x5b, 0x47, 0x37, 0x01, 0x53, 0x32, 0x03, 0x10, 0x33, 0x1e,
0x10, 0x34, 0x02, 0x0f, 0x35, 0x00, 0xda, 0xd8, 0xd8, 0x10, 0x36, 0x03,
0x51, 0x37, 0x5c, 0x10, 0x38, 0x04, 0x51, 0x39, 0x5d, 0x47, 0x39, 0x01,
0x53, 0x34, 0x03, 0x10, 0x35, 0x1f, 0x10, 0x36, 0x02, 0x0f, 0x37, 0x00,
0xdd, 0x4c, 0x35, 0x10, 0x38, 0x03, 0x51, 0x39, 0x5e, 0x10, 0x3a, 0x04,
0x51, 0x3b, 0x5f, 0x51, 0x3c, 0x60, 0x47, 0x3b, 0x02, 0x53, 0x36, 0x03,
0x10, 0x37, 0x20, 0x10, 0x38, 0x02, 0x0f, 0x39, 0x00, 0xf0, 0x50, 0x32,
0x10, 0x3a, 0x03, 0x51, 0x3b, 0x61, 0x10, 0x3c, 0x04, 0x51, 0x3d, 0x62,
0x47, 0x3d, 0x01, 0x53, 0x38, 0x03, 0x10, 0x39, 0x21, 0x10, 0x3a, 0x02,
0x0f, 0x3b, 0x00, 0xf0, 0x50, 0x32, 0x10, 0x3c, 0x03, 0x51, 0x3d, 0x61,
0x10, 0x3e, 0x04, 0x51, 0x3f, 0x63, 0x47, 0x3f, 0x01, 0x53, 0x3a, 0x03,
0x10, 0x3b, 0x22, 0x10, 0x3c, 0x02, 0x0f, 0x3d, 0x00, 0x9e, 0x9e, 0x9e,
0x10, 0x3e, 0x03, 0x51, 0x3f, 0x64, 0x10, 0x40, 0x04, 0x51, 0x41, 0x65,
0x47, 0x41, 0x01, 0x53, 0x3c, 0x03, 0x10, 0x3d, 0x23, 0x10, 0x3e, 0x02,
0x0f, 0x3f, 0x00, 0x6d, 0x80, 0x86, 0x10, 0x40, 0x03, 0x51, 0x41, 0x4f,
0x10, 0x42, 0x04, 0x51, 0x43, 0x66, 0x47, 0x43, 0x01, 0x53, 0x3e, 0x03,
0x10, 0x3f, 0x24, 0x10, 0x40, 0x02, 0x0f, 0x41, 0x00, 0xff, 0x80, 0x87,
0x10, 0x42, 0x03, 0x51, 0x43, 0x67, 0x10, 0x44, 0x04, 0x51, 0x45, 0x68,
0x47, 0x45, 0x01, 0x10, 0x46, 0x25, 0x51, 0x47, 0x69, 0x47, 0x47, 0x01,
0x10, 0x48, 0x05, 0x51, 0x49, 0x09, 0x53, 0x40, 0x05, 0x10, 0x41, 0x26,
0x10, 0x42, 0x02, 0x0f, 0x43, 0x00, 0x4d, 0x5a, 0x5e, 0x10, 0x44, 0x03,
0x51, 0x45, 0x6a, 0x10, 0x46, 0x04, 0x51, 0x47, 0x6b, 0x47, 0x47, 0x01,
0x10, 0x48, 0x25, 0x51, 0x49, 0x6c, 0x51, 0x4a, 0x6d, 0x47, 0x49, 0x02,
0x10, 0x4a, 0x05, 0x51, 0x4b, 0x0b, 0x53, 0x42, 0x05, 0x53, 0x01, 0x21,
0x1a, 0x01, 0x27, 0x10, 0x01, 0x28, 0x10, 0x02, 0x29, 0x0f, 0x03, 0x00,
0xee, 0xee, 0xee, 0x53, 0x02, 0x01, 0x10, 0x03, 0x2a, 0x10, 0x04, 0x29,
0x0f, 0x05, 0x00, 0x7d, 0xcf, 0xff, 0x53, 0x04, 0x01, 0x10, 0x05, 0x2b,
0x10, 0x06, 0x29, 0x0f, 0x07, 0x00, 0xef, 0x51, 0x68, 0x53, 0x06, 0x01,
0x10, 0x07, 0x2c, 0x10, 0x08, 0x29, 0x0f, 0x09, 0x00, 0xaa, 0xaa, 0xaa,
0x10, 0x0a, 0x2d, 0x13, 0x0b, 0x53, 0x08, 0x02, 0x10, 0x09, 0x2e, 0x10,
0x0a, 0x29, 0x0f, 0x0b, 0x00, 0xaa, 0xd9, 0x4c, 0x53, 0x0a, 0x01, 0x10,
0x0b, 0x2f, 0x10, 0x0c, 0x29, 0x0f, 0x0d, 0x00, 0x7d, 0xcf, 0xff, 0x53,
0x0c, 0x01, 0x10, 0x0d, 0x30, 0x10, 0x0e, 0x29, 0x0f, 0x0f, 0x00, 0x7d,
0xcf, 0xff, 0x53, 0x0e, 0x01, 0x10, 0x0f, 0x31, 0x10, 0x10, 0x29, 0x0f,
0x11, 0x00, 0xd2, 0xa6, 0xff, 0x53, 0x10, 0x01, 0x10, 0x11, 0x32, 0x10,
0x12, 0x29, 0x0f, 0x13, 0x00, 0xe6, 0xc0, 0x8a, 0x53, 0x12, 0x01, 0x10,
0x13, 0x33, 0x10, 0x14, 0x29, 0x0f, 0x15, 0x00, 0x7a, 0xe9, 0x3c, 0x53,
0x14, 0x01, 0x10, 0x15, 0x34, 0x10, 0x16, 0x29, 0x0f, 0x17, 0x00, 0xef,
0x51, 0x68, 0x53, 0x16, 0x01, 0x10, 0x17, 0x35, 0x10, 0x18, 0x29, 0x0f,
0x19, 0x00, 0xff, 0xaf, 0x70, 0x53, 0x18, 0x01, 0x10, 0x19, 0x36, 0x10,
0x1a, 0x29, 0x0f, 0x1b, 0x00, 0xff, 0x8f, 0x40, 0x53, 0x1a, 0x01, 0x10,
0x1b, 0x37, 0x10, 0x1c, 0x29, 0x0f, 0x1d, 0x00, 0xf0, 0x71, 0x78, 0x53,
0x1c, 0x01, 0x10, 0x1d, 0x38, 0x10, 0x1e, 0x29, 0x0f, 0x1f, 0x00, 0xff,
0xff, 0xff, 0x10, 0x20, 0x2d, 0x13, 0x21, 0x53, 0x1e, 0x02, 0x10, 0x1f,
0x39, 0x10, 0x20, 0x29, 0x0f, 0x21, 0x00, 0xff, 0xaf, 0x70, 0x53, 0x20,
0x01, 0x10, 0x21, 0x3a, 0x10, 0x22, 0x29, 0x0f, 0x23, 0x00, 0xf0, 0x71,
0x78, 0x53, 0x22, 0x01, 0x10, 0x23, 0x3b, 0x10, 0x24, 0x29, 0x0f, 0x25,
0x00, 0x7d, 0xcf, 0xff, 0x53, 0x24, 0x01, 0x10, 0x25, 0x3c, 0x10, 0x26,
0x29, 0x0f, 0x27, 0x00, 0x95, 0xe6, 0xcb, 0x53, 0x26, 0x01, 0x10, 0x27,
0x3d, 0x10, 0x28, 0x29, 0x0f, 0x29, 0x00, 0xf0, 0x71, 0x78, 0x53, 0x28,
0x01, 0x10, 0x29, 0x3e, 0x10, 0x2a, 0x29, 0x0f, 0x2b, 0x00, 0x7d, 0xcf,
0xff, 0x53, 0x2a, 0x01, 0x10, 0x2b, 0x3f, 0x10, 0x2c, 0x29, 0x0f, 0x2d,
0x00, 0xff, 0x8f, 0x40, 0x53, 0x2c, 0x01, 0x10, 0x2d, 0x40, 0x10, 0x2e,
0x29, 0x0f, 0x2f, 0x00, 0xd2, 0xa6, 0xff, 0x53, 0x2e, 0x01, 0x10, 0x2f,
0x41, 0x10, 0x30, 0x29, 0x0f, 0x31, 0x00, 0xd2, 0xa6, 0xff, 0x53, 0x30,
0x01, 0x10, 0x31, 0x42, 0x10, 0x32, 0x29, 0x0f, 0x33, 0x00, 0xff, 0xaf,
0xaf, 0x53, 0x32, 0x01, 0x10, 0x33, 0x43, 0x10, 0x34, 0x29, 0x0f, 0x35,
0x00, 0xff, 0xff, 0x00, 0x53, 0x34, 0x01, 0x10, 0x35, 0x44, 0x10, 0x36,
0x29, 0x0f, 0x37, 0x00, 0x0f, 0xff, 0x0f, 0x53, 0x36, 0x01, 0x10, 0x37,
0x45, 0x10, 0x38, 0x29, 0x0f, 0x39, 0x00, 0xff, 0x0f, 0x0f, 0x53, 0x38,
0x01, 0x53, 0x01, 0x1c, 0x1a, 0x01, 0x46, 0x10, 0x01, 0x47, 0x1a, 0x01,
0x48, 0x53, 0x01, 0x00, 0x1a, 0x01, 0x49, 0x53, 0x01, 0x00, 0x1a, 0x01,
0x4a, 0x53, 0x01, 0x00, 0x1a, 0x01, 0x4b, 0x11, 0x01, 0x1a, 0x01, 0x4c,
0x11, 0x01, 0x1a, 0x01, 0x4d, 0x12, 0x01, 0x62, 0x01, 0x5e, 0x01, 0x00,
0x38, 0x01, 0x00, 0x6e, 0x00, 0x00, 0x06, 0x63, 0x6c, 0x61, 0x6e, 0x67,
0x64, 0x00, 0x00, 0x00, 0x12, 0x2d, 0x2d, 0x62, 0x61, 0x63, 0x6b, 0x67,
0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2d, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x00,
0x00, 0x00, 0x0c, 0x2d, 0x2d, 0x63, 0x6c, 0x61, 0x6e, 0x67, 0x2d, 0x74,
0x69, 0x64, 0x79, 0x00, 0x00, 0x00, 0x1b, 0x2d, 0x2d, 0x63, 0x6f, 0x6d,
0x70, 0x6c, 0x65, 0x74, 0x69, 0x6f, 0x6e, 0x2d, 0x73, 0x74, 0x79, 0x6c,
0x65, 0x3d, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x00, 0x00,
0x00, 0x18, 0x2d, 0x2d, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x2d, 0x69,
0x6e, 0x73, 0x65, 0x72, 0x74, 0x69, 0x6f, 0x6e, 0x3d, 0x6e, 0x65, 0x76,
0x65, 0x72, 0x00, 0x00, 0x00, 0x14, 0x2d, 0x2d, 0x70, 0x63, 0x68, 0x2d,
0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x3d, 0x6d, 0x65, 0x6d, 0x6f,
0x72, 0x79, 0x00, 0x00, 0x00, 0x12, 0x2d, 0x2d, 0x6c, 0x69, 0x6d, 0x69,
0x74, 0x2d, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x3d, 0x35, 0x30,
0x00, 0x00, 0x00, 0x0b, 0x2d, 0x2d, 0x6c, 0x6f, 0x67, 0x3d, 0x65, 0x72,
0x72, 0x6f, 0x72, 0x00, 0x00, 0x00, 0x08, 0x72, 0x75, 0x62, 0x79, 0x2d,
0x6c, 0x73, 0x70, 0x00, 0x00, 0x00, 0x0a, 0x73, 0x6f, 0x6c, 0x61, 0x72,
0x67, 0x72, 0x61, 0x70, 0x68, 0x00, 0x00, 0x00, 0x05, 0x73, 0x74, 0x64,
0x69, 0x6f, 0x00, 0x00, 0x00, 0x14, 0x62, 0x61, 0x73, 0x68, 0x2d, 0x6c,
0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2d, 0x73, 0x65, 0x72, 0x76,
0x65, 0x72, 0x00, 0x00, 0x00, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x00,
0x00, 0x00, 0x1a, 0x76, 0x73, 0x63, 0x6f, 0x64, 0x65, 0x2d, 0x63, 0x73,
0x73, 0x2d, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2d, 0x73,
0x65, 0x72, 0x76, 0x65, 0x72, 0x00, 0x00, 0x00, 0x07, 0x2d, 0x2d, 0x73,
0x74, 0x64, 0x69, 0x6f, 0x00, 0x00, 0x00, 0x1b, 0x76, 0x73, 0x63, 0x6f,
0x64, 0x65, 0x2d, 0x6a, 0x73, 0x6f, 0x6e, 0x2d, 0x6c, 0x61, 0x6e, 0x67,
0x75, 0x61, 0x67, 0x65, 0x2d, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x00,
0x00, 0x00, 0x08, 0x66, 0x69, 0x73, 0x68, 0x2d, 0x6c, 0x73, 0x70, 0x00,
0x00, 0x00, 0x05, 0x67, 0x6f, 0x70, 0x6c, 0x73, 0x00, 0x00, 0x00, 0x05,
0x73, 0x65, 0x72, 0x76, 0x65, 0x00, 0x00, 0x00, 0x17, 0x68, 0x61, 0x73,
0x6b, 0x65, 0x6c, 0x6c, 0x2d, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67,
0x65, 0x2d, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x00, 0x00, 0x00, 0x03,
0x6c, 0x73, 0x70, 0x00, 0x00, 0x00, 0x15, 0x65, 0x6d, 0x6d, 0x65, 0x74,
0x2d, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2d, 0x73, 0x65,
0x72, 0x76, 0x65, 0x72, 0x00, 0x00, 0x00, 0x1a, 0x74, 0x79, 0x70, 0x65,
0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2d, 0x6c, 0x61, 0x6e, 0x67, 0x75,
0x61, 0x67, 0x65, 0x2d, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x00, 0x00,
0x00, 0x13, 0x6c, 0x75, 0x61, 0x2d, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61,
0x67, 0x65, 0x2d, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x00, 0x00, 0x00,
0x12, 0x70, 0x79, 0x72, 0x69, 0x67, 0x68, 0x74, 0x2d, 0x6c, 0x61, 0x6e,
0x67, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x00, 0x00, 0x00, 0x0d, 0x72,
0x75, 0x73, 0x74, 0x2d, 0x61, 0x6e, 0x61, 0x6c, 0x79, 0x7a, 0x65, 0x72,
0x00, 0x00, 0x00, 0x0c, 0x69, 0x6e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x68,
0x65, 0x6e, 0x73, 0x65, 0x00, 0x00, 0x00, 0x08, 0x6d, 0x61, 0x72, 0x6b,
0x73, 0x6d, 0x61, 0x6e, 0x00, 0x00, 0x00, 0x06, 0x73, 0x65, 0x72, 0x76,
0x65, 0x72, 0x00, 0x00, 0x00, 0x15, 0x6e, 0x67, 0x69, 0x6e, 0x78, 0x2d,
0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2d, 0x73, 0x65, 0x72,
0x76, 0x65, 0x72, 0x00, 0x00, 0x00, 0x05, 0x74, 0x61, 0x70, 0x6c, 0x6f,
0x00, 0x00, 0x00, 0x14, 0x79, 0x61, 0x6d, 0x6c, 0x2d, 0x6c, 0x61, 0x6e,
0x67, 0x75, 0x61, 0x67, 0x65, 0x2d, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72,
0x00, 0x00, 0x00, 0x04, 0x73, 0x71, 0x6c, 0x73, 0x00, 0x00, 0x00, 0x14,
0x6d, 0x61, 0x6b, 0x65, 0x2d, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67,
0x65, 0x2d, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x00, 0x00, 0x00, 0x13,
0x73, 0x71, 0x6c, 0x2d, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65,
0x2d, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x00, 0x00, 0x00, 0x02, 0x75,
0x70, 0x00, 0x00, 0x00, 0x08, 0x2d, 0x2d, 0x6d, 0x65, 0x74, 0x68, 0x6f,
0x64, 0x00, 0x00, 0x00, 0x04, 0xee, 0x98, 0x9e, 0x20, 0x00, 0x00, 0x00,
0x01, 0x63, 0x00, 0x00, 0x00, 0x04, 0xee, 0x98, 0x9d, 0x20, 0x00, 0x00,
0x00, 0x03, 0x63, 0x70, 0x70, 0x00, 0x00, 0x00, 0x02, 0x63, 0x63, 0x00,
0x00, 0x00, 0x03, 0x63, 0x78, 0x78, 0x00, 0x00, 0x00, 0x04, 0xef, 0x83,
0xbd, 0x20, 0x00, 0x00, 0x00, 0x01, 0x68, 0x00, 0x00, 0x00, 0x03, 0x68,
0x70, 0x70, 0x00, 0x00, 0x00, 0x04, 0xee, 0x9a, 0xb8, 0x20, 0x00, 0x00,
0x00, 0x03, 0x63, 0x73, 0x73, 0x00, 0x00, 0x00, 0x04, 0xee, 0xb9, 0x81,
0x20, 0x00, 0x00, 0x00, 0x04, 0x66, 0x69, 0x73, 0x68, 0x00, 0x00, 0x00,
0x04, 0xee, 0x98, 0xa7, 0x20, 0x00, 0x00, 0x00, 0x02, 0x67, 0x6f, 0x00,
0x00, 0x00, 0x03, 0x6d, 0x6f, 0x64, 0x00, 0x00, 0x00, 0x04, 0xee, 0x9d,
0xb7, 0x20, 0x00, 0x00, 0x00, 0x02, 0x68, 0x73, 0x00, 0x00, 0x00, 0x03,
0x6c, 0x68, 0x73, 0x00, 0x00, 0x00, 0x04, 0xef, 0x84, 0xa1, 0x20, 0x00,
0x00, 0x00, 0x04, 0x68, 0x74, 0x6d, 0x6c, 0x00, 0x00, 0x00, 0x03, 0x68,
0x74, 0x6d, 0x00, 0x00, 0x00, 0x04, 0xef, 0x8b, 0xaf, 0x20, 0x00, 0x00,
0x00, 0x02, 0x6a, 0x73, 0x00, 0x00, 0x00, 0x04, 0xee, 0x9a, 0x9d, 0x20,
0x00, 0x00, 0x00, 0x02, 0x74, 0x73, 0x00, 0x00, 0x00, 0x02, 0x7b, 0x7d,
0x00, 0x00, 0x00, 0x04, 0x6a, 0x73, 0x6f, 0x6e, 0x00, 0x00, 0x00, 0x05,
0x6a, 0x73, 0x6f, 0x6e, 0x63, 0x00, 0x00, 0x00, 0x03, 0x65, 0x72, 0x62,
0x00, 0x00, 0x00, 0x05, 0xf3, 0xb0, 0xa2, 0xb1, 0x20, 0x00, 0x00, 0x00,
0x03, 0x6c, 0x75, 0x61, 0x00, 0x00, 0x00, 0x05, 0xf3, 0xb0, 0x8c, 0xa0,
0x20, 0x00, 0x00, 0x00, 0x02, 0x70, 0x79, 0x00, 0x00, 0x00, 0x07, 0x70,
0x79, 0x72, 0x69, 0x67, 0x68, 0x74, 0x00, 0x00, 0x00, 0x05, 0xf3, 0xb1,
0x98, 0x97, 0x20, 0x00, 0x00, 0x00, 0x02, 0x72, 0x73, 0x00, 0x00, 0x00,
0x05, 0xf3, 0xb0, 0x8c, 0x9f, 0x20, 0x00, 0x00, 0x00, 0x03, 0x70, 0x68,
0x70, 0x00, 0x00, 0x00, 0x04, 0xee, 0xba, 0xab, 0x20, 0x00, 0x00, 0x00,
0x02, 0x6d, 0x64, 0x00, 0x00, 0x00, 0x08, 0x6d, 0x61, 0x72, 0x6b, 0x64,
0x6f, 0x77, 0x6e, 0x00, 0x00, 0x00, 0x04, 0xee, 0x98, 0x95, 0x20, 0x00,
0x00, 0x00, 0x04, 0x63, 0x6f, 0x6e, 0x66, 0x00, 0x00, 0x00, 0x04, 0xee,
0x9a, 0xb2, 0x20, 0x00, 0x00, 0x00, 0x04, 0x74, 0x6f, 0x6d, 0x6c, 0x00,
0x00, 0x00, 0x03, 0x79, 0x6d, 0x6c, 0x00, 0x00, 0x00, 0x04, 0x79, 0x61,
0x6d, 0x6c, 0x00, 0x00, 0x00, 0x04, 0xee, 0x99, 0x8d, 0x20, 0x00, 0x00,
0x00, 0x03, 0x73, 0x71, 0x6c, 0x00, 0x00, 0x00, 0x04, 0xee, 0x99, 0xb3,
0x20, 0x00, 0x00, 0x00, 0x08, 0x4d, 0x61, 0x6b, 0x65, 0x66, 0x69, 0x6c,
0x65, 0x00, 0x00, 0x00, 0x08, 0x6d, 0x61, 0x6b, 0x65, 0x66, 0x69, 0x6c,
0x65, 0x00, 0x00, 0x00, 0x04, 0xee, 0x99, 0x9f, 0x20, 0x00, 0x00, 0x00,
0x02, 0x67, 0x64, 0x00, 0x00, 0x00, 0x04, 0xef, 0x80, 0xad, 0x20, 0x00,
0x00, 0x00, 0x03, 0x6d, 0x61, 0x6e, 0x00, 0x00, 0x00, 0x04, 0xee, 0x9c,
0xa8, 0x20, 0x00, 0x00, 0x00, 0x04, 0x64, 0x69, 0x66, 0x66, 0x00, 0x00,
0x00, 0x05, 0x70, 0x61, 0x74, 0x63, 0x68, 0x00, 0x00, 0x00, 0x04, 0xee,
0x99, 0x9d, 0x20, 0x00, 0x00, 0x00, 0x0d, 0x67, 0x69, 0x74, 0x61, 0x74,
0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x00, 0x00, 0x00, 0x09,
0x67, 0x69, 0x74, 0x69, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x00, 0x00, 0x00,
0x02, 0x2e, 0x2a, 0x00, 0x00, 0x00, 0x05, 0x72, 0x65, 0x67, 0x65, 0x78,
0x00, 0x00, 0x00, 0x03, 0x69, 0x6e, 0x69, 0x00, 0x00, 0x00, 0x05, 0xf3,
0xb0, 0xb4, 0xad, 0x20, 0x00, 0x00, 0x00, 0x02, 0x72, 0x62, 0x00, 0x00,
0x00, 0x07, 0x47, 0x65, 0x6d, 0x66, 0x69, 0x6c, 0x65, 0x00, 0x00, 0x00,
0x04, 0xee, 0xaf, 0x8a, 0x20, 0x00, 0x00, 0x00, 0x02, 0x73, 0x68, 0x00,
0x00, 0x00, 0x0c, 0x62, 0x61, 0x73, 0x68, 0x5f, 0x70, 0x72, 0x6f, 0x66,
0x69, 0x6c, 0x65, 0x00, 0x00, 0x00, 0x06, 0x62, 0x61, 0x73, 0x68, 0x72,
0x63, 0x00, 0x00, 0x4e, 0x00, 0x0b, 0x40, 0x6c, 0x73, 0x70, 0x5f, 0x63,
0x6f, 0x6e, 0x66, 0x69, 0x67, 0x00, 0x00, 0x01, 0x63, 0x00, 0x00, 0x05,
0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x00, 0x00, 0x06, 0x73, 0x79, 0x6d, 0x62,
0x6f, 0x6c, 0x00, 0x00, 0x0a, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69,
0x6f, 0x6e, 0x73, 0x00, 0x00, 0x03, 0x6c, 0x73, 0x70, 0x00, 0x00, 0x03,
0x63, 0x70, 0x70, 0x00, 0x00, 0x01, 0x68, 0x00, 0x00, 0x03, 0x63, 0x73,
0x73, 0x00, 0x00, 0x04, 0x66, 0x69, 0x73, 0x68, 0x00, 0x00, 0x02, 0x67,
0x6f, 0x00, 0x00, 0x05, 0x67, 0x6f, 0x6d, 0x6f, 0x64, 0x00, 0x00, 0x07,
0x68, 0x61, 0x73, 0x6b, 0x65, 0x6c, 0x6c, 0x00, 0x00, 0x04, 0x68, 0x74,
0x6d, 0x6c, 0x00, 0x00, 0x0a, 0x6a, 0x61, 0x76, 0x61, 0x73, 0x63, 0x72,
0x69, 0x70, 0x74, 0x00, 0x00, 0x0a, 0x74, 0x79, 0x70, 0x65, 0x73, 0x63,
0x72, 0x69, 0x70, 0x74, 0x00, 0x00, 0x04, 0x6a, 0x73, 0x6f, 0x6e, 0x00,
0x00, 0x05, 0x6a, 0x73, 0x6f, 0x6e, 0x63, 0x00, 0x00, 0x03, 0x65, 0x72,
0x62, 0x00, 0x00, 0x03, 0x6c, 0x75, 0x61, 0x00, 0x00, 0x06, 0x70, 0x79,
0x74, 0x68, 0x6f, 0x6e, 0x00, 0x00, 0x04, 0x72, 0x75, 0x73, 0x74, 0x00,
0x00, 0x03, 0x70, 0x68, 0x70, 0x00, 0x00, 0x08, 0x6d, 0x61, 0x72, 0x6b,
0x64, 0x6f, 0x77, 0x6e, 0x00, 0x00, 0x05, 0x6e, 0x67, 0x69, 0x6e, 0x78,
0x00, 0x00, 0x04, 0x74, 0x6f, 0x6d, 0x6c, 0x00, 0x00, 0x04, 0x79, 0x61,
0x6d, 0x6c, 0x00, 0x00, 0x03, 0x73, 0x71, 0x6c, 0x00, 0x00, 0x04, 0x6d,
0x61, 0x6b, 0x65, 0x00, 0x00, 0x08, 0x67, 0x64, 0x73, 0x63, 0x72, 0x69,
0x70, 0x74, 0x00, 0x00, 0x03, 0x6d, 0x61, 0x6e, 0x00, 0x00, 0x04, 0x64,
0x69, 0x66, 0x66, 0x00, 0x00, 0x0d, 0x67, 0x69, 0x74, 0x61, 0x74, 0x74,
0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x00, 0x00, 0x09, 0x67, 0x69,
0x74, 0x69, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x00, 0x00, 0x05, 0x72, 0x65,
0x67, 0x65, 0x78, 0x00, 0x00, 0x03, 0x69, 0x6e, 0x69, 0x00, 0x00, 0x04,
0x72, 0x75, 0x62, 0x79, 0x00, 0x00, 0x09, 0x66, 0x69, 0x6c, 0x65, 0x6e,
0x61, 0x6d, 0x65, 0x73, 0x00, 0x00, 0x04, 0x62, 0x61, 0x73, 0x68, 0x00,
0x00, 0x0a, 0x40, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x73,
0x00, 0x00, 0x07, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x00, 0x00,
0x02, 0x66, 0x67, 0x00, 0x00, 0x07, 0x73, 0x68, 0x65, 0x62, 0x61, 0x6e,
0x67, 0x00, 0x00, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x00, 0x00, 0x07,
0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x00, 0x00, 0x06, 0x69, 0x74,
0x61, 0x6c, 0x69, 0x63, 0x00, 0x00, 0x06, 0x73, 0x74, 0x72, 0x69, 0x6e,
0x67, 0x00, 0x00, 0x06, 0x65, 0x73, 0x63, 0x61, 0x70, 0x65, 0x00, 0x00,
0x0d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x6f, 0x6c, 0x61, 0x74, 0x69,
0x6f, 0x6e, 0x00, 0x00, 0x06, 0x72, 0x65, 0x67, 0x65, 0x78, 0x70, 0x00,
0x00, 0x06, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x00, 0x00, 0x04, 0x74,
0x72, 0x75, 0x65, 0x00, 0x00, 0x05, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x00,
0x00, 0x04, 0x63, 0x68, 0x61, 0x72, 0x00, 0x00, 0x07, 0x6b, 0x65, 0x79,
0x77, 0x6f, 0x72, 0x64, 0x00, 0x00, 0x0f, 0x6b, 0x65, 0x79, 0x77, 0x6f,
0x72, 0x64, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x00, 0x00,
0x08, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x00, 0x00, 0x08,
0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x04, 0x74,
0x79, 0x70, 0x65, 0x00, 0x00, 0x08, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61,
0x6e, 0x74, 0x00, 0x00, 0x10, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c,
0x65, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x00, 0x00, 0x0e,
0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x67, 0x6c, 0x6f, 0x62,
0x61, 0x6c, 0x00, 0x00, 0x0a, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74,
0x69, 0x6f, 0x6e, 0x00, 0x00, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74,
0x69, 0x76, 0x65, 0x00, 0x00, 0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x00,
0x00, 0x06, 0x62, 0x72, 0x61, 0x63, 0x65, 0x31, 0x00, 0x00, 0x06, 0x62,
0x72, 0x61, 0x63, 0x65, 0x32, 0x00, 0x00, 0x06, 0x62, 0x72, 0x61, 0x63,
0x65, 0x33, 0x00, 0x00, 0x06, 0x62, 0x72, 0x61, 0x63, 0x65, 0x34, 0x00,
0x00, 0x06, 0x62, 0x72, 0x61, 0x63, 0x65, 0x35, 0x00, 0x00, 0x06, 0x40,
0x74, 0x68, 0x65, 0x6d, 0x65, 0x00, 0x00, 0x09, 0x61, 0x75, 0x74, 0x6f,
0x5f, 0x75, 0x6e, 0x69, 0x78, 0x00, 0x00, 0x0d, 0x40, 0x6c, 0x69, 0x6e,
0x65, 0x5f, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x73, 0x00, 0x00, 0x0d,
0x40, 0x6b, 0x65, 0x79, 0x5f, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72,
0x73, 0x00, 0x00, 0x0a, 0x40, 0x6b, 0x65, 0x79, 0x5f, 0x62, 0x69, 0x6e,
0x64, 0x73, 0x00, 0x00, 0x0d, 0x40, 0x68, 0x69, 0x67, 0x68, 0x6c, 0x69,
0x67, 0x68, 0x74, 0x65, 0x72, 0x73, 0x00, 0x00, 0x0a, 0x40, 0x62, 0x5f,
0x73, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x00, 0x00, 0x0b, 0x40, 0x62,
0x5f, 0x73, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x00, 0x00, 0x00,
0x01, 0x10, 0x00, 0x01, 0x00, 0x08, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00,
0x00, 0x42, 0x10, 0x02, 0x00, 0x10, 0x03, 0x01, 0x10, 0x04, 0x02, 0x10,
0x05, 0x03, 0x10, 0x06, 0x04, 0x2d, 0x01, 0x05, 0x05, 0x10, 0x02, 0x06,
0x10, 0x03, 0x07, 0x10, 0x04, 0x08, 0x2d, 0x01, 0x09, 0x03, 0x63, 0x01,
0x58, 0x02, 0x00, 0x5f, 0x01, 0x0a, 0x63, 0x01, 0x58, 0x02, 0x01, 0x5f,
0x01, 0x0b, 0x63, 0x01, 0x58, 0x02, 0x02, 0x5f, 0x01, 0x0c, 0x63, 0x01,
0x58, 0x02, 0x03, 0x5f, 0x01, 0x0d, 0x38, 0x01, 0x00, 0x00, 0x00, 0x0e,
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, 0x0b, 0x61, 0x74, 0x74, 0x72, 0x5f,
0x72, 0x65, 0x61, 0x64, 0x65, 0x72, 0x00, 0x00, 0x07, 0x73, 0x74, 0x61,
0x72, 0x74, 0x75, 0x70, 0x00, 0x00, 0x08, 0x73, 0x68, 0x75, 0x74, 0x64,
0x6f, 0x77, 0x6e, 0x00, 0x00, 0x10, 0x65, 0x78, 0x74, 0x72, 0x61, 0x5f,
0x68, 0x69, 0x67, 0x68, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x73, 0x00, 0x00,
0x04, 0x62, 0x69, 0x6e, 0x64, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x02,
0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x34, 0x00,
0x00, 0x01, 0x01, 0x02, 0x01, 0x1a, 0x02, 0x00, 0x38, 0x02, 0x00, 0x00,
0x00, 0x01, 0x00, 0x0a, 0x40, 0x62, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74,
0x75, 0x70, 0x00, 0x00, 0x00, 0x00, 0x2e, 0x00, 0x02, 0x00, 0x03, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x34, 0x00, 0x00, 0x01, 0x01,
0x02, 0x01, 0x1a, 0x02, 0x00, 0x38, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00,
0x0b, 0x40, 0x62, 0x5f, 0x73, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e,
0x00, 0x00, 0x00, 0x00, 0x36, 0x00, 0x02, 0x00, 0x03, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x0c, 0x34, 0x00, 0x00, 0x01, 0x01, 0x02, 0x01,
0x1a, 0x02, 0x00, 0x38, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x13, 0x40,
0x62, 0x5f, 0x65, 0x78, 0x74, 0x72, 0x61, 0x5f, 0x68, 0x69, 0x67, 0x68,
0x6c, 0x69, 0x67, 0x68, 0x74, 0x73, 0x00, 0x00, 0x00, 0x01, 0x43, 0x00,
0x07, 0x00, 0x0a, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc1, 0x34,
0x04, 0x40, 0x01, 0x25, 0x00, 0x06, 0x25, 0x00, 0x05, 0x25, 0x00, 0x04,
0x11, 0x02, 0x11, 0x03, 0x01, 0x07, 0x01, 0x1d, 0x08, 0x00, 0x2f, 0x07,
0x01, 0x01, 0x26, 0x07, 0x00, 0x07, 0x01, 0x07, 0x01, 0x48, 0x01, 0x07,
0x01, 0x01, 0x07, 0x02, 0x28, 0x07, 0x00, 0x03, 0x25, 0x00, 0x33, 0x12,
0x05, 0x1d, 0x07, 0x02, 0x2f, 0x07, 0x03, 0x00, 0x01, 0x06, 0x07, 0x01,
0x07, 0x06, 0x10, 0x08, 0x04, 0x57, 0x09, 0x00, 0x30, 0x07, 0x05, 0x01,
0x2d, 0x07, 0x06, 0x00, 0x27, 0x07, 0x00, 0x0d, 0x01, 0x07, 0x06, 0x01,
0x08, 0x04, 0x30, 0x07, 0x07, 0x00, 0x25, 0x00, 0x02, 0x11, 0x07, 0x25,
0x00, 0x5c, 0x2d, 0x07, 0x06, 0x00, 0x27, 0x07, 0x00, 0x22, 0x01, 0x07,
0x02, 0x1d, 0x08, 0x00, 0x2f, 0x07, 0x01, 0x01, 0x26, 0x07, 0x00, 0x07,
0x01, 0x07, 0x02, 0x48, 0x02, 0x07, 0x01, 0x01, 0x07, 0x01, 0x57, 0x08,
0x01, 0x30, 0x07, 0x08, 0x00, 0x25, 0x00, 0x32, 0x01, 0x07, 0x03, 0x1d,
0x08, 0x09, 0x2f, 0x07, 0x01, 0x01, 0x27, 0x07, 0x00, 0x22, 0x01, 0x07,
0x02, 0x1d, 0x08, 0x00, 0x2f, 0x07, 0x01, 0x01, 0x26, 0x07, 0x00, 0x07,
0x01, 0x07, 0x02, 0x48, 0x02, 0x07, 0x01, 0x01, 0x07, 0x01, 0x57, 0x08,
0x02, 0x30, 0x07, 0x08, 0x00, 0x25, 0x00, 0x02, 0x11, 0x07, 0x38, 0x07,
0x00, 0x00, 0x00, 0x0a, 0x00, 0x05, 0x41, 0x72, 0x72, 0x61, 0x79, 0x00,
0x00, 0x05, 0x69, 0x73, 0x5f, 0x61, 0x3f, 0x00, 0x00, 0x06, 0x4f, 0x62,
0x6a, 0x65, 0x63, 0x74, 0x00, 0x00, 0x03, 0x6e, 0x65, 0x77, 0x00, 0x00,
0x03, 0x73, 0x65, 0x74, 0x00, 0x00, 0x17, 0x64, 0x65, 0x66, 0x69, 0x6e,
0x65, 0x5f, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x74, 0x6f, 0x6e, 0x5f,
0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x00, 0x00, 0x0c, 0x62, 0x6c, 0x6f,
0x63, 0x6b, 0x5f, 0x67, 0x69, 0x76, 0x65, 0x6e, 0x3f, 0x00, 0x00, 0x0d,
0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x65, 0x78, 0x65,
0x63, 0x00, 0x00, 0x04, 0x65, 0x61, 0x63, 0x68, 0x00, 0x00, 0x06, 0x53,
0x74, 0x72, 0x69, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x04,
0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x34, 0x04,
0x20, 0x01, 0x25, 0x00, 0x03, 0x25, 0x00, 0x02, 0x11, 0x02, 0x21, 0x04,
0x05, 0x00, 0x21, 0x05, 0x01, 0x00, 0x01, 0x06, 0x01, 0x01, 0x07, 0x02,
0x01, 0x08, 0x03, 0x30, 0x04, 0x00, 0x03, 0x38, 0x04, 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,
0x34, 0x04, 0x00, 0x00, 0x21, 0x03, 0x02, 0x00, 0x57, 0x04, 0x00, 0x30,
0x03, 0x00, 0x00, 0x38, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x04, 0x65,
0x61, 0x63, 0x68, 0x00, 0x00, 0x00, 0x00, 0x8d, 0x00, 0x03, 0x00, 0x08,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x59, 0x34, 0x04, 0x00, 0x00,
0x19, 0x03, 0x00, 0x21, 0x04, 0x01, 0x00, 0x01, 0x05, 0x03, 0x01, 0x06,
0x04, 0x2f, 0x05, 0x01, 0x01, 0x26, 0x05, 0x00, 0x07, 0x53, 0x05, 0x00,
0x2f, 0x03, 0x02, 0x02, 0x19, 0x03, 0x00, 0x21, 0x04, 0x01, 0x00, 0x23,
0x03, 0x01, 0x04, 0x01, 0x01, 0x05, 0x03, 0x01, 0x06, 0x04, 0x2f, 0x05,
0x01, 0x01, 0x26, 0x05, 0x00, 0x07, 0x47, 0x05, 0x00, 0x2f, 0x03, 0x02,
0x02, 0x19, 0x03, 0x00, 0x21, 0x04, 0x01, 0x00, 0x23, 0x03, 0x01, 0x04,
0x01, 0x23, 0x03, 0x21, 0x04, 0x04, 0x01, 0x2f, 0x03, 0x03, 0x01, 0x38,
0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0d, 0x40, 0x6b, 0x65, 0x79, 0x5f,
0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x73, 0x00, 0x00, 0x02, 0x5b,
0x5d, 0x00, 0x00, 0x03, 0x5b, 0x5d, 0x3d, 0x00, 0x00, 0x02, 0x3c, 0x3c,
0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x03, 0x00, 0x05, 0x00, 0x01, 0x00,
0x00, 0x00, 0x00, 0x00, 0x11, 0x34, 0x04, 0x00, 0x00, 0x21, 0x03, 0x02,
0x00, 0x57, 0x04, 0x00, 0x30, 0x03, 0x00, 0x00, 0x38, 0x03, 0x00, 0x00,
0x00, 0x01, 0x00, 0x04, 0x65, 0x61, 0x63, 0x68, 0x00, 0x00, 0x00, 0x00,
0x8a, 0x00, 0x03, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x59, 0x34, 0x04, 0x00, 0x00, 0x19, 0x03, 0x00, 0x21, 0x04, 0x01, 0x00,
0x01, 0x05, 0x03, 0x01, 0x06, 0x04, 0x2f, 0x05, 0x01, 0x01, 0x26, 0x05,
0x00, 0x07, 0x53, 0x05, 0x00, 0x2f, 0x03, 0x02, 0x02, 0x19, 0x03, 0x00,
0x21, 0x04, 0x01, 0x00, 0x23, 0x03, 0x01, 0x04, 0x01, 0x01, 0x05, 0x03,
0x01, 0x06, 0x04, 0x2f, 0x05, 0x01, 0x01, 0x26, 0x05, 0x00, 0x07, 0x47,
0x05, 0x00, 0x2f, 0x03, 0x02, 0x02, 0x19, 0x03, 0x00, 0x21, 0x04, 0x01,
0x00, 0x23, 0x03, 0x01, 0x04, 0x01, 0x23, 0x03, 0x21, 0x04, 0x03, 0x01,
0x2f, 0x03, 0x03, 0x01, 0x38, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0a,
0x40, 0x6b, 0x65, 0x79, 0x5f, 0x62, 0x69, 0x6e, 0x64, 0x73, 0x00, 0x00,
0x02, 0x5b, 0x5d, 0x00, 0x00, 0x03, 0x5b, 0x5d, 0x3d, 0x00, 0x00, 0x02,
0x3c, 0x3c, 0x00, 0x4c, 0x56, 0x41, 0x52, 0x00, 0x00, 0x00, 0x72, 0x00,
0x00, 0x00, 0x0b, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x05, 0x00,
0x06, 0x00, 0x07, 0x00, 0x08, 0x00, 0x09, 0xff, 0xff, 0x00, 0x0a, 0xff,
0xff, 0x00, 0x09, 0xff, 0xff, 0x00, 0x0a, 0xff, 0xff, 0x45, 0x4e, 0x44,
0x00, 0x00, 0x00, 0x00, 0x08
};
constexpr unsigned int _tmp___crib_precompiled_mrb_len = 6305;
+18 -7
View File
@@ -3,8 +3,11 @@
#include "io/knot.h" #include "io/knot.h"
#include "io/sysio.h" #include "io/sysio.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};
@@ -34,15 +37,23 @@ struct Token {
TokenKind type; TokenKind type;
}; };
struct LineData { struct StateBase {
std::shared_ptr<void> in_state{nullptr}; virtual ~StateBase() = default;
std::vector<Token> tokens; virtual std::unique_ptr<StateBase> clone() const = 0;
std::shared_ptr<void> out_state{nullptr};
}; };
struct CustomState { struct CustomState : StateBase {
mrb_value state; std::string value;
CustomState(mrb_value s) : state(s) {} explicit CustomState(std::string v) : value(std::move(v)) {}
std::unique_ptr<StateBase> clone() const override {
return std::make_unique<CustomState>(value);
}
};
struct LineData {
std::unique_ptr<StateBase> out_state;
std::vector<Token> tokens;
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
-245
View File
@@ -1,245 +0,0 @@
#ifndef LINE_TREE_H
#define LINE_TREE_H
#include "syntax/decl.h"
struct LineTree {
void clear() {
std::unique_lock lock(mtx);
clear_node(root);
root = nullptr;
stack_size = 0;
}
void build(uint32_t x) {
std::unique_lock lock(mtx);
root = build_node(x);
}
LineData *at(uint32_t x) {
std::shared_lock lock(mtx);
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) {
std::shared_lock lock(mtx);
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() {
std::shared_lock lock(mtx);
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) {
std::unique_lock lock(mtx);
if (x > subtree_size(root))
x = subtree_size(root);
root = insert_node(root, x, y);
}
void erase(uint32_t x, uint32_t y) {
std::unique_lock lock(mtx);
if (x + y > subtree_size(root))
x = subtree_size(root) - y;
root = erase_node(root, x, y);
}
uint32_t count() {
std::shared_lock lock(mtx);
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;
std::shared_mutex mtx;
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
+10 -14
View File
@@ -1,31 +1,27 @@
#ifndef SYNTAX_PARSER_H #ifndef SYNTAX_PARSER_H
#define SYNTAX_PARSER_H #define SYNTAX_PARSER_H
#include "scripting/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{UINT32_MAX - 2048}; std::atomic<uint32_t> scroll_max{0};
std::atomic<bool> scroll_dirty{false}; std::atomic<bool> scroll_dirty{false};
std::mutex mutex; LineMap line_map = LineMap();
std::mutex data_mutex;
LineTree line_tree;
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 old_end_line, uint32_t inserted_rows); void edit(uint32_t start_line, uint32_t removed_rows, uint32_t inserted_rows);
void work(); void work();
void scroll(uint32_t line); void scroll(uint32_t line);
}; };
+5 -3
View File
@@ -8,11 +8,13 @@
struct Bar { struct Bar {
Coord screen; Coord screen;
std::string command = ""; std::string command = "";
std::string log_line = "";
uint32_t cursor = 0; uint32_t cursor = 0;
Bar(Coord screen) : 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
+11 -7
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;
@@ -102,7 +108,6 @@ struct Language {
std::string name; std::string name;
std::string lsp_name; std::string lsp_name;
uint32_t color; uint32_t color;
std::string symbol = "󱓧 ";
}; };
struct LSP { struct LSP {
@@ -119,6 +124,7 @@ extern std::unordered_map<std::string, LSP> lsps;
#define ABS(x) ((x) < 0 ? -(x) : (x)) #define ABS(x) ((x) < 0 ? -(x) : (x))
#define UNUSED(x) (void)(x) #define UNUSED(x) (void)(x)
#define USING(x) UNUSED(sizeof(x)) #define USING(x) UNUSED(sizeof(x))
#define CTRL(x) ((x) & 0x1f)
inline uint32_t HEX(const std::string &s) { inline uint32_t HEX(const std::string &s) {
if (s.empty()) if (s.empty())
@@ -133,6 +139,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);
@@ -156,9 +163,6 @@ std::string get_exe_dir();
char *load_file(const char *path, uint32_t *out_len, bool *out_eol); char *load_file(const char *path, uint32_t *out_len, bool *out_eol);
Language language_for_file(const char *filename); Language language_for_file(const char *filename);
void copy_to_clipboard(const char *text, size_t len);
char *get_from_clipboard(uint32_t *out_len);
template <typename T> template <typename T>
inline T *safe_get(std::map<uint16_t, T> &m, uint16_t key) { inline T *safe_get(std::map<uint16_t, T> &m, uint16_t key) {
auto it = m.find(key); auto it = m.find(key);
+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
+2 -14
View File
@@ -4,21 +4,9 @@ set -eu
install() { install() {
BINARY_NAME="crib" BINARY_NAME="crib"
VERSION="v0.0.1-alpha" BIN_URL="https://git.syedm.dev/SyedM/crib/releases/download/v0.0.5-alpha/crib"
BIN_URL="https://git.syedm.dev/SyedM-dev/crib/releases/download/$VERSION/crib"
ldconfig -p | grep libmagic >/dev/null 2>&1 echo "Install or update locally (~/.local/bin) or globally (/usr/bin)? [l/g]"
if ! ldconfig -p | grep libmagic >/dev/null 2>&1; then
echo "Missing dependency: libmagic (part of \`file\` package)"
echo "Install them using your package manager:"
echo "Ubuntu/Debian: sudo apt install ruby libmagic1"
echo "Arch: sudo pacman -S file"
echo "Void: sudo xbps-install -Sy file"
exit 1
fi
echo "Install locally (~/.local/bin) or globally (/usr/bin)? [l/g]"
read -r choice </dev/tty read -r choice </dev/tty
case "$choice" in case "$choice" in
l | L) l | L)
Vendored Submodule
+1
Submodule libs/json added at 3b0dd69928
Vendored Submodule
+1
Submodule libs/mruby added at 88b93f66c4
+1 -3
View File
@@ -22,15 +22,13 @@ colorize() {
} }
# Example of error handling # Example of error handling
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'
int main() { int main() {
} }
+1 -1
View File
@@ -89,6 +89,7 @@ BASH
puts multi puts multi
# Arrays mixing everything # Arrays mixing everything
mixed = [ mixed = [
'🐍 Ruby + Python? sacrilege! 🐍', '🐍 Ruby + Python? sacrilege! 🐍',
@@ -338,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; 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);
}
}
+105 -89
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) {
@@ -25,6 +24,23 @@ inline static std::string completion_prefix(Editor *editor) {
return prefix; return prefix;
} }
inline static void completion_adjust_scroll(CompletionSession &s) {
if (s.visible.empty())
return;
int vi = -1;
for (size_t i = 0; i < s.visible.size(); i++)
if (s.visible[i] == s.select) {
vi = (int)i;
break;
}
if (vi < 0)
return;
if ((uint32_t)vi < s.scroll)
s.scroll = vi;
else if ((uint32_t)vi >= s.scroll + 8)
s.scroll = vi - 7;
}
void completion_filter(Editor *editor) { void completion_filter(Editor *editor) {
auto &session = editor->completion; auto &session = editor->completion;
std::string prefix = completion_prefix(editor); std::string prefix = completion_prefix(editor);
@@ -44,23 +60,22 @@ void completion_filter(Editor *editor) {
session.select) == session.visible.end()) session.select) == session.visible.end())
session.select = session.visible[0]; session.select = session.visible[0];
session.box.hidden = false; session.box.hidden = false;
session.scroll = 0;
completion_adjust_scroll(session);
session.box.render_update(); session.box.render_update();
} }
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;
LSPPending *pending = new LSPPending(); LSPPending *pending = new LSPPending();
pending->editor = editor; pending->editor = editor;
pending->method = "textDocument/completion"; pending->callback = [](Editor *editor, const json &message) {
pending->callback = [](Editor *editor, std::string, json message) {
auto &session = editor->completion; auto &session = editor->completion;
std::unique_lock lock(session.mtx); std::unique_lock lock(session.mtx);
std::vector<json> items_json; std::vector<json> items_json;
@@ -82,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())
@@ -180,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"]) {
@@ -190,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);
} }
} }
@@ -209,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);
@@ -221,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"},
@@ -238,110 +252,111 @@ 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 = "";
LSPPending *pending = new LSPPending(); LSPPending *pending = new LSPPending();
pending->editor = editor; pending->editor = editor;
pending->method = "completionItem/resolve"; pending->callback = [](Editor *editor, const json &message) {
pending->callback = [](Editor *editor, std::string, json message) {
std::unique_lock lock(editor->completion.mtx); std::unique_lock lock(editor->completion.mtx);
auto &item = editor->completion.items[editor->completion.select]; auto &item = editor->completion.items[editor->completion.select];
if (message["result"].contains("documentation")) { if (message["result"].contains("documentation")) {
@@ -373,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) {
@@ -395,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) {
@@ -406,35 +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();
editor->completion.box.render_update(); completion_adjust_scroll(*this);
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();
editor->completion.box.render_update(); completion_adjust_scroll(*this);
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 -111
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, 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, 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, pos.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",
{{"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,23 +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 (editor->parser) if (this->parser)
editor->parser->edit(start.row, end.row - 1, rows); this->parser->edit(start.row, end.row - start.row, rows);
if (editor->lsp) { this->hooks.edit(start.row, end.row - start.row, rows);
if (editor->lsp->incremental_sync) { auto lsp = this->lsp.load();
json message = { if (lsp) {
{"jsonrpc", "2.0"}, if (lsp->incremental_sync) {
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( json::array(
{{{"range", {{{"range",
@@ -262,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));
} }
} }
} }
+40 -104
View File
@@ -1,72 +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 "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');
@@ -77,66 +73,6 @@ void save_file(Editor *editor) {
} }
out.close(); out.close();
free(str); free(str);
if (editor->lsp) { ui::bar.log("Written " + std::to_string(char_count) + " bytes to " +
json save_msg = {{"jsonrpc", "2.0"}, this->filename);
{"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->method = "textDocument/formatting";
pending->callback = [save_msg, version](Editor *editor, std::string,
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);
}
}
} }
+75 -78
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,35 +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') {
edit_insert(editor, editor->cursor, (char *)" ", 2); this->indents.insert_tab(this->cursor);
cursor_right(editor, 2);
} 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:
@@ -190,48 +188,47 @@ 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 ',':
this->dedent_selection();
break;
case '>':
case '.':
this->indent_selection();
break;
} }
} }
break; break;
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();
if ((event.key_type == KEY_CHAR || event.key_type == KEY_PASTE) && event.c)
free(event.c);
} }
+317 -191
View File
@@ -1,83 +1,135 @@
#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"
void cut(Editor *editor) { void Editor::cut() {
if (ABS((int64_t)this->cursor.row - (int64_t)this->selection.row) > 1500) {
ui::bar.log("Selection too large!");
return;
}
if (mode != SELECT) 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);
copy_to_clipboard(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)this->cursor.row - (int64_t)this->selection.row) > 1500) {
ui::bar.log("Selection too large!");
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);
copy_to_clipboard(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() {
uint32_t len;
if (mode == NORMAL) { if (mode == NORMAL) {
char *text = get_from_clipboard(&len); std::string text = ruby_paste();
if (text) { if (text.empty())
insert_str(editor, text, len); return;
free(text); this->insert_str((char *)text.c_str(), text.length());
}
} else if (mode == SELECT) { } else if (mode == SELECT) {
char *text = get_from_clipboard(&len); std::string text = ruby_paste();
if (text) { 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, text, len);
free(text);
} }
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;
} }
void insert_char(Editor *editor, char c) { static void move_coord_by_delta(Coord &c, uint32_t row, int64_t delta) {
uint32_t col = editor->cursor.col; if (c.row == row) {
LineIterator *it = begin_l_iter(editor->root, editor->cursor.row); int64_t new_col = (int64_t)c.col + delta;
c.col = (uint32_t)MAX(new_col, 0);
}
}
void Editor::indent_selection() {
uint32_t top = MIN(this->cursor.row, this->selection.row);
uint32_t bot = MAX(this->cursor.row, this->selection.row);
if (bot - top > 1500) {
ui::bar.log("Can't indent more than 1500 lines at once!");
return;
}
if (bot - top >= 2)
this->indents.indent_block(top + 1, bot - 1);
uint32_t delta_top = this->indents.indent_line(top);
uint32_t delta_bot =
(bot == top) ? delta_top : this->indents.indent_line(bot);
move_coord_by_delta(this->cursor, top, delta_top);
move_coord_by_delta(this->selection, top, delta_top);
if (bot != top) {
move_coord_by_delta(this->cursor, bot, delta_bot);
move_coord_by_delta(this->selection, bot, delta_bot);
}
}
void Editor::dedent_selection() {
uint32_t top = MIN(this->cursor.row, this->selection.row);
uint32_t bot = MAX(this->cursor.row, this->selection.row);
if (bot - top > 1500) {
ui::bar.log("Can't dedent more than 1500 lines at once!");
return;
}
if (bot - top >= 2)
this->indents.dedent_block(top + 1, bot - 1);
uint32_t delta_top = this->indents.dedent_line(top);
uint32_t delta_bot =
(bot == top) ? delta_top : this->indents.dedent_line(bot);
move_coord_by_delta(this->cursor, top, -(int64_t)delta_top);
move_coord_by_delta(this->selection, top, -(int64_t)delta_top);
if (bot != top) {
move_coord_by_delta(this->cursor, bot, -(int64_t)delta_bot);
move_coord_by_delta(this->selection, bot, -(int64_t)delta_bot);
}
}
void Editor::insert_char(char c) {
uint32_t col = this->cursor.col;
LineIterator *it = begin_l_iter(this->root, this->cursor.row);
if (!it) if (!it)
return; return;
uint32_t len; uint32_t len;
@@ -93,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;
} }
} }
@@ -120,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;
@@ -139,31 +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->method = "textDocument/onTypeFormatting"; if (version != message.editor->lsp_version)
pending->callback = [version](Editor *editor, std::string,
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());
@@ -174,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;
} }
} }
@@ -189,91 +238,171 @@ 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;
char *line = next_line(it, nullptr); uint32_t len;
char prev_char = line[prev_pos.col]; char *line = next_line(it, &len);
char next_char = line[editor->cursor.col]; if (!line) {
free(it->buffer);
free(it);
return;
}
if (len > 0 && line[len - 1] == '\n')
--len;
char prev_char = (prev_pos.col < len) ? line[prev_pos.col] : 0;
char next_char = (this->cursor.col < len) ? line[this->cursor.col] : 0;
bool before_content = false;
if (this->cursor.col > 0) {
before_content = true;
for (uint32_t i = 0; i < this->cursor.col; i++)
if (line[i] != ' ' && line[i] != '\t') {
before_content = false;
break;
}
}
free(it->buffer); free(it->buffer);
free(it); free(it);
if (before_content) {
this->dedent_current_line();
return;
}
bool is_pair = (prev_char == '{' && next_char == '}') || bool is_pair = (prev_char == '{' && next_char == '}') ||
(prev_char == '(' && next_char == ')') || (prev_char == '(' && next_char == ')') ||
(prev_char == '[' && next_char == ']') || (prev_char == '[' && next_char == ']') ||
(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;
@@ -285,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) {
@@ -304,19 +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->method = "textDocument/hover"; auto &hover = message.message;
pending->callback = [](Editor *editor, std::string, json hover) {
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 = "";
@@ -340,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>(
@@ -366,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;
@@ -390,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;
@@ -426,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;
@@ -471,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;
} }
@@ -479,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;
} }
+153 -69
View File
@@ -38,6 +38,18 @@ void IndentationEngine::compute_indent(Editor *n_editor) {
indent = 2; indent = 2;
free(it->buffer); free(it->buffer);
free(it); free(it);
auto x = kLangtoBlockStartsEnd.find(editor->lang.name);
if (x != kLangtoBlockStartsEnd.end())
start_end = &x->second;
x = kLangtoBlockStartsStart.find(editor->lang.name);
if (x != kLangtoBlockStartsStart.end())
start_start = &x->second;
x = kLangtoBlockEndsFull.find(editor->lang.name);
if (x != kLangtoBlockEndsFull.end())
end_full = &x->second;
x = kLangtoBlockEndsStart.find(editor->lang.name);
if (x != kLangtoBlockEndsStart.end())
end_start = &x->second;
} }
uint32_t IndentationEngine::indent_real(char *line, uint32_t len) { uint32_t IndentationEngine::indent_real(char *line, uint32_t len) {
@@ -56,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;
@@ -75,18 +86,16 @@ uint32_t IndentationEngine::indent_expected(uint32_t row) {
if (len == 0) if (len == 0)
continue; continue;
c_indent = indent_real(line, len); c_indent = indent_real(line, len);
auto is_end_set = kLangtoBlockStartsEnd.find(editor->lang.name);
auto is_start_set = kLangtoBlockStartsStart.find(editor->lang.name);
bool is_end = false; bool is_end = false;
if (is_end_set != kLangtoBlockStartsEnd.end()) if (start_end)
for (auto end : is_end_set->second) for (auto end : *start_end)
if (ends_with(line, end)) { if (ends_with(line, end)) {
c_indent++; c_indent++;
is_end = true; is_end = true;
break; break;
} }
if (!is_end && is_start_set != kLangtoBlockStartsStart.end()) if (!is_end && start_start)
for (auto end : is_start_set->second) for (auto end : *start_start)
if (starts_with(line, end)) { if (starts_with(line, end)) {
c_indent++; c_indent++;
break; break;
@@ -99,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;
@@ -112,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;
@@ -125,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;
@@ -143,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;
@@ -155,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;
@@ -174,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;
@@ -188,16 +191,107 @@ 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));
} }
void IndentationEngine::indent_block(uint32_t start_row, uint32_t end_row) {
indent_block(start_row, end_row, +1);
}
void IndentationEngine::dedent_block(uint32_t start_row, uint32_t end_row) {
indent_block(start_row, end_row, -1);
}
void IndentationEngine::indent_block(uint32_t start_row, uint32_t end_row,
int delta) {
if (start_row > end_row)
std::swap(start_row, end_row);
uint32_t start_len, end_len;
uint32_t start_off = line_to_byte(editor->root, start_row, &start_len);
uint32_t end_off = line_to_byte(editor->root, end_row, &end_len);
uint32_t total_len = (end_off - start_off) + end_len;
char *block = read(editor->root, start_off, total_len);
if (!block)
return;
uint32_t cap = total_len + 128;
char *out = (char *)malloc(cap);
uint32_t out_len = 0;
char *p = block;
char *end = block + total_len;
while (p < end) {
char *line_start = p;
while (p < end && *p != '\n')
p++;
uint32_t len = (uint32_t)(p - line_start);
uint32_t ws = 0;
while (ws < len && (line_start[ws] == ' ' || line_start[ws] == '\t'))
ws++;
uint32_t real_indent = indent_real(line_start, len);
int64_t new_indent = (int64_t)real_indent + delta;
if (new_indent < 0)
new_indent = 0;
uint32_t indent_chars = (indent == 1) ? new_indent : new_indent * indent;
uint32_t new_line_len = indent_chars + (len - ws);
if (out_len + new_line_len + 2 >= cap) {
cap = (cap * 2) + new_line_len + 32;
out = (char *)realloc(out, cap);
}
if (indent == 1) {
memset(out + out_len, '\t', indent_chars);
out_len += indent_chars;
} else {
memset(out + out_len, ' ', indent_chars);
out_len += indent_chars;
}
memcpy(out + out_len, line_start + ws, len - ws);
out_len += (len - ws);
if (p < end && *p == '\n') {
out[out_len++] = '\n';
p++;
}
}
free(block);
editor->edit_replace({start_row, 0}, {end_row, end_len}, out, out_len);
free(out);
}
void IndentationEngine::insert_tab(Coord cursor) {
LineIterator *it = begin_l_iter(editor->root, cursor.row);
if (!it)
return;
uint32_t len;
char *line = next_line(it, &len);
if (!line) {
free(it->buffer);
free(it);
return;
}
if (len > 0 && line[len - 1] == '\n')
--len;
uint32_t ws_len = 0;
while (ws_len < len && (line[ws_len] == ' ' || line[ws_len] == '\t'))
ws_len++;
std::string insert;
if (cursor.col <= ws_len) {
if (indent == 1)
insert = "\t";
else
insert.assign(indent - ((cursor.col) % indent), ' ');
} else {
insert = "\t";
}
free(it->buffer);
free(it);
editor->edit_insert(cursor, (char *)insert.c_str(), 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;
@@ -208,29 +302,25 @@ 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) {
auto is_end_full = kLangtoBlockEndsFull.find(editor->lang.name);
auto is_end_start = kLangtoBlockEndsStart.find(editor->lang.name);
bool end_matched = false; bool end_matched = false;
if (is_end_full != kLangtoBlockEndsFull.end()) if (end_full)
for (auto end : is_end_full->second) for (auto end : *end_full)
if (end == trim(line)) { if (end == trim(line)) {
cursor.col = cursor.col = set_indent(
set_indent(cursor.row, (int64_t)indent_expected(cursor.row) - 1); cursor.row, (int64_t)indent_expected(cursor.row) - (int64_t)1);
end_matched = true; end_matched = true;
break; break;
} }
if (!end_matched && is_end_start != kLangtoBlockEndsStart.end()) if (!end_matched && end_start)
for (auto end : is_end_start->second) for (auto end : *end_start)
if (starts_with(trim(line), end)) { if (starts_with(trim(line), end)) {
cursor.col = cursor.col = set_indent(
set_indent(cursor.row, (int64_t)indent_expected(cursor.row) - 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);
@@ -244,41 +334,35 @@ 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));
uint32_t c_indent = indent_real(line, len); int64_t c_indent = indent_real(line, len);
if (!ending.empty()) { if (!ending.empty()) {
bool ending_valid = false; bool ending_valid = false;
bool starting_valid = false; bool starting_valid = false;
auto is_end_full = kLangtoBlockEndsFull.find(editor->lang.name); if (end_full)
auto is_end_start = kLangtoBlockEndsStart.find(editor->lang.name); for (auto end : *end_full)
auto is_end_set = kLangtoBlockStartsEnd.find(editor->lang.name);
auto is_start_set = kLangtoBlockStartsStart.find(editor->lang.name);
if (is_end_full != kLangtoBlockEndsFull.end())
for (auto end : is_end_full->second)
if (ending == end) { if (ending == end) {
ending_valid = true; ending_valid = true;
break; break;
} }
if (!ending_valid && is_end_start != kLangtoBlockEndsStart.end()) if (!ending_valid && end_start)
for (auto end : is_end_start->second) for (auto end : *end_start)
if (starts_with(ending, end)) { if (starts_with(ending, end)) {
ending_valid = true; ending_valid = true;
break; break;
} }
if (is_end_set != kLangtoBlockStartsEnd.end()) if (start_end)
for (auto end : is_end_set->second) for (auto end : *start_end)
if (ends_with(before, end)) { if (ends_with(before, end)) {
c_indent++;
starting_valid = true; starting_valid = true;
break; break;
} }
if (!starting_valid && is_start_set != kLangtoBlockStartsStart.end()) if (!starting_valid && start_start)
for (auto end : is_start_set->second) for (auto end : *start_start)
if (starts_with(before, end)) { if (starts_with(before, end)) {
c_indent++; starting_valid = true;
break; break;
} }
if (ending_valid && starting_valid) if (ending_valid && starting_valid)
@@ -289,36 +373,37 @@ void IndentationEngine::insert_new_line(Coord cursor) {
else if (ending_valid) else if (ending_valid)
c_indent--; c_indent--;
} }
auto is_end_set = kLangtoBlockStartsEnd.find(editor->lang.name);
auto is_start_set = kLangtoBlockStartsStart.find(editor->lang.name);
bool is_end = false; bool is_end = false;
if (is_end_set != kLangtoBlockStartsEnd.end()) if (start_end)
for (auto end : is_end_set->second) for (auto end : *start_end)
if (ends_with(before, end)) { if (ends_with(before, end)) {
c_indent++; c_indent++;
is_end = true; is_end = true;
break; break;
} }
if (!is_end && is_start_set != kLangtoBlockStartsStart.end()) if (!is_end && start_start)
for (auto end : is_start_set->second) for (auto end : *start_start)
if (starts_with(before, end)) { if (starts_with(before, end)) {
c_indent++; c_indent++;
break; break;
} }
if (c_indent < 0)
c_indent = 0;
formatted = "\n" + formatted = "\n" +
(indent == 1 ? std::string(c_indent, '\t') (indent == 1 ? std::string(c_indent, '\t')
: std::string(c_indent * indent, ' ')) + : std::string(c_indent * indent, ' ')) +
ending; ending;
Coord new_cursor = {cursor.row + 1, 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)
@@ -334,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",
@@ -346,13 +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->method = "textDocument/onTypeFormatting";
pending->callback = [version](Editor *editor, std::string, 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());
@@ -363,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};
} }
} }
+134 -163
View File
@@ -3,31 +3,18 @@
#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; 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;
std::unique_lock<std::mutex> lock;
if (editor->parser)
lock = std::unique_lock<std::mutex>(editor->parser->mutex);
LineData *line_data = nullptr; LineData *line_data = nullptr;
auto get_type = [&](uint32_t col) { auto get_type = [&](uint32_t col) {
if (!line_data) if (!line_data)
@@ -37,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;
@@ -78,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')
@@ -126,56 +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 = int len = snprintf(buf, sizeof(buf), "%*u ", numlen, line_index + 1);
snprintf(buf, sizeof(buf), "%*u ", numlen - 3, 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, update(pos.row + rendered_rows, pos.col + i, (char[2]){buf[i], 0},
editor->position.col + i + 2, (char[2]){buf[i], 0}, num_color, num_color, 0, 0, 0, 1);
0, 0);
} else { } else {
for (uint32_t i = 0; i < numlen; 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);
@@ -186,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;
@@ -222,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)
@@ -281,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;
@@ -318,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++;
@@ -338,40 +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 - 3, 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, update(pos.row + rendered_rows, pos.col + i, (char[2]){buf[i], 0},
editor->position.col + i + 2, (char[2]){buf[i], 0}, num_color, 0, num_color, 0, 0, 0, 1);
0); if (this->cursor.row == line_index) {
if (editor->cursor.row == line_index) { cursor.row = pos.row + rendered_rows;
cursor.row = editor->position.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)
@@ -397,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;
@@ -434,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++;
@@ -453,12 +429,9 @@ void render_editor(Editor *editor) {
global_byte_offset += line_len + 1; global_byte_offset += line_len + 1;
line_index++; line_index++;
} }
if (lock.owns_lock()) while (rendered_rows < this->size.row) {
lock.unlock(); for (uint32_t col = 0; col < this->size.col; col++)
while (rendered_rows < editor->size.row) { update(pos.row + rendered_rows, pos.col + col, " ", 0xFFFFFF, 0, 0, 0, 1);
for (uint32_t col = 0; col < editor->size.col; col++)
update(editor->position.row + rendered_rows, editor->position.col + col,
" ", 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) {
@@ -476,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;
} // }
} }
@@ -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());
}
}
}
-205
View File
@@ -1,205 +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->method = "initialize";
pending->editor = nullptr;
pending->callback = [lsp, lsp_id](Editor *, std::string, json msg) {
if (msg.contains("result") && msg["result"].contains("capabilities")) {
auto &caps = msg["result"]["capabilities"];
// if (caps.contains("positionEncoding")) {
// std::string s = caps["positionEncoding"].get<std::string>();
// if (s == "utf-8")
// lsp->is_utf8 = true;
// log("Lsp name: %s, supports: %s", lsp->lsp->command.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_lock active_lsps_lock(active_lsps_mtx);
auto it = active_lsps.find(lsp_id);
if (it == active_lsps.end())
return;
std::shared_ptr<LSPInstance> lsp = it->second;
active_lsps_lock.unlock();
lsp->exited = true;
lsp->initialized = false;
LSPPending *shutdown_pending = new LSPPending();
shutdown_pending->method = "shutdown";
shutdown_pending->callback = [lsp](Editor *, std::string, json) {
json exit = {{"jsonrpc", "2.0"}, {"method", "exit"}};
lsp_send(lsp, exit, nullptr);
};
json shutdown = {{"jsonrpc", "2.0"}, {"method", "shutdown"}};
lsp_send(lsp, shutdown, shutdown_pending);
std::thread t([lsp, lsp_id] {
std::this_thread::sleep_for(100ms);
std::unique_lock active_lsps_lock(active_lsps_mtx);
std::unique_lock lock(lsp->mtx);
if (lsp->pid != -1 && kill(lsp->pid, 0) == 0)
kill(lsp->pid, SIGKILL);
waitpid(lsp->pid, nullptr, 0);
close(lsp->stdin_fd);
close(lsp->stdout_fd);
for (auto &kv : lsp->pending)
delete kv.second;
for (auto &editor : lsp->editors) {
std::unique_lock editor_lock(editor->lsp_mtx);
editor->lsp = nullptr;
}
active_lsps.erase(lsp_id);
});
t.detach();
}
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);
}
static 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, pend->method, *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();
}
}
}
}
+53 -87
View File
@@ -2,73 +2,22 @@
#include "editor/editor.h" #include "editor/editor.h"
#include "io/sysio.h" #include "io/sysio.h"
#include "lsp/lsp.h" #include "lsp/lsp.h"
#include "scripting/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;
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;
}
void input_listener(Bar bar) {
while (running) {
KeyEvent event = throttle(1ms, read_key);
if (event.key_type == KEY_NONE)
goto render;
if (event.key_type == KEY_CHAR && event.len == 1 &&
event.c[0] == CTRL('q')) {
free(event.c);
running = false;
break;
}
if (mode != RUNNER) {
if (event.key_type == KEY_MOUSE) {
Editor *target = editor_at(event.mouse_x, event.mouse_y);
if (target) {
if (event.mouse_state == PRESS)
current_editor = index_of(target);
event.mouse_x -= target->position.col;
event.mouse_y -= target->position.row;
handle_editor_event(target, event);
}
} else {
handle_editor_event(editors[current_editor], event);
}
} else {
bar.handle(event);
}
render:
render_editor(editors[current_editor]);
bar.render();
throttle(4ms, render);
}
} }
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
@@ -80,46 +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 bar(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 input_thread(input_listener, bar);
std::thread lsp_thread(background_lsp); std::thread lsp_thread(background_lsp);
while (running) while (running) {
throttle(16ms, editor_worker, editors[current_editor]); layout::focused_window->work();
render();
throttle(16ms, io_render);
KeyEvent event;
while (running && lsp::response_queue.empty() &&
(event = read_key()).key_type == KEY_NONE)
std::this_thread::sleep_for(1ms);
uint8_t consumed = 0;
do {
if (event.key_type != KEY_NONE) {
if (event.key_type == KEY_CHAR && event.len == 1 &&
event.c[0] == CTRL('q')) {
free(event.c);
running = false;
goto quit;
}
if (mode != RUNNER) {
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);
}
} while (++consumed < 32 && (event = read_key()).key_type != KEY_NONE);
std::unique_ptr<LSPMessage> msg;
while (lsp::response_queue.pop(msg)) {
msg->callback(*msg);
};
}
if (input_thread.joinable()) quit:
input_thread.join();
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);
std::unique_lock lk(active_lsps_mtx);
lk.unlock();
while (true) {
lk.lock();
if (active_lsps.empty())
break;
lk.unlock();
throttle(16ms, lsp_worker);
}
ruby_shutdown(); ruby_shutdown();
return 0; return 0;
+11
View File
@@ -0,0 +1,11 @@
#include "ruby/decl.h"
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());
}
void setup_ruby_bindings(mrb_state *mrb, RClass *C_module) {
mrb_define_module_function(mrb, C_module, "config_file", get_config_file,
MRB_ARGS_NONE());
}
+313 -34
View File
@@ -1,5 +1,8 @@
#include "scripting/decl.h" #include "io/sysio.h"
#include "scripting/ruby_compiled.h" #include "main.h"
#include "pch.h"
#include "ruby/decl.h"
#include "ruby/ruby_compiled.h"
#include "utils/utils.h" #include "utils/utils.h"
std::unordered_map<std::string, std::pair<mrb_value, mrb_value>> std::unordered_map<std::string, std::pair<mrb_value, mrb_value>>
@@ -18,7 +21,6 @@ struct R_ThemeEntry {
struct R_Language { struct R_Language {
std::string name; std::string name;
uint32_t color = 0xFFFFFF; uint32_t color = 0xFFFFFF;
std::string symbol;
std::vector<std::string> extensions; std::vector<std::string> extensions;
std::vector<std::string> filenames; std::vector<std::string> filenames;
std::string lsp_command; // link to LSP by name std::string lsp_command; // link to LSP by name
@@ -26,12 +28,9 @@ struct R_Language {
mrb_state *mrb = nullptr; mrb_state *mrb = nullptr;
RClass *C_module; RClass *C_module;
std::mutex ruby_mutex; fs::path ruby_config_path;
namespace fs = std::filesystem;
void ruby_start() { void ruby_start() {
std::lock_guard lock(ruby_mutex);
mrb = mrb_open(); mrb = mrb_open();
if (!mrb) { if (!mrb) {
fprintf(stderr, "Failed to init mruby\n"); fprintf(stderr, "Failed to init mruby\n");
@@ -39,6 +38,14 @@ void ruby_start() {
} }
fs::path exe_dir = get_exe_dir(); fs::path exe_dir = get_exe_dir();
std::vector<fs::path> candidates; std::vector<fs::path> candidates;
const char *crib_config = std::getenv("CRIB_CONFIG");
if (crib_config)
candidates.emplace_back(fs::path(crib_config));
const char *crib_config_dir = std::getenv("CRIB_CONFIG_DIR");
if (crib_config_dir) {
candidates.emplace_back(fs::path(crib_config_dir) / "crib.rb");
candidates.emplace_back(fs::path(crib_config_dir) / "main.rb");
}
candidates.emplace_back("./crib.rb"); candidates.emplace_back("./crib.rb");
const char *xdg = std::getenv("XDG_CONFIG_HOME"); const char *xdg = std::getenv("XDG_CONFIG_HOME");
const char *home = std::getenv("HOME"); const char *home = std::getenv("HOME");
@@ -46,19 +53,21 @@ void ruby_start() {
candidates.emplace_back(fs::path(xdg) / "crib/crib.rb"); candidates.emplace_back(fs::path(xdg) / "crib/crib.rb");
candidates.emplace_back(fs::path(xdg) / "crib/main.rb"); candidates.emplace_back(fs::path(xdg) / "crib/main.rb");
candidates.emplace_back(fs::path(xdg) / "crib.rb"); candidates.emplace_back(fs::path(xdg) / "crib.rb");
} else if (home) { }
if (home) {
fs::path base = fs::path(home) / ".config"; fs::path base = fs::path(home) / ".config";
candidates.emplace_back(base / "crib/crib.rb"); candidates.emplace_back(base / "crib/crib.rb");
candidates.emplace_back(base / "crib/main.rb"); candidates.emplace_back(base / "crib/main.rb");
candidates.emplace_back(base / "crib.rb"); candidates.emplace_back(base / "crib.rb");
} }
candidates.emplace_back(exe_dir / "../config/main.rb");
candidates.emplace_back(exe_dir / "../config/crib.rb");
mrb_load_irep(mrb, _tmp___crib_precompiled_mrb); mrb_load_irep(mrb, _tmp___crib_precompiled_mrb);
C_module = mrb_module_get(mrb, "C");
setup_ruby_bindings(mrb, C_module);
for (const auto &p : candidates) { for (const auto &p : candidates) {
if (fs::exists(p)) { if (fs::exists(p)) {
FILE *f = fopen(p.string().c_str(), "r"); FILE *f = fopen(p.string().c_str(), "r");
if (f) { if (f) {
ruby_config_path = p;
mrb_load_file(mrb, f); mrb_load_file(mrb, f);
if (mrb->exc) if (mrb->exc)
exit(1); exit(1);
@@ -67,19 +76,280 @@ void ruby_start() {
break; break;
} }
} }
C_module = mrb_module_get(mrb, "C");
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_startup", 0); mrb_value block = mrb_funcall(mrb, mod_val, "b_startup", 0);
mrb_funcall(mrb, block, "call", 0); if (!mrb_nil_p(block))
mrb_funcall(mrb, block, "call", 0);
mrb_garbage_collect(mrb);
}
static mrb_value sym_fg;
static mrb_value sym_bg;
static mrb_value sym_flags;
static mrb_value sym_start;
static mrb_value sym_length;
static mrb_value sym_mode;
static mrb_value sym_data;
static mrb_value sym_foldername;
static mrb_value sym_width;
static mrb_value sym_normal;
static mrb_value sym_insert;
static mrb_value sym_select;
static mrb_value sym_runner;
static mrb_value sym_jumper;
inline void initialize_symbols() {
sym_fg = mrb_symbol_value(mrb_intern_cstr(mrb, "fg"));
sym_bg = mrb_symbol_value(mrb_intern_cstr(mrb, "bg"));
sym_flags = mrb_symbol_value(mrb_intern_cstr(mrb, "flags"));
sym_start = mrb_symbol_value(mrb_intern_cstr(mrb, "start"));
sym_length = mrb_symbol_value(mrb_intern_cstr(mrb, "length"));
sym_mode = mrb_symbol_value(mrb_intern_cstr(mrb, "mode"));
sym_data = mrb_symbol_value(mrb_intern_cstr(mrb, "data"));
sym_foldername = mrb_symbol_value(mrb_intern_cstr(mrb, "foldername"));
sym_width = mrb_symbol_value(mrb_intern_cstr(mrb, "width"));
sym_normal = mrb_symbol_value(mrb_intern_cstr(mrb, "normal"));
sym_insert = mrb_symbol_value(mrb_intern_cstr(mrb, "insert"));
sym_select = mrb_symbol_value(mrb_intern_cstr(mrb, "select"));
sym_runner = mrb_symbol_value(mrb_intern_cstr(mrb, "runner"));
sym_jumper = mrb_symbol_value(mrb_intern_cstr(mrb, "jumper"));
}
inline static std::vector<BarLight>
convert_highlights(mrb_state *mrb, mrb_value highlights_val) {
std::vector<BarLight> result;
if (!mrb_array_p(highlights_val))
return result;
mrb_int len = RARRAY_LEN(highlights_val);
for (mrb_int i = 0; i < len; i++) {
mrb_value item = mrb_ary_ref(mrb, highlights_val, i);
if (!mrb_hash_p(item))
continue;
mrb_value fg_v = mrb_hash_get(mrb, item, sym_fg);
mrb_value bg_v = mrb_hash_get(mrb, item, sym_bg);
mrb_value flags_v = mrb_hash_get(mrb, item, sym_flags);
mrb_value start_v = mrb_hash_get(mrb, item, sym_start);
mrb_value length_v = mrb_hash_get(mrb, item, sym_length);
BarLight bl{};
if (!mrb_nil_p(fg_v))
bl.highlight.fg = (uint32_t)mrb_fixnum(fg_v);
if (!mrb_nil_p(bg_v))
bl.highlight.bg = (uint32_t)mrb_fixnum(bg_v);
if (!mrb_nil_p(flags_v))
bl.highlight.flags = (uint32_t)mrb_fixnum(flags_v);
uint32_t start = !mrb_nil_p(start_v) ? (uint32_t)mrb_fixnum(start_v) : 0;
uint32_t length = !mrb_nil_p(length_v) ? (uint32_t)mrb_fixnum(length_v) : 0;
bl.start = start;
bl.end = start + length;
result.push_back(bl);
}
return result;
}
BarLine bar_contents(uint8_t mode, uint32_t width, std::string foldername,
Window *window) {
BarLine bar_line;
static bool initialed = false;
if (!initialed) {
initialize_symbols();
initialed = true;
}
int ai = mrb_gc_arena_save(mrb);
mrb_value info = mrb_hash_new(mrb);
mrb_value val_mode;
switch (mode) {
case NORMAL:
val_mode = sym_normal;
break;
case INSERT:
val_mode = sym_insert;
break;
case SELECT:
val_mode = sym_select;
break;
case RUNNER:
val_mode = sym_runner;
break;
case JUMPER:
val_mode = sym_jumper;
break;
}
mrb_hash_set(mrb, info, sym_mode, val_mode);
mrb_value val_foldername =
mrb_str_new(mrb, foldername.c_str(), foldername.length());
mrb_hash_set(mrb, info, sym_foldername, val_foldername);
std::array<std::string, 5> arr = window->bar_info();
mrb_value ary = mrb_ary_new(mrb);
mrb_ary_push(mrb, ary, mrb_str_new(mrb, arr[0].c_str(), arr[0].length()));
mrb_ary_push(mrb, ary, mrb_str_new(mrb, arr[1].c_str(), arr[1].length()));
mrb_ary_push(mrb, ary, mrb_str_new(mrb, arr[2].c_str(), arr[2].length()));
mrb_ary_push(mrb, ary, mrb_str_new(mrb, arr[3].c_str(), arr[3].length()));
mrb_ary_push(mrb, ary, mrb_str_new(mrb, arr[4].c_str(), arr[4].length()));
mrb_hash_set(mrb, info, sym_data, ary);
mrb_value val_width = mrb_fixnum_value(width);
mrb_hash_set(mrb, info, sym_width, val_width);
mrb_value mod_val = mrb_obj_value(C_module);
mrb_value block = mrb_funcall(mrb, mod_val, "b_bar", 0);
mrb_value val_line = mrb_funcall(mrb, block, "call", 1, info);
if (mrb_array_p(val_line) && RARRAY_LEN(val_line) == 1) {
val_line = mrb_ary_ref(mrb, val_line, 0);
}
if (!mrb_hash_p(val_line)) {
mrb_value inspected = mrb_funcall(mrb, val_line, "inspect", 0);
printf("bar returned: %s\n", RSTRING_PTR(inspected));
fprintf(stderr, "bar returned: %s\n", mrb_obj_classname(mrb, val_line));
abort();
}
if (mrb->exc) {
end_screen();
fputs("Error when executing Ruby code:\n", stderr);
mrb_print_error(mrb);
mrb_close(mrb);
exit(1);
}
mrb_value text_val = mrb_hash_get(
mrb, val_line, mrb_symbol_value(mrb_intern_cstr(mrb, "text")));
if (!mrb_string_p(text_val)) {
fprintf(stderr, "text is not a string: %s\n",
mrb_obj_classname(mrb, text_val));
abort();
}
const char *ptr = RSTRING_PTR(text_val);
mrb_int len = RSTRING_LEN(text_val);
bar_line.line = std::string(ptr, len);
mrb_value highlights_val = mrb_hash_get(
mrb, val_line, mrb_symbol_value(mrb_intern_cstr(mrb, "highlights")));
bar_line.highlights = convert_highlights(mrb, highlights_val);
mrb_gc_arena_restore(mrb, ai);
return bar_line;
}
std::string serialize_value(mrb_state *mrb, mrb_value val) {
int ai = mrb_gc_arena_save(mrb);
mrb_value marshal_module = mrb_obj_value(mrb_module_get(mrb, "Marshal"));
mrb_value dumped = mrb_funcall(mrb, marshal_module, "dump", 1, val);
if (mrb->exc) {
end_screen();
fputs("Error when executing Ruby code:\n", stderr);
mrb_print_error(mrb);
mrb_close(mrb);
exit(1);
}
std::string bytes(RSTRING_PTR(dumped), RSTRING_LEN(dumped));
mrb_gc_arena_restore(mrb, ai);
return bytes;
}
mrb_value deserialize_value(mrb_state *mrb, std::string bytes) {
if (bytes.empty())
return mrb_nil_value();
mrb_value marshal_module = mrb_obj_value(mrb_module_get(mrb, "Marshal"));
mrb_value val = mrb_funcall(mrb, marshal_module, "load", 1,
mrb_str_new(mrb, bytes.c_str(), bytes.length()));
if (mrb->exc) {
end_screen();
fputs("Error when executing Ruby code:\n", stderr);
mrb_print_error(mrb);
mrb_close(mrb);
exit(1);
}
return val;
}
void ruby_copy(const char *text, size_t len) {
int ai = mrb_gc_arena_save(mrb);
if (C_module == nullptr)
return;
mrb_value mod_val = mrb_obj_value(C_module);
mrb_value block = mrb_funcall(mrb, mod_val, "b_copy", 0);
if (!mrb_nil_p(block))
mrb_funcall(mrb, block, "call", 1, mrb_str_new(mrb, text, len));
if (mrb->exc) {
end_screen();
fputs("Error when executing Ruby code:\n", stderr);
mrb_print_error(mrb);
mrb_close(mrb);
exit(1);
}
mrb_gc_arena_restore(mrb, ai);
}
std::string ruby_file_detect(std::string filename) {
int ai = mrb_gc_arena_save(mrb);
if (C_module == nullptr)
return "";
mrb_value mod_val = mrb_obj_value(C_module);
mrb_value block = mrb_funcall(mrb, mod_val, "b_file_detect", 0);
if (mrb->exc) {
end_screen();
fputs("Error when executing Ruby code:\n", stderr);
mrb_print_error(mrb);
mrb_close(mrb);
exit(1);
}
if (!mrb_nil_p(block)) {
mrb_value val =
mrb_funcall(mrb, block, "call", 1,
mrb_str_new(mrb, filename.c_str(), filename.length()));
if (mrb->exc) {
end_screen();
fputs("Error when executing Ruby code:\n", stderr);
mrb_print_error(mrb);
mrb_close(mrb);
exit(1);
}
mrb_value s_val = mrb_funcall(mrb, val, "to_s", 0);
if (mrb->exc) {
end_screen();
fputs("Error when executing Ruby code:\n", stderr);
mrb_print_error(mrb);
mrb_close(mrb);
exit(1);
}
if (mrb_string_p(s_val)) {
std::string result = std::string(RSTRING_PTR(s_val), RSTRING_LEN(s_val));
mrb_gc_arena_restore(mrb, ai);
return result;
}
}
mrb_gc_arena_restore(mrb, ai);
return "";
}
std::string ruby_paste() {
int ai = mrb_gc_arena_save(mrb);
if (C_module == nullptr)
return "";
mrb_value mod_val = mrb_obj_value(C_module);
mrb_value block = mrb_funcall(mrb, mod_val, "b_paste", 0);
if (!mrb_nil_p(block)) {
mrb_value val = mrb_funcall(mrb, block, "call", 0);
if (mrb->exc) {
end_screen();
fputs("Error when executing Ruby code:\n", stderr);
mrb_print_error(mrb);
mrb_close(mrb);
exit(1);
}
if (mrb_string_p(val)) {
std::string result = std::string(RSTRING_PTR(val), RSTRING_LEN(val));
mrb_gc_arena_restore(mrb, ai);
return result;
}
mrb_gc_arena_restore(mrb, ai);
return "";
}
mrb_gc_arena_restore(mrb, ai);
return "";
} }
void ruby_shutdown() { void ruby_shutdown() {
std::lock_guard lock(ruby_mutex);
if (C_module == nullptr) if (C_module == nullptr)
return; return;
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_shutdown", 0); mrb_value block = mrb_funcall(mrb, mod_val, "b_shutdown", 0);
mrb_funcall(mrb, block, "call", 0); if (!mrb_nil_p(block))
mrb_funcall(mrb, block, "call", 0);
mrb_close(mrb); mrb_close(mrb);
mrb = nullptr; mrb = nullptr;
C_module = nullptr; C_module = nullptr;
@@ -99,7 +369,6 @@ std::vector<std::string> array_to_vector(mrb_value ary) {
} }
void load_custom_highlighters() { void load_custom_highlighters() {
std::lock_guard<std::mutex> lock(ruby_mutex);
if (!C_module) if (!C_module)
return; return;
mrb_value mod_val = mrb_obj_value((struct RObject *)C_module); mrb_value mod_val = mrb_obj_value((struct RObject *)C_module);
@@ -124,27 +393,36 @@ void load_custom_highlighters() {
mrb_hash_get(mrb, val_hash, mrb_symbol_value(matcher_sym)); mrb_hash_get(mrb, val_hash, mrb_symbol_value(matcher_sym));
custom_highlighters[key] = {parse_block, match_block}; custom_highlighters[key] = {parse_block, match_block};
} }
mrb_garbage_collect(mrb);
} }
bool custom_compare(mrb_value match_block, mrb_value state1, mrb_value state2) { bool custom_compare(mrb_value match_block, std::string state1,
std::lock_guard<std::mutex> lock(ruby_mutex); std::string state2) {
if (state1.empty() || state2.empty())
return false;
int ai = mrb_gc_arena_save(mrb);
if (mrb_type(match_block) != MRB_TT_PROC) if (mrb_type(match_block) != MRB_TT_PROC)
return false; return false;
mrb_value ret = mrb_funcall(mrb, match_block, "call", 2, state1, state2); mrb_value ret =
return mrb_test(ret); mrb_funcall(mrb, match_block, "call", 2, deserialize_value(mrb, state1),
deserialize_value(mrb, state2));
bool result = mrb_test(ret);
mrb_gc_arena_restore(mrb, ai);
return result;
} }
mrb_value 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, mrb_value state, const char *line, uint32_t len, std::string state,
uint32_t c_line) { uint32_t c_line) {
std::lock_guard<std::mutex> lock(ruby_mutex); int ai = mrb_gc_arena_save(mrb);
tokens->clear(); tokens->clear();
if (mrb_nil_p(parser_block)) if (mrb_nil_p(parser_block))
return mrb_nil_value(); return "";
mrb_value ruby_line = mrb_str_new(mrb, line, len); mrb_value ruby_line = mrb_str_new(mrb, line, len);
mrb_value line_idx = mrb_fixnum_value(c_line); mrb_value line_idx = mrb_fixnum_value(c_line);
mrb_value tokens_and_state_hash = mrb_value tokens_and_state_hash =
mrb_funcall(mrb, parser_block, "call", 3, ruby_line, state, line_idx); mrb_funcall(mrb, parser_block, "call", 3, ruby_line,
deserialize_value(mrb, state), line_idx);
mrb_sym tokens_sym = mrb_intern_lit(mrb, "tokens"); mrb_sym tokens_sym = mrb_intern_lit(mrb, "tokens");
mrb_value tokens_rb = mrb_value tokens_rb =
mrb_hash_get(mrb, tokens_and_state_hash, mrb_symbol_value(tokens_sym)); mrb_hash_get(mrb, tokens_and_state_hash, mrb_symbol_value(tokens_sym));
@@ -164,11 +442,16 @@ mrb_value parse_custom(std::vector<Token> *tokens, mrb_value parser_block,
} }
} }
mrb_sym state_sym = mrb_intern_lit(mrb, "state"); mrb_sym state_sym = mrb_intern_lit(mrb, "state");
return mrb_hash_get(mrb, tokens_and_state_hash, mrb_symbol_value(state_sym)); mrb_value state_rb =
mrb_hash_get(mrb, tokens_and_state_hash, mrb_symbol_value(state_sym));
std::string result;
if (mrb_type(state_rb) == MRB_TT_STRING)
result = std::string(RSTRING_PTR(state_rb), RSTRING_LEN(state_rb));
mrb_gc_arena_restore(mrb, ai);
return result;
} }
static std::vector<R_ThemeEntry> read_theme() { static std::vector<R_ThemeEntry> read_theme() {
std::lock_guard<std::mutex> lock(ruby_mutex);
std::vector<R_ThemeEntry> result; std::vector<R_ThemeEntry> result;
if (!C_module) if (!C_module)
return result; return result;
@@ -214,6 +497,7 @@ static std::vector<R_ThemeEntry> read_theme() {
entry.strikethrough = mrb_test(strikethrough); entry.strikethrough = mrb_test(strikethrough);
result.push_back(entry); result.push_back(entry);
} }
mrb_garbage_collect(mrb);
return result; return result;
} }
@@ -282,6 +566,7 @@ std::vector<LSP> read_lsps() {
std::vector<std::string> args = array_to_vector(args_array); std::vector<std::string> args = array_to_vector(args_array);
result.push_back({cmd, args}); result.push_back({cmd, args});
} }
mrb_garbage_collect(mrb);
return result; return result;
} }
@@ -307,8 +592,6 @@ std::vector<R_Language> read_languages() {
lang.name = std::string(RSTRING_PTR(key), RSTRING_LEN(key)); lang.name = std::string(RSTRING_PTR(key), RSTRING_LEN(key));
mrb_value fg = mrb_hash_get(mrb, val_hash, mrb_value fg = mrb_hash_get(mrb, val_hash,
mrb_symbol_value(mrb_intern_lit(mrb, "color"))); mrb_symbol_value(mrb_intern_lit(mrb, "color")));
mrb_value symbol = mrb_hash_get(
mrb, val_hash, mrb_symbol_value(mrb_intern_lit(mrb, "symbol")));
mrb_value extensions = mrb_hash_get( mrb_value extensions = mrb_hash_get(
mrb, val_hash, mrb_symbol_value(mrb_intern_lit(mrb, "extensions"))); mrb, val_hash, mrb_symbol_value(mrb_intern_lit(mrb, "extensions")));
mrb_value filenames = mrb_hash_get( mrb_value filenames = mrb_hash_get(
@@ -317,8 +600,6 @@ std::vector<R_Language> read_languages() {
mrb_symbol_value(mrb_intern_lit(mrb, "lsp"))); mrb_symbol_value(mrb_intern_lit(mrb, "lsp")));
if (!mrb_nil_p(fg)) if (!mrb_nil_p(fg))
lang.color = (uint32_t)mrb_fixnum(fg); lang.color = (uint32_t)mrb_fixnum(fg);
if (!mrb_nil_p(symbol))
lang.symbol = std::string(RSTRING_PTR(symbol), RSTRING_LEN(symbol));
lang.extensions = array_to_vector(extensions); lang.extensions = array_to_vector(extensions);
if (!mrb_nil_p(filenames)) if (!mrb_nil_p(filenames))
lang.filenames = array_to_vector(filenames); lang.filenames = array_to_vector(filenames);
@@ -326,11 +607,11 @@ std::vector<R_Language> read_languages() {
lang.lsp_command = std::string(RSTRING_PTR(lsp), RSTRING_LEN(lsp)); lang.lsp_command = std::string(RSTRING_PTR(lsp), RSTRING_LEN(lsp));
result.push_back(lang); result.push_back(lang);
} }
mrb_garbage_collect(mrb);
return result; return result;
} }
void load_languages_info() { void load_languages_info() {
std::lock_guard lock(ruby_mutex);
auto langs = read_languages(); auto langs = read_languages();
auto lsps_t = read_lsps(); auto lsps_t = read_lsps();
languages.clear(); languages.clear();
@@ -339,7 +620,6 @@ void load_languages_info() {
l.name = lang.name; l.name = lang.name;
l.color = lang.color; l.color = lang.color;
l.lsp_name = lang.lsp_command; l.lsp_name = lang.lsp_command;
l.symbol = lang.symbol;
languages[lang.name] = l; languages[lang.name] = l;
for (auto &ext : lang.extensions) for (auto &ext : lang.extensions)
language_extensions[ext] = lang.name; language_extensions[ext] = lang.name;
@@ -352,7 +632,6 @@ void load_languages_info() {
} }
uint8_t read_line_endings() { uint8_t read_line_endings() {
std::lock_guard<std::mutex> lock(ruby_mutex);
if (!C_module) if (!C_module)
return 1; return 1;
mrb_value mod_val = mrb_obj_value((struct RObject *)C_module); mrb_value mod_val = mrb_obj_value((struct RObject *)C_module);
+18 -3
View File
@@ -19,20 +19,35 @@ while read -r line; do
fi fi
done <"$INPUT" done <"$INPUT"
OS="$(uname -s)"
OS_TYPE="unknown"
case "$OS" in
Linux*)
OS_TYPE="linux"
;;
Darwin*)
OS_TYPE="mac"
;;
CYGWIN* | MINGW* | MSYS*)
OS_TYPE="windows"
;;
esac
{ {
echo " freeze" echo " freeze"
echo "end" echo "end"
echo echo
cat "$SCRIPT_DIR/../include/scripting/libcrib.rb" cat "$SCRIPT_DIR/../include/ruby/libcrib.rb" | sed "s/os_name_placed_here/$OS_TYPE/g"
} >>"$TMP" } >>"$TMP"
mrbc -o$OUTPUT $TMP "$SCRIPT_DIR/../libs/mruby/bin/mrbc" -o$OUTPUT $TMP
{ {
echo "#pragma once" echo "#pragma once"
xxd -i $OUTPUT | sed 's/^unsigned char /constexpr unsigned char /' | xxd -i $OUTPUT | sed 's/^unsigned char /constexpr unsigned char /' |
sed 's/^unsigned int /constexpr unsigned int /' sed 's/^unsigned int /constexpr unsigned int /'
} >"$SCRIPT_DIR/../include/scripting/ruby_compiled.h" } >"$SCRIPT_DIR/../include/ruby/ruby_compiled.h"
rm $TMP rm $TMP
rm $OUTPUT rm $OUTPUT
+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] == ' '))
+100 -152
View File
@@ -26,176 +26,124 @@ 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); line_map.apply_edit(0, editor->root->line_count + 1);
} }
void Parser::edit(uint32_t start_line, uint32_t old_end_line, void Parser::edit(uint32_t start_line, uint32_t removed_rows,
uint32_t inserted_rows) { uint32_t inserted_rows) {
std::lock_guard lock(data_mutex); int64_t delta = (int64_t)inserted_rows - (int64_t)removed_rows;
if (((int64_t)old_end_line - (int64_t)start_line) > 0) line_map.apply_edit(start_line, delta);
line_tree.erase(start_line, old_end_line - start_line); uint32_t span = MAX(removed_rows, inserted_rows);
if (inserted_rows > 0) uint32_t begin = (start_line > 0) ? start_line - 1 : 0;
line_tree.insert(start_line, inserted_rows); uint32_t end = start_line + span;
if (start_line > 0) for (uint32_t line = begin; line <= end + 1; ++line)
dirty_lines.push(start_line - 1); if (LineData *ld = line_map.at(line))
dirty_lines.push(start_line); ld->out_state = nullptr;
dirty_lines.push(start_line + 1);
} }
void Parser::work() { void Parser::work() {
if (!editor || !editor->root) if (!editor || !editor->root)
return; return;
std::shared_lock k_lock(editor->knot_mtx); std::vector<uint32_t> batch;
k_lock.unlock(); uint32_t line_count = editor->root->line_count + 1;
uint32_t capacity = 256; uint32_t min_line =
char *text = (char *)calloc((capacity + 1), sizeof(char)); scroll_max > MAX_LINES_LOOKAROUND ? scroll_max - MAX_LINES_LOOKAROUND : 0;
std::unique_lock lock_data(data_mutex); uint32_t max_line = MIN(scroll_max + MAX_LINES_LOOKAROUND, line_count - 1);
lock_data.unlock(); bool sequential = false;
std::unique_lock lock(mutex); for (uint32_t i = min_line; i <= max_line; ++i) {
lock.unlock(); LineData *ld = line_map.at(i);
uint32_t c_line; if ((!ld || !ld->in_state || !ld->out_state) && !sequential) {
while (dirty_lines.pop(c_line)) { batch.push_back(i);
if (!running.load(std::memory_order_relaxed)) { sequential = true;
free(text);
return;
}
if (c_line > scroll_max + 40) {
dirty_lines.push(c_line);
continue; continue;
} }
if (scroll_max > 50 && c_line < scroll_max - 50) { sequential = false;
dirty_lines.push(c_line); }
for (uint32_t c_line : batch) {
if (!running.load(std::memory_order_relaxed))
break;
uint32_t min_line = scroll_max > MAX_LINES_LOOKAROUND
? scroll_max - MAX_LINES_LOOKAROUND
: 0;
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;
std::unique_ptr<StateBase> prev_state = nullptr;
if (c_line > 0 && c_line < line_count) {
auto lm = line_map.at(c_line - 1);
if (lm && lm->out_state)
prev_state = lm->out_state ? lm->out_state->clone() : nullptr;
} }
uint32_t line_count = line_tree.count(); LineIterator *it = begin_l_iter(editor->root, c_line);
lock_data.lock(); if (!it)
std::shared_ptr<void> prev_state = continue;
(c_line > 0) && c_line < line_tree.count() uint32_t cur_line = c_line;
? line_tree.at(c_line - 1)->out_state while (cur_line < line_count) {
: nullptr; if (!running.load(std::memory_order_relaxed))
lock_data.unlock(); break;
while (c_line < line_count) { if (scroll_snapshot != scroll_max)
if (!running.load(std::memory_order_relaxed)) { break;
free(text); if (cur_line < min_line || cur_line > max_line) {
return; LineData *line_data = line_map.at(cur_line);
} if (line_data)
if (scroll_dirty.exchange(false, std::memory_order_acq_rel)) { line_data->out_state = nullptr;
dirty_lines.push(c_line);
c_line = scroll_max < 50 ? 0 : scroll_max - 50;
}
k_lock.lock();
if (c_line > editor->root->line_count) {
k_lock.unlock();
continue;
}
uint32_t r_offset, r_len;
r_offset = line_to_byte(editor->root, c_line, &r_len);
if (r_len > capacity) {
capacity = r_len;
text = (char *)realloc(text, capacity + 1);
memset(text, 0, capacity + 1);
}
read_into(editor->root, r_offset, r_len, text);
k_lock.unlock();
if (c_line < scroll_max &&
((scroll_max > 100 && c_line > scroll_max - 100) || c_line < 100))
lock.lock();
if (line_tree.count() < c_line) {
if (lock.owns_lock())
lock.unlock();
continue;
}
lock_data.lock();
LineData *line_data = line_tree.at(c_line);
if (!line_data) {
lock_data.unlock();
if (lock.owns_lock())
lock.unlock();
continue;
}
std::shared_ptr<void> new_state{nullptr};
if (is_custom) {
mrb_value state = mrb_nil_value();
if (prev_state) {
std::shared_ptr<CustomState> state_ptr =
std::static_pointer_cast<CustomState>(prev_state);
state = state_ptr->state;
}
mrb_value out_state = parse_custom(&line_data->tokens, parser_block,
text, r_len, state, c_line);
std::shared_ptr<CustomState> out_state_ptr =
std::make_shared<CustomState>(out_state);
new_state = out_state_ptr;
} else {
new_state =
parse_func(&line_data->tokens, prev_state, text, r_len, c_line);
}
line_data->in_state = prev_state;
line_data->out_state = new_state;
if (!running.load(std::memory_order_relaxed)) {
free(text);
return;
}
prev_state = new_state;
c_line++;
if (c_line < line_count && c_line > scroll_max + 50 && scroll_max < 50 &&
c_line < scroll_max + 50) {
lock_data.unlock();
if (lock.owns_lock())
lock.unlock();
if (c_line > 0)
dirty_lines.push(c_line - 1);
dirty_lines.push(c_line);
break; break;
} }
if (c_line < line_count && (line_data = line_tree.at(c_line))) { uint32_t len;
bool done = false; char *line = next_line(it, &len);
if (is_custom) { if (!line)
mrb_value in_state_v = mrb_nil_value(); break;
if (prev_state) LineData *line_data = line_map.create_at(cur_line);
in_state_v = std::unique_ptr<StateBase> new_state;
std::static_pointer_cast<CustomState>(prev_state)->state; if (is_custom) {
mrb_value out_state_v = mrb_nil_value(); std::string prev_value = "";
if (line_data->in_state) if (prev_state) {
out_state_v = CustomState *prev_custom =
std::static_pointer_cast<CustomState>(line_data->in_state) static_cast<CustomState *>(prev_state.get());
->state; prev_value = prev_custom->value;
done = custom_compare(match_block, in_state_v, out_state_v);
} else {
done = state_match_func(prev_state, line_data->in_state);
} }
if (done) { std::string out_value = parse_custom(&line_data->tokens, parser_block,
lock_data.unlock(); line, len, prev_value, cur_line);
if (lock.owns_lock()) new_state = std::make_unique<CustomState>(out_value);
lock.unlock(); } else {
break; new_state = parse_func(&line_data->tokens, prev_state.get(), line, len,
cur_line);
}
line_data->in_state = std::move(prev_state);
line_data->out_state = std::move(new_state);
bool done = false;
if (cur_line + 1 < line_count) {
LineData *next_line_data = line_map.at(cur_line + 1);
if (next_line_data) {
if (is_custom) {
std::string a = "";
if (new_state) {
CustomState *cs = static_cast<CustomState *>(new_state.get());
a = cs->value;
}
std::string b = "";
if (next_line_data->in_state) {
CustomState *cs =
static_cast<CustomState *>(next_line_data->in_state.get());
b = cs->value;
}
done = custom_compare(match_block, a, b);
} else {
done = state_match_func(new_state.get(),
next_line_data->in_state.get());
}
} }
} }
lock_data.unlock(); prev_state =
if (lock.owns_lock()) line_data->out_state ? line_data->out_state->clone() : nullptr;
lock.unlock(); cur_line++;
} if (done)
if (!running.load(std::memory_order_relaxed)) { break;
free(text);
return;
} }
free(it->buffer);
free(it);
} }
free(text);
lock_data.lock();
} }
void Parser::scroll(uint32_t line) { void Parser::scroll(uint32_t line) { scroll_max = line; }
if (line != scroll_max) {
scroll_max = line;
uint32_t c_line = line > 50 ? line - 50 : 0;
if (c_line >= line_tree.count())
return;
std::unique_lock lock_data(data_mutex);
if (line_tree.at(c_line)->in_state || line_tree.at(c_line)->out_state)
return;
scroll_dirty = true;
dirty_lines.push(c_line);
} else {
scroll_max = line;
}
}
+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]);
+71 -59
View File
@@ -1,78 +1,90 @@
#include "ui/bar.h" #include "ui/bar.h"
#include "io/sysio.h" #include "io/sysio.h"
#include "lsp/lsp.h"
#include "main.h" #include "main.h"
#include "syntax/decl.h"
#include "windows/decl.h"
void Bar::render() { void Bar::log(std::string message) { log_line = strip_newlines(message); }
Editor *editor = editors[current_editor];
void Bar::render(std::vector<ScreenCell> &buffer) {
USING(LSPInstance);
BarLine bar_line;
bar_line =
bar_contents(mode, screen.col, pwd.string(), layout::focused_window);
auto update = [&](uint32_t row, uint32_t col, std::string text, uint32_t fg,
uint32_t bg, uint8_t flags, uint32_t width) {
ScreenCell &c = buffer[row * screen.col + col];
c.utf8 = text;
c.width = width;
c.fg = fg;
c.bg = bg;
c.flags = flags;
c.ul_color = 0;
};
uint32_t row = screen.row - 2; uint32_t row = screen.row - 2;
uint32_t col = 0;
uint32_t width = screen.col; uint32_t width = screen.col;
UNUSED(width); std::string &line = bar_line.line;
uint32_t color = 0; uint32_t i = 0;
uint32_t black = 0x0b0e14; uint32_t col = 0;
uint32_t grey = 0x33363c; while (i < line.length()) {
uint32_t dark_grey = 0x24272d; uint32_t cluster_len =
uint32_t name_color = 0xced4df; grapheme_next_character_break_utf8(line.c_str() + i, line.length() - i);
uint32_t lang_color = editor->lang.color; std::string cluster = line.substr(i, cluster_len);
const char *symbol = "󱓧 "; int width = display_width(cluster.c_str(), cluster_len);
const char *name = "EDITOR"; Highlight highlight = bar_line.get_highlight(col);
switch (mode) { update(row, col, cluster.c_str(), highlight.fg, highlight.bg,
case NORMAL: highlight.flags, width);
color = 0x82AAFF; col += width;
symbol = ""; i += cluster_len;
name = "NORMAL"; for (int w = 1; w < width; w++)
break; update(row, col - w, "\x1b", highlight.fg, highlight.bg, highlight.flags,
case INSERT: 0);
color = 0xFF8F40;
symbol = "󱓧 ";
name = "INSERT";
break;
case SELECT:
color = 0x9ADE7A;
symbol = "󱩧 ";
name = "SELECT";
break;
case RUNNER:
color = 0xFFD700;
symbol = "";
name = "RUNNER";
break;
case JUMPER:
color = 0xF29CC3;
symbol = "";
name = "JUMPER";
break;
} }
update(row, col, " ", black, color, CF_BOLD); while (col < width)
update(row, ++col, symbol, black, color, CF_BOLD); update(row, col++, " ", 0, 0, 0, 1);
update(row, ++col, "\x1b", black, color, CF_BOLD); col = 0;
update(row, ++col, " ", black, color, CF_BOLD); row++;
for (uint32_t i = 0; i < 6; i++) if (mode == RUNNER) {
update(row, ++col, {name[i], 0}, black, color, CF_BOLD); update(row, col++, ":", 0xFFFFFF, 0, 0, 1);
update(row, ++col, " ", black, color, CF_BOLD); for (char c : command)
update(row, ++col, "", color, grey, CF_BOLD); update(row, col++, (char[2]){c, 0}, 0xFFFFFF, 0, 0, 1);
update(row, ++col, "", grey, dark_grey, CF_BOLD); } else {
update(row, ++col, " ", name_color, dark_grey, CF_BOLD); for (char c : log_line)
update(row, ++col, editor->lang.symbol, lang_color, dark_grey, 0); update(row, col++, (char[2]){c, 0}, 0xFFFFFF, 0, 0, 1);
update(row, ++col, "\x1b", lang_color, dark_grey, 0); }
update(row, ++col, " ", name_color, dark_grey, CF_BOLD); while (col < width)
std::string filename = filename_from_path(editor->filename); update(row, col++, " ", 0, 0, 0, 1);
for (uint32_t i = 0; i < filename.length(); i++)
update(row, ++col, {filename[i], 0}, name_color, dark_grey, CF_BOLD);
update(row, ++col, " ", name_color, dark_grey, CF_BOLD);
update(row, ++col, "", dark_grey, 1, CF_BOLD);
} }
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 = "";
mode = NORMAL; mode = NORMAL;
} else if (event.c[0] == '\n' || event.c[0] == '\r') { } else if (event.c[0] == '\n' || event.c[0] == '\r') {
// execute command while stripping starting `[:;]` command = trim(command);
handle_command(command);
mode = NORMAL; mode = NORMAL;
command = ""; command = "";
} else if (isprint((unsigned char)(event.c[0]))) { } else if (isprint((unsigned char)(event.c[0]))) {
} else if (event.c[0] == 0x7F || event.c[0] == 0x08) { // backspace command += event.c[0];
} else if (event.c[0] == 0x7F || event.c[0] == 0x08) {
if (command.length() > 0) {
command = command.substr(0, command.length() - 1);
} else {
mode = NORMAL;
command = "";
}
} }
} else if (event.key_type == KEY_SPECIAL) { } else if (event.key_type == KEY_SPECIAL) {
switch (event.special_key) { switch (event.special_key) {
+33 -21
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"
@@ -71,7 +71,9 @@ void CompletionBox::render_update() {
uint32_t max_label_len = 0; uint32_t max_label_len = 0;
uint32_t max_detail_len = 0; uint32_t max_detail_len = 0;
uint32_t max_kind_len = 0; uint32_t max_kind_len = 0;
for (auto i : session->visible) { for (uint32_t x = session->scroll;
x < session->scroll + 8 && x < session->visible.size(); x++) {
uint32_t i = session->visible[x];
if (i >= session->items.size()) if (i >= session->items.size())
continue; continue;
auto &item = session->items[i]; auto &item = session->items[i];
@@ -81,10 +83,12 @@ void CompletionBox::render_update() {
max_kind_len = max_kind_len =
MAX(max_kind_len, (uint32_t)item_kind_name(item.kind).size()); MAX(max_kind_len, (uint32_t)item_kind_name(item.kind).size());
} }
size.row = session->visible.size() + 2; uint32_t total = session->visible.size();
size.col = 2 + 2 + max_label_len + 1 + max_detail_len + 2 + max_kind_len + 1; uint32_t rows = MIN(total, 8);
cells.assign(size.row * size.col, {" ", 0, 0, 0, 0, 0}); size.row = rows + 2;
auto set = [&](uint32_t r, uint32_t c, const char *text, uint32_t fg, size.col = 2 + 2 + max_label_len + 1 + max_detail_len + 2 + max_kind_len +
1; cells.assign(size.row * size.col, {" ", 0, 0, 0, 0, 0}); auto set =
[&](uint32_t r, uint32_t c, const char *text, uint32_t fg,
uint32_t bg, uint8_t flags) { 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};
@@ -95,17 +99,16 @@ void CompletionBox::render_update() {
for (uint32_t c = 1; c < size.col - 1; c++) for (uint32_t c = 1; c < size.col - 1; c++)
set(0, c, "─", border_fg, 0, 0); set(0, c, "─", border_fg, 0, 0);
set(0, size.col - 1, "┐", border_fg, 0, 0); set(0, size.col - 1, "┐", border_fg, 0, 0);
for (uint32_t row_idx = 0; row_idx < session->visible.size(); row_idx++) { uint32_t start = session->scroll;
uint32_t r = row_idx + 1; uint32_t end = MIN(start + 8, session->visible.size());
for (uint32_t row_idx = start; row_idx < end; row_idx++) {
uint32_t r = (row_idx - start) + 1;
auto &item = session->items[session->visible[row_idx]]; 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();
@@ -122,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;
@@ -130,6 +134,13 @@ void CompletionBox::render_update() {
for (uint32_t c = 1; c < size.col - 1; c++) for (uint32_t c = 1; c < size.col - 1; c++)
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) {
std::string info = std::to_string(start + 1) + "-" + std::to_string(end)
+
"/" + std::to_string(session->visible.size());
for (size_t i = 0; i < info.size() && i < size.col - 2; i++)
set(bottom, 1 + i, (char[2]){info[i], 0}, border_fg, 0, 0);
}
} }
void CompletionBox::render(Coord pos) { void CompletionBox::render(Coord pos) {
@@ -140,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;
} }
@@ -168,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});
} }
} }
} }
} }
*/
+8 -50
View File
@@ -1,3 +1,4 @@
#include "ruby/decl.h"
#include "utils/utils.h" #include "utils/utils.h"
std::unordered_map<std::string, Language> languages; std::unordered_map<std::string, Language> languages;
@@ -58,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)
@@ -107,60 +109,16 @@ static std::string file_extension(const char *filename) {
Language language_for_file(const char *filename) { Language language_for_file(const char *filename) {
std::string ext = file_extension(filename); std::string ext = file_extension(filename);
std::string lang_name;
if (!ext.empty()) { if (!ext.empty()) {
auto it = language_extensions.find(ext); auto it = language_extensions.find(ext);
if (it != language_extensions.end()) if (it != language_extensions.end())
return languages.find(it->second)->second; return languages.find(it->second)->second;
} }
std::string lang_name = ruby_file_detect(filename);
if (!lang_name.empty()) {
auto it = languages.find(lang_name);
if (it != languages.end())
return it->second;
}
return Language{}; return Language{};
} }
char *get_from_clipboard(uint32_t *out_len) {
FILE *pipe = popen("xclip -selection clipboard -o", "r");
if (!pipe) {
*out_len = 0;
return nullptr;
}
size_t capacity = 4096;
size_t length = 0;
char *buffer = (char *)malloc(capacity);
if (!buffer) {
pclose(pipe);
*out_len = 0;
return nullptr;
}
size_t n;
while ((n = fread(buffer + length, 1, capacity - length, pipe)) > 0) {
length += n;
if (length == capacity) {
capacity *= 2;
char *tmp = (char *)realloc(buffer, capacity);
if (!tmp) {
free(buffer);
pclose(pipe);
*out_len = 0;
return nullptr;
}
buffer = tmp;
}
}
pclose(pipe);
char *result = (char *)realloc(buffer, length + 1);
if (result) {
result[length] = '\0';
buffer = result;
} else {
buffer[length] = '\0';
}
*out_len = length;
return buffer;
}
void copy_to_clipboard(const char *text, size_t len) {
FILE *pipe = popen("xclip -selection clipboard", "w");
if (!pipe)
return;
fwrite(text, sizeof(char), len, pipe);
pclose(pipe);
}
+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);
}