diff options
Diffstat (limited to 'src/misc1.c')
-rw-r--r-- | src/misc1.c | 8455 |
1 files changed, 8455 insertions, 0 deletions
diff --git a/src/misc1.c b/src/misc1.c new file mode 100644 index 0000000000..0e0705dbd6 --- /dev/null +++ b/src/misc1.c @@ -0,0 +1,8455 @@ +/* vi:set ts=8 sts=4 sw=4: + * + * 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. + */ + +/* + * misc1.c: functions that didn't seem to fit elsewhere + */ + +#include "vim.h" +#include "version.h" + +#ifdef HAVE_FCNTL_H +# include <fcntl.h> /* for chdir() */ +#endif + +static char_u *vim_version_dir __ARGS((char_u *vimdir)); +static char_u *remove_tail __ARGS((char_u *p, char_u *pend, char_u *name)); +#if defined(USE_EXE_NAME) && defined(MACOS_X) +static char_u *remove_tail_with_ext __ARGS((char_u *p, char_u *pend, char_u *ext)); +#endif +static int get_indent_str __ARGS((char_u *ptr, int ts)); +static int copy_indent __ARGS((int size, char_u *src)); + +/* + * Count the size (in window cells) of the indent in the current line. + */ + int +get_indent() +{ + return get_indent_str(ml_get_curline(), (int)curbuf->b_p_ts); +} + +/* + * Count the size (in window cells) of the indent in line "lnum". + */ + int +get_indent_lnum(lnum) + linenr_T lnum; +{ + return get_indent_str(ml_get(lnum), (int)curbuf->b_p_ts); +} + +#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, lnum) + buf_T *buf; + linenr_T lnum; +{ + return get_indent_str(ml_get_buf(buf, lnum, FALSE), (int)buf->b_p_ts); +} +#endif + +/* + * count the size (in window cells) of the indent in line "ptr", with + * 'tabstop' at "ts" + */ + static int +get_indent_str(ptr, ts) + char_u *ptr; + int ts; +{ + int count = 0; + + for ( ; *ptr; ++ptr) + { + if (*ptr == TAB) /* count a tab for what it is worth */ + count += ts - (count % ts); + else if (*ptr == ' ') + ++count; /* count a space for one */ + else + break; + } + return (count); +} + +/* + * 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(size, flags) + int size; + int flags; +{ + char_u *p; + char_u *newline; + char_u *oldline; + char_u *s; + int todo; + int ind_len; + int line_len; + int doit = FALSE; + int ind_done; + int tab_pad; + + /* + * 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 (!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) + { + ind_done = 0; + + /* count as many characters as we can use */ + while (todo > 0 && vim_iswhite(*p)) + { + if (*p == TAB) + { + tab_pad = (int)curbuf->b_p_ts + - (ind_done % (int)curbuf->b_p_ts); + /* 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; + } + + /* Fill to next tabstop with a tab, if possible */ + tab_pad = (int)curbuf->b_p_ts - (ind_done % (int)curbuf->b_p_ts); + if (todo >= tab_pad) + { + doit = TRUE; + todo -= tab_pad; + ++ind_len; + /* ind_done += tab_pad; */ + } + } + + /* count tabs required for indent */ + 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; */ + } + } + /* 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; + newline = alloc(ind_len + line_len); + if (newline == NULL) + return FALSE; + + /* Put the characters in the new line. */ + s = newline; + todo = size; + /* 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) + { + tab_pad = (int)curbuf->b_p_ts + - (ind_done % (int)curbuf->b_p_ts); + /* 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 */ + tab_pad = (int)curbuf->b_p_ts - (ind_done % (int)curbuf->b_p_ts); + if (todo >= tab_pad) + { + *s++ = TAB; + todo -= tab_pad; + } + + p = skipwhite(p); + } + + while (todo >= (int)curbuf->b_p_ts) + { + *s++ = TAB; + todo -= (int)curbuf->b_p_ts; + } + } + 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's after the indent. */ + if (saved_cursor.lnum == curwin->w_cursor.lnum + && saved_cursor.col >= (colnr_T)(p - oldline)) + saved_cursor.col += ind_len - (p - oldline); + } + else + vim_free(newline); + + curwin->w_cursor.col = ind_len; + return TRUE; +} + +/* + * 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(size, src) + 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; + + /* 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; + s = src; + + /* Count/copy the usable portion of the source line */ + while (todo > 0 && vim_iswhite(*s)) + { + if (*s == TAB) + { + tab_pad = (int)curbuf->b_p_ts + - (ind_done % (int)curbuf->b_p_ts); + /* Stop if this tab will overshoot the target */ + if (todo < tab_pad) + break; + todo -= tab_pad; + ind_done += tab_pad; + } + else + { + --todo; + ++ind_done; + } + ++ind_len; + if (round == 2) + *p++ = *s; + ++s; + } + + /* Fill to next tabstop with a tab, if possible */ + tab_pad = (int)curbuf->b_p_ts - (ind_done % (int)curbuf->b_p_ts); + if (todo >= tab_pad) + { + todo -= tab_pad; + ++ind_len; + if (round == 2) + *p++ = TAB; + } + + /* Add tabs required for indent */ + while (todo >= (int)curbuf->b_p_ts) + { + todo -= (int)curbuf->b_p_ts; + ++ind_len; + if (round == 2) + *p++ = TAB; + } + + /* Count/add spaces required for indent */ + while (todo > 0) + { + --todo; + ++ind_len; + if (round == 2) + *p++ = ' '; + } + + if (round == 1) + { + /* 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; +} + +/* + * Return the indent of the current line after a number. Return -1 if no + * number was found. Used for 'n' in 'formatoptions': numbered list. + */ + int +get_number_indent(lnum) + linenr_T lnum; +{ + char_u *line; + char_u *p; + colnr_T col; + pos_T pos; + + if (lnum > curbuf->b_ml.ml_line_count) + return -1; + line = ml_get(lnum); + p = skipwhite(line); + if (!VIM_ISDIGIT(*p)) + return -1; + p = skipdigits(p); + if (vim_strchr((char_u *)":.)]}\t ", *p) == NULL) + return -1; + p = skipwhite(p + 1); + if (*p == NUL) + return -1; + pos.lnum = lnum; + pos.col = (colnr_T)(p - line); + getvcol(curwin, &pos, &col, NULL, NULL); + return (int)col; +} + +#if defined(FEAT_CINDENT) || defined(FEAT_SMARTINDENT) + +static int cin_is_cinword __ARGS((char_u *line)); + +/* + * Return TRUE if the string "line" starts with a word from 'cinwords'. + */ + static int +cin_is_cinword(line) + 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((unsigned)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 + +/* + * 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 + * file, otherwise we just start replacing the next line. + * + * Caller must take care of undo. Since VREPLACE may affect any number of + * lines however, it may call u_save_cursor() again when starting to change a + * new line. + * "flags": OPENLINE_DELSPACES delete spaces after cursor + * OPENLINE_DO_COM format comments + * OPENLINE_KEEPTRAIL keep trailing spaces + * OPENLINE_MARKFIX adjust mark positions after the line break + * + * Return TRUE for success, FALSE for failure + */ + int +open_line(dir, flags, old_indent) + int dir; /* FORWARD or BACKWARD */ + int flags; + int old_indent; /* indent for after ^^D in Insert mode */ +{ + char_u *saved_line; /* copy of the original line */ + char_u *next_line = NULL; /* copy of the next line */ + char_u *p_extra = NULL; /* what goes to next line */ + int less_cols = 0; /* less columns for mark in new line */ + int less_cols_off = 0; /* columns to skip for mark adjust */ + pos_T old_cursor; /* old cursor position */ + int newcol = 0; /* new cursor column */ + int newindent = 0; /* auto-indent of the new line */ + int n; + int trunc_line = FALSE; /* truncate current line afterwards */ + int retval = FALSE; /* return value, default is FAIL */ +#ifdef FEAT_COMMENTS + int extra_len = 0; /* length of p_extra string */ + int lead_len; /* length of comment leader */ + char_u *lead_flags; /* position in 'comments' for comment leader */ + char_u *leader = NULL; /* copy of comment leader */ +#endif + char_u *allocated = NULL; /* allocated memory */ +#if defined(FEAT_SMARTINDENT) || defined(FEAT_VREPLACE) || defined(FEAT_LISP) \ + || defined(FEAT_CINDENT) || defined(FEAT_COMMENTS) + char_u *p; +#endif + int saved_char = NUL; /* init for GCC */ +#if defined(FEAT_SMARTINDENT) || defined(FEAT_COMMENTS) + pos_T *pos; +#endif +#ifdef FEAT_SMARTINDENT + int do_si = (!p_paste && curbuf->b_p_si +# ifdef FEAT_CINDENT + && !curbuf->b_p_cin +# endif + ); + int no_si = FALSE; /* reset did_si afterwards */ + int first_char = NUL; /* init for GCC */ +#endif +#if defined(FEAT_VREPLACE) && (defined(FEAT_LISP) || defined(FEAT_CINDENT)) + int vreplace_mode; +#endif + int did_append; /* appended a new line */ + int saved_pi = curbuf->b_p_pi; /* copy of preserveindent setting */ + + /* + * make a copy of the current line so we can mess with it + */ + saved_line = vim_strsave(ml_get_curline()); + if (saved_line == NULL) /* out of memory! */ + return FALSE; + +#ifdef FEAT_VREPLACE + if (State & VREPLACE_FLAG) + { + /* + * With VREPLACE we make a copy of the next line, which we will be + * starting to replace. First make the new line empty and let vim play + * with the indenting and comment leader to its heart's content. Then + * we grab what it ended up putting on the new line, put back the + * original line, and call ins_char() to put each new character onto + * the line, replacing what was there before and pushing the right + * stuff onto the replace stack. -- webb. + */ + if (curwin->w_cursor.lnum < orig_line_count) + next_line = vim_strsave(ml_get(curwin->w_cursor.lnum + 1)); + else + next_line = vim_strsave((char_u *)""); + if (next_line == NULL) /* out of memory! */ + goto theend; + + /* + * In VREPLACE mode, a NL replaces the rest of the line, and starts + * replacing the next line, so push all of the characters left on the + * line onto the replace stack. We'll push any other characters that + * might be replaced at the start of the next line (due to autoindent + * etc) a bit later. + */ + replace_push(NUL); /* Call twice because BS over NL expects it */ + replace_push(NUL); + p = saved_line + curwin->w_cursor.col; + while (*p != NUL) + replace_push(*p++); + saved_line[curwin->w_cursor.col] = NUL; + } +#endif + + if ((State & INSERT) +#ifdef FEAT_VREPLACE + && !(State & VREPLACE_FLAG) +#endif + ) + { + p_extra = saved_line + curwin->w_cursor.col; +#ifdef FEAT_SMARTINDENT + if (do_si) /* need first char after new line break */ + { + p = skipwhite(p_extra); + first_char = *p; + } +#endif +#ifdef FEAT_COMMENTS + extra_len = (int)STRLEN(p_extra); +#endif + saved_char = *p_extra; + *p_extra = NUL; + } + + u_clearline(); /* cannot do "U" command when adding lines */ +#ifdef FEAT_SMARTINDENT + did_si = FALSE; +#endif + ai_col = 0; + + /* + * If we just did an auto-indent, then we didn't type anything on + * the prior line, and it should be truncated. Do this even if 'ai' is not + * set because automatically inserting a comment leader also sets did_ai. + */ + if (dir == FORWARD && did_ai) + trunc_line = TRUE; + + /* + * If 'autoindent' and/or 'smartindent' is set, try to figure out what + * indent to use for the new line. + */ + if (curbuf->b_p_ai +#ifdef FEAT_SMARTINDENT + || do_si +#endif + ) + { + /* + * count white space on current line + */ + newindent = get_indent_str(saved_line, (int)curbuf->b_p_ts); + if (newindent == 0) + newindent = old_indent; /* for ^^D command in insert mode */ + +#ifdef FEAT_SMARTINDENT + /* + * Do smart indenting. + * In insert/replace mode (only when dir == FORWARD) + * we may move some text to the next line. If it starts with '{' + * don't add an indent. Fixes inserting a NL before '{' in line + * "if (condition) {" + */ + if (!trunc_line && do_si && *saved_line != NUL + && (p_extra == NULL || first_char != '{')) + { + char_u *ptr; + char_u last_char; + + old_cursor = curwin->w_cursor; + ptr = saved_line; +# ifdef FEAT_COMMENTS + if (flags & OPENLINE_DO_COM) + lead_len = get_leader_len(ptr, NULL, FALSE); + else + lead_len = 0; +# endif + if (dir == FORWARD) + { + /* + * Skip preprocessor directives, unless they are + * recognised as comments. + */ + if ( +# ifdef FEAT_COMMENTS + lead_len == 0 && +# endif + ptr[0] == '#') + { + while (ptr[0] == '#' && curwin->w_cursor.lnum > 1) + ptr = ml_get(--curwin->w_cursor.lnum); + newindent = get_indent(); + } +# ifdef FEAT_COMMENTS + if (flags & OPENLINE_DO_COM) + lead_len = get_leader_len(ptr, NULL, FALSE); + else + lead_len = 0; + if (lead_len > 0) + { + /* + * This case gets the following right: + * \* + * * A comment (read '\' as '/'). + * *\ + * #define IN_THE_WAY + * This should line up here; + */ + p = skipwhite(ptr); + if (p[0] == '/' && p[1] == '*') + p++; + if (p[0] == '*') + { + for (p++; *p; p++) + { + if (p[0] == '/' && p[-1] == '*') + { + /* + * End of C comment, indent should line up + * with the line containing the start of + * the comment + */ + curwin->w_cursor.col = (colnr_T)(p - ptr); + if ((pos = findmatch(NULL, NUL)) != NULL) + { + curwin->w_cursor.lnum = pos->lnum; + newindent = get_indent(); + } + } + } + } + } + else /* Not a comment line */ +# endif + { + /* Find last non-blank in line */ + p = ptr + STRLEN(ptr) - 1; + while (p > ptr && vim_iswhite(*p)) + --p; + last_char = *p; + + /* + * find the character just before the '{' or ';' + */ + if (last_char == '{' || last_char == ';') + { + if (p > ptr) + --p; + while (p > ptr && vim_iswhite(*p)) + --p; + } + /* + * Try to catch lines that are split over multiple + * lines. eg: + * if (condition && + * condition) { + * Should line up here! + * } + */ + if (*p == ')') + { + curwin->w_cursor.col = (colnr_T)(p - ptr); + if ((pos = findmatch(NULL, '(')) != NULL) + { + curwin->w_cursor.lnum = pos->lnum; + newindent = get_indent(); + ptr = ml_get_curline(); + } + } + /* + * If last character is '{' do indent, without + * checking for "if" and the like. + */ + if (last_char == '{') + { + did_si = TRUE; /* do indent */ + no_si = TRUE; /* don't delete it when '{' typed */ + } + /* + * Look for "if" and the like, use 'cinwords'. + * Don't do this if the previous line ended in ';' or + * '}'. + */ + else if (last_char != ';' && last_char != '}' + && cin_is_cinword(ptr)) + did_si = TRUE; + } + } + else /* dir == BACKWARD */ + { + /* + * Skip preprocessor directives, unless they are + * recognised as comments. + */ + if ( +# ifdef FEAT_COMMENTS + lead_len == 0 && +# endif + ptr[0] == '#') + { + int was_backslashed = FALSE; + + while ((ptr[0] == '#' || was_backslashed) && + curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count) + { + if (*ptr && ptr[STRLEN(ptr) - 1] == '\\') + was_backslashed = TRUE; + else + was_backslashed = FALSE; + ptr = ml_get(++curwin->w_cursor.lnum); + } + if (was_backslashed) + newindent = 0; /* Got to end of file */ + else + newindent = get_indent(); + } + p = skipwhite(ptr); + if (*p == '}') /* if line starts with '}': do indent */ + did_si = TRUE; + else /* can delete indent when '{' typed */ + can_si_back = TRUE; + } + curwin->w_cursor = old_cursor; + } + if (do_si) + can_si = TRUE; +#endif /* FEAT_SMARTINDENT */ + + did_ai = TRUE; + } + +#ifdef FEAT_COMMENTS + /* + * Find out if the current line starts with a comment leader. + * This may then be inserted in front of the new line. + */ + end_comment_pending = NUL; + if (flags & OPENLINE_DO_COM) + lead_len = get_leader_len(saved_line, &lead_flags, dir == BACKWARD); + else + lead_len = 0; + if (lead_len > 0) + { + char_u *lead_repl = NULL; /* replaces comment leader */ + int lead_repl_len = 0; /* length of *lead_repl */ + char_u lead_middle[COM_MAX_LEN]; /* middle-comment string */ + char_u lead_end[COM_MAX_LEN]; /* end-comment string */ + char_u *comment_end = NULL; /* where lead_end has been found */ + int extra_space = FALSE; /* append extra space */ + int current_flag; + int require_blank = FALSE; /* requires blank after middle */ + char_u *p2; + + /* + * If the comment leader has the start, middle or end flag, it may not + * be used or may be replaced with the middle leader. + */ + for (p = lead_flags; *p && *p != ':'; ++p) + { + if (*p == COM_BLANK) + { + require_blank = TRUE; + continue; + } + if (*p == COM_START || *p == COM_MIDDLE) + { + current_flag = *p; + if (*p == COM_START) + { + /* + * Doing "O" on a start of comment does not insert leader. + */ + if (dir == BACKWARD) + { + lead_len = 0; + break; + } + + /* find start of middle part */ + (void)copy_option_part(&p, lead_middle, COM_MAX_LEN, ","); + require_blank = FALSE; + } + + /* + * Isolate the strings of the middle and end leader. + */ + while (*p && p[-1] != ':') /* find end of middle flags */ + { + if (*p == COM_BLANK) + require_blank = TRUE; + ++p; + } + (void)copy_option_part(&p, lead_middle, COM_MAX_LEN, ","); + + while (*p && p[-1] != ':') /* find end of end flags */ + { + /* Check whether we allow automatic ending of comments */ + if (*p == COM_AUTO_END) + end_comment_pending = -1; /* means we want to set it */ + ++p; + } + n = copy_option_part(&p, lead_end, COM_MAX_LEN, ","); + + if (end_comment_pending == -1) /* we can set it now */ + end_comment_pending = lead_end[n - 1]; + + /* + * If the end of the comment is in the same line, don't use + * the comment leader. + */ + if (dir == FORWARD) + { + for (p = saved_line + lead_len; *p; ++p) + if (STRNCMP(p, lead_end, n) == 0) + { + comment_end = p; + lead_len = 0; + break; + } + } + + /* + * Doing "o" on a start of comment inserts the middle leader. + */ + if (lead_len > 0) + { + if (current_flag == COM_START) + { + lead_repl = lead_middle; + lead_repl_len = (int)STRLEN(lead_middle); + } + + /* + * If we have hit RETURN immediately after the start + * comment leader, then put a space after the middle + * comment leader on the next line. + */ + if (!vim_iswhite(saved_line[lead_len - 1]) + && ((p_extra != NULL + && (int)curwin->w_cursor.col == lead_len) + || (p_extra == NULL + && saved_line[lead_len] == NUL) + || require_blank)) + extra_space = TRUE; + } + break; + } + if (*p == COM_END) + { + /* + * Doing "o" on the end of a comment does not insert leader. + * Remember where the end is, might want to use it to find the + * start (for C-comments). + */ + if (dir == FORWARD) + { + comment_end = skipwhite(saved_line); + lead_len = 0; + break; + } + + /* + * Doing "O" on the end of a comment inserts the middle leader. + * Find the string for the middle leader, searching backwards. + */ + while (p > curbuf->b_p_com && *p != ',') + --p; + for (lead_repl = p; lead_repl > curbuf->b_p_com + && lead_repl[-1] != ':'; --lead_repl) + ; + lead_repl_len = (int)(p - lead_repl); + + /* We can probably always add an extra space when doing "O" on + * the comment-end */ + extra_space = TRUE; + + /* Check whether we allow automatic ending of comments */ + for (p2 = p; *p2 && *p2 != ':'; p2++) + { + if (*p2 == COM_AUTO_END) + end_comment_pending = -1; /* means we want to set it */ + } + if (end_comment_pending == -1) + { + /* Find last character in end-comment string */ + while (*p2 && *p2 != ',') + p2++; + end_comment_pending = p2[-1]; + } + break; + } + if (*p == COM_FIRST) + { + /* + * Comment leader for first line only: Don't repeat leader + * when using "O", blank out leader when using "o". + */ + if (dir == BACKWARD) + lead_len = 0; + else + { + lead_repl = (char_u *)""; + lead_repl_len = 0; + } + break; + } + } + if (lead_len) + { + /* allocate buffer (may concatenate p_exta later) */ + leader = alloc(lead_len + lead_repl_len + extra_space + + extra_len + 1); + allocated = leader; /* remember to free it later */ + + if (leader == NULL) + lead_len = 0; + else + { + STRNCPY(leader, saved_line, lead_len); + leader[lead_len] = NUL; + + /* + * Replace leader with lead_repl, right or left adjusted + */ + if (lead_repl != NULL) + { + int c = 0; + int off = 0; + + for (p = lead_flags; *p && *p != ':'; ++p) + { + if (*p == COM_RIGHT || *p == COM_LEFT) + c = *p; + else if (VIM_ISDIGIT(*p) || *p == '-') + off = getdigits(&p); + } + if (c == COM_RIGHT) /* right adjusted leader */ + { + /* find last non-white in the leader to line up with */ + for (p = leader + lead_len - 1; p > leader + && vim_iswhite(*p); --p) + ; + + ++p; + if (p < leader + lead_repl_len) + p = leader; + else + p -= lead_repl_len; + mch_memmove(p, lead_repl, (size_t)lead_repl_len); + if (p + lead_repl_len > leader + lead_len) + p[lead_repl_len] = NUL; + + /* blank-out any other chars from the old leader. */ + while (--p >= leader) + if (!vim_iswhite(*p)) + *p = ' '; + } + else /* left adjusted leader */ + { + p = skipwhite(leader); + mch_memmove(p, lead_repl, (size_t)lead_repl_len); + + /* Replace any remaining non-white chars in the old + * leader by spaces. Keep Tabs, the indent must + * remain the same. */ + for (p += lead_repl_len; p < leader + lead_len; ++p) + if (!vim_iswhite(*p)) + { + /* Don't put a space before a TAB. */ + if (p + 1 < leader + lead_len && p[1] == TAB) + { + --lead_len; + mch_memmove(p, p + 1, + (leader + lead_len) - p); + } + else + *p = ' '; + } + *p = NUL; + } + + /* Recompute the indent, it may have changed. */ + if (curbuf->b_p_ai +#ifdef FEAT_SMARTINDENT + || do_si +#endif + ) + newindent = get_indent_str(leader, (int)curbuf->b_p_ts); + + /* Add the indent offset */ + if (newindent + off < 0) + { + off = -newindent; + newindent = 0; + } + else + newindent += off; + + /* Correct trailing spaces for the shift, so that + * alignment remains equal. */ + while (off > 0 && lead_len > 0 + && leader[lead_len - 1] == ' ') + { + /* Don't do it when there is a tab before the space */ + if (vim_strchr(skipwhite(leader), '\t') != NULL) + break; + --lead_len; + --off; + } + + /* If the leader ends in white space, don't add an + * extra space */ + if (lead_len > 0 && vim_iswhite(leader[lead_len - 1])) + extra_space = FALSE; + leader[lead_len] = NUL; + } + + if (extra_space) + { + leader[lead_len++] = ' '; + leader[lead_len] = NUL; + } + + newcol = lead_len; + + /* + * if a new indent will be set below, remove the indent that + * is in the comment leader + */ + if (newindent +#ifdef FEAT_SMARTINDENT + || did_si +#endif + ) + { + while (lead_len && vim_iswhite(*leader)) + { + --lead_len; + --newcol; + ++leader; + } + } + + } +#ifdef FEAT_SMARTINDENT + did_si = can_si = FALSE; +#endif + } + else if (comment_end != NULL) + { + /* + * We have finished a comment, so we don't use the leader. + * If this was a C-comment and 'ai' or 'si' is set do a normal + * indent to align with the line containing the start of the + * comment. + */ + if (comment_end[0] == '*' && comment_end[1] == '/' && + (curbuf->b_p_ai +#ifdef FEAT_SMARTINDENT + || do_si +#endif + )) + { + old_cursor = curwin->w_cursor; + curwin->w_cursor.col = (colnr_T)(comment_end - saved_line); + if ((pos = findmatch(NULL, NUL)) != NULL) + { + curwin->w_cursor.lnum = pos->lnum; + newindent = get_indent(); + } + curwin->w_cursor = old_cursor; + } + } + } +#endif + + /* (State == INSERT || State == REPLACE), only when dir == FORWARD */ + if (p_extra != NULL) + { + *p_extra = saved_char; /* restore char that NUL replaced */ + + /* + * When 'ai' set or "flags" has OPENLINE_DELSPACES, skip to the first + * non-blank. + * + * When in REPLACE mode, put the deleted blanks on the replace stack, + * preceded by a NUL, so they can be put back when a BS is entered. + */ + if (REPLACE_NORMAL(State)) + replace_push(NUL); /* end of extra blanks */ + if (curbuf->b_p_ai || (flags & OPENLINE_DELSPACES)) + { + while ((*p_extra == ' ' || *p_extra == '\t') +#ifdef FEAT_MBYTE + && (!enc_utf8 + || !utf_iscomposing(utf_ptr2char(p_extra + 1))) +#endif + ) + { + if (REPLACE_NORMAL(State)) + replace_push(*p_extra); + ++p_extra; + ++less_cols_off; + } + } + if (*p_extra != NUL) + did_ai = FALSE; /* append some text, don't truncate now */ + + /* columns for marks adjusted for removed columns */ + less_cols = (int)(p_extra - saved_line); + } + + if (p_extra == NULL) + p_extra = (char_u *)""; /* append empty line */ + +#ifdef FEAT_COMMENTS + /* concatenate leader and p_extra, if there is a leader */ + if (lead_len) + { + STRCAT(leader, p_extra); + p_extra = leader; + did_ai = TRUE; /* So truncating blanks works with comments */ + less_cols -= lead_len; + } + else + end_comment_pending = NUL; /* turns out there was no leader */ +#endif + + old_cursor = curwin->w_cursor; + if (dir == BACKWARD) + --curwin->w_cursor.lnum; +#ifdef FEAT_VREPLACE + if (!(State & VREPLACE_FLAG) || old_cursor.lnum >= orig_line_count) +#endif + { + if (ml_append(curwin->w_cursor.lnum, p_extra, (colnr_T)0, FALSE) + == FAIL) + goto theend; + /* Postpone calling changed_lines(), because it would mess up folding + * with markers. */ + mark_adjust(curwin->w_cursor.lnum + 1, (linenr_T)MAXLNUM, 1L, 0L); + did_append = TRUE; + } +#ifdef FEAT_VREPLACE + else + { + /* + * In VREPLACE mode we are starting to replace the next line. + */ + curwin->w_cursor.lnum++; + if (curwin->w_cursor.lnum >= Insstart.lnum + vr_lines_changed) + { + /* In case we NL to a new line, BS to the previous one, and NL + * again, we don't want to save the new line for undo twice. + */ + (void)u_save_cursor(); /* errors are ignored! */ + vr_lines_changed++; + } + ml_replace(curwin->w_cursor.lnum, p_extra, TRUE); + changed_bytes(curwin->w_cursor.lnum, 0); + curwin->w_cursor.lnum--; + did_append = FALSE; + } +#endif + + if (newindent +#ifdef FEAT_SMARTINDENT + || did_si +#endif + ) + { + ++curwin->w_cursor.lnum; +#ifdef FEAT_SMARTINDENT + if (did_si) + { + if (p_sr) + newindent -= newindent % (int)curbuf->b_p_sw; + newindent += (int)curbuf->b_p_sw; + } +#endif + /* Copy the indent only if expand tab is disabled */ + if (curbuf->b_p_ci && !curbuf->b_p_et) + { + (void)copy_indent(newindent, saved_line); + + /* + * Set the 'preserveindent' option so that any further screwing + * with the line doesn't entirely destroy our efforts to preserve + * it. It gets restored |