mirror of
https://github.com/neovim/neovim.git
synced 2026-07-10 19:39:38 +00:00
refactor(cmdpreview): lift undo-checkpoint code
This is a mechanical lift of undo save-and-restore logic into... the module that owns undo.
This commit is contained in:
@@ -1222,7 +1222,7 @@ Object nvim_buf_call(Buffer buf, LuaRef fn, lua_State *lstate, Error *err)
|
||||
}
|
||||
|
||||
TRY_WRAP(err, {
|
||||
CtxSwitch cs;
|
||||
CtxSwitch cs = { 0 };
|
||||
ctx_switch(&cs, NULL, NULL, b, 0);
|
||||
|
||||
Array args = ARRAY_DICT_INIT;
|
||||
|
||||
@@ -375,7 +375,7 @@ Object nvim_win_call(Window win, LuaRef fn, lua_State *lstate, Error *err)
|
||||
tabpage_T *tabpage = win_find_tabpage(w);
|
||||
|
||||
TRY_WRAP(err, {
|
||||
CtxSwitch cs;
|
||||
CtxSwitch cs = { 0 };
|
||||
if (ctx_switch(&cs, w, tabpage, NULL, kCtxNoDisplay | kCtxKeepCwd | kCtxValidate)) {
|
||||
Array args = ARRAY_DICT_INIT;
|
||||
nlua_call_ref(fn, NULL, args, kRetMultiStack, NULL, err);
|
||||
|
||||
@@ -518,7 +518,7 @@ void f_win_execute(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
||||
return;
|
||||
}
|
||||
|
||||
CtxSwitch cs;
|
||||
CtxSwitch cs = { 0 };
|
||||
if (ctx_switch(&cs, wp, tp, NULL, kCtxNoDisplay | kCtxKeepCwd | kCtxValidate)) {
|
||||
execute_common(argvars, rettv, 1);
|
||||
}
|
||||
|
||||
@@ -157,31 +157,13 @@ typedef struct {
|
||||
bool did_hist_navigate;
|
||||
} CommandLineState;
|
||||
|
||||
typedef struct {
|
||||
u_header_T *save_b_u_oldhead;
|
||||
u_header_T *save_b_u_newhead;
|
||||
u_header_T *save_b_u_curhead;
|
||||
int save_b_u_numhead;
|
||||
bool save_b_u_synced;
|
||||
int save_b_u_seq_last;
|
||||
int save_b_u_save_nr_last;
|
||||
int save_b_u_seq_cur;
|
||||
time_t save_b_u_time_cur;
|
||||
int save_b_u_save_nr_cur;
|
||||
char *save_b_u_line_ptr;
|
||||
linenr_T save_b_u_line_lnum;
|
||||
colnr_T save_b_u_line_colnr;
|
||||
} CpUndoInfo;
|
||||
|
||||
typedef struct {
|
||||
buf_T *buf;
|
||||
OptInt save_b_p_ul;
|
||||
int save_b_p_ma;
|
||||
int save_b_changed;
|
||||
pos_T save_b_op_start;
|
||||
pos_T save_b_op_end;
|
||||
varnumber_T save_changedtick;
|
||||
CpUndoInfo undo_info;
|
||||
UndoCheckpoint undo_checkpoint;
|
||||
} CpBufInfo;
|
||||
|
||||
typedef struct {
|
||||
@@ -2574,45 +2556,6 @@ static void cmdpreview_close_win(void)
|
||||
}
|
||||
}
|
||||
|
||||
/// Save the undo state of a buffer for command preview.
|
||||
static void cmdpreview_save_undo(CpUndoInfo *cp_undoinfo, buf_T *buf)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
cp_undoinfo->save_b_u_synced = buf->b_u_synced;
|
||||
cp_undoinfo->save_b_u_oldhead = buf->b_u_oldhead;
|
||||
cp_undoinfo->save_b_u_newhead = buf->b_u_newhead;
|
||||
cp_undoinfo->save_b_u_curhead = buf->b_u_curhead;
|
||||
cp_undoinfo->save_b_u_numhead = buf->b_u_numhead;
|
||||
cp_undoinfo->save_b_u_seq_last = buf->b_u_seq_last;
|
||||
cp_undoinfo->save_b_u_save_nr_last = buf->b_u_save_nr_last;
|
||||
cp_undoinfo->save_b_u_seq_cur = buf->b_u_seq_cur;
|
||||
cp_undoinfo->save_b_u_time_cur = buf->b_u_time_cur;
|
||||
cp_undoinfo->save_b_u_save_nr_cur = buf->b_u_save_nr_cur;
|
||||
cp_undoinfo->save_b_u_line_ptr = buf->b_u_line_ptr;
|
||||
cp_undoinfo->save_b_u_line_lnum = buf->b_u_line_lnum;
|
||||
cp_undoinfo->save_b_u_line_colnr = buf->b_u_line_colnr;
|
||||
}
|
||||
|
||||
/// Restore the undo state of a buffer for command preview.
|
||||
static void cmdpreview_restore_undo(const CpUndoInfo *cp_undoinfo, buf_T *buf)
|
||||
{
|
||||
buf->b_u_oldhead = cp_undoinfo->save_b_u_oldhead;
|
||||
buf->b_u_newhead = cp_undoinfo->save_b_u_newhead;
|
||||
buf->b_u_curhead = cp_undoinfo->save_b_u_curhead;
|
||||
buf->b_u_numhead = cp_undoinfo->save_b_u_numhead;
|
||||
buf->b_u_seq_last = cp_undoinfo->save_b_u_seq_last;
|
||||
buf->b_u_save_nr_last = cp_undoinfo->save_b_u_save_nr_last;
|
||||
buf->b_u_seq_cur = cp_undoinfo->save_b_u_seq_cur;
|
||||
buf->b_u_time_cur = cp_undoinfo->save_b_u_time_cur;
|
||||
buf->b_u_save_nr_cur = cp_undoinfo->save_b_u_save_nr_cur;
|
||||
buf->b_u_line_ptr = cp_undoinfo->save_b_u_line_ptr;
|
||||
buf->b_u_line_lnum = cp_undoinfo->save_b_u_line_lnum;
|
||||
buf->b_u_line_colnr = cp_undoinfo->save_b_u_line_colnr;
|
||||
if (buf->b_u_curhead == NULL) {
|
||||
buf->b_u_synced = cp_undoinfo->save_b_u_synced;
|
||||
}
|
||||
}
|
||||
|
||||
/// Save current state and prepare windows and buffers for command preview.
|
||||
static void cmdpreview_prepare(CpInfo *cpinfo)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
@@ -2634,17 +2577,12 @@ static void cmdpreview_prepare(CpInfo *cpinfo)
|
||||
CpBufInfo cp_bufinfo;
|
||||
cp_bufinfo.buf = buf;
|
||||
cp_bufinfo.save_b_p_ma = buf->b_p_ma;
|
||||
cp_bufinfo.save_b_p_ul = buf->b_p_ul;
|
||||
cp_bufinfo.save_b_changed = buf->b_changed;
|
||||
cp_bufinfo.save_b_op_start = buf->b_op_start;
|
||||
cp_bufinfo.save_b_op_end = buf->b_op_end;
|
||||
cp_bufinfo.save_changedtick = buf_get_changedtick(buf);
|
||||
cmdpreview_save_undo(&cp_bufinfo.undo_info, buf);
|
||||
u_checkpoint(&cp_bufinfo.undo_checkpoint, buf);
|
||||
kv_push(cpinfo->buf_info, cp_bufinfo);
|
||||
set_put(ptr_t, &saved_bufs, buf);
|
||||
|
||||
u_clearall(buf);
|
||||
buf->b_p_ul = INT_MAX; // Make sure we can undo all changes
|
||||
}
|
||||
|
||||
CpWinInfo cp_wininfo;
|
||||
@@ -2692,38 +2630,11 @@ static void cmdpreview_restore_state(CpInfo *cpinfo)
|
||||
// Clear preview highlights.
|
||||
extmark_clear(buf, (uint32_t)cmdpreview_ns, 0, 0, MAXLNUM, MAXCOL);
|
||||
|
||||
if (buf->b_u_seq_cur != cp_bufinfo.undo_info.save_b_u_seq_cur) {
|
||||
int count = 0;
|
||||
|
||||
// Calculate how many undo steps are necessary to restore earlier state.
|
||||
for (u_header_T *uhp = buf->b_u_curhead ? buf->b_u_curhead : buf->b_u_newhead;
|
||||
uhp != NULL;
|
||||
uhp = uhp->uh_next.ptr, ++count) {}
|
||||
|
||||
aco_save_T aco = { 0 };
|
||||
aucmd_prepbuf(&aco, buf);
|
||||
// Ensure all the entries will be undone
|
||||
if (curbuf->b_u_synced == false) {
|
||||
u_sync(true);
|
||||
}
|
||||
// Undo invisibly. This also moves the cursor!
|
||||
if (!u_undo_and_forget(count, false)) {
|
||||
abort();
|
||||
}
|
||||
aucmd_restbuf(&aco);
|
||||
}
|
||||
|
||||
u_blockfree(buf);
|
||||
cmdpreview_restore_undo(&cp_bufinfo.undo_info, buf);
|
||||
u_rollback(&cp_bufinfo.undo_checkpoint, buf);
|
||||
|
||||
buf->b_op_start = cp_bufinfo.save_b_op_start;
|
||||
buf->b_op_end = cp_bufinfo.save_b_op_end;
|
||||
|
||||
if (cp_bufinfo.save_changedtick != buf_get_changedtick(buf)) {
|
||||
buf_set_changedtick(buf, cp_bufinfo.save_changedtick);
|
||||
}
|
||||
|
||||
buf->b_p_ul = cp_bufinfo.save_b_p_ul; // Restore 'undolevels'
|
||||
buf->b_p_ma = cp_bufinfo.save_b_p_ma; // Restore 'modifiable'
|
||||
}
|
||||
|
||||
|
||||
@@ -88,6 +88,7 @@
|
||||
#include "nvim/buffer_defs.h"
|
||||
#include "nvim/buffer_updates.h"
|
||||
#include "nvim/change.h"
|
||||
#include "nvim/context.h"
|
||||
#include "nvim/cursor.h"
|
||||
#include "nvim/drawscreen.h"
|
||||
#include "nvim/edit.h"
|
||||
@@ -3248,3 +3249,78 @@ u_header_T *u_force_get_undo_header(buf_T *buf)
|
||||
}
|
||||
return uhp;
|
||||
}
|
||||
|
||||
/// Checkpoints the undo state of `buf` and detaches its undotree: subsequent edits build
|
||||
/// a disposable tree. Then u_rollback() can revert them without a trace ("undo-invisible"
|
||||
/// speculative edits, e.g. 'inccommand' preview). Sets 'undolevels' so every edit stays undoable.
|
||||
void u_checkpoint(UndoCheckpoint *uc, buf_T *buf)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
uc->uc_synced = buf->b_u_synced;
|
||||
uc->uc_oldhead = buf->b_u_oldhead;
|
||||
uc->uc_newhead = buf->b_u_newhead;
|
||||
uc->uc_curhead = buf->b_u_curhead;
|
||||
uc->uc_numhead = buf->b_u_numhead;
|
||||
uc->uc_seq_last = buf->b_u_seq_last;
|
||||
uc->uc_save_nr_last = buf->b_u_save_nr_last;
|
||||
uc->uc_seq_cur = buf->b_u_seq_cur;
|
||||
uc->uc_time_cur = buf->b_u_time_cur;
|
||||
uc->uc_save_nr_cur = buf->b_u_save_nr_cur;
|
||||
uc->uc_line_ptr = buf->b_u_line_ptr;
|
||||
uc->uc_line_lnum = buf->b_u_line_lnum;
|
||||
uc->uc_line_colnr = buf->b_u_line_colnr;
|
||||
uc->uc_undolevels = buf->b_p_ul;
|
||||
uc->uc_changedtick = buf_get_changedtick(buf);
|
||||
|
||||
u_clearall(buf);
|
||||
buf->b_p_ul = INT_MAX; // Make sure we can undo all changes
|
||||
}
|
||||
|
||||
/// Reverts `buf` from a checkpoint: drops all edits made since u_checkpoint(), and reattaches the
|
||||
/// checkpointed undotree. Also restores b:changedtick and 'undolevels'.
|
||||
void u_rollback(UndoCheckpoint *uc, buf_T *buf)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
if (buf->b_u_seq_cur != uc->uc_seq_cur) {
|
||||
int count = 0;
|
||||
|
||||
// Calculate how many undo steps are necessary to restore earlier state.
|
||||
for (u_header_T *uhp = buf->b_u_curhead ? buf->b_u_curhead : buf->b_u_newhead;
|
||||
uhp != NULL;
|
||||
uhp = uhp->uh_next.ptr, ++count) {}
|
||||
|
||||
CtxSwitch cs = { 0 };
|
||||
ctx_switch(&cs, NULL, NULL, buf, 0);
|
||||
// Ensure all the entries will be undone
|
||||
if (curbuf->b_u_synced == false) {
|
||||
u_sync(true);
|
||||
}
|
||||
// Undo invisibly. This also moves the cursor!
|
||||
if (!u_undo_and_forget(count, false)) {
|
||||
abort();
|
||||
}
|
||||
ctx_restore(&cs);
|
||||
}
|
||||
|
||||
u_blockfree(buf);
|
||||
buf->b_u_oldhead = uc->uc_oldhead;
|
||||
buf->b_u_newhead = uc->uc_newhead;
|
||||
buf->b_u_curhead = uc->uc_curhead;
|
||||
buf->b_u_numhead = uc->uc_numhead;
|
||||
buf->b_u_seq_last = uc->uc_seq_last;
|
||||
buf->b_u_save_nr_last = uc->uc_save_nr_last;
|
||||
buf->b_u_seq_cur = uc->uc_seq_cur;
|
||||
buf->b_u_time_cur = uc->uc_time_cur;
|
||||
buf->b_u_save_nr_cur = uc->uc_save_nr_cur;
|
||||
buf->b_u_line_ptr = uc->uc_line_ptr;
|
||||
buf->b_u_line_lnum = uc->uc_line_lnum;
|
||||
buf->b_u_line_colnr = uc->uc_line_colnr;
|
||||
if (buf->b_u_curhead == NULL) {
|
||||
buf->b_u_synced = uc->uc_synced;
|
||||
}
|
||||
|
||||
if (uc->uc_changedtick != buf_get_changedtick(buf)) {
|
||||
buf_set_changedtick(buf, uc->uc_changedtick);
|
||||
}
|
||||
buf->b_p_ul = uc->uc_undolevels; // Restore 'undolevels'
|
||||
}
|
||||
|
||||
@@ -2,8 +2,10 @@
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#include "nvim/eval/typval_defs.h"
|
||||
#include "nvim/extmark_defs.h"
|
||||
#include "nvim/mark_defs.h"
|
||||
#include "nvim/option_defs.h"
|
||||
|
||||
enum { UNDO_HASH_SIZE = 32, }; ///< Size in bytes of the hash used in the undo file.
|
||||
|
||||
@@ -73,3 +75,24 @@ enum {
|
||||
UH_EMPTYBUF = 0x02, ///< buffer was empty
|
||||
UH_RELOAD = 0x04, ///< buffer was reloaded
|
||||
};
|
||||
|
||||
/// Checkpoint of buffer undo state, for "undo-invisible" speculative edits: u_checkpoint() detaches
|
||||
/// the undo tree so subsequent edits build a disposable one; u_rollback() reverts those edits and
|
||||
/// reattaches the checkpointed tree. Used by 'inccommand' preview.
|
||||
typedef struct {
|
||||
u_header_T *uc_oldhead;
|
||||
u_header_T *uc_newhead;
|
||||
u_header_T *uc_curhead;
|
||||
int uc_numhead;
|
||||
bool uc_synced;
|
||||
int uc_seq_last;
|
||||
int uc_save_nr_last;
|
||||
int uc_seq_cur;
|
||||
time_t uc_time_cur;
|
||||
int uc_save_nr_cur;
|
||||
char *uc_line_ptr;
|
||||
linenr_T uc_line_lnum;
|
||||
colnr_T uc_line_colnr;
|
||||
OptInt uc_undolevels; ///< saved 'undolevels'
|
||||
varnumber_T uc_changedtick; ///< saved b:changedtick
|
||||
} UndoCheckpoint;
|
||||
|
||||
Reference in New Issue
Block a user