updated for version 7.0197
diff --git a/src/buffer.c b/src/buffer.c
index 6d654b2..eb8799c 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -1700,6 +1700,9 @@
     clear_string_option(&buf->b_p_inde);
     clear_string_option(&buf->b_p_indk);
 #endif
+#if defined(FEAT_EVAL)
+    clear_string_option(&buf->b_p_fex);
+#endif
 #ifdef FEAT_CRYPT
     clear_string_option(&buf->b_p_key);
 #endif
@@ -2840,9 +2843,12 @@
 }
 #endif
 
+/*
+ * Print info about the current buffer.
+ */
     void
 fileinfo(fullname, shorthelp, dont_truncate)
-    int fullname;
+    int fullname;	    /* when non-zero print full path */
     int shorthelp;
     int	dont_truncate;
 {
@@ -3476,7 +3482,7 @@
 	    else
 	    {
 		t = (opt == STL_FULLPATH) ? wp->w_buffer->b_ffname
-					: wp->w_buffer->b_fname;
+					  : wp->w_buffer->b_fname;
 		home_replace(wp->w_buffer, t, NameBuff, MAXPATHL, TRUE);
 	    }
 	    trans_characters(NameBuff, MAXPATHL);
diff --git a/src/edit.c b/src/edit.c
index 18a1cf1..9224c58 100644
--- a/src/edit.c
+++ b/src/edit.c
@@ -130,6 +130,7 @@
 static int  pum_wanted __ARGS((void));
 static int  pum_two_or_more __ARGS((void));
 static void ins_compl_dictionaries __ARGS((char_u *dict, char_u *pat, int flags, int thesaurus));
+static char_u *find_line_end __ARGS((char_u *ptr));
 static void ins_compl_free __ARGS((void));
 static void ins_compl_clear __ARGS((void));
 static int  ins_compl_bs __ARGS((void));
@@ -157,6 +158,7 @@
 static void ins_ctrl_v __ARGS((void));
 static void undisplay_dollar __ARGS((void));
 static void insert_special __ARGS((int, int, int));
+static void internal_format __ARGS((int textwidth, int second_indent, int flags, int format_only));
 static void check_auto_format __ARGS((int));
 static void redo_literal __ARGS((int c));
 static void start_arrow __ARGS((pos_T *end_insert_pos));
@@ -2184,13 +2186,13 @@
 	    if (has_mbyte)
 	    {
 		l = mb_ptr2len(p);
-		if (STRNCMP(p, s, l) != 0)
+		if (STRNICMP(p, s, l) != 0)
 			break;
 	    }
 	    else
 #endif
 	    {
-		if (*p != *s)
+		if (MB_TOLOWER(*p) != MB_TOLOWER(*s))
 		    break;
 		l = 1;
 	    }
@@ -2471,7 +2473,7 @@
 ins_compl_dictionaries(dict, pat, flags, thesaurus)
     char_u	*dict;
     char_u	*pat;
-    int		flags;
+    int		flags;		/* DICT_FIRST and/or DICT_EXACT */
     int		thesaurus;
 {
     char_u	*ptr;
@@ -2490,7 +2492,23 @@
     save_p_scs = p_scs;
     if (curbuf->b_p_inf)
 	p_scs = FALSE;
-    regmatch.regprog = vim_regcomp(pat, p_magic ? RE_MAGIC : 0);
+
+    /* When invoked to match whole lines for CTRL-X CTRL-L adjust the pattern
+     * to only match at the start of a line.  Otherwise just match the
+     * pattern. */
+    if (ctrl_x_mode == CTRL_X_WHOLE_LINE)
+    {
+	i = STRLEN(pat) + 8;
+	ptr = alloc(i);
+	if (ptr == NULL)
+	    return;
+	vim_snprintf((char *)ptr, i, "^\\s*\\zs%s", pat);
+	regmatch.regprog = vim_regcomp(ptr, p_magic ? RE_MAGIC : 0);
+	vim_free(ptr);
+    }
+    else
+	regmatch.regprog = vim_regcomp(pat, p_magic ? RE_MAGIC : 0);
+
     /* ignore case depends on 'ignorecase', 'smartcase' and "pat" */
     regmatch.rm_ic = ignorecase(pat);
     while (buf != NULL && regmatch.regprog != NULL && *dict != NUL
@@ -2537,7 +2555,10 @@
 		    while (vim_regexec(&regmatch, buf, (colnr_T)(ptr - buf)))
 		    {
 			ptr = regmatch.startp[0];
-			ptr = find_word_end(ptr);
+			if (ctrl_x_mode == CTRL_X_WHOLE_LINE)
+			    ptr = find_line_end(ptr);
+			else
+			    ptr = find_word_end(ptr);
 			add_r = ins_compl_add_infercase(regmatch.startp[0],
 					      (int)(ptr - regmatch.startp[0]),
 							    files[i], dir, 0);
@@ -2653,6 +2674,22 @@
 }
 
 /*
+ * Find the end of the line, omitting CR and NL at the end.
+ * Returns a pointer to just after the line.
+ */
+    static char_u *
+find_line_end(ptr)
+    char_u	*ptr;
+{
+    char_u	*s;
+
+    s = ptr + STRLEN(ptr);
+    while (s > ptr && (s[-1] == CAR || s[-1] == NL))
+	--s;
+    return s;
+}
+
+/*
  * Free the list of completions
  */
     static void
@@ -3102,9 +3139,7 @@
 			? buf->b_p_bl
 			: (!buf->b_p_bl
 			    || (buf->b_ml.ml_mfp == NULL) != (flag == 'u')))
-		    || buf->b_scanned
-		    || (buf->b_ml.ml_mfp == NULL
-			&& ctrl_x_mode == CTRL_X_WHOLE_LINE)))
+		    || buf->b_scanned))
 	    ;
     return buf;
 }
@@ -3176,8 +3211,8 @@
  * Get the next expansion(s), using "compl_pattern".
  * The search starts at position "ini" in curbuf and in the direction
  * compl_direction.
- * When "compl_started" is FALSE start at that position, otherwise
- * continue where we stopped searching before.
+ * When "compl_started" is FALSE start at that position, otherwise continue
+ * where we stopped searching before.
  * This may return before finding all the matches.
  * Return the total number of matches or -1 if still unknown -- Acevedo
  */
