Add lsp's for many different languages and minor fixes

This commit is contained in:
2025-12-27 04:31:08 +00:00
parent a38ba1f813
commit bfaba81317
16 changed files with 369 additions and 131 deletions

View File

@@ -29,6 +29,8 @@ Editor *new_editor(const char *filename_arg, Coord position, Coord size) {
editor->ts.parser = ts_parser_new();
editor->ts.language = language.fn();
ts_parser_set_language(editor->ts.parser, editor->ts.language);
log("set language %s\n", language.name.c_str());
log("lsp_id: %d\n", language.lsp_id);
editor->ts.query_file =
get_exe_dir() + "/../grammar/" + language.name + ".scm";
request_add_to_lsp(language, editor);
@@ -48,6 +50,8 @@ void free_tsset(TSSetMain *set) {
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);
}
}
@@ -67,6 +71,11 @@ void save_file(Editor *editor) {
std::ofstream out(editor->filename);
out.write(str, editor->root->char_count);
free(str);
json msg = {{"jsonrpc", "2.0"},
{"method", "textDocument/didSave"},
{"params", {{"textDocument", {{"uri", editor->uri}}}}}};
if (editor->lsp)
lsp_send(editor->lsp, msg, nullptr);
}
void render_editor(Editor *editor) {
@@ -83,6 +92,7 @@ void render_editor(Editor *editor) {
auto hook_it = v.begin();
while (hook_it != v.end() && hook_it->first <= editor->scroll.row)
++hook_it;
std::unique_lock warn_lock(editor->v_mtx);
auto warn_it = editor->warnings.begin();
while (warn_it != editor->warnings.end() &&
warn_it->line < editor->scroll.row)
@@ -147,7 +157,6 @@ 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) {
@@ -370,6 +379,9 @@ void render_editor(Editor *editor) {
cluster.c_str(), fg_color, color, 0);
col += width;
warn_idx += cluster_len;
while (width-- > 1)
update(editor->position.row + rendered_rows, render_x + col - width,
"\x1b", fg_color, color, 0);
}
}
while (col < render_width) {

View File

@@ -394,15 +394,29 @@ 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);
if (editor->lsp->incremental_sync) {
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);
} else {
char *buf = read(editor->root, 0, editor->root->char_count);
std::string text(buf);
free(buf);
json message = {
{"jsonrpc", "2.0"},
{"method", "textDocument/didChange"},
{"params",
{{"textDocument",
{{"uri", editor->uri}, {"version", ++editor->lsp_version}}},
{"contentChanges", json::array({{{"text", text}}})}}}};
lsp_send(editor->lsp, message, nullptr);
}
}
std::unique_lock lock_3(editor->spans.mtx);
apply_edit(editor->spans.spans, start, start - byte_pos);
@@ -469,15 +483,29 @@ 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);
if (editor->lsp->incremental_sync) {
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);
} else {
char *buf = read(editor->root, 0, editor->root->char_count);
std::string text(buf);
free(buf);
json message = {
{"jsonrpc", "2.0"},
{"method", "textDocument/didChange"},
{"params",
{{"textDocument",
{{"uri", editor->uri}, {"version", ++editor->lsp_version}}},
{"contentChanges", json::array({{{"text", 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);
@@ -530,28 +558,42 @@ 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);
if (editor->lsp->incremental_sync) {
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);
} else {
char *buf = read(editor->root, 0, editor->root->char_count);
std::string text(buf);
free(buf);
json message = {
{"jsonrpc", "2.0"},
{"method", "textDocument/didChange"},
{"params",
{{"textDocument",
{{"uri", editor->uri}, {"version", ++editor->lsp_version}}},
{"contentChanges", json::array({{{"text", text}}})}}}};
lsp_send(editor->lsp, message, nullptr);
}
}
std::unique_lock lock_3(editor->spans.mtx);
apply_edit(editor->spans.spans, byte_pos, len);

View File

@@ -610,6 +610,8 @@ void editor_lsp_handle(Editor *editor, json msg) {
for (size_t i = 0; i < diagnostics.size(); i++) {
json d = diagnostics[i];
VWarn w;
// HACK: convert back to utf-8 but as this is only visually affecting it
// is not worth the performance hit
w.line = d["range"]["start"]["line"];
w.start = d["range"]["start"]["character"];
uint32_t end = d["range"]["end"]["character"];

View File

@@ -13,6 +13,7 @@ std::unordered_map<uint8_t, LSPInstance *> active_lsps;
Queue<LSPOpenRequest> lsp_open_queue;
static bool init_lsp(LSPInstance *lsp) {
log("initializing %s\n", lsp->lsp->command);
int in_pipe[2];
int out_pipe[2];
if (pipe(in_pipe) == -1 || pipe(out_pipe) == -1) {
@@ -59,23 +60,35 @@ LSPInstance *get_or_init_lsp(uint8_t lsp_id) {
delete lsp;
return nullptr;
}
log("starting %s\n", lsp->lsp->command);
LSPPending *pending = new LSPPending();
pending->method = "initialize";
pending->editor = nullptr;
pending->callback = [lsp](Editor *, std::string, json) {
pending->callback = [lsp](Editor *, std::string, json msg) {
if (msg.contains("result") && msg["result"].contains("capabilities")) {
auto &caps = msg["result"]["capabilities"];
if (caps.contains("textDocumentSync") &&
caps["textDocumentSync"].contains("change")) {
int change_type = caps["textDocumentSync"]["change"];
lsp->incremental_sync = (change_type == 2);
}
}
lsp->initialized = true;
json initialized = {{"jsonrpc", "2.0"},
{"method", "initialized"},
{"params", json::object()}};
lsp_send(lsp, initialized, nullptr);
log("initialized %s\n", lsp->lsp->command);
};
json init_message = {
{"jsonrpc", "2.0"},
{"method", "initialize"},
{"params",
{{"processId", getpid()},
{"rootUri", "file://" + std::filesystem::current_path().string()},
{"capabilities", json::object()}}}};
{"rootUri", "file://" + percent_encode(path_abs("."))},
{"capabilities",
{{"textDocument",
{{"publishDiagnostics", {{"relatedInformation", true}}}}}}}}}};
lsp_send(lsp, init_message, pending);
active_lsps[lsp_id] = lsp;
return lsp;
@@ -160,6 +173,7 @@ static std::optional<json> read_lsp_message(int fd) {
return std::nullopt;
got += n;
}
log("%s\n", body.c_str());
return json::parse(body);
}
@@ -249,6 +263,7 @@ void lsp_worker() {
}
void request_add_to_lsp(Language language, Editor *editor) {
log("request_add_to_lsp %d\n", language.lsp_id);
lsp_open_queue.push({language, editor});
}

View File

@@ -235,8 +235,12 @@ void ts_collect_spans(Editor *editor) {
parse_counter = 0;
editor->spans.mid_parse = true;
std::shared_lock lock(editor->knot_mtx);
editor->ts.tree =
ts_parser_parse(editor->ts.parser, editor->ts.tree, tsinput);
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<Span> new_spans;
new_spans.reserve(4096);
@@ -324,7 +328,12 @@ void ts_collect_spans(Editor *editor) {
ts_parser_set_included_ranges(tsset->parser, tsset->ranges.data(),
tsset->ranges.size());
lock.lock();
tsset->tree = ts_parser_parse(tsset->parser, tsset->tree, tsinput);
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<TSSetBase *>(tsset), tsset->tree,
item.depth + 1});

View File

@@ -5,7 +5,7 @@ extern "C" {
#include "../include/maps.h"
#include "../include/utils.h"
static std::string percent_encode(const std::string &s) {
std::string percent_encode(const std::string &s) {
static const char *hex = "0123456789ABCDEF";
std::string out;
for (unsigned char c : s) {