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