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
- [ ] 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 support for LSP & autocomplete / snippets.
- 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)
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.
- [ ] 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.
- [ ] 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)
@@ -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 this thing where selection double click on a bracket selects whole block.
- (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)
- [ ] Make whole thing event driven and not clock driven.

View File

@@ -123,6 +123,12 @@
;; !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
(info_string
(language) @injection.language (#match? @injection.language "^h$"))

View File

@@ -33,6 +33,8 @@ struct Editor {
Queue<TSInputEdit> edit_queue;
std::vector<Fold> folds;
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;
uint32_t hooks[94];
bool jumper_set;

View File

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

View File

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

View File

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

View File

@@ -1,3 +1,4 @@
#include <cstdint>
extern "C" {
#include "../libs/libgrapheme/grapheme.h"
}
@@ -206,6 +207,14 @@ void render_editor(Editor *editor) {
break;
if (line_len > 0 && line[line_len - 1] == '\n')
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;
while (warn_it != editor->warnings.end() && warn_it->line == line_index) {
line_warnings.push_back(*warn_it);
@@ -258,9 +267,9 @@ void render_editor(Editor *editor) {
uint8_t fl = hl ? hl->flags : 0;
if (def_hl) {
if (def_hl->fg != 0)
fg |= def_hl->fg;
fg = def_hl->fg;
if (def_hl->bg != 0)
bg |= def_hl->bg;
bg = def_hl->bg;
fl |= def_hl->flags;
}
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);
if (col + width > render_width)
break;
update(editor->position.row + rendered_rows, render_x + col,
cluster.c_str(), fg, bg | color, fl, u_color);
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,
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;
line_left -= cluster_len;
col += width;

View File

@@ -264,7 +264,6 @@ void handle_editor_event(Editor *editor, KeyEvent event) {
pending->editor = editor;
pending->method = "textDocument/hover";
pending->callback = [](Editor *editor, std::string, json hover) {
log("%s\n", hover.dump().c_str());
if (hover.contains("result") && !hover["result"].is_null()) {
auto &contents = hover["result"]["contents"];
std::string hover_text = "";
@@ -367,6 +366,45 @@ void handle_editor_event(Editor *editor, KeyEvent event) {
case CTRL('s'):
save_file(editor);
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':
uint32_t len;
char *text = get_from_clipboard(&len);
@@ -652,7 +690,7 @@ void hover_diagnostic(Editor *editor) {
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) {
if (!editor || !editor->root)
@@ -665,17 +703,18 @@ void editor_worker(Editor *editor) {
ts_collect_spans(editor);
uint32_t 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) {
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);
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::vector<std::pair<size_t, size_t>> results =
search_rope(editor->root, buf);
std::unique_lock lock(editor->def_spans.mtx);
editor->def_spans.spans.clear();
for (const auto &match : results) {
Span s;
s.start = match.first;
@@ -683,15 +722,35 @@ void editor_worker(Editor *editor) {
s.hl = &HL_UNDERLINE;
editor->def_spans.spans.push_back(s);
}
std::sort(editor->def_spans.spans.begin(), editor->def_spans.spans.end());
lock.unlock();
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);
}

View File

@@ -84,11 +84,12 @@ std::shared_ptr<LSPInstance> get_or_init_lsp(uint8_t lsp_id) {
lsp->incremental_sync = (change_type == 2);
}
}
if (caps.contains("hoverProvider")) {
if (caps.contains("hoverProvider"))
lsp->allow_hover = caps["hoverProvider"].get<bool>();
} else {
else
lsp->allow_hover = false;
}
if (caps.contains("completionProvider"))
lsp->allow_completion = true;
}
lsp->initialized = true;
json initialized = {{"jsonrpc", "2.0"},
@@ -110,7 +111,20 @@ std::shared_ptr<LSPInstance> get_or_init_lsp(uint8_t lsp_id) {
{"capabilities",
{{"textDocument",
{{"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);
active_lsps[lsp_id] = 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].bg = bg;
screen[idx].flags = flags;
screen[idx].ul_color = 0;
}
void update(uint32_t row, uint32_t col, const char *utf8, uint32_t fg,
@@ -77,6 +78,7 @@ void update(uint32_t row, uint32_t col, const char *utf8, uint32_t fg,
screen[idx].fg = fg;
screen[idx].bg = bg;
screen[idx].flags = flags;
screen[idx].ul_color = 0;
}
void update(uint32_t row, uint32_t col, std::string utf8, uint32_t fg,