Completions bug fixes
This commit is contained in:
@@ -1,9 +1,11 @@
|
||||
#include "editor/decl.h"
|
||||
#include "editor/editor.h"
|
||||
#include "io/knot.h"
|
||||
#include "io/sysio.h"
|
||||
#include "lsp/lsp.h"
|
||||
#include "main.h"
|
||||
#include "utils/utils.h"
|
||||
#include <regex>
|
||||
|
||||
inline static std::string completion_prefix(Editor *editor) {
|
||||
Coord hook = editor->completion.hook;
|
||||
@@ -50,23 +52,15 @@ void completion_filter(Editor *editor) {
|
||||
void completion_request(Editor *editor) {
|
||||
Coord hook = editor->cursor;
|
||||
word_boundaries(editor, editor->cursor, &hook.col, nullptr, nullptr, nullptr);
|
||||
LineIterator *it = begin_l_iter(editor->root, hook.row);
|
||||
char *line = next_line(it, nullptr);
|
||||
if (!line) {
|
||||
free(it->buffer);
|
||||
free(it);
|
||||
return;
|
||||
}
|
||||
editor->completion.active = true;
|
||||
editor->completion.items.clear();
|
||||
editor->completion.visible.clear();
|
||||
editor->completion.select = 0;
|
||||
hook.col = utf8_byte_offset_to_utf16(line, hook.col);
|
||||
editor->completion.hook = hook;
|
||||
LSPPending *pending = new LSPPending();
|
||||
pending->editor = editor;
|
||||
pending->method = "textDocument/completion";
|
||||
pending->callback = [line, it](Editor *editor, std::string, json message) {
|
||||
pending->callback = [](Editor *editor, std::string, json message) {
|
||||
auto &session = editor->completion;
|
||||
std::unique_lock lock(session.mtx);
|
||||
std::vector<json> items_json;
|
||||
@@ -115,8 +109,15 @@ void completion_request(Editor *editor) {
|
||||
item.is_markup =
|
||||
item_json["documentation"]["kind"].get<std::string>() ==
|
||||
"markdown";
|
||||
item.documentation =
|
||||
std::string documentation =
|
||||
item_json["documentation"]["value"].get<std::string>();
|
||||
if (item.is_markup) {
|
||||
static const std::regex fence_no_lang("```(\\s*\\n)");
|
||||
item.documentation = std::regex_replace(
|
||||
documentation, fence_no_lang, "```" + editor->lang.name + "$1");
|
||||
} else {
|
||||
item.documentation = documentation;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (item_json.contains("deprecated") &&
|
||||
@@ -147,26 +148,27 @@ void completion_request(Editor *editor) {
|
||||
edit.start.col = te["insert"]["start"]["character"];
|
||||
edit.end.row = te["insert"]["end"]["line"];
|
||||
edit.end.col = te["insert"]["end"]["character"];
|
||||
} else if (te.contains("range")) {
|
||||
edit.start.row = te["range"]["start"]["line"];
|
||||
edit.start.col = te["range"]["start"]["character"];
|
||||
edit.end.row = te["range"]["end"]["line"];
|
||||
edit.end.col = te["range"]["end"]["character"];
|
||||
} else {
|
||||
edit.start = session.hook;
|
||||
edit.end = editor->cursor;
|
||||
}
|
||||
} else {
|
||||
edit.text = te.value("newText", "");
|
||||
edit.start.row = te["range"]["start"]["line"];
|
||||
edit.start.col = te["range"]["start"]["character"];
|
||||
edit.end.row = te["range"]["end"]["line"];
|
||||
edit.end.col = te["range"]["end"]["character"];
|
||||
}
|
||||
} else if (item_json.contains("insertText") &&
|
||||
item_json["insertText"].is_string()) {
|
||||
edit.text = item_json["insertText"].get<std::string>();
|
||||
edit.start = session.hook;
|
||||
uint32_t col = utf8_byte_offset_to_utf16(line, editor->cursor.col);
|
||||
edit.end = {editor->cursor.row, col};
|
||||
edit.end = editor->cursor;
|
||||
} else {
|
||||
edit.text = item.label;
|
||||
edit.start = session.hook;
|
||||
uint32_t col = utf8_byte_offset_to_utf16(line, editor->cursor.col);
|
||||
edit.end = {editor->cursor.row, col};
|
||||
edit.end = editor->cursor;
|
||||
}
|
||||
utf8_normalize_edit(editor, &edit);
|
||||
item.edits.push_back(edit);
|
||||
if (item_json.contains("additionalTextEdits")) {
|
||||
for (auto &te : item_json["additionalTextEdits"]) {
|
||||
@@ -176,6 +178,7 @@ void completion_request(Editor *editor) {
|
||||
edit.start.col = te["range"]["start"]["character"];
|
||||
edit.end.row = te["range"]["end"]["line"];
|
||||
edit.end.col = te["range"]["end"]["character"];
|
||||
utf8_normalize_edit(editor, &edit);
|
||||
item.edits.push_back(edit);
|
||||
}
|
||||
}
|
||||
@@ -187,15 +190,23 @@ void completion_request(Editor *editor) {
|
||||
if (c.is_string() && c.get<std::string>().size() == 1)
|
||||
item.end_chars.push_back(c.get<std::string>()[0]);
|
||||
session.items.push_back(std::move(item));
|
||||
session.visible.push_back(session.items.size() - 1);
|
||||
}
|
||||
completion_filter(editor);
|
||||
session.box.hidden = false;
|
||||
session.box.render_update();
|
||||
};
|
||||
std::shared_lock lock(editor->knot_mtx);
|
||||
LineIterator *it = begin_l_iter(editor->root, hook.row);
|
||||
char *line = next_line(it, nullptr);
|
||||
if (!line) {
|
||||
free(it->buffer);
|
||||
free(it);
|
||||
};
|
||||
return;
|
||||
}
|
||||
uint32_t col = utf8_byte_offset_to_utf16(line, editor->cursor.col);
|
||||
free(it->buffer);
|
||||
free(it);
|
||||
lock.unlock();
|
||||
json message = {
|
||||
{"jsonrpc", "2.0"},
|
||||
{"method", "textDocument/completion"},
|
||||
@@ -315,10 +326,26 @@ void completion_resolve_doc(Editor *editor) {
|
||||
pending->callback = [](Editor *editor, std::string, json message) {
|
||||
std::unique_lock lock(editor->completion.mtx);
|
||||
auto &item = editor->completion.items[editor->completion.select];
|
||||
if (message.contains("documentation"))
|
||||
item.documentation = message["documentation"].get<std::string>();
|
||||
else
|
||||
item.documentation = "";
|
||||
if (message["result"].contains("documentation")) {
|
||||
if (message["result"]["documentation"].is_string()) {
|
||||
item.documentation =
|
||||
message["result"]["documentation"].get<std::string>();
|
||||
} else if (message["result"]["documentation"].contains("value") &&
|
||||
message["result"]["documentation"]["value"].is_string()) {
|
||||
item.is_markup =
|
||||
message["result"]["documentation"]["kind"].get<std::string>() ==
|
||||
"markdown";
|
||||
std::string documentation =
|
||||
message["result"]["documentation"]["value"].get<std::string>();
|
||||
if (item.is_markup) {
|
||||
static const std::regex fence_no_lang("```(\\s*\\n)");
|
||||
item.documentation = std::regex_replace(
|
||||
documentation, fence_no_lang, "```" + editor->lang.name + "$1");
|
||||
} else {
|
||||
item.documentation = documentation;
|
||||
}
|
||||
}
|
||||
}
|
||||
editor->completion.box.render_update();
|
||||
};
|
||||
json message = {{"jsonrpc", "2.0"},
|
||||
@@ -332,7 +359,7 @@ void complete_accept(Editor *editor) {
|
||||
return;
|
||||
auto &item = editor->completion.items[editor->completion.select];
|
||||
// TODO: support snippets here
|
||||
apply_lsp_edits(editor, item.edits);
|
||||
apply_lsp_edits(editor, item.edits, true);
|
||||
editor->completion.active = false;
|
||||
}
|
||||
|
||||
|
||||
@@ -283,21 +283,15 @@ void edit_insert(Editor *editor, Coord pos, char *data, uint32_t len) {
|
||||
void edit_replace(Editor *editor, Coord start, Coord end, const char *text,
|
||||
uint32_t len) {
|
||||
std::shared_lock lock(editor->knot_mtx);
|
||||
uint32_t start_line_byte = line_to_byte(editor->root, start.row, nullptr);
|
||||
uint32_t end_len;
|
||||
uint32_t end_line_byte_start = line_to_byte(editor->root, end.row, &end_len);
|
||||
uint32_t end_line_byte = end_line_byte_start + len;
|
||||
uint32_t start_byte =
|
||||
line_to_byte(editor->root, start.row, nullptr) + start.col;
|
||||
uint32_t end_byte = line_to_byte(editor->root, end.row, nullptr) + end.col;
|
||||
lock.unlock();
|
||||
char *buf =
|
||||
read(editor->root, start_line_byte, end_line_byte - start_line_byte);
|
||||
char *buf = read(editor->root, start_byte, end_byte - start_byte);
|
||||
if (!buf)
|
||||
return;
|
||||
uint32_t start_col = utf16_offset_to_utf8(buf, start.col);
|
||||
uint32_t end_col = utf16_offset_to_utf8(
|
||||
buf + (end_line_byte_start - start_line_byte), end.col);
|
||||
uint32_t erase_len =
|
||||
count_clusters(buf, end_line_byte - start_line_byte, start_col,
|
||||
(end_line_byte_start - start_line_byte) + end_col);
|
||||
count_clusters(buf, end_byte - start_byte, 0, end_byte - start_byte);
|
||||
free(buf);
|
||||
if (erase_len != 0)
|
||||
edit_erase(editor, start, erase_len);
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#include "editor/editor.h"
|
||||
#include "editor/decl.h"
|
||||
#include "lsp/lsp.h"
|
||||
#include "utils/utils.h"
|
||||
#include <shared_mutex>
|
||||
|
||||
Editor *new_editor(const char *filename_arg, Coord position, Coord size) {
|
||||
Editor *editor = new Editor();
|
||||
@@ -66,15 +68,62 @@ void free_editor(Editor *editor) {
|
||||
void save_file(Editor *editor) {
|
||||
if (!editor || !editor->root)
|
||||
return;
|
||||
std::shared_lock lock(editor->knot_mtx);
|
||||
char *str = read(editor->root, 0, editor->root->char_count);
|
||||
if (!str)
|
||||
return;
|
||||
std::ofstream out(editor->filename);
|
||||
out.write(str, editor->root->char_count);
|
||||
out.close();
|
||||
free(str);
|
||||
json msg = {{"jsonrpc", "2.0"},
|
||||
{"method", "textDocument/didSave"},
|
||||
{"params", {{"textDocument", {{"uri", editor->uri}}}}}};
|
||||
if (editor->lsp)
|
||||
lsp_send(editor->lsp, msg, nullptr);
|
||||
lock.unlock();
|
||||
if (editor->lsp) {
|
||||
json save_msg = {{"jsonrpc", "2.0"},
|
||||
{"method", "textDocument/didSave"},
|
||||
{"params", {{"textDocument", {{"uri", editor->uri}}}}}};
|
||||
lsp_send(editor->lsp, save_msg, nullptr);
|
||||
if (editor->lsp->allow_formatting) {
|
||||
json msg = {{"jsonrpc", "2.0"},
|
||||
{"method", "textDocument/formatting"},
|
||||
{"params",
|
||||
{{"textDocument", {{"uri", editor->uri}}},
|
||||
{"options",
|
||||
{{"tabSize", 2},
|
||||
{"insertSpaces", true},
|
||||
{"trimTrailingWhitespace", true},
|
||||
{"trimFinalNewlines", true}}}}}};
|
||||
LSPPending *pending = new LSPPending();
|
||||
pending->editor = editor;
|
||||
pending->method = "textDocument/formatting";
|
||||
pending->callback = [save_msg](Editor *editor, std::string,
|
||||
json message) {
|
||||
auto &edits = message["result"];
|
||||
if (edits.is_array()) {
|
||||
std::vector<TextEdit> t_edits;
|
||||
t_edits.reserve(edits.size());
|
||||
for (auto &edit : edits) {
|
||||
TextEdit t_edit;
|
||||
t_edit.text = edit.value("newText", "");
|
||||
t_edit.start.row = edit["range"]["start"]["line"];
|
||||
t_edit.start.col = edit["range"]["start"]["character"];
|
||||
t_edit.end.row = edit["range"]["end"]["line"];
|
||||
t_edit.end.col = edit["range"]["end"]["character"];
|
||||
utf8_normalize_edit(editor, &t_edit);
|
||||
t_edits.push_back(t_edit);
|
||||
}
|
||||
apply_lsp_edits(editor, t_edits, false);
|
||||
ensure_scroll(editor);
|
||||
std::unique_lock lock(editor->knot_mtx);
|
||||
char *str = read(editor->root, 0, editor->root->char_count);
|
||||
if (!str)
|
||||
return;
|
||||
std::ofstream out(editor->filename);
|
||||
out.write(str, editor->root->char_count);
|
||||
free(str);
|
||||
lsp_send(editor->lsp, save_msg, nullptr);
|
||||
}
|
||||
};
|
||||
lsp_send(editor->lsp, msg, pending);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -612,9 +612,9 @@ void handle_editor_event(Editor *editor, KeyEvent event) {
|
||||
mode = NORMAL;
|
||||
break;
|
||||
}
|
||||
ensure_scroll(editor);
|
||||
if (old_mode == mode || mode != INSERT)
|
||||
handle_completion(editor, event);
|
||||
ensure_scroll(editor);
|
||||
if ((event.key_type == KEY_CHAR || event.key_type == KEY_PASTE) && event.c)
|
||||
free(event.c);
|
||||
}
|
||||
|
||||
@@ -1,17 +1,37 @@
|
||||
#include "editor/decl.h"
|
||||
#include "editor/editor.h"
|
||||
|
||||
void apply_lsp_edits(Editor *editor, std::vector<TextEdit> edits) {
|
||||
void apply_lsp_edits(Editor *editor, std::vector<TextEdit> edits, bool move) {
|
||||
if (!edits.size())
|
||||
return;
|
||||
TextEdit first = edits[0];
|
||||
Coord cursor = editor->cursor;
|
||||
std::sort(
|
||||
edits.begin(), edits.end(),
|
||||
[](const TextEdit &a, const TextEdit &b) { return a.start > b.start; });
|
||||
for (const auto &edit : edits)
|
||||
edit_replace(editor, edit.start, edit.end, edit.text.c_str(),
|
||||
edit.text.size());
|
||||
editor->cursor = edits[0].start;
|
||||
editor->cursor = move_right_pure(editor, editor->cursor,
|
||||
count_clusters(edits[0].text.c_str(),
|
||||
edits[0].text.size(), 0,
|
||||
edits[0].text.size()));
|
||||
if (move) {
|
||||
std::shared_lock lock(editor->knot_mtx);
|
||||
editor->cursor = first.start;
|
||||
editor->cursor =
|
||||
move_right_pure(editor, editor->cursor,
|
||||
count_clusters(first.text.c_str(), first.text.size(), 0,
|
||||
first.text.size()));
|
||||
} else {
|
||||
if (cursor.row >= editor->root->line_count) {
|
||||
editor->cursor.row = editor->root->line_count - 1;
|
||||
editor->cursor.col = 0;
|
||||
} else {
|
||||
std::shared_lock lock(editor->knot_mtx);
|
||||
uint32_t len;
|
||||
line_to_byte(editor->root, cursor.row, &len);
|
||||
len--;
|
||||
editor->cursor.row = cursor.row;
|
||||
editor->cursor.col = cursor.col < len ? cursor.col : len;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void editor_lsp_handle(Editor *editor, json msg) {
|
||||
|
||||
Reference in New Issue
Block a user