neovim

Neovim text editor
git clone https://git.dasho.dev/neovim.git
Log | Files | Refs | README

autocmd.c (80875B)


      1 // autocmd.c: Autocommand related functions
      2 
      3 #include <assert.h>
      4 #include <stddef.h>
      5 #include <stdint.h>
      6 #include <stdio.h>
      7 #include <stdlib.h>
      8 #include <string.h>
      9 
     10 #include "nvim/api/private/converter.h"
     11 #include "nvim/autocmd.h"
     12 #include "nvim/autocmd_defs.h"
     13 #include "nvim/buffer.h"
     14 #include "nvim/charset.h"
     15 #include "nvim/cmdexpand_defs.h"
     16 #include "nvim/cursor.h"
     17 #include "nvim/errors.h"
     18 #include "nvim/eval.h"
     19 #include "nvim/eval/typval.h"
     20 #include "nvim/eval/userfunc.h"
     21 #include "nvim/eval/vars.h"
     22 #include "nvim/event/loop.h"
     23 #include "nvim/event/multiqueue.h"
     24 #include "nvim/ex_docmd.h"
     25 #include "nvim/ex_eval.h"
     26 #include "nvim/fileio.h"
     27 #include "nvim/getchar.h"
     28 #include "nvim/getchar_defs.h"
     29 #include "nvim/gettext_defs.h"
     30 #include "nvim/globals.h"
     31 #include "nvim/grid.h"
     32 #include "nvim/grid_defs.h"
     33 #include "nvim/hashtab.h"
     34 #include "nvim/highlight_defs.h"
     35 #include "nvim/insexpand.h"
     36 #include "nvim/lua/executor.h"
     37 #include "nvim/main.h"
     38 #include "nvim/map_defs.h"
     39 #include "nvim/memory.h"
     40 #include "nvim/message.h"
     41 #include "nvim/option.h"
     42 #include "nvim/option_defs.h"
     43 #include "nvim/option_vars.h"
     44 #include "nvim/os/input.h"
     45 #include "nvim/os/os.h"
     46 #include "nvim/os/os_defs.h"
     47 #include "nvim/os/time.h"
     48 #include "nvim/os/time_defs.h"
     49 #include "nvim/path.h"
     50 #include "nvim/profile.h"
     51 #include "nvim/regexp.h"
     52 #include "nvim/runtime.h"
     53 #include "nvim/runtime_defs.h"
     54 #include "nvim/search.h"
     55 #include "nvim/state.h"
     56 #include "nvim/state_defs.h"
     57 #include "nvim/strings.h"
     58 #include "nvim/types_defs.h"
     59 #include "nvim/ui.h"
     60 #include "nvim/ui_compositor.h"
     61 #include "nvim/vim_defs.h"
     62 #include "nvim/window.h"
     63 #include "nvim/winfloat.h"
     64 
     65 #include "auevents_name_map.generated.h"
     66 #include "autocmd.c.generated.h"
     67 
     68 static const char e_autocommand_nesting_too_deep[]
     69  = N_("E218: Autocommand nesting too deep");
     70 
     71 // Naming Conventions:
     72 //  - general autocmd behavior start with au_
     73 //  - AutoCmd start with aucmd_
     74 //  - AutoPat start with aupat_
     75 //  - Groups start with augroup_
     76 //  - Events start with event_
     77 
     78 // The autocommands are stored in a contiguous vector for each event.
     79 //
     80 // The order of AutoCmds is important, this is the order in which they
     81 // were defined and will have to be executed.
     82 //
     83 // To avoid having to match the pattern too often, patterns are reference
     84 // counted and reused for consecutive autocommands.
     85 
     86 // Code for automatic commands.
     87 static AutoPatCmd *active_apc_list = NULL;  // stack of active autocommands
     88 
     89 // ID for associating autocmds created via nvim_create_autocmd
     90 // Used to delete autocmds from nvim_del_autocmd
     91 static int next_augroup_id = 1;
     92 
     93 // use get_deleted_augroup() to get this
     94 static const char *deleted_augroup = NULL;
     95 
     96 // The ID of the current group.
     97 static int current_augroup = AUGROUP_DEFAULT;
     98 
     99 // Whether we need to delete marked patterns.
    100 // While deleting autocmds, they aren't actually remover, just marked.
    101 static bool au_need_clean = false;
    102 
    103 static int autocmd_blocked = 0;  // block all autocmds
    104 
    105 static bool autocmd_nested = false;
    106 static bool autocmd_include_groups = false;
    107 
    108 static bool termresponse_changed = false;
    109 
    110 // Map of autocmd group names and ids.
    111 //  name -> ID
    112 //  ID -> name
    113 static Map(String, int) map_augroup_name_to_id = MAP_INIT;
    114 static Map(int, String) map_augroup_id_to_name = MAP_INIT;
    115 
    116 void autocmd_init(void)
    117 {
    118  deferred_events = multiqueue_new_child(main_loop.events);
    119 }
    120 
    121 #ifdef EXITFREE
    122 void autocmd_free_all_mem(void)
    123 {
    124  multiqueue_free(deferred_events);
    125 }
    126 #endif
    127 
    128 static void augroup_map_del(int id, const char *name)
    129 {
    130  if (name != NULL) {
    131    String key;
    132    map_del(String, int)(&map_augroup_name_to_id, cstr_as_string(name), &key);
    133    api_free_string(key);
    134  }
    135  if (id > 0) {
    136    String mapped = map_del(int, String)(&map_augroup_id_to_name, id, NULL);
    137    api_free_string(mapped);
    138  }
    139 }
    140 
    141 static inline const char *get_deleted_augroup(void) FUNC_ATTR_ALWAYS_INLINE
    142 {
    143  if (deleted_augroup == NULL) {
    144    deleted_augroup = _("--Deleted--");
    145  }
    146  return deleted_augroup;
    147 }
    148 
    149 static void au_show_for_all_events(int group, const char *pat)
    150 {
    151  FOR_ALL_AUEVENTS(event) {
    152    au_show_for_event(group, event, pat);
    153  }
    154 }
    155 
    156 static void au_show_for_event(int group, event_T event, const char *pat)
    157  FUNC_ATTR_NONNULL_ALL
    158 {
    159  AutoCmdVec *const acs = &autocmds[(int)event];
    160  // Return early if there are no autocmds for this event
    161  if (kv_size(*acs) == 0) {
    162    return;
    163  }
    164 
    165  // Empty pattern shows all autocommands for this event
    166  int patlen = 0;
    167  if (*pat != NUL) {
    168    patlen = (int)aucmd_span_pattern(pat, &pat);
    169    if (patlen == 0) {  // Don't show if it contains only commas
    170      return;
    171    }
    172  }
    173 
    174  char buflocal_pat[BUFLOCAL_PAT_LEN];  // for "<buffer=X>"
    175  int last_group = AUGROUP_ERROR;
    176  const char *last_group_name = NULL;
    177 
    178  // Loop through all the specified patterns.
    179  do {
    180    AutoPat *last_ap = NULL;
    181    const char *endpat = pat + patlen;
    182 
    183    // detect special <buffer[=X]> buffer-local patterns
    184    if (aupat_is_buflocal(pat, patlen)) {
    185      // normalize pat into standard "<buffer>#N" form
    186      aupat_normalize_buflocal_pat(buflocal_pat, pat, patlen, aupat_get_buflocal_nr(pat, patlen));
    187      pat = buflocal_pat;
    188      patlen = (int)strlen(buflocal_pat);
    189    }
    190 
    191    for (size_t i = 0; i < kv_size(*acs); i++) {
    192      AutoCmd *const ac = &kv_A(*acs, i);
    193 
    194      // Skip deleted autocommands.
    195      if (ac->pat == NULL) {
    196        continue;
    197      }
    198 
    199      // Accept a pattern when:
    200      // - a group was specified and it's that group
    201      // - the length of the pattern matches
    202      // - the pattern matches.
    203      // For <buffer[=X]>, this condition works because we normalize
    204      // all buffer-local patterns.
    205      if ((group != AUGROUP_ALL && ac->pat->group != group)
    206          || (patlen
    207              && (ac->pat->patlen != patlen || strncmp(pat, ac->pat->pat, (size_t)patlen) != 0))) {
    208        continue;
    209      }
    210 
    211      // Show event name and group only if one of them changed.
    212      if (ac->pat->group != last_group) {
    213        last_group = ac->pat->group;
    214        last_group_name = augroup_name(ac->pat->group);
    215 
    216        if (got_int) {
    217          return;
    218        }
    219 
    220        msg_putchar('\n');
    221        if (got_int) {
    222          return;
    223        }
    224 
    225        // When switching groups, we need to show the new group information.
    226        // show the group name, if it's not the default group
    227        if (ac->pat->group != AUGROUP_DEFAULT) {
    228          if (last_group_name == NULL) {
    229            msg_puts_hl(get_deleted_augroup(), HLF_E, false);
    230          } else {
    231            msg_puts_hl(last_group_name, HLF_T, false);
    232          }
    233          msg_puts("  ");
    234        }
    235        // show the event name
    236        msg_puts_hl(event_nr2name(event), HLF_T, false);
    237      }
    238 
    239      // Show pattern only if it changed.
    240      if (last_ap != ac->pat) {
    241        last_ap = ac->pat;
    242 
    243        msg_putchar('\n');
    244        if (got_int) {
    245          return;
    246        }
    247 
    248        msg_advance(4);
    249        msg_outtrans(ac->pat->pat, 0, false);
    250      }
    251 
    252      if (got_int) {
    253        return;
    254      }
    255 
    256      if (msg_col >= 14) {
    257        msg_putchar('\n');
    258      }
    259      msg_advance(14);
    260      if (got_int) {
    261        return;
    262      }
    263 
    264      char *handler_str = aucmd_handler_to_string(ac);
    265      if (ac->desc != NULL) {
    266        size_t msglen = 100;
    267        char *msg = xmallocz(msglen);
    268        if (ac->handler_cmd) {
    269          snprintf(msg, msglen, "%s [%s]", handler_str, ac->desc);
    270        } else {
    271          msg_puts_hl(handler_str, HLF_8, false);
    272          snprintf(msg, msglen, " [%s]", ac->desc);
    273        }
    274        msg_outtrans(msg, 0, false);
    275        XFREE_CLEAR(msg);
    276      } else if (ac->handler_cmd) {
    277        msg_outtrans(handler_str, 0, false);
    278      } else {
    279        msg_puts_hl(handler_str, HLF_8, false);
    280      }
    281      XFREE_CLEAR(handler_str);
    282      if (p_verbose > 0) {
    283        last_set_msg(ac->script_ctx);
    284      }
    285 
    286      if (got_int) {
    287        return;
    288      }
    289    }
    290 
    291    patlen = (int)aucmd_span_pattern(endpat, &pat);
    292  } while (patlen);
    293 }
    294 
    295 // Delete autocommand.
    296 static void aucmd_del(AutoCmd *ac)
    297 {
    298  if (ac->pat != NULL && --ac->pat->refcount == 0) {
    299    XFREE_CLEAR(ac->pat->pat);
    300    vim_regfree(ac->pat->reg_prog);
    301    xfree(ac->pat);
    302  }
    303  ac->pat = NULL;
    304  if (ac->handler_cmd) {
    305    XFREE_CLEAR(ac->handler_cmd);
    306  } else {
    307    callback_free(&ac->handler_fn);
    308  }
    309  XFREE_CLEAR(ac->desc);
    310 
    311  au_need_clean = true;
    312 }
    313 
    314 void aucmd_del_for_event_and_group(event_T event, int group)
    315 {
    316  AutoCmdVec *const acs = &autocmds[(int)event];
    317  for (size_t i = 0; i < kv_size(*acs); i++) {
    318    AutoCmd *const ac = &kv_A(*acs, i);
    319    if (ac->pat != NULL && ac->pat->group == group) {
    320      aucmd_del(ac);
    321    }
    322  }
    323 
    324  au_cleanup();
    325 }
    326 
    327 /// Cleanup autocommands that have been deleted.
    328 /// This is only done when not executing autocommands.
    329 static void au_cleanup(void)
    330 {
    331  if (autocmd_busy || !au_need_clean) {
    332    return;
    333  }
    334 
    335  // Loop over all events.
    336  FOR_ALL_AUEVENTS(event) {
    337    // Loop over all autocommands.
    338    AutoCmdVec *const acs = &autocmds[(int)event];
    339    size_t nsize = 0;
    340    for (size_t i = 0; i < kv_size(*acs); i++) {
    341      AutoCmd *const ac = &kv_A(*acs, i);
    342      if (nsize != i) {
    343        kv_A(*acs, nsize) = *ac;
    344      }
    345      if (ac->pat != NULL) {
    346        nsize++;
    347      }
    348    }
    349    if (nsize == 0) {
    350      kv_destroy(*acs);
    351    } else {
    352      acs->size = nsize;
    353    }
    354  }
    355 
    356  au_need_clean = false;
    357 }
    358 
    359 AutoCmdVec *au_get_autocmds_for_event(event_T event)
    360  FUNC_ATTR_PURE
    361 {
    362  return &autocmds[(int)event];
    363 }
    364 
    365 // Called when buffer is freed, to remove/invalidate related buffer-local autocmds.
    366 void aubuflocal_remove(buf_T *buf)
    367 {
    368  // invalidate currently executing autocommands
    369  for (AutoPatCmd *apc = active_apc_list; apc != NULL; apc = apc->next) {
    370    if (buf->b_fnum == apc->arg_bufnr) {
    371      apc->arg_bufnr = 0;
    372    }
    373  }
    374 
    375  // invalidate buflocals looping through events
    376  FOR_ALL_AUEVENTS(event) {
    377    AutoCmdVec *const acs = &autocmds[(int)event];
    378    for (size_t i = 0; i < kv_size(*acs); i++) {
    379      AutoCmd *const ac = &kv_A(*acs, i);
    380      if (ac->pat == NULL || ac->pat->buflocal_nr != buf->b_fnum) {
    381        continue;
    382      }
    383 
    384      aucmd_del(ac);
    385 
    386      if (p_verbose >= 6) {
    387        verbose_enter();
    388        smsg(0, _("auto-removing autocommand: %s <buffer=%d>"), event_nr2name(event), buf->b_fnum);
    389        verbose_leave();
    390      }
    391    }
    392  }
    393  au_cleanup();
    394 }
    395 
    396 // Add an autocmd group name or return existing group matching name.
    397 // Return its ID.
    398 int augroup_add(const char *name)
    399 {
    400  assert(STRICMP(name, "end") != 0);
    401 
    402  int existing_id = augroup_find(name);
    403  if (existing_id > 0) {
    404    assert(existing_id != AUGROUP_DELETED);
    405    return existing_id;
    406  }
    407 
    408  if (existing_id == AUGROUP_DELETED) {
    409    augroup_map_del(existing_id, name);
    410  }
    411 
    412  int next_id = next_augroup_id++;
    413  String name_key = cstr_to_string(name);
    414  String name_val = cstr_to_string(name);
    415  map_put(String, int)(&map_augroup_name_to_id, name_key, next_id);
    416  map_put(int, String)(&map_augroup_id_to_name, next_id, name_val);
    417 
    418  return next_id;
    419 }
    420 
    421 /// Delete the augroup that matches name.
    422 /// @param stupid_legacy_mode bool: This parameter determines whether to run the augroup
    423 ///     deletion in the same fashion as `:augroup! {name}` where if there are any remaining
    424 ///     autocmds left in the augroup, it will change the name of the augroup to `--- DELETED ---`
    425 ///     but leave the autocmds existing. These are _separate_ augroups, so if you do this for
    426 ///     multiple augroups, you will have a bunch of `--- DELETED ---` augroups at the same time.
    427 ///     There is no way, as far as I could tell, how to actually delete them at this point as a user
    428 ///
    429 ///     I did not consider this good behavior, so now when NOT in stupid_legacy_mode, we actually
    430 ///     delete these groups and their commands, like you would expect (and don't leave hanging
    431 ///     `--- DELETED ---` groups around)
    432 void augroup_del(char *name, bool stupid_legacy_mode)
    433 {
    434  int group = augroup_find(name);
    435  if (group == AUGROUP_ERROR) {  // the group doesn't exist
    436    semsg(_("E367: No such group: \"%s\""), name);
    437    return;
    438  } else if (group == current_augroup) {
    439    emsg(_("E936: Cannot delete the current group"));
    440    return;
    441  }
    442 
    443  if (stupid_legacy_mode) {
    444    FOR_ALL_AUEVENTS(event) {
    445      AutoCmdVec *const acs = &autocmds[(int)event];
    446      for (size_t i = 0; i < kv_size(*acs); i++) {
    447        AutoPat *const ap = kv_A(*acs, i).pat;
    448        if (ap != NULL && ap->group == group) {
    449          give_warning(_("W19: Deleting augroup that is still in use"), true, true);
    450          map_put(String, int)(&map_augroup_name_to_id, cstr_as_string(name), AUGROUP_DELETED);
    451          augroup_map_del(ap->group, NULL);
    452          return;
    453        }
    454      }
    455    }
    456  } else {
    457    FOR_ALL_AUEVENTS(event) {
    458      AutoCmdVec *const acs = &autocmds[(int)event];
    459      for (size_t i = 0; i < kv_size(*acs); i++) {
    460        AutoCmd *const ac = &kv_A(*acs, i);
    461        if (ac->pat != NULL && ac->pat->group == group) {
    462          aucmd_del(ac);
    463        }
    464      }
    465    }
    466  }
    467 
    468  // Remove the group because it's not currently in use.
    469  augroup_map_del(group, name);
    470  au_cleanup();
    471 }
    472 
    473 /// Find the ID of an autocmd group name.
    474 ///
    475 /// @param name augroup name
    476 ///
    477 /// @return the ID or AUGROUP_ERROR (< 0) for error.
    478 int augroup_find(const char *name)
    479  FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
    480 {
    481  int existing_id = map_get(String, int)(&map_augroup_name_to_id, cstr_as_string(name));
    482  if (existing_id == AUGROUP_DELETED) {
    483    return existing_id;
    484  }
    485 
    486  if (existing_id > 0) {
    487    return existing_id;
    488  }
    489 
    490  return AUGROUP_ERROR;
    491 }
    492 
    493 /// Gets the name for a particular group.
    494 char *augroup_name(int group)
    495 {
    496  assert(group != 0);
    497 
    498  if (group == AUGROUP_DELETED) {
    499    return (char *)get_deleted_augroup();
    500  }
    501 
    502  if (group == AUGROUP_ALL) {
    503    group = current_augroup;
    504  }
    505 
    506  // next_augroup_id is the "source of truth" about what autocmds have existed
    507  //
    508  // The map_size is not the source of truth because groups can be removed from
    509  // the map. When this happens, the map size is reduced. That's why this function
    510  // relies on next_augroup_id instead.
    511 
    512  // "END" is always considered the last augroup ID.
    513  // Used for expand_get_event_name and expand_get_augroup_name
    514  if (group == next_augroup_id) {
    515    return "END";
    516  }
    517 
    518  // If it's larger than the largest group, then it doesn't have a name
    519  if (group > next_augroup_id) {
    520    return NULL;
    521  }
    522 
    523  String key = map_get(int, String)(&map_augroup_id_to_name, group);
    524  if (key.data != NULL) {
    525    return key.data;
    526  }
    527 
    528  // If it's not in the map anymore, then it must have been deleted.
    529  return (char *)get_deleted_augroup();
    530 }
    531 
    532 /// Return true if augroup "name" exists.
    533 ///
    534 /// @param name augroup name
    535 bool augroup_exists(const char *name)
    536  FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
    537 {
    538  return augroup_find(name) > 0;
    539 }
    540 
    541 /// ":augroup {name}".
    542 void do_augroup(char *arg, bool del_group)
    543 {
    544  if (del_group) {
    545    if (*arg == NUL) {
    546      emsg(_(e_argreq));
    547    } else {
    548      augroup_del(arg, true);
    549    }
    550  } else if (STRICMP(arg, "end") == 0) {  // ":aug end": back to group 0
    551    current_augroup = AUGROUP_DEFAULT;
    552  } else if (*arg) {  // ":aug xxx": switch to group xxx
    553    current_augroup = augroup_add(arg);
    554  } else {  // ":aug": list the group names
    555    msg_start();
    556    msg_ext_set_kind("list_cmd");
    557 
    558    String name;
    559    int value;
    560    map_foreach(&map_augroup_name_to_id, name, value, {
    561      if (value > 0) {
    562        msg_puts(name.data);
    563      } else {
    564        msg_puts(augroup_name(value));
    565      }
    566 
    567      msg_puts("  ");
    568    });
    569 
    570    msg_clr_eos();
    571    msg_end();
    572  }
    573 }
    574 
    575 #if defined(EXITFREE)
    576 void free_all_autocmds(void)
    577 {
    578  FOR_ALL_AUEVENTS(event) {
    579    AutoCmdVec *const acs = &autocmds[(int)event];
    580    for (size_t i = 0; i < kv_size(*acs); i++) {
    581      aucmd_del(&kv_A(*acs, i));
    582    }
    583    kv_destroy(*acs);
    584    au_need_clean = false;
    585  }
    586 
    587  // Delete the augroup_map, including free the data
    588  String name;
    589  map_foreach_key(&map_augroup_name_to_id, name, {
    590    api_free_string(name);
    591  })
    592  map_destroy(String, &map_augroup_name_to_id);
    593 
    594  map_foreach_value(&map_augroup_id_to_name, name, {
    595    api_free_string(name);
    596  })
    597  map_destroy(int, &map_augroup_id_to_name);
    598 
    599  // aucmd_win[] is freed in win_free_all()
    600 }
    601 #endif
    602 
    603 /// Return true if "win" is an active entry in aucmd_win[].
    604 bool is_aucmd_win(win_T *win)
    605 {
    606  for (int i = 0; i < AUCMD_WIN_COUNT; i++) {
    607    if (aucmd_win[i].auc_win_used && aucmd_win[i].auc_win == win) {
    608      return true;
    609    }
    610  }
    611  return false;
    612 }
    613 
    614 /// Return the event number for event name "start".
    615 /// Return NUM_EVENTS if the event name was not found.
    616 /// Return a pointer to the next event name in "end".
    617 event_T event_name2nr(const char *start, char **end)
    618 {
    619  const char *p;
    620 
    621  // the event name ends with end of line, '|', a blank or a comma
    622  for (p = start; *p && !ascii_iswhite(*p) && *p != ',' && *p != '|'; p++) {}
    623 
    624  int hash_idx = event_name2nr_hash(start, (size_t)(p - start));
    625  if (*p == ',') {
    626    p++;
    627  }
    628  *end = (char *)p;
    629  if (hash_idx < 0) {
    630    return NUM_EVENTS;
    631  }
    632  return (event_T)abs(event_names[event_hash[hash_idx]].event);
    633 }
    634 
    635 /// Return the event number for event name "str".
    636 /// Return NUM_EVENTS if the event name was not found.
    637 event_T event_name2nr_str(String str)
    638 {
    639  int hash_idx = event_name2nr_hash(str.data, str.size);
    640  if (hash_idx < 0) {
    641    return NUM_EVENTS;
    642  }
    643  return (event_T)abs(event_names[event_hash[hash_idx]].event);
    644 }
    645 
    646 /// Return the name for event
    647 ///
    648 /// @param[in]  event  Event to return name for.
    649 ///
    650 /// @return Event name, static string. Returns "Unknown" for unknown events.
    651 const char *event_nr2name(event_T event)
    652  FUNC_ATTR_NONNULL_RET FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_CONST
    653 {
    654  return event >= 0 && event < NUM_EVENTS ? event_names[event].name : "Unknown";
    655 }
    656 
    657 /// Return true if "event" is included in 'eventignore(win)'.
    658 ///
    659 /// @param event event to check
    660 bool event_ignored(event_T event, char *ei)
    661  FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
    662 {
    663  bool ignored = false;
    664  while (*ei != NUL) {
    665    bool unignore = *ei == '-';
    666    ei += unignore;
    667    if (STRNICMP(ei, "all", 3) == 0 && (ei[3] == NUL || ei[3] == ',')) {
    668      ignored = ei == p_ei || event_names[event].event <= 0;
    669      ei += 3 + (ei[3] == ',');
    670    } else if (event_name2nr(ei, &ei) == event) {
    671      if (unignore) {
    672        return false;
    673      }
    674      ignored = true;
    675    }
    676  }
    677 
    678  return ignored;
    679 }
    680 
    681 /// Return OK when the contents of 'eventignore' or 'eventignorewin' is valid,
    682 /// FAIL otherwise.
    683 int check_ei(char *ei)
    684 {
    685  bool win = ei != p_ei;
    686 
    687  while (*ei) {
    688    if (STRNICMP(ei, "all", 3) == 0 && (ei[3] == NUL || ei[3] == ',')) {
    689      ei += 3 + (ei[3] == ',');
    690    } else {
    691      ei += (*ei == '-');
    692      event_T event = event_name2nr(ei, &ei);
    693      if (event == NUM_EVENTS || (win && event_names[event].event > 0)) {
    694        return FAIL;
    695      }
    696    }
    697  }
    698 
    699  return OK;
    700 }
    701 
    702 // Add "what" to 'eventignore' to skip loading syntax highlighting for every
    703 // buffer loaded into the window.  "what" must start with a comma.
    704 // Returns the old value of 'eventignore' in allocated memory.
    705 char *au_event_disable(char *what)
    706 {
    707  size_t p_ei_len = strlen(p_ei);
    708  char *save_ei = xmemdupz(p_ei, p_ei_len);
    709  char *new_ei = xstrnsave(p_ei, p_ei_len + strlen(what));
    710  if (*what == ',' && *p_ei == NUL) {
    711    STRCPY(new_ei, what + 1);
    712  } else {
    713    STRCPY(new_ei + p_ei_len, what);
    714  }
    715  set_option_direct(kOptEventignore, CSTR_AS_OPTVAL(new_ei), 0, SID_NONE);
    716  xfree(new_ei);
    717  return save_ei;
    718 }
    719 
    720 void au_event_restore(char *old_ei)
    721 {
    722  if (old_ei != NULL) {
    723    set_option_direct(kOptEventignore, CSTR_AS_OPTVAL(old_ei), 0, SID_NONE);
    724    xfree(old_ei);
    725  }
    726 }
    727 
    728 // Implements :autocmd.
    729 // Defines an autocmd (does not execute; cf. apply_autocmds_group).
    730 //
    731 // Can be used in the following ways:
    732 //
    733 // :autocmd <event> <pat> <cmd>     Add <cmd> to the list of commands that
    734 //                                  will be automatically executed for <event>
    735 //                                  when editing a file matching <pat>, in
    736 //                                  the current group.
    737 // :autocmd <event> <pat>           Show the autocommands associated with
    738 //                                  <event> and <pat>.
    739 // :autocmd <event>                 Show the autocommands associated with
    740 //                                  <event>.
    741 // :autocmd                         Show all autocommands.
    742 // :autocmd! <event> <pat> <cmd>    Remove all autocommands associated with
    743 //                                  <event> and <pat>, and add the command
    744 //                                  <cmd>, for the current group.
    745 // :autocmd! <event> <pat>          Remove all autocommands associated with
    746 //                                  <event> and <pat> for the current group.
    747 // :autocmd! <event>                Remove all autocommands associated with
    748 //                                  <event> for the current group.
    749 // :autocmd!                        Remove ALL autocommands for the current
    750 //                                  group.
    751 //
    752 //  Multiple events and patterns may be given separated by commas.  Here are
    753 //  some examples:
    754 // :autocmd bufread,bufenter *.c,*.h    set tw=0 smartindent noic
    755 // :autocmd bufleave         *          set tw=79 nosmartindent ic infercase
    756 //
    757 // :autocmd * *.c               show all autocommands for *.c files.
    758 //
    759 // Mostly a {group} argument can optionally appear before <event>.
    760 void do_autocmd(exarg_T *eap, char *arg_in, int forceit)
    761 {
    762  char *arg = arg_in;
    763  char *envpat = NULL;
    764  char *cmd;
    765  bool need_free = false;
    766  bool nested = false;
    767  bool once = false;
    768  int group;
    769 
    770  if (*arg == '|') {
    771    eap->nextcmd = arg + 1;
    772    arg = "";
    773    group = AUGROUP_ALL;  // no argument, use all groups
    774  } else {
    775    // Check for a legal group name.  If not, use AUGROUP_ALL.
    776    group = arg_augroup_get(&arg);
    777  }
    778 
    779  // Scan over the events.
    780  // If we find an illegal name, return here, don't do anything.
    781  char *pat = arg_event_skip(arg, group != AUGROUP_ALL);
    782  if (pat == NULL) {
    783    return;
    784  }
    785 
    786  pat = skipwhite(pat);
    787  if (*pat == '|') {
    788    eap->nextcmd = pat + 1;
    789    pat = "";
    790    cmd = "";
    791  } else {
    792    // Scan over the pattern.  Put a NUL at the end.
    793    cmd = pat;
    794    while (*cmd && (!ascii_iswhite(*cmd) || cmd[-1] == '\\')) {
    795      cmd++;
    796    }
    797    if (*cmd) {
    798      *cmd++ = NUL;
    799    }
    800 
    801    // Expand environment variables in the pattern.  Set 'shellslash', we want
    802    // forward slashes here.
    803    if (vim_strchr(pat, '$') != NULL || vim_strchr(pat, '~') != NULL) {
    804 #ifdef BACKSLASH_IN_FILENAME
    805      int p_ssl_save = p_ssl;
    806 
    807      p_ssl = true;
    808 #endif
    809      envpat = expand_env_save(pat);
    810 #ifdef BACKSLASH_IN_FILENAME
    811      p_ssl = p_ssl_save;
    812 #endif
    813      if (envpat != NULL) {
    814        pat = envpat;
    815      }
    816    }
    817 
    818    cmd = skipwhite(cmd);
    819 
    820    bool invalid_flags = false;
    821    for (size_t i = 0; i < 2; i++) {
    822      if (*cmd == NUL) {
    823        continue;
    824      }
    825 
    826      invalid_flags |= arg_autocmd_flag_get(&once, &cmd, "++once", 6);
    827      invalid_flags |= arg_autocmd_flag_get(&nested, &cmd, "++nested", 8);
    828 
    829      // Check the deprecated "nested" flag.
    830      invalid_flags |= arg_autocmd_flag_get(&nested, &cmd, "nested", 6);
    831    }
    832 
    833    if (invalid_flags) {
    834      return;
    835    }
    836 
    837    // Find the start of the commands.
    838    // Expand <sfile> in it.
    839    if (*cmd != NUL) {
    840      cmd = expand_sfile(cmd);
    841      if (cmd == NULL) {  // some error
    842        return;
    843      }
    844      need_free = true;
    845    }
    846  }
    847 
    848  const bool is_showing = !forceit && *cmd == NUL;
    849 
    850  // Print header when showing autocommands.
    851  if (is_showing) {
    852    // Highlight title
    853    msg_ext_set_kind("list_cmd");
    854    msg_puts_title(_("\n--- Autocommands ---"));
    855 
    856    if (*arg == '*' || *arg == '|' || *arg == NUL) {
    857      au_show_for_all_events(group, pat);
    858    } else {
    859      event_T event = event_name2nr(arg, &arg);
    860      assert(event < NUM_EVENTS);
    861      au_show_for_event(group, event, pat);
    862    }
    863  } else {
    864    if (*arg == '*' || *arg == NUL || *arg == '|') {
    865      if (*cmd != NUL) {
    866        emsg(_(e_cannot_define_autocommands_for_all_events));
    867      } else {
    868        do_all_autocmd_events(pat, once, nested, cmd, forceit, group);
    869      }
    870    } else {
    871      while (*arg && *arg != '|' && !ascii_iswhite(*arg)) {
    872        event_T event = event_name2nr(arg, &arg);
    873        assert(event < NUM_EVENTS);
    874        if (do_autocmd_event(event, pat, once, nested, cmd, forceit, group) == FAIL) {
    875          break;
    876        }
    877      }
    878    }
    879  }
    880 
    881  if (need_free) {
    882    xfree(cmd);
    883  }
    884  xfree(envpat);
    885 }
    886 
    887 void do_all_autocmd_events(const char *pat, bool once, int nested, char *cmd, bool del, int group)
    888 {
    889  FOR_ALL_AUEVENTS(event) {
    890    if (do_autocmd_event(event, pat, once, nested, cmd, del, group) == FAIL) {
    891      return;
    892    }
    893  }
    894 }
    895 
    896 // do_autocmd() for one event.
    897 // Defines an autocmd (does not execute; cf. apply_autocmds_group).
    898 //
    899 // If *pat == NUL: do for all patterns.
    900 // If *cmd == NUL: show entries.
    901 // If forceit == true: delete entries.
    902 // If group is not AUGROUP_ALL: only use this group.
    903 int do_autocmd_event(event_T event, const char *pat, bool once, int nested, const char *cmd,
    904                     bool del, int group)
    905  FUNC_ATTR_NONNULL_ALL
    906 {
    907  // Cannot be used to show all patterns. See au_show_for_event or au_show_for_all_events
    908  assert(*pat != NUL || del);
    909 
    910  char buflocal_pat[BUFLOCAL_PAT_LEN];  // for "<buffer=X>"
    911 
    912  bool is_adding_cmd = *cmd != NUL;
    913  const int findgroup = group == AUGROUP_ALL ? current_augroup : group;
    914 
    915  // Delete all aupat for an event.
    916  if (*pat == NUL && del) {
    917    aucmd_del_for_event_and_group(event, findgroup);
    918    return OK;
    919  }
    920 
    921  // Loop through all the specified patterns.
    922  int patlen = (int)aucmd_span_pattern(pat, &pat);
    923  while (patlen) {
    924    const char *endpat = pat + patlen;
    925 
    926    // detect special <buffer[=X]> buffer-local patterns
    927    bool is_buflocal = aupat_is_buflocal(pat, patlen);
    928    if (is_buflocal) {
    929      const int buflocal_nr = aupat_get_buflocal_nr(pat, patlen);
    930 
    931      // normalize pat into standard "<buffer>#N" form
    932      aupat_normalize_buflocal_pat(buflocal_pat, pat, patlen, buflocal_nr);
    933 
    934      pat = buflocal_pat;
    935      patlen = (int)strlen(buflocal_pat);
    936    }
    937 
    938    if (del) {
    939      assert(*pat != NUL);
    940 
    941      // Find existing autocommands with this pattern.
    942      AutoCmdVec *const acs = &autocmds[(int)event];
    943      for (size_t i = 0; i < kv_size(*acs); i++) {
    944        AutoCmd *const ac = &kv_A(*acs, i);
    945        AutoPat *const ap = ac->pat;
    946        // Accept a pattern when:
    947        // - a group was specified and it's that group
    948        // - the length of the pattern matches
    949        // - the pattern matches.
    950        // For <buffer[=X]>, this condition works because we normalize
    951        // all buffer-local patterns.
    952        if (ap != NULL && ap->group == findgroup && ap->patlen == patlen
    953            && strncmp(pat, ap->pat, (size_t)patlen) == 0) {
    954          // Remove existing autocommands.
    955          // If adding any new autocmd's for this AutoPat, don't
    956          // delete the pattern from the autopat list, append to
    957          // this list.
    958          aucmd_del(ac);
    959        }
    960      }
    961    }
    962 
    963    if (is_adding_cmd) {
    964      Callback handler_fn = CALLBACK_INIT;
    965      autocmd_register(0, event, pat, patlen, group, once, nested, NULL, cmd, &handler_fn);
    966    }
    967 
    968    patlen = (int)aucmd_span_pattern(endpat, &pat);
    969  }
    970 
    971  au_cleanup();  // may really delete removed patterns/commands now
    972  return OK;
    973 }
    974 
    975 /// Registers an autocmd. The handler may be a Ex command or callback function, decided by
    976 /// the `handler_cmd` or `handler_fn` args.
    977 ///
    978 /// @param handler_cmd Handler Ex command, or NULL if handler is a function (`handler_fn`).
    979 /// @param handler_fn Handler function, ignored if `handler_cmd` is not NULL.
    980 int autocmd_register(int64_t id, event_T event, const char *pat, int patlen, int group, bool once,
    981                     bool nested, char *desc, const char *handler_cmd, Callback *handler_fn)
    982 {
    983  // 0 is not a valid group.
    984  assert(group != 0);
    985 
    986  if (patlen > (int)strlen(pat)) {
    987    return FAIL;
    988  }
    989 
    990  const int findgroup = group == AUGROUP_ALL ? current_augroup : group;
    991 
    992  // detect special <buffer[=X]> buffer-local patterns
    993  const bool is_buflocal = aupat_is_buflocal(pat, patlen);
    994  int buflocal_nr = 0;
    995 
    996  char buflocal_pat[BUFLOCAL_PAT_LEN];  // for "<buffer=X>"
    997  if (is_buflocal) {
    998    buflocal_nr = aupat_get_buflocal_nr(pat, patlen);
    999 
   1000    // normalize pat into standard "<buffer>#N" form
   1001    aupat_normalize_buflocal_pat(buflocal_pat, pat, patlen, buflocal_nr);
   1002 
   1003    pat = buflocal_pat;
   1004    patlen = (int)strlen(buflocal_pat);
   1005  }
   1006 
   1007  // Try to reuse pattern from the last existing autocommand.
   1008  AutoPat *ap = NULL;
   1009  AutoCmdVec *const acs = &autocmds[(int)event];
   1010  for (ptrdiff_t i = (ptrdiff_t)kv_size(*acs) - 1; i >= 0; i--) {
   1011    ap = kv_A(*acs, i).pat;
   1012    if (ap == NULL) {
   1013      continue;  // Skip deleted autocommands.
   1014    }
   1015    // Set result back to NULL if the last pattern doesn't match.
   1016    if (ap->group != findgroup || ap->patlen != patlen
   1017        || strncmp(pat, ap->pat, (size_t)patlen) != 0) {
   1018      ap = NULL;
   1019    }
   1020    break;
   1021  }
   1022 
   1023  // No matching pattern found, allocate a new one.
   1024  if (ap == NULL) {
   1025    // refuse to add buffer-local ap if buffer number is invalid
   1026    if (is_buflocal && (buflocal_nr == 0 || buflist_findnr(buflocal_nr) == NULL)) {
   1027      semsg(_("E680: <buffer=%d>: invalid buffer number "), buflocal_nr);
   1028      return FAIL;
   1029    }
   1030 
   1031    ap = xmalloc(sizeof(AutoPat));
   1032 
   1033    if (is_buflocal) {
   1034      ap->buflocal_nr = buflocal_nr;
   1035      ap->reg_prog = NULL;
   1036    } else {
   1037      ap->buflocal_nr = 0;
   1038      char *reg_pat = file_pat_to_reg_pat(pat, pat + patlen, &ap->allow_dirs, true);
   1039      if (reg_pat != NULL) {
   1040        ap->reg_prog = vim_regcomp(reg_pat, RE_MAGIC);
   1041      }
   1042      xfree(reg_pat);
   1043      if (reg_pat == NULL || ap->reg_prog == NULL) {
   1044        xfree(ap);
   1045        return FAIL;
   1046      }
   1047    }
   1048 
   1049    ap->refcount = 0;
   1050    ap->pat = xmemdupz(pat, (size_t)patlen);
   1051    ap->patlen = patlen;
   1052 
   1053    // need to initialize last_mode for the first ModeChanged autocmd
   1054    if (event == EVENT_MODECHANGED && !has_event(EVENT_MODECHANGED)) {
   1055      get_mode(last_mode);
   1056    }
   1057 
   1058    // If the event is CursorMoved or CursorMovedI, update the last cursor position
   1059    // position to avoid immediately triggering the autocommand
   1060    if ((event == EVENT_CURSORMOVED && !has_event(EVENT_CURSORMOVED))
   1061        || (event == EVENT_CURSORMOVEDI && !has_event(EVENT_CURSORMOVEDI))) {
   1062      last_cursormoved_win = curwin;
   1063      last_cursormoved = curwin->w_cursor;
   1064    }
   1065 
   1066    // Initialize the fields checked by the WinScrolled and
   1067    // WinResized trigger to prevent them from firing right after
   1068    // the first autocmd is defined.
   1069    if ((event == EVENT_WINSCROLLED || event == EVENT_WINRESIZED)
   1070        && !(has_event(EVENT_WINSCROLLED) || has_event(EVENT_WINRESIZED))) {
   1071      tabpage_T *save_curtab = curtab;
   1072      FOR_ALL_TABS(tp) {
   1073        unuse_tabpage(curtab);
   1074        use_tabpage(tp);
   1075        snapshot_windows_scroll_size();
   1076      }
   1077      unuse_tabpage(curtab);
   1078      use_tabpage(save_curtab);
   1079    }
   1080 
   1081    ap->group = group == AUGROUP_ALL ? current_augroup : group;
   1082  }
   1083 
   1084  ap->refcount++;
   1085 
   1086  // Add the autocmd at the end of the AutoCmd vector.
   1087  AutoCmd *ac = kv_pushp(autocmds[(int)event]);
   1088  ac->pat = ap;
   1089  ac->id = id;
   1090  if (handler_cmd) {
   1091    ac->handler_cmd = xstrdup(handler_cmd);
   1092  } else {
   1093    ac->handler_cmd = NULL;
   1094    callback_copy(&ac->handler_fn, handler_fn);
   1095  }
   1096  ac->script_ctx = current_sctx;
   1097  ac->script_ctx.sc_lnum += SOURCING_LNUM;
   1098  nlua_set_sctx(&ac->script_ctx);
   1099  ac->once = once;
   1100  ac->nested = nested;
   1101  ac->desc = desc == NULL ? NULL : xstrdup(desc);
   1102 
   1103  return OK;
   1104 }
   1105 
   1106 size_t aucmd_span_pattern(const char *pat, const char **start)
   1107  FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
   1108 {
   1109  // Skip leading commas.
   1110  while (*pat == ',') {
   1111    pat++;
   1112  }
   1113 
   1114  // Find end of the pattern.
   1115  // Watch out for a comma in braces, like "*.\{obj,o\}".
   1116  const char *p = pat;
   1117  int brace_level = 0;
   1118  for (; *p && (*p != ',' || brace_level || (p > pat && p[-1] == '\\')); p++) {
   1119    if (*p == '{') {
   1120      brace_level++;
   1121    } else if (*p == '}') {
   1122      brace_level--;
   1123    }
   1124  }
   1125 
   1126  *start = pat;
   1127  return (size_t)(p - pat);
   1128 }
   1129 
   1130 /// Implementation of ":doautocmd [group] event [fname]".
   1131 /// Return OK for success, FAIL for failure;
   1132 ///
   1133 /// @param do_msg  give message for no matching autocmds?
   1134 int do_doautocmd(char *arg_start, bool do_msg, bool *did_something)
   1135 {
   1136  char *arg = arg_start;
   1137  int nothing_done = true;
   1138 
   1139  if (did_something != NULL) {
   1140    *did_something = false;
   1141  }
   1142 
   1143  // Check for a legal group name.  If not, use AUGROUP_ALL.
   1144  int group = arg_augroup_get(&arg);
   1145 
   1146  if (*arg == '*') {
   1147    emsg(_("E217: Can't execute autocommands for ALL events"));
   1148    return FAIL;
   1149  }
   1150 
   1151  // Scan over the events.
   1152  // If we find an illegal name, return here, don't do anything.
   1153  char *fname = arg_event_skip(arg, group != AUGROUP_ALL);
   1154  if (fname == NULL) {
   1155    return FAIL;
   1156  }
   1157 
   1158  fname = skipwhite(fname);
   1159 
   1160  // Loop over the events.
   1161  while (*arg && !ends_excmd(*arg) && !ascii_iswhite(*arg)) {
   1162    if (apply_autocmds_group(event_name2nr(arg, &arg), fname, NULL, true, group,
   1163                             curbuf, NULL, NULL)) {
   1164      nothing_done = false;
   1165    }
   1166  }
   1167 
   1168  if (nothing_done && do_msg && !aborting()) {
   1169    smsg(0, _("No matching autocommands: %s"), arg_start);
   1170  }
   1171  if (did_something != NULL) {
   1172    *did_something = !nothing_done;
   1173  }
   1174 
   1175  return aborting() ? FAIL : OK;
   1176 }
   1177 
   1178 // ":doautoall": execute autocommands for each loaded buffer.
   1179 void ex_doautoall(exarg_T *eap)
   1180 {
   1181  int retval = OK;
   1182  aco_save_T aco;
   1183  char *arg = eap->arg;
   1184  int call_do_modelines = check_nomodeline(&arg);
   1185  bufref_T bufref;
   1186  bool did_aucmd;
   1187 
   1188  // This is a bit tricky: For some commands curwin->w_buffer needs to be
   1189  // equal to curbuf, but for some buffers there may not be a window.
   1190  // So we change the buffer for the current window for a moment.  This
   1191  // gives problems when the autocommands make changes to the list of
   1192  // buffers or windows...
   1193  FOR_ALL_BUFFERS(buf) {
   1194    // Only do loaded buffers and skip the current buffer, it's done last.
   1195    if (buf->b_ml.ml_mfp == NULL || buf == curbuf) {
   1196      continue;
   1197    }
   1198 
   1199    // Find a window for this buffer and save some values.
   1200    aucmd_prepbuf(&aco, buf);
   1201    set_bufref(&bufref, buf);
   1202 
   1203    // execute the autocommands for this buffer
   1204    retval = do_doautocmd(arg, false, &did_aucmd);
   1205 
   1206    if (call_do_modelines && did_aucmd) {
   1207      // Execute the modeline settings, but don't set window-local
   1208      // options if we are using the current window for another
   1209      // buffer.
   1210      do_modelines(is_aucmd_win(curwin) ? OPT_NOWIN : 0);
   1211    }
   1212 
   1213    // restore the current window
   1214    aucmd_restbuf(&aco);
   1215 
   1216    // Stop if there is some error or buffer was deleted.
   1217    if (retval == FAIL || !bufref_valid(&bufref)) {
   1218      retval = FAIL;
   1219      break;
   1220    }
   1221  }
   1222 
   1223  // Execute autocommands for the current buffer last.
   1224  if (retval == OK) {
   1225    do_doautocmd(arg, false, &did_aucmd);
   1226    if (call_do_modelines && did_aucmd) {
   1227      do_modelines(0);
   1228    }
   1229  }
   1230 }
   1231 
   1232 /// Check *argp for <nomodeline>.  When it is present return false, otherwise
   1233 /// return true and advance *argp to after it. Thus do_modelines() should be
   1234 /// called when true is returned.
   1235 ///
   1236 /// @param[in,out] argp argument string
   1237 bool check_nomodeline(char **argp)
   1238  FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
   1239 {
   1240  if (strncmp(*argp, "<nomodeline>", 12) == 0) {
   1241    *argp = skipwhite(*argp + 12);
   1242    return false;
   1243  }
   1244  return true;
   1245 }
   1246 
   1247 /// Prepare for executing autocommands for (hidden) buffer `buf`.
   1248 /// If the current buffer is not in any visible window, put it in a temporary
   1249 /// floating window using an entry in `aucmd_win[]`.
   1250 /// Set `curbuf` and `curwin` to match `buf`.
   1251 ///
   1252 /// @param aco  structure to save values in
   1253 /// @param buf  new curbuf
   1254 void aucmd_prepbuf(aco_save_T *aco, buf_T *buf)
   1255 {
   1256  win_T *win;
   1257  bool need_append = true;  // Append `aucmd_win` to the window list.
   1258  const bool same_buffer = buf == curbuf;
   1259 
   1260  // Find a window that is for the new buffer
   1261  if (same_buffer) {  // be quick when buf is curbuf
   1262    win = curwin;
   1263  } else {
   1264    win = NULL;
   1265    FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
   1266      if (wp->w_buffer == buf) {
   1267        win = wp;
   1268        break;
   1269      }
   1270    }
   1271  }
   1272 
   1273  // Allocate a window when needed.
   1274  win_T *auc_win = NULL;
   1275  int auc_idx = AUCMD_WIN_COUNT;
   1276  if (win == NULL) {
   1277    for (auc_idx = 0; auc_idx < AUCMD_WIN_COUNT; auc_idx++) {
   1278      if (!aucmd_win[auc_idx].auc_win_used) {
   1279        break;
   1280      }
   1281    }
   1282 
   1283    if (auc_idx == AUCMD_WIN_COUNT) {
   1284      kv_push(aucmd_win_vec, ((aucmdwin_T){
   1285        .auc_win = NULL,
   1286        .auc_win_used = false,
   1287      }));
   1288    }
   1289 
   1290    if (aucmd_win[auc_idx].auc_win == NULL) {
   1291      win_alloc_aucmd_win(auc_idx);
   1292      need_append = false;
   1293    }
   1294    auc_win = aucmd_win[auc_idx].auc_win;
   1295    aucmd_win[auc_idx].auc_win_used = true;
   1296  }
   1297 
   1298  aco->save_curwin_handle = curwin->handle;
   1299  aco->save_prevwin_handle = prevwin == NULL ? 0 : prevwin->handle;
   1300  if (bt_prompt(curbuf)) {
   1301    aco->save_prompt_insert = curbuf->b_prompt_insert;
   1302  }
   1303 
   1304  if (win != NULL) {
   1305    // There is a window for "buf" in the current tab page, make it the
   1306    // curwin.  This is preferred, it has the least side effects (esp. if
   1307    // "buf" is curbuf).
   1308    aco->use_aucmd_win_idx = -1;
   1309    curwin = win;
   1310  } else {
   1311    // There is no window for "buf", use "auc_win".  To minimize the side
   1312    // effects, insert it in the current tab page.
   1313    // Anything related to a window (e.g., setting folds) may have
   1314    // unexpected results.
   1315    aco->use_aucmd_win_idx = auc_idx;
   1316    auc_win->w_buffer = buf;
   1317    auc_win->w_s = &buf->b_s;
   1318    buf->b_nwindows++;
   1319    win_init_empty(auc_win);  // set cursor and topline to safe values
   1320 
   1321    // Make sure w_localdir, tp_localdir and globaldir are NULL to avoid a
   1322    // chdir() in win_enter_ext().
   1323    XFREE_CLEAR(auc_win->w_localdir);
   1324    aco->tp_localdir = curtab->tp_localdir;
   1325    curtab->tp_localdir = NULL;
   1326    aco->globaldir = globaldir;
   1327    globaldir = NULL;
   1328 
   1329    block_autocmds();  // We don't want BufEnter/WinEnter autocommands.
   1330    if (need_append) {
   1331      win_append(lastwin, auc_win, NULL);
   1332      pmap_put(int)(&window_handles, auc_win->handle, auc_win);
   1333      win_config_float(auc_win, auc_win->w_config);
   1334    }
   1335    // Prevent chdir() call in win_enter_ext(), through do_autochdir()
   1336    const int save_acd = p_acd;
   1337    p_acd = false;
   1338    // no redrawing and don't set the window title
   1339    RedrawingDisabled++;
   1340    win_enter(auc_win, false);
   1341    RedrawingDisabled--;
   1342    p_acd = save_acd;
   1343    unblock_autocmds();
   1344    curwin = auc_win;
   1345  }
   1346  curbuf = buf;
   1347  aco->new_curwin_handle = curwin->handle;
   1348  set_bufref(&aco->new_curbuf, curbuf);
   1349 
   1350  aco->save_VIsual_active = VIsual_active;
   1351  if (!same_buffer) {
   1352    // disable the Visual area, position may be invalid in another buffer
   1353    VIsual_active = false;
   1354  }
   1355 }
   1356 
   1357 /// Cleanup after executing autocommands for a (hidden) buffer.
   1358 /// Restore the window as it was (if possible).
   1359 ///
   1360 /// @param aco  structure holding saved values
   1361 void aucmd_restbuf(aco_save_T *aco)
   1362 {
   1363  if (aco->use_aucmd_win_idx >= 0) {
   1364    win_T *awp = aucmd_win[aco->use_aucmd_win_idx].auc_win;
   1365 
   1366    // Find "awp", it can't be closed, but it may be in another tab page.
   1367    // Do not trigger autocommands here.
   1368    block_autocmds();
   1369    if (curwin != awp) {
   1370      FOR_ALL_TAB_WINDOWS(tp, wp) {
   1371        if (wp == awp) {
   1372          if (tp != curtab) {
   1373            goto_tabpage_tp(tp, true, true);
   1374          }
   1375          win_goto(awp);
   1376          goto win_found;
   1377        }
   1378      }
   1379    }
   1380 win_found:
   1381    curbuf->b_nwindows--;
   1382    // Remove the window.
   1383    win_remove(curwin, NULL);
   1384    pmap_del(int)(&window_handles, curwin->handle, NULL);
   1385    if (curwin->w_grid_alloc.chars != NULL) {
   1386      ui_comp_remove_grid(&curwin->w_grid_alloc);
   1387      ui_call_win_hide(curwin->w_grid_alloc.handle);
   1388      grid_free(&curwin->w_grid_alloc);
   1389    }
   1390 
   1391    // The window is marked as not used, but it is not freed, it can be
   1392    // used again.
   1393    aucmd_win[aco->use_aucmd_win_idx].auc_win_used = false;
   1394 
   1395    if (!valid_tabpage_win(curtab)) {
   1396      // no valid window in current tabpage
   1397      close_tabpage(curtab);
   1398    }
   1399 
   1400    unblock_autocmds();
   1401 
   1402    win_T *const save_curwin = win_find_by_handle(aco->save_curwin_handle);
   1403    if (save_curwin != NULL) {
   1404      curwin = save_curwin;
   1405    } else {
   1406      // Hmm, original window disappeared.  Just use the first one.
   1407      curwin = firstwin;
   1408    }
   1409    curbuf = curwin->w_buffer;
   1410    // May need to restore insert mode for a prompt buffer.
   1411    entering_window(curwin);
   1412    if (bt_prompt(curbuf)) {
   1413      curbuf->b_prompt_insert = aco->save_prompt_insert;
   1414    }
   1415 
   1416    prevwin = win_find_by_handle(aco->save_prevwin_handle);
   1417    vars_clear(&awp->w_vars->dv_hashtab);         // free all w: variables
   1418    hash_init(&awp->w_vars->dv_hashtab);          // re-use the hashtab
   1419 
   1420    // If :lcd has been used in the autocommand window, correct current
   1421    // directory before restoring tp_localdir and globaldir.
   1422    if (awp->w_localdir != NULL) {
   1423      win_fix_current_dir();
   1424    }
   1425    xfree(curtab->tp_localdir);
   1426    curtab->tp_localdir = aco->tp_localdir;
   1427    xfree(globaldir);
   1428    globaldir = aco->globaldir;
   1429 
   1430    // the buffer contents may have changed
   1431    VIsual_active = aco->save_VIsual_active;
   1432    check_cursor(curwin);
   1433    if (curwin->w_topline > curbuf->b_ml.ml_line_count) {
   1434      curwin->w_topline = curbuf->b_ml.ml_line_count;
   1435      curwin->w_topfill = 0;
   1436    }
   1437  } else {
   1438    // Restore curwin.  Use the window ID, a window may have been closed
   1439    // and the memory re-used for another one.
   1440    win_T *const save_curwin = win_find_by_handle(aco->save_curwin_handle);
   1441    if (save_curwin != NULL) {
   1442      // Restore the buffer which was previously edited by curwin, if it was
   1443      // changed, we are still the same window and the buffer is valid.
   1444      if (curwin->handle == aco->new_curwin_handle
   1445          && curbuf != aco->new_curbuf.br_buf
   1446          && bufref_valid(&aco->new_curbuf)
   1447          && aco->new_curbuf.br_buf->b_ml.ml_mfp != NULL) {
   1448        if (curwin->w_s == &curbuf->b_s) {
   1449          curwin->w_s = &aco->new_curbuf.br_buf->b_s;
   1450        }
   1451        curbuf->b_nwindows--;
   1452        curbuf = aco->new_curbuf.br_buf;
   1453        curwin->w_buffer = curbuf;
   1454        curbuf->b_nwindows++;
   1455      }
   1456 
   1457      curwin = save_curwin;
   1458      curbuf = curwin->w_buffer;
   1459      prevwin = win_find_by_handle(aco->save_prevwin_handle);
   1460 
   1461      // In case the autocommand moves the cursor to a position that does not
   1462      // exist in curbuf
   1463      VIsual_active = aco->save_VIsual_active;
   1464      check_cursor(curwin);
   1465    }
   1466  }
   1467 
   1468  VIsual_active = aco->save_VIsual_active;
   1469  check_cursor(curwin);  // just in case lines got deleted
   1470  if (VIsual_active) {
   1471    check_pos(curbuf, &VIsual);
   1472  }
   1473 }
   1474 
   1475 /// Schedules an autocommand event, to be executed at the next event-loop tick.
   1476 ///
   1477 /// @param event Event to schedule
   1478 /// @param fname Name to use as `<amatch>` (the "pattern"). NULL/empty means use actual filename.
   1479 /// @param fname_io Filename to use for <afile> on cmdline, NULL means use `fname`.
   1480 /// @param group Group ID or AUGROUP_ALL
   1481 /// @param buf Buffer for <abuf>
   1482 /// @param eap Ex command arguments
   1483 /// @param data Event-specific data. Will be copied, caller must free `data`.
   1484 /// The `data` items will also be copied to `v:event`.
   1485 void aucmd_defer(event_T event, char *fname, char *fname_io, int group, buf_T *buf, exarg_T *eap,
   1486                 Object *data)
   1487 {
   1488  AutoCmdEvent *evdata = xmalloc(sizeof(AutoCmdEvent));
   1489  evdata->event = event;
   1490  evdata->fname = fname != NULL ? xstrdup(fname) : NULL;
   1491  evdata->fname_io = fname_io != NULL ? xstrdup(fname_io) : NULL;
   1492  evdata->group = group;
   1493  evdata->buf = buf->handle;
   1494  evdata->eap = eap;
   1495  if (data) {
   1496    evdata->data = xmalloc(sizeof(Object));
   1497    *evdata->data = copy_object(*data, NULL);
   1498  } else {
   1499    evdata->data = NULL;
   1500  }
   1501 
   1502  multiqueue_put(deferred_events, deferred_event, evdata);
   1503 }
   1504 
   1505 /// Executes a deferred autocommand event.
   1506 static void deferred_event(void **argv)
   1507 {
   1508  AutoCmdEvent *e = argv[0];
   1509  event_T event = e->event;
   1510  char *fname = e->fname;
   1511  char *fname_io = e->fname_io;
   1512  int group = e->group;
   1513  exarg_T *eap = e->eap;
   1514  Object *data = e->data;
   1515 
   1516  Error err = ERROR_INIT;
   1517  buf_T *buf = find_buffer_by_handle(e->buf, &err);
   1518  if (buf) {
   1519    // Copy `data` to `v:event`.
   1520    save_v_event_T save_v_event;
   1521    dict_T *v_event = get_v_event(&save_v_event);
   1522    if (data && data->type == kObjectTypeDict) {
   1523      for (size_t i = 0; i < data->data.dict.size; i++) {
   1524        KeyValuePair item = data->data.dict.items[i];
   1525        typval_T tv;
   1526        object_to_vim(item.value, &tv, &err);
   1527        if (ERROR_SET(&err)) {
   1528          api_clear_error(&err);
   1529          continue;
   1530        }
   1531        tv_dict_add_tv(v_event, item.key.data, item.key.size, &tv);
   1532        tv_clear(&tv);
   1533      }
   1534    }
   1535    tv_dict_set_keys_readonly(v_event);
   1536 
   1537    aco_save_T aco;
   1538    aucmd_prepbuf(&aco, buf);
   1539    apply_autocmds_group(event, fname, fname_io, false, group, buf, eap, data);
   1540    aucmd_restbuf(&aco);
   1541 
   1542    restore_v_event(v_event, &save_v_event);
   1543  }
   1544 
   1545  xfree(fname);
   1546  xfree(fname_io);
   1547  if (data) {
   1548    api_free_object(*data);
   1549    xfree(data);
   1550  }
   1551  xfree(e);
   1552 }
   1553 
   1554 /// Execute autocommands for "event" and file name "fname".
   1555 ///
   1556 /// @param event event that occurred
   1557 /// @param fname filename, NULL or empty means use actual file name
   1558 /// @param fname_io filename to use for <afile> on cmdline
   1559 /// @param force When true, ignore autocmd_busy
   1560 /// @param buf Buffer for <abuf>
   1561 ///
   1562 /// @return true if some commands were executed.
   1563 bool apply_autocmds(event_T event, char *fname, char *fname_io, bool force, buf_T *buf)
   1564 {
   1565  return apply_autocmds_group(event, fname, fname_io, force, AUGROUP_ALL, buf, NULL, NULL);
   1566 }
   1567 
   1568 /// Like apply_autocmds(), but with extra "eap" argument.  This takes care of
   1569 /// setting v:filearg.
   1570 ///
   1571 /// @param event event that occurred
   1572 /// @param fname NULL or empty means use actual file name
   1573 /// @param fname_io fname to use for <afile> on cmdline
   1574 /// @param force When true, ignore autocmd_busy
   1575 /// @param buf Buffer for <abuf>
   1576 /// @param exarg Ex command arguments
   1577 ///
   1578 /// @return true if some commands were executed.
   1579 bool apply_autocmds_exarg(event_T event, char *fname, char *fname_io, bool force, buf_T *buf,
   1580                          exarg_T *eap)
   1581 {
   1582  return apply_autocmds_group(event, fname, fname_io, force, AUGROUP_ALL, buf, eap, NULL);
   1583 }
   1584 
   1585 /// Like apply_autocmds(), but handles the caller's retval.  If the script
   1586 /// processing is being aborted or if retval is FAIL when inside a try
   1587 /// conditional, no autocommands are executed.  If otherwise the autocommands
   1588 /// cause the script to be aborted, retval is set to FAIL.
   1589 ///
   1590 /// @param event event that occurred
   1591 /// @param fname NULL or empty means use actual file name
   1592 /// @param fname_io fname to use for <afile> on cmdline
   1593 /// @param force When true, ignore autocmd_busy
   1594 /// @param buf Buffer for <abuf>
   1595 /// @param[in,out] retval caller's retval
   1596 ///
   1597 /// @return true if some autocommands were executed
   1598 bool apply_autocmds_retval(event_T event, char *fname, char *fname_io, bool force, buf_T *buf,
   1599                           int *retval)
   1600 {
   1601  if (should_abort(*retval)) {
   1602    return false;
   1603  }
   1604 
   1605  bool did_cmd = apply_autocmds_group(event, fname, fname_io, force, AUGROUP_ALL, buf, NULL, NULL);
   1606  if (did_cmd && aborting()) {
   1607    *retval = FAIL;
   1608  }
   1609  return did_cmd;
   1610 }
   1611 
   1612 /// Return true if "event" autocommand is defined.
   1613 ///
   1614 /// @param event the autocommand to check
   1615 bool has_event(event_T event) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
   1616 {
   1617  return kv_size(autocmds[(int)event]) != 0;
   1618 }
   1619 
   1620 /// Return true when there is a CursorHold/CursorHoldI autocommand defined for
   1621 /// the current mode.
   1622 static bool has_cursorhold(void) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
   1623 {
   1624  return has_event((get_real_state() == MODE_NORMAL_BUSY ? EVENT_CURSORHOLD : EVENT_CURSORHOLDI));
   1625 }
   1626 
   1627 /// Return true if the CursorHold/CursorHoldI event can be triggered.
   1628 bool trigger_cursorhold(void) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
   1629 {
   1630  if (!did_cursorhold && has_cursorhold() && reg_recording == 0
   1631      && typebuf.tb_len == 0 && !ins_compl_active()) {
   1632    int state = get_real_state();
   1633    if (state == MODE_NORMAL_BUSY || (state & MODE_INSERT) != 0) {
   1634      return true;
   1635    }
   1636  }
   1637  return false;
   1638 }
   1639 
   1640 /// Execute autocommands for "event" and file name "fname".
   1641 ///
   1642 /// @param event event that occurred
   1643 /// @param fname filename, NULL or empty means use actual file name
   1644 /// @param fname_io filename to use for <afile> on cmdline,
   1645 ///                 NULL means use `fname`.
   1646 /// @param force When true, ignore autocmd_busy
   1647 /// @param group autocmd group ID or AUGROUP_ALL
   1648 /// @param buf Buffer for <abuf>
   1649 /// @param eap Ex command arguments
   1650 ///
   1651 /// @return true if some commands were executed.
   1652 bool apply_autocmds_group(event_T event, char *fname, char *fname_io, bool force, int group,
   1653                          buf_T *buf, exarg_T *eap, Object *data)
   1654 {
   1655  char *sfname = NULL;  // short file name
   1656  bool retval = false;
   1657  static int nesting = 0;
   1658  char *save_cmdarg;
   1659  static bool filechangeshell_busy = false;
   1660  proftime_T wait_time;
   1661  bool did_save_redobuff = false;
   1662  save_redo_T save_redo;
   1663  const bool save_KeyTyped = KeyTyped;
   1664  ESTACK_CHECK_DECLARATION;
   1665 
   1666  // Quickly return if there are no autocommands for this event or
   1667  // autocommands are blocked.
   1668  if (event == NUM_EVENTS || kv_size(autocmds[(int)event]) == 0 || is_autocmd_blocked()) {
   1669    goto BYPASS_AU;
   1670  }
   1671 
   1672  // When autocommands are busy, new autocommands are only executed when
   1673  // explicitly enabled with the "nested" flag.
   1674  if (autocmd_busy && !(force || autocmd_nested)) {
   1675    goto BYPASS_AU;
   1676  }
   1677 
   1678  // Quickly return when immediately aborting on error, or when an interrupt
   1679  // occurred or an exception was thrown but not caught.
   1680  if (aborting()) {
   1681    goto BYPASS_AU;
   1682  }
   1683 
   1684  // FileChangedShell never nests, because it can create an endless loop.
   1685  if (filechangeshell_busy
   1686      && (event == EVENT_FILECHANGEDSHELL || event == EVENT_FILECHANGEDSHELLPOST)) {
   1687    goto BYPASS_AU;
   1688  }
   1689 
   1690  // Ignore events in 'eventignore'.
   1691  if (event_ignored(event, p_ei)) {
   1692    goto BYPASS_AU;
   1693  }
   1694 
   1695  bool win_ignore = false;
   1696  // If event is allowed in 'eventignorewin', check if curwin or all windows
   1697  // into "buf" are ignoring the event.
   1698  if (buf == curbuf && event_names[event].event <= 0) {
   1699    win_ignore = event_ignored(event, curwin->w_p_eiw);
   1700  } else if (buf != NULL && event_names[event].event <= 0 && buf->b_nwindows > 0) {
   1701    win_ignore = true;
   1702    FOR_ALL_TAB_WINDOWS(tp, wp) {
   1703      if (wp->w_buffer == buf && !event_ignored(event, wp->w_p_eiw)) {
   1704        win_ignore = false;
   1705        break;
   1706      }
   1707    }
   1708  }
   1709  if (win_ignore) {
   1710    goto BYPASS_AU;
   1711  }
   1712 
   1713  // Allow nesting of autocommands, but restrict the depth, because it's
   1714  // possible to create an endless loop.
   1715  if (nesting == 10) {
   1716    emsg(_(e_autocommand_nesting_too_deep));
   1717    goto BYPASS_AU;
   1718  }
   1719 
   1720  // Check if these autocommands are disabled.  Used when doing ":all" or
   1721  // ":ball".
   1722  if ((autocmd_no_enter && (event == EVENT_WINENTER || event == EVENT_BUFENTER))
   1723      || (autocmd_no_leave && (event == EVENT_WINLEAVE || event == EVENT_BUFLEAVE))) {
   1724    goto BYPASS_AU;
   1725  }
   1726 
   1727  // Save the autocmd_* variables and info about the current buffer.
   1728  char *save_autocmd_fname = autocmd_fname;
   1729  bool save_autocmd_fname_full = autocmd_fname_full;
   1730  int save_autocmd_bufnr = autocmd_bufnr;
   1731  char *save_autocmd_match = autocmd_match;
   1732  int save_autocmd_busy = autocmd_busy;
   1733  int save_autocmd_nested = autocmd_nested;
   1734  bool save_changed = curbuf->b_changed;
   1735  buf_T *old_curbuf = curbuf;
   1736 
   1737  // Set the file name to be used for <afile>.
   1738  // Make a copy to avoid that changing a buffer name or directory makes it
   1739  // invalid.
   1740  if (fname_io == NULL) {
   1741    if (event == EVENT_COLORSCHEME || event == EVENT_COLORSCHEMEPRE
   1742        || event == EVENT_OPTIONSET || event == EVENT_MODECHANGED
   1743        || event == EVENT_MARKSET) {
   1744      autocmd_fname = NULL;
   1745    } else if (fname != NULL && !ends_excmd(*fname)) {
   1746      autocmd_fname = fname;
   1747    } else if (buf != NULL) {
   1748      autocmd_fname = buf->b_ffname;
   1749    } else {
   1750      autocmd_fname = NULL;
   1751    }
   1752  } else {
   1753    autocmd_fname = fname_io;
   1754  }
   1755  char *afile_orig = NULL;  ///< Unexpanded <afile>
   1756  if (autocmd_fname != NULL) {
   1757    afile_orig = xstrdup(autocmd_fname);
   1758    // Allocate MAXPATHL for when eval_vars() resolves the fullpath.
   1759    autocmd_fname = xstrnsave(autocmd_fname, MAXPATHL);
   1760  }
   1761  autocmd_fname_full = false;  // call FullName_save() later
   1762 
   1763  // Set the buffer number to be used for <abuf>.
   1764  autocmd_bufnr = buf == NULL ? 0 : buf->b_fnum;
   1765 
   1766  // When the file name is NULL or empty, use the file name of buffer "buf".
   1767  // Always use the full path of the file name to match with, in case
   1768  // "allow_dirs" is set.
   1769  if (fname == NULL || *fname == NUL) {
   1770    if (buf == NULL) {
   1771      fname = NULL;
   1772    } else {
   1773      if (event == EVENT_SYNTAX) {
   1774        fname = buf->b_p_syn;
   1775      } else if (event == EVENT_FILETYPE) {
   1776        fname = buf->b_p_ft;
   1777      } else {
   1778        if (buf->b_sfname != NULL) {
   1779          sfname = xstrdup(buf->b_sfname);
   1780        }
   1781        fname = buf->b_ffname;
   1782      }
   1783    }
   1784    if (fname == NULL) {
   1785      fname = "";
   1786    }
   1787    fname = xstrdup(fname);  // make a copy, so we can change it
   1788  } else {
   1789    sfname = xstrdup(fname);
   1790    // Don't try expanding the following events.
   1791    if (event == EVENT_CMDLINECHANGED
   1792        || event == EVENT_CMDLINEENTER
   1793        || event == EVENT_CMDLINELEAVEPRE
   1794        || event == EVENT_CMDLINELEAVE
   1795        || event == EVENT_CMDUNDEFINED
   1796        || event == EVENT_CURSORMOVEDC
   1797        || event == EVENT_CMDWINENTER
   1798        || event == EVENT_CMDWINLEAVE
   1799        || event == EVENT_COLORSCHEME
   1800        || event == EVENT_COLORSCHEMEPRE
   1801        || event == EVENT_DIRCHANGED
   1802        || event == EVENT_DIRCHANGEDPRE
   1803        || event == EVENT_FILETYPE
   1804        || event == EVENT_FUNCUNDEFINED
   1805        || event == EVENT_MARKSET
   1806        || event == EVENT_MENUPOPUP
   1807        || event == EVENT_MODECHANGED
   1808        || event == EVENT_OPTIONSET
   1809        || event == EVENT_QUICKFIXCMDPOST
   1810        || event == EVENT_QUICKFIXCMDPRE
   1811        || event == EVENT_REMOTEREPLY
   1812        || event == EVENT_SIGNAL
   1813        || event == EVENT_SPELLFILEMISSING
   1814        || event == EVENT_SYNTAX
   1815        || event == EVENT_TABCLOSED
   1816        || event == EVENT_USER
   1817        || event == EVENT_WINCLOSED
   1818        || event == EVENT_WINRESIZED
   1819        || event == EVENT_WINSCROLLED) {
   1820      fname = xstrdup(fname);
   1821      autocmd_fname_full = true;  // don't expand it later
   1822    } else {
   1823      fname = FullName_save(fname, false);
   1824    }
   1825  }
   1826  if (fname == NULL) {  // out of memory
   1827    xfree(sfname);
   1828    retval = false;
   1829    goto BYPASS_AU;
   1830  }
   1831 
   1832 #ifdef BACKSLASH_IN_FILENAME
   1833  // Replace all backslashes with forward slashes. This makes the
   1834  // autocommand patterns portable between Unix and Windows.
   1835  if (sfname != NULL) {
   1836    forward_slash(sfname);
   1837  }
   1838  forward_slash(fname);
   1839 #endif
   1840 
   1841  // Set the name to be used for <amatch>.
   1842  autocmd_match = fname;
   1843 
   1844  // Don't redraw while doing autocommands.
   1845  RedrawingDisabled++;
   1846 
   1847  // name and lnum are filled in later
   1848  estack_push(ETYPE_AUCMD, NULL, 0);
   1849  ESTACK_CHECK_SETUP;
   1850 
   1851  const sctx_T save_current_sctx = current_sctx;
   1852 
   1853  if (do_profiling == PROF_YES) {
   1854    prof_child_enter(&wait_time);  // doesn't count for the caller itself
   1855  }
   1856 
   1857  // Don't use local function variables, if called from a function.
   1858  funccal_entry_T funccal_entry;
   1859  save_funccal(&funccal_entry);
   1860 
   1861  // When starting to execute autocommands, save the search patterns.
   1862  if (!autocmd_busy) {
   1863    save_search_patterns();
   1864    if (!ins_compl_active()) {
   1865      saveRedobuff(&save_redo);
   1866      did_save_redobuff = true;
   1867    }
   1868    curbuf->b_did_filetype = curbuf->b_keep_filetype;
   1869  }
   1870 
   1871  // Note that we are applying autocmds.  Some commands need to know.
   1872  autocmd_busy = true;
   1873  filechangeshell_busy = (event == EVENT_FILECHANGEDSHELL);
   1874  nesting++;  // see matching decrement below
   1875 
   1876  // Remember that FileType was triggered.  Used for did_filetype().
   1877  if (event == EVENT_FILETYPE) {
   1878    curbuf->b_did_filetype = true;
   1879  }
   1880 
   1881  char *tail = path_tail(fname);
   1882 
   1883  // Find first autocommand that matches
   1884  AutoPatCmd patcmd = {
   1885    // aucmd_next will set lastpat back to NULL if there are no more autocommands left to run
   1886    .lastpat = NULL,
   1887    // current autocommand index
   1888    .auidx = 0,
   1889    // save vector size, to avoid an endless loop when more patterns
   1890    // are added when executing autocommands
   1891    .ausize = kv_size(autocmds[(int)event]),
   1892    .afile_orig = afile_orig,
   1893    .fname = fname,
   1894    .sfname = sfname,
   1895    .tail = tail,
   1896    .group = group,
   1897    .event = event,
   1898    .arg_bufnr = autocmd_bufnr,
   1899  };
   1900  aucmd_next(&patcmd);
   1901 
   1902  // Found first autocommand, start executing them
   1903  if (patcmd.lastpat != NULL) {
   1904    // add to active_apc_list
   1905    patcmd.next = active_apc_list;
   1906    active_apc_list = &patcmd;
   1907 
   1908    // Attach data to command
   1909    patcmd.data = data;
   1910 
   1911    // set v:cmdarg (only when there is a matching pattern)
   1912    varnumber_T save_cmdbang = get_vim_var_nr(VV_CMDBANG);
   1913    if (eap != NULL) {
   1914      save_cmdarg = set_cmdarg(eap, NULL);
   1915      set_vim_var_nr(VV_CMDBANG, eap->forceit);
   1916    } else {
   1917      save_cmdarg = NULL;  // avoid gcc warning
   1918    }
   1919    retval = true;
   1920 
   1921    // Make sure cursor and topline are valid.  The first time the current
   1922    // values are saved, restored by reset_lnums().  When nested only the
   1923    // values are corrected when needed.
   1924    if (nesting == 1) {
   1925      check_lnums(true);
   1926    } else {
   1927      check_lnums_nested(true);
   1928    }
   1929 
   1930    const int save_did_emsg = did_emsg;
   1931    const bool save_ex_pressedreturn = get_pressedreturn();
   1932 
   1933    // Execute the autocmd. The `getnextac` callback handles iteration.
   1934    do_cmdline(NULL, getnextac, &patcmd, DOCMD_NOWAIT | DOCMD_VERBOSE | DOCMD_REPEAT);
   1935 
   1936    did_emsg += save_did_emsg;
   1937    set_pressedreturn(save_ex_pressedreturn);
   1938 
   1939    if (nesting == 1) {
   1940      // restore cursor and topline, unless they were changed
   1941      reset_lnums();
   1942    }
   1943 
   1944    if (eap != NULL) {
   1945      set_cmdarg(NULL, save_cmdarg);
   1946      set_vim_var_nr(VV_CMDBANG, save_cmdbang);
   1947    }
   1948    // delete from active_apc_list
   1949    if (active_apc_list == &patcmd) {  // just in case
   1950      active_apc_list = patcmd.next;
   1951    }
   1952  }
   1953 
   1954  RedrawingDisabled--;
   1955  autocmd_busy = save_autocmd_busy;
   1956  filechangeshell_busy = false;
   1957  autocmd_nested = save_autocmd_nested;
   1958  xfree(SOURCING_NAME);
   1959  ESTACK_CHECK_NOW;
   1960  estack_pop();
   1961  xfree(afile_orig);
   1962  xfree(autocmd_fname);
   1963  autocmd_fname = save_autocmd_fname;
   1964  autocmd_fname_full = save_autocmd_fname_full;
   1965  autocmd_bufnr = save_autocmd_bufnr;
   1966  autocmd_match = save_autocmd_match;
   1967  current_sctx = save_current_sctx;
   1968  restore_funccal();
   1969  if (do_profiling == PROF_YES) {
   1970    prof_child_exit(&wait_time);
   1971  }
   1972  KeyTyped = save_KeyTyped;
   1973  xfree(fname);
   1974  xfree(sfname);
   1975  nesting--;  // see matching increment above
   1976 
   1977  // When stopping to execute autocommands, restore the search patterns and
   1978  // the redo buffer. Free any buffers in the au_pending_free_buf list and
   1979  // free any windows in the au_pending_free_win list.
   1980  if (!autocmd_busy) {
   1981    restore_search_patterns();
   1982    if (did_save_redobuff) {
   1983      restoreRedobuff(&save_redo);
   1984    }
   1985    curbuf->b_did_filetype = false;
   1986    while (au_pending_free_buf != NULL) {
   1987      buf_T *b = au_pending_free_buf->b_next;
   1988 
   1989      xfree(au_pending_free_buf);
   1990      au_pending_free_buf = b;
   1991    }
   1992    while (au_pending_free_win != NULL) {
   1993      win_T *w = au_pending_free_win->w_next;
   1994 
   1995      xfree(au_pending_free_win);
   1996      au_pending_free_win = w;
   1997    }
   1998  }
   1999 
   2000  // Some events don't set or reset the Changed flag.
   2001  // Check if still in the same buffer!
   2002  if (curbuf == old_curbuf
   2003      && (event == EVENT_BUFREADPOST || event == EVENT_BUFWRITEPOST
   2004          || event == EVENT_FILEAPPENDPOST || event == EVENT_VIMLEAVE
   2005          || event == EVENT_VIMLEAVEPRE)) {
   2006    if (curbuf->b_changed != save_changed) {
   2007      need_maketitle = true;
   2008    }
   2009    curbuf->b_changed = save_changed;
   2010  }
   2011 
   2012  au_cleanup();  // may really delete removed patterns/commands now
   2013 
   2014 BYPASS_AU:
   2015  // When wiping out a buffer make sure all its buffer-local autocommands
   2016  // are deleted.
   2017  if (event == EVENT_BUFWIPEOUT && buf != NULL) {
   2018    aubuflocal_remove(buf);
   2019  }
   2020 
   2021  if (retval == OK && event == EVENT_FILETYPE) {
   2022    curbuf->b_au_did_filetype = true;
   2023  }
   2024 
   2025  return retval;
   2026 }
   2027 
   2028 void do_termresponse_autocmd(const String sequence)
   2029 {
   2030  MAXSIZE_TEMP_DICT(data, 1);
   2031  PUT_C(data, "sequence", STRING_OBJ(sequence));
   2032  apply_autocmds_group(EVENT_TERMRESPONSE, NULL, NULL, true, AUGROUP_ALL, NULL, NULL,
   2033                       &DICT_OBJ(data));
   2034  termresponse_changed = true;
   2035 }
   2036 
   2037 // Block triggering autocommands until unblock_autocmd() is called.
   2038 // Can be used recursively, so long as it's symmetric.
   2039 void block_autocmds(void)
   2040 {
   2041  // Detect if v:termresponse is set while blocked.
   2042  if (!is_autocmd_blocked()) {
   2043    termresponse_changed = false;
   2044  }
   2045  autocmd_blocked++;
   2046 }
   2047 
   2048 void unblock_autocmds(void)
   2049 {
   2050  autocmd_blocked--;
   2051 
   2052  // When v:termresponse was set while autocommands were blocked, trigger
   2053  // the autocommands now.  Esp. useful when executing a shell command
   2054  // during startup (nvim -d).
   2055  if (!is_autocmd_blocked() && termresponse_changed && has_event(EVENT_TERMRESPONSE)) {
   2056    // Copied to a new allocation, as termresponse may be freed during the event.
   2057    const String sequence = cstr_to_string(get_vim_var_str(VV_TERMRESPONSE));
   2058    do_termresponse_autocmd(sequence);
   2059    api_free_string(sequence);
   2060  }
   2061 }
   2062 
   2063 bool is_autocmd_blocked(void)
   2064  FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
   2065 {
   2066  return autocmd_blocked != 0;
   2067 }
   2068 
   2069 /// Find next matching autocommand.
   2070 /// If next autocommand was not found, sets lastpat to NULL and cmdidx to SIZE_MAX on apc.
   2071 static void aucmd_next(AutoPatCmd *apc)
   2072 {
   2073  estack_T *const entry = ((estack_T *)exestack.ga_data) + exestack.ga_len - 1;
   2074 
   2075  AutoCmdVec *const acs = &autocmds[(int)apc->event];
   2076  assert(apc->ausize <= kv_size(*acs));
   2077  for (size_t i = apc->auidx; i < apc->ausize && !got_int; i++) {
   2078    AutoCmd *const ac = &kv_A(*acs, i);
   2079    AutoPat *const ap = ac->pat;
   2080 
   2081    // Skip deleted autocommands.
   2082    if (ap == NULL) {
   2083      continue;
   2084    }
   2085    // Skip matching if pattern didn't change.
   2086    if (ap != apc->lastpat) {
   2087      // Skip autocommands that don't match the group.
   2088      if (apc->group != AUGROUP_ALL && apc->group != ap->group) {
   2089        continue;
   2090      }
   2091      // Skip autocommands that don't match the pattern or buffer number.
   2092      if (ap->buflocal_nr == 0
   2093          ? !match_file_pat(NULL, &ap->reg_prog, apc->fname, apc->sfname, apc->tail, ap->allow_dirs)
   2094          : ap->buflocal_nr != apc->arg_bufnr) {
   2095        continue;
   2096      }
   2097 
   2098      const char *const name = event_nr2name(apc->event);
   2099      const char *const s = _("%s Autocommands for \"%s\"");
   2100 
   2101      const size_t sourcing_name_len = strlen(s) + strlen(name) + (size_t)ap->patlen + 1;
   2102      char *const namep = xmalloc(sourcing_name_len);
   2103      snprintf(namep, sourcing_name_len, s, name, ap->pat);
   2104      if (p_verbose >= 8) {
   2105        verbose_enter();
   2106        smsg(0, _("Executing %s"), namep);
   2107        verbose_leave();
   2108      }
   2109 
   2110      // Update the exestack entry for this autocmd.
   2111      XFREE_CLEAR(entry->es_name);
   2112      entry->es_name = namep;
   2113      entry->es_info.aucmd = apc;
   2114    }
   2115 
   2116    apc->lastpat = ap;
   2117    apc->auidx = i;
   2118 
   2119    line_breakcheck();
   2120    return;
   2121  }
   2122 
   2123  // Clear the exestack entry for this ETYPE_AUCMD entry.
   2124  XFREE_CLEAR(entry->es_name);
   2125  entry->es_info.aucmd = NULL;
   2126 
   2127  apc->lastpat = NULL;
   2128  apc->auidx = SIZE_MAX;
   2129 }
   2130 
   2131 /// Executes an autocmd callback function (as opposed to an Ex command).
   2132 static bool au_callback(const AutoCmd *ac, const AutoPatCmd *apc)
   2133 {
   2134  Callback callback = ac->handler_fn;
   2135  if (callback.type == kCallbackLua) {
   2136    MAXSIZE_TEMP_DICT(data, 7);
   2137    PUT_C(data, "id", INTEGER_OBJ(ac->id));
   2138    PUT_C(data, "event", CSTR_AS_OBJ(event_nr2name(apc->event)));
   2139    PUT_C(data, "file", CSTR_AS_OBJ(apc->afile_orig));
   2140    PUT_C(data, "match", CSTR_AS_OBJ(autocmd_match));
   2141    PUT_C(data, "buf", INTEGER_OBJ(autocmd_bufnr));
   2142 
   2143    if (apc->data) {
   2144      PUT_C(data, "data", *apc->data);
   2145    }
   2146 
   2147    int group = ac->pat->group;
   2148    switch (group) {
   2149    case AUGROUP_ERROR:
   2150      abort();  // unreachable
   2151    case AUGROUP_DEFAULT:
   2152    case AUGROUP_ALL:
   2153    case AUGROUP_DELETED:
   2154      // omit group in these cases
   2155      break;
   2156    default:
   2157      PUT_C(data, "group", INTEGER_OBJ(group));
   2158      break;
   2159    }
   2160 
   2161    MAXSIZE_TEMP_ARRAY(args, 1);
   2162    ADD_C(args, DICT_OBJ(data));
   2163 
   2164    Object result = nlua_call_ref(callback.data.luaref, NULL, args, kRetNilBool, NULL, NULL);
   2165    return LUARET_TRUTHY(result);
   2166  } else {
   2167    typval_T argsin = TV_INITIAL_VALUE;
   2168    typval_T rettv = TV_INITIAL_VALUE;
   2169    callback_call(&callback, 0, &argsin, &rettv);
   2170    return false;
   2171  }
   2172 }
   2173 
   2174 /// Get next autocommand command.
   2175 /// Called by do_cmdline() to get the next line for ":if".
   2176 /// @return allocated string, or NULL for end of autocommands.
   2177 char *getnextac(int c, void *cookie, int indent, bool do_concat)
   2178 {
   2179  // These arguments are required for do_cmdline.
   2180  (void)c;
   2181  (void)indent;
   2182  (void)do_concat;
   2183 
   2184  AutoPatCmd *const apc = (AutoPatCmd *)cookie;
   2185  AutoCmdVec *const acs = &autocmds[(int)apc->event];
   2186 
   2187  aucmd_next(apc);
   2188  if (apc->lastpat == NULL) {
   2189    return NULL;
   2190  }
   2191 
   2192  assert(apc->auidx < kv_size(*acs));
   2193  AutoCmd *const ac = &kv_A(*acs, apc->auidx);
   2194  assert(ac->pat != NULL);
   2195  bool oneshot = ac->once;
   2196 
   2197  if (p_verbose >= 9) {
   2198    verbose_enter_scroll();
   2199    char *handler_str = aucmd_handler_to_string(ac);
   2200    smsg(0, _("autocommand %s"), handler_str);
   2201    msg_puts("\n");  // don't overwrite this either
   2202    XFREE_CLEAR(handler_str);
   2203    verbose_leave_scroll();
   2204  }
   2205 
   2206  // Make sure to set autocmd_nested before executing
   2207  // lua code, so that it works properly
   2208  autocmd_nested = ac->nested;
   2209  current_sctx = ac->script_ctx;
   2210  apc->script_ctx = current_sctx;
   2211 
   2212  char *retval;
   2213  if (ac->handler_cmd) {
   2214    retval = xstrdup(ac->handler_cmd);
   2215  } else {
   2216    AutoCmd ac_copy = *ac;
   2217    // Mark oneshot handler as "removed" now, to prevent recursion by e.g. `:doautocmd`. #25526
   2218    ac->pat = oneshot ? NULL : ac->pat;
   2219    // May reallocate `acs` kvec_t data and invalidate the `ac` pointer.
   2220    bool rv = au_callback(&ac_copy, apc);
   2221    if (oneshot) {
   2222      // Restore `pat`. Use `acs` because `ac` may have been invalidated by the callback.
   2223      kv_A(*acs, apc->auidx).pat = ac_copy.pat;
   2224    }
   2225    // If an autocommand callback returns true, delete the autocommand
   2226    oneshot = oneshot || rv;
   2227 
   2228    // HACK(tjdevries):
   2229    //  We just return "not-null" and continue going.
   2230    //  This would be a good candidate for a refactor. You would need to refactor:
   2231    //      1. do_cmdline to accept something besides a string
   2232    //      OR
   2233    //      2. make where we call do_cmdline for autocmds not have to return anything,
   2234    //      and instead we loop over all the matches and just execute one-by-one.
   2235    //          However, my expectation would be that could be expensive.
   2236    retval = xcalloc(1, 1);
   2237  }
   2238 
   2239  // Remove one-shot ("once") autocmd in anticipation of its execution.
   2240  if (oneshot) {
   2241    aucmd_del(&kv_A(*acs, apc->auidx));
   2242  }
   2243 
   2244  if (apc->auidx < apc->ausize) {
   2245    apc->auidx++;
   2246  } else {
   2247    apc->auidx = SIZE_MAX;
   2248  }
   2249 
   2250  return retval;
   2251 }
   2252 
   2253 /// Return true if there is a matching autocommand for "fname".
   2254 /// To account for buffer-local autocommands, function needs to know
   2255 /// in which buffer the file will be opened.
   2256 ///
   2257 /// @param event event that occurred.
   2258 /// @param sfname filename the event occurred in.
   2259 /// @param buf buffer the file is open in
   2260 bool has_autocmd(event_T event, char *sfname, buf_T *buf)
   2261  FUNC_ATTR_WARN_UNUSED_RESULT
   2262 {
   2263  char *tail = path_tail(sfname);
   2264  bool retval = false;
   2265 
   2266  char *fname = FullName_save(sfname, false);
   2267  if (fname == NULL) {
   2268    return false;
   2269  }
   2270 
   2271 #ifdef BACKSLASH_IN_FILENAME
   2272  // Replace all backslashes with forward slashes. This makes the
   2273  // autocommand patterns portable between Unix and Windows.
   2274  sfname = xstrdup(sfname);
   2275  forward_slash(sfname);
   2276  forward_slash(fname);
   2277 #endif
   2278 
   2279  AutoCmdVec *const acs = &autocmds[(int)event];
   2280  for (size_t i = 0; i < kv_size(*acs); i++) {
   2281    AutoPat *const ap = kv_A(*acs, i).pat;
   2282    if (ap != NULL
   2283        && (ap->buflocal_nr == 0
   2284            ? match_file_pat(NULL, &ap->reg_prog, fname, sfname, tail, ap->allow_dirs)
   2285            : buf != NULL && ap->buflocal_nr == buf->b_fnum)) {
   2286      retval = true;
   2287      break;
   2288    }
   2289  }
   2290 
   2291  xfree(fname);
   2292 #ifdef BACKSLASH_IN_FILENAME
   2293  xfree(sfname);
   2294 #endif
   2295 
   2296  return retval;
   2297 }
   2298 
   2299 // Function given to ExpandGeneric() to obtain the list of autocommand group names.
   2300 char *expand_get_augroup_name(expand_T *xp, int idx)
   2301 {
   2302  (void)xp;  // Required for ExpandGeneric
   2303  return augroup_name(idx + 1);
   2304 }
   2305 
   2306 /// @param doautocmd  true for :doauto*, false for :autocmd
   2307 char *set_context_in_autocmd(expand_T *xp, char *arg, bool doautocmd)
   2308 {
   2309  // check for a group name, skip it if present
   2310  autocmd_include_groups = false;
   2311  char *p = arg;
   2312  int group = arg_augroup_get(&arg);
   2313 
   2314  // If there only is a group name that's what we expand.
   2315  if (*arg == NUL && group != AUGROUP_ALL && !ascii_iswhite(arg[-1])) {
   2316    arg = p;
   2317    group = AUGROUP_ALL;
   2318  }
   2319 
   2320  // skip over event name
   2321  for (p = arg; *p != NUL && !ascii_iswhite(*p); p++) {
   2322    if (*p == ',') {
   2323      arg = p + 1;
   2324    }
   2325  }
   2326  if (*p == NUL) {
   2327    if (group == AUGROUP_ALL) {
   2328      autocmd_include_groups = true;
   2329    }
   2330    xp->xp_context = EXPAND_EVENTS;  // expand event name
   2331    xp->xp_pattern = arg;
   2332    return NULL;
   2333  }
   2334 
   2335  // skip over pattern
   2336  arg = skipwhite(p);
   2337  while (*arg && (!ascii_iswhite(*arg) || arg[-1] == '\\')) {
   2338    arg++;
   2339  }
   2340  if (*arg) {
   2341    return arg;  // expand (next) command
   2342  }
   2343 
   2344  if (doautocmd) {
   2345    xp->xp_context = EXPAND_FILES;  // expand file names
   2346  } else {
   2347    xp->xp_context = EXPAND_NOTHING;  // pattern is not expanded
   2348  }
   2349  return NULL;
   2350 }
   2351 
   2352 /// Function given to ExpandGeneric() to obtain the list of event names.
   2353 char *expand_get_event_name(expand_T *xp, int idx)
   2354 {
   2355  (void)xp;  // xp is a required parameter to be used with ExpandGeneric
   2356 
   2357  // List group names
   2358  char *name = augroup_name(idx + 1);
   2359  if (name != NULL) {
   2360    // skip when not including groups or skip deleted entries
   2361    if (!autocmd_include_groups || name == get_deleted_augroup()) {
   2362      return "";
   2363    }
   2364 
   2365    return name;
   2366  }
   2367 
   2368  int i = idx - next_augroup_id;
   2369  if (i < 0 || i >= NUM_EVENTS) {
   2370    return NULL;
   2371  }
   2372 
   2373  // List event names
   2374  return event_names[i].name;
   2375 }
   2376 
   2377 /// Function given to ExpandGeneric() to obtain the list of event names. Don't
   2378 /// include groups.
   2379 char *get_event_name_no_group(expand_T *xp FUNC_ATTR_UNUSED, int idx, bool win)
   2380 {
   2381  if (idx < 0 || idx >= NUM_EVENTS) {
   2382    return NULL;
   2383  }
   2384 
   2385  if (!win) {
   2386    return event_names[idx].name;
   2387  }
   2388 
   2389  // Need to check subset of allowed values for 'eventignorewin'.
   2390  int j = 0;
   2391  for (int i = 0; i < NUM_EVENTS; i++) {
   2392    j += event_names[i].event <= 0;
   2393    if (j == idx + 1) {
   2394      return event_names[i].name;
   2395    }
   2396  }
   2397  return NULL;
   2398 }
   2399 
   2400 /// Check whether given autocommand is supported
   2401 ///
   2402 /// @param[in]  event  Event to check.
   2403 ///
   2404 /// @return True if it is, false otherwise.
   2405 bool autocmd_supported(const char *const event)
   2406  FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
   2407 {
   2408  char *p;
   2409  return event_name2nr(event, &p) != NUM_EVENTS;
   2410 }
   2411 
   2412 /// Return true if an autocommand is defined for a group, event and
   2413 /// pattern:  The group can be omitted to accept any group.
   2414 /// `event` and `pattern` can be omitted to accept any event and pattern.
   2415 /// Buffer-local patterns <buffer> or <buffer=N> are accepted.
   2416 /// Used for:
   2417 ///   exists("#Group") or
   2418 ///   exists("#Group#Event") or
   2419 ///   exists("#Group#Event#pat") or
   2420 ///   exists("#Event") or
   2421 ///   exists("#Event#pat")
   2422 ///
   2423 /// @param arg autocommand string
   2424 bool au_exists(const char *const arg)
   2425  FUNC_ATTR_WARN_UNUSED_RESULT
   2426 {
   2427  buf_T *buflocal_buf = NULL;
   2428  bool retval = false;
   2429 
   2430  // Make a copy so that we can change the '#' chars to a NUL.
   2431  char *const arg_save = xstrdup(arg);
   2432  char *p = strchr(arg_save, '#');
   2433  if (p != NULL) {
   2434    *p++ = NUL;
   2435  }
   2436 
   2437  // First, look for an autocmd group name.
   2438  int group = augroup_find(arg_save);
   2439  char *event_name;
   2440  if (group == AUGROUP_ERROR) {
   2441    // Didn't match a group name, assume the first argument is an event.
   2442    group = AUGROUP_ALL;
   2443    event_name = arg_save;
   2444  } else {
   2445    if (p == NULL) {
   2446      // "Group": group name is present and it's recognized
   2447      retval = true;
   2448      goto theend;
   2449    }
   2450 
   2451    // Must be "Group#Event" or "Group#Event#pat".
   2452    event_name = p;
   2453    p = strchr(event_name, '#');
   2454    if (p != NULL) {
   2455      *p++ = NUL;  // "Group#Event#pat"
   2456    }
   2457  }
   2458 
   2459  char *pattern = p;  // "pattern" is NULL when there is no pattern.
   2460 
   2461  // Find the index (enum) for the event name.
   2462  event_T event = event_name2nr(event_name, &p);
   2463 
   2464  // return false if the event name is not recognized
   2465  if (event == NUM_EVENTS) {
   2466    goto theend;
   2467  }
   2468 
   2469  // Find the first autocommand for this event.
   2470  // If there isn't any, return false;
   2471  // If there is one and no pattern given, return true;
   2472  AutoCmdVec *const acs = &autocmds[(int)event];
   2473  if (kv_size(*acs) == 0) {
   2474    goto theend;
   2475  }
   2476 
   2477  // if pattern is "<buffer>", special handling is needed which uses curbuf
   2478  // for pattern "<buffer=N>, path_fnamecmp() will work fine
   2479  if (pattern != NULL && STRICMP(pattern, "<buffer>") == 0) {
   2480    buflocal_buf = curbuf;
   2481  }
   2482 
   2483  // Check if there is an autocommand with the given pattern.
   2484  for (size_t i = 0; i < kv_size(*acs); i++) {
   2485    AutoPat *const ap = kv_A(*acs, i).pat;
   2486    // Only use a pattern when it has not been removed.
   2487    // For buffer-local autocommands, path_fnamecmp() works fine.
   2488    if (ap != NULL
   2489        && (group == AUGROUP_ALL || ap->group == group)
   2490        && (pattern == NULL
   2491            || (buflocal_buf == NULL
   2492                ? path_fnamecmp(ap->pat, pattern) == 0
   2493                : ap->buflocal_nr == buflocal_buf->b_fnum))) {
   2494      retval = true;
   2495      break;
   2496    }
   2497  }
   2498 
   2499 theend:
   2500  xfree(arg_save);
   2501  return retval;
   2502 }
   2503 
   2504 // Checks if a pattern is buflocal
   2505 bool aupat_is_buflocal(const char *pat, int patlen)
   2506  FUNC_ATTR_PURE
   2507 {
   2508  return patlen >= 8 && strncmp(pat, "<buffer", 7) == 0 && (pat)[patlen - 1] == '>';
   2509 }
   2510 
   2511 int aupat_get_buflocal_nr(const char *pat, int patlen)
   2512 {
   2513  assert(aupat_is_buflocal(pat, patlen));
   2514 
   2515  // "<buffer>"
   2516  if (patlen == 8) {
   2517    return curbuf->b_fnum;
   2518  }
   2519 
   2520  if (patlen > 9 && (pat)[7] == '=') {
   2521    // "<buffer=abuf>"
   2522    if (patlen == 13 && STRNICMP(pat, "<buffer=abuf>", 13) == 0) {
   2523      return autocmd_bufnr;
   2524    }
   2525 
   2526    // "<buffer=123>"
   2527    if (skipdigits(pat + 8) == pat + patlen - 1) {
   2528      return atoi(pat + 8);
   2529    }
   2530  }
   2531 
   2532  return 0;
   2533 }
   2534 
   2535 // normalize buffer pattern
   2536 void aupat_normalize_buflocal_pat(char *dest, const char *pat, int patlen, int buflocal_nr)
   2537 {
   2538  assert(aupat_is_buflocal(pat, patlen));
   2539 
   2540  if (buflocal_nr == 0) {
   2541    buflocal_nr = curbuf->handle;
   2542  }
   2543 
   2544  // normalize pat into standard "<buffer>#N" form
   2545  snprintf(dest, BUFLOCAL_PAT_LEN, "<buffer=%d>", buflocal_nr);
   2546 }
   2547 
   2548 int autocmd_delete_event(int group, event_T event, const char *pat)
   2549  FUNC_ATTR_NONNULL_ALL
   2550 {
   2551  return do_autocmd_event(event, pat, false, false, "", true, group);
   2552 }
   2553 
   2554 /// Deletes an autocmd by ID.
   2555 /// Only autocmds created via the API have IDs associated with them. There
   2556 /// is no way to delete a specific autocmd created via :autocmd
   2557 bool autocmd_delete_id(int64_t id)
   2558 {
   2559  assert(id > 0);
   2560  bool success = false;
   2561 
   2562  // Note that since multiple AutoCmd objects can have the same ID, we need to do a full scan.
   2563  FOR_ALL_AUEVENTS(event) {
   2564    AutoCmdVec *const acs = &autocmds[(int)event];
   2565    for (size_t i = 0; i < kv_size(*acs); i++) {
   2566      AutoCmd *const ac = &kv_A(*acs, i);
   2567      if (ac->id == id) {
   2568        aucmd_del(ac);
   2569        success = true;
   2570      }
   2571    }
   2572  }
   2573  return success;
   2574 }
   2575 
   2576 /// Gets an (allocated) string representation of an autocmd command/callback.
   2577 char *aucmd_handler_to_string(AutoCmd *ac)
   2578  FUNC_ATTR_PURE
   2579 {
   2580  if (ac->handler_cmd) {
   2581    return xstrdup(ac->handler_cmd);
   2582  }
   2583  return callback_to_string(&ac->handler_fn, NULL);
   2584 }
   2585 
   2586 // Arg Parsing Functions
   2587 
   2588 /// Scan over the events.  "*" stands for all events.
   2589 /// true when group name was found
   2590 static char *arg_event_skip(char *arg, bool have_group)
   2591 {
   2592  char *pat;
   2593  char *p;
   2594 
   2595  if (*arg == '*') {
   2596    if (arg[1] && !ascii_iswhite(arg[1])) {
   2597      semsg(_("E215: Illegal character after *: %s"), arg);
   2598      return NULL;
   2599    }
   2600    pat = arg + 1;
   2601  } else {
   2602    for (pat = arg; *pat && *pat != '|' && !ascii_iswhite(*pat); pat = p) {
   2603      if ((int)event_name2nr(pat, &p) >= NUM_EVENTS) {
   2604        if (have_group) {
   2605          semsg(_("E216: No such event: %s"), pat);
   2606        } else {
   2607          semsg(_("E216: No such group or event: %s"), pat);
   2608        }
   2609        return NULL;
   2610      }
   2611    }
   2612  }
   2613  return pat;
   2614 }
   2615 
   2616 // Find the group ID in a ":autocmd" or ":doautocmd" argument.
   2617 // The "argp" argument is advanced to the following argument.
   2618 //
   2619 // Returns the group ID or AUGROUP_ALL.
   2620 static int arg_augroup_get(char **argp)
   2621 {
   2622  char *p;
   2623  char *arg = *argp;
   2624 
   2625  for (p = arg; *p && !ascii_iswhite(*p) && *p != '|'; p++) {}
   2626  if (p <= arg) {
   2627    return AUGROUP_ALL;
   2628  }
   2629 
   2630  char *group_name = xmemdupz(arg, (size_t)(p - arg));
   2631  int group = augroup_find(group_name);
   2632  if (group == AUGROUP_ERROR) {
   2633    group = AUGROUP_ALL;  // no match, use all groups
   2634  } else {
   2635    *argp = skipwhite(p);  // match, skip over group name
   2636  }
   2637  xfree(group_name);
   2638  return group;
   2639 }
   2640 
   2641 /// Handles grabbing arguments from `:autocmd` such as ++once and ++nested
   2642 static bool arg_autocmd_flag_get(bool *flag, char **cmd_ptr, char *pattern, int len)
   2643 {
   2644  if (strncmp(*cmd_ptr, pattern, (size_t)len) == 0 && ascii_iswhite((*cmd_ptr)[len])) {
   2645    if (*flag) {
   2646      semsg(_(e_duparg2), pattern);
   2647      return true;
   2648    }
   2649 
   2650    *flag = true;
   2651    *cmd_ptr = skipwhite(*cmd_ptr + len);
   2652  }
   2653 
   2654  return false;
   2655 }
   2656 
   2657 /// When kFalse: VimSuspend should be triggered next.
   2658 /// When kTrue: VimResume should be triggered next.
   2659 /// When kNone: Currently triggering VimSuspend or VimResume.
   2660 static TriState pending_vimresume = kFalse;
   2661 
   2662 static void vimresume_event(void **argv)
   2663 {
   2664  apply_autocmds(EVENT_VIMRESUME, NULL, NULL, false, NULL);
   2665  pending_vimresume = kFalse;
   2666 }
   2667 
   2668 /// Trigger VimSuspend or VimResume autocommand.
   2669 void may_trigger_vim_suspend_resume(bool suspend)
   2670 {
   2671  if (suspend && pending_vimresume == kFalse) {
   2672    pending_vimresume = kNone;
   2673    apply_autocmds(EVENT_VIMSUSPEND, NULL, NULL, false, NULL);
   2674    pending_vimresume = kTrue;
   2675  } else if (!suspend && pending_vimresume == kTrue) {
   2676    pending_vimresume = kNone;
   2677    multiqueue_put(main_loop.events, vimresume_event, NULL);
   2678  }
   2679 }
   2680 
   2681 // UI Enter
   2682 void do_autocmd_uienter(uint64_t chanid, bool attached)
   2683 {
   2684  static bool recursive = false;
   2685 
   2686 #ifdef EXITFREE
   2687  if (entered_free_all_mem) {
   2688    return;
   2689  }
   2690 #endif
   2691  if (starting == NO_SCREEN) {
   2692    return;  // user config hasn't been sourced yet
   2693  }
   2694  if (recursive) {
   2695    return;  // disallow recursion
   2696  }
   2697  recursive = true;
   2698 
   2699  save_v_event_T save_v_event;
   2700  dict_T *dict = get_v_event(&save_v_event);
   2701  assert(chanid < VARNUMBER_MAX);
   2702  tv_dict_add_nr(dict, S_LEN("chan"), (varnumber_T)chanid);
   2703  tv_dict_set_keys_readonly(dict);
   2704  apply_autocmds(attached ? EVENT_UIENTER : EVENT_UILEAVE, NULL, NULL, false, curbuf);
   2705  restore_v_event(dict, &save_v_event);
   2706 
   2707  recursive = false;
   2708 }
   2709 
   2710 // FocusGained
   2711 
   2712 void do_autocmd_focusgained(bool gained)
   2713 {
   2714  static bool recursive = false;
   2715  static Timestamp last_time = 0;
   2716 
   2717  if (recursive) {
   2718    return;  // disallow recursion
   2719  }
   2720  recursive = true;
   2721  apply_autocmds((gained ? EVENT_FOCUSGAINED : EVENT_FOCUSLOST), NULL, NULL, false, curbuf);
   2722 
   2723  // When activated: Check if any file was modified outside of Vim.
   2724  // Only do this when not done within the last two seconds as:
   2725  // 1. Some filesystems have modification time granularity in seconds. Fat32
   2726  //    has a granularity of 2 seconds.
   2727  // 2. We could get multiple notifications in a row.
   2728  if (gained && last_time + (Timestamp)2000 < os_now()) {
   2729    check_timestamps(true);
   2730    last_time = os_now();
   2731  }
   2732 
   2733  recursive = false;
   2734 }
   2735 
   2736 void do_filetype_autocmd(buf_T *buf, bool force)
   2737 {
   2738  static int ft_recursive = 0;
   2739 
   2740  if (ft_recursive > 0 && !force) {
   2741    return;  // disallow recursion
   2742  }
   2743 
   2744  int secure_save = secure;
   2745 
   2746  // Reset the secure flag, since the value of 'filetype' has
   2747  // been checked to be safe.
   2748  secure = 0;
   2749 
   2750  ft_recursive++;
   2751  buf->b_did_filetype = true;
   2752  // Only pass true for "force" when it is true or
   2753  // used recursively, to avoid endless recurrence.
   2754  apply_autocmds(EVENT_FILETYPE, buf->b_p_ft, buf->b_fname, force || ft_recursive == 1, buf);
   2755  ft_recursive--;
   2756 
   2757  secure = secure_save;
   2758 }