blob: e1f7446768afa7b5fa77a9a2c280c890f2bfda63 [file] [log] [blame]
Bram Moolenaar14c01f82019-10-09 22:53:08 +02001/* vi:set ts=8 sts=4 sw=4 noet:
2 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 *
5 * Do ":help uganda" in Vim to read copying and usage conditions.
6 * Do ":help credits" in Vim to see a list of people who contributed.
7 * See README.txt for an overview of the Vim source code.
8 */
9
10/*
11 * cindent.c: C indentation related functions
12 *
13 * Many of C-indenting functions originally come from Eric Fischer.
14 *
15 * Below "XXX" means that this function may unlock the current line.
16 */
17
18#include "vim.h"
19
20// values for the "lookfor" state
21#define LOOKFOR_INITIAL 0
22#define LOOKFOR_IF 1
23#define LOOKFOR_DO 2
24#define LOOKFOR_CASE 3
25#define LOOKFOR_ANY 4
26#define LOOKFOR_TERM 5
27#define LOOKFOR_UNTERM 6
28#define LOOKFOR_SCOPEDECL 7
29#define LOOKFOR_NOBREAK 8
30#define LOOKFOR_CPP_BASECLASS 9
31#define LOOKFOR_ENUM_OR_INIT 10
32#define LOOKFOR_JS_KEY 11
33#define LOOKFOR_COMMA 12
34
Bram Moolenaar14c01f82019-10-09 22:53:08 +020035/*
36 * Return TRUE if the string "line" starts with a word from 'cinwords'.
37 */
38 int
39cin_is_cinword(char_u *line)
40{
41 char_u *cinw;
42 char_u *cinw_buf;
43 int cinw_len;
44 int retval = FALSE;
45 int len;
46
47 cinw_len = (int)STRLEN(curbuf->b_p_cinw) + 1;
48 cinw_buf = alloc(cinw_len);
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +000049 if (cinw_buf == NULL)
50 return FALSE;
51
52 line = skipwhite(line);
53 for (cinw = curbuf->b_p_cinw; *cinw; )
Bram Moolenaar14c01f82019-10-09 22:53:08 +020054 {
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +000055 len = copy_option_part(&cinw, cinw_buf, cinw_len, ",");
56 if (STRNCMP(line, cinw_buf, len) == 0
57 && (!vim_iswordc(line[len]) || !vim_iswordc(line[len - 1])))
Bram Moolenaar14c01f82019-10-09 22:53:08 +020058 {
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +000059 retval = TRUE;
60 break;
Bram Moolenaar14c01f82019-10-09 22:53:08 +020061 }
Bram Moolenaar14c01f82019-10-09 22:53:08 +020062 }
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +000063 vim_free(cinw_buf);
Bram Moolenaar14c01f82019-10-09 22:53:08 +020064 return retval;
65}
Bram Moolenaar14c01f82019-10-09 22:53:08 +020066
Bram Moolenaar14c01f82019-10-09 22:53:08 +020067/*
68 * Skip to the end of a "string" and a 'c' character.
69 * If there is no string or character, return argument unmodified.
70 */
71 static char_u *
72skip_string(char_u *p)
73{
74 int i;
75
76 // We loop, because strings may be concatenated: "date""time".
77 for ( ; ; ++p)
78 {
79 if (p[0] == '\'') // 'c' or '\n' or '\000'
80 {
Bram Moolenaar78e0fa42021-10-05 21:58:53 +010081 if (p[1] == NUL) // ' at end of line
Bram Moolenaar14c01f82019-10-09 22:53:08 +020082 break;
83 i = 2;
Bram Moolenaar78e0fa42021-10-05 21:58:53 +010084 if (p[1] == '\\' && p[2] != NUL) // '\n' or '\000'
Bram Moolenaar14c01f82019-10-09 22:53:08 +020085 {
86 ++i;
87 while (vim_isdigit(p[i - 1])) // '\000'
88 ++i;
89 }
Bram Moolenaar60ae0e72022-05-16 18:06:15 +010090 if (p[i - 1] != NUL && p[i] == '\'') // check for trailing '
Bram Moolenaar14c01f82019-10-09 22:53:08 +020091 {
92 p += i;
93 continue;
94 }
95 }
96 else if (p[0] == '"') // start of string
97 {
98 for (++p; p[0]; ++p)
99 {
100 if (p[0] == '\\' && p[1] != NUL)
101 ++p;
102 else if (p[0] == '"') // end of string
103 break;
104 }
105 if (p[0] == '"')
106 continue; // continue for another string
107 }
108 else if (p[0] == 'R' && p[1] == '"')
109 {
110 // Raw string: R"[delim](...)[delim]"
111 char_u *delim = p + 2;
112 char_u *paren = vim_strchr(delim, '(');
113
114 if (paren != NULL)
115 {
116 size_t delim_len = paren - delim;
117
118 for (p += 3; *p; ++p)
119 if (p[0] == ')' && STRNCMP(p + 1, delim, delim_len) == 0
120 && p[delim_len + 1] == '"')
121 {
122 p += delim_len + 1;
123 break;
124 }
125 if (p[0] == '"')
126 continue; // continue for another string
127 }
128 }
129 break; // no string found
130 }
131 if (!*p)
132 --p; // backup from NUL
133 return p;
134}
135
136/*
Bram Moolenaarba263672021-12-29 18:09:13 +0000137 * Return TRUE if "line[col]" is inside a C string.
138 */
139 int
140is_pos_in_string(char_u *line, colnr_T col)
141{
142 char_u *p;
143
144 for (p = line; *p && (colnr_T)(p - line) < col; ++p)
145 p = skip_string(p);
146 return !((colnr_T)(p - line) <= col);
147}
148
Bram Moolenaarba263672021-12-29 18:09:13 +0000149/*
Bram Moolenaar14c01f82019-10-09 22:53:08 +0200150 * Find the start of a comment, not knowing if we are in a comment right now.
151 * Search starts at w_cursor.lnum and goes backwards.
152 * Return NULL when not inside a comment.
153 */
154 static pos_T *
155ind_find_start_comment(void) // XXX
156{
157 return find_start_comment(curbuf->b_ind_maxcomment);
158}
159
160 pos_T *
161find_start_comment(int ind_maxcomment) // XXX
162{
163 pos_T *pos;
Bram Moolenaar14c01f82019-10-09 22:53:08 +0200164 int cur_maxcomment = ind_maxcomment;
165
166 for (;;)
167 {
168 pos = findmatchlimit(NULL, '*', FM_BACKWARD, cur_maxcomment);
169 if (pos == NULL)
170 break;
171
172 // Check if the comment start we found is inside a string.
173 // If it is then restrict the search to below this line and try again.
Bram Moolenaarba263672021-12-29 18:09:13 +0000174 if (!is_pos_in_string(ml_get(pos->lnum), pos->col))
Bram Moolenaar14c01f82019-10-09 22:53:08 +0200175 break;
176 cur_maxcomment = curwin->w_cursor.lnum - pos->lnum - 1;
177 if (cur_maxcomment <= 0)
178 {
179 pos = NULL;
180 break;
181 }
182 }
183 return pos;
184}
185
186/*
187 * Find the start of a raw string, not knowing if we are in one right now.
188 * Search starts at w_cursor.lnum and goes backwards.
189 * Return NULL when not inside a raw string.
190 */
191 static pos_T *
192find_start_rawstring(int ind_maxcomment) // XXX
193{
194 pos_T *pos;
Bram Moolenaar14c01f82019-10-09 22:53:08 +0200195 int cur_maxcomment = ind_maxcomment;
196
197 for (;;)
198 {
199 pos = findmatchlimit(NULL, 'R', FM_BACKWARD, cur_maxcomment);
200 if (pos == NULL)
201 break;
202
203 // Check if the raw string start we found is inside a string.
204 // If it is then restrict the search to below this line and try again.
Bram Moolenaarba263672021-12-29 18:09:13 +0000205 if (!is_pos_in_string(ml_get(pos->lnum), pos->col))
Bram Moolenaar14c01f82019-10-09 22:53:08 +0200206 break;
207 cur_maxcomment = curwin->w_cursor.lnum - pos->lnum - 1;
208 if (cur_maxcomment <= 0)
209 {
210 pos = NULL;
211 break;
212 }
213 }
214 return pos;
215}
216
217/*
218 * Find the start of a comment or raw string, not knowing if we are in a
219 * comment or raw string right now.
220 * Search starts at w_cursor.lnum and goes backwards.
221 * If is_raw is given and returns start of raw_string, sets it to true.
222 * Return NULL when not inside a comment or raw string.
223 * "CORS" -> Comment Or Raw String
224 */
225 static pos_T *
226ind_find_start_CORS(linenr_T *is_raw) // XXX
227{
228 static pos_T comment_pos_copy;
229 pos_T *comment_pos;
230 pos_T *rs_pos;
231
232 comment_pos = find_start_comment(curbuf->b_ind_maxcomment);
233 if (comment_pos != NULL)
234 {
235 // Need to make a copy of the static pos in findmatchlimit(),
236 // calling find_start_rawstring() may change it.
237 comment_pos_copy = *comment_pos;
238 comment_pos = &comment_pos_copy;
239 }
240 rs_pos = find_start_rawstring(curbuf->b_ind_maxcomment);
241
242 // If comment_pos is before rs_pos the raw string is inside the comment.
243 // If rs_pos is before comment_pos the comment is inside the raw string.
244 if (comment_pos == NULL || (rs_pos != NULL
245 && LT_POS(*rs_pos, *comment_pos)))
246 {
247 if (is_raw != NULL && rs_pos != NULL)
248 *is_raw = rs_pos->lnum;
249 return rs_pos;
250 }
251 return comment_pos;
252}
Bram Moolenaar14c01f82019-10-09 22:53:08 +0200253
Bram Moolenaar14c01f82019-10-09 22:53:08 +0200254
255/*
256 * Return TRUE if C-indenting is on.
257 */
258 int
259cindent_on(void)
260{
261 return (!p_paste && (curbuf->b_p_cin
Bram Moolenaar8e145b82022-05-21 20:17:31 +0100262#ifdef FEAT_EVAL
Bram Moolenaar14c01f82019-10-09 22:53:08 +0200263 || *curbuf->b_p_inde != NUL
Bram Moolenaar8e145b82022-05-21 20:17:31 +0100264#endif
Bram Moolenaar14c01f82019-10-09 22:53:08 +0200265 ));
266}
267
268// Find result cache for cpp_baseclass
269typedef struct {
270 int found;
271 lpos_T lpos;
272} cpp_baseclass_cache_T;
273
274/*
275 * Skip over white space and C comments within the line.
276 * Also skip over Perl/shell comments if desired.
277 */
278 static char_u *
279cin_skipcomment(char_u *s)
280{
281 while (*s)
282 {
283 char_u *prev_s = s;
284
285 s = skipwhite(s);
286
287 // Perl/shell # comment comment continues until eol. Require a space
288 // before # to avoid recognizing $#array.
289 if (curbuf->b_ind_hash_comment != 0 && s != prev_s && *s == '#')
290 {
291 s += STRLEN(s);
292 break;
293 }
294 if (*s != '/')
295 break;
296 ++s;
297 if (*s == '/') // slash-slash comment continues till eol
298 {
299 s += STRLEN(s);
300 break;
301 }
302 if (*s != '*')
303 break;
304 for (++s; *s; ++s) // skip slash-star comment
305 if (s[0] == '*' && s[1] == '/')
306 {
307 s += 2;
308 break;
309 }
310 }
311 return s;
312}
313
314/*
315 * Return TRUE if there is no code at *s. White space and comments are
316 * not considered code.
317 */
318 static int
319cin_nocode(char_u *s)
320{
321 return *cin_skipcomment(s) == NUL;
322}
323
324/*
325 * Recognize the start of a C or C++ comment.
326 */
327 static int
328cin_iscomment(char_u *p)
329{
330 return (p[0] == '/' && (p[1] == '*' || p[1] == '/'));
331}
332
333/*
334 * Recognize the start of a "//" comment.
335 */
336 static int
337cin_islinecomment(char_u *p)
338{
339 return (p[0] == '/' && p[1] == '/');
340}
341
342/*
343 * Check previous lines for a "//" line comment, skipping over blank lines.
344 */
345 static pos_T *
346find_line_comment(void) // XXX
347{
348 static pos_T pos;
349 char_u *line;
350 char_u *p;
351
352 pos = curwin->w_cursor;
353 while (--pos.lnum > 0)
354 {
355 line = ml_get(pos.lnum);
356 p = skipwhite(line);
357 if (cin_islinecomment(p))
358 {
359 pos.col = (int)(p - line);
360 return &pos;
361 }
362 if (*p != NUL)
363 break;
364 }
365 return NULL;
366}
367
368/*
369 * Return TRUE if "text" starts with "key:".
370 */
371 static int
372cin_has_js_key(char_u *text)
373{
374 char_u *s = skipwhite(text);
375 int quote = -1;
376
377 if (*s == '\'' || *s == '"')
378 {
379 // can be 'key': or "key":
380 quote = *s;
381 ++s;
382 }
383 if (!vim_isIDc(*s)) // need at least one ID character
384 return FALSE;
385
386 while (vim_isIDc(*s))
387 ++s;
388 if (*s == quote)
389 ++s;
390
391 s = cin_skipcomment(s);
392
393 // "::" is not a label, it's C++
394 return (*s == ':' && s[1] != ':');
395}
396
397/*
398 * Check if string matches "label:"; move to character after ':' if true.
399 * "*s" must point to the start of the label, if there is one.
400 */
401 static int
402cin_islabel_skip(char_u **s)
403{
404 if (!vim_isIDc(**s)) // need at least one ID character
405 return FALSE;
406
407 while (vim_isIDc(**s))
Anttoni Erkkiläf4d87ff2025-03-09 16:07:15 +0100408 {
Naruhiko Nishinoc2a90002025-05-04 20:05:47 +0200409 if (has_mbyte)
410 (*s) += (*mb_ptr2len)(*s);
411 else
412 (*s)++;
Anttoni Erkkiläf4d87ff2025-03-09 16:07:15 +0100413 }
Bram Moolenaar14c01f82019-10-09 22:53:08 +0200414
415 *s = cin_skipcomment(*s);
416
417 // "::" is not a label, it's C++
418 return (**s == ':' && *++*s != ':');
419}
420
421/*
Bram Moolenaara9549c92022-04-17 14:18:11 +0100422 * Recognize a scope declaration label from the 'cinscopedecls' option.
Bram Moolenaar14c01f82019-10-09 22:53:08 +0200423 */
424 static int
Tom Praschan3506cf32022-04-07 12:39:08 +0100425cin_isscopedecl(char_u *p)
Bram Moolenaar14c01f82019-10-09 22:53:08 +0200426{
Bram Moolenaarcb49a1d2022-04-07 13:08:00 +0100427 size_t cinsd_len;
428 char_u *cinsd_buf;
429 char_u *cinsd;
430 size_t len;
431 char_u *skip;
432 char_u *s = cin_skipcomment(p);
433 int found = FALSE;
Bram Moolenaar14c01f82019-10-09 22:53:08 +0200434
Tom Praschan3506cf32022-04-07 12:39:08 +0100435 cinsd_len = STRLEN(curbuf->b_p_cinsd) + 1;
436 cinsd_buf = alloc(cinsd_len);
Bram Moolenaarcb49a1d2022-04-07 13:08:00 +0100437 if (cinsd_buf == NULL)
438 return FALSE;
439
440 for (cinsd = curbuf->b_p_cinsd; *cinsd; )
Tom Praschan3506cf32022-04-07 12:39:08 +0100441 {
Bram Moolenaara9549c92022-04-17 14:18:11 +0100442 len = copy_option_part(&cinsd, cinsd_buf, (int)cinsd_len, ",");
Bram Moolenaarcb49a1d2022-04-07 13:08:00 +0100443 if (STRNCMP(s, cinsd_buf, len) == 0)
Tom Praschan3506cf32022-04-07 12:39:08 +0100444 {
Bram Moolenaarcb49a1d2022-04-07 13:08:00 +0100445 skip = cin_skipcomment(s + len);
446 if (*skip == ':' && skip[1] != ':')
Tom Praschan3506cf32022-04-07 12:39:08 +0100447 {
Bram Moolenaarcb49a1d2022-04-07 13:08:00 +0100448 found = TRUE;
449 break;
Tom Praschan3506cf32022-04-07 12:39:08 +0100450 }
451 }
Tom Praschan3506cf32022-04-07 12:39:08 +0100452 }
453
Bram Moolenaarcb49a1d2022-04-07 13:08:00 +0100454 vim_free(cinsd_buf);
455 return found;
Bram Moolenaar14c01f82019-10-09 22:53:08 +0200456}
457
458/*
459 * Recognize a preprocessor statement: Any line that starts with '#'.
460 */
461 static int
462cin_ispreproc(char_u *s)
463{
464 if (*skipwhite(s) == '#')
465 return TRUE;
466 return FALSE;
467}
468
469/*
470 * Return TRUE if line "*pp" at "*lnump" is a preprocessor statement or a
471 * continuation line of a preprocessor statement. Decrease "*lnump" to the
472 * start and return the line in "*pp".
473 * Put the amount of indent in "*amount".
474 */
475 static int
476cin_ispreproc_cont(char_u **pp, linenr_T *lnump, int *amount)
477{
478 char_u *line = *pp;
479 linenr_T lnum = *lnump;
480 int retval = FALSE;
481 int candidate_amount = *amount;
482
483 if (*line != NUL && line[STRLEN(line) - 1] == '\\')
484 candidate_amount = get_indent_lnum(lnum);
485
486 for (;;)
487 {
488 if (cin_ispreproc(line))
489 {
490 retval = TRUE;
491 *lnump = lnum;
492 break;
493 }
494 if (lnum == 1)
495 break;
496 line = ml_get(--lnum);
497 if (*line == NUL || line[STRLEN(line) - 1] != '\\')
498 break;
499 }
500
501 if (lnum != *lnump)
502 *pp = ml_get(*lnump);
503 if (retval)
504 *amount = candidate_amount;
505 return retval;
506}
507
508 static int
509cin_iselse(
510 char_u *p)
511{
512 if (*p == '}') // accept "} else"
513 p = cin_skipcomment(p + 1);
514 return (STRNCMP(p, "else", 4) == 0 && !vim_isIDc(p[4]));
515}
516
517/*
518 * Recognize a line that starts with '{' or '}', or ends with ';', ',', '{' or
519 * '}'.
520 * Don't consider "} else" a terminated line.
521 * If a line begins with an "else", only consider it terminated if no unmatched
522 * opening braces follow (handle "else { foo();" correctly).
523 * Return the character terminating the line (ending char's have precedence if
524 * both apply in order to determine initializations).
525 */
526 static int
527cin_isterminated(
528 char_u *s,
529 int incl_open, // include '{' at the end as terminator
530 int incl_comma) // recognize a trailing comma
531{
532 char_u found_start = 0;
533 unsigned n_open = 0;
534 int is_else = FALSE;
535
536 s = cin_skipcomment(s);
537
538 if (*s == '{' || (*s == '}' && !cin_iselse(s)))
539 found_start = *s;
540
541 if (!found_start)
542 is_else = cin_iselse(s);
543
544 while (*s)
545 {
546 // skip over comments, "" strings and 'c'haracters
547 s = skip_string(cin_skipcomment(s));
548 if (*s == '}' && n_open > 0)
549 --n_open;
550 if ((!is_else || n_open == 0)
551 && (*s == ';' || *s == '}' || (incl_comma && *s == ','))
552 && cin_nocode(s + 1))
553 return *s;
554 else if (*s == '{')
555 {
556 if (incl_open && cin_nocode(s + 1))
557 return *s;
558 else
559 ++n_open;
560 }
561
562 if (*s)
563 s++;
564 }
565 return found_start;
566}
567
568/*
569 * Return TRUE when "s" starts with "word" and then a non-ID character.
570 */
571 static int
572cin_starts_with(char_u *s, char *word)
573{
574 int l = (int)STRLEN(word);
575
576 return (STRNCMP(s, word, l) == 0 && !vim_isIDc(s[l]));
577}
578
579/*
580 * Recognize a "default" switch label.
581 */
582 static int
583cin_isdefault(char_u *s)
584{
585 return (STRNCMP(s, "default", 7) == 0
586 && *(s = cin_skipcomment(s + 7)) == ':'
587 && s[1] != ':');
588}
589
590/*
591 * Recognize a switch label: "case .*:" or "default:".
592 */
Yegappan Lakshmanan562610c2025-05-04 21:05:51 +0200593 static int
Bram Moolenaar14c01f82019-10-09 22:53:08 +0200594cin_iscase(
595 char_u *s,
596 int strict) // Allow relaxed check of case statement for JS
597{
598 s = cin_skipcomment(s);
599 if (cin_starts_with(s, "case"))
600 {
601 for (s += 4; *s; ++s)
602 {
603 s = cin_skipcomment(s);
Bram Moolenaar02ad4632020-01-12 13:48:18 +0100604 if (*s == NUL)
605 break;
Bram Moolenaar14c01f82019-10-09 22:53:08 +0200606 if (*s == ':')
607 {
608 if (s[1] == ':') // skip over "::" for C++
609 ++s;
610 else
611 return TRUE;
612 }
613 if (*s == '\'' && s[1] && s[2] == '\'')
614 s += 2; // skip over ':'
615 else if (*s == '/' && (s[1] == '*' || s[1] == '/'))
616 return FALSE; // stop at comment
617 else if (*s == '"')
618 {
619 // JS etc.
620 if (strict)
621 return FALSE; // stop at string
622 else
623 return TRUE;
624 }
625 }
626 return FALSE;
627 }
628
629 if (cin_isdefault(s))
630 return TRUE;
631 return FALSE;
632}
633
634/*
635 * Recognize a label: "label:".
636 * Note: curwin->w_cursor must be where we are looking for the label.
637 */
638 static int
639cin_islabel(void) // XXX
640{
641 char_u *s;
642
643 s = cin_skipcomment(ml_get_curline());
644
645 // Exclude "default" from labels, since it should be indented
646 // like a switch label. Same for C++ scope declarations.
647 if (cin_isdefault(s))
648 return FALSE;
649 if (cin_isscopedecl(s))
650 return FALSE;
651
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +0000652 if (!cin_islabel_skip(&s))
653 return FALSE;
654
655 // Only accept a label if the previous line is terminated or is a case
656 // label.
657 pos_T cursor_save;
658 pos_T *trypos;
659 char_u *line;
660
661 cursor_save = curwin->w_cursor;
662 while (curwin->w_cursor.lnum > 1)
Bram Moolenaar14c01f82019-10-09 22:53:08 +0200663 {
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +0000664 --curwin->w_cursor.lnum;
Bram Moolenaar14c01f82019-10-09 22:53:08 +0200665
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +0000666 // If we're in a comment or raw string now, skip to the start of
667 // it.
668 curwin->w_cursor.col = 0;
669 if ((trypos = ind_find_start_CORS(NULL)) != NULL) // XXX
670 curwin->w_cursor = *trypos;
Bram Moolenaar14c01f82019-10-09 22:53:08 +0200671
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +0000672 line = ml_get_curline();
673 if (cin_ispreproc(line)) // ignore #defines, #if, etc.
674 continue;
675 if (*(line = cin_skipcomment(line)) == NUL)
676 continue;
Bram Moolenaar14c01f82019-10-09 22:53:08 +0200677
Bram Moolenaar14c01f82019-10-09 22:53:08 +0200678 curwin->w_cursor = cursor_save;
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +0000679 if (cin_isterminated(line, TRUE, FALSE)
680 || cin_isscopedecl(line)
681 || cin_iscase(line, TRUE)
682 || (cin_islabel_skip(&line) && cin_nocode(line)))
683 return TRUE;
684 return FALSE;
Bram Moolenaar14c01f82019-10-09 22:53:08 +0200685 }
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +0000686 curwin->w_cursor = cursor_save;
687 return TRUE; // label at start of file???
Bram Moolenaar14c01f82019-10-09 22:53:08 +0200688}
689
690/*
691 * Return TRUE if string "s" ends with the string "find", possibly followed by
692 * white space and comments. Skip strings and comments.
693 * Ignore "ignore" after "find" if it's not NULL.
694 */
695 static int
696cin_ends_in(char_u *s, char_u *find, char_u *ignore)
697{
698 char_u *p = s;
699 char_u *r;
700 int len = (int)STRLEN(find);
701
702 while (*p != NUL)
703 {
704 p = cin_skipcomment(p);
705 if (STRNCMP(p, find, len) == 0)
706 {
707 r = skipwhite(p + len);
708 if (ignore != NULL && STRNCMP(r, ignore, STRLEN(ignore)) == 0)
709 r = skipwhite(r + STRLEN(ignore));
710 if (cin_nocode(r))
711 return TRUE;
712 }
713 if (*p != NUL)
714 ++p;
715 }
716 return FALSE;
717}
718
719/*
720 * Recognize structure initialization and enumerations:
721 * "[typedef] [static|public|protected|private] enum"
722 * "[typedef] [static|public|protected|private] = {"
723 */
724 static int
725cin_isinit(void)
726{
727 char_u *s;
728 static char *skip[] = {"static", "public", "protected", "private"};
729
730 s = cin_skipcomment(ml_get_curline());
731
732 if (cin_starts_with(s, "typedef"))
733 s = cin_skipcomment(s + 7);
734
735 for (;;)
736 {
737 int i, l;
738
K.Takataeeec2542021-06-02 13:28:16 +0200739 for (i = 0; i < (int)ARRAY_LENGTH(skip); ++i)
Bram Moolenaar14c01f82019-10-09 22:53:08 +0200740 {
741 l = (int)strlen(skip[i]);
742 if (cin_starts_with(s, skip[i]))
743 {
744 s = cin_skipcomment(s + l);
745 l = 0;
746 break;
747 }
748 }
749 if (l != 0)
750 break;
751 }
752
753 if (cin_starts_with(s, "enum"))
754 return TRUE;
755
756 if (cin_ends_in(s, (char_u *)"=", (char_u *)"{"))
757 return TRUE;
758
759 return FALSE;
760}
761
762// Maximum number of lines to search back for a "namespace" line.
763#define FIND_NAMESPACE_LIM 20
764
765/*
766 * Recognize a "namespace" scope declaration.
767 */
768 static int
769cin_is_cpp_namespace(char_u *s)
770{
771 char_u *p;
772 int has_name = FALSE;
773 int has_name_start = FALSE;
774
775 s = cin_skipcomment(s);
zeertzjqf2f0bdd2021-12-22 20:55:30 +0000776
Virginia Senioria99e4ab22023-03-24 19:25:06 +0000777 // skip over "inline" and "export" in any order
778 while ((STRNCMP(s, "inline", 6) == 0 || STRNCMP(s, "export", 6) == 0)
779 && (s[6] == NUL || !vim_iswordc(s[6])))
zeertzjqf2f0bdd2021-12-22 20:55:30 +0000780 s = cin_skipcomment(skipwhite(s + 6));
781
Bram Moolenaar14c01f82019-10-09 22:53:08 +0200782 if (STRNCMP(s, "namespace", 9) == 0 && (s[9] == NUL || !vim_iswordc(s[9])))
783 {
784 p = cin_skipcomment(skipwhite(s + 9));
785 while (*p != NUL)
786 {
787 if (VIM_ISWHITE(*p))
788 {
789 has_name = TRUE; // found end of a name
790 p = cin_skipcomment(skipwhite(p));
791 }
792 else if (*p == '{')
793 {
794 break;
795 }
796 else if (vim_iswordc(*p))
797 {
798 has_name_start = TRUE;
799 if (has_name)
800 return FALSE; // word character after skipping past name
801 ++p;
802 }
803 else if (p[0] == ':' && p[1] == ':' && vim_iswordc(p[2]))
804 {
805 if (!has_name_start || has_name)
806 return FALSE;
807 // C++ 17 nested namespace
808 p += 3;
809 }
810 else
811 {
812 return FALSE;
813 }
814 }
815 return TRUE;
816 }
817 return FALSE;
818}
819
820/*
821 * Recognize a `extern "C"` or `extern "C++"` linkage specifications.
822 */
823 static int
824cin_is_cpp_extern_c(char_u *s)
825{
826 char_u *p;
827 int has_string_literal = FALSE;
828
829 s = cin_skipcomment(s);
830 if (STRNCMP(s, "extern", 6) == 0 && (s[6] == NUL || !vim_iswordc(s[6])))
831 {
832 p = cin_skipcomment(skipwhite(s + 6));
833 while (*p != NUL)
834 {
835 if (VIM_ISWHITE(*p))
836 {
837 p = cin_skipcomment(skipwhite(p));
838 }
839 else if (*p == '{')
840 {
841 break;
842 }
843 else if (p[0] == '"' && p[1] == 'C' && p[2] == '"')
844 {
845 if (has_string_literal)
846 return FALSE;
847 has_string_literal = TRUE;
848 p += 3;
849 }
850 else if (p[0] == '"' && p[1] == 'C' && p[2] == '+' && p[3] == '+'
851 && p[4] == '"')
852 {
853 if (has_string_literal)
854 return FALSE;
855 has_string_literal = TRUE;
856 p += 5;
857 }
858 else
859 {
860 return FALSE;
861 }
862 }
863 return has_string_literal ? TRUE : FALSE;
864 }
865 return FALSE;
866}
867
868/*
869 * Return a pointer to the first non-empty non-comment character after a ':'.
870 * Return NULL if not found.
871 * case 234: a = b;
872 * ^
873 */
874 static char_u *
875after_label(char_u *l)
876{
877 for ( ; *l; ++l)
878 {
879 if (*l == ':')
880 {
881 if (l[1] == ':') // skip over "::" for C++
882 ++l;
883 else if (!cin_iscase(l + 1, FALSE))
884 break;
885 }
886 else if (*l == '\'' && l[1] && l[2] == '\'')
887 l += 2; // skip over 'x'
888 }
889 if (*l == NUL)
890 return NULL;
891 l = cin_skipcomment(l + 1);
892 if (*l == NUL)
893 return NULL;
894 return l;
895}
896
897/*
898 * Get indent of line "lnum", skipping a label.
899 * Return 0 if there is nothing after the label.
900 */
901 static int
902get_indent_nolabel (linenr_T lnum) // XXX
903{
904 char_u *l;
905 pos_T fp;
906 colnr_T col;
907 char_u *p;
908
909 l = ml_get(lnum);
910 p = after_label(l);
911 if (p == NULL)
912 return 0;
913
914 fp.col = (colnr_T)(p - l);
915 fp.lnum = lnum;
916 getvcol(curwin, &fp, &col, NULL, NULL);
917 return (int)col;
918}
919
920/*
921 * Find indent for line "lnum", ignoring any case or jump label.
922 * Also return a pointer to the text (after the label) in "pp".
923 * label: if (asdf && asdfasdf)
924 * ^
925 */
926 static int
927skip_label(linenr_T lnum, char_u **pp)
928{
929 char_u *l;
930 int amount;
931 pos_T cursor_save;
932
933 cursor_save = curwin->w_cursor;
934 curwin->w_cursor.lnum = lnum;
935 l = ml_get_curline();
936 // XXX
937 if (cin_iscase(l, FALSE) || cin_isscopedecl(l) || cin_islabel())
938 {
939 amount = get_indent_nolabel(lnum);
940 l = after_label(ml_get_curline());
941 if (l == NULL) // just in case
942 l = ml_get_curline();
943 }
944 else
945 {
946 amount = get_indent();
947 l = ml_get_curline();
948 }
949 *pp = l;
950
951 curwin->w_cursor = cursor_save;
952 return amount;
953}
954
955/*
956 * Return the indent of the first variable name after a type in a declaration.
957 * int a, indent of "a"
958 * static struct foo b, indent of "b"
959 * enum bla c, indent of "c"
960 * Returns zero when it doesn't look like a declaration.
961 */
962 static int
963cin_first_id_amount(void)
964{
965 char_u *line, *p, *s;
966 int len;
967 pos_T fp;
968 colnr_T col;
969
970 line = ml_get_curline();
971 p = skipwhite(line);
972 len = (int)(skiptowhite(p) - p);
973 if (len == 6 && STRNCMP(p, "static", 6) == 0)
974 {
975 p = skipwhite(p + 6);
976 len = (int)(skiptowhite(p) - p);
977 }
978 if (len == 6 && STRNCMP(p, "struct", 6) == 0)
979 p = skipwhite(p + 6);
980 else if (len == 4 && STRNCMP(p, "enum", 4) == 0)
981 p = skipwhite(p + 4);
982 else if ((len == 8 && STRNCMP(p, "unsigned", 8) == 0)
983 || (len == 6 && STRNCMP(p, "signed", 6) == 0))
984 {
985 s = skipwhite(p + len);
986 if ((STRNCMP(s, "int", 3) == 0 && VIM_ISWHITE(s[3]))
987 || (STRNCMP(s, "long", 4) == 0 && VIM_ISWHITE(s[4]))
988 || (STRNCMP(s, "short", 5) == 0 && VIM_ISWHITE(s[5]))
989 || (STRNCMP(s, "char", 4) == 0 && VIM_ISWHITE(s[4])))
990 p = s;
991 }
992 for (len = 0; vim_isIDc(p[len]); ++len)
993 ;
994 if (len == 0 || !VIM_ISWHITE(p[len]) || cin_nocode(p))
995 return 0;
996
997 p = skipwhite(p + len);
998 fp.lnum = curwin->w_cursor.lnum;
999 fp.col = (colnr_T)(p - line);
1000 getvcol(curwin, &fp, &col, NULL, NULL);
1001 return (int)col;
1002}
1003
1004/*
1005 * Return the indent of the first non-blank after an equal sign.
1006 * char *foo = "here";
1007 * Return zero if no (useful) equal sign found.
1008 * Return -1 if the line above "lnum" ends in a backslash.
1009 * foo = "asdf\
1010 * asdf\
1011 * here";
1012 */
1013 static int
1014cin_get_equal_amount(linenr_T lnum)
1015{
1016 char_u *line;
1017 char_u *s;
1018 colnr_T col;
1019 pos_T fp;
1020
1021 if (lnum > 1)
1022 {
1023 line = ml_get(lnum - 1);
1024 if (*line != NUL && line[STRLEN(line) - 1] == '\\')
1025 return -1;
1026 }
1027
1028 line = s = ml_get(lnum);
1029 while (*s != NUL && vim_strchr((char_u *)"=;{}\"'", *s) == NULL)
1030 {
1031 if (cin_iscomment(s)) // ignore comments
1032 s = cin_skipcomment(s);
1033 else
1034 ++s;
1035 }
1036 if (*s != '=')
1037 return 0;
1038
1039 s = skipwhite(s + 1);
1040 if (cin_nocode(s))
1041 return 0;
1042
1043 if (*s == '"') // nice alignment for continued strings
1044 ++s;
1045
1046 fp.lnum = lnum;
1047 fp.col = (colnr_T)(s - line);
1048 getvcol(curwin, &fp, &col, NULL, NULL);
1049 return (int)col;
1050}
1051
1052/*
1053 * Skip strings, chars and comments until at or past "trypos".
1054 * Return the column found.
1055 */
1056 static int
1057cin_skip2pos(pos_T *trypos)
1058{
1059 char_u *line;
1060 char_u *p;
1061 char_u *new_p;
1062
1063 p = line = ml_get(trypos->lnum);
1064 while (*p && (colnr_T)(p - line) < trypos->col)
1065 {
1066 if (cin_iscomment(p))
1067 p = cin_skipcomment(p);
1068 else
1069 {
1070 new_p = skip_string(p);
1071 if (new_p == p)
1072 ++p;
1073 else
1074 p = new_p;
1075 }
1076 }
1077 return (int)(p - line);
1078}
1079
1080 static pos_T *
1081find_match_char(int c, int ind_maxparen) // XXX
1082{
1083 pos_T cursor_save;
1084 pos_T *trypos;
1085 static pos_T pos_copy;
1086 int ind_maxp_wk;
1087
1088 cursor_save = curwin->w_cursor;
1089 ind_maxp_wk = ind_maxparen;
1090retry:
1091 if ((trypos = findmatchlimit(NULL, c, 0, ind_maxp_wk)) != NULL)
1092 {
1093 // check if the ( is in a // comment
1094 if ((colnr_T)cin_skip2pos(trypos) > trypos->col)
1095 {
1096 ind_maxp_wk = ind_maxparen - (int)(cursor_save.lnum - trypos->lnum);
1097 if (ind_maxp_wk > 0)
1098 {
1099 curwin->w_cursor = *trypos;
1100 curwin->w_cursor.col = 0; // XXX
1101 goto retry;
1102 }
1103 trypos = NULL;
1104 }
1105 else
1106 {
1107 pos_T *trypos_wk;
1108
1109 pos_copy = *trypos; // copy trypos, findmatch will change it
1110 trypos = &pos_copy;
1111 curwin->w_cursor = *trypos;
1112 if ((trypos_wk = ind_find_start_CORS(NULL)) != NULL) // XXX
1113 {
1114 ind_maxp_wk = ind_maxparen - (int)(cursor_save.lnum
1115 - trypos_wk->lnum);
1116 if (ind_maxp_wk > 0)
1117 {
1118 curwin->w_cursor = *trypos_wk;
1119 goto retry;
1120 }
1121 trypos = NULL;
1122 }
1123 }
1124 }
1125 curwin->w_cursor = cursor_save;
1126 return trypos;
1127}
1128
1129/*
1130 * Find the matching '(', ignoring it if it is in a comment.
1131 * Return NULL if no match found.
1132 */
1133 static pos_T *
1134find_match_paren(int ind_maxparen) // XXX
1135{
1136 return find_match_char('(', ind_maxparen);
1137}
1138
1139/*
1140 * Set w_cursor.col to the column number of the last unmatched ')' or '{' in
1141 * line "l". "l" must point to the start of the line.
1142 */
1143 static int
1144find_last_paren(char_u *l, int start, int end)
1145{
1146 int i;
1147 int retval = FALSE;
1148 int open_count = 0;
1149
1150 curwin->w_cursor.col = 0; // default is start of line
1151
1152 for (i = 0; l[i] != NUL; i++)
1153 {
1154 i = (int)(cin_skipcomment(l + i) - l); // ignore parens in comments
1155 i = (int)(skip_string(l + i) - l); // ignore parens in quotes
1156 if (l[i] == start)
1157 ++open_count;
1158 else if (l[i] == end)
1159 {
1160 if (open_count > 0)
1161 --open_count;
1162 else
1163 {
1164 curwin->w_cursor.col = i;
1165 retval = TRUE;
1166 }
1167 }
1168 }
1169 return retval;
1170}
1171
1172/*
1173 * Recognize the basic picture of a function declaration -- it needs to
1174 * have an open paren somewhere and a close paren at the end of the line and
1175 * no semicolons anywhere.
1176 * When a line ends in a comma we continue looking in the next line.
1177 * "sp" points to a string with the line. When looking at other lines it must
1178 * be restored to the line. When it's NULL fetch lines here.
1179 * "first_lnum" is where we start looking.
1180 * "min_lnum" is the line before which we will not be looking.
1181 */
1182 static int
1183cin_isfuncdecl(
1184 char_u **sp,
1185 linenr_T first_lnum,
1186 linenr_T min_lnum)
1187{
1188 char_u *s;
1189 linenr_T lnum = first_lnum;
1190 linenr_T save_lnum = curwin->w_cursor.lnum;
1191 int retval = FALSE;
1192 pos_T *trypos;
1193 int just_started = TRUE;
1194
1195 if (sp == NULL)
1196 s = ml_get(lnum);
1197 else
1198 s = *sp;
1199
1200 curwin->w_cursor.lnum = lnum;
1201 if (find_last_paren(s, '(', ')')
1202 && (trypos = find_match_paren(curbuf->b_ind_maxparen)) != NULL)
1203 {
1204 lnum = trypos->lnum;
1205 if (lnum < min_lnum)
1206 {
1207 curwin->w_cursor.lnum = save_lnum;
1208 return FALSE;
1209 }
1210
1211 s = ml_get(lnum);
1212 }
1213 curwin->w_cursor.lnum = save_lnum;
1214
1215 // Ignore line starting with #.
1216 if (cin_ispreproc(s))
1217 return FALSE;
1218
1219 while (*s && *s != '(' && *s != ';' && *s != '\'' && *s != '"')
1220 {
1221 if (cin_iscomment(s)) // ignore comments
1222 s = cin_skipcomment(s);
1223 else if (*s == ':')
1224 {
1225 if (*(s + 1) == ':')
1226 s += 2;
1227 else
1228 // To avoid a mistake in the following situation:
1229 // A::A(int a, int b)
1230 // : a(0) // <--not a function decl
1231 // , b(0)
1232 // {...
1233 return FALSE;
1234 }
1235 else
1236 ++s;
1237 }
1238 if (*s != '(')
1239 return FALSE; // ';', ' or " before any () or no '('
1240
1241 while (*s && *s != ';' && *s != '\'' && *s != '"')
1242 {
1243 if (*s == ')' && cin_nocode(s + 1))
1244 {
1245 // ')' at the end: may have found a match
Dominique Pelleaf4a61a2021-12-27 17:21:41 +00001246 // Check for the previous line not to end in a backslash:
Bram Moolenaar14c01f82019-10-09 22:53:08 +02001247 // #if defined(x) && {backslash}
1248 // defined(y)
1249 lnum = first_lnum - 1;
1250 s = ml_get(lnum);
1251 if (*s == NUL || s[STRLEN(s) - 1] != '\\')
1252 retval = TRUE;
1253 goto done;
1254 }
1255 if ((*s == ',' && cin_nocode(s + 1)) || s[1] == NUL || cin_nocode(s))
1256 {
1257 int comma = (*s == ',');
1258
1259 // ',' at the end: continue looking in the next line.
1260 // At the end: check for ',' in the next line, for this style:
1261 // func(arg1
1262 // , arg2)
1263 for (;;)
1264 {
1265 if (lnum >= curbuf->b_ml.ml_line_count)
1266 break;
1267 s = ml_get(++lnum);
1268 if (!cin_ispreproc(s))
1269 break;
1270 }
1271 if (lnum >= curbuf->b_ml.ml_line_count)
1272 break;
1273 // Require a comma at end of the line or a comma or ')' at the
1274 // start of next line.
1275 s = skipwhite(s);
1276 if (!just_started && (!comma && *s != ',' && *s != ')'))
1277 break;
1278 just_started = FALSE;
1279 }
1280 else if (cin_iscomment(s)) // ignore comments
1281 s = cin_skipcomment(s);
1282 else
1283 {
1284 ++s;
1285 just_started = FALSE;
1286 }
1287 }
1288
1289done:
1290 if (lnum != first_lnum && sp != NULL)
1291 *sp = ml_get(first_lnum);
1292
1293 return retval;
1294}
1295
1296 static int
1297cin_isif(char_u *p)
1298{
Yegappan Lakshmanan562610c2025-05-04 21:05:51 +02001299 return (STRNCMP(p, "if", 2) == 0 && !vim_isIDc(p[2]));
Bram Moolenaar14c01f82019-10-09 22:53:08 +02001300}
1301
1302 static int
1303cin_isdo(char_u *p)
1304{
1305 return (STRNCMP(p, "do", 2) == 0 && !vim_isIDc(p[2]));
1306}
1307
1308/*
1309 * Check if this is a "while" that should have a matching "do".
1310 * We only accept a "while (condition) ;", with only white space between the
1311 * ')' and ';'. The condition may be spread over several lines.
1312 */
1313 static int
1314cin_iswhileofdo (char_u *p, linenr_T lnum) // XXX
1315{
1316 pos_T cursor_save;
1317 pos_T *trypos;
1318 int retval = FALSE;
1319
1320 p = cin_skipcomment(p);
1321 if (*p == '}') // accept "} while (cond);"
1322 p = cin_skipcomment(p + 1);
1323 if (cin_starts_with(p, "while"))
1324 {
1325 cursor_save = curwin->w_cursor;
1326 curwin->w_cursor.lnum = lnum;
1327 curwin->w_cursor.col = 0;
1328 p = ml_get_curline();
1329 while (*p && *p != 'w') // skip any '}', until the 'w' of the "while"
1330 {
1331 ++p;
1332 ++curwin->w_cursor.col;
1333 }
1334 if ((trypos = findmatchlimit(NULL, 0, 0,
1335 curbuf->b_ind_maxparen)) != NULL
1336 && *cin_skipcomment(ml_get_pos(trypos) + 1) == ';')
1337 retval = TRUE;
1338 curwin->w_cursor = cursor_save;
1339 }
1340 return retval;
1341}
1342
1343/*
1344 * Check whether in "p" there is an "if", "for" or "while" before "*poffset".
1345 * Return 0 if there is none.
1346 * Otherwise return !0 and update "*poffset" to point to the place where the
1347 * string was found.
1348 */
1349 static int
1350cin_is_if_for_while_before_offset(char_u *line, int *poffset)
1351{
1352 int offset = *poffset;
1353
1354 if (offset-- < 2)
1355 return 0;
1356 while (offset > 2 && VIM_ISWHITE(line[offset]))
1357 --offset;
1358
1359 offset -= 1;
1360 if (!STRNCMP(line + offset, "if", 2))
1361 goto probablyFound;
1362
1363 if (offset >= 1)
1364 {
1365 offset -= 1;
1366 if (!STRNCMP(line + offset, "for", 3))
1367 goto probablyFound;
1368
1369 if (offset >= 2)
1370 {
1371 offset -= 2;
1372 if (!STRNCMP(line + offset, "while", 5))
1373 goto probablyFound;
1374 }
1375 }
1376 return 0;
1377
1378probablyFound:
1379 if (!offset || !vim_isIDc(line[offset - 1]))
1380 {
1381 *poffset = offset;
1382 return 1;
1383 }
1384 return 0;
1385}
1386
1387/*
1388 * Return TRUE if we are at the end of a do-while.
1389 * do
1390 * nothing;
1391 * while (foo
1392 * && bar); <-- here
1393 * Adjust the cursor to the line with "while".
1394 */
1395 static int
1396cin_iswhileofdo_end(int terminated)
1397{
1398 char_u *line;
1399 char_u *p;
1400 char_u *s;
1401 pos_T *trypos;
1402 int i;
1403
1404 if (terminated != ';') // there must be a ';' at the end
1405 return FALSE;
1406
1407 p = line = ml_get_curline();
1408 while (*p != NUL)
1409 {
1410 p = cin_skipcomment(p);
1411 if (*p == ')')
1412 {
1413 s = skipwhite(p + 1);
1414 if (*s == ';' && cin_nocode(s + 1))
1415 {
1416 // Found ");" at end of the line, now check there is "while"
1417 // before the matching '('. XXX
1418 i = (int)(p - line);
1419 curwin->w_cursor.col = i;
1420 trypos = find_match_paren(curbuf->b_ind_maxparen);
1421 if (trypos != NULL)
1422 {
1423 s = cin_skipcomment(ml_get(trypos->lnum));
1424 if (*s == '}') // accept "} while (cond);"
1425 s = cin_skipcomment(s + 1);
1426 if (cin_starts_with(s, "while"))
1427 {
1428 curwin->w_cursor.lnum = trypos->lnum;
1429 return TRUE;
1430 }
1431 }
1432
1433 // Searching may have made "line" invalid, get it again.
1434 line = ml_get_curline();
1435 p = line + i;
1436 }
1437 }
1438 if (*p != NUL)
1439 ++p;
1440 }
1441 return FALSE;
1442}
1443
1444 static int
1445cin_isbreak(char_u *p)
1446{
1447 return (STRNCMP(p, "break", 5) == 0 && !vim_isIDc(p[5]));
1448}
1449
1450/*
1451 * Find the position of a C++ base-class declaration or
1452 * constructor-initialization. eg:
1453 *
1454 * class MyClass :
1455 * baseClass <-- here
1456 * class MyClass : public baseClass,
1457 * anotherBaseClass <-- here (should probably lineup ??)
1458 * MyClass::MyClass(...) :
1459 * baseClass(...) <-- here (constructor-initialization)
1460 *
1461 * This is a lot of guessing. Watch out for "cond ? func() : foo".
1462 */
1463 static int
1464cin_is_cpp_baseclass(
1465 cpp_baseclass_cache_T *cached) // input and output
1466{
1467 lpos_T *pos = &cached->lpos; // find position
1468 char_u *s;
1469 int class_or_struct, lookfor_ctor_init, cpp_base_class;
1470 linenr_T lnum = curwin->w_cursor.lnum;
1471 char_u *line = ml_get_curline();
1472
1473 if (pos->lnum <= lnum)
1474 return cached->found; // Use the cached result
1475
1476 pos->col = 0;
1477
1478 s = skipwhite(line);
1479 if (*s == '#') // skip #define FOO x ? (x) : x
1480 return FALSE;
1481 s = cin_skipcomment(s);
1482 if (*s == NUL)
1483 return FALSE;
1484
1485 cpp_base_class = lookfor_ctor_init = class_or_struct = FALSE;
1486
1487 // Search for a line starting with '#', empty, ending in ';' or containing
1488 // '{' or '}' and start below it. This handles the following situations:
1489 // a = cond ?
1490 // func() :
1491 // asdf;
1492 // func::foo()
1493 // : something
1494 // {}
1495 // Foo::Foo (int one, int two)
1496 // : something(4),
1497 // somethingelse(3)
1498 // {}
1499 while (lnum > 1)
1500 {
1501 line = ml_get(lnum - 1);
1502 s = skipwhite(line);
1503 if (*s == '#' || *s == NUL)
1504 break;
1505 while (*s != NUL)
1506 {
1507 s = cin_skipcomment(s);
1508 if (*s == '{' || *s == '}'
1509 || (*s == ';' && cin_nocode(s + 1)))
1510 break;
1511 if (*s != NUL)
1512 ++s;
1513 }
1514 if (*s != NUL)
1515 break;
1516 --lnum;
1517 }
1518
1519 pos->lnum = lnum;
1520 line = ml_get(lnum);
1521 s = line;
1522 for (;;)
1523 {
1524 if (*s == NUL)
1525 {
1526 if (lnum == curwin->w_cursor.lnum)
1527 break;
1528 // Continue in the cursor line.
1529 line = ml_get(++lnum);
1530 s = line;
1531 }
1532 if (s == line)
1533 {
1534 // don't recognize "case (foo):" as a baseclass
1535 if (cin_iscase(s, FALSE))
1536 break;
1537 s = cin_skipcomment(line);
1538 if (*s == NUL)
1539 continue;
1540 }
1541
1542 if (s[0] == '"' || (s[0] == 'R' && s[1] == '"'))
1543 s = skip_string(s) + 1;
1544 else if (s[0] == ':')
1545 {
1546 if (s[1] == ':')
1547 {
1548 // skip double colon. It can't be a constructor
1549 // initialization any more
1550 lookfor_ctor_init = FALSE;
1551 s = cin_skipcomment(s + 2);
1552 }
1553 else if (lookfor_ctor_init || class_or_struct)
1554 {
1555 // we have something found, that looks like the start of
1556 // cpp-base-class-declaration or constructor-initialization
1557 cpp_base_class = TRUE;
1558 lookfor_ctor_init = class_or_struct = FALSE;
1559 pos->col = 0;
1560 s = cin_skipcomment(s + 1);
1561 }
1562 else
1563 s = cin_skipcomment(s + 1);
1564 }
1565 else if ((STRNCMP(s, "class", 5) == 0 && !vim_isIDc(s[5]))
1566 || (STRNCMP(s, "struct", 6) == 0 && !vim_isIDc(s[6])))
1567 {
1568 class_or_struct = TRUE;
1569 lookfor_ctor_init = FALSE;
1570
1571 if (*s == 'c')
1572 s = cin_skipcomment(s + 5);
1573 else
1574 s = cin_skipcomment(s + 6);
1575 }
1576 else
1577 {
1578 if (s[0] == '{' || s[0] == '}' || s[0] == ';')
1579 {
1580 cpp_base_class = lookfor_ctor_init = class_or_struct = FALSE;
1581 }
1582 else if (s[0] == ')')
1583 {
1584 // Constructor-initialization is assumed if we come across
1585 // something like "):"
1586 class_or_struct = FALSE;
1587 lookfor_ctor_init = TRUE;
1588 }
1589 else if (s[0] == '?')
1590 {
1591 // Avoid seeing '() :' after '?' as constructor init.
1592 return FALSE;
1593 }
1594 else if (!vim_isIDc(s[0]))
1595 {
1596 // if it is not an identifier, we are wrong
1597 class_or_struct = FALSE;
1598 lookfor_ctor_init = FALSE;
1599 }
1600 else if (pos->col == 0)
1601 {
1602 // it can't be a constructor-initialization any more
1603 lookfor_ctor_init = FALSE;
1604
1605 // the first statement starts here: lineup with this one...
1606 if (cpp_base_class)
1607 pos->col = (colnr_T)(s - line);
1608 }
1609
1610 // When the line ends in a comma don't align with it.
1611 if (lnum == curwin->w_cursor.lnum && *s == ',' && cin_nocode(s + 1))
1612 pos->col = 0;
1613
1614 s = cin_skipcomment(s + 1);
1615 }
1616 }
1617
1618 cached->found = cpp_base_class;
1619 if (cpp_base_class)
1620 pos->lnum = lnum;
1621 return cpp_base_class;
1622}
1623
1624 static int
1625get_baseclass_amount(int col)
1626{
1627 int amount;
1628 colnr_T vcol;
1629 pos_T *trypos;
1630
1631 if (col == 0)
1632 {
1633 amount = get_indent();
1634 if (find_last_paren(ml_get_curline(), '(', ')')
1635 && (trypos = find_match_paren(curbuf->b_ind_maxparen)) != NULL)
1636 amount = get_indent_lnum(trypos->lnum); // XXX
1637 if (!cin_ends_in(ml_get_curline(), (char_u *)",", NULL))
1638 amount += curbuf->b_ind_cpp_baseclass;
1639 }
1640 else
1641 {
1642 curwin->w_cursor.col = col;
1643 getvcol(curwin, &curwin->w_cursor, &vcol, NULL, NULL);
1644 amount = (int)vcol;
1645 }
1646 if (amount < curbuf->b_ind_cpp_baseclass)
1647 amount = curbuf->b_ind_cpp_baseclass;
1648 return amount;
1649}
1650
1651/*
1652 * Find the '{' at the start of the block we are in.
1653 * Return NULL if no match found.
1654 * Ignore a '{' that is in a comment, makes indenting the next three lines
1655 * work.
1656 */
Bram Moolenaarc667da52019-11-30 20:52:27 +01001657// foo()
1658// {
1659// }
Bram Moolenaar14c01f82019-10-09 22:53:08 +02001660
1661 static pos_T *
1662find_start_brace(void) // XXX
1663{
Bram Moolenaar2de9b7c2021-11-19 19:41:13 +00001664 pos_T cursor_save;
1665 pos_T *trypos;
1666 pos_T *pos;
1667 static pos_T pos_copy;
Bram Moolenaar14c01f82019-10-09 22:53:08 +02001668
1669 cursor_save = curwin->w_cursor;
1670 while ((trypos = findmatchlimit(NULL, '{', FM_BLOCKSTOP, 0)) != NULL)
1671 {
1672 pos_copy = *trypos; // copy pos_T, next findmatch will change it
1673 trypos = &pos_copy;
1674 curwin->w_cursor = *trypos;
1675 pos = NULL;
1676 // ignore the { if it's in a // or / * * / comment
1677 if ((colnr_T)cin_skip2pos(trypos) == trypos->col
1678 && (pos = ind_find_start_CORS(NULL)) == NULL) // XXX
1679 break;
1680 if (pos != NULL)
Bram Moolenaar2de9b7c2021-11-19 19:41:13 +00001681 curwin->w_cursor = *pos;
Bram Moolenaar14c01f82019-10-09 22:53:08 +02001682 }
1683 curwin->w_cursor = cursor_save;
1684 return trypos;
1685}
1686
1687/*
1688 * Find the matching '(', ignoring it if it is in a comment or before an
1689 * unmatched {.
1690 * Return NULL if no match found.
1691 */
1692 static pos_T *
1693find_match_paren_after_brace (int ind_maxparen) // XXX
1694{
1695 pos_T *trypos = find_match_paren(ind_maxparen);
1696
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001697 if (trypos == NULL)
1698 return NULL;
Bram Moolenaar14c01f82019-10-09 22:53:08 +02001699
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001700 pos_T *tryposBrace = find_start_brace();
1701
1702 // If both an unmatched '(' and '{' is found. Ignore the '('
1703 // position if the '{' is further down.
1704 if (tryposBrace != NULL
1705 && (trypos->lnum != tryposBrace->lnum
1706 ? trypos->lnum < tryposBrace->lnum
1707 : trypos->col < tryposBrace->col))
1708 trypos = NULL;
Bram Moolenaar14c01f82019-10-09 22:53:08 +02001709 return trypos;
1710}
1711
1712/*
1713 * Return ind_maxparen corrected for the difference in line number between the
1714 * cursor position and "startpos". This makes sure that searching for a
1715 * matching paren above the cursor line doesn't find a match because of
1716 * looking a few lines further.
1717 */
1718 static int
1719corr_ind_maxparen(pos_T *startpos)
1720{
1721 long n = (long)startpos->lnum - (long)curwin->w_cursor.lnum;
1722
1723 if (n > 0 && n < curbuf->b_ind_maxparen / 2)
1724 return curbuf->b_ind_maxparen - (int)n;
1725 return curbuf->b_ind_maxparen;
1726}
1727
1728/*
1729 * Parse 'cinoptions' and set the values in "curbuf".
1730 * Must be called when 'cinoptions', 'shiftwidth' and/or 'tabstop' changes.
1731 */
1732 void
1733parse_cino(buf_T *buf)
1734{
1735 char_u *p;
1736 char_u *l;
1737 char_u *digits;
Ernie Raelfda700c2023-11-30 18:20:00 +01001738 vimlong_T n;
Bram Moolenaar14c01f82019-10-09 22:53:08 +02001739 int divider;
1740 int fraction = 0;
Christian Brabandt37705742023-11-22 22:18:35 +01001741 int sw;
Ernie Rael2b0882f2023-11-23 20:21:45 +01001742 long t = get_sw_value(buf);
Christian Brabandt37705742023-11-22 22:18:35 +01001743
1744 // needed for cino-(, it will be multiplied by 2 again
1745 if (t > INT_MAX / 2)
1746 sw = INT_MAX / 2;
1747 else
1748 sw = (int)t;
Bram Moolenaar14c01f82019-10-09 22:53:08 +02001749
1750 // Set the default values.
1751
1752 // Spaces from a block's opening brace the prevailing indent for that
1753 // block should be.
1754 buf->b_ind_level = sw;
1755
1756 // Spaces from the edge of the line an open brace that's at the end of a
1757 // line is imagined to be.
1758 buf->b_ind_open_imag = 0;
1759
1760 // Spaces from the prevailing indent for a line that is not preceded by
1761 // an opening brace.
1762 buf->b_ind_no_brace = 0;
1763
1764 // Column where the first { of a function should be located }.
1765 buf->b_ind_first_open = 0;
1766
1767 // Spaces from the prevailing indent a leftmost open brace should be
1768 // located.
1769 buf->b_ind_open_extra = 0;
1770
1771 // Spaces from the matching open brace (real location for one at the left
1772 // edge; imaginary location from one that ends a line) the matching close
1773 // brace should be located.
1774 buf->b_ind_close_extra = 0;
1775
1776 // Spaces from the edge of the line an open brace sitting in the leftmost
1777 // column is imagined to be.
1778 buf->b_ind_open_left_imag = 0;
1779
1780 // Spaces jump labels should be shifted to the left if N is non-negative,
1781 // otherwise the jump label will be put to column 1.
1782 buf->b_ind_jump_label = -1;
1783
1784 // Spaces from the switch() indent a "case xx" label should be located.
1785 buf->b_ind_case = sw;
1786
1787 // Spaces from the "case xx:" code after a switch() should be located.
1788 buf->b_ind_case_code = sw;
1789
1790 // Lineup break at end of case in switch() with case label.
1791 buf->b_ind_case_break = 0;
1792
1793 // Spaces from the class declaration indent a scope declaration label
1794 // should be located.
1795 buf->b_ind_scopedecl = sw;
1796
1797 // Spaces from the scope declaration label code should be located.
1798 buf->b_ind_scopedecl_code = sw;
1799
1800 // Amount K&R-style parameters should be indented.
1801 buf->b_ind_param = sw;
1802
1803 // Amount a function type spec should be indented.
1804 buf->b_ind_func_type = sw;
1805
1806 // Amount a cpp base class declaration or constructor initialization
1807 // should be indented.
1808 buf->b_ind_cpp_baseclass = sw;
1809
1810 // additional spaces beyond the prevailing indent a continuation line
1811 // should be located.
1812 buf->b_ind_continuation = sw;
1813
Bram Moolenaar32aa1022019-11-02 22:54:41 +01001814 // Spaces from the indent of the line with an unclosed parenthesis.
Bram Moolenaar14c01f82019-10-09 22:53:08 +02001815 buf->b_ind_unclosed = sw * 2;
1816
Bram Moolenaar32aa1022019-11-02 22:54:41 +01001817 // Spaces from the indent of the line with an unclosed parenthesis, which
Bram Moolenaar14c01f82019-10-09 22:53:08 +02001818 // itself is also unclosed.
1819 buf->b_ind_unclosed2 = sw;
1820
1821 // Suppress ignoring spaces from the indent of a line starting with an
Dominique Pelleaf4a61a2021-12-27 17:21:41 +00001822 // unclosed parenthesis.
Bram Moolenaar14c01f82019-10-09 22:53:08 +02001823 buf->b_ind_unclosed_noignore = 0;
1824
1825 // If the opening paren is the last nonwhite character on the line, and
1826 // b_ind_unclosed_wrapped is nonzero, use this indent relative to the outer
1827 // context (for very long lines).
1828 buf->b_ind_unclosed_wrapped = 0;
1829
1830 // Suppress ignoring white space when lining up with the character after
Bram Moolenaar32aa1022019-11-02 22:54:41 +01001831 // an unclosed parenthesis.
Bram Moolenaar14c01f82019-10-09 22:53:08 +02001832 buf->b_ind_unclosed_whiteok = 0;
1833
Dominique Pelleaf4a61a2021-12-27 17:21:41 +00001834 // Indent a closing parenthesis under the line start of the matching
1835 // opening parenthesis.
Bram Moolenaar14c01f82019-10-09 22:53:08 +02001836 buf->b_ind_matching_paren = 0;
1837
Dominique Pelleaf4a61a2021-12-27 17:21:41 +00001838 // Indent a closing parenthesis under the previous line.
Bram Moolenaar14c01f82019-10-09 22:53:08 +02001839 buf->b_ind_paren_prev = 0;
1840
1841 // Extra indent for comments.
1842 buf->b_ind_comment = 0;
1843
1844 // Spaces from the comment opener when there is nothing after it.
1845 buf->b_ind_in_comment = 3;
1846
1847 // Boolean: if non-zero, use b_ind_in_comment even if there is something
1848 // after the comment opener.
1849 buf->b_ind_in_comment2 = 0;
1850
1851 // Max lines to search for an open paren.
1852 buf->b_ind_maxparen = 20;
1853
1854 // Max lines to search for an open comment.
1855 buf->b_ind_maxcomment = 70;
1856
1857 // Handle braces for java code.
1858 buf->b_ind_java = 0;
1859
1860 // Not to confuse JS object properties with labels.
1861 buf->b_ind_js = 0;
1862
1863 // Handle blocked cases correctly.
1864 buf->b_ind_keep_case_label = 0;
1865
1866 // Handle C++ namespace.
1867 buf->b_ind_cpp_namespace = 0;
1868
Bram Moolenaarc9471b12023-05-09 15:00:00 +01001869 // Handle continuation lines containing conditions of if (), for () and
1870 // while ().
Bram Moolenaar14c01f82019-10-09 22:53:08 +02001871 buf->b_ind_if_for_while = 0;
1872
1873 // indentation for # comments
1874 buf->b_ind_hash_comment = 0;
1875
1876 // Handle C++ extern "C" or "C++"
1877 buf->b_ind_cpp_extern_c = 0;
1878
Bram Moolenaard881b512020-05-31 17:49:30 +02001879 // Handle C #pragma directives
1880 buf->b_ind_pragma = 0;
1881
Bram Moolenaar14c01f82019-10-09 22:53:08 +02001882 for (p = buf->b_p_cino; *p; )
1883 {
1884 l = p++;
1885 if (*p == '-')
1886 ++p;
1887 digits = p; // remember where the digits start
1888 n = getdigits(&p);
1889 divider = 0;
1890 if (*p == '.') // ".5s" means a fraction
1891 {
1892 fraction = atol((char *)++p);
1893 while (VIM_ISDIGIT(*p))
1894 {
1895 ++p;
1896 if (divider)
1897 divider *= 10;
1898 else
1899 divider = 10;
1900 }
1901 }
1902 if (*p == 's') // "2s" means two times 'shiftwidth'
1903 {
1904 if (p == digits)
1905 n = sw; // just "s" is one 'shiftwidth'
1906 else
1907 {
1908 n *= sw;
1909 if (divider)
Ernie Raelfda700c2023-11-30 18:20:00 +01001910 n += ((vimlong_T)sw * fraction + divider / 2) / divider;
Bram Moolenaar14c01f82019-10-09 22:53:08 +02001911 }
1912 ++p;
1913 }
1914 if (l[1] == '-')
1915 n = -n;
1916
Ernie Rael2b0882f2023-11-23 20:21:45 +01001917 n = trim_to_int(n);
Christian Brabandt37705742023-11-22 22:18:35 +01001918
Bram Moolenaar14c01f82019-10-09 22:53:08 +02001919 // When adding an entry here, also update the default 'cinoptions' in
1920 // doc/indent.txt, and add explanation for it!
1921 switch (*l)
1922 {
Christian Brabandt37705742023-11-22 22:18:35 +01001923 case '>': buf->b_ind_level = (int)n; break;
1924 case 'e': buf->b_ind_open_imag = (int)n; break;
1925 case 'n': buf->b_ind_no_brace = (int)n; break;
1926 case 'f': buf->b_ind_first_open = (int)n; break;
1927 case '{': buf->b_ind_open_extra = (int)n; break;
1928 case '}': buf->b_ind_close_extra = (int)n; break;
1929 case '^': buf->b_ind_open_left_imag = (int)n; break;
1930 case 'L': buf->b_ind_jump_label = (int)n; break;
1931 case ':': buf->b_ind_case = (int)n; break;
1932 case '=': buf->b_ind_case_code = (int)n; break;
1933 case 'b': buf->b_ind_case_break = (int)n; break;
1934 case 'p': buf->b_ind_param = (int)n; break;
1935 case 't': buf->b_ind_func_type = (int)n; break;
1936 case '/': buf->b_ind_comment = (int)n; break;
1937 case 'c': buf->b_ind_in_comment = (int)n; break;
1938 case 'C': buf->b_ind_in_comment2 = (int)n; break;
1939 case 'i': buf->b_ind_cpp_baseclass = (int)n; break;
1940 case '+': buf->b_ind_continuation = (int)n; break;
1941 case '(': buf->b_ind_unclosed = (int)n; break;
1942 case 'u': buf->b_ind_unclosed2 = (int)n; break;
1943 case 'U': buf->b_ind_unclosed_noignore = (int)n; break;
1944 case 'W': buf->b_ind_unclosed_wrapped = (int)n; break;
1945 case 'w': buf->b_ind_unclosed_whiteok = (int)n; break;
1946 case 'm': buf->b_ind_matching_paren = (int)n; break;
1947 case 'M': buf->b_ind_paren_prev = (int)n; break;
1948 case ')': buf->b_ind_maxparen = (int)n; break;
1949 case '*': buf->b_ind_maxcomment = (int)n; break;
1950 case 'g': buf->b_ind_scopedecl = (int)n; break;
1951 case 'h': buf->b_ind_scopedecl_code = (int)n; break;
1952 case 'j': buf->b_ind_java = (int)n; break;
1953 case 'J': buf->b_ind_js = (int)n; break;
1954 case 'l': buf->b_ind_keep_case_label = (int)n; break;
1955 case '#': buf->b_ind_hash_comment = (int)n; break;
1956 case 'N': buf->b_ind_cpp_namespace = (int)n; break;
1957 case 'k': buf->b_ind_if_for_while = (int)n; break;
1958 case 'E': buf->b_ind_cpp_extern_c = (int)n; break;
1959 case 'P': buf->b_ind_pragma = (int)n; break;
Bram Moolenaar14c01f82019-10-09 22:53:08 +02001960 }
1961 if (*p == ',')
1962 ++p;
1963 }
1964}
1965
1966 static int
1967find_match(int lookfor, linenr_T ourscope)
1968{
1969 char_u *look;
1970 pos_T *theirscope;
1971 char_u *mightbeif;
1972 int elselevel;
1973 int whilelevel;
1974
1975 if (lookfor == LOOKFOR_IF)
1976 {
1977 elselevel = 1;
1978 whilelevel = 0;
1979 }
1980 else
1981 {
1982 elselevel = 0;
1983 whilelevel = 1;
1984 }
1985
1986 curwin->w_cursor.col = 0;
1987
1988 while (curwin->w_cursor.lnum > ourscope + 1)
1989 {
1990 curwin->w_cursor.lnum--;
1991 curwin->w_cursor.col = 0;
1992
1993 look = cin_skipcomment(ml_get_curline());
1994 if (cin_iselse(look)
1995 || cin_isif(look)
1996 || cin_isdo(look) // XXX
1997 || cin_iswhileofdo(look, curwin->w_cursor.lnum))
1998 {
1999 // if we've gone outside the braces entirely,
2000 // we must be out of scope...
2001 theirscope = find_start_brace(); // XXX
2002 if (theirscope == NULL)
2003 break;
2004
2005 // and if the brace enclosing this is further
2006 // back than the one enclosing the else, we're
2007 // out of luck too.
2008 if (theirscope->lnum < ourscope)
2009 break;
2010
2011 // and if they're enclosed in a *deeper* brace,
2012 // then we can ignore it because it's in a
2013 // different scope...
2014 if (theirscope->lnum > ourscope)
2015 continue;
2016
2017 // if it was an "else" (that's not an "else if")
2018 // then we need to go back to another if, so
2019 // increment elselevel
2020 look = cin_skipcomment(ml_get_curline());
2021 if (cin_iselse(look))
2022 {
2023 mightbeif = cin_skipcomment(look + 4);
2024 if (!cin_isif(mightbeif))
2025 ++elselevel;
2026 continue;
2027 }
2028
2029 // if it was a "while" then we need to go back to
2030 // another "do", so increment whilelevel. XXX
2031 if (cin_iswhileofdo(look, curwin->w_cursor.lnum))
2032 {
2033 ++whilelevel;
2034 continue;
2035 }
2036
2037 // If it's an "if" decrement elselevel
2038 look = cin_skipcomment(ml_get_curline());
2039 if (cin_isif(look))
2040 {
2041 elselevel--;
2042 // When looking for an "if" ignore "while"s that
2043 // get in the way.
2044 if (elselevel == 0 && lookfor == LOOKFOR_IF)
2045 whilelevel = 0;
2046 }
2047
2048 // If it's a "do" decrement whilelevel
2049 if (cin_isdo(look))
2050 whilelevel--;
2051
2052 // if we've used up all the elses, then
2053 // this must be the if that we want!
2054 // match the indent level of that if.
2055 if (elselevel <= 0 && whilelevel <= 0)
2056 return OK;
2057 }
2058 }
2059 return FAIL;
2060}
2061
2062/*
2063 * Return the desired indent for C code.
2064 * Return -1 if the indent should be left alone (inside a raw string).
2065 */
2066 int
2067get_c_indent(void)
2068{
2069 pos_T cur_curpos;
2070 int amount;
2071 int scope_amount;
2072 int cur_amount = MAXCOL;
2073 colnr_T col;
2074 char_u *theline;
2075 char_u *linecopy;
2076 pos_T *trypos;
2077 pos_T *comment_pos;
2078 pos_T *tryposBrace = NULL;
2079 pos_T tryposCopy;
2080 pos_T our_paren_pos;
2081 char_u *start;
2082 int start_brace;
2083#define BRACE_IN_COL0 1 // '{' is in column 0
2084#define BRACE_AT_START 2 // '{' is at start of line
2085#define BRACE_AT_END 3 // '{' is at end of line
2086 linenr_T ourscope;
2087 char_u *l;
2088 char_u *look;
2089 char_u terminated;
2090 int lookfor;
2091 int whilelevel;
2092 linenr_T lnum;
2093 int n;
2094 int iscase;
2095 int lookfor_break;
2096 int lookfor_cpp_namespace = FALSE;
2097 int cont_amount = 0; // amount for continuation line
2098 int original_line_islabel;
2099 int added_to_amount = 0;
2100 int js_cur_has_key = 0;
2101 linenr_T raw_string_start = 0;
2102 cpp_baseclass_cache_T cache_cpp_baseclass = { FALSE, { MAXLNUM, 0 } };
2103
2104 // make a copy, value is changed below
2105 int ind_continuation = curbuf->b_ind_continuation;
2106
2107 // remember where the cursor was when we started
2108 cur_curpos = curwin->w_cursor;
2109
2110 // if we are at line 1 zero indent is fine, right?
2111 if (cur_curpos.lnum == 1)
2112 return 0;
2113
2114 // Get a copy of the current contents of the line.
2115 // This is required, because only the most recent line obtained with
2116 // ml_get is valid!
2117 linecopy = vim_strsave(ml_get(cur_curpos.lnum));
2118 if (linecopy == NULL)
2119 return 0;
2120
2121 // In insert mode and the cursor is on a ')' truncate the line at the
2122 // cursor position. We don't want to line up with the matching '(' when
2123 // inserting new stuff.
2124 // For unknown reasons the cursor might be past the end of the line, thus
2125 // check for that.
Bram Moolenaar24959102022-05-07 20:01:16 +01002126 if ((State & MODE_INSERT)
Bram Moolenaar14c01f82019-10-09 22:53:08 +02002127 && curwin->w_cursor.col < (colnr_T)STRLEN(linecopy)
2128 && linecopy[curwin->w_cursor.col] == ')')
2129 linecopy[curwin->w_cursor.col] = NUL;
2130
2131 theline = skipwhite(linecopy);
2132
2133 // move the cursor to the start of the line
2134
2135 curwin->w_cursor.col = 0;
2136
2137 original_line_islabel = cin_islabel(); // XXX
2138
2139 // If we are inside a raw string don't change the indent.
2140 // Ignore a raw string inside a comment.
2141 comment_pos = ind_find_start_comment();
2142 if (comment_pos != NULL)
2143 {
2144 // findmatchlimit() static pos is overwritten, make a copy
2145 tryposCopy = *comment_pos;
2146 comment_pos = &tryposCopy;
2147 }
2148 trypos = find_start_rawstring(curbuf->b_ind_maxcomment);
2149 if (trypos != NULL && (comment_pos == NULL
2150 || LT_POS(*trypos, *comment_pos)))
2151 {
2152 amount = -1;
2153 goto laterend;
2154 }
2155
Bram Moolenaard881b512020-05-31 17:49:30 +02002156 // #defines and so on go at the left when included in 'cinkeys',
Bram Moolenaar8e7d6222020-12-18 19:49:56 +01002157 // excluding pragmas when customized in 'cinoptions'
Bram Moolenaar14c01f82019-10-09 22:53:08 +02002158 if (*theline == '#' && (*linecopy == '#' || in_cinkeys('#', ' ', TRUE)))
2159 {
Bram Moolenaard881b512020-05-31 17:49:30 +02002160 char_u *directive = skipwhite(theline + 1);
2161 if (curbuf->b_ind_pragma == 0 || STRNCMP(directive, "pragma", 6) != 0)
2162 {
2163 amount = curbuf->b_ind_hash_comment;
2164 goto theend;
2165 }
Bram Moolenaar14c01f82019-10-09 22:53:08 +02002166 }
2167
2168 // Is it a non-case label? Then that goes at the left margin too unless:
2169 // - JS flag is set.
2170 // - 'L' item has a positive value.
2171 if (original_line_islabel && !curbuf->b_ind_js
2172 && curbuf->b_ind_jump_label < 0)
2173 {
2174 amount = 0;
2175 goto theend;
2176 }
2177
2178 // If we're inside a "//" comment and there is a "//" comment in a
2179 // previous line, lineup with that one.
Bram Moolenaar6e371ec2021-12-12 14:16:39 +00002180 if (cin_islinecomment(theline))
Bram Moolenaar14c01f82019-10-09 22:53:08 +02002181 {
Bram Moolenaar6e371ec2021-12-12 14:16:39 +00002182 pos_T linecomment_pos;
2183
2184 trypos = find_line_comment(); // XXX
2185 if (trypos == NULL && curwin->w_cursor.lnum > 1)
2186 {
2187 // There may be a statement before the comment, search from the end
2188 // of the line for a comment start.
2189 linecomment_pos.col =
2190 check_linecomment(ml_get(curwin->w_cursor.lnum - 1));
2191 if (linecomment_pos.col != MAXCOL)
2192 {
Bram Moolenaar6ed545e2022-05-09 20:09:23 +01002193 trypos = &linecomment_pos;
2194 trypos->lnum = curwin->w_cursor.lnum - 1;
Bram Moolenaar6e371ec2021-12-12 14:16:39 +00002195 }
2196 }
2197 if (trypos != NULL)
2198 {
2199 // find how indented the line beginning the comment is
2200 getvcol(curwin, trypos, &col, NULL, NULL);
2201 amount = col;
2202 goto theend;
2203 }
Bram Moolenaar14c01f82019-10-09 22:53:08 +02002204 }
2205
2206 // If we're inside a comment and not looking at the start of the
2207 // comment, try using the 'comments' option.
2208 if (!cin_iscomment(theline) && comment_pos != NULL) // XXX
2209 {
2210 int lead_start_len = 2;
2211 int lead_middle_len = 1;
2212 char_u lead_start[COM_MAX_LEN]; // start-comment string
2213 char_u lead_middle[COM_MAX_LEN]; // middle-comment string
2214 char_u lead_end[COM_MAX_LEN]; // end-comment string
2215 char_u *p;
2216 int start_align = 0;
2217 int start_off = 0;
2218 int done = FALSE;
2219
2220 // find how indented the line beginning the comment is
2221 getvcol(curwin, comment_pos, &col, NULL, NULL);
2222 amount = col;
2223 *lead_start = NUL;
2224 *lead_middle = NUL;
2225
2226 p = curbuf->b_p_com;
2227 while (*p != NUL)
2228 {
2229 int align = 0;
2230 int off = 0;
2231 int what = 0;
2232
2233 while (*p != NUL && *p != ':')
2234 {
2235 if (*p == COM_START || *p == COM_END || *p == COM_MIDDLE)
2236 what = *p++;
2237 else if (*p == COM_LEFT || *p == COM_RIGHT)
2238 align = *p++;
2239 else if (VIM_ISDIGIT(*p) || *p == '-')
2240 off = getdigits(&p);
2241 else
2242 ++p;
2243 }
2244
2245 if (*p == ':')
2246 ++p;
2247 (void)copy_option_part(&p, lead_end, COM_MAX_LEN, ",");
2248 if (what == COM_START)
2249 {
2250 STRCPY(lead_start, lead_end);
2251 lead_start_len = (int)STRLEN(lead_start);
2252 start_off = off;
2253 start_align = align;
2254 }
2255 else if (what == COM_MIDDLE)
2256 {
2257 STRCPY(lead_middle, lead_end);
2258 lead_middle_len = (int)STRLEN(lead_middle);
2259 }
2260 else if (what == COM_END)
2261 {
2262 // If our line starts with the middle comment string, line it
2263 // up with the comment opener per the 'comments' option.
2264 if (STRNCMP(theline, lead_middle, lead_middle_len) == 0
2265 && STRNCMP(theline, lead_end, STRLEN(lead_end)) != 0)
2266 {
2267 done = TRUE;
2268 if (curwin->w_cursor.lnum > 1)
2269 {
2270 // If the start comment string matches in the previous
2271 // line, use the indent of that line plus offset. If
2272 // the middle comment string matches in the previous
2273 // line, use the indent of that line. XXX
2274 look = skipwhite(ml_get(curwin->w_cursor.lnum - 1));
2275 if (STRNCMP(look, lead_start, lead_start_len) == 0)
2276 amount = get_indent_lnum(curwin->w_cursor.lnum - 1);
2277 else if (STRNCMP(look, lead_middle,
2278 lead_middle_len) == 0)
2279 {
2280 amount = get_indent_lnum(curwin->w_cursor.lnum - 1);
2281 break;
2282 }
2283 // If the start comment string doesn't match with the
2284 // start of the comment, skip this entry. XXX
zeertzjq122dea72022-07-27 15:48:45 +01002285 else if (STRNCMP(ml_get(comment_pos->lnum)
2286 + comment_pos->col,
Bram Moolenaar14c01f82019-10-09 22:53:08 +02002287 lead_start, lead_start_len) != 0)
2288 continue;
2289 }
2290 if (start_off != 0)
2291 amount += start_off;
2292 else if (start_align == COM_RIGHT)
2293 amount += vim_strsize(lead_start)
2294 - vim_strsize(lead_middle);
2295 break;
2296 }
2297
2298 // If our line starts with the end comment string, line it up
2299 // with the middle comment
2300 if (STRNCMP(theline, lead_middle, lead_middle_len) != 0
2301 && STRNCMP(theline, lead_end, STRLEN(lead_end)) == 0)
2302 {
2303 amount = get_indent_lnum(curwin->w_cursor.lnum - 1);
2304 // XXX
2305 if (off != 0)
2306 amount += off;
2307 else if (align == COM_RIGHT)
2308 amount += vim_strsize(lead_start)
2309 - vim_strsize(lead_middle);
2310 done = TRUE;
2311 break;
2312 }
2313 }
2314 }
2315
2316 // If our line starts with an asterisk, line up with the
2317 // asterisk in the comment opener; otherwise, line up
2318 // with the first character of the comment text.
2319 if (done)
2320 ;
2321 else if (theline[0] == '*')
2322 amount += 1;
2323 else
2324 {
2325 // If we are more than one line away from the comment opener, take
2326 // the indent of the previous non-empty line. If 'cino' has "CO"
2327 // and we are just below the comment opener and there are any
2328 // white characters after it line up with the text after it;
2329 // otherwise, add the amount specified by "c" in 'cino'
2330 amount = -1;
2331 for (lnum = cur_curpos.lnum - 1; lnum > comment_pos->lnum; --lnum)
2332 {
2333 if (linewhite(lnum)) // skip blank lines
2334 continue;
2335 amount = get_indent_lnum(lnum); // XXX
2336 break;
2337 }
2338 if (amount == -1) // use the comment opener
2339 {
2340 if (!curbuf->b_ind_in_comment2)
2341 {
2342 start = ml_get(comment_pos->lnum);
2343 look = start + comment_pos->col + 2; // skip / and *
2344 if (*look != NUL) // if something after it
2345 comment_pos->col = (colnr_T)(skipwhite(look) - start);
2346 }
2347 getvcol(curwin, comment_pos, &col, NULL, NULL);
2348 amount = col;
2349 if (curbuf->b_ind_in_comment2 || *look == NUL)
2350 amount += curbuf->b_ind_in_comment;
2351 }
2352 }
2353 goto theend;
2354 }
2355
2356 // Are we looking at a ']' that has a match?
2357 if (*skipwhite(theline) == ']'
2358 && (trypos = find_match_char('[', curbuf->b_ind_maxparen)) != NULL)
2359 {
2360 // align with the line containing the '['.
2361 amount = get_indent_lnum(trypos->lnum);
2362 goto theend;
2363 }
2364
2365 // Are we inside parentheses or braces? XXX
2366 if (((trypos = find_match_paren(curbuf->b_ind_maxparen)) != NULL
2367 && curbuf->b_ind_java == 0)
2368 || (tryposBrace = find_start_brace()) != NULL
2369 || trypos != NULL)
2370 {
2371 if (trypos != NULL && tryposBrace != NULL)
2372 {
2373 // Both an unmatched '(' and '{' is found. Use the one which is
2374 // closer to the current cursor position, set the other to NULL.
2375 if (trypos->lnum != tryposBrace->lnum
2376 ? trypos->lnum < tryposBrace->lnum
2377 : trypos->col < tryposBrace->col)
2378 trypos = NULL;
2379 else
2380 tryposBrace = NULL;
2381 }
2382
2383 if (trypos != NULL)
2384 {
2385 // If the matching paren is more than one line away, use the indent of
2386 // a previous non-empty line that matches the same paren.
2387 if (theline[0] == ')' && curbuf->b_ind_paren_prev)
2388 {
2389 // Line up with the start of the matching paren line.
2390 amount = get_indent_lnum(curwin->w_cursor.lnum - 1); // XXX
2391 }
2392 else
2393 {
2394 amount = -1;
2395 our_paren_pos = *trypos;
2396 for (lnum = cur_curpos.lnum - 1; lnum > our_paren_pos.lnum; --lnum)
2397 {
2398 l = skipwhite(ml_get(lnum));
2399 if (cin_nocode(l)) // skip comment lines
2400 continue;
2401 if (cin_ispreproc_cont(&l, &lnum, &amount))
2402 continue; // ignore #define, #if, etc.
2403 curwin->w_cursor.lnum = lnum;
2404
2405 // Skip a comment or raw string. XXX
2406 if ((trypos = ind_find_start_CORS(NULL)) != NULL)
2407 {
2408 lnum = trypos->lnum + 1;
2409 continue;
2410 }
2411
2412 // XXX
2413 if ((trypos = find_match_paren(
2414 corr_ind_maxparen(&cur_curpos))) != NULL
2415 && trypos->lnum == our_paren_pos.lnum
2416 && trypos->col == our_paren_pos.col)
2417 {
Yegappan Lakshmanan562610c2025-05-04 21:05:51 +02002418 amount = get_indent_lnum(lnum); // XXX
Bram Moolenaar14c01f82019-10-09 22:53:08 +02002419
Yegappan Lakshmanan562610c2025-05-04 21:05:51 +02002420 if (theline[0] == ')')
2421 {
2422 if (our_paren_pos.lnum != lnum
2423 && cur_amount > amount)
2424 cur_amount = amount;
2425 amount = -1;
2426 }
Bram Moolenaar14c01f82019-10-09 22:53:08 +02002427 break;
2428 }
2429 }
2430 }
2431
2432 // Line up with line where the matching paren is. XXX
2433 // If the line starts with a '(' or the indent for unclosed
2434 // parentheses is zero, line up with the unclosed parentheses.
2435 if (amount == -1)
2436 {
2437 int ignore_paren_col = 0;
2438 int is_if_for_while = 0;
2439
2440 if (curbuf->b_ind_if_for_while)
2441 {
2442 // Look for the outermost opening parenthesis on this line
2443 // and check whether it belongs to an "if", "for" or "while".
2444
2445 pos_T cursor_save = curwin->w_cursor;
2446 pos_T outermost;
2447 char_u *line;
2448
2449 trypos = &our_paren_pos;
Hirohito Higashia4a00a72025-05-08 22:58:31 +02002450 do
2451 {
Bram Moolenaar14c01f82019-10-09 22:53:08 +02002452 outermost = *trypos;
2453 curwin->w_cursor.lnum = outermost.lnum;
2454 curwin->w_cursor.col = outermost.col;
2455
2456 trypos = find_match_paren(curbuf->b_ind_maxparen);
2457 } while (trypos && trypos->lnum == outermost.lnum);
2458
2459 curwin->w_cursor = cursor_save;
2460
2461 line = ml_get(outermost.lnum);
2462
2463 is_if_for_while =
2464 cin_is_if_for_while_before_offset(line, &outermost.col);
2465 }
2466
2467 amount = skip_label(our_paren_pos.lnum, &look);
2468 look = skipwhite(look);
2469 if (*look == '(')
2470 {
2471 linenr_T save_lnum = curwin->w_cursor.lnum;
2472 char_u *line;
2473 int look_col;
2474
2475 // Ignore a '(' in front of the line that has a match before
2476 // our matching '('.
2477 curwin->w_cursor.lnum = our_paren_pos.lnum;
2478 line = ml_get_curline();
2479 look_col = (int)(look - line);
2480 curwin->w_cursor.col = look_col + 1;
2481 if ((trypos = findmatchlimit(NULL, ')', 0,
2482 curbuf->b_ind_maxparen))
2483 != NULL
2484 && trypos->lnum == our_paren_pos.lnum
2485 && trypos->col < our_paren_pos.col)
2486 ignore_paren_col = trypos->col + 1;
2487
2488 curwin->w_cursor.lnum = save_lnum;
2489 look = ml_get(our_paren_pos.lnum) + look_col;
2490 }
2491 if (theline[0] == ')' || (curbuf->b_ind_unclosed == 0
2492 && is_if_for_while == 0)
2493 || (!curbuf->b_ind_unclosed_noignore && *look == '('
2494 && ignore_paren_col == 0))
2495 {
2496 // If we're looking at a close paren, line up right there;
2497 // otherwise, line up with the next (non-white) character.
2498 // When b_ind_unclosed_wrapped is set and the matching paren is
2499 // the last nonwhite character of the line, use either the
2500 // indent of the current line or the indentation of the next
2501 // outer paren and add b_ind_unclosed_wrapped (for very long
2502 // lines).
2503 if (theline[0] != ')')
2504 {
2505 cur_amount = MAXCOL;
2506 l = ml_get(our_paren_pos.lnum);
2507 if (curbuf->b_ind_unclosed_wrapped
2508 && cin_ends_in(l, (char_u *)"(", NULL))
2509 {
2510 // look for opening unmatched paren, indent one level
2511 // for each additional level
2512 n = 1;
2513 for (col = 0; col < our_paren_pos.col; ++col)
2514 {
2515 switch (l[col])
2516 {
2517 case '(':
2518 case '{': ++n;
2519 break;
2520
2521 case ')':
2522 case '}': if (n > 1)
2523 --n;
2524 break;
2525 }
2526 }
2527
2528 our_paren_pos.col = 0;
2529 amount += n * curbuf->b_ind_unclosed_wrapped;
2530 }
2531 else if (curbuf->b_ind_unclosed_whiteok)
2532 our_paren_pos.col++;
2533 else
2534 {
2535 col = our_paren_pos.col + 1;
2536 while (VIM_ISWHITE(l[col]))
2537 col++;
2538 if (l[col] != NUL) // In case of trailing space
2539 our_paren_pos.col = col;
2540 else
2541 our_paren_pos.col++;
2542 }
2543 }
2544
2545 // Find how indented the paren is, or the character after it
2546 // if we did the above "if".
2547 if (our_paren_pos.col > 0)
2548 {
2549 getvcol(curwin, &our_paren_pos, &col, NULL, NULL);
2550 if (cur_amount > (int)col)
2551 cur_amount = col;
2552 }
2553 }
2554
2555 if (theline[0] == ')' && curbuf->b_ind_matching_paren)
2556 {
2557 // Line up with the start of the matching paren line.
2558 }
2559 else if ((curbuf->b_ind_unclosed == 0 && is_if_for_while == 0)
2560 || (!curbuf->b_ind_unclosed_noignore
2561 && *look == '(' && ignore_paren_col == 0))
2562 {
2563 if (cur_amount != MAXCOL)
2564 amount = cur_amount;
2565 }
2566 else
2567 {
2568 // Add b_ind_unclosed2 for each '(' before our matching one,
2569 // but ignore (void) before the line (ignore_paren_col).
2570 col = our_paren_pos.col;
2571 while ((int)our_paren_pos.col > ignore_paren_col)
2572 {
2573 --our_paren_pos.col;
2574 switch (*ml_get_pos(&our_paren_pos))
2575 {
2576 case '(': amount += curbuf->b_ind_unclosed2;
2577 col = our_paren_pos.col;
2578 break;
2579 case ')': amount -= curbuf->b_ind_unclosed2;
2580 col = MAXCOL;
2581 break;
2582 }
2583 }
2584
2585 // Use b_ind_unclosed once, when the first '(' is not inside
2586 // braces
2587 if (col == MAXCOL)
2588 amount += curbuf->b_ind_unclosed;
2589 else
2590 {
2591 curwin->w_cursor.lnum = our_paren_pos.lnum;
2592 curwin->w_cursor.col = col;
2593 if (find_match_paren_after_brace(curbuf->b_ind_maxparen)
2594 != NULL)
2595 amount += curbuf->b_ind_unclosed2;
2596 else
2597 {
2598 if (is_if_for_while)
2599 amount += curbuf->b_ind_if_for_while;
2600 else
2601 amount += curbuf->b_ind_unclosed;
2602 }
2603 }
2604 // For a line starting with ')' use the minimum of the two
2605 // positions, to avoid giving it more indent than the previous
2606 // lines:
2607 // func_long_name( if (x
2608 // arg && yy
2609 // ) ^ not here ) ^ not here
2610 if (cur_amount < amount)
2611 amount = cur_amount;
2612 }
2613 }
2614
2615 // add extra indent for a comment
2616 if (cin_iscomment(theline))
2617 amount += curbuf->b_ind_comment;
2618 }
2619 else
2620 {
2621 // We are inside braces, there is a { before this line at the position
2622 // stored in tryposBrace.
2623 // Make a copy of tryposBrace, it may point to pos_copy inside
2624 // find_start_brace(), which may be changed somewhere.
2625 tryposCopy = *tryposBrace;
2626 tryposBrace = &tryposCopy;
2627 trypos = tryposBrace;
2628 ourscope = trypos->lnum;
2629 start = ml_get(ourscope);
2630
2631 // Now figure out how indented the line is in general.
2632 // If the brace was at the start of the line, we use that;
2633 // otherwise, check out the indentation of the line as
2634 // a whole and then add the "imaginary indent" to that.
2635 look = skipwhite(start);
2636 if (*look == '{')
2637 {
2638 getvcol(curwin, trypos, &col, NULL, NULL);
2639 amount = col;
2640 if (*start == '{')
2641 start_brace = BRACE_IN_COL0;
2642 else
2643 start_brace = BRACE_AT_START;
2644 }
2645 else
2646 {
2647 // That opening brace might have been on a continuation
2648 // line. if so, find the start of the line.
2649 curwin->w_cursor.lnum = ourscope;
2650
2651 // Position the cursor over the rightmost paren, so that
2652 // matching it will take us back to the start of the line.
2653 lnum = ourscope;
2654 if (find_last_paren(start, '(', ')')
2655 && (trypos = find_match_paren(curbuf->b_ind_maxparen))
2656 != NULL)
2657 lnum = trypos->lnum;
2658
2659 // It could have been something like
2660 // case 1: if (asdf &&
Bram Moolenaarebfec1c2023-01-22 21:14:53 +00002661 // condition) {
Bram Moolenaar14c01f82019-10-09 22:53:08 +02002662 // }
2663 if ((curbuf->b_ind_js || curbuf->b_ind_keep_case_label)
2664 && cin_iscase(skipwhite(ml_get_curline()), FALSE))
2665 amount = get_indent();
2666 else if (curbuf->b_ind_js)
2667 amount = get_indent_lnum(lnum);
2668 else
2669 amount = skip_label(lnum, &l);
2670
2671 start_brace = BRACE_AT_END;
2672 }
2673
2674 // For Javascript check if the line starts with "key:".
2675 if (curbuf->b_ind_js)
2676 js_cur_has_key = cin_has_js_key(theline);
2677
2678 // If we're looking at a closing brace, that's where
2679 // we want to be. otherwise, add the amount of room
2680 // that an indent is supposed to be.
2681 if (theline[0] == '}')
2682 {
2683 // they may want closing braces to line up with something
2684 // other than the open brace. indulge them, if so.
2685 amount += curbuf->b_ind_close_extra;
2686 }
2687 else
2688 {
2689 // If we're looking at an "else", try to find an "if"
2690 // to match it with.
2691 // If we're looking at a "while", try to find a "do"
2692 // to match it with.
2693 lookfor = LOOKFOR_INITIAL;
2694 if (cin_iselse(theline))
2695 lookfor = LOOKFOR_IF;
2696 else if (cin_iswhileofdo(theline, cur_curpos.lnum)) // XXX
2697 lookfor = LOOKFOR_DO;
2698 if (lookfor != LOOKFOR_INITIAL)
2699 {
2700 curwin->w_cursor.lnum = cur_curpos.lnum;
2701 if (find_match(lookfor, ourscope) == OK)
2702 {
2703 amount = get_indent(); // XXX
2704 goto theend;
2705 }
2706 }
2707
2708 // We get here if we are not on an "while-of-do" or "else" (or
2709 // failed to find a matching "if").
2710 // Search backwards for something to line up with.
2711 // First set amount for when we don't find anything.
2712
2713 // if the '{' is _really_ at the left margin, use the imaginary
2714 // location of a left-margin brace. Otherwise, correct the
2715 // location for b_ind_open_extra.
2716
2717 if (start_brace == BRACE_IN_COL0) // '{' is in column 0
2718 {
2719 amount = curbuf->b_ind_open_left_imag;
2720 lookfor_cpp_namespace = TRUE;
2721 }
2722 else if (start_brace == BRACE_AT_START &&
2723 lookfor_cpp_namespace) // '{' is at start
2724 {
2725
2726 lookfor_cpp_namespace = TRUE;
2727 }
2728 else
2729 {
2730 if (start_brace == BRACE_AT_END) // '{' is at end of line
2731 {
2732 amount += curbuf->b_ind_open_imag;
2733
2734 l = skipwhite(ml_get_curline());
2735 if (cin_is_cpp_namespace(l))
2736 amount += curbuf->b_ind_cpp_namespace;
2737 else if (cin_is_cpp_extern_c(l))
2738 amount += curbuf->b_ind_cpp_extern_c;
2739 }
2740 else
2741 {
2742 // Compensate for adding b_ind_open_extra later.
2743 amount -= curbuf->b_ind_open_extra;
2744 if (amount < 0)
2745 amount = 0;
2746 }
2747 }
2748
2749 lookfor_break = FALSE;
2750
2751 if (cin_iscase(theline, FALSE)) // it's a switch() label
2752 {
2753 lookfor = LOOKFOR_CASE; // find a previous switch() label
2754 amount += curbuf->b_ind_case;
2755 }
2756 else if (cin_isscopedecl(theline)) // private:, ...
2757 {
2758 lookfor = LOOKFOR_SCOPEDECL; // class decl is this block
2759 amount += curbuf->b_ind_scopedecl;
2760 }
2761 else
2762 {
2763 if (curbuf->b_ind_case_break && cin_isbreak(theline))
2764 // break; ...
2765 lookfor_break = TRUE;
2766
2767 lookfor = LOOKFOR_INITIAL;
2768 // b_ind_level from start of block
2769 amount += curbuf->b_ind_level;
2770 }
2771 scope_amount = amount;
2772 whilelevel = 0;
2773
2774 // Search backwards. If we find something we recognize, line up
2775 // with that.
2776 //
2777 // If we're looking at an open brace, indent
2778 // the usual amount relative to the conditional
2779 // that opens the block.
2780 curwin->w_cursor = cur_curpos;
2781 for (;;)
2782 {
2783 curwin->w_cursor.lnum--;
2784 curwin->w_cursor.col = 0;
2785
2786 // If we went all the way back to the start of our scope, line
2787 // up with it.
2788 if (curwin->w_cursor.lnum <= ourscope)
2789 {
2790 // We reached end of scope:
Bram Moolenaar32aa1022019-11-02 22:54:41 +01002791 // If looking for an enum or structure initialization
Bram Moolenaar14c01f82019-10-09 22:53:08 +02002792 // go further back:
2793 // If it is an initializer (enum xxx or xxx =), then
2794 // don't add ind_continuation, otherwise it is a variable
2795 // declaration:
2796 // int x,
2797 // here; <-- add ind_continuation
2798 if (lookfor == LOOKFOR_ENUM_OR_INIT)
2799 {
2800 if (curwin->w_cursor.lnum == 0
2801 || curwin->w_cursor.lnum
2802 < ourscope - curbuf->b_ind_maxparen)
2803 {
2804 // nothing found (abuse curbuf->b_ind_maxparen as
2805 // limit) assume terminated line (i.e. a variable
2806 // initialization)
2807 if (cont_amount > 0)
2808 amount = cont_amount;
2809 else if (!curbuf->b_ind_js)
2810 amount += ind_continuation;
2811 break;
2812 }
2813
Bram Moolenaar14c01f82019-10-09 22:53:08 +02002814 // If we're in a comment or raw string now, skip to
2815 // the start of it.
2816 trypos = ind_find_start_CORS(NULL);
2817 if (trypos != NULL)
2818 {
2819 curwin->w_cursor.lnum = trypos->lnum + 1;
2820 curwin->w_cursor.col = 0;
2821 continue;
2822 }
2823
Bram Moolenaarfa4873c2022-06-30 22:13:59 +01002824 l = ml_get_curline();
2825
Bram Moolenaar14c01f82019-10-09 22:53:08 +02002826 // Skip preprocessor directives and blank lines.
2827 if (cin_ispreproc_cont(&l, &curwin->w_cursor.lnum,
2828 &amount))
2829 continue;
2830
2831 if (cin_nocode(l))
2832 continue;
2833
2834 terminated = cin_isterminated(l, FALSE, TRUE);
2835
2836 // If we are at top level and the line looks like a
2837 // function declaration, we are done
2838 // (it's a variable declaration).
2839 if (start_brace != BRACE_IN_COL0
2840 || !cin_isfuncdecl(&l, curwin->w_cursor.lnum, 0))
2841 {
2842 // if the line is terminated with another ','
2843 // it is a continued variable initialization.
2844 // don't add extra indent.
2845 // TODO: does not work, if a function
2846 // declaration is split over multiple lines:
2847 // cin_isfuncdecl returns FALSE then.
2848 if (terminated == ',')
2849 break;
2850
Bram Moolenaar32aa1022019-11-02 22:54:41 +01002851 // if it is an enum declaration or an assignment,
Bram Moolenaar14c01f82019-10-09 22:53:08 +02002852 // we are done.
2853 if (terminated != ';' && cin_isinit())
2854 break;
2855
2856 // nothing useful found
2857 if (terminated == 0 || terminated == '{')
2858 continue;
2859 }
2860
2861 if (terminated != ';')
2862 {
2863 // Skip parens and braces. Position the cursor
2864 // over the rightmost paren, so that matching it
2865 // will take us back to the start of the line.
2866 // XXX
2867 trypos = NULL;
2868 if (find_last_paren(l, '(', ')'))
2869 trypos = find_match_paren(
2870 curbuf->b_ind_maxparen);
2871
2872 if (trypos == NULL && find_last_paren(l, '{', '}'))
2873 trypos = find_start_brace();
2874
2875 if (trypos != NULL)
2876 {
2877 curwin->w_cursor.lnum = trypos->lnum + 1;
2878 curwin->w_cursor.col = 0;
2879 continue;
2880 }
2881 }
2882
2883 // it's a variable declaration, add indentation
2884 // like in
2885 // int a,
2886 // b;
2887 if (cont_amount > 0)
2888 amount = cont_amount;
2889 else
2890 amount += ind_continuation;
2891 }
2892 else if (lookfor == LOOKFOR_UNTERM)
2893 {
2894 if (cont_amount > 0)
2895 amount = cont_amount;
2896 else
2897 amount += ind_continuation;
2898 }
2899 else
2900 {
2901 if (lookfor != LOOKFOR_TERM
2902 && lookfor != LOOKFOR_CPP_BASECLASS
2903 && lookfor != LOOKFOR_COMMA)
2904 {
2905 amount = scope_amount;
2906 if (theline[0] == '{')
2907 {
2908 amount += curbuf->b_ind_open_extra;
2909 added_to_amount = curbuf->b_ind_open_extra;
2910 }
2911 }
2912
2913 if (lookfor_cpp_namespace)
2914 {
2915 // Looking for C++ namespace, need to look further
2916 // back.
2917 if (curwin->w_cursor.lnum == ourscope)
2918 continue;
2919
2920 if (curwin->w_cursor.lnum == 0
2921 || curwin->w_cursor.lnum
2922 < ourscope - FIND_NAMESPACE_LIM)
2923 break;
2924
Bram Moolenaar14c01f82019-10-09 22:53:08 +02002925 // If we're in a comment or raw string now, skip
2926 // to the start of it.
2927 trypos = ind_find_start_CORS(NULL);
2928 if (trypos != NULL)
2929 {
2930 curwin->w_cursor.lnum = trypos->lnum + 1;
2931 curwin->w_cursor.col = 0;
2932 continue;
2933 }
2934
Bram Moolenaarfa4873c2022-06-30 22:13:59 +01002935 l = ml_get_curline();
2936
Bram Moolenaar14c01f82019-10-09 22:53:08 +02002937 // Skip preprocessor directives and blank lines.
2938 if (cin_ispreproc_cont(&l, &curwin->w_cursor.lnum,
2939 &amount))
2940 continue;
2941
2942 // Finally the actual check for "namespace".
2943 if (cin_is_cpp_namespace(l))
2944 {
2945 amount += curbuf->b_ind_cpp_namespace
2946 - added_to_amount;
2947 break;
2948 }
2949 else if (cin_is_cpp_extern_c(l))
2950 {
2951 amount += curbuf->b_ind_cpp_extern_c
2952 - added_to_amount;
2953 break;
2954 }
2955
2956 if (cin_nocode(l))
2957 continue;
2958 }
2959 }
2960 break;
2961 }
2962
2963 // If we're in a comment or raw string now, skip to the start
2964 // of it. XXX
2965 if ((trypos = ind_find_start_CORS(&raw_string_start)) != NULL)
2966 {
2967 curwin->w_cursor.lnum = trypos->lnum + 1;
2968 curwin->w_cursor.col = 0;
2969 continue;
2970 }
2971
2972 l = ml_get_curline();
2973
2974 // If this is a switch() label, may line up relative to that.
2975 // If this is a C++ scope declaration, do the same.
2976 iscase = cin_iscase(l, FALSE);
2977 if (iscase || cin_isscopedecl(l))
2978 {
2979 // we are only looking for cpp base class
2980 // declaration/initialization any longer
2981 if (lookfor == LOOKFOR_CPP_BASECLASS)
2982 break;
2983
2984 // When looking for a "do" we are not interested in
2985 // labels.
2986 if (whilelevel > 0)
2987 continue;
2988
2989 // case xx:
2990 // c = 99 + <- this indent plus continuation
2991 //-> here;
2992 if (lookfor == LOOKFOR_UNTERM
2993 || lookfor == LOOKFOR_ENUM_OR_INIT)
2994 {
2995 if (cont_amount > 0)
2996 amount = cont_amount;
2997 else
2998 amount += ind_continuation;
2999 break;
3000 }
3001
3002 // case xx: <- line up with this case
3003 // x = 333;
3004 // case yy:
3005 if ( (iscase && lookfor == LOOKFOR_CASE)
3006 || (iscase && lookfor_break)
3007 || (!iscase && lookfor == LOOKFOR_SCOPEDECL))
3008 {
3009 // Check that this case label is not for another
3010 // switch() XXX
3011 if ((trypos = find_start_brace()) == NULL
3012 || trypos->lnum == ourscope)
3013 {
3014 amount = get_indent(); // XXX
3015 break;
3016 }
3017 continue;
3018 }
3019
3020 n = get_indent_nolabel(curwin->w_cursor.lnum); // XXX
3021
3022 // case xx: if (cond) <- line up with this if
3023 // y = y + 1;
3024 // -> s = 99;
3025 //
3026 // case xx:
3027 // if (cond) <- line up with this line
3028 // y = y + 1;
3029 // -> s = 99;
3030 if (lookfor == LOOKFOR_TERM)
3031 {
3032 if (n)
3033 amount = n;
3034
3035 if (!lookfor_break)
3036 break;
3037 }
3038
3039 // case xx: x = x + 1; <- line up with this x
3040 // -> y = y + 1;
3041 //
3042 // case xx: if (cond) <- line up with this if
3043 // -> y = y + 1;
3044 if (n)
3045 {
3046 amount = n;
3047 l = after_label(ml_get_curline());
3048 if (l != NULL && cin_is_cinword(l))
3049 {
3050 if (theline[0] == '{')
3051 amount += curbuf->b_ind_open_extra;
3052 else
3053 amount += curbuf->b_ind_level
3054 + curbuf->b_ind_no_brace;
3055 }
3056 break;
3057 }
3058
3059 // Try to get the indent of a statement before the switch
3060 // label. If nothing is found, line up relative to the
3061 // switch label.
3062 // break; <- may line up with this line
3063 // case xx:
3064 // -> y = 1;
3065 scope_amount = get_indent() + (iscase // XXX
3066 ? curbuf->b_ind_case_code
3067 : curbuf->b_ind_scopedecl_code);
3068 lookfor = curbuf->b_ind_case_break
3069 ? LOOKFOR_NOBREAK : LOOKFOR_ANY;
3070 continue;
3071 }
3072
3073 // Looking for a switch() label or C++ scope declaration,
3074 // ignore other lines, skip {}-blocks.
3075 if (lookfor == LOOKFOR_CASE || lookfor == LOOKFOR_SCOPEDECL)
3076 {
3077 if (find_last_paren(l, '{', '}')
3078 && (trypos = find_start_brace()) != NULL)
3079 {
3080 curwin->w_cursor.lnum = trypos->lnum + 1;
3081 curwin->w_cursor.col = 0;
3082 }
3083 continue;
3084 }
3085
3086 // Ignore jump labels with nothing after them.
3087 if (!curbuf->b_ind_js && cin_islabel())
3088 {
3089 l = after_label(ml_get_curline());
3090 if (l == NULL || cin_nocode(l))
3091 continue;
3092 }
3093
3094 // Ignore #defines, #if, etc.
3095 // Ignore comment and empty lines.
3096 // (need to get the line again, cin_islabel() may have
3097 // unlocked it)
3098 l = ml_get_curline();
3099 if (cin_ispreproc_cont(&l, &curwin->w_cursor.lnum, &amount)
3100 || cin_nocode(l))
3101 continue;
3102
3103 // Are we at the start of a cpp base class declaration or
3104 // constructor initialization? XXX
3105 n = FALSE;
3106 if (lookfor != LOOKFOR_TERM && curbuf->b_ind_cpp_baseclass > 0)
3107 {
3108 n = cin_is_cpp_baseclass(&cache_cpp_baseclass);
3109 l = ml_get_curline();
3110 }
3111 if (n)
3112 {
3113 if (lookfor == LOOKFOR_UNTERM)
3114 {
3115 if (cont_amount > 0)
3116 amount = cont_amount;
3117 else
3118 amount += ind_continuation;
3119 }
3120 else if (theline[0] == '{')
3121 {
3122 // Need to find start of the declaration.
3123 lookfor = LOOKFOR_UNTERM;
3124 ind_continuation = 0;
3125 continue;
3126 }
3127 else
3128 // XXX
3129 amount = get_baseclass_amount(
3130 cache_cpp_baseclass.lpos.col);
3131 break;
3132 }
3133 else if (lookfor == LOOKFOR_CPP_BASECLASS)
3134 {
3135 // only look, whether there is a cpp base class
3136 // declaration or initialization before the opening brace.
3137 if (cin_isterminated(l, TRUE, FALSE))
3138 break;
3139 else
3140 continue;
3141 }
3142
3143 // What happens next depends on the line being terminated.
3144 // If terminated with a ',' only consider it terminating if
3145 // there is another unterminated statement behind, eg:
3146 // 123,
3147 // sizeof
3148 // here
Bram Moolenaar32aa1022019-11-02 22:54:41 +01003149 // Otherwise check whether it is an enumeration or structure
Bram Moolenaar14c01f82019-10-09 22:53:08 +02003150 // initialisation (not indented) or a variable declaration
3151 // (indented).
3152 terminated = cin_isterminated(l, FALSE, TRUE);
3153
3154 if (js_cur_has_key)
3155 {
3156 js_cur_has_key = 0; // only check the first line
3157 if (curbuf->b_ind_js && terminated == ',')
3158 {
3159 // For Javascript we might be inside an object:
3160 // key: something, <- align with this
3161 // key: something
3162 // or:
3163 // key: something + <- align with this
3164 // something,
3165 // key: something
3166 lookfor = LOOKFOR_JS_KEY;
3167 }
3168 }
3169 if (lookfor == LOOKFOR_JS_KEY && cin_has_js_key(l))
3170 {
3171 amount = get_indent();
3172 break;
3173 }
3174 if (lookfor == LOOKFOR_COMMA)
3175 {
3176 if (tryposBrace != NULL && tryposBrace->lnum
3177 >= curwin->w_cursor.lnum)
3178 break;
3179 if (terminated == ',')
3180 // line below current line is the one that starts a
3181 // (possibly broken) line ending in a comma.
3182 break;
3183 else
3184 {
3185 amount = get_indent();
3186 if (curwin->w_cursor.lnum - 1 == ourscope)
3187 // line above is start of the scope, thus current
3188 // line is the one that stars a (possibly broken)
3189 // line ending in a comma.
3190 break;
3191 }
3192 }
3193
3194 if (terminated == 0 || (lookfor != LOOKFOR_UNTERM
3195 && terminated == ','))
3196 {
3197 if (lookfor != LOOKFOR_ENUM_OR_INIT &&
3198 (*skipwhite(l) == '[' || l[STRLEN(l) - 1] == '['))
3199 amount += ind_continuation;
3200 // if we're in the middle of a paren thing,
3201 // go back to the line that starts it so
3202 // we can get the right prevailing indent
3203 // if ( foo &&
3204 // bar )
3205
3206 // Position the cursor over the rightmost paren, so that
3207 // matching it will take us back to the start of the line.
3208 // Ignore a match before the start of the block.
3209 (void)find_last_paren(l, '(', ')');
3210 trypos = find_match_paren(corr_ind_maxparen(&cur_curpos));
3211 if (trypos != NULL && (trypos->lnum < tryposBrace->lnum
3212 || (trypos->lnum == tryposBrace->lnum
3213 && trypos->col < tryposBrace->col)))
3214 trypos = NULL;
3215
Bram Moolenaarfa4873c2022-06-30 22:13:59 +01003216 l = ml_get_curline();
3217
Bram Moolenaar14c01f82019-10-09 22:53:08 +02003218 // If we are looking for ',', we also look for matching
3219 // braces.
Bram Moolenaarfa4873c2022-06-30 22:13:59 +01003220 if (trypos == NULL && terminated == ',')
3221 {
3222 if (find_last_paren(l, '{', '}'))
3223 trypos = find_start_brace();
3224 l = ml_get_curline();
3225 }
Bram Moolenaar14c01f82019-10-09 22:53:08 +02003226
3227 if (trypos != NULL)
3228 {
3229 // Check if we are on a case label now. This is
3230 // handled above.
3231 // case xx: if ( asdf &&
3232 // asdf)
3233 curwin->w_cursor = *trypos;
3234 l = ml_get_curline();
3235 if (cin_iscase(l, FALSE) || cin_isscopedecl(l))
3236 {
3237 ++curwin->w_cursor.lnum;
3238 curwin->w_cursor.col = 0;
3239 continue;
3240 }
3241 }
3242
3243 // Skip over continuation lines to find the one to get the
3244 // indent from
3245 // char *usethis = "bla{backslash}
3246 // bla",
3247 // here;
3248 if (terminated == ',')
3249 {
3250 while (curwin->w_cursor.lnum > 1)
3251 {
3252 l = ml_get(curwin->w_cursor.lnum - 1);
3253 if (*l == NUL || l[STRLEN(l) - 1] != '\\')
3254 break;
3255 --curwin->w_cursor.lnum;
3256 curwin->w_cursor.col = 0;
3257 }
Bram Moolenaarfa4873c2022-06-30 22:13:59 +01003258 l = ml_get_curline();
Bram Moolenaar14c01f82019-10-09 22:53:08 +02003259 }
3260
3261 // Get indent and pointer to text for current line,
3262 // ignoring any jump label. XXX
3263 if (curbuf->b_ind_js)
3264 cur_amount = get_indent();
3265 else
3266 cur_amount = skip_label(curwin->w_cursor.lnum, &l);
3267 // If this is just above the line we are indenting, and it
3268 // starts with a '{', line it up with this line.
3269 // while (not)
3270 // -> {
3271 // }
3272 if (terminated != ',' && lookfor != LOOKFOR_TERM
3273 && theline[0] == '{')
3274 {
3275 amount = cur_amount;
3276 // Only add b_ind_open_extra when the current line
3277 // doesn't start with a '{', which must have a match
3278 // in the same line (scope is the same). Probably:
3279 // { 1, 2 },
3280 // -> { 3, 4 }
3281 if (*skipwhite(l) != '{')
3282 amount += curbuf->b_ind_open_extra;
3283
3284 if (curbuf->b_ind_cpp_baseclass && !curbuf->b_ind_js)
3285 {
3286 // have to look back, whether it is a cpp base
3287 // class declaration or initialization
3288 lookfor = LOOKFOR_CPP_BASECLASS;
3289 continue;
3290 }
3291 break;
3292 }
3293
3294 // Check if we are after an "if", "while", etc.
Bram Moolenaarebfec1c2023-01-22 21:14:53 +00003295 // Also allow "} else".
Bram Moolenaar14c01f82019-10-09 22:53:08 +02003296 if (cin_is_cinword(l) || cin_iselse(skipwhite(l)))
3297 {
3298 // Found an unterminated line after an if (), line up
3299 // with the last one.
3300 // if (cond)
3301 // 100 +
3302 // -> here;
3303 if (lookfor == LOOKFOR_UNTERM
3304 || lookfor == LOOKFOR_ENUM_OR_INIT)
3305 {
3306 if (cont_amount > 0)
3307 amount = cont_amount;
3308 else
3309 amount += ind_continuation;
3310 break;
3311 }
3312
3313 // If this is just above the line we are indenting, we
3314 // are finished.
3315 // while (not)
3316 // -> here;
3317 // Otherwise this indent can be used when the line
3318 // before this is terminated.
3319 // yyy;
3320 // if (stat)
3321 // while (not)
3322 // xxx;
3323 // -> here;
3324 amount = cur_amount;
3325 if (theline[0] == '{')
3326 amount += curbuf->b_ind_open_extra;
3327 if (lookfor != LOOKFOR_TERM)
3328 {
3329 amount += curbuf->b_ind_level
3330 + curbuf->b_ind_no_brace;
3331 break;
3332 }
3333
3334 // Special trick: when expecting the while () after a
Bram Moolenaarc9471b12023-05-09 15:00:00 +01003335 // do, line up with the while ()
Bram Moolenaar14c01f82019-10-09 22:53:08 +02003336 // do
3337 // x = 1;
3338 // -> here
3339 l = skipwhite(ml_get_curline());
3340 if (cin_isdo(l))
3341 {
3342 if (whilelevel == 0)
3343 break;
3344 --whilelevel;
3345 }
3346
3347 // When searching for a terminated line, don't use the
3348 // one between the "if" and the matching "else".
3349 // Need to use the scope of this "else". XXX
3350 // If whilelevel != 0 continue looking for a "do {".
3351 if (cin_iselse(l) && whilelevel == 0)
3352 {
3353 // If we're looking at "} else", let's make sure we
3354 // find the opening brace of the enclosing scope,
Bram Moolenaarebfec1c2023-01-22 21:14:53 +00003355 // not the one from "if (condition) {".
Bram Moolenaar14c01f82019-10-09 22:53:08 +02003356 if (*l == '}')
3357 curwin->w_cursor.col =
3358 (colnr_T)(l - ml_get_curline()) + 1;
3359
3360 if ((trypos = find_start_brace()) == NULL
3361 || find_match(LOOKFOR_IF, trypos->lnum)
3362 == FAIL)
3363 break;
3364 }
3365 }
3366
3367 // If we're below an unterminated line that is not an
3368 // "if" or something, we may line up with this line or
3369 // add something for a continuation line, depending on
3370 // the line before this one.
3371 else
3372 {
3373 // Found two unterminated lines on a row, line up with
3374 // the last one.
3375 // c = 99 +
3376 // 100 +
3377 // -> here;
3378 if (lookfor == LOOKFOR_UNTERM)
3379 {
3380 // When line ends in a comma add extra indent
3381 if (terminated == ',')
3382 amount += ind_continuation;
3383 break;
3384 }
3385
3386 if (lookfor == LOOKFOR_ENUM_OR_INIT)
3387 {
3388 // Found two lines ending in ',', lineup with the
3389 // lowest one, but check for cpp base class
3390 // declaration/initialization, if it is an
3391 // opening brace or we are looking just for
3392 // enumerations/initializations.
3393 if (terminated == ',')
3394 {
3395 if (curbuf->b_ind_cpp_baseclass == 0)
3396 break;
3397
3398 lookfor = LOOKFOR_CPP_BASECLASS;
3399 continue;
3400 }
3401
3402 // Ignore unterminated lines in between, but
3403 // reduce indent.
3404 if (amount > cur_amount)
3405 amount = cur_amount;
3406 }
3407 else
3408 {
3409 // Found first unterminated line on a row, may
3410 // line up with this line, remember its indent
3411 // 100 +
3412 // -> here;
3413 l = ml_get_curline();
3414 amount = cur_amount;
3415
3416 n = (int)STRLEN(l);
3417 if (terminated == ',' && (*skipwhite(l) == ']'
3418 || (n >=2 && l[n - 2] == ']')))
3419 break;
3420
3421 // If previous line ends in ',', check whether we
3422 // are in an initialization or enum
3423 // struct xxx =
3424 // {
3425 // sizeof a,
3426 // 124 };
3427 // or a normal possible continuation line.
3428 // but only, of no other statement has been found
3429 // yet.
3430 if (lookfor == LOOKFOR_INITIAL && terminated == ',')
3431 {
3432 if (curbuf->b_ind_js)
3433 {
3434 // Search for a line ending in a comma
3435 // and line up with the line below it
3436 // (could be the current line).
3437 // some = [
3438 // 1, <- line up here
3439 // 2,
3440 // some = [
3441 // 3 + <- line up here
3442 // 4 *
3443 // 5,
3444 // 6,
3445 if (cin_iscomment(skipwhite(l)))
3446 break;
3447 lookfor = LOOKFOR_COMMA;
3448 trypos = find_match_char('[',
3449 curbuf->b_ind_maxparen);
3450 if (trypos != NULL)
3451 {
3452 if (trypos->lnum
3453 == curwin->w_cursor.lnum - 1)
3454 {
3455 // Current line is first inside
3456 // [], line up with it.
3457 break;
3458 }
3459 ourscope = trypos->lnum;
3460 }
3461 }
3462 else
3463 {
3464 lookfor = LOOKFOR_ENUM_OR_INIT;
3465 cont_amount = cin_first_id_amount();
3466 }
3467 }
3468 else
3469 {
3470 if (lookfor == LOOKFOR_INITIAL
3471 && *l != NUL
3472 && l[STRLEN(l) - 1] == '\\')
3473 // XXX
3474 cont_amount = cin_get_equal_amount(
3475 curwin->w_cursor.lnum);
3476 if (lookfor != LOOKFOR_TERM
3477 && lookfor != LOOKFOR_JS_KEY
3478 && lookfor != LOOKFOR_COMMA
3479 && raw_string_start != curwin->w_cursor.lnum)
3480 lookfor = LOOKFOR_UNTERM;
3481 }
3482 }
3483 }
3484 }
3485
3486 // Check if we are after a while (cond);
3487 // If so: Ignore until the matching "do".
3488 else if (cin_iswhileofdo_end(terminated)) // XXX
3489 {
3490 // Found an unterminated line after a while ();, line up
3491 // with the last one.
3492 // while (cond);
3493 // 100 + <- line up with this one
3494 // -> here;
3495 if (lookfor == LOOKFOR_UNTERM
3496 || lookfor == LOOKFOR_ENUM_OR_INIT)
3497 {
3498 if (cont_amount > 0)
3499 amount = cont_amount;
3500 else
3501 amount += ind_continuation;
3502 break;
3503 }
3504
3505 if (whilelevel == 0)
3506 {
3507 lookfor = LOOKFOR_TERM;
3508 amount = get_indent(); // XXX
3509 if (theline[0] == '{')
3510 amount += curbuf->b_ind_open_extra;
3511 }
3512 ++whilelevel;
3513 }
3514
3515 // We are after a "normal" statement.
3516 // If we had another statement we can stop now and use the
3517 // indent of that other statement.
3518 // Otherwise the indent of the current statement may be used,
3519 // search backwards for the next "normal" statement.
3520 else
3521 {
3522 // Skip single break line, if before a switch label. It
3523 // may be lined up with the case label.
3524 if (lookfor == LOOKFOR_NOBREAK
3525 && cin_isbreak(skipwhite(ml_get_curline())))
3526 {
3527 lookfor = LOOKFOR_ANY;
3528 continue;
3529 }
3530
3531 // Handle "do {" line.
3532 if (whilelevel > 0)
3533 {
3534 l = cin_skipcomment(ml_get_curline());
3535 if (cin_isdo(l))
3536 {
3537 amount = get_indent(); // XXX
3538 --whilelevel;
3539 continue;
3540 }
3541 }
3542
3543 // Found a terminated line above an unterminated line. Add
3544 // the amount for a continuation line.
3545 // x = 1;
3546 // y = foo +
3547 // -> here;
3548 // or
3549 // int x = 1;
3550 // int foo,
3551 // -> here;
3552 if (lookfor == LOOKFOR_UNTERM
3553 || lookfor == LOOKFOR_ENUM_OR_INIT)
3554 {
3555 if (cont_amount > 0)
3556 amount = cont_amount;
3557 else
3558 amount += ind_continuation;
3559 break;
3560 }
3561
3562 // Found a terminated line above a terminated line or "if"
3563 // etc. line. Use the amount of the line below us.
3564 // x = 1; x = 1;
3565 // if (asdf) y = 2;
3566 // while (asdf) ->here;
3567 // here;
3568 // ->foo;
3569 if (lookfor == LOOKFOR_TERM)
3570 {
3571 if (!lookfor_break && whilelevel == 0)
3572 break;
3573 }
3574
3575 // First line above the one we're indenting is terminated.
3576 // To know what needs to be done look further backward for
3577 // a terminated line.
3578 else
3579 {
3580 // position the cursor over the rightmost paren, so
3581 // that matching it will take us back to the start of
3582 // the line. Helps for:
3583 // func(asdr,
3584 // asdfasdf);
3585 // here;
3586term_again:
3587 l = ml_get_curline();
3588 if (find_last_paren(l, '(', ')')
3589 && (trypos = find_match_paren(
3590 curbuf->b_ind_maxparen)) != NULL)
3591 {
3592 // Check if we are on a case label now. This is
3593 // handled above.
3594 // case xx: if ( asdf &&
3595 // asdf)
3596 curwin->w_cursor = *trypos;
3597 l = ml_get_curline();
3598 if (cin_iscase(l, FALSE) || cin_isscopedecl(l))
3599 {
3600 ++curwin->w_cursor.lnum;
3601 curwin->w_cursor.col = 0;
3602 continue;
3603 }
3604 }
3605
3606 // When aligning with the case statement, don't align
3607 // with a statement after it.
3608 // case 1: { <-- don't use this { position
3609 // stat;
3610 // }
3611 // case 2:
3612 // stat;
3613 // }
3614 iscase = (curbuf->b_ind_keep_case_label
3615 && cin_iscase(l, FALSE));
3616
3617 // Get indent and pointer to text for current line,
3618 // ignoring any jump label.
3619 amount = skip_label(curwin->w_cursor.lnum, &l);
3620
3621 if (theline[0] == '{')
3622 amount += curbuf->b_ind_open_extra;
3623 // See remark above: "Only add b_ind_open_extra.."
3624 l = skipwhite(l);
3625 if (*l == '{')
3626 amount -= curbuf->b_ind_open_extra;
3627 lookfor = iscase ? LOOKFOR_ANY : LOOKFOR_TERM;
3628
3629 // When a terminated line starts with "else" skip to
3630 // the matching "if":
3631 // else 3;
3632 // indent this;
3633 // Need to use the scope of this "else". XXX
3634 // If whilelevel != 0 continue looking for a "do {".
3635 if (lookfor == LOOKFOR_TERM
3636 && *l != '}'
3637 && cin_iselse(l)
3638 && whilelevel == 0)
3639 {
3640 if ((trypos = find_start_brace()) == NULL
3641 || find_match(LOOKFOR_IF, trypos->lnum)
3642 == FAIL)
3643 break;
3644 continue;
3645 }
3646
3647 // If we're at the end of a block, skip to the start of
3648 // that block.
3649 l = ml_get_curline();
3650 if (find_last_paren(l, '{', '}') // XXX
3651 && (trypos = find_start_brace()) != NULL)
3652 {
3653 curwin->w_cursor = *trypos;
3654 // if not "else {" check for terminated again
3655 // but skip block for "} else {"
3656 l = cin_skipcomment(ml_get_curline());
3657 if (*l == '}' || !cin_iselse(l))
3658 goto term_again;
3659 ++curwin->w_cursor.lnum;
3660 curwin->w_cursor.col = 0;
3661 }
3662 }
3663 }
3664 }
3665 }
3666 }
3667
3668 // add extra indent for a comment
3669 if (cin_iscomment(theline))
3670 amount += curbuf->b_ind_comment;
3671
3672 // subtract extra left-shift for jump labels
3673 if (curbuf->b_ind_jump_label > 0 && original_line_islabel)
3674 amount -= curbuf->b_ind_jump_label;
3675
3676 goto theend;
3677 }
3678
3679 // ok -- we're not inside any sort of structure at all!
3680 //
3681 // This means we're at the top level, and everything should
3682 // basically just match where the previous line is, except
3683 // for the lines immediately following a function declaration,
3684 // which are K&R-style parameters and need to be indented.
3685 //
3686 // if our line starts with an open brace, forget about any
3687 // prevailing indent and make sure it looks like the start
3688 // of a function
3689
3690 if (theline[0] == '{')
3691 {
3692 amount = curbuf->b_ind_first_open;
3693 goto theend;
3694 }
3695
3696 // If the NEXT line is a function declaration, the current
3697 // line needs to be indented as a function type spec.
3698 // Don't do this if the current line looks like a comment or if the
3699 // current line is terminated, ie. ends in ';', or if the current line
Bram Moolenaarebfec1c2023-01-22 21:14:53 +00003700 // contains { or }: "void f(condition) {\n if (1)"
Bram Moolenaar14c01f82019-10-09 22:53:08 +02003701 if (cur_curpos.lnum < curbuf->b_ml.ml_line_count
3702 && !cin_nocode(theline)
3703 && vim_strchr(theline, '{') == NULL
3704 && vim_strchr(theline, '}') == NULL
3705 && !cin_ends_in(theline, (char_u *)":", NULL)
3706 && !cin_ends_in(theline, (char_u *)",", NULL)
3707 && cin_isfuncdecl(NULL, cur_curpos.lnum + 1,
3708 cur_curpos.lnum + 1)
3709 && !cin_isterminated(theline, FALSE, TRUE))
3710 {
3711 amount = curbuf->b_ind_func_type;
3712 goto theend;
3713 }
3714
3715 // search backwards until we find something we recognize
3716 amount = 0;
3717 curwin->w_cursor = cur_curpos;
3718 while (curwin->w_cursor.lnum > 1)
3719 {
3720 curwin->w_cursor.lnum--;
3721 curwin->w_cursor.col = 0;
3722
3723 l = ml_get_curline();
3724
3725 // If we're in a comment or raw string now, skip to the start
3726 // of it. XXX
3727 if ((trypos = ind_find_start_CORS(NULL)) != NULL)
3728 {
3729 curwin->w_cursor.lnum = trypos->lnum + 1;
3730 curwin->w_cursor.col = 0;
3731 continue;
3732 }
3733
3734 // Are we at the start of a cpp base class declaration or
3735 // constructor initialization? XXX
3736 n = FALSE;
zeertzjq122dea72022-07-27 15:48:45 +01003737 if (curbuf->b_ind_cpp_baseclass != 0)
Bram Moolenaar14c01f82019-10-09 22:53:08 +02003738 {
3739 n = cin_is_cpp_baseclass(&cache_cpp_baseclass);
3740 l = ml_get_curline();
3741 }
3742 if (n)
3743 {
3744 // XXX
3745 amount = get_baseclass_amount(cache_cpp_baseclass.lpos.col);
3746 break;
3747 }
3748
3749 // Skip preprocessor directives and blank lines.
3750 if (cin_ispreproc_cont(&l, &curwin->w_cursor.lnum, &amount))
3751 continue;
3752
3753 if (cin_nocode(l))
3754 continue;
3755
3756 // If the previous line ends in ',', use one level of
3757 // indentation:
3758 // int foo,
3759 // bar;
3760 // do this before checking for '}' in case of eg.
3761 // enum foobar
3762 // {
3763 // ...
3764 // } foo,
3765 // bar;
3766 n = 0;
3767 if (cin_ends_in(l, (char_u *)",", NULL)
3768 || (*l != NUL && (n = l[STRLEN(l) - 1]) == '\\'))
3769 {
3770 // take us back to opening paren
3771 if (find_last_paren(l, '(', ')')
3772 && (trypos = find_match_paren(
3773 curbuf->b_ind_maxparen)) != NULL)
3774 curwin->w_cursor = *trypos;
3775
3776 // For a line ending in ',' that is a continuation line go
3777 // back to the first line with a backslash:
3778 // char *foo = "bla{backslash}
3779 // bla",
3780 // here;
3781 while (n == 0 && curwin->w_cursor.lnum > 1)
3782 {
3783 l = ml_get(curwin->w_cursor.lnum - 1);
3784 if (*l == NUL || l[STRLEN(l) - 1] != '\\')
3785 break;
3786 --curwin->w_cursor.lnum;
3787 curwin->w_cursor.col = 0;
3788 }
3789
3790 amount = get_indent(); // XXX
3791
3792 if (amount == 0)
3793 amount = cin_first_id_amount();
3794 if (amount == 0)
3795 amount = ind_continuation;
3796 break;
3797 }
3798
3799 // If the line looks like a function declaration, and we're
3800 // not in a comment, put it the left margin.
3801 if (cin_isfuncdecl(NULL, cur_curpos.lnum, 0)) // XXX
3802 break;
3803 l = ml_get_curline();
3804
3805 // Finding the closing '}' of a previous function. Put
3806 // current line at the left margin. For when 'cino' has "fs".
3807 if (*skipwhite(l) == '}')
3808 break;
3809
3810 // (matching {)
3811 // If the previous line ends on '};' (maybe followed by
3812 // comments) align at column 0. For example:
3813 // char *string_array[] = { "foo",
3814 // / * x * / "b};ar" }; / * foobar * /
3815 if (cin_ends_in(l, (char_u *)"};", NULL))
3816 break;
3817
3818 // If the previous line ends on '[' we are probably in an
3819 // array constant:
3820 // something = [
3821 // 234, <- extra indent
3822 if (cin_ends_in(l, (char_u *)"[", NULL))
3823 {
3824 amount = get_indent() + ind_continuation;
3825 break;
3826 }
3827
3828 // Find a line only has a semicolon that belongs to a previous
3829 // line ending in '}', e.g. before an #endif. Don't increase
3830 // indent then.
3831 if (*(look = skipwhite(l)) == ';' && cin_nocode(look + 1))
3832 {
3833 pos_T curpos_save = curwin->w_cursor;
3834
3835 while (curwin->w_cursor.lnum > 1)
3836 {
3837 look = ml_get(--curwin->w_cursor.lnum);
3838 if (!(cin_nocode(look) || cin_ispreproc_cont(
3839 &look, &curwin->w_cursor.lnum, &amount)))
3840 break;
3841 }
3842 if (curwin->w_cursor.lnum > 0
3843 && cin_ends_in(look, (char_u *)"}", NULL))
3844 break;
3845
3846 curwin->w_cursor = curpos_save;
3847 }
3848
3849 // If the PREVIOUS line is a function declaration, the current
3850 // line (and the ones that follow) needs to be indented as
3851 // parameters.
3852 if (cin_isfuncdecl(&l, curwin->w_cursor.lnum, 0))
3853 {
3854 amount = curbuf->b_ind_param;
3855 break;
3856 }
3857
3858 // If the previous line ends in ';' and the line before the
3859 // previous line ends in ',' or '\', ident to column zero:
3860 // int foo,
3861 // bar;
3862 // indent_to_0 here;
3863 if (cin_ends_in(l, (char_u *)";", NULL))
3864 {
3865 l = ml_get(curwin->w_cursor.lnum - 1);
3866 if (cin_ends_in(l, (char_u *)",", NULL)
3867 || (*l != NUL && l[STRLEN(l) - 1] == '\\'))
3868 break;
3869 l = ml_get_curline();
3870 }
3871
3872 // Doesn't look like anything interesting -- so just
3873 // use the indent of this line.
3874 //
3875 // Position the cursor over the rightmost paren, so that
3876 // matching it will take us back to the start of the line.
3877 find_last_paren(l, '(', ')');
3878
3879 if ((trypos = find_match_paren(curbuf->b_ind_maxparen)) != NULL)
3880 curwin->w_cursor = *trypos;
3881 amount = get_indent(); // XXX
3882 break;
3883 }
3884
3885 // add extra indent for a comment
3886 if (cin_iscomment(theline))
3887 amount += curbuf->b_ind_comment;
3888
3889 // add extra indent if the previous line ended in a backslash:
3890 // "asdfasdf{backslash}
3891 // here";
3892 // char *foo = "asdf{backslash}
3893 // here";
3894 if (cur_curpos.lnum > 1)
3895 {
3896 l = ml_get(cur_curpos.lnum - 1);
3897 if (*l != NUL && l[STRLEN(l) - 1] == '\\')
3898 {
3899 cur_amount = cin_get_equal_amount(cur_curpos.lnum - 1);
3900 if (cur_amount > 0)
3901 amount = cur_amount;
3902 else if (cur_amount == 0)
3903 amount += ind_continuation;
3904 }
3905 }
3906
3907theend:
3908 if (amount < 0)
3909 amount = 0;
3910
3911laterend:
3912 // put the cursor back where it belongs
3913 curwin->w_cursor = cur_curpos;
3914
3915 vim_free(linecopy);
3916
3917 return amount;
3918}
3919
3920/*
3921 * return TRUE if 'cinkeys' contains the key "keytyped",
3922 * when == '*': Only if key is preceded with '*' (indent before insert)
3923 * when == '!': Only if key is preceded with '!' (don't insert)
3924 * when == ' ': Only if key is not preceded with '*'(indent afterwards)
3925 *
3926 * "keytyped" can have a few special values:
3927 * KEY_OPEN_FORW
3928 * KEY_OPEN_BACK
3929 * KEY_COMPLETE just finished completion.
3930 *
3931 * If line_is_empty is TRUE accept keys with '0' before them.
3932 */
3933 int
3934in_cinkeys(
3935 int keytyped,
3936 int when,
3937 int line_is_empty)
3938{
3939 char_u *look;
3940 int try_match;
3941 int try_match_word;
3942 char_u *p;
3943 char_u *line;
3944 int icase;
3945 int i;
3946
3947 if (keytyped == NUL)
3948 // Can happen with CTRL-Y and CTRL-E on a short line.
3949 return FALSE;
3950
3951#ifdef FEAT_EVAL
3952 if (*curbuf->b_p_inde != NUL)
3953 look = curbuf->b_p_indk; // 'indentexpr' set: use 'indentkeys'
3954 else
3955#endif
3956 look = curbuf->b_p_cink; // 'indentexpr' empty: use 'cinkeys'
3957 while (*look)
3958 {
3959 // Find out if we want to try a match with this key, depending on
3960 // 'when' and a '*' or '!' before the key.
3961 switch (when)
3962 {
3963 case '*': try_match = (*look == '*'); break;
3964 case '!': try_match = (*look == '!'); break;
Yegappan Lakshmanan562610c2025-05-04 21:05:51 +02003965 default: try_match = (*look != '*'); break;
Bram Moolenaar14c01f82019-10-09 22:53:08 +02003966 }
3967 if (*look == '*' || *look == '!')
3968 ++look;
3969
3970 // If there is a '0', only accept a match if the line is empty.
3971 // But may still match when typing last char of a word.
3972 if (*look == '0')
3973 {
3974 try_match_word = try_match;
3975 if (!line_is_empty)
3976 try_match = FALSE;
3977 ++look;
3978 }
3979 else
3980 try_match_word = FALSE;
3981
3982 // does it look like a control character?
Bram Moolenaar424bcae2022-01-31 14:59:41 +00003983 if (*look == '^' && look[1] >= '?' && look[1] <= '_')
Bram Moolenaar14c01f82019-10-09 22:53:08 +02003984 {
3985 if (try_match && keytyped == Ctrl_chr(look[1]))
3986 return TRUE;
3987 look += 2;
3988 }
3989 // 'o' means "o" command, open forward.
3990 // 'O' means "O" command, open backward.
3991 else if (*look == 'o')
3992 {
3993 if (try_match && keytyped == KEY_OPEN_FORW)
3994 return TRUE;
3995 ++look;
3996 }
3997 else if (*look == 'O')
3998 {
3999 if (try_match && keytyped == KEY_OPEN_BACK)
4000 return TRUE;
4001 ++look;
4002 }
4003
4004 // 'e' means to check for "else" at start of line and just before the
4005 // cursor.
4006 else if (*look == 'e')
4007 {
4008 if (try_match && keytyped == 'e' && curwin->w_cursor.col >= 4)
4009 {
4010 p = ml_get_curline();
4011 if (skipwhite(p) == p + curwin->w_cursor.col - 4 &&
4012 STRNCMP(p + curwin->w_cursor.col - 4, "else", 4) == 0)
4013 return TRUE;
4014 }
4015 ++look;
4016 }
4017
4018 // ':' only causes an indent if it is at the end of a label or case
4019 // statement, or when it was before typing the ':' (to fix
4020 // class::method for C++).
4021 else if (*look == ':')
4022 {
4023 if (try_match && keytyped == ':')
4024 {
4025 p = ml_get_curline();
4026 if (cin_iscase(p, FALSE) || cin_isscopedecl(p) || cin_islabel())
4027 return TRUE;
4028 // Need to get the line again after cin_islabel().
4029 p = ml_get_curline();
4030 if (curwin->w_cursor.col > 2
4031 && p[curwin->w_cursor.col - 1] == ':'
4032 && p[curwin->w_cursor.col - 2] == ':')
4033 {
4034 p[curwin->w_cursor.col - 1] = ' ';
4035 i = (cin_iscase(p, FALSE) || cin_isscopedecl(p)
4036 || cin_islabel());
4037 p = ml_get_curline();
4038 p[curwin->w_cursor.col - 1] = ':';
4039 if (i)
4040 return TRUE;
4041 }
4042 }
4043 ++look;
4044 }
4045
4046
4047 // Is it a key in <>, maybe?
4048 else if (*look == '<')
4049 {
4050 if (try_match)
4051 {
4052 // make up some named keys <o>, <O>, <e>, <0>, <>>, <<>, <*>,
4053 // <:> and <!> so that people can re-indent on o, O, e, 0, <,
4054 // >, *, : and ! keys if they really really want to.
4055 if (vim_strchr((char_u *)"<>!*oOe0:", look[1]) != NULL
4056 && keytyped == look[1])
4057 return TRUE;
4058
4059 if (keytyped == get_special_key_code(look + 1))
4060 return TRUE;
4061 }
4062 while (*look && *look != '>')
4063 look++;
4064 while (*look == '>')
4065 look++;
4066 }
4067
4068 // Is it a word: "=word"?
4069 else if (*look == '=' && look[1] != ',' && look[1] != NUL)
4070 {
4071 ++look;
4072 if (*look == '~')
4073 {
4074 icase = TRUE;
4075 ++look;
4076 }
4077 else
4078 icase = FALSE;
4079 p = vim_strchr(look, ',');
4080 if (p == NULL)
4081 p = look + STRLEN(look);
4082 if ((try_match || try_match_word)
4083 && curwin->w_cursor.col >= (colnr_T)(p - look))
4084 {
4085 int match = FALSE;
4086
4087 if (keytyped == KEY_COMPLETE)
4088 {
4089 char_u *s;
4090
4091 // Just completed a word, check if it starts with "look".
4092 // search back for the start of a word.
4093 line = ml_get_curline();
4094 if (has_mbyte)
4095 {
4096 char_u *n;
4097
4098 for (s = line + curwin->w_cursor.col; s > line; s = n)
4099 {
4100 n = mb_prevptr(line, s);
4101 if (!vim_iswordp(n))
4102 break;
4103 }
4104 }
4105 else
4106 for (s = line + curwin->w_cursor.col; s > line; --s)
4107 if (!vim_iswordc(s[-1]))
4108 break;
4109 if (s + (p - look) <= line + curwin->w_cursor.col
4110 && (icase
4111 ? MB_STRNICMP(s, look, p - look)
4112 : STRNCMP(s, look, p - look)) == 0)
4113 match = TRUE;
4114 }
4115 else
4116 // TODO: multi-byte
Christian Brabandt49471962024-01-12 17:48:08 +01004117 if (keytyped == (int)p[-1] || (icase
4118 && keytyped < 256 && keytyped >= 0
Bram Moolenaar14c01f82019-10-09 22:53:08 +02004119 && TOLOWER_LOC(keytyped) == TOLOWER_LOC((int)p[-1])))
4120 {
4121 line = ml_get_cursor();
4122 if ((curwin->w_cursor.col == (colnr_T)(p - look)
4123 || !vim_iswordc(line[-(p - look) - 1]))
4124 && (icase
4125 ? MB_STRNICMP(line - (p - look), look, p - look)
4126 : STRNCMP(line - (p - look), look, p - look))
4127 == 0)
4128 match = TRUE;
4129 }
4130 if (match && try_match_word && !try_match)
4131 {
4132 // "0=word": Check if there are only blanks before the
4133 // word.
4134 if (getwhitecols_curline() !=
4135 (int)(curwin->w_cursor.col - (p - look)))
4136 match = FALSE;
4137 }
4138 if (match)
4139 return TRUE;
4140 }
4141 look = p;
4142 }
4143
4144 // ok, it's a boring generic character.
4145 else
4146 {
4147 if (try_match && *look == keytyped)
4148 return TRUE;
4149 if (*look != NUL)
4150 ++look;
4151 }
4152
4153 // Skip over ", ".
4154 look = skip_to_option_part(look);
4155 }
4156 return FALSE;
4157}
4158
4159/*
4160 * Do C or expression indenting on the current line.
4161 */
4162 void
4163do_c_expr_indent(void)
4164{
K.Takata161b6ac2022-11-14 15:31:07 +00004165#ifdef FEAT_EVAL
Bram Moolenaar14c01f82019-10-09 22:53:08 +02004166 if (*curbuf->b_p_inde != NUL)
4167 fixthisline(get_expr_indent);
4168 else
K.Takata161b6ac2022-11-14 15:31:07 +00004169#endif
Bram Moolenaar14c01f82019-10-09 22:53:08 +02004170 fixthisline(get_c_indent);
4171}
Bram Moolenaar14c01f82019-10-09 22:53:08 +02004172
4173#if defined(FEAT_EVAL) || defined(PROTO)
4174/*
4175 * "cindent(lnum)" function
4176 */
4177 void
4178f_cindent(typval_T *argvars UNUSED, typval_T *rettv)
4179{
Bram Moolenaar14c01f82019-10-09 22:53:08 +02004180 pos_T pos;
4181 linenr_T lnum;
4182
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02004183 if (in_vim9script() && check_for_lnum_arg(argvars, 0) == FAIL)
4184 return;
4185
Bram Moolenaar14c01f82019-10-09 22:53:08 +02004186 pos = curwin->w_cursor;
4187 lnum = tv_get_lnum(argvars);
4188 if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count)
4189 {
4190 curwin->w_cursor.lnum = lnum;
4191 rettv->vval.v_number = get_c_indent();
4192 curwin->w_cursor = pos;
4193 }
4194 else
Bram Moolenaar14c01f82019-10-09 22:53:08 +02004195 rettv->vval.v_number = -1;
4196}
4197#endif