Fix parsing bugs and add better indentation support

This commit is contained in:
2026-02-01 20:40:52 +00:00
parent f77caf604f
commit 8b93b955e8
17 changed files with 304 additions and 103 deletions
+127 -32
View File
@@ -38,6 +38,18 @@ void IndentationEngine::compute_indent(Editor *n_editor) {
indent = 2;
free(it->buffer);
free(it);
auto x = kLangtoBlockStartsEnd.find(editor->lang.name);
if (x != kLangtoBlockStartsEnd.end())
start_end = &x->second;
x = kLangtoBlockStartsStart.find(editor->lang.name);
if (x != kLangtoBlockStartsStart.end())
start_start = &x->second;
x = kLangtoBlockEndsFull.find(editor->lang.name);
if (x != kLangtoBlockEndsFull.end())
end_full = &x->second;
x = kLangtoBlockEndsStart.find(editor->lang.name);
if (x != kLangtoBlockEndsStart.end())
end_start = &x->second;
}
uint32_t IndentationEngine::indent_real(char *line, uint32_t len) {
@@ -75,18 +87,16 @@ uint32_t IndentationEngine::indent_expected(uint32_t row) {
if (len == 0)
continue;
c_indent = indent_real(line, len);
auto is_end_set = kLangtoBlockStartsEnd.find(editor->lang.name);
auto is_start_set = kLangtoBlockStartsStart.find(editor->lang.name);
bool is_end = false;
if (is_end_set != kLangtoBlockStartsEnd.end())
for (auto end : is_end_set->second)
if (start_end)
for (auto end : *start_end)
if (ends_with(line, end)) {
c_indent++;
is_end = true;
break;
}
if (!is_end && is_start_set != kLangtoBlockStartsStart.end())
for (auto end : is_start_set->second)
if (!is_end && start_start)
for (auto end : *start_start)
if (starts_with(line, end)) {
c_indent++;
break;
@@ -195,6 +205,100 @@ uint32_t IndentationEngine::dedent_line(uint32_t row) {
return (uint32_t)ABS((int64_t)ws_len - (new_indent * indent));
}
void IndentationEngine::indent_block(uint32_t start_row, uint32_t end_row) {
indent_block(start_row, end_row, +1);
}
void IndentationEngine::dedent_block(uint32_t start_row, uint32_t end_row) {
indent_block(start_row, end_row, -1);
}
void IndentationEngine::indent_block(uint32_t start_row, uint32_t end_row,
int delta) {
if (start_row > end_row)
std::swap(start_row, end_row);
uint32_t start_len, end_len;
uint32_t start_off = line_to_byte(editor->root, start_row, &start_len);
uint32_t end_off = line_to_byte(editor->root, end_row, &end_len);
uint32_t total_len = (end_off - start_off) + end_len;
char *block = read(editor->root, start_off, total_len);
if (!block)
return;
uint32_t cap = total_len + 128;
char *out = (char *)malloc(cap);
uint32_t out_len = 0;
char *p = block;
char *end = block + total_len;
while (p < end) {
char *line_start = p;
while (p < end && *p != '\n')
p++;
uint32_t len = (uint32_t)(p - line_start);
uint32_t ws = 0;
while (ws < len && (line_start[ws] == ' ' || line_start[ws] == '\t'))
ws++;
uint32_t real_indent = indent_real(line_start, len);
int64_t new_indent = (int64_t)real_indent + delta;
if (new_indent < 0)
new_indent = 0;
uint32_t indent_chars = (indent == 1) ? new_indent : new_indent * indent;
uint32_t new_line_len = indent_chars + (len - ws);
if (out_len + new_line_len + 2 >= cap) {
cap = (cap * 2) + new_line_len + 32;
out = (char *)realloc(out, cap);
}
if (indent == 1) {
memset(out + out_len, '\t', indent_chars);
out_len += indent_chars;
} else {
memset(out + out_len, ' ', indent_chars);
out_len += indent_chars;
}
memcpy(out + out_len, line_start + ws, len - ws);
out_len += (len - ws);
if (p < end && *p == '\n') {
out[out_len++] = '\n';
p++;
}
}
free(block);
edit_replace(editor, {start_row, 0}, {end_row, end_len}, out, out_len);
free(out);
}
void IndentationEngine::insert_tab(Coord cursor) {
std::shared_lock lock(editor->knot_mtx);
LineIterator *it = begin_l_iter(editor->root, cursor.row);
if (!it)
return;
uint32_t len;
char *line = next_line(it, &len);
if (!line) {
free(it->buffer);
free(it);
return;
}
lock.unlock();
if (len > 0 && line[len - 1] == '\n')
--len;
uint32_t ws_len = 0;
while (ws_len < len && (line[ws_len] == ' ' || line[ws_len] == '\t'))
ws_len++;
std::string insert;
if (cursor.col <= ws_len) {
if (indent == 1)
insert = "\t";
else
insert.assign(indent - ((cursor.col) % indent), ' ');
} else {
insert = "\t";
}
free(it->buffer);
free(it);
edit_insert(editor, cursor, (char *)insert.c_str(), insert.size());
editor->cursor.col += insert.size();
}
void IndentationEngine::insert_new_line(Coord cursor) {
std::string formatted;
std::shared_lock lock(editor->knot_mtx);
@@ -212,19 +316,17 @@ void IndentationEngine::insert_new_line(Coord cursor) {
if (len > 0 && line[len - 1] == '\n')
--len;
if (cursor.col >= len) {
auto is_end_full = kLangtoBlockEndsFull.find(editor->lang.name);
auto is_end_start = kLangtoBlockEndsStart.find(editor->lang.name);
bool end_matched = false;
if (is_end_full != kLangtoBlockEndsFull.end())
for (auto end : is_end_full->second)
if (end_full)
for (auto end : *end_full)
if (end == trim(line)) {
cursor.col = set_indent(
cursor.row, (int64_t)indent_expected(cursor.row) - (int64_t)1);
end_matched = true;
break;
}
if (!end_matched && is_end_start != kLangtoBlockEndsStart.end())
for (auto end : is_end_start->second)
if (!end_matched && end_start)
for (auto end : *end_start)
if (starts_with(trim(line), end)) {
cursor.col = set_indent(
cursor.row, (int64_t)indent_expected(cursor.row) - (int64_t)1);
@@ -252,30 +354,26 @@ void IndentationEngine::insert_new_line(Coord cursor) {
if (!ending.empty()) {
bool ending_valid = false;
bool starting_valid = false;
auto is_end_full = kLangtoBlockEndsFull.find(editor->lang.name);
auto is_end_start = kLangtoBlockEndsStart.find(editor->lang.name);
auto is_end_set = kLangtoBlockStartsEnd.find(editor->lang.name);
auto is_start_set = kLangtoBlockStartsStart.find(editor->lang.name);
if (is_end_full != kLangtoBlockEndsFull.end())
for (auto end : is_end_full->second)
if (end_full)
for (auto end : *end_full)
if (ending == end) {
ending_valid = true;
break;
}
if (!ending_valid && is_end_start != kLangtoBlockEndsStart.end())
for (auto end : is_end_start->second)
if (!ending_valid && end_start)
for (auto end : *end_start)
if (starts_with(ending, end)) {
ending_valid = true;
break;
}
if (is_end_set != kLangtoBlockStartsEnd.end())
for (auto end : is_end_set->second)
if (start_end)
for (auto end : *start_end)
if (ends_with(before, end)) {
starting_valid = true;
break;
}
if (!starting_valid && is_start_set != kLangtoBlockStartsStart.end())
for (auto end : is_start_set->second)
if (!starting_valid && start_start)
for (auto end : *start_start)
if (starts_with(before, end)) {
starting_valid = true;
break;
@@ -288,18 +386,16 @@ void IndentationEngine::insert_new_line(Coord cursor) {
else if (ending_valid)
c_indent--;
}
auto is_end_set = kLangtoBlockStartsEnd.find(editor->lang.name);
auto is_start_set = kLangtoBlockStartsStart.find(editor->lang.name);
bool is_end = false;
if (is_end_set != kLangtoBlockStartsEnd.end())
for (auto end : is_end_set->second)
if (start_end)
for (auto end : *start_end)
if (ends_with(before, end)) {
c_indent++;
is_end = true;
break;
}
if (!is_end && is_start_set != kLangtoBlockStartsStart.end())
for (auto end : is_start_set->second)
if (!is_end && start_start)
for (auto end : *start_start)
if (starts_with(before, end)) {
c_indent++;
break;
@@ -349,8 +445,7 @@ void IndentationEngine::insert_new_line(Coord cursor) {
{"trimFinalNewlines", true}}}}}};
LSPPending *pending = new LSPPending();
pending->editor = editor;
pending->method = "textDocument/onTypeFormatting";
pending->callback = [version](Editor *editor, std::string, json message) {
pending->callback = [version](Editor *editor, const json &message) {
if (version != editor->lsp_version)
return;
auto &edits = message["result"];