1// This is an open source non-commercial project. Dear PVS-Studio, please check
2// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
3
4#include <assert.h>
5
6#include "nvim/lib/kvec.h"
7
8#include "nvim/ascii.h"
9#include "nvim/log.h"
10#include "nvim/state.h"
11#include "nvim/vim.h"
12#include "nvim/main.h"
13#include "nvim/getchar.h"
14#include "nvim/option_defs.h"
15#include "nvim/ui.h"
16#include "nvim/os/input.h"
17#include "nvim/ex_docmd.h"
18#include "nvim/edit.h"
19
20#ifdef INCLUDE_GENERATED_DECLARATIONS
21# include "state.c.generated.h"
22#endif
23
24
25void state_enter(VimState *s)
26{
27 for (;;) {
28 int check_result = s->check ? s->check(s) : 1;
29
30 if (!check_result) {
31 break; // Terminate this state.
32 } else if (check_result == -1) {
33 continue; // check() again.
34 }
35 // Execute this state.
36
37 int key;
38
39getkey:
40 if (char_avail() || using_script() || input_available()) {
41 // Don't block for events if there's a character already available for
42 // processing. Characters can come from mappings, scripts and other
43 // sources, so this scenario is very common.
44 key = safe_vgetc();
45 } else if (!multiqueue_empty(main_loop.events)) {
46 // Event was made available after the last multiqueue_process_events call
47 key = K_EVENT;
48 } else {
49 // Flush screen updates before blocking
50 ui_flush();
51 // Call `os_inchar` directly to block for events or user input without
52 // consuming anything from `input_buffer`(os/input.c) or calling the
53 // mapping engine.
54 (void)os_inchar(NULL, 0, -1, 0, main_loop.events);
55 // If an event was put into the queue, we send K_EVENT directly.
56 key = !multiqueue_empty(main_loop.events)
57 ? K_EVENT
58 : safe_vgetc();
59 }
60
61 if (key == K_EVENT) {
62 may_sync_undo();
63 }
64
65#if MIN_LOG_LEVEL <= DEBUG_LOG_LEVEL
66 log_key(DEBUG_LOG_LEVEL, key);
67#endif
68
69 int execute_result = s->execute(s, key);
70 if (!execute_result) {
71 break;
72 } else if (execute_result == -1) {
73 goto getkey;
74 }
75 }
76}
77
78/// Return true if in the current mode we need to use virtual.
79bool virtual_active(void)
80{
81 // While an operator is being executed we return "virtual_op", because
82 // VIsual_active has already been reset, thus we can't check for "block"
83 // being used.
84 if (virtual_op != kNone) {
85 return virtual_op;
86 }
87 return ve_flags == VE_ALL
88 || ((ve_flags & VE_BLOCK) && VIsual_active && VIsual_mode == Ctrl_V)
89 || ((ve_flags & VE_INSERT) && (State & INSERT));
90}
91
92/// VISUAL, SELECTMODE and OP_PENDING State are never set, they are equal to
93/// NORMAL State with a condition. This function returns the real State.
94int get_real_state(void)
95{
96 if (State & NORMAL) {
97 if (VIsual_active) {
98 if (VIsual_select) {
99 return SELECTMODE;
100 }
101 return VISUAL;
102 } else if (finish_op) {
103 return OP_PENDING;
104 }
105 }
106 return State;
107}
108
109/// @returns[allocated] mode string
110char *get_mode(void)
111{
112 char *buf = xcalloc(4, sizeof(char));
113
114 if (VIsual_active) {
115 if (VIsual_select) {
116 buf[0] = (char)(VIsual_mode + 's' - 'v');
117 } else {
118 buf[0] = (char)VIsual_mode;
119 }
120 } else if (State == HITRETURN || State == ASKMORE || State == SETWSIZE
121 || State == CONFIRM) {
122 buf[0] = 'r';
123 if (State == ASKMORE) {
124 buf[1] = 'm';
125 } else if (State == CONFIRM) {
126 buf[1] = '?';
127 }
128 } else if (State == EXTERNCMD) {
129 buf[0] = '!';
130 } else if (State & INSERT) {
131 if (State & VREPLACE_FLAG) {
132 buf[0] = 'R';
133 buf[1] = 'v';
134 } else {
135 if (State & REPLACE_FLAG) {
136 buf[0] = 'R';
137 } else {
138 buf[0] = 'i';
139 }
140 if (ins_compl_active()) {
141 buf[1] = 'c';
142 } else if (ctrl_x_mode_not_defined_yet()) {
143 buf[1] = 'x';
144 }
145 }
146 } else if ((State & CMDLINE) || exmode_active) {
147 buf[0] = 'c';
148 if (exmode_active == EXMODE_VIM) {
149 buf[1] = 'v';
150 } else if (exmode_active == EXMODE_NORMAL) {
151 buf[1] = 'e';
152 }
153 } else if (State & TERM_FOCUS) {
154 buf[0] = 't';
155 } else {
156 buf[0] = 'n';
157 if (finish_op) {
158 buf[1] = 'o';
159 // to be able to detect force-linewise/blockwise/characterwise operations
160 buf[2] = (char)motion_force;
161 } else if (restart_edit == 'I' || restart_edit == 'R'
162 || restart_edit == 'V') {
163 buf[1] = 'i';
164 buf[2] = (char)restart_edit;
165 }
166 }
167
168 return buf;
169}
170