Add lsp warnings and updates support (and other minor fixes)

This commit is contained in:
2025-12-25 18:14:45 +00:00
parent 69b981097e
commit f3c87431a3
15 changed files with 928 additions and 61 deletions

View File

@@ -71,6 +71,10 @@ void render_editor(Editor *editor) {
auto hook_it = v.begin();
while (hook_it != v.end() && hook_it->first <= editor->scroll.row)
++hook_it;
auto warn_it = editor->warnings.begin();
while (warn_it != editor->warnings.end() &&
warn_it->line < editor->scroll.row)
++warn_it;
std::shared_lock knot_lock(editor->knot_mtx);
if (editor->selection_active) {
Coord start, end;
@@ -131,6 +135,7 @@ void render_editor(Editor *editor) {
uint32_t global_byte_offset = line_to_byte(editor->root, line_index, nullptr);
span_cursor.sync(global_byte_offset);
def_span_cursor.sync(global_byte_offset);
std::shared_lock v_lock(editor->v_mtx);
while (rendered_rows < editor->size.row) {
const Fold *fold = fold_for_line(editor->folds, line_index);
if (fold) {
@@ -156,6 +161,8 @@ void render_editor(Editor *editor) {
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)
@@ -174,6 +181,11 @@ void render_editor(Editor *editor) {
break;
if (line_len > 0 && line[line_len - 1] == '\n')
line_len--;
std::vector<VWarn> line_warnings;
while (warn_it != editor->warnings.end() && warn_it->line == line_index) {
line_warnings.push_back(*warn_it);
++warn_it;
}
uint32_t current_byte_offset = 0;
if (rendered_rows == 0)
current_byte_offset += editor->scroll.col;
@@ -257,6 +269,78 @@ void render_editor(Editor *editor) {
0x555555 | color, 0);
col++;
}
if (!line_warnings.empty() && line_left == 0) {
VWarn warn = line_warnings.front();
update(editor->position.row + rendered_rows, render_x + col, " ", 0,
color, 0);
col++;
for (size_t i = 0; i < line_warnings.size(); i++) {
if (line_warnings[i].type < warn.type)
warn = line_warnings[i];
std::string err_sym = " ";
uint32_t fg_color = 0;
switch (line_warnings[i].type) {
case 1:
err_sym = "";
fg_color = 0xFF0000;
goto final;
case 2:
err_sym = "";
fg_color = 0xFFFF00;
goto final;
case 3:
err_sym = "";
fg_color = 0xFF00FF;
goto final;
case 4:
err_sym = "";
fg_color = 0xAAAAAA;
goto final;
final:
if (col < render_width) {
update(editor->position.row + rendered_rows, render_x + col,
err_sym, fg_color, color, 0);
col++;
update(editor->position.row + rendered_rows, render_x + col, " ",
fg_color, color, 0);
col++;
}
}
}
if (col < render_width) {
update(editor->position.row + rendered_rows, render_x + col, " ", 0,
0 | color, 0);
col++;
}
size_t warn_idx = 0;
uint32_t fg_color = 0;
switch (warn.type) {
case 1:
fg_color = 0xFF0000;
break;
case 2:
fg_color = 0xFFFF00;
break;
case 3:
fg_color = 0xFF00FF;
break;
case 4:
fg_color = 0xAAAAAA;
break;
}
while (col < render_width && warn_idx < warn.text.length()) {
uint32_t cluster_len = grapheme_next_character_break_utf8(
warn.text.c_str() + warn_idx, warn.text.length() - warn_idx);
std::string cluster = warn.text.substr(warn_idx, cluster_len);
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_color, color, 0);
col += width;
warn_idx += cluster_len;
}
}
while (col < render_width) {
update(editor->position.row + rendered_rows, render_x + col, " ", 0,
0 | color, 0);

View File

@@ -2,6 +2,7 @@ extern "C" {
#include "../libs/libgrapheme/grapheme.h"
}
#include "../include/editor.h"
#include "../include/lsp.h"
#include "../include/main.h"
#include "../include/utils.h"
@@ -342,6 +343,26 @@ void edit_erase(Editor *editor, Coord pos, int64_t len) {
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);
json lsp_range;
bool do_lsp = (editor->lsp != nullptr);
if (do_lsp) {
LineIterator *it = begin_l_iter(editor->root, point.row);
char *line = next_line(it, nullptr);
int utf16_start = 0;
if (line)
utf16_start = utf8_byte_offset_to_utf16(line, point.col);
free(it->buffer);
free(it);
it = begin_l_iter(editor->root, pos.row);
line = next_line(it, nullptr);
int utf16_end = 0;
if (line)
utf16_end = utf8_byte_offset_to_utf16(line, pos.col);
free(it->buffer);
free(it);
lsp_range = {{"start", {{"line", point.row}, {"character", utf16_start}}},
{"end", {{"line", pos.row}, {"character", utf16_end}}}};
}
uint32_t start = line_to_byte(editor->root, point.row, nullptr) + point.col;
if (cursor_original > start && cursor_original <= byte_pos) {
editor->cursor = point;
@@ -372,6 +393,17 @@ void edit_erase(Editor *editor, Coord pos, int64_t len) {
};
editor->edit_queue.push(edit);
}
if (do_lsp) {
json message = {
{"jsonrpc", "2.0"},
{"method", "textDocument/didChange"},
{"params",
{{"textDocument",
{{"uri", editor->uri}, {"version", ++editor->lsp_version}}},
{"contentChanges",
json::array({{{"range", lsp_range}, {"text", ""}}})}}}};
lsp_send(editor->lsp, message, nullptr);
}
std::unique_lock lock_3(editor->spans.mtx);
apply_edit(editor->spans.spans, start, start - byte_pos);
if (editor->spans.mid_parse)
@@ -386,6 +418,26 @@ void edit_erase(Editor *editor, Coord pos, int64_t len) {
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);
json lsp_range;
bool do_lsp = (editor->lsp != nullptr);
if (do_lsp) {
LineIterator *it = begin_l_iter(editor->root, pos.row);
char *line = next_line(it, nullptr);
int utf16_start = 0;
if (line)
utf16_start = utf8_byte_offset_to_utf16(line, pos.col);
free(it->buffer);
free(it);
it = begin_l_iter(editor->root, point.row);
line = next_line(it, nullptr);
int utf16_end = 0;
if (line)
utf16_end = utf8_byte_offset_to_utf16(line, point.col);
free(it->buffer);
free(it);
lsp_range = {{"start", {{"line", pos.row}, {"character", utf16_start}}},
{"end", {{"line", point.row}, {"character", utf16_end}}}};
}
uint32_t end = line_to_byte(editor->root, point.row, nullptr) + point.col;
if (cursor_original > byte_pos && cursor_original <= end) {
editor->cursor = pos;
@@ -416,6 +468,17 @@ void edit_erase(Editor *editor, Coord pos, int64_t len) {
};
editor->edit_queue.push(edit);
}
if (do_lsp) {
json message = {
{"jsonrpc", "2.0"},
{"method", "textDocument/didChange"},
{"params",
{{"textDocument",
{{"uri", editor->uri}, {"version", ++editor->lsp_version}}},
{"contentChanges",
json::array({{{"range", lsp_range}, {"text", ""}}})}}}};
lsp_send(editor->lsp, message, nullptr);
}
std::unique_lock lock_3(editor->spans.mtx);
apply_edit(editor->spans.spans, byte_pos, byte_pos - end);
if (editor->spans.mid_parse)
@@ -466,6 +529,30 @@ void edit_insert(Editor *editor, Coord pos, char *data, uint32_t len) {
};
editor->edit_queue.push(edit);
}
if (editor->lsp) {
lock_1.lock();
LineIterator *it = begin_l_iter(editor->root, pos.row);
char *line = next_line(it, nullptr);
int utf16_col = 0;
if (line)
utf16_col = utf8_byte_offset_to_utf16(line, pos.col);
free(it->buffer);
free(it);
lock_1.unlock();
json message = {
{"jsonrpc", "2.0"},
{"method", "textDocument/didChange"},
{"params",
{{"textDocument",
{{"uri", editor->uri}, {"version", ++editor->lsp_version}}},
{"contentChanges",
json::array(
{{{"range",
{{"start", {{"line", pos.row}, {"character", utf16_col}}},
{"end", {{"line", pos.row}, {"character", utf16_col}}}}},
{"text", std::string(data, len)}}})}}}};
lsp_send(editor->lsp, message, nullptr);
}
std::unique_lock lock_3(editor->spans.mtx);
apply_edit(editor->spans.spans, byte_pos, len);
if (editor->spans.mid_parse)

View File

@@ -351,24 +351,49 @@ void handle_editor_event(Editor *editor, KeyEvent event) {
edit_erase(editor, editor->cursor, -(int64_t)prev_col_cluster);
} else if (isprint((unsigned char)(event.c[0]))) {
char c = event.c[0];
char closing = 0;
if (c == '{')
closing = '}';
else if (c == '(')
closing = ')';
else if (c == '[')
closing = ']';
else if (c == '"')
closing = '"';
else if (c == '\'')
closing = '\'';
if (closing) {
char pair[2] = {c, closing};
edit_insert(editor, editor->cursor, pair, 2);
cursor_right(editor, 1);
} else {
edit_insert(editor, editor->cursor, event.c, 1);
cursor_right(editor, 1);
uint32_t col = editor->cursor.col;
LineIterator *it = begin_l_iter(editor->root, editor->cursor.row);
uint32_t len;
char *line = next_line(it, &len);
bool skip_insert = false;
if (line && col < len) {
char next = line[col];
if ((c == '}' && next == '}') || (c == ')' && next == ')') ||
(c == ']' && next == ']') || (c == '"' && next == '"') ||
(c == '\'' && next == '\'')) {
cursor_right(editor, 1);
skip_insert = true;
}
}
free(it->buffer);
free(it);
if (!skip_insert) {
char closing = 0;
switch (c) {
case '{':
closing = '}';
break;
case '(':
closing = ')';
break;
case '[':
closing = ']';
break;
case '"':
closing = '"';
break;
case '\'':
closing = '\'';
break;
}
if (closing) {
char pair[2] = {c, closing};
edit_insert(editor, editor->cursor, pair, 2);
cursor_right(editor, 1);
} else {
edit_insert(editor, editor->cursor, &c, 1);
cursor_right(editor, 1);
}
}
} else if (event.c[0] == 0x7F || event.c[0] == 0x08) {
Coord prev_pos = editor->cursor;
@@ -571,3 +596,23 @@ void editor_worker(Editor *editor) {
lock.unlock();
}
}
void editor_lsp_handle(Editor *editor, json msg) {
if (msg.contains("method") &&
msg["method"] == "textDocument/publishDiagnostics") {
std::unique_lock lock(editor->v_mtx);
editor->warnings.clear();
json diagnostics = msg["params"]["diagnostics"];
for (size_t i = 0; i < diagnostics.size(); i++) {
json d = diagnostics[i];
VWarn w;
w.line = d["range"]["start"]["line"];
std::string text = d["message"].get<std::string>();
auto pos = text.find('\n');
w.text = (pos == std::string::npos) ? text : text.substr(0, pos);
w.type = d["severity"].get<int>();
editor->warnings.push_back(w);
}
std::sort(editor->warnings.begin(), editor->warnings.end());
}
}

View File

@@ -17,6 +17,7 @@ uint32_t get_indent(Editor *editor, Coord cursor) {
if (!editor)
return 0;
LineIterator *it = begin_l_iter(editor->root, cursor.row);
next_line(it, nullptr);
uint32_t line_len;
char *line;
while ((line = prev_line(it, &line_len)) != nullptr) {

View File

@@ -13,7 +13,6 @@ std::unordered_map<uint8_t, LSPInstance *> active_lsps;
Queue<LSPOpenRequest> lsp_open_queue;
static bool init_lsp(LSPInstance *lsp) {
log("starting %s\n", lsp->lsp->command);
int in_pipe[2];
int out_pipe[2];
if (pipe(in_pipe) == -1 || pipe(out_pipe) == -1) {
@@ -240,8 +239,7 @@ void lsp_worker() {
Editor *ed = editor_for_uri(lsp, uri);
lock.unlock();
if (ed)
// editor_lsp_handle(ed, *msg)
;
editor_lsp_handle(ed, *msg);
else
lsp_handle(lsp, *msg);
lock.lock();

View File

@@ -48,14 +48,24 @@ void end_screen() { disable_raw_mode(); }
Coord get_size() { return {rows, cols}; }
void update(uint32_t row, uint32_t col, std::string utf8, uint32_t fg,
uint32_t bg, uint8_t flags) {
if (row >= rows || col >= cols)
return;
uint32_t idx = row * cols + col;
std::lock_guard<std::mutex> lock(screen_mutex);
screen[idx].utf8 = utf8 != "" ? utf8 : "";
screen[idx].fg = fg;
screen[idx].bg = bg;
screen[idx].flags = flags;
}
void update(uint32_t row, uint32_t col, const char *utf8, uint32_t fg,
uint32_t bg, uint8_t flags) {
if (row >= rows || col >= cols)
return;
uint32_t idx = row * cols + col;
std::lock_guard<std::mutex> lock(screen_mutex);
screen[idx].utf8 = utf8 ? utf8 : "";
screen[idx].fg = fg;
screen[idx].bg = bg;

View File

@@ -304,6 +304,7 @@ void ts_collect_spans(Editor *editor) {
while (editor->spans.edits.pop(span_edit))
apply_edit(new_spans, span_edit.first, span_edit.second);
TSTree *inj_tree = ts_parser_parse(inj.parser, nullptr, tsinput);
knot_mtx.unlock();
TSQueryCursor *inj_cursor = ts_query_cursor_new();
ts_query_cursor_exec(inj_cursor, inj.query, ts_tree_root_node(inj_tree));
TSQueryMatch inj_match;

View File

@@ -242,6 +242,28 @@ char *detect_file_type(const char *filename) {
return result;
}
int utf8_byte_offset_to_utf16(const char *s, size_t byte_pos) {
int utf16_units = 0;
size_t i = 0;
while (i < byte_pos) {
unsigned char c = s[i];
if ((c & 0x80) == 0x00) {
i += 1;
utf16_units += 1;
} else if ((c & 0xE0) == 0xC0) {
i += 2;
utf16_units += 1;
} else if ((c & 0xF0) == 0xE0) {
i += 3;
utf16_units += 1;
} else {
i += 4;
utf16_units += 2;
}
}
return utf16_units;
}
Language language_for_file(const char *filename) {
std::string ext = file_extension(filename);
std::string lang_name;