📄 undo.c
字号:
u_sync()
{
if (curbuf->b_u_synced)
return; /* already synced */
u_getbot(); /* compute ue_bot of previous u_save */
curbuf->b_u_curhead = NULL;
}
/*
* Called after writing the file and setting b_changed to FALSE.
* Now an undo means that the buffer is modified.
*/
void
u_unchanged(buf)
BUF *buf;
{
struct u_header *uh;
for (uh = buf->b_u_newhead; uh; uh = uh->uh_next)
uh->uh_flags |= UH_CHANGED;
buf->b_did_warn = FALSE;
}
/*
* u_getbot(): compute the line number of the previous u_save
* It is called only when b_u_synced is FALSE.
*/
static void
u_getbot()
{
struct u_entry *uep;
if (curbuf->b_u_newhead == NULL ||
(uep = curbuf->b_u_newhead->uh_entry) == NULL)
{
EMSG("undo list corrupt");
return;
}
if (uep->ue_lcount != 0)
{
/*
* the new ue_bot is computed from the number of lines that has been
* inserted (0 - deleted) since calling u_save. This is equal to the old
* line count subtracted from the current line count.
*/
uep->ue_bot = uep->ue_top + uep->ue_size + 1 +
(curbuf->b_ml.ml_line_count - uep->ue_lcount);
if (uep->ue_bot < 1 || uep->ue_bot > curbuf->b_ml.ml_line_count)
{
EMSG("undo line missing");
uep->ue_bot = uep->ue_top + 1; /* assume all lines deleted, will
* get all the old lines back
* without deleting the current
* ones */
}
uep->ue_lcount = 0;
}
curbuf->b_u_synced = TRUE;
}
/*
* u_freelist: free one entry list and adjust the pointers
*/
static void
u_freelist(uhp)
struct u_header *uhp;
{
struct u_entry *uep, *nuep;
for (uep = uhp->uh_entry; uep != NULL; uep = nuep)
{
nuep = uep->ue_next;
u_freeentry(uep, uep->ue_size);
}
if (curbuf->b_u_curhead == uhp)
curbuf->b_u_curhead = NULL;
if (uhp->uh_next == NULL)
curbuf->b_u_oldhead = uhp->uh_prev;
else
uhp->uh_next->uh_prev = uhp->uh_prev;
if (uhp->uh_prev == NULL)
curbuf->b_u_newhead = uhp->uh_next;
else
uhp->uh_prev->uh_next = uhp->uh_next;
u_free_line((char_u *)uhp);
--curbuf->b_u_numhead;
}
/*
* free entry 'uep' and 'n' lines in uep->ue_array[]
*/
static void
u_freeentry(uep, n)
struct u_entry *uep;
long n;
{
while (n)
u_free_line(uep->ue_array[--n]);
u_free_line((char_u *)uep);
}
/*
* invalidate the undo buffer; called when storage has already been released
*/
void
u_clearall(buf)
BUF *buf;
{
buf->b_u_newhead = buf->b_u_oldhead = buf->b_u_curhead = NULL;
buf->b_u_synced = TRUE;
buf->b_u_numhead = 0;
buf->b_u_line_ptr = NULL;
buf->b_u_line_lnum = 0;
}
/*
* save the line "lnum" for the "U" command
*/
void
u_saveline(lnum)
linenr_t lnum;
{
if (lnum == curbuf->b_u_line_lnum) /* line is already saved */
return;
if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count) /* should never happen */
return;
u_clearline();
curbuf->b_u_line_lnum = lnum;
if (curwin->w_cursor.lnum == lnum)
curbuf->b_u_line_colnr = curwin->w_cursor.col;
else
curbuf->b_u_line_colnr = 0;
if ((curbuf->b_u_line_ptr = u_save_line(lnum)) == NULL)
do_outofmem_msg();
}
/*
* clear the line saved for the "U" command
* (this is used externally for crossing a line while in insert mode)
*/
void
u_clearline()
{
if (curbuf->b_u_line_ptr != NULL)
{
u_free_line(curbuf->b_u_line_ptr);
curbuf->b_u_line_ptr = NULL;
curbuf->b_u_line_lnum = 0;
}
}
/*
* Implementation of the "U" command.
* Differentiation from vi: "U" can be undone with the next "U".
* We also allow the cursor to be in another line.
*/
void
u_undoline()
{
colnr_t t;
char_u *oldp;
if (undo_off)
return;
if (curbuf->b_u_line_ptr == NULL ||
curbuf->b_u_line_lnum > curbuf->b_ml.ml_line_count)
{
beep_flush();
return;
}
/* first save the line for the 'u' command */
if (u_savecommon(curbuf->b_u_line_lnum - 1,
curbuf->b_u_line_lnum + 1, (linenr_t)0) == FAIL)
return;
oldp = u_save_line(curbuf->b_u_line_lnum);
if (oldp == NULL)
{
do_outofmem_msg();
return;
}
ml_replace(curbuf->b_u_line_lnum, curbuf->b_u_line_ptr, TRUE);
u_free_line(curbuf->b_u_line_ptr);
curbuf->b_u_line_ptr = oldp;
#ifdef SYNTAX_HL
/* recompute syntax hl., starting with current line */
syn_changed(curbuf->b_u_line_lnum);
#endif
t = curbuf->b_u_line_colnr;
if (curwin->w_cursor.lnum == curbuf->b_u_line_lnum)
curbuf->b_u_line_colnr = curwin->w_cursor.col;
changed_cline_bef_curs();
curwin->w_cursor.col = t;
curwin->w_cursor.lnum = curbuf->b_u_line_lnum;
approximate_botline(); /* w_botline may have changed a bit */
update_screen(VALID_TO_CURSCHAR);
}
/*
* storage allocation for the undo lines and blocks of the current file
*/
/*
* Memory is allocated in relatively large blocks. These blocks are linked
* in the allocated block list, headed by curbuf->b_block_head. They are all
* freed when abandoning a file, so we don't have to free every single line.
* The list is kept sorted on memory address.
* block_alloc() allocates a block.
* m_blockfree() frees all blocks.
*
* The available chunks of memory are kept in free chunk lists. There is
* one free list for each block of allocated memory. The list is kept sorted
* on memory address.
* u_alloc_line() gets a chunk from the free lists.
* u_free_line() returns a chunk to the free lists.
* curbuf->b_m_search points to the chunk before the chunk that was
* freed/allocated the last time.
* curbuf->b_mb_current points to the b_head where curbuf->b_m_search
* points into the free list.
*
*
* b_block_head /---> block #1 /---> block #2
* mb_next ---/ mb_next ---/ mb_next ---> NULL
* mb_info mb_info mb_info
* | | |
* V V V
* NULL free chunk #1.1 free chunk #2.1
* | |
* V V
* free chunk #1.2 NULL
* |
* V
* NULL
*
* When a single free chunk list would have been used, it could take a lot
* of time in u_free_line() to find the correct place to insert a chunk in the
* free list. The single free list would become very long when many lines are
* changed (e.g. with :%s/^M$//).
*/
/*
* this blocksize is used when allocating new lines
*/
#define MEMBLOCKSIZE 2044
/*
* The size field contains the size of the chunk, including the size field
* itself.
*
* When the chunk is not in-use it is preceded with the m_info structure.
* The m_next field links it in one of the free chunk lists.
*
* On most unix systems structures have to be longword (32 or 64 bit) aligned.
* On most other systems they are short (16 bit) aligned.
*/
/* the structure definitions are now in structs.h */
#ifdef ALIGN_LONG
/* size of m_size */
# define M_OFFSET (sizeof(long_u))
#else
/* size of m_size */
# define M_OFFSET (sizeof(short_u))
#endif
/*
* Allocate a block of memory and link it in the allocated block list.
*/
static char_u *
u_blockalloc(size)
long_u size;
{
struct m_block *p;
struct m_block *mp, *next;
p = (struct m_block *)lalloc(size + sizeof(struct m_block), FALSE);
if (p != NULL)
{
/* Insert the block into the allocated block list, keeping it
sorted on address. */
for (mp = &curbuf->b_block_head;
(next = mp->mb_next) != NULL && next < p;
mp = next)
;
p->mb_next = next; /* link in block list */
mp->mb_next = p;
p->mb_info.m_next = NULL; /* clear free list */
p->mb_info.m_size = 0;
curbuf->b_mb_current = p; /* remember current block */
curbuf->b_m_search = NULL;
p++; /* return usable memory */
}
return (char_u *)p;
}
/*
* free all allocated memory blocks for the buffer 'buf'
*/
void
u_blockfree(buf)
BUF *buf;
{
struct m_block *p, *np;
for (p = buf->b_block_head.mb_next; p != NULL; p = np)
{
np = p->mb_next;
vim_free(p);
}
buf->b_block_head.mb_next = NULL;
buf->b_m_search = NULL;
buf->b_mb_current = NULL;
}
/*
* Free a chunk of memory.
* Insert the chunk into the correct free list, keeping it sorted on address.
*/
static void
u_free_line(ptr)
char_u *ptr;
{
info_t *next;
info_t *prev, *curr;
info_t *mp;
struct m_block *nextb;
if (ptr == NULL || ptr == IObuff)
return; /* illegal address can happen in out-of-memory situations */
mp = (info_t *)(ptr - M_OFFSET);
/* find block where chunk could be a part off */
/* if we change curbuf->b_mb_current, curbuf->b_m_search is set to NULL */
if (curbuf->b_mb_current == NULL || mp < (info_t *)curbuf->b_mb_current)
{
curbuf->b_mb_current = curbuf->b_block_head.mb_next;
curbuf->b_m_search = NULL;
}
if ((nextb = curbuf->b_mb_current->mb_next) != NULL && (info_t *)nextb < mp)
{
curbuf->b_mb_current = nextb;
curbuf->b_m_search = NULL;
}
while ((nextb = curbuf->b_mb_current->mb_next) != NULL &&
(info_t *)nextb < mp)
curbuf->b_mb_current = nextb;
curr = NULL;
/*
* If mp is smaller than curbuf->b_m_search->m_next go to the start of
* the free list
*/
if (curbuf->b_m_search == NULL || mp < (curbuf->b_m_search->m_next))
next = &(curbuf->b_mb_current->mb_info);
else
next = curbuf->b_m_search;
/*
* The following loop is executed very often.
* Therefore it has been optimized at the cost of readability.
* Keep it fast!
*/
#ifdef SLOW_BUT_EASY_TO_READ
do
{
prev = curr;
curr = next;
next = next->m_next;
}
while (mp > next && next != NULL);
#else
do /* first, middle, last */
{
prev = next->m_next; /* curr, next, prev */
if (prev == NULL || mp <= prev)
{
prev = curr;
curr = next;
next = next->m_next;
break;
}
curr = prev->m_next; /* next, prev, curr */
if (curr == NULL || mp <= curr)
{
prev = next;
curr = prev->m_next;
next = curr->m_next;
break;
}
next = curr->m_next; /* prev, curr, next */
}
while (mp > next && next != NULL);
#endif
/* if *mp and *next are concatenated, join them into one chunk */
if ((char_u *)mp + mp->m_size == (char_u *)next)
{
mp->m_size += next->m_size;
mp->m_next = next->m_next;
}
else
mp->m_next = next;
/* if *curr and *mp are concatenated, join them */
if (prev != NULL && (char_u *)curr + curr->m_size == (char_u *)mp)
{
curr->m_size += mp->m_size;
curr->m_next = mp->m_next;
curbuf->b_m_search = prev;
}
else
{
curr->m_next = mp;
curbuf->b_m_search = curr; /* put curbuf->b_m_search before freed
chunk */
}
}
/*
* Allocate and initialize a new line structure with room for at least
* 'size' characters plus a terminating NUL.
*/
static char_u *
u_alloc_line(size)
unsigned size;
{
info_t *mp, *mprev, *mp2;
struct m_block *mbp;
int size_align;
/*
* Add room for size field and trailing NUL byte.
* Adjust for minimal size (must be able to store info_t
* plus a trailing NUL, so the chunk can be released again)
*/
size += M_OFFSET + 1;
if (size < sizeof(info_t) + 1)
size = sizeof(info_t) + 1;
/*
* round size up for alignment
*/
size_align = (size + ALIGN_MASK) & ~ALIGN_MASK;
/*
* If curbuf->b_m_search is NULL (uninitialized free list) start at
* curbuf->b_block_head
*/
if (curbuf->b_mb_current == NULL || curbuf->b_m_search == NULL)
{
curbuf->b_mb_current = &curbuf->b_block_head;
curbuf->b_m_search = &(curbuf->b_block_head.mb_info);
}
/* search for space in free list */
mprev = curbuf->b_m_search;
mbp = curbuf->b_mb_current;
mp = curbuf->b_m_search->m_next;
if (mp == NULL)
{
if (mbp->mb_next)
mbp = mbp->mb_next;
else
mbp = &curbuf->b_block_head;
mp = curbuf->b_m_search = &(mbp->mb_info);
}
while (mp->m_size < size)
{
if (mp == curbuf->b_m_search) /* back where we started in free
chunk list */
{
if (mbp->mb_next)
mbp = mbp->mb_next;
else
mbp = &curbuf->b_block_head;
mp = curbuf->b_m_search = &(mbp->mb_info);
if (mbp == curbuf->b_mb_current) /* back where we started in
block list */
{
int n = (size_align > (MEMBLOCKSIZE / 4)
? size_align : MEMBLOCKSIZE);
mp = (info_t *)u_blockalloc((long_u)n);
if (mp == NULL)
return (NULL);
mp->m_size = n;
u_free_line((char_u *)mp + M_OFFSET);
mp = curbuf->b_m_search;
mbp = curbuf->b_mb_current;
}
}
mprev = mp;
if ((mp = mp->m_next) == NULL) /* at end of the list */
mp = &(mbp->mb_info); /* wrap around to begin */
}
/* if the chunk we found is large enough, split it up in two */
if ((long)mp->m_size - size_align >= (long)(sizeof(info_t) + 1))
{
mp2 = (info_t *)((char_u *)mp + size_align);
mp2->m_size = mp->m_size - size_align;
mp2->m_next = mp->m_next;
mprev->m_next = mp2;
mp->m_size = size_align;
}
else /* remove *mp from the free list */
{
mprev->m_next = mp->m_next;
}
curbuf->b_m_search = mprev;
curbuf->b_mb_current = mbp;
mp = (info_t *)((char_u *)mp + M_OFFSET);
*(char_u *)mp = NUL; /* set the first byte to NUL */
return ((char_u *)mp);
}
/*
* u_save_line(): allocate memory with u_alloc_line() and copy line 'lnum'
* into it.
*/
static char_u *
u_save_line(lnum)
linenr_t lnum;
{
char_u *src;
char_u *dst;
unsigned len;
src = ml_get(lnum);
len = STRLEN(src);
if ((dst = u_alloc_line(len)) != NULL)
mch_memmove(dst, src, (size_t)(len + 1));
return (dst);
}
/*
* Check if the 'modified' flag is set, or 'ff' has changed (only need to
* check the first character, because it can only be "dos", "unix" or "mac").
*/
int
buf_changed(buf)
BUF *buf;
{
return (buf->b_changed || *buf->b_p_ff != buf->b_start_ffc);
}
int
curbuf_changed()
{
return (curbuf->b_changed || *curbuf->b_p_ff != curbuf->b_start_ffc);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -