aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/input.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim/input.c')
-rw-r--r--src/nvim/input.c255
1 files changed, 255 insertions, 0 deletions
diff --git a/src/nvim/input.c b/src/nvim/input.c
new file mode 100644
index 0000000000..2f7c5c2c16
--- /dev/null
+++ b/src/nvim/input.c
@@ -0,0 +1,255 @@
+// This is an open source non-commercial project. Dear PVS-Studio, please check
+// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+
+// input.c: high level functions for prompting the user or input
+// like yes/no or number prompts.
+
+#include <inttypes.h>
+#include <stdbool.h>
+
+#include "nvim/func_attr.h"
+#include "nvim/getchar.h"
+#include "nvim/mbyte.h"
+#include "nvim/memory.h"
+#include "nvim/input.h"
+#include "nvim/mouse.h"
+#include "nvim/os/input.h"
+#include "nvim/ui.h"
+#include "nvim/vim.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "input.c.generated.h"
+#endif
+
+/// Ask for a reply from the user, 'y' or 'n'
+///
+/// No other characters are accepted, the message is repeated until a valid
+/// reply is entered or <C-c> is hit.
+///
+/// @param[in] str Prompt: question to ask user. Is always followed by
+/// " (y/n)?".
+/// @param[in] direct Determines what function to use to get user input. If
+/// true then ui_inchar() will be used, otherwise vgetc().
+/// I.e. when direct is true then characters are obtained
+/// directly from the user without buffers involved.
+///
+/// @return 'y' or 'n'. Last is also what will be returned in case of interrupt.
+int ask_yesno(const char *const str, const bool direct)
+{
+ const int save_State = State;
+
+ no_wait_return++;
+ State = CONFIRM; // Mouse behaves like with :confirm.
+ setmouse(); // Disable mouse in xterm.
+ no_mapping++;
+
+ int r = ' ';
+ while (r != 'y' && r != 'n') {
+ // Same highlighting as for wait_return.
+ smsg_attr(HL_ATTR(HLF_R), "%s (y/n)?", str);
+ if (direct) {
+ r = get_keystroke(NULL);
+ } else {
+ r = plain_vgetc();
+ }
+ if (r == Ctrl_C || r == ESC) {
+ r = 'n';
+ }
+ msg_putchar(r); // Show what you typed.
+ ui_flush();
+ }
+ no_wait_return--;
+ State = save_State;
+ setmouse();
+ no_mapping--;
+
+ return r;
+}
+
+/// Get a key stroke directly from the user.
+///
+/// Ignores mouse clicks and scrollbar events, except a click for the left
+/// button (used at the more prompt).
+/// Doesn't use vgetc(), because it syncs undo and eats mapped characters.
+/// Disadvantage: typeahead is ignored.
+/// Translates the interrupt character for unix to ESC.
+int get_keystroke(MultiQueue *events)
+{
+ char_u *buf = NULL;
+ int buflen = 150;
+ int maxlen;
+ int len = 0;
+ int n;
+ int save_mapped_ctrl_c = mapped_ctrl_c;
+ int waited = 0;
+
+ mapped_ctrl_c = 0; // mappings are not used here
+ for (;;) {
+ // flush output before waiting
+ ui_flush();
+ // Leave some room for check_termcode() to insert a key code into (max
+ // 5 chars plus NUL). And fix_input_buffer() can triple the number of
+ // bytes.
+ maxlen = (buflen - 6 - len) / 3;
+ if (buf == NULL) {
+ buf = xmalloc((size_t)buflen);
+ } else if (maxlen < 10) {
+ // Need some more space. This might happen when receiving a long
+ // escape sequence.
+ buflen += 100;
+ buf = xrealloc(buf, (size_t)buflen);
+ maxlen = (buflen - 6 - len) / 3;
+ }
+
+ // First time: blocking wait. Second time: wait up to 100ms for a
+ // terminal code to complete.
+ n = os_inchar(buf + len, maxlen, len == 0 ? -1L : 100L, 0, events);
+ if (n > 0) {
+ // Replace zero and CSI by a special key code.
+ n = fix_input_buffer(buf + len, n);
+ len += n;
+ waited = 0;
+ } else if (len > 0) {
+ waited++; // keep track of the waiting time
+ }
+ if (n > 0) { // found a termcode: adjust length
+ len = n;
+ }
+ if (len == 0) { // nothing typed yet
+ continue;
+ }
+
+ // Handle modifier and/or special key code.
+ n = buf[0];
+ if (n == K_SPECIAL) {
+ n = TO_SPECIAL(buf[1], buf[2]);
+ if (buf[1] == KS_MODIFIER
+ || n == K_IGNORE
+ || (is_mouse_key(n) && n != K_LEFTMOUSE)) {
+ if (buf[1] == KS_MODIFIER) {
+ mod_mask = buf[2];
+ }
+ len -= 3;
+ if (len > 0) {
+ memmove(buf, buf + 3, (size_t)len);
+ }
+ continue;
+ }
+ break;
+ }
+ if (MB_BYTE2LEN(n) > len) {
+ // more bytes to get.
+ continue;
+ }
+ buf[len >= buflen ? buflen - 1 : len] = NUL;
+ n = utf_ptr2char(buf);
+ break;
+ }
+ xfree(buf);
+
+ mapped_ctrl_c = save_mapped_ctrl_c;
+ return n;
+}
+
+/// Get a number from the user.
+/// When "mouse_used" is not NULL allow using the mouse.
+///
+/// @param colon allow colon to abort
+int get_number(int colon, int *mouse_used)
+{
+ int n = 0;
+ int c;
+ int typed = 0;
+
+ if (mouse_used != NULL) {
+ *mouse_used = false;
+ }
+
+ // When not printing messages, the user won't know what to type, return a
+ // zero (as if CR was hit).
+ if (msg_silent != 0) {
+ return 0;
+ }
+
+ no_mapping++;
+ for (;;) {
+ ui_cursor_goto(msg_row, msg_col);
+ c = safe_vgetc();
+ if (ascii_isdigit(c)) {
+ n = n * 10 + c - '0';
+ msg_putchar(c);
+ typed++;
+ } else if (c == K_DEL || c == K_KDEL || c == K_BS || c == Ctrl_H) {
+ if (typed > 0) {
+ msg_puts("\b \b");
+ typed--;
+ }
+ n /= 10;
+ } else if (mouse_used != NULL && c == K_LEFTMOUSE) {
+ *mouse_used = true;
+ n = mouse_row + 1;
+ break;
+ } else if (n == 0 && c == ':' && colon) {
+ stuffcharReadbuff(':');
+ if (!exmode_active) {
+ cmdline_row = msg_row;
+ }
+ skip_redraw = true; // skip redraw once
+ do_redraw = false;
+ break;
+ } else if (c == Ctrl_C || c == ESC || c == 'q') {
+ n = 0;
+ break;
+ } else if (c == CAR || c == NL) {
+ break;
+ }
+ }
+ no_mapping--;
+ return n;
+}
+
+/// Ask the user to enter a number.
+///
+/// When "mouse_used" is not NULL allow using the mouse and in that case return
+/// the line number.
+int prompt_for_number(int *mouse_used)
+{
+ int i;
+ int save_cmdline_row;
+ int save_State;
+
+ // When using ":silent" assume that <CR> was entered.
+ if (mouse_used != NULL) {
+ msg_puts(_("Type number and <Enter> or click with the mouse "
+ "(q or empty cancels): "));
+ } else {
+ msg_puts(_("Type number and <Enter> (q or empty cancels): "));
+ }
+
+ // Set the state such that text can be selected/copied/pasted and we still
+ // get mouse events.
+ save_cmdline_row = cmdline_row;
+ cmdline_row = 0;
+ save_State = State;
+ State = ASKMORE; // prevents a screen update when using a timer
+ // May show different mouse shape.
+ setmouse();
+
+ i = get_number(true, mouse_used);
+ if (KeyTyped) {
+ // don't call wait_return() now
+ if (msg_row > 0) {
+ cmdline_row = msg_row - 1;
+ }
+ need_wait_return = false;
+ msg_didany = false;
+ msg_didout = false;
+ } else {
+ cmdline_row = save_cmdline_row;
+ }
+ State = save_State;
+ // May need to restore mouse shape.
+ setmouse();
+
+ return i;
+}