diff src/fileio.c @ 40:f1d2a58883b9 v7.0024

updated for version 7.0024
author vimboss
date Fri, 24 Dec 2004 14:35:23 +0000
parents 410fa1a31baf
children f529edb9bab3
line wrap: on
line diff
--- a/src/fileio.c
+++ b/src/fileio.c
@@ -6590,6 +6590,7 @@ typedef struct AutoPat
     char	    last;		/* last pattern for apply_autocmds() */
     AutoCmd	    *cmds;		/* list of commands to do */
     struct AutoPat  *next;		/* next AutoPat in AutoPat list */
+    int		    buflocal_nr;	/* !=0 for buffer-local AutoPat */
 } AutoPat;
 
 static struct event_name
@@ -6686,8 +6687,13 @@ typedef struct AutoPatCmd
     char_u	*sfname;	/* sfname to match with */
     char_u	*tail;		/* tail of fname */
     EVENT_T	event;		/* current event */
+    int		arg_bufnr;	/* initially equal to <abuf>, set to zero when
+				   buf is deleted */
+    struct AutoPatCmd   *next;	/* chain of active apc-s for auto-invalidation*/
 } AutoPatCmd;
 
+AutoPatCmd *active_apc_list = NULL;	/* stack of active autocommands */
+
 /*
  * augroups stores a list of autocmd group names.
  */