@@ -3432,7 +3467,7 @@
 								     RE_LAST);
 		if (!compl_started)
 		{
-		    /* set compl_started even on fail */
+		    /* set "compl_started" even on fail */
 		    compl_started = TRUE;
 		    first_match_pos = *pos;
 		    last_match_pos = *pos;
@@ -4242,8 +4277,7 @@
     /*
      * Find next match (and following matches).
      */
-    n = ins_compl_next(TRUE, ins_compl_key2count(c),
-						c != K_UP && c != K_DOWN);
+    n = ins_compl_next(TRUE, ins_compl_key2count(c), c != K_UP && c != K_DOWN);
 
     /* may undisplay the popup menu */
     ins_compl_upd_pum();
@@ -4671,29 +4705,14 @@
     int		flags;			/* INSCHAR_FORMAT, etc. */
     int		second_indent;		/* indent for second line if >= 0 */
 {
-    int		haveto_redraw = FALSE;
     int		textwidth;
 #ifdef FEAT_COMMENTS
-    colnr_T	leader_len;
     char_u	*p;
-    int		no_leader = FALSE;
-    int		do_comments = (flags & INSCHAR_DO_COM);
 #endif
-    int		fo_white_par;
-    int		first_line = TRUE;
     int		fo_ins_blank;
-#ifdef FEAT_MBYTE
-    int		fo_multibyte;
-#endif
-    int		save_char = NUL;
-    int		cc;
 
     textwidth = comp_textwidth(flags & INSCHAR_FORMAT);
     fo_ins_blank = has_format_option(FO_INS_BLANK);
-#ifdef FEAT_MBYTE
-    fo_multibyte = has_format_option(FO_MBYTE_BREAK);
-#endif
-    fo_white_par = has_format_option(FO_WHITE_PAR);
 
     /*
      * Try to break the line in two or more pieces when:
@@ -4710,7 +4729,7 @@
      *	    - 'formatoptions' doesn't have 'b' or a blank was inserted at or
      *	      before 'textwidth'
      */
-    if (textwidth
+    if (textwidth > 0
 	    && ((flags & INSCHAR_FORMAT)
 		|| (!vim_iswhite(c)
 		    && !((State & REPLACE_FLAG)
@@ -4725,288 +4744,15 @@
 				|| Insstart_blank_vcol <= (colnr_T)textwidth
 			    ))))))
     {
-	/*
-	 * When 'ai' is off we don't want a space under the cursor to be
-	 * deleted.  Replace it with an 'x' temporarily.
-	 */
-	if (!curbuf->b_p_ai)
-	{
-	    cc = gchar_cursor();
-	    if (vim_iswhite(cc))
-	    {
-		save_char = cc;
-		pchar_cursor('x');
-	    }
-	}
-
-	/*
-	 * Repeat breaking lines, until the current line is not too long.
-	 */
-	while (!got_int)
-	{
-	    int		startcol;		/* Cursor column at entry */
-	    int		wantcol;		/* column at textwidth border */
-	    int		foundcol;		/* column for start of spaces */
-	    int		end_foundcol = 0;	/* column for start of word */
-	    colnr_T	len;
-	    colnr_T	virtcol;
-#ifdef FEAT_VREPLACE
-	    int		orig_col = 0;
-	    char_u	*saved_text = NULL;
+	/* Format with 'formatexpr' when it's set.  Use internal formatting
+	 * when 'formatexpr' isn't set or it returns non-zero. */
+#if defined(FEAT_EVAL)
+	if (*curbuf->b_p_fex == NUL
+				|| fex_format(curwin->w_cursor.lnum, 1L) != 0)
 #endif
-	    colnr_T	col;
-
-	    virtcol = get_nolist_virtcol();
-	    if (virtcol < (colnr_T)textwidth)
-		break;
-
-#ifdef FEAT_COMMENTS
-	    if (no_leader)
-		do_comments = FALSE;
-	    else if (!(flags & INSCHAR_FORMAT)
-					   && has_format_option(FO_WRAP_COMS))
-		do_comments = TRUE;
-
-	    /* Don't break until after the comment leader */
-	    if (do_comments)
-		leader_len = get_leader_len(ml_get_curline(), NULL, FALSE);
-	    else
-		leader_len = 0;
-
-	    /* If the line doesn't start with a comment leader, then don't
-	     * start one in a following broken line.  Avoids that a %word
-	     * moved to the start of the next line causes all following lines
-	     * to start with %. */
-	    if (leader_len == 0)
-		no_leader = TRUE;
-#endif
-	    if (!(flags & INSCHAR_FORMAT)
-#ifdef FEAT_COMMENTS
-		    && leader_len == 0
-#endif
-		    && !has_format_option(FO_WRAP))
-
-	    {
-		textwidth = 0;
-		break;
-	    }
-	    if ((startcol = curwin->w_cursor.col) == 0)
-		break;
-
-	    /* find column of textwidth border */
-	    coladvance((colnr_T)textwidth);
-	    wantcol = curwin->w_cursor.col;
-
-	    curwin->w_cursor.col = startcol - 1;
-#ifdef FEAT_MBYTE
-	    /* Correct cursor for multi-byte character. */
-	    if (has_mbyte)
-		mb_adjust_cursor();
-#endif
-	    foundcol = 0;
-
-	    /*
-	     * Find position to break at.
-	     * Stop at first entered white when 'formatoptions' has 'v'
-	     */
-	    while ((!fo_ins_blank && !has_format_option(FO_INS_VI))
-			|| curwin->w_cursor.lnum != Insstart.lnum
-			|| curwin->w_cursor.col >= Insstart.col)
-	    {
-		cc = gchar_cursor();
-		if (WHITECHAR(cc))
-		{
-		    /* remember position of blank just before text */
-		    end_foundcol = curwin->w_cursor.col;
-
-		    /* find start of sequence of blanks */
-		    while (curwin->w_cursor.col > 0 && WHITECHAR(cc))
-		    {
-			dec_cursor();
-			cc = gchar_cursor();
-		    }
-		    if (curwin->w_cursor.col == 0 && WHITECHAR(cc))
-			break;		/* only spaces in front of text */
-#ifdef FEAT_COMMENTS
-		    /* Don't break until after the comment leader */
-		    if (curwin->w_cursor.col < leader_len)
-			break;
-#endif
-		    if (has_format_option(FO_ONE_LETTER))
-		    {
-			/* do not break after one-letter words */
-			if (curwin->w_cursor.col == 0)
-			    break;	/* one-letter word at begin */
-
-			col = curwin->w_cursor.col;
-			dec_cursor();
-			cc = gchar_cursor();
-
-			if (WHITECHAR(cc))
-			    continue;	/* one-letter, continue */
-			curwin->w_cursor.col = col;
-		    }
-#ifdef FEAT_MBYTE
-		    if (has_mbyte)
-			foundcol = curwin->w_cursor.col
-					     + (*mb_ptr2len)(ml_get_cursor());
-		    else
-#endif
-			foundcol = curwin->w_cursor.col + 1;
-		    if (curwin->w_cursor.col < (colnr_T)wantcol)
-			break;
-		}
-#ifdef FEAT_MBYTE
-		else if (cc >= 0x100 && fo_multibyte
-				  && curwin->w_cursor.col <= (colnr_T)wantcol)
-		{
-		    /* Break after or before a multi-byte character. */
-		    foundcol = curwin->w_cursor.col;
-		    if (curwin->w_cursor.col < (colnr_T)wantcol)
-			foundcol += (*mb_char2len)(cc);
-		    end_foundcol = foundcol;
-		    break;
-		}
-#endif
-		if (curwin->w_cursor.col == 0)
-		    break;
-		dec_cursor();
-	    }
-
-	    if (foundcol == 0)		/* no spaces, cannot break line */
-	    {
-		curwin->w_cursor.col = startcol;
-		break;
-	    }
-
-	    /* Going to break the line, remove any "$" now. */
-	    undisplay_dollar();
-
-	    /*
-	     * Offset between cursor position and line break is used by replace
-	     * stack functions.  VREPLACE does not use this, and backspaces
-	     * over the text instead.
-	     */
-#ifdef FEAT_VREPLACE
-	    if (State & VREPLACE_FLAG)
-		orig_col = startcol;	/* Will start backspacing from here */
-	    else
-#endif
-		replace_offset = startcol - end_foundcol - 1;
-
-	    /*
-	     * adjust startcol for spaces that will be deleted and
-	     * characters that will remain on top line
-	     */
-	    curwin->w_cursor.col = foundcol;
-	    while (cc = gchar_cursor(), WHITECHAR(cc))
-		inc_cursor();
-	    startcol -= curwin->w_cursor.col;
-	    if (startcol < 0)
-		startcol = 0;
-
-#ifdef FEAT_VREPLACE
-	    if (State & VREPLACE_FLAG)
-	    {
-		/*
-		 * In VREPLACE mode, we will backspace over the text to be
-		 * wrapped, so save a copy now to put on the next line.
-		 */
-		saved_text = vim_strsave(ml_get_cursor());
-		curwin->w_cursor.col = orig_col;
-		if (saved_text == NULL)
-		    break;	/* Can't do it, out of memory */
-		saved_text[startcol] = NUL;
-
-		/* Backspace over characters that will move to the next line */
-		if (!fo_white_par)
-		    backspace_until_column(foundcol);
-	    }
-	    else
-#endif
-	    {
-		/* put cursor after pos. to break line */
-		if (!fo_white_par)
-		    curwin->w_cursor.col = foundcol;
-	    }
-
-	    /*
-	     * Split the line just before the margin.
-	     * Only insert/delete lines, but don't really redraw the window.
-	     */
-	    open_line(FORWARD, OPENLINE_DELSPACES + OPENLINE_MARKFIX
-		    + (fo_white_par ? OPENLINE_KEEPTRAIL : 0)
-#ifdef FEAT_COMMENTS
-		    + (do_comments ? OPENLINE_DO_COM : 0)
-#endif
-		    , old_indent);
-	    old_indent = 0;
-
-	    replace_offset = 0;
-	    if (first_line)
-	    {
-		if (second_indent < 0 && has_format_option(FO_Q_NUMBER))
-		    second_indent = get_number_indent(curwin->w_cursor.lnum -1);
-		if (second_indent >= 0)
-		{
-#ifdef FEAT_VREPLACE
-		    if (State & VREPLACE_FLAG)
-			change_indent(INDENT_SET, second_indent, FALSE, NUL);
-		    else
-#endif
-			(void)set_indent(second_indent, SIN_CHANGED);
-		}
-		first_line = FALSE;
-	    }
-
-#ifdef FEAT_VREPLACE
-	    if (State & VREPLACE_FLAG)
-	    {
-		/*
-		 * In VREPLACE mode we have backspaced over the text to be
-		 * moved, now we re-insert it into the new line.
-		 */
-		ins_bytes(saved_text);
-		vim_free(saved_text);
-	    }
-	    else
-#endif
-	    {
-		/*
-		 * Check if cursor is not past the NUL off the line, cindent
-		 * may have added or removed indent.
-		 */
-		curwin->w_cursor.col += startcol;
-		len = (colnr_T)STRLEN(ml_get_curline());
-		if (curwin->w_cursor.col > len)
-		    curwin->w_cursor.col = len;
-	    }
-
-	    haveto_redraw = TRUE;
-#ifdef FEAT_CINDENT
-	    can_cindent = TRUE;
-#endif
-	    /* moved the cursor, don't autoindent or cindent now */
-	    did_ai = FALSE;
-#ifdef FEAT_SMARTINDENT
-	    did_si = FALSE;
-	    can_si = FALSE;
-	    can_si_back = FALSE;
-#endif
-	    line_breakcheck();
-	}
-
-	if (save_char)			/* put back space after cursor */
-	    pchar_cursor(save_char);
-
-	if (c == NUL)			/* formatting only */
-	    return;
-	if (haveto_redraw)
-	{
-	    update_topline();
-	    redraw_curbuf_later(VALID);
-	}
+	    internal_format(textwidth, second_indent, flags, c == NUL);
     }
+
     if (c == NUL)	    /* only formatting was wanted */
 	return;
 
@@ -5104,7 +4850,7 @@
 
 	buf[0] = c;
 	i = 1;
-	if (textwidth)
+	if (textwidth > 0)
 	    virtcol = get_nolist_virtcol();
 	/*
 	 * Stop the string when:
@@ -5157,6 +4903,8 @@
     else
     {
 #ifdef FEAT_MBYTE
+	int		cc;
+
 	if (has_mbyte && (cc = (*mb_char2len)(c)) > 1)
 	{
 	    char_u	buf[MB_MAXBYTES + 1];
@@ -5179,6 +4927,312 @@
 }
 
 /*
+ * Format text at the current insert position.
+ */
+    static void
+internal_format(textwidth, second_indent, flags, format_only)
+    int		textwidth;
+    int		second_indent;
+    int		flags;
+    int		format_only;
+{
+    int		cc;
+    int		save_char = NUL;
+    int		haveto_redraw = FALSE;
+    int		fo_ins_blank = has_format_option(FO_INS_BLANK);
+#ifdef FEAT_MBYTE
+    int		fo_multibyte = has_format_option(FO_MBYTE_BREAK);
+#endif
+    int		fo_white_par = has_format_option(FO_WHITE_PAR);
+    int		first_line = TRUE;
+#ifdef FEAT_COMMENTS
+    colnr_T	leader_len;
+    int		no_leader = FALSE;
+    int		do_comments = (flags & INSCHAR_DO_COM);
+#endif
+
+    /*
+     * When 'ai' is off we don't want a space under the cursor to be
+     * deleted.  Replace it with an 'x' temporarily.
+     */
+    if (!curbuf->b_p_ai)
+    {
+	cc = gchar_cursor();
+	if (vim_iswhite(cc))
+	{
+	    save_char = cc;
+	    pchar_cursor('x');
+	}
+    }
+
+    /*
+     * Repeat breaking lines, until the current line is not too long.
+     */
+    while (!got_int)
+    {
+	int	startcol;		/* Cursor column at entry */
+	int	wantcol;		/* column at textwidth border */
+	int	foundcol;		/* column for start of spaces */
+	int	end_foundcol = 0;	/* column for start of word */
+	colnr_T	len;
+	colnr_T	virtcol;
+#ifdef FEAT_VREPLACE
+	int	orig_col = 0;
+	char_u	*saved_text = NULL;
+#endif
+	colnr_T	col;
+
+	virtcol = get_nolist_virtcol();
+	if (virtcol < (colnr_T)textwidth)
+	    break;
+
+#ifdef FEAT_COMMENTS
+	if (no_leader)
+	    do_comments = FALSE;
+	else if (!(flags & INSCHAR_FORMAT)
+				       && has_format_option(FO_WRAP_COMS))
+	    do_comments = TRUE;
+
+	/* Don't break until after the comment leader */
+	if (do_comments)
+	    leader_len = get_leader_len(ml_get_curline(), NULL, FALSE);
+	else
+	    leader_len = 0;
+
+	/* If the line doesn't start with a comment leader, then don't
+	 * start one in a following broken line.  Avoids that a %word
+	 * moved to the start of the next line causes all following lines
+	 * to start with %. */
+	if (leader_len == 0)
+	    no_leader = TRUE;
+#endif
+	if (!(flags & INSCHAR_FORMAT)
+#ifdef FEAT_COMMENTS
+		&& leader_len == 0
+#endif
+		&& !has_format_option(FO_WRAP))
+
+	{
+	    textwidth = 0;
+	    break;
+	}
+	if ((startcol = curwin->w_cursor.col) == 0)
+	    break;
+
+	/* find column of textwidth border */
+	coladvance((colnr_T)textwidth);
+	wantcol = curwin->w_cursor.col;
+
+	curwin->w_cursor.col = startcol - 1;
+#ifdef FEAT_MBYTE
+	/* Correct cursor for multi-byte character. */
+	if (has_mbyte)
+	    mb_adjust_cursor();
+#endif
+	foundcol = 0;
+
+	/*
+	 * Find position to break at.
+	 * Stop at first entered white when 'formatoptions' has 'v'
+	 */
+	while ((!fo_ins_blank && !has_format_option(FO_INS_VI))
+		    || curwin->w_cursor.lnum != Insstart.lnum
+		    || curwin->w_cursor.col >= Insstart.col)
+	{
+	    cc = gchar_cursor();
+	    if (WHITECHAR(cc))
+	    {
+		/* remember position of blank just before text */
+		end_foundcol = curwin->w_cursor.col;
+
+		/* find start of sequence of blanks */
+		while (curwin->w_cursor.col > 0 && WHITECHAR(cc))
+		{
+		    dec_cursor();
+		    cc = gchar_cursor();
+		}
+		if (curwin->w_cursor.col == 0 && WHITECHAR(cc))
+		    break;		/* only spaces in front of text */
+#ifdef FEAT_COMMENTS
+		/* Don't break until after the comment leader */
+		if (curwin->w_cursor.col < leader_len)
+		    break;
+#endif
+		if (has_format_option(FO_ONE_LETTER))
+		{
+		    /* do not break after one-letter words */
+		    if (curwin->w_cursor.col == 0)
+			break;	/* one-letter word at begin */
+
+		    col = curwin->w_cursor.col;
+		    dec_cursor();
+		    cc = gchar_cursor();
+
+		    if (WHITECHAR(cc))
+			continue;	/* one-letter, continue */
+		    curwin->w_cursor.col = col;
+		}
+#ifdef FEAT_MBYTE
+		if (has_mbyte)
+		    foundcol = curwin->w_cursor.col
+					 + (*mb_ptr2len)(ml_get_cursor());
+		else
+#endif
+		    foundcol = curwin->w_cursor.col + 1;
+		if (curwin->w_cursor.col < (colnr_T)wantcol)
+		    break;
+	    }
+#ifdef FEAT_MBYTE
+	    else if (cc >= 0x100 && fo_multibyte
+			      && curwin->w_cursor.col <= (colnr_T)wantcol)
+	    {
+		/* Break after or before a multi-byte character. */
+		foundcol = curwin->w_cursor.col;
+		if (curwin->w_cursor.col < (colnr_T)wantcol)
+		    foundcol += (*mb_char2len)(cc);
+		end_foundcol = foundcol;
+		break;
+	    }
+#endif
+	    if (curwin->w_cursor.col == 0)
+		break;
+	    dec_cursor();
+	}
+
+	if (foundcol == 0)		/* no spaces, cannot break line */
+	{
+	    curwin->w_cursor.col = startcol;
+	    break;
+	}
+
+	/* Going to break the line, remove any "$" now. */
+	undisplay_dollar();
+
+	/*
+	 * Offset between cursor position and line break is used by replace
+	 * stack functions.  VREPLACE does not use this, and backspaces
+	 * over the text instead.
+	 */
+#ifdef FEAT_VREPLACE
+	if (State & VREPLACE_FLAG)
+	    orig_col = startcol;	/* Will start backspacing from here */
+	else
+#endif
+	    replace_offset = startcol - end_foundcol - 1;
+
+	/*
+	 * adjust startcol for spaces that will be deleted and
+	 * characters that will remain on top line
+	 */
+	curwin->w_cursor.col = foundcol;
+	while (cc = gchar_cursor(), WHITECHAR(cc))
+	    inc_cursor();
+	startcol -= curwin->w_cursor.col;
+	if (startcol < 0)
+	    startcol = 0;
+
+#ifdef FEAT_VREPLACE
+	if (State & VREPLACE_FLAG)
+	{
+	    /*
+	     * In VREPLACE mode, we will backspace over the text to be
+	     * wrapped, so save a copy now to put on the next line.
+	     */
+	    saved_text = vim_strsave(ml_get_cursor());
+	    curwin->w_cursor.col = orig_col;
+	    if (saved_text == NULL)
+		break;	/* Can't do it, out of memory */
+	    saved_text[startcol] = NUL;
+
+	    /* Backspace over characters that will move to the next line */
+	    if (!fo_white_par)
+		backspace_until_column(foundcol);
+	}
+	else
+#endif
+	{
+	    /* put cursor after pos. to break line */
+	    if (!fo_white_par)
+		curwin->w_cursor.col = foundcol;
+	}
+
+	/*
+	 * Split the line just before the margin.
+	 * Only insert/delete lines, but don't really redraw the window.
+	 */
+	open_line(FORWARD, OPENLINE_DELSPACES + OPENLINE_MARKFIX
+		+ (fo_white_par ? OPENLINE_KEEPTRAIL : 0)
+#ifdef FEAT_COMMENTS
+		+ (do_comments ? OPENLINE_DO_COM : 0)
+#endif
+		, old_indent);
+	old_indent = 0;
+
+	replace_offset = 0;
+	if (first_line)
+	{
+	    if (second_indent < 0 && has_format_option(FO_Q_NUMBER))
+		second_indent = get_number_indent(curwin->w_cursor.lnum -1);
+	    if (second_indent >= 0)
+	    {
+#ifdef FEAT_VREPLACE
+		if (State & VREPLACE_FLAG)
+		    change_indent(INDENT_SET, second_indent, FALSE, NUL);
+		else
+#endif
+		    (void)set_indent(second_indent, SIN_CHANGED);
+	    }
+	    first_line = FALSE;
+	}
+
+#ifdef FEAT_VREPLACE
+	if (State & VREPLACE_FLAG)
+	{
+	    /*
+	     * In VREPLACE mode we have backspaced over the text to be
+	     * moved, now we re-insert it into the new line.
+	     */
+	    ins_bytes(saved_text);
+	    vim_free(saved_text);
+	}
+	else
+#endif
+	{
+	    /*
+	     * Check if cursor is not past the NUL off the line, cindent
+	     * may have added or removed indent.
+	     */
+	    curwin->w_cursor.col += startcol;
+	    len = (colnr_T)STRLEN(ml_get_curline());
+	    if (curwin->w_cursor.col > len)
+		curwin->w_cursor.col = len;
+	}
+
+	haveto_redraw = TRUE;
+#ifdef FEAT_CINDENT
+	can_cindent = TRUE;
+#endif
+	/* moved the cursor, don't autoindent or cindent now */
+	did_ai = FALSE;
+#ifdef FEAT_SMARTINDENT
+	did_si = FALSE;
+	can_si = FALSE;
+	can_si_back = FALSE;
+#endif
+	line_breakcheck();
+    }
+
+    if (save_char != NUL)		/* put back space after cursor */
+	pchar_cursor(save_char);
+
+    if (!format_only && haveto_redraw)
+    {
+	update_topline();
+	redraw_curbuf_later(VALID);
+    }
+}
+
+/*
  * Called after inserting or deleting text: When 'formatoptions' includes the
  * 'a' flag format from the current line until the end of the paragraph.
  * Keep the cursor at the same position relative to the text.
@@ -7254,7 +7308,10 @@
 # ifdef FEAT_EVAL
     set_vim_var_string(VV_INSERTMODE,
 		   (char_u *)((State & REPLACE_FLAG) ? "i" :
-			    replaceState == VREPLACE ? "v" : "r"), 1);
+#  ifdef FEAT_VREPLACE
+			    replaceState == VREPLACE ? "v" :
+#  endif
+			    "r"), 1);
 # endif
     apply_autocmds(EVENT_INSERTCHANGE, NULL, NULL, FALSE, curbuf);
 #endif
diff --git a/src/eval.c b/src/eval.c
index 50f688a..6ec0975 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -573,6 +573,7 @@
 static void f_nr2char __ARGS((typval_T *argvars, typval_T *rettv));
 static void f_prevnonblank __ARGS((typval_T *argvars, typval_T *rettv));
 static void f_printf __ARGS((typval_T *argvars, typval_T *rettv));
+static void f_pumvisible __ARGS((typval_T *argvars, typval_T *rettv));
 static void f_range __ARGS((typval_T *argvars, typval_T *rettv));
 static void f_readfile __ARGS((typval_T *argvars, typval_T *rettv));
 static void f_remote_expr __ARGS((typval_T *argvars, typval_T *rettv));
@@ -588,6 +589,8 @@
 static void f_search __ARGS((typval_T *argvars, typval_T *rettv));
 static void f_searchdecl __ARGS((typval_T *argvars, typval_T *rettv));
 static void f_searchpair __ARGS((typval_T *argvars, typval_T *rettv));
+static void f_searchpairpos __ARGS((typval_T *argvars, typval_T *rettv));
+static void f_searchpos __ARGS((typval_T *argvars, typval_T *rettv));
 static void f_server2client __ARGS((typval_T *argvars, typval_T *rettv));
 static void f_serverlist __ARGS((typval_T *argvars, typval_T *rettv));
 static void f_setbufvar __ARGS((typval_T *argvars, typval_T *rettv));
@@ -704,6 +707,8 @@
 static void call_user_func __ARGS((ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rettv, linenr_T firstline, linenr_T lastline, dict_T *selfdict));
 static void add_nr_var __ARGS((dict_T *dp, dictitem_T *v, char *name, varnumber_T nr));
 static win_T *find_win_by_nr __ARGS((typval_T *vp));
+static int searchpair_cmn __ARGS((typval_T *argvars, pos_T *match_pos));
+static int search_cmn __ARGS((typval_T *argvars, pos_T *match_pos));
 
 /* Character used as separated in autoload function/variable names. */
 #define AUTOLOAD_CHAR '#'
@@ -6930,6 +6935,7 @@
     {"nr2char",		1, 1, f_nr2char},
     {"prevnonblank",	1, 1, f_prevnonblank},
     {"printf",		2, 19, f_printf},
+    {"pumvisible",	0, 0, f_pumvisible},
     {"range",		1, 3, f_range},
     {"readfile",	1, 3, f_readfile},
     {"remote_expr",	2, 3, f_remote_expr},
@@ -6945,6 +6951,8 @@
     {"search",		1, 2, f_search},
     {"searchdecl",	1, 3, f_searchdecl},
     {"searchpair",	3, 5, f_searchpair},
+    {"searchpairpos",	3, 5, f_searchpairpos},
+    {"searchpos",	1, 2, f_searchpos},
     {"server2client",	2, 2, f_server2client},
     {"serverlist",	0, 0, f_serverlist},
     {"setbufvar",	3, 3, f_setbufvar},
@@ -12214,6 +12222,22 @@
 }
 
 /*
+ * "pumvisible()" function
+ */
+/*ARGSUSED*/
+    static void
+f_pumvisible(argvars, rettv)
+    typval_T	*argvars;
+    typval_T	*rettv;
+{
+    rettv->vval.v_number = 0;
+#ifdef FEAT_INS_EXPAND
+    if (pum_visible())
+	rettv->vval.v_number = 1;
+#endif
+}
+
+/*
  * "range()" function
  */
     static void
