diff options
author | zeertzjq <zeertzjq@outlook.com> | 2022-02-01 04:23:59 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-02-01 04:23:59 +0800 |
commit | 5205bcc9049a171e90ebd01bbc0367f4ae2371d1 (patch) | |
tree | 326fb170c0e4c18b6d1941031c107397084e56bd /src | |
parent | c00b844988ddbb5c0dc2ee2b6b4dbbc2901b5fbe (diff) | |
parent | ba2bb6a81b0b25cc1670a5b9524d17c9cba27296 (diff) | |
download | rneovim-5205bcc9049a171e90ebd01bbc0367f4ae2371d1.tar.gz rneovim-5205bcc9049a171e90ebd01bbc0367f4ae2371d1.tar.bz2 rneovim-5205bcc9049a171e90ebd01bbc0367f4ae2371d1.zip |
Merge pull request #17254 from zeertzjq/vim-8.2.3787
vim-patch:8.2.{3787,3932,3934,3935,3938}: text formatting patches
Diffstat (limited to 'src')
-rw-r--r-- | src/nvim/change.c | 42 | ||||
-rw-r--r-- | src/nvim/edit.c | 23 | ||||
-rw-r--r-- | src/nvim/indent_c.c | 213 | ||||
-rw-r--r-- | src/nvim/normal.c | 5 | ||||
-rw-r--r-- | src/nvim/ops.c | 11 | ||||
-rw-r--r-- | src/nvim/search.c | 21 | ||||
-rw-r--r-- | src/nvim/testdir/test_cindent.vim | 4 | ||||
-rw-r--r-- | src/nvim/testdir/test_textformat.vim | 98 |
8 files changed, 288 insertions, 129 deletions
diff --git a/src/nvim/change.c b/src/nvim/change.c index 0c16b204e3..54c4ba5319 100644 --- a/src/nvim/change.c +++ b/src/nvim/change.c @@ -952,11 +952,13 @@ int copy_indent(int size, char_u *src) /// /// "second_line_indent": indent for after ^^D in Insert mode or if flag /// OPENLINE_COM_LIST +/// "did_do_comment" is set to true when intentionally putting the comment +/// leader in fromt of the new line. /// /// @param dir FORWARD or BACKWARD /// /// @return true on success, false on failure -int open_line(int dir, int flags, int second_line_indent) +int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) { char_u *next_line = NULL; // copy of the next line char_u *p_extra = NULL; // what goes to next line @@ -969,6 +971,7 @@ int open_line(int dir, int flags, int second_line_indent) bool retval = false; // return value int extra_len = 0; // length of p_extra string int lead_len; // length of comment leader + int comment_start = 0; // start index of the comment leader char_u *lead_flags; // position in 'comments' for comment leader char_u *leader = NULL; // copy of comment leader char_u *allocated = NULL; // allocated memory @@ -977,6 +980,7 @@ int open_line(int dir, int flags, int second_line_indent) pos_T *pos; bool do_si = (!p_paste && curbuf->b_p_si && !curbuf->b_p_cin && *curbuf->b_p_inde == NUL); + bool do_cindent; bool no_si = false; // reset did_si afterwards int first_char = NUL; // init for GCC int vreplace_mode; @@ -1189,11 +1193,30 @@ int open_line(int dir, int flags, int second_line_indent) did_ai = true; } + // May do indenting after opening a new line. + do_cindent = !p_paste && (curbuf->b_p_cin || *curbuf->b_p_inde != NUL) + && in_cinkeys(dir == FORWARD ? KEY_OPEN_FORW : KEY_OPEN_BACK, + ' ', linewhite(curwin->w_cursor.lnum)); + // Find out if the current line starts with a comment leader. // This may then be inserted in front of the new line. end_comment_pending = NUL; - if (flags & OPENLINE_DO_COM) { + if (flags & OPENLINE_DO_COM && dir == FORWARD) { + // Check for a line comment after code. lead_len = get_leader_len(saved_line, &lead_flags, dir == BACKWARD, true); + if (lead_len == 0 && do_cindent) { + comment_start = check_linecomment(saved_line); + if (comment_start != MAXCOL) { + lead_len = get_leader_len(saved_line + comment_start, + &lead_flags, false, true); + if (lead_len != 0) { + lead_len += comment_start; + if (did_do_comment != NULL) { + *did_do_comment = true; + } + } + } + } } else { lead_len = 0; } @@ -1349,6 +1372,13 @@ int open_line(int dir, int flags, int second_line_indent) STRLCPY(leader, saved_line, lead_len + 1); + // TODO(vim): handle multi-byte and double width chars + for (int li = 0; li < comment_start; li++) { + if (!ascii_iswhite(leader[li])) { + leader[li] = ' '; + } + } + // Replace leader with lead_repl, right or left adjusted if (lead_repl != NULL) { int c = 0; @@ -1758,13 +1788,7 @@ int open_line(int dir, int flags, int second_line_indent) ai_col = (colnr_T)getwhitecols_curline(); } // May do indenting after opening a new line. - if (!p_paste - && (curbuf->b_p_cin - || *curbuf->b_p_inde != NUL - ) - && in_cinkeys(dir == FORWARD - ? KEY_OPEN_FORW - : KEY_OPEN_BACK, ' ', linewhite(curwin->w_cursor.lnum))) { + if (do_cindent) { do_c_expr_indent(); ai_col = (colnr_T)getwhitecols_curline(); } diff --git a/src/nvim/edit.c b/src/nvim/edit.c index 095fa14752..f96e7261ca 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -6021,6 +6021,7 @@ static void internal_format(int textwidth, int second_indent, int flags, int for char_u *saved_text = NULL; colnr_T col; colnr_T end_col; + bool did_do_comment = false; virtcol = get_nolist_virtcol() + char2cells(c != NUL ? c : gchar_cursor()); @@ -6136,8 +6137,7 @@ static void internal_format(int textwidth, int second_indent, int flags, int for if (curwin->w_cursor.col <= (colnr_T)wantcol) { break; } - } else if ((cc >= 0x100 || !utf_allow_break_before(cc)) - && fo_multibyte) { + } else if ((cc >= 0x100 || !utf_allow_break_before(cc)) && fo_multibyte) { int ncc; bool allow_break; @@ -6294,11 +6294,18 @@ static void internal_format(int textwidth, int second_indent, int flags, int for + (fo_white_par ? OPENLINE_KEEPTRAIL : 0) + (do_comments ? OPENLINE_DO_COM : 0) + ((flags & INSCHAR_COM_LIST) ? OPENLINE_COM_LIST : 0), - ((flags & INSCHAR_COM_LIST) ? second_indent : old_indent)); + ((flags & INSCHAR_COM_LIST) ? second_indent : old_indent), + &did_do_comment); if (!(flags & INSCHAR_COM_LIST)) { old_indent = 0; } + // If a comment leader was inserted, may also do this on a following + // line. + if (did_do_comment) { + no_leader = false; + } + replace_offset = 0; if (first_line) { if (!(flags & INSCHAR_COM_LIST)) { @@ -8292,6 +8299,7 @@ static bool ins_bs(int c, int mode, int *inserted_space_p) int in_indent; int oldState; int cpc[MAX_MCO]; // composing characters + bool call_fix_indent = false; // can't delete anything in an empty file // can't backup past first character in buffer @@ -8435,6 +8443,8 @@ static bool ins_bs(int c, int mode, int *inserted_space_p) beginline(BL_WHITE); if (curwin->w_cursor.col < save_col) { mincol = curwin->w_cursor.col; + // should now fix the indent to match with the previous line + call_fix_indent = true; } curwin->w_cursor.col = save_col; } @@ -8569,6 +8579,11 @@ static bool ins_bs(int c, int mode, int *inserted_space_p) if (curwin->w_cursor.col <= 1) { did_ai = false; } + + if (call_fix_indent) { + fix_indent(); + } + // It's a little strange to put backspaces into the redo // buffer, but it makes auto-indent a lot easier to deal // with. @@ -9183,7 +9198,7 @@ static bool ins_eol(int c) AppendToRedobuff(NL_STR); bool i = open_line(FORWARD, has_format_option(FO_RET_COMS) ? OPENLINE_DO_COM : 0, - old_indent); + old_indent, NULL); old_indent = 0; can_cindent = true; // When inserting a line the cursor line must never be in a closed fold. diff --git a/src/nvim/indent_c.c b/src/nvim/indent_c.c index eb24e13d24..7f483d02ab 100644 --- a/src/nvim/indent_c.c +++ b/src/nvim/indent_c.c @@ -41,9 +41,7 @@ static pos_T *ind_find_start_comment(void) // XXX pos_T *find_start_comment(int ind_maxcomment) // XXX { - pos_T *pos; - char_u *line; - char_u *p; + pos_T *pos; int64_t cur_maxcomment = ind_maxcomment; for (;; ) { @@ -55,11 +53,9 @@ pos_T *find_start_comment(int ind_maxcomment) // XXX * Check if the comment 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) + if (!is_pos_in_string(ml_get(pos->lnum), pos->col)) { break; + } cur_maxcomment = curwin->w_cursor.lnum - pos->lnum - 1; if (cur_maxcomment <= 0) { pos = NULL; @@ -110,8 +106,6 @@ static pos_T *ind_find_start_CORS(linenr_T *is_raw) static pos_T *find_start_rawstring(int ind_maxcomment) // XXX { pos_T *pos; - char_u *line; - char_u *p; long cur_maxcomment = ind_maxcomment; for (;;) @@ -124,11 +118,9 @@ static pos_T *find_start_rawstring(int ind_maxcomment) // XXX * 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; + if (!is_pos_in_string(ml_get(pos->lnum), pos->col)) { + break; + } cur_maxcomment = curwin->w_cursor.lnum - pos->lnum - 1; if (cur_maxcomment <= 0) { @@ -143,7 +135,7 @@ static pos_T *find_start_rawstring(int ind_maxcomment) // XXX * Skip to the end of a "string" and a 'c' character. * If there is no string or character, return argument unmodified. */ -static char_u *skip_string(char_u *p) +static const char_u *skip_string(const char_u *p) { int i; @@ -178,24 +170,24 @@ static char_u *skip_string(char_u *p) 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) { - const ptrdiff_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 - } + // Raw string: R"[delim](...)[delim]" + const char_u *delim = p + 2; + const char_u *paren = vim_strchr(delim, '('); + + if (paren != NULL) { + const ptrdiff_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 } @@ -205,6 +197,16 @@ static char_u *skip_string(char_u *p) return p; } +/// @returns true if "line[col]" is inside a C string. +int is_pos_in_string(const char_u *line, colnr_T col) +{ + const char_u *p; + + for (p = line; *p && (colnr_T)(p - line) < col; p++) { + p = skip_string(p); + } + return !((colnr_T)(p - line) <= col); +} /* * Functions for C-indenting. @@ -218,7 +220,7 @@ static char_u *skip_string(char_u *p) /* * Return true if the string "line" starts with a word from 'cinwords'. */ -bool cin_is_cinword(char_u *line) +bool cin_is_cinword(const char_u *line) { bool retval = false; @@ -246,10 +248,10 @@ bool cin_is_cinword(char_u *line) * Skip over white space and C comments within the line. * Also skip over Perl/shell comments if desired. */ -static char_u *cin_skipcomment(char_u *s) +static const char_u *cin_skipcomment(const char_u *s) { while (*s) { - char_u *prev_s = s; + const char_u *prev_s = s; s = skipwhite(s); @@ -283,7 +285,7 @@ static char_u *cin_skipcomment(char_u *s) * Return TRUE if there is no code at *s. White space and comments are * not considered code. */ -static int cin_nocode(char_u *s) +static int cin_nocode(const char_u *s) { return *cin_skipcomment(s) == NUL; } @@ -312,9 +314,9 @@ static pos_T *find_line_comment(void) // XXX } /// Checks if `text` starts with "key:". -static bool cin_has_js_key(char_u *text) +static bool cin_has_js_key(const char_u *text) { - char_u *s = skipwhite(text); + const char_u *s = skipwhite(text); char_u quote = 0; if (*s == '\'' || *s == '"') { @@ -341,7 +343,7 @@ static bool cin_has_js_key(char_u *text) /// Checks if string matches "label:"; move to character after ':' if true. /// "*s" must point to the start of the label, if there is one. -static bool cin_islabel_skip(char_u **s) +static bool cin_islabel_skip(const char_u **s) FUNC_ATTR_NONNULL_ALL { if (!vim_isIDc(**s)) { // need at least one ID character @@ -361,7 +363,7 @@ static bool cin_islabel_skip(char_u **s) // Note: curwin->w_cursor must be where we are looking for the label. bool cin_islabel(void) // XXX { - char_u *s = cin_skipcomment(get_cursor_line_ptr()); + const char_u *s = cin_skipcomment(get_cursor_line_ptr()); // Exclude "default" from labels, since it should be indented // like a switch label. Same for C++ scope declarations. @@ -380,8 +382,8 @@ bool cin_islabel(void) // XXX * label. */ pos_T cursor_save; - pos_T *trypos; - char_u *line; + pos_T *trypos; + const char_u *line; cursor_save = curwin->w_cursor; while (curwin->w_cursor.lnum > 1) { @@ -424,8 +426,8 @@ bool cin_islabel(void) // XXX */ static int cin_isinit(void) { - char_u *s; - static char *skip[] = {"static", "public", "protected", "private"}; + const char_u *s; + static char *skip[] = { "static", "public", "protected", "private" }; s = cin_skipcomment(get_cursor_line_ptr()); @@ -460,7 +462,7 @@ static int cin_isinit(void) * Recognize a switch label: "case .*:" or "default:". */ bool cin_iscase( - char_u *s, + const char_u *s, bool strict // Allow relaxed check of case statement for JS ) { @@ -503,7 +505,7 @@ bool cin_iscase( /* * Recognize a "default" switch label. */ -static int cin_isdefault(char_u *s) +static int cin_isdefault(const char_u *s) { return STRNCMP(s, "default", 7) == 0 && *(s = cin_skipcomment(s + 7)) == ':' @@ -513,7 +515,7 @@ static int cin_isdefault(char_u *s) /* * Recognize a "public/private/protected" scope declaration label. */ -bool cin_isscopedecl(char_u *s) +bool cin_isscopedecl(const char_u *s) { int i; @@ -534,9 +536,9 @@ bool cin_isscopedecl(char_u *s) #define FIND_NAMESPACE_LIM 20 // Recognize a "namespace" scope declaration. -static bool cin_is_cpp_namespace(char_u *s) +static bool cin_is_cpp_namespace(const char_u *s) { - char_u *p; + const char_u *p; bool has_name = false; bool has_name_start = false; @@ -581,7 +583,7 @@ static bool cin_is_cpp_namespace(char_u *s) * case 234: a = b; * ^ */ -static char_u *after_label(char_u *l) +static const char_u *after_label(const char_u *l) { for (; *l; ++l) { if (*l == ':') { @@ -608,10 +610,10 @@ static char_u *after_label(char_u *l) */ static int get_indent_nolabel(linenr_T lnum) // XXX { - char_u *l; + const char_u *l; pos_T fp; colnr_T col; - char_u *p; + const char_u *p; l = ml_get(lnum); p = after_label(l); @@ -630,9 +632,9 @@ static int get_indent_nolabel(linenr_T lnum) // XXX * label: if (asdf && asdfasdf) * ^ */ -static int skip_label(linenr_T lnum, char_u **pp) +static int skip_label(linenr_T lnum, const char_u **pp) { - char_u *l; + const char_u *l; int amount; pos_T cursor_save; @@ -713,8 +715,8 @@ static int cin_first_id_amount(void) */ static int cin_get_equal_amount(linenr_T lnum) { - char_u *line; - char_u *s; + const char_u *line; + const char_u *s; colnr_T col; pos_T fp; @@ -752,7 +754,7 @@ static int cin_get_equal_amount(linenr_T lnum) /* * Recognize a preprocessor statement: Any line that starts with '#'. */ -static int cin_ispreproc(char_u *s) +static int cin_ispreproc(const char_u *s) { if (*skipwhite(s) == '#') return TRUE; @@ -763,9 +765,9 @@ static int cin_ispreproc(char_u *s) /// continuation line of a preprocessor statement. Decrease "*lnump" to the /// start and return the line in "*pp". /// Put the amount of indent in "*amount". -static int cin_ispreproc_cont(char_u **pp, linenr_T *lnump, int *amount) +static int cin_ispreproc_cont(const char_u **pp, linenr_T *lnump, int *amount) { - char_u *line = *pp; + const char_u *line = *pp; linenr_T lnum = *lnump; int retval = false; int candidate_amount = *amount; @@ -799,7 +801,7 @@ static int cin_ispreproc_cont(char_u **pp, linenr_T *lnump, int *amount) /* * Recognize the start of a C or C++ comment. */ -static int cin_iscomment(char_u *p) +static int cin_iscomment(const char_u *p) { return p[0] == '/' && (p[1] == '*' || p[1] == '/'); } @@ -807,7 +809,7 @@ static int cin_iscomment(char_u *p) /* * Recognize the start of a "//" comment. */ -static int cin_islinecomment(char_u *p) +static int cin_islinecomment(const char_u *p) { return p[0] == '/' && p[1] == '/'; } @@ -822,8 +824,8 @@ static int cin_islinecomment(char_u *p) * both apply in order to determine initializations). */ static char_u -cin_isterminated ( - char_u *s, +cin_isterminated( + const char_u *s, int incl_open, // include '{' at the end as terminator int incl_comma // recognize a trailing comma ) @@ -872,9 +874,9 @@ cin_isterminated ( /// lines here. /// @param[in] first_lnum Where to start looking. /// @param[in] min_lnum The line before which we will not be looking. -static int cin_isfuncdecl(char_u **sp, linenr_T first_lnum, linenr_T min_lnum) +static int cin_isfuncdecl(const char_u **sp, linenr_T first_lnum, linenr_T min_lnum) { - char_u *s; + const char_u *s; linenr_T lnum = first_lnum; linenr_T save_lnum = curwin->w_cursor.lnum; int retval = false; @@ -975,12 +977,12 @@ done: return retval; } -static int cin_isif(char_u *p) +static int cin_isif(const char_u *p) { return STRNCMP(p, "if", 2) == 0 && !vim_isIDc(p[2]); } -static int cin_iselse(char_u *p) +static int cin_iselse(const char_u *p) { if (*p == '}') { // accept "} else" p = cin_skipcomment(p + 1); @@ -988,7 +990,7 @@ static int cin_iselse(char_u *p) return STRNCMP(p, "else", 4) == 0 && !vim_isIDc(p[4]); } -static int cin_isdo(char_u *p) +static int cin_isdo(const char_u *p) { return STRNCMP(p, "do", 2) == 0 && !vim_isIDc(p[2]); } @@ -998,7 +1000,7 @@ static int cin_isdo(char_u *p) * We only accept a "while (condition) ;", with only white space between the * ')' and ';'. The condition may be spread over several lines. */ -static int cin_iswhileofdo(char_u *p, linenr_T lnum) // XXX +static int cin_iswhileofdo(const char_u *p, linenr_T lnum) // XXX { pos_T cursor_save; pos_T *trypos; @@ -1032,7 +1034,7 @@ static int cin_iswhileofdo(char_u *p, linenr_T lnum) // XXX * Otherwise return !0 and update "*poffset" to point to the place where the * string was found. */ -static int cin_is_if_for_while_before_offset(char_u *line, int *poffset) +static int cin_is_if_for_while_before_offset(const char_u *line, int *poffset) { int offset = *poffset; @@ -1076,10 +1078,10 @@ probablyFound: */ static int cin_iswhileofdo_end(int terminated) { - char_u *line; - char_u *p; - char_u *s; - pos_T *trypos; + const char_u *line; + const char_u *p; + const char_u *s; + pos_T *trypos; int i; if (terminated != ';') { // there must be a ';' at the end @@ -1119,7 +1121,7 @@ static int cin_iswhileofdo_end(int terminated) return FALSE; } -static int cin_isbreak(char_u *p) +static int cin_isbreak(const char_u *p) { return STRNCMP(p, "break", 5) == 0 && !vim_isIDc(p[5]); } @@ -1139,10 +1141,10 @@ static int cin_isbreak(char_u *p) */ static int cin_is_cpp_baseclass(cpp_baseclass_cache_T *cached) { lpos_T *pos = &cached->lpos; // find position - char_u *s; + const char_u *s; int class_or_struct, lookfor_ctor_init, cpp_base_class; linenr_T lnum = curwin->w_cursor.lnum; - char_u *line = get_cursor_line_ptr(); + const char_u *line = get_cursor_line_ptr(); if (pos->lnum <= lnum) { return cached->found; // Use the cached result @@ -1310,10 +1312,10 @@ static int get_baseclass_amount(int col) * white space and comments. Skip strings and comments. * Ignore "ignore" after "find" if it's not NULL. */ -static int cin_ends_in(char_u *s, char_u *find, char_u *ignore) +static int cin_ends_in(const char_u *s, const char_u *find, const char_u *ignore) { - char_u *p = s; - char_u *r; + const char_u *p = s; + const char_u *r; int len = (int)STRLEN(find); while (*p != NUL) { @@ -1334,7 +1336,7 @@ static int cin_ends_in(char_u *s, char_u *find, char_u *ignore) /* * Return TRUE when "s" starts with "word" and then a non-ID character. */ -static int cin_starts_with(char_u *s, char *word) +static int cin_starts_with(const char_u *s, const char *word) { int l = (int)STRLEN(word); @@ -1342,10 +1344,10 @@ static int cin_starts_with(char_u *s, char *word) } /// Recognize a `extern "C"` or `extern "C++"` linkage specifications. -static int cin_is_cpp_extern_c(char_u *s) +static int cin_is_cpp_extern_c(const char_u *s) { - char_u *p; - int has_string_literal = false; + const char_u *p; + int has_string_literal = false; s = cin_skipcomment(s); if (STRNCMP(s, "extern", 6) == 0 && (s[6] == NUL || !vim_iswordc(s[6]))) { @@ -1384,9 +1386,9 @@ static int cin_is_cpp_extern_c(char_u *s) */ static int cin_skip2pos(pos_T *trypos) { - char_u *line; - char_u *p; - char_u *new_p; + const char_u *line; + const char_u *p; + const char_u *new_p; p = line = ml_get(trypos->lnum); while (*p && (colnr_T)(p - line) < trypos->col) { @@ -1529,7 +1531,7 @@ static int corr_ind_maxparen(pos_T *startpos) * Set w_cursor.col to the column number of the last unmatched ')' or '{' in * line "l". "l" must point to the start of the line. */ -static int find_last_paren(char_u *l, int start, int end) +static int find_last_paren(const char_u *l, int start, int end) { int i; int retval = FALSE; @@ -1801,8 +1803,8 @@ int get_c_indent(void) #define BRACE_AT_START 2 // '{' is at start of line #define BRACE_AT_END 3 // '{' is at end of line linenr_T ourscope; - char_u *l; - char_u *look; + const char_u *l; + const char_u *look; char_u terminated; int lookfor; #define LOOKFOR_INITIAL 0 @@ -1906,12 +1908,25 @@ int get_c_indent(void) * If we're inside a "//" comment and there is a "//" comment in a * previous line, lineup with that one. */ - 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 (cin_islinecomment(theline)) { + pos_T linecomment_pos; + + trypos = find_line_comment(); // XXX + if (trypos == NULL && curwin->w_cursor.lnum > 1) { + // There may be a statement before the comment, search from the end + // of the line for a comment start. + linecomment_pos.col = check_linecomment(ml_get(curwin->w_cursor.lnum - 1)); + if (linecomment_pos.col != MAXCOL) { + trypos = &linecomment_pos; + trypos->lnum = curwin->w_cursor.lnum - 1; + } + } + if (trypos != NULL) { + // 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 @@ -3602,9 +3617,9 @@ laterend: static int find_match(int lookfor, linenr_T ourscope) { - char_u *look; - pos_T *theirscope; - char_u *mightbeif; + const char_u *look; + pos_T *theirscope; + const char_u *mightbeif; int elselevel; int whilelevel; diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 28e5d47dbc..6e36226914 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -6685,9 +6685,8 @@ static void n_opencmd(cmdarg_T *cap) (cap->cmdchar == 'o' ? 1 : 0)) ) && open_line(cap->cmdchar == 'O' ? BACKWARD : FORWARD, - has_format_option(FO_OPEN_COMS) - ? OPENLINE_DO_COM : 0, - 0)) { + has_format_option(FO_OPEN_COMS) ? OPENLINE_DO_COM : 0, + 0, NULL)) { if (win_cursorline_standout(curwin)) { // force redraw of cursorline curwin->w_valid &= ~VALID_CROW; diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 83a7c31991..11484eee57 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -4350,7 +4350,7 @@ void format_lines(linenr_T line_count, int avoid_fex) int leader_len = 0; // leader len of current line int next_leader_len; // leader len of next line char_u *leader_flags = NULL; // flags for leader of current line - char_u *next_leader_flags; // flags for leader of next line + char_u *next_leader_flags = NULL; // flags for leader of next line bool advance = true; int second_indent = -1; // indent for second line (comment aware) bool first_par_line = true; @@ -4467,7 +4467,14 @@ void format_lines(linenr_T line_count, int avoid_fex) leader_len, leader_flags, next_leader_len, next_leader_flags)) { - is_end_par = true; + // Special case: If the next line starts with a line comment + // and this line has a line comment after some text, the + // paragraph doesn't really end. + if (next_leader_flags == NULL + || STRNCMP(next_leader_flags, "://", 3) != 0 + || check_linecomment(get_cursor_line_ptr()) == MAXCOL) { + is_end_par = true; + } } /* diff --git a/src/nvim/search.c b/src/nvim/search.c index 906c9a6f47..93180f97fe 100644 --- a/src/nvim/search.c +++ b/src/nvim/search.c @@ -26,6 +26,7 @@ #include "nvim/func_attr.h" #include "nvim/getchar.h" #include "nvim/indent.h" +#include "nvim/indent_c.h" #include "nvim/main.h" #include "nvim/mark.h" #include "nvim/mbyte.h" @@ -2313,12 +2314,9 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel) return (pos_T *)NULL; // never found it } -/* - * Check if line[] contains a / / comment. - * Return MAXCOL if not, otherwise return the column. - * TODO: skip strings. - */ -static int check_linecomment(const char_u *line) +/// Check if line[] contains a / / comment. +/// @returns MAXCOL if not, otherwise return the column. +int check_linecomment(const char_u *line) { const char_u *p = line; // scan from start // skip Lispish one-line comments @@ -2338,7 +2336,8 @@ static int check_linecomment(const char_u *line) in_str = true; } } else if (!in_str && ((p - line) < 2 - || (*(p - 1) != '\\' && *(p - 2) != '#'))) { + || (*(p - 1) != '\\' && *(p - 2) != '#')) + && !is_pos_in_string(line, (colnr_T)(p - line))) { break; // found! } p++; @@ -2348,9 +2347,11 @@ static int check_linecomment(const char_u *line) } } else { while ((p = vim_strchr(p, '/')) != NULL) { - // accept a double /, unless it's preceded with * and followed by *, - // because * / / * is an end and start of a C comment - if (p[1] == '/' && (p == line || p[-1] != '*' || p[2] != '*')) { + // Accept a double /, unless it's preceded with * and followed by *, + // because * / / * is an end and start of a C comment. + // Only accept the position if it is not inside a string. + if (p[1] == '/' && (p == line || p[-1] != '*' || p[2] != '*') + && !is_pos_in_string(line, (colnr_T)(p - line))) { break; } ++p; diff --git a/src/nvim/testdir/test_cindent.vim b/src/nvim/testdir/test_cindent.vim index 6554d034d3..5dc54111e7 100644 --- a/src/nvim/testdir/test_cindent.vim +++ b/src/nvim/testdir/test_cindent.vim @@ -1707,9 +1707,9 @@ func Test_cindent_1() #endif int y; // comment - // comment + // comment - // comment + // comment { Constructor(int a, diff --git a/src/nvim/testdir/test_textformat.vim b/src/nvim/testdir/test_textformat.vim index bf0706a0c2..052c32214d 100644 --- a/src/nvim/testdir/test_textformat.vim +++ b/src/nvim/testdir/test_textformat.vim @@ -196,6 +196,104 @@ func Test_text_format() enew! endfunc +func Test_format_c_comment() + new + setl ai cindent tw=40 et fo=croql + let text =<< trim END + var = 2345; // asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf + END + call setline(1, text) + normal gql + let expected =<< trim END + var = 2345; // asdf asdf asdf asdf asdf + // asdf asdf asdf asdf asdf + END + call assert_equal(expected, getline(1, '$')) + + %del + let text =<< trim END + var = 2345; // asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf + END + call setline(1, text) + normal gql + let expected =<< trim END + var = 2345; // asdf asdf asdf asdf asdf + // asdf asdf asdf asdf asdf + // asdf asdf + END + call assert_equal(expected, getline(1, '$')) + + %del + let text =<< trim END + #if 0 // This is another long end of + // line comment that + // wraps. + END + call setline(1, text) + normal gq2j + let expected =<< trim END + #if 0 // This is another long + // end of line comment + // that wraps. + END + call assert_equal(expected, getline(1, '$')) + + " Using "o" repeats the line comment, "O" does not. + %del + let text =<< trim END + nop; + val = val; // This is a comment + END + call setline(1, text) + normal 2Go + let expected =<< trim END + nop; + val = val; // This is a comment + // + END + call assert_equal(expected, getline(1, '$')) + normal 2GO + let expected =<< trim END + nop; + + val = val; // This is a comment + // + END + call assert_equal(expected, getline(1, '$')) + + " Using "o" does not repeat a comment in a string + %del + let text =<< trim END + nop; + val = " // This is not a comment"; + END + call setline(1, text) + normal 2Gox + let expected =<< trim END + nop; + val = " // This is not a comment"; + x + END + call assert_equal(expected, getline(1, '$')) + + " Using CTRL-U after "o" fixes the indent + %del + let text =<< trim END + { + val = val; // This is a comment + END + call setline(1, text) + exe "normal! 2Go\<C-U>x\<Esc>" + let expected =<< trim END + { + val = val; // This is a comment + x + END + call assert_equal(expected, getline(1, '$')) + + bwipe! +endfunc + " Tests for :right, :center and :left on text with embedded TAB. func Test_format_align() enew! |