diff --git a/README.md b/README.md index 01a36b6..ddab1f9 100644 --- a/README.md +++ b/README.md @@ -6,14 +6,12 @@ A TUI IDE. # TODO -- [ ] Get lsp warnings byte offsets/lengths and render them as background color. - [ ] Add support for LSP & autocomplete / snippets. - First research - `textDocument/documentHighlight` - for highlighting stuff (probably tree-sitter is enough) - `textDocument/selectionRange` // - `textDocument/completion` - Obviously - `textDocument/onTypeFormatting` - seems promising for auto formatting (indentation etc) - - `textDocument/inlayHint` & `textDocument/inlineHint` & `textDocument/codeLens` - `textDocument/formatting` & `textDocument/rangeFormatting` - `textDocument/semanticTokens/*` (probably tree-sitter is enough) - `textDocument/linkedEditingRange` - probably useful @@ -28,9 +26,6 @@ A TUI IDE. 2. One for stuff that only affects highlighting and styles . like symbol highlighting etc. 3. One for Warnings/errors and inlay hints etc. (stuff that adds virtual text to the editor) 4. One for fromatting and stuff like that. (stuff that edits the buffer text) -- [ ] Make tree sitter spans truly incremental - or atleast make them pos based and not byte so minor changes only shifts inline - - And make inner trees incremental too -- [ ] Use LSP to add inlay hints in order to test virtual text. then make an iterator over screen that mimics the renderer for scrolling functions. - [ ] Add codeium/copilot support for auto-completion (uses the VAI virtual text) as a test phase. - [ ] Add a whitespace highlighter (nerd font). for spaces and tabs at start/end of line. not as virtual but instead at render time. - [ ] Once renderer is proven to work well (i.e. redo this commit) merge `experimental` branch into `main`. commit `43f443e` on `experimental`. @@ -42,7 +37,6 @@ A TUI IDE. - [ ] Add alt + click to set multiple cursors. - [ ] Add search / replace along with search / virtual cursors are searched pos. - [ ] Add support for undo/redo. -- [ ] Add `.scm` files for all the supported languages. (2/14) Done. - [ ] Add splash screen / minigame jumping. - [ ] Normalize / validate unicode on file open. - [ ] Add git stuff. diff --git a/grammar/javascript.scm b/grammar/javascript.scm index 1d81abf..50fad16 100644 --- a/grammar/javascript.scm +++ b/grammar/javascript.scm @@ -14,7 +14,7 @@ (#match? @variable.builtin "^(arguments|console|window|document|globalThis|process|module|exports)$")) -;; #59C2FF #000000 0 0 0 3 +;; #59C2FF #000000 0 0 0 1 ((identifier) @constructor (#match? @constructor "^[A-Z][a-zA-Z0-9]*$")) @@ -84,12 +84,11 @@ key: (property_identifier) @name value: [(arrow_function) (function_expression)]) @definition.function -;; #59C2FF #000000 0 0 0 2 +;; #59C2FF #000000 0 0 0 0 ( (call_expression function: (identifier) @name) @reference.call - (#not-match? @name "^(require)$") -) + (#not-match? @name "^(require)$")) ;; #7dcfff #000000 0 0 0 2 (new_expression diff --git a/include/editor.h b/include/editor.h index 550c79b..e6fe823 100644 --- a/include/editor.h +++ b/include/editor.h @@ -94,17 +94,12 @@ struct SpanCursor { } }; -struct VHint { - Coord pos; - std::string hint; - - bool operator<(const VHint &other) const { return pos < other.pos; } -}; - struct VWarn { uint32_t line; std::string text; - int8_t type; // For hl + int8_t type; + uint32_t start; + uint32_t end{UINT32_MAX}; bool operator<(const VWarn &other) const { return line < other.line; } }; @@ -123,6 +118,7 @@ struct TSSetBase { TSParser *parser; std::string query_file; TSQuery *query; + TSTree *tree; std::map query_map; std::map injection_map; const TSLanguage *language; @@ -133,7 +129,6 @@ struct TSSet : TSSetBase { }; struct TSSetMain : TSSetBase { - TSTree *tree; std::unordered_map injections; }; @@ -158,7 +153,6 @@ struct Editor { uint32_t hooks[94]; bool jumper_set; std::shared_mutex v_mtx; - std::vector hints; std::vector warnings; VAI ai; std::shared_mutex lsp_mtx; diff --git a/src/editor.cc b/src/editor.cc index 8078f05..7d2c5a0 100644 --- a/src/editor.cc +++ b/src/editor.cc @@ -253,6 +253,25 @@ void render_editor(Editor *editor) { if (editor->selection_active && absolute_byte_pos >= sel_start && absolute_byte_pos < sel_end) bg = 0x555555; + for (const auto &w : line_warnings) { + if (w.start <= current_byte_offset + local_render_offset && + current_byte_offset + local_render_offset < w.end) { + switch (w.type) { + case 1: + bg = 0x500000; + break; + case 2: + bg = 0x505000; + break; + case 3: + bg = 0x500050; + break; + case 4: + bg = 0x505050; + break; + } + } + } uint32_t cluster_len = grapheme_next_character_break_utf8( line + current_byte_offset + local_render_offset, line_left); std::string cluster(line + current_byte_offset + local_render_offset, diff --git a/src/editor_events.cc b/src/editor_events.cc index 709bab4..2bff03d 100644 --- a/src/editor_events.cc +++ b/src/editor_events.cc @@ -611,6 +611,10 @@ void editor_lsp_handle(Editor *editor, json msg) { json d = diagnostics[i]; VWarn w; w.line = d["range"]["start"]["line"]; + w.start = d["range"]["start"]["character"]; + uint32_t end = d["range"]["end"]["character"]; + if (d["range"]["end"]["line"] == w.line) + w.end = end; std::string text = d["message"].get(); auto pos = text.find('\n'); w.text = (pos == std::string::npos) ? text : text.substr(0, pos); diff --git a/src/ts.cc b/src/ts.cc index 77f62c3..e03a929 100644 --- a/src/ts.cc +++ b/src/ts.cc @@ -204,35 +204,40 @@ void ts_collect_spans(Editor *editor) { .encoding = TSInputEncodingUTF8, .decode = nullptr, }; - TSTree *tree = nullptr; - TSTree *copy = nullptr; - std::unique_lock knot_mtx(editor->knot_mtx); - if (editor->ts.tree) - copy = ts_tree_copy(editor->ts.tree); - knot_mtx.unlock(); std::vector edits; TSInputEdit edit; - if (copy) - while (editor->edit_queue.pop(edit)) { + if (!editor->edit_queue.empty()) { + while (editor->edit_queue.pop(edit)) edits.push_back(edit); - ts_tree_edit(copy, &edits.back()); + if (editor->ts.tree) { + for (auto &e : edits) + ts_tree_edit(editor->ts.tree, &e); } - if (copy && edits.empty() && parse_counter < 64) { + for (auto &inj : editor->ts.injections) { + if (inj.second.tree) { + for (auto &e : edits) { + TSInputEdit inj_edit = e; + for (auto &r : inj.second.ranges) { + if (e.start_byte >= r.start_byte && e.start_byte <= r.end_byte) { + inj_edit.start_byte -= r.start_byte; + inj_edit.old_end_byte -= r.start_byte; + inj_edit.new_end_byte -= r.start_byte; + } + } + ts_tree_edit(inj.second.tree, &inj_edit); + } + } + } + } else if (editor->ts.tree && parse_counter < 64) { parse_counter++; - ts_tree_delete(copy); return; } parse_counter = 0; editor->spans.mid_parse = true; std::shared_lock lock(editor->knot_mtx); - tree = ts_parser_parse(editor->ts.parser, copy, tsinput); + editor->ts.tree = + ts_parser_parse(editor->ts.parser, editor->ts.tree, tsinput); lock.unlock(); - if (copy) - ts_tree_delete(copy); - if (editor->ts.tree) - ts_tree_delete(editor->ts.tree); - editor->ts.tree = tree; - copy = ts_tree_copy(tree); std::vector new_spans; new_spans.reserve(4096); struct PendingRanges { @@ -243,12 +248,11 @@ void ts_collect_spans(Editor *editor) { TSSetBase *tsset; TSTree *tree; int depth; - TSSet *as_injection; }; const int kMaxInjectionDepth = 4; std::vector work; work.push_back( - {reinterpret_cast(&editor->ts), copy, 0, nullptr}); + {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); }; @@ -268,12 +272,10 @@ void ts_collect_spans(Editor *editor) { WorkItem item = work.back(); work.pop_back(); TSQuery *q = item.tsset->query; - if (!q) { - ts_tree_delete(item.tree); + if (!q) continue; - } TSQueryCursor *cursor = ts_query_cursor_new(); - ts_query_cursor_exec(cursor, q, ts_tree_root_node(item.tree)); + ts_query_cursor_exec(cursor, q, ts_tree_root_node(item.tsset->tree)); std::unordered_map pending_injections; TSQueryMatch match; while (ts_query_cursor_next_match(cursor, &match)) { @@ -322,13 +324,12 @@ void ts_collect_spans(Editor *editor) { ts_parser_set_included_ranges(tsset->parser, tsset->ranges.data(), tsset->ranges.size()); lock.lock(); - TSTree *inj_tree = ts_parser_parse(tsset->parser, nullptr, tsinput); + tsset->tree = ts_parser_parse(tsset->parser, tsset->tree, tsinput); lock.unlock(); - work.push_back({reinterpret_cast(tsset), inj_tree, - item.depth + 1, tsset}); + work.push_back({reinterpret_cast(tsset), tsset->tree, + item.depth + 1}); } } - ts_tree_delete(item.tree); } std::pair span_edit; while (editor->spans.edits.pop(span_edit))