Fix parsing bugs and add better indentation support
This commit is contained in:
+127
-32
@@ -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"];
|
||||
|
||||
Reference in New Issue
Block a user