api/buffer: add "on_bytes" callback to nvim_buf_attach

This implements byte-resolution updates of buffer changes.
Note: there is no promise that the buffer state is valid inside
the callback!
This commit is contained in:
Björn Linse
2020-02-29 15:27:17 +01:00
parent 81fa107f59
commit bc86f76c0a
13 changed files with 280 additions and 125 deletions

View File

@@ -479,18 +479,18 @@ void extmark_apply_undo(ExtmarkUndoObject undo_info, bool undo)
// Undo
ExtmarkSplice splice = undo_info.data.splice;
if (undo) {
extmark_splice(curbuf,
splice.start_row, splice.start_col,
splice.new_row, splice.new_col,
splice.old_row, splice.old_col,
kExtmarkNoUndo);
extmark_splice_impl(curbuf,
splice.start_row, splice.start_col, splice.start_byte,
splice.new_row, splice.new_col, splice.new_byte,
splice.old_row, splice.old_col, splice.old_byte,
kExtmarkNoUndo);
} else {
extmark_splice(curbuf,
splice.start_row, splice.start_col,
splice.old_row, splice.old_col,
splice.new_row, splice.new_col,
kExtmarkNoUndo);
extmark_splice_impl(curbuf,
splice.start_row, splice.start_col, splice.start_byte,
splice.old_row, splice.old_col, splice.old_byte,
splice.new_row, splice.new_col, splice.new_byte,
kExtmarkNoUndo);
}
// kExtmarkSavePos
} else if (undo_info.type == kExtmarkSavePos) {
@@ -509,15 +509,15 @@ void extmark_apply_undo(ExtmarkUndoObject undo_info, bool undo)
ExtmarkMove move = undo_info.data.move;
if (undo) {
extmark_move_region(curbuf,
move.new_row, move.new_col,
move.extent_row, move.extent_col,
move.start_row, move.start_col,
move.new_row, move.new_col, move.new_byte,
move.extent_row, move.extent_col, move.extent_byte,
move.start_row, move.start_col, move.start_byte,
kExtmarkNoUndo);
} else {
extmark_move_region(curbuf,
move.start_row, move.start_col,
move.extent_row, move.extent_col,
move.new_row, move.new_col,
move.start_row, move.start_col, move.start_byte,
move.extent_row, move.extent_col, move.extent_byte,
move.new_row, move.new_col, move.new_byte,
kExtmarkNoUndo);
}
}
@@ -532,36 +532,57 @@ void extmark_adjust(buf_T *buf,
long amount_after,
ExtmarkOp undo)
{
if (!curbuf_splice_pending) {
int old_extent, new_extent;
if (amount == MAXLNUM) {
old_extent = (int)(line2 - line1+1);
new_extent = (int)(amount_after + old_extent);
} else {
// A region is either deleted (amount == MAXLNUM) or
// added (line2 == MAXLNUM). The only other case is :move
// which is handled by a separate entry point extmark_move_region.
assert(line2 == MAXLNUM);
old_extent = 0;
new_extent = (int)amount;
}
extmark_splice(buf,
(int)line1-1, 0,
old_extent, 0,
new_extent, 0, undo);
if (curbuf_splice_pending) {
return;
}
bcount_t start_byte = ml_find_line_or_offset(buf, line1, NULL, true);
bcount_t old_byte = 0, new_byte = 0;
int old_row, new_row;
if (amount == MAXLNUM) {
old_row = (int)(line2 - line1+1);
// TODO: ej kasta?
old_byte = (bcount_t)buf->deleted_bytes2;
new_row = (int)(amount_after + old_row);
} else {
// A region is either deleted (amount == MAXLNUM) or
// added (line2 == MAXLNUM). The only other case is :move
// which is handled by a separate entry point extmark_move_region.
assert(line2 == MAXLNUM);
old_row = 0;
new_row = (int)amount;
}
if (new_row > 0) {
new_byte = ml_find_line_or_offset(buf, line1+new_row, NULL, true)-start_byte;
}
extmark_splice_impl(buf,
(int)line1-1, 0, start_byte,
old_row, 0, old_byte,
new_row, 0, new_byte, undo);
}
void extmark_splice(buf_T *buf,
int start_row, colnr_T start_col,
int old_row, colnr_T old_col,
int new_row, colnr_T new_col,
ExtmarkOp undo)
int start_row, colnr_T start_col,
int old_row, colnr_T old_col, bcount_t old_byte,
int new_row, colnr_T new_col, bcount_t new_byte,
ExtmarkOp undo)
{
buf_updates_send_splice(buf, start_row, start_col,
old_row, old_col,
new_row, new_col);
long offset = ml_find_line_or_offset(buf, start_row+1, NULL, true);
extmark_splice_impl(buf, start_row, start_col, offset+start_col,
old_row, old_col, old_byte, new_row, new_col, new_byte,
undo);
}
void extmark_splice_impl(buf_T *buf,
int start_row, colnr_T start_col, bcount_t start_byte,
int old_row, colnr_T old_col, bcount_t old_byte,
int new_row, colnr_T new_col, bcount_t new_byte,
ExtmarkOp undo)
{
curbuf->deleted_bytes2 = 0;
buf_updates_send_splice(buf, start_row, start_col, start_byte,
old_row, old_col, old_byte,
new_row, new_col, new_byte);
if (undo == kExtmarkUndo && (old_row > 0 || old_col > 0)) {
// Copy marks that would be effected by delete
@@ -599,15 +620,19 @@ void extmark_splice(buf_T *buf,
if (old_col == 0 && start_col >= splice->start_col
&& start_col <= splice->start_col+splice->new_col) {
splice->new_col += new_col;
splice->new_byte += new_byte;
merged = true;
} else if (new_col == 0
&& start_col == splice->start_col+splice->new_col) {
splice->old_col += old_col;
splice->old_byte += old_byte;
merged = true;
} else if (new_col == 0
&& start_col + old_col == splice->start_col) {
splice->start_col = start_col;
splice->start_byte = start_byte;
splice->old_col += old_col;
splice->old_byte += old_byte;
merged = true;
}
}
@@ -618,10 +643,13 @@ void extmark_splice(buf_T *buf,
ExtmarkSplice splice;
splice.start_row = start_row;
splice.start_col = start_col;
splice.start_byte = start_byte;
splice.old_row = old_row;
splice.old_col = old_col;
splice.old_byte = old_byte;
splice.new_row = new_row;
splice.new_col = new_col;
splice.new_byte = new_byte;
kv_push(uhp->uh_extmark,
((ExtmarkUndoObject){ .type = kExtmarkSplice,
@@ -635,29 +663,31 @@ void extmark_splice_cols(buf_T *buf,
colnr_T old_col, colnr_T new_col,
ExtmarkOp undo)
{
extmark_splice(buf, start_row, start_col, 0, old_col, 0, new_col, undo);
extmark_splice(buf, start_row, start_col,
0, old_col, old_col,
0, new_col, new_col, undo);
}
void extmark_move_region(buf_T *buf,
int start_row, colnr_T start_col,
int extent_row, colnr_T extent_col,
int new_row, colnr_T new_col,
int start_row, colnr_T start_col, bcount_t start_byte,
int extent_row, colnr_T extent_col, bcount_t extent_byte,
int new_row, colnr_T new_col, bcount_t new_byte,
ExtmarkOp undo)
{
// TODO(bfredl): this is not synced to the buffer state inside the callback.
// But unless we make the undo implementation smarter, this is not ensured
// anyway.
buf_updates_send_splice(buf, start_row, start_col,
extent_row, extent_col,
0, 0);
buf_updates_send_splice(buf, start_row, start_col, start_byte,
extent_row, extent_col, extent_byte,
0, 0, 0);
marktree_move_region(buf->b_marktree, start_row, start_col,
extent_row, extent_col,
new_row, new_col);
buf_updates_send_splice(buf, new_row, new_col,
0, 0,
extent_row, extent_col);
buf_updates_send_splice(buf, new_row, new_col, new_byte,
0, 0, 0,
extent_row, extent_col, extent_byte);
if (undo == kExtmarkUndo) {
@@ -669,10 +699,13 @@ void extmark_move_region(buf_T *buf,
ExtmarkMove move;
move.start_row = start_row;
move.start_col = start_col;
move.start_byte = start_byte;
move.extent_row = extent_row;
move.extent_col = extent_col;
move.extent_byte = extent_byte;
move.new_row = new_row;
move.new_col = new_col;
move.new_byte = new_byte;
kv_push(uhp->uh_extmark,
((ExtmarkUndoObject){ .type = kExtmarkMove,