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#include <stdint.h>
6#include "nvim/vim.h"
7#include "nvim/ascii.h"
8#include "nvim/cursor_shape.h"
9#include "nvim/ex_getln.h"
10#include "nvim/charset.h"
11#include "nvim/strings.h"
12#include "nvim/syntax.h"
13#include "nvim/api/private/helpers.h"
14#include "nvim/ui.h"
15
16/// Handling of cursor and mouse pointer shapes in various modes.
17cursorentry_T shape_table[SHAPE_IDX_COUNT] =
18{
19 // Values are set by 'guicursor' and 'mouseshape'.
20 // Adjust the SHAPE_IDX_ defines when changing this!
21 { "normal", 0, 0, 0, 700L, 400L, 250L, 0, 0, "n", SHAPE_CURSOR+SHAPE_MOUSE },
22 { "visual", 0, 0, 0, 700L, 400L, 250L, 0, 0, "v", SHAPE_CURSOR+SHAPE_MOUSE },
23 { "insert", 0, 0, 0, 700L, 400L, 250L, 0, 0, "i", SHAPE_CURSOR+SHAPE_MOUSE },
24 { "replace", 0, 0, 0, 700L, 400L, 250L, 0, 0, "r", SHAPE_CURSOR+SHAPE_MOUSE },
25 { "cmdline_normal", 0, 0, 0, 700L, 400L, 250L, 0, 0, "c", SHAPE_CURSOR+SHAPE_MOUSE },
26 { "cmdline_insert", 0, 0, 0, 700L, 400L, 250L, 0, 0, "ci", SHAPE_CURSOR+SHAPE_MOUSE },
27 { "cmdline_replace", 0, 0, 0, 700L, 400L, 250L, 0, 0, "cr", SHAPE_CURSOR+SHAPE_MOUSE },
28 { "operator", 0, 0, 0, 700L, 400L, 250L, 0, 0, "o", SHAPE_CURSOR+SHAPE_MOUSE },
29 { "visual_select", 0, 0, 0, 700L, 400L, 250L, 0, 0, "ve", SHAPE_CURSOR+SHAPE_MOUSE },
30 { "cmdline_hover", 0, 0, 0, 0L, 0L, 0L, 0, 0, "e", SHAPE_MOUSE },
31 { "statusline_hover", 0, 0, 0, 0L, 0L, 0L, 0, 0, "s", SHAPE_MOUSE },
32 { "statusline_drag", 0, 0, 0, 0L, 0L, 0L, 0, 0, "sd", SHAPE_MOUSE },
33 { "vsep_hover", 0, 0, 0, 0L, 0L, 0L, 0, 0, "vs", SHAPE_MOUSE },
34 { "vsep_drag", 0, 0, 0, 0L, 0L, 0L, 0, 0, "vd", SHAPE_MOUSE },
35 { "more", 0, 0, 0, 0L, 0L, 0L, 0, 0, "m", SHAPE_MOUSE },
36 { "more_lastline", 0, 0, 0, 0L, 0L, 0L, 0, 0, "ml", SHAPE_MOUSE },
37 { "showmatch", 0, 0, 0, 100L, 100L, 100L, 0, 0, "sm", SHAPE_CURSOR },
38};
39
40/// Converts cursor_shapes into an Array of Dictionaries
41/// @return Array of the form {[ "cursor_shape": ... ], ...}
42Array mode_style_array(void)
43{
44 Array all = ARRAY_DICT_INIT;
45
46 for (int i = 0; i < SHAPE_IDX_COUNT; i++) {
47 Dictionary dic = ARRAY_DICT_INIT;
48 cursorentry_T *cur = &shape_table[i];
49 if (cur->used_for & SHAPE_MOUSE) {
50 PUT(dic, "mouse_shape", INTEGER_OBJ(cur->mshape));
51 }
52 if (cur->used_for & SHAPE_CURSOR) {
53 String shape_str;
54 switch (cur->shape) {
55 case SHAPE_BLOCK: shape_str = cstr_to_string("block"); break;
56 case SHAPE_VER: shape_str = cstr_to_string("vertical"); break;
57 case SHAPE_HOR: shape_str = cstr_to_string("horizontal"); break;
58 default: shape_str = cstr_to_string("unknown");
59 }
60 PUT(dic, "cursor_shape", STRING_OBJ(shape_str));
61 PUT(dic, "cell_percentage", INTEGER_OBJ(cur->percentage));
62 PUT(dic, "blinkwait", INTEGER_OBJ(cur->blinkwait));
63 PUT(dic, "blinkon", INTEGER_OBJ(cur->blinkon));
64 PUT(dic, "blinkoff", INTEGER_OBJ(cur->blinkoff));
65 PUT(dic, "hl_id", INTEGER_OBJ(cur->id));
66 PUT(dic, "id_lm", INTEGER_OBJ(cur->id_lm));
67 PUT(dic, "attr_id", INTEGER_OBJ(cur->id ? syn_id2attr(cur->id) : 0));
68 PUT(dic, "attr_id_lm", INTEGER_OBJ(cur->id_lm ? syn_id2attr(cur->id_lm)
69 : 0));
70 }
71 PUT(dic, "name", STRING_OBJ(cstr_to_string(cur->full_name)));
72 PUT(dic, "short_name", STRING_OBJ(cstr_to_string(cur->name)));
73
74 ADD(all, DICTIONARY_OBJ(dic));
75 }
76
77 return all;
78}
79
80/// Parse the 'guicursor' option
81///
82/// @param what SHAPE_CURSOR or SHAPE_MOUSE ('mouseshape')
83///
84/// @returns error message for an illegal option, NULL otherwise.
85char_u *parse_shape_opt(int what)
86{
87 char_u *modep;
88 char_u *colonp;
89 char_u *commap;
90 char_u *slashp;
91 char_u *p = NULL;
92 char_u *endp;
93 int idx = 0; // init for GCC
94 int all_idx;
95 int len;
96 int i;
97 int found_ve = false; // found "ve" flag
98 int round;
99
100 // First round: check for errors; second round: do it for real.
101 for (round = 1; round <= 2; round++) {
102 // Repeat for all comma separated parts.
103 modep = p_guicursor;
104 if (*p_guicursor == NUL) {
105 modep = (char_u *)"a:block-blinkon0";
106 }
107 while (modep != NULL && *modep != NUL) {
108 colonp = vim_strchr(modep, ':');
109 commap = vim_strchr(modep, ',');
110
111 if (colonp == NULL || (commap != NULL && commap < colonp)) {
112 return (char_u *)N_("E545: Missing colon");
113 }
114 if (colonp == modep) {
115 return (char_u *)N_("E546: Illegal mode");
116 }
117
118 // Repeat for all modes before the colon.
119 // For the 'a' mode, we loop to handle all the modes.
120 all_idx = -1;
121 while (modep < colonp || all_idx >= 0) {
122 if (all_idx < 0) {
123 // Find the mode
124 if (modep[1] == '-' || modep[1] == ':') {
125 len = 1;
126 } else {
127 len = 2;
128 }
129
130 if (len == 1 && TOLOWER_ASC(modep[0]) == 'a') {
131 all_idx = SHAPE_IDX_COUNT - 1;
132 } else {
133 for (idx = 0; idx < SHAPE_IDX_COUNT; ++idx)
134 if (STRNICMP(modep, shape_table[idx].name, len) == 0)
135 break;
136 if (idx == SHAPE_IDX_COUNT
137 || (shape_table[idx].used_for & what) == 0)
138 return (char_u *)N_("E546: Illegal mode");
139 if (len == 2 && modep[0] == 'v' && modep[1] == 'e')
140 found_ve = true;
141 }
142 modep += len + 1;
143 }
144
145 if (all_idx >= 0) {
146 idx = all_idx--;
147 } else if (round == 2) {
148 {
149 // Set the defaults, for the missing parts
150 shape_table[idx].shape = SHAPE_BLOCK;
151 shape_table[idx].blinkwait = 0L;
152 shape_table[idx].blinkon = 0L;
153 shape_table[idx].blinkoff = 0L;
154 }
155 }
156
157 /* Parse the part after the colon */
158 for (p = colonp + 1; *p && *p != ','; ) {
159 {
160 /*
161 * First handle the ones with a number argument.
162 */
163 i = *p;
164 len = 0;
165 if (STRNICMP(p, "ver", 3) == 0)
166 len = 3;
167 else if (STRNICMP(p, "hor", 3) == 0)
168 len = 3;
169 else if (STRNICMP(p, "blinkwait", 9) == 0)
170 len = 9;
171 else if (STRNICMP(p, "blinkon", 7) == 0)
172 len = 7;
173 else if (STRNICMP(p, "blinkoff", 8) == 0)
174 len = 8;
175 if (len != 0) {
176 p += len;
177 if (!ascii_isdigit(*p))
178 return (char_u *)N_("E548: digit expected");
179 int n = getdigits_int(&p, false, 0);
180 if (len == 3) { // "ver" or "hor"
181 if (n == 0) {
182 return (char_u *)N_("E549: Illegal percentage");
183 }
184 if (round == 2) {
185 if (TOLOWER_ASC(i) == 'v') {
186 shape_table[idx].shape = SHAPE_VER;
187 } else {
188 shape_table[idx].shape = SHAPE_HOR;
189 }
190 shape_table[idx].percentage = n;
191 }
192 } else if (round == 2) {
193 if (len == 9)
194 shape_table[idx].blinkwait = n;
195 else if (len == 7)
196 shape_table[idx].blinkon = n;
197 else
198 shape_table[idx].blinkoff = n;
199 }
200 } else if (STRNICMP(p, "block", 5) == 0) {
201 if (round == 2)
202 shape_table[idx].shape = SHAPE_BLOCK;
203 p += 5;
204 } else { /* must be a highlight group name then */
205 endp = vim_strchr(p, '-');
206 if (commap == NULL) { /* last part */
207 if (endp == NULL)
208 endp = p + STRLEN(p); /* find end of part */
209 } else if (endp > commap || endp == NULL) {
210 endp = commap;
211 }
212 slashp = vim_strchr(p, '/');
213 if (slashp != NULL && slashp < endp) {
214 /* "group/langmap_group" */
215 i = syn_check_group(p, (int)(slashp - p));
216 p = slashp + 1;
217 }
218 if (round == 2) {
219 shape_table[idx].id = syn_check_group(p,
220 (int)(endp - p));
221 shape_table[idx].id_lm = shape_table[idx].id;
222 if (slashp != NULL && slashp < endp)
223 shape_table[idx].id = i;
224 }
225 p = endp;
226 }
227 } /* if (what != SHAPE_MOUSE) */
228
229 if (*p == '-')
230 ++p;
231 }
232 }
233 modep = p;
234 if (modep != NULL && *modep == ',') {
235 modep++;
236 }
237 }
238 }
239
240 /* If the 's' flag is not given, use the 'v' cursor for 's' */
241 if (!found_ve) {
242 {
243 shape_table[SHAPE_IDX_VE].shape = shape_table[SHAPE_IDX_V].shape;
244 shape_table[SHAPE_IDX_VE].percentage =
245 shape_table[SHAPE_IDX_V].percentage;
246 shape_table[SHAPE_IDX_VE].blinkwait =
247 shape_table[SHAPE_IDX_V].blinkwait;
248 shape_table[SHAPE_IDX_VE].blinkon =
249 shape_table[SHAPE_IDX_V].blinkon;
250 shape_table[SHAPE_IDX_VE].blinkoff =
251 shape_table[SHAPE_IDX_V].blinkoff;
252 shape_table[SHAPE_IDX_VE].id = shape_table[SHAPE_IDX_V].id;
253 shape_table[SHAPE_IDX_VE].id_lm = shape_table[SHAPE_IDX_V].id_lm;
254 }
255 }
256 ui_mode_info_set();
257 return NULL;
258}
259
260/// Returns true if the cursor is non-blinking "block" shape during
261/// visual selection.
262///
263/// @param exclusive If 'selection' option is "exclusive".
264bool cursor_is_block_during_visual(bool exclusive)
265{
266 int mode_idx = exclusive ? SHAPE_IDX_VE : SHAPE_IDX_V;
267 return (SHAPE_BLOCK == shape_table[mode_idx].shape
268 && 0 == shape_table[mode_idx].blinkon);
269}
270
271/// Map cursor mode from string to integer
272///
273/// @param mode Fullname of the mode whose id we are looking for
274/// @return -1 in case of failure, else the matching SHAPE_ID* integer
275int cursor_mode_str2int(const char *mode)
276{
277 for (int mode_idx = 0; mode_idx < SHAPE_IDX_COUNT; mode_idx++) {
278 if (strcmp(shape_table[mode_idx].full_name, mode) == 0) {
279 return mode_idx;
280 }
281 }
282 WLOG("Unknown mode %s", mode);
283 return -1;
284}
285
286/// Check if a syntax id is used as a cursor style.
287bool cursor_mode_uses_syn_id(int syn_id)
288{
289 if (*p_guicursor == NUL) {
290 return false;
291 }
292 for (int mode_idx = 0; mode_idx < SHAPE_IDX_COUNT; mode_idx++) {
293 if (shape_table[mode_idx].id == syn_id
294 || shape_table[mode_idx].id_lm == syn_id) {
295 return true;
296 }
297 }
298 return false;
299}
300
301
302/// Return the index into shape_table[] for the current mode.
303int cursor_get_mode_idx(void)
304{
305 if (State == SHOWMATCH) {
306 return SHAPE_IDX_SM;
307 } else if (State & VREPLACE_FLAG) {
308 return SHAPE_IDX_R;
309 } else if (State & REPLACE_FLAG) {
310 return SHAPE_IDX_R;
311 } else if (State & INSERT) {
312 return SHAPE_IDX_I;
313 } else if (State & CMDLINE) {
314 if (cmdline_at_end()) {
315 return SHAPE_IDX_C;
316 } else if (cmdline_overstrike()) {
317 return SHAPE_IDX_CR;
318 } else {
319 return SHAPE_IDX_CI;
320 }
321 } else if (finish_op) {
322 return SHAPE_IDX_O;
323 } else if (VIsual_active) {
324 if (*p_sel == 'e') {
325 return SHAPE_IDX_VE;
326 } else {
327 return SHAPE_IDX_V;
328 }
329 } else {
330 return SHAPE_IDX_N;
331 }
332}
333