⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 undo.c

📁 VIM文本编辑器
💻 C
📖 第 1 页 / 共 2 页
字号:
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 + -