diff --git a/.clangd b/.clangd index 1d1d6c3..c60fe0f 100644 --- a/.clangd +++ b/.clangd @@ -3,3 +3,5 @@ CompileFlags: Remove: [] Compiler: clang++ +HeaderInsertion: + Policy: Never diff --git a/.gitmodules b/.gitmodules index 0ea80f4..36e6c60 100644 --- a/.gitmodules +++ b/.gitmodules @@ -2,139 +2,3 @@ path = libs/libgrapheme url = git://git.suckless.org/libgrapheme ignore = dirty - -; tree-sitter -[submodule "libs/tree-sitter"] - path = libs/tree-sitter - url = https://github.com/tree-sitter/tree-sitter.git - ignore = dirty - -; Tree-sitter languages -[submodule "libs/tree-sitter-ruby"] - path = libs/tree-sitter-ruby - url = https://github.com/tree-sitter/tree-sitter-ruby.git - ignore = dirty -[submodule "libs/tree-sitter-cpp"] - path = libs/tree-sitter-cpp - url = https://github.com/tree-sitter/tree-sitter-cpp.git - ignore = dirty -[submodule "libs/tree-sitter-css"] - path = libs/tree-sitter-css - url = https://github.com/tree-sitter/tree-sitter-css.git - ignore = dirty -[submodule "libs/tree-sitter-html"] - path = libs/tree-sitter-html - url = https://github.com/tree-sitter/tree-sitter-html.git - ignore = dirty -[submodule "libs/tree-sitter-javascript"] - path = libs/tree-sitter-javascript - url = https://github.com/tree-sitter/tree-sitter-javascript.git - ignore = dirty -[submodule "libs/tree-sitter-json"] - path = libs/tree-sitter-json - url = https://github.com/tree-sitter/tree-sitter-json.git - ignore = dirty -[submodule "libs/tree-sitter-python"] - path = libs/tree-sitter-python - url = https://github.com/tree-sitter/tree-sitter-python.git - ignore = dirty -[submodule "libs/tree-sitter-haskell"] - path = libs/tree-sitter-haskell - url = https://github.com/tree-sitter/tree-sitter-haskell.git - ignore = dirty -[submodule "libs/tree-sitter-go"] - path = libs/tree-sitter-go - url = https://github.com/tree-sitter/tree-sitter-go.git - ignore = dirty -[submodule "libs/tree-sitter-bash"] - path = libs/tree-sitter-bash - url = https://github.com/tree-sitter/tree-sitter-bash.git - ignore = dirty -[submodule "libs/tree-sitter-make"] - path = libs/tree-sitter-make - url = https://github.com/tree-sitter-grammars/tree-sitter-make - ignore = dirty -[submodule "libs/tree-sitter-lua"] - path = libs/tree-sitter-lua - url = https://github.com/tree-sitter-grammars/tree-sitter-lua - ignore = dirty -[submodule "libs/tree-sitter-fish"] - path = libs/tree-sitter-fish - url = https://github.com/ram02z/tree-sitter-fish - ignore = dirty -[submodule "libs/tree-sitter-rust"] - path = libs/tree-sitter-rust - url = https://github.com/tree-sitter/tree-sitter-rust.git - ignore = dirty -[submodule "libs/tree-sitter-nginx"] - path = libs/tree-sitter-nginx - url = https://gitlab.com/joncoole/tree-sitter-nginx - ignore = dirty -[submodule "libs/tree-sitter-yaml"] - path = libs/tree-sitter-yaml - url = https://github.com/tree-sitter-grammars/tree-sitter-yaml.git - ignore = dirty -[submodule "libs/tree-sitter-gdscript"] - path = libs/tree-sitter-gdscript - url = https://github.com/PrestonKnopp/tree-sitter-gdscript - ignore = dirty -[submodule "libs/tree-sitter-ini"] - path = libs/tree-sitter-ini - url = https://github.com/justinmk/tree-sitter-ini - ignore = dirty -[submodule "libs/tree-sitter-php"] - path = libs/tree-sitter-php - url = https://github.com/tree-sitter/tree-sitter-php - ignore = dirty -[submodule "libs/tree-sitter-query"] - path = libs/tree-sitter-query - url = https://github.com/tree-sitter-grammars/tree-sitter-query - ignore = dirty -[submodule "libs/tree-sitter-go-mod"] - path = libs/tree-sitter-go-mod - url = https://github.com/camdencheek/tree-sitter-go-mod - ignore = dirty -[submodule "libs/tree-sitter-gitattributes"] - path = libs/tree-sitter-gitattributes - url = https://github.com/tree-sitter-grammars/tree-sitter-gitattributes - ignore = dirty -[submodule "libs/tree-sitter-gitignore"] - path = libs/tree-sitter-gitignore - url = https://github.com/shunsambongi/tree-sitter-gitignore - ignore = dirty -[submodule "libs/tree-sitter-diff"] - path = libs/tree-sitter-diff - url = https://github.com/tree-sitter-grammars/tree-sitter-diff - ignore = dirty -[submodule "libs/tree-sitter-regex"] - path = libs/tree-sitter-regex - url = https://github.com/tree-sitter/tree-sitter-regex - ignore = dirty -[submodule "libs/tree-sitter-embedded-template"] - path = libs/tree-sitter-embedded-template - url = https://github.com/tree-sitter/tree-sitter-embedded-template - ignore = dirty -[submodule "libs/tree-sitter-sql"] - path = libs/tree-sitter-sql - url = https://github.com/DerekStride/tree-sitter-sql.git - ignore = dirty -[submodule "libs/libs/tree-sitter-yaml"] - path = libs/libs/tree-sitter-yaml - url = https://github.com/tree-sitter-grammars/tree-sitter-yaml.git - ignore = dirty -[submodule "libs/tree-sitter-toml"] - path = libs/tree-sitter-toml - url = https://github.com/tree-sitter-grammars/tree-sitter-toml.git - ignore = dirty -[submodule "libs/tree-sitter-markdown"] - path = libs/tree-sitter-markdown - url = https://github.com/tree-sitter-grammars/tree-sitter-markdown.git - ignore = dirty -[submodule "libs/tree-sitter-typescript"] - path = libs/tree-sitter-typescript - url = https://github.com/tree-sitter/tree-sitter-typescript.git - ignore = dirty -[submodule "libs/tree-sitter-man"] - path = libs/tree-sitter-man - url = https://github.com/ribru17/tree-sitter-man.git - ignore = dirty diff --git a/Makefile b/Makefile index 17e0836..2dd0152 100644 --- a/Makefile +++ b/Makefile @@ -10,8 +10,7 @@ PCH_DEBUG := $(OBJ_DIR)/debug/pch.h.gch PCH_RELEASE := $(OBJ_DIR)/release/pch.h.gch CCACHE := ccache -CXX_DEBUG := $(CCACHE) g++ -CXX_RELEASE := $(CCACHE) clang++ +CXX := $(CCACHE) clang++ CFLAGS_DEBUG :=\ -std=c++20 -Wall -Wextra \ @@ -38,44 +37,8 @@ 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_RELEASE := $(patsubst libs/unicode_width/%.c,$(OBJ_DIR)/release/unicode_width/%.o,$(UNICODE_SRC)) -TREE_SITTER_LIBS := $(wildcard libs/tree-sitter-*/libtree-sitter*.a) - -PHP_LIB := libs/tree-sitter-php/php/libtree-sitter-php.a - -TSX_LIB := libs/tree-sitter-typescript/tsx/libtree-sitter-tsx.a - -NGINX_OBJ_PARSER := libs/tree-sitter-nginx/build/Release/obj.target/tree_sitter_nginx_binding/src/parser.o - -GITIGNORE_OBJ_PARSER := libs/tree-sitter-gitignore/build/Release/obj.target/tree_sitter_ignore_binding/src/parser.o - -FISH_OBJ_PARSER := libs/tree-sitter-fish/build/Release/obj.target/tree_sitter_fish_binding/src/parser.o -FISH_OBJ_SCANNER := libs/tree-sitter-fish/build/Release/obj.target/tree_sitter_fish_binding/src/scanner.o - -MAN_OBJ_PARSER := libs/tree-sitter-man/build/Release/obj.target/tree_sitter_man_binding/src/parser.o -MAN_OBJ_SCANNER := libs/tree-sitter-man/build/Release/obj.target/tree_sitter_man_binding/src/scanner.o - -MD_OBJ_PARSER := libs/tree-sitter-markdown/build/Release/obj.target/tree_sitter_markdown_binding/tree-sitter-markdown/src/parser.o -MD_OBJ_SCANNER := libs/tree-sitter-markdown/build/Release/obj.target/tree_sitter_markdown_binding/tree-sitter-markdown/src/scanner.o - -MD_I_OBJ_PARSER := libs/tree-sitter-markdown/build/Release/obj.target/tree_sitter_markdown_binding/tree-sitter-markdown-inline/src/parser.o -MD_I_OBJ_SCANNER := libs/tree-sitter-markdown/build/Release/obj.target/tree_sitter_markdown_binding/tree-sitter-markdown-inline/src/scanner.o - LIBS := \ libs/libgrapheme/libgrapheme.a \ - libs/tree-sitter/libtree-sitter.a \ - $(TREE_SITTER_LIBS) \ - $(PHP_LIB) \ - $(TSX_LIB) \ - $(NGINX_OBJ_PARSER) \ - $(GITIGNORE_OBJ_PARSER) \ - $(FISH_OBJ_PARSER) \ - $(FISH_OBJ_SCANNER) \ - $(MAN_OBJ_PARSER) \ - $(MAN_OBJ_SCANNER) \ - $(MD_OBJ_PARSER) \ - $(MD_OBJ_SCANNER) \ - $(MD_I_OBJ_PARSER) \ - $(MD_I_OBJ_SCANNER) \ -lpcre2-8 -lmagic SRC := $(wildcard $(SRC_DIR)/**/*.cc) $(wildcard $(SRC_DIR)/*.cc) @@ -95,35 +58,35 @@ release: $(TARGET_RELEASE) $(PCH_DEBUG): $(INCLUDE_DIR)/pch.h mkdir -p $(dir $@) - $(CXX_DEBUG) $(PCH_CFLAGS_DEBUG) -o $@ $< + $(CXX) $(PCH_CFLAGS_DEBUG) -o $@ $< $(PCH_RELEASE): $(INCLUDE_DIR)/pch.h mkdir -p $(dir $@) - $(CXX_RELEASE) $(PCH_CFLAGS_RELEASE) -o $@ $< + $(CXX) $(PCH_CFLAGS_RELEASE) -o $@ $< $(TARGET_DEBUG): $(PCH_DEBUG) $(OBJ_DEBUG) $(UNICODE_OBJ_DEBUG) mkdir -p $(BIN_DIR) - $(CXX_DEBUG) $(CFLAGS_DEBUG) -o $@ $(OBJ_DEBUG) $(UNICODE_OBJ_DEBUG) $(LIBS) + $(CXX) $(CFLAGS_DEBUG) -o $@ $(OBJ_DEBUG) $(UNICODE_OBJ_DEBUG) $(LIBS) $(TARGET_RELEASE): $(PCH_RELEASE) $(OBJ_RELEASE) $(UNICODE_OBJ_RELEASE) mkdir -p $(BIN_DIR) - $(CXX_RELEASE) $(CFLAGS_RELEASE) -o $@ $(OBJ_RELEASE) $(UNICODE_OBJ_RELEASE) $(LIBS) + $(CXX) $(CFLAGS_RELEASE) -o $@ $(OBJ_RELEASE) $(UNICODE_OBJ_RELEASE) $(LIBS) $(OBJ_DIR)/debug/%.o: $(SRC_DIR)/%.cc $(PCH_DEBUG) mkdir -p $(dir $@) - $(CXX_DEBUG) $(CFLAGS_DEBUG) -include $(INCLUDE_DIR)/pch.h -MMD -MP -c $< -o $@ + $(CXX) $(CFLAGS_DEBUG) -include $(INCLUDE_DIR)/pch.h -MMD -MP -c $< -o $@ $(OBJ_DIR)/release/%.o: $(SRC_DIR)/%.cc $(PCH_RELEASE) mkdir -p $(dir $@) - $(CXX_RELEASE) $(CFLAGS_RELEASE) -include $(INCLUDE_DIR)/pch.h -MMD -MP -c $< -o $@ + $(CXX) $(CFLAGS_RELEASE) -include $(INCLUDE_DIR)/pch.h -MMD -MP -c $< -o $@ $(OBJ_DIR)/debug/unicode_width/%.o: libs/unicode_width/%.c mkdir -p $(dir $@) - $(CXX_DEBUG) $(CFLAGS_DEBUG) -MMD -MP -c $< -o $@ + $(CXX) $(CFLAGS_DEBUG) -MMD -MP -c $< -o $@ $(OBJ_DIR)/release/unicode_width/%.o: libs/unicode_width/%.c mkdir -p $(dir $@) - $(CXX_RELEASE) $(CFLAGS_RELEASE) -MMD -MP -c $< -o $@ + $(CXX) $(CFLAGS_RELEASE) -MMD -MP -c $< -o $@ DEP_DEBUG += $(UNICODE_OBJ_DEBUG:.o=.d) DEP_RELEASE += $(UNICODE_OBJ_RELEASE:.o=.d) diff --git a/TODO.md b/TODO.md index 6b57162..acd0c2b 100644 --- a/TODO.md +++ b/TODO.md @@ -31,6 +31,10 @@ Copyright 2025 Syed Daanish * [ ] Do trextmate like regex grammar parsing with lsp symbols for semantic highlighting. * Probably remove tre--sitter or just keep it for context tree. * Making bracket matching andignoring strings/comments easier. +* remove tree-sitter mention from everywhere especially submodules +* make it faster for line inserts/deletes too (treeify the vector) +* Try to make all functions better now that folds have been purged +* Cleanup syntax and renderer files ### Core Editing Mechanics diff --git a/include/config.h b/include/config.h index b5c2458..a864be0 100644 --- a/include/config.h +++ b/include/config.h @@ -3,7 +3,6 @@ #include "lsp/lsp.h" #include "pch.h" -#include "ts/decl.h" static const std::unordered_map kLsps = { {1, @@ -168,43 +167,41 @@ static const std::unordered_map kLsps = { }; static const std::unordered_map kLanguages = { - {"bash", {"bash", LANG(bash), 4, 0x4d5a5e, " "}}, - {"c", {"c", LANG(cpp), 1, 0x555555, " "}}, - {"cpp", {"cpp", LANG(cpp), 1, 0x00599C, " "}}, - {"h", {"h", LANG(cpp), 1, 0xA8B9CC, " "}}, - {"css", {"css", LANG(css), 5, 0x36a3d9, " "}}, - {"fish", {"fish", LANG(fish), 7, 0x4d5a5e, " "}}, - {"go", {"go", LANG(go), 8, 0x00add8, " "}}, - {"gomod", {"gomod", LANG(gomod), 8, 0x00add8, " "}}, - {"haskell", {"haskell", LANG(haskell), 9, 0xa074c4, " "}}, - {"html", {"html", LANG(html), 10, 0xef8a91, " "}}, - {"javascript", {"javascript", LANG(javascript), 11, 0xf0df8a, " "}}, - {"typescript", {"typescript", LANG(tsx), 11, 0x36a3d9, " "}}, - {"json", {"json", LANG(json), 6, 0xcbcb41, "{}"}}, - {"jsonc", {"jsonc", LANG(json), 6, 0xcbcb41, "{}"}}, - {"erb", {"erb", LANG(embedded_template), 10, 0x6e1516, " "}}, - {"ruby", {"ruby", LANG(ruby), 3, 0xff8087, "󰴭 "}}, - {"lua", {"lua", LANG(lua), 12, 0x36a3d9, "󰢱 "}}, - {"python", {"python", LANG(python), 13, 0x95e6cb, "󰌠 "}}, - {"rust", {"rust", LANG(rust), 14, 0xdea584, "󱘗 "}}, - {"php", {"php", LANG(php), 15, 0xa074c4, "󰌟 "}}, - {"markdown", {"markdown", LANG(markdown), 16, 0x36a3d9, " "}}, - {"markdown_inline", - {"markdown_inline", LANG(markdown_inline), 16, 0x36a3d9, " "}}, - {"nginx", {"nginx", LANG(nginx), 17, 0x6d8086, " "}}, - {"toml", {"toml", LANG(toml), 18, 0x36a3d9, " "}}, - {"yaml", {"yaml", LANG(yaml), 19, 0x6d8086, " "}}, - {"sql", {"sql", LANG(sql), 20, 0xdad8d8, " "}}, - {"make", {"make", LANG(make), 21, 0x4e5c61, " "}}, - {"gdscript", {"gdscript", LANG(gdscript), 0, 0x6d8086, " "}}, - {"man", {"man", LANG(man), 0, 0xdad8d8, " "}}, - {"diff", {"diff", LANG(diff), 0, 0xDD4C35, " "}}, - {"gitattributes", - {"gitattributes", LANG(gitattributes), 0, 0xF05032, " "}}, - {"gitignore", {"gitignore", LANG(gitignore), 0, 0xF05032, " "}}, - {"query", {"query", LANG(query), 0, 0x7E57C2, " "}}, - {"regex", {"regex", LANG(regex), 0, 0x9E9E9E, ".*"}}, - {"ini", {"ini", LANG(ini), 0, 0x6d8086, " "}}, + {"bash", {"bash", 4, 0x4d5a5e, " "}}, + {"c", {"c", 1, 0x555555, " "}}, + {"cpp", {"cpp", 1, 0x00599C, " "}}, + {"h", {"h", 1, 0xA8B9CC, " "}}, + {"css", {"css", 5, 0x36a3d9, " "}}, + {"fish", {"fish", 7, 0x4d5a5e, " "}}, + {"go", {"go", 8, 0x00add8, " "}}, + {"gomod", {"gomod", 8, 0x00add8, " "}}, + {"haskell", {"haskell", 9, 0xa074c4, " "}}, + {"html", {"html", 10, 0xef8a91, " "}}, + {"javascript", {"javascript", 11, 0xf0df8a, " "}}, + {"typescript", {"typescript", 11, 0x36a3d9, " "}}, + {"json", {"json", 6, 0xcbcb41, "{}"}}, + {"jsonc", {"jsonc", 6, 0xcbcb41, "{}"}}, + {"erb", {"erb", 10, 0x6e1516, " "}}, + {"ruby", {"ruby", 3, 0xff8087, "󰴭 "}}, + {"lua", {"lua", 12, 0x36a3d9, "󰢱 "}}, + {"python", {"python", 13, 0x95e6cb, "󰌠 "}}, + {"rust", {"rust", 14, 0xdea584, "󱘗 "}}, + {"php", {"php", 15, 0xa074c4, "󰌟 "}}, + {"markdown", {"markdown", 16, 0x36a3d9, " "}}, + {"markdown_inline", {"markdown_inline", 16, 0x36a3d9, " "}}, + {"nginx", {"nginx", 17, 0x6d8086, " "}}, + {"toml", {"toml", 18, 0x36a3d9, " "}}, + {"yaml", {"yaml", 19, 0x6d8086, " "}}, + {"sql", {"sql", 20, 0xdad8d8, " "}}, + {"make", {"make", 21, 0x4e5c61, " "}}, + {"gdscript", {"gdscript", 0, 0x6d8086, " "}}, + {"man", {"man", 0, 0xdad8d8, " "}}, + {"diff", {"diff", 0, 0xDD4C35, " "}}, + {"gitattributes", {"gitattributes", 0, 0xF05032, " "}}, + {"gitignore", {"gitignore", 0, 0xF05032, " "}}, + {"query", {"query", 0, 0x7E57C2, " "}}, + {"regex", {"regex", 0, 0x9E9E9E, ".*"}}, + {"ini", {"ini", 0, 0x6d8086, " "}}, }; static const std::unordered_map kExtToLang = { diff --git a/include/editor/completions.h b/include/editor/completions.h index 31540a5..c5157d2 100644 --- a/include/editor/completions.h +++ b/include/editor/completions.h @@ -1,7 +1,6 @@ #ifndef EDITOR_COMPLETIONS_H #define EDITOR_COMPLETIONS_H -#include "editor/decl.h" #include "pch.h" #include "ui/completionbox.h" #include "ui/hover.h" diff --git a/include/editor/decl.h b/include/editor/decl.h index 6830f37..bfb9eeb 100644 --- a/include/editor/decl.h +++ b/include/editor/decl.h @@ -9,22 +9,6 @@ struct TextEdit { std::string text; }; -struct Fold { - uint32_t start; - uint32_t end; - - bool contains(uint32_t line) const { return line >= start && line <= end; } - bool operator<(const Fold &other) const { return start < other.start; } -}; - -struct Span { - uint32_t start; - uint32_t end; - Highlight *hl; - - bool operator<(const Span &other) const { return start < other.start; } -}; - struct VWarn { uint32_t line; std::string text; diff --git a/include/editor/editor.h b/include/editor/editor.h index 1784298..05536e1 100644 --- a/include/editor/editor.h +++ b/include/editor/editor.h @@ -3,10 +3,9 @@ #include "editor/completions.h" #include "editor/indents.h" -#include "editor/spans.h" #include "io/knot.h" #include "io/sysio.h" -#include "ts/decl.h" +#include "syntax/parser.h" #include "ui/completionbox.h" #include "ui/diagnostics.h" #include "ui/hover.h" @@ -35,12 +34,6 @@ struct Editor { Coord size; Coord scroll; Language lang; - TSSetMain ts; - Queue edit_queue; - std::vector folds; - Spans spans; - Spans word_spans; - Spans hex_color_spans; uint32_t hooks[94]; bool jumper_set; std::shared_mutex v_mtx; @@ -56,19 +49,17 @@ struct Editor { std::atomic lsp_version = 1; CompletionSession completion; IndentationEngine indents; + Parser *parser; }; Editor *new_editor(const char *filename_arg, Coord position, Coord size); void save_file(Editor *editor); void free_editor(Editor *editor); void render_editor(Editor *editor); -void fold(Editor *editor, uint32_t start_line, uint32_t end_line); 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); -Coord move_left_pure(Editor *editor, Coord cursor, uint32_t number); -Coord move_right_pure(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); @@ -90,9 +81,6 @@ void word_boundaries(Editor *editor, Coord coord, uint32_t *prev_col, uint32_t *next_clusters); void word_boundaries_exclusive(Editor *editor, Coord coord, uint32_t *prev_col, uint32_t *next_col); -std::vector::iterator find_fold_iter(Editor *editor, uint32_t line); -bool add_fold(Editor *editor, uint32_t start, uint32_t end); -bool remove_fold(Editor *editor, uint32_t line); void editor_lsp_handle(Editor *editor, json msg); void apply_lsp_edits(Editor *editor, std::vector edits, bool move); void completion_resolve_doc(Editor *editor); diff --git a/include/editor/folds.h b/include/editor/folds.h deleted file mode 100644 index cc08739..0000000 --- a/include/editor/folds.h +++ /dev/null @@ -1,150 +0,0 @@ -#ifndef EDITOR_FOLDS_H -#define EDITOR_FOLDS_H - -#include "editor/editor.h" - -inline std::vector::iterator find_fold_iter(Editor *editor, - uint32_t line) { - auto &folds = editor->folds; - auto it = std::lower_bound( - folds.begin(), folds.end(), line, - [](const Fold &fold, uint32_t value) { return fold.start < value; }); - if (it != folds.end() && it->start == line) - return it; - if (it != folds.begin()) { - --it; - if (it->contains(line)) - return it; - } - return folds.end(); -} - -inline bool add_fold(Editor *editor, uint32_t start, uint32_t end) { - if (!editor || !editor->root) - return false; - if (start > end) - std::swap(start, end); - if (start >= editor->root->line_count) - return false; - end = std::min(end, editor->root->line_count - 1); - if (start == end) - return false; - Fold new_fold{start, end}; - auto &folds = editor->folds; - auto it = std::lower_bound( - folds.begin(), folds.end(), new_fold.start, - [](const Fold &fold, uint32_t value) { return fold.start < value; }); - if (it != folds.begin()) { - auto prev = std::prev(it); - if (prev->end + 1 >= new_fold.start) { - new_fold.start = std::min(new_fold.start, prev->start); - new_fold.end = std::max(new_fold.end, prev->end); - it = folds.erase(prev); - } - } - while (it != folds.end() && it->start <= new_fold.end + 1) { - new_fold.end = std::max(new_fold.end, it->end); - it = folds.erase(it); - } - folds.insert(it, new_fold); - return true; -} - -inline bool remove_fold(Editor *editor, uint32_t line) { - auto it = find_fold_iter(editor, line); - if (it == editor->folds.end()) - return false; - editor->folds.erase(it); - return true; -} - -inline void apply_line_insertion(Editor *editor, uint32_t line, uint32_t rows) { - for (auto it = editor->folds.begin(); it != editor->folds.end();) { - if (line <= it->start) { - it->start += rows; - it->end += rows; - ++it; - } else if (line <= it->end) { - it = editor->folds.erase(it); - } else { - ++it; - } - } -} - -inline void apply_line_deletion(Editor *editor, uint32_t removal_start, - uint32_t removal_end) { - if (removal_start > removal_end) - return; - uint32_t rows_removed = removal_end - removal_start + 1; - std::vector updated; - updated.reserve(editor->folds.size()); - for (auto fold : editor->folds) { - if (removal_end < fold.start) { - fold.start -= rows_removed; - fold.end -= rows_removed; - updated.push_back(fold); - continue; - } - if (removal_start > fold.end) { - updated.push_back(fold); - continue; - } - } - editor->folds.swap(updated); -} - -inline const Fold *fold_for_line(const std::vector &folds, - uint32_t line) { - auto it = std::lower_bound( - folds.begin(), folds.end(), line, - [](const Fold &fold, uint32_t value) { return fold.start < value; }); - if (it != folds.end() && it->start == line) - return &(*it); - if (it != folds.begin()) { - --it; - if (it->contains(line)) - return &(*it); - } - return nullptr; -} - -inline Fold *fold_for_line(std::vector &folds, uint32_t line) { - const auto *fold = - fold_for_line(static_cast &>(folds), line); - return const_cast(fold); -} - -inline bool line_is_fold_start(const std::vector &folds, uint32_t line) { - const Fold *fold = fold_for_line(folds, line); - return fold && fold->start == line; -} - -inline bool line_is_folded(const std::vector &folds, uint32_t line) { - return fold_for_line(folds, line) != nullptr; -} - -inline uint32_t next_unfolded_row(const Editor *editor, uint32_t row) { - uint32_t limit = editor && editor->root ? editor->root->line_count : 0; - while (row < limit) { - const Fold *fold = fold_for_line(editor->folds, row); - if (!fold) - return row; - row = fold->end + 1; - } - return limit; -} - -inline uint32_t prev_unfolded_row(const Editor *editor, uint32_t row) { - while (row > 0) { - const Fold *fold = fold_for_line(editor->folds, row); - if (!fold) - return row; - if (fold->start == 0) - return 0; - row = fold->start - 1; - } - return 0; -} - -#endif diff --git a/include/editor/spans.h b/include/editor/spans.h deleted file mode 100644 index a620ee6..0000000 --- a/include/editor/spans.h +++ /dev/null @@ -1,85 +0,0 @@ -#ifndef EDITOR_SPANS_H -#define EDITOR_SPANS_H - -#include "editor/decl.h" -#include "utils/utils.h" - -struct Spans { - std::vector spans; - Queue> edits; - std::atomic mid_parse = false; - std::shared_mutex mtx; -}; - -struct SpanCursor { - Spans &spans; - size_t index = 0; - std::vector active; - std::shared_lock lock; - - SpanCursor(Spans &s) : spans(s) {} - Highlight *get_highlight(uint32_t byte_offset) { - for (int i = (int)active.size() - 1; i >= 0; i--) - if (active[i]->end <= byte_offset) - active.erase(active.begin() + i); - while (index < spans.spans.size() && - spans.spans[index].start <= byte_offset) { - if (spans.spans[index].end > byte_offset) - active.push_back(const_cast(&spans.spans[index])); - index++; - } - Highlight *best = nullptr; - int max_prio = -1; - for (auto *s : active) - if (s->hl->priority > max_prio) { - max_prio = s->hl->priority; - best = s->hl; - } - return best; - } - void sync(uint32_t byte_offset) { - lock = std::shared_lock(spans.mtx); - active.clear(); - size_t left = 0, right = spans.spans.size(); - while (left < right) { - size_t mid = (left + right) / 2; - if (spans.spans[mid].start <= byte_offset) - left = mid + 1; - else - right = mid; - } - index = left; - while (left > 0) { - left--; - if (spans.spans[left].end > byte_offset) - active.push_back(const_cast(&spans.spans[left])); - else if (byte_offset - spans.spans[left].end > 1000) - break; - } - } -}; - -inline void apply_edit(std::vector &spans, uint32_t x, int64_t y) { - Span key{.start = x, .end = 0, .hl = nullptr}; - auto it = std::lower_bound( - spans.begin(), spans.end(), key, - [](const Span &a, const Span &b) { return a.start < b.start; }); - size_t idx = std::distance(spans.begin(), it); - while (idx > 0 && spans.at(idx - 1).end >= x) - --idx; - for (size_t i = idx; i < spans.size();) { - Span &s = spans.at(i); - if (s.start < x && s.end >= x) { - s.end += y; - } else if (s.start > x) { - s.start += y; - s.end += y; - } - if (s.end <= s.start) - spans.erase(spans.begin() + i); - else - ++i; - } -} - -#endif diff --git a/include/io/knot.h b/include/io/knot.h index 2659a12..1083ee8 100644 --- a/include/io/knot.h +++ b/include/io/knot.h @@ -91,6 +91,9 @@ Knot *erase(Knot *node, uint32_t offset, uint32_t len); // returns a null terminated string, should be freed by the caller char *read(Knot *root, uint32_t offset, uint32_t len); +// Used to read into an existing buffer +void read_into(Knot *node, uint32_t offset, uint32_t len, char *dest); + // Used to split the rope into left and right ropes // node is the rope to be split (it is no longer valid after call / do not free) // offset is the position of the split relative to the start of the rope @@ -111,9 +114,9 @@ LineIterator *begin_l_iter(Knot *root, uint32_t start_line); // Each subsequent call returns the next line as a null terminated string // `it` is the iterator returned from begin_l_iter -// 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 `must` be -// freed by the caller +// 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 +// `must` be freed by the caller char *next_line(LineIterator *it, uint32_t *out_len); // Returns the previous line as a null terminated string diff --git a/include/io/sysio.h b/include/io/sysio.h index e31ec3b..44b1e37 100644 --- a/include/io/sysio.h +++ b/include/io/sysio.h @@ -92,11 +92,6 @@ struct KeyEvent { uint8_t mouse_modifier; }; -extern uint32_t rows, cols; -extern std::vector screen; -extern std::vector old_screen; -extern std::mutex screen_mutex; - inline bool is_empty_cell(const ScreenCell &c) { return c.utf8.empty() || c.utf8 == " " || c.utf8 == "\x1b"; } diff --git a/include/pch.h b/include/pch.h index c1b3684..0c10f19 100644 --- a/include/pch.h +++ b/include/pch.h @@ -11,7 +11,6 @@ extern "C" { #include "libgrapheme/grapheme.h" #include "unicode_width/unicode_width.h" } -#include "tree-sitter/lib/include/tree_sitter/api.h" #include #include #include @@ -32,8 +31,10 @@ extern "C" { #include #include #include +#include #include #include +#include #include #include #include diff --git a/include/syntax/decl.h b/include/syntax/decl.h new file mode 100644 index 0000000..b3f28aa --- /dev/null +++ b/include/syntax/decl.h @@ -0,0 +1,97 @@ +#ifndef SYNTAX_DECL_H +#define SYNTAX_DECL_H + +#include "io/knot.h" +#include "io/sysio.h" + +struct Trie { + struct TrieNode { + bool is_word = false; + std::array children{}; + TrieNode() { children.fill(nullptr); } + }; + + Trie() : root(new TrieNode()) {} + ~Trie() { clear_trie(root); } + + void build(const std::vector &words) { + for (const auto &word : words) { + TrieNode *node = root; + for (char c : word) { + unsigned char uc = static_cast(c); + if (!node->children[uc]) + node->children[uc] = new TrieNode(); + node = node->children[uc]; + } + node->is_word = true; + } + } + + uint32_t match(const char *text, uint32_t pos, uint32_t len, + bool (*is_word_char)(char c)) const { + const TrieNode *node = root; + uint32_t max_len = 0; + for (uint32_t i = pos; i < len; ++i) { + unsigned char uc = static_cast(text[i]); + if (uc >= 128) + return 0; + if (!node->children[uc]) { + if (node->is_word && !is_word_char(text[i])) + return i - pos; + break; + } + node = node->children[uc]; + if (node->is_word) + max_len = i - pos + 1; + } + if (max_len > 0) + if (pos + max_len < len && is_word_char(text[pos + max_len])) + return 0; + return max_len; + } + + void clear() { + clear_trie(root); + root = new TrieNode(); + } + +private: + TrieNode *root; + + void clear_trie(TrieNode *node) { + if (!node) + return; + for (auto *child : node->children) + clear_trie(child); + delete node; + } +}; + +struct Highlight { + uint32_t fg; + uint32_t bg; + uint8_t flags; +}; + +inline static const std::unordered_map highlight_map = { + {0, {0xFFFFFF, 0, 0}}, {1, {0xAAAAAA, 0, CF_ITALIC}}, + {2, {0xAAD94C, 0, 0}}, {3, {0xFFFFFF, 0, CF_ITALIC}}, + {4, {0xFF8F40, 0, 0}}, {5, {0xFFB454, 0, 0}}, + {6, {0xD2A6FF, 0, 0}}, {7, {0x95E6CB, 0, 0}}, + {8, {0xF07178, 0, 0}}, {9, {0xE6C08A, 0, 0}}, + {10, {0x7dcfff, 0, 0}}, +}; + +struct Token { + uint32_t start; + uint32_t end; + uint8_t type; +}; + +struct LineData { + std::shared_ptr in_state{nullptr}; + std::vector tokens; + std::shared_ptr out_state{nullptr}; +}; + +#endif diff --git a/include/syntax/langs.h b/include/syntax/langs.h new file mode 100644 index 0000000..3ce5519 --- /dev/null +++ b/include/syntax/langs.h @@ -0,0 +1,28 @@ +#ifndef SYNTAX_LANGS_H +#define SYNTAX_LANGS_H + +#include "syntax/decl.h" + +#define DEF_LANG(name) \ + std::shared_ptr name##_parse(std::vector *tokens, \ + std::shared_ptr in_state, \ + const char *text, uint32_t len); \ + bool name##_state_match(std::shared_ptr state_1, \ + std::shared_ptr state_2); + +#define LANG_A(name) {name##_parse, name##_state_match} + +DEF_LANG(ruby); + +inline static const std::unordered_map< + std::string, + std::tuple (*)(std::vector *tokens, + std::shared_ptr in_state, + const char *text, uint32_t len), + bool (*)(std::shared_ptr state_1, + std::shared_ptr state_2)>> + parsers = { + {"ruby", LANG_A(ruby)}, +}; + +#endif diff --git a/include/syntax/line_tree.h b/include/syntax/line_tree.h new file mode 100644 index 0000000..f9076ff --- /dev/null +++ b/include/syntax/line_tree.h @@ -0,0 +1,212 @@ +// #include "syntax/decl.h" +// +// struct LineTree { +// void clear() { +// clear_node(root); +// root = nullptr; +// stack_size = 0; +// } +// void build(uint32_t x) { root = build_node(x); } +// LineData *at(uint32_t x) { +// LineNode *n = root; +// while (n) { +// uint32_t left_size = n->left ? n->left->size : 0; +// if (x < left_size) { +// n = n->left; +// } else if (x < left_size + n->data.size()) { +// return &n->data[x - left_size]; +// } else { +// x -= left_size + n->data.size(); +// n = n->right; +// } +// } +// return nullptr; +// } +// LineData *start_iter(uint32_t x) { +// stack_size = 0; +// LineNode *n = root; +// while (n) { +// uint32_t left_size = n->left ? n->left->size : 0; +// if (x < left_size) { +// push(n, 0); +// n = n->left; +// } else if (x < left_size + n->data.size()) { +// push(n, x - left_size + 1); +// return &n->data[x - left_size]; +// } else { +// x -= left_size + n->data.size(); +// push(n, UINT32_MAX); +// n = n->right; +// } +// } +// return nullptr; +// } +// void end_iter() { stack_size = 0; } +// LineData *next() { +// while (stack_size) { +// auto &f = stack[stack_size - 1]; +// LineNode *n = f.node; +// if (f.index < n->data.size()) +// return &n->data[f.index++]; +// stack_size--; +// if (n->right) { +// n = n->right; +// while (n) { +// push(n, 0); +// if (!n->left) +// break; +// n = n->left; +// } +// return &stack[stack_size - 1].node->data[0]; +// } +// } +// return nullptr; +// } +// void insert(uint32_t x, uint32_t y) { root = insert_node(root, x, y); } +// void erase(uint32_t x, uint32_t y) { root = erase_node(root, x, y); } +// uint32_t count() { return subtree_size(root); } +// ~LineTree() { clear(); } +// +// private: +// struct LineNode { +// LineNode *left = nullptr; +// LineNode *right = nullptr; +// uint8_t depth = 1; +// uint32_t size = 0; +// std::vector 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]; +// uint8_t stack_size = 0; +// static constexpr uint32_t LEAF_TARGET = 256; +// LineTree::LineNode *erase_node(LineNode *n, uint32_t x, uint32_t y) { +// if (!n) +// return nullptr; +// if (!n->left && !n->right) { +// n->data.erase(n->data.begin() + x, n->data.begin() + x + y); +// fix(n); +// return n; +// } +// uint32_t left_size = subtree_size(n->left); +// if (x < left_size) +// n->left = erase_node(n->left, x, y); +// else +// n->right = erase_node(n->right, x - left_size - 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; } +// }; diff --git a/include/syntax/parser.h b/include/syntax/parser.h new file mode 100644 index 0000000..bf5db3b --- /dev/null +++ b/include/syntax/parser.h @@ -0,0 +1,33 @@ +#include "syntax/decl.h" + +struct Parser { + Knot *root; + std::shared_mutex *knot_mutex; + std::string lang; + std::shared_ptr (*parse_func)(std::vector *tokens, + std::shared_ptr in_state, + const char *text, uint32_t len); + bool (*state_match_func)(std::shared_ptr state_1, + std::shared_ptr state_2); + std::atomic scroll_max{UINT32_MAX - 2048}; + std::mutex mutex; + std::mutex data_mutex; + std::vector line_data; + std::set dirty_lines; + + Parser(Knot *n_root, std::shared_mutex *n_knot_mutex, std::string n_lang, + uint32_t n_scroll_max); + void edit(Knot *n_root, uint32_t start_line, uint32_t old_end_line, + uint32_t new_end_line); + void work(); + void scroll(uint32_t line); + uint8_t get_type(Coord c) { + if (c.row >= line_data.size()) + return 0; + const LineData &line = line_data[c.row]; + for (const Token &t : line.tokens) + if (t.start <= c.col && c.col < t.end) + return t.type; + return 0; + } +}; diff --git a/include/ts/decl.h b/include/ts/decl.h deleted file mode 100644 index a797186..0000000 --- a/include/ts/decl.h +++ /dev/null @@ -1,76 +0,0 @@ -#ifndef TS_DECL_H -#define TS_DECL_H - -#include "pch.h" - -#define LANG(name) tree_sitter_##name -#define TS_DEF(name) extern "C" const TSLanguage *LANG(name)() - -struct Language { - std::string name = "unknown"; - const TSLanguage *(*fn)() = nullptr; - uint8_t lsp_id = 0; - uint32_t color = 0xFFFFFF; - const char *symbol = " "; -}; - -struct Highlight { - uint32_t fg; - uint32_t bg; - uint32_t flags; - uint8_t priority; -}; - -struct TSSetBase { - std::string lang; - TSParser *parser; - std::string query_file; - TSQuery *query; - TSTree *tree; - std::map query_map; - std::map injection_map; - const TSLanguage *language; -}; - -struct TSSet : TSSetBase { - std::vector ranges; -}; - -struct TSSetMain : TSSetBase { - std::unordered_map injections; -}; - -TS_DEF(ruby); -TS_DEF(bash); -TS_DEF(cpp); -TS_DEF(css); -TS_DEF(fish); -TS_DEF(go); -TS_DEF(haskell); -TS_DEF(html); -TS_DEF(javascript); -TS_DEF(tsx); -TS_DEF(man); -TS_DEF(json); -TS_DEF(lua); -TS_DEF(regex); -TS_DEF(query); -TS_DEF(markdown); -TS_DEF(markdown_inline); -TS_DEF(embedded_template); -TS_DEF(php); -TS_DEF(python); -TS_DEF(rust); -TS_DEF(sql); -TS_DEF(gitattributes); -TS_DEF(gitignore); -TS_DEF(gomod); -TS_DEF(nginx); -TS_DEF(toml); -TS_DEF(yaml); -TS_DEF(ini); -TS_DEF(diff); -TS_DEF(make); -TS_DEF(gdscript); - -#endif diff --git a/include/ts/ts.h b/include/ts/ts.h deleted file mode 100644 index 9bfcf35..0000000 --- a/include/ts/ts.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef TS_H -#define TS_H - -#include "editor/editor.h" -#include "pch.h" -#include "utils/utils.h" - -#define HEX(s) (static_cast(std::stoul(s, nullptr, 16))) - -extern std::unordered_map regex_cache; - -TSQuery *load_query(const char *query_path, TSSetBase *set); -void ts_collect_spans(Editor *editor); -bool ts_predicate( - TSQuery *query, const TSQueryMatch &match, - std::function - subject_fn); -void clear_regex_cache(); - -#endif diff --git a/include/ui/hover.h b/include/ui/hover.h index 0b54cd7..3255281 100644 --- a/include/ui/hover.h +++ b/include/ui/hover.h @@ -4,7 +4,6 @@ #include "editor/decl.h" #include "io/sysio.h" #include "pch.h" -#include "ts/decl.h" #include "utils/utils.h" struct HoverBox { @@ -13,8 +12,6 @@ struct HoverBox { uint32_t scroll_; std::vector cells; Coord size; - std::vector highlights; - std::vector hover_spans; void clear(); void scroll(int32_t number); diff --git a/include/utils/utils.h b/include/utils/utils.h index 6d60a31..9105be9 100644 --- a/include/utils/utils.h +++ b/include/utils/utils.h @@ -2,7 +2,6 @@ #define UTILS_H #include "pch.h" -#include "ts/decl.h" template struct Queue { std::queue q; @@ -59,6 +58,13 @@ struct Match { std::string text; }; +struct Language { + std::string name; + uint8_t lsp_id; + uint32_t color; + const char *symbol; +}; + #define MAX(a, b) ((a) > (b) ? (a) : (b)) #define MIN(a, b) ((a) < (b) ? (a) : (b)) #define ABS(x) ((x) < 0 ? -(x) : (x)) @@ -80,6 +86,7 @@ uint32_t get_bytes_from_visual_col(const char *line, uint32_t len, uint32_t target_visual_col); uint32_t utf8_byte_offset_to_utf16(const char *s, uint32_t byte_pos); uint32_t utf16_offset_to_utf8(const char *s, uint32_t utf16_pos); +uint8_t utf8_codepoint_width(unsigned char c); void log(const char *fmt, ...); diff --git a/libs/tree-sitter b/libs/tree-sitter deleted file mode 160000 index 0ca8fe8..0000000 --- a/libs/tree-sitter +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 0ca8fe8c12dde40008305e7925cac410952f0752 diff --git a/libs/tree-sitter-bash b/libs/tree-sitter-bash deleted file mode 160000 index a06c2e4..0000000 --- a/libs/tree-sitter-bash +++ /dev/null @@ -1 +0,0 @@ -Subproject commit a06c2e4415e9bc0346c6b86d401879ffb44058f7 diff --git a/libs/tree-sitter-cpp b/libs/tree-sitter-cpp deleted file mode 160000 index 12bd6f7..0000000 --- a/libs/tree-sitter-cpp +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 12bd6f7e96080d2e70ec51d4068f2f66120dde35 diff --git a/libs/tree-sitter-css b/libs/tree-sitter-css deleted file mode 160000 index dda5cfc..0000000 --- a/libs/tree-sitter-css +++ /dev/null @@ -1 +0,0 @@ -Subproject commit dda5cfc5722c429eaba1c910ca32c2c0c5bb1a3f diff --git a/libs/tree-sitter-diff b/libs/tree-sitter-diff deleted file mode 160000 index 2520c3f..0000000 --- a/libs/tree-sitter-diff +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 2520c3f934b3179bb540d23e0ef45f75304b5fed diff --git a/libs/tree-sitter-embedded-template b/libs/tree-sitter-embedded-template deleted file mode 160000 index 3499d85..0000000 --- a/libs/tree-sitter-embedded-template +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 3499d85f0a0d937c507a4a65368f2f63772786e1 diff --git a/libs/tree-sitter-fish b/libs/tree-sitter-fish deleted file mode 160000 index aa074a0..0000000 --- a/libs/tree-sitter-fish +++ /dev/null @@ -1 +0,0 @@ -Subproject commit aa074a0bacde8b5823c592574d7138f156a95776 diff --git a/libs/tree-sitter-gdscript b/libs/tree-sitter-gdscript deleted file mode 160000 index 89e66b6..0000000 --- a/libs/tree-sitter-gdscript +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 89e66b6bdc002ab976283f277cbb48b780c5d0e9 diff --git a/libs/tree-sitter-gitattributes b/libs/tree-sitter-gitattributes deleted file mode 160000 index 1b7af09..0000000 --- a/libs/tree-sitter-gitattributes +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 1b7af09d45b579f9f288453b95ad555f1f431645 diff --git a/libs/tree-sitter-gitignore b/libs/tree-sitter-gitignore deleted file mode 160000 index f4685bf..0000000 --- a/libs/tree-sitter-gitignore +++ /dev/null @@ -1 +0,0 @@ -Subproject commit f4685bf11ac466dd278449bcfe5fd014e94aa504 diff --git a/libs/tree-sitter-go b/libs/tree-sitter-go deleted file mode 160000 index 2346a3a..0000000 --- a/libs/tree-sitter-go +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 2346a3ab1bb3857b48b29d779a1ef9799a248cd7 diff --git a/libs/tree-sitter-go-mod b/libs/tree-sitter-go-mod deleted file mode 160000 index 2e88687..0000000 --- a/libs/tree-sitter-go-mod +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 2e886870578eeba1927a2dc4bd2e2b3f598c5f9a diff --git a/libs/tree-sitter-haskell b/libs/tree-sitter-haskell deleted file mode 160000 index 0975ef7..0000000 --- a/libs/tree-sitter-haskell +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 0975ef72fc3c47b530309ca93937d7d143523628 diff --git a/libs/tree-sitter-html b/libs/tree-sitter-html deleted file mode 160000 index 73a3947..0000000 --- a/libs/tree-sitter-html +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 73a3947324f6efddf9e17c0ea58d454843590cc0 diff --git a/libs/tree-sitter-ini b/libs/tree-sitter-ini deleted file mode 160000 index e4018b5..0000000 --- a/libs/tree-sitter-ini +++ /dev/null @@ -1 +0,0 @@ -Subproject commit e4018b5176132b4f3c5d6e61cea383f42288d0f5 diff --git a/libs/tree-sitter-javascript b/libs/tree-sitter-javascript deleted file mode 160000 index 58404d8..0000000 --- a/libs/tree-sitter-javascript +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 58404d8cf191d69f2674a8fd507bd5776f46cb11 diff --git a/libs/tree-sitter-json b/libs/tree-sitter-json deleted file mode 160000 index 001c28d..0000000 --- a/libs/tree-sitter-json +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 001c28d7a29832b06b0e831ec77845553c89b56d diff --git a/libs/tree-sitter-lua b/libs/tree-sitter-lua deleted file mode 160000 index d760230..0000000 --- a/libs/tree-sitter-lua +++ /dev/null @@ -1 +0,0 @@ -Subproject commit d76023017f7485eae629cb60d406c7a1ca0f40c9 diff --git a/libs/tree-sitter-make b/libs/tree-sitter-make deleted file mode 160000 index 5e9e8f8..0000000 --- a/libs/tree-sitter-make +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 5e9e8f8ff3387b0edcaa90f46ddf3629f4cfeb1d diff --git a/libs/tree-sitter-man b/libs/tree-sitter-man deleted file mode 160000 index e332ea9..0000000 --- a/libs/tree-sitter-man +++ /dev/null @@ -1 +0,0 @@ -Subproject commit e332ea95d5c921d1108a46d1b2b0f017079e1fd8 diff --git a/libs/tree-sitter-markdown b/libs/tree-sitter-markdown deleted file mode 160000 index 2dfd57f..0000000 --- a/libs/tree-sitter-markdown +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 2dfd57f547f06ca5631a80f601e129d73fc8e9f0 diff --git a/libs/tree-sitter-nginx b/libs/tree-sitter-nginx deleted file mode 160000 index f6d13cf..0000000 --- a/libs/tree-sitter-nginx +++ /dev/null @@ -1 +0,0 @@ -Subproject commit f6d13cf6281b25f2ce342a49a41a10a0381e00f0 diff --git a/libs/tree-sitter-php b/libs/tree-sitter-php deleted file mode 160000 index 7d07b41..0000000 --- a/libs/tree-sitter-php +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 7d07b41ce2d442ca9a90ed85d0075eccc17ae315 diff --git a/libs/tree-sitter-python b/libs/tree-sitter-python deleted file mode 160000 index 26855ea..0000000 --- a/libs/tree-sitter-python +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 26855eabccb19c6abf499fbc5b8dc7cc9ab8bc64 diff --git a/libs/tree-sitter-query b/libs/tree-sitter-query deleted file mode 160000 index a4e379d..0000000 --- a/libs/tree-sitter-query +++ /dev/null @@ -1 +0,0 @@ -Subproject commit a4e379d4a4b77a09e91c1c9e12d4b898214f990e diff --git a/libs/tree-sitter-regex b/libs/tree-sitter-regex deleted file mode 160000 index b2ac15e..0000000 --- a/libs/tree-sitter-regex +++ /dev/null @@ -1 +0,0 @@ -Subproject commit b2ac15e27fce703d2f37a79ccd94a5c0cbe9720b diff --git a/libs/tree-sitter-ruby b/libs/tree-sitter-ruby deleted file mode 160000 index 89bd7a8..0000000 --- a/libs/tree-sitter-ruby +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 89bd7a8e5450cb6a942418a619d30469f259e5d6 diff --git a/libs/tree-sitter-rust b/libs/tree-sitter-rust deleted file mode 160000 index 261b202..0000000 --- a/libs/tree-sitter-rust +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 261b20226c04ef601adbdf185a800512a5f66291 diff --git a/libs/tree-sitter-sql b/libs/tree-sitter-sql deleted file mode 160000 index 2d5dcd1..0000000 --- a/libs/tree-sitter-sql +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 2d5dcd16f9ee49cb5a6d99eabb00fd4ea298587f diff --git a/libs/tree-sitter-toml b/libs/tree-sitter-toml deleted file mode 160000 index 64b5683..0000000 --- a/libs/tree-sitter-toml +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 64b56832c2cffe41758f28e05c756a3a98d16f41 diff --git a/libs/tree-sitter-typescript b/libs/tree-sitter-typescript deleted file mode 160000 index 75b3874..0000000 --- a/libs/tree-sitter-typescript +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 75b3874edb2dc714fb1fd77a32013d0f8699989f diff --git a/libs/tree-sitter-yaml b/libs/tree-sitter-yaml deleted file mode 160000 index 7708026..0000000 --- a/libs/tree-sitter-yaml +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 7708026449bed86239b1cd5bce6e3c34dbca6415 diff --git a/samples/ruby.rb b/samples/ruby.rb index 7e9df03..dcebc78 100644 --- a/samples/ruby.rb +++ b/samples/ruby.rb @@ -12,7 +12,7 @@ end # Emoji-heavy strings emojis = "👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏" -# Mixed-width CJK block +# Mixed-width CJKssssssssssssssss LoadErssssssssssssssssssssssss cjk_samples = [ "漢字テスト", "測試中文字串", @@ -22,11 +22,12 @@ cjk_samples = [ ] # Ruby regex with unicode -unicode_regex = /[一-龯ぁ-んァ-ヶー々〆〤]/ +$unicode_regex = /[一-龯ぁ-んァ-ヶー々〆〤]/ + # Unicode identifiers (valid in Ruby) -变量 = 123 -π = 3.14159 +变量 = 0x5_4eddaee +π = 3.14_159e+2, ?\u0234, ?\,, ?\x0A, ?s 挨拶 = -> { "こんにちは" } # Method using unicode variable names @@ -35,15 +36,18 @@ def math_test end # Iterate through CJK samples -cjk_samples.each_with_index do |str, idx| - puts "CJK[#{idx}] => #{str} (len=#{str.length})" +cjk_samples.each_with_index do |str, idx:| + puts %Q! CJK[#{idx}] => #{str} (len=#{str.length})\! ! + symbol = :" + a + " end # Test emoji width behaviors puts "Emoji count: #{emojis.length}" # Multi-line string with unicode -multi = <<~BASH +multi = <knot_mtx); if (editor->cursor < editor->scroll) { - uint32_t line_idx = next_unfolded_row(editor, editor->scroll.row); - editor->cursor.row = line_idx; - editor->cursor.col = - line_idx == editor->scroll.row ? editor->scroll.col : 0; + editor->cursor.row = editor->scroll.row; + editor->cursor.col = editor->scroll.col; editor->cursor_preffered = UINT32_MAX; return; } @@ -24,20 +21,6 @@ void ensure_cursor(Editor *editor) { while (true) { if (visual_rows >= editor->size.row) break; - const Fold *fold = fold_for_line(editor->folds, line_index); - if (fold) { - Coord c = {fold->start, 0}; - last_visible = c; - visual_rows++; - uint32_t skip_until = fold->end; - while (line_index <= skip_until) { - char *line = next_line(it, nullptr); - if (!line) - break; - line_index++; - } - continue; - } uint32_t line_len; char *line = next_line(it, &line_len); if (!line) @@ -81,14 +64,8 @@ void ensure_cursor(Editor *editor) { } line_index++; } - uint32_t last_real_row = last_visible.row; - const Fold *last_fold = fold_for_line(editor->folds, last_visible.row); - if (last_fold) { - last_visible.row = last_fold->start == 0 ? 0 : last_fold->start - 1; - last_visible.col = 0; - } editor->cursor.row = last_visible.row; - editor->cursor.col = last_visible.row == last_real_row ? last_visible.col : 0; + editor->cursor.col = last_visible.col; editor->cursor_preffered = UINT32_MAX; free(it->buffer); free(it); @@ -112,7 +89,6 @@ void ensure_scroll(Editor *editor) { } if (len > 0 && line[len - 1] == '\n') --len; - uint32_t rows = 1; uint32_t cols = 0; uint32_t offset = 0; uint32_t old_offset = 0; @@ -121,7 +97,6 @@ void ensure_scroll(Editor *editor) { grapheme_next_character_break_utf8(line + offset, len - offset); int width = display_width(line + offset, inc); if (cols + width > render_width) { - rows++; cols = 0; if (editor->cursor.col > old_offset && editor->cursor.col <= offset) { editor->scroll.row = editor->cursor.row; @@ -139,7 +114,7 @@ void ensure_scroll(Editor *editor) { free(it); editor->scroll.row = editor->cursor.row; editor->scroll.col = (editor->cursor.col == 0) ? 0 : old_offset; - } else { + } else if (editor->cursor.row - editor->scroll.row < editor->size.row * 2) { uint32_t line_index = editor->scroll.row; LineIterator *it = begin_l_iter(editor->root, line_index); if (!it) @@ -150,30 +125,6 @@ void ensure_scroll(Editor *editor) { uint32_t q_size = 0; bool first_visual_line = true; while (true) { - const Fold *fold = fold_for_line(editor->folds, line_index); - if (fold) { - Coord fold_coord = {fold->start, 0}; - if (q_size < max_visual_lines) { - scroll_queue[(q_head + q_size) % max_visual_lines] = fold_coord; - q_size++; - } else { - scroll_queue[q_head] = fold_coord; - q_head = (q_head + 1) % max_visual_lines; - } - if (fold->start <= editor->cursor.row && - editor->cursor.row <= fold->end) { - editor->scroll = scroll_queue[q_head]; - break; - } - uint32_t skip_until = fold->end; - while (line_index <= skip_until) { - char *line = next_line(it, nullptr); - if (!line) - break; - line_index++; - } - continue; - } uint32_t line_len; char *line = next_line(it, &line_len); if (!line) @@ -234,5 +185,11 @@ void ensure_scroll(Editor *editor) { free(scroll_queue); free(it->buffer); free(it); + } else { + editor->scroll.row = (editor->cursor.row > editor->size.row * 1.5) + ? editor->cursor.row - editor->size.row * 1.5 + : 0; + editor->scroll.col = 0; + ensure_scroll(editor); } } diff --git a/src/editor/click.cc b/src/editor/click.cc index fd51479..7099610 100644 --- a/src/editor/click.cc +++ b/src/editor/click.cc @@ -1,5 +1,4 @@ #include "editor/editor.h" -#include "editor/folds.h" #include "main.h" Coord editor_hit_test(Editor *editor, uint32_t x, uint32_t y) { @@ -7,7 +6,6 @@ Coord editor_hit_test(Editor *editor, uint32_t x, uint32_t y) { x++; uint32_t numlen = EXTRA_META + static_cast(std::log10(editor->root->line_count + 1)); - bool is_gutter_click = (x < numlen); uint32_t render_width = editor->size.col - numlen; x = MAX(x, numlen) - numlen; uint32_t target_visual_row = y; @@ -21,28 +19,6 @@ Coord editor_hit_test(Editor *editor, uint32_t x, uint32_t y) { if (!it) return editor->scroll; while (visual_row <= target_visual_row) { - const Fold *fold = fold_for_line(editor->folds, line_index); - if (fold) { - if (visual_row == target_visual_row) { - free(it->buffer); - free(it); - if (is_gutter_click) { - remove_fold(editor, fold->start); - return {UINT32_MAX, UINT32_MAX}; - } - return {fold->start > 0 ? fold->start - 1 : 0, 0}; - } - visual_row++; - while (line_index <= fold->end) { - char *l = next_line(it, nullptr); - if (!l) - break; - line_index++; - } - last_line_index = fold->end; - last_col = 0; - continue; - } uint32_t line_len; char *line = next_line(it, &line_len); if (!line) diff --git a/src/editor/cursor.cc b/src/editor/cursor.cc index 127654b..90b7ed2 100644 --- a/src/editor/cursor.cc +++ b/src/editor/cursor.cc @@ -1,8 +1,7 @@ #include "editor/editor.h" -#include "editor/folds.h" #include "utils/utils.h" -Coord move_right_pure(Editor *editor, Coord cursor, uint32_t number) { +Coord move_right(Editor *editor, Coord cursor, uint32_t number) { Coord result = cursor; if (!editor || !editor->root || number == 0) return result; @@ -50,7 +49,7 @@ Coord move_right_pure(Editor *editor, Coord cursor, uint32_t number) { return result; } -Coord move_left_pure(Editor *editor, Coord cursor, uint32_t number) { +Coord move_left(Editor *editor, Coord cursor, uint32_t number) { Coord result = cursor; if (!editor || !editor->root || number == 0) return result; @@ -103,170 +102,38 @@ Coord move_left_pure(Editor *editor, Coord cursor, uint32_t number) { return result; } -Coord move_right(Editor *editor, Coord cursor, uint32_t number) { - Coord result = cursor; +void cursor_down(Editor *editor, uint32_t number) { if (!editor || !editor->root || number == 0) - return result; - uint32_t row = result.row; - uint32_t col = result.col; - uint32_t line_len = 0; - LineIterator *it = begin_l_iter(editor->root, row); - if (!it) - return result; - uint32_t target_row = next_unfolded_row(editor, row); - while (row < target_row) { - if (!next_line(it, &line_len)) { + return; + uint32_t visual_col = editor->cursor_preffered; + if (visual_col == UINT32_MAX) { + uint32_t len; + LineIterator *it = begin_l_iter(editor->root, editor->cursor.row); + char *line = next_line(it, &len); + if (!line) { free(it->buffer); free(it); - return result; + return; } - ++row; - } - char *line = next_line(it, &line_len); - if (!line) { + editor->cursor_preffered = + get_visual_col_from_bytes(line, len, editor->cursor.col); + visual_col = editor->cursor_preffered; free(it->buffer); free(it); - return result; } - if (line_len > 0 && line[line_len - 1] == '\n') - --line_len; - while (number > 0) { - if (col >= line_len) { - uint32_t next_row = next_unfolded_row(editor, row + 1); - if (next_row >= editor->root->line_count) { - col = line_len; - break; - } - while (row < next_row) { - line = next_line(it, &line_len); - if (!line) { - free(it->buffer); - free(it); - result.row = row; - result.col = col; - return result; - } - ++row; - } - if (line_len > 0 && line[line_len - 1] == '\n') - --line_len; - col = 0; - --number; - continue; - } else { - uint32_t inc = - grapheme_next_character_break_utf8(line + col, line_len - col); - if (inc == 0) - break; - col += inc; - --number; - } - } - free(it->buffer); - free(it); - result.row = row; - result.col = col; - return result; -} - -Coord move_left(Editor *editor, Coord cursor, uint32_t number) { - Coord result = cursor; - if (!editor || !editor->root || number == 0) - return result; - uint32_t row = result.row; - uint32_t col = result.col; - uint32_t len = 0; - LineIterator *it = begin_l_iter(editor->root, row); + editor->cursor.row = + MIN(editor->cursor.row + number, editor->root->line_count - 1); + uint32_t len; + LineIterator *it = begin_l_iter(editor->root, editor->cursor.row); char *line = next_line(it, &len); if (!line) { free(it->buffer); free(it); - return result; + return; } if (len > 0 && line[len - 1] == '\n') --len; - bool iterator_ahead = true; - while (number > 0) { - if (col == 0) { - if (row == 0) - break; - if (iterator_ahead) { - prev_line(it, nullptr); - iterator_ahead = false; - } - line = nullptr; - while (row > 0) { - row--; - line = prev_line(it, &len); - if (!line) - break; - const Fold *fold = fold_for_line(editor->folds, row); - if (fold) { - while (line && row > fold->start) { - line = prev_line(it, &len); - row--; - } - line = nullptr; - continue; - } - break; - } - if (!line) - break; - if (len > 0 && line[len - 1] == '\n') - --len; - col = len; - } else { - uint32_t new_col = 0; - while (new_col < col) { - uint32_t inc = - grapheme_next_character_break_utf8(line + new_col, len - new_col); - if (new_col + inc >= col) - break; - new_col += inc; - } - col = new_col; - } - number--; - } - free(it->buffer); - free(it); - result.row = row; - result.col = col; - return result; -} - -void cursor_down(Editor *editor, uint32_t number) { - if (!editor || !editor->root || number == 0) - return; - uint32_t len; - LineIterator *it = begin_l_iter(editor->root, editor->cursor.row); - char *line_content = next_line(it, &len); - if (line_content == nullptr) - return; - if (editor->cursor_preffered == UINT32_MAX) - editor->cursor_preffered = - get_visual_col_from_bytes(line_content, len, editor->cursor.col); - uint32_t visual_col = editor->cursor_preffered; - free(it->buffer); - free(it); - uint32_t target_row = editor->cursor.row; - while (number > 0 && target_row < editor->root->line_count - 1) { - target_row = next_unfolded_row(editor, target_row + 1); - if (target_row >= editor->root->line_count) { - target_row = editor->root->line_count - 1; - break; - } - number--; - } - it = begin_l_iter(editor->root, target_row); - line_content = next_line(it, &len); - if (!line_content) - return; - if (len > 0 && line_content[len - 1] == '\n') - --len; - editor->cursor.row = target_row; - editor->cursor.col = get_bytes_from_visual_col(line_content, len, visual_col); + editor->cursor.col = get_bytes_from_visual_col(line, len, visual_col); free(it->buffer); free(it); } @@ -287,7 +154,7 @@ void cursor_up(Editor *editor, uint32_t number) { free(it); uint32_t target_row = editor->cursor.row; while (number > 0 && target_row > 0) { - target_row = prev_unfolded_row(editor, target_row - 1); + target_row--; if (target_row == 0) { number--; break; diff --git a/src/editor/edit.cc b/src/editor/edit.cc index 0ea76b8..b652e18 100644 --- a/src/editor/edit.cc +++ b/src/editor/edit.cc @@ -1,7 +1,7 @@ #include "editor/editor.h" -#include "editor/folds.h" #include "lsp/lsp.h" #include "utils/utils.h" +#include void edit_erase(Editor *editor, Coord pos, int64_t len) { if (len == 0) @@ -11,9 +11,8 @@ void edit_erase(Editor *editor, Coord pos, int64_t len) { uint32_t cursor_original = line_to_byte(editor->root, editor->cursor.row, nullptr) + editor->cursor.col; - TSPoint old_point = {pos.row, pos.col}; uint32_t byte_pos = line_to_byte(editor->root, pos.row, nullptr) + pos.col; - Coord point = move_left_pure(editor, pos, -len); + Coord point = move_left(editor, pos, -len); json lsp_range; bool do_lsp = (editor->lsp != nullptr); if (do_lsp) { @@ -48,30 +47,12 @@ void edit_erase(Editor *editor, Coord pos, int64_t len) { lock_1.unlock(); uint32_t start_row = point.row; uint32_t end_row = pos.row; - apply_line_deletion(editor, start_row + 1, end_row); apply_hook_deletion(editor, start_row + 1, end_row); std::unique_lock lock_2(editor->knot_mtx); editor->root = erase(editor->root, start, byte_pos - start); - if (editor->ts.tree) { - TSInputEdit edit = { - .start_byte = start, - .old_end_byte = byte_pos, - .new_end_byte = start, - .start_point = {point.row, point.col}, - .old_end_point = old_point, - .new_end_point = {point.row, point.col}, - }; - editor->edit_queue.push(edit); - } - std::unique_lock lock_3(editor->spans.mtx); - apply_edit(editor->spans.spans, start, start - byte_pos); - if (editor->spans.mid_parse) - editor->spans.edits.push({start, start - byte_pos}); - lock_3.unlock(); lock_2.unlock(); - std::unique_lock lock_4(editor->hex_color_spans.mtx); - apply_edit(editor->hex_color_spans.spans, byte_pos, start - byte_pos); - lock_4.unlock(); + if (editor->parser) + editor->parser->edit(editor->root, start_row, end_row, start_row); if (do_lsp) { if (editor->lsp->incremental_sync) { json message = { @@ -102,9 +83,8 @@ void edit_erase(Editor *editor, Coord pos, int64_t len) { uint32_t cursor_original = line_to_byte(editor->root, editor->cursor.row, nullptr) + editor->cursor.col; - TSPoint old_point = {pos.row, pos.col}; uint32_t byte_pos = line_to_byte(editor->root, pos.row, nullptr) + pos.col; - Coord point = move_right_pure(editor, pos, len); + Coord point = move_right(editor, pos, len); json lsp_range; bool do_lsp = (editor->lsp != nullptr); if (do_lsp) { @@ -139,30 +119,12 @@ void edit_erase(Editor *editor, Coord pos, int64_t len) { lock_1.unlock(); uint32_t start_row = pos.row; uint32_t end_row = point.row; - apply_line_deletion(editor, start_row + 1, end_row); apply_hook_deletion(editor, start_row + 1, end_row); std::unique_lock lock_2(editor->knot_mtx); editor->root = erase(editor->root, byte_pos, end - byte_pos); - if (editor->ts.tree) { - TSInputEdit edit = { - .start_byte = byte_pos, - .old_end_byte = end, - .new_end_byte = byte_pos, - .start_point = old_point, - .old_end_point = {point.row, point.col}, - .new_end_point = old_point, - }; - editor->edit_queue.push(edit); - } - std::unique_lock lock_3(editor->spans.mtx); - apply_edit(editor->spans.spans, byte_pos, byte_pos - end); - if (editor->spans.mid_parse) - editor->spans.edits.push({byte_pos, byte_pos - end}); - lock_3.unlock(); lock_2.unlock(); - std::unique_lock lock_4(editor->hex_color_spans.mtx); - apply_edit(editor->hex_color_spans.spans, byte_pos, byte_pos - end); - lock_4.unlock(); + if (editor->parser) + editor->parser->edit(editor->root, start_row, end_row, start_row); if (do_lsp) { if (editor->lsp->incremental_sync) { json message = { @@ -197,7 +159,6 @@ void edit_insert(Editor *editor, Coord pos, char *data, uint32_t len) { line_to_byte(editor->root, editor->cursor.row, nullptr) + editor->cursor.col; uint32_t byte_pos = line_to_byte(editor->root, pos.row, nullptr) + pos.col; - TSPoint start_point = {pos.row, pos.col}; if (cursor_original > byte_pos) { uint32_t cursor_new = cursor_original + len; uint32_t new_col; @@ -207,39 +168,14 @@ void edit_insert(Editor *editor, Coord pos, char *data, uint32_t len) { lock_1.unlock(); std::unique_lock lock_2(editor->knot_mtx); editor->root = insert(editor->root, byte_pos, data, len); - uint32_t cols = 0; uint32_t rows = 0; - for (uint32_t i = 0; i < len; i++) { - if (data[i] == '\n') { + for (uint32_t i = 0; i < len; i++) + if (data[i] == '\n') rows++; - cols = 0; - } else { - cols++; - } - } - apply_line_insertion(editor, pos.row, rows); apply_hook_insertion(editor, pos.row, rows); - if (editor->ts.tree) { - TSInputEdit edit = { - .start_byte = byte_pos, - .old_end_byte = byte_pos, - .new_end_byte = byte_pos + len, - .start_point = start_point, - .old_end_point = start_point, - .new_end_point = {start_point.row + rows, - (rows == 0) ? (start_point.column + cols) : cols}, - }; - editor->edit_queue.push(edit); - } - std::unique_lock lock_3(editor->spans.mtx); - apply_edit(editor->spans.spans, byte_pos, len); - if (editor->spans.mid_parse) - editor->spans.edits.push({byte_pos, len}); - lock_3.unlock(); lock_2.unlock(); - std::unique_lock lock_4(editor->hex_color_spans.mtx); - apply_edit(editor->hex_color_spans.spans, byte_pos, len); - lock_4.unlock(); + if (editor->parser) + editor->parser->edit(editor->root, pos.row, pos.row, pos.row + rows); if (editor->lsp) { if (editor->lsp->incremental_sync) { lock_1.lock(); diff --git a/src/editor/editor.cc b/src/editor/editor.cc index dc918f1..a1ae7dc 100644 --- a/src/editor/editor.cc +++ b/src/editor/editor.cc @@ -29,39 +29,20 @@ Editor *new_editor(const char *filename_arg, Coord position, Coord size) { editor->root = load(str, len, optimal_chunk_size(len)); free(str); editor->lang = language_for_file(filename.c_str()); - if (editor->lang.name != "unknown" && len <= (1024 * 128)) { - editor->ts.parser = ts_parser_new(); - editor->ts.language = editor->lang.fn(); - ts_parser_set_language(editor->ts.parser, editor->ts.language); - editor->ts.query_file = - get_exe_dir() + "/../grammar/" + editor->lang.name + ".scm"; - } - if (len <= (1024 * 28)) - request_add_to_lsp(editor->lang, editor); + if (editor->lang.name != "unknown") + editor->parser = new Parser(editor->root, &editor->knot_mtx, + editor->lang.name, size.row + 5); + // if (len <= (1024 * 28)) + // request_add_to_lsp(editor->lang, editor); editor->indents.compute_indent(editor); return editor; } -void free_tsset(TSSetMain *set) { - if (set->parser) - ts_parser_delete(set->parser); - if (set->tree) - ts_tree_delete(set->tree); - if (set->query) - ts_query_delete(set->query); - for (auto &inj : set->injections) { - if (inj.second.parser) - ts_parser_delete(inj.second.parser); - if (inj.second.query) - ts_query_delete(inj.second.query); - if (inj.second.tree) - ts_tree_delete(inj.second.tree); - } -} - void free_editor(Editor *editor) { remove_from_lsp(editor); - free_tsset(&editor->ts); + if (editor->parser) + delete editor->parser; + editor->parser = nullptr; free_rope(editor->root); delete editor; } diff --git a/src/editor/events.cc b/src/editor/events.cc index ed7e2b0..9d5792a 100644 --- a/src/editor/events.cc +++ b/src/editor/events.cc @@ -1,9 +1,7 @@ #include "editor/editor.h" -#include "editor/folds.h" #include "lsp/lsp.h" #include "main.h" #include "utils/utils.h" -#include void handle_editor_event(Editor *editor, KeyEvent event) { static std::chrono::steady_clock::time_point last_click_time = @@ -579,17 +577,6 @@ void handle_editor_event(Editor *editor, KeyEvent event) { char *text; Coord start; switch (event.c[0]) { - case 'f': - if (editor->cursor.row != editor->selection.row) { - uint32_t start = MIN(editor->cursor.row, editor->selection.row); - uint32_t end = MAX(editor->cursor.row, editor->selection.row); - add_fold(editor, start, end); - } - cursor_left(editor, 1); - cursor_down(editor, 1); - editor->selection_active = false; - mode = NORMAL; - break; case 0x1B: case 's': case 'v': @@ -648,10 +635,8 @@ void handle_editor_event(Editor *editor, KeyEvent event) { } editor->hooks[event.c[0] - '!'] = editor->cursor.row + 1; } else { - uint32_t line = editor->hooks[event.c[0] - '!']; + uint32_t line = editor->hooks[event.c[0] - '!'] - 1; if (line > 0) { - if (line_is_folded(editor->folds, --line)) - break; editor->cursor = {line, 0}; editor->cursor_preffered = UINT32_MAX; } diff --git a/src/editor/lsp.cc b/src/editor/lsp.cc index 707c280..6732a03 100644 --- a/src/editor/lsp.cc +++ b/src/editor/lsp.cc @@ -17,9 +17,9 @@ void apply_lsp_edits(Editor *editor, std::vector edits, bool move) { std::shared_lock lock(editor->knot_mtx); editor->cursor = first.start; editor->cursor = - move_right_pure(editor, editor->cursor, - count_clusters(first.text.c_str(), first.text.size(), 0, - first.text.size())); + move_right(editor, editor->cursor, + count_clusters(first.text.c_str(), first.text.size(), 0, + first.text.size())); } else { if (cursor.row >= editor->root->line_count) { editor->cursor.row = editor->root->line_count - 1; diff --git a/src/editor/move_line.cc b/src/editor/move_line.cc index 87ffc8c..8cd8611 100644 --- a/src/editor/move_line.cc +++ b/src/editor/move_line.cc @@ -1,5 +1,4 @@ #include "editor/editor.h" -#include "editor/folds.h" #include "main.h" void move_line_up(Editor *editor) { @@ -17,7 +16,7 @@ void move_line_up(Editor *editor) { if (line_len > 0 && line[line_len - 1] == '\n') line_len--; line_cluster_len = count_clusters(line, line_len, 0, line_len); - uint32_t target_row = prev_unfolded_row(editor, editor->cursor.row - 1); + uint32_t target_row = editor->cursor.row - 1; uint32_t up_by = editor->cursor.row - target_row; if (up_by > 1) up_by--; @@ -68,7 +67,7 @@ void move_line_down(Editor *editor) { if (line_len && line[line_len - 1] == '\n') line_len--; line_cluster_len = count_clusters(line, line_len, 0, line_len); - uint32_t target_row = next_unfolded_row(editor, editor->cursor.row + 1); + uint32_t target_row = editor->cursor.row + 1; if (target_row >= editor->root->line_count) { free(line); lock.unlock(); @@ -95,7 +94,7 @@ void move_line_down(Editor *editor) { std::shared_lock lock(editor->knot_mtx); uint32_t start_row = MIN(editor->cursor.row, editor->selection.row); uint32_t end_row = MAX(editor->cursor.row, editor->selection.row); - uint32_t target_row = next_unfolded_row(editor, end_row + 1); + uint32_t target_row = end_row + 1; if (target_row >= editor->root->line_count) return; uint32_t down_by = target_row - end_row; diff --git a/src/editor/renderer.cc b/src/editor/renderer.cc index e030d06..8cdcf19 100644 --- a/src/editor/renderer.cc +++ b/src/editor/renderer.cc @@ -1,7 +1,5 @@ #include "editor/editor.h" -#include "editor/folds.h" #include "main.h" -#include "ts/decl.h" void render_editor(Editor *editor) { uint32_t sel_start = 0, sel_end = 0; @@ -22,6 +20,9 @@ void render_editor(Editor *editor) { while (warn_it != editor->warnings.end() && warn_it->line < editor->scroll.row) ++warn_it; + std::unique_lock lock; + if (editor->parser) + lock = std::unique_lock(editor->parser->mutex); std::shared_lock knot_lock(editor->knot_mtx); if (editor->selection_active) { Coord start, end; @@ -73,56 +74,12 @@ void render_editor(Editor *editor) { } Coord cursor = {UINT32_MAX, UINT32_MAX}; uint32_t line_index = editor->scroll.row; - SpanCursor span_cursor(editor->spans); - SpanCursor word_span_cursor(editor->word_spans); - SpanCursor hex_span_cursor(editor->hex_color_spans); LineIterator *it = begin_l_iter(editor->root, line_index); if (!it) return; uint32_t rendered_rows = 0; uint32_t global_byte_offset = line_to_byte(editor->root, line_index, nullptr); - span_cursor.sync(global_byte_offset); - word_span_cursor.sync(global_byte_offset); - hex_span_cursor.sync(global_byte_offset); while (rendered_rows < editor->size.row) { - const Fold *fold = fold_for_line(editor->folds, line_index); - if (fold) { - update(editor->position.row + rendered_rows, editor->position.col, "", - 0xAAAAAA, 0, 0); - char buf[16]; - int len = snprintf(buf, sizeof(buf), "%*u ", numlen - 3, fold->start + 1); - uint32_t num_color = - editor->cursor.row == fold->start ? 0xFFFFFF : 0x555555; - for (int i = 0; i < len; i++) - update(editor->position.row + rendered_rows, - editor->position.col + i + 2, (char[2]){buf[i], 0}, num_color, 0, - 0); - const char marker[15] = "... folded ..."; - uint32_t i = 0; - for (; i < 14 && i < render_width; i++) - update(rendered_rows, i + render_x, (char[2]){marker[i], 0}, 0xc6c6c6, - 0, 0); - for (; i < render_width; i++) - update(rendered_rows, i + render_x, " ", 0xc6c6c6, 0, 0); - rendered_rows++; - uint32_t skip_until = fold->end; - while (line_index <= skip_until) { - if (hook_it != v.end() && hook_it->first == line_index + 1) - hook_it++; - while (warn_it != editor->warnings.end() && warn_it->line == line_index) - ++warn_it; - uint32_t line_len; - char *line = next_line(it, &line_len); - if (!line) - break; - global_byte_offset += line_len; - if (line_len > 0 && line[line_len - 1] == '\n') - global_byte_offset--; - global_byte_offset++; - line_index++; - } - continue; - } uint32_t line_len; char *line = next_line(it, &line_len); if (!line) @@ -182,25 +139,13 @@ void render_editor(Editor *editor) { } uint32_t absolute_byte_pos = global_byte_offset + current_byte_offset + local_render_offset; - Highlight *hl = span_cursor.get_highlight(absolute_byte_pos); - Highlight *word_hl = word_span_cursor.get_highlight(absolute_byte_pos); - Highlight *hex_hl = hex_span_cursor.get_highlight(absolute_byte_pos); + const Highlight *hl = nullptr; + if (editor->parser && editor->parser->line_data.size() > line_index) + hl = &highlight_map.at(editor->parser->get_type( + {line_index, current_byte_offset + local_render_offset})); uint32_t fg = hl ? hl->fg : 0xFFFFFF; uint32_t bg = hl ? hl->bg : 0; uint8_t fl = hl ? hl->flags : 0; - if (hex_hl) { - if (hex_hl->fg != 0) - fg = hex_hl->fg; - if (hex_hl->bg != 0) - bg = hex_hl->bg; - fl |= hex_hl->flags; - } else if (word_hl) { - if (word_hl->fg != 0) - fg |= word_hl->fg; - if (word_hl->bg != 0) - bg |= word_hl->bg; - fl |= word_hl->flags; - } if (editor->selection_active && absolute_byte_pos >= sel_start && absolute_byte_pos < sel_end) bg = 0x555555; @@ -468,6 +413,8 @@ void render_editor(Editor *editor) { global_byte_offset += line_len + 1; line_index++; } + if (lock.owns_lock()) + lock.unlock(); while (rendered_rows < editor->size.row) { for (uint32_t col = 0; col < editor->size.col; col++) update(editor->position.row + rendered_rows, editor->position.col + col, @@ -498,4 +445,6 @@ void render_editor(Editor *editor) { } free(it->buffer); free(it); + if (editor->parser) + editor->parser->scroll(line_index + 5); } diff --git a/src/editor/scroll.cc b/src/editor/scroll.cc index 40b5ff7..cf68698 100644 --- a/src/editor/scroll.cc +++ b/src/editor/scroll.cc @@ -1,5 +1,4 @@ #include "editor/editor.h" -#include "editor/folds.h" void scroll_up(Editor *editor, int32_t number) { if (!editor || number == 0) @@ -63,41 +62,6 @@ void scroll_up(Editor *editor, int32_t number) { free(it); return; } - const Fold *fold = fold_for_line(editor->folds, line_index); - if (fold) { - while (line && line_index > fold->start) { - free(line); - line = prev_line(it, &len); - line_index--; - if (!line) { - editor->scroll = {0, 0}; - free(it->buffer); - free(it); - return; - } - } - if (--number == 0) { - editor->scroll = {fold->start, 0}; - free(it->buffer); - free(it); - return; - } - if (fold->start == 0) { - editor->scroll = {0, 0}; - free(it->buffer); - free(it); - return; - } - line_index = fold->start - 1; - line = prev_line(it, &len); - if (!line) { - editor->scroll = {0, 0}; - free(it->buffer); - free(it); - return; - } - continue; - } if (len > 0 && line[len - 1] == '\n') len--; current_byte_offset = 0; @@ -147,34 +111,6 @@ void scroll_down(Editor *editor, uint32_t number) { uint32_t visual_seen = 0; bool first_visual_line = true; while (true) { - const Fold *fold = fold_for_line(editor->folds, line_index); - if (fold) { - Coord fold_coord = {fold->start, 0}; - if (q_size < max_visual_lines) { - scroll_queue[(q_head + q_size) % max_visual_lines] = fold_coord; - q_size++; - } else { - scroll_queue[q_head] = fold_coord; - q_head = (q_head + 1) % max_visual_lines; - } - visual_seen++; - if (visual_seen >= number + max_visual_lines) { - editor->scroll = scroll_queue[q_head]; - break; - } - uint32_t skip_until = fold->end; - while (line_index <= skip_until) { - char *line = next_line(it, nullptr); - if (!line) { - free(scroll_queue); - free(it->buffer); - free(it); - return; - } - line_index++; - } - continue; - } uint32_t line_len; char *line = next_line(it, &line_len); if (!line) diff --git a/src/editor/worker.cc b/src/editor/worker.cc index 96f8d11..2329a08 100644 --- a/src/editor/worker.cc +++ b/src/editor/worker.cc @@ -1,7 +1,4 @@ #include "editor/editor.h" -#include "ts/ts.h" - -static Highlight HL_UNDERLINE = {0, 0, CF_UNDERLINE, UINT8_MAX - 1}; void hover_diagnostic(Editor *editor) { std::shared_lock lock(editor->v_mtx); @@ -30,78 +27,8 @@ void hover_diagnostic(Editor *editor) { void editor_worker(Editor *editor) { if (!editor || !editor->root) return; - if (editor->root->char_count > (1024 * 128)) - return; - if (editor->ts.query_file != "" && !editor->ts.query) - editor->ts.query = load_query(editor->ts.query_file.c_str(), &editor->ts); - if (editor->ts.parser && editor->ts.query) - ts_collect_spans(editor); - if (editor->root->char_count > (1024 * 32)) - return; - uint32_t prev_col, next_col; - word_boundaries_exclusive(editor, editor->cursor, &prev_col, &next_col); - std::unique_lock lock(editor->word_spans.mtx); - editor->word_spans.spans.clear(); - lock.unlock(); - if (next_col - prev_col > 0 && next_col - prev_col < 256 - 4) { - std::shared_lock lockk(editor->knot_mtx); - uint32_t offset = line_to_byte(editor->root, editor->cursor.row, nullptr); - char *word = read(editor->root, offset + prev_col, next_col - prev_col); - lockk.unlock(); - if (word) { - char buf[256]; - snprintf(buf, sizeof(buf), "\\b%s\\b", word); - std::shared_lock lockk(editor->knot_mtx); - std::vector> results = - search_rope_dfa(editor->root, buf); - lockk.unlock(); - std::unique_lock lock2(editor->word_spans.mtx); - editor->word_spans.spans.reserve(results.size()); - for (const auto &match : results) { - Span s; - s.start = match.first; - s.end = match.first + match.second; - s.hl = &HL_UNDERLINE; - editor->word_spans.spans.push_back(s); - } - free(word); - lock2.unlock(); - } - } - static uint16_t limit = 150; - static Highlight *hl_s = (Highlight *)calloc(limit, sizeof(Highlight)); - if (!hl_s) - exit(ENOMEM); - std::shared_lock lockk(editor->knot_mtx); - std::vector results = - search_rope(editor->root, "(?:0x|#)[0-9a-fA-F]{6,8}\\b"); - if (results.size() > limit) { - limit = results.size() + 50; - free(hl_s); - hl_s = (Highlight *)calloc(limit, sizeof(Highlight)); - if (!hl_s) - exit(ENOMEM); - } - lockk.unlock(); - std::unique_lock lock2(editor->hex_color_spans.mtx); - editor->hex_color_spans.spans.clear(); - editor->hex_color_spans.spans.reserve(results.size()); - for (size_t i = 0; i < results.size(); ++i) { - Span s; - s.start = results[i].start; - s.end = results[i].end; - int x = results[i].text[0] == '#' ? 1 : 2; - uint32_t bg = HEX(results[i].text.substr(x, 6)); - uint8_t r = bg >> 16; - uint8_t g = (bg >> 8) & 0xFF; - uint8_t b = bg & 0xFF; - double luminance = 0.299 * r + 0.587 * g + 0.114 * b; - uint32_t fg = (luminance > 128) ? 0x010101 : 0xFEFEFE; - hl_s[i] = {fg, bg, CF_BOLD, UINT8_MAX}; - s.hl = &hl_s[i]; - editor->hex_color_spans.spans.push_back(s); - } - lock2.unlock(); + if (editor->parser) + editor->parser->work(); hover_diagnostic(editor); if (editor->completion.active && editor->completion.hover_dirty) { editor->completion.hover.render_first(); diff --git a/src/io/knot.cc b/src/io/knot.cc index 0ff993d..19c296a 100644 --- a/src/io/knot.cc +++ b/src/io/knot.cc @@ -301,7 +301,7 @@ Knot *erase(Knot *node, uint32_t offset, uint32_t len) { return balance(node); } -static void _read_into(Knot *node, uint32_t offset, uint32_t len, char *dest) { +void read_into(Knot *node, uint32_t offset, uint32_t len, char *dest) { if (!node || len == 0) return; if (node->depth == 0) { @@ -314,7 +314,7 @@ static void _read_into(Knot *node, uint32_t offset, uint32_t len, char *dest) { uint32_t chunk_len = left_count - offset; if (chunk_len > len) chunk_len = len; - _read_into(left, offset, chunk_len, dest); + read_into(left, offset, chunk_len, dest); dest += chunk_len; len -= chunk_len; offset = 0; @@ -322,7 +322,7 @@ static void _read_into(Knot *node, uint32_t offset, uint32_t len, char *dest) { offset -= left_count; } if (len > 0 && node->right) - _read_into(node->right, offset, len, dest); + read_into(node->right, offset, len, dest); } char *read(Knot *root, uint32_t offset, uint32_t len) { @@ -340,7 +340,7 @@ char *read(Knot *root, uint32_t offset, uint32_t len) { char *buffer = (char *)malloc((len + 1) * sizeof(char)); if (!buffer) return nullptr; - _read_into(root, offset, len, buffer); + read_into(root, offset, len, buffer); buffer[len] = '\0'; return buffer; } diff --git a/src/io/renderer.cc b/src/io/renderer.cc index 01febba..2ffb39c 100644 --- a/src/io/renderer.cc +++ b/src/io/renderer.cc @@ -1,12 +1,11 @@ #include "io/sysio.h" -#include -uint32_t rows, cols; -bool show_cursor = 0; -std::vector screen; -std::vector old_screen; -std::mutex screen_mutex; -termios orig_termios; +static uint32_t rows, cols; +static bool show_cursor = 0; +static std::vector screen; +static std::vector old_screen; +static std::mutex screen_mutex; +static termios orig_termios; void disable_raw_mode() { std::string os = "\x1b[?1049l\x1b[2 q\x1b[?1002l\x1b[?25h\x1b[?2004l"; diff --git a/src/lsp/process.cc b/src/lsp/process.cc index 61dbe4d..d93165e 100644 --- a/src/lsp/process.cc +++ b/src/lsp/process.cc @@ -161,7 +161,7 @@ void close_lsp(uint8_t lsp_id) { lsp->initialized = false; LSPPending *shutdown_pending = new LSPPending(); shutdown_pending->method = "shutdown"; - shutdown_pending->callback = [lsp, lsp_id](Editor *, std::string, json) { + shutdown_pending->callback = [lsp](Editor *, std::string, json) { json exit = {{"jsonrpc", "2.0"}, {"method", "exit"}}; lsp_send(lsp, exit, nullptr); }; diff --git a/src/main.cc b/src/main.cc index ddef93c..a406bb0 100644 --- a/src/main.cc +++ b/src/main.cc @@ -2,7 +2,6 @@ #include "editor/editor.h" #include "io/sysio.h" #include "lsp/lsp.h" -#include "ts/ts.h" #include "ui/bar.h" #include "utils/utils.h" @@ -15,7 +14,7 @@ std::atomic mode = NORMAL; void background_worker() { while (running) - throttle(8ms, editor_worker, editors[current_editor]); + throttle(16ms, editor_worker, editors[current_editor]); } void background_lsp() { @@ -119,14 +118,15 @@ int main(int argc, char *argv[]) { for (auto editor : editors) free_editor(editor); + std::unique_lock lk(active_lsps_mtx); + lk.unlock(); while (true) { - std::unique_lock lk(active_lsps_mtx); + lk.lock(); if (active_lsps.empty()) break; lk.unlock(); throttle(16ms, lsp_worker); } - clear_regex_cache(); return 0; } diff --git a/src/syntax/ruby.cc b/src/syntax/ruby.cc new file mode 100644 index 0000000..3ea3c19 --- /dev/null +++ b/src/syntax/ruby.cc @@ -0,0 +1,730 @@ +#include "syntax/langs.h" + +const static std::vector base_keywords = { + // style 4 + "if", "else", "elsif", "case", "rescue", "ensure", "do", "for", + "while", "until", "def", "class", "module", "begin", "end", "unless", +}; + +const static std::vector operator_keywords = { + // style 5 + "alias", "and", "BEGIN", "break", "catch", "defined?", "in", "next", + "not", "or", "redo", "rescue", "retry", "return", "super", "yield", + "self", "nil", "true", "false", "undef", "when", +}; + +const static std::vector operators = { + "+", "-", "*", "/", "%", "**", "==", "!=", "===", + "<=>", ">", ">=", "<", "<=", "&&", "||", "!", "&", + "|", "^", "~", "<<", ">>", "=", "+=", "-=", "*=", + "/=", "%=", "**=", "&=", "|=", "^=", "<<=", ">>=", "..", + "...", "===", "=", "=>", "&.", "[]", "[]=", "`", "->", +}; + +struct HeredocInfo { + std::string delim; + bool allow_interpolation{true}; + bool allow_indentation{false}; + + bool operator==(const HeredocInfo &other) const { + return delim == other.delim && + allow_interpolation == other.allow_interpolation && + allow_indentation == other.allow_indentation; + } +}; + +struct RubyFullState { + // TODO: use this to highlight each level seperaletly like vscode colored + // braces extention thingy does + int brace_level = 0; + int paren_level = 0; + int bracket_level = 0; + + enum : uint8_t { NONE, STRING, REGEXP, COMMENT, HEREDOC, END }; + uint8_t in_state = RubyFullState::NONE; + + struct Lit { + char delim_start = '\0'; + char delim_end = '\0'; + // For stuff like %Q{ { these braces are valid } this part is still str } + int brace_level = 1; + bool allow_interp = false; + + bool operator==(const RubyFullState::Lit &other) const { + return delim_start == other.delim_start && delim_end == other.delim_end && + brace_level == other.brace_level && + allow_interp == other.allow_interp; + } + } lit; + + bool operator==(const RubyFullState &other) const { + return in_state == other.in_state && lit == other.lit && + brace_level == other.brace_level && + paren_level == other.paren_level && + bracket_level == other.bracket_level; + } +}; + +struct RubyState { + int interp_level = 0; + std::stack> interp_stack; + std::shared_ptr full_state; + std::deque heredocs; + + bool operator==(const RubyState &other) const { + return interp_level == other.interp_level && + interp_stack == other.interp_stack && + ((full_state && other.full_state && + *full_state == *other.full_state)) && + heredocs == other.heredocs; + } +}; + +inline std::shared_ptr +ensure_state(std::shared_ptr state) { + if (!state) + state = std::make_shared(); + if (state.unique()) + return state; + return std::make_shared(*state); +} + +inline std::shared_ptr +ensure_full_state(std::shared_ptr state) { + state = ensure_state(state); + if (!state->full_state) + state->full_state = std::make_shared(); + else if (!state->full_state.unique()) + state->full_state = std::make_shared(*state->full_state); + return state; +} + +bool identifier_start_char(char c) { + return !isascii(c) || isalpha(c) || c == '_'; +} + +bool identifier_char(char c) { return !isascii(c) || isalnum(c) || c == '_'; } + +uint32_t get_next_word(const char *text, uint32_t i, uint32_t len) { + if (i >= len || !identifier_start_char(text[i])) + return 0; + uint32_t width = 1; + while (i + width < len && identifier_char(text[i + width])) + width++; + if (i + width < len && (text[i + width] == '!' || text[i + width] == '?')) + width++; + return width; +} + +bool compare(const char *a, const char *b, size_t n) { + size_t i = 0; + for (; i < n; ++i) + if (a[i] != b[i]) + return false; + return true; +} + +std::shared_ptr ruby_parse(std::vector *tokens, + std::shared_ptr in_state, + const char *text, uint32_t len) { + static bool keywords_trie_init = false; + static Trie base_keywords_trie; + static Trie operator_keywords_trie; + static Trie operator_trie; + if (!keywords_trie_init) { + base_keywords_trie.build(base_keywords); + operator_keywords_trie.build(operator_keywords); + operator_trie.build(operators); + keywords_trie_init = true; + } + tokens->clear(); + if (!in_state) + in_state = std::make_shared(); + std::shared_ptr state = + std::static_pointer_cast(in_state); + if (!state->full_state) + state->full_state = std::make_shared(); + uint32_t i = 0; + while (len > 0 && (text[len - 1] == '\n' || text[len - 1] == '\r' || + text[len - 1] == '\t' || text[len - 1] == ' ')) + len--; + if (len == 0) + return state; + bool heredoc_first = false; + while (i < len) { + if (state->full_state->in_state == RubyFullState::END) { + tokens->clear(); + return state; + } + if (state->full_state->in_state == RubyFullState::COMMENT) { + tokens->push_back({i, len, 1}); + if (i == 0 && len == 4 && text[i] == '=' && text[i + 1] == 'e' && + text[i + 2] == 'n' && text[i + 3] == 'd') { + state = ensure_full_state(state); + state->full_state->in_state = RubyFullState::NONE; + } + return state; + } + if (!heredoc_first && + state->full_state->in_state == RubyFullState::HEREDOC) { + if (i == 0) { + uint32_t start = 0; + if (state->heredocs.front().allow_indentation) + while (start < len && (text[start] == ' ' || text[start] == '\t')) + start++; + if (len - start == state->heredocs.front().delim.length() && + compare(text + start, state->heredocs.front().delim.c_str(), + state->heredocs.front().delim.length())) { + state = ensure_full_state(state); + state->heredocs.pop_front(); + if (state->heredocs.empty()) + state->full_state->in_state = RubyFullState::NONE; + tokens->push_back({i, len, 10}); + return state; + } + } + uint32_t start = i; + if (!state->heredocs.front().allow_interpolation) { + tokens->push_back({i, len, 2}); + return state; + } else { + while (i < len) { + if (text[i] == '\\') { + // TODO: highlight the escape character + i++; + if (i < len) + i++; + continue; + } + if (text[i] == '#' && i + 1 < len && text[i + 1] == '{') { + tokens->push_back({start, i, 2}); + tokens->push_back({i, i + 2, 10}); + i += 2; + state = ensure_state(state); + state->interp_stack.push(state->full_state); + state->full_state = std::make_shared(); + state->interp_level = 1; + break; + } + i++; + } + if (i == len) + tokens->push_back({start, len, 2}); + continue; + } + } + if (state->full_state->in_state == RubyFullState::STRING) { + uint32_t start = i; + while (i < len) { + if (text[i] == '\\') { + // TODO: highlight the escape character - need to make priority work + // and this have higher + i++; + if (i < len) + i++; + continue; + } + if (state->full_state->lit.allow_interp && text[i] == '#' && + i + 1 < len && text[i + 1] == '{') { + tokens->push_back({start, i, 2}); + tokens->push_back({i, i + 2, 10}); + i += 2; + state = ensure_state(state); + state->interp_stack.push(state->full_state); + state->full_state = std::make_shared(); + state->interp_level = 1; + break; + } + if (text[i] == state->full_state->lit.delim_start && + state->full_state->lit.delim_start != + state->full_state->lit.delim_end) { + state = ensure_full_state(state); + state->full_state->lit.brace_level++; + } + if (text[i] == state->full_state->lit.delim_end) { + state = ensure_full_state(state); + if (state->full_state->lit.delim_start == + state->full_state->lit.delim_end) { + i++; + tokens->push_back({start, i, 2}); + state->full_state->in_state = RubyFullState::NONE; + break; + } else { + state->full_state->lit.brace_level--; + if (state->full_state->lit.brace_level == 0) { + i++; + tokens->push_back({start, i, 2}); + state->full_state->in_state = RubyFullState::NONE; + break; + } + } + } + i++; + } + if (i == len) + tokens->push_back({start, len, 2}); + continue; + } + if (i == 0 && len == 6) { + if (text[i] == '=' && text[i + 1] == 'b' && text[i + 2] == 'e' && + text[i + 3] == 'g' && text[i + 4] == 'i' && text[i + 5] == 'n') { + state = ensure_full_state(state); + state->full_state->in_state = RubyFullState::COMMENT; + tokens->push_back({0, len, 1}); + return state; + } + } + if (i == 0 && len == 7) { + if (text[i] == '_' && text[i + 1] == '_' && text[i + 2] == 'E' && + text[i + 3] == 'N' && text[i + 4] == 'D' && text[i + 5] == '_' && + text[i + 6] == '_') { + state = ensure_full_state(state); + tokens->clear(); + state->full_state->in_state = RubyFullState::END; + return state; + } + } + if (i + 3 <= len && text[i] == '<' && text[i + 1] == '<') { + uint32_t j = i + 2; + bool indented = false; + if (text[j] == '~') + indented = true; + if (text[j] == '~' || text[j] == '-') + j++; + tokens->push_back({i, j, 10}); + if (j >= len) + continue; + std::string delim; + bool interpolation = true; + uint32_t s = j; + if (text[j] == '\'' || text[j] == '"') { + char q = text[j++]; + if (q == '\'') + interpolation = false; + while (j < len && text[j] != q) + delim += text[j++]; + } else { + while (j < len && identifier_char(text[j])) + delim += text[j++]; + } + if (!delim.empty()) { + tokens->push_back({s, j, 10}); + state = ensure_full_state(state); + state->heredocs.push_back({delim, interpolation, indented}); + state->full_state->in_state = RubyFullState::HEREDOC; + heredoc_first = true; + } + i = j; + continue; + } + if (text[i] == '#') { + tokens->push_back({i, len, 1}); + return state; + } else if (text[i] == ':') { + uint32_t start = i; + i++; + if (i >= len) { + tokens->push_back({start, i, 3}); + continue; + } + if (text[i] == '\'' || text[i] == '"') { + tokens->push_back({start, i, 6}); + continue; + } + if (text[i] == '$' || text[i] == '@') { + uint32_t var_start = i; + i++; + if (i < len && text[var_start] == '@' && text[var_start + 1] == '@') + i++; + while (i < len && identifier_char(text[i])) + i++; + tokens->push_back({start, i, 6}); + continue; + } + uint32_t op_len = operator_trie.match(text, i, len, identifier_char); + if (op_len > 0) { + tokens->push_back({start, i + op_len, 6}); + i += op_len; + continue; + } + if (identifier_start_char(text[i])) { + uint32_t word_len = get_next_word(text, i, len); + tokens->push_back({start, i + word_len, 6}); + i += word_len; + continue; + } + tokens->push_back({start, i, 3}); + continue; + } else if (text[i] == '@') { + uint32_t start = i; + i++; + if (i >= len) + continue; + if (text[i] == '@') + i++; + if (i < len && identifier_start_char(text[i])) + i++; + else + continue; + while (i < len && identifier_char(text[i])) + i++; + tokens->push_back({start, i, 7}); + continue; + } else if (text[i] == '$') { + uint32_t start = i; + i++; + if (i >= len) + continue; + if (identifier_start_char(text[i])) { + i++; + while (i < len && identifier_char(text[i])) + i++; + } else if (i + 1 < len && text[i] == '-' && isalpha(text[i + 1])) { + i += 2; + } else if (isdigit(text[i])) { + i++; + while (i < len && isdigit(text[i])) + i++; + } else if (text[i] != '-' && !isalnum(text[i])) { + i++; + } else { + continue; + } + tokens->push_back({start, i, 8}); + continue; + } else if (text[i] == '?') { + uint32_t start = i; + i++; + if (i < len && text[i] == '\\') { + i++; + if (i < len && text[i] == 'x') { + i++; + if (i < len && isxdigit(text[i])) + i++; + else + continue; + if (i < len && isxdigit(text[i])) + i++; + tokens->push_back({start, i, 7}); + continue; + } else if (i < len && text[i] == 'u') { + i++; + if (i < len && isxdigit(text[i])) + i++; + else + continue; + if (i < len && isxdigit(text[i])) + i++; + else + continue; + if (i < len && isxdigit(text[i])) + i++; + else + continue; + if (i < len && isxdigit(text[i])) + i++; + else + continue; + tokens->push_back({start, i, 7}); + continue; + } else if (i < len) { + i++; + tokens->push_back({start, i, 7}); + continue; + } + } else if (i < len && text[i] != ' ') { + i++; + tokens->push_back({start, i, 7}); + continue; + } else { + tokens->push_back({start, i, 3}); + continue; + } + } else if (text[i] == '{') { + tokens->push_back({i, i + 1, 3}); + state = ensure_state(state); + state->interp_level++; + i++; + continue; + } else if (text[i] == '}') { + state = ensure_full_state(state); + state->interp_level--; + if (state->interp_level == 0 && !state->interp_stack.empty()) { + state->full_state = state->interp_stack.top(); + state->interp_stack.pop(); + tokens->push_back({i, i + 1, 10}); + } else { + tokens->push_back({i, i + 1, 3}); + } + i++; + continue; + } else if (text[i] == '\'') { + tokens->push_back({i, i + 1, 2}); + state = ensure_full_state(state); + state->full_state->in_state = RubyFullState::STRING; + state->full_state->lit.delim_start = '\''; + state->full_state->lit.delim_end = '\''; + state->full_state->lit.allow_interp = false; + i++; + continue; + } else if (text[i] == '"') { + tokens->push_back({i, i + 1, 2}); + state = ensure_full_state(state); + state->full_state->in_state = RubyFullState::STRING; + state->full_state->lit.delim_start = '"'; + state->full_state->lit.delim_end = '"'; + state->full_state->lit.allow_interp = true; + i++; + continue; + } else if (text[i] == '`') { + tokens->push_back({i, i + 1, 2}); + state = ensure_full_state(state); + state->full_state->in_state = RubyFullState::STRING; + state->full_state->lit.delim_start = '`'; + state->full_state->lit.delim_end = '`'; + state->full_state->lit.allow_interp = true; + i++; + continue; + } else if (text[i] == '%') { + if (i + 1 >= len) { + i++; + continue; + } + char type = text[i + 1]; + char delim_start = '\0'; + char delim_end = '\0'; + bool allow_interp = true; + int prefix_len = 1; + switch (type) { + case 'Q': + case 'x': + allow_interp = true; + prefix_len = 2; + break; + case 'w': + case 'q': + case 'i': + allow_interp = false; + prefix_len = 2; + break; + default: + allow_interp = true; + prefix_len = 1; + break; + } + if (i + prefix_len >= len) { + i += prefix_len; + continue; + } + delim_start = text[i + prefix_len]; + if (!isascii(delim_start) || + (isalnum(delim_start) || delim_start == '_' || delim_start == ' ')) { + i += prefix_len; + continue; + } + switch (delim_start) { + case '(': + delim_end = ')'; + break; + case '{': + delim_end = '}'; + break; + case '[': + delim_end = ']'; + break; + case '<': + delim_end = '>'; + break; + default: + delim_end = delim_start; + break; + } + tokens->push_back({i, i + prefix_len + 1, 2}); + state = ensure_full_state(state); + state->full_state->in_state = RubyFullState::STRING; + state->full_state->lit.delim_start = delim_start; + state->full_state->lit.delim_end = delim_end; + state->full_state->lit.allow_interp = allow_interp; + state->full_state->lit.brace_level = 1; + i += prefix_len + 1; + continue; + } else if (isdigit(text[i])) { + uint32_t start = i; + if (text[i] == '0') { + i++; + if (i < len && text[i] == 'x') { + i++; + if (i < len && isxdigit(text[i])) + i++; + else + continue; + bool is_underscore = false; + while (i < len && (isxdigit(text[i]) || text[i] == '_')) { + if (text[i] == '_') + is_underscore = true; + else + is_underscore = false; + i++; + } + if (is_underscore) + i--; + } else if (i < len && text[i] == 'b') { + i++; + if (i < len && (text[i] == '0' || text[i] == '1')) + i++; + else + continue; + bool is_underscore = false; + while (i < len && + (text[i] == '0' || text[i] == '1' || text[i] == '_')) { + if (text[i] == '_') + is_underscore = true; + else + is_underscore = false; + i++; + } + if (is_underscore) + i--; + } else if (i < len && text[i] == 'o') { + i++; + if (i < len && text[i] >= '0' && text[i] <= '7') + i++; + else + continue; + bool is_underscore = false; + while (i < len && + ((text[i] >= '0' && text[i] <= '7') || text[i] == '_')) { + if (text[i] == '_') + is_underscore = true; + else + is_underscore = false; + i++; + } + if (is_underscore) + i--; + } + } else { + bool is_underscore = true; + while (i < len && + (isdigit(text[i]) || (text[i] == '_' && !is_underscore))) { + if (text[i] == '_') + is_underscore = true; + else + is_underscore = false; + i++; + } + if (is_underscore) + i--; + if (i < len && text[i] == '.') { + i++; + bool is_underscore = true; + while (i < len && + (isdigit(text[i]) || (text[i] == '_' && !is_underscore))) { + if (text[i] == '_') + is_underscore = true; + else + is_underscore = false; + i++; + } + if (is_underscore) + i--; + } + if (i < len && (text[i] == 'E' || text[i] == 'e')) { + i++; + if (i < len && (text[i] == '+' || text[i] == '-')) + i++; + bool is_underscore = true; + while (i < len && + (isdigit(text[i]) || (text[i] == '_' && !is_underscore))) { + if (text[i] == '_') + is_underscore = true; + else + is_underscore = false; + i++; + } + if (is_underscore) + i--; + } + } + tokens->push_back({start, i, 9}); + continue; + } else if (identifier_start_char(text[i])) { + uint32_t length; + if ((length = base_keywords_trie.match(text, i, len, identifier_char)) > + 0) { + tokens->push_back({i, i + length, 4}); + i += length; + continue; + } else if ((length = operator_keywords_trie.match(text, i, len, + identifier_char)) > 0) { + tokens->push_back({i, i + length, 5}); + i += length; + continue; + } else if (text[i] >= 'A' && text[i] <= 'Z') { + uint32_t start = i; + i += get_next_word(text, i, len); + tokens->push_back({start, i, 10}); + continue; + } else { + uint32_t start = i; + while (i < len && identifier_char(text[i])) + i++; + if (i < len && text[i] == ':') { + i++; + tokens->push_back({start, i, 6}); + continue; + } else if (i < len && (text[i] == '!' || text[i] == '?')) { + i++; + } + continue; + } + } else { + uint32_t op_len; + if ((op_len = operator_trie.match(text, i, len, + [](char) { return false; })) > 0) { + tokens->push_back({i, i + op_len, 3}); + i += op_len; + continue; + } + } + i += utf8_codepoint_width(text[i]); + } + return state; +} + +bool ruby_state_match(std::shared_ptr state_1, + std::shared_ptr state_2) { + if (!state_1 || !state_2) + return false; + return *std::static_pointer_cast(state_1) == + *std::static_pointer_cast(state_2); +} + +// function calls matched with alphanumeric names followed immediately by ! +// or ? or `(` immediately or siwth space or are followed by a non-keyword +// or non-operator (some operators like - for negating and ! for not or { +// for block might be allowed?) +// a word following :: or . is matched as a property +// and any random word is matched as a variable name +// or as a class/module name if it starts with a capital letter +// +// regex are matched as text within / and / as long as +// the first / is not +// following a literal (int/float/string) or variable or brace close +// and is following a keyword or operator liek return /regex/ or x = +// /regex/ . so maybe add feild expecting_expr to state that is true right +// after keyword or some operators like = , =~ , `,` etc? +// +// (left to implement) - +// +// words - breaks up into these submatches +// - Constants that start with a capital letter +// - a word following :: or . is matched as a property +// - function call if ending with ! or ? or ( or are followed by a +// non-keyword or non-operator . ill figure it out +// +// regex (and distinguish between / for division and / for regex) and +// %r{} ones too +// +// Matching brace colors by brace depth +// diff --git a/src/syntax/syntax.cc b/src/syntax/syntax.cc index e69de29..682ebdf 100644 --- a/src/syntax/syntax.cc +++ b/src/syntax/syntax.cc @@ -0,0 +1,167 @@ +#include "io/knot.h" +#include "main.h" +#include "syntax/langs.h" +#include "syntax/parser.h" + +Parser::Parser(Knot *n_root, std::shared_mutex *n_knot_mutex, + std::string n_lang, uint32_t n_scroll_max) { + scroll_max = n_scroll_max; + line_data.reserve(n_root->line_count + 1); + knot_mutex = n_knot_mutex; + lang = n_lang; + auto pair = parsers.find(n_lang); + if (pair != parsers.end()) { + parse_func = std::get<0>(pair->second); + state_match_func = std::get<1>(pair->second); + } else { + assert("unknown lang should be checked by caller" && 0); + } + edit(n_root, 0, 0, n_root->line_count); +} + +void Parser::edit(Knot *n_root, uint32_t start_line, uint32_t old_end_line, + uint32_t new_end_line) { + std::lock_guard lock(data_mutex); + root = n_root; + if (((int64_t)old_end_line - (int64_t)start_line) > 0) + line_data.erase(line_data.begin() + start_line, + line_data.begin() + start_line + old_end_line - start_line); + if (((int64_t)new_end_line - (int64_t)old_end_line) > 0) + line_data.insert(line_data.begin() + start_line, + new_end_line - old_end_line, LineData{}); + dirty_lines.insert(start_line); +} + +void Parser::work() { + std::shared_lock k_lock(*knot_mutex); + k_lock.unlock(); + uint32_t capacity = 256; + char *text = (char *)calloc((capacity + 1), sizeof(char)); + std::set tmp_dirty; + std::unique_lock lock_data(data_mutex); + tmp_dirty.swap(dirty_lines); + lock_data.unlock(); + std::set remaining_dirty; + for (uint32_t c_line : tmp_dirty) { + if (c_line > scroll_max) { + remaining_dirty.insert(c_line); + continue; + } + std::unique_lock lock(mutex); + uint32_t line_count = (uint32_t)line_data.size(); + std::shared_ptr prev_state = + (c_line > 0) ? line_data[c_line - 1].out_state : nullptr; + lock.unlock(); + while (c_line < line_count) { + if (!running.load(std::memory_order_relaxed)) { + free(text); + return; + } + k_lock.lock(); + uint32_t r_offset, r_len; + r_offset = line_to_byte(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(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(); + lock_data.lock(); + std::shared_ptr new_state = + parse_func(&line_data[c_line].tokens, prev_state, text, r_len); + lock_data.unlock(); + line_data[c_line].in_state = prev_state; + line_data[c_line].out_state = new_state; + if (lock.owns_lock()) + lock.unlock(); + if (!running.load(std::memory_order_relaxed)) { + free(text); + return; + } + prev_state = new_state; + c_line++; + if (c_line < line_count && c_line > scroll_max + 50) { + if (c_line > 0) + remaining_dirty.insert(c_line - 1); + remaining_dirty.insert(c_line); + break; + } + lock.lock(); + if (c_line < line_count && + state_match_func(prev_state, line_data[c_line].in_state)) + break; + lock.unlock(); + } + if (!running.load(std::memory_order_relaxed)) { + free(text); + return; + } + } + free(text); + lock_data.lock(); + dirty_lines = std::move(remaining_dirty); +} + +void Parser::scroll(uint32_t line) { + if (line != scroll_max) { + scroll_max = line; + uint32_t c_line = line > 100 ? line - 100 : 0; + if (line_data.size() < c_line) + return; + if (line_data[c_line].in_state || line_data[c_line].out_state) + return; + std::shared_lock k_lock(*knot_mutex); + k_lock.unlock(); + uint32_t capacity = 256; + char *text = (char *)calloc((capacity + 1), sizeof(char)); + std::unique_lock lock_data(data_mutex); + lock_data.unlock(); + std::unique_lock lock(mutex); + uint32_t line_count = (uint32_t)line_data.size(); + std::shared_ptr prev_state = + (c_line > 0) ? line_data[c_line - 1].out_state : nullptr; + lock.unlock(); + while (c_line < line_count) { + if (!running.load(std::memory_order_relaxed)) { + free(text); + return; + } + k_lock.lock(); + uint32_t r_offset, r_len; + r_offset = line_to_byte(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(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(); + lock_data.lock(); + std::shared_ptr new_state = + parse_func(&line_data[c_line].tokens, prev_state, text, r_len); + lock_data.unlock(); + line_data[c_line].in_state = nullptr; + line_data[c_line].out_state = new_state; + if (lock.owns_lock()) + lock.unlock(); + if (!running.load(std::memory_order_relaxed)) { + free(text); + return; + } + prev_state = new_state; + c_line++; + if (c_line < line_count && c_line > scroll_max + 50) + break; + } + free(text); + } else { + scroll_max = line; + } +} diff --git a/src/ts/ts.cc b/src/ts/ts.cc deleted file mode 100644 index 54ba326..0000000 --- a/src/ts/ts.cc +++ /dev/null @@ -1,171 +0,0 @@ -#include "ts/ts.h" -#include "editor/editor.h" -#include "io/knot.h" - -const char *read_ts(void *payload, uint32_t byte_index, TSPoint, - uint32_t *bytes_read) { - Editor *editor = (Editor *)payload; - if (byte_index >= editor->root->char_count) { - *bytes_read = 0; - return ""; - } - return leaf_from_offset(editor->root, byte_index, bytes_read); -} - -void ts_collect_spans(Editor *editor) { - static int parse_counter = 64; - if (!editor->ts.parser || !editor->root || !editor->ts.query) - return; - const bool injections_enabled = editor->root->char_count < (1024 * 20); - for (auto &inj : editor->ts.injections) - inj.second.ranges.clear(); - TSInput tsinput{ - .payload = editor, - .read = read_ts, - .encoding = TSInputEncodingUTF8, - .decode = nullptr, - }; - std::vector edits; - TSInputEdit edit; - if (!editor->edit_queue.empty()) { - while (editor->edit_queue.pop(edit)) - edits.push_back(edit); - if (editor->ts.tree) - for (auto &e : edits) - ts_tree_edit(editor->ts.tree, &e); - for (auto &inj : editor->ts.injections) - if (inj.second.tree) - for (auto &e : edits) - ts_tree_edit(inj.second.tree, &e); - } else if (editor->ts.tree && parse_counter++ < 64) { - return; - } - parse_counter = 0; - std::shared_lock lock(editor->knot_mtx); - editor->spans.mid_parse = true; - TSTree *tree = ts_parser_parse(editor->ts.parser, editor->ts.tree, tsinput); - if (!tree) - return; - if (editor->ts.tree) - ts_tree_delete(editor->ts.tree); - editor->ts.tree = tree; - lock.unlock(); - std::vector new_spans; - new_spans.reserve(4096); - struct PendingRanges { - std::vector ranges; - TSSet *tsset = nullptr; - }; - struct WorkItem { - TSSetBase *tsset; - TSTree *tree; - int depth; - }; - const int kMaxInjectionDepth = 4; - std::vector work; - work.push_back( - {reinterpret_cast(&editor->ts), editor->ts.tree, 0}); - auto overlaps = [](const Span &s, const TSRange &r) { - return !(s.end <= r.start_byte || s.start >= r.end_byte); - }; - auto remove_overlapping_spans = [&](const std::vector &ranges) { - if (ranges.empty()) - return; - new_spans.erase( - std::remove_if(new_spans.begin(), new_spans.end(), - [&](const Span &sp) { - return std::any_of( - ranges.begin(), ranges.end(), - [&](const TSRange &r) { return overlaps(sp, r); }); - }), - new_spans.end()); - }; - while (!work.empty()) { - WorkItem item = work.back(); - work.pop_back(); - TSQuery *q = item.tsset->query; - if (!q) - continue; - TSQueryCursor *cursor = ts_query_cursor_new(); - ts_query_cursor_exec(cursor, q, ts_tree_root_node(item.tsset->tree)); - std::unordered_map pending_injections; - TSQueryMatch match; - auto subject_fn = [&](const TSNode *node, uint32_t *len, - bool *allocated) -> char * { - uint32_t start = ts_node_start_byte(*node); - uint32_t end = ts_node_end_byte(*node); - if (start == end || end > editor->root->char_count) - return nullptr; - std::shared_lock lock(editor->knot_mtx); - char *text = read(editor->root, start, end - start); - *len = end - start; - *allocated = true; - return text; - }; - while (ts_query_cursor_next_match(cursor, &match)) { - if (!ts_predicate(q, match, subject_fn)) - continue; - for (uint32_t i = 0; i < match.capture_count; i++) { - TSQueryCapture cap = match.captures[i]; - uint32_t start = ts_node_start_byte(cap.node); - uint32_t end = ts_node_end_byte(cap.node); - if (Highlight *hl = safe_get(item.tsset->query_map, cap.index)) - new_spans.push_back({start, end, hl}); - if (!injections_enabled) - continue; - if (Language *inj_lang = - safe_get(item.tsset->injection_map, cap.index)) { - auto &pending = pending_injections[inj_lang->name]; - TSSet &tsset = - editor->ts.injections.try_emplace(inj_lang->name).first->second; - if (!tsset.parser) { - tsset.lang = inj_lang->name; - tsset.parser = ts_parser_new(); - ts_parser_set_language(tsset.parser, inj_lang->fn()); - tsset.language = inj_lang->fn(); - tsset.query_file = - get_exe_dir() + "/../grammar/" + inj_lang->name + ".scm"; - tsset.query = load_query(tsset.query_file.c_str(), &tsset); - } - pending.tsset = &tsset; - pending.ranges.push_back(TSRange{ - ts_node_start_point(cap.node), - ts_node_end_point(cap.node), - start, - end, - }); - } - } - } - ts_query_cursor_delete(cursor); - if (injections_enabled && item.depth < kMaxInjectionDepth) { - for (auto &[lang_name, pending] : pending_injections) { - TSSet *tsset = pending.tsset; - if (!tsset || pending.ranges.empty() || !tsset->parser || !tsset->query) - continue; - tsset->ranges = std::move(pending.ranges); - remove_overlapping_spans(tsset->ranges); - ts_parser_set_included_ranges(tsset->parser, tsset->ranges.data(), - tsset->ranges.size()); - lock.lock(); - TSTree *tree = ts_parser_parse(tsset->parser, tsset->tree, tsinput); - if (!tree) - continue; - if (tsset->tree) - ts_tree_delete(tsset->tree); - tsset->tree = tree; - lock.unlock(); - work.push_back({reinterpret_cast(tsset), tsset->tree, - item.depth + 1}); - } - } - } - lock.lock(); - std::pair span_edit; - while (editor->spans.edits.pop(span_edit)) - apply_edit(new_spans, span_edit.first, span_edit.second); - std::sort(new_spans.begin(), new_spans.end()); - editor->spans.mid_parse = false; - std::unique_lock span_mtx(editor->spans.mtx); - editor->spans.spans.swap(new_spans); -} diff --git a/src/ts/utils.cc b/src/ts/utils.cc deleted file mode 100644 index e197bbb..0000000 --- a/src/ts/utils.cc +++ /dev/null @@ -1,167 +0,0 @@ -#include "config.h" -#include "io/sysio.h" -#include "ts/ts.h" -#include - -std::unordered_map regex_cache; - -static inline const TSNode *find_capture_node(const TSQueryMatch &match, - uint32_t capture_id) { - for (uint32_t i = 0; i < match.capture_count; i++) - if (match.captures[i].index == capture_id) - return &match.captures[i].node; - return nullptr; -} - -void clear_regex_cache() { - for (auto &kv : regex_cache) - pcre2_code_free(kv.second); - regex_cache.clear(); -} - -pcre2_code *get_re(const std::string &pattern) { - auto it = regex_cache.find(pattern); - if (it != regex_cache.end()) - return it->second; - int errornum; - PCRE2_SIZE erroffset; - pcre2_code *re = - pcre2_compile((PCRE2_SPTR)pattern.c_str(), PCRE2_ZERO_TERMINATED, 0, - &errornum, &erroffset, nullptr); - regex_cache[pattern] = re; - return re; -} - -TSQuery *load_query(const char *query_path, TSSetBase *set) { - const TSLanguage *lang = set->language; - std::ifstream file(query_path, std::ios::in | std::ios::binary); - if (!file.is_open()) - return nullptr; - std::string highlight_query((std::istreambuf_iterator(file)), - std::istreambuf_iterator()); - int errornumber = 0; - PCRE2_SIZE erroroffset = 0; - pcre2_code *re = pcre2_compile( - (PCRE2_SPTR) R"((@[A-Za-z0-9_.]+)|(;; \#[0-9a-fA-F]{6} \#[0-9a-fA-F]{6} [01] [01] [01] [01] \d+)|(;; !(\w+)))", - PCRE2_ZERO_TERMINATED, 0, &errornumber, &erroroffset, nullptr); - if (!re) - return nullptr; - pcre2_match_data *match_data = - pcre2_match_data_create_from_pattern(re, nullptr); - std::map capture_name_cache; - Highlight *c_hl = nullptr; - Language c_lang = {"unknown", nullptr, 0}; - int i = 0; - PCRE2_SIZE offset = 0; - PCRE2_SIZE subject_length = highlight_query.size(); - while (offset < subject_length) { - int rc = pcre2_match(re, (PCRE2_SPTR)highlight_query.c_str(), - subject_length, offset, 0, match_data, nullptr); - if (rc <= 0) - break; - PCRE2_SIZE *ovector = pcre2_get_ovector_pointer(match_data); - std::string mct = - highlight_query.substr(ovector[0], ovector[1] - ovector[0]); - if (!mct.empty() && mct[0] == '@') { - std::string capture_name = mct; - if (!capture_name_cache.count(capture_name)) { - if (c_hl) { - set->query_map[i] = *c_hl; - delete c_hl; - c_hl = nullptr; - } - if (c_lang.fn != nullptr) { - set->injection_map[i] = c_lang; - c_lang = {"unknown", nullptr, 0}; - } - capture_name_cache[capture_name] = i; - i++; - } - } else if (mct.substr(0, 4) == ";; #") { - if (c_hl) - delete c_hl; - c_hl = new Highlight(); - c_hl->fg = HEX(mct.substr(4, 6)); - c_hl->bg = HEX(mct.substr(12, 6)); - int bold = std::stoi(mct.substr(19, 1)); - int italic = std::stoi(mct.substr(21, 1)); - int underline = std::stoi(mct.substr(23, 1)); - int strike = std::stoi(mct.substr(25, 1)); - c_hl->priority = std::stoi(mct.substr(27)); - c_hl->flags = (bold ? CF_BOLD : 0) | (italic ? CF_ITALIC : 0) | - (underline ? CF_UNDERLINE : 0) | - (strike ? CF_STRIKETHROUGH : 0); - } else if (mct.substr(0, 4) == ";; !") { - auto it = kLanguages.find(mct.substr(4)); - if (it != kLanguages.end()) - c_lang = it->second; - else - c_lang = {"unknown", nullptr, 0}; - } - offset = ovector[1]; - } - if (c_hl) - delete c_hl; - pcre2_match_data_free(match_data); - pcre2_code_free(re); - uint32_t error_offset = 0; - TSQueryError error_type = (TSQueryError)0; - TSQuery *q = ts_query_new(lang, highlight_query.c_str(), - (uint32_t)highlight_query.length(), &error_offset, - &error_type); - if (!q) - log("Failed to create TSQuery at offset %u, error type %d", error_offset, - (int)error_type); - return q; -} - -bool ts_predicate( - TSQuery *query, const TSQueryMatch &match, - std::function - subject_fn) { - uint32_t step_count; - const TSQueryPredicateStep *steps = - ts_query_predicates_for_pattern(query, match.pattern_index, &step_count); - if (!steps || step_count != 4) - return true; - std::string command; - std::string regex_txt; - uint32_t subject_id = 0; - for (uint32_t i = 0; i < step_count; i++) { - const TSQueryPredicateStep *step = &steps[i]; - if (step->type == TSQueryPredicateStepTypeDone) - break; - switch (step->type) { - case TSQueryPredicateStepTypeString: { - uint32_t length = 0; - const char *s = - ts_query_string_value_for_id(query, step->value_id, &length); - if (i == 0) - command.assign(s, length); - else - regex_txt.assign(s, length); - break; - } - case TSQueryPredicateStepTypeCapture: { - subject_id = step->value_id; - break; - } - case TSQueryPredicateStepTypeDone: - break; - } - } - const TSNode *node = find_capture_node(match, subject_id); - pcre2_code *re = get_re(regex_txt); - uint32_t len; - bool allocated; - char *subject = subject_fn(node, &len, &allocated); - if (!subject) - return false; - pcre2_match_data *md = pcre2_match_data_create_from_pattern(re, nullptr); - int rc = pcre2_match(re, (PCRE2_SPTR)subject, len, 0, 0, md, nullptr); - pcre2_match_data_free(md); - bool ok = (rc >= 0); - if (allocated) - free(subject); - return (command == "match?" ? ok : !ok); -} diff --git a/src/ui/completionbox.cc b/src/ui/completionbox.cc index 79a095b..77304de 100644 --- a/src/ui/completionbox.cc +++ b/src/ui/completionbox.cc @@ -139,11 +139,11 @@ void CompletionBox::render(Coord pos) { if (start_row < 0) start_row = pos.row + 1; int32_t start_col = pos.col; - if (start_col + size.col > cols) { - start_col = cols - size.col; - if (start_col < 0) - start_col = 0; - } + // if (start_col + size.col > cols) { + // start_col = cols - size.col; + // if (start_col < 0) + // start_col = 0; + // } position = {(uint32_t)start_row, (uint32_t)start_col}; for (uint32_t r = 0; r < size.row; r++) for (uint32_t c = 0; c < size.col; c++) diff --git a/src/ui/diagnostics.cc b/src/ui/diagnostics.cc index f20fb70..dca2720 100644 --- a/src/ui/diagnostics.cc +++ b/src/ui/diagnostics.cc @@ -148,11 +148,11 @@ void DiagnosticBox::render(Coord pos) { if (start_row < 0) start_row = pos.row + 1; int32_t start_col = pos.col; - if (start_col + size.col > cols) { - start_col = cols - size.col; - if (start_col < 0) - start_col = 0; - } + // if (start_col + size.col > cols) { + // start_col = cols - 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, diff --git a/src/ui/hover.cc b/src/ui/hover.cc index b05b470..a24f971 100644 --- a/src/ui/hover.cc +++ b/src/ui/hover.cc @@ -1,5 +1,5 @@ #include "ui/hover.h" -#include "ts/ts.h" +#include "syntax/decl.h" void HoverBox::clear() { text = ""; @@ -24,107 +24,6 @@ void HoverBox::scroll(int32_t number) { void HoverBox::render_first(bool scroll) { if (!scroll) { - std::vector base_spans; - std::vector injected_spans; - TSSetBase ts = TSSetBase{}; - if (is_markup) { - highlights.clear(); - highlights.reserve(1024); - base_spans.reserve(1024); - injected_spans.reserve(1024); - hover_spans.clear(); - hover_spans.reserve(1024); - std::string query_path = get_exe_dir() + "/../grammar/hover.scm"; - ts.language = LANG(markdown)(); - ts.query = load_query(query_path.c_str(), &ts); - ts.parser = ts_parser_new(); - ts_parser_set_language(ts.parser, ts.language); - ts.tree = ts_parser_parse_string(ts.parser, nullptr, text.c_str(), - text.length()); - TSQueryCursor *cursor = ts_query_cursor_new(); - ts_query_cursor_exec(cursor, ts.query, ts_tree_root_node(ts.tree)); - TSQueryMatch match; - auto subject_fn = [&](const TSNode *node, uint32_t *len, - bool *allocated) -> char * { - uint32_t start = ts_node_start_byte(*node); - uint32_t end = ts_node_end_byte(*node); - *len = end - start; - *allocated = false; - return text.data() + start; - }; - while (ts_query_cursor_next_match(cursor, &match)) { - if (!ts_predicate(ts.query, match, subject_fn)) - continue; - for (uint32_t i = 0; i < match.capture_count; i++) { - TSQueryCapture cap = match.captures[i]; - uint32_t start = ts_node_start_byte(cap.node); - uint32_t end = ts_node_end_byte(cap.node); - if (Language *inj_lang = safe_get(ts.injection_map, cap.index)) { - TSSetBase inj_ts = TSSetBase{}; - inj_ts.language = inj_lang->fn(); - inj_ts.query_file = - get_exe_dir() + "/../grammar/" + inj_lang->name + ".scm"; - inj_ts.query = load_query(inj_ts.query_file.c_str(), &inj_ts); - inj_ts.parser = ts_parser_new(); - ts_parser_set_language(inj_ts.parser, inj_ts.language); - TSPoint start_p = ts_node_start_point(cap.node); - TSPoint end_p = ts_node_end_point(cap.node); - std::vector ranges = {{start_p, end_p, start, end}}; - ts_parser_set_included_ranges(inj_ts.parser, ranges.data(), 1); - inj_ts.tree = ts_parser_parse_string(inj_ts.parser, nullptr, - text.c_str(), text.length()); - TSQueryCursor *inj_cursor = ts_query_cursor_new(); - ts_query_cursor_exec(inj_cursor, inj_ts.query, - ts_tree_root_node(inj_ts.tree)); - TSQueryMatch inj_match; - while (ts_query_cursor_next_match(inj_cursor, &inj_match)) { - if (!ts_predicate(inj_ts.query, inj_match, subject_fn)) - continue; - for (uint32_t i = 0; i < inj_match.capture_count; i++) { - TSQueryCapture inj_cap = inj_match.captures[i]; - uint32_t start = ts_node_start_byte(inj_cap.node); - uint32_t end = ts_node_end_byte(inj_cap.node); - if (Highlight *hl = safe_get(inj_ts.query_map, inj_cap.index)) { - if (highlights.size() >= 1000) - continue; - highlights.push_back(*hl); - Highlight *hl_f = &highlights.back(); - injected_spans.push_back({start, end, hl_f}); - } - } - } - ts_query_cursor_delete(inj_cursor); - ts_tree_delete(inj_ts.tree); - ts_parser_delete(inj_ts.parser); - ts_query_delete(inj_ts.query); - continue; - } - if (Highlight *hl = safe_get(ts.query_map, cap.index)) { - if (highlights.size() >= 1000) - continue; - highlights.push_back(*hl); - Highlight *hl_f = &highlights.back(); - base_spans.push_back({start, end, hl_f}); - } - } - } - ts_query_cursor_delete(cursor); - ts_query_delete(ts.query); - ts_tree_delete(ts.tree); - ts_parser_delete(ts.parser); - } - for (const auto &inj : injected_spans) { - base_spans.erase(std::remove_if(base_spans.begin(), base_spans.end(), - [&](const Span &base) { - return !(base.end <= inj.start || - base.start >= inj.end); - }), - base_spans.end()); - } - hover_spans.insert(hover_spans.end(), base_spans.begin(), base_spans.end()); - hover_spans.insert(hover_spans.end(), injected_spans.begin(), - injected_spans.end()); - std::sort(hover_spans.begin(), hover_spans.end()); } uint32_t longest_line = 0; uint32_t current_width = 0; @@ -148,12 +47,8 @@ void HoverBox::render_first(bool scroll) { lines_skipped++; i++; } - Spans spans{}; - spans.spans = hover_spans; uint32_t border_fg = 0x82AAFF; uint32_t base_bg = 0; - SpanCursor span_cursor(spans); - span_cursor.sync(i); cells.assign(size.col * 26, ScreenCell{" ", 0, 0, 0, 0, 0}); auto set = [&](uint32_t r, uint32_t c, const char *text, uint32_t fg, uint32_t bg, uint8_t flags) { @@ -174,7 +69,8 @@ void HoverBox::render_first(bool scroll) { int width = display_width(cluster.c_str(), cluster_len); if (c + width > content_width) break; - Highlight *hl = span_cursor.get_highlight(i); + // TODO: Use new highlights + Highlight *hl = nullptr; uint32_t fg = hl ? hl->fg : 0xFFFFFF; uint32_t bg = hl ? hl->bg : 0; uint32_t flags = hl ? hl->flags : 0; @@ -208,11 +104,11 @@ void HoverBox::render(Coord pos) { if (start_row < 0) start_row = pos.row + 1; int32_t start_col = pos.col; - if (start_col + size.col > cols) { - start_col = cols - size.col; - if (start_col < 0) - start_col = 0; - } + // if (start_col + size.col > cols) { + // start_col = cols - 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, diff --git a/src/utils/unicode.cc b/src/utils/unicode.cc index cf48202..941fa2a 100644 --- a/src/utils/unicode.cc +++ b/src/utils/unicode.cc @@ -28,6 +28,18 @@ int display_width(const char *str, size_t len) { return width; } +uint8_t utf8_codepoint_width(unsigned char c) { + if ((c & 0x80) == 0x00) + return 1; + if ((c & 0xE0) == 0xC0) + return 2; + if ((c & 0xF0) == 0xE0) + return 3; + if ((c & 0xF8) == 0xF0) + return 4; + return 1; +} + uint32_t get_visual_col_from_bytes(const char *line, uint32_t len, uint32_t byte_limit) { if (!line)