summaryrefslogtreecommitdiffstats
path: root/src/misc1.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/misc1.c')
-rw-r--r--src/misc1.c8455
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