diff options
| -rw-r--r-- | src/nvim/edit.c | 11 | ||||
| -rw-r--r-- | src/nvim/indent_c.c | 602 | ||||
| -rw-r--r-- | src/nvim/ops.c | 8 | ||||
| -rw-r--r-- | src/nvim/search.c | 89 | ||||
| -rw-r--r-- | src/nvim/version.c | 2 | ||||
| -rw-r--r-- | test/functional/legacy/003_cindent_spec.lua | 36 | 
6 files changed, 484 insertions, 264 deletions
| diff --git a/src/nvim/edit.c b/src/nvim/edit.c index 9ba5d96e16..ceedcb5a99 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -6584,9 +6584,14 @@ static int cindent_on(void) {   */  void fixthisline(IndentGetter get_the_indent)  { -  change_indent(INDENT_SET, get_the_indent(), FALSE, 0, TRUE); -  if (linewhite(curwin->w_cursor.lnum)) -    did_ai = TRUE;          /* delete the indent if the line stays empty */ +    int amount = get_the_indent(); + +    if (amount >= 0) { +        change_indent(INDENT_SET, amount, false, 0, true); +        if (linewhite(curwin->w_cursor.lnum)) { +            did_ai = TRUE;  // delete the indent if the line stays empty +        } +    }  }  void fix_indent(void) { diff --git a/src/nvim/indent_c.c b/src/nvim/indent_c.c index 40fb66186d..bfbd30484a 100644 --- a/src/nvim/indent_c.c +++ b/src/nvim/indent_c.c @@ -29,6 +29,7 @@ typedef struct {  /*   * Find the start of a comment, not knowing if we are in a comment right now.   * Search starts at w_cursor.lnum and goes backwards. + * Return NULL when not inside a comment.   */  static pos_T *ind_find_start_comment(void)  { /* XXX */ @@ -69,6 +70,62 @@ find_start_comment (  /* XXX */  }  /* + * Find the start of a comment or raw string, not knowing if we are in a + * comment or raw string right now. + * Search starts at w_cursor.lnum and goes backwards. + * Return NULL when not inside a comment or raw string. + * "CORS" -> Comment Or Raw String + */ +static pos_T *ind_find_start_CORS(void) +{ /* XXX */ +    pos_T	*comment_pos = find_start_comment(curbuf->b_ind_maxcomment); +    pos_T	*rs_pos = find_start_rawstring(curbuf->b_ind_maxcomment); + +    /* If comment_pos is before rs_pos the raw string is inside the comment. +     * If rs_pos is before comment_pos the comment is inside the raw string. */ +    if (comment_pos == NULL || (rs_pos != NULL && lt(*rs_pos, *comment_pos))) +        return rs_pos; +    return comment_pos; +} + +/* + * Find the start of a raw string, not knowing if we are in one right now. + * Search starts at w_cursor.lnum and goes backwards. + * Return NULL when not inside a raw string. + */ +static pos_T *find_start_rawstring(int ind_maxcomment) +{ /* XXX */ +    pos_T	*pos; +    char_u	*line; +    char_u	*p; +    int		cur_maxcomment = ind_maxcomment; + +    for (;;) +    { +        pos = findmatchlimit(NULL, 'R', FM_BACKWARD, cur_maxcomment); +        if (pos == NULL) +            break; + +        /* +         * Check if the raw string start we found is inside a string. +         * If it is then restrict the search to below this line and try again. +         */ +        line = ml_get(pos->lnum); +        for (p = line; *p && (colnr_T)(p - line) < pos->col; ++p) +            p = skip_string(p); +        if ((colnr_T)(p - line) <= pos->col) +            break; +        cur_maxcomment = curwin->w_cursor.lnum - pos->lnum - 1; +        if (cur_maxcomment <= 0) +        { +            pos = NULL; +            break; +        } +    } +    return pos; +} + +/*   * Skip to the end of a "string" and a 'c' character.   * If there is no string or character, return argument unmodified.   */ @@ -101,7 +158,28 @@ static char_u *skip_string(char_u *p)            break;        }        if (p[0] == '"') -        continue; +          continue; /* continue for another string */ +    } +    else if (p[0] == 'R' && p[1] == '"') +    { +        /* Raw string: R"[delim](...)[delim]" */ +        char_u *delim = p + 2; +        char_u *paren = vim_strchr(delim, '('); + +        if (paren != NULL) +        { +            size_t delim_len = paren - delim; + +            for (p += 3; *p; ++p) +                if (p[0] == ')' && STRNCMP(p + 1, delim, delim_len) == 0 +                        && p[delim_len + 1] == '"') +                { +                    p += delim_len + 1; +                    break; +                } +            if (p[0] == '"') +                continue; /* continue for another string */ +        }      }      break;                                  /* no string found */    } @@ -292,10 +370,11 @@ int cin_islabel(void)      --curwin->w_cursor.lnum;      /* -     * If we're in a comment now, skip to the start of the comment. +     * If we're in a comment or raw string now, skip to the start of +     * it.       */      curwin->w_cursor.col = 0; -    if ((trypos = ind_find_start_comment()) != NULL)       /* XXX */ +    if ((trypos = ind_find_start_CORS()) != NULL) /* XXX */        curwin->w_cursor = *trypos;      line = get_cursor_line_ptr(); @@ -1057,7 +1136,7 @@ static int cin_is_cpp_baseclass(cpp_baseclass_cache_T *cached) {          continue;      } -    if (s[0] == '"') +    if (s[0] == '"' || (s[0] == 'R' && s[1] == '"'))        s = skip_string(s) + 1;      else if (s[0] == ':') {        if (s[1] == ':') { @@ -1228,7 +1307,7 @@ static pos_T *find_start_brace(void)      pos = NULL;      /* ignore the { if it's in a // or / *  * / comment */      if ((colnr_T)cin_skip2pos(trypos) == trypos->col -        && (pos = ind_find_start_comment()) == NULL)                /* XXX */ +            && (pos = ind_find_start_CORS()) == NULL) /* XXX */        break;      if (pos != NULL)        curwin->w_cursor.lnum = pos->lnum; @@ -1270,7 +1349,7 @@ retry:        pos_copy = *trypos;           /* copy trypos, findmatch will change it */        trypos = &pos_copy;        curwin->w_cursor = *trypos; -      if ((trypos_wk = ind_find_start_comment()) != NULL) { /* XXX */ +      if ((trypos_wk = ind_find_start_CORS()) != NULL) { /* XXX */          ind_maxp_wk = ind_maxparen - (int)(cursor_save.lnum              - trypos_wk->lnum);          if (ind_maxp_wk > 0) { @@ -1562,6 +1641,10 @@ void parse_cino(buf_T *buf)    }  } +/* + * Return the desired indent for C code. + * Return -1 if the indent should be left alone (inside a raw string). + */  int get_c_indent(void)  {    pos_T cur_curpos; @@ -1572,8 +1655,9 @@ int get_c_indent(void)    char_u      *theline;    char_u      *linecopy;    pos_T       *trypos; +  pos_T       *comment_pos;    pos_T       *tryposBrace = NULL; -  pos_T       tryposBraceCopy; +  pos_T       tryposCopy;    pos_T our_paren_pos;    char_u      *start;    int start_brace; @@ -1616,7 +1700,7 @@ int get_c_indent(void)    /* remember where the cursor was when we started */    cur_curpos = curwin->w_cursor; -  /* if we are at line 1 0 is fine, right? */ +  /* if we are at line 1 zero indent is fine, right? */    if (cur_curpos.lnum == 1)      return 0; @@ -1646,37 +1730,55 @@ int get_c_indent(void)    original_line_islabel = cin_islabel();    /* XXX */    /* +   * If we are inside a raw string don't change the indent. +   * Ignore a raw string inside a comment. +   */ +  comment_pos = ind_find_start_comment(); +  if (comment_pos != NULL) { +    /* findmatchlimit() static pos is overwritten, make a copy */ +    tryposCopy = *comment_pos; +    comment_pos = &tryposCopy; +  } +  trypos = find_start_rawstring(curbuf->b_ind_maxcomment); +  if (trypos != NULL && (comment_pos == NULL || lt(*trypos, *comment_pos))) { +    amount = -1; +    goto laterend; +  } + +  /*     * #defines and so on always go at the left when included in 'cinkeys'.     */ -  if (*theline == '#' && (*linecopy == '#' || in_cinkeys('#', ' ', TRUE))) +  if (*theline == '#' && (*linecopy == '#' || in_cinkeys('#', ' ', TRUE))) {      amount = curbuf->b_ind_hash_comment; +    goto theend; +  }    /*     * Is it a non-case label?	Then that goes at the left margin too unless:     *  - JS flag is set.     *  - 'L' item has a positive value.     */ -  else if (original_line_islabel && !curbuf->b_ind_js +  if (original_line_islabel && !curbuf->b_ind_js             && curbuf->b_ind_jump_label < 0) {      amount = 0; +    goto theend;    }    /*     * If we're inside a "//" comment and there is a "//" comment in a     * previous line, lineup with that one.     */ -  else if (cin_islinecomment(theline) +  if (cin_islinecomment(theline)             && (trypos = find_line_comment()) != NULL) { /* XXX */      /* find how indented the line beginning the comment is */      getvcol(curwin, trypos, &col, NULL, NULL);      amount = col; +    goto theend;    }    /*     * If we're inside a comment and not looking at the start of the     * comment, try using the 'comments' option.     */ -  else if (!cin_iscomment(theline) -           && (trypos = ind_find_start_comment()) != NULL) { -    /* XXX */ +  if (!cin_iscomment(theline) && comment_pos != NULL) { /* XXX */      int lead_start_len = 2;      int lead_middle_len = 1;      char_u lead_start[COM_MAX_LEN];             /* start-comment string */ @@ -1688,7 +1790,7 @@ int get_c_indent(void)      int done = FALSE;      /* find how indented the line beginning the comment is */ -    getvcol(curwin, trypos, &col, NULL, NULL); +    getvcol(curwin, comment_pos, &col, NULL, NULL);      amount = col;      *lead_start = NUL;      *lead_middle = NUL; @@ -1743,7 +1845,7 @@ int get_c_indent(void)              }              /* If the start comment string doesn't match with the               * start of the comment, skip this entry. XXX */ -            else if (STRNCMP(ml_get(trypos->lnum) + trypos->col, +            else if (STRNCMP(ml_get(comment_pos->lnum) + comment_pos->col,                           lead_start, lead_start_len) != 0)                continue;            } @@ -1789,7 +1891,7 @@ int get_c_indent(void)         * otherwise, add the amount specified by "c" in 'cino'         */        amount = -1; -      for (lnum = cur_curpos.lnum - 1; lnum > trypos->lnum; --lnum) { +      for (lnum = cur_curpos.lnum - 1; lnum > comment_pos->lnum; --lnum) {          if (linewhite(lnum))                        /* skip blank lines */            continue;          amount = get_indent_lnum(lnum);             /* XXX */ @@ -1797,28 +1899,30 @@ int get_c_indent(void)        }        if (amount == -1) {                           /* use the comment opener */          if (!curbuf->b_ind_in_comment2) { -          start = ml_get(trypos->lnum); -          look = start + trypos->col + 2;           /* skip / and * */ +            start = ml_get(comment_pos->lnum); +            look = start + comment_pos->col + 2; /* skip / and * */            if (*look != NUL)                         /* if something after it */ -            trypos->col = (colnr_T)(skipwhite(look) - start); +              comment_pos->col = (colnr_T)(skipwhite(look) - start);          } -        getvcol(curwin, trypos, &col, NULL, NULL); +        getvcol(curwin, comment_pos, &col, NULL, NULL);          amount = col;          if (curbuf->b_ind_in_comment2 || *look == NUL)            amount += curbuf->b_ind_in_comment;        }      } +  goto theend;    }    // Are we looking at a ']' that has a match? -  else if (*skipwhite(theline) == ']' +  if (*skipwhite(theline) == ']'             && (trypos = find_match_char('[', curbuf->b_ind_maxparen)) != NULL) {      // align with the line containing the '['.      amount = get_indent_lnum(trypos->lnum); +    goto theend;    }    /*     * Are we inside parentheses or braces?     */						    /* XXX */ -  else if (((trypos = find_match_paren(curbuf->b_ind_maxparen)) != NULL +  if (((trypos = find_match_paren(curbuf->b_ind_maxparen)) != NULL              && curbuf->b_ind_java == 0)             || (tryposBrace = find_start_brace()) != NULL             || trypos != NULL) { @@ -1852,8 +1956,8 @@ int get_c_indent(void)              continue;                           /* ignore #define, #if, etc. */            curwin->w_cursor.lnum = lnum; -          /* Skip a comment. XXX */ -          if ((trypos = ind_find_start_comment()) != NULL) { +          /* Skip a comment or raw string. XXX */ +          if ((trypos = ind_find_start_CORS()) != NULL) {              lnum = trypos->lnum + 1;              continue;            } @@ -2054,8 +2158,8 @@ int get_c_indent(void)        // stored in tryposBrace.        // Make a copy of tryposBrace, it may point to pos_copy inside        // find_start_brace(), which may be changed somewhere. -      tryposBraceCopy = *tryposBrace; -      tryposBrace = &tryposBraceCopy; +      tryposCopy = *tryposBrace; +      tryposBrace = &tryposCopy;        trypos = tryposBrace;        ourscope = trypos->lnum;        start = ml_get(ourscope); @@ -2232,10 +2336,10 @@ int get_c_indent(void)                l = get_cursor_line_ptr();                /* -               * If we're in a comment now, skip to the start of the -               * comment. +               * If we're in a comment or raw string now, skip to +               * the start of it.                 */ -              trypos = ind_find_start_comment(); +              trypos = ind_find_start_CORS();                if (trypos != NULL) {                  curwin->w_cursor.lnum = trypos->lnum + 1;                  curwin->w_cursor.col = 0; @@ -2341,9 +2445,9 @@ int get_c_indent(void)                  l = get_cursor_line_ptr(); -                /* If we're in a comment now, skip to the start of -                 * the comment. */ -                trypos = ind_find_start_comment(); +                /* If we're in a comment or raw string now, skip +                 * to the start of it. */ +                trypos = ind_find_start_CORS();                  if (trypos != NULL) {                    curwin->w_cursor.lnum = trypos->lnum + 1;                    curwin->w_cursor.col = 0; @@ -2369,9 +2473,10 @@ int get_c_indent(void)            }            /* -           * If we're in a comment now, skip to the start of the comment. +           * If we're in a comment or raw string now, skip to the start +           * of it.             */					    /* XXX */ -          if ((trypos = ind_find_start_comment()) != NULL) { +          if ((trypos = ind_find_start_CORS()) != NULL) {              curwin->w_cursor.lnum = trypos->lnum + 1;              curwin->w_cursor.col = 0;              continue; @@ -3086,252 +3191,257 @@ term_again:      /* subtract extra left-shift for jump labels */      if (curbuf->b_ind_jump_label > 0 && original_line_islabel)        amount -= curbuf->b_ind_jump_label; -  } -  else { -    // Ok -- we're not inside any sort of structure at all! -    // -    // this means we're at the top level, and everything should -    // basically just match where the previous line is, except -    // for the lines immediately following a function declaration, -    // which are K&R-style parameters and need to be indented. - -    // if our line starts with an open brace, forget about any -    // prevailing indent and make sure it looks like the start -    // of a function -    if (theline[0] == '{') { -      amount = curbuf->b_ind_first_open; -    } -    /* -     * If the NEXT line is a function declaration, the current -     * line needs to be indented as a function type spec. -     * Don't do this if the current line looks like a comment or if the -     * current line is terminated, ie. ends in ';', or if the current line -     * contains { or }: "void f() {\n if (1)" -     */ -    else if (cur_curpos.lnum < curbuf->b_ml.ml_line_count -             && !cin_nocode(theline) -             && vim_strchr(theline, '{') == NULL -             && vim_strchr(theline, '}') == NULL -             && !cin_ends_in(theline, (char_u *)":", NULL) -             && !cin_ends_in(theline, (char_u *)",", NULL) -             && cin_isfuncdecl(NULL, cur_curpos.lnum + 1, -                 cur_curpos.lnum + 1) -             && !cin_isterminated(theline, FALSE, TRUE)) { -      amount = curbuf->b_ind_func_type; -    } else { -      amount = 0; -      curwin->w_cursor = cur_curpos; -      /* search backwards until we find something we recognize */ +    goto theend; +  } -      while (curwin->w_cursor.lnum > 1) { -        curwin->w_cursor.lnum--; -        curwin->w_cursor.col = 0; +  // Ok -- we're not inside any sort of structure at all! +  // +  // this means we're at the top level, and everything should +  // basically just match where the previous line is, except +  // for the lines immediately following a function declaration, +  // which are K&R-style parameters and need to be indented. +   +  // if our line starts with an open brace, forget about any +  // prevailing indent and make sure it looks like the start +  // of a function +   +  if (theline[0] == '{') { +      amount = curbuf->b_ind_first_open; +      goto theend; +  } +  /* +   * If the NEXT line is a function declaration, the current +   * line needs to be indented as a function type spec. +   * Don't do this if the current line looks like a comment or if the +   * current line is terminated, ie. ends in ';', or if the current line +   * contains { or }: "void f() {\n if (1)" +   */ +  if (cur_curpos.lnum < curbuf->b_ml.ml_line_count +          && !cin_nocode(theline) +          && vim_strchr(theline, '{') == NULL +          && vim_strchr(theline, '}') == NULL +          && !cin_ends_in(theline, (char_u *)":", NULL) +          && !cin_ends_in(theline, (char_u *)",", NULL) +          && cin_isfuncdecl(NULL, cur_curpos.lnum + 1, +              cur_curpos.lnum + 1) +          && !cin_isterminated(theline, FALSE, TRUE)) { +    amount = curbuf->b_ind_func_type; +    goto theend; +  } -        l = get_cursor_line_ptr(); +  /* search backwards until we find something we recognize */ +  amount = 0; +  curwin->w_cursor = cur_curpos; +  while (curwin->w_cursor.lnum > 1) { +    curwin->w_cursor.lnum--; +    curwin->w_cursor.col = 0; -        /* -         * If we're in a comment now, skip to the start of the comment. -         */						/* XXX */ -        if ((trypos = ind_find_start_comment()) != NULL) { -          curwin->w_cursor.lnum = trypos->lnum + 1; -          curwin->w_cursor.col = 0; -          continue; -        } +    l = get_cursor_line_ptr(); -        /* -         * Are we at the start of a cpp base class declaration or -         * constructor initialization? -         */						    /* XXX */ -        n = FALSE; -        if (curbuf->b_ind_cpp_baseclass != 0 && theline[0] != '{') { -          n = cin_is_cpp_baseclass(&cache_cpp_baseclass); -          l = get_cursor_line_ptr(); -        } -        if (n) { -          /* XXX */ -          amount = get_baseclass_amount(cache_cpp_baseclass.lpos.col); -          break; -        } +    /* +     * If we're in a comment or raw string now, skip to the start +     * of it. +     */						/* XXX */ +    if ((trypos = ind_find_start_CORS()) != NULL) { +      curwin->w_cursor.lnum = trypos->lnum + 1; +      curwin->w_cursor.col = 0; +      continue; +    } -        /* -         * Skip preprocessor directives and blank lines. -         */ -        if (cin_ispreproc_cont(&l, &curwin->w_cursor.lnum)) -          continue; +    /* +     * Are we at the start of a cpp base class declaration or +     * constructor initialization? +     */						    /* XXX */ +    n = FALSE; +    if (curbuf->b_ind_cpp_baseclass != 0 && theline[0] != '{') { +      n = cin_is_cpp_baseclass(&cache_cpp_baseclass); +      l = get_cursor_line_ptr(); +    } +    if (n) { +      /* XXX */ +      amount = get_baseclass_amount(cache_cpp_baseclass.lpos.col); +      break; +    } -        if (cin_nocode(l)) -          continue; +    /* +     * Skip preprocessor directives and blank lines. +     */ +    if (cin_ispreproc_cont(&l, &curwin->w_cursor.lnum)) +      continue; -        /* -         * If the previous line ends in ',', use one level of -         * indentation: -         * int foo, -         *     bar; -         * do this before checking for '}' in case of eg. -         * enum foobar -         * { -         *   ... -         * } foo, -         *   bar; -         */ -        n = 0; -        if (cin_ends_in(l, (char_u *)",", NULL) -            || (*l != NUL && (n = l[STRLEN(l) - 1]) == '\\')) { -          /* take us back to opening paren */ -          if (find_last_paren(l, '(', ')') -              && (trypos = find_match_paren( -                      curbuf->b_ind_maxparen)) != NULL) -            curwin->w_cursor = *trypos; - -          /* For a line ending in ',' that is a continuation line go -           * back to the first line with a backslash: -           * char *foo = "bla\ -           *		 bla", -           *      here; -           */ -          while (n == 0 && curwin->w_cursor.lnum > 1) { -            l = ml_get(curwin->w_cursor.lnum - 1); -            if (*l == NUL || l[STRLEN(l) - 1] != '\\') -              break; -            --curwin->w_cursor.lnum; -            curwin->w_cursor.col = 0; -          } +    if (cin_nocode(l)) +      continue; -          amount = get_indent();                    /* XXX */ +    /* +     * If the previous line ends in ',', use one level of +     * indentation: +     * int foo, +     *     bar; +     * do this before checking for '}' in case of eg. +     * enum foobar +     * { +     *   ... +     * } foo, +     *   bar; +     */ +    n = 0; +    if (cin_ends_in(l, (char_u *)",", NULL) +        || (*l != NUL && (n = l[STRLEN(l) - 1]) == '\\')) { +      /* take us back to opening paren */ +      if (find_last_paren(l, '(', ')') +          && (trypos = find_match_paren( +              curbuf->b_ind_maxparen)) != NULL) +        curwin->w_cursor = *trypos; -          if (amount == 0) -            amount = cin_first_id_amount(); -          if (amount == 0) -            amount = ind_continuation; +      /* For a line ending in ',' that is a continuation line go +       * back to the first line with a backslash: +       * char *foo = "bla\ +       *		 bla", +       *      here; +       */ +      while (n == 0 && curwin->w_cursor.lnum > 1) { +        l = ml_get(curwin->w_cursor.lnum - 1); +        if (*l == NUL || l[STRLEN(l) - 1] != '\\')            break; -        } +        --curwin->w_cursor.lnum; +        curwin->w_cursor.col = 0; +      } -        /* -         * If the line looks like a function declaration, and we're -         * not in a comment, put it the left margin. -         */ -        if (cin_isfuncdecl(NULL, cur_curpos.lnum, 0))          /* XXX */ -          break; -        l = get_cursor_line_ptr(); +      amount = get_indent();                    /* XXX */ -        /* -         * Finding the closing '}' of a previous function.  Put -         * current line at the left margin.  For when 'cino' has "fs". -         */ -        if (*skipwhite(l) == '}') -          break; +      if (amount == 0) +        amount = cin_first_id_amount(); +      if (amount == 0) +        amount = ind_continuation; +      break; +    } -        /*			    (matching {) -         * If the previous line ends on '};' (maybe followed by -         * comments) align at column 0.  For example: -         * char *string_array[] = { "foo", -         *     / * x * / "b};ar" }; / * foobar * / -         */ -        if (cin_ends_in(l, (char_u *)"};", NULL)) -          break; +    /* +     * If the line looks like a function declaration, and we're +     * not in a comment, put it the left margin. +     */ +    if (cin_isfuncdecl(NULL, cur_curpos.lnum, 0))          /* XXX */ +      break; +    l = get_cursor_line_ptr(); -        // If the previous line ends on '[' we are probably in an -        // array constant: -        // something = [ -        //     234,  <- extra indent -        if (cin_ends_in(l, (char_u *)"[", NULL)) { -          amount = get_indent() + ind_continuation; -          break; -        } +    /* +     * Finding the closing '}' of a previous function.  Put +     * current line at the left margin.  For when 'cino' has "fs". +     */ +    if (*skipwhite(l) == '}') +      break; -        /* -         * Find a line only has a semicolon that belongs to a previous -         * line ending in '}', e.g. before an #endif.  Don't increase -         * indent then. -         */ -        if (*(look = skipwhite(l)) == ';' && cin_nocode(look + 1)) { -          pos_T curpos_save = curwin->w_cursor; +    /*			    (matching {) +     * If the previous line ends on '};' (maybe followed by +     * comments) align at column 0.  For example: +     * char *string_array[] = { "foo", +     *     / * x * / "b};ar" }; / * foobar * / +     */ +    if (cin_ends_in(l, (char_u *)"};", NULL)) +      break; -          while (curwin->w_cursor.lnum > 1) { -            look = ml_get(--curwin->w_cursor.lnum); -            if (!(cin_nocode(look) || cin_ispreproc_cont( -                      &look, &curwin->w_cursor.lnum))) -              break; -          } -          if (curwin->w_cursor.lnum > 0 -              && cin_ends_in(look, (char_u *)"}", NULL)) -            break; +    // If the previous line ends on '[' we are probably in an +    // array constant: +    // something = [ +    //     234,  <- extra indent +    if (cin_ends_in(l, (char_u *)"[", NULL)) { +      amount = get_indent() + ind_continuation; +      break; +    } -          curwin->w_cursor = curpos_save; -        } +    /* +     * Find a line only has a semicolon that belongs to a previous +     * line ending in '}', e.g. before an #endif.  Don't increase +     * indent then. +     */ +    if (*(look = skipwhite(l)) == ';' && cin_nocode(look + 1)) { +      pos_T curpos_save = curwin->w_cursor; -        /* -         * If the PREVIOUS line is a function declaration, the current -         * line (and the ones that follow) needs to be indented as -         * parameters. -         */ -        if (cin_isfuncdecl(&l, curwin->w_cursor.lnum, 0)) { -          amount = curbuf->b_ind_param; +      while (curwin->w_cursor.lnum > 1) { +        look = ml_get(--curwin->w_cursor.lnum); +        if (!(cin_nocode(look) || cin_ispreproc_cont( +                &look, &curwin->w_cursor.lnum)))            break; -        } +      } +      if (curwin->w_cursor.lnum > 0 +          && cin_ends_in(look, (char_u *)"}", NULL)) +        break; -        /* -         * If the previous line ends in ';' and the line before the -         * previous line ends in ',' or '\', ident to column zero: -         * int foo, -         *     bar; -         * indent_to_0 here; -         */ -        if (cin_ends_in(l, (char_u *)";", NULL)) { -          l = ml_get(curwin->w_cursor.lnum - 1); -          if (cin_ends_in(l, (char_u *)",", NULL) -              || (*l != NUL && l[STRLEN(l) - 1] == '\\')) -            break; -          l = get_cursor_line_ptr(); -        } +      curwin->w_cursor = curpos_save; +    } -        /* -         * Doesn't look like anything interesting -- so just -         * use the indent of this line. -         * -         * Position the cursor over the rightmost paren, so that -         * matching it will take us back to the start of the line. -         */ -        find_last_paren(l, '(', ')'); +    /* +     * If the PREVIOUS line is a function declaration, the current +     * line (and the ones that follow) needs to be indented as +     * parameters. +     */ +    if (cin_isfuncdecl(&l, curwin->w_cursor.lnum, 0)) { +      amount = curbuf->b_ind_param; +      break; +    } -        if ((trypos = find_match_paren(curbuf->b_ind_maxparen)) != NULL) -          curwin->w_cursor = *trypos; -        amount = get_indent();              /* XXX */ +    /* +     * If the previous line ends in ';' and the line before the +     * previous line ends in ',' or '\', ident to column zero: +     * int foo, +     *     bar; +     * indent_to_0 here; +     */ +    if (cin_ends_in(l, (char_u *)";", NULL)) { +      l = ml_get(curwin->w_cursor.lnum - 1); +      if (cin_ends_in(l, (char_u *)",", NULL) +          || (*l != NUL && l[STRLEN(l) - 1] == '\\'))          break; -      } +      l = get_cursor_line_ptr(); +    } -      /* add extra indent for a comment */ -      if (cin_iscomment(theline)) -        amount += curbuf->b_ind_comment; +    /* +     * Doesn't look like anything interesting -- so just +     * use the indent of this line. +     * +     * Position the cursor over the rightmost paren, so that +     * matching it will take us back to the start of the line. +     */ +    find_last_paren(l, '(', ')'); -      /* add extra indent if the previous line ended in a backslash: -       *	      "asdfasdf\ -       *		  here"; -       *	    char *foo = "asdf\ -       *			 here"; -       */ -      if (cur_curpos.lnum > 1) { -        l = ml_get(cur_curpos.lnum - 1); -        if (*l != NUL && l[STRLEN(l) - 1] == '\\') { -          cur_amount = cin_get_equal_amount(cur_curpos.lnum - 1); -          if (cur_amount > 0) -            amount = cur_amount; -          else if (cur_amount == 0) -            amount += ind_continuation; -        } -      } +    if ((trypos = find_match_paren(curbuf->b_ind_maxparen)) != NULL) +      curwin->w_cursor = *trypos; +    amount = get_indent();              /* XXX */ +    break; +  } + +  /* add extra indent for a comment */ +  if (cin_iscomment(theline)) +    amount += curbuf->b_ind_comment; + +  /* add extra indent if the previous line ended in a backslash: +   *	      "asdfasdf\ +   *		  here"; +   *	    char *foo = "asdf\ +   *			 here"; +   */ +  if (cur_curpos.lnum > 1) { +    l = ml_get(cur_curpos.lnum - 1); +    if (*l != NUL && l[STRLEN(l) - 1] == '\\') { +      cur_amount = cin_get_equal_amount(cur_curpos.lnum - 1); +      if (cur_amount > 0) +        amount = cur_amount; +      else if (cur_amount == 0) +        amount += ind_continuation;      }    }  theend: +  if (amount < 0) +    amount = 0; + +laterend:    /* put the cursor back where it belongs */    curwin->w_cursor = cur_curpos;    xfree(linecopy); -  if (amount < 0) -    return 0;    return amount;  } diff --git a/src/nvim/ops.c b/src/nvim/ops.c index bef0ebaeed..52b4fed9d7 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -554,7 +554,7 @@ void op_reindent(oparg_T *oap, Indenter how)  {    long i;    char_u      *l; -  int count; +  int amount;    linenr_T first_changed = 0;    linenr_T last_changed = 0;    linenr_T start_lnum = curwin->w_cursor.lnum; @@ -582,11 +582,11 @@ void op_reindent(oparg_T *oap, Indenter how)          || how != get_lisp_indent) {        l = skipwhite(get_cursor_line_ptr());        if (*l == NUL)                        /* empty or blank line */ -        count = 0; +        amount = 0;        else -        count = how();                      /* get the indent for this line */ +        amount = how();                     /* get the indent for this line */ -      if (set_indent(count, SIN_UNDO)) { +      if (amount >= 0 && set_indent(amount, SIN_UNDO)) {          /* did change the indent, call changed_lines() later */          if (first_changed == 0)            first_changed = curwin->w_cursor.lnum; diff --git a/src/nvim/search.c b/src/nvim/search.c index fb7dfa350b..fa078dfcbf 100644 --- a/src/nvim/search.c +++ b/src/nvim/search.c @@ -1443,19 +1443,68 @@ static int check_prevcol(char_u *linep, int col, int ch, int *prevcol)  }  /* + * Raw string start is found at linep[startpos.col - 1]. + * Return TRUE if the matching end can be found between startpos and endpos. + */ +    static int +find_rawstring_end(linep, startpos, endpos) +    char_u  *linep; +    pos_T   *startpos; +    pos_T   *endpos; +{ +    char_u	*p; +    char_u	*delim_copy; +    size_t	delim_len; +    linenr_T	lnum; +    int		found = FALSE; + +    for (p = linep + startpos->col + 1; *p && *p != '('; ++p) +	; +    delim_len = (p - linep) - startpos->col - 1; +    delim_copy = vim_strnsave(linep + startpos->col + 1, delim_len); +    if (delim_copy == NULL) +	return FALSE; +    for (lnum = startpos->lnum; lnum <= endpos->lnum; ++lnum) +    { +	char_u *line = ml_get(lnum); + +	for (p = line + (lnum == startpos->lnum +					    ? startpos->col + 1 : 0); *p; ++p) +	{ +	    if (lnum == endpos->lnum && (colnr_T)(p - line) >= endpos->col) +		break; +	    if (*p == ')' && p[delim_len + 1] == '"' +			  && STRNCMP(delim_copy, p + 1, delim_len) == 0) +	    { +		found = TRUE; +		break; +	    } +	} +	if (found) +	    break; +    } +    xfree(delim_copy); +    return found; +} + +/*   * findmatchlimit -- find the matching paren or brace, if it exists within - * maxtravel lines of here.  A maxtravel of 0 means search until falling off - * the edge of the file. + * maxtravel lines of the cursor.  A maxtravel of 0 means search until falling + * off the edge of the file.   *   * "initc" is the character to find a match for.  NUL means to find the - * character at or after the cursor. + * character at or after the cursor. Special values: + * '*'  look for C-style comment / * + * '/'  look for C-style comment / *, ignoring comment-end + * '#'  look for preprocessor directives + * 'R'  look for raw string start: R"delim(text)delim" (only backwards)   *   * flags: FM_BACKWARD	search backwards (when initc is '/', '*' or '#')   *	  FM_FORWARD	search forwards (when initc is '/', '*' or '#')   *	  FM_BLOCKSTOP	stop at start/end of block ({ or } in column 0)   *	  FM_SKIPCOMM	skip comments (not implemented yet!)   * - * "oap" is only used to set oap->motion_type for a linewise motion, it be + * "oap" is only used to set oap->motion_type for a linewise motion, it can be   * NULL   */ @@ -1466,6 +1515,7 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel)    int c;    int count = 0;                        /* cumulative number of braces */    int backwards = FALSE;                /* init for gcc */ +  int raw_string = FALSE;               /* search for raw string */    int inquote = FALSE;                  /* TRUE when inside quotes */    char_u      *linep;                   /* pointer to current line */    char_u      *ptr; @@ -1506,22 +1556,23 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel)     * When '/' is used, we ignore running backwards into a star-slash, for     * "[*" command, we just want to find any comment.     */ -  if (initc == '/' || initc == '*') { +  if (initc == '/' || initc == '*' || initc == 'R') {      comment_dir = dir;      if (initc == '/')        ignore_cend = TRUE;      backwards = (dir == FORWARD) ? FALSE : TRUE; +    raw_string = (initc == 'R');      initc = NUL;    } else if (initc != '#' && initc != NUL) {      find_mps_values(&initc, &findc, &backwards, TRUE);      if (findc == NUL)        return NULL;    } -  /* -   * Either initc is '#', or no initc was given and we need to look under the -   * cursor. -   */    else { +    /* +     * Either initc is '#', or no initc was given and we need to look +     * under the cursor. +     */      if (initc == '#') {        hash_dir = dir;      } else { @@ -1766,6 +1817,26 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel)           */          if (pos.col == 0)            continue; +        else if (raw_string) +        { +          if (linep[pos.col - 1] == 'R' +              && linep[pos.col] == '"' +              && vim_strchr(linep + pos.col + 1, '(') != NULL) +          { +            /* Possible start of raw string. Now that we have the +             * delimiter we can check if it ends before where we +             * started searching, or before the previously found +             * raw string start. */ +            if (!find_rawstring_end(linep, &pos, +                  count > 0 ? &match_pos : &curwin->w_cursor)) +            { +              count++; +              match_pos = pos; +              match_pos.col--; +            } +            linep = ml_get(pos.lnum); /* may have been released */ +          } +        }          else if (  linep[pos.col - 1] == '/'                     && linep[pos.col] == '*'                     && (int)pos.col < comment_col) { diff --git a/src/nvim/version.c b/src/nvim/version.c index 49def41bb2..662fcabf17 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -190,7 +190,7 @@ static int included_patches[] = {    // 806,    // 805,    // 804, -  // 803, +  803,    802,    // 801,    // 800, diff --git a/test/functional/legacy/003_cindent_spec.lua b/test/functional/legacy/003_cindent_spec.lua index 50909e625d..19694550f4 100644 --- a/test/functional/legacy/003_cindent_spec.lua +++ b/test/functional/legacy/003_cindent_spec.lua @@ -901,6 +901,23 @@ describe('cindent', function()        {          111111111111111111;        } +      void getstring() { +      /* Raw strings */ +      const char* s = R"( +        test { +          # comment +          field: 123 +        } +       )"; +           } +      void getstring() { +      const char* s = R"foo( +        test { +          # comment +          field: 123 +        } +          )foo"; +           }        /* end of AUTO */        ]=]) @@ -1790,6 +1807,23 @@ describe('cindent', function()        {        	111111111111111111;        } +      void getstring() { +      	/* Raw strings */ +      	const char* s = R"( +        test { +          # comment +          field: 123 +        } +       )"; +      } +      void getstring() { +      	const char* s = R"foo( +        test { +          # comment +          field: 123 +        } +          )foo"; +      }        /* end of AUTO */        ]=]) @@ -4214,7 +4248,7 @@ describe('cindent', function()        ]=])    end) -  it('54 is working', function() +  it('javascript indent / vim-patch 7.4.670', function()      insert_([=[        JSSTART | 
