neovim

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

input.c (5048B)


      1 // input.c: high level functions for prompting the user or input
      2 // like yes/no or number prompts.
      3 
      4 #include <stdbool.h>
      5 #include <stdint.h>
      6 #include <string.h>
      7 
      8 #include "nvim/ascii_defs.h"
      9 #include "nvim/ex_getln.h"
     10 #include "nvim/getchar.h"
     11 #include "nvim/gettext_defs.h"
     12 #include "nvim/globals.h"
     13 #include "nvim/highlight_defs.h"
     14 #include "nvim/input.h"
     15 #include "nvim/keycodes.h"
     16 #include "nvim/math.h"
     17 #include "nvim/mbyte.h"
     18 #include "nvim/memory.h"
     19 #include "nvim/message.h"
     20 #include "nvim/mouse.h"
     21 #include "nvim/os/input.h"
     22 #include "nvim/state_defs.h"
     23 #include "nvim/ui.h"
     24 #include "nvim/vim_defs.h"
     25 
     26 #include "input.c.generated.h"  // IWYU pragma: export
     27 
     28 /// Ask for a reply from the user, a 'y' or a 'n', with prompt "str" (which
     29 /// should have been translated already).
     30 ///
     31 /// No other characters are accepted, the message is repeated until a valid
     32 /// reply is entered or <C-c> is hit.
     33 ///
     34 /// @param[in]  str  Prompt: question to ask user. Is always followed by " (y/n)?".
     35 ///
     36 /// @return 'y' or 'n'. Last is also what will be returned in case of interrupt.
     37 int ask_yesno(const char *const str)
     38 {
     39  const int save_State = State;
     40 
     41  no_wait_return++;
     42  snprintf(IObuff, IOSIZE, _("%s (y/n)?"), str);
     43  char *prompt = xstrdup(IObuff);
     44 
     45  int r = ' ';
     46  while (r != 'y' && r != 'n') {
     47    // same highlighting as for wait_return()
     48    r = prompt_for_input(prompt, HLF_R, true, NULL);
     49    if (r == Ctrl_C || r == ESC) {
     50      r = 'n';
     51      if (!ui_has(kUIMessages)) {
     52        msg_putchar(r);
     53      }
     54    }
     55  }
     56 
     57  need_wait_return = msg_scrolled;
     58  no_wait_return--;
     59  State = save_State;
     60  setmouse();
     61  xfree(prompt);
     62 
     63  return r;
     64 }
     65 
     66 /// Get a key stroke directly from the user.
     67 ///
     68 /// Ignores mouse clicks and scrollbar events, except a click for the left
     69 /// button (used at the more prompt).
     70 /// Doesn't use vgetc(), because it syncs undo and eats mapped characters.
     71 /// Disadvantage: typeahead is ignored.
     72 /// Translates the interrupt character for unix to ESC.
     73 int get_keystroke(MultiQueue *events)
     74 {
     75  uint8_t *buf = NULL;
     76  int buflen = 150;
     77  int len = 0;
     78  int n;
     79  int save_mapped_ctrl_c = mapped_ctrl_c;
     80 
     81  mod_mask = 0;
     82  mapped_ctrl_c = 0;        // mappings are not used here
     83  while (true) {
     84    // flush output before waiting
     85    ui_flush();
     86    // Leave some room for check_termcode() to insert a key code into (max
     87    // 5 chars plus NUL).  And fix_input_buffer() can triple the number of
     88    // bytes.
     89    int maxlen = (buflen - 6 - len) / 3;
     90    if (buf == NULL) {
     91      buf = xmalloc((size_t)buflen);
     92    } else if (maxlen < 10) {
     93      // Need some more space. This might happen when receiving a long
     94      // escape sequence.
     95      buflen += 100;
     96      buf = xrealloc(buf, (size_t)buflen);
     97      maxlen = (buflen - 6 - len) / 3;
     98    }
     99 
    100    // First time: blocking wait.  Second time: wait up to 100ms for a
    101    // terminal code to complete.
    102    n = input_get(buf + len, maxlen, len == 0 ? -1 : 100, 0, events);
    103    if (n > 0) {
    104      // Replace zero and K_SPECIAL by a special key code.
    105      n = fix_input_buffer(buf + len, n);
    106      len += n;
    107    }
    108 
    109    if (n > 0) {  // found a termcode: adjust length
    110      len = n;
    111    }
    112    if (len == 0) {  // nothing typed yet
    113      continue;
    114    }
    115 
    116    // Handle modifier and/or special key code.
    117    n = buf[0];
    118    if (n == K_SPECIAL) {
    119      n = TO_SPECIAL(buf[1], buf[2]);
    120      if (buf[1] == KS_MODIFIER
    121          || n == K_IGNORE
    122          || (is_mouse_key(n) && n != K_LEFTMOUSE)) {
    123        if (buf[1] == KS_MODIFIER) {
    124          mod_mask = buf[2];
    125        }
    126        len -= 3;
    127        if (len > 0) {
    128          memmove(buf, buf + 3, (size_t)len);
    129        }
    130        continue;
    131      }
    132      break;
    133    }
    134    if (MB_BYTE2LEN(n) > len) {
    135      // more bytes to get.
    136      continue;
    137    }
    138    buf[len >= buflen ? buflen - 1 : len] = NUL;
    139    n = utf_ptr2char((char *)buf);
    140    break;
    141  }
    142  xfree(buf);
    143 
    144  mapped_ctrl_c = save_mapped_ctrl_c;
    145  return merge_modifiers(n, &mod_mask);
    146 }
    147 
    148 /// Ask the user for input through a cmdline prompt.
    149 ///
    150 /// @param one_key Return from cmdline after one key press.
    151 /// @param mouse_used When not NULL, allow using the mouse to press a number.
    152 int prompt_for_input(char *prompt, int hl_id, bool one_key, bool *mouse_used)
    153 {
    154  int ret = one_key ? ESC : 0;
    155  char *kmsg = keep_msg ? xstrdup(keep_msg) : NULL;
    156 
    157  if (prompt == NULL) {
    158    if (mouse_used != NULL) {
    159      prompt = _("Type number and <Enter> or click with the mouse (q or empty cancels): ");
    160    } else {
    161      prompt = _("Type number and <Enter> (q or empty cancels): ");
    162    }
    163  }
    164 
    165  cmdline_row = msg_row;
    166  ui_flush();
    167 
    168  no_mapping++;  // don't map prompt input
    169  allow_keys++;  // allow special keys
    170  char *resp = getcmdline_prompt(-1, prompt, hl_id, EXPAND_NOTHING, NULL,
    171                                 CALLBACK_NONE, one_key, mouse_used);
    172  allow_keys--;
    173  no_mapping--;
    174 
    175  if (resp) {
    176    ret = one_key ? (int)(*resp) : atoi(resp);
    177    xfree(resp);
    178  }
    179 
    180  if (kmsg != NULL) {
    181    set_keep_msg(kmsg, keep_msg_hl_id);
    182    xfree(kmsg);
    183  }
    184 
    185  return ret;
    186 }