invoke_listeners(buf_T *buf)
{
    listener_T	*lnr;
    typval_T	rettv;
    typval_T	argv[6];
    listitem_T	*li;
    linenr_T	start = MAXLNUM;
    linenr_T	end = 0;
    linenr_T	added = 0;
    int		save_updating_screen = updating_screen;
    static int	recursive = FALSE;
    listener_T	*next;

    if (buf->b_recorded_changes == NULL  // nothing changed
	    || buf->b_listener == NULL   // no listeners
	    || recursive)		 // already busy
	return;
    recursive = TRUE;

    // Block messages on channels from being handled, so that they don't make
    // text changes here.
    ++updating_screen;

    argv[0].v_type = VAR_NUMBER;
    argv[0].vval.v_number = buf->b_fnum; // a:bufnr

    FOR_ALL_LIST_ITEMS(buf->b_recorded_changes, li)
    {
	varnumber_T lnum;

	lnum = dict_get_number(li->li_tv.vval.v_dict, (char_u *)"lnum");
	if (start > lnum)
	    start = lnum;
	lnum = dict_get_number(li->li_tv.vval.v_dict, (char_u *)"end");
	if (end < lnum)
	    end = lnum;
	added += dict_get_number(li->li_tv.vval.v_dict, (char_u *)"added");
    }
    argv[1].v_type = VAR_NUMBER;
    argv[1].vval.v_number = start;
    argv[2].v_type = VAR_NUMBER;
    argv[2].vval.v_number = end;
    argv[3].v_type = VAR_NUMBER;
    argv[3].vval.v_number = added;

    argv[4].v_type = VAR_LIST;
    argv[4].vval.v_list = buf->b_recorded_changes;
    ++textwinlock;

    for (lnr = buf->b_listener; lnr != NULL; lnr = lnr->lr_next)
    {
	call_callback(&lnr->lr_callback, -1, &rettv, 5, argv);
	clear_tv(&rettv);
    }

    // If f_listener_remove() was called may have to remove a listener now.
    for (lnr = buf->b_listener; lnr != NULL; lnr = next)
    {
	listener_T	*prev = NULL;

	next = lnr->lr_next;
	if (lnr->lr_id == 0)
	    remove_listener(buf, lnr, prev);
	else
	    prev = lnr;
    }

    --textwinlock;
    list_unref(buf->b_recorded_changes);
    buf->b_recorded_changes = NULL;

    if (save_updating_screen)
	updating_screen = TRUE;
    else
	after_updating_screen(TRUE);
    recursive = FALSE;
}
invoke_listeners(buf_T *buf)
{
    listener_T	*lnr;
    typval_T	rettv;
    typval_T	argv[6];
    listitem_T	*li;
    linenr_T	start = MAXLNUM;
    linenr_T	end = 0;
    linenr_T	added = 0;
    int		save_updating_screen = updating_screen;
    static int	recursive = FALSE;
    listener_T	*next;

    if (buf->b_recorded_changes == NULL   
	    || buf->b_listener == NULL    
	    || recursive)		  
	return;
    recursive = TRUE;

     
     
    ++updating_screen;

    argv[0].v_type = VAR_NUMBER;
    argv[0].vval.v_number = buf->b_fnum;  

    FOR_ALL_LIST_ITEMS(buf->b_recorded_changes, li)
    {
	varnumber_T lnum;

	lnum = dict_get_number(li->li_tv.vval.v_dict, (char_u *)"lnum");
	if (start > lnum)
	    start = lnum;
	lnum = dict_get_number(li->li_tv.vval.v_dict, (char_u *)"end");
	if (end < lnum)
	    end = lnum;
	added += dict_get_number(li->li_tv.vval.v_dict, (char_u *)"added");
    }
    argv[1].v_type = VAR_NUMBER;
    argv[1].vval.v_number = start;
    argv[2].v_type = VAR_NUMBER;
    argv[2].vval.v_number = end;
    argv[3].v_type = VAR_NUMBER;
    argv[3].vval.v_number = added;

    argv[4].v_type = VAR_LIST;
    argv[4].vval.v_list = buf->b_recorded_changes;
    ++textwinlock;

    for (lnr = buf->b_listener; lnr != NULL; lnr = lnr->lr_next)
    {
	call_callback(&lnr->lr_callback, -1, &rettv, 5, argv);
	clear_tv(&rettv);
    }

     
    for (lnr = buf->b_listener; lnr != NULL; lnr = next)
    {
	listener_T	*prev = NULL;

	next = lnr->lr_next;
	if (lnr->lr_id == 0)
	    remove_listener(buf, lnr, prev);
	else
	    prev = lnr;
    }

    --textwinlock;
    list_unref(buf->b_recorded_changes);
    buf->b_recorded_changes = NULL;

    if (save_updating_screen)
	updating_screen = TRUE;
    else
	after_updating_screen(TRUE);
    recursive = FALSE;
}