@@ -13135,12 +13159,12 @@
 }
 
 /*
- * "search()" function
+ * Shared by search() and searchpos() functions
  */
-    static void
-f_search(argvars, rettv)
+    static int
+search_cmn(argvars, match_pos)
     typval_T	*argvars;
-    typval_T	*rettv;
+    pos_T   	*match_pos;
 {
     char_u	*pat;
     pos_T	pos;
@@ -13148,8 +13172,7 @@
     int		save_p_ws = p_ws;
     int		dir;
     int		flags = 0;
-
-    rettv->vval.v_number = 0;	/* default: FAIL */
+    int		retval = 0;	/* default: FAIL */
 
     pat = get_tv_string(&argvars[0]);
     dir = get_search_arg(&argvars[1], &flags);	/* may set p_ws */
@@ -13172,10 +13195,16 @@
     if (searchit(curwin, curbuf, &pos, dir, pat, 1L,
 					      SEARCH_KEEP, RE_SEARCH) != FAIL)
     {
-	rettv->vval.v_number = pos.lnum;
+	retval = pos.lnum;
 	if (flags & SP_SETPCMARK)
 	    setpcmark();
 	curwin->w_cursor = pos;
+	if (match_pos != NULL)
+	{
+	    /* Store the match cursor position */
+	    match_pos->lnum = pos.lnum;
+	    match_pos->col = pos.col + 1;
+	}
 	/* "/$" will put the cursor after the end of the line, may need to
 	 * correct that here */
 	check_cursor();
@@ -13186,6 +13215,19 @@
 	curwin->w_cursor = save_cursor;
 theend:
     p_ws = save_p_ws;
+
+    return retval;
+}
+
+/*
+ * "search()" function
+ */
+    static void
+f_search(argvars, rettv)
+    typval_T	*argvars;
+    typval_T	*rettv;
+{
+    rettv->vval.v_number = search_cmn(argvars, NULL);
 }
 
 /*
@@ -13216,12 +13258,12 @@
 }
 
 /*
- * "searchpair()" function
+ * Used by searchpair() and searchpairpos()
  */
-    static void
-f_searchpair(argvars, rettv)
+    static int
+searchpair_cmn(argvars, match_pos)
     typval_T	*argvars;
-    typval_T	*rettv;
+    pos_T	*match_pos;
 {
     char_u	*spat, *mpat, *epat;
     char_u	*skip;
@@ -13231,8 +13273,7 @@
     char_u	nbuf1[NUMBUFLEN];
     char_u	nbuf2[NUMBUFLEN];
     char_u	nbuf3[NUMBUFLEN];
-
-    rettv->vval.v_number = 0;	/* default: FAIL */
+    int		retval = 0;		/* default: FAIL */
 
     /* Get the three pattern arguments: start, middle, end. */
     spat = get_tv_string_chk(&argvars[0]);
@@ -13254,7 +13295,7 @@
 	goto theend;
     }
 
-    /* Optional fifth argument: skip expresion */
+    /* Optional fifth argument: skip expression */
     if (argvars[3].v_type == VAR_UNKNOWN
 	    || argvars[4].v_type == VAR_UNKNOWN)
 	skip = (char_u *)"";
@@ -13263,10 +13304,55 @@
     if (skip == NULL)
 	goto theend;	    /* type error */
 
-    rettv->vval.v_number = do_searchpair(spat, mpat, epat, dir, skip, flags);
+    retval = do_searchpair(spat, mpat, epat, dir, skip, flags, match_pos);
 
 theend:
     p_ws = save_p_ws;
+
+    return retval;
+}
+
+/*
+ * "searchpair()" function
+ */
+    static void
+f_searchpair(argvars, rettv)
+    typval_T	*argvars;
+    typval_T	*rettv;
+{
+    rettv->vval.v_number = searchpair_cmn(argvars, NULL);
+}
+
+/*
+ * "searchpairpos()" function
+ */
+    static void
+f_searchpairpos(argvars, rettv)
+    typval_T	*argvars;
+    typval_T	*rettv;
+{
+    list_T	*l;
+    pos_T	match_pos;
+    int		lnum = 0;
+    int		col = 0;
+
+    rettv->vval.v_number = 0;
+
+    l = list_alloc();
+    if (l == NULL)
+	return;
+    rettv->v_type = VAR_LIST;
+    rettv->vval.v_list = l;
+    ++l->lv_refcount;
+
+    if (searchpair_cmn(argvars, &match_pos) > 0)
+    {
+	lnum = match_pos.lnum;
+	col = match_pos.col;
+    }
+
+    list_append_number(l, (varnumber_T)lnum);
+    list_append_number(l, (varnumber_T)col);
 }
 
 /*
@@ -13275,13 +13361,14 @@
  * Returns 0 or -1 for no match,
  */
     long
