aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/state.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim/state.c')
-rw-r--r--src/nvim/state.c168
1 files changed, 93 insertions, 75 deletions
diff --git a/src/nvim/state.c b/src/nvim/state.c
index 1fe8bb671d..6475105192 100644
--- a/src/nvim/state.c
+++ b/src/nvim/state.c
@@ -12,8 +12,10 @@
#include "nvim/lib/kvec.h"
#include "nvim/log.h"
#include "nvim/main.h"
+#include "nvim/option.h"
#include "nvim/option_defs.h"
#include "nvim/os/input.h"
+#include "nvim/screen.h"
#include "nvim/state.h"
#include "nvim/ui.h"
#include "nvim/vim.h"
@@ -22,7 +24,6 @@
# include "state.c.generated.h"
#endif
-
void state_enter(VimState *s)
{
for (;;) {
@@ -38,15 +39,27 @@ void state_enter(VimState *s)
int key;
getkey:
- if (char_avail() || using_script() || input_available()) {
- // Don't block for events if there's a character already available for
- // processing. Characters can come from mappings, scripts and other
- // sources, so this scenario is very common.
+ // Apply mappings first by calling vpeekc() directly.
+ // - If vpeekc() returns non-NUL, there is a character already available for processing, so
+ // don't block for events. vgetc() may still block, in case of an incomplete UTF-8 sequence.
+ // - If vpeekc() returns NUL, vgetc() will block, and there are three cases:
+ // - There is no input available.
+ // - All of available input maps to an empty string.
+ // - There is an incomplete mapping.
+ // A blocking wait for a character should only be done in the third case, which is the only
+ // case of the three where typebuf.tb_len > 0 after vpeekc() returns NUL.
+ if (vpeekc() != NUL || typebuf.tb_len > 0) {
key = safe_vgetc();
} else if (!multiqueue_empty(main_loop.events)) {
// Event was made available after the last multiqueue_process_events call
key = K_EVENT;
} else {
+ // Duplicate display updating logic in vgetorpeek()
+ if (((State & MODE_INSERT) != 0 || p_lz) && (State & MODE_CMDLINE) == 0
+ && must_redraw != 0 && !need_wait_return) {
+ update_screen(0);
+ setcursor(); // put cursor back where it belongs
+ }
// Flush screen updates before blocking
ui_flush();
// Call `os_inchar` directly to block for events or user input without
@@ -54,17 +67,22 @@ getkey:
// mapping engine.
(void)os_inchar(NULL, 0, -1, 0, main_loop.events);
// If an event was put into the queue, we send K_EVENT directly.
- key = !multiqueue_empty(main_loop.events)
- ? K_EVENT
- : safe_vgetc();
+ if (!multiqueue_empty(main_loop.events)) {
+ key = K_EVENT;
+ } else {
+ goto getkey;
+ }
}
if (key == K_EVENT) {
+ // An event handler may use the value of reg_executing.
+ // Clear it if it should be cleared when getting the next character.
+ check_end_reg_executing(true);
may_sync_undo();
}
-#if MIN_LOG_LEVEL <= DEBUG_LOG_LEVEL
- log_key(DEBUG_LOG_LEVEL, key);
+#if MIN_LOG_LEVEL <= LOGLVL_DBG
+ log_key(LOGLVL_DBG, key);
#endif
int execute_result = s->execute(s, key);
@@ -103,134 +121,134 @@ void state_handle_k_event(void)
}
}
-
/// Return true if in the current mode we need to use virtual.
bool virtual_active(void)
{
+ unsigned int cur_ve_flags = get_ve_flags();
+
// While an operator is being executed we return "virtual_op", because
// VIsual_active has already been reset, thus we can't check for "block"
// being used.
if (virtual_op != kNone) {
return virtual_op;
}
- return ve_flags == VE_ALL
- || ((ve_flags & VE_BLOCK) && VIsual_active && VIsual_mode == Ctrl_V)
- || ((ve_flags & VE_INSERT) && (State & INSERT));
+ return cur_ve_flags == VE_ALL
+ || ((cur_ve_flags & VE_BLOCK) && VIsual_active && VIsual_mode == Ctrl_V)
+ || ((cur_ve_flags & VE_INSERT) && (State & MODE_INSERT));
}
-/// VISUAL, SELECTMODE and OP_PENDING State are never set, they are equal to
-/// NORMAL State with a condition. This function returns the real State.
+/// MODE_VISUAL, MODE_SELECT and MODE_OP_PENDING State are never set, they are
+/// equal to MODE_NORMAL State with a condition. This function returns the real
+/// State.
int get_real_state(void)
{
- if (State & NORMAL) {
+ if (State & MODE_NORMAL) {
if (VIsual_active) {
if (VIsual_select) {
- return SELECTMODE;
+ return MODE_SELECT;
}
- return VISUAL;
+ return MODE_VISUAL;
} else if (finish_op) {
- return OP_PENDING;
+ return MODE_OP_PENDING;
}
}
return State;
}
-/// @returns[allocated] mode string
-char *get_mode(void)
+/// Returns the current mode as a string in "buf[MODE_MAX_LENGTH]", NUL
+/// terminated.
+/// The first character represents the major mode, the following ones the minor
+/// ones.
+void get_mode(char *buf)
{
- char *buf = xcalloc(MODE_MAX_LENGTH, sizeof(char));
+ int i = 0;
if (VIsual_active) {
if (VIsual_select) {
- buf[0] = (char)(VIsual_mode + 's' - 'v');
+ buf[i++] = (char)(VIsual_mode + 's' - 'v');
} else {
- buf[0] = (char)VIsual_mode;
+ buf[i++] = (char)VIsual_mode;
if (restart_VIsual_select) {
- buf[1] = 's';
+ buf[i++] = 's';
}
}
- } else if (State == HITRETURN || State == ASKMORE || State == SETWSIZE
- || State == CONFIRM) {
- buf[0] = 'r';
- if (State == ASKMORE) {
- buf[1] = 'm';
- } else if (State == CONFIRM) {
- buf[1] = '?';
+ } else if (State == MODE_HITRETURN || State == MODE_ASKMORE || State == MODE_SETWSIZE
+ || State == MODE_CONFIRM) {
+ buf[i++] = 'r';
+ if (State == MODE_ASKMORE) {
+ buf[i++] = 'm';
+ } else if (State == MODE_CONFIRM) {
+ buf[i++] = '?';
}
- } else if (State == EXTERNCMD) {
- buf[0] = '!';
- } else if (State & INSERT) {
+ } else if (State == MODE_EXTERNCMD) {
+ buf[i++] = '!';
+ } else if (State & MODE_INSERT) {
if (State & VREPLACE_FLAG) {
- buf[0] = 'R';
- buf[1] = 'v';
- if (ins_compl_active()) {
- buf[2] = 'c';
- } else if (ctrl_x_mode_not_defined_yet()) {
- buf[2] = 'x';
- }
+ buf[i++] = 'R';
+ buf[i++] = 'v';
} else {
if (State & REPLACE_FLAG) {
- buf[0] = 'R';
+ buf[i++] = 'R';
} else {
- buf[0] = 'i';
- }
- if (ins_compl_active()) {
- buf[1] = 'c';
- } else if (ctrl_x_mode_not_defined_yet()) {
- buf[1] = 'x';
+ buf[i++] = 'i';
}
}
- } else if ((State & CMDLINE) || exmode_active) {
- buf[0] = 'c';
+ if (ins_compl_active()) {
+ buf[i++] = 'c';
+ } else if (ctrl_x_mode_not_defined_yet()) {
+ buf[i++] = 'x';
+ }
+ } else if ((State & MODE_CMDLINE) || exmode_active) {
+ buf[i++] = 'c';
if (exmode_active) {
- buf[1] = 'v';
+ buf[i++] = 'v';
}
- } else if (State & TERM_FOCUS) {
- buf[0] = 't';
+ } else if (State & MODE_TERMINAL) {
+ buf[i++] = 't';
} else {
- buf[0] = 'n';
+ buf[i++] = 'n';
if (finish_op) {
- buf[1] = 'o';
+ buf[i++] = 'o';
// to be able to detect force-linewise/blockwise/charwise operations
- buf[2] = (char)motion_force;
+ buf[i++] = (char)motion_force;
} else if (restart_edit == 'I' || restart_edit == 'R'
|| restart_edit == 'V') {
- buf[1] = 'i';
- buf[2] = (char)restart_edit;
+ buf[i++] = 'i';
+ buf[i++] = (char)restart_edit;
} else if (curbuf->terminal) {
- buf[1] = 't';
+ buf[i++] = 't';
}
}
- return buf;
+ buf[i] = NUL;
}
-/// Fires a ModeChanged autocmd.
-void trigger_modechanged(void)
+/// Fires a ModeChanged autocmd if appropriate.
+void may_trigger_modechanged(void)
{
if (!has_event(EVENT_MODECHANGED)) {
return;
}
- char *mode = get_mode();
- if (STRCMP(mode, last_mode) == 0) {
- xfree(mode);
+ char curr_mode[MODE_MAX_LENGTH];
+ char pattern_buf[2 * MODE_MAX_LENGTH];
+
+ get_mode(curr_mode);
+ if (STRCMP(curr_mode, last_mode) == 0) {
return;
}
save_v_event_T save_v_event;
dict_T *v_event = get_v_event(&save_v_event);
- tv_dict_add_str(v_event, S_LEN("new_mode"), mode);
+ tv_dict_add_str(v_event, S_LEN("new_mode"), curr_mode);
tv_dict_add_str(v_event, S_LEN("old_mode"), last_mode);
+ tv_dict_set_keys_readonly(v_event);
- char_u *pat_pre = concat_str((char_u *)last_mode, (char_u *)":");
- char_u *pat = concat_str(pat_pre, (char_u *)mode);
- xfree(pat_pre);
+ // concatenate modes in format "old_mode:new_mode"
+ vim_snprintf(pattern_buf, sizeof(pattern_buf), "%s:%s", last_mode, curr_mode);
- apply_autocmds(EVENT_MODECHANGED, pat, NULL, false, curbuf);
- xfree(last_mode);
- last_mode = mode;
+ apply_autocmds(EVENT_MODECHANGED, pattern_buf, NULL, false, curbuf);
+ STRCPY(last_mode, curr_mode);
- xfree(pat);
restore_v_event(v_event, &save_v_event);
}