@@ -6721,6 +6727,7 @@ static char_u *getnextac __ARGS((int c, 
 static int apply_autocmds_group __ARGS((EVENT_T event, char_u *fname, char_u *fname_io, int force, int group, buf_T *buf, exarg_T *eap));
 static void auto_next_pat __ARGS((AutoPatCmd *apc, int stop_at_last));
 
+
 static EVENT_T	last_event;
 static int	last_group;
 
@@ -6795,6 +6802,7 @@ au_remove_pat(ap)
 {
     vim_free(ap->pat);
     ap->pat = NULL;
+    ap->buflocal_nr = -1;
     au_need_clean = TRUE;
 }
 
@@ -6869,6 +6877,39 @@ au_cleanup()
 }
 
 /*
+ * Called when buffer is freed, to remove/invalidate related buffer-local
+ * autocmds.
+ */
+    void
+aubuflocal_remove(buf)
+    buf_T	*buf;
+{
+    AutoPat	*ap;
+    EVENT_T	event;
+    AutoPatCmd	*apc;
+
+    /* invalidate currently executing autocommands */
+    for (apc = active_apc_list; apc; apc = apc->next)
+	if (buf->b_fnum == apc->arg_bufnr)
+	    apc->arg_bufnr = 0;
+
+    /* invalidate buflocals looping through events */
+    for (event = (EVENT_T)0; (int)event < (int)NUM_EVENTS;
+					    event = (EVENT_T)((int)event + 1))
+	/* loop over all autocommand patterns */
+	for (ap = first_autopat[(int)event]; ap != NULL; ap = ap->next)
+	    if (ap->buflocal_nr == buf->b_fnum)
+	    {
+		au_remove_pat(ap);
+		if (p_verbose >= 6)
+		    smsg((char_u *)
+			    _("auto-removing autocommand: %s <buffer=%d>"),
+					   event_nr2name(event), buf->b_fnum);
+	    }
+    au_cleanup();
+}
+
+/*
  * Add an autocmd group name.
  * Return it's ID.  Returns AUGROUP_ERROR (< 0) for error.
  */
@@ -7292,6 +7333,9 @@ do_autocmd_event(event, pat, nested, cmd
     int		findgroup;
     int		allgroups;
     int		patlen;
+    int	        is_buflocal;
+    int		buflocal_nr;
+    char_u	buflocal_pat[25];	/* for "<buffer=X>" */
 
     if (group == AUGROUP_ALL)
 	findgroup = current_augroup;
@@ -7339,6 +7383,39 @@ do_autocmd_event(event, pat, nested, cmd
 	patlen = (int)(endpat - pat);
 
 	/*
+	 * detect special <buflocal[=X]> buffer-local patterns
+	 */
+	is_buflocal = FALSE;
+	buflocal_nr = 0;
+
+	if (patlen >= 7 && STRNCMP(pat, "<buffer", 7) == 0
+						    && pat[patlen - 1] == '>')
+	{
+	    /* Error will be printed only for addition. printing and removing
+	     * will proceed silently. */
+	    is_buflocal = TRUE;
+	    if (patlen == 8)
+		buflocal_nr = curbuf->b_fnum;
+	    else if (patlen > 9 && pat[7] == '=')
+	    {
+		/* <buffer=abuf> */
+		if (patlen == 13 && STRNICMP(pat, "<buffer=abuf>", 13))
+		    buflocal_nr = autocmd_bufnr;
+		/* <buffer=123> */
+		else if (skipdigits(pat + 8) == pat + patlen - 1)
+		    buflocal_nr = atoi((char *)pat + 8);
+	    }
+	}
+
+	if (is_buflocal)
+	{
+	    /* normalize pat into standard "<buffer>#N" form */
+	    sprintf((char *)buflocal_pat, "<buffer=%d>", buflocal_nr);
+	    pat = buflocal_pat;			/* can modify pat and patlen */
+	    patlen = STRLEN(buflocal_pat);	/*   but not endpat */
+	}
+
+	/*
 	 * Find AutoPat entries with this pattern.
 	 */
 	prev_ap = &first_autopat[(int)event];
@@ -7351,7 +7428,9 @@ do_autocmd_event(event, pat, nested, cmd
 		 *   not specified and it's the current group, or a group was
 		 *   not specified and we are listing
 		 * - the length of the pattern matches
-		 * - the pattern matches
+		 * - the pattern matches.
+		 * For <buffer[=X]>, this condition works because we normalize
+		 * all buffer-local patterns.
 		 */
 		if ((allgroups || ap->group == findgroup)
 			&& ap->patlen == patlen
@@ -7374,7 +7453,7 @@ do_autocmd_event(event, pat, nested, cmd
 		    }
 
 		    /*
-		     * Show autocmd's for this autopat
+		     * Show autocmd's for this autopat, or buflocals <buffer=X>
 		     */
 		    else if (*cmd == NUL)
 			show_autocmd(ap, event);
@@ -7401,6 +7480,15 @@ do_autocmd_event(event, pat, nested, cmd
 	     */
 	    if (ap == NULL)
 	    {
+		/* refuse to add buffer-local ap if buffer number is invalid */
+		if (is_buflocal && (buflocal_nr == 0
+				      || buflist_findnr(buflocal_nr) == NULL))
+		{
+		    EMSGN(_("E680: <buffer=%d>: invalid buffer number "),
+								 buflocal_nr);
+		    return FAIL;
+		}
+
 		ap = (AutoPat *)alloc((unsigned)sizeof(AutoPat));
 		if (ap == NULL)
 		    return FAIL;
@@ -7411,13 +7499,23 @@ do_autocmd_event(event, pat, nested, cmd
 		    vim_free(ap);
 		    return FAIL;
 		}
-		ap->reg_pat = file_pat_to_reg_pat(pat, endpat,
-						       &ap->allow_dirs, TRUE);
-		if (ap->reg_pat == NULL)
+
+		if (is_buflocal)
+		{
+		    ap->buflocal_nr = buflocal_nr;
+		    ap->reg_pat = NULL;
+		}
+		else
 		{
-		    vim_free(ap->pat);
-		    vim_free(ap);
-		    return FAIL;
+		    ap->buflocal_nr = 0;
+		    ap->reg_pat = file_pat_to_reg_pat(pat, endpat,
+							 &ap->allow_dirs, TRUE);
+		    if (ap->reg_pat == NULL)
+		    {
+			vim_free(ap->pat);
+			vim_free(ap);
+			return FAIL;
+		    }
 		}
 		ap->cmds = NULL;
 		*prev_ap = ap;
@@ -7786,14 +7884,14 @@ apply_autocmds_group(event, fname, fname
      * autocommands are blocked.
      */
     if (first_autopat[(int)event] == NULL || autocmd_block > 0)
-	return retval;
+	goto BYPASS_AU;
 
     /*
      * When autocommands are busy, new autocommands are only executed when
      * explicitly enabled with the "nested" flag.
      */
     if (autocmd_busy && !(force || autocmd_nested))
-	return retval;
+	goto BYPASS_AU;
 
 #ifdef FEAT_EVAL
     /*
@@ -7801,20 +7899,20 @@ apply_autocmds_group(event, fname, fname
      * occurred or an exception was thrown but not caught.
      */
     if (aborting())
-	return retval;
+	goto BYPASS_AU;
 #endif
 
     /*
      * FileChangedShell never nests, because it can create an endless loop.
      */
     if (filechangeshell_busy && event == EVENT_FILECHANGEDSHELL)
-	return retval;
+	goto BYPASS_AU;
 
     /*
      * Ignore events in 'eventignore'.
      */
     if (event_ignored(event))
-	return retval;
+	goto BYPASS_AU;
 
     /*
      * Allow nesting of autocommands, but restrict the depth, because it's
@@ -7823,7 +7921,7 @@ apply_autocmds_group(event, fname, fname
     if (nesting == 10)
     {
 	EMSG(_("E218: autocommand nesting too deep"));
-	return retval;
+	goto BYPASS_AU;
     }
 
     /*
@@ -7834,7 +7932,7 @@ apply_autocmds_group(event, fname, fname
 		&& (event == EVENT_WINENTER || event == EVENT_BUFENTER))
 	    || (autocmd_no_leave
 		&& (event == EVENT_WINLEAVE || event == EVENT_BUFLEAVE)))
-	return retval;
+	goto BYPASS_AU;
 
     /*
      * Save the autocmd_* variables and info about the current buffer.
@@ -7904,7 +8002,7 @@ apply_autocmds_group(event, fname, fname
 	sfname = vim_strsave(fname);
 	/* Don't try expanding FileType, Syntax or WindowID. */
 	if (event == EVENT_FILETYPE || event == EVENT_SYNTAX
-		|| event == EVENT_REMOTEREPLY)
+						|| event == EVENT_REMOTEREPLY)
 	    fname = vim_strsave(fname);
 	else
 	    fname = FullName_save(fname, FALSE);
@@ -7912,7 +8010,8 @@ apply_autocmds_group(event, fname, fname
     if (fname == NULL)	    /* out of memory */
     {
 	vim_free(sfname);
-	return FALSE;
+	retval = FALSE;
+	goto BYPASS_AU;
     }
 
 #ifdef BACKSLASH_IN_FILENAME
@@ -7983,11 +8082,17 @@ apply_autocmds_group(event, fname, fname
     patcmd.sfname = sfname;
     patcmd.tail = tail;
     patcmd.event = event;
+    patcmd.arg_bufnr = autocmd_bufnr;
+    patcmd.next = NULL;
     auto_next_pat(&patcmd, FALSE);
 
     /* found one, start executing the autocommands */
     if (patcmd.curpat != NULL)
     {
+	/* add to active_apc_list */
+	patcmd.next = active_apc_list;
+	active_apc_list = &patcmd;
+
 #ifdef FEAT_EVAL
 	/* set v:cmdarg (only when there is a matching pattern) */
 	save_cmdbang = get_vim_var_nr(VV_CMDBANG);
@@ -8015,6 +8120,9 @@ apply_autocmds_group(event, fname, fname
 	    set_vim_var_nr(VV_CMDBANG, save_cmdbang);
 	}
 #endif
+	/* delete from active_apc_list */
+	if (active_apc_list == &patcmd)	    /* just in case */
+	    active_apc_list = patcmd.next;
     }
 
     --RedrawingDisabled;
@@ -8065,6 +8173,13 @@ apply_autocmds_group(event, fname, fname
     }
 
     au_cleanup();	/* may really delete removed patterns/commands now */
+
+BYPASS_AU:
+    /* When wiping out a buffer make sure all its buffer-local autocommands
+     * are deleted. */
+    if (event == EVENT_BUFWIPEOUT && buf != NULL)
+	aubuflocal_remove(buf);
+
     return retval;
 }
 
@@ -8089,12 +8204,16 @@ auto_next_pat(apc, stop_at_last)
 	apc->curpat = NULL;
 
 	/* only use a pattern when it has not been removed, has commands and
-	 * the group matches */
+	 * the group matches. For buffer-local autocommands only check the
+	 * buffer number. */
 	if (ap->pat != NULL && ap->cmds != NULL
 		&& (apc->group == AUGROUP_ALL || apc->group == ap->group))
 	{
-	    if (match_file_pat(ap->reg_pat, apc->fname, apc->sfname, apc->tail,
-							      ap->allow_dirs))
+	    /* execution-condition */
+	    if (ap->buflocal_nr == 0
+		    ? (match_file_pat(ap->reg_pat, apc->fname, apc->sfname,
+						   apc->tail, ap->allow_dirs))
+		    : ap->buflocal_nr == apc->arg_bufnr)
 	    {
 		name = event_nr2name(apc->event);
 		s = _("%s Auto commands for \"%s\"");
@@ -8191,11 +8310,14 @@ getnextac(c, cookie, indent)
 
 /*
  * Return TRUE if there is a matching autocommand for "fname".
+ * To account for buffer-local autocommands, function needs to know
+ * in which buffer the file will be opened.
  */
     int
-has_autocmd(event, sfname)
+has_autocmd(event, sfname, buf)
     EVENT_T	event;
     char_u	*sfname;
+    buf_T       *buf;
 {
     AutoPat	*ap;
     char_u	*fname;
@@ -8219,8 +8341,11 @@ has_autocmd(event, sfname)
 
     for (ap = first_autopat[(int)event]; ap != NULL; ap = ap->next)
 	if (ap->pat != NULL && ap->cmds != NULL
-		&& match_file_pat(ap->reg_pat, fname, sfname, tail,
-							      ap->allow_dirs))
+            && (ap->buflocal_nr == 0
+		? match_file_pat(ap->reg_pat, fname, sfname, tail,
+							       ap->allow_dirs)
+                : buf != NULL && ap->buflocal_nr == buf->b_fnum
+	   ))
 	{
 	    retval = TRUE;
 	    break;
@@ -8327,7 +8452,9 @@ get_event_name(xp, idx)
 
 /*
  * Return TRUE if an autocommand is defined for "event" and "pattern".
- * "pattern" can be NULL to accept any pattern.
+ * "pattern" can be NULL to accept any pattern. Buffer-local patterns
+ * <buffer> or <buffer=N> are accepted.
+ * Used for exists("#Event#pat")
  */
     int
 au_exists(name, name_end, pattern)
@@ -8339,6 +8466,7 @@ au_exists(name, name_end, pattern)
     char_u	*p;
     EVENT_T	event;
     AutoPat	*ap;
+    buf_T	*buflocal_buf = NULL;
 
     /* find the index (enum) for the event name */
     event_name = vim_strnsave(name, (int)(name_end - name));
@@ -8360,15 +8488,24 @@ au_exists(name, name_end, pattern)
     if (pattern == NULL)
 	return TRUE;
 
+    /* if pattern is "<buffer>", special handling is needed which uses curbuf */
+    /* for pattern "<buffer=N>, fnamecmp() will work fine */
+    if (STRICMP(pattern, "<buffer>") == 0)
+	buflocal_buf = curbuf;
+
     /* Check if there is an autocommand with the given pattern. */
     for ( ; ap != NULL; ap = ap->next)
-	/* only use a pattern when it has not been removed and has commands */
+	/* only use a pattern when it has not been removed and has commands. */
+	/* For buffer-local autocommands, fnamecmp() works fine. */
 	if (ap->pat != NULL && ap->cmds != NULL
-					   && fnamecmp(ap->pat, pattern) == 0)
+	    && (buflocal_buf == NULL
+		 ? fnamecmp(ap->pat, pattern) == 0
+		 : ap->buflocal_nr == buflocal_buf->b_fnum))
 	    return TRUE;
 
     return FALSE;
 }
+
 #endif	/* FEAT_AUTOCMD */
 
 #if defined(FEAT_AUTOCMD) || defined(FEAT_WILDIGN) || defined(PROTO)