-do_searchpair(spat, mpat, epat, dir, skip, flags)
+do_searchpair(spat, mpat, epat, dir, skip, flags, match_pos)
     char_u	*spat;	    /* start pattern */
     char_u	*mpat;	    /* middle pattern */
     char_u	*epat;	    /* end pattern */
     int		dir;	    /* BACKWARD or FORWARD */
     char_u	*skip;	    /* skip expression */
     int		flags;	    /* SP_RETCOUNT, SP_REPEAT, SP_NOMOVE */
+    pos_T	*match_pos;
 {
     char_u	*save_cpo;
     char_u	*pat, *pat2 = NULL, *pat3 = NULL;
@@ -13389,6 +13476,13 @@
 	}
     }
 
+    if (match_pos != NULL)
+    {
+	/* Store the match cursor position */
+	match_pos->lnum = curwin->w_cursor.lnum;
+	match_pos->col = curwin->w_cursor.col + 1;
+    }
+
     /* If 'n' flag is used or search failed: restore cursor position. */
     if ((flags & SP_NOMOVE) || retval == 0)
 	curwin->w_cursor = save_cursor;
@@ -13401,6 +13495,40 @@
     return retval;
 }
 
+/*
+ * "searchpos()" function
+ */
+    static void
+f_searchpos(argvars, rettv)
+    typval_T	*argvars;
+    typval_T	*rettv;
+{
+    list_T	*l;
+    pos_T	match_pos;
+    int		lnum = 0;
+    int		col = 0;
+
+    rettv->vval.v_number = 0;
+
+    l = list_alloc();
+    if (l == NULL)
+	return;
+    rettv->v_type = VAR_LIST;
+    rettv->vval.v_list = l;
+    ++l->lv_refcount;
+
+    if (search_cmn(argvars, &match_pos) > 0)
+    {
+	lnum = match_pos.lnum;
+	col = match_pos.col;
+    }
+
+    list_append_number(l, (varnumber_T)lnum);
+    list_append_number(l, (varnumber_T)col);
+
+}
+
+
 /*ARGSUSED*/
     static void
 f_server2client(argvars, rettv)
@@ -14027,11 +14155,9 @@
     typval_T	*rettv;
 {
     char_u	*word = (char_u *)"";
-#ifdef FEAT_SYN_HL
-    int		len = 0;
     hlf_T	attr = HLF_COUNT;
+    int		len = 0;
     list_T	*l;
-#endif
 
     l = list_alloc();
     if (l == NULL)
diff --git a/src/ex_cmds.h b/src/ex_cmds.h
index ca3bed0..6571cce 100644
--- a/src/ex_cmds.h
+++ b/src/ex_cmds.h
@@ -877,6 +877,16 @@
 			RANGE|NOTADR|BANG|WORD1|TRLBAR|ZEROR),
 EX(CMD_tags,		"tags",		do_tags,
 			TRLBAR|CMDWIN),
+EX(CMD_tab,		"tab",		ex_tab,
+			RANGE|NOTADR|COUNT|TRLBAR),
+EX(CMD_tabclose,	"tabclose",	ex_tabclose,
+			BANG|TRLBAR|CMDWIN),
+EX(CMD_tabedit,		"tabedit",	ex_tabedit,
+			BANG|FILE1|RANGE|NOTADR|EDITCMD|ARGOPT|TRLBAR),
+EX(CMD_tabfind,		"tabfind",	ex_tabedit,
+			BANG|FILE1|RANGE|NOTADR|EDITCMD|ARGOPT|TRLBAR),
+EX(CMD_tabs,		"tabs",		ex_tabs,
+			TRLBAR|CMDWIN),
 EX(CMD_tcl,		"tcl",		ex_tcl,
 			RANGE|EXTRA|NEEDARG|CMDWIN),
 EX(CMD_tcldo,		"tcldo",	ex_tcldo,
diff --git a/src/ex_docmd.c b/src/ex_docmd.c
index 098f42c..e87bb6f 100644
--- a/src/ex_docmd.c
+++ b/src/ex_docmd.c
@@ -148,11 +148,13 @@
 static void	ex_quit_all __ARGS((exarg_T *eap));
 #ifdef FEAT_WINDOWS
 static void	ex_close __ARGS((exarg_T *eap));
-static void	ex_win_close __ARGS((exarg_T *eap, win_T *win));
+static void	ex_win_close __ARGS((int forceit, win_T *win));
 static void	ex_only __ARGS((exarg_T *eap));
 static void	ex_all __ARGS((exarg_T *eap));
 static void	ex_resize __ARGS((exarg_T *eap));
 static void	ex_stag __ARGS((exarg_T *eap));
+static void	ex_tabclose __ARGS((exarg_T *eap));
+static void	ex_tabs __ARGS((exarg_T *eap));
 #else
 # define ex_close		ex_ni
 # define ex_only		ex_ni
@@ -160,6 +162,10 @@
 # define ex_resize		ex_ni
 # define ex_splitview		ex_ni
 # define ex_stag		ex_ni
+# define ex_tabedit		ex_ni
+# define ex_tab			ex_ni
+# define ex_tabs		ex_ni
+# define ex_tabclose		ex_ni
 #endif
 #if defined(FEAT_WINDOWS) && defined(FEAT_QUICKFIX)
 static void	ex_pclose __ARGS((exarg_T *eap));
@@ -6138,19 +6144,38 @@
     else
 # endif
 	if (!text_locked())
-	    ex_win_close(eap, curwin);
+	    ex_win_close(eap->forceit, curwin);
 }
 
+#ifdef FEAT_QUICKFIX
+/*
+ * ":pclose": Close any preview window.
+ */
     static void
-ex_win_close(eap, win)
+ex_pclose(eap)
     exarg_T	*eap;
