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