neovim

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

state.c (9721B)


      1 #include <stdbool.h>
      2 #include <string.h>
      3 
      4 #include "nvim/ascii_defs.h"
      5 #include "nvim/autocmd.h"
      6 #include "nvim/autocmd_defs.h"
      7 #include "nvim/buffer_defs.h"
      8 #include "nvim/drawscreen.h"
      9 #include "nvim/eval.h"
     10 #include "nvim/eval/typval.h"
     11 #include "nvim/eval/typval_defs.h"
     12 #include "nvim/event/defs.h"
     13 #include "nvim/event/loop.h"
     14 #include "nvim/event/multiqueue.h"
     15 #include "nvim/ex_getln.h"
     16 #include "nvim/getchar.h"
     17 #include "nvim/globals.h"
     18 #include "nvim/insexpand.h"
     19 #include "nvim/keycodes.h"
     20 #include "nvim/log.h"
     21 #include "nvim/macros_defs.h"
     22 #include "nvim/main.h"
     23 #include "nvim/memory.h"
     24 #include "nvim/option.h"
     25 #include "nvim/option_vars.h"
     26 #include "nvim/os/input.h"
     27 #include "nvim/state.h"
     28 #include "nvim/strings.h"
     29 #include "nvim/types_defs.h"
     30 #include "nvim/ui.h"
     31 
     32 #include "state.c.generated.h"
     33 
     34 void state_enter(VimState *s)
     35  FUNC_ATTR_NONNULL_ALL
     36 {
     37  while (true) {
     38    int check_result = s->check ? s->check(s) : 1;
     39 
     40    if (!check_result) {
     41      break;     // Terminate this state.
     42    } else if (check_result == -1) {
     43      continue;  // check() again.
     44    }
     45    // Execute this state.
     46 
     47    int key;
     48 
     49 getkey:
     50    // Apply mappings first by calling vpeekc() directly.
     51    // - If vpeekc() returns non-NUL, there is a character already available for processing, so
     52    //   don't block for events. vgetc() may still block, in case of an incomplete UTF-8 sequence.
     53    // - If vpeekc() returns NUL, vgetc() will block, and there are three cases:
     54    //   - There is no input available.
     55    //   - All of available input maps to an empty string.
     56    //   - There is an incomplete mapping.
     57    //   A blocking wait for a character should only be done in the third case, which is the only
     58    //   case of the three where typebuf.tb_len > 0 after vpeekc() returns NUL.
     59    if (vpeekc() != NUL || typebuf.tb_len > 0) {
     60      key = safe_vgetc();
     61    } else if (!multiqueue_empty(main_loop.events)) {
     62      // No input available and processing events may take time, flush now.
     63      ui_flush();
     64      // Event was made available after the last multiqueue_process_events call
     65      key = K_EVENT;
     66    } else {
     67      // Ensure the screen is fully updated before blocking for input. Because of the duality of
     68      // redraw_later, this can't be done in command-line or when waiting for "Press ENTER".
     69      // In many of those cases the redraw is expected AFTER the key press, while normally it should
     70      // update the screen immediately.
     71      if (must_redraw != 0 && !need_wait_return && (State & MODE_CMDLINE) == 0) {
     72        update_screen();
     73        setcursor();  // put cursor back where it belongs
     74      }
     75      // Flush screen updates before blocking.
     76      ui_flush();
     77      // Call `input_get` directly to block for events or user input without consuming anything from
     78      // `os/input.c:input_buffer` or calling the mapping engine.
     79      input_get(NULL, 0, -1, typebuf.tb_change_cnt, main_loop.events);
     80      // If an event was put into the queue, we send K_EVENT directly.
     81      if (!input_available() && !multiqueue_empty(main_loop.events)) {
     82        key = K_EVENT;
     83      } else {
     84        goto getkey;
     85      }
     86    }
     87 
     88    if (key == K_EVENT) {
     89      // An event handler may use the value of reg_executing.
     90      // Clear it if it should be cleared when getting the next character.
     91      check_end_reg_executing(true);
     92      may_sync_undo();
     93    }
     94 
     95 #ifdef NVIM_LOG_DEBUG
     96    char *keyname = key == K_EVENT ? "K_EVENT" : get_special_key_name(key, mod_mask);
     97    DLOG("input: %s", keyname);
     98 #endif
     99 
    100    int execute_result = s->execute(s, key);
    101    if (!execute_result) {
    102      break;
    103    } else if (execute_result == -1) {
    104      goto getkey;
    105    }
    106  }
    107 }
    108 
    109 /// process events on main_loop, but interrupt if input is available
    110 ///
    111 /// This should be used to handle K_EVENT in states accepting input
    112 /// otherwise bursts of events can block break checking indefinitely.
    113 void state_handle_k_event(void)
    114 {
    115  while (true) {
    116    Event event = multiqueue_get(main_loop.events);
    117    if (event.handler) {
    118      event.handler(event.argv);
    119    }
    120 
    121    if (multiqueue_empty(main_loop.events)) {
    122      // don't breakcheck before return, caller should return to main-loop
    123      // and handle input already.
    124      return;
    125    }
    126 
    127    // TODO(bfredl): as an further micro-optimization, we could check whether
    128    // event.handler already checked input.
    129    os_breakcheck();
    130    if (input_available() || got_int) {
    131      return;
    132    }
    133  }
    134 }
    135 
    136 /// Return true if in the current mode we need to use virtual.
    137 bool virtual_active(win_T *wp)
    138 {
    139  // While an operator is being executed we return "virtual_op", because
    140  // VIsual_active has already been reset, thus we can't check for "block"
    141  // being used.
    142  if (virtual_op != kNone) {
    143    return virtual_op;
    144  }
    145 
    146  // In Terminal mode the cursor can be positioned anywhere by the application
    147  if (State & MODE_TERMINAL) {
    148    return true;
    149  }
    150 
    151  unsigned cur_ve_flags = get_ve_flags(wp);
    152 
    153  return cur_ve_flags == kOptVeFlagAll
    154         || ((cur_ve_flags & kOptVeFlagBlock) && VIsual_active && VIsual_mode == Ctrl_V)
    155         || ((cur_ve_flags & kOptVeFlagInsert) && (State & MODE_INSERT));
    156 }
    157 
    158 /// MODE_VISUAL, MODE_SELECT and MODE_OP_PENDING State are never set, they are
    159 /// equal to MODE_NORMAL State with a condition.  This function returns the real
    160 /// State.
    161 int get_real_state(void)
    162 {
    163  if (State & MODE_NORMAL) {
    164    if (VIsual_active) {
    165      if (VIsual_select) {
    166        return MODE_SELECT;
    167      }
    168      return MODE_VISUAL;
    169    } else if (finish_op) {
    170      return MODE_OP_PENDING;
    171    }
    172  }
    173  return State;
    174 }
    175 
    176 /// Returns the current mode as a string in "buf[MODE_MAX_LENGTH]", NUL
    177 /// terminated.
    178 /// The first character represents the major mode, the following ones the minor
    179 /// ones.
    180 void get_mode(char *buf)
    181  FUNC_ATTR_NONNULL_ALL
    182 {
    183  int i = 0;
    184 
    185  if (State == MODE_HITRETURN || State == MODE_ASKMORE || State == MODE_SETWSIZE
    186      || ((State & MODE_CMDLINE) && get_cmdline_info()->one_key)) {
    187    buf[i++] = 'r';
    188    if (State == MODE_ASKMORE) {
    189      buf[i++] = 'm';
    190    } else if (State & MODE_CMDLINE) {
    191      buf[i++] = '?';
    192    }
    193  } else if (State == MODE_EXTERNCMD) {
    194    buf[i++] = '!';
    195  } else if (State & MODE_INSERT) {
    196    if (State & VREPLACE_FLAG) {
    197      buf[i++] = 'R';
    198      buf[i++] = 'v';
    199    } else {
    200      if (State & REPLACE_FLAG) {
    201        buf[i++] = 'R';
    202      } else {
    203        buf[i++] = 'i';
    204      }
    205    }
    206    if (ins_compl_active()) {
    207      buf[i++] = 'c';
    208    } else if (ctrl_x_mode_not_defined_yet()) {
    209      buf[i++] = 'x';
    210    }
    211  } else if ((State & MODE_CMDLINE) || exmode_active) {
    212    buf[i++] = 'c';
    213    if (exmode_active) {
    214      buf[i++] = 'v';
    215    }
    216    if ((State & MODE_CMDLINE) && cmdline_overstrike()) {
    217      buf[i++] = 'r';
    218    }
    219  } else if (State & MODE_TERMINAL) {
    220    buf[i++] = 't';
    221  } else if (VIsual_active) {
    222    if (VIsual_select) {
    223      buf[i++] = (char)(VIsual_mode + 's' - 'v');
    224    } else {
    225      buf[i++] = (char)VIsual_mode;
    226      if (restart_VIsual_select) {
    227        buf[i++] = 's';
    228      }
    229    }
    230  } else {
    231    buf[i++] = 'n';
    232    if (finish_op) {
    233      buf[i++] = 'o';
    234      // to be able to detect force-linewise/blockwise/charwise operations
    235      buf[i++] = (char)motion_force;
    236    } else if (curbuf->terminal) {
    237      buf[i++] = 't';
    238      if (restart_edit == 'I') {
    239        buf[i++] = 'T';
    240      }
    241    } else if (restart_edit == 'I' || restart_edit == 'R' || restart_edit == 'V') {
    242      buf[i++] = 'i';
    243      buf[i++] = (char)restart_edit;
    244    }
    245  }
    246 
    247  buf[i] = NUL;
    248 }
    249 
    250 /// Fires a ModeChanged autocmd if appropriate.
    251 void may_trigger_modechanged(void)
    252 {
    253  // Skip this when got_int is set, the autocommand will not be executed.
    254  // Better trigger it next time.
    255  if (!has_event(EVENT_MODECHANGED) || got_int) {
    256    return;
    257  }
    258 
    259  char curr_mode[MODE_MAX_LENGTH];
    260  char pattern_buf[2 * MODE_MAX_LENGTH];
    261 
    262  get_mode(curr_mode);
    263  if (strcmp(curr_mode, last_mode) == 0) {
    264    return;
    265  }
    266 
    267  save_v_event_T save_v_event;
    268  dict_T *v_event = get_v_event(&save_v_event);
    269  tv_dict_add_str(v_event, S_LEN("new_mode"), curr_mode);
    270  tv_dict_add_str(v_event, S_LEN("old_mode"), last_mode);
    271  tv_dict_set_keys_readonly(v_event);
    272 
    273  // concatenate modes in format "old_mode:new_mode"
    274  vim_snprintf(pattern_buf, sizeof(pattern_buf), "%s:%s", last_mode, curr_mode);
    275 
    276  apply_autocmds(EVENT_MODECHANGED, pattern_buf, NULL, false, curbuf);
    277  STRCPY(last_mode, curr_mode);
    278 
    279  restore_v_event(v_event, &save_v_event);
    280 }
    281 
    282 /// When true in a safe state when starting to wait for a character.
    283 static bool was_safe = false;
    284 
    285 /// Return whether currently it is safe, assuming it was safe before (high level
    286 /// state didn't change).
    287 static bool is_safe_now(void)
    288 {
    289  return stuff_empty()
    290         && typebuf.tb_len == 0
    291         && !using_script()
    292         && !global_busy
    293         && !debug_mode;
    294 }
    295 
    296 /// Trigger SafeState if currently in a safe state, that is "safe" is true and
    297 /// there is no typeahead.
    298 void may_trigger_safestate(bool safe)
    299 {
    300  bool is_safe = safe && is_safe_now();
    301 
    302  if (was_safe != is_safe) {
    303    // Only log when the state changes, otherwise it happens at nearly
    304    // every key stroke.
    305    DLOG(is_safe ? "SafeState: Start triggering" : "SafeState: Stop triggering");
    306  }
    307  if (is_safe) {
    308    apply_autocmds(EVENT_SAFESTATE, NULL, NULL, false, curbuf);
    309  }
    310  was_safe = is_safe;
    311 }
    312 
    313 /// Something changed which causes the state possibly to be unsafe, e.g. a
    314 /// character was typed.  It will remain unsafe until the next call to
    315 /// may_trigger_safestate().
    316 void state_no_longer_safe(const char *reason)
    317 {
    318  if (was_safe && reason != NULL) {
    319    DLOG("SafeState reset: %s", reason);
    320  }
    321  was_safe = false;
    322 }
    323 
    324 bool get_was_safe_state(void)
    325 {
    326  return was_safe;
    327 }