neovim

Neovim text editor
git clone https://git.dasho.dev/neovim.git
Log | Files | Refs | README

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 }