Shift spans between highlights properly

This commit is contained in:
2025-12-08 17:02:43 +00:00
parent c121fa9c15
commit 2121339e67
4 changed files with 76 additions and 29 deletions

View File

@@ -3,9 +3,11 @@
#include "./rope.h"
#include "./ui.h"
#include "./utils.h"
#include <algorithm>
#include <cstdint>
#include <map>
#include <shared_mutex>
#include <unordered_map>
struct Highlight {
uint32_t fg;
@@ -22,37 +24,71 @@ struct Span {
bool operator<(const Span &other) const { return start < other.start; }
};
struct Spans {
std::vector<Span> spans;
Queue<std::pair<uint32_t, int64_t>> edits;
bool mid_parse = false;
std::shared_mutex mtx;
void apply(uint32_t x, int64_t y) {
std::unique_lock lock(mtx);
auto it = std::lower_bound(
spans.begin(), spans.end(), Span{.start = x, .end = 0, .hl = nullptr},
[](auto &a, auto &b) { return a.start < b.start; });
while (it != spans.begin()) {
auto prev = std::prev(it);
if (prev->end <= x)
break;
it = prev;
}
while (it != spans.end()) {
if (it->start < x && it->end > x) {
it->end += y;
} else if (it->start > x) {
it->start += y;
it->end += y;
}
if (it->end <= it->start)
it = spans.erase(it);
else
++it;
}
}
};
struct SpanCursor {
const std::vector<Span> &spans;
Spans &spans;
size_t index = 0;
std::vector<Span *> active;
std::shared_lock<std::shared_mutex> lock;
SpanCursor(const std::vector<Span> &s) : spans(s) {}
SpanCursor(Spans &s) : spans(s) {}
Highlight *get_highlight(uint32_t byte_offset) {
for (int i = (int)active.size() - 1; i >= 0; i--)
if (active[i]->end <= byte_offset)
active.erase(active.begin() + i);
while (index < spans.size() && spans[index].start <= byte_offset) {
if (spans[index].end > byte_offset)
active.push_back(const_cast<Span *>(&spans[index]));
while (index < spans.spans.size() &&
spans.spans[index].start <= byte_offset) {
if (spans.spans[index].end > byte_offset)
active.push_back(const_cast<Span *>(&spans.spans[index]));
index++;
}
Highlight *best = nullptr;
int max_prio = -1;
for (auto *s : active) {
for (auto *s : active)
if (s->hl->priority > max_prio) {
max_prio = s->hl->priority;
best = s->hl;
}
}
return best;
}
void sync(uint32_t byte_offset) {
lock = std::shared_lock(spans.mtx);
active.clear();
size_t left = 0, right = spans.size();
size_t left = 0, right = spans.spans.size();
while (left < right) {
size_t mid = (left + right) / 2;
if (spans[mid].start <= byte_offset)
if (spans.spans[mid].start <= byte_offset)
left = mid + 1;
else
right = mid;
@@ -60,9 +96,9 @@ struct SpanCursor {
index = left;
while (left > 0) {
left--;
if (spans[left].end > byte_offset)
active.push_back(const_cast<Span *>(&spans[left]));
else if (byte_offset - spans[left].end > 1000)
if (spans.spans[left].end > byte_offset)
active.push_back(const_cast<Span *>(&spans.spans[left]));
else if (byte_offset - spans.spans[left].end > 1000)
break;
}
}
@@ -72,7 +108,6 @@ struct Editor {
const char *filename; // Filename of the editor
Knot *root; // A rope
std::shared_mutex knot_mtx; // A mutex
std::shared_mutex span_mtx; // A mutex
Coord cursor; // position of the cursor
uint32_t cursor_preffered; // preffered visual column
Coord selection; // position of the selection
@@ -87,7 +122,7 @@ struct Editor {
Queue<TSInputEdit> edit_queue; // Tree-sitter edit queue
std::vector<Highlight> query_map; // Tree-sitter query map
std::vector<int8_t> folded; // folded lines indexed by line number
std::vector<Span> spans; // Highlighted spans
Spans spans; // Highlighted spans
std::map<uint32_t, bool> folded_node; // maps content hash to fold state
// - built by tree-sitter helpers
};

View File

@@ -382,7 +382,6 @@ void render_editor(Editor *editor) {
uint32_t line_index = editor->scroll.row;
SpanCursor span_cursor(editor->spans);
std::shared_lock knot_lock(editor->knot_mtx);
std::shared_lock span_lock(editor->span_mtx);
LineIterator *it = begin_l_iter(editor->root, line_index);
if (!it)
return;

View File

@@ -75,6 +75,9 @@ void handle_editor_event(Editor *editor, KeyEvent event) {
};
editor->edit_queue.push(edit);
}
editor->spans.apply(pos, 1);
if (editor->spans.mid_parse)
editor->spans.edits.push({pos, 1});
cursor_right(editor, 1);
}
if (event.key_type == KEY_CHAR && event.c == '\t') {
@@ -96,6 +99,9 @@ void handle_editor_event(Editor *editor, KeyEvent event) {
};
editor->edit_queue.push(edit);
}
editor->spans.apply(pos, 2);
if (editor->spans.mid_parse)
editor->spans.edits.push({pos, 2});
cursor_right(editor, 2);
}
if (event.key_type == KEY_CHAR && (event.c == '\n' || event.c == '\r')) {
@@ -118,6 +124,9 @@ void handle_editor_event(Editor *editor, KeyEvent event) {
};
editor->edit_queue.push(edit);
}
editor->spans.apply(pos + 1, 1);
if (editor->spans.mid_parse)
editor->spans.edits.push({pos + 1, 1});
cursor_right(editor, 1);
}
if (event.key_type == KEY_CHAR && event.c == 0x7F) {
@@ -143,6 +152,9 @@ void handle_editor_event(Editor *editor, KeyEvent event) {
};
editor->edit_queue.push(edit);
}
editor->spans.apply(start, start - pos);
if (editor->spans.mid_parse)
editor->spans.edits.push({start, start - pos});
}
ensure_scroll(editor);
}

View File

@@ -1,6 +1,7 @@
#include "../include/ts.h"
#include "../include/editor.h"
#include "../include/rope.h"
#include <algorithm>
#include <cstdint>
#include <fstream>
#include <regex>
@@ -177,10 +178,10 @@ void ts_collect_spans(Editor *editor) {
.decode = nullptr,
};
TSTree *tree, *copy = nullptr;
std::unique_lock lock_0(editor->knot_mtx);
std::unique_lock knot_mtx(editor->knot_mtx);
if (editor->tree)
copy = ts_tree_copy(editor->tree);
lock_0.unlock();
knot_mtx.unlock();
if (!running)
return;
std::vector<TSInputEdit> edits;
@@ -189,21 +190,17 @@ void ts_collect_spans(Editor *editor) {
while (editor->edit_queue.pop(edit)) {
edits.push_back(edit);
ts_tree_edit(copy, &edits.back());
}
};
editor->spans.mid_parse = true;
tree = ts_parser_parse(editor->parser, copy, tsinput);
if (copy)
ts_tree_delete(copy);
while (editor->edit_queue.pop(edit)) {
edits.push_back(edit);
ts_tree_edit(tree, &edits.back());
}
lock_0.lock();
knot_mtx.lock();
if (editor->tree)
ts_tree_delete(editor->tree);
editor->tree = tree;
copy = ts_tree_copy(tree);
lock_0.unlock();
std::shared_lock lock_1(editor->span_mtx);
knot_mtx.unlock();
TSQueryCursor *cursor = ts_query_cursor_new();
ts_query_cursor_exec(cursor, editor->query, ts_tree_root_node(copy));
std::vector<Span> new_spans;
@@ -232,12 +229,16 @@ void ts_collect_spans(Editor *editor) {
free(load.prev);
return;
}
lock_1.unlock();
std::sort(new_spans.begin(), new_spans.end());
std::unique_lock lock_2(editor->span_mtx);
editor->spans.swap(new_spans);
lock_2.unlock();
std::unique_lock span_mtx(editor->spans.mtx);
editor->spans.mid_parse = false;
editor->spans.spans.swap(new_spans);
span_mtx.unlock();
std::pair<uint32_t, int64_t> span_edit;
while (editor->spans.edits.pop(span_edit))
editor->spans.apply(span_edit.first, span_edit.second);
ts_query_cursor_delete(cursor);
ts_tree_delete(copy);
if (load.prev)
free(load.prev);
}