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 }