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 | |
25 | void 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 | |
39 | getkey: |
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. |
79 | bool 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. |
94 | int 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 |
110 | char *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 | |