Cleanup and add visible whitespaces.

This commit is contained in:
2025-12-29 21:55:49 +00:00
parent c7068d33d7
commit 04179d1a4e
10 changed files with 130 additions and 24 deletions

View File

@@ -6,6 +6,9 @@ A TUI IDE.
# TODO # TODO
- [ ] Fix indentation logic
- [ ] For `"insertTextFormat": 2` in `clangd` and similar use only the last word in the signature when replacing
- [ ] Keep a list of words in the current buffer. (for auto completion) (maybe?)
- [ ] Add ecma to js and make tsx - [ ] Add ecma to js and make tsx
- [ ] Add support for LSP & autocomplete / snippets. - [ ] Add support for LSP & autocomplete / snippets.
- First research - First research
@@ -27,7 +30,6 @@ A TUI IDE.
3. One for Warnings/errors and inlay hints etc. (stuff that adds virtual text to the editor) 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) 4. One for fromatting and stuff like that. (stuff that edits the buffer text)
- [ ] Add codeium/copilot support for auto-completion (uses the VAI virtual text) as a test phase. - [ ] Add codeium/copilot support for auto-completion (uses the VAI virtual text) as a test phase.
- [ ] Redo cpp/c/h scm file . also pretty much all of them do manually
- [ ] Add a whitespace highlighter (nerd font). for spaces and tabs at start/end of line. not as virtual but instead at render time. - [ ] 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`. - [ ] Once renderer is proven to work well (i.e. redo this commit) merge `experimental` branch into `main`. commit `43f443e` on `experimental`.
- [ ] Add snippets from wherever i get them. (like luasnip or vsnip) - [ ] Add snippets from wherever i get them. (like luasnip or vsnip)
@@ -50,6 +52,8 @@ A TUI IDE.
- [ ] Add the highlight of block edges when cursor is on a bracket (or in). (prolly from lsp) - [ ] Add the highlight of block edges when cursor is on a bracket (or in). (prolly from lsp)
- [ ] Add this thing where selection double click on a bracket selects whole block. - [ ] Add this thing where selection double click on a bracket selects whole block.
- (only on the first time) and sets mode to `WORD`. - (only on the first time) and sets mode to `WORD`.
- [ ] Redo cpp/c/h scm file . also pretty much all of them do manually
- [ ] Try making `lua-typed` and man pages `tree-sitter` grammar.
- [ ] Redo folding system and its relation to move_line_* functions. (Currently its a mess) - [ ] Redo folding system and its relation to move_line_* functions. (Currently its a mess)
- [ ] Make whole thing event driven and not clock driven. - [ ] Make whole thing event driven and not clock driven.

View File

@@ -123,6 +123,12 @@
;; !cpp ;; !cpp
(code_fence_content) @injection.cpp) (code_fence_content) @injection.cpp)
(fenced_code_block
(info_string
(language) @injection.language (#match? @injection.language "^objective-cpp$"))
;; !cpp
(code_fence_content) @injection.cpp)
(fenced_code_block (fenced_code_block
(info_string (info_string
(language) @injection.language (#match? @injection.language "^h$")) (language) @injection.language (#match? @injection.language "^h$"))

View File

@@ -33,6 +33,8 @@ struct Editor {
Queue<TSInputEdit> edit_queue; Queue<TSInputEdit> edit_queue;
std::vector<Fold> folds; std::vector<Fold> folds;
Spans spans; Spans spans;
// TODO: Split into 2 groups to have their own mutex's . one for word hl and
// one for hex colors
Spans def_spans; Spans def_spans;
uint32_t hooks[94]; uint32_t hooks[94];
bool jumper_set; bool jumper_set;

View File

@@ -33,6 +33,8 @@ struct LSPInstance {
std::atomic<bool> exited = false; std::atomic<bool> exited = false;
bool incremental_sync = false; bool incremental_sync = false;
bool allow_hover = false; bool allow_hover = false;
bool allow_completion = false;
std::string trigger_chars;
uint32_t last_id = 0; uint32_t last_id = 0;
Queue<json> inbox; Queue<json> inbox;
Queue<json> outbox; Queue<json> outbox;

View File

@@ -3,7 +3,6 @@
#include "./pch.h" #include "./pch.h"
#include "./utils.h" #include "./utils.h"
#include <cstdint>
#define KEY_CHAR 0 #define KEY_CHAR 0
#define KEY_SPECIAL 1 #define KEY_SPECIAL 1

View File

@@ -12,9 +12,7 @@ end
# Emoji-heavy strings # Emoji-heavy strings
emojis = "👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏" emojis = "👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏👋🌏"
# Mixed-width CJK blocks # Mixed-width CJK block
#
# https://chatgpt.com/c/695232e9-febc-8331-88c0-b47020948e5b
cjk_samples = [ cjk_samples = [
"漢字テスト", "漢字テスト",
"測試中文字串", "測試中文字串",

View File

@@ -1,3 +1,4 @@
#include <cstdint>
extern "C" { extern "C" {
#include "../libs/libgrapheme/grapheme.h" #include "../libs/libgrapheme/grapheme.h"
} }
@@ -206,6 +207,14 @@ void render_editor(Editor *editor) {
break; break;
if (line_len > 0 && line[line_len - 1] == '\n') if (line_len > 0 && line[line_len - 1] == '\n')
line_len--; line_len--;
uint32_t content_end = line_len;
while (content_end > 0 &&
(line[content_end - 1] == ' ' || line[content_end - 1] == '\t'))
content_end--;
uint32_t content_start = 0;
while (content_start < line_len &&
(line[content_start] == ' ' || line[content_start] == '\t'))
content_start++;
std::vector<VWarn> line_warnings; std::vector<VWarn> line_warnings;
while (warn_it != editor->warnings.end() && warn_it->line == line_index) { while (warn_it != editor->warnings.end() && warn_it->line == line_index) {
line_warnings.push_back(*warn_it); line_warnings.push_back(*warn_it);
@@ -258,9 +267,9 @@ void render_editor(Editor *editor) {
uint8_t fl = hl ? hl->flags : 0; uint8_t fl = hl ? hl->flags : 0;
if (def_hl) { if (def_hl) {
if (def_hl->fg != 0) if (def_hl->fg != 0)
fg |= def_hl->fg; fg = def_hl->fg;
if (def_hl->bg != 0) if (def_hl->bg != 0)
bg |= def_hl->bg; bg = def_hl->bg;
fl |= def_hl->flags; fl |= def_hl->flags;
} }
if (editor->selection_active && absolute_byte_pos >= sel_start && if (editor->selection_active && absolute_byte_pos >= sel_start &&
@@ -297,8 +306,19 @@ void render_editor(Editor *editor) {
int width = display_width(cluster.c_str(), cluster_len); int width = display_width(cluster.c_str(), cluster_len);
if (col + width > render_width) if (col + width > render_width)
break; break;
if (current_byte_offset + local_render_offset >= content_start &&
current_byte_offset + local_render_offset < content_end) {
update(editor->position.row + rendered_rows, render_x + col, update(editor->position.row + rendered_rows, render_x + col,
cluster.c_str(), fg, bg | color, fl, u_color); cluster.c_str(), fg, bg | color, fl, u_color);
} else {
if (cluster[0] == ' ') {
update(editor->position.row + rendered_rows, render_x + col, "·",
0x282828, bg | color, fl, u_color);
} else {
update(editor->position.row + rendered_rows, render_x + col, "-> ",
0x282828, bg | color, (fl & ~CF_BOLD) | CF_ITALIC, u_color);
}
}
local_render_offset += cluster_len; local_render_offset += cluster_len;
line_left -= cluster_len; line_left -= cluster_len;
col += width; col += width;

View File

@@ -264,7 +264,6 @@ void handle_editor_event(Editor *editor, KeyEvent event) {
pending->editor = editor; pending->editor = editor;
pending->method = "textDocument/hover"; pending->method = "textDocument/hover";
pending->callback = [](Editor *editor, std::string, json hover) { pending->callback = [](Editor *editor, std::string, json hover) {
log("%s\n", hover.dump().c_str());
if (hover.contains("result") && !hover["result"].is_null()) { if (hover.contains("result") && !hover["result"].is_null()) {
auto &contents = hover["result"]["contents"]; auto &contents = hover["result"]["contents"];
std::string hover_text = ""; std::string hover_text = "";
@@ -367,6 +366,45 @@ void handle_editor_event(Editor *editor, KeyEvent event) {
case CTRL('s'): case CTRL('s'):
save_file(editor); save_file(editor);
break; break;
case CTRL(' '):
if (editor->lsp) {
json msg = {
{"jsonrpc", "2.0"},
{"method", "textDocument/completion"},
{
"params",
{
{
"textDocument",
{
{"uri", editor->uri},
},
},
{
"position",
{
{"line", editor->cursor.row},
{"character", editor->cursor.col},
},
},
{
"context",
{
{"triggerKind", 1},
},
},
},
},
};
LSPPending *pending = new LSPPending();
pending->editor = editor;
pending->method = "textDocument/completion";
pending->callback = [](Editor *editor, std::string, json completion) {
log("%s\n", completion.dump().c_str());
};
lsp_send(editor->lsp, msg, pending);
}
break;
case 'p': case 'p':
uint32_t len; uint32_t len;
char *text = get_from_clipboard(&len); char *text = get_from_clipboard(&len);
@@ -652,7 +690,7 @@ void hover_diagnostic(Editor *editor) {
editor->diagnostics_active = true; editor->diagnostics_active = true;
} }
static Highlight HL_UNDERLINE = {0, 0, 1 << 2, 100}; static Highlight HL_UNDERLINE = {0, 0, CF_UNDERLINE, UINT8_MAX - 1};
void editor_worker(Editor *editor) { void editor_worker(Editor *editor) {
if (!editor || !editor->root) if (!editor || !editor->root)
@@ -665,17 +703,18 @@ void editor_worker(Editor *editor) {
ts_collect_spans(editor); ts_collect_spans(editor);
uint32_t prev_col, next_col; uint32_t prev_col, next_col;
word_boundaries_exclusive(editor, editor->cursor, &prev_col, &next_col); word_boundaries_exclusive(editor, editor->cursor, &prev_col, &next_col);
std::unique_lock lock(editor->def_spans.mtx);
editor->def_spans.spans.clear();
if (next_col - prev_col > 0 && next_col - prev_col < 256 - 4) { if (next_col - prev_col > 0 && next_col - prev_col < 256 - 4) {
std::shared_lock lock(editor->knot_mtx); std::shared_lock lockk(editor->knot_mtx);
uint32_t offset = line_to_byte(editor->root, editor->cursor.row, nullptr); uint32_t offset = line_to_byte(editor->root, editor->cursor.row, nullptr);
char *word = read(editor->root, offset + prev_col, next_col - prev_col); char *word = read(editor->root, offset + prev_col, next_col - prev_col);
lockk.unlock();
if (word) { if (word) {
char buf[256]; char buf[256];
snprintf(buf, sizeof(buf), "\\b%s\\b", word); snprintf(buf, sizeof(buf), "\\b%s\\b", word);
std::vector<std::pair<size_t, size_t>> results = std::vector<std::pair<size_t, size_t>> results =
search_rope(editor->root, buf); search_rope(editor->root, buf);
std::unique_lock lock(editor->def_spans.mtx);
editor->def_spans.spans.clear();
for (const auto &match : results) { for (const auto &match : results) {
Span s; Span s;
s.start = match.first; s.start = match.first;
@@ -683,15 +722,35 @@ void editor_worker(Editor *editor) {
s.hl = &HL_UNDERLINE; s.hl = &HL_UNDERLINE;
editor->def_spans.spans.push_back(s); editor->def_spans.spans.push_back(s);
} }
std::sort(editor->def_spans.spans.begin(), editor->def_spans.spans.end());
lock.unlock();
free(word); free(word);
} }
} else {
std::unique_lock lock(editor->def_spans.mtx);
editor->def_spans.spans.clear();
lock.unlock();
} }
uint8_t top = 0;
static Highlight *hl_s = (Highlight *)calloc(200, sizeof(Highlight));
if (!hl_s)
exit(ENOMEM);
std::vector<std::pair<size_t, size_t>> results =
search_rope(editor->root, "(0x|#)[0-9a-fA-F]{6}");
std::shared_lock lockk(editor->knot_mtx);
for (int i = 0; i < results.size() && top < 200; i++) {
Span s;
s.start = results[i].first;
s.end = results[i].first + results[i].second;
char *buf = read(editor->root, s.start, s.end - s.start);
int x = buf[0] == '#' ? 1 : 2;
uint32_t bg = HEX(buf + x);
free(buf);
uint8_t r = bg >> 16, g = (bg >> 8) & 0xFF, b = bg & 0xFF;
double luminance = 0.299 * r + 0.587 * g + 0.114 * b;
uint32_t fg = (luminance > 128) ? 0x010101 : 0xFEFEFE;
hl_s[top] = {fg, bg, CF_BOLD, UINT8_MAX};
s.hl = &hl_s[top];
editor->def_spans.spans.push_back(s);
top++;
}
std::sort(editor->def_spans.spans.begin(), editor->def_spans.spans.end());
lock.unlock();
lockk.unlock();
hover_diagnostic(editor); hover_diagnostic(editor);
} }

View File

@@ -84,11 +84,12 @@ std::shared_ptr<LSPInstance> get_or_init_lsp(uint8_t lsp_id) {
lsp->incremental_sync = (change_type == 2); lsp->incremental_sync = (change_type == 2);
} }
} }
if (caps.contains("hoverProvider")) { if (caps.contains("hoverProvider"))
lsp->allow_hover = caps["hoverProvider"].get<bool>(); lsp->allow_hover = caps["hoverProvider"].get<bool>();
} else { else
lsp->allow_hover = false; lsp->allow_hover = false;
} if (caps.contains("completionProvider"))
lsp->allow_completion = true;
} }
lsp->initialized = true; lsp->initialized = true;
json initialized = {{"jsonrpc", "2.0"}, json initialized = {{"jsonrpc", "2.0"},
@@ -110,7 +111,20 @@ std::shared_ptr<LSPInstance> get_or_init_lsp(uint8_t lsp_id) {
{"capabilities", {"capabilities",
{{"textDocument", {{"textDocument",
{{"publishDiagnostics", {{"relatedInformation", true}}}, {{"publishDiagnostics", {{"relatedInformation", true}}},
{"hover", {{"contentFormat", {"markdown", "plaintext"}}}}}}}}}}}; {"hover", {{"contentFormat", {"markdown", "plaintext"}}}},
{"completion",
{{"completionItem",
{{"snippetSupport", true},
{"documentationFormat", {"markdown", "plaintext"}},
{"resolveSupport",
{{"properties", {"documentation", "detail"}}}},
{"insertReplaceSupport", true},
{"labelDetailsSupport", true},
{"insertTextModeSupport", {{"valueSet", json::array({1})}}}}},
{"completionItemKind",
{{"valueSet", {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}}}},
{"contextSupport", true},
{"insertTextMode", 1}}}}}}}}}};
lsp_send(lsp, init_message, pending); lsp_send(lsp, init_message, pending);
active_lsps[lsp_id] = lsp; active_lsps[lsp_id] = lsp;
return lsp; return lsp;

View File

@@ -62,6 +62,7 @@ void update(uint32_t row, uint32_t col, std::string utf8, uint32_t fg,
screen[idx].fg = fg; screen[idx].fg = fg;
screen[idx].bg = bg; screen[idx].bg = bg;
screen[idx].flags = flags; screen[idx].flags = flags;
screen[idx].ul_color = 0;
} }
void update(uint32_t row, uint32_t col, const char *utf8, uint32_t fg, void update(uint32_t row, uint32_t col, const char *utf8, uint32_t fg,
@@ -77,6 +78,7 @@ void update(uint32_t row, uint32_t col, const char *utf8, uint32_t fg,
screen[idx].fg = fg; screen[idx].fg = fg;
screen[idx].bg = bg; screen[idx].bg = bg;
screen[idx].flags = flags; screen[idx].flags = flags;
screen[idx].ul_color = 0;
} }
void update(uint32_t row, uint32_t col, std::string utf8, uint32_t fg, void update(uint32_t row, uint32_t col, std::string utf8, uint32_t fg,