To: vim_dev@googlegroups.com Subject: Patch 8.1.2127 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.1.2127 Problem: The indent.c file is a bit big. Solution: Move C-indent code a a new cindent.c file. Move other indent-related code to indent.c. (Yegappan Lakshmanan, closes #5031) Files: Filelist, src/Make_cyg_ming.mak, src/Make_morph.mak, src/Make_mvc.mak, src/Make_vms.mms, src/Makefile, src/README.md, src/change.c, src/cindent.c, src/edit.c, src/evalfunc.c, src/ex_cmds.c, src/globals.h, src/indent.c, src/misc1.c, src/ops.c, src/proto.h, src/proto/cindent.pro, src/proto/edit.pro, src/proto/ex_cmds.pro, src/proto/indent.pro, src/proto/misc1.pro, src/proto/ops.pro, src/userfunc.c *** ../vim-8.1.2126/Filelist 2019-09-28 16:29:44.167649811 +0200 --- Filelist 2019-10-09 22:10:04.862760590 +0200 *************** *** 25,30 **** --- 25,31 ---- src/change.c \ src/channel.c \ src/charset.c \ + src/cindent.c \ src/cmdexpand.c \ src/cmdhist.c \ src/crypt.c \ *************** *** 191,196 **** --- 192,198 ---- src/proto/change.pro \ src/proto/channel.pro \ src/proto/charset.pro \ + src/proto/cindent.pro \ src/proto/cmdexpand.pro \ src/proto/cmdhist.pro \ src/proto/crypt.pro \ *** ../vim-8.1.2126/src/Make_cyg_ming.mak 2019-09-28 16:29:44.167649811 +0200 --- src/Make_cyg_ming.mak 2019-10-09 22:10:04.862760590 +0200 *************** *** 712,717 **** --- 712,718 ---- $(OUTDIR)/bufwrite.o \ $(OUTDIR)/change.o \ $(OUTDIR)/charset.o \ + $(OUTDIR)/cindent.o \ $(OUTDIR)/cmdexpand.o \ $(OUTDIR)/cmdhist.o \ $(OUTDIR)/crypt.o \ *** ../vim-8.1.2126/src/Make_morph.mak 2019-09-28 16:29:44.167649811 +0200 --- src/Make_morph.mak 2019-10-09 22:10:04.862760590 +0200 *************** *** 32,37 **** --- 32,38 ---- bufwrite.c \ change.c \ charset.c \ + cindent.c \ cmdexpand.c \ cmdhist.c \ crypt.c \ *** ../vim-8.1.2126/src/Make_mvc.mak 2019-09-28 16:29:44.167649811 +0200 --- src/Make_mvc.mak 2019-10-09 22:10:04.862760590 +0200 *************** *** 719,724 **** --- 719,725 ---- $(OUTDIR)\bufwrite.obj \ $(OUTDIR)\change.obj \ $(OUTDIR)\charset.obj \ + $(OUTDIR)\cindent.obj \ $(OUTDIR)\cmdexpand.obj \ $(OUTDIR)\cmdhist.obj \ $(OUTDIR)\crypt.obj \ *************** *** 1464,1469 **** --- 1465,1472 ---- $(OUTDIR)/charset.obj: $(OUTDIR) charset.c $(INCL) + $(OUTDIR)/cindent.obj: $(OUTDIR) cindent.c $(INCL) + $(OUTDIR)/cmdexpand.obj: $(OUTDIR) cmdexpand.c $(INCL) $(OUTDIR)/cmdhist.obj: $(OUTDIR) cmdhist.c $(INCL) *************** *** 1794,1799 **** --- 1797,1803 ---- proto/bufwrite.pro \ proto/change.pro \ proto/charset.pro \ + proto/cindent.pro \ proto/cmdexpand.pro \ proto/cmdhist.pro \ proto/crypt.pro \ *** ../vim-8.1.2126/src/Make_vms.mms 2019-09-28 16:29:44.167649811 +0200 --- src/Make_vms.mms 2019-10-09 22:10:04.862760590 +0200 *************** *** 318,323 **** --- 318,324 ---- bufwrite.c \ change.c \ charset.c \ + cindent.c \ cmdexpand.c \ cmdhist.c \ crypt.c \ *************** *** 420,425 **** --- 421,427 ---- bufwrite.obj \ change.obj \ charset.obj \ + cindent.obj \ cmdexpand.obj \ cmdhist.obj \ crypt.obj \ *************** *** 700,705 **** --- 702,711 ---- ascii.h keymap.h term.h macros.h structs.h regexp.h \ gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \ globals.h + cindent.obj : cindent.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h structs.h regexp.h \ + gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \ + globals.h cmdexpand.obj : cmdexpand.c vim.h [.auto]config.h feature.h os_unix.h \ ascii.h keymap.h term.h macros.h structs.h regexp.h \ gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \ *** ../vim-8.1.2126/src/Makefile 2019-09-28 16:29:44.167649811 +0200 --- src/Makefile 2019-10-09 22:10:04.866760561 +0200 *************** *** 1585,1590 **** --- 1587,1593 ---- buffer.c \ change.c \ charset.c \ + cindent.c \ cmdexpand.c \ cmdhist.c \ crypt.c \ *************** *** 1725,1730 **** --- 1728,1734 ---- objects/change.o \ objects/blob.o \ objects/blowfish.o \ + objects/cindent.o \ objects/cmdexpand.o \ objects/cmdhist.o \ objects/crypt.o \ *************** *** 1878,1883 **** --- 1882,1888 ---- buffer.pro \ change.pro \ charset.pro \ + cindent.pro \ cmdexpand.pro \ cmdhist.pro \ crypt.pro \ *************** *** 3081,3086 **** --- 3086,3094 ---- objects/charset.o: charset.c $(CCC) -o $@ charset.c + objects/cindent.o: cindent.c + $(CCC) -o $@ cindent.c + objects/cmdexpand.o: cmdexpand.c $(CCC) -o $@ cmdexpand.c *************** *** 3621,3626 **** --- 3629,3638 ---- auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ proto.h globals.h + objects/cindent.o: cindent.c vim.h protodef.h auto/config.h feature.h os_unix.h \ + auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h objects/cmdexpand.o: cmdexpand.c vim.h protodef.h auto/config.h feature.h os_unix.h \ auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ *** ../vim-8.1.2126/src/README.md 2019-09-28 16:29:44.167649811 +0200 --- src/README.md 2019-10-09 22:10:04.866760561 +0200 *************** *** 29,34 **** --- 29,35 ---- buffer.c | manipulating buffers (loaded files) bufwrite.c | writing a buffer to file change.c | handling changes to text + cindent.c | C and Lisp indentation cmdexpand.c | command-line completion cmdhist.c | command-line history debugger.c | vim script debugger *************** *** 46,52 **** fold.c | folding getchar.c | getting characters and key mapping highlight.c | syntax highlighting ! indent.c | C and Lisp indentation insexpand.c | Insert mode completion mark.c | marks map.c | mapping and abbreviations --- 47,53 ---- fold.c | folding getchar.c | getting characters and key mapping highlight.c | syntax highlighting ! indent.c | text indentation insexpand.c | Insert mode completion mark.c | marks map.c | mapping and abbreviations *** ../vim-8.1.2126/src/change.c 2019-10-01 17:01:56.342282818 +0200 --- src/change.c 2019-10-09 22:10:04.866760561 +0200 *************** *** 1251,1401 **** } /* - * Copy the indent from ptr to the current line (and fill to size) - * Leaves the cursor on the first non-blank in the line. - * Returns TRUE if the line was changed. - */ - static int - copy_indent(int size, char_u *src) - { - char_u *p = NULL; - char_u *line = NULL; - char_u *s; - int todo; - int ind_len; - int line_len = 0; - int tab_pad; - int ind_done; - int round; - #ifdef FEAT_VARTABS - int ind_col; - #endif - - // Round 1: compute the number of characters needed for the indent - // Round 2: copy the characters. - for (round = 1; round <= 2; ++round) - { - todo = size; - ind_len = 0; - ind_done = 0; - #ifdef FEAT_VARTABS - ind_col = 0; - #endif - s = src; - - // Count/copy the usable portion of the source line - while (todo > 0 && VIM_ISWHITE(*s)) - { - if (*s == TAB) - { - #ifdef FEAT_VARTABS - tab_pad = tabstop_padding(ind_done, curbuf->b_p_ts, - curbuf->b_p_vts_array); - #else - tab_pad = (int)curbuf->b_p_ts - - (ind_done % (int)curbuf->b_p_ts); - #endif - // Stop if this tab will overshoot the target - if (todo < tab_pad) - break; - todo -= tab_pad; - ind_done += tab_pad; - #ifdef FEAT_VARTABS - ind_col += tab_pad; - #endif - } - else - { - --todo; - ++ind_done; - #ifdef FEAT_VARTABS - ++ind_col; - #endif - } - ++ind_len; - if (p != NULL) - *p++ = *s; - ++s; - } - - // Fill to next tabstop with a tab, if possible - #ifdef FEAT_VARTABS - tab_pad = tabstop_padding(ind_done, curbuf->b_p_ts, - curbuf->b_p_vts_array); - #else - tab_pad = (int)curbuf->b_p_ts - (ind_done % (int)curbuf->b_p_ts); - #endif - if (todo >= tab_pad && !curbuf->b_p_et) - { - todo -= tab_pad; - ++ind_len; - #ifdef FEAT_VARTABS - ind_col += tab_pad; - #endif - if (p != NULL) - *p++ = TAB; - } - - // Add tabs required for indent - if (!curbuf->b_p_et) - { - #ifdef FEAT_VARTABS - for (;;) - { - tab_pad = tabstop_padding(ind_col, curbuf->b_p_ts, - curbuf->b_p_vts_array); - if (todo < tab_pad) - break; - todo -= tab_pad; - ++ind_len; - ind_col += tab_pad; - if (p != NULL) - *p++ = TAB; - } - #else - while (todo >= (int)curbuf->b_p_ts) - { - todo -= (int)curbuf->b_p_ts; - ++ind_len; - if (p != NULL) - *p++ = TAB; - } - #endif - } - - // Count/add spaces required for indent - while (todo > 0) - { - --todo; - ++ind_len; - if (p != NULL) - *p++ = ' '; - } - - if (p == NULL) - { - // Allocate memory for the result: the copied indent, new indent - // and the rest of the line. - line_len = (int)STRLEN(ml_get_curline()) + 1; - line = alloc(ind_len + line_len); - if (line == NULL) - return FALSE; - p = line; - } - } - - // Append the original line - mch_memmove(p, ml_get_curline(), (size_t)line_len); - - // Replace the line - ml_replace(curwin->w_cursor.lnum, line, FALSE); - - // Put the cursor after the indent. - curwin->w_cursor.col = ind_len; - return TRUE; - } - - /* * open_line: Add a new line below or above the current line. * * For VREPLACE mode, we only add a new line when we get to the end of the --- 1251,1256 ---- *** ../vim-8.1.2126/src/cindent.c 2019-10-09 22:52:01.924291383 +0200 --- src/cindent.c 2019-10-09 22:36:38.301533106 +0200 *************** *** 0 **** --- 1,4133 ---- + /* vi:set ts=8 sts=4 sw=4 noet: + * + * VIM - Vi IMproved by Bram Moolenaar + * + * Do ":help uganda" in Vim to read copying and usage conditions. + * Do ":help credits" in Vim to see a list of people who contributed. + * See README.txt for an overview of the Vim source code. + */ + + /* + * cindent.c: C indentation related functions + * + * Many of C-indenting functions originally come from Eric Fischer. + * + * Below "XXX" means that this function may unlock the current line. + */ + + #include "vim.h" + + // values for the "lookfor" state + #define LOOKFOR_INITIAL 0 + #define LOOKFOR_IF 1 + #define LOOKFOR_DO 2 + #define LOOKFOR_CASE 3 + #define LOOKFOR_ANY 4 + #define LOOKFOR_TERM 5 + #define LOOKFOR_UNTERM 6 + #define LOOKFOR_SCOPEDECL 7 + #define LOOKFOR_NOBREAK 8 + #define LOOKFOR_CPP_BASECLASS 9 + #define LOOKFOR_ENUM_OR_INIT 10 + #define LOOKFOR_JS_KEY 11 + #define LOOKFOR_COMMA 12 + + #if defined(FEAT_CINDENT) || defined(FEAT_SMARTINDENT) + /* + * Return TRUE if the string "line" starts with a word from 'cinwords'. + */ + int + cin_is_cinword(char_u *line) + { + char_u *cinw; + char_u *cinw_buf; + int cinw_len; + int retval = FALSE; + int len; + + cinw_len = (int)STRLEN(curbuf->b_p_cinw) + 1; + cinw_buf = alloc(cinw_len); + if (cinw_buf != NULL) + { + line = skipwhite(line); + for (cinw = curbuf->b_p_cinw; *cinw; ) + { + len = copy_option_part(&cinw, cinw_buf, cinw_len, ","); + if (STRNCMP(line, cinw_buf, len) == 0 + && (!vim_iswordc(line[len]) || !vim_iswordc(line[len - 1]))) + { + retval = TRUE; + break; + } + } + vim_free(cinw_buf); + } + return retval; + } + #endif + + #if defined(FEAT_CINDENT) || defined(FEAT_SYN_HL) + + /* + * 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) + { + int i; + + // We loop, because strings may be concatenated: "date""time". + for ( ; ; ++p) + { + if (p[0] == '\'') // 'c' or '\n' or '\000' + { + if (!p[1]) // ' at end of line + break; + i = 2; + if (p[1] == '\\') // '\n' or '\000' + { + ++i; + while (vim_isdigit(p[i - 1])) // '\000' + ++i; + } + if (p[i] == '\'') // check for trailing ' + { + p += i; + continue; + } + } + else if (p[0] == '"') // start of string + { + for (++p; p[0]; ++p) + { + if (p[0] == '\\' && p[1] != NUL) + ++p; + else if (p[0] == '"') // end of string + break; + } + if (p[0] == '"') + 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 + } + if (!*p) + --p; // backup from NUL + return p; + } + + /* + * 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 + { + return find_start_comment(curbuf->b_ind_maxcomment); + } + + pos_T * + find_start_comment(int ind_maxcomment) // XXX + { + pos_T *pos; + char_u *line; + char_u *p; + int cur_maxcomment = ind_maxcomment; + + for (;;) + { + pos = findmatchlimit(NULL, '*', FM_BACKWARD, cur_maxcomment); + if (pos == NULL) + break; + + // 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) + break; + cur_maxcomment = curwin->w_cursor.lnum - pos->lnum - 1; + if (cur_maxcomment <= 0) + { + pos = NULL; + break; + } + } + return 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; + } + + /* + * 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. + * If is_raw is given and returns start of raw_string, sets it to true. + * Return NULL when not inside a comment or raw string. + * "CORS" -> Comment Or Raw String + */ + static pos_T * + ind_find_start_CORS(linenr_T *is_raw) // XXX + { + static pos_T comment_pos_copy; + pos_T *comment_pos; + pos_T *rs_pos; + + comment_pos = find_start_comment(curbuf->b_ind_maxcomment); + if (comment_pos != NULL) + { + // Need to make a copy of the static pos in findmatchlimit(), + // calling find_start_rawstring() may change it. + comment_pos_copy = *comment_pos; + comment_pos = &comment_pos_copy; + } + 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_POS(*rs_pos, *comment_pos))) + { + if (is_raw != NULL && rs_pos != NULL) + *is_raw = rs_pos->lnum; + return rs_pos; + } + return comment_pos; + } + #endif // FEAT_CINDENT || FEAT_SYN_HL + + #if defined(FEAT_CINDENT) || defined(PROTO) + + /* + * Return TRUE if C-indenting is on. + */ + int + cindent_on(void) + { + return (!p_paste && (curbuf->b_p_cin + # ifdef FEAT_EVAL + || *curbuf->b_p_inde != NUL + # endif + )); + } + + // Find result cache for cpp_baseclass + typedef struct { + int found; + lpos_T lpos; + } cpp_baseclass_cache_T; + + /* + * 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) + { + while (*s) + { + char_u *prev_s = s; + + s = skipwhite(s); + + // Perl/shell # comment comment continues until eol. Require a space + // before # to avoid recognizing $#array. + if (curbuf->b_ind_hash_comment != 0 && s != prev_s && *s == '#') + { + s += STRLEN(s); + break; + } + if (*s != '/') + break; + ++s; + if (*s == '/') // slash-slash comment continues till eol + { + s += STRLEN(s); + break; + } + if (*s != '*') + break; + for (++s; *s; ++s) // skip slash-star comment + if (s[0] == '*' && s[1] == '/') + { + s += 2; + break; + } + } + return 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) + { + return *cin_skipcomment(s) == NUL; + } + + /* + * Recognize the start of a C or C++ comment. + */ + static int + cin_iscomment(char_u *p) + { + return (p[0] == '/' && (p[1] == '*' || p[1] == '/')); + } + + /* + * Recognize the start of a "//" comment. + */ + static int + cin_islinecomment(char_u *p) + { + return (p[0] == '/' && p[1] == '/'); + } + + /* + * Check previous lines for a "//" line comment, skipping over blank lines. + */ + static pos_T * + find_line_comment(void) // XXX + { + static pos_T pos; + char_u *line; + char_u *p; + + pos = curwin->w_cursor; + while (--pos.lnum > 0) + { + line = ml_get(pos.lnum); + p = skipwhite(line); + if (cin_islinecomment(p)) + { + pos.col = (int)(p - line); + return &pos; + } + if (*p != NUL) + break; + } + return NULL; + } + + /* + * Return TRUE if "text" starts with "key:". + */ + static int + cin_has_js_key(char_u *text) + { + char_u *s = skipwhite(text); + int quote = -1; + + if (*s == '\'' || *s == '"') + { + // can be 'key': or "key": + quote = *s; + ++s; + } + if (!vim_isIDc(*s)) // need at least one ID character + return FALSE; + + while (vim_isIDc(*s)) + ++s; + if (*s == quote) + ++s; + + s = cin_skipcomment(s); + + // "::" is not a label, it's C++ + return (*s == ':' && s[1] != ':'); + } + + /* + * Check if string matches "label:"; move to character after ':' if true. + * "*s" must point to the start of the label, if there is one. + */ + static int + cin_islabel_skip(char_u **s) + { + if (!vim_isIDc(**s)) // need at least one ID character + return FALSE; + + while (vim_isIDc(**s)) + (*s)++; + + *s = cin_skipcomment(*s); + + // "::" is not a label, it's C++ + return (**s == ':' && *++*s != ':'); + } + + /* + * Recognize a "public/private/protected" scope declaration label. + */ + static int + cin_isscopedecl(char_u *s) + { + int i; + + s = cin_skipcomment(s); + if (STRNCMP(s, "public", 6) == 0) + i = 6; + else if (STRNCMP(s, "protected", 9) == 0) + i = 9; + else if (STRNCMP(s, "private", 7) == 0) + i = 7; + else + return FALSE; + return (*(s = cin_skipcomment(s + i)) == ':' && s[1] != ':'); + } + + /* + * Recognize a preprocessor statement: Any line that starts with '#'. + */ + static int + cin_ispreproc(char_u *s) + { + if (*skipwhite(s) == '#') + return TRUE; + return FALSE; + } + + /* + * Return TRUE if line "*pp" at "*lnump" is a preprocessor statement or a + * 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) + { + char_u *line = *pp; + linenr_T lnum = *lnump; + int retval = FALSE; + int candidate_amount = *amount; + + if (*line != NUL && line[STRLEN(line) - 1] == '\\') + candidate_amount = get_indent_lnum(lnum); + + for (;;) + { + if (cin_ispreproc(line)) + { + retval = TRUE; + *lnump = lnum; + break; + } + if (lnum == 1) + break; + line = ml_get(--lnum); + if (*line == NUL || line[STRLEN(line) - 1] != '\\') + break; + } + + if (lnum != *lnump) + *pp = ml_get(*lnump); + if (retval) + *amount = candidate_amount; + return retval; + } + + static int + cin_iselse( + char_u *p) + { + if (*p == '}') // accept "} else" + p = cin_skipcomment(p + 1); + return (STRNCMP(p, "else", 4) == 0 && !vim_isIDc(p[4])); + } + + /* + * Recognize a line that starts with '{' or '}', or ends with ';', ',', '{' or + * '}'. + * Don't consider "} else" a terminated line. + * If a line begins with an "else", only consider it terminated if no unmatched + * opening braces follow (handle "else { foo();" correctly). + * Return the character terminating the line (ending char's have precedence if + * both apply in order to determine initializations). + */ + static int + cin_isterminated( + char_u *s, + int incl_open, // include '{' at the end as terminator + int incl_comma) // recognize a trailing comma + { + char_u found_start = 0; + unsigned n_open = 0; + int is_else = FALSE; + + s = cin_skipcomment(s); + + if (*s == '{' || (*s == '}' && !cin_iselse(s))) + found_start = *s; + + if (!found_start) + is_else = cin_iselse(s); + + while (*s) + { + // skip over comments, "" strings and 'c'haracters + s = skip_string(cin_skipcomment(s)); + if (*s == '}' && n_open > 0) + --n_open; + if ((!is_else || n_open == 0) + && (*s == ';' || *s == '}' || (incl_comma && *s == ',')) + && cin_nocode(s + 1)) + return *s; + else if (*s == '{') + { + if (incl_open && cin_nocode(s + 1)) + return *s; + else + ++n_open; + } + + if (*s) + s++; + } + return found_start; + } + + /* + * Return TRUE when "s" starts with "word" and then a non-ID character. + */ + static int + cin_starts_with(char_u *s, char *word) + { + int l = (int)STRLEN(word); + + return (STRNCMP(s, word, l) == 0 && !vim_isIDc(s[l])); + } + + /* + * Recognize a "default" switch label. + */ + static int + cin_isdefault(char_u *s) + { + return (STRNCMP(s, "default", 7) == 0 + && *(s = cin_skipcomment(s + 7)) == ':' + && s[1] != ':'); + } + + /* + * Recognize a switch label: "case .*:" or "default:". + */ + static int + cin_iscase( + char_u *s, + int strict) // Allow relaxed check of case statement for JS + { + s = cin_skipcomment(s); + if (cin_starts_with(s, "case")) + { + for (s += 4; *s; ++s) + { + s = cin_skipcomment(s); + if (*s == ':') + { + if (s[1] == ':') // skip over "::" for C++ + ++s; + else + return TRUE; + } + if (*s == '\'' && s[1] && s[2] == '\'') + s += 2; // skip over ':' + else if (*s == '/' && (s[1] == '*' || s[1] == '/')) + return FALSE; // stop at comment + else if (*s == '"') + { + // JS etc. + if (strict) + return FALSE; // stop at string + else + return TRUE; + } + } + return FALSE; + } + + if (cin_isdefault(s)) + return TRUE; + return FALSE; + } + + /* + * Recognize a label: "label:". + * Note: curwin->w_cursor must be where we are looking for the label. + */ + static int + cin_islabel(void) // XXX + { + char_u *s; + + s = cin_skipcomment(ml_get_curline()); + + // Exclude "default" from labels, since it should be indented + // like a switch label. Same for C++ scope declarations. + if (cin_isdefault(s)) + return FALSE; + if (cin_isscopedecl(s)) + return FALSE; + + if (cin_islabel_skip(&s)) + { + // Only accept a label if the previous line is terminated or is a case + // label. + pos_T cursor_save; + pos_T *trypos; + char_u *line; + + cursor_save = curwin->w_cursor; + while (curwin->w_cursor.lnum > 1) + { + --curwin->w_cursor.lnum; + + // 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_CORS(NULL)) != NULL) // XXX + curwin->w_cursor = *trypos; + + line = ml_get_curline(); + if (cin_ispreproc(line)) // ignore #defines, #if, etc. + continue; + if (*(line = cin_skipcomment(line)) == NUL) + continue; + + curwin->w_cursor = cursor_save; + if (cin_isterminated(line, TRUE, FALSE) + || cin_isscopedecl(line) + || cin_iscase(line, TRUE) + || (cin_islabel_skip(&line) && cin_nocode(line))) + return TRUE; + return FALSE; + } + curwin->w_cursor = cursor_save; + return TRUE; // label at start of file??? + } + return FALSE; + } + + /* + * Return TRUE if string "s" ends with the string "find", possibly followed by + * 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) + { + char_u *p = s; + char_u *r; + int len = (int)STRLEN(find); + + while (*p != NUL) + { + p = cin_skipcomment(p); + if (STRNCMP(p, find, len) == 0) + { + r = skipwhite(p + len); + if (ignore != NULL && STRNCMP(r, ignore, STRLEN(ignore)) == 0) + r = skipwhite(r + STRLEN(ignore)); + if (cin_nocode(r)) + return TRUE; + } + if (*p != NUL) + ++p; + } + return FALSE; + } + + /* + * Recognize structure initialization and enumerations: + * "[typedef] [static|public|protected|private] enum" + * "[typedef] [static|public|protected|private] = {" + */ + static int + cin_isinit(void) + { + char_u *s; + static char *skip[] = {"static", "public", "protected", "private"}; + + s = cin_skipcomment(ml_get_curline()); + + if (cin_starts_with(s, "typedef")) + s = cin_skipcomment(s + 7); + + for (;;) + { + int i, l; + + for (i = 0; i < (int)(sizeof(skip) / sizeof(char *)); ++i) + { + l = (int)strlen(skip[i]); + if (cin_starts_with(s, skip[i])) + { + s = cin_skipcomment(s + l); + l = 0; + break; + } + } + if (l != 0) + break; + } + + if (cin_starts_with(s, "enum")) + return TRUE; + + if (cin_ends_in(s, (char_u *)"=", (char_u *)"{")) + return TRUE; + + return FALSE; + } + + // Maximum number of lines to search back for a "namespace" line. + #define FIND_NAMESPACE_LIM 20 + + /* + * Recognize a "namespace" scope declaration. + */ + static int + cin_is_cpp_namespace(char_u *s) + { + char_u *p; + int has_name = FALSE; + int has_name_start = FALSE; + + s = cin_skipcomment(s); + if (STRNCMP(s, "namespace", 9) == 0 && (s[9] == NUL || !vim_iswordc(s[9]))) + { + p = cin_skipcomment(skipwhite(s + 9)); + while (*p != NUL) + { + if (VIM_ISWHITE(*p)) + { + has_name = TRUE; // found end of a name + p = cin_skipcomment(skipwhite(p)); + } + else if (*p == '{') + { + break; + } + else if (vim_iswordc(*p)) + { + has_name_start = TRUE; + if (has_name) + return FALSE; // word character after skipping past name + ++p; + } + else if (p[0] == ':' && p[1] == ':' && vim_iswordc(p[2])) + { + if (!has_name_start || has_name) + return FALSE; + // C++ 17 nested namespace + p += 3; + } + else + { + return FALSE; + } + } + return TRUE; + } + return FALSE; + } + + /* + * Recognize a `extern "C"` or `extern "C++"` linkage specifications. + */ + static int + cin_is_cpp_extern_c(char_u *s) + { + 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]))) + { + p = cin_skipcomment(skipwhite(s + 6)); + while (*p != NUL) + { + if (VIM_ISWHITE(*p)) + { + p = cin_skipcomment(skipwhite(p)); + } + else if (*p == '{') + { + break; + } + else if (p[0] == '"' && p[1] == 'C' && p[2] == '"') + { + if (has_string_literal) + return FALSE; + has_string_literal = TRUE; + p += 3; + } + else if (p[0] == '"' && p[1] == 'C' && p[2] == '+' && p[3] == '+' + && p[4] == '"') + { + if (has_string_literal) + return FALSE; + has_string_literal = TRUE; + p += 5; + } + else + { + return FALSE; + } + } + return has_string_literal ? TRUE : FALSE; + } + return FALSE; + } + + /* + * Return a pointer to the first non-empty non-comment character after a ':'. + * Return NULL if not found. + * case 234: a = b; + * ^ + */ + static char_u * + after_label(char_u *l) + { + for ( ; *l; ++l) + { + if (*l == ':') + { + if (l[1] == ':') // skip over "::" for C++ + ++l; + else if (!cin_iscase(l + 1, FALSE)) + break; + } + else if (*l == '\'' && l[1] && l[2] == '\'') + l += 2; // skip over 'x' + } + if (*l == NUL) + return NULL; + l = cin_skipcomment(l + 1); + if (*l == NUL) + return NULL; + return l; + } + + /* + * Get indent of line "lnum", skipping a label. + * Return 0 if there is nothing after the label. + */ + static int + get_indent_nolabel (linenr_T lnum) // XXX + { + char_u *l; + pos_T fp; + colnr_T col; + char_u *p; + + l = ml_get(lnum); + p = after_label(l); + if (p == NULL) + return 0; + + fp.col = (colnr_T)(p - l); + fp.lnum = lnum; + getvcol(curwin, &fp, &col, NULL, NULL); + return (int)col; + } + + /* + * Find indent for line "lnum", ignoring any case or jump label. + * Also return a pointer to the text (after the label) in "pp". + * label: if (asdf && asdfasdf) + * ^ + */ + static int + skip_label(linenr_T lnum, char_u **pp) + { + char_u *l; + int amount; + pos_T cursor_save; + + cursor_save = curwin->w_cursor; + curwin->w_cursor.lnum = lnum; + l = ml_get_curline(); + // XXX + if (cin_iscase(l, FALSE) || cin_isscopedecl(l) || cin_islabel()) + { + amount = get_indent_nolabel(lnum); + l = after_label(ml_get_curline()); + if (l == NULL) // just in case + l = ml_get_curline(); + } + else + { + amount = get_indent(); + l = ml_get_curline(); + } + *pp = l; + + curwin->w_cursor = cursor_save; + return amount; + } + + /* + * Return the indent of the first variable name after a type in a declaration. + * int a, indent of "a" + * static struct foo b, indent of "b" + * enum bla c, indent of "c" + * Returns zero when it doesn't look like a declaration. + */ + static int + cin_first_id_amount(void) + { + char_u *line, *p, *s; + int len; + pos_T fp; + colnr_T col; + + line = ml_get_curline(); + p = skipwhite(line); + len = (int)(skiptowhite(p) - p); + if (len == 6 && STRNCMP(p, "static", 6) == 0) + { + p = skipwhite(p + 6); + len = (int)(skiptowhite(p) - p); + } + if (len == 6 && STRNCMP(p, "struct", 6) == 0) + p = skipwhite(p + 6); + else if (len == 4 && STRNCMP(p, "enum", 4) == 0) + p = skipwhite(p + 4); + else if ((len == 8 && STRNCMP(p, "unsigned", 8) == 0) + || (len == 6 && STRNCMP(p, "signed", 6) == 0)) + { + s = skipwhite(p + len); + if ((STRNCMP(s, "int", 3) == 0 && VIM_ISWHITE(s[3])) + || (STRNCMP(s, "long", 4) == 0 && VIM_ISWHITE(s[4])) + || (STRNCMP(s, "short", 5) == 0 && VIM_ISWHITE(s[5])) + || (STRNCMP(s, "char", 4) == 0 && VIM_ISWHITE(s[4]))) + p = s; + } + for (len = 0; vim_isIDc(p[len]); ++len) + ; + if (len == 0 || !VIM_ISWHITE(p[len]) || cin_nocode(p)) + return 0; + + p = skipwhite(p + len); + fp.lnum = curwin->w_cursor.lnum; + fp.col = (colnr_T)(p - line); + getvcol(curwin, &fp, &col, NULL, NULL); + return (int)col; + } + + /* + * Return the indent of the first non-blank after an equal sign. + * char *foo = "here"; + * Return zero if no (useful) equal sign found. + * Return -1 if the line above "lnum" ends in a backslash. + * foo = "asdf\ + * asdf\ + * here"; + */ + static int + cin_get_equal_amount(linenr_T lnum) + { + char_u *line; + char_u *s; + colnr_T col; + pos_T fp; + + if (lnum > 1) + { + line = ml_get(lnum - 1); + if (*line != NUL && line[STRLEN(line) - 1] == '\\') + return -1; + } + + line = s = ml_get(lnum); + while (*s != NUL && vim_strchr((char_u *)"=;{}\"'", *s) == NULL) + { + if (cin_iscomment(s)) // ignore comments + s = cin_skipcomment(s); + else + ++s; + } + if (*s != '=') + return 0; + + s = skipwhite(s + 1); + if (cin_nocode(s)) + return 0; + + if (*s == '"') // nice alignment for continued strings + ++s; + + fp.lnum = lnum; + fp.col = (colnr_T)(s - line); + getvcol(curwin, &fp, &col, NULL, NULL); + return (int)col; + } + + /* + * Skip strings, chars and comments until at or past "trypos". + * Return the column found. + */ + static int + cin_skip2pos(pos_T *trypos) + { + char_u *line; + char_u *p; + char_u *new_p; + + p = line = ml_get(trypos->lnum); + while (*p && (colnr_T)(p - line) < trypos->col) + { + if (cin_iscomment(p)) + p = cin_skipcomment(p); + else + { + new_p = skip_string(p); + if (new_p == p) + ++p; + else + p = new_p; + } + } + return (int)(p - line); + } + + static pos_T * + find_match_char(int c, int ind_maxparen) // XXX + { + pos_T cursor_save; + pos_T *trypos; + static pos_T pos_copy; + int ind_maxp_wk; + + cursor_save = curwin->w_cursor; + ind_maxp_wk = ind_maxparen; + retry: + if ((trypos = findmatchlimit(NULL, c, 0, ind_maxp_wk)) != NULL) + { + // check if the ( is in a // comment + if ((colnr_T)cin_skip2pos(trypos) > trypos->col) + { + ind_maxp_wk = ind_maxparen - (int)(cursor_save.lnum - trypos->lnum); + if (ind_maxp_wk > 0) + { + curwin->w_cursor = *trypos; + curwin->w_cursor.col = 0; // XXX + goto retry; + } + trypos = NULL; + } + else + { + pos_T *trypos_wk; + + pos_copy = *trypos; // copy trypos, findmatch will change it + trypos = &pos_copy; + curwin->w_cursor = *trypos; + if ((trypos_wk = ind_find_start_CORS(NULL)) != NULL) // XXX + { + ind_maxp_wk = ind_maxparen - (int)(cursor_save.lnum + - trypos_wk->lnum); + if (ind_maxp_wk > 0) + { + curwin->w_cursor = *trypos_wk; + goto retry; + } + trypos = NULL; + } + } + } + curwin->w_cursor = cursor_save; + return trypos; + } + + /* + * Find the matching '(', ignoring it if it is in a comment. + * Return NULL if no match found. + */ + static pos_T * + find_match_paren(int ind_maxparen) // XXX + { + return find_match_char('(', ind_maxparen); + } + + /* + * 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) + { + int i; + int retval = FALSE; + int open_count = 0; + + curwin->w_cursor.col = 0; // default is start of line + + for (i = 0; l[i] != NUL; i++) + { + i = (int)(cin_skipcomment(l + i) - l); // ignore parens in comments + i = (int)(skip_string(l + i) - l); // ignore parens in quotes + if (l[i] == start) + ++open_count; + else if (l[i] == end) + { + if (open_count > 0) + --open_count; + else + { + curwin->w_cursor.col = i; + retval = TRUE; + } + } + } + return retval; + } + + /* + * Recognize the basic picture of a function declaration -- it needs to + * have an open paren somewhere and a close paren at the end of the line and + * no semicolons anywhere. + * When a line ends in a comma we continue looking in the next line. + * "sp" points to a string with the line. When looking at other lines it must + * be restored to the line. When it's NULL fetch lines here. + * "first_lnum" is where we start looking. + * "min_lnum" is the line before which we will not be looking. + */ + static int + cin_isfuncdecl( + char_u **sp, + linenr_T first_lnum, + linenr_T min_lnum) + { + char_u *s; + linenr_T lnum = first_lnum; + linenr_T save_lnum = curwin->w_cursor.lnum; + int retval = FALSE; + pos_T *trypos; + int just_started = TRUE; + + if (sp == NULL) + s = ml_get(lnum); + else + s = *sp; + + curwin->w_cursor.lnum = lnum; + if (find_last_paren(s, '(', ')') + && (trypos = find_match_paren(curbuf->b_ind_maxparen)) != NULL) + { + lnum = trypos->lnum; + if (lnum < min_lnum) + { + curwin->w_cursor.lnum = save_lnum; + return FALSE; + } + + s = ml_get(lnum); + } + curwin->w_cursor.lnum = save_lnum; + + // Ignore line starting with #. + if (cin_ispreproc(s)) + return FALSE; + + while (*s && *s != '(' && *s != ';' && *s != '\'' && *s != '"') + { + if (cin_iscomment(s)) // ignore comments + s = cin_skipcomment(s); + else if (*s == ':') + { + if (*(s + 1) == ':') + s += 2; + else + // To avoid a mistake in the following situation: + // A::A(int a, int b) + // : a(0) // <--not a function decl + // , b(0) + // {... + return FALSE; + } + else + ++s; + } + if (*s != '(') + return FALSE; // ';', ' or " before any () or no '(' + + while (*s && *s != ';' && *s != '\'' && *s != '"') + { + if (*s == ')' && cin_nocode(s + 1)) + { + // ')' at the end: may have found a match + // Check for he previous line not to end in a backslash: + // #if defined(x) && {backslash} + // defined(y) + lnum = first_lnum - 1; + s = ml_get(lnum); + if (*s == NUL || s[STRLEN(s) - 1] != '\\') + retval = TRUE; + goto done; + } + if ((*s == ',' && cin_nocode(s + 1)) || s[1] == NUL || cin_nocode(s)) + { + int comma = (*s == ','); + + // ',' at the end: continue looking in the next line. + // At the end: check for ',' in the next line, for this style: + // func(arg1 + // , arg2) + for (;;) + { + if (lnum >= curbuf->b_ml.ml_line_count) + break; + s = ml_get(++lnum); + if (!cin_ispreproc(s)) + break; + } + if (lnum >= curbuf->b_ml.ml_line_count) + break; + // Require a comma at end of the line or a comma or ')' at the + // start of next line. + s = skipwhite(s); + if (!just_started && (!comma && *s != ',' && *s != ')')) + break; + just_started = FALSE; + } + else if (cin_iscomment(s)) // ignore comments + s = cin_skipcomment(s); + else + { + ++s; + just_started = FALSE; + } + } + + done: + if (lnum != first_lnum && sp != NULL) + *sp = ml_get(first_lnum); + + return retval; + } + + static int + cin_isif(char_u *p) + { + return (STRNCMP(p, "if", 2) == 0 && !vim_isIDc(p[2])); + } + + static int + cin_isdo(char_u *p) + { + return (STRNCMP(p, "do", 2) == 0 && !vim_isIDc(p[2])); + } + + /* + * Check if this is a "while" that should have a matching "do". + * 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 + { + pos_T cursor_save; + pos_T *trypos; + int retval = FALSE; + + p = cin_skipcomment(p); + if (*p == '}') // accept "} while (cond);" + p = cin_skipcomment(p + 1); + if (cin_starts_with(p, "while")) + { + cursor_save = curwin->w_cursor; + curwin->w_cursor.lnum = lnum; + curwin->w_cursor.col = 0; + p = ml_get_curline(); + while (*p && *p != 'w') // skip any '}', until the 'w' of the "while" + { + ++p; + ++curwin->w_cursor.col; + } + if ((trypos = findmatchlimit(NULL, 0, 0, + curbuf->b_ind_maxparen)) != NULL + && *cin_skipcomment(ml_get_pos(trypos) + 1) == ';') + retval = TRUE; + curwin->w_cursor = cursor_save; + } + return retval; + } + + /* + * Check whether in "p" there is an "if", "for" or "while" before "*poffset". + * Return 0 if there is none. + * 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) + { + int offset = *poffset; + + if (offset-- < 2) + return 0; + while (offset > 2 && VIM_ISWHITE(line[offset])) + --offset; + + offset -= 1; + if (!STRNCMP(line + offset, "if", 2)) + goto probablyFound; + + if (offset >= 1) + { + offset -= 1; + if (!STRNCMP(line + offset, "for", 3)) + goto probablyFound; + + if (offset >= 2) + { + offset -= 2; + if (!STRNCMP(line + offset, "while", 5)) + goto probablyFound; + } + } + return 0; + + probablyFound: + if (!offset || !vim_isIDc(line[offset - 1])) + { + *poffset = offset; + return 1; + } + return 0; + } + + /* + * Return TRUE if we are at the end of a do-while. + * do + * nothing; + * while (foo + * && bar); <-- here + * Adjust the cursor to the line with "while". + */ + static int + cin_iswhileofdo_end(int terminated) + { + char_u *line; + char_u *p; + char_u *s; + pos_T *trypos; + int i; + + if (terminated != ';') // there must be a ';' at the end + return FALSE; + + p = line = ml_get_curline(); + while (*p != NUL) + { + p = cin_skipcomment(p); + if (*p == ')') + { + s = skipwhite(p + 1); + if (*s == ';' && cin_nocode(s + 1)) + { + // Found ");" at end of the line, now check there is "while" + // before the matching '('. XXX + i = (int)(p - line); + curwin->w_cursor.col = i; + trypos = find_match_paren(curbuf->b_ind_maxparen); + if (trypos != NULL) + { + s = cin_skipcomment(ml_get(trypos->lnum)); + if (*s == '}') // accept "} while (cond);" + s = cin_skipcomment(s + 1); + if (cin_starts_with(s, "while")) + { + curwin->w_cursor.lnum = trypos->lnum; + return TRUE; + } + } + + // Searching may have made "line" invalid, get it again. + line = ml_get_curline(); + p = line + i; + } + } + if (*p != NUL) + ++p; + } + return FALSE; + } + + static int + cin_isbreak(char_u *p) + { + return (STRNCMP(p, "break", 5) == 0 && !vim_isIDc(p[5])); + } + + /* + * Find the position of a C++ base-class declaration or + * constructor-initialization. eg: + * + * class MyClass : + * baseClass <-- here + * class MyClass : public baseClass, + * anotherBaseClass <-- here (should probably lineup ??) + * MyClass::MyClass(...) : + * baseClass(...) <-- here (constructor-initialization) + * + * This is a lot of guessing. Watch out for "cond ? func() : foo". + */ + static int + cin_is_cpp_baseclass( + cpp_baseclass_cache_T *cached) // input and output + { + lpos_T *pos = &cached->lpos; // find position + char_u *s; + int class_or_struct, lookfor_ctor_init, cpp_base_class; + linenr_T lnum = curwin->w_cursor.lnum; + char_u *line = ml_get_curline(); + + if (pos->lnum <= lnum) + return cached->found; // Use the cached result + + pos->col = 0; + + s = skipwhite(line); + if (*s == '#') // skip #define FOO x ? (x) : x + return FALSE; + s = cin_skipcomment(s); + if (*s == NUL) + return FALSE; + + cpp_base_class = lookfor_ctor_init = class_or_struct = FALSE; + + // Search for a line starting with '#', empty, ending in ';' or containing + // '{' or '}' and start below it. This handles the following situations: + // a = cond ? + // func() : + // asdf; + // func::foo() + // : something + // {} + // Foo::Foo (int one, int two) + // : something(4), + // somethingelse(3) + // {} + while (lnum > 1) + { + line = ml_get(lnum - 1); + s = skipwhite(line); + if (*s == '#' || *s == NUL) + break; + while (*s != NUL) + { + s = cin_skipcomment(s); + if (*s == '{' || *s == '}' + || (*s == ';' && cin_nocode(s + 1))) + break; + if (*s != NUL) + ++s; + } + if (*s != NUL) + break; + --lnum; + } + + pos->lnum = lnum; + line = ml_get(lnum); + s = line; + for (;;) + { + if (*s == NUL) + { + if (lnum == curwin->w_cursor.lnum) + break; + // Continue in the cursor line. + line = ml_get(++lnum); + s = line; + } + if (s == line) + { + // don't recognize "case (foo):" as a baseclass + if (cin_iscase(s, FALSE)) + break; + s = cin_skipcomment(line); + if (*s == NUL) + continue; + } + + if (s[0] == '"' || (s[0] == 'R' && s[1] == '"')) + s = skip_string(s) + 1; + else if (s[0] == ':') + { + if (s[1] == ':') + { + // skip double colon. It can't be a constructor + // initialization any more + lookfor_ctor_init = FALSE; + s = cin_skipcomment(s + 2); + } + else if (lookfor_ctor_init || class_or_struct) + { + // we have something found, that looks like the start of + // cpp-base-class-declaration or constructor-initialization + cpp_base_class = TRUE; + lookfor_ctor_init = class_or_struct = FALSE; + pos->col = 0; + s = cin_skipcomment(s + 1); + } + else + s = cin_skipcomment(s + 1); + } + else if ((STRNCMP(s, "class", 5) == 0 && !vim_isIDc(s[5])) + || (STRNCMP(s, "struct", 6) == 0 && !vim_isIDc(s[6]))) + { + class_or_struct = TRUE; + lookfor_ctor_init = FALSE; + + if (*s == 'c') + s = cin_skipcomment(s + 5); + else + s = cin_skipcomment(s + 6); + } + else + { + if (s[0] == '{' || s[0] == '}' || s[0] == ';') + { + cpp_base_class = lookfor_ctor_init = class_or_struct = FALSE; + } + else if (s[0] == ')') + { + // Constructor-initialization is assumed if we come across + // something like "):" + class_or_struct = FALSE; + lookfor_ctor_init = TRUE; + } + else if (s[0] == '?') + { + // Avoid seeing '() :' after '?' as constructor init. + return FALSE; + } + else if (!vim_isIDc(s[0])) + { + // if it is not an identifier, we are wrong + class_or_struct = FALSE; + lookfor_ctor_init = FALSE; + } + else if (pos->col == 0) + { + // it can't be a constructor-initialization any more + lookfor_ctor_init = FALSE; + + // the first statement starts here: lineup with this one... + if (cpp_base_class) + pos->col = (colnr_T)(s - line); + } + + // When the line ends in a comma don't align with it. + if (lnum == curwin->w_cursor.lnum && *s == ',' && cin_nocode(s + 1)) + pos->col = 0; + + s = cin_skipcomment(s + 1); + } + } + + cached->found = cpp_base_class; + if (cpp_base_class) + pos->lnum = lnum; + return cpp_base_class; + } + + static int + get_baseclass_amount(int col) + { + int amount; + colnr_T vcol; + pos_T *trypos; + + if (col == 0) + { + amount = get_indent(); + if (find_last_paren(ml_get_curline(), '(', ')') + && (trypos = find_match_paren(curbuf->b_ind_maxparen)) != NULL) + amount = get_indent_lnum(trypos->lnum); // XXX + if (!cin_ends_in(ml_get_curline(), (char_u *)",", NULL)) + amount += curbuf->b_ind_cpp_baseclass; + } + else + { + curwin->w_cursor.col = col; + getvcol(curwin, &curwin->w_cursor, &vcol, NULL, NULL); + amount = (int)vcol; + } + if (amount < curbuf->b_ind_cpp_baseclass) + amount = curbuf->b_ind_cpp_baseclass; + return amount; + } + + /* + * Find the '{' at the start of the block we are in. + * Return NULL if no match found. + * Ignore a '{' that is in a comment, makes indenting the next three lines + * work. + */ + /* foo() */ + /* { */ + /* } */ + + static pos_T * + find_start_brace(void) // XXX + { + pos_T cursor_save; + pos_T *trypos; + pos_T *pos; + static pos_T pos_copy; + + cursor_save = curwin->w_cursor; + while ((trypos = findmatchlimit(NULL, '{', FM_BLOCKSTOP, 0)) != NULL) + { + pos_copy = *trypos; // copy pos_T, next findmatch will change it + trypos = &pos_copy; + curwin->w_cursor = *trypos; + pos = NULL; + // ignore the { if it's in a // or / * * / comment + if ((colnr_T)cin_skip2pos(trypos) == trypos->col + && (pos = ind_find_start_CORS(NULL)) == NULL) // XXX + break; + if (pos != NULL) + curwin->w_cursor.lnum = pos->lnum; + } + curwin->w_cursor = cursor_save; + return trypos; + } + + /* + * Find the matching '(', ignoring it if it is in a comment or before an + * unmatched {. + * Return NULL if no match found. + */ + static pos_T * + find_match_paren_after_brace (int ind_maxparen) // XXX + { + pos_T *trypos = find_match_paren(ind_maxparen); + + if (trypos != NULL) + { + pos_T *tryposBrace = find_start_brace(); + + // If both an unmatched '(' and '{' is found. Ignore the '(' + // position if the '{' is further down. + if (tryposBrace != NULL + && (trypos->lnum != tryposBrace->lnum + ? trypos->lnum < tryposBrace->lnum + : trypos->col < tryposBrace->col)) + trypos = NULL; + } + return trypos; + } + + /* + * Return ind_maxparen corrected for the difference in line number between the + * cursor position and "startpos". This makes sure that searching for a + * matching paren above the cursor line doesn't find a match because of + * looking a few lines further. + */ + static int + corr_ind_maxparen(pos_T *startpos) + { + long n = (long)startpos->lnum - (long)curwin->w_cursor.lnum; + + if (n > 0 && n < curbuf->b_ind_maxparen / 2) + return curbuf->b_ind_maxparen - (int)n; + return curbuf->b_ind_maxparen; + } + + /* + * Parse 'cinoptions' and set the values in "curbuf". + * Must be called when 'cinoptions', 'shiftwidth' and/or 'tabstop' changes. + */ + void + parse_cino(buf_T *buf) + { + char_u *p; + char_u *l; + char_u *digits; + int n; + int divider; + int fraction = 0; + int sw = (int)get_sw_value(buf); + + // Set the default values. + + // Spaces from a block's opening brace the prevailing indent for that + // block should be. + buf->b_ind_level = sw; + + // Spaces from the edge of the line an open brace that's at the end of a + // line is imagined to be. + buf->b_ind_open_imag = 0; + + // Spaces from the prevailing indent for a line that is not preceded by + // an opening brace. + buf->b_ind_no_brace = 0; + + // Column where the first { of a function should be located }. + buf->b_ind_first_open = 0; + + // Spaces from the prevailing indent a leftmost open brace should be + // located. + buf->b_ind_open_extra = 0; + + // Spaces from the matching open brace (real location for one at the left + // edge; imaginary location from one that ends a line) the matching close + // brace should be located. + buf->b_ind_close_extra = 0; + + // Spaces from the edge of the line an open brace sitting in the leftmost + // column is imagined to be. + buf->b_ind_open_left_imag = 0; + + // Spaces jump labels should be shifted to the left if N is non-negative, + // otherwise the jump label will be put to column 1. + buf->b_ind_jump_label = -1; + + // Spaces from the switch() indent a "case xx" label should be located. + buf->b_ind_case = sw; + + // Spaces from the "case xx:" code after a switch() should be located. + buf->b_ind_case_code = sw; + + // Lineup break at end of case in switch() with case label. + buf->b_ind_case_break = 0; + + // Spaces from the class declaration indent a scope declaration label + // should be located. + buf->b_ind_scopedecl = sw; + + // Spaces from the scope declaration label code should be located. + buf->b_ind_scopedecl_code = sw; + + // Amount K&R-style parameters should be indented. + buf->b_ind_param = sw; + + // Amount a function type spec should be indented. + buf->b_ind_func_type = sw; + + // Amount a cpp base class declaration or constructor initialization + // should be indented. + buf->b_ind_cpp_baseclass = sw; + + // additional spaces beyond the prevailing indent a continuation line + // should be located. + buf->b_ind_continuation = sw; + + // Spaces from the indent of the line with an unclosed parentheses. + buf->b_ind_unclosed = sw * 2; + + // Spaces from the indent of the line with an unclosed parentheses, which + // itself is also unclosed. + buf->b_ind_unclosed2 = sw; + + // Suppress ignoring spaces from the indent of a line starting with an + // unclosed parentheses. + buf->b_ind_unclosed_noignore = 0; + + // If the opening paren is the last nonwhite character on the line, and + // b_ind_unclosed_wrapped is nonzero, use this indent relative to the outer + // context (for very long lines). + buf->b_ind_unclosed_wrapped = 0; + + // Suppress ignoring white space when lining up with the character after + // an unclosed parentheses. + buf->b_ind_unclosed_whiteok = 0; + + // Indent a closing parentheses under the line start of the matching + // opening parentheses. + buf->b_ind_matching_paren = 0; + + // Indent a closing parentheses under the previous line. + buf->b_ind_paren_prev = 0; + + // Extra indent for comments. + buf->b_ind_comment = 0; + + // Spaces from the comment opener when there is nothing after it. + buf->b_ind_in_comment = 3; + + // Boolean: if non-zero, use b_ind_in_comment even if there is something + // after the comment opener. + buf->b_ind_in_comment2 = 0; + + // Max lines to search for an open paren. + buf->b_ind_maxparen = 20; + + // Max lines to search for an open comment. + buf->b_ind_maxcomment = 70; + + // Handle braces for java code. + buf->b_ind_java = 0; + + // Not to confuse JS object properties with labels. + buf->b_ind_js = 0; + + // Handle blocked cases correctly. + buf->b_ind_keep_case_label = 0; + + // Handle C++ namespace. + buf->b_ind_cpp_namespace = 0; + + // Handle continuation lines containing conditions of if(), for() and + // while(). + buf->b_ind_if_for_while = 0; + + // indentation for # comments + buf->b_ind_hash_comment = 0; + + // Handle C++ extern "C" or "C++" + buf->b_ind_cpp_extern_c = 0; + + for (p = buf->b_p_cino; *p; ) + { + l = p++; + if (*p == '-') + ++p; + digits = p; // remember where the digits start + n = getdigits(&p); + divider = 0; + if (*p == '.') // ".5s" means a fraction + { + fraction = atol((char *)++p); + while (VIM_ISDIGIT(*p)) + { + ++p; + if (divider) + divider *= 10; + else + divider = 10; + } + } + if (*p == 's') // "2s" means two times 'shiftwidth' + { + if (p == digits) + n = sw; // just "s" is one 'shiftwidth' + else + { + n *= sw; + if (divider) + n += (sw * fraction + divider / 2) / divider; + } + ++p; + } + if (l[1] == '-') + n = -n; + + // When adding an entry here, also update the default 'cinoptions' in + // doc/indent.txt, and add explanation for it! + switch (*l) + { + case '>': buf->b_ind_level = n; break; + case 'e': buf->b_ind_open_imag = n; break; + case 'n': buf->b_ind_no_brace = n; break; + case 'f': buf->b_ind_first_open = n; break; + case '{': buf->b_ind_open_extra = n; break; + case '}': buf->b_ind_close_extra = n; break; + case '^': buf->b_ind_open_left_imag = n; break; + case 'L': buf->b_ind_jump_label = n; break; + case ':': buf->b_ind_case = n; break; + case '=': buf->b_ind_case_code = n; break; + case 'b': buf->b_ind_case_break = n; break; + case 'p': buf->b_ind_param = n; break; + case 't': buf->b_ind_func_type = n; break; + case '/': buf->b_ind_comment = n; break; + case 'c': buf->b_ind_in_comment = n; break; + case 'C': buf->b_ind_in_comment2 = n; break; + case 'i': buf->b_ind_cpp_baseclass = n; break; + case '+': buf->b_ind_continuation = n; break; + case '(': buf->b_ind_unclosed = n; break; + case 'u': buf->b_ind_unclosed2 = n; break; + case 'U': buf->b_ind_unclosed_noignore = n; break; + case 'W': buf->b_ind_unclosed_wrapped = n; break; + case 'w': buf->b_ind_unclosed_whiteok = n; break; + case 'm': buf->b_ind_matching_paren = n; break; + case 'M': buf->b_ind_paren_prev = n; break; + case ')': buf->b_ind_maxparen = n; break; + case '*': buf->b_ind_maxcomment = n; break; + case 'g': buf->b_ind_scopedecl = n; break; + case 'h': buf->b_ind_scopedecl_code = n; break; + case 'j': buf->b_ind_java = n; break; + case 'J': buf->b_ind_js = n; break; + case 'l': buf->b_ind_keep_case_label = n; break; + case '#': buf->b_ind_hash_comment = n; break; + case 'N': buf->b_ind_cpp_namespace = n; break; + case 'k': buf->b_ind_if_for_while = n; break; + case 'E': buf->b_ind_cpp_extern_c = n; break; + } + if (*p == ',') + ++p; + } + } + + static int + find_match(int lookfor, linenr_T ourscope) + { + char_u *look; + pos_T *theirscope; + char_u *mightbeif; + int elselevel; + int whilelevel; + + if (lookfor == LOOKFOR_IF) + { + elselevel = 1; + whilelevel = 0; + } + else + { + elselevel = 0; + whilelevel = 1; + } + + curwin->w_cursor.col = 0; + + while (curwin->w_cursor.lnum > ourscope + 1) + { + curwin->w_cursor.lnum--; + curwin->w_cursor.col = 0; + + look = cin_skipcomment(ml_get_curline()); + if (cin_iselse(look) + || cin_isif(look) + || cin_isdo(look) // XXX + || cin_iswhileofdo(look, curwin->w_cursor.lnum)) + { + // if we've gone outside the braces entirely, + // we must be out of scope... + theirscope = find_start_brace(); // XXX + if (theirscope == NULL) + break; + + // and if the brace enclosing this is further + // back than the one enclosing the else, we're + // out of luck too. + if (theirscope->lnum < ourscope) + break; + + // and if they're enclosed in a *deeper* brace, + // then we can ignore it because it's in a + // different scope... + if (theirscope->lnum > ourscope) + continue; + + // if it was an "else" (that's not an "else if") + // then we need to go back to another if, so + // increment elselevel + look = cin_skipcomment(ml_get_curline()); + if (cin_iselse(look)) + { + mightbeif = cin_skipcomment(look + 4); + if (!cin_isif(mightbeif)) + ++elselevel; + continue; + } + + // if it was a "while" then we need to go back to + // another "do", so increment whilelevel. XXX + if (cin_iswhileofdo(look, curwin->w_cursor.lnum)) + { + ++whilelevel; + continue; + } + + // If it's an "if" decrement elselevel + look = cin_skipcomment(ml_get_curline()); + if (cin_isif(look)) + { + elselevel--; + // When looking for an "if" ignore "while"s that + // get in the way. + if (elselevel == 0 && lookfor == LOOKFOR_IF) + whilelevel = 0; + } + + // If it's a "do" decrement whilelevel + if (cin_isdo(look)) + whilelevel--; + + // if we've used up all the elses, then + // this must be the if that we want! + // match the indent level of that if. + if (elselevel <= 0 && whilelevel <= 0) + return OK; + } + } + return FAIL; + } + + /* + * 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; + int amount; + int scope_amount; + int cur_amount = MAXCOL; + colnr_T col; + char_u *theline; + char_u *linecopy; + pos_T *trypos; + pos_T *comment_pos; + pos_T *tryposBrace = NULL; + pos_T tryposCopy; + pos_T our_paren_pos; + char_u *start; + int start_brace; + #define BRACE_IN_COL0 1 // '{' is in column 0 + #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; + char_u terminated; + int lookfor; + int whilelevel; + linenr_T lnum; + int n; + int iscase; + int lookfor_break; + int lookfor_cpp_namespace = FALSE; + int cont_amount = 0; // amount for continuation line + int original_line_islabel; + int added_to_amount = 0; + int js_cur_has_key = 0; + linenr_T raw_string_start = 0; + cpp_baseclass_cache_T cache_cpp_baseclass = { FALSE, { MAXLNUM, 0 } }; + + // make a copy, value is changed below + int ind_continuation = curbuf->b_ind_continuation; + + // remember where the cursor was when we started + cur_curpos = curwin->w_cursor; + + // if we are at line 1 zero indent is fine, right? + if (cur_curpos.lnum == 1) + return 0; + + // Get a copy of the current contents of the line. + // This is required, because only the most recent line obtained with + // ml_get is valid! + linecopy = vim_strsave(ml_get(cur_curpos.lnum)); + if (linecopy == NULL) + return 0; + + // In insert mode and the cursor is on a ')' truncate the line at the + // cursor position. We don't want to line up with the matching '(' when + // inserting new stuff. + // For unknown reasons the cursor might be past the end of the line, thus + // check for that. + if ((State & INSERT) + && curwin->w_cursor.col < (colnr_T)STRLEN(linecopy) + && linecopy[curwin->w_cursor.col] == ')') + linecopy[curwin->w_cursor.col] = NUL; + + theline = skipwhite(linecopy); + + // move the cursor to the start of the line + + curwin->w_cursor.col = 0; + + 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_POS(*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))) + { + 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. + 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. + 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. + 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 + char_u lead_middle[COM_MAX_LEN]; // middle-comment string + char_u lead_end[COM_MAX_LEN]; // end-comment string + char_u *p; + int start_align = 0; + int start_off = 0; + int done = FALSE; + + // find how indented the line beginning the comment is + getvcol(curwin, comment_pos, &col, NULL, NULL); + amount = col; + *lead_start = NUL; + *lead_middle = NUL; + + p = curbuf->b_p_com; + while (*p != NUL) + { + int align = 0; + int off = 0; + int what = 0; + + while (*p != NUL && *p != ':') + { + if (*p == COM_START || *p == COM_END || *p == COM_MIDDLE) + what = *p++; + else if (*p == COM_LEFT || *p == COM_RIGHT) + align = *p++; + else if (VIM_ISDIGIT(*p) || *p == '-') + off = getdigits(&p); + else + ++p; + } + + if (*p == ':') + ++p; + (void)copy_option_part(&p, lead_end, COM_MAX_LEN, ","); + if (what == COM_START) + { + STRCPY(lead_start, lead_end); + lead_start_len = (int)STRLEN(lead_start); + start_off = off; + start_align = align; + } + else if (what == COM_MIDDLE) + { + STRCPY(lead_middle, lead_end); + lead_middle_len = (int)STRLEN(lead_middle); + } + else if (what == COM_END) + { + // If our line starts with the middle comment string, line it + // up with the comment opener per the 'comments' option. + if (STRNCMP(theline, lead_middle, lead_middle_len) == 0 + && STRNCMP(theline, lead_end, STRLEN(lead_end)) != 0) + { + done = TRUE; + if (curwin->w_cursor.lnum > 1) + { + // If the start comment string matches in the previous + // line, use the indent of that line plus offset. If + // the middle comment string matches in the previous + // line, use the indent of that line. XXX + look = skipwhite(ml_get(curwin->w_cursor.lnum - 1)); + if (STRNCMP(look, lead_start, lead_start_len) == 0) + amount = get_indent_lnum(curwin->w_cursor.lnum - 1); + else if (STRNCMP(look, lead_middle, + lead_middle_len) == 0) + { + amount = get_indent_lnum(curwin->w_cursor.lnum - 1); + break; + } + // If the start comment string doesn't match with the + // start of the comment, skip this entry. XXX + else if (STRNCMP(ml_get(comment_pos->lnum) + comment_pos->col, + lead_start, lead_start_len) != 0) + continue; + } + if (start_off != 0) + amount += start_off; + else if (start_align == COM_RIGHT) + amount += vim_strsize(lead_start) + - vim_strsize(lead_middle); + break; + } + + // If our line starts with the end comment string, line it up + // with the middle comment + if (STRNCMP(theline, lead_middle, lead_middle_len) != 0 + && STRNCMP(theline, lead_end, STRLEN(lead_end)) == 0) + { + amount = get_indent_lnum(curwin->w_cursor.lnum - 1); + // XXX + if (off != 0) + amount += off; + else if (align == COM_RIGHT) + amount += vim_strsize(lead_start) + - vim_strsize(lead_middle); + done = TRUE; + break; + } + } + } + + // If our line starts with an asterisk, line up with the + // asterisk in the comment opener; otherwise, line up + // with the first character of the comment text. + if (done) + ; + else if (theline[0] == '*') + amount += 1; + else + { + // If we are more than one line away from the comment opener, take + // the indent of the previous non-empty line. If 'cino' has "CO" + // and we are just below the comment opener and there are any + // white characters after it line up with the text after it; + // otherwise, add the amount specified by "c" in 'cino' + amount = -1; + for (lnum = cur_curpos.lnum - 1; lnum > comment_pos->lnum; --lnum) + { + if (linewhite(lnum)) // skip blank lines + continue; + amount = get_indent_lnum(lnum); // XXX + break; + } + if (amount == -1) // use the comment opener + { + if (!curbuf->b_ind_in_comment2) + { + start = ml_get(comment_pos->lnum); + look = start + comment_pos->col + 2; // skip / and * + if (*look != NUL) // if something after it + comment_pos->col = (colnr_T)(skipwhite(look) - start); + } + 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? + 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 + if (((trypos = find_match_paren(curbuf->b_ind_maxparen)) != NULL + && curbuf->b_ind_java == 0) + || (tryposBrace = find_start_brace()) != NULL + || trypos != NULL) + { + if (trypos != NULL && tryposBrace != NULL) + { + // Both an unmatched '(' and '{' is found. Use the one which is + // closer to the current cursor position, set the other to NULL. + if (trypos->lnum != tryposBrace->lnum + ? trypos->lnum < tryposBrace->lnum + : trypos->col < tryposBrace->col) + trypos = NULL; + else + tryposBrace = NULL; + } + + if (trypos != NULL) + { + // If the matching paren is more than one line away, use the indent of + // a previous non-empty line that matches the same paren. + if (theline[0] == ')' && curbuf->b_ind_paren_prev) + { + // Line up with the start of the matching paren line. + amount = get_indent_lnum(curwin->w_cursor.lnum - 1); // XXX + } + else + { + amount = -1; + our_paren_pos = *trypos; + for (lnum = cur_curpos.lnum - 1; lnum > our_paren_pos.lnum; --lnum) + { + l = skipwhite(ml_get(lnum)); + if (cin_nocode(l)) // skip comment lines + continue; + if (cin_ispreproc_cont(&l, &lnum, &amount)) + continue; // ignore #define, #if, etc. + curwin->w_cursor.lnum = lnum; + + // Skip a comment or raw string. XXX + if ((trypos = ind_find_start_CORS(NULL)) != NULL) + { + lnum = trypos->lnum + 1; + continue; + } + + // XXX + if ((trypos = find_match_paren( + corr_ind_maxparen(&cur_curpos))) != NULL + && trypos->lnum == our_paren_pos.lnum + && trypos->col == our_paren_pos.col) + { + amount = get_indent_lnum(lnum); // XXX + + if (theline[0] == ')') + { + if (our_paren_pos.lnum != lnum + && cur_amount > amount) + cur_amount = amount; + amount = -1; + } + break; + } + } + } + + // Line up with line where the matching paren is. XXX + // If the line starts with a '(' or the indent for unclosed + // parentheses is zero, line up with the unclosed parentheses. + if (amount == -1) + { + int ignore_paren_col = 0; + int is_if_for_while = 0; + + if (curbuf->b_ind_if_for_while) + { + // Look for the outermost opening parenthesis on this line + // and check whether it belongs to an "if", "for" or "while". + + pos_T cursor_save = curwin->w_cursor; + pos_T outermost; + char_u *line; + + trypos = &our_paren_pos; + do { + outermost = *trypos; + curwin->w_cursor.lnum = outermost.lnum; + curwin->w_cursor.col = outermost.col; + + trypos = find_match_paren(curbuf->b_ind_maxparen); + } while (trypos && trypos->lnum == outermost.lnum); + + curwin->w_cursor = cursor_save; + + line = ml_get(outermost.lnum); + + is_if_for_while = + cin_is_if_for_while_before_offset(line, &outermost.col); + } + + amount = skip_label(our_paren_pos.lnum, &look); + look = skipwhite(look); + if (*look == '(') + { + linenr_T save_lnum = curwin->w_cursor.lnum; + char_u *line; + int look_col; + + // Ignore a '(' in front of the line that has a match before + // our matching '('. + curwin->w_cursor.lnum = our_paren_pos.lnum; + line = ml_get_curline(); + look_col = (int)(look - line); + curwin->w_cursor.col = look_col + 1; + if ((trypos = findmatchlimit(NULL, ')', 0, + curbuf->b_ind_maxparen)) + != NULL + && trypos->lnum == our_paren_pos.lnum + && trypos->col < our_paren_pos.col) + ignore_paren_col = trypos->col + 1; + + curwin->w_cursor.lnum = save_lnum; + look = ml_get(our_paren_pos.lnum) + look_col; + } + if (theline[0] == ')' || (curbuf->b_ind_unclosed == 0 + && is_if_for_while == 0) + || (!curbuf->b_ind_unclosed_noignore && *look == '(' + && ignore_paren_col == 0)) + { + // If we're looking at a close paren, line up right there; + // otherwise, line up with the next (non-white) character. + // When b_ind_unclosed_wrapped is set and the matching paren is + // the last nonwhite character of the line, use either the + // indent of the current line or the indentation of the next + // outer paren and add b_ind_unclosed_wrapped (for very long + // lines). + if (theline[0] != ')') + { + cur_amount = MAXCOL; + l = ml_get(our_paren_pos.lnum); + if (curbuf->b_ind_unclosed_wrapped + && cin_ends_in(l, (char_u *)"(", NULL)) + { + // look for opening unmatched paren, indent one level + // for each additional level + n = 1; + for (col = 0; col < our_paren_pos.col; ++col) + { + switch (l[col]) + { + case '(': + case '{': ++n; + break; + + case ')': + case '}': if (n > 1) + --n; + break; + } + } + + our_paren_pos.col = 0; + amount += n * curbuf->b_ind_unclosed_wrapped; + } + else if (curbuf->b_ind_unclosed_whiteok) + our_paren_pos.col++; + else + { + col = our_paren_pos.col + 1; + while (VIM_ISWHITE(l[col])) + col++; + if (l[col] != NUL) // In case of trailing space + our_paren_pos.col = col; + else + our_paren_pos.col++; + } + } + + // Find how indented the paren is, or the character after it + // if we did the above "if". + if (our_paren_pos.col > 0) + { + getvcol(curwin, &our_paren_pos, &col, NULL, NULL); + if (cur_amount > (int)col) + cur_amount = col; + } + } + + if (theline[0] == ')' && curbuf->b_ind_matching_paren) + { + // Line up with the start of the matching paren line. + } + else if ((curbuf->b_ind_unclosed == 0 && is_if_for_while == 0) + || (!curbuf->b_ind_unclosed_noignore + && *look == '(' && ignore_paren_col == 0)) + { + if (cur_amount != MAXCOL) + amount = cur_amount; + } + else + { + // Add b_ind_unclosed2 for each '(' before our matching one, + // but ignore (void) before the line (ignore_paren_col). + col = our_paren_pos.col; + while ((int)our_paren_pos.col > ignore_paren_col) + { + --our_paren_pos.col; + switch (*ml_get_pos(&our_paren_pos)) + { + case '(': amount += curbuf->b_ind_unclosed2; + col = our_paren_pos.col; + break; + case ')': amount -= curbuf->b_ind_unclosed2; + col = MAXCOL; + break; + } + } + + // Use b_ind_unclosed once, when the first '(' is not inside + // braces + if (col == MAXCOL) + amount += curbuf->b_ind_unclosed; + else + { + curwin->w_cursor.lnum = our_paren_pos.lnum; + curwin->w_cursor.col = col; + if (find_match_paren_after_brace(curbuf->b_ind_maxparen) + != NULL) + amount += curbuf->b_ind_unclosed2; + else + { + if (is_if_for_while) + amount += curbuf->b_ind_if_for_while; + else + amount += curbuf->b_ind_unclosed; + } + } + // For a line starting with ')' use the minimum of the two + // positions, to avoid giving it more indent than the previous + // lines: + // func_long_name( if (x + // arg && yy + // ) ^ not here ) ^ not here + if (cur_amount < amount) + amount = cur_amount; + } + } + + // add extra indent for a comment + if (cin_iscomment(theline)) + amount += curbuf->b_ind_comment; + } + else + { + // We are inside braces, there is a { before this line at the position + // stored in tryposBrace. + // Make a copy of tryposBrace, it may point to pos_copy inside + // find_start_brace(), which may be changed somewhere. + tryposCopy = *tryposBrace; + tryposBrace = &tryposCopy; + trypos = tryposBrace; + ourscope = trypos->lnum; + start = ml_get(ourscope); + + // Now figure out how indented the line is in general. + // If the brace was at the start of the line, we use that; + // otherwise, check out the indentation of the line as + // a whole and then add the "imaginary indent" to that. + look = skipwhite(start); + if (*look == '{') + { + getvcol(curwin, trypos, &col, NULL, NULL); + amount = col; + if (*start == '{') + start_brace = BRACE_IN_COL0; + else + start_brace = BRACE_AT_START; + } + else + { + // That opening brace might have been on a continuation + // line. if so, find the start of the line. + curwin->w_cursor.lnum = ourscope; + + // Position the cursor over the rightmost paren, so that + // matching it will take us back to the start of the line. + lnum = ourscope; + if (find_last_paren(start, '(', ')') + && (trypos = find_match_paren(curbuf->b_ind_maxparen)) + != NULL) + lnum = trypos->lnum; + + // It could have been something like + // case 1: if (asdf && + // ldfd) { + // } + if ((curbuf->b_ind_js || curbuf->b_ind_keep_case_label) + && cin_iscase(skipwhite(ml_get_curline()), FALSE)) + amount = get_indent(); + else if (curbuf->b_ind_js) + amount = get_indent_lnum(lnum); + else + amount = skip_label(lnum, &l); + + start_brace = BRACE_AT_END; + } + + // For Javascript check if the line starts with "key:". + if (curbuf->b_ind_js) + js_cur_has_key = cin_has_js_key(theline); + + // If we're looking at a closing brace, that's where + // we want to be. otherwise, add the amount of room + // that an indent is supposed to be. + if (theline[0] == '}') + { + // they may want closing braces to line up with something + // other than the open brace. indulge them, if so. + amount += curbuf->b_ind_close_extra; + } + else + { + // If we're looking at an "else", try to find an "if" + // to match it with. + // If we're looking at a "while", try to find a "do" + // to match it with. + lookfor = LOOKFOR_INITIAL; + if (cin_iselse(theline)) + lookfor = LOOKFOR_IF; + else if (cin_iswhileofdo(theline, cur_curpos.lnum)) // XXX + lookfor = LOOKFOR_DO; + if (lookfor != LOOKFOR_INITIAL) + { + curwin->w_cursor.lnum = cur_curpos.lnum; + if (find_match(lookfor, ourscope) == OK) + { + amount = get_indent(); // XXX + goto theend; + } + } + + // We get here if we are not on an "while-of-do" or "else" (or + // failed to find a matching "if"). + // Search backwards for something to line up with. + // First set amount for when we don't find anything. + + // if the '{' is _really_ at the left margin, use the imaginary + // location of a left-margin brace. Otherwise, correct the + // location for b_ind_open_extra. + + if (start_brace == BRACE_IN_COL0) // '{' is in column 0 + { + amount = curbuf->b_ind_open_left_imag; + lookfor_cpp_namespace = TRUE; + } + else if (start_brace == BRACE_AT_START && + lookfor_cpp_namespace) // '{' is at start + { + + lookfor_cpp_namespace = TRUE; + } + else + { + if (start_brace == BRACE_AT_END) // '{' is at end of line + { + amount += curbuf->b_ind_open_imag; + + l = skipwhite(ml_get_curline()); + if (cin_is_cpp_namespace(l)) + amount += curbuf->b_ind_cpp_namespace; + else if (cin_is_cpp_extern_c(l)) + amount += curbuf->b_ind_cpp_extern_c; + } + else + { + // Compensate for adding b_ind_open_extra later. + amount -= curbuf->b_ind_open_extra; + if (amount < 0) + amount = 0; + } + } + + lookfor_break = FALSE; + + if (cin_iscase(theline, FALSE)) // it's a switch() label + { + lookfor = LOOKFOR_CASE; // find a previous switch() label + amount += curbuf->b_ind_case; + } + else if (cin_isscopedecl(theline)) // private:, ... + { + lookfor = LOOKFOR_SCOPEDECL; // class decl is this block + amount += curbuf->b_ind_scopedecl; + } + else + { + if (curbuf->b_ind_case_break && cin_isbreak(theline)) + // break; ... + lookfor_break = TRUE; + + lookfor = LOOKFOR_INITIAL; + // b_ind_level from start of block + amount += curbuf->b_ind_level; + } + scope_amount = amount; + whilelevel = 0; + + // Search backwards. If we find something we recognize, line up + // with that. + // + // If we're looking at an open brace, indent + // the usual amount relative to the conditional + // that opens the block. + curwin->w_cursor = cur_curpos; + for (;;) + { + curwin->w_cursor.lnum--; + curwin->w_cursor.col = 0; + + // If we went all the way back to the start of our scope, line + // up with it. + if (curwin->w_cursor.lnum <= ourscope) + { + // We reached end of scope: + // If looking for a enum or structure initialization + // go further back: + // If it is an initializer (enum xxx or xxx =), then + // don't add ind_continuation, otherwise it is a variable + // declaration: + // int x, + // here; <-- add ind_continuation + if (lookfor == LOOKFOR_ENUM_OR_INIT) + { + if (curwin->w_cursor.lnum == 0 + || curwin->w_cursor.lnum + < ourscope - curbuf->b_ind_maxparen) + { + // nothing found (abuse curbuf->b_ind_maxparen as + // limit) assume terminated line (i.e. a variable + // initialization) + if (cont_amount > 0) + amount = cont_amount; + else if (!curbuf->b_ind_js) + amount += ind_continuation; + break; + } + + l = ml_get_curline(); + + // If we're in a comment or raw string now, skip to + // the start of it. + trypos = ind_find_start_CORS(NULL); + if (trypos != 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, + &amount)) + continue; + + if (cin_nocode(l)) + continue; + + terminated = cin_isterminated(l, FALSE, TRUE); + + // If we are at top level and the line looks like a + // function declaration, we are done + // (it's a variable declaration). + if (start_brace != BRACE_IN_COL0 + || !cin_isfuncdecl(&l, curwin->w_cursor.lnum, 0)) + { + // if the line is terminated with another ',' + // it is a continued variable initialization. + // don't add extra indent. + // TODO: does not work, if a function + // declaration is split over multiple lines: + // cin_isfuncdecl returns FALSE then. + if (terminated == ',') + break; + + // if it es a enum declaration or an assignment, + // we are done. + if (terminated != ';' && cin_isinit()) + break; + + // nothing useful found + if (terminated == 0 || terminated == '{') + continue; + } + + if (terminated != ';') + { + // Skip parens and braces. Position the cursor + // over the rightmost paren, so that matching it + // will take us back to the start of the line. + // XXX + trypos = NULL; + if (find_last_paren(l, '(', ')')) + trypos = find_match_paren( + curbuf->b_ind_maxparen); + + if (trypos == NULL && find_last_paren(l, '{', '}')) + trypos = find_start_brace(); + + if (trypos != NULL) + { + curwin->w_cursor.lnum = trypos->lnum + 1; + curwin->w_cursor.col = 0; + continue; + } + } + + // it's a variable declaration, add indentation + // like in + // int a, + // b; + if (cont_amount > 0) + amount = cont_amount; + else + amount += ind_continuation; + } + else if (lookfor == LOOKFOR_UNTERM) + { + if (cont_amount > 0) + amount = cont_amount; + else + amount += ind_continuation; + } + else + { + if (lookfor != LOOKFOR_TERM + && lookfor != LOOKFOR_CPP_BASECLASS + && lookfor != LOOKFOR_COMMA) + { + amount = scope_amount; + if (theline[0] == '{') + { + amount += curbuf->b_ind_open_extra; + added_to_amount = curbuf->b_ind_open_extra; + } + } + + if (lookfor_cpp_namespace) + { + // Looking for C++ namespace, need to look further + // back. + if (curwin->w_cursor.lnum == ourscope) + continue; + + if (curwin->w_cursor.lnum == 0 + || curwin->w_cursor.lnum + < ourscope - FIND_NAMESPACE_LIM) + break; + + l = ml_get_curline(); + + // If we're in a comment or raw string now, skip + // to the start of it. + trypos = ind_find_start_CORS(NULL); + if (trypos != 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, + &amount)) + continue; + + // Finally the actual check for "namespace". + if (cin_is_cpp_namespace(l)) + { + amount += curbuf->b_ind_cpp_namespace + - added_to_amount; + break; + } + else if (cin_is_cpp_extern_c(l)) + { + amount += curbuf->b_ind_cpp_extern_c + - added_to_amount; + break; + } + + if (cin_nocode(l)) + continue; + } + } + break; + } + + // If we're in a comment or raw string now, skip to the start + // of it. XXX + if ((trypos = ind_find_start_CORS(&raw_string_start)) != NULL) + { + curwin->w_cursor.lnum = trypos->lnum + 1; + curwin->w_cursor.col = 0; + continue; + } + + l = ml_get_curline(); + + // If this is a switch() label, may line up relative to that. + // If this is a C++ scope declaration, do the same. + iscase = cin_iscase(l, FALSE); + if (iscase || cin_isscopedecl(l)) + { + // we are only looking for cpp base class + // declaration/initialization any longer + if (lookfor == LOOKFOR_CPP_BASECLASS) + break; + + // When looking for a "do" we are not interested in + // labels. + if (whilelevel > 0) + continue; + + // case xx: + // c = 99 + <- this indent plus continuation + //-> here; + if (lookfor == LOOKFOR_UNTERM + || lookfor == LOOKFOR_ENUM_OR_INIT) + { + if (cont_amount > 0) + amount = cont_amount; + else + amount += ind_continuation; + break; + } + + // case xx: <- line up with this case + // x = 333; + // case yy: + if ( (iscase && lookfor == LOOKFOR_CASE) + || (iscase && lookfor_break) + || (!iscase && lookfor == LOOKFOR_SCOPEDECL)) + { + // Check that this case label is not for another + // switch() XXX + if ((trypos = find_start_brace()) == NULL + || trypos->lnum == ourscope) + { + amount = get_indent(); // XXX + break; + } + continue; + } + + n = get_indent_nolabel(curwin->w_cursor.lnum); // XXX + + // case xx: if (cond) <- line up with this if + // y = y + 1; + // -> s = 99; + // + // case xx: + // if (cond) <- line up with this line + // y = y + 1; + // -> s = 99; + if (lookfor == LOOKFOR_TERM) + { + if (n) + amount = n; + + if (!lookfor_break) + break; + } + + // case xx: x = x + 1; <- line up with this x + // -> y = y + 1; + // + // case xx: if (cond) <- line up with this if + // -> y = y + 1; + if (n) + { + amount = n; + l = after_label(ml_get_curline()); + if (l != NULL && cin_is_cinword(l)) + { + if (theline[0] == '{') + amount += curbuf->b_ind_open_extra; + else + amount += curbuf->b_ind_level + + curbuf->b_ind_no_brace; + } + break; + } + + // Try to get the indent of a statement before the switch + // label. If nothing is found, line up relative to the + // switch label. + // break; <- may line up with this line + // case xx: + // -> y = 1; + scope_amount = get_indent() + (iscase // XXX + ? curbuf->b_ind_case_code + : curbuf->b_ind_scopedecl_code); + lookfor = curbuf->b_ind_case_break + ? LOOKFOR_NOBREAK : LOOKFOR_ANY; + continue; + } + + // Looking for a switch() label or C++ scope declaration, + // ignore other lines, skip {}-blocks. + if (lookfor == LOOKFOR_CASE || lookfor == LOOKFOR_SCOPEDECL) + { + if (find_last_paren(l, '{', '}') + && (trypos = find_start_brace()) != NULL) + { + curwin->w_cursor.lnum = trypos->lnum + 1; + curwin->w_cursor.col = 0; + } + continue; + } + + // Ignore jump labels with nothing after them. + if (!curbuf->b_ind_js && cin_islabel()) + { + l = after_label(ml_get_curline()); + if (l == NULL || cin_nocode(l)) + continue; + } + + // Ignore #defines, #if, etc. + // Ignore comment and empty lines. + // (need to get the line again, cin_islabel() may have + // unlocked it) + l = ml_get_curline(); + if (cin_ispreproc_cont(&l, &curwin->w_cursor.lnum, &amount) + || cin_nocode(l)) + continue; + + // Are we at the start of a cpp base class declaration or + // constructor initialization? XXX + n = FALSE; + if (lookfor != LOOKFOR_TERM && curbuf->b_ind_cpp_baseclass > 0) + { + n = cin_is_cpp_baseclass(&cache_cpp_baseclass); + l = ml_get_curline(); + } + if (n) + { + if (lookfor == LOOKFOR_UNTERM) + { + if (cont_amount > 0) + amount = cont_amount; + else + amount += ind_continuation; + } + else if (theline[0] == '{') + { + // Need to find start of the declaration. + lookfor = LOOKFOR_UNTERM; + ind_continuation = 0; + continue; + } + else + // XXX + amount = get_baseclass_amount( + cache_cpp_baseclass.lpos.col); + break; + } + else if (lookfor == LOOKFOR_CPP_BASECLASS) + { + // only look, whether there is a cpp base class + // declaration or initialization before the opening brace. + if (cin_isterminated(l, TRUE, FALSE)) + break; + else + continue; + } + + // What happens next depends on the line being terminated. + // If terminated with a ',' only consider it terminating if + // there is another unterminated statement behind, eg: + // 123, + // sizeof + // here + // Otherwise check whether it is a enumeration or structure + // initialisation (not indented) or a variable declaration + // (indented). + terminated = cin_isterminated(l, FALSE, TRUE); + + if (js_cur_has_key) + { + js_cur_has_key = 0; // only check the first line + if (curbuf->b_ind_js && terminated == ',') + { + // For Javascript we might be inside an object: + // key: something, <- align with this + // key: something + // or: + // key: something + <- align with this + // something, + // key: something + lookfor = LOOKFOR_JS_KEY; + } + } + if (lookfor == LOOKFOR_JS_KEY && cin_has_js_key(l)) + { + amount = get_indent(); + break; + } + if (lookfor == LOOKFOR_COMMA) + { + if (tryposBrace != NULL && tryposBrace->lnum + >= curwin->w_cursor.lnum) + break; + if (terminated == ',') + // line below current line is the one that starts a + // (possibly broken) line ending in a comma. + break; + else + { + amount = get_indent(); + if (curwin->w_cursor.lnum - 1 == ourscope) + // line above is start of the scope, thus current + // line is the one that stars a (possibly broken) + // line ending in a comma. + break; + } + } + + if (terminated == 0 || (lookfor != LOOKFOR_UNTERM + && terminated == ',')) + { + if (lookfor != LOOKFOR_ENUM_OR_INIT && + (*skipwhite(l) == '[' || l[STRLEN(l) - 1] == '[')) + amount += ind_continuation; + // if we're in the middle of a paren thing, + // go back to the line that starts it so + // we can get the right prevailing indent + // if ( foo && + // bar ) + + // Position the cursor over the rightmost paren, so that + // matching it will take us back to the start of the line. + // Ignore a match before the start of the block. + (void)find_last_paren(l, '(', ')'); + trypos = find_match_paren(corr_ind_maxparen(&cur_curpos)); + if (trypos != NULL && (trypos->lnum < tryposBrace->lnum + || (trypos->lnum == tryposBrace->lnum + && trypos->col < tryposBrace->col))) + trypos = NULL; + + // If we are looking for ',', we also look for matching + // braces. + if (trypos == NULL && terminated == ',' + && find_last_paren(l, '{', '}')) + trypos = find_start_brace(); + + if (trypos != NULL) + { + // Check if we are on a case label now. This is + // handled above. + // case xx: if ( asdf && + // asdf) + curwin->w_cursor = *trypos; + l = ml_get_curline(); + if (cin_iscase(l, FALSE) || cin_isscopedecl(l)) + { + ++curwin->w_cursor.lnum; + curwin->w_cursor.col = 0; + continue; + } + } + + // Skip over continuation lines to find the one to get the + // indent from + // char *usethis = "bla{backslash} + // bla", + // here; + if (terminated == ',') + { + while (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; + } + } + + // Get indent and pointer to text for current line, + // ignoring any jump label. XXX + if (curbuf->b_ind_js) + cur_amount = get_indent(); + else + cur_amount = skip_label(curwin->w_cursor.lnum, &l); + // If this is just above the line we are indenting, and it + // starts with a '{', line it up with this line. + // while (not) + // -> { + // } + if (terminated != ',' && lookfor != LOOKFOR_TERM + && theline[0] == '{') + { + amount = cur_amount; + // Only add b_ind_open_extra when the current line + // doesn't start with a '{', which must have a match + // in the same line (scope is the same). Probably: + // { 1, 2 }, + // -> { 3, 4 } + if (*skipwhite(l) != '{') + amount += curbuf->b_ind_open_extra; + + if (curbuf->b_ind_cpp_baseclass && !curbuf->b_ind_js) + { + // have to look back, whether it is a cpp base + // class declaration or initialization + lookfor = LOOKFOR_CPP_BASECLASS; + continue; + } + break; + } + + // Check if we are after an "if", "while", etc. + // Also allow " } else". + if (cin_is_cinword(l) || cin_iselse(skipwhite(l))) + { + // Found an unterminated line after an if (), line up + // with the last one. + // if (cond) + // 100 + + // -> here; + if (lookfor == LOOKFOR_UNTERM + || lookfor == LOOKFOR_ENUM_OR_INIT) + { + if (cont_amount > 0) + amount = cont_amount; + else + amount += ind_continuation; + break; + } + + // If this is just above the line we are indenting, we + // are finished. + // while (not) + // -> here; + // Otherwise this indent can be used when the line + // before this is terminated. + // yyy; + // if (stat) + // while (not) + // xxx; + // -> here; + amount = cur_amount; + if (theline[0] == '{') + amount += curbuf->b_ind_open_extra; + if (lookfor != LOOKFOR_TERM) + { + amount += curbuf->b_ind_level + + curbuf->b_ind_no_brace; + break; + } + + // Special trick: when expecting the while () after a + // do, line up with the while() + // do + // x = 1; + // -> here + l = skipwhite(ml_get_curline()); + if (cin_isdo(l)) + { + if (whilelevel == 0) + break; + --whilelevel; + } + + // When searching for a terminated line, don't use the + // one between the "if" and the matching "else". + // Need to use the scope of this "else". XXX + // If whilelevel != 0 continue looking for a "do {". + if (cin_iselse(l) && whilelevel == 0) + { + // If we're looking at "} else", let's make sure we + // find the opening brace of the enclosing scope, + // not the one from "if () {". + if (*l == '}') + curwin->w_cursor.col = + (colnr_T)(l - ml_get_curline()) + 1; + + if ((trypos = find_start_brace()) == NULL + || find_match(LOOKFOR_IF, trypos->lnum) + == FAIL) + break; + } + } + + // If we're below an unterminated line that is not an + // "if" or something, we may line up with this line or + // add something for a continuation line, depending on + // the line before this one. + else + { + // Found two unterminated lines on a row, line up with + // the last one. + // c = 99 + + // 100 + + // -> here; + if (lookfor == LOOKFOR_UNTERM) + { + // When line ends in a comma add extra indent + if (terminated == ',') + amount += ind_continuation; + break; + } + + if (lookfor == LOOKFOR_ENUM_OR_INIT) + { + // Found two lines ending in ',', lineup with the + // lowest one, but check for cpp base class + // declaration/initialization, if it is an + // opening brace or we are looking just for + // enumerations/initializations. + if (terminated == ',') + { + if (curbuf->b_ind_cpp_baseclass == 0) + break; + + lookfor = LOOKFOR_CPP_BASECLASS; + continue; + } + + // Ignore unterminated lines in between, but + // reduce indent. + if (amount > cur_amount) + amount = cur_amount; + } + else + { + // Found first unterminated line on a row, may + // line up with this line, remember its indent + // 100 + + // -> here; + l = ml_get_curline(); + amount = cur_amount; + + n = (int)STRLEN(l); + if (terminated == ',' && (*skipwhite(l) == ']' + || (n >=2 && l[n - 2] == ']'))) + break; + + // If previous line ends in ',', check whether we + // are in an initialization or enum + // struct xxx = + // { + // sizeof a, + // 124 }; + // or a normal possible continuation line. + // but only, of no other statement has been found + // yet. + if (lookfor == LOOKFOR_INITIAL && terminated == ',') + { + if (curbuf->b_ind_js) + { + // Search for a line ending in a comma + // and line up with the line below it + // (could be the current line). + // some = [ + // 1, <- line up here + // 2, + // some = [ + // 3 + <- line up here + // 4 * + // 5, + // 6, + if (cin_iscomment(skipwhite(l))) + break; + lookfor = LOOKFOR_COMMA; + trypos = find_match_char('[', + curbuf->b_ind_maxparen); + if (trypos != NULL) + { + if (trypos->lnum + == curwin->w_cursor.lnum - 1) + { + // Current line is first inside + // [], line up with it. + break; + } + ourscope = trypos->lnum; + } + } + else + { + lookfor = LOOKFOR_ENUM_OR_INIT; + cont_amount = cin_first_id_amount(); + } + } + else + { + if (lookfor == LOOKFOR_INITIAL + && *l != NUL + && l[STRLEN(l) - 1] == '\\') + // XXX + cont_amount = cin_get_equal_amount( + curwin->w_cursor.lnum); + if (lookfor != LOOKFOR_TERM + && lookfor != LOOKFOR_JS_KEY + && lookfor != LOOKFOR_COMMA + && raw_string_start != curwin->w_cursor.lnum) + lookfor = LOOKFOR_UNTERM; + } + } + } + } + + // Check if we are after a while (cond); + // If so: Ignore until the matching "do". + else if (cin_iswhileofdo_end(terminated)) // XXX + { + // Found an unterminated line after a while ();, line up + // with the last one. + // while (cond); + // 100 + <- line up with this one + // -> here; + if (lookfor == LOOKFOR_UNTERM + || lookfor == LOOKFOR_ENUM_OR_INIT) + { + if (cont_amount > 0) + amount = cont_amount; + else + amount += ind_continuation; + break; + } + + if (whilelevel == 0) + { + lookfor = LOOKFOR_TERM; + amount = get_indent(); // XXX + if (theline[0] == '{') + amount += curbuf->b_ind_open_extra; + } + ++whilelevel; + } + + // We are after a "normal" statement. + // If we had another statement we can stop now and use the + // indent of that other statement. + // Otherwise the indent of the current statement may be used, + // search backwards for the next "normal" statement. + else + { + // Skip single break line, if before a switch label. It + // may be lined up with the case label. + if (lookfor == LOOKFOR_NOBREAK + && cin_isbreak(skipwhite(ml_get_curline()))) + { + lookfor = LOOKFOR_ANY; + continue; + } + + // Handle "do {" line. + if (whilelevel > 0) + { + l = cin_skipcomment(ml_get_curline()); + if (cin_isdo(l)) + { + amount = get_indent(); // XXX + --whilelevel; + continue; + } + } + + // Found a terminated line above an unterminated line. Add + // the amount for a continuation line. + // x = 1; + // y = foo + + // -> here; + // or + // int x = 1; + // int foo, + // -> here; + if (lookfor == LOOKFOR_UNTERM + || lookfor == LOOKFOR_ENUM_OR_INIT) + { + if (cont_amount > 0) + amount = cont_amount; + else + amount += ind_continuation; + break; + } + + // Found a terminated line above a terminated line or "if" + // etc. line. Use the amount of the line below us. + // x = 1; x = 1; + // if (asdf) y = 2; + // while (asdf) ->here; + // here; + // ->foo; + if (lookfor == LOOKFOR_TERM) + { + if (!lookfor_break && whilelevel == 0) + break; + } + + // First line above the one we're indenting is terminated. + // To know what needs to be done look further backward for + // a terminated line. + else + { + // position the cursor over the rightmost paren, so + // that matching it will take us back to the start of + // the line. Helps for: + // func(asdr, + // asdfasdf); + // here; + term_again: + l = ml_get_curline(); + if (find_last_paren(l, '(', ')') + && (trypos = find_match_paren( + curbuf->b_ind_maxparen)) != NULL) + { + // Check if we are on a case label now. This is + // handled above. + // case xx: if ( asdf && + // asdf) + curwin->w_cursor = *trypos; + l = ml_get_curline(); + if (cin_iscase(l, FALSE) || cin_isscopedecl(l)) + { + ++curwin->w_cursor.lnum; + curwin->w_cursor.col = 0; + continue; + } + } + + // When aligning with the case statement, don't align + // with a statement after it. + // case 1: { <-- don't use this { position + // stat; + // } + // case 2: + // stat; + // } + iscase = (curbuf->b_ind_keep_case_label + && cin_iscase(l, FALSE)); + + // Get indent and pointer to text for current line, + // ignoring any jump label. + amount = skip_label(curwin->w_cursor.lnum, &l); + + if (theline[0] == '{') + amount += curbuf->b_ind_open_extra; + // See remark above: "Only add b_ind_open_extra.." + l = skipwhite(l); + if (*l == '{') + amount -= curbuf->b_ind_open_extra; + lookfor = iscase ? LOOKFOR_ANY : LOOKFOR_TERM; + + // When a terminated line starts with "else" skip to + // the matching "if": + // else 3; + // indent this; + // Need to use the scope of this "else". XXX + // If whilelevel != 0 continue looking for a "do {". + if (lookfor == LOOKFOR_TERM + && *l != '}' + && cin_iselse(l) + && whilelevel == 0) + { + if ((trypos = find_start_brace()) == NULL + || find_match(LOOKFOR_IF, trypos->lnum) + == FAIL) + break; + continue; + } + + // If we're at the end of a block, skip to the start of + // that block. + l = ml_get_curline(); + if (find_last_paren(l, '{', '}') // XXX + && (trypos = find_start_brace()) != NULL) + { + curwin->w_cursor = *trypos; + // if not "else {" check for terminated again + // but skip block for "} else {" + l = cin_skipcomment(ml_get_curline()); + if (*l == '}' || !cin_iselse(l)) + goto term_again; + ++curwin->w_cursor.lnum; + curwin->w_cursor.col = 0; + } + } + } + } + } + } + + // add extra indent for a comment + if (cin_iscomment(theline)) + amount += curbuf->b_ind_comment; + + // subtract extra left-shift for jump labels + if (curbuf->b_ind_jump_label > 0 && original_line_islabel) + amount -= curbuf->b_ind_jump_label; + + goto theend; + } + + // 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; + } + + // 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; + + l = ml_get_curline(); + + // If we're in a comment or raw string now, skip to the start + // of it. XXX + if ((trypos = ind_find_start_CORS(NULL)) != NULL) + { + curwin->w_cursor.lnum = trypos->lnum + 1; + curwin->w_cursor.col = 0; + 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 = ml_get_curline(); + } + if (n) + { + // XXX + amount = get_baseclass_amount(cache_cpp_baseclass.lpos.col); + break; + } + + // Skip preprocessor directives and blank lines. + if (cin_ispreproc_cont(&l, &curwin->w_cursor.lnum, &amount)) + continue; + + if (cin_nocode(l)) + 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{backslash} + // 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; + } + + amount = get_indent(); // XXX + + if (amount == 0) + amount = cin_first_id_amount(); + if (amount == 0) + amount = ind_continuation; + 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 = ml_get_curline(); + + // Finding the closing '}' of a previous function. Put + // current line at the left margin. For when 'cino' has "fs". + if (*skipwhite(l) == '}') + 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 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; + } + + // 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; + + 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, &amount))) + break; + } + if (curwin->w_cursor.lnum > 0 + && cin_ends_in(look, (char_u *)"}", NULL)) + break; + + curwin->w_cursor = curpos_save; + } + + // 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 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 = ml_get_curline(); + } + + // 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 ((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{backslash} + // here"; + // char *foo = "asdf{backslash} + // 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; + + vim_free(linecopy); + + return amount; + } + + /* + * return TRUE if 'cinkeys' contains the key "keytyped", + * when == '*': Only if key is preceded with '*' (indent before insert) + * when == '!': Only if key is preceded with '!' (don't insert) + * when == ' ': Only if key is not preceded with '*'(indent afterwards) + * + * "keytyped" can have a few special values: + * KEY_OPEN_FORW + * KEY_OPEN_BACK + * KEY_COMPLETE just finished completion. + * + * If line_is_empty is TRUE accept keys with '0' before them. + */ + int + in_cinkeys( + int keytyped, + int when, + int line_is_empty) + { + char_u *look; + int try_match; + int try_match_word; + char_u *p; + char_u *line; + int icase; + int i; + + if (keytyped == NUL) + // Can happen with CTRL-Y and CTRL-E on a short line. + return FALSE; + + #ifdef FEAT_EVAL + if (*curbuf->b_p_inde != NUL) + look = curbuf->b_p_indk; // 'indentexpr' set: use 'indentkeys' + else + #endif + look = curbuf->b_p_cink; // 'indentexpr' empty: use 'cinkeys' + while (*look) + { + // Find out if we want to try a match with this key, depending on + // 'when' and a '*' or '!' before the key. + switch (when) + { + case '*': try_match = (*look == '*'); break; + case '!': try_match = (*look == '!'); break; + default: try_match = (*look != '*'); break; + } + if (*look == '*' || *look == '!') + ++look; + + // If there is a '0', only accept a match if the line is empty. + // But may still match when typing last char of a word. + if (*look == '0') + { + try_match_word = try_match; + if (!line_is_empty) + try_match = FALSE; + ++look; + } + else + try_match_word = FALSE; + + // does it look like a control character? + if (*look == '^' + #ifdef EBCDIC + && (Ctrl_chr(look[1]) != 0) + #else + && look[1] >= '?' && look[1] <= '_' + #endif + ) + { + if (try_match && keytyped == Ctrl_chr(look[1])) + return TRUE; + look += 2; + } + // 'o' means "o" command, open forward. + // 'O' means "O" command, open backward. + else if (*look == 'o') + { + if (try_match && keytyped == KEY_OPEN_FORW) + return TRUE; + ++look; + } + else if (*look == 'O') + { + if (try_match && keytyped == KEY_OPEN_BACK) + return TRUE; + ++look; + } + + // 'e' means to check for "else" at start of line and just before the + // cursor. + else if (*look == 'e') + { + if (try_match && keytyped == 'e' && curwin->w_cursor.col >= 4) + { + p = ml_get_curline(); + if (skipwhite(p) == p + curwin->w_cursor.col - 4 && + STRNCMP(p + curwin->w_cursor.col - 4, "else", 4) == 0) + return TRUE; + } + ++look; + } + + // ':' only causes an indent if it is at the end of a label or case + // statement, or when it was before typing the ':' (to fix + // class::method for C++). + else if (*look == ':') + { + if (try_match && keytyped == ':') + { + p = ml_get_curline(); + if (cin_iscase(p, FALSE) || cin_isscopedecl(p) || cin_islabel()) + return TRUE; + // Need to get the line again after cin_islabel(). + p = ml_get_curline(); + if (curwin->w_cursor.col > 2 + && p[curwin->w_cursor.col - 1] == ':' + && p[curwin->w_cursor.col - 2] == ':') + { + p[curwin->w_cursor.col - 1] = ' '; + i = (cin_iscase(p, FALSE) || cin_isscopedecl(p) + || cin_islabel()); + p = ml_get_curline(); + p[curwin->w_cursor.col - 1] = ':'; + if (i) + return TRUE; + } + } + ++look; + } + + + // Is it a key in <>, maybe? + else if (*look == '<') + { + if (try_match) + { + // make up some named keys , , , <0>, <>>, <<>, <*>, + // <:> and so that people can re-indent on o, O, e, 0, <, + // >, *, : and ! keys if they really really want to. + if (vim_strchr((char_u *)"<>!*oOe0:", look[1]) != NULL + && keytyped == look[1]) + return TRUE; + + if (keytyped == get_special_key_code(look + 1)) + return TRUE; + } + while (*look && *look != '>') + look++; + while (*look == '>') + look++; + } + + // Is it a word: "=word"? + else if (*look == '=' && look[1] != ',' && look[1] != NUL) + { + ++look; + if (*look == '~') + { + icase = TRUE; + ++look; + } + else + icase = FALSE; + p = vim_strchr(look, ','); + if (p == NULL) + p = look + STRLEN(look); + if ((try_match || try_match_word) + && curwin->w_cursor.col >= (colnr_T)(p - look)) + { + int match = FALSE; + + if (keytyped == KEY_COMPLETE) + { + char_u *s; + + // Just completed a word, check if it starts with "look". + // search back for the start of a word. + line = ml_get_curline(); + if (has_mbyte) + { + char_u *n; + + for (s = line + curwin->w_cursor.col; s > line; s = n) + { + n = mb_prevptr(line, s); + if (!vim_iswordp(n)) + break; + } + } + else + for (s = line + curwin->w_cursor.col; s > line; --s) + if (!vim_iswordc(s[-1])) + break; + if (s + (p - look) <= line + curwin->w_cursor.col + && (icase + ? MB_STRNICMP(s, look, p - look) + : STRNCMP(s, look, p - look)) == 0) + match = TRUE; + } + else + // TODO: multi-byte + if (keytyped == (int)p[-1] || (icase && keytyped < 256 + && TOLOWER_LOC(keytyped) == TOLOWER_LOC((int)p[-1]))) + { + line = ml_get_cursor(); + if ((curwin->w_cursor.col == (colnr_T)(p - look) + || !vim_iswordc(line[-(p - look) - 1])) + && (icase + ? MB_STRNICMP(line - (p - look), look, p - look) + : STRNCMP(line - (p - look), look, p - look)) + == 0) + match = TRUE; + } + if (match && try_match_word && !try_match) + { + // "0=word": Check if there are only blanks before the + // word. + if (getwhitecols_curline() != + (int)(curwin->w_cursor.col - (p - look))) + match = FALSE; + } + if (match) + return TRUE; + } + look = p; + } + + // ok, it's a boring generic character. + else + { + if (try_match && *look == keytyped) + return TRUE; + if (*look != NUL) + ++look; + } + + // Skip over ", ". + look = skip_to_option_part(look); + } + return FALSE; + } + + /* + * Do C or expression indenting on the current line. + */ + void + do_c_expr_indent(void) + { + # ifdef FEAT_EVAL + if (*curbuf->b_p_inde != NUL) + fixthisline(get_expr_indent); + else + # endif + fixthisline(get_c_indent); + } + #endif + + #if defined(FEAT_EVAL) || defined(PROTO) + /* + * "cindent(lnum)" function + */ + void + f_cindent(typval_T *argvars UNUSED, typval_T *rettv) + { + # ifdef FEAT_CINDENT + pos_T pos; + linenr_T lnum; + + pos = curwin->w_cursor; + lnum = tv_get_lnum(argvars); + if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count) + { + curwin->w_cursor.lnum = lnum; + rettv->vval.v_number = get_c_indent(); + curwin->w_cursor = pos; + } + else + # endif + rettv->vval.v_number = -1; + } + #endif *** ../vim-8.1.2126/src/edit.c 2019-09-28 19:04:06.989029610 +0200 --- src/edit.c 2019-10-09 22:10:04.866760561 +0200 *************** *** 37,43 **** #endif static void stop_insert(pos_T *end_insert_pos, int esc, int nomove); static int echeck_abbr(int); - static void replace_join(int off); static void mb_replace_pop_ins(int cc); static void replace_flush(void); static void replace_do_bs(int limit_col); --- 37,42 ---- *************** *** 76,84 **** static int ins_digraph(void); #endif static int ins_ctrl_ey(int tc); - #ifdef FEAT_SMARTINDENT - static void ins_try_si(int c); - #endif #if defined(FEAT_EVAL) static char_u *do_insert_char_pre(int c); #endif --- 75,80 ---- *************** *** 97,104 **** static int can_cindent; /* may do cindenting on this line */ #endif - static int old_indent = 0; /* for ^^D command in insert mode */ - #ifdef FEAT_RIGHTLEFT static int revins_on; /* reverse insert mode on */ static int revins_chars; /* how much to skip after edit */ --- 93,98 ---- *************** *** 1763,2010 **** } /* - * Insert an indent (for or CTRL-T) or delete an indent (for CTRL-D). - * Keep the cursor on the same character. - * type == INDENT_INC increase indent (for CTRL-T or ) - * type == INDENT_DEC decrease indent (for CTRL-D) - * type == INDENT_SET set indent to "amount" - * if round is TRUE, round the indent to 'shiftwidth' (only with _INC and _Dec). - */ - void - change_indent( - int type, - int amount, - int round, - int replaced, /* replaced character, put on replace stack */ - int call_changed_bytes) /* call changed_bytes() */ - { - int vcol; - int last_vcol; - int insstart_less; /* reduction for Insstart.col */ - int new_cursor_col; - int i; - char_u *ptr; - int save_p_list; - int start_col; - colnr_T vc; - colnr_T orig_col = 0; /* init for GCC */ - char_u *new_line, *orig_line = NULL; /* init for GCC */ - - /* VREPLACE mode needs to know what the line was like before changing */ - if (State & VREPLACE_FLAG) - { - orig_line = vim_strsave(ml_get_curline()); /* Deal with NULL below */ - orig_col = curwin->w_cursor.col; - } - - /* for the following tricks we don't want list mode */ - save_p_list = curwin->w_p_list; - curwin->w_p_list = FALSE; - vc = getvcol_nolist(&curwin->w_cursor); - vcol = vc; - - /* - * For Replace mode we need to fix the replace stack later, which is only - * possible when the cursor is in the indent. Remember the number of - * characters before the cursor if it's possible. - */ - start_col = curwin->w_cursor.col; - - /* determine offset from first non-blank */ - new_cursor_col = curwin->w_cursor.col; - beginline(BL_WHITE); - new_cursor_col -= curwin->w_cursor.col; - - insstart_less = curwin->w_cursor.col; - - /* - * If the cursor is in the indent, compute how many screen columns the - * cursor is to the left of the first non-blank. - */ - if (new_cursor_col < 0) - vcol = get_indent() - vcol; - - if (new_cursor_col > 0) /* can't fix replace stack */ - start_col = -1; - - /* - * Set the new indent. The cursor will be put on the first non-blank. - */ - if (type == INDENT_SET) - (void)set_indent(amount, call_changed_bytes ? SIN_CHANGED : 0); - else - { - int save_State = State; - - /* Avoid being called recursively. */ - if (State & VREPLACE_FLAG) - State = INSERT; - shift_line(type == INDENT_DEC, round, 1, call_changed_bytes); - State = save_State; - } - insstart_less -= curwin->w_cursor.col; - - /* - * Try to put cursor on same character. - * If the cursor is at or after the first non-blank in the line, - * compute the cursor column relative to the column of the first - * non-blank character. - * If we are not in insert mode, leave the cursor on the first non-blank. - * If the cursor is before the first non-blank, position it relative - * to the first non-blank, counted in screen columns. - */ - if (new_cursor_col >= 0) - { - /* - * When changing the indent while the cursor is touching it, reset - * Insstart_col to 0. - */ - if (new_cursor_col == 0) - insstart_less = MAXCOL; - new_cursor_col += curwin->w_cursor.col; - } - else if (!(State & INSERT)) - new_cursor_col = curwin->w_cursor.col; - else - { - /* - * Compute the screen column where the cursor should be. - */ - vcol = get_indent() - vcol; - curwin->w_virtcol = (colnr_T)((vcol < 0) ? 0 : vcol); - - /* - * Advance the cursor until we reach the right screen column. - */ - vcol = last_vcol = 0; - new_cursor_col = -1; - ptr = ml_get_curline(); - while (vcol <= (int)curwin->w_virtcol) - { - last_vcol = vcol; - if (has_mbyte && new_cursor_col >= 0) - new_cursor_col += (*mb_ptr2len)(ptr + new_cursor_col); - else - ++new_cursor_col; - vcol += lbr_chartabsize(ptr, ptr + new_cursor_col, (colnr_T)vcol); - } - vcol = last_vcol; - - /* - * May need to insert spaces to be able to position the cursor on - * the right screen column. - */ - if (vcol != (int)curwin->w_virtcol) - { - curwin->w_cursor.col = (colnr_T)new_cursor_col; - i = (int)curwin->w_virtcol - vcol; - ptr = alloc(i + 1); - if (ptr != NULL) - { - new_cursor_col += i; - ptr[i] = NUL; - while (--i >= 0) - ptr[i] = ' '; - ins_str(ptr); - vim_free(ptr); - } - } - - /* - * When changing the indent while the cursor is in it, reset - * Insstart_col to 0. - */ - insstart_less = MAXCOL; - } - - curwin->w_p_list = save_p_list; - - if (new_cursor_col <= 0) - curwin->w_cursor.col = 0; - else - curwin->w_cursor.col = (colnr_T)new_cursor_col; - curwin->w_set_curswant = TRUE; - changed_cline_bef_curs(); - - /* - * May have to adjust the start of the insert. - */ - if (State & INSERT) - { - if (curwin->w_cursor.lnum == Insstart.lnum && Insstart.col != 0) - { - if ((int)Insstart.col <= insstart_less) - Insstart.col = 0; - else - Insstart.col -= insstart_less; - } - if ((int)ai_col <= insstart_less) - ai_col = 0; - else - ai_col -= insstart_less; - } - - /* - * For REPLACE mode, may have to fix the replace stack, if it's possible. - * If the number of characters before the cursor decreased, need to pop a - * few characters from the replace stack. - * If the number of characters before the cursor increased, need to push a - * few NULs onto the replace stack. - */ - if (REPLACE_NORMAL(State) && start_col >= 0) - { - while (start_col > (int)curwin->w_cursor.col) - { - replace_join(0); /* remove a NUL from the replace stack */ - --start_col; - } - while (start_col < (int)curwin->w_cursor.col || replaced) - { - replace_push(NUL); - if (replaced) - { - replace_push(replaced); - replaced = NUL; - } - ++start_col; - } - } - - /* - * For VREPLACE mode, we also have to fix the replace stack. In this case - * it is always possible because we backspace over the whole line and then - * put it back again the way we wanted it. - */ - if (State & VREPLACE_FLAG) - { - /* If orig_line didn't allocate, just return. At least we did the job, - * even if you can't backspace. */ - if (orig_line == NULL) - return; - - /* Save new line */ - new_line = vim_strsave(ml_get_curline()); - if (new_line == NULL) - return; - - /* We only put back the new line up to the cursor */ - new_line[curwin->w_cursor.col] = NUL; - - /* Put back original line */ - ml_replace(curwin->w_cursor.lnum, orig_line, FALSE); - curwin->w_cursor.col = orig_col; - - /* Backspace from cursor to start of line */ - backspace_until_column(0); - - /* Insert new stuff into line again */ - ins_bytes(new_line); - - vim_free(new_line); - } - } - - /* * Truncate the space at the end of a line. This is to be used only in an * insert mode. It handles fixing the replace stack for REPLACE and VREPLACE * modes. --- 1757,1762 ---- *************** *** 3840,3846 **** * Join the top two items on the replace stack. This removes to "off"'th NUL * encountered. */ ! static void replace_join( int off) /* offset for which NUL to remove */ { --- 3592,3598 ---- * Join the top two items on the replace stack. This removes to "off"'th NUL * encountered. */ ! void replace_join( int off) /* offset for which NUL to remove */ { *************** *** 6070,6166 **** return c; } - #ifdef FEAT_SMARTINDENT - /* - * Try to do some very smart auto-indenting. - * Used when inserting a "normal" character. - */ - static void - ins_try_si(int c) - { - pos_T *pos, old_pos; - char_u *ptr; - int i; - int temp; - - /* - * do some very smart indenting when entering '{' or '}' - */ - if (((did_si || can_si_back) && c == '{') || (can_si && c == '}')) - { - /* - * for '}' set indent equal to indent of line containing matching '{' - */ - if (c == '}' && (pos = findmatch(NULL, '{')) != NULL) - { - old_pos = curwin->w_cursor; - /* - * If the matching '{' has a ')' immediately before it (ignoring - * white-space), then line up with the start of the line - * containing the matching '(' if there is one. This handles the - * case where an "if (..\n..) {" statement continues over multiple - * lines -- webb - */ - ptr = ml_get(pos->lnum); - i = pos->col; - if (i > 0) /* skip blanks before '{' */ - while (--i > 0 && VIM_ISWHITE(ptr[i])) - ; - curwin->w_cursor.lnum = pos->lnum; - curwin->w_cursor.col = i; - if (ptr[i] == ')' && (pos = findmatch(NULL, '(')) != NULL) - curwin->w_cursor = *pos; - i = get_indent(); - curwin->w_cursor = old_pos; - if (State & VREPLACE_FLAG) - change_indent(INDENT_SET, i, FALSE, NUL, TRUE); - else - (void)set_indent(i, SIN_CHANGED); - } - else if (curwin->w_cursor.col > 0) - { - /* - * when inserting '{' after "O" reduce indent, but not - * more than indent of previous line - */ - temp = TRUE; - if (c == '{' && can_si_back && curwin->w_cursor.lnum > 1) - { - old_pos = curwin->w_cursor; - i = get_indent(); - while (curwin->w_cursor.lnum > 1) - { - ptr = skipwhite(ml_get(--(curwin->w_cursor.lnum))); - - /* ignore empty lines and lines starting with '#'. */ - if (*ptr != '#' && *ptr != NUL) - break; - } - if (get_indent() >= i) - temp = FALSE; - curwin->w_cursor = old_pos; - } - if (temp) - shift_line(TRUE, FALSE, 1, TRUE); - } - } - - /* - * set indent of '#' always to 0 - */ - if (curwin->w_cursor.col > 0 && can_si && c == '#') - { - /* remember current indent for next line */ - old_indent = get_indent(); - (void)set_indent(0, SIN_CHANGED); - } - - /* Adjust ai_col, the char at this position can be deleted. */ - if (ai_col > curwin->w_cursor.col) - ai_col = curwin->w_cursor.col; - } - #endif - /* * Get the value that w_virtcol would have when 'list' is off. * Unless 'cpo' contains the 'L' flag. --- 5822,5827 ---- *** ../vim-8.1.2126/src/evalfunc.c 2019-10-06 22:00:08.293244132 +0200 --- src/evalfunc.c 2019-10-09 22:10:04.866760561 +0200 *************** *** 51,57 **** #endif static void f_changenr(typval_T *argvars, typval_T *rettv); static void f_char2nr(typval_T *argvars, typval_T *rettv); - static void f_cindent(typval_T *argvars, typval_T *rettv); static void f_col(typval_T *argvars, typval_T *rettv); static void f_confirm(typval_T *argvars, typval_T *rettv); static void f_copy(typval_T *argvars, typval_T *rettv); --- 51,56 ---- *************** *** 108,114 **** static void f_hlexists(typval_T *argvars, typval_T *rettv); static void f_hostname(typval_T *argvars, typval_T *rettv); static void f_iconv(typval_T *argvars, typval_T *rettv); - static void f_indent(typval_T *argvars, typval_T *rettv); static void f_index(typval_T *argvars, typval_T *rettv); static void f_input(typval_T *argvars, typval_T *rettv); static void f_inputdialog(typval_T *argvars, typval_T *rettv); --- 107,112 ---- *************** *** 128,134 **** static void f_libcallnr(typval_T *argvars, typval_T *rettv); static void f_line(typval_T *argvars, typval_T *rettv); static void f_line2byte(typval_T *argvars, typval_T *rettv); - static void f_lispindent(typval_T *argvars, typval_T *rettv); static void f_localtime(typval_T *argvars, typval_T *rettv); #ifdef FEAT_FLOAT static void f_log(typval_T *argvars, typval_T *rettv); --- 126,131 ---- *************** *** 1491,1519 **** rettv->vval.v_number = tv_get_string(&argvars[0])[0]; } - /* - * "cindent(lnum)" function - */ - static void - f_cindent(typval_T *argvars UNUSED, typval_T *rettv) - { - #ifdef FEAT_CINDENT - pos_T pos; - linenr_T lnum; - - pos = curwin->w_cursor; - lnum = tv_get_lnum(argvars); - if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count) - { - curwin->w_cursor.lnum = lnum; - rettv->vval.v_number = get_c_indent(); - curwin->w_cursor = pos; - } - else - #endif - rettv->vval.v_number = -1; - } - win_T * get_optional_window(typval_T *argvars, int idx) { --- 1488,1493 ---- *************** *** 3946,3966 **** } /* - * "indent()" function - */ - static void - f_indent(typval_T *argvars, typval_T *rettv) - { - linenr_T lnum; - - lnum = tv_get_lnum(argvars); - if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count) - rettv->vval.v_number = get_indent_lnum(lnum); - else - rettv->vval.v_number = -1; - } - - /* * "index()" function */ static void --- 3920,3925 ---- *************** *** 4451,4479 **** } /* - * "lispindent(lnum)" function - */ - static void - f_lispindent(typval_T *argvars UNUSED, typval_T *rettv) - { - #ifdef FEAT_LISP - pos_T pos; - linenr_T lnum; - - pos = curwin->w_cursor; - lnum = tv_get_lnum(argvars); - if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count) - { - curwin->w_cursor.lnum = lnum; - rettv->vval.v_number = get_lisp_indent(); - curwin->w_cursor = pos; - } - else - #endif - rettv->vval.v_number = -1; - } - - /* * "localtime()" function */ static void --- 4410,4415 ---- *** ../vim-8.1.2126/src/ex_cmds.c 2019-10-05 11:30:04.975429899 +0200 --- src/ex_cmds.c 2019-10-09 22:10:04.870760534 +0200 *************** *** 661,881 **** } /* - * ":retab". - */ - void - ex_retab(exarg_T *eap) - { - linenr_T lnum; - int got_tab = FALSE; - long num_spaces = 0; - long num_tabs; - long len; - long col; - long vcol; - long start_col = 0; /* For start of white-space string */ - long start_vcol = 0; /* For start of white-space string */ - long old_len; - char_u *ptr; - char_u *new_line = (char_u *)1; /* init to non-NULL */ - int did_undo; /* called u_save for current line */ - #ifdef FEAT_VARTABS - int *new_vts_array = NULL; - char_u *new_ts_str; /* string value of tab argument */ - #else - int temp; - int new_ts; - #endif - int save_list; - linenr_T first_line = 0; /* first changed line */ - linenr_T last_line = 0; /* last changed line */ - - save_list = curwin->w_p_list; - curwin->w_p_list = 0; /* don't want list mode here */ - - #ifdef FEAT_VARTABS - new_ts_str = eap->arg; - if (!tabstop_set(eap->arg, &new_vts_array)) - return; - while (vim_isdigit(*(eap->arg)) || *(eap->arg) == ',') - ++(eap->arg); - - // This ensures that either new_vts_array and new_ts_str are freshly - // allocated, or new_vts_array points to an existing array and new_ts_str - // is null. - if (new_vts_array == NULL) - { - new_vts_array = curbuf->b_p_vts_array; - new_ts_str = NULL; - } - else - new_ts_str = vim_strnsave(new_ts_str, eap->arg - new_ts_str); - #else - new_ts = getdigits(&(eap->arg)); - if (new_ts < 0) - { - emsg(_(e_positive)); - return; - } - if (new_ts == 0) - new_ts = curbuf->b_p_ts; - #endif - for (lnum = eap->line1; !got_int && lnum <= eap->line2; ++lnum) - { - ptr = ml_get(lnum); - col = 0; - vcol = 0; - did_undo = FALSE; - for (;;) - { - if (VIM_ISWHITE(ptr[col])) - { - if (!got_tab && num_spaces == 0) - { - /* First consecutive white-space */ - start_vcol = vcol; - start_col = col; - } - if (ptr[col] == ' ') - num_spaces++; - else - got_tab = TRUE; - } - else - { - if (got_tab || (eap->forceit && num_spaces > 1)) - { - /* Retabulate this string of white-space */ - - /* len is virtual length of white string */ - len = num_spaces = vcol - start_vcol; - num_tabs = 0; - if (!curbuf->b_p_et) - { - #ifdef FEAT_VARTABS - int t, s; - - tabstop_fromto(start_vcol, vcol, - curbuf->b_p_ts, new_vts_array, &t, &s); - num_tabs = t; - num_spaces = s; - #else - temp = new_ts - (start_vcol % new_ts); - if (num_spaces >= temp) - { - num_spaces -= temp; - num_tabs++; - } - num_tabs += num_spaces / new_ts; - num_spaces -= (num_spaces / new_ts) * new_ts; - #endif - } - if (curbuf->b_p_et || got_tab || - (num_spaces + num_tabs < len)) - { - if (did_undo == FALSE) - { - did_undo = TRUE; - if (u_save((linenr_T)(lnum - 1), - (linenr_T)(lnum + 1)) == FAIL) - { - new_line = NULL; /* flag out-of-memory */ - break; - } - } - - /* len is actual number of white characters used */ - len = num_spaces + num_tabs; - old_len = (long)STRLEN(ptr); - new_line = alloc(old_len - col + start_col + len + 1); - if (new_line == NULL) - break; - if (start_col > 0) - mch_memmove(new_line, ptr, (size_t)start_col); - mch_memmove(new_line + start_col + len, - ptr + col, (size_t)(old_len - col + 1)); - ptr = new_line + start_col; - for (col = 0; col < len; col++) - ptr[col] = (col < num_tabs) ? '\t' : ' '; - ml_replace(lnum, new_line, FALSE); - if (first_line == 0) - first_line = lnum; - last_line = lnum; - ptr = new_line; - col = start_col + len; - } - } - got_tab = FALSE; - num_spaces = 0; - } - if (ptr[col] == NUL) - break; - vcol += chartabsize(ptr + col, (colnr_T)vcol); - if (has_mbyte) - col += (*mb_ptr2len)(ptr + col); - else - ++col; - } - if (new_line == NULL) /* out of memory */ - break; - line_breakcheck(); - } - if (got_int) - emsg(_(e_interr)); - - #ifdef FEAT_VARTABS - // If a single value was given then it can be considered equal to - // either the value of 'tabstop' or the value of 'vartabstop'. - if (tabstop_count(curbuf->b_p_vts_array) == 0 - && tabstop_count(new_vts_array) == 1 - && curbuf->b_p_ts == tabstop_first(new_vts_array)) - ; /* not changed */ - else if (tabstop_count(curbuf->b_p_vts_array) > 0 - && tabstop_eq(curbuf->b_p_vts_array, new_vts_array)) - ; /* not changed */ - else - redraw_curbuf_later(NOT_VALID); - #else - if (curbuf->b_p_ts != new_ts) - redraw_curbuf_later(NOT_VALID); - #endif - if (first_line != 0) - changed_lines(first_line, 0, last_line + 1, 0L); - - curwin->w_p_list = save_list; /* restore 'list' */ - - #ifdef FEAT_VARTABS - if (new_ts_str != NULL) /* set the new tabstop */ - { - // If 'vartabstop' is in use or if the value given to retab has more - // than one tabstop then update 'vartabstop'. - int *old_vts_ary = curbuf->b_p_vts_array; - - if (tabstop_count(old_vts_ary) > 0 || tabstop_count(new_vts_array) > 1) - { - set_string_option_direct((char_u *)"vts", -1, new_ts_str, - OPT_FREE|OPT_LOCAL, 0); - curbuf->b_p_vts_array = new_vts_array; - vim_free(old_vts_ary); - } - else - { - // 'vartabstop' wasn't in use and a single value was given to - // retab then update 'tabstop'. - curbuf->b_p_ts = tabstop_first(new_vts_array); - vim_free(new_vts_array); - } - vim_free(new_ts_str); - } - #else - curbuf->b_p_ts = new_ts; - #endif - coladvance(curwin->w_curswant); - - u_clearline(); - } - - /* * :move command - move lines line1-line2 to line dest * * return FAIL for failure, OK otherwise --- 661,666 ---- *** ../vim-8.1.2126/src/globals.h 2019-09-30 23:12:10.874289153 +0200 --- src/globals.h 2019-10-09 22:10:04.870760534 +0200 *************** *** 843,848 **** --- 843,850 ---- EXTERN int can_si_back INIT(= FALSE); #endif + EXTERN int old_indent INIT(= 0); // for ^^D command in insert mode + EXTERN pos_T saved_cursor // w_cursor before formatting text. #ifdef DO_INIT = {0, 0, 0} *** ../vim-8.1.2126/src/indent.c 2019-09-02 22:31:08.010296361 +0200 --- src/indent.c 2019-10-09 22:38:54.252685028 +0200 *************** *** 13,3885 **** #include "vim.h" ! #if defined(FEAT_CINDENT) || defined(FEAT_SMARTINDENT) ! ! static int cin_iscase(char_u *s, int strict); ! static int cin_isscopedecl(char_u *s); /* ! * Return TRUE if the string "line" starts with a word from 'cinwords'. */ int ! cin_is_cinword(char_u *line) ! { ! char_u *cinw; ! char_u *cinw_buf; ! int cinw_len; ! int retval = FALSE; ! int len; ! ! cinw_len = (int)STRLEN(curbuf->b_p_cinw) + 1; ! cinw_buf = alloc(cinw_len); ! if (cinw_buf != NULL) ! { ! line = skipwhite(line); ! for (cinw = curbuf->b_p_cinw; *cinw; ) ! { ! len = copy_option_part(&cinw, cinw_buf, cinw_len, ","); ! if (STRNCMP(line, cinw_buf, len) == 0 ! && (!vim_iswordc(line[len]) || !vim_iswordc(line[len - 1]))) ! { ! retval = TRUE; ! break; ! } ! } ! vim_free(cinw_buf); ! } ! return retval; ! } ! #endif ! ! #if defined(FEAT_CINDENT) || defined(FEAT_SYN_HL) ! ! static char_u *skip_string(char_u *p); ! static pos_T *find_start_rawstring(int ind_maxcomment); ! ! /* ! * 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 ! { ! return find_start_comment(curbuf->b_ind_maxcomment); ! } ! ! pos_T * ! find_start_comment(int ind_maxcomment) // XXX { ! pos_T *pos; ! char_u *line; ! char_u *p; ! int cur_maxcomment = ind_maxcomment; ! for (;;) { ! pos = findmatchlimit(NULL, '*', FM_BACKWARD, cur_maxcomment); ! if (pos == NULL) ! break; ! ! // 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) ! break; ! cur_maxcomment = curwin->w_cursor.lnum - pos->lnum - 1; ! if (cur_maxcomment <= 0) ! { ! pos = NULL; ! break; ! } ! } ! return pos; ! } ! ! /* ! * 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. ! * If is_raw is given and returns start of raw_string, sets it to true. ! * Return NULL when not inside a comment or raw string. ! * "CORS" -> Comment Or Raw String ! */ ! static pos_T * ! ind_find_start_CORS(linenr_T *is_raw) // XXX ! { ! static pos_T comment_pos_copy; ! pos_T *comment_pos; ! pos_T *rs_pos; ! ! comment_pos = find_start_comment(curbuf->b_ind_maxcomment); ! if (comment_pos != NULL) ! { ! // Need to make a copy of the static pos in findmatchlimit(), ! // calling find_start_rawstring() may change it. ! comment_pos_copy = *comment_pos; ! comment_pos = &comment_pos_copy; ! } ! 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_POS(*rs_pos, *comment_pos))) ! { ! if (is_raw != NULL && rs_pos != NULL) ! *is_raw = rs_pos->lnum; ! 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. ! */ ! static char_u * ! skip_string(char_u *p) ! { ! int i; ! // We loop, because strings may be concatenated: "date""time". ! for ( ; ; ++p) ! { ! if (p[0] == '\'') // 'c' or '\n' or '\000' ! { ! if (!p[1]) // ' at end of line ! break; ! i = 2; ! if (p[1] == '\\') // '\n' or '\000' ! { ! ++i; ! while (vim_isdigit(p[i - 1])) // '\000' ! ++i; ! } ! if (p[i] == '\'') // check for trailing ' { ! p += i; ! continue; } } ! else if (p[0] == '"') // start of string { ! for (++p; p[0]; ++p) ! { ! if (p[0] == '\\' && p[1] != NUL) ! ++p; ! else if (p[0] == '"') // end of string ! break; ! } ! if (p[0] == '"') ! 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 } - if (!*p) - --p; // backup from NUL - return p; - } - #endif // FEAT_CINDENT || FEAT_SYN_HL ! #if defined(FEAT_CINDENT) || defined(PROTO) /* ! * Return TRUE if C-indenting is on. */ int ! cindent_on(void) { ! return (!p_paste && (curbuf->b_p_cin ! # ifdef FEAT_EVAL ! || *curbuf->b_p_inde != NUL ! # endif ! )); ! } ! ! // Find result cache for cpp_baseclass ! typedef struct { ! int found; ! lpos_T lpos; ! } cpp_baseclass_cache_T; ! /* ! * Functions for C-indenting. ! * Most of this originally comes from Eric Fischer. ! */ ! /* ! * Below "XXX" means that this function may unlock the current line. ! */ ! static int cin_isdefault(char_u *); ! static int cin_ispreproc(char_u *); ! static int cin_iscomment(char_u *); ! static int cin_islinecomment(char_u *); ! static int cin_isterminated(char_u *, int, int); ! static int cin_iselse(char_u *); ! static int cin_ends_in(char_u *, char_u *, char_u *); ! static int cin_starts_with(char_u *s, char *word); ! static pos_T *find_match_paren(int); ! static pos_T *find_match_char(int c, int ind_maxparen); ! static int find_last_paren(char_u *l, int start, int end); ! static int find_match(int lookfor, linenr_T ourscope); ! /* ! * 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) ! { ! while (*s) { ! char_u *prev_s = s; ! ! s = skipwhite(s); ! ! // Perl/shell # comment comment continues until eol. Require a space ! // before # to avoid recognizing $#array. ! if (curbuf->b_ind_hash_comment != 0 && s != prev_s && *s == '#') ! { ! s += STRLEN(s); ! break; ! } ! if (*s != '/') ! break; ! ++s; ! if (*s == '/') // slash-slash comment continues till eol { ! s += STRLEN(s); break; } - if (*s != '*') - break; - for (++s; *s; ++s) // skip slash-star comment - if (s[0] == '*' && s[1] == '/') - { - s += 2; - break; - } } ! return 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) ! { ! return *cin_skipcomment(s) == NUL; } /* ! * Check previous lines for a "//" line comment, skipping over blank lines. */ ! static pos_T * ! find_line_comment(void) // XXX { ! static pos_T pos; ! char_u *line; ! char_u *p; ! pos = curwin->w_cursor; ! while (--pos.lnum > 0) { ! line = ml_get(pos.lnum); ! p = skipwhite(line); ! if (cin_islinecomment(p)) { ! pos.col = (int)(p - line); ! return &pos; ! } ! if (*p != NUL) break; } ! return NULL; } /* ! * Return TRUE if "text" starts with "key:". */ ! static int ! cin_has_js_key(char_u *text) { ! char_u *s = skipwhite(text); ! int quote = -1; ! if (*s == '\'' || *s == '"') { ! // can be 'key': or "key": ! quote = *s; ! ++s; } - if (!vim_isIDc(*s)) // need at least one ID character - return FALSE; - - while (vim_isIDc(*s)) - ++s; - if (*s == quote) - ++s; ! s = cin_skipcomment(s); ! ! // "::" is not a label, it's C++ ! return (*s == ':' && s[1] != ':'); } /* ! * Check if string matches "label:"; move to character after ':' if true. ! * "*s" must point to the start of the label, if there is one. */ ! static int ! cin_islabel_skip(char_u **s) { ! if (!vim_isIDc(**s)) // need at least one ID character ! return FALSE; ! ! while (vim_isIDc(**s)) ! (*s)++; ! ! *s = cin_skipcomment(*s); ! ! // "::" is not a label, it's C++ ! return (**s == ':' && *++*s != ':'); ! } ! /* ! * Recognize a label: "label:". ! * Note: curwin->w_cursor must be where we are looking for the label. ! */ ! static int ! cin_islabel(void) // XXX ! { ! char_u *s; ! s = cin_skipcomment(ml_get_curline()); ! // Exclude "default" from labels, since it should be indented ! // like a switch label. Same for C++ scope declarations. ! if (cin_isdefault(s)) ! return FALSE; ! if (cin_isscopedecl(s)) ! return FALSE; ! if (cin_islabel_skip(&s)) { ! // Only accept a label if the previous line is terminated or is a case ! // label. ! pos_T cursor_save; ! pos_T *trypos; ! char_u *line; ! ! cursor_save = curwin->w_cursor; ! while (curwin->w_cursor.lnum > 1) { ! --curwin->w_cursor.lnum; ! // 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_CORS(NULL)) != NULL) // XXX ! curwin->w_cursor = *trypos; ! line = ml_get_curline(); ! if (cin_ispreproc(line)) // ignore #defines, #if, etc. ! continue; ! if (*(line = cin_skipcomment(line)) == NUL) ! continue; ! curwin->w_cursor = cursor_save; ! if (cin_isterminated(line, TRUE, FALSE) ! || cin_isscopedecl(line) ! || cin_iscase(line, TRUE) ! || (cin_islabel_skip(&line) && cin_nocode(line))) ! return TRUE; ! return FALSE; } ! curwin->w_cursor = cursor_save; ! return TRUE; // label at start of file??? } ! return FALSE; } /* ! * Recognize structure initialization and enumerations: ! * "[typedef] [static|public|protected|private] enum" ! * "[typedef] [static|public|protected|private] = {" */ static int ! cin_isinit(void) { ! char_u *s; ! static char *skip[] = {"static", "public", "protected", "private"}; ! ! s = cin_skipcomment(ml_get_curline()); ! ! if (cin_starts_with(s, "typedef")) ! s = cin_skipcomment(s + 7); ! ! for (;;) ! { ! int i, l; ! ! for (i = 0; i < (int)(sizeof(skip) / sizeof(char *)); ++i) ! { ! l = (int)strlen(skip[i]); ! if (cin_starts_with(s, skip[i])) ! { ! s = cin_skipcomment(s + l); ! l = 0; ! break; ! } ! } ! if (l != 0) ! break; ! } ! if (cin_starts_with(s, "enum")) return TRUE; ! if (cin_ends_in(s, (char_u *)"=", (char_u *)"{")) ! return TRUE; ! return FALSE; } /* ! * Recognize a switch label: "case .*:" or "default:". */ ! static int ! cin_iscase( ! char_u *s, ! int strict) // Allow relaxed check of case statement for JS { ! s = cin_skipcomment(s); ! if (cin_starts_with(s, "case")) ! { ! for (s += 4; *s; ++s) ! { ! s = cin_skipcomment(s); ! if (*s == ':') ! { ! if (s[1] == ':') // skip over "::" for C++ ! ++s; ! else ! return TRUE; ! } ! if (*s == '\'' && s[1] && s[2] == '\'') ! s += 2; // skip over ':' ! else if (*s == '/' && (s[1] == '*' || s[1] == '/')) ! return FALSE; // stop at comment ! else if (*s == '"') ! { ! // JS etc. ! if (strict) ! return FALSE; // stop at string ! else ! return TRUE; ! } ! } ! return FALSE; ! } ! if (cin_isdefault(s)) ! return TRUE; ! return FALSE; } /* ! * Recognize a "default" switch label. */ ! static int ! cin_isdefault(char_u *s) { ! return (STRNCMP(s, "default", 7) == 0 ! && *(s = cin_skipcomment(s + 7)) == ':' ! && s[1] != ':'); } /* ! * Recognize a "public/private/protected" scope declaration label. */ ! static int ! cin_isscopedecl(char_u *s) { ! int i; ! ! s = cin_skipcomment(s); ! if (STRNCMP(s, "public", 6) == 0) ! i = 6; ! else if (STRNCMP(s, "protected", 9) == 0) ! i = 9; ! else if (STRNCMP(s, "private", 7) == 0) ! i = 7; ! else ! return FALSE; ! return (*(s = cin_skipcomment(s + i)) == ':' && s[1] != ':'); } ! // Maximum number of lines to search back for a "namespace" line. ! #define FIND_NAMESPACE_LIM 20 /* ! * Recognize a "namespace" scope declaration. */ ! static int ! cin_is_cpp_namespace(char_u *s) { ! char_u *p; ! int has_name = FALSE; ! int has_name_start = FALSE; ! ! s = cin_skipcomment(s); ! if (STRNCMP(s, "namespace", 9) == 0 && (s[9] == NUL || !vim_iswordc(s[9]))) ! { ! p = cin_skipcomment(skipwhite(s + 9)); ! while (*p != NUL) ! { ! if (VIM_ISWHITE(*p)) ! { ! has_name = TRUE; // found end of a name ! p = cin_skipcomment(skipwhite(p)); ! } ! else if (*p == '{') ! { ! break; ! } ! else if (vim_iswordc(*p)) ! { ! has_name_start = TRUE; ! if (has_name) ! return FALSE; // word character after skipping past name ! ++p; ! } ! else if (p[0] == ':' && p[1] == ':' && vim_iswordc(p[2])) ! { ! if (!has_name_start || has_name) ! return FALSE; ! // C++ 17 nested namespace ! p += 3; ! } ! else ! { ! return FALSE; ! } ! } ! return TRUE; ! } ! return FALSE; } /* ! * Recognize a `extern "C"` or `extern "C++"` linkage specifications. */ ! static int ! cin_is_cpp_extern_c(char_u *s) { ! 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]))) ! { ! p = cin_skipcomment(skipwhite(s + 6)); ! while (*p != NUL) ! { ! if (VIM_ISWHITE(*p)) ! { ! p = cin_skipcomment(skipwhite(p)); ! } ! else if (*p == '{') ! { ! break; ! } ! else if (p[0] == '"' && p[1] == 'C' && p[2] == '"') ! { ! if (has_string_literal) ! return FALSE; ! has_string_literal = TRUE; ! p += 3; ! } ! else if (p[0] == '"' && p[1] == 'C' && p[2] == '+' && p[3] == '+' ! && p[4] == '"') ! { ! if (has_string_literal) ! return FALSE; ! has_string_literal = TRUE; ! p += 5; ! } ! else ! { ! return FALSE; ! } ! } ! return has_string_literal ? TRUE : FALSE; ! } ! return FALSE; } /* ! * Return a pointer to the first non-empty non-comment character after a ':'. ! * Return NULL if not found. ! * case 234: a = b; ! * ^ */ ! static char_u * ! after_label(char_u *l) { ! for ( ; *l; ++l) ! { ! if (*l == ':') ! { ! if (l[1] == ':') // skip over "::" for C++ ! ++l; ! else if (!cin_iscase(l + 1, FALSE)) ! break; ! } ! else if (*l == '\'' && l[1] && l[2] == '\'') ! l += 2; // skip over 'x' ! } ! if (*l == NUL) ! return NULL; ! l = cin_skipcomment(l + 1); ! if (*l == NUL) ! return NULL; ! return l; } /* ! * Get indent of line "lnum", skipping a label. ! * Return 0 if there is nothing after the label. */ ! static int ! get_indent_nolabel (linenr_T lnum) // XXX { ! char_u *l; ! pos_T fp; ! colnr_T col; ! char_u *p; ! ! l = ml_get(lnum); ! p = after_label(l); ! if (p == NULL) ! return 0; ! ! fp.col = (colnr_T)(p - l); ! fp.lnum = lnum; ! getvcol(curwin, &fp, &col, NULL, NULL); ! return (int)col; } /* ! * Find indent for line "lnum", ignoring any case or jump label. ! * Also return a pointer to the text (after the label) in "pp". ! * label: if (asdf && asdfasdf) ! * ^ */ ! static int ! skip_label(linenr_T lnum, char_u **pp) { ! char_u *l; ! int amount; ! pos_T cursor_save; ! ! cursor_save = curwin->w_cursor; ! curwin->w_cursor.lnum = lnum; ! l = ml_get_curline(); ! // XXX ! if (cin_iscase(l, FALSE) || cin_isscopedecl(l) || cin_islabel()) ! { ! amount = get_indent_nolabel(lnum); ! l = after_label(ml_get_curline()); ! if (l == NULL) // just in case ! l = ml_get_curline(); ! } ! else ! { ! amount = get_indent(); ! l = ml_get_curline(); ! } ! *pp = l; ! ! curwin->w_cursor = cursor_save; ! return amount; } /* ! * Return the indent of the first variable name after a type in a declaration. ! * int a, indent of "a" ! * static struct foo b, indent of "b" ! * enum bla c, indent of "c" ! * Returns zero when it doesn't look like a declaration. */ ! static int ! cin_first_id_amount(void) { ! char_u *line, *p, *s; ! int len; ! pos_T fp; ! colnr_T col; ! ! line = ml_get_curline(); ! p = skipwhite(line); ! len = (int)(skiptowhite(p) - p); ! if (len == 6 && STRNCMP(p, "static", 6) == 0) ! { ! p = skipwhite(p + 6); ! len = (int)(skiptowhite(p) - p); ! } ! if (len == 6 && STRNCMP(p, "struct", 6) == 0) ! p = skipwhite(p + 6); ! else if (len == 4 && STRNCMP(p, "enum", 4) == 0) ! p = skipwhite(p + 4); ! else if ((len == 8 && STRNCMP(p, "unsigned", 8) == 0) ! || (len == 6 && STRNCMP(p, "signed", 6) == 0)) ! { ! s = skipwhite(p + len); ! if ((STRNCMP(s, "int", 3) == 0 && VIM_ISWHITE(s[3])) ! || (STRNCMP(s, "long", 4) == 0 && VIM_ISWHITE(s[4])) ! || (STRNCMP(s, "short", 5) == 0 && VIM_ISWHITE(s[5])) ! || (STRNCMP(s, "char", 4) == 0 && VIM_ISWHITE(s[4]))) ! p = s; ! } ! for (len = 0; vim_isIDc(p[len]); ++len) ! ; ! if (len == 0 || !VIM_ISWHITE(p[len]) || cin_nocode(p)) ! return 0; ! ! p = skipwhite(p + len); ! fp.lnum = curwin->w_cursor.lnum; ! fp.col = (colnr_T)(p - line); ! getvcol(curwin, &fp, &col, NULL, NULL); ! return (int)col; } /* ! * Return the indent of the first non-blank after an equal sign. ! * char *foo = "here"; ! * Return zero if no (useful) equal sign found. ! * Return -1 if the line above "lnum" ends in a backslash. ! * foo = "asdf\ ! * asdf\ ! * here"; */ ! static int ! cin_get_equal_amount(linenr_T lnum) { ! char_u *line; ! char_u *s; ! colnr_T col; ! pos_T fp; ! ! if (lnum > 1) ! { ! line = ml_get(lnum - 1); ! if (*line != NUL && line[STRLEN(line) - 1] == '\\') ! return -1; ! } ! ! line = s = ml_get(lnum); ! while (*s != NUL && vim_strchr((char_u *)"=;{}\"'", *s) == NULL) ! { ! if (cin_iscomment(s)) // ignore comments ! s = cin_skipcomment(s); ! else ! ++s; ! } ! if (*s != '=') ! return 0; ! ! s = skipwhite(s + 1); ! if (cin_nocode(s)) ! return 0; ! ! if (*s == '"') // nice alignment for continued strings ! ++s; ! ! fp.lnum = lnum; ! fp.col = (colnr_T)(s - line); ! getvcol(curwin, &fp, &col, NULL, NULL); ! return (int)col; } /* ! * Recognize a preprocessor statement: Any line that starts with '#'. */ ! static int ! cin_ispreproc(char_u *s) { ! if (*skipwhite(s) == '#') ! return TRUE; ! return FALSE; } /* ! * Return TRUE if line "*pp" at "*lnump" is a preprocessor statement or a ! * 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) { ! char_u *line = *pp; ! linenr_T lnum = *lnump; ! int retval = FALSE; ! int candidate_amount = *amount; ! if (*line != NUL && line[STRLEN(line) - 1] == '\\') ! candidate_amount = get_indent_lnum(lnum); ! ! for (;;) { ! if (cin_ispreproc(line)) { ! retval = TRUE; ! *lnump = lnum; ! break; } ! if (lnum == 1) ! break; ! line = ml_get(--lnum); ! if (*line == NUL || line[STRLEN(line) - 1] != '\\') break; } ! ! if (lnum != *lnump) ! *pp = ml_get(*lnump); ! if (retval) ! *amount = candidate_amount; ! return retval; ! } ! ! /* ! * Recognize the start of a C or C++ comment. ! */ ! static int ! cin_iscomment(char_u *p) ! { ! return (p[0] == '/' && (p[1] == '*' || p[1] == '/')); } /* ! * Recognize the start of a "//" comment. */ ! static int ! cin_islinecomment(char_u *p) { ! return (p[0] == '/' && p[1] == '/'); ! } ! /* ! * Recognize a line that starts with '{' or '}', or ends with ';', ',', '{' or ! * '}'. ! * Don't consider "} else" a terminated line. ! * If a line begins with an "else", only consider it terminated if no unmatched ! * opening braces follow (handle "else { foo();" correctly). ! * Return the character terminating the line (ending char's have precedence if ! * both apply in order to determine initializations). ! */ ! static int ! cin_isterminated( ! char_u *s, ! int incl_open, // include '{' at the end as terminator ! int incl_comma) // recognize a trailing comma ! { ! char_u found_start = 0; ! unsigned n_open = 0; ! int is_else = FALSE; ! ! s = cin_skipcomment(s); ! ! if (*s == '{' || (*s == '}' && !cin_iselse(s))) ! found_start = *s; ! ! if (!found_start) ! is_else = cin_iselse(s); ! ! while (*s) ! { ! // skip over comments, "" strings and 'c'haracters ! s = skip_string(cin_skipcomment(s)); ! if (*s == '}' && n_open > 0) ! --n_open; ! if ((!is_else || n_open == 0) ! && (*s == ';' || *s == '}' || (incl_comma && *s == ',')) ! && cin_nocode(s + 1)) ! return *s; ! else if (*s == '{') { ! if (incl_open && cin_nocode(s + 1)) ! return *s; else ! ++n_open; } ! ! if (*s) ! s++; } ! return found_start; } /* ! * Recognize the basic picture of a function declaration -- it needs to ! * have an open paren somewhere and a close paren at the end of the line and ! * no semicolons anywhere. ! * When a line ends in a comma we continue looking in the next line. ! * "sp" points to a string with the line. When looking at other lines it must ! * be restored to the line. When it's NULL fetch lines here. ! * "first_lnum" is where we start looking. ! * "min_lnum" is the line before which we will not be looking. */ ! static int ! cin_isfuncdecl( ! char_u **sp, ! linenr_T first_lnum, ! linenr_T min_lnum) { char_u *s; ! linenr_T lnum = first_lnum; ! linenr_T save_lnum = curwin->w_cursor.lnum; int retval = FALSE; ! pos_T *trypos; ! int just_started = TRUE; ! if (sp == NULL) ! s = ml_get(lnum); ! else ! s = *sp; ! curwin->w_cursor.lnum = lnum; ! if (find_last_paren(s, '(', ')') ! && (trypos = find_match_paren(curbuf->b_ind_maxparen)) != NULL) ! { ! lnum = trypos->lnum; ! if (lnum < min_lnum) ! { ! curwin->w_cursor.lnum = save_lnum; ! return FALSE; ! } ! ! s = ml_get(lnum); ! } ! curwin->w_cursor.lnum = save_lnum; ! // Ignore line starting with #. ! if (cin_ispreproc(s)) ! return FALSE; ! ! while (*s && *s != '(' && *s != ';' && *s != '\'' && *s != '"') { ! if (cin_iscomment(s)) // ignore comments ! s = cin_skipcomment(s); ! else if (*s == ':') { ! if (*(s + 1) == ':') ! s += 2; ! else ! // To avoid a mistake in the following situation: ! // A::A(int a, int b) ! // : a(0) // <--not a function decl ! // , b(0) ! // {... ! return FALSE; ! } ! else ! ++s; ! } ! if (*s != '(') ! return FALSE; // ';', ' or " before any () or no '(' ! while (*s && *s != ';' && *s != '\'' && *s != '"') ! { ! if (*s == ')' && cin_nocode(s + 1)) ! { ! /* ! * ')' at the end: may have found a match ! * Check for he previous line not to end in a backslash: ! * #if defined(x) && \ ! * defined(y) ! */ ! lnum = first_lnum - 1; ! s = ml_get(lnum); ! if (*s == NUL || s[STRLEN(s) - 1] != '\\') ! retval = TRUE; ! goto done; ! } ! if ((*s == ',' && cin_nocode(s + 1)) || s[1] == NUL || cin_nocode(s)) ! { ! int comma = (*s == ','); ! // ',' at the end: continue looking in the next line. ! // At the end: check for ',' in the next line, for this style: ! // func(arg1 ! // , arg2) ! for (;;) { ! if (lnum >= curbuf->b_ml.ml_line_count) ! break; ! s = ml_get(++lnum); ! if (!cin_ispreproc(s)) ! break; } ! if (lnum >= curbuf->b_ml.ml_line_count) ! break; ! // Require a comma at end of the line or a comma or ')' at the ! // start of next line. ! s = skipwhite(s); ! if (!just_started && (!comma && *s != ',' && *s != ')')) break; ! just_started = FALSE; } ! else if (cin_iscomment(s)) // ignore comments ! s = cin_skipcomment(s); ! else { ! ++s; ! just_started = FALSE; } } ! ! done: ! if (lnum != first_lnum && sp != NULL) ! *sp = ml_get(first_lnum); ! ! return retval; ! } ! ! static int ! cin_isif(char_u *p) ! { ! return (STRNCMP(p, "if", 2) == 0 && !vim_isIDc(p[2])); ! } ! ! static int ! cin_iselse( ! char_u *p) ! { ! if (*p == '}') // accept "} else" ! p = cin_skipcomment(p + 1); ! return (STRNCMP(p, "else", 4) == 0 && !vim_isIDc(p[4])); ! } ! ! static int ! cin_isdo(char_u *p) ! { ! return (STRNCMP(p, "do", 2) == 0 && !vim_isIDc(p[2])); ! } ! ! /* ! * Check if this is a "while" that should have a matching "do". ! * 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 ! { ! pos_T cursor_save; ! pos_T *trypos; ! int retval = FALSE; ! ! p = cin_skipcomment(p); ! if (*p == '}') // accept "} while (cond);" ! p = cin_skipcomment(p + 1); ! if (cin_starts_with(p, "while")) { ! cursor_save = curwin->w_cursor; ! curwin->w_cursor.lnum = lnum; ! curwin->w_cursor.col = 0; ! p = ml_get_curline(); ! while (*p && *p != 'w') // skip any '}', until the 'w' of the "while" ! { ++p; ! ++curwin->w_cursor.col; ! } ! if ((trypos = findmatchlimit(NULL, 0, 0, ! curbuf->b_ind_maxparen)) != NULL ! && *cin_skipcomment(ml_get_pos(trypos) + 1) == ';') ! retval = TRUE; ! curwin->w_cursor = cursor_save; } - return retval; - } ! /* ! * Check whether in "p" there is an "if", "for" or "while" before "*poffset". ! * Return 0 if there is none. ! * 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) ! { ! int offset = *poffset; ! ! if (offset-- < 2) ! return 0; ! while (offset > 2 && VIM_ISWHITE(line[offset])) ! --offset; ! offset -= 1; ! if (!STRNCMP(line + offset, "if", 2)) ! goto probablyFound; ! if (offset >= 1) { ! offset -= 1; ! if (!STRNCMP(line + offset, "for", 3)) ! goto probablyFound; ! ! if (offset >= 2) ! { ! offset -= 2; ! if (!STRNCMP(line + offset, "while", 5)) ! goto probablyFound; } - } - return 0; ! probablyFound: ! if (!offset || !vim_isIDc(line[offset - 1])) { ! *poffset = offset; ! return 1; } - return 0; - } ! /* ! * Return TRUE if we are at the end of a do-while. ! * do ! * nothing; ! * while (foo ! * && bar); <-- here ! * Adjust the cursor to the line with "while". ! */ ! static int ! cin_iswhileofdo_end(int terminated) ! { ! char_u *line; ! char_u *p; ! char_u *s; ! pos_T *trypos; ! int i; ! ! if (terminated != ';') // there must be a ';' at the end ! return FALSE; ! ! p = line = ml_get_curline(); ! while (*p != NUL) { ! p = cin_skipcomment(p); ! if (*p == ')') { ! s = skipwhite(p + 1); ! if (*s == ';' && cin_nocode(s + 1)) { ! // Found ");" at end of the line, now check there is "while" ! // before the matching '('. XXX ! i = (int)(p - line); ! curwin->w_cursor.col = i; ! trypos = find_match_paren(curbuf->b_ind_maxparen); ! if (trypos != NULL) { ! s = cin_skipcomment(ml_get(trypos->lnum)); ! if (*s == '}') // accept "} while (cond);" ! s = cin_skipcomment(s + 1); ! if (cin_starts_with(s, "while")) ! { ! curwin->w_cursor.lnum = trypos->lnum; ! return TRUE; ! } } ! ! // Searching may have made "line" invalid, get it again. ! line = ml_get_curline(); ! p = line + i; } - } - if (*p != NUL) - ++p; - } - return FALSE; - } - - static int - cin_isbreak(char_u *p) - { - return (STRNCMP(p, "break", 5) == 0 && !vim_isIDc(p[5])); - } - - /* - * Find the position of a C++ base-class declaration or - * constructor-initialization. eg: - * - * class MyClass : - * baseClass <-- here - * class MyClass : public baseClass, - * anotherBaseClass <-- here (should probably lineup ??) - * MyClass::MyClass(...) : - * baseClass(...) <-- here (constructor-initialization) - * - * This is a lot of guessing. Watch out for "cond ? func() : foo". - */ - static int - cin_is_cpp_baseclass( - cpp_baseclass_cache_T *cached) // input and output - { - lpos_T *pos = &cached->lpos; // find position - char_u *s; - int class_or_struct, lookfor_ctor_init, cpp_base_class; - linenr_T lnum = curwin->w_cursor.lnum; - char_u *line = ml_get_curline(); - - if (pos->lnum <= lnum) - return cached->found; // Use the cached result - - pos->col = 0; - - s = skipwhite(line); - if (*s == '#') // skip #define FOO x ? (x) : x - return FALSE; - s = cin_skipcomment(s); - if (*s == NUL) - return FALSE; ! cpp_base_class = lookfor_ctor_init = class_or_struct = FALSE; ! // Search for a line starting with '#', empty, ending in ';' or containing ! // '{' or '}' and start below it. This handles the following situations: ! // a = cond ? ! // func() : ! // asdf; ! // func::foo() ! // : something ! // {} ! // Foo::Foo (int one, int two) ! // : something(4), ! // somethingelse(3) ! // {} ! while (lnum > 1) ! { ! line = ml_get(lnum - 1); ! s = skipwhite(line); ! if (*s == '#' || *s == NUL) ! break; ! while (*s != NUL) ! { ! s = cin_skipcomment(s); ! if (*s == '{' || *s == '}' ! || (*s == ';' && cin_nocode(s + 1))) ! break; ! if (*s != NUL) ! ++s; } - if (*s != NUL) - break; - --lnum; - } ! pos->lnum = lnum; ! line = ml_get(lnum); ! s = line; ! for (;;) ! { ! if (*s == NUL) { ! if (lnum == curwin->w_cursor.lnum) break; ! // Continue in the cursor line. ! line = ml_get(++lnum); ! s = line; } ! if (s == line) { ! // don't recognize "case (foo):" as a baseclass ! if (cin_iscase(s, FALSE)) ! break; ! s = cin_skipcomment(line); ! if (*s == NUL) ! continue; } ! if (s[0] == '"' || (s[0] == 'R' && s[1] == '"')) ! s = skip_string(s) + 1; ! else if (s[0] == ':') ! { ! if (s[1] == ':') ! { ! // skip double colon. It can't be a constructor ! // initialization any more ! lookfor_ctor_init = FALSE; ! s = cin_skipcomment(s + 2); ! } ! else if (lookfor_ctor_init || class_or_struct) ! { ! // we have something found, that looks like the start of ! // cpp-base-class-declaration or constructor-initialization ! cpp_base_class = TRUE; ! lookfor_ctor_init = class_or_struct = FALSE; ! pos->col = 0; ! s = cin_skipcomment(s + 1); ! } ! else ! s = cin_skipcomment(s + 1); ! } ! else if ((STRNCMP(s, "class", 5) == 0 && !vim_isIDc(s[5])) ! || (STRNCMP(s, "struct", 6) == 0 && !vim_isIDc(s[6]))) ! { ! class_or_struct = TRUE; ! lookfor_ctor_init = FALSE; ! if (*s == 'c') ! s = cin_skipcomment(s + 5); ! else ! s = cin_skipcomment(s + 6); } ! else { ! if (s[0] == '{' || s[0] == '}' || s[0] == ';') ! { ! cpp_base_class = lookfor_ctor_init = class_or_struct = FALSE; ! } ! else if (s[0] == ')') ! { ! // Constructor-initialization is assumed if we come across ! // something like "):" ! class_or_struct = FALSE; ! lookfor_ctor_init = TRUE; ! } ! else if (s[0] == '?') ! { ! // Avoid seeing '() :' after '?' as constructor init. ! return FALSE; ! } ! else if (!vim_isIDc(s[0])) ! { ! // if it is not an identifier, we are wrong ! class_or_struct = FALSE; ! lookfor_ctor_init = FALSE; ! } ! else if (pos->col == 0) ! { ! // it can't be a constructor-initialization any more ! lookfor_ctor_init = FALSE; ! ! // the first statement starts here: lineup with this one... ! if (cpp_base_class) ! pos->col = (colnr_T)(s - line); ! } ! ! // When the line ends in a comma don't align with it. ! if (lnum == curwin->w_cursor.lnum && *s == ',' && cin_nocode(s + 1)) ! pos->col = 0; ! s = cin_skipcomment(s + 1); } ! } ! ! cached->found = cpp_base_class; ! if (cpp_base_class) ! pos->lnum = lnum; ! return cpp_base_class; ! } ! ! static int ! get_baseclass_amount(int col) ! { ! int amount; ! colnr_T vcol; ! pos_T *trypos; ! ! if (col == 0) ! { ! amount = get_indent(); ! if (find_last_paren(ml_get_curline(), '(', ')') ! && (trypos = find_match_paren(curbuf->b_ind_maxparen)) != NULL) ! amount = get_indent_lnum(trypos->lnum); // XXX ! if (!cin_ends_in(ml_get_curline(), (char_u *)",", NULL)) ! amount += curbuf->b_ind_cpp_baseclass; } else ! { ! curwin->w_cursor.col = col; ! getvcol(curwin, &curwin->w_cursor, &vcol, NULL, NULL); ! amount = (int)vcol; ! } ! if (amount < curbuf->b_ind_cpp_baseclass) ! amount = curbuf->b_ind_cpp_baseclass; ! return amount; } /* ! * Return TRUE if string "s" ends with the string "find", possibly followed by ! * 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) { ! char_u *p = s; ! char_u *r; ! int len = (int)STRLEN(find); ! while (*p != NUL) { ! p = cin_skipcomment(p); ! if (STRNCMP(p, find, len) == 0) { ! r = skipwhite(p + len); ! if (ignore != NULL && STRNCMP(r, ignore, STRLEN(ignore)) == 0) ! r = skipwhite(r + STRLEN(ignore)); ! if (cin_nocode(r)) ! return TRUE; } ! if (*p != NUL) ! ++p; } - return FALSE; - } - - /* - * Return TRUE when "s" starts with "word" and then a non-ID character. - */ - static int - cin_starts_with(char_u *s, char *word) - { - int l = (int)STRLEN(word); ! return (STRNCMP(s, word, l) == 0 && !vim_isIDc(s[l])); } /* ! * Skip strings, chars and comments until at or past "trypos". ! * Return the column found. */ ! static int ! cin_skip2pos(pos_T *trypos) ! { ! char_u *line; ! char_u *p; ! char_u *new_p; ! ! p = line = ml_get(trypos->lnum); ! while (*p && (colnr_T)(p - line) < trypos->col) { ! if (cin_iscomment(p)) ! p = cin_skipcomment(p); ! else ! { ! new_p = skip_string(p); ! if (new_p == p) ! ++p; ! else ! p = new_p; ! } } ! return (int)(p - line); } /* ! * Find the '{' at the start of the block we are in. ! * Return NULL if no match found. ! * Ignore a '{' that is in a comment, makes indenting the next three lines ! * work. */ ! /* foo() */ ! /* { */ ! /* } */ ! ! static pos_T * ! find_start_brace(void) // XXX { ! pos_T cursor_save; ! pos_T *trypos; ! pos_T *pos; ! static pos_T pos_copy; ! ! cursor_save = curwin->w_cursor; ! while ((trypos = findmatchlimit(NULL, '{', FM_BLOCKSTOP, 0)) != NULL) ! { ! pos_copy = *trypos; // copy pos_T, next findmatch will change it ! trypos = &pos_copy; ! curwin->w_cursor = *trypos; ! pos = NULL; ! // ignore the { if it's in a // or / * * / comment ! if ((colnr_T)cin_skip2pos(trypos) == trypos->col ! && (pos = ind_find_start_CORS(NULL)) == NULL) // XXX ! break; ! if (pos != NULL) ! curwin->w_cursor.lnum = pos->lnum; ! } ! curwin->w_cursor = cursor_save; ! return trypos; } /* ! * Find the matching '(', ignoring it if it is in a comment. ! * Return NULL if no match found. */ ! static pos_T * ! find_match_paren(int ind_maxparen) // XXX { ! return find_match_char('(', ind_maxparen); ! } ! static pos_T * ! find_match_char(int c, int ind_maxparen) // XXX ! { ! pos_T cursor_save; ! pos_T *trypos; ! static pos_T pos_copy; ! int ind_maxp_wk; ! ! cursor_save = curwin->w_cursor; ! ind_maxp_wk = ind_maxparen; ! retry: ! if ((trypos = findmatchlimit(NULL, c, 0, ind_maxp_wk)) != NULL) { ! // check if the ( is in a // comment ! if ((colnr_T)cin_skip2pos(trypos) > trypos->col) ! { ! ind_maxp_wk = ind_maxparen - (int)(cursor_save.lnum - trypos->lnum); ! if (ind_maxp_wk > 0) ! { ! curwin->w_cursor = *trypos; ! curwin->w_cursor.col = 0; // XXX ! goto retry; ! } ! trypos = NULL; ! } ! else { ! pos_T *trypos_wk; ! pos_copy = *trypos; // copy trypos, findmatch will change it ! trypos = &pos_copy; ! curwin->w_cursor = *trypos; ! if ((trypos_wk = ind_find_start_CORS(NULL)) != NULL) // XXX { ! ind_maxp_wk = ind_maxparen - (int)(cursor_save.lnum ! - trypos_wk->lnum); ! if (ind_maxp_wk > 0) ! { ! curwin->w_cursor = *trypos_wk; ! goto retry; ! } ! trypos = NULL; } } } - curwin->w_cursor = cursor_save; - return trypos; - } ! /* ! * Find the matching '(', ignoring it if it is in a comment or before an ! * unmatched {. ! * Return NULL if no match found. ! */ ! static pos_T * ! find_match_paren_after_brace (int ind_maxparen) // XXX ! { ! pos_T *trypos = find_match_paren(ind_maxparen); ! if (trypos != NULL) ! { ! pos_T *tryposBrace = find_start_brace(); ! // If both an unmatched '(' and '{' is found. Ignore the '(' ! // position if the '{' is further down. ! if (tryposBrace != NULL ! && (trypos->lnum != tryposBrace->lnum ! ? trypos->lnum < tryposBrace->lnum ! : trypos->col < tryposBrace->col)) ! trypos = NULL; } ! return trypos; } /* ! * Return ind_maxparen corrected for the difference in line number between the ! * cursor position and "startpos". This makes sure that searching for a ! * matching paren above the cursor line doesn't find a match because of ! * looking a few lines further. */ ! static int ! corr_ind_maxparen(pos_T *startpos) { ! long n = (long)startpos->lnum - (long)curwin->w_cursor.lnum; ! ! if (n > 0 && n < curbuf->b_ind_maxparen / 2) ! return curbuf->b_ind_maxparen - (int)n; ! return curbuf->b_ind_maxparen; } /* ! * 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) { int i; ! int retval = FALSE; ! int open_count = 0; ! ! curwin->w_cursor.col = 0; // default is start of line ! for (i = 0; l[i] != NUL; i++) { ! i = (int)(cin_skipcomment(l + i) - l); // ignore parens in comments ! i = (int)(skip_string(l + i) - l); // ignore parens in quotes ! if (l[i] == start) ! ++open_count; ! else if (l[i] == end) { ! if (open_count > 0) ! --open_count; else { ! curwin->w_cursor.col = i; ! retval = TRUE; } } } ! return retval; } /* ! * Parse 'cinoptions' and set the values in "curbuf". ! * Must be called when 'cinoptions', 'shiftwidth' and/or 'tabstop' changes. */ void ! parse_cino(buf_T *buf) ! { ! char_u *p; ! char_u *l; ! char_u *digits; ! int n; ! int divider; ! int fraction = 0; ! int sw = (int)get_sw_value(buf); ! ! // Set the default values. ! ! // Spaces from a block's opening brace the prevailing indent for that ! // block should be. ! buf->b_ind_level = sw; ! ! // Spaces from the edge of the line an open brace that's at the end of a ! // line is imagined to be. ! buf->b_ind_open_imag = 0; ! ! // Spaces from the prevailing indent for a line that is not preceded by ! // an opening brace. ! buf->b_ind_no_brace = 0; ! ! // Column where the first { of a function should be located }. ! buf->b_ind_first_open = 0; ! ! // Spaces from the prevailing indent a leftmost open brace should be ! // located. ! buf->b_ind_open_extra = 0; ! ! // Spaces from the matching open brace (real location for one at the left ! // edge; imaginary location from one that ends a line) the matching close ! // brace should be located. ! buf->b_ind_close_extra = 0; ! ! // Spaces from the edge of the line an open brace sitting in the leftmost ! // column is imagined to be. ! buf->b_ind_open_left_imag = 0; ! ! // Spaces jump labels should be shifted to the left if N is non-negative, ! // otherwise the jump label will be put to column 1. ! buf->b_ind_jump_label = -1; ! ! // Spaces from the switch() indent a "case xx" label should be located. ! buf->b_ind_case = sw; ! ! // Spaces from the "case xx:" code after a switch() should be located. ! buf->b_ind_case_code = sw; ! ! // Lineup break at end of case in switch() with case label. ! buf->b_ind_case_break = 0; ! ! // Spaces from the class declaration indent a scope declaration label ! // should be located. ! buf->b_ind_scopedecl = sw; ! ! // Spaces from the scope declaration label code should be located. ! buf->b_ind_scopedecl_code = sw; ! ! // Amount K&R-style parameters should be indented. ! buf->b_ind_param = sw; ! ! // Amount a function type spec should be indented. ! buf->b_ind_func_type = sw; ! ! // Amount a cpp base class declaration or constructor initialization ! // should be indented. ! buf->b_ind_cpp_baseclass = sw; ! ! // additional spaces beyond the prevailing indent a continuation line ! // should be located. ! buf->b_ind_continuation = sw; ! ! // Spaces from the indent of the line with an unclosed parentheses. ! buf->b_ind_unclosed = sw * 2; ! ! // Spaces from the indent of the line with an unclosed parentheses, which ! // itself is also unclosed. ! buf->b_ind_unclosed2 = sw; ! ! // Suppress ignoring spaces from the indent of a line starting with an ! // unclosed parentheses. ! buf->b_ind_unclosed_noignore = 0; ! ! // If the opening paren is the last nonwhite character on the line, and ! // b_ind_unclosed_wrapped is nonzero, use this indent relative to the outer ! // context (for very long lines). ! buf->b_ind_unclosed_wrapped = 0; ! ! // Suppress ignoring white space when lining up with the character after ! // an unclosed parentheses. ! buf->b_ind_unclosed_whiteok = 0; ! ! // Indent a closing parentheses under the line start of the matching ! // opening parentheses. ! buf->b_ind_matching_paren = 0; ! ! // Indent a closing parentheses under the previous line. ! buf->b_ind_paren_prev = 0; ! ! // Extra indent for comments. ! buf->b_ind_comment = 0; ! ! // Spaces from the comment opener when there is nothing after it. ! buf->b_ind_in_comment = 3; ! ! // Boolean: if non-zero, use b_ind_in_comment even if there is something ! // after the comment opener. ! buf->b_ind_in_comment2 = 0; ! ! // Max lines to search for an open paren. ! buf->b_ind_maxparen = 20; ! ! // Max lines to search for an open comment. ! buf->b_ind_maxcomment = 70; ! ! // Handle braces for java code. ! buf->b_ind_java = 0; ! ! // Not to confuse JS object properties with labels. ! buf->b_ind_js = 0; ! ! // Handle blocked cases correctly. ! buf->b_ind_keep_case_label = 0; ! ! // Handle C++ namespace. ! buf->b_ind_cpp_namespace = 0; ! ! // Handle continuation lines containing conditions of if(), for() and ! // while(). ! buf->b_ind_if_for_while = 0; ! ! // indentation for # comments ! buf->b_ind_hash_comment = 0; ! ! // Handle C++ extern "C" or "C++" ! buf->b_ind_cpp_extern_c = 0; ! for (p = buf->b_p_cino; *p; ) { ! l = p++; ! if (*p == '-') ! ++p; ! digits = p; // remember where the digits start ! n = getdigits(&p); ! divider = 0; ! if (*p == '.') // ".5s" means a fraction ! { ! fraction = atol((char *)++p); ! while (VIM_ISDIGIT(*p)) ! { ! ++p; ! if (divider) ! divider *= 10; ! else ! divider = 10; ! } ! } ! if (*p == 's') // "2s" means two times 'shiftwidth' ! { ! if (p == digits) ! n = sw; // just "s" is one 'shiftwidth' else ! { ! n *= sw; ! if (divider) ! n += (sw * fraction + divider / 2) / divider; ! } ! ++p; } ! if (l[1] == '-') ! n = -n; ! // When adding an entry here, also update the default 'cinoptions' in ! // doc/indent.txt, and add explanation for it! ! switch (*l) { ! case '>': buf->b_ind_level = n; break; ! case 'e': buf->b_ind_open_imag = n; break; ! case 'n': buf->b_ind_no_brace = n; break; ! case 'f': buf->b_ind_first_open = n; break; ! case '{': buf->b_ind_open_extra = n; break; ! case '}': buf->b_ind_close_extra = n; break; ! case '^': buf->b_ind_open_left_imag = n; break; ! case 'L': buf->b_ind_jump_label = n; break; ! case ':': buf->b_ind_case = n; break; ! case '=': buf->b_ind_case_code = n; break; ! case 'b': buf->b_ind_case_break = n; break; ! case 'p': buf->b_ind_param = n; break; ! case 't': buf->b_ind_func_type = n; break; ! case '/': buf->b_ind_comment = n; break; ! case 'c': buf->b_ind_in_comment = n; break; ! case 'C': buf->b_ind_in_comment2 = n; break; ! case 'i': buf->b_ind_cpp_baseclass = n; break; ! case '+': buf->b_ind_continuation = n; break; ! case '(': buf->b_ind_unclosed = n; break; ! case 'u': buf->b_ind_unclosed2 = n; break; ! case 'U': buf->b_ind_unclosed_noignore = n; break; ! case 'W': buf->b_ind_unclosed_wrapped = n; break; ! case 'w': buf->b_ind_unclosed_whiteok = n; break; ! case 'm': buf->b_ind_matching_paren = n; break; ! case 'M': buf->b_ind_paren_prev = n; break; ! case ')': buf->b_ind_maxparen = n; break; ! case '*': buf->b_ind_maxcomment = n; break; ! case 'g': buf->b_ind_scopedecl = n; break; ! case 'h': buf->b_ind_scopedecl_code = n; break; ! case 'j': buf->b_ind_java = n; break; ! case 'J': buf->b_ind_js = n; break; ! case 'l': buf->b_ind_keep_case_label = n; break; ! case '#': buf->b_ind_hash_comment = n; break; ! case 'N': buf->b_ind_cpp_namespace = n; break; ! case 'k': buf->b_ind_if_for_while = n; break; ! case 'E': buf->b_ind_cpp_extern_c = n; break; } - if (*p == ',') - ++p; - } - } - - /* - * 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; - int amount; - int scope_amount; - int cur_amount = MAXCOL; - colnr_T col; - char_u *theline; - char_u *linecopy; - pos_T *trypos; - pos_T *comment_pos; - pos_T *tryposBrace = NULL; - pos_T tryposCopy; - pos_T our_paren_pos; - char_u *start; - int start_brace; - #define BRACE_IN_COL0 1 // '{' is in column 0 - #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; - char_u terminated; - int lookfor; - #define LOOKFOR_INITIAL 0 - #define LOOKFOR_IF 1 - #define LOOKFOR_DO 2 - #define LOOKFOR_CASE 3 - #define LOOKFOR_ANY 4 - #define LOOKFOR_TERM 5 - #define LOOKFOR_UNTERM 6 - #define LOOKFOR_SCOPEDECL 7 - #define LOOKFOR_NOBREAK 8 - #define LOOKFOR_CPP_BASECLASS 9 - #define LOOKFOR_ENUM_OR_INIT 10 - #define LOOKFOR_JS_KEY 11 - #define LOOKFOR_COMMA 12 - - int whilelevel; - linenr_T lnum; - int n; - int iscase; - int lookfor_break; - int lookfor_cpp_namespace = FALSE; - int cont_amount = 0; // amount for continuation line - int original_line_islabel; - int added_to_amount = 0; - int js_cur_has_key = 0; - linenr_T raw_string_start = 0; - cpp_baseclass_cache_T cache_cpp_baseclass = { FALSE, { MAXLNUM, 0 } }; - - // make a copy, value is changed below - int ind_continuation = curbuf->b_ind_continuation; - - // remember where the cursor was when we started - cur_curpos = curwin->w_cursor; - - // if we are at line 1 zero indent is fine, right? - if (cur_curpos.lnum == 1) - return 0; - - // Get a copy of the current contents of the line. - // This is required, because only the most recent line obtained with - // ml_get is valid! - linecopy = vim_strsave(ml_get(cur_curpos.lnum)); - if (linecopy == NULL) - return 0; - - // In insert mode and the cursor is on a ')' truncate the line at the - // cursor position. We don't want to line up with the matching '(' when - // inserting new stuff. - // For unknown reasons the cursor might be past the end of the line, thus - // check for that. - if ((State & INSERT) - && curwin->w_cursor.col < (colnr_T)STRLEN(linecopy) - && linecopy[curwin->w_cursor.col] == ')') - linecopy[curwin->w_cursor.col] = NUL; - - theline = skipwhite(linecopy); ! // move the cursor to the start of the line ! ! curwin->w_cursor.col = 0; ! ! 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_POS(*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))) ! { ! 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. ! 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. ! 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. ! 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 ! char_u lead_middle[COM_MAX_LEN]; // middle-comment string ! char_u lead_end[COM_MAX_LEN]; // end-comment string ! char_u *p; ! int start_align = 0; ! int start_off = 0; ! int done = FALSE; ! ! // find how indented the line beginning the comment is ! getvcol(curwin, comment_pos, &col, NULL, NULL); ! amount = col; ! *lead_start = NUL; ! *lead_middle = NUL; ! ! p = curbuf->b_p_com; ! while (*p != NUL) { ! int align = 0; ! int off = 0; ! int what = 0; ! ! while (*p != NUL && *p != ':') ! { ! if (*p == COM_START || *p == COM_END || *p == COM_MIDDLE) ! what = *p++; ! else if (*p == COM_LEFT || *p == COM_RIGHT) ! align = *p++; ! else if (VIM_ISDIGIT(*p) || *p == '-') ! off = getdigits(&p); ! else ! ++p; ! } ! ! if (*p == ':') ! ++p; ! (void)copy_option_part(&p, lead_end, COM_MAX_LEN, ","); ! if (what == COM_START) ! { ! STRCPY(lead_start, lead_end); ! lead_start_len = (int)STRLEN(lead_start); ! start_off = off; ! start_align = align; ! } ! else if (what == COM_MIDDLE) ! { ! STRCPY(lead_middle, lead_end); ! lead_middle_len = (int)STRLEN(lead_middle); ! } ! else if (what == COM_END) ! { ! // If our line starts with the middle comment string, line it ! // up with the comment opener per the 'comments' option. ! if (STRNCMP(theline, lead_middle, lead_middle_len) == 0 ! && STRNCMP(theline, lead_end, STRLEN(lead_end)) != 0) ! { ! done = TRUE; ! if (curwin->w_cursor.lnum > 1) ! { ! // If the start comment string matches in the previous ! // line, use the indent of that line plus offset. If ! // the middle comment string matches in the previous ! // line, use the indent of that line. XXX ! look = skipwhite(ml_get(curwin->w_cursor.lnum - 1)); ! if (STRNCMP(look, lead_start, lead_start_len) == 0) ! amount = get_indent_lnum(curwin->w_cursor.lnum - 1); ! else if (STRNCMP(look, lead_middle, ! lead_middle_len) == 0) ! { ! amount = get_indent_lnum(curwin->w_cursor.lnum - 1); ! break; ! } ! // If the start comment string doesn't match with the ! // start of the comment, skip this entry. XXX ! else if (STRNCMP(ml_get(comment_pos->lnum) + comment_pos->col, ! lead_start, lead_start_len) != 0) ! continue; ! } ! if (start_off != 0) ! amount += start_off; ! else if (start_align == COM_RIGHT) ! amount += vim_strsize(lead_start) ! - vim_strsize(lead_middle); ! break; ! } ! ! // If our line starts with the end comment string, line it up ! // with the middle comment ! if (STRNCMP(theline, lead_middle, lead_middle_len) != 0 ! && STRNCMP(theline, lead_end, STRLEN(lead_end)) == 0) ! { ! amount = get_indent_lnum(curwin->w_cursor.lnum - 1); ! // XXX ! if (off != 0) ! amount += off; ! else if (align == COM_RIGHT) ! amount += vim_strsize(lead_start) ! - vim_strsize(lead_middle); ! done = TRUE; ! break; ! } ! } } ! ! // If our line starts with an asterisk, line up with the ! // asterisk in the comment opener; otherwise, line up ! // with the first character of the comment text. ! if (done) ! ; ! else if (theline[0] == '*') ! amount += 1; else ! { ! // If we are more than one line away from the comment opener, take ! // the indent of the previous non-empty line. If 'cino' has "CO" ! // and we are just below the comment opener and there are any ! // white characters after it line up with the text after it; ! // otherwise, add the amount specified by "c" in 'cino' ! amount = -1; ! for (lnum = cur_curpos.lnum - 1; lnum > comment_pos->lnum; --lnum) ! { ! if (linewhite(lnum)) // skip blank lines ! continue; ! amount = get_indent_lnum(lnum); // XXX ! break; ! } ! if (amount == -1) // use the comment opener ! { ! if (!curbuf->b_ind_in_comment2) ! { ! start = ml_get(comment_pos->lnum); ! look = start + comment_pos->col + 2; // skip / and * ! if (*look != NUL) // if something after it ! comment_pos->col = (colnr_T)(skipwhite(look) - start); ! } ! 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? ! 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 ! if (((trypos = find_match_paren(curbuf->b_ind_maxparen)) != NULL ! && curbuf->b_ind_java == 0) ! || (tryposBrace = find_start_brace()) != NULL ! || trypos != NULL) ! { ! if (trypos != NULL && tryposBrace != NULL) ! { ! // Both an unmatched '(' and '{' is found. Use the one which is ! // closer to the current cursor position, set the other to NULL. ! if (trypos->lnum != tryposBrace->lnum ! ? trypos->lnum < tryposBrace->lnum ! : trypos->col < tryposBrace->col) ! trypos = NULL; ! else ! tryposBrace = NULL; ! } ! ! if (trypos != NULL) ! { ! // If the matching paren is more than one line away, use the indent of ! // a previous non-empty line that matches the same paren. ! if (theline[0] == ')' && curbuf->b_ind_paren_prev) { ! // Line up with the start of the matching paren line. ! amount = get_indent_lnum(curwin->w_cursor.lnum - 1); // XXX } ! else { ! amount = -1; ! our_paren_pos = *trypos; ! for (lnum = cur_curpos.lnum - 1; lnum > our_paren_pos.lnum; --lnum) { ! l = skipwhite(ml_get(lnum)); ! if (cin_nocode(l)) // skip comment lines ! continue; ! if (cin_ispreproc_cont(&l, &lnum, &amount)) ! continue; // ignore #define, #if, etc. ! curwin->w_cursor.lnum = lnum; ! ! // Skip a comment or raw string. XXX ! if ((trypos = ind_find_start_CORS(NULL)) != NULL) ! { ! lnum = trypos->lnum + 1; ! continue; ! } ! ! // XXX ! if ((trypos = find_match_paren( ! corr_ind_maxparen(&cur_curpos))) != NULL ! && trypos->lnum == our_paren_pos.lnum ! && trypos->col == our_paren_pos.col) ! { ! amount = get_indent_lnum(lnum); // XXX ! ! if (theline[0] == ')') ! { ! if (our_paren_pos.lnum != lnum ! && cur_amount > amount) ! cur_amount = amount; ! amount = -1; ! } ! break; ! } } } ! // Line up with line where the matching paren is. XXX ! // If the line starts with a '(' or the indent for unclosed ! // parentheses is zero, line up with the unclosed parentheses. ! if (amount == -1) ! { ! int ignore_paren_col = 0; ! int is_if_for_while = 0; ! ! if (curbuf->b_ind_if_for_while) ! { ! // Look for the outermost opening parenthesis on this line ! // and check whether it belongs to an "if", "for" or "while". ! ! pos_T cursor_save = curwin->w_cursor; ! pos_T outermost; ! char_u *line; ! ! trypos = &our_paren_pos; ! do { ! outermost = *trypos; ! curwin->w_cursor.lnum = outermost.lnum; ! curwin->w_cursor.col = outermost.col; ! trypos = find_match_paren(curbuf->b_ind_maxparen); ! } while (trypos && trypos->lnum == outermost.lnum); ! curwin->w_cursor = cursor_save; ! line = ml_get(outermost.lnum); ! is_if_for_while = ! cin_is_if_for_while_before_offset(line, &outermost.col); ! } ! amount = skip_label(our_paren_pos.lnum, &look); ! look = skipwhite(look); ! if (*look == '(') ! { ! linenr_T save_lnum = curwin->w_cursor.lnum; ! char_u *line; ! int look_col; ! ! // Ignore a '(' in front of the line that has a match before ! // our matching '('. ! curwin->w_cursor.lnum = our_paren_pos.lnum; ! line = ml_get_curline(); ! look_col = (int)(look - line); ! curwin->w_cursor.col = look_col + 1; ! if ((trypos = findmatchlimit(NULL, ')', 0, ! curbuf->b_ind_maxparen)) ! != NULL ! && trypos->lnum == our_paren_pos.lnum ! && trypos->col < our_paren_pos.col) ! ignore_paren_col = trypos->col + 1; ! curwin->w_cursor.lnum = save_lnum; ! look = ml_get(our_paren_pos.lnum) + look_col; ! } ! if (theline[0] == ')' || (curbuf->b_ind_unclosed == 0 ! && is_if_for_while == 0) ! || (!curbuf->b_ind_unclosed_noignore && *look == '(' ! && ignore_paren_col == 0)) ! { ! // If we're looking at a close paren, line up right there; ! // otherwise, line up with the next (non-white) character. ! // When b_ind_unclosed_wrapped is set and the matching paren is ! // the last nonwhite character of the line, use either the ! // indent of the current line or the indentation of the next ! // outer paren and add b_ind_unclosed_wrapped (for very long ! // lines). ! if (theline[0] != ')') ! { ! cur_amount = MAXCOL; ! l = ml_get(our_paren_pos.lnum); ! if (curbuf->b_ind_unclosed_wrapped ! && cin_ends_in(l, (char_u *)"(", NULL)) ! { ! // look for opening unmatched paren, indent one level ! // for each additional level ! n = 1; ! for (col = 0; col < our_paren_pos.col; ++col) ! { ! switch (l[col]) ! { ! case '(': ! case '{': ++n; ! break; ! ! case ')': ! case '}': if (n > 1) ! --n; ! break; ! } ! } ! our_paren_pos.col = 0; ! amount += n * curbuf->b_ind_unclosed_wrapped; ! } ! else if (curbuf->b_ind_unclosed_whiteok) ! our_paren_pos.col++; ! else ! { ! col = our_paren_pos.col + 1; ! while (VIM_ISWHITE(l[col])) ! col++; ! if (l[col] != NUL) // In case of trailing space ! our_paren_pos.col = col; ! else ! our_paren_pos.col++; ! } ! } ! // Find how indented the paren is, or the character after it ! // if we did the above "if". ! if (our_paren_pos.col > 0) ! { ! getvcol(curwin, &our_paren_pos, &col, NULL, NULL); ! if (cur_amount > (int)col) ! cur_amount = col; ! } ! } ! if (theline[0] == ')' && curbuf->b_ind_matching_paren) ! { ! // Line up with the start of the matching paren line. ! } ! else if ((curbuf->b_ind_unclosed == 0 && is_if_for_while == 0) ! || (!curbuf->b_ind_unclosed_noignore ! && *look == '(' && ignore_paren_col == 0)) { ! if (cur_amount != MAXCOL) ! amount = cur_amount; } else { ! // Add b_ind_unclosed2 for each '(' before our matching one, ! // but ignore (void) before the line (ignore_paren_col). ! col = our_paren_pos.col; ! while ((int)our_paren_pos.col > ignore_paren_col) ! { ! --our_paren_pos.col; ! switch (*ml_get_pos(&our_paren_pos)) ! { ! case '(': amount += curbuf->b_ind_unclosed2; ! col = our_paren_pos.col; ! break; ! case ')': amount -= curbuf->b_ind_unclosed2; ! col = MAXCOL; ! break; ! } ! } ! ! // Use b_ind_unclosed once, when the first '(' is not inside ! // braces ! if (col == MAXCOL) ! amount += curbuf->b_ind_unclosed; ! else ! { ! curwin->w_cursor.lnum = our_paren_pos.lnum; ! curwin->w_cursor.col = col; ! if (find_match_paren_after_brace(curbuf->b_ind_maxparen) ! != NULL) ! amount += curbuf->b_ind_unclosed2; ! else ! { ! if (is_if_for_while) ! amount += curbuf->b_ind_if_for_while; ! else ! amount += curbuf->b_ind_unclosed; ! } ! } ! // For a line starting with ')' use the minimum of the two ! // positions, to avoid giving it more indent than the previous ! // lines: ! // func_long_name( if (x ! // arg && yy ! // ) ^ not here ) ^ not here ! if (cur_amount < amount) ! amount = cur_amount; } } ! // add extra indent for a comment ! if (cin_iscomment(theline)) ! amount += curbuf->b_ind_comment; ! } ! else ! { ! // We are inside braces, there is a { before this line at the position ! // stored in tryposBrace. ! // Make a copy of tryposBrace, it may point to pos_copy inside ! // find_start_brace(), which may be changed somewhere. ! tryposCopy = *tryposBrace; ! tryposBrace = &tryposCopy; ! trypos = tryposBrace; ! ourscope = trypos->lnum; ! start = ml_get(ourscope); ! ! // Now figure out how indented the line is in general. ! // If the brace was at the start of the line, we use that; ! // otherwise, check out the indentation of the line as ! // a whole and then add the "imaginary indent" to that. ! look = skipwhite(start); ! if (*look == '{') { ! getvcol(curwin, trypos, &col, NULL, NULL); ! amount = col; ! if (*start == '{') ! start_brace = BRACE_IN_COL0; ! else ! start_brace = BRACE_AT_START; } - else - { - // That opening brace might have been on a continuation - // line. if so, find the start of the line. - curwin->w_cursor.lnum = ourscope; - - // Position the cursor over the rightmost paren, so that - // matching it will take us back to the start of the line. - lnum = ourscope; - if (find_last_paren(start, '(', ')') - && (trypos = find_match_paren(curbuf->b_ind_maxparen)) - != NULL) - lnum = trypos->lnum; - - // It could have been something like - // case 1: if (asdf && - // ldfd) { - // } - if ((curbuf->b_ind_js || curbuf->b_ind_keep_case_label) - && cin_iscase(skipwhite(ml_get_curline()), FALSE)) - amount = get_indent(); - else if (curbuf->b_ind_js) - amount = get_indent_lnum(lnum); - else - amount = skip_label(lnum, &l); ! start_brace = BRACE_AT_END; } ! // For Javascript check if the line starts with "key:". ! if (curbuf->b_ind_js) ! js_cur_has_key = cin_has_js_key(theline); ! ! // If we're looking at a closing brace, that's where ! // we want to be. otherwise, add the amount of room ! // that an indent is supposed to be. ! if (theline[0] == '}') { ! // they may want closing braces to line up with something ! // other than the open brace. indulge them, if so. ! amount += curbuf->b_ind_close_extra; } ! else ! { ! // If we're looking at an "else", try to find an "if" ! // to match it with. ! // If we're looking at a "while", try to find a "do" ! // to match it with. ! lookfor = LOOKFOR_INITIAL; ! if (cin_iselse(theline)) ! lookfor = LOOKFOR_IF; ! else if (cin_iswhileofdo(theline, cur_curpos.lnum)) // XXX ! lookfor = LOOKFOR_DO; ! if (lookfor != LOOKFOR_INITIAL) ! { ! curwin->w_cursor.lnum = cur_curpos.lnum; ! if (find_match(lookfor, ourscope) == OK) ! { ! amount = get_indent(); // XXX ! goto theend; ! } ! } ! // We get here if we are not on an "while-of-do" or "else" (or ! // failed to find a matching "if"). ! // Search backwards for something to line up with. ! // First set amount for when we don't find anything. ! ! // if the '{' is _really_ at the left margin, use the imaginary ! // location of a left-margin brace. Otherwise, correct the ! // location for b_ind_open_extra. ! if (start_brace == BRACE_IN_COL0) // '{' is in column 0 ! { ! amount = curbuf->b_ind_open_left_imag; ! lookfor_cpp_namespace = TRUE; ! } ! else if (start_brace == BRACE_AT_START && ! lookfor_cpp_namespace) // '{' is at start ! { ! lookfor_cpp_namespace = TRUE; ! } ! else { ! if (start_brace == BRACE_AT_END) // '{' is at end of line { ! amount += curbuf->b_ind_open_imag; ! ! l = skipwhite(ml_get_curline()); ! if (cin_is_cpp_namespace(l)) ! amount += curbuf->b_ind_cpp_namespace; ! else if (cin_is_cpp_extern_c(l)) ! amount += curbuf->b_ind_cpp_extern_c; } else ! { ! // Compensate for adding b_ind_open_extra later. ! amount -= curbuf->b_ind_open_extra; ! if (amount < 0) ! amount = 0; ! } ! } ! ! lookfor_break = FALSE; ! ! if (cin_iscase(theline, FALSE)) // it's a switch() label ! { ! lookfor = LOOKFOR_CASE; // find a previous switch() label ! amount += curbuf->b_ind_case; ! } ! else if (cin_isscopedecl(theline)) // private:, ... ! { ! lookfor = LOOKFOR_SCOPEDECL; // class decl is this block ! amount += curbuf->b_ind_scopedecl; } else { ! if (curbuf->b_ind_case_break && cin_isbreak(theline)) ! // break; ... ! lookfor_break = TRUE; ! ! lookfor = LOOKFOR_INITIAL; ! // b_ind_level from start of block ! amount += curbuf->b_ind_level; ! } ! scope_amount = amount; ! whilelevel = 0; ! ! // Search backwards. If we find something we recognize, line up ! // with that. ! // ! // If we're looking at an open brace, indent ! // the usual amount relative to the conditional ! // that opens the block. ! curwin->w_cursor = cur_curpos; ! for (;;) ! { ! curwin->w_cursor.lnum--; ! curwin->w_cursor.col = 0; ! ! // If we went all the way back to the start of our scope, line ! // up with it. ! if (curwin->w_cursor.lnum <= ourscope) { ! // We reached end of scope: ! // If looking for a enum or structure initialization ! // go further back: ! // If it is an initializer (enum xxx or xxx =), then ! // don't add ind_continuation, otherwise it is a variable ! // declaration: ! // int x, ! // here; <-- add ind_continuation ! if (lookfor == LOOKFOR_ENUM_OR_INIT) ! { ! if (curwin->w_cursor.lnum == 0 ! || curwin->w_cursor.lnum ! < ourscope - curbuf->b_ind_maxparen) ! { ! // nothing found (abuse curbuf->b_ind_maxparen as ! // limit) assume terminated line (i.e. a variable ! // initialization) ! if (cont_amount > 0) ! amount = cont_amount; ! else if (!curbuf->b_ind_js) ! amount += ind_continuation; ! break; ! } ! ! l = ml_get_curline(); ! ! // If we're in a comment or raw string now, skip to ! // the start of it. ! trypos = ind_find_start_CORS(NULL); ! if (trypos != 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, ! &amount)) ! continue; ! ! if (cin_nocode(l)) ! continue; ! ! terminated = cin_isterminated(l, FALSE, TRUE); ! ! // If we are at top level and the line looks like a ! // function declaration, we are done ! // (it's a variable declaration). ! if (start_brace != BRACE_IN_COL0 ! || !cin_isfuncdecl(&l, curwin->w_cursor.lnum, 0)) ! { ! // if the line is terminated with another ',' ! // it is a continued variable initialization. ! // don't add extra indent. ! // TODO: does not work, if a function ! // declaration is split over multiple lines: ! // cin_isfuncdecl returns FALSE then. ! if (terminated == ',') ! break; ! // if it es a enum declaration or an assignment, ! // we are done. ! if (terminated != ';' && cin_isinit()) ! break; ! ! // nothing useful found ! if (terminated == 0 || terminated == '{') ! continue; ! } ! ! if (terminated != ';') ! { ! // Skip parens and braces. Position the cursor ! // over the rightmost paren, so that matching it ! // will take us back to the start of the line. ! // XXX ! trypos = NULL; ! if (find_last_paren(l, '(', ')')) ! trypos = find_match_paren( ! curbuf->b_ind_maxparen); ! ! if (trypos == NULL && find_last_paren(l, '{', '}')) ! trypos = find_start_brace(); ! ! if (trypos != NULL) ! { ! curwin->w_cursor.lnum = trypos->lnum + 1; ! curwin->w_cursor.col = 0; ! continue; ! } ! } ! ! // it's a variable declaration, add indentation ! // like in ! // int a, ! // b; ! if (cont_amount > 0) ! amount = cont_amount; ! else ! amount += ind_continuation; ! } ! else if (lookfor == LOOKFOR_UNTERM) ! { ! if (cont_amount > 0) ! amount = cont_amount; ! else ! amount += ind_continuation; ! } ! else ! { ! if (lookfor != LOOKFOR_TERM ! && lookfor != LOOKFOR_CPP_BASECLASS ! && lookfor != LOOKFOR_COMMA) ! { ! amount = scope_amount; ! if (theline[0] == '{') ! { ! amount += curbuf->b_ind_open_extra; ! added_to_amount = curbuf->b_ind_open_extra; ! } ! } ! ! if (lookfor_cpp_namespace) ! { ! // Looking for C++ namespace, need to look further ! // back. ! if (curwin->w_cursor.lnum == ourscope) ! continue; ! ! if (curwin->w_cursor.lnum == 0 ! || curwin->w_cursor.lnum ! < ourscope - FIND_NAMESPACE_LIM) ! break; ! ! l = ml_get_curline(); ! ! // If we're in a comment or raw string now, skip ! // to the start of it. ! trypos = ind_find_start_CORS(NULL); ! if (trypos != 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, ! &amount)) ! continue; ! ! // Finally the actual check for "namespace". ! if (cin_is_cpp_namespace(l)) ! { ! amount += curbuf->b_ind_cpp_namespace ! - added_to_amount; ! break; ! } ! else if (cin_is_cpp_extern_c(l)) ! { ! amount += curbuf->b_ind_cpp_extern_c ! - added_to_amount; ! break; ! } ! ! if (cin_nocode(l)) ! continue; ! } ! } ! break; ! } ! ! // If we're in a comment or raw string now, skip to the start ! // of it. XXX ! if ((trypos = ind_find_start_CORS(&raw_string_start)) != NULL) ! { ! curwin->w_cursor.lnum = trypos->lnum + 1; ! curwin->w_cursor.col = 0; ! continue; ! } ! ! l = ml_get_curline(); ! ! // If this is a switch() label, may line up relative to that. ! // If this is a C++ scope declaration, do the same. ! iscase = cin_iscase(l, FALSE); ! if (iscase || cin_isscopedecl(l)) ! { ! // we are only looking for cpp base class ! // declaration/initialization any longer ! if (lookfor == LOOKFOR_CPP_BASECLASS) ! break; ! ! // When looking for a "do" we are not interested in ! // labels. ! if (whilelevel > 0) ! continue; ! ! // case xx: ! // c = 99 + <- this indent plus continuation ! //-> here; ! if (lookfor == LOOKFOR_UNTERM ! || lookfor == LOOKFOR_ENUM_OR_INIT) ! { ! if (cont_amount > 0) ! amount = cont_amount; ! else ! amount += ind_continuation; ! break; ! } ! ! // case xx: <- line up with this case ! // x = 333; ! // case yy: ! if ( (iscase && lookfor == LOOKFOR_CASE) ! || (iscase && lookfor_break) ! || (!iscase && lookfor == LOOKFOR_SCOPEDECL)) ! { ! // Check that this case label is not for another ! // switch() XXX ! if ((trypos = find_start_brace()) == NULL ! || trypos->lnum == ourscope) ! { ! amount = get_indent(); // XXX ! break; ! } ! continue; ! } ! ! n = get_indent_nolabel(curwin->w_cursor.lnum); // XXX ! ! // case xx: if (cond) <- line up with this if ! // y = y + 1; ! // -> s = 99; ! // ! // case xx: ! // if (cond) <- line up with this line ! // y = y + 1; ! // -> s = 99; ! if (lookfor == LOOKFOR_TERM) ! { ! if (n) ! amount = n; ! ! if (!lookfor_break) ! break; ! } ! ! // case xx: x = x + 1; <- line up with this x ! // -> y = y + 1; ! // ! // case xx: if (cond) <- line up with this if ! // -> y = y + 1; ! if (n) ! { ! amount = n; ! l = after_label(ml_get_curline()); ! if (l != NULL && cin_is_cinword(l)) ! { ! if (theline[0] == '{') ! amount += curbuf->b_ind_open_extra; ! else ! amount += curbuf->b_ind_level ! + curbuf->b_ind_no_brace; ! } ! break; ! } ! ! // Try to get the indent of a statement before the switch ! // label. If nothing is found, line up relative to the ! // switch label. ! // break; <- may line up with this line ! // case xx: ! // -> y = 1; ! scope_amount = get_indent() + (iscase // XXX ! ? curbuf->b_ind_case_code ! : curbuf->b_ind_scopedecl_code); ! lookfor = curbuf->b_ind_case_break ! ? LOOKFOR_NOBREAK : LOOKFOR_ANY; ! continue; ! } ! ! // Looking for a switch() label or C++ scope declaration, ! // ignore other lines, skip {}-blocks. ! if (lookfor == LOOKFOR_CASE || lookfor == LOOKFOR_SCOPEDECL) ! { ! if (find_last_paren(l, '{', '}') ! && (trypos = find_start_brace()) != NULL) ! { ! curwin->w_cursor.lnum = trypos->lnum + 1; ! curwin->w_cursor.col = 0; ! } ! continue; ! } ! ! // Ignore jump labels with nothing after them. ! if (!curbuf->b_ind_js && cin_islabel()) ! { ! l = after_label(ml_get_curline()); ! if (l == NULL || cin_nocode(l)) ! continue; ! } ! ! // Ignore #defines, #if, etc. ! // Ignore comment and empty lines. ! // (need to get the line again, cin_islabel() may have ! // unlocked it) ! l = ml_get_curline(); ! if (cin_ispreproc_cont(&l, &curwin->w_cursor.lnum, &amount) ! || cin_nocode(l)) ! continue; ! ! // Are we at the start of a cpp base class declaration or ! // constructor initialization? XXX ! n = FALSE; ! if (lookfor != LOOKFOR_TERM && curbuf->b_ind_cpp_baseclass > 0) ! { ! n = cin_is_cpp_baseclass(&cache_cpp_baseclass); ! l = ml_get_curline(); ! } ! if (n) ! { ! if (lookfor == LOOKFOR_UNTERM) ! { ! if (cont_amount > 0) ! amount = cont_amount; ! else ! amount += ind_continuation; ! } ! else if (theline[0] == '{') ! { ! // Need to find start of the declaration. ! lookfor = LOOKFOR_UNTERM; ! ind_continuation = 0; ! continue; ! } ! else ! // XXX ! amount = get_baseclass_amount( ! cache_cpp_baseclass.lpos.col); ! break; ! } ! else if (lookfor == LOOKFOR_CPP_BASECLASS) ! { ! // only look, whether there is a cpp base class ! // declaration or initialization before the opening brace. ! if (cin_isterminated(l, TRUE, FALSE)) ! break; ! else ! continue; ! } ! ! // What happens next depends on the line being terminated. ! // If terminated with a ',' only consider it terminating if ! // there is another unterminated statement behind, eg: ! // 123, ! // sizeof ! // here ! // Otherwise check whether it is a enumeration or structure ! // initialisation (not indented) or a variable declaration ! // (indented). ! terminated = cin_isterminated(l, FALSE, TRUE); ! ! if (js_cur_has_key) ! { ! js_cur_has_key = 0; // only check the first line ! if (curbuf->b_ind_js && terminated == ',') ! { ! // For Javascript we might be inside an object: ! // key: something, <- align with this ! // key: something ! // or: ! // key: something + <- align with this ! // something, ! // key: something ! lookfor = LOOKFOR_JS_KEY; ! } ! } ! if (lookfor == LOOKFOR_JS_KEY && cin_has_js_key(l)) ! { ! amount = get_indent(); ! break; ! } ! if (lookfor == LOOKFOR_COMMA) ! { ! if (tryposBrace != NULL && tryposBrace->lnum ! >= curwin->w_cursor.lnum) ! break; ! if (terminated == ',') ! // line below current line is the one that starts a ! // (possibly broken) line ending in a comma. ! break; ! else ! { ! amount = get_indent(); ! if (curwin->w_cursor.lnum - 1 == ourscope) ! // line above is start of the scope, thus current ! // line is the one that stars a (possibly broken) ! // line ending in a comma. ! break; ! } ! } ! ! if (terminated == 0 || (lookfor != LOOKFOR_UNTERM ! && terminated == ',')) ! { ! if (lookfor != LOOKFOR_ENUM_OR_INIT && ! (*skipwhite(l) == '[' || l[STRLEN(l) - 1] == '[')) ! amount += ind_continuation; ! // if we're in the middle of a paren thing, ! // go back to the line that starts it so ! // we can get the right prevailing indent ! // if ( foo && ! // bar ) ! ! // Position the cursor over the rightmost paren, so that ! // matching it will take us back to the start of the line. ! // Ignore a match before the start of the block. ! (void)find_last_paren(l, '(', ')'); ! trypos = find_match_paren(corr_ind_maxparen(&cur_curpos)); ! if (trypos != NULL && (trypos->lnum < tryposBrace->lnum ! || (trypos->lnum == tryposBrace->lnum ! && trypos->col < tryposBrace->col))) ! trypos = NULL; ! ! // If we are looking for ',', we also look for matching ! // braces. ! if (trypos == NULL && terminated == ',' ! && find_last_paren(l, '{', '}')) ! trypos = find_start_brace(); ! ! if (trypos != NULL) ! { ! // Check if we are on a case label now. This is ! // handled above. ! // case xx: if ( asdf && ! // asdf) ! curwin->w_cursor = *trypos; ! l = ml_get_curline(); ! if (cin_iscase(l, FALSE) || cin_isscopedecl(l)) ! { ! ++curwin->w_cursor.lnum; ! curwin->w_cursor.col = 0; ! continue; ! } ! } ! ! /* ! * Skip over continuation lines to find the one to get the ! * indent from ! * char *usethis = "bla\ ! * bla", ! * here; ! */ ! if (terminated == ',') ! { ! while (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; ! } ! } ! ! // Get indent and pointer to text for current line, ! // ignoring any jump label. XXX ! if (curbuf->b_ind_js) ! cur_amount = get_indent(); ! else ! cur_amount = skip_label(curwin->w_cursor.lnum, &l); ! // If this is just above the line we are indenting, and it ! // starts with a '{', line it up with this line. ! // while (not) ! // -> { ! // } ! if (terminated != ',' && lookfor != LOOKFOR_TERM ! && theline[0] == '{') ! { ! amount = cur_amount; ! // Only add b_ind_open_extra when the current line ! // doesn't start with a '{', which must have a match ! // in the same line (scope is the same). Probably: ! // { 1, 2 }, ! // -> { 3, 4 } ! if (*skipwhite(l) != '{') ! amount += curbuf->b_ind_open_extra; ! ! if (curbuf->b_ind_cpp_baseclass && !curbuf->b_ind_js) ! { ! // have to look back, whether it is a cpp base ! // class declaration or initialization ! lookfor = LOOKFOR_CPP_BASECLASS; ! continue; ! } ! break; ! } ! ! // Check if we are after an "if", "while", etc. ! // Also allow " } else". ! if (cin_is_cinword(l) || cin_iselse(skipwhite(l))) ! { ! // Found an unterminated line after an if (), line up ! // with the last one. ! // if (cond) ! // 100 + ! // -> here; ! if (lookfor == LOOKFOR_UNTERM ! || lookfor == LOOKFOR_ENUM_OR_INIT) ! { ! if (cont_amount > 0) ! amount = cont_amount; ! else ! amount += ind_continuation; ! break; ! } ! ! // If this is just above the line we are indenting, we ! // are finished. ! // while (not) ! // -> here; ! // Otherwise this indent can be used when the line ! // before this is terminated. ! // yyy; ! // if (stat) ! // while (not) ! // xxx; ! // -> here; ! amount = cur_amount; ! if (theline[0] == '{') ! amount += curbuf->b_ind_open_extra; ! if (lookfor != LOOKFOR_TERM) ! { ! amount += curbuf->b_ind_level ! + curbuf->b_ind_no_brace; ! break; ! } ! ! // Special trick: when expecting the while () after a ! // do, line up with the while() ! // do ! // x = 1; ! // -> here ! l = skipwhite(ml_get_curline()); ! if (cin_isdo(l)) ! { ! if (whilelevel == 0) ! break; ! --whilelevel; ! } ! ! // When searching for a terminated line, don't use the ! // one between the "if" and the matching "else". ! // Need to use the scope of this "else". XXX ! // If whilelevel != 0 continue looking for a "do {". ! if (cin_iselse(l) && whilelevel == 0) { ! // If we're looking at "} else", let's make sure we ! // find the opening brace of the enclosing scope, ! // not the one from "if () {". ! if (*l == '}') ! curwin->w_cursor.col = ! (colnr_T)(l - ml_get_curline()) + 1; ! ! if ((trypos = find_start_brace()) == NULL ! || find_match(LOOKFOR_IF, trypos->lnum) ! == FAIL) ! break; } } ! ! // If we're below an unterminated line that is not an ! // "if" or something, we may line up with this line or ! // add something for a continuation line, depending on ! // the line before this one. ! else { ! // Found two unterminated lines on a row, line up with ! // the last one. ! // c = 99 + ! // 100 + ! // -> here; ! if (lookfor == LOOKFOR_UNTERM) { ! // When line ends in a comma add extra indent ! if (terminated == ',') ! amount += ind_continuation; ! break; ! } ! ! if (lookfor == LOOKFOR_ENUM_OR_INIT) ! { ! // Found two lines ending in ',', lineup with the ! // lowest one, but check for cpp base class ! // declaration/initialization, if it is an ! // opening brace or we are looking just for ! // enumerations/initializations. ! if (terminated == ',') { ! if (curbuf->b_ind_cpp_baseclass == 0) ! break; ! ! lookfor = LOOKFOR_CPP_BASECLASS; ! continue; ! } ! ! // Ignore unterminated lines in between, but ! // reduce indent. ! if (amount > cur_amount) ! amount = cur_amount; ! } ! else ! { ! // Found first unterminated line on a row, may ! // line up with this line, remember its indent ! // 100 + ! // -> here; ! l = ml_get_curline(); ! amount = cur_amount; ! ! n = (int)STRLEN(l); ! if (terminated == ',' && (*skipwhite(l) == ']' ! || (n >=2 && l[n - 2] == ']'))) break; - - // If previous line ends in ',', check whether we - // are in an initialization or enum - // struct xxx = - // { - // sizeof a, - // 124 }; - // or a normal possible continuation line. - // but only, of no other statement has been found - // yet. - if (lookfor == LOOKFOR_INITIAL && terminated == ',') - { - if (curbuf->b_ind_js) - { - // Search for a line ending in a comma - // and line up with the line below it - // (could be the current line). - // some = [ - // 1, <- line up here - // 2, - // some = [ - // 3 + <- line up here - // 4 * - // 5, - // 6, - if (cin_iscomment(skipwhite(l))) - break; - lookfor = LOOKFOR_COMMA; - trypos = find_match_char('[', - curbuf->b_ind_maxparen); - if (trypos != NULL) - { - if (trypos->lnum - == curwin->w_cursor.lnum - 1) - { - // Current line is first inside - // [], line up with it. - break; - } - ourscope = trypos->lnum; - } - } - else - { - lookfor = LOOKFOR_ENUM_OR_INIT; - cont_amount = cin_first_id_amount(); - } - } - else - { - if (lookfor == LOOKFOR_INITIAL - && *l != NUL - && l[STRLEN(l) - 1] == '\\') - // XXX - cont_amount = cin_get_equal_amount( - curwin->w_cursor.lnum); - if (lookfor != LOOKFOR_TERM - && lookfor != LOOKFOR_JS_KEY - && lookfor != LOOKFOR_COMMA - && raw_string_start != curwin->w_cursor.lnum) - lookfor = LOOKFOR_UNTERM; } } - } - } - - // Check if we are after a while (cond); - // If so: Ignore until the matching "do". - else if (cin_iswhileofdo_end(terminated)) // XXX - { - // Found an unterminated line after a while ();, line up - // with the last one. - // while (cond); - // 100 + <- line up with this one - // -> here; - if (lookfor == LOOKFOR_UNTERM - || lookfor == LOOKFOR_ENUM_OR_INIT) - { - if (cont_amount > 0) - amount = cont_amount; - else - amount += ind_continuation; - break; - } - - if (whilelevel == 0) - { - lookfor = LOOKFOR_TERM; - amount = get_indent(); // XXX - if (theline[0] == '{') - amount += curbuf->b_ind_open_extra; - } - ++whilelevel; - } - - // We are after a "normal" statement. - // If we had another statement we can stop now and use the - // indent of that other statement. - // Otherwise the indent of the current statement may be used, - // search backwards for the next "normal" statement. - else - { - // Skip single break line, if before a switch label. It - // may be lined up with the case label. - if (lookfor == LOOKFOR_NOBREAK - && cin_isbreak(skipwhite(ml_get_curline()))) - { - lookfor = LOOKFOR_ANY; - continue; - } - - // Handle "do {" line. - if (whilelevel > 0) - { - l = cin_skipcomment(ml_get_curline()); - if (cin_isdo(l)) - { - amount = get_indent(); // XXX - --whilelevel; - continue; - } - } ! // Found a terminated line above an unterminated line. Add ! // the amount for a continuation line. ! // x = 1; ! // y = foo + ! // -> here; ! // or ! // int x = 1; ! // int foo, ! // -> here; ! if (lookfor == LOOKFOR_UNTERM ! || lookfor == LOOKFOR_ENUM_OR_INIT) ! { ! if (cont_amount > 0) ! amount = cont_amount; ! else ! amount += ind_continuation; ! break; ! } ! ! // Found a terminated line above a terminated line or "if" ! // etc. line. Use the amount of the line below us. ! // x = 1; x = 1; ! // if (asdf) y = 2; ! // while (asdf) ->here; ! // here; ! // ->foo; ! if (lookfor == LOOKFOR_TERM) ! { ! if (!lookfor_break && whilelevel == 0) break; ! } ! ! // First line above the one we're indenting is terminated. ! // To know what needs to be done look further backward for ! // a terminated line. ! else ! { ! // position the cursor over the rightmost paren, so ! // that matching it will take us back to the start of ! // the line. Helps for: ! // func(asdr, ! // asdfasdf); ! // here; ! term_again: ! l = ml_get_curline(); ! if (find_last_paren(l, '(', ')') ! && (trypos = find_match_paren( ! curbuf->b_ind_maxparen)) != NULL) ! { ! // Check if we are on a case label now. This is ! // handled above. ! // case xx: if ( asdf && ! // asdf) ! curwin->w_cursor = *trypos; ! l = ml_get_curline(); ! if (cin_iscase(l, FALSE) || cin_isscopedecl(l)) ! { ! ++curwin->w_cursor.lnum; ! curwin->w_cursor.col = 0; ! continue; ! } ! } ! ! // When aligning with the case statement, don't align ! // with a statement after it. ! // case 1: { <-- don't use this { position ! // stat; ! // } ! // case 2: ! // stat; ! // } ! iscase = (curbuf->b_ind_keep_case_label ! && cin_iscase(l, FALSE)); ! ! // Get indent and pointer to text for current line, ! // ignoring any jump label. ! amount = skip_label(curwin->w_cursor.lnum, &l); ! ! if (theline[0] == '{') ! amount += curbuf->b_ind_open_extra; ! // See remark above: "Only add b_ind_open_extra.." ! l = skipwhite(l); ! if (*l == '{') ! amount -= curbuf->b_ind_open_extra; ! lookfor = iscase ? LOOKFOR_ANY : LOOKFOR_TERM; ! ! // When a terminated line starts with "else" skip to ! // the matching "if": ! // else 3; ! // indent this; ! // Need to use the scope of this "else". XXX ! // If whilelevel != 0 continue looking for a "do {". ! if (lookfor == LOOKFOR_TERM ! && *l != '}' ! && cin_iselse(l) ! && whilelevel == 0) ! { ! if ((trypos = find_start_brace()) == NULL ! || find_match(LOOKFOR_IF, trypos->lnum) ! == FAIL) ! break; ! continue; ! } ! ! // If we're at the end of a block, skip to the start of ! // that block. ! l = ml_get_curline(); ! if (find_last_paren(l, '{', '}') // XXX ! && (trypos = find_start_brace()) != NULL) ! { ! curwin->w_cursor = *trypos; ! // if not "else {" check for terminated again ! // but skip block for "} else {" ! l = cin_skipcomment(ml_get_curline()); ! if (*l == '}' || !cin_iselse(l)) ! goto term_again; ! ++curwin->w_cursor.lnum; ! curwin->w_cursor.col = 0; ! } } } } ! } ! } ! ! // add extra indent for a comment ! if (cin_iscomment(theline)) ! amount += curbuf->b_ind_comment; ! ! // subtract extra left-shift for jump labels ! if (curbuf->b_ind_jump_label > 0 && original_line_islabel) ! amount -= curbuf->b_ind_jump_label; ! ! goto theend; ! } ! ! // 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; ! } ! ! // 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; ! ! l = ml_get_curline(); ! ! // If we're in a comment or raw string now, skip to the start ! // of it. XXX ! if ((trypos = ind_find_start_CORS(NULL)) != NULL) ! { ! curwin->w_cursor.lnum = trypos->lnum + 1; ! curwin->w_cursor.col = 0; ! 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 = ml_get_curline(); ! } ! if (n) ! { ! // XXX ! amount = get_baseclass_amount(cache_cpp_baseclass.lpos.col); ! break; ! } ! ! // Skip preprocessor directives and blank lines. ! if (cin_ispreproc_cont(&l, &curwin->w_cursor.lnum, &amount)) ! continue; ! ! if (cin_nocode(l)) ! 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; ! } ! ! amount = get_indent(); // XXX ! ! if (amount == 0) ! amount = cin_first_id_amount(); ! if (amount == 0) ! amount = ind_continuation; ! 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 = ml_get_curline(); ! ! // Finding the closing '}' of a previous function. Put ! // current line at the left margin. For when 'cino' has "fs". ! if (*skipwhite(l) == '}') ! 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 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; ! } ! ! // 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; ! ! 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, &amount))) ! break; ! } ! if (curwin->w_cursor.lnum > 0 ! && cin_ends_in(look, (char_u *)"}", NULL)) break; ! ! curwin->w_cursor = curpos_save; } ! ! // 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 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 = ml_get_curline(); ! } ! ! // 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 ((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; ! ! vim_free(linecopy); ! ! return amount; ! } ! ! static int ! find_match(int lookfor, linenr_T ourscope) ! { ! char_u *look; ! pos_T *theirscope; ! char_u *mightbeif; ! int elselevel; ! int whilelevel; ! ! if (lookfor == LOOKFOR_IF) ! { ! elselevel = 1; ! whilelevel = 0; ! } else ! { ! elselevel = 0; ! whilelevel = 1; ! } ! curwin->w_cursor.col = 0; ! while (curwin->w_cursor.lnum > ourscope + 1) { ! curwin->w_cursor.lnum--; ! curwin->w_cursor.col = 0; ! look = cin_skipcomment(ml_get_curline()); ! if (cin_iselse(look) ! || cin_isif(look) ! || cin_isdo(look) // XXX ! || cin_iswhileofdo(look, curwin->w_cursor.lnum)) { ! // if we've gone outside the braces entirely, ! // we must be out of scope... ! theirscope = find_start_brace(); // XXX ! if (theirscope == NULL) ! break; ! ! // and if the brace enclosing this is further ! // back than the one enclosing the else, we're ! // out of luck too. ! if (theirscope->lnum < ourscope) ! break; ! ! // and if they're enclosed in a *deeper* brace, ! // then we can ignore it because it's in a ! // different scope... ! if (theirscope->lnum > ourscope) ! continue; ! ! // if it was an "else" (that's not an "else if") ! // then we need to go back to another if, so ! // increment elselevel ! look = cin_skipcomment(ml_get_curline()); ! if (cin_iselse(look)) ! { ! mightbeif = cin_skipcomment(look + 4); ! if (!cin_isif(mightbeif)) ! ++elselevel; ! continue; ! } ! ! // if it was a "while" then we need to go back to ! // another "do", so increment whilelevel. XXX ! if (cin_iswhileofdo(look, curwin->w_cursor.lnum)) ! { ! ++whilelevel; ! continue; ! } ! ! // If it's an "if" decrement elselevel ! look = cin_skipcomment(ml_get_curline()); ! if (cin_isif(look)) ! { ! elselevel--; ! // When looking for an "if" ignore "while"s that ! // get in the way. ! if (elselevel == 0 && lookfor == LOOKFOR_IF) ! whilelevel = 0; ! } ! ! // If it's a "do" decrement whilelevel ! if (cin_isdo(look)) ! whilelevel--; ! ! // if we've used up all the elses, then ! // this must be the if that we want! ! // match the indent level of that if. ! if (elselevel <= 0 && whilelevel <= 0) ! return OK; } } ! return FAIL; } ! # if defined(FEAT_EVAL) || defined(PROTO) /* * Get indent level from 'indentexpr'. */ --- 13,1695 ---- #include "vim.h" ! #if defined(FEAT_VARTABS) || defined(PROTO) /* ! * Set the integer values corresponding to the string setting of 'vartabstop'. ! * "array" will be set, caller must free it if needed. */ int ! tabstop_set(char_u *var, int **array) { ! int valcount = 1; ! int t; ! char_u *cp; ! if (var[0] == NUL || (var[0] == '0' && var[1] == NUL)) { ! *array = NULL; ! return TRUE; } ! for (cp = var; *cp != NUL; ++cp) { ! if (cp == var || cp[-1] == ',') { ! char_u *end; ! if (strtol((char *)cp, (char **)&end, 10) <= 0) { ! if (cp != end) ! emsg(_(e_positive)); ! else ! emsg(_(e_invarg)); ! return FALSE; } } ! ! if (VIM_ISDIGIT(*cp)) ! continue; ! if (cp[0] == ',' && cp > var && cp[-1] != ',' && cp[1] != NUL) { ! ++valcount; ! continue; } ! emsg(_(e_invarg)); ! return FALSE; ! } ! *array = ALLOC_MULT(int, valcount + 1); ! if (*array == NULL) ! return FALSE; ! (*array)[0] = valcount; ! t = 1; ! for (cp = var; *cp != NUL;) ! { ! (*array)[t++] = atoi((char *)cp); ! while (*cp != NUL && *cp != ',') ! ++cp; ! if (*cp != NUL) ! ++cp; } ! return TRUE; ! } /* ! * Calculate the number of screen spaces a tab will occupy. ! * If "vts" is set then the tab widths are taken from that array, ! * otherwise the value of ts is used. */ int ! tabstop_padding(colnr_T col, int ts_arg, int *vts) { ! int ts = ts_arg == 0 ? 8 : ts_arg; ! int tabcount; ! colnr_T tabcol = 0; ! int t; ! int padding = 0; ! if (vts == NULL || vts[0] == 0) ! return ts - (col % ts); ! tabcount = vts[0]; ! for (t = 1; t <= tabcount; ++t) { ! tabcol += vts[t]; ! if (tabcol > col) { ! padding = (int)(tabcol - col); break; } } ! if (t > tabcount) ! padding = vts[tabcount] - (int)((col - tabcol) % vts[tabcount]); ! return padding; } /* ! * Find the size of the tab that covers a particular column. */ ! int ! tabstop_at(colnr_T col, int ts, int *vts) { ! int tabcount; ! colnr_T tabcol = 0; ! int t; ! int tab_size = 0; ! if (vts == 0 || vts[0] == 0) ! return ts; ! ! tabcount = vts[0]; ! for (t = 1; t <= tabcount; ++t) { ! tabcol += vts[t]; ! if (tabcol > col) { ! tab_size = vts[t]; break; + } } ! if (t > tabcount) ! tab_size = vts[tabcount]; ! ! return tab_size; } /* ! * Find the column on which a tab starts. */ ! colnr_T ! tabstop_start(colnr_T col, int ts, int *vts) { ! int tabcount; ! colnr_T tabcol = 0; ! int t; ! int excess; ! ! if (vts == NULL || vts[0] == 0) ! return (col / ts) * ts; ! tabcount = vts[0]; ! for (t = 1; t <= tabcount; ++t) { ! tabcol += vts[t]; ! if (tabcol > col) ! return tabcol - vts[t]; } ! excess = tabcol % vts[tabcount]; ! return excess + ((col - excess) / vts[tabcount]) * vts[tabcount]; } /* ! * Find the number of tabs and spaces necessary to get from one column ! * to another. */ ! void ! tabstop_fromto( ! colnr_T start_col, ! colnr_T end_col, ! int ts_arg, ! int *vts, ! int *ntabs, ! int *nspcs) { ! int spaces = end_col - start_col; ! colnr_T tabcol = 0; ! int padding = 0; ! int tabcount; ! int t; ! int ts = ts_arg == 0 ? curbuf->b_p_ts : ts_arg; ! if (vts == NULL || vts[0] == 0) ! { ! int tabs = 0; ! int initspc = 0; ! initspc = ts - (start_col % ts); ! if (spaces >= initspc) ! { ! spaces -= initspc; ! tabs++; ! } ! tabs += spaces / ts; ! spaces -= (spaces / ts) * ts; ! *ntabs = tabs; ! *nspcs = spaces; ! return; ! } ! // Find the padding needed to reach the next tabstop. ! tabcount = vts[0]; ! for (t = 1; t <= tabcount; ++t) { ! tabcol += vts[t]; ! if (tabcol > start_col) { ! padding = (int)(tabcol - start_col); ! break; ! } ! } ! if (t > tabcount) ! padding = vts[tabcount] - (int)((start_col - tabcol) % vts[tabcount]); ! // If the space needed is less than the padding no tabs can be used. ! if (spaces < padding) ! { ! *ntabs = 0; ! *nspcs = spaces; ! return; ! } ! *ntabs = 1; ! spaces -= padding; ! // At least one tab has been used. See if any more will fit. ! while (spaces != 0 && ++t <= tabcount) ! { ! padding = vts[t]; ! if (spaces < padding) ! { ! *nspcs = spaces; ! return; } ! ++*ntabs; ! spaces -= padding; } ! ! *ntabs += spaces / vts[tabcount]; ! *nspcs = spaces % vts[tabcount]; } /* ! * See if two tabstop arrays contain the same values. */ static int ! tabstop_eq(int *ts1, int *ts2) { ! int t; ! if ((ts1 == 0 && ts2) || (ts1 && ts2 == 0)) ! return FALSE; ! if (ts1 == ts2) return TRUE; + if (ts1[0] != ts2[0]) + return FALSE; ! for (t = 1; t <= ts1[0]; ++t) ! if (ts1[t] != ts2[t]) ! return FALSE; ! return TRUE; } + # if defined(FEAT_BEVAL) || defined(PROTO) /* ! * Copy a tabstop array, allocating space for the new array. */ ! int * ! tabstop_copy(int *oldts) { ! int *newts; ! int t; ! if (oldts == NULL) ! return NULL; ! newts = ALLOC_MULT(int, oldts[0] + 1); ! if (newts != NULL) ! for (t = 0; t <= oldts[0]; ++t) ! newts[t] = oldts[t]; ! return newts; } + # endif /* ! * Return a count of the number of tabstops. */ ! int ! tabstop_count(int *ts) { ! return ts != NULL ? ts[0] : 0; } /* ! * Return the first tabstop, or 8 if there are no tabstops defined. */ ! int ! tabstop_first(int *ts) { ! return ts != NULL ? ts[1] : 8; } ! #endif /* ! * Return the effective shiftwidth value for current buffer, using the ! * 'tabstop' value when 'shiftwidth' is zero. */ ! long ! get_sw_value(buf_T *buf) { ! return get_sw_value_col(buf, 0); } /* ! * Idem, using "pos". */ ! static long ! get_sw_value_pos(buf_T *buf, pos_T *pos) { ! pos_T save_cursor = curwin->w_cursor; ! long sw_value; ! curwin->w_cursor = *pos; ! sw_value = get_sw_value_col(buf, get_nolist_virtcol()); ! curwin->w_cursor = save_cursor; ! return sw_value; } /* ! * Idem, using the first non-black in the current line. */ ! long ! get_sw_value_indent(buf_T *buf) { ! pos_T pos = curwin->w_cursor; ! ! pos.col = getwhitecols_curline(); ! return get_sw_value_pos(buf, &pos); } /* ! * Idem, using virtual column "col". */ ! long ! get_sw_value_col(buf_T *buf, colnr_T col UNUSED) { ! return buf->b_p_sw ? buf->b_p_sw : ! #ifdef FEAT_VARTABS ! tabstop_at(col, buf->b_p_ts, buf->b_p_vts_array); ! #else ! buf->b_p_ts; ! #endif } /* ! * Return the effective softtabstop value for the current buffer, using the ! * 'shiftwidth' value when 'softtabstop' is negative. */ ! long ! get_sts_value(void) { ! return curbuf->b_p_sts < 0 ? get_sw_value(curbuf) : curbuf->b_p_sts; } /* ! * Count the size (in window cells) of the indent in the current line. */ ! int ! get_indent(void) { ! #ifdef FEAT_VARTABS ! return get_indent_str_vtab(ml_get_curline(), (int)curbuf->b_p_ts, ! curbuf->b_p_vts_array, FALSE); ! #else ! return get_indent_str(ml_get_curline(), (int)curbuf->b_p_ts, FALSE); ! #endif } /* ! * Count the size (in window cells) of the indent in line "lnum". */ ! int ! get_indent_lnum(linenr_T lnum) { ! #ifdef FEAT_VARTABS ! return get_indent_str_vtab(ml_get(lnum), (int)curbuf->b_p_ts, ! curbuf->b_p_vts_array, FALSE); ! #else ! return get_indent_str(ml_get(lnum), (int)curbuf->b_p_ts, FALSE); ! #endif } + #if defined(FEAT_FOLDING) || defined(PROTO) /* ! * Count the size (in window cells) of the indent in line "lnum" of buffer ! * "buf". */ ! int ! get_indent_buf(buf_T *buf, linenr_T lnum) { ! # ifdef FEAT_VARTABS ! return get_indent_str_vtab(ml_get_buf(buf, lnum, FALSE), ! (int)curbuf->b_p_ts, buf->b_p_vts_array, FALSE); ! # else ! return get_indent_str(ml_get_buf(buf, lnum, FALSE), (int)buf->b_p_ts, FALSE); ! # endif } + #endif /* ! * count the size (in window cells) of the indent in line "ptr", with ! * 'tabstop' at "ts" */ ! int ! get_indent_str( ! char_u *ptr, ! int ts, ! int list) // if TRUE, count only screen size for tabs { ! int count = 0; ! for ( ; *ptr; ++ptr) { ! if (*ptr == TAB) { ! if (!list || lcs_tab1) // count a tab for what it is worth ! count += ts - (count % ts); ! else ! // In list mode, when tab is not set, count screen char width ! // for Tab, displays: ^I ! count += ptr2cells(ptr); } ! else if (*ptr == ' ') ! ++count; // count a space for one ! else break; } ! return count; } + #ifdef FEAT_VARTABS /* ! * Count the size (in window cells) of the indent in line "ptr", using ! * variable tabstops. ! * if "list" is TRUE, count only screen size for tabs. */ ! int ! get_indent_str_vtab(char_u *ptr, int ts, int *vts, int list) { ! int count = 0; ! for ( ; *ptr; ++ptr) ! { ! if (*ptr == TAB) // count a tab for what it is worth { ! if (!list || lcs_tab1) ! count += tabstop_padding(count, ts, vts); else ! // In list mode, when tab is not set, count screen char width ! // for Tab, displays: ^I ! count += ptr2cells(ptr); } ! else if (*ptr == ' ') ! ++count; // count a space for one ! else ! break; } ! return count; } + #endif /* ! * Set the indent of the current line. ! * Leaves the cursor on the first non-blank in the line. ! * Caller must take care of undo. ! * "flags": ! * SIN_CHANGED: call changed_bytes() if the line was changed. ! * SIN_INSERT: insert the indent in front of the line. ! * SIN_UNDO: save line for undo before changing it. ! * Returns TRUE if the line was changed. */ ! int ! set_indent( ! int size, // measured in spaces ! int flags) { + char_u *p; + char_u *newline; + char_u *oldline; char_u *s; ! int todo; ! int ind_len; // measured in characters ! int line_len; ! int doit = FALSE; ! int ind_done = 0; // measured in spaces ! #ifdef FEAT_VARTABS ! int ind_col = 0; ! #endif ! int tab_pad; int retval = FALSE; ! int orig_char_len = -1; // number of initial whitespace chars when ! // 'et' and 'pi' are both set ! // First check if there is anything to do and compute the number of ! // characters needed for the indent. ! todo = size; ! ind_len = 0; ! p = oldline = ml_get_curline(); ! // Calculate the buffer size for the new indent, and check to see if it ! // isn't already set ! // if 'expandtab' isn't set: use TABs; if both 'expandtab' and ! // 'preserveindent' are set count the number of characters at the ! // beginning of the line to be copied ! if (!curbuf->b_p_et || (!(flags & SIN_INSERT) && curbuf->b_p_pi)) { ! // If 'preserveindent' is set then reuse as much as possible of ! // the existing indent structure for the new indent ! if (!(flags & SIN_INSERT) && curbuf->b_p_pi) { ! ind_done = 0; ! // count as many characters as we can use ! while (todo > 0 && VIM_ISWHITE(*p)) ! { ! if (*p == TAB) ! { ! #ifdef FEAT_VARTABS ! tab_pad = tabstop_padding(ind_done, curbuf->b_p_ts, ! curbuf->b_p_vts_array); ! #else ! tab_pad = (int)curbuf->b_p_ts ! - (ind_done % (int)curbuf->b_p_ts); ! #endif ! // stop if this tab will overshoot the target ! if (todo < tab_pad) ! break; ! todo -= tab_pad; ! ++ind_len; ! ind_done += tab_pad; ! } ! else ! { ! --todo; ! ++ind_len; ! ++ind_done; ! } ! ++p; ! } ! #ifdef FEAT_VARTABS ! // These diverge from this point. ! ind_col = ind_done; ! #endif ! // Set initial number of whitespace chars to copy if we are ! // preserving indent but expandtab is set ! if (curbuf->b_p_et) ! orig_char_len = ind_len; ! ! // Fill to next tabstop with a tab, if possible ! #ifdef FEAT_VARTABS ! tab_pad = tabstop_padding(ind_done, curbuf->b_p_ts, ! curbuf->b_p_vts_array); ! #else ! tab_pad = (int)curbuf->b_p_ts - (ind_done % (int)curbuf->b_p_ts); ! #endif ! if (todo >= tab_pad && orig_char_len == -1) { ! doit = TRUE; ! todo -= tab_pad; ! ++ind_len; ! // ind_done += tab_pad; ! #ifdef FEAT_VARTABS ! ind_col += tab_pad; ! #endif } ! } ! ! // count tabs required for indent ! #ifdef FEAT_VARTABS ! for (;;) ! { ! tab_pad = tabstop_padding(ind_col, curbuf->b_p_ts, ! curbuf->b_p_vts_array); ! if (todo < tab_pad) break; ! if (*p != TAB) ! doit = TRUE; ! else ! ++p; ! todo -= tab_pad; ! ++ind_len; ! ind_col += tab_pad; } ! #else ! while (todo >= (int)curbuf->b_p_ts) { ! if (*p != TAB) ! doit = TRUE; ! else ! ++p; ! todo -= (int)curbuf->b_p_ts; ! ++ind_len; ! // ind_done += (int)curbuf->b_p_ts; } + #endif } ! // count spaces required for indent ! while (todo > 0) { ! if (*p != ' ') ! doit = TRUE; ! else ++p; ! --todo; ! ++ind_len; ! // ++ind_done; } ! // Return if the indent is OK already. ! if (!doit && !VIM_ISWHITE(*p) && !(flags & SIN_INSERT)) ! return FALSE; ! // Allocate memory for the new line. ! if (flags & SIN_INSERT) ! p = oldline; ! else ! p = skipwhite(p); ! line_len = (int)STRLEN(p) + 1; ! // If 'preserveindent' and 'expandtab' are both set keep the original ! // characters and allocate accordingly. We will fill the rest with spaces ! // after the if (!curbuf->b_p_et) below. ! if (orig_char_len != -1) { ! newline = alloc(orig_char_len + size - ind_done + line_len); ! if (newline == NULL) ! return FALSE; ! todo = size - ind_done; ! ind_len = orig_char_len + todo; // Set total length of indent in ! // characters, which may have been ! // undercounted until now ! p = oldline; ! s = newline; ! while (orig_char_len > 0) ! { ! *s++ = *p++; ! orig_char_len--; } ! // Skip over any additional white space (useful when newindent is less ! // than old) ! while (VIM_ISWHITE(*p)) ! ++p; ! ! } ! else { ! todo = size; ! newline = alloc(ind_len + line_len); ! if (newline == NULL) ! return FALSE; ! s = newline; } ! // Put the characters in the new line. ! // if 'expandtab' isn't set: use TABs ! if (!curbuf->b_p_et) { ! // If 'preserveindent' is set then reuse as much as possible of ! // the existing indent structure for the new indent ! if (!(flags & SIN_INSERT) && curbuf->b_p_pi) { ! p = oldline; ! ind_done = 0; ! ! while (todo > 0 && VIM_ISWHITE(*p)) { ! if (*p == TAB) { ! #ifdef FEAT_VARTABS ! tab_pad = tabstop_padding(ind_done, curbuf->b_p_ts, ! curbuf->b_p_vts_array); ! #else ! tab_pad = (int)curbuf->b_p_ts ! - (ind_done % (int)curbuf->b_p_ts); ! #endif ! // stop if this tab will overshoot the target ! if (todo < tab_pad) ! break; ! todo -= tab_pad; ! ind_done += tab_pad; } ! else ! { ! --todo; ! ++ind_done; ! } ! *s++ = *p++; } ! // Fill to next tabstop with a tab, if possible ! #ifdef FEAT_VARTABS ! tab_pad = tabstop_padding(ind_done, curbuf->b_p_ts, ! curbuf->b_p_vts_array); ! #else ! tab_pad = (int)curbuf->b_p_ts - (ind_done % (int)curbuf->b_p_ts); ! #endif ! if (todo >= tab_pad) ! { ! *s++ = TAB; ! todo -= tab_pad; ! #ifdef FEAT_VARTABS ! ind_done += tab_pad; ! #endif ! } ! p = skipwhite(p); } ! #ifdef FEAT_VARTABS ! for (;;) { ! tab_pad = tabstop_padding(ind_done, curbuf->b_p_ts, ! curbuf->b_p_vts_array); ! if (todo < tab_pad) break; ! *s++ = TAB; ! todo -= tab_pad; ! ind_done += tab_pad; } ! #else ! while (todo >= (int)curbuf->b_p_ts) { ! *s++ = TAB; ! todo -= (int)curbuf->b_p_ts; } + #endif + } + while (todo > 0) + { + *s++ = ' '; + --todo; + } + mch_memmove(s, p, (size_t)line_len); ! // Replace the line (unless undo fails). ! if (!(flags & SIN_UNDO) || u_savesub(curwin->w_cursor.lnum) == OK) ! { ! ml_replace(curwin->w_cursor.lnum, newline, FALSE); ! if (flags & SIN_CHANGED) ! changed_bytes(curwin->w_cursor.lnum, 0); ! // Correct saved cursor position if it is in this line. ! if (saved_cursor.lnum == curwin->w_cursor.lnum) ! { ! if (saved_cursor.col >= (colnr_T)(p - oldline)) ! // cursor was after the indent, adjust for the number of ! // bytes added/removed ! saved_cursor.col += ind_len - (colnr_T)(p - oldline); ! else if (saved_cursor.col >= (colnr_T)(s - newline)) ! // cursor was in the indent, and is now after it, put it back ! // at the start of the indent (replacing spaces with TAB) ! saved_cursor.col = (colnr_T)(s - newline); } ! #ifdef FEAT_TEXT_PROP { ! int added = ind_len - (colnr_T)(p - oldline); ! // When increasing indent this behaves like spaces were inserted at ! // the old indent, when decreasing indent it behaves like spaces ! // were deleted at the new indent. ! adjust_prop_columns(curwin->w_cursor.lnum, ! (colnr_T)(added > 0 ? (p - oldline) : ind_len), added, 0); } ! #endif ! retval = TRUE; } else ! vim_free(newline); ! ! curwin->w_cursor.col = ind_len; ! return retval; } /* ! * Return the indent of the current line after a number. Return -1 if no ! * number was found. Used for 'n' in 'formatoptions': numbered list. ! * Since a pattern is used it can actually handle more than numbers. */ ! int ! get_number_indent(linenr_T lnum) { ! colnr_T col; ! pos_T pos; ! ! regmatch_T regmatch; ! int lead_len = 0; // length of comment leader ! ! if (lnum > curbuf->b_ml.ml_line_count) ! return -1; ! pos.lnum = 0; ! ! // In format_lines() (i.e. not insert mode), fo+=q is needed too... ! if ((State & INSERT) || has_format_option(FO_Q_COMS)) ! lead_len = get_leader_len(ml_get(lnum), NULL, FALSE, TRUE); ! regmatch.regprog = vim_regcomp(curbuf->b_p_flp, RE_MAGIC); ! if (regmatch.regprog != NULL) { ! regmatch.rm_ic = FALSE; ! ! // vim_regexec() expects a pointer to a line. This lets us ! // start matching for the flp beyond any comment leader... ! if (vim_regexec(®match, ml_get(lnum) + lead_len, (colnr_T)0)) { ! pos.lnum = lnum; ! pos.col = (colnr_T)(*regmatch.endp - ml_get(lnum)); ! pos.coladd = 0; } ! vim_regfree(regmatch.regprog); } ! if (pos.lnum == 0 || *ml_get_pos(&pos) == NUL) ! return -1; ! getvcol(curwin, &pos, &col, NULL, NULL); ! return (int)col; } + #if defined(FEAT_LINEBREAK) || defined(PROTO) /* ! * Return appropriate space number for breakindent, taking influencing ! * parameters into account. Window must be specified, since it is not ! * necessarily always the current one. */ ! int ! get_breakindent_win( ! win_T *wp, ! char_u *line) // start of the line ! { ! static int prev_indent = 0; // cached indent value ! static long prev_ts = 0L; // cached tabstop value ! static char_u *prev_line = NULL; // cached pointer to line ! static varnumber_T prev_tick = 0; // changedtick of cached value ! # ifdef FEAT_VARTABS ! static int *prev_vts = NULL; // cached vartabs values ! # endif ! int bri = 0; ! // window width minus window margin space, i.e. what rests for text ! const int eff_wwidth = wp->w_width ! - ((wp->w_p_nu || wp->w_p_rnu) ! && (vim_strchr(p_cpo, CPO_NUMCOL) == NULL) ! ? number_width(wp) + 1 : 0); ! ! // used cached indent, unless pointer or 'tabstop' changed ! if (prev_line != line || prev_ts != wp->w_buffer->b_p_ts ! || prev_tick != CHANGEDTICK(wp->w_buffer) ! # ifdef FEAT_VARTABS ! || prev_vts != wp->w_buffer->b_p_vts_array ! # endif ! ) { ! prev_line = line; ! prev_ts = wp->w_buffer->b_p_ts; ! prev_tick = CHANGEDTICK(wp->w_buffer); ! # ifdef FEAT_VARTABS ! prev_vts = wp->w_buffer->b_p_vts_array; ! prev_indent = get_indent_str_vtab(line, ! (int)wp->w_buffer->b_p_ts, ! wp->w_buffer->b_p_vts_array, wp->w_p_list); ! # else ! prev_indent = get_indent_str(line, ! (int)wp->w_buffer->b_p_ts, wp->w_p_list); ! # endif } ! bri = prev_indent + wp->w_p_brishift; ! ! // indent minus the length of the showbreak string ! if (wp->w_p_brisbr) ! bri -= vim_strsize(p_sbr); ! ! // Add offset for number column, if 'n' is in 'cpoptions' ! bri += win_col_off2(wp); ! ! // never indent past left window margin ! if (bri < 0) ! bri = 0; ! // always leave at least bri_min characters on the left, ! // if text width is sufficient ! else if (bri > eff_wwidth - wp->w_p_brimin) ! bri = (eff_wwidth - wp->w_p_brimin < 0) ! ? 0 : eff_wwidth - wp->w_p_brimin; ! ! return bri; } + #endif /* ! * When extra == 0: Return TRUE if the cursor is before or on the first ! * non-blank in the line. ! * When extra == 1: Return TRUE if the cursor is before the first non-blank in ! * the line. */ ! int ! inindent(int extra) { ! char_u *ptr; ! colnr_T col; ! ! for (col = 0, ptr = ml_get_curline(); VIM_ISWHITE(*ptr); ++col) ! ++ptr; ! if (col >= curwin->w_cursor.col + extra) ! return TRUE; ! else ! return FALSE; } + #if defined(FEAT_LISP) || defined(FEAT_CINDENT) || defined(PROTO) /* ! * op_reindent - handle reindenting a block of lines. */ ! void ! op_reindent(oparg_T *oap, int (*how)(void)) { ! long i; ! char_u *l; ! int amount; ! linenr_T first_changed = 0; ! linenr_T last_changed = 0; ! linenr_T start_lnum = curwin->w_cursor.lnum; ! // Don't even try when 'modifiable' is off. ! if (!curbuf->b_p_ma) { ! emsg(_(e_modifiable)); ! return; ! } ! ! for (i = oap->line_count; --i >= 0 && !got_int; ) ! { ! // it's a slow thing to do, so give feedback so there's no worry that ! // the computer's just hung. ! ! if (i > 1 ! && (i % 50 == 0 || i == oap->line_count - 1) ! && oap->line_count > p_report) ! smsg(_("%ld lines to indent... "), i); ! ! // Be vi-compatible: For lisp indenting the first line is not ! // indented, unless there is only one line. ! # ifdef FEAT_LISP ! if (i != oap->line_count - 1 || oap->line_count == 1 ! || how != get_lisp_indent) ! # endif { ! l = skipwhite(ml_get_curline()); ! if (*l == NUL) // empty or blank line ! amount = 0; ! else ! amount = how(); // get the indent for this line ! 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; ! last_changed = curwin->w_cursor.lnum; } } + ++curwin->w_cursor.lnum; + curwin->w_cursor.col = 0; // make sure it's valid } ! // put cursor on first non-blank of indented line ! curwin->w_cursor.lnum = start_lnum; ! beginline(BL_SOL | BL_FIX); ! // Mark changed lines so that they will be redrawn. When Visual ! // highlighting was present, need to continue until the last line. When ! // there is no change still need to remove the Visual highlighting. ! if (last_changed != 0) ! changed_lines(first_changed, 0, ! oap->is_VIsual ? start_lnum + oap->line_count : ! last_changed + 1, 0L); ! else if (oap->is_VIsual) ! redraw_curbuf_later(INVERTED); ! if (oap->line_count > p_report) ! { ! i = oap->line_count - (i + 1); ! smsg(NGETTEXT("%ld line indented ", ! "%ld lines indented ", i), i); } ! // set '[ and '] marks ! curbuf->b_op_start = oap->start; ! curbuf->b_op_end = oap->end; } + #endif // defined(FEAT_LISP) || defined(FEAT_CINDENT) + #if defined(FEAT_SMARTINDENT) || defined(FEAT_CINDENT) || defined(PROTO) /* ! * Return TRUE if lines starting with '#' should be left aligned. */ ! int ! preprocs_left(void) { ! return ! # ifdef FEAT_SMARTINDENT ! # ifdef FEAT_CINDENT ! (curbuf->b_p_si && !curbuf->b_p_cin) || ! # else ! curbuf->b_p_si ! # endif ! # endif ! # ifdef FEAT_CINDENT ! (curbuf->b_p_cin && in_cinkeys('#', ' ', TRUE) ! && curbuf->b_ind_hash_comment == 0) ! # endif ! ; } + #endif + #ifdef FEAT_SMARTINDENT /* ! * Try to do some very smart auto-indenting. ! * Used when inserting a "normal" character. */ ! void ! ins_try_si(int c) { + pos_T *pos, old_pos; + char_u *ptr; int i; ! int temp; ! // do some very smart indenting when entering '{' or '}' ! if (((did_si || can_si_back) && c == '{') || (can_si && c == '}')) { ! // for '}' set indent equal to indent of line containing matching '{' ! if (c == '}' && (pos = findmatch(NULL, '{')) != NULL) { ! old_pos = curwin->w_cursor; ! // If the matching '{' has a ')' immediately before it (ignoring ! // white-space), then line up with the start of the line ! // containing the matching '(' if there is one. This handles the ! // case where an "if (..\n..) {" statement continues over multiple ! // lines -- webb ! ptr = ml_get(pos->lnum); ! i = pos->col; ! if (i > 0) // skip blanks before '{' ! while (--i > 0 && VIM_ISWHITE(ptr[i])) ! ; ! curwin->w_cursor.lnum = pos->lnum; ! curwin->w_cursor.col = i; ! if (ptr[i] == ')' && (pos = findmatch(NULL, '(')) != NULL) ! curwin->w_cursor = *pos; ! i = get_indent(); ! curwin->w_cursor = old_pos; ! if (State & VREPLACE_FLAG) ! change_indent(INDENT_SET, i, FALSE, NUL, TRUE); else + (void)set_indent(i, SIN_CHANGED); + } + else if (curwin->w_cursor.col > 0) + { + // when inserting '{' after "O" reduce indent, but not + // more than indent of previous line + temp = TRUE; + if (c == '{' && can_si_back && curwin->w_cursor.lnum > 1) { ! old_pos = curwin->w_cursor; ! i = get_indent(); ! while (curwin->w_cursor.lnum > 1) ! { ! ptr = skipwhite(ml_get(--(curwin->w_cursor.lnum))); ! ! // ignore empty lines and lines starting with '#'. ! if (*ptr != '#' && *ptr != NUL) ! break; ! } ! if (get_indent() >= i) ! temp = FALSE; ! curwin->w_cursor = old_pos; } + if (temp) + shift_line(TRUE, FALSE, 1, TRUE); } } ! ! // set indent of '#' always to 0 ! if (curwin->w_cursor.col > 0 && can_si && c == '#') ! { ! // remember current indent for next line ! old_indent = get_indent(); ! (void)set_indent(0, SIN_CHANGED); ! } ! ! // Adjust ai_col, the char at this position can be deleted. ! if (ai_col > curwin->w_cursor.col) ! ai_col = curwin->w_cursor.col; } + #endif /* ! * Insert an indent (for or CTRL-T) or delete an indent (for CTRL-D). ! * Keep the cursor on the same character. ! * type == INDENT_INC increase indent (for CTRL-T or ) ! * type == INDENT_DEC decrease indent (for CTRL-D) ! * type == INDENT_SET set indent to "amount" ! * if round is TRUE, round the indent to 'shiftwidth' (only with _INC and _Dec). */ void ! change_indent( ! int type, ! int amount, ! int round, ! int replaced, // replaced character, put on replace stack ! int call_changed_bytes) // call changed_bytes() ! { ! int vcol; ! int last_vcol; ! int insstart_less; // reduction for Insstart.col ! int new_cursor_col; ! int i; ! char_u *ptr; ! int save_p_list; ! int start_col; ! colnr_T vc; ! colnr_T orig_col = 0; // init for GCC ! char_u *new_line, *orig_line = NULL; // init for GCC ! ! // VREPLACE mode needs to know what the line was like before changing ! if (State & VREPLACE_FLAG) ! { ! orig_line = vim_strsave(ml_get_curline()); // Deal with NULL below ! orig_col = curwin->w_cursor.col; ! } ! ! // for the following tricks we don't want list mode ! save_p_list = curwin->w_p_list; ! curwin->w_p_list = FALSE; ! vc = getvcol_nolist(&curwin->w_cursor); ! vcol = vc; ! ! // For Replace mode we need to fix the replace stack later, which is only ! // possible when the cursor is in the indent. Remember the number of ! // characters before the cursor if it's possible. ! start_col = curwin->w_cursor.col; ! ! // determine offset from first non-blank ! new_cursor_col = curwin->w_cursor.col; ! beginline(BL_WHITE); ! new_cursor_col -= curwin->w_cursor.col; ! ! insstart_less = curwin->w_cursor.col; ! ! // If the cursor is in the indent, compute how many screen columns the ! // cursor is to the left of the first non-blank. ! if (new_cursor_col < 0) ! vcol = get_indent() - vcol; ! ! if (new_cursor_col > 0) // can't fix replace stack ! start_col = -1; ! ! // Set the new indent. The cursor will be put on the first non-blank. ! if (type == INDENT_SET) ! (void)set_indent(amount, call_changed_bytes ? SIN_CHANGED : 0); ! else ! { ! int save_State = State; ! // Avoid being called recursively. ! if (State & VREPLACE_FLAG) ! State = INSERT; ! shift_line(type == INDENT_DEC, round, 1, call_changed_bytes); ! State = save_State; ! } ! insstart_less -= curwin->w_cursor.col; ! ! // Try to put cursor on same character. ! // If the cursor is at or after the first non-blank in the line, ! // compute the cursor column relative to the column of the first ! // non-blank character. ! // If we are not in insert mode, leave the cursor on the first non-blank. ! // If the cursor is before the first non-blank, position it relative ! // to the first non-blank, counted in screen columns. ! if (new_cursor_col >= 0) ! { ! // When changing the indent while the cursor is touching it, reset ! // Insstart_col to 0. ! if (new_cursor_col == 0) ! insstart_less = MAXCOL; ! new_cursor_col += curwin->w_cursor.col; ! } ! else if (!(State & INSERT)) ! new_cursor_col = curwin->w_cursor.col; ! else { ! // Compute the screen column where the cursor should be. ! vcol = get_indent() - vcol; ! curwin->w_virtcol = (colnr_T)((vcol < 0) ? 0 : vcol); ! ! // Advance the cursor until we reach the right screen column. ! vcol = last_vcol = 0; ! new_cursor_col = -1; ! ptr = ml_get_curline(); ! while (vcol <= (int)curwin->w_virtcol) ! { ! last_vcol = vcol; ! if (has_mbyte && new_cursor_col >= 0) ! new_cursor_col += (*mb_ptr2len)(ptr + new_cursor_col); else ! ++new_cursor_col; ! vcol += lbr_chartabsize(ptr, ptr + new_cursor_col, (colnr_T)vcol); } ! vcol = last_vcol; ! // May need to insert spaces to be able to position the cursor on ! // the right screen column. ! if (vcol != (int)curwin->w_virtcol) { ! curwin->w_cursor.col = (colnr_T)new_cursor_col; ! i = (int)curwin->w_virtcol - vcol; ! ptr = alloc(i + 1); ! if (ptr != NULL) ! { ! new_cursor_col += i; ! ptr[i] = NUL; ! while (--i >= 0) ! ptr[i] = ' '; ! ins_str(ptr); ! vim_free(ptr); ! } } ! // When changing the indent while the cursor is in it, reset ! // Insstart_col to 0. ! insstart_less = MAXCOL; } ! curwin->w_p_list = save_p_list; ! if (new_cursor_col <= 0) ! curwin->w_cursor.col = 0; ! else ! curwin->w_cursor.col = (colnr_T)new_cursor_col; ! curwin->w_set_curswant = TRUE; ! changed_cline_bef_curs(); ! // May have to adjust the start of the insert. ! if (State & INSERT) { ! if (curwin->w_cursor.lnum == Insstart.lnum && Insstart.col != 0) { ! if ((int)Insstart.col <= insstart_less) ! Insstart.col = 0; ! else ! Insstart.col -= insstart_less; } ! if ((int)ai_col <= insstart_less) ! ai_col = 0; else ! ai_col -= insstart_less; } ! // For REPLACE mode, may have to fix the replace stack, if it's possible. ! // If the number of characters before the cursor decreased, need to pop a ! // few characters from the replace stack. ! // If the number of characters before the cursor increased, need to push a ! // few NULs onto the replace stack. ! if (REPLACE_NORMAL(State) && start_col >= 0) ! { ! while (start_col > (int)curwin->w_cursor.col) { ! replace_join(0); // remove a NUL from the replace stack ! --start_col; } ! while (start_col < (int)curwin->w_cursor.col || replaced) { ! replace_push(NUL); ! if (replaced) { ! replace_push(replaced); ! replaced = NUL; } + ++start_col; } + } ! // For VREPLACE mode, we also have to fix the replace stack. In this case ! // it is always possible because we backspace over the whole line and then ! // put it back again the way we wanted it. ! if (State & VREPLACE_FLAG) ! { ! // If orig_line didn't allocate, just return. At least we did the job, ! // even if you can't backspace. ! if (orig_line == NULL) ! return; ! // Save new line ! new_line = vim_strsave(ml_get_curline()); ! if (new_line == NULL) ! return; ! // We only put back the new line up to the cursor ! new_line[curwin->w_cursor.col] = NUL; ! // Put back original line ! ml_replace(curwin->w_cursor.lnum, orig_line, FALSE); ! curwin->w_cursor.col = orig_col; ! // Backspace from cursor to start of line ! backspace_until_column(0); ! // Insert new stuff into line again ! ins_bytes(new_line); ! vim_free(new_line); ! } ! } ! /* ! * Copy the indent from ptr to the current line (and fill to size) ! * Leaves the cursor on the first non-blank in the line. ! * Returns TRUE if the line was changed. ! */ ! int ! copy_indent(int size, char_u *src) ! { ! char_u *p = NULL; ! char_u *line = NULL; ! char_u *s; ! int todo; ! int ind_len; ! int line_len = 0; ! int tab_pad; ! int ind_done; ! int round; ! #ifdef FEAT_VARTABS ! int ind_col; ! #endif ! // Round 1: compute the number of characters needed for the indent ! // Round 2: copy the characters. ! for (round = 1; round <= 2; ++round) ! { ! todo = size; ! ind_len = 0; ! ind_done = 0; ! #ifdef FEAT_VARTABS ! ind_col = 0; ! #endif ! s = src; ! // Count/copy the usable portion of the source line ! while (todo > 0 && VIM_ISWHITE(*s)) ! { ! if (*s == TAB) { ! #ifdef FEAT_VARTABS ! tab_pad = tabstop_padding(ind_done, curbuf->b_p_ts, ! curbuf->b_p_vts_array); ! #else ! tab_pad = (int)curbuf->b_p_ts ! - (ind_done % (int)curbuf->b_p_ts); ! #endif ! // Stop if this tab will overshoot the target ! if (todo < tab_pad) ! break; ! todo -= tab_pad; ! ind_done += tab_pad; ! #ifdef FEAT_VARTABS ! ind_col += tab_pad; ! #endif } else { ! --todo; ! ++ind_done; ! #ifdef FEAT_VARTABS ! ++ind_col; ! #endif } + ++ind_len; + if (p != NULL) + *p++ = *s; + ++s; } ! // Fill to next tabstop with a tab, if possible ! #ifdef FEAT_VARTABS ! tab_pad = tabstop_padding(ind_done, curbuf->b_p_ts, ! curbuf->b_p_vts_array); ! #else ! tab_pad = (int)curbuf->b_p_ts - (ind_done % (int)curbuf->b_p_ts); ! #endif ! if (todo >= tab_pad && !curbuf->b_p_et) { ! todo -= tab_pad; ! ++ind_len; ! #ifdef FEAT_VARTABS ! ind_col += tab_pad; ! #endif ! if (p != NULL) ! *p++ = TAB; } ! // Add tabs required for indent ! if (!curbuf->b_p_et) ! { ! #ifdef FEAT_VARTABS ! for (;;) ! { ! tab_pad = tabstop_padding(ind_col, curbuf->b_p_ts, ! curbuf->b_p_vts_array); ! if (todo < tab_pad) ! break; ! todo -= tab_pad; ! ++ind_len; ! ind_col += tab_pad; ! if (p != NULL) ! *p++ = TAB; ! } ! #else ! while (todo >= (int)curbuf->b_p_ts) ! { ! todo -= (int)curbuf->b_p_ts; ! ++ind_len; ! if (p != NULL) ! *p++ = TAB; ! } ! #endif } ! // Count/add spaces required for indent ! while (todo > 0) { ! --todo; ! ++ind_len; ! if (p != NULL) ! *p++ = ' '; ! } ! ! if (p == NULL) ! { ! // Allocate memory for the result: the copied indent, new indent ! // and the rest of the line. ! line_len = (int)STRLEN(ml_get_curline()) + 1; ! line = alloc(ind_len + line_len); ! if (line == NULL) ! return FALSE; ! p = line; } ! } ! // Append the original line ! mch_memmove(p, ml_get_curline(), (size_t)line_len); ! // Replace the line ! ml_replace(curwin->w_cursor.lnum, line, FALSE); ! // Put the cursor after the indent. ! curwin->w_cursor.col = ind_len; ! return TRUE; ! } ! ! /* ! * ":retab". ! */ ! void ! ex_retab(exarg_T *eap) ! { ! linenr_T lnum; ! int got_tab = FALSE; ! long num_spaces = 0; ! long num_tabs; ! long len; ! long col; ! long vcol; ! long start_col = 0; /* For start of white-space string */ ! long start_vcol = 0; /* For start of white-space string */ ! long old_len; ! char_u *ptr; ! char_u *new_line = (char_u *)1; /* init to non-NULL */ ! int did_undo; /* called u_save for current line */ ! #ifdef FEAT_VARTABS ! int *new_vts_array = NULL; ! char_u *new_ts_str; /* string value of tab argument */ ! #else ! int temp; ! int new_ts; ! #endif ! int save_list; ! linenr_T first_line = 0; /* first changed line */ ! linenr_T last_line = 0; /* last changed line */ ! ! save_list = curwin->w_p_list; ! curwin->w_p_list = 0; /* don't want list mode here */ ! ! #ifdef FEAT_VARTABS ! new_ts_str = eap->arg; ! if (!tabstop_set(eap->arg, &new_vts_array)) ! return; ! while (vim_isdigit(*(eap->arg)) || *(eap->arg) == ',') ! ++(eap->arg); ! ! // This ensures that either new_vts_array and new_ts_str are freshly ! // allocated, or new_vts_array points to an existing array and new_ts_str ! // is null. ! if (new_vts_array == NULL) ! { ! new_vts_array = curbuf->b_p_vts_array; ! new_ts_str = NULL; ! } ! else ! new_ts_str = vim_strnsave(new_ts_str, eap->arg - new_ts_str); ! #else ! new_ts = getdigits(&(eap->arg)); ! if (new_ts < 0) ! { ! emsg(_(e_positive)); ! return; ! } ! if (new_ts == 0) ! new_ts = curbuf->b_p_ts; ! #endif ! for (lnum = eap->line1; !got_int && lnum <= eap->line2; ++lnum) ! { ! ptr = ml_get(lnum); ! col = 0; ! vcol = 0; ! did_undo = FALSE; ! for (;;) ! { ! if (VIM_ISWHITE(ptr[col])) { ! if (!got_tab && num_spaces == 0) { ! /* First consecutive white-space */ ! start_vcol = vcol; ! start_col = col; } + if (ptr[col] == ' ') + num_spaces++; else ! got_tab = TRUE; } else { ! if (got_tab || (eap->forceit && num_spaces > 1)) { ! /* Retabulate this string of white-space */ ! /* len is virtual length of white string */ ! len = num_spaces = vcol - start_vcol; ! num_tabs = 0; ! if (!curbuf->b_p_et) ! { ! #ifdef FEAT_VARTABS ! int t, s; ! ! tabstop_fromto(start_vcol, vcol, ! curbuf->b_p_ts, new_vts_array, &t, &s); ! num_tabs = t; ! num_spaces = s; ! #else ! temp = new_ts - (start_vcol % new_ts); ! if (num_spaces >= temp) { ! num_spaces -= temp; ! num_tabs++; } + num_tabs += num_spaces / new_ts; + num_spaces -= (num_spaces / new_ts) * new_ts; + #endif } ! if (curbuf->b_p_et || got_tab || ! (num_spaces + num_tabs < len)) { ! if (did_undo == FALSE) { ! did_undo = TRUE; ! if (u_save((linenr_T)(lnum - 1), ! (linenr_T)(lnum + 1)) == FAIL) { ! new_line = NULL; /* flag out-of-memory */ break; } } ! /* len is actual number of white characters used */ ! len = num_spaces + num_tabs; ! old_len = (long)STRLEN(ptr); ! new_line = alloc(old_len - col + start_col + len + 1); ! if (new_line == NULL) break; ! if (start_col > 0) ! mch_memmove(new_line, ptr, (size_t)start_col); ! mch_memmove(new_line + start_col + len, ! ptr + col, (size_t)(old_len - col + 1)); ! ptr = new_line + start_col; ! for (col = 0; col < len; col++) ! ptr[col] = (col < num_tabs) ? '\t' : ' '; ! ml_replace(lnum, new_line, FALSE); ! if (first_line == 0) ! first_line = lnum; ! last_line = lnum; ! ptr = new_line; ! col = start_col + len; } } + got_tab = FALSE; + num_spaces = 0; } ! if (ptr[col] == NUL) break; ! vcol += chartabsize(ptr + col, (colnr_T)vcol); ! if (has_mbyte) ! col += (*mb_ptr2len)(ptr + col); ! else ! ++col; } ! if (new_line == NULL) /* out of memory */ break; ! line_breakcheck(); } + if (got_int) + emsg(_(e_interr)); ! #ifdef FEAT_VARTABS ! // If a single value was given then it can be considered equal to ! // either the value of 'tabstop' or the value of 'vartabstop'. ! if (tabstop_count(curbuf->b_p_vts_array) == 0 ! && tabstop_count(new_vts_array) == 1 ! && curbuf->b_p_ts == tabstop_first(new_vts_array)) ! ; /* not changed */ ! else if (tabstop_count(curbuf->b_p_vts_array) > 0 ! && tabstop_eq(curbuf->b_p_vts_array, new_vts_array)) ! ; /* not changed */ else ! redraw_curbuf_later(NOT_VALID); ! #else ! if (curbuf->b_p_ts != new_ts) ! redraw_curbuf_later(NOT_VALID); ! #endif ! if (first_line != 0) ! changed_lines(first_line, 0, last_line + 1, 0L); ! curwin->w_p_list = save_list; /* restore 'list' */ ! #ifdef FEAT_VARTABS ! if (new_ts_str != NULL) /* set the new tabstop */ { ! // If 'vartabstop' is in use or if the value given to retab has more ! // than one tabstop then update 'vartabstop'. ! int *old_vts_ary = curbuf->b_p_vts_array; ! if (tabstop_count(old_vts_ary) > 0 || tabstop_count(new_vts_array) > 1) { ! set_string_option_direct((char_u *)"vts", -1, new_ts_str, ! OPT_FREE|OPT_LOCAL, 0); ! curbuf->b_p_vts_array = new_vts_array; ! vim_free(old_vts_ary); } + else + { + // 'vartabstop' wasn't in use and a single value was given to + // retab then update 'tabstop'. + curbuf->b_p_ts = tabstop_first(new_vts_array); + vim_free(new_vts_array); + } + vim_free(new_ts_str); } ! #else ! curbuf->b_p_ts = new_ts; ! #endif ! coladvance(curwin->w_curswant); ! ! u_clearline(); } ! #if (defined(FEAT_CINDENT) && defined(FEAT_EVAL)) || defined(PROTO) /* * Get indent level from 'indentexpr'. */ *************** *** 3935,4186 **** return indent; } - # endif - - /* - * return TRUE if 'cinkeys' contains the key "keytyped", - * when == '*': Only if key is preceded with '*' (indent before insert) - * when == '!': Only if key is preceded with '!' (don't insert) - * when == ' ': Only if key is not preceded with '*'(indent afterwards) - * - * "keytyped" can have a few special values: - * KEY_OPEN_FORW - * KEY_OPEN_BACK - * KEY_COMPLETE just finished completion. - * - * If line_is_empty is TRUE accept keys with '0' before them. - */ - int - in_cinkeys( - int keytyped, - int when, - int line_is_empty) - { - char_u *look; - int try_match; - int try_match_word; - char_u *p; - char_u *line; - int icase; - int i; - - if (keytyped == NUL) - // Can happen with CTRL-Y and CTRL-E on a short line. - return FALSE; - - #ifdef FEAT_EVAL - if (*curbuf->b_p_inde != NUL) - look = curbuf->b_p_indk; // 'indentexpr' set: use 'indentkeys' - else - #endif - look = curbuf->b_p_cink; // 'indentexpr' empty: use 'cinkeys' - while (*look) - { - // Find out if we want to try a match with this key, depending on - // 'when' and a '*' or '!' before the key. - switch (when) - { - case '*': try_match = (*look == '*'); break; - case '!': try_match = (*look == '!'); break; - default: try_match = (*look != '*'); break; - } - if (*look == '*' || *look == '!') - ++look; - - // If there is a '0', only accept a match if the line is empty. - // But may still match when typing last char of a word. - if (*look == '0') - { - try_match_word = try_match; - if (!line_is_empty) - try_match = FALSE; - ++look; - } - else - try_match_word = FALSE; - - // does it look like a control character? - if (*look == '^' - #ifdef EBCDIC - && (Ctrl_chr(look[1]) != 0) - #else - && look[1] >= '?' && look[1] <= '_' #endif - ) - { - if (try_match && keytyped == Ctrl_chr(look[1])) - return TRUE; - look += 2; - } - // 'o' means "o" command, open forward. - // 'O' means "O" command, open backward. - else if (*look == 'o') - { - if (try_match && keytyped == KEY_OPEN_FORW) - return TRUE; - ++look; - } - else if (*look == 'O') - { - if (try_match && keytyped == KEY_OPEN_BACK) - return TRUE; - ++look; - } - - // 'e' means to check for "else" at start of line and just before the - // cursor. - else if (*look == 'e') - { - if (try_match && keytyped == 'e' && curwin->w_cursor.col >= 4) - { - p = ml_get_curline(); - if (skipwhite(p) == p + curwin->w_cursor.col - 4 && - STRNCMP(p + curwin->w_cursor.col - 4, "else", 4) == 0) - return TRUE; - } - ++look; - } - - // ':' only causes an indent if it is at the end of a label or case - // statement, or when it was before typing the ':' (to fix - // class::method for C++). - else if (*look == ':') - { - if (try_match && keytyped == ':') - { - p = ml_get_curline(); - if (cin_iscase(p, FALSE) || cin_isscopedecl(p) || cin_islabel()) - return TRUE; - // Need to get the line again after cin_islabel(). - p = ml_get_curline(); - if (curwin->w_cursor.col > 2 - && p[curwin->w_cursor.col - 1] == ':' - && p[curwin->w_cursor.col - 2] == ':') - { - p[curwin->w_cursor.col - 1] = ' '; - i = (cin_iscase(p, FALSE) || cin_isscopedecl(p) - || cin_islabel()); - p = ml_get_curline(); - p[curwin->w_cursor.col - 1] = ':'; - if (i) - return TRUE; - } - } - ++look; - } - - - // Is it a key in <>, maybe? - else if (*look == '<') - { - if (try_match) - { - // make up some named keys , , , <0>, <>>, <<>, <*>, - // <:> and so that people can re-indent on o, O, e, 0, <, - // >, *, : and ! keys if they really really want to. - if (vim_strchr((char_u *)"<>!*oOe0:", look[1]) != NULL - && keytyped == look[1]) - return TRUE; - - if (keytyped == get_special_key_code(look + 1)) - return TRUE; - } - while (*look && *look != '>') - look++; - while (*look == '>') - look++; - } - - // Is it a word: "=word"? - else if (*look == '=' && look[1] != ',' && look[1] != NUL) - { - ++look; - if (*look == '~') - { - icase = TRUE; - ++look; - } - else - icase = FALSE; - p = vim_strchr(look, ','); - if (p == NULL) - p = look + STRLEN(look); - if ((try_match || try_match_word) - && curwin->w_cursor.col >= (colnr_T)(p - look)) - { - int match = FALSE; - - if (keytyped == KEY_COMPLETE) - { - char_u *s; - - // Just completed a word, check if it starts with "look". - // search back for the start of a word. - line = ml_get_curline(); - if (has_mbyte) - { - char_u *n; - - for (s = line + curwin->w_cursor.col; s > line; s = n) - { - n = mb_prevptr(line, s); - if (!vim_iswordp(n)) - break; - } - } - else - for (s = line + curwin->w_cursor.col; s > line; --s) - if (!vim_iswordc(s[-1])) - break; - if (s + (p - look) <= line + curwin->w_cursor.col - && (icase - ? MB_STRNICMP(s, look, p - look) - : STRNCMP(s, look, p - look)) == 0) - match = TRUE; - } - else - // TODO: multi-byte - if (keytyped == (int)p[-1] || (icase && keytyped < 256 - && TOLOWER_LOC(keytyped) == TOLOWER_LOC((int)p[-1]))) - { - line = ml_get_cursor(); - if ((curwin->w_cursor.col == (colnr_T)(p - look) - || !vim_iswordc(line[-(p - look) - 1])) - && (icase - ? MB_STRNICMP(line - (p - look), look, p - look) - : STRNCMP(line - (p - look), look, p - look)) - == 0) - match = TRUE; - } - if (match && try_match_word && !try_match) - { - // "0=word": Check if there are only blanks before the - // word. - if (getwhitecols_curline() != - (int)(curwin->w_cursor.col - (p - look))) - match = FALSE; - } - if (match) - return TRUE; - } - look = p; - } - - // ok, it's a boring generic character. - else - { - if (try_match && *look == keytyped) - return TRUE; - if (*look != NUL) - ++look; - } - - // Skip over ", ". - look = skip_to_option_part(look); - } - return FALSE; - } - #endif // FEAT_CINDENT #if defined(FEAT_LISP) || defined(PROTO) --- 1745,1751 ---- *************** *** 4401,4422 **** } #endif // FEAT_LISP - #if defined(FEAT_CINDENT) || defined(PROTO) - /* - * Do C or expression indenting on the current line. - */ - void - do_c_expr_indent(void) - { - # ifdef FEAT_EVAL - if (*curbuf->b_p_inde != NUL) - fixthisline(get_expr_indent); - else - # endif - fixthisline(get_c_indent); - } - #endif - #if defined(FEAT_LISP) || defined(FEAT_CINDENT) || defined(PROTO) /* * Re-indent the current line, based on the current contents of it and the --- 1966,1971 ---- *************** *** 4455,4817 **** do_c_expr_indent(); # endif } - #endif ! #if defined(FEAT_VARTABS) || defined(PROTO) ! ! /* ! * Set the integer values corresponding to the string setting of 'vartabstop'. ! * "array" will be set, caller must free it if needed. ! */ ! int ! tabstop_set(char_u *var, int **array) ! { ! int valcount = 1; ! int t; ! char_u *cp; ! ! if (var[0] == NUL || (var[0] == '0' && var[1] == NUL)) ! { ! *array = NULL; ! return TRUE; ! } ! ! for (cp = var; *cp != NUL; ++cp) ! { ! if (cp == var || cp[-1] == ',') ! { ! char_u *end; ! ! if (strtol((char *)cp, (char **)&end, 10) <= 0) ! { ! if (cp != end) ! emsg(_(e_positive)); ! else ! emsg(_(e_invarg)); ! return FALSE; ! } ! } ! ! if (VIM_ISDIGIT(*cp)) ! continue; ! if (cp[0] == ',' && cp > var && cp[-1] != ',' && cp[1] != NUL) ! { ! ++valcount; ! continue; ! } ! emsg(_(e_invarg)); ! return FALSE; ! } ! ! *array = ALLOC_MULT(int, valcount + 1); ! if (*array == NULL) ! return FALSE; ! (*array)[0] = valcount; ! ! t = 1; ! for (cp = var; *cp != NUL;) ! { ! (*array)[t++] = atoi((char *)cp); ! while (*cp != NUL && *cp != ',') ! ++cp; ! if (*cp != NUL) ! ++cp; ! } ! ! return TRUE; ! } ! ! /* ! * Calculate the number of screen spaces a tab will occupy. ! * If "vts" is set then the tab widths are taken from that array, ! * otherwise the value of ts is used. ! */ ! int ! tabstop_padding(colnr_T col, int ts_arg, int *vts) ! { ! int ts = ts_arg == 0 ? 8 : ts_arg; ! int tabcount; ! colnr_T tabcol = 0; ! int t; ! int padding = 0; ! ! if (vts == NULL || vts[0] == 0) ! return ts - (col % ts); ! ! tabcount = vts[0]; ! ! for (t = 1; t <= tabcount; ++t) ! { ! tabcol += vts[t]; ! if (tabcol > col) ! { ! padding = (int)(tabcol - col); ! break; ! } ! } ! if (t > tabcount) ! padding = vts[tabcount] - (int)((col - tabcol) % vts[tabcount]); ! ! return padding; ! } ! ! /* ! * Find the size of the tab that covers a particular column. ! */ ! int ! tabstop_at(colnr_T col, int ts, int *vts) ! { ! int tabcount; ! colnr_T tabcol = 0; ! int t; ! int tab_size = 0; ! ! if (vts == 0 || vts[0] == 0) ! return ts; ! ! tabcount = vts[0]; ! for (t = 1; t <= tabcount; ++t) ! { ! tabcol += vts[t]; ! if (tabcol > col) ! { ! tab_size = vts[t]; ! break; ! } ! } ! if (t > tabcount) ! tab_size = vts[tabcount]; ! ! return tab_size; ! } ! /* ! * Find the column on which a tab starts. */ ! colnr_T ! tabstop_start(colnr_T col, int ts, int *vts) { ! int tabcount; ! colnr_T tabcol = 0; ! int t; ! int excess; ! ! if (vts == NULL || vts[0] == 0) ! return (col / ts) * ts; ! ! tabcount = vts[0]; ! for (t = 1; t <= tabcount; ++t) ! { ! tabcol += vts[t]; ! if (tabcol > col) ! return tabcol - vts[t]; ! } ! excess = tabcol % vts[tabcount]; ! return excess + ((col - excess) / vts[tabcount]) * vts[tabcount]; } /* ! * Find the number of tabs and spaces necessary to get from one column ! * to another. */ void ! tabstop_fromto( ! colnr_T start_col, ! colnr_T end_col, ! int ts_arg, ! int *vts, ! int *ntabs, ! int *nspcs) { ! int spaces = end_col - start_col; ! colnr_T tabcol = 0; ! int padding = 0; ! int tabcount; ! int t; ! int ts = ts_arg == 0 ? curbuf->b_p_ts : ts_arg; ! ! if (vts == NULL || vts[0] == 0) ! { ! int tabs = 0; ! int initspc = 0; ! ! initspc = ts - (start_col % ts); ! if (spaces >= initspc) ! { ! spaces -= initspc; ! tabs++; ! } ! tabs += spaces / ts; ! spaces -= (spaces / ts) * ts; ! ! *ntabs = tabs; ! *nspcs = spaces; ! return; ! } ! ! // Find the padding needed to reach the next tabstop. ! tabcount = vts[0]; ! for (t = 1; t <= tabcount; ++t) ! { ! tabcol += vts[t]; ! if (tabcol > start_col) ! { ! padding = (int)(tabcol - start_col); ! break; ! } ! } ! if (t > tabcount) ! padding = vts[tabcount] - (int)((start_col - tabcol) % vts[tabcount]); ! ! // If the space needed is less than the padding no tabs can be used. ! if (spaces < padding) ! { ! *ntabs = 0; ! *nspcs = spaces; ! return; ! } ! ! *ntabs = 1; ! spaces -= padding; ! // At least one tab has been used. See if any more will fit. ! while (spaces != 0 && ++t <= tabcount) { ! padding = vts[t]; ! if (spaces < padding) ! { ! *nspcs = spaces; ! return; ! } ! ++*ntabs; ! spaces -= padding; } ! ! *ntabs += spaces / vts[tabcount]; ! *nspcs = spaces % vts[tabcount]; ! } ! ! /* ! * See if two tabstop arrays contain the same values. ! */ ! int ! tabstop_eq(int *ts1, int *ts2) ! { ! int t; ! ! if ((ts1 == 0 && ts2) || (ts1 && ts2 == 0)) ! return FALSE; ! if (ts1 == ts2) ! return TRUE; ! if (ts1[0] != ts2[0]) ! return FALSE; ! ! for (t = 1; t <= ts1[0]; ++t) ! if (ts1[t] != ts2[t]) ! return FALSE; ! ! return TRUE; ! } ! ! #if defined(FEAT_BEVAL) || defined(PROTO) ! /* ! * Copy a tabstop array, allocating space for the new array. ! */ ! int * ! tabstop_copy(int *oldts) ! { ! int *newts; ! int t; ! ! if (oldts == NULL) ! return NULL; ! newts = ALLOC_MULT(int, oldts[0] + 1); ! if (newts != NULL) ! for (t = 0; t <= oldts[0]; ++t) ! newts[t] = oldts[t]; ! return newts; ! } #endif ! ! /* ! * Return a count of the number of tabstops. ! */ ! int ! tabstop_count(int *ts) ! { ! return ts != NULL ? ts[0] : 0; ! } ! ! /* ! * Return the first tabstop, or 8 if there are no tabstops defined. ! */ ! int ! tabstop_first(int *ts) ! { ! return ts != NULL ? ts[1] : 8; } - #endif - - /* - * Return the effective shiftwidth value for current buffer, using the - * 'tabstop' value when 'shiftwidth' is zero. - */ - long - get_sw_value(buf_T *buf) - { - return get_sw_value_col(buf, 0); - } - - /* - * Idem, using "pos". - */ - static long - get_sw_value_pos(buf_T *buf, pos_T *pos) - { - pos_T save_cursor = curwin->w_cursor; - long sw_value; - - curwin->w_cursor = *pos; - sw_value = get_sw_value_col(buf, get_nolist_virtcol()); - curwin->w_cursor = save_cursor; - return sw_value; - } - - /* - * Idem, using the first non-black in the current line. - */ - long - get_sw_value_indent(buf_T *buf) - { - pos_T pos = curwin->w_cursor; - - pos.col = getwhitecols_curline(); - return get_sw_value_pos(buf, &pos); - } - - /* - * Idem, using virtual column "col". - */ - long - get_sw_value_col(buf_T *buf, colnr_T col UNUSED) - { - return buf->b_p_sw ? buf->b_p_sw : - #ifdef FEAT_VARTABS - tabstop_at(col, buf->b_p_ts, buf->b_p_vts_array); - #else - buf->b_p_ts; - #endif - } - - /* - * Return the effective softtabstop value for the current buffer, using the - * 'shiftwidth' value when 'softtabstop' is negative. - */ - long - get_sts_value(void) - { - return curbuf->b_p_sts < 0 ? get_sw_value(curbuf) : curbuf->b_p_sts; - } --- 2004,2047 ---- do_c_expr_indent(); # endif } #endif ! #if defined(FEAT_EVAL) || defined(PROTO) /* ! * "indent()" function */ ! void ! f_indent(typval_T *argvars, typval_T *rettv) { ! linenr_T lnum; ! lnum = tv_get_lnum(argvars); ! if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count) ! rettv->vval.v_number = get_indent_lnum(lnum); ! else ! rettv->vval.v_number = -1; } /* ! * "lispindent(lnum)" function */ void ! f_lispindent(typval_T *argvars UNUSED, typval_T *rettv) { ! #ifdef FEAT_LISP ! pos_T pos; ! linenr_T lnum; ! pos = curwin->w_cursor; ! lnum = tv_get_lnum(argvars); ! if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count) { ! curwin->w_cursor.lnum = lnum; ! rettv->vval.v_number = get_lisp_indent(); ! curwin->w_cursor = pos; } ! else #endif ! rettv->vval.v_number = -1; } #endif *** ../vim-8.1.2126/src/misc1.c 2019-09-28 19:04:06.993029586 +0200 --- src/misc1.c 2019-10-09 22:10:04.870760534 +0200 *************** *** 25,566 **** static garray_T ga_users; /* - * Count the size (in window cells) of the indent in the current line. - */ - int - get_indent(void) - { - #ifdef FEAT_VARTABS - return get_indent_str_vtab(ml_get_curline(), (int)curbuf->b_p_ts, - curbuf->b_p_vts_array, FALSE); - #else - return get_indent_str(ml_get_curline(), (int)curbuf->b_p_ts, FALSE); - #endif - } - - /* - * Count the size (in window cells) of the indent in line "lnum". - */ - int - get_indent_lnum(linenr_T lnum) - { - #ifdef FEAT_VARTABS - return get_indent_str_vtab(ml_get(lnum), (int)curbuf->b_p_ts, - curbuf->b_p_vts_array, FALSE); - #else - return get_indent_str(ml_get(lnum), (int)curbuf->b_p_ts, FALSE); - #endif - } - - #if defined(FEAT_FOLDING) || defined(PROTO) - /* - * Count the size (in window cells) of the indent in line "lnum" of buffer - * "buf". - */ - int - get_indent_buf(buf_T *buf, linenr_T lnum) - { - #ifdef FEAT_VARTABS - return get_indent_str_vtab(ml_get_buf(buf, lnum, FALSE), - (int)curbuf->b_p_ts, buf->b_p_vts_array, FALSE); - #else - return get_indent_str(ml_get_buf(buf, lnum, FALSE), (int)buf->b_p_ts, FALSE); - #endif - } - #endif - - /* - * count the size (in window cells) of the indent in line "ptr", with - * 'tabstop' at "ts" - */ - int - get_indent_str( - char_u *ptr, - int ts, - int list) /* if TRUE, count only screen size for tabs */ - { - int count = 0; - - for ( ; *ptr; ++ptr) - { - if (*ptr == TAB) - { - if (!list || lcs_tab1) /* count a tab for what it is worth */ - count += ts - (count % ts); - else - /* In list mode, when tab is not set, count screen char width - * for Tab, displays: ^I */ - count += ptr2cells(ptr); - } - else if (*ptr == ' ') - ++count; /* count a space for one */ - else - break; - } - return count; - } - - #ifdef FEAT_VARTABS - /* - * Count the size (in window cells) of the indent in line "ptr", using - * variable tabstops. - * if "list" is TRUE, count only screen size for tabs. - */ - int - get_indent_str_vtab(char_u *ptr, int ts, int *vts, int list) - { - int count = 0; - - for ( ; *ptr; ++ptr) - { - if (*ptr == TAB) /* count a tab for what it is worth */ - { - if (!list || lcs_tab1) - count += tabstop_padding(count, ts, vts); - else - /* In list mode, when tab is not set, count screen char width - * for Tab, displays: ^I */ - count += ptr2cells(ptr); - } - else if (*ptr == ' ') - ++count; /* count a space for one */ - else - break; - } - return count; - } - #endif - - /* - * Set the indent of the current line. - * Leaves the cursor on the first non-blank in the line. - * Caller must take care of undo. - * "flags": - * SIN_CHANGED: call changed_bytes() if the line was changed. - * SIN_INSERT: insert the indent in front of the line. - * SIN_UNDO: save line for undo before changing it. - * Returns TRUE if the line was changed. - */ - int - set_indent( - int size, /* measured in spaces */ - int flags) - { - char_u *p; - char_u *newline; - char_u *oldline; - char_u *s; - int todo; - int ind_len; /* measured in characters */ - int line_len; - int doit = FALSE; - int ind_done = 0; /* measured in spaces */ - #ifdef FEAT_VARTABS - int ind_col = 0; - #endif - int tab_pad; - int retval = FALSE; - int orig_char_len = -1; /* number of initial whitespace chars when - 'et' and 'pi' are both set */ - - /* - * First check if there is anything to do and compute the number of - * characters needed for the indent. - */ - todo = size; - ind_len = 0; - p = oldline = ml_get_curline(); - - /* Calculate the buffer size for the new indent, and check to see if it - * isn't already set */ - - /* if 'expandtab' isn't set: use TABs; if both 'expandtab' and - * 'preserveindent' are set count the number of characters at the - * beginning of the line to be copied */ - if (!curbuf->b_p_et || (!(flags & SIN_INSERT) && curbuf->b_p_pi)) - { - /* If 'preserveindent' is set then reuse as much as possible of - * the existing indent structure for the new indent */ - if (!(flags & SIN_INSERT) && curbuf->b_p_pi) - { - ind_done = 0; - - /* count as many characters as we can use */ - while (todo > 0 && VIM_ISWHITE(*p)) - { - if (*p == TAB) - { - #ifdef FEAT_VARTABS - tab_pad = tabstop_padding(ind_done, curbuf->b_p_ts, - curbuf->b_p_vts_array); - #else - tab_pad = (int)curbuf->b_p_ts - - (ind_done % (int)curbuf->b_p_ts); - #endif - /* stop if this tab will overshoot the target */ - if (todo < tab_pad) - break; - todo -= tab_pad; - ++ind_len; - ind_done += tab_pad; - } - else - { - --todo; - ++ind_len; - ++ind_done; - } - ++p; - } - - #ifdef FEAT_VARTABS - /* These diverge from this point. */ - ind_col = ind_done; - #endif - /* Set initial number of whitespace chars to copy if we are - * preserving indent but expandtab is set */ - if (curbuf->b_p_et) - orig_char_len = ind_len; - - /* Fill to next tabstop with a tab, if possible */ - #ifdef FEAT_VARTABS - tab_pad = tabstop_padding(ind_done, curbuf->b_p_ts, - curbuf->b_p_vts_array); - #else - tab_pad = (int)curbuf->b_p_ts - (ind_done % (int)curbuf->b_p_ts); - #endif - if (todo >= tab_pad && orig_char_len == -1) - { - doit = TRUE; - todo -= tab_pad; - ++ind_len; - /* ind_done += tab_pad; */ - #ifdef FEAT_VARTABS - ind_col += tab_pad; - #endif - } - } - - /* count tabs required for indent */ - #ifdef FEAT_VARTABS - for (;;) - { - tab_pad = tabstop_padding(ind_col, curbuf->b_p_ts, - curbuf->b_p_vts_array); - if (todo < tab_pad) - break; - if (*p != TAB) - doit = TRUE; - else - ++p; - todo -= tab_pad; - ++ind_len; - ind_col += tab_pad; - } - #else - while (todo >= (int)curbuf->b_p_ts) - { - if (*p != TAB) - doit = TRUE; - else - ++p; - todo -= (int)curbuf->b_p_ts; - ++ind_len; - /* ind_done += (int)curbuf->b_p_ts; */ - } - #endif - } - /* count spaces required for indent */ - while (todo > 0) - { - if (*p != ' ') - doit = TRUE; - else - ++p; - --todo; - ++ind_len; - /* ++ind_done; */ - } - - /* Return if the indent is OK already. */ - if (!doit && !VIM_ISWHITE(*p) && !(flags & SIN_INSERT)) - return FALSE; - - /* Allocate memory for the new line. */ - if (flags & SIN_INSERT) - p = oldline; - else - p = skipwhite(p); - line_len = (int)STRLEN(p) + 1; - - /* If 'preserveindent' and 'expandtab' are both set keep the original - * characters and allocate accordingly. We will fill the rest with spaces - * after the if (!curbuf->b_p_et) below. */ - if (orig_char_len != -1) - { - newline = alloc(orig_char_len + size - ind_done + line_len); - if (newline == NULL) - return FALSE; - todo = size - ind_done; - ind_len = orig_char_len + todo; /* Set total length of indent in - * characters, which may have been - * undercounted until now */ - p = oldline; - s = newline; - while (orig_char_len > 0) - { - *s++ = *p++; - orig_char_len--; - } - - /* Skip over any additional white space (useful when newindent is less - * than old) */ - while (VIM_ISWHITE(*p)) - ++p; - - } - else - { - todo = size; - newline = alloc(ind_len + line_len); - if (newline == NULL) - return FALSE; - s = newline; - } - - /* Put the characters in the new line. */ - /* if 'expandtab' isn't set: use TABs */ - if (!curbuf->b_p_et) - { - /* If 'preserveindent' is set then reuse as much as possible of - * the existing indent structure for the new indent */ - if (!(flags & SIN_INSERT) && curbuf->b_p_pi) - { - p = oldline; - ind_done = 0; - - while (todo > 0 && VIM_ISWHITE(*p)) - { - if (*p == TAB) - { - #ifdef FEAT_VARTABS - tab_pad = tabstop_padding(ind_done, curbuf->b_p_ts, - curbuf->b_p_vts_array); - #else - tab_pad = (int)curbuf->b_p_ts - - (ind_done % (int)curbuf->b_p_ts); - #endif - /* stop if this tab will overshoot the target */ - if (todo < tab_pad) - break; - todo -= tab_pad; - ind_done += tab_pad; - } - else - { - --todo; - ++ind_done; - } - *s++ = *p++; - } - - /* Fill to next tabstop with a tab, if possible */ - #ifdef FEAT_VARTABS - tab_pad = tabstop_padding(ind_done, curbuf->b_p_ts, - curbuf->b_p_vts_array); - #else - tab_pad = (int)curbuf->b_p_ts - (ind_done % (int)curbuf->b_p_ts); - #endif - if (todo >= tab_pad) - { - *s++ = TAB; - todo -= tab_pad; - #ifdef FEAT_VARTABS - ind_done += tab_pad; - #endif - } - - p = skipwhite(p); - } - - #ifdef FEAT_VARTABS - for (;;) - { - tab_pad = tabstop_padding(ind_done, curbuf->b_p_ts, - curbuf->b_p_vts_array); - if (todo < tab_pad) - break; - *s++ = TAB; - todo -= tab_pad; - ind_done += tab_pad; - } - #else - while (todo >= (int)curbuf->b_p_ts) - { - *s++ = TAB; - todo -= (int)curbuf->b_p_ts; - } - #endif - } - while (todo > 0) - { - *s++ = ' '; - --todo; - } - mch_memmove(s, p, (size_t)line_len); - - // Replace the line (unless undo fails). - if (!(flags & SIN_UNDO) || u_savesub(curwin->w_cursor.lnum) == OK) - { - ml_replace(curwin->w_cursor.lnum, newline, FALSE); - if (flags & SIN_CHANGED) - changed_bytes(curwin->w_cursor.lnum, 0); - - // Correct saved cursor position if it is in this line. - if (saved_cursor.lnum == curwin->w_cursor.lnum) - { - if (saved_cursor.col >= (colnr_T)(p - oldline)) - // cursor was after the indent, adjust for the number of - // bytes added/removed - saved_cursor.col += ind_len - (colnr_T)(p - oldline); - else if (saved_cursor.col >= (colnr_T)(s - newline)) - // cursor was in the indent, and is now after it, put it back - // at the start of the indent (replacing spaces with TAB) - saved_cursor.col = (colnr_T)(s - newline); - } - #ifdef FEAT_TEXT_PROP - { - int added = ind_len - (colnr_T)(p - oldline); - - // When increasing indent this behaves like spaces were inserted at - // the old indent, when decreasing indent it behaves like spaces - // were deleted at the new indent. - adjust_prop_columns(curwin->w_cursor.lnum, - (colnr_T)(added > 0 ? (p - oldline) : ind_len), added, 0); - } - #endif - retval = TRUE; - } - else - vim_free(newline); - - curwin->w_cursor.col = ind_len; - return retval; - } - - /* - * Return the indent of the current line after a number. Return -1 if no - * number was found. Used for 'n' in 'formatoptions': numbered list. - * Since a pattern is used it can actually handle more than numbers. - */ - int - get_number_indent(linenr_T lnum) - { - colnr_T col; - pos_T pos; - - regmatch_T regmatch; - int lead_len = 0; /* length of comment leader */ - - if (lnum > curbuf->b_ml.ml_line_count) - return -1; - pos.lnum = 0; - - /* In format_lines() (i.e. not insert mode), fo+=q is needed too... */ - if ((State & INSERT) || has_format_option(FO_Q_COMS)) - lead_len = get_leader_len(ml_get(lnum), NULL, FALSE, TRUE); - - regmatch.regprog = vim_regcomp(curbuf->b_p_flp, RE_MAGIC); - if (regmatch.regprog != NULL) - { - regmatch.rm_ic = FALSE; - - /* vim_regexec() expects a pointer to a line. This lets us - * start matching for the flp beyond any comment leader... */ - if (vim_regexec(®match, ml_get(lnum) + lead_len, (colnr_T)0)) - { - pos.lnum = lnum; - pos.col = (colnr_T)(*regmatch.endp - ml_get(lnum)); - pos.coladd = 0; - } - vim_regfree(regmatch.regprog); - } - - if (pos.lnum == 0 || *ml_get_pos(&pos) == NUL) - return -1; - getvcol(curwin, &pos, &col, NULL, NULL); - return (int)col; - } - - #if defined(FEAT_LINEBREAK) || defined(PROTO) - /* - * Return appropriate space number for breakindent, taking influencing - * parameters into account. Window must be specified, since it is not - * necessarily always the current one. - */ - int - get_breakindent_win( - win_T *wp, - char_u *line) /* start of the line */ - { - static int prev_indent = 0; /* cached indent value */ - static long prev_ts = 0L; /* cached tabstop value */ - static char_u *prev_line = NULL; /* cached pointer to line */ - static varnumber_T prev_tick = 0; /* changedtick of cached value */ - #ifdef FEAT_VARTABS - static int *prev_vts = NULL; /* cached vartabs values */ - #endif - int bri = 0; - /* window width minus window margin space, i.e. what rests for text */ - const int eff_wwidth = wp->w_width - - ((wp->w_p_nu || wp->w_p_rnu) - && (vim_strchr(p_cpo, CPO_NUMCOL) == NULL) - ? number_width(wp) + 1 : 0); - - /* used cached indent, unless pointer or 'tabstop' changed */ - if (prev_line != line || prev_ts != wp->w_buffer->b_p_ts - || prev_tick != CHANGEDTICK(wp->w_buffer) - #ifdef FEAT_VARTABS - || prev_vts != wp->w_buffer->b_p_vts_array - #endif - ) - { - prev_line = line; - prev_ts = wp->w_buffer->b_p_ts; - prev_tick = CHANGEDTICK(wp->w_buffer); - #ifdef FEAT_VARTABS - prev_vts = wp->w_buffer->b_p_vts_array; - prev_indent = get_indent_str_vtab(line, - (int)wp->w_buffer->b_p_ts, - wp->w_buffer->b_p_vts_array, wp->w_p_list); - #else - prev_indent = get_indent_str(line, - (int)wp->w_buffer->b_p_ts, wp->w_p_list); - #endif - } - bri = prev_indent + wp->w_p_brishift; - - /* indent minus the length of the showbreak string */ - if (wp->w_p_brisbr) - bri -= vim_strsize(p_sbr); - - /* Add offset for number column, if 'n' is in 'cpoptions' */ - bri += win_col_off2(wp); - - /* never indent past left window margin */ - if (bri < 0) - bri = 0; - /* always leave at least bri_min characters on the left, - * if text width is sufficient */ - else if (bri > eff_wwidth - wp->w_p_brimin) - bri = (eff_wwidth - wp->w_p_brimin < 0) - ? 0 : eff_wwidth - wp->w_p_brimin; - - return bri; - } - #endif - - /* * get_leader_len() returns the length in bytes of the prefix of the given * string which introduces a comment. If this string is not a comment then * 0 is returned. --- 25,30 ---- *************** *** 1076,1101 **** } /* - * When extra == 0: Return TRUE if the cursor is before or on the first - * non-blank in the line. - * When extra == 1: Return TRUE if the cursor is before the first non-blank in - * the line. - */ - int - inindent(int extra) - { - char_u *ptr; - colnr_T col; - - for (col = 0, ptr = ml_get_curline(); VIM_ISWHITE(*ptr); ++col) - ++ptr; - if (col >= curwin->w_cursor.col + extra) - return TRUE; - else - return FALSE; - } - - /* * Skip to next part of an option argument: Skip space and comma. */ char_u * --- 540,545 ---- *** ../vim-8.1.2126/src/ops.c 2019-10-06 22:00:08.297244105 +0200 --- src/ops.c 2019-10-09 22:10:04.870760534 +0200 *************** *** 590,679 **** State = oldstate; } - #if defined(FEAT_LISP) || defined(FEAT_CINDENT) || defined(PROTO) - /* - * op_reindent - handle reindenting a block of lines. - */ - static void - op_reindent(oparg_T *oap, int (*how)(void)) - { - long i; - char_u *l; - int amount; - linenr_T first_changed = 0; - linenr_T last_changed = 0; - linenr_T start_lnum = curwin->w_cursor.lnum; - - /* Don't even try when 'modifiable' is off. */ - if (!curbuf->b_p_ma) - { - emsg(_(e_modifiable)); - return; - } - - for (i = oap->line_count; --i >= 0 && !got_int; ) - { - /* it's a slow thing to do, so give feedback so there's no worry that - * the computer's just hung. */ - - if (i > 1 - && (i % 50 == 0 || i == oap->line_count - 1) - && oap->line_count > p_report) - smsg(_("%ld lines to indent... "), i); - - /* - * Be vi-compatible: For lisp indenting the first line is not - * indented, unless there is only one line. - */ - #ifdef FEAT_LISP - if (i != oap->line_count - 1 || oap->line_count == 1 - || how != get_lisp_indent) - #endif - { - l = skipwhite(ml_get_curline()); - if (*l == NUL) /* empty or blank line */ - amount = 0; - else - amount = how(); /* get the indent for this line */ - - 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; - last_changed = curwin->w_cursor.lnum; - } - } - ++curwin->w_cursor.lnum; - curwin->w_cursor.col = 0; /* make sure it's valid */ - } - - /* put cursor on first non-blank of indented line */ - curwin->w_cursor.lnum = start_lnum; - beginline(BL_SOL | BL_FIX); - - /* Mark changed lines so that they will be redrawn. When Visual - * highlighting was present, need to continue until the last line. When - * there is no change still need to remove the Visual highlighting. */ - if (last_changed != 0) - changed_lines(first_changed, 0, - oap->is_VIsual ? start_lnum + oap->line_count : - last_changed + 1, 0L); - else if (oap->is_VIsual) - redraw_curbuf_later(INVERTED); - - if (oap->line_count > p_report) - { - i = oap->line_count - (i + 1); - smsg(NGETTEXT("%ld line indented ", - "%ld lines indented ", i), i); - } - /* set '[ and '] marks */ - curbuf->b_op_start = oap->start; - curbuf->b_op_end = oap->end; - } - #endif /* defined(FEAT_LISP) || defined(FEAT_CINDENT) */ - /* * Stuff a string into the typeahead buffer, such that edit() will insert it * literally ("literally" TRUE) or interpret is as typed characters. --- 590,595 ---- *************** *** 1917,1945 **** } } - #if defined(FEAT_SMARTINDENT) || defined(FEAT_CINDENT) || defined(PROTO) - /* - * Return TRUE if lines starting with '#' should be left aligned. - */ - int - preprocs_left(void) - { - return - # ifdef FEAT_SMARTINDENT - # ifdef FEAT_CINDENT - (curbuf->b_p_si && !curbuf->b_p_cin) || - # else - curbuf->b_p_si - # endif - # endif - # ifdef FEAT_CINDENT - (curbuf->b_p_cin && in_cinkeys('#', ' ', TRUE) - && curbuf->b_ind_hash_comment == 0) - # endif - ; - } - #endif - /* * If "process" is TRUE and the line begins with a comment leader (possibly * after some white space), return a pointer to the text after it. Put a boolean --- 1833,1838 ---- *** ../vim-8.1.2126/src/proto.h 2019-09-28 16:29:44.171649790 +0200 --- src/proto.h 2019-10-09 22:10:04.874760503 +0200 *************** *** 67,72 **** --- 67,73 ---- # include "bufwrite.pro" # include "change.pro" # include "charset.pro" + # include "cindent.pro" # include "cmdexpand.pro" # include "cmdhist.pro" # include "if_cscope.pro" *** ../vim-8.1.2126/src/proto/cindent.pro 2019-10-09 22:52:01.988291045 +0200 --- src/proto/cindent.pro 2019-10-09 22:39:49.252353265 +0200 *************** *** 0 **** --- 1,10 ---- + /* cindent.c */ + int cin_is_cinword(char_u *line); + pos_T *find_start_comment(int ind_maxcomment); + int cindent_on(void); + void parse_cino(buf_T *buf); + int get_c_indent(void); + int in_cinkeys(int keytyped, int when, int line_is_empty); + void do_c_expr_indent(void); + void f_cindent(typval_T *argvars, typval_T *rettv); + /* vim: set ft=c : */ *** ../vim-8.1.2126/src/proto/edit.pro 2019-09-21 20:46:14.728275744 +0200 --- src/proto/edit.pro 2019-10-09 22:10:04.874760503 +0200 *************** *** 8,14 **** void edit_unputchar(void); void display_dollar(colnr_T col); void undisplay_dollar(void); - void change_indent(int type, int amount, int round, int replaced, int call_changed_bytes); void truncate_spaces(char_u *line); void backspace_until_column(int col); int get_literal(void); --- 8,13 ---- *************** *** 30,35 **** --- 29,35 ---- char_u *get_last_insert_save(void); void replace_push(int c); int replace_push_mb(char_u *p); + void replace_join(int off); int hkmap(int c); int bracketed_paste(paste_mode_T mode, int drop, garray_T *gap); void ins_scroll(void); *** ../vim-8.1.2126/src/proto/ex_cmds.pro 2019-08-20 20:13:40.330821936 +0200 --- src/proto/ex_cmds.pro 2019-10-09 22:10:04.874760503 +0200 *************** *** 2,8 **** void do_ascii(exarg_T *eap); void ex_align(exarg_T *eap); void ex_sort(exarg_T *eap); - void ex_retab(exarg_T *eap); int do_move(linenr_T line1, linenr_T line2, linenr_T dest); void ex_copy(linenr_T line1, linenr_T line2, linenr_T n); void free_prev_shellcmd(void); --- 2,7 ---- *** ../vim-8.1.2126/src/proto/indent.pro 2019-09-25 20:37:22.073343455 +0200 --- src/proto/indent.pro 2019-10-09 22:39:49.524351639 +0200 *************** *** 1,21 **** /* indent.c */ - int cin_is_cinword(char_u *line); - pos_T *find_start_comment(int ind_maxcomment); - int cindent_on(void); - void parse_cino(buf_T *buf); - int get_c_indent(void); - int get_expr_indent(void); - int in_cinkeys(int keytyped, int when, int line_is_empty); - int get_lisp_indent(void); - void do_c_expr_indent(void); - void fixthisline(int (*get_the_indent)(void)); - void fix_indent(void); int tabstop_set(char_u *var, int **array); int tabstop_padding(colnr_T col, int ts_arg, int *vts); int tabstop_at(colnr_T col, int ts, int *vts); colnr_T tabstop_start(colnr_T col, int ts, int *vts); void tabstop_fromto(colnr_T start_col, colnr_T end_col, int ts_arg, int *vts, int *ntabs, int *nspcs); - int tabstop_eq(int *ts1, int *ts2); int *tabstop_copy(int *oldts); int tabstop_count(int *ts); int tabstop_first(int *ts); --- 1,9 ---- *************** *** 23,26 **** --- 11,35 ---- long get_sw_value_indent(buf_T *buf); long get_sw_value_col(buf_T *buf, colnr_T col); long get_sts_value(void); + int get_indent(void); + int get_indent_lnum(linenr_T lnum); + int get_indent_buf(buf_T *buf, linenr_T lnum); + int get_indent_str(char_u *ptr, int ts, int list); + int get_indent_str_vtab(char_u *ptr, int ts, int *vts, int list); + int set_indent(int size, int flags); + int get_number_indent(linenr_T lnum); + int get_breakindent_win(win_T *wp, char_u *line); + int inindent(int extra); + void op_reindent(oparg_T *oap, int (*how)(void)); + int preprocs_left(void); + void ins_try_si(int c); + void change_indent(int type, int amount, int round, int replaced, int call_changed_bytes); + int copy_indent(int size, char_u *src); + void ex_retab(exarg_T *eap); + int get_expr_indent(void); + int get_lisp_indent(void); + void fixthisline(int (*get_the_indent)(void)); + void fix_indent(void); + void f_indent(typval_T *argvars, typval_T *rettv); + void f_lispindent(typval_T *argvars, typval_T *rettv); /* vim: set ft=c : */ *** ../vim-8.1.2126/src/proto/misc1.pro 2019-09-21 20:46:14.728275744 +0200 --- src/proto/misc1.pro 2019-10-09 22:10:04.874760503 +0200 *************** *** 1,12 **** /* misc1.c */ - int get_indent(void); - int get_indent_lnum(linenr_T lnum); - int get_indent_buf(buf_T *buf, linenr_T lnum); - int get_indent_str(char_u *ptr, int ts, int list); - int get_indent_str_vtab(char_u *ptr, int ts, int *vts, int list); - int set_indent(int size, int flags); - int get_number_indent(linenr_T lnum); - int get_breakindent_win(win_T *wp, char_u *line); int get_leader_len(char_u *line, char_u **flags, int backward, int include_space); int get_last_leader_offset(char_u *line, char_u **flags); int plines(linenr_T lnum); --- 1,4 ---- *************** *** 19,25 **** int gchar_pos(pos_T *pos); int gchar_cursor(void); void pchar_cursor(int c); - int inindent(int extra); char_u *skip_to_option_part(char_u *p); void check_status(buf_T *buf); int ask_yesno(char_u *str, int direct); --- 11,16 ---- *** ../vim-8.1.2126/src/proto/ops.pro 2019-09-30 23:12:10.874289153 +0200 --- src/proto/ops.pro 2019-10-09 22:10:04.874760503 +0200 *************** *** 12,18 **** void op_insert(oparg_T *oap, long count1); int op_change(oparg_T *oap); void adjust_cursor_eol(void); - int preprocs_left(void); char_u *skip_comment(char_u *line, int process, int include_space, int *is_comment); int do_join(long count, int insert_space, int save_undo, int use_formatoptions, int setmark); int fex_format(linenr_T lnum, long count, int c); --- 12,17 ---- *** ../vim-8.1.2126/src/userfunc.c 2019-09-16 22:55:57.732006874 +0200 --- src/userfunc.c 2019-10-09 22:10:04.874760503 +0200 *************** *** 8,14 **** */ /* ! * eval.c: User defined function support */ #include "vim.h" --- 8,14 ---- */ /* ! * userfunc.c: User defined function support */ #include "vim.h" *** ../vim-8.1.2126/src/version.c 2019-10-09 22:01:20.603437955 +0200 --- src/version.c 2019-10-09 22:11:37.182118833 +0200 *************** *** 755,756 **** --- 755,758 ---- { /* Add new patch number below this line */ + /**/ + 2127, /**/ -- Veni, Vidi, VW -- I came, I saw, I drove around in a little car. /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\ \\\ an exciting new programming language -- http://www.Zimbu.org /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///