+{
+    win_T	*win;
+
+    for (win = firstwin; win != NULL; win = win->w_next)
+	if (win->w_p_pvw)
+	{
+	    ex_win_close(eap->forceit, win);
+	    break;
+	}
+}
+#endif
+
+    static void
+ex_win_close(forceit, win)
+    int		forceit;
     win_T	*win;
 {
     int		need_hide;
     buf_T	*buf = win->w_buffer;
 
     need_hide = (bufIsChanged(buf) && buf->b_nwindows <= 1);
-    if (need_hide && !P_HID(buf) && !eap->forceit)
+    if (need_hide && !P_HID(buf) && !forceit)
     {
 #if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)
 	if ((p_confirm || cmdmod.confirm) && p_write)
@@ -6175,24 +6200,33 @@
     win_close(win, !need_hide && !P_HID(buf));
 }
 
-#ifdef FEAT_QUICKFIX
 /*
- * ":pclose": Close any preview window.
+ * ":tabclose": close current tab page, unless it is the last one
  */
     static void
-ex_pclose(eap)
+ex_tabclose(eap)
     exarg_T	*eap;
 {
-    win_T	*win;
-
-    for (win = firstwin; win != NULL; win = win->w_next)
-	if (win->w_p_pvw)
+# ifdef FEAT_CMDWIN
+    if (cmdwin_type != 0)
+	cmdwin_result = K_IGNORE;
+    else
+# endif
+	if (!text_locked())
 	{
-	    ex_win_close(eap, win);
-	    break;
+	    if (first_tabpage->tp_next == NULL)
+		EMSG(_("E999: Cannot close last tab page"));
+	    else
+	    {
+		/* First close all the windows but the current one.  If that
+		 * worked then close the last window in this tab, that will
+		 * close it. */
+		ex_only(eap);
+		if (lastwin == firstwin)
+		    ex_win_close(eap->forceit, curwin);
+	    }
 	}
 }
-#endif
 
 /*
  * ":only".
@@ -6722,41 +6756,41 @@
     exarg_T	*eap;
 {
     win_T	*old_curwin;
-#if defined(FEAT_SEARCHPATH) || defined(FEAT_BROWSE)
+# if defined(FEAT_SEARCHPATH) || defined(FEAT_BROWSE)
     char_u	*fname = NULL;
-#endif
-#ifdef FEAT_BROWSE
+# endif
+# ifdef FEAT_BROWSE
     int		browse_flag = cmdmod.browse;
-#endif
+# endif
 
-#ifndef FEAT_VERTSPLIT
+# ifndef FEAT_VERTSPLIT
     if (eap->cmdidx == CMD_vsplit || eap->cmdidx == CMD_vnew)
     {
 	ex_ni(eap);
 	return;
     }
-#endif
+# endif
 
     old_curwin = curwin;
-#ifdef FEAT_GUI
+# ifdef FEAT_GUI
     need_mouse_correct = TRUE;
-#endif
+# endif
 
-#ifdef FEAT_QUICKFIX
+# ifdef FEAT_QUICKFIX
     /* A ":split" in the quickfix window works like ":new".  Don't want two
      * quickfix windows. */
     if (bt_quickfix(curbuf))
     {
 	if (eap->cmdidx == CMD_split)
 	    eap->cmdidx = CMD_new;
-# ifdef FEAT_VERTSPLIT
+#  ifdef FEAT_VERTSPLIT
 	if (eap->cmdidx == CMD_vsplit)
 	    eap->cmdidx = CMD_vnew;
-# endif
+#  endif
     }
-#endif
+# endif
 
-#ifdef FEAT_SEARCHPATH
+# ifdef FEAT_SEARCHPATH
     if (eap->cmdidx == CMD_sfind)
     {
 	fname = find_file_in_path(eap->arg, (int)STRLEN(eap->arg),
@@ -6765,15 +6799,15 @@
 	    goto theend;
 	eap->arg = fname;
     }
-# ifdef FEAT_BROWSE
+#  ifdef FEAT_BROWSE
     else
+#  endif
 # endif
-#endif
-#ifdef FEAT_BROWSE
+# ifdef FEAT_BROWSE
     if (cmdmod.browse
-# ifdef FEAT_VERTSPLIT
+#  ifdef FEAT_VERTSPLIT
 	    && eap->cmdidx != CMD_vnew
-#endif
+# endif
 	    && eap->cmdidx != CMD_new)
     {
 	if (
@@ -6797,36 +6831,165 @@
 	}
     }
     cmdmod.browse = FALSE;	/* Don't browse again in do_ecmd(). */
-#endif
+# endif
 
     if (win_split(eap->addr_count > 0 ? (int)eap->line2 : 0,
 				     *eap->cmd == 'v' ? WSP_VERT : 0) != FAIL)
     {
-#ifdef FEAT_SCROLLBIND
+# ifdef FEAT_SCROLLBIND
 	/* Reset 'scrollbind' when editing another file, but keep it when
 	 * doing ":split" without arguments. */
 	if (*eap->arg != NUL
-#ifdef FEAT_BROWSE
+#  ifdef FEAT_BROWSE
 		|| cmdmod.browse
-#endif
+#  endif
 	   )
 	    curwin->w_p_scb = FALSE;
 	else
 	    do_check_scrollbind(FALSE);
-#endif
+# endif
 	do_exedit(eap, old_curwin);
     }
 
-#ifdef FEAT_BROWSE
+# ifdef FEAT_BROWSE
     cmdmod.browse = browse_flag;
-#endif
+# endif
 
-#if defined(FEAT_SEARCHPATH) || defined(FEAT_BROWSE)
+# if defined(FEAT_SEARCHPATH) || defined(FEAT_BROWSE)
 theend:
     vim_free(fname);
-#endif
+# endif
 }
-#endif
+
+/*
+ * :tabedit [[+command] file]	open new Tab page with empty window
+ * :tabedit [[+command] file]	open new Tab page and edit "file"
+ * :tabfind [[+command] file]	open new Tab page and find "file"
+ */
+    void
+ex_tabedit(eap)
+    exarg_T	*eap;
+{
+# if defined(FEAT_SEARCHPATH) || defined(FEAT_BROWSE)
+    char_u	*fname = NULL;
+# endif
+# ifdef FEAT_BROWSE
+    int		browse_flag = cmdmod.browse;
+# endif
+
+# ifdef FEAT_GUI
+    need_mouse_correct = TRUE;
+# endif
+
+# ifdef FEAT_SEARCHPATH
+    if (eap->cmdidx == CMD_tabfind)
+    {
+	fname = find_file_in_path(eap->arg, (int)STRLEN(eap->arg),
+					  FNAME_MESS, TRUE, curbuf->b_ffname);
+	if (fname == NULL)
+	    goto theend;
+	eap->arg = fname;
+    }
+#  ifdef FEAT_BROWSE
+    else
+#  endif
+# endif
+# ifdef FEAT_BROWSE
+    if (cmdmod.browse)
+    {
+	if (
+#  ifdef FEAT_GUI
+	    !gui.in_use &&
+#  endif
+		au_has_group((char_u *)"FileExplorer"))
+	{
+	    /* No browsing supported but we do have the file explorer:
+	     * Edit the directory. */
+	    if (*eap->arg == NUL || !mch_isdir(eap->arg))
+		eap->arg = (char_u *)".";
+	}
+	else
+	{
+	    fname = do_browse(0, (char_u *)_("Edit File in new tab page"),
+					  eap->arg, NULL, NULL, NULL, curbuf);
+	    if (fname == NULL)
+		goto theend;
+	    eap->arg = fname;
+	}
+    }
+    cmdmod.browse = FALSE;	/* Don't browse again in do_ecmd(). */
+# endif
+
+    if (win_new_tabpage() != FAIL)
+    {
+# ifdef FEAT_SCROLLBIND
+	curwin->w_p_scb = FALSE;
+# endif
+	do_exedit(eap, NULL);
+    }
+
+# ifdef FEAT_BROWSE
+    cmdmod.browse = browse_flag;
+# endif
+
+# if defined(FEAT_SEARCHPATH) || defined(FEAT_BROWSE)
+theend:
+    vim_free(fname);
+# endif
+}
+
+/*
+ * :tab command
+ */
+    void
+ex_tab(eap)
+    exarg_T	*eap;
+{
+    goto_tabpage((int)eap->line2);
+}
+
+/*
+ * :tabs command: List tabs and their contents.
+ */
+/*ARGSUSED*/
+    static void
+ex_tabs(eap)
+    exarg_T	*eap;
+{
+    tabpage_T	*tp;
+    win_T	*wp;
+    int		tabcount = 1;
+
+    msg_start();
+    msg_scroll = TRUE;
+    for (tp = first_tabpage; tp != NULL && !got_int; tp = tp->tp_next)
+    {
+	msg_putchar('\n');
+	vim_snprintf((char *)IObuff, IOSIZE, _("Tab page %d"), tabcount++);
+	msg_outtrans_attr(IObuff, hl_attr(HLF_T));
+	out_flush();	    /* output one line at a time */
+	ui_breakcheck();
+
+	if (tp->tp_topframe == topframe)
+	    wp = firstwin;
+	else
+	    wp = tp->tp_firstwin;
+	for ( ; wp != NULL && !got_int; wp = wp->w_next)
+	{
+	    msg_puts((char_u *)"\n    ");
+	    if (buf_spname(wp->w_buffer) != NULL)
+		STRCPY(IObuff, buf_spname(wp->w_buffer));
+	    else
+		home_replace(wp->w_buffer, wp->w_buffer->b_fname,
+							IObuff, IOSIZE, TRUE);
+	    msg_outtrans(IObuff);
+	    out_flush();	    /* output one line at a time */
+	    ui_breakcheck();
+	}
+    }
+}
+
+#endif /* FEAT_WINDOWS */
 
 /*
  * ":mode": Set screen mode.
diff --git a/src/globals.h b/src/globals.h
index 85aeeb9..57d7d09 100644
--- a/src/globals.h
+++ b/src/globals.h
@@ -488,6 +488,7 @@
 # define W_NEXT(wp) NULL
 # define FOR_ALL_WINDOWS(wp) wp = curwin;
 #endif
+
 EXTERN win_T	*curwin;	/* currently active window */
 
 /*
@@ -496,6 +497,15 @@
  */
 EXTERN frame_T	*topframe;	/* top of the window frame tree */
 
+#ifdef FEAT_WINDOWS
+/*
+ * Tab pages are nothing more than alternative topframes.  "first_tabpage"
+ * points to the first one in the list, "topframe" is the current one.
+ */
+EXTERN tabpage_T *first_tabpage;
+EXTERN int	  redraw_tabpage INIT(= FALSE);	/* redraw tab pages line */
+#endif
+
 /*
  * All buffers are linked in a list. 'firstbuf' points to the first entry,
  * 'lastbuf' to the last entry and 'curbuf' to the currently active buffer.
diff --git a/src/gui.h b/src/gui.h
index 2f5275c..c706302 100644
--- a/src/gui.h
+++ b/src/gui.h
@@ -168,7 +168,7 @@
 typedef struct GuiScrollbar
 {
     long	ident;		/* Unique identifier for each scrollbar */
-    struct window *wp;		/* Scrollbar's window, NULL for bottom */
+    win_T	*wp;		/* Scrollbar's window, NULL for bottom */
     int		type;		/* one of SBAR_{LEFT,RIGHT,BOTTOM} */
     long	value;		/* Represents top line number visible */
 #ifdef FEAT_GUI_ATHENA
diff --git a/src/gui_beval.c b/src/gui_beval.c
index bb26497..6c628cd 100644
--- a/src/gui_beval.c
+++ b/src/gui_beval.c
@@ -609,7 +609,10 @@
 						 ? (int)GDK_CONTROL_MASK : 0);
 		break;
 	    default:
-		cancelBalloon(beval);
+		/* Don't do this for key release, we apparently get these with
+		 * focus changes in some GTK version. */
+		if (is_keypress)
+		    cancelBalloon(beval);
 		break;
 	}
     }
diff --git a/src/if_mzsch.c b/src/if_mzsch.c
index 940c991..4d5bb32 100644
--- a/src/if_mzsch.c
+++ b/src/if_mzsch.c
@@ -40,7 +40,7 @@
 typedef struct
 {
     Scheme_Type	    tag;
-    struct window   *win;
+    win_T	    *win;
 } vim_mz_window;
 
 #define INVALID_WINDOW_VALUE ((win_T *)(-1))
diff --git a/src/main.c b/src/main.c
index 0b96d16..81e41b6 100644
--- a/src/main.c
+++ b/src/main.c
@@ -297,9 +297,11 @@
     TIME_MSG("window checked");
 
     /*
-     * Allocate the first window and buffer. Can't do much without it.
+     * Allocate the first window and buffer.
+     * Can't do anything without it, exit when it fails.
      */
-    win_alloc_first();
+    if (win_alloc_first() == FAIL)
+	mch_exit(0);
 
     init_yank();		/* init yank buffers */
 
@@ -505,12 +507,7 @@
     if (usingNetbeans)
 	Columns += 2;		/* leave room for glyph gutter */
 #endif
-    firstwin->w_height = Rows - p_ch;
-    topframe->fr_height = Rows - p_ch;
-#ifdef FEAT_VERTSPLIT
-    firstwin->w_width = Columns;
-    topframe->fr_width = Columns;
-#endif
+    win_init_size();
 #ifdef FEAT_DIFF
     /* Set the 'diff' option now, so that it can be checked for in a .vimrc
      * file.  There is no buffer yet though. */
diff --git a/src/netbeans.c b/src/netbeans.c
index a2c57fc..d183e49 100644
--- a/src/netbeans.c
+++ b/src/netbeans.c
@@ -3355,7 +3355,7 @@
 
 
 /*
- * Convert lnum,col to character offset
+ * Convert (lnum,col) to byte offset in the file.
  */
     static long
 pos2off(buf_T *buf, pos_T *pos)
diff --git a/src/normal.c b/src/normal.c
index 09410f0..d4001b4 100644
--- a/src/normal.c
+++ b/src/normal.c
@@ -1935,7 +1935,12 @@
 	    break;
 
 	case OP_FORMAT:
-	    if (*p_fp != NUL)
+#if defined(FEAT_EVAL)
+	    if (*curbuf->b_p_fex != NUL)
+		op_formatexpr(oap);	/* use expression */
+	    else
+#endif
+		if (*p_fp != NUL)
 		op_colon(oap);		/* use external command */
 	    else
 		op_format(oap, FALSE);	/* use internal function */
@@ -7832,6 +7837,12 @@
 	break;
 #endif
 
+#ifdef FEAT_WINDOWS
+    case 't':
+	goto_tabpage((int)cap->count0);
+	break;
+#endif
+
     default:
 	clearopbeep(oap);
 	break;
diff --git a/src/ops.c b/src/ops.c
index 0c1e853..a48b6be 100644
--- a/src/ops.c
+++ b/src/ops.c
@@ -4312,6 +4312,49 @@
 #endif
 }
 
+#if defined(FEAT_EVAL) || defined(PROTO)
+/*
+ * Implementation of the format operator 'gq' for when using 'formatexpr'.
+ */
+    void
+op_formatexpr(oap)
+    oparg_T	*oap;
+{
+# ifdef FEAT_VISUAL
+    if (oap->is_VIsual)
+	/* When there is no change: need to remove the Visual selection */
+	redraw_curbuf_later(INVERTED);
+# endif
+
+    (void)fex_format(oap->start.lnum, oap->line_count);
+}
+
+    int
+fex_format(lnum, count)
+    linenr_T	lnum;
+    long	count;
+{
+    int		use_sandbox = was_set_insecurely((char_u *)"formatexpr");
+    int		r;
+
+    /*
+     * Set v:lnum to the first line number and v:count to the number of lines.
+     */
+    set_vim_var_nr(VV_LNUM, lnum);
+    set_vim_var_nr(VV_COUNT, count);
+
+    /*
+     * Evaluate the function.
+     */
+    if (use_sandbox)
+	++sandbox;
+    r = eval_to_number(curbuf->b_p_fex);
+    if (use_sandbox)
+	--sandbox;
+    return r;
+}
+#endif
+
 /*
  * Format "line_count" lines, starting at the cursor position.
  * When "line_count" is negative, format until the end of the paragraph.
diff --git a/src/option.c b/src/option.c
index 62cd0e7..cca91cc 100644
--- a/src/option.c
+++ b/src/option.c
@@ -79,6 +79,7 @@
     , PV_FDT
     , PV_FEN
     , PV_FENC
+    , PV_FEX
     , PV_FF
     , PV_FML
     , PV_FMR
@@ -204,6 +205,9 @@
 static char_u	*p_inde;
 static char_u	*p_indk;
 #endif
+#if defined(FEAT_EVAL)
+static char_u	*p_fex;
+#endif
 static int	p_inf;
 static char_u	*p_isk;
 #ifdef FEAT_CRYPT
@@ -974,6 +978,15 @@
 # endif
 			    },
 #endif
+    {"formatexpr", "fex",   P_STRING|P_ALLOCED|P_VI_DEF|P_VIM,
+#if defined(FEAT_EVAL)
+			    (char_u *)&p_fex, PV_FEX,
+			    {(char_u *)"", (char_u *)0L}
+#else
+			    (char_u *)NULL, PV_NONE,
+			    {(char_u *)0L, (char_u *)0L}
+#endif
+			    },
     {"formatoptions","fo",  P_STRING|P_ALLOCED|P_VIM|P_FLAGLIST,
 			    (char_u *)&p_fo, PV_FO,
 			    {(char_u *)DFLT_FO_VI, (char_u *)DFLT_FO_VIM}},
@@ -1129,7 +1142,7 @@
 			    {(char_u *)FALSE, (char_u *)0L}},
     {"highlight",   "hl",   P_STRING|P_VI_DEF|P_RCLR|P_COMMA|P_NODUP,
 			    (char_u *)&p_hl, PV_NONE,
-			    {(char_u *)"8:SpecialKey,@:NonText,d:Directory,e:ErrorMsg,i:IncSearch,l:Search,m:MoreMsg,M:ModeMsg,n:LineNr,r:Question,s:StatusLine,S:StatusLineNC,c:VertSplit,t:Title,v:Visual,V:VisualNOS,w:WarningMsg,W:WildMenu,f:Folded,F:FoldColumn,A:DiffAdd,C:DiffChange,D:DiffDelete,T:DiffText,>:SignColumn,B:SpellBad,P:SpellCap,R:SpellRare,L:SpellLocal,+:Pmenu,=:PmenuSel,x:PmenuSbar,X:PmenuThumb",
+			    {(char_u *)"8:SpecialKey,@:NonText,d:Directory,e:ErrorMsg,i:IncSearch,l:Search,m:MoreMsg,M:ModeMsg,n:LineNr,r:Question,s:StatusLine,S:StatusLineNC,c:VertSplit,t:Title,v:Visual,V:VisualNOS,w:WarningMsg,W:WildMenu,f:Folded,F:FoldColumn,A:DiffAdd,C:DiffChange,D:DiffDelete,T:DiffText,>:SignColumn,B:SpellBad,P:SpellCap,R:SpellRare,L:SpellLocal,+:Pmenu,=:PmenuSel,x:PmenuSbar,X:PmenuThumb,*:TabPage,#:TabPageSel,_:TabPageFill",
 				(char_u *)0L}},
     {"history",	    "hi",   P_NUM|P_VIM,
 			    (char_u *)&p_hi, PV_NONE,
@@ -2264,7 +2277,7 @@
 			    (char_u *)NULL, PV_NONE,
 #endif
 			    {(char_u *)85L, (char_u *)0L}},
-    {"titleold",    NULL,   P_STRING|P_VI_DEF|P_GETTEXT|P_SECURE,
+    {"titleold",    NULL,   P_STRING|P_VI_DEF|P_GETTEXT|P_SECURE|P_NO_MKRC,
 #ifdef FEAT_TITLE
 			    (char_u *)&p_titleold, PV_NONE,
 			    {(char_u *)N_("Thanks for flying Vim"),
@@ -4755,6 +4768,9 @@
     check_string_option(&buf->b_p_inde);
     check_string_option(&buf->b_p_indk);
 #endif
+#if defined(FEAT_EVAL)
+    check_string_option(&buf->b_p_fex);
+#endif
 #ifdef FEAT_CRYPT
     check_string_option(&buf->b_p_key);
 #endif
@@ -8583,6 +8599,9 @@
 	case PV_INDE:	return (char_u *)&(curbuf->b_p_inde);
 	case PV_INDK:	return (char_u *)&(curbuf->b_p_indk);
 #endif
+#if defined(FEAT_EVAL)
+	case PV_FEX:	return (char_u *)&(curbuf->b_p_fex);
+#endif
 #ifdef FEAT_CRYPT
 	case PV_KEY:	return (char_u *)&(curbuf->b_p_key);
 #endif
@@ -8942,6 +8961,9 @@
 	    buf->b_p_inde = vim_strsave(p_inde);
 	    buf->b_p_indk = vim_strsave(p_indk);
 #endif
+#if defined(FEAT_EVAL)
+	    buf->b_p_fex = vim_strsave(p_fex);
+#endif
 #ifdef FEAT_CRYPT
 	    buf->b_p_key = vim_strsave(p_key);
 #endif
diff --git a/src/proto/eval.pro b/src/proto/eval.pro
index b8ba215..1c2942c 100644
--- a/src/proto/eval.pro
+++ b/src/proto/eval.pro
@@ -54,7 +54,7 @@
 long get_dict_number __ARGS((dict_T *d, char_u *key));
 char_u *get_function_name __ARGS((expand_T *xp, int idx));
 char_u *get_expr_name __ARGS((expand_T *xp, int idx));
-long do_searchpair __ARGS((char_u *spat, char_u *mpat, char_u *epat, int dir, char_u *skip, int flags));
+long do_searchpair __ARGS((char_u *spat, char_u *mpat, char_u *epat, int dir, char_u *skip, int flags, pos_T *match_pos));
 void set_vim_var_nr __ARGS((int idx, long val));
 long get_vim_var_nr __ARGS((int idx));
 char_u *get_vim_var_str __ARGS((int idx));
diff --git a/src/proto/ex_docmd.pro b/src/proto/ex_docmd.pro
index 25601e1..a6998cf 100644
--- a/src/proto/ex_docmd.pro
+++ b/src/proto/ex_docmd.pro
@@ -33,6 +33,8 @@
 void alist_add __ARGS((alist_T *al, char_u *fname, int set_fnum));
 void alist_slash_adjust __ARGS((void));
 void ex_splitview __ARGS((exarg_T *eap));
+void ex_tabedit __ARGS((exarg_T *eap));
+void ex_tab __ARGS((exarg_T *eap));
 void do_exedit __ARGS((exarg_T *eap, win_T *old_curwin));
 void free_cd_dir __ARGS((void));
 void do_sleep __ARGS((long msec));
diff --git a/src/proto/ops.pro b/src/proto/ops.pro
index da9973b..3b4c9c4 100644
--- a/src/proto/ops.pro
+++ b/src/proto/ops.pro
@@ -38,6 +38,8 @@
 void do_do_join __ARGS((long count, int insert_space));
 int do_join __ARGS((int insert_space));
 void op_format __ARGS((oparg_T *oap, int keep_cursor));
+void op_formatexpr __ARGS((oparg_T *oap));
+int fex_format __ARGS((linenr_T lnum, long count));
 void format_lines __ARGS((linenr_T line_count));
 int paragraph_start __ARGS((linenr_T lnum));
 int do_addsub __ARGS((int command, linenr_T Prenum1));
diff --git a/src/proto/window.pro b/src/proto/window.pro
index fd3d62f..a1808bc 100644
--- a/src/proto/window.pro
+++ b/src/proto/window.pro
@@ -7,11 +7,15 @@
 void win_move_after __ARGS((win_T *win1, win_T *win2));
 void win_equal __ARGS((win_T *next_curwin, int current, int dir));
 void close_windows __ARGS((buf_T *buf));
+int last_window __ARGS((void));
 void win_close __ARGS((win_T *win, int free_buf));
 void win_free_all __ARGS((void));
 void close_others __ARGS((int message, int forceit));
 void win_init __ARGS((win_T *wp));
-void win_alloc_first __ARGS((void));
+int win_alloc_first __ARGS((void));
+void win_init_size __ARGS((void));
+int win_new_tabpage __ARGS((void));
+void goto_tabpage __ARGS((int n));
 void win_goto __ARGS((win_T *wp));
 win_T *win_find_nr __ARGS((int winnr));
 void win_enter __ARGS((win_T *wp, int undo_sync));
@@ -32,6 +36,7 @@
 void win_comp_scroll __ARGS((win_T *wp));
 void command_height __ARGS((long old_p_ch));
 void last_status __ARGS((int morewin));
+int tabpageline_height __ARGS((void));
 char_u *grab_file_name __ARGS((long count));
 char_u *file_name_at_cursor __ARGS((int options, long count));
 char_u *file_name_in_line __ARGS((char_u *line, int col, int options, long count, char_u *rel_fname));
diff --git a/src/screen.c b/src/screen.c
index e2f10db..ba5475b 100644
--- a/src/screen.c
+++ b/src/screen.c
@@ -167,6 +167,9 @@
 static int win_do_lines __ARGS((win_T *wp, int row, int line_count, int mayclear, int del));
 static void win_rest_invalid __ARGS((win_T *wp));
 static void msg_pos_mode __ARGS((void));
+#if defined(FEAT_WINDOWS)
+static void draw_tabpage __ARGS((void));
+#endif
 #if defined(FEAT_WINDOWS) || defined(FEAT_WILDMENU) || defined(FEAT_STL_OPT)
 static int fillchar_status __ARGS((int *attr, int is_curwin));
 #endif
@@ -390,6 +393,9 @@
 		}
 	    }
 	    redraw_cmdline = TRUE;
+#ifdef FEAT_WINDOWS
+	    redraw_tabpage = TRUE;
+#endif
 	}
 	msg_scrolled = 0;
 	need_wait_return = FALSE;
@@ -468,6 +474,12 @@
     }
 #endif
 
+#ifdef FEAT_WINDOWS
+    /* Redraw the tab pages line if needed. */
+    if (redraw_tabpage || type >= NOT_VALID)
+	draw_tabpage();
+#endif
+
     /*
      * Go from top to bottom through the windows, redrawing the ones that need
      * it.
@@ -4947,6 +4959,8 @@
     for (wp = firstwin; wp; wp = wp->w_next)
 	if (wp->w_redr_status)
 	    win_redr_status(wp);
+    if (redraw_tabpage)
+	draw_tabpage();
 }
 #endif
 
@@ -8409,6 +8423,79 @@
     }
 }
 
+#if defined(FEAT_WINDOWS)
+/*
+ * Draw the tab pages line at the top of the Vim window.
+ */
+    static void
+draw_tabpage()
+{
+    int		tabcount = 0;
+    tabpage_T	*tp;
+    int		tabwidth;
+    int		col = 0;
+    int		had_current = FALSE;
+    int		attr;
+    win_T	*wp;
+    int		c;
+    int		len;
+    int		attr_sel = hl_attr(HLF_TPS);
+    int		attr_nosel = hl_attr(HLF_TP);
+    int		attr_fill = hl_attr(HLF_TPF);
+
+    redraw_tabpage = FALSE;
+
+    if (tabpageline_height() < 1)
+	return;
+
+    for (tp = first_tabpage; tp != NULL; tp = tp->tp_next)
+	++tabcount;
+
+    tabwidth = Columns / tabcount;
+    if (tabwidth < 6)
+	tabwidth = 6;
+
+    attr = attr_nosel;
+    for (tp = first_tabpage; tp != NULL; tp = tp->tp_next)
+    {
+	if (tp->tp_topframe == topframe)
+	{
+	    c = '/';
+	    had_current = TRUE;
+	    attr = attr_sel;
+	}
+	else if (!had_current)
+	    c = '/';
+	else
+	    c = '\\';
+	screen_putchar(c, 0, col++, attr);
+
+	if (tp->tp_topframe != topframe)
+	    attr = attr_nosel;
+
+	if (tp->tp_topframe == topframe)
+	    wp = curwin;
+	else
+	    wp = tp->tp_curwin;
+	if (buf_spname(wp->w_buffer) != NULL)
+	    STRCPY(NameBuff, buf_spname(wp->w_buffer));
+	else
+	    home_replace(wp->w_buffer, wp->w_buffer->b_fname, NameBuff,
+							      MAXPATHL, TRUE);
+	trans_characters(NameBuff, MAXPATHL);
+	len = STRLEN(NameBuff);
+	if (len > tabwidth) /* TODO: multi-byte chars */
+	    len = tabwidth;
+	screen_puts_len(NameBuff, len, 0, col, attr);
+	col += len;
+    }
+
+    screen_putchar('\\', 0, col++, attr);
+    while (col < Columns)
+	screen_putchar('_', 0, col++, attr_fill);
+}
+#endif
+
 #if defined(FEAT_WINDOWS) || defined(FEAT_WILDMENU) || defined(FEAT_STL_OPT)
 /*
  * Get the character to use in a status line.  Get its attributes in "*attr".
diff --git a/src/search.c b/src/search.c
index bcb23aa..5531fb6 100644
--- a/src/search.c
+++ b/src/search.c
@@ -3733,7 +3733,7 @@
     {
 	if (do_searchpair((char_u *)"<[^ \t>/!]\\+\\%(\\_s\\_[^>]\\{-}[^/]>\\|$\\|\\_s\\=>\\)",
 		    (char_u *)"",
-		    (char_u *)"</[^>]*>", BACKWARD, (char_u *)"", 0) <= 0)
+		    (char_u *)"</[^>]*>", BACKWARD, (char_u *)"", 0, NULL) <= 0)
 	{
 	    curwin->w_cursor = old_pos;
 	    goto theend;
@@ -3766,7 +3766,7 @@
     sprintf((char *)spat, "<%.*s\\%%(\\_[^>]\\{-}[^/]>\\|>\\)\\c", len, p);
     sprintf((char *)epat, "</%.*s>\\c", len, p);
 
-    r = do_searchpair(spat, (char_u *)"", epat, FORWARD, (char_u *)"", 0);
+    r = do_searchpair(spat, (char_u *)"", epat, FORWARD, (char_u *)"", 0, NULL);
 
     vim_free(spat);
     vim_free(epat);
diff --git a/src/structs.h b/src/structs.h
index 462ee52..a743a4f 100644
--- a/src/structs.h
+++ b/src/structs.h
@@ -68,10 +68,10 @@
  */
 #include "regexp.h"
 
-typedef struct window	win_T;
-typedef struct wininfo	wininfo_T;
-typedef struct frame	frame_T;
-typedef int		scid_T;		/* script ID */
+typedef struct window_S		win_T;
+typedef struct wininfo_S	wininfo_T;
+typedef struct frame_S		frame_T;
+typedef int			scid_T;		/* script ID */
 
 /*
  * This is here because gui.h needs the pos_T and win_T, and win_T needs gui.h
@@ -215,7 +215,7 @@
  * The window-info is kept in a list at b_wininfo.  It is kept in
  * most-recently-used order.
  */
-struct wininfo
+struct wininfo_S
 {
     wininfo_T	*wi_next;	/* next entry or NULL for last entry */
     wininfo_T	*wi_prev;	/* previous entry or NULL for first entry */
@@ -1330,6 +1330,9 @@
     char_u	*b_p_inde;	/* 'indentexpr' */
     char_u	*b_p_indk;	/* 'indentkeys' */
 #endif
+#if defined(FEAT_EVAL)
+    char_u	*b_p_fex;	/* 'formatexpr' */
+#endif
 #ifdef FEAT_CRYPT
     char_u	*b_p_key;	/* 'key' */
 #endif
@@ -1547,10 +1550,23 @@
 } wline_T;
 
 /*
+ * Tab pages point to the top frame of each tab page.
+ */
+typedef struct tabpage_S tabpage_T;
+struct tabpage_S
+{
+    tabpage_T	    *tp_next;	/* next tabpage or NULL */
+    frame_T	    *tp_topframe;
+    win_T	    *tp_curwin;	/* current window in this Tab page */
+    win_T	    *tp_firstwin; /* first window in this Tab page */
+    win_T	    *tp_lastwin;  /* last window in this Tab page */
+};
+
+/*
  * Windows are kept in a tree of frames.  Each frame has a column (FR_COL)
  * or row (FR_ROW) layout or is a leaf, which has a window.
  */
-struct frame
+struct frame_S
 {
     char	fr_layout;	/* FR_LEAF, FR_COL or FR_ROW */
 #ifdef FEAT_VERTSPLIT
@@ -1577,7 +1593,7 @@
  *
  * All row numbers are relative to the start of the window, except w_winrow.
  */
-struct window
+struct window_S
 {
     buf_T	*w_buffer;	    /* buffer we are a window into (used
 				       often, keep it the first item!) */
diff --git a/src/syntax.c b/src/syntax.c
index ab9bcce..0db3a6e 100644
--- a/src/syntax.c
+++ b/src/syntax.c
@@ -6079,6 +6079,9 @@
 	"DiffText term=reverse cterm=bold ctermbg=Red gui=bold guibg=Red",
 	"PmenuThumb cterm=reverse gui=reverse",
 	"PmenuSbar ctermbg=Grey guibg=Grey",
+	"TabPage term=underline cterm=underline ctermbg=grey gui=underline guibg=grey",
+	"TabPageSel term=reverse,bold cterm=reverse,bold gui=reverse,bold",
+	"TabPageFill term=underline cterm=underline ctermbg=grey gui=underline guibg=grey",
 	NULL
     };
 
diff --git a/src/term.c b/src/term.c
index e5f2c9a..37b84c7 100644
--- a/src/term.c
+++ b/src/term.c
@@ -2980,6 +2980,10 @@
 	    ++len;	/* skip KE_FILLER */
 	    /* else it should be KS_SPECIAL, and c already equals K_SPECIAL */
 	}
+	else if (c == CSI && buf[len] == KS_EXTRA && buf[len + 1] == (int)KE_CSI)
+	    /* CSI is stored as CSI KS_SPECIAL KE_CSI to avoid confusion with
+	     * the start of a special key, see add_to_input_buf_csi(). */
+	    len += 2;
 	bytes[i] = c;
     }
     return len;
diff --git a/src/ui.c b/src/ui.c
index b638c0d..d1013ef 100644
--- a/src/ui.c
+++ b/src/ui.c
@@ -2897,6 +2897,7 @@
     frame_T	*fp;
 
     fp = topframe;
+    *rowp -= firstwin->w_winrow;
     for (;;)
     {
 	if (fp->fr_layout == FR_LEAF)
diff --git a/src/version.h b/src/version.h
index 8324c33..6ff870f 100644
--- a/src/version.h
+++ b/src/version.h
@@ -36,5 +36,5 @@
 #define VIM_VERSION_NODOT	"vim70aa"
 #define VIM_VERSION_SHORT	"7.0aa"
 #define VIM_VERSION_MEDIUM	"7.0aa ALPHA"
-#define VIM_VERSION_LONG	"VIM - Vi IMproved 7.0aa ALPHA (2006 Feb 10)"
-#define VIM_VERSION_LONG_DATE	"VIM - Vi IMproved 7.0aa ALPHA (2006 Feb 10, compiled "
+#define VIM_VERSION_LONG	"VIM - Vi IMproved 7.0aa ALPHA (2006 Feb 14)"
+#define VIM_VERSION_LONG_DATE	"VIM - Vi IMproved 7.0aa ALPHA (2006 Feb 14, compiled "
diff --git a/src/vim.h b/src/vim.h
index 07acb56..9af9514 100644
--- a/src/vim.h
+++ b/src/vim.h
@@ -1157,6 +1157,9 @@
     , HLF_PSI	    /* popup menu selected item */
     , HLF_PSB	    /* popup menu scrollbar */
     , HLF_PST	    /* popup menu scrollbar thumb */
+    , HLF_TP	    /* tabpage line */
+    , HLF_TPS	    /* tabpage line selected */
+    , HLF_TPF	    /* tabpage line filler */
     , HLF_COUNT	    /* MUST be the last one */
 } hlf_T;
 
@@ -1165,7 +1168,7 @@
 		  'n', 'r', 's', 'S', 'c', 't', 'v', 'V', 'w', 'W', \
 		  'f', 'F', 'A', 'C', 'D', 'T', '>', \
 		  'B', 'P', 'R', 'L', \
-		  '+', '=', 'x', 'X'}
+		  '+', '=', 'x', 'X', '*', '#', '_'}
 
 /*
  * Boolean constants
diff --git a/src/window.c b/src/window.c
index 6bb9c46..4dc9885 100644
--- a/src/window.c
+++ b/src/window.c
@@ -29,6 +29,7 @@
 static win_T *win_free_mem __ARGS((win_T *win, int *dirp));
 static win_T *winframe_remove __ARGS((win_T *win, int *dirp));
 static frame_T *win_altframe __ARGS((win_T *win));
+static tabpage_T *alt_tabpage __ARGS((void));
 static win_T *frame2win __ARGS((frame_T *frp));
 static int frame_has_win __ARGS((frame_T *frp, win_T *wp));
 static void frame_new_height __ARGS((frame_T *topfrp, int height, int topfirst, int wfh));
@@ -40,6 +41,12 @@
 static int frame_minwidth __ARGS((frame_T *topfrp, win_T *next_curwin));
 static void frame_fix_width __ARGS((win_T *wp));
 #endif
+#endif
+static int win_alloc_firstwin __ARGS((void));
+#if defined(FEAT_WINDOWS) || defined(PROTO)
+static tabpage_T *current_tabpage __ARGS((void));
+static void leave_tabpage __ARGS((tabpage_T *tp));
+static void enter_tabpage __ARGS((tabpage_T *tp, buf_T *old_curbuf));
 static void frame_fix_height __ARGS((win_T *wp));
 static int frame_minheight __ARGS((frame_T *topfrp, win_T *next_curwin));
 static void win_enter_ext __ARGS((win_T *wp, int undo_sync, int no_curwin));
@@ -77,6 +84,9 @@
 #ifdef FEAT_WINDOWS
 static long p_ch_used = 1L;		/* value of 'cmdheight' when frame
 					   size was set */
+# define ROWS_AVAIL (Rows - p_ch - tabpageline_height())
+#else
+# define ROWS_AVAIL (Rows - p_ch)
 #endif
 
 #if defined(FEAT_WINDOWS) || defined(PROTO)
@@ -932,7 +942,7 @@
 	if (flags & (WSP_TOP | WSP_BOT))
 	{
 	    /* set height and row of new window to full height */
-	    wp->w_winrow = 0;
+	    wp->w_winrow = tabpageline_height();
 	    wp->w_height = curfrp->fr_height - (p_ls > 0);
 	    wp->w_status_height = (p_ls > 0);
 	}
@@ -1507,7 +1517,8 @@
 	dir = 'b';
 #endif
     win_equal_rec(next_curwin == NULL ? curwin : next_curwin, current,
-		      topframe, dir, 0, 0, (int)Columns, topframe->fr_height);
+		      topframe, dir, 0, tabpageline_height(),
+					   (int)Columns, topframe->fr_height);
 }
 
 /*
@@ -1807,6 +1818,16 @@
 }
 
 /*
+ * Return TRUE if the current window is the only window that exists.
+ * Returns FALSE if there is a window in another tab page.
+ */
+    int
+last_window()
+{
+    return (lastwin == firstwin && first_tabpage->tp_next == NULL);
+}
+
+/*
  * close window "win"
  * If "free_buf" is TRUE related buffer may be unloaded.
  *
@@ -1818,6 +1839,7 @@
     int		free_buf;
 {
     win_T	*wp;
+    buf_T	*old_curbuf = curbuf;
 #ifdef FEAT_AUTOCMD
     int		other_buffer = FALSE;
 #endif
@@ -1825,7 +1847,7 @@
     int		dir;
     int		help_window = FALSE;
 
-    if (lastwin == firstwin)
+    if (last_window())
     {
 	EMSG(_("E444: Cannot close last window"));
 	return;
@@ -1854,11 +1876,11 @@
 	{
 	    other_buffer = TRUE;
 	    apply_autocmds(EVENT_BUFLEAVE, NULL, NULL, FALSE, curbuf);
-	    if (!win_valid(win) || firstwin == lastwin)
+	    if (!win_valid(win) || last_window())
 		return;
 	}
 	apply_autocmds(EVENT_WINLEAVE, NULL, NULL, FALSE, curbuf);
-	if (!win_valid(win) || firstwin == lastwin)
+	if (!win_valid(win) || last_window())
 	    return;
 # ifdef FEAT_EVAL
 	/* autocmds may abort script processing */
@@ -1874,16 +1896,42 @@
     close_buffer(win, win->w_buffer, free_buf ? DOBUF_UNLOAD : 0);
     /* Autocommands may have closed the window already, or closed the only
      * other window. */
-    if (!win_valid(win) || firstwin == lastwin)
+    if (!win_valid(win) || last_window())
 	return;
 
     /* Free the memory used for the window. */
     wp = win_free_mem(win, &dir);
 
+    /* When closing the last window in a tab page go to another tab page. */
+    if (wp == NULL)
+    {
+	tabpage_T   *ptp = NULL;
+	tabpage_T   *tp;
+	tabpage_T   *atp = alt_tabpage();
+
+	for (tp = first_tabpage; tp->tp_topframe != topframe; tp = tp->tp_next)
+	    ptp = tp;
+	if (tp == NULL)
+	{
+	    EMSG2(_(e_intern2), "win_close()");
+	    return;
+	}
+	if (ptp == NULL)
+	    first_tabpage = tp->tp_next;
+	else
+	    ptp->tp_next = tp->tp_next;
+	vim_free(tp);
+
+	/* We don't do the window resizing stuff, let enter_tabpage() take
+	 * care of entering a window in another tab page. */
+	enter_tabpage(atp, old_curbuf);
+	return;
+    }
+
     /* Make sure curwin isn't invalid.  It can cause severe trouble when
      * printing an error message.  For win_equal() curbuf needs to be valid
      * too. */
-    if (win == curwin)
+    else if (win == curwin)
     {
 	curwin = wp;
 #ifdef FEAT_QUICKFIX
@@ -1937,8 +1985,8 @@
     }
 
     /*
-     * if last window has a status line now and we don't want one,
-     * remove the status line
+     * If last window has a status line now and we don't want one,
+     * remove the status line.
      */
     last_status(FALSE);
 
@@ -1975,9 +2023,13 @@
     /* reduce the reference count to the argument list. */
     alist_unlink(win->w_alist);
 
-    /* remove the window and its frame from the tree of frames. */
+    /* Remove the window and its frame from the tree of frames. */
     frp = win->w_frame;
-    wp = winframe_remove(win, dirp);
+    if (firstwin == lastwin)
+	/* Last window in a tab page. */
+	wp = NULL;
+    else
+	wp = winframe_remove(win, dirp);
     vim_free(frp);
     win_free(win);
 
@@ -2115,6 +2167,10 @@
     frame_T	*frp;
     int		b;
 
+    if (firstwin == lastwin)
+	/* Last window in this tab page, will go to next tab page. */
+	return alt_tabpage()->tp_curwin->w_frame;
+
     frp = win->w_frame;
 #ifdef FEAT_VERTSPLIT
     if (frp->fr_parent != NULL && frp->fr_parent->fr_layout == FR_ROW)
@@ -2128,6 +2184,28 @@
 }
 
 /*
+ * Return the tabpage that will be used if the current one is closed.
+ */
+    static tabpage_T *
+alt_tabpage()
+{
+    tabpage_T	*tp = current_tabpage();
+
+    if (tp != NULL)
+    {
+	/* Use the next tab page if it exists. */
+	if (tp->tp_next != NULL)
+	    return tp->tp_next;
+
+	/* Find the previous tab page. */
+	for (tp = first_tabpage; tp->tp_next != NULL; tp = tp->tp_next)
+	    if (tp->tp_next == current_tabpage())
+		return tp;
+    }
+    return first_tabpage;
+}
+
+/*
  * Find the left-upper window in frame "frp".
  */
     static win_T *
@@ -2640,11 +2718,7 @@
 	}
     }
 
-    /*
-     * If current window has a status line and we don't want one,
-     * remove the status line.
-     */
-    if (lastwin != firstwin)
+    if (message && lastwin != firstwin)
 	EMSG(_("E445: Other window contains changes"));
 }
 
@@ -2686,15 +2760,36 @@
 /*
  * Allocate the first window and put an empty buffer in it.
  * Called from main().
- * When this fails we can't do anything: exit.
+ * Return FAIL when something goes wrong (out of memory).
  */
-    void
+    int
 win_alloc_first()
 {
+    if (win_alloc_firstwin() == FAIL)
+	return FAIL;
+
+#ifdef FEAT_WINDOWS
+    first_tabpage = (tabpage_T *)alloc((unsigned)sizeof(tabpage_T));
+    if (first_tabpage == NULL)
+	return FAIL;
+    first_tabpage->tp_topframe = topframe;
+    first_tabpage->tp_next = NULL;
+#endif
+    return OK;
+}
+
+/*
+ * Allocate one window and put an empty buffer in it.
+ * Called to create the first window in a new tab page.
+ * Return FAIL when something goes wrong (out of memory).
+ */
+    static int
+win_alloc_firstwin()
+{
     curwin = win_alloc(NULL);
     curbuf = buflist_new(NULL, NULL, 1L, BLN_LISTED);
     if (curwin == NULL || curbuf == NULL)
-	mch_exit(0);
+	return FAIL;
     curwin->w_buffer = curbuf;
     curbuf->b_nwindows = 1;	/* there is one window */
 #ifdef FEAT_WINDOWS
@@ -2704,7 +2799,7 @@
 
     topframe = (frame_T *)alloc_clear((unsigned)sizeof(frame_T));
     if (topframe == NULL)
-	mch_exit(0);
+	return FAIL;
     topframe->fr_layout = FR_LEAF;
 #ifdef FEAT_VERTSPLIT
     topframe->fr_width = Columns;
@@ -2715,9 +2810,169 @@
 #endif
     topframe->fr_win = curwin;
     curwin->w_frame = topframe;
+
+    return OK;
+}
+
+/*
+ * Initialize the window and frame size to the maximum.
+ */
+    void
+win_init_size()
+{
+    firstwin->w_height = ROWS_AVAIL;
+    topframe->fr_height = ROWS_AVAIL;
+#ifdef FEAT_VERTSPLIT
+    firstwin->w_width = Columns;
+    topframe->fr_width = Columns;
+#endif
 }
 
 #if defined(FEAT_WINDOWS) || defined(PROTO)
+/*
+ * Create a new Tab page with one empty window.
+ * Put it just after the current Tab page.
+ * Return FAIL or OK.
+ */
+    int
+win_new_tabpage()
+{
+    tabpage_T	*tp;
+    tabpage_T	*newtp;
+
+    newtp = (tabpage_T *)alloc((unsigned)sizeof(tabpage_T));
+    if (newtp == NULL)
+	return FAIL;
+
+    tp = current_tabpage();
+
+    /* Remember the current windows in this Tab page. */
+    leave_tabpage(tp);
+
+    /* Create a new empty window. */
+    if (win_alloc_firstwin() == OK)
+    {
+	/* copy options from previous to new curwin */
+	win_copy_options(tp->tp_curwin, curwin);
+
+	/* Make the new Tab page the new topframe. */
+	newtp->tp_next = tp->tp_next;
+	tp->tp_next = newtp;
+	win_init_size();
+	firstwin->w_winrow = tabpageline_height();
+
+	newtp->tp_topframe = topframe;
+	redraw_all_later(CLEAR);
+	return OK;
+    }
+
+    /* Failed, get back the previous Tab page */
+    topframe = tp->tp_topframe;
+    curwin = tp->tp_curwin;
+    firstwin = tp->tp_firstwin;
+    lastwin = tp->tp_lastwin;
+    return FAIL;
+}
+
+/*
+ * Return a pointer to the current tab page.
+ */
+    static tabpage_T *
+current_tabpage()
+{
+    tabpage_T	*tp;
+
+    for (tp = first_tabpage; tp != NULL; tp = tp->tp_next)
+	if (tp->tp_topframe == topframe)
+	    break;
+    if (tp == NULL)
+	EMSG2(_(e_intern2), "current_tabpage()");
+    return tp;
+}
+
+/*
+ * Prepare for leaving the current tab page "tp".
+ */
+    static void
+leave_tabpage(tp)
+    tabpage_T	*tp;
+{
+    tp->tp_curwin = curwin;
+    tp->tp_firstwin = firstwin;
+    tp->tp_lastwin = lastwin;
+    firstwin = NULL;
+    lastwin = NULL;
+}
+
+/*
+ * Start using tab page "tp".
+ */
+/*ARGSUSED*/
+    static void
+enter_tabpage(tp, old_curbuf)
+    tabpage_T	*tp;
+    buf_T	*old_curbuf;
+{
+    firstwin = tp->tp_firstwin;
+    lastwin = tp->tp_lastwin;
+    topframe = tp->tp_topframe;
+    win_enter_ext(tp->tp_curwin, FALSE, TRUE);
+
+#ifdef FEAT_AUTOCMD
+    if (old_curbuf != curbuf)
+	apply_autocmds(EVENT_BUFENTER, NULL, NULL, FALSE, curbuf);
+#endif
+
+    /* status line may appear or disappear */
+    last_status(FALSE);
+
+#if defined(FEAT_GUI) && defined(FEAT_VERTSPLIT)
+    /* When 'guioptions' includes 'L' or 'R' may have to add or remove
+     * scrollbars. */
+    if (gui.in_use && !win_hasvertsplit())
+	gui_init_which_components(NULL);
+#endif
+
+    redraw_all_later(CLEAR);
+}
+
+/*
+ * Go to tab page "n".  For ":tab N" and "Ngt".
+ */
+    void
+goto_tabpage(n)
+    int	    n;
+{
+    tabpage_T	*otp = current_tabpage();
+    tabpage_T	*tp;
+    int		i;
+
+    if (otp == NULL)
+	return;
+
+    if (n == 0)
+    {
+	/* No count, go to next tab page, wrap around end. */
+	if (otp->tp_next == NULL)
+	    tp = first_tabpage;
+	else
+	    tp = otp->tp_next;
+    }
+    else
+    {
+	/* Go to tab page "n". */
+	i = 0;
+	for (tp = first_tabpage; ++i != n; tp = tp->tp_next)
+	    if (tp == NULL)
+	    {
+		beep_flush();
+		return;
+	    }
+    }
+
+    leave_tabpage(otp);
+    enter_tabpage(tp, curbuf);
+}
 
 /*
  * Go to another window.
@@ -3007,6 +3262,7 @@
     maketitle();
 #endif
     curwin->w_redr_status = TRUE;
+    redraw_tabpage = TRUE;
     if (restart_edit)
 	redraw_later(VALID);	/* causes status line redraw */
 
@@ -3325,7 +3581,7 @@
     void
 shell_new_rows()
 {
-    int		h = (int)(Rows - p_ch);
+    int		h = (int)ROWS_AVAIL;
 
     if (firstwin == NULL)	/* not initialized yet */
 	return;
@@ -3430,7 +3686,7 @@
     static int
 win_comp_pos()
 {
-    int		row = 0;
+    int		row = tabpageline_height();
     int		col = 0;
 
     frame_comp_pos(topframe, &row, &col);
@@ -3593,8 +3849,8 @@
     if (curfrp->fr_parent == NULL)
     {
 	/* topframe: can only change the command line */
-	if (height > Rows - p_ch)
-	    height = Rows - p_ch;
+	if (height > ROWS_AVAIL)
+	    height = ROWS_AVAIL;
 	if (height > 0)
 	    frame_new_height(curfrp, height, FALSE, FALSE);
     }
@@ -3841,7 +4097,7 @@
 
 	    if (width <= room)
 		break;
-	    if (run == 2 || curfrp->fr_height >= Rows - p_ch)
+	    if (run == 2 || curfrp->fr_height >= ROWS_AVAIL)
 	    {
 		if (width > room)
 		    width = room;
@@ -4524,6 +4780,18 @@
     }
 }
 
+/*
+ * Return TRUE if the tab page line is to be drawn.
+ */
+    int
+tabpageline_height()
+{
+    /* TODO: option to tell when to show the tabs. */
+    if (first_tabpage->tp_next == NULL)
+	return 0;
+    return 1;
+}
+
 #endif /* FEAT_WINDOWS */
 
 #if defined(FEAT_SEARCHPATH) || defined(PROTO)
@@ -4846,6 +5114,10 @@
     int		count = 0;
     win_T	*wp;
 
+    /* If there is another tab page there always is another window. */
+    if (first_tabpage->tp_next != NULL)
+	return FALSE;
+
     for (wp = firstwin; wp != NULL; wp = wp->w_next)
 	if (!((wp->w_buffer->b_help && !curbuf->b_help)
 # ifdef FEAT_QUICKFIX