changeset 20237:918245588b50 v8.2.0674

patch 8.2.0674: some source files are too big Commit: https://github.com/vim/vim/commit/11abd095210fc84e5dcee87b9baed86061caefe4 Author: Bram Moolenaar <Bram@vim.org> Date: Fri May 1 14:26:37 2020 +0200 patch 8.2.0674: some source files are too big Problem: Some source files are too big. Solution: Move text formatting functions to a new file. (Yegappan Lakshmanan, closes #6021)
author Bram Moolenaar <Bram@vim.org>
date Fri, 01 May 2020 14:30:04 +0200
parents 366b1c4f8c49
children d3fee66f8f3e
files Filelist src/Make_cyg_ming.mak src/Make_morph.mak src/Make_mvc.mak src/Make_vms.mms src/Makefile src/README.md src/edit.c src/getchar.c src/ops.c src/option.c src/proto.h src/proto/edit.pro src/proto/getchar.pro src/proto/ops.pro src/proto/option.pro src/proto/textformat.pro src/textformat.c src/version.c
diffstat 19 files changed, 1167 insertions(+), 1176 deletions(-) [+]
line wrap: on
line diff
--- a/Filelist
+++ b/Filelist
@@ -128,6 +128,7 @@ SRC_ALL =	\
 		src/term.h \
 		src/termlib.c \
 		src/testing.c \
+		src/textformat.c \
 		src/textobject.c \
 		src/textprop.c \
 		src/time.c \
@@ -280,6 +281,7 @@ SRC_ALL =	\
 		src/proto/terminal.pro \
 		src/proto/termlib.pro \
 		src/proto/testing.pro \
+		src/proto/textformat.pro \
 		src/proto/textobject.pro \
 		src/proto/textprop.pro \
 		src/proto/time.pro \
--- a/src/Make_cyg_ming.mak
+++ b/src/Make_cyg_ming.mak
@@ -787,6 +787,7 @@ OBJ = \
 	$(OUTDIR)/tag.o \
 	$(OUTDIR)/term.o \
 	$(OUTDIR)/testing.o \
+	$(OUTDIR)/textformat.o \
 	$(OUTDIR)/textobject.o \
 	$(OUTDIR)/textprop.o \
 	$(OUTDIR)/time.o \
--- a/src/Make_morph.mak
+++ b/src/Make_morph.mak
@@ -103,6 +103,7 @@ SRC =	arabic.c						\
 	tag.c							\
 	term.c							\
 	testing.c						\
+	textformat.c						\
 	textobject.c						\
 	textprop.c						\
 	time.c							\
--- a/src/Make_mvc.mak
+++ b/src/Make_mvc.mak
@@ -806,6 +806,7 @@ OBJ = \
 	$(OUTDIR)\tag.obj \
 	$(OUTDIR)\term.obj \
 	$(OUTDIR)\testing.obj \
+	$(OUTDIR)\textformat.obj \
 	$(OUTDIR)\textobject.obj \
 	$(OUTDIR)\textprop.obj \
 	$(OUTDIR)\time.obj \
@@ -1745,6 +1746,8 @@ lib$(MZSCHEME_MAIN_LIB)$(MZSCHEME_VER).l
 
 $(OUTDIR)/term.obj:	$(OUTDIR) testing.c  $(INCL)
 
+$(OUTDIR)/textformat.obj:	$(OUTDIR) textformat.c  $(INCL)
+
 $(OUTDIR)/textobject.obj:	$(OUTDIR) textobject.c  $(INCL)
 
 $(OUTDIR)/textprop.obj:	$(OUTDIR) textprop.c  $(INCL)
@@ -1945,6 +1948,7 @@ proto.h: \
 	proto/tag.pro \
 	proto/term.pro \
 	proto/testing.pro \
+	proto/textformat.pro \
 	proto/textobject.pro \
 	proto/textprop.pro \
 	proto/time.pro \
--- a/src/Make_vms.mms
+++ b/src/Make_vms.mms
@@ -382,6 +382,7 @@ SRC = \
 	term.c \
 	termlib.c \
 	testing.c \
+	textformat.c \
 	textobject.c \
 	textprop.c \
 	time.c \
@@ -492,6 +493,7 @@ OBJ = \
 	term.obj \
 	termlib.obj \
 	testing.obj \
+	textformat.obj \
 	textobject.obj \
 	textprop.obj \
 	time.obj \
@@ -991,6 +993,9 @@ termlib.obj : termlib.c vim.h [.auto]con
 testing.obj : testing.c vim.h [.auto]config.h feature.h os_unix.h   \
  ascii.h keymap.h term.h macros.h structs.h regexp.h gui.h beval.h \
  [.proto]gui_beval.pro option.h ex_cmds.h proto.h globals.h
+textformat.obj : textformat.c vim.h [.auto]config.h feature.h os_unix.h   \
+ ascii.h keymap.h term.h macros.h structs.h regexp.h gui.h beval.h \
+ [.proto]gui_beval.pro option.h ex_cmds.h proto.h globals.h
 textobject.obj : textobject.c vim.h [.auto]config.h feature.h os_unix.h   \
  ascii.h keymap.h term.h macros.h structs.h regexp.h gui.h beval.h \
  [.proto]gui_beval.pro option.h ex_cmds.h proto.h globals.h
--- a/src/Makefile
+++ b/src/Makefile
@@ -1680,6 +1680,7 @@ BASIC_SRC = \
 	term.c \
 	terminal.c \
 	testing.c \
+	textformat.c \
 	textobject.c \
 	textprop.c \
 	time.c \
@@ -1823,6 +1824,7 @@ OBJ_COMMON = \
 	objects/term.o \
 	objects/terminal.o \
 	objects/testing.o \
+	objects/textformat.o \
 	objects/textobject.o \
 	objects/textprop.o \
 	objects/time.o \
@@ -1998,6 +2000,7 @@ PRO_AUTO = \
 	terminal.pro \
 	termlib.pro \
 	testing.pro \
+	textformat.pro \
 	textobject.pro \
 	textprop.pro \
 	time.pro \
@@ -3479,6 +3482,9 @@ objects/terminal.o: terminal.c $(TERM_DE
 objects/testing.o: testing.c
 	$(CCC) -o $@ testing.c
 
+objects/textformat.o: textformat.c
+	$(CCC) -o $@ textformat.c
+
 objects/textobject.o: textobject.c
 	$(CCC) -o $@ textobject.c
 
@@ -4073,6 +4079,10 @@ objects/testing.o: testing.c vim.h proto
  auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \
  proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \
  proto.h globals.h
+objects/textformat.o: textformat.c vim.h protodef.h auto/config.h feature.h os_unix.h \
+ auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \
+ proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \
+ proto.h globals.h
 objects/textobject.o: textobject.c vim.h protodef.h auto/config.h feature.h os_unix.h \
  auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \
  proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \
--- a/src/README.md
+++ b/src/README.md
@@ -80,6 +80,7 @@ syntax.c	| syntax and other highlighting
 tag.c		| tags
 term.c		| terminal handling, termcap codes
 testing.c	| testing: assert and test functions
+textformat.c	| text formatting
 textobject.c	| text objects
 textprop.c	| text properties
 time.c		| time and timer functions
--- a/src/edit.c
+++ b/src/edit.c
@@ -28,8 +28,6 @@ static void ins_ctrl_v(void);
 static void init_prompt(int cmdchar_todo);
 #endif
 static void insert_special(int, int, int);
-static void internal_format(int textwidth, int second_indent, int flags, int format_only, int c);
-static void check_auto_format(int);
 static void redo_literal(int c);
 static void start_arrow_common(pos_T *end_insert_pos, int change);
 #ifdef FEAT_SPELL
@@ -104,8 +102,6 @@ static int	ins_need_undo;		// call u_sav
 					// char.  Set when edit() is called.
 					// after that arrow_used is used.
 
-static int	did_add_space = FALSE;	// auto_format() added an extra space
-					// under the cursor
 static int	dont_sync_undo = FALSE;	// CTRL-G U prevents syncing undo for
 					// the next left/right cursor key
 
@@ -2063,8 +2059,6 @@ insert_special(
 # define ISSPECIAL(c)	((c) < ' ' || (c) >= DEL || (c) == '0' || (c) == '^')
 #endif
 
-#define WHITECHAR(cc) (VIM_ISWHITE(cc) && (!enc_utf8 || !utf_iscomposing(utf_ptr2char(ml_get_cursor() + 1))))
-
 /*
  * "flags": INSCHAR_FORMAT - force formatting
  *	    INSCHAR_CTRLV  - char typed just after CTRL-V
@@ -2302,567 +2296,6 @@ insertchar(
 }
 
 /*
- * Format text at the current insert position.
- *
- * If the INSCHAR_COM_LIST flag is present, then the value of second_indent
- * will be the comment leader length sent to open_line().
- */
-    static void
-internal_format(
-    int		textwidth,
-    int		second_indent,
-    int		flags,
-    int		format_only,
-    int		c) // character to be inserted (can be NUL)
-{
-    int		cc;
-    int		save_char = NUL;
-    int		haveto_redraw = FALSE;
-    int		fo_ins_blank = has_format_option(FO_INS_BLANK);
-    int		fo_multibyte = has_format_option(FO_MBYTE_BREAK);
-    int		fo_white_par = has_format_option(FO_WHITE_PAR);
-    int		first_line = TRUE;
-    colnr_T	leader_len;
-    int		no_leader = FALSE;
-    int		do_comments = (flags & INSCHAR_DO_COM);
-#ifdef FEAT_LINEBREAK
-    int		has_lbr = curwin->w_p_lbr;
-
-    // make sure win_lbr_chartabsize() counts correctly
-    curwin->w_p_lbr = FALSE;
-#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 && !(State & VREPLACE_FLAG))
-    {
-	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;
-	int	orig_col = 0;
-	char_u	*saved_text = NULL;
-	colnr_T	col;
-	colnr_T	end_col;
-	int	wcc;			// counter for whitespace chars
-
-	virtcol = get_nolist_virtcol()
-		+ char2cells(c != NUL ? c : gchar_cursor());
-	if (virtcol <= (colnr_T)textwidth)
-	    break;
-
-	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, TRUE);
-	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;
-	if (!(flags & INSCHAR_FORMAT)
-		&& leader_len == 0
-		&& !has_format_option(FO_WRAP))
-
-	    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;
-	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))
-		    || (flags & INSCHAR_FORMAT)
-		    || curwin->w_cursor.lnum != Insstart.lnum
-		    || curwin->w_cursor.col >= Insstart.col)
-	{
-	    if (curwin->w_cursor.col == startcol && c != NUL)
-		cc = c;
-	    else
-		cc = gchar_cursor();
-	    if (WHITECHAR(cc))
-	    {
-		// remember position of blank just before text
-		end_col = curwin->w_cursor.col;
-
-		// find start of sequence of blanks
-		wcc = 0;
-		while (curwin->w_cursor.col > 0 && WHITECHAR(cc))
-		{
-		    dec_cursor();
-		    cc = gchar_cursor();
-
-		    // Increment count of how many whitespace chars in this
-		    // group; we only need to know if it's more than one.
-		    if (wcc < 2)
-		        wcc++;
-		}
-		if (curwin->w_cursor.col == 0 && WHITECHAR(cc))
-		    break;		// only spaces in front of text
-
-		// Don't break after a period when 'formatoptions' has 'p' and
-		// there are less than two spaces.
-		if (has_format_option(FO_PERIOD_ABBR) && cc == '.' && wcc < 2)
-		    continue;
-
-		// Don't break until after the comment leader
-		if (curwin->w_cursor.col < leader_len)
-		    break;
-		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
-		    // do not break "#a b" when 'tw' is 2
-		    if (curwin->w_cursor.col <= leader_len)
-			break;
-		    col = curwin->w_cursor.col;
-		    dec_cursor();
-		    cc = gchar_cursor();
-
-		    if (WHITECHAR(cc))
-			continue;	// one-letter, continue
-		    curwin->w_cursor.col = col;
-		}
-
-		inc_cursor();
-
-		end_foundcol = end_col + 1;
-		foundcol = curwin->w_cursor.col;
-		if (curwin->w_cursor.col <= (colnr_T)wantcol)
-		    break;
-	    }
-	    else if (cc >= 0x100 && fo_multibyte)
-	    {
-		// Break after or before a multi-byte character.
-		if (curwin->w_cursor.col != startcol)
-		{
-		    // Don't break until after the comment leader
-		    if (curwin->w_cursor.col < leader_len)
-			break;
-		    col = curwin->w_cursor.col;
-		    inc_cursor();
-		    // Don't change end_foundcol if already set.
-		    if (foundcol != curwin->w_cursor.col)
-		    {
-			foundcol = curwin->w_cursor.col;
-			end_foundcol = foundcol;
-			if (curwin->w_cursor.col <= (colnr_T)wantcol)
-			    break;
-		    }
-		    curwin->w_cursor.col = col;
-		}
-
-		if (curwin->w_cursor.col == 0)
-		    break;
-
-		col = curwin->w_cursor.col;
-
-		dec_cursor();
-		cc = gchar_cursor();
-
-		if (WHITECHAR(cc))
-		    continue;		// break with space
-		// Don't break until after the comment leader
-		if (curwin->w_cursor.col < leader_len)
-		    break;
-
-		curwin->w_cursor.col = col;
-
-		foundcol = curwin->w_cursor.col;
-		end_foundcol = foundcol;
-		if (curwin->w_cursor.col <= (colnr_T)wantcol)
-		    break;
-	    }
-	    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.
-	 */
-	if (State & VREPLACE_FLAG)
-	    orig_col = startcol;	// Will start backspacing from here
-	else
-	    replace_offset = startcol - end_foundcol;
-
-	/*
-	 * 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))
-		    && (!fo_white_par || curwin->w_cursor.col < startcol))
-	    inc_cursor();
-	startcol -= curwin->w_cursor.col;
-	if (startcol < 0)
-	    startcol = 0;
-
-	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
-	{
-	    // 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)
-		+ (do_comments ? OPENLINE_DO_COM : 0)
-		+ ((flags & INSCHAR_COM_LIST) ? OPENLINE_COM_LIST : 0)
-		, ((flags & INSCHAR_COM_LIST) ? second_indent : old_indent));
-	if (!(flags & INSCHAR_COM_LIST))
-	    old_indent = 0;
-
-	replace_offset = 0;
-	if (first_line)
-	{
-	    if (!(flags & INSCHAR_COM_LIST))
-	    {
-		/*
-		 * This section is for auto-wrap of numeric lists.  When not
-		 * in insert mode (i.e. format_lines()), the INSCHAR_COM_LIST
-		 * flag will be set and open_line() will handle it (as seen
-		 * above).  The code here (and in get_number_indent()) will
-		 * recognize comments if needed...
-		 */
-		if (second_indent < 0 && has_format_option(FO_Q_NUMBER))
-		    second_indent =
-				 get_number_indent(curwin->w_cursor.lnum - 1);
-		if (second_indent >= 0)
-		{
-		    if (State & VREPLACE_FLAG)
-			change_indent(INDENT_SET, second_indent,
-							    FALSE, NUL, TRUE);
-		    else
-			if (leader_len > 0 && second_indent - leader_len > 0)
-		    {
-			int i;
-			int padding = second_indent - leader_len;
-
-			// We started at the first_line of a numbered list
-			// that has a comment.  the open_line() function has
-			// inserted the proper comment leader and positioned
-			// the cursor at the end of the split line.  Now we
-			// add the additional whitespace needed after the
-			// comment leader for the numbered list.
-			for (i = 0; i < padding; i++)
-			    ins_str((char_u *)" ");
-		    }
-		    else
-		    {
-			(void)set_indent(second_indent, SIN_CHANGED);
-		    }
-		}
-	    }
-	    first_line = FALSE;
-	}
-
-	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
-	{
-	    /*
-	     * 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);
-
-#ifdef FEAT_LINEBREAK
-    curwin->w_p_lbr = has_lbr;
-#endif
-    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.
- * The caller must have saved the cursor line for undo, following ones will be
- * saved here.
- */
-    void
-auto_format(
-    int		trailblank,	// when TRUE also format with trailing blank
-    int		prev_line)	// may start in previous line
-{
-    pos_T	pos;
-    colnr_T	len;
-    char_u	*old;
-    char_u	*new, *pnew;
-    int		wasatend;
-    int		cc;
-
-    if (!has_format_option(FO_AUTO))
-	return;
-
-    pos = curwin->w_cursor;
-    old = ml_get_curline();
-
-    // may remove added space
-    check_auto_format(FALSE);
-
-    // Don't format in Insert mode when the cursor is on a trailing blank, the
-    // user might insert normal text next.  Also skip formatting when "1" is
-    // in 'formatoptions' and there is a single character before the cursor.
-    // Otherwise the line would be broken and when typing another non-white
-    // next they are not joined back together.
-    wasatend = (pos.col == (colnr_T)STRLEN(old));
-    if (*old != NUL && !trailblank && wasatend)
-    {
-	dec_cursor();
-	cc = gchar_cursor();
-	if (!WHITECHAR(cc) && curwin->w_cursor.col > 0
-					  && has_format_option(FO_ONE_LETTER))
-	    dec_cursor();
-	cc = gchar_cursor();
-	if (WHITECHAR(cc))
-	{
-	    curwin->w_cursor = pos;
-	    return;
-	}
-	curwin->w_cursor = pos;
-    }
-
-    // With the 'c' flag in 'formatoptions' and 't' missing: only format
-    // comments.
-    if (has_format_option(FO_WRAP_COMS) && !has_format_option(FO_WRAP)
-				&& get_leader_len(old, NULL, FALSE, TRUE) == 0)
-	return;
-
-    /*
-     * May start formatting in a previous line, so that after "x" a word is
-     * moved to the previous line if it fits there now.  Only when this is not
-     * the start of a paragraph.
-     */
-    if (prev_line && !paragraph_start(curwin->w_cursor.lnum))
-    {
-	--curwin->w_cursor.lnum;
-	if (u_save_cursor() == FAIL)
-	    return;
-    }
-
-    /*
-     * Do the formatting and restore the cursor position.  "saved_cursor" will
-     * be adjusted for the text formatting.
-     */
-    saved_cursor = pos;
-    format_lines((linenr_T)-1, FALSE);
-    curwin->w_cursor = saved_cursor;
-    saved_cursor.lnum = 0;
-
-    if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count)
-    {
-	// "cannot happen"
-	curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
-	coladvance((colnr_T)MAXCOL);
-    }
-    else
-	check_cursor_col();
-
-    // Insert mode: If the cursor is now after the end of the line while it
-    // previously wasn't, the line was broken.  Because of the rule above we
-    // need to add a space when 'w' is in 'formatoptions' to keep a paragraph
-    // formatted.
-    if (!wasatend && has_format_option(FO_WHITE_PAR))
-    {
-	new = ml_get_curline();
-	len = (colnr_T)STRLEN(new);
-	if (curwin->w_cursor.col == len)
-	{
-	    pnew = vim_strnsave(new, len + 2);
-	    pnew[len] = ' ';
-	    pnew[len + 1] = NUL;
-	    ml_replace(curwin->w_cursor.lnum, pnew, FALSE);
-	    // remove the space later
-	    did_add_space = TRUE;
-	}
-	else
-	    // may remove added space
-	    check_auto_format(FALSE);
-    }
-
-    check_cursor();
-}
-
-/*
- * When an extra space was added to continue a paragraph for auto-formatting,
- * delete it now.  The space must be under the cursor, just after the insert
- * position.
- */
-    static void
-check_auto_format(
-    int		end_insert)	    // TRUE when ending Insert mode
-{
-    int		c = ' ';
-    int		cc;
-
-    if (did_add_space)
-    {
-	cc = gchar_cursor();
-	if (!WHITECHAR(cc))
-	    // Somehow the space was removed already.
-	    did_add_space = FALSE;
-	else
-	{
-	    if (!end_insert)
-	    {
-		inc_cursor();
-		c = gchar_cursor();
-		dec_cursor();
-	    }
-	    if (c != NUL)
-	    {
-		// The space is no longer at the end of the line, delete it.
-		del_char(FALSE);
-		did_add_space = FALSE;
-	    }
-	}
-    }
-}
-
-/*
- * Find out textwidth to be used for formatting:
- *	if 'textwidth' option is set, use it
- *	else if 'wrapmargin' option is set, use curwin->w_width - 'wrapmargin'
- *	if invalid value, use 0.
- *	Set default to window width (maximum 79) for "gq" operator.
- */
-    int
-comp_textwidth(
-    int		ff)	// force formatting (for "gq" command)
-{
-    int		textwidth;
-
-    textwidth = curbuf->b_p_tw;
-    if (textwidth == 0 && curbuf->b_p_wm)
-    {
-	// The width is the window width minus 'wrapmargin' minus all the
-	// things that add to the margin.
-	textwidth = curwin->w_width - curbuf->b_p_wm;
-#ifdef FEAT_CMDWIN
-	if (cmdwin_type != 0)
-	    textwidth -= 1;
-#endif
-#ifdef FEAT_FOLDING
-	textwidth -= curwin->w_p_fdc;
-#endif
-#ifdef FEAT_SIGNS
-	if (signcolumn_on(curwin))
-	    textwidth -= 1;
-#endif
-	if (curwin->w_p_nu || curwin->w_p_rnu)
-	    textwidth -= 8;
-    }
-    if (textwidth < 0)
-	textwidth = 0;
-    if (ff && textwidth == 0)
-    {
-	textwidth = curwin->w_width - 1;
-	if (textwidth > 79)
-	    textwidth = 79;
-    }
-    return textwidth;
-}
-
-/*
  * Put a character in the redo buffer, for when just after a CTRL-V.
  */
     static void
--- a/src/getchar.c
+++ b/src/getchar.c
@@ -685,6 +685,46 @@ stuffnumReadbuff(long n)
 }
 
 /*
+ * Stuff a string into the typeahead buffer, such that edit() will insert it
+ * literally ("literally" TRUE) or interpret is as typed characters.
+ */
+    void
+stuffescaped(char_u *arg, int literally)
+{
+    int		c;
+    char_u	*start;
+
+    while (*arg != NUL)
+    {
+	// Stuff a sequence of normal ASCII characters, that's fast.  Also
+	// stuff K_SPECIAL to get the effect of a special key when "literally"
+	// is TRUE.
+	start = arg;
+	while ((*arg >= ' '
+#ifndef EBCDIC
+		    && *arg < DEL // EBCDIC: chars above space are normal
+#endif
+		    )
+		|| (*arg == K_SPECIAL && !literally))
+	    ++arg;
+	if (arg > start)
+	    stuffReadbuffLen(start, (long)(arg - start));
+
+	// stuff a single special character
+	if (*arg != NUL)
+	{
+	    if (has_mbyte)
+		c = mb_cptr2char_adv(&arg);
+	    else
+		c = *arg++;
+	    if (literally && ((c < ' ' && c != TAB) || c == DEL))
+		stuffcharReadbuff(Ctrl_V);
+	    stuffcharReadbuff(c);
+	}
+    }
+}
+
+/*
  * Read a character from the redo buffer.  Translates K_SPECIAL, CSI and
  * multibyte characters.
  * The redo buffer is left as it is.
--- a/src/ops.c
+++ b/src/ops.c
@@ -17,8 +17,6 @@
 static void shift_block(oparg_T *oap, int amount);
 static void	mb_adjust_opend(oparg_T *oap);
 static int	do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1);
-static int	ends_in_white(linenr_T lnum);
-static int	fmt_check_par(linenr_T, int *, char_u **, int do_comments);
 
 // Flags for third item in "opchars".
 #define OPF_LINES  1	// operator always works on lines
@@ -591,46 +589,6 @@ block_insert(
 }
 
 /*
- * Stuff a string into the typeahead buffer, such that edit() will insert it
- * literally ("literally" TRUE) or interpret is as typed characters.
- */
-    void
-stuffescaped(char_u *arg, int literally)
-{
-    int		c;
-    char_u	*start;
-
-    while (*arg != NUL)
-    {
-	// Stuff a sequence of normal ASCII characters, that's fast.  Also
-	// stuff K_SPECIAL to get the effect of a special key when "literally"
-	// is TRUE.
-	start = arg;
-	while ((*arg >= ' '
-#ifndef EBCDIC
-		    && *arg < DEL // EBCDIC: chars above space are normal
-#endif
-		    )
-		|| (*arg == K_SPECIAL && !literally))
-	    ++arg;
-	if (arg > start)
-	    stuffReadbuffLen(start, (long)(arg - start));
-
-	// stuff a single special character
-	if (*arg != NUL)
-	{
-	    if (has_mbyte)
-		c = mb_cptr2char_adv(&arg);
-	    else
-		c = *arg++;
-	    if (literally && ((c < ' ' && c != TAB) || c == DEL))
-		stuffcharReadbuff(Ctrl_V);
-	    stuffcharReadbuff(c);
-	}
-    }
-}
-
-/*
  * Handle a delete operation.
  *
  * Return FAIL if undo failed, OK otherwise.
@@ -2171,554 +2129,6 @@ theend:
 }
 
 /*
- * Return TRUE if the two comment leaders given are the same.  "lnum" is
- * the first line.  White-space is ignored.  Note that the whole of
- * 'leader1' must match 'leader2_len' characters from 'leader2' -- webb
- */
-    static int
-same_leader(
-    linenr_T lnum,
-    int	    leader1_len,
-    char_u  *leader1_flags,
-    int	    leader2_len,
-    char_u  *leader2_flags)
-{
-    int	    idx1 = 0, idx2 = 0;
-    char_u  *p;
-    char_u  *line1;
-    char_u  *line2;
-
-    if (leader1_len == 0)
-	return (leader2_len == 0);
-
-    /*
-     * If first leader has 'f' flag, the lines can be joined only if the
-     * second line does not have a leader.
-     * If first leader has 'e' flag, the lines can never be joined.
-     * If fist leader has 's' flag, the lines can only be joined if there is
-     * some text after it and the second line has the 'm' flag.
-     */
-    if (leader1_flags != NULL)
-    {
-	for (p = leader1_flags; *p && *p != ':'; ++p)
-	{
-	    if (*p == COM_FIRST)
-		return (leader2_len == 0);
-	    if (*p == COM_END)
-		return FALSE;
-	    if (*p == COM_START)
-	    {
-		if (*(ml_get(lnum) + leader1_len) == NUL)
-		    return FALSE;
-		if (leader2_flags == NULL || leader2_len == 0)
-		    return FALSE;
-		for (p = leader2_flags; *p && *p != ':'; ++p)
-		    if (*p == COM_MIDDLE)
-			return TRUE;
-		return FALSE;
-	    }
-	}
-    }
-
-    /*
-     * Get current line and next line, compare the leaders.
-     * The first line has to be saved, only one line can be locked at a time.
-     */
-    line1 = vim_strsave(ml_get(lnum));
-    if (line1 != NULL)
-    {
-	for (idx1 = 0; VIM_ISWHITE(line1[idx1]); ++idx1)
-	    ;
-	line2 = ml_get(lnum + 1);
-	for (idx2 = 0; idx2 < leader2_len; ++idx2)
-	{
-	    if (!VIM_ISWHITE(line2[idx2]))
-	    {
-		if (line1[idx1++] != line2[idx2])
-		    break;
-	    }
-	    else
-		while (VIM_ISWHITE(line1[idx1]))
-		    ++idx1;
-	}
-	vim_free(line1);
-    }
-    return (idx2 == leader2_len && idx1 == leader1_len);
-}
-
-/*
- * Implementation of the format operator 'gq'.
- */
-    static void
-op_format(
-    oparg_T	*oap,
-    int		keep_cursor)		// keep cursor on same text char
-{
-    long	old_line_count = curbuf->b_ml.ml_line_count;
-
-    // Place the cursor where the "gq" or "gw" command was given, so that "u"
-    // can put it back there.
-    curwin->w_cursor = oap->cursor_start;
-
-    if (u_save((linenr_T)(oap->start.lnum - 1),
-				       (linenr_T)(oap->end.lnum + 1)) == FAIL)
-	return;
-    curwin->w_cursor = oap->start;
-
-    if (oap->is_VIsual)
-	// When there is no change: need to remove the Visual selection
-	redraw_curbuf_later(INVERTED);
-
-    if (!cmdmod.lockmarks)
-	// Set '[ mark at the start of the formatted area
-	curbuf->b_op_start = oap->start;
-
-    // For "gw" remember the cursor position and put it back below (adjusted
-    // for joined and split lines).
-    if (keep_cursor)
-	saved_cursor = oap->cursor_start;
-
-    format_lines(oap->line_count, keep_cursor);
-
-    /*
-     * Leave the cursor at the first non-blank of the last formatted line.
-     * If the cursor was moved one line back (e.g. with "Q}") go to the next
-     * line, so "." will do the next lines.
-     */
-    if (oap->end_adjusted && curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count)
-	++curwin->w_cursor.lnum;
-    beginline(BL_WHITE | BL_FIX);
-    old_line_count = curbuf->b_ml.ml_line_count - old_line_count;
-    msgmore(old_line_count);
-
-    if (!cmdmod.lockmarks)
-	// put '] mark on the end of the formatted area
-	curbuf->b_op_end = curwin->w_cursor;
-
-    if (keep_cursor)
-    {
-	curwin->w_cursor = saved_cursor;
-	saved_cursor.lnum = 0;
-    }
-
-    if (oap->is_VIsual)
-    {
-	win_T	*wp;
-
-	FOR_ALL_WINDOWS(wp)
-	{
-	    if (wp->w_old_cursor_lnum != 0)
-	    {
-		// When lines have been inserted or deleted, adjust the end of
-		// the Visual area to be redrawn.
-		if (wp->w_old_cursor_lnum > wp->w_old_visual_lnum)
-		    wp->w_old_cursor_lnum += old_line_count;
-		else
-		    wp->w_old_visual_lnum += old_line_count;
-	    }
-	}
-    }
-}
-
-#if defined(FEAT_EVAL) || defined(PROTO)
-/*
- * Implementation of the format operator 'gq' for when using 'formatexpr'.
- */
-    static void
-op_formatexpr(oparg_T *oap)
-{
-    if (oap->is_VIsual)
-	// When there is no change: need to remove the Visual selection
-	redraw_curbuf_later(INVERTED);
-
-    if (fex_format(oap->start.lnum, oap->line_count, NUL) != 0)
-	// As documented: when 'formatexpr' returns non-zero fall back to
-	// internal formatting.
-	op_format(oap, FALSE);
-}
-
-    int
-fex_format(
-    linenr_T	lnum,
-    long	count,
-    int		c)	// character to be inserted
-{
-    int		use_sandbox = was_set_insecurely((char_u *)"formatexpr",
-								   OPT_LOCAL);
-    int		r;
-    char_u	*fex;
-
-    /*
-     * Set v:lnum to the first line number and v:count to the number of lines.
-     * Set v:char to the character to be inserted (can be NUL).
-     */
-    set_vim_var_nr(VV_LNUM, lnum);
-    set_vim_var_nr(VV_COUNT, count);
-    set_vim_var_char(c);
-
-    // Make a copy, the option could be changed while calling it.
-    fex = vim_strsave(curbuf->b_p_fex);
-    if (fex == NULL)
-	return 0;
-
-    /*
-     * Evaluate the function.
-     */
-    if (use_sandbox)
-	++sandbox;
-    r = (int)eval_to_number(fex);
-    if (use_sandbox)
-	--sandbox;
-
-    set_vim_var_string(VV_CHAR, NULL, -1);
-    vim_free(fex);
-
-    return r;
-}
-#endif
-
-/*
- * Format "line_count" lines, starting at the cursor position.
- * When "line_count" is negative, format until the end of the paragraph.
- * Lines after the cursor line are saved for undo, caller must have saved the
- * first line.
- */
-    void
-format_lines(
-    linenr_T	line_count,
-    int		avoid_fex)		// don't use 'formatexpr'
-{
-    int		max_len;
-    int		is_not_par;		// current line not part of parag.
-    int		next_is_not_par;	// next line not part of paragraph
-    int		is_end_par;		// at end of paragraph
-    int		prev_is_end_par = FALSE;// prev. line not part of parag.
-    int		next_is_start_par = FALSE;
-    int		leader_len = 0;		// leader len of current line
-    int		next_leader_len;	// leader len of next line
-    char_u	*leader_flags = NULL;	// flags for leader of current line
-    char_u	*next_leader_flags;	// flags for leader of next line
-    int		do_comments;		// format comments
-    int		do_comments_list = 0;	// format comments with 'n' or '2'
-    int		advance = TRUE;
-    int		second_indent = -1;	// indent for second line (comment
-					// aware)
-    int		do_second_indent;
-    int		do_number_indent;
-    int		do_trail_white;
-    int		first_par_line = TRUE;
-    int		smd_save;
-    long	count;
-    int		need_set_indent = TRUE;	// set indent of next paragraph
-    int		force_format = FALSE;
-    int		old_State = State;
-
-    // length of a line to force formatting: 3 * 'tw'
-    max_len = comp_textwidth(TRUE) * 3;
-
-    // check for 'q', '2' and '1' in 'formatoptions'
-    do_comments = has_format_option(FO_Q_COMS);
-    do_second_indent = has_format_option(FO_Q_SECOND);
-    do_number_indent = has_format_option(FO_Q_NUMBER);
-    do_trail_white = has_format_option(FO_WHITE_PAR);
-
-    /*
-     * Get info about the previous and current line.
-     */
-    if (curwin->w_cursor.lnum > 1)
-	is_not_par = fmt_check_par(curwin->w_cursor.lnum - 1
-				, &leader_len, &leader_flags, do_comments);
-    else
-	is_not_par = TRUE;
-    next_is_not_par = fmt_check_par(curwin->w_cursor.lnum
-			  , &next_leader_len, &next_leader_flags, do_comments);
-    is_end_par = (is_not_par || next_is_not_par);
-    if (!is_end_par && do_trail_white)
-	is_end_par = !ends_in_white(curwin->w_cursor.lnum - 1);
-
-    curwin->w_cursor.lnum--;
-    for (count = line_count; count != 0 && !got_int; --count)
-    {
-	/*
-	 * Advance to next paragraph.
-	 */
-	if (advance)
-	{
-	    curwin->w_cursor.lnum++;
-	    prev_is_end_par = is_end_par;
-	    is_not_par = next_is_not_par;
-	    leader_len = next_leader_len;
-	    leader_flags = next_leader_flags;
-	}
-
-	/*
-	 * The last line to be formatted.
-	 */
-	if (count == 1 || curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count)
-	{
-	    next_is_not_par = TRUE;
-	    next_leader_len = 0;
-	    next_leader_flags = NULL;
-	}
-	else
-	{
-	    next_is_not_par = fmt_check_par(curwin->w_cursor.lnum + 1
-			  , &next_leader_len, &next_leader_flags, do_comments);
-	    if (do_number_indent)
-		next_is_start_par =
-			   (get_number_indent(curwin->w_cursor.lnum + 1) > 0);
-	}
-	advance = TRUE;
-	is_end_par = (is_not_par || next_is_not_par || next_is_start_par);
-	if (!is_end_par && do_trail_white)
-	    is_end_par = !ends_in_white(curwin->w_cursor.lnum);
-
-	/*
-	 * Skip lines that are not in a paragraph.
-	 */
-	if (is_not_par)
-	{
-	    if (line_count < 0)
-		break;
-	}
-	else
-	{
-	    /*
-	     * For the first line of a paragraph, check indent of second line.
-	     * Don't do this for comments and empty lines.
-	     */
-	    if (first_par_line
-		    && (do_second_indent || do_number_indent)
-		    && prev_is_end_par
-		    && curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count)
-	    {
-		if (do_second_indent && !LINEEMPTY(curwin->w_cursor.lnum + 1))
-		{
-		    if (leader_len == 0 && next_leader_len == 0)
-		    {
-			// no comment found
-			second_indent =
-				   get_indent_lnum(curwin->w_cursor.lnum + 1);
-		    }
-		    else
-		    {
-			second_indent = next_leader_len;
-			do_comments_list = 1;
-		    }
-		}
-		else if (do_number_indent)
-		{
-		    if (leader_len == 0 && next_leader_len == 0)
-		    {
-			// no comment found
-			second_indent =
-				     get_number_indent(curwin->w_cursor.lnum);
-		    }
-		    else
-		    {
-			// get_number_indent() is now "comment aware"...
-			second_indent =
-				     get_number_indent(curwin->w_cursor.lnum);
-			do_comments_list = 1;
-		    }
-		}
-	    }
-
-	    /*
-	     * When the comment leader changes, it's the end of the paragraph.
-	     */
-	    if (curwin->w_cursor.lnum >= curbuf->b_ml.ml_line_count
-		    || !same_leader(curwin->w_cursor.lnum,
-					leader_len, leader_flags,
-					   next_leader_len, next_leader_flags))
-		is_end_par = TRUE;
-
-	    /*
-	     * If we have got to the end of a paragraph, or the line is
-	     * getting long, format it.
-	     */
-	    if (is_end_par || force_format)
-	    {
-		if (need_set_indent)
-		    // replace indent in first line with minimal number of
-		    // tabs and spaces, according to current options
-		    (void)set_indent(get_indent(), SIN_CHANGED);
-
-		// put cursor on last non-space
-		State = NORMAL;	// don't go past end-of-line
-		coladvance((colnr_T)MAXCOL);
-		while (curwin->w_cursor.col && vim_isspace(gchar_cursor()))
-		    dec_cursor();
-
-		// do the formatting, without 'showmode'
-		State = INSERT;	// for open_line()
-		smd_save = p_smd;
-		p_smd = FALSE;
-		insertchar(NUL, INSCHAR_FORMAT
-			+ (do_comments ? INSCHAR_DO_COM : 0)
-			+ (do_comments && do_comments_list
-						       ? INSCHAR_COM_LIST : 0)
-			+ (avoid_fex ? INSCHAR_NO_FEX : 0), second_indent);
-		State = old_State;
-		p_smd = smd_save;
-		second_indent = -1;
-		// at end of par.: need to set indent of next par.
-		need_set_indent = is_end_par;
-		if (is_end_par)
-		{
-		    // When called with a negative line count, break at the
-		    // end of the paragraph.
-		    if (line_count < 0)
-			break;
-		    first_par_line = TRUE;
-		}
-		force_format = FALSE;
-	    }
-
-	    /*
-	     * When still in same paragraph, join the lines together.  But
-	     * first delete the leader from the second line.
-	     */
-	    if (!is_end_par)
-	    {
-		advance = FALSE;
-		curwin->w_cursor.lnum++;
-		curwin->w_cursor.col = 0;
-		if (line_count < 0 && u_save_cursor() == FAIL)
-		    break;
-		if (next_leader_len > 0)
-		{
-		    (void)del_bytes((long)next_leader_len, FALSE, FALSE);
-		    mark_col_adjust(curwin->w_cursor.lnum, (colnr_T)0, 0L,
-						    (long)-next_leader_len, 0);
-		}
-		else if (second_indent > 0)  // the "leader" for FO_Q_SECOND
-		{
-		    int indent = getwhitecols_curline();
-
-		    if (indent > 0)
-		    {
-			(void)del_bytes(indent, FALSE, FALSE);
-			mark_col_adjust(curwin->w_cursor.lnum,
-					       (colnr_T)0, 0L, (long)-indent, 0);
-		    }
-		}
-		curwin->w_cursor.lnum--;
-		if (do_join(2, TRUE, FALSE, FALSE, FALSE) == FAIL)
-		{
-		    beep_flush();
-		    break;
-		}
-		first_par_line = FALSE;
-		// If the line is getting long, format it next time
-		if (STRLEN(ml_get_curline()) > (size_t)max_len)
-		    force_format = TRUE;
-		else
-		    force_format = FALSE;
-	    }
-	}
-	line_breakcheck();
-    }
-}
-
-/*
- * Return TRUE if line "lnum" ends in a white character.
- */
-    static int
-ends_in_white(linenr_T lnum)
-{
-    char_u	*s = ml_get(lnum);
-    size_t	l;
-
-    if (*s == NUL)
-	return FALSE;
-    // Don't use STRLEN() inside VIM_ISWHITE(), SAS/C complains: "macro
-    // invocation may call function multiple times".
-    l = STRLEN(s) - 1;
-    return VIM_ISWHITE(s[l]);
-}
-
-/*
- * Blank lines, and lines containing only the comment leader, are left
- * untouched by the formatting.  The function returns TRUE in this
- * case.  It also returns TRUE when a line starts with the end of a comment
- * ('e' in comment flags), so that this line is skipped, and not joined to the
- * previous line.  A new paragraph starts after a blank line, or when the
- * comment leader changes -- webb.
- */
-    static int
-fmt_check_par(
-    linenr_T	lnum,
-    int		*leader_len,
-    char_u	**leader_flags,
-    int		do_comments)
-{
-    char_u	*flags = NULL;	    // init for GCC
-    char_u	*ptr;
-
-    ptr = ml_get(lnum);
-    if (do_comments)
-	*leader_len = get_leader_len(ptr, leader_flags, FALSE, TRUE);
-    else
-	*leader_len = 0;
-
-    if (*leader_len > 0)
-    {
-	/*
-	 * Search for 'e' flag in comment leader flags.
-	 */
-	flags = *leader_flags;
-	while (*flags && *flags != ':' && *flags != COM_END)
-	    ++flags;
-    }
-
-    return (*skipwhite(ptr + *leader_len) == NUL
-	    || (*leader_len > 0 && *flags == COM_END)
-	    || startPS(lnum, NUL, FALSE));
-}
-
-/*
- * Return TRUE when a paragraph starts in line "lnum".  Return FALSE when the
- * previous line is in the same paragraph.  Used for auto-formatting.
- */
-    int
-paragraph_start(linenr_T lnum)
-{
-    char_u	*p;
-    int		leader_len = 0;		// leader len of current line
-    char_u	*leader_flags = NULL;	// flags for leader of current line
-    int		next_leader_len;	// leader len of next line
-    char_u	*next_leader_flags;	// flags for leader of next line
-    int		do_comments;		// format comments
-
-    if (lnum <= 1)
-	return TRUE;		// start of the file
-
-    p = ml_get(lnum - 1);
-    if (*p == NUL)
-	return TRUE;		// after empty line
-
-    do_comments = has_format_option(FO_Q_COMS);
-    if (fmt_check_par(lnum - 1, &leader_len, &leader_flags, do_comments))
-	return TRUE;		// after non-paragraph line
-
-    if (fmt_check_par(lnum, &next_leader_len, &next_leader_flags, do_comments))
-	return TRUE;		// "lnum" is not a paragraph line
-
-    if (has_format_option(FO_WHITE_PAR) && !ends_in_white(lnum - 1))
-	return TRUE;		// missing trailing space in previous line.
-
-    if (has_format_option(FO_Q_NUMBER) && (get_number_indent(lnum) > 0))
-	return TRUE;		// numbered item starts in "lnum".
-
-    if (!same_leader(lnum - 1, leader_len, leader_flags,
-					  next_leader_len, next_leader_flags))
-	return TRUE;		// change of comment leader.
-
-    return FALSE;
-}
-
-/*
  * prepare a few things for block mode yank/delete/tilde
  *
  * for delete:
--- a/src/option.c
+++ b/src/option.c
@@ -6492,18 +6492,6 @@ wc_use_keyname(char_u *varp, long *wcp)
 }
 
 /*
- * Return TRUE if format option 'x' is in effect.
- * Take care of no formatting when 'paste' is set.
- */
-    int
-has_format_option(int x)
-{
-    if (p_paste)
-	return FALSE;
-    return (vim_strchr(curbuf->b_p_fo, x) != NULL);
-}
-
-/*
  * Return TRUE if "x" is present in 'shortmess' option, or
  * 'shortmess' contains 'a' and "x" is present in SHM_A.
  */
--- a/src/proto.h
+++ b/src/proto.h
@@ -224,6 +224,7 @@ void mbyte_im_set_active(int active_arg)
 # endif
 # include "testing.pro"
 # include "textobject.pro"
+# include "textformat.pro"
 # include "time.pro"
 # include "ui.pro"
 # include "undo.pro"
--- a/src/proto/edit.pro
+++ b/src/proto/edit.pro
@@ -13,8 +13,6 @@ void truncate_spaces(char_u *line);
 void backspace_until_column(int col);
 int get_literal(void);
 void insertchar(int c, int flags, int second_indent);
-void auto_format(int trailblank, int prev_line);
-int comp_textwidth(int ff);
 void start_arrow(pos_T *end_insert_pos);
 int stop_arrow(void);
 void set_last_insert(int c);
--- a/src/proto/getchar.pro
+++ b/src/proto/getchar.pro
@@ -19,6 +19,7 @@ void stuffReadbuffLen(char_u *s, long le
 void stuffReadbuffSpec(char_u *s);
 void stuffcharReadbuff(int c);
 void stuffnumReadbuff(long n);
+void stuffescaped(char_u *arg, int literally);
 int start_redo(long count, int old_redo);
 int start_redo_ins(void);
 void stop_redo_ins(void);
--- a/src/proto/ops.pro
+++ b/src/proto/ops.pro
@@ -5,7 +5,6 @@ int get_op_char(int optype);
 int get_extra_op_char(int optype);
 void op_shift(oparg_T *oap, int curs_top, int amount);
 void shift_line(int left, int round, int amount, int call_changed_bytes);
-void stuffescaped(char_u *arg, int literally);
 int op_delete(oparg_T *oap);
 int op_replace(oparg_T *oap, int c);
 int swapchar(int op_type, pos_T *pos);
@@ -14,9 +13,6 @@ int op_change(oparg_T *oap);
 void adjust_cursor_eol(void);
 char_u *skip_comment(char_u *line, int process, int include_space, int *is_comment);
 int do_join(long count, int insert_space, int save_undo, int use_formatoptions, int setmark);
-int fex_format(linenr_T lnum, long count, int c);
-void format_lines(linenr_T line_count, int avoid_fex);
-int paragraph_start(linenr_T lnum);
 void block_prep(oparg_T *oap, struct block_def *bdp, linenr_T lnum, int is_del);
 void op_addsub(oparg_T *oap, linenr_T Prenum1, int g_cmd);
 void clear_oparg(oparg_T *oap);
--- a/src/proto/option.pro
+++ b/src/proto/option.pro
@@ -62,7 +62,6 @@ void set_imsearch_global(void);
 void set_context_in_set_cmd(expand_T *xp, char_u *arg, int opt_flags);
 int ExpandSettings(expand_T *xp, regmatch_T *regmatch, int *num_file, char_u ***file);
 int ExpandOldSetting(int *num_file, char_u ***file);
-int has_format_option(int x);
 int shortmess(int x);
 void vimrc_found(char_u *fname, char_u *envname);
 void change_compatible(int on);
new file mode 100644
--- /dev/null
+++ b/src/proto/textformat.pro
@@ -0,0 +1,11 @@
+/* textformat.c */
+int has_format_option(int x);
+void internal_format(int textwidth, int second_indent, int flags, int format_only, int c);
+void auto_format(int trailblank, int prev_line);
+void check_auto_format(int);
+int comp_textwidth(int ff);
+void op_format(oparg_T *oap, int keep_cursor);
+void op_formatexpr(oparg_T *oap);
+int fex_format(linenr_T lnum, long count, int c);
+void format_lines(linenr_T line_count, int avoid_fex);
+/* vim: set ft=c : */
new file mode 100644
--- /dev/null
+++ b/src/textformat.c
@@ -0,0 +1,1088 @@
+/* vi:set ts=8 sts=4 sw=4 noet:
+ *
+ * VIM - Vi IMproved	by Bram Moolenaar
+ *
+ * Do ":help uganda"  in Vim to read copying and usage conditions.
+ * Do ":help credits" in Vim to see a list of people who contributed.
+ * See README.txt for an overview of the Vim source code.
+ */
+
+/*
+ * textformat.c: text formatting functions
+ */
+
+#include "vim.h"
+
+static int	did_add_space = FALSE;	// auto_format() added an extra space
+					// under the cursor
+
+#define WHITECHAR(cc) (VIM_ISWHITE(cc) && (!enc_utf8 || !utf_iscomposing(utf_ptr2char(ml_get_cursor() + 1))))
+
+/*
+ * Return TRUE if format option 'x' is in effect.
+ * Take care of no formatting when 'paste' is set.
+ */
+    int
+has_format_option(int x)
+{
+    if (p_paste)
+	return FALSE;
+    return (vim_strchr(curbuf->b_p_fo, x) != NULL);
+}
+
+/*
+ * Format text at the current insert position.
+ *
+ * If the INSCHAR_COM_LIST flag is present, then the value of second_indent
+ * will be the comment leader length sent to open_line().
+ */
+    void
+internal_format(
+    int		textwidth,
+    int		second_indent,
+    int		flags,
+    int		format_only,
+    int		c) // character to be inserted (can be NUL)
+{
+    int		cc;
+    int		save_char = NUL;
+    int		haveto_redraw = FALSE;
+    int		fo_ins_blank = has_format_option(FO_INS_BLANK);
+    int		fo_multibyte = has_format_option(FO_MBYTE_BREAK);
+    int		fo_white_par = has_format_option(FO_WHITE_PAR);
+    int		first_line = TRUE;
+    colnr_T	leader_len;
+    int		no_leader = FALSE;
+    int		do_comments = (flags & INSCHAR_DO_COM);
+#ifdef FEAT_LINEBREAK
+    int		has_lbr = curwin->w_p_lbr;
+
+    // make sure win_lbr_chartabsize() counts correctly
+    curwin->w_p_lbr = FALSE;
+#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 && !(State & VREPLACE_FLAG))
+    {
+	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;
+	int	orig_col = 0;
+	char_u	*saved_text = NULL;
+	colnr_T	col;
+	colnr_T	end_col;
+	int	wcc;			// counter for whitespace chars
+
+	virtcol = get_nolist_virtcol()
+		+ char2cells(c != NUL ? c : gchar_cursor());
+	if (virtcol <= (colnr_T)textwidth)
+	    break;
+
+	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, TRUE);
+	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;
+	if (!(flags & INSCHAR_FORMAT)
+		&& leader_len == 0
+		&& !has_format_option(FO_WRAP))
+
+	    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;
+	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))
+		    || (flags & INSCHAR_FORMAT)
+		    || curwin->w_cursor.lnum != Insstart.lnum
+		    || curwin->w_cursor.col >= Insstart.col)
+	{
+	    if (curwin->w_cursor.col == startcol && c != NUL)
+		cc = c;
+	    else
+		cc = gchar_cursor();
+	    if (WHITECHAR(cc))
+	    {
+		// remember position of blank just before text
+		end_col = curwin->w_cursor.col;
+
+		// find start of sequence of blanks
+		wcc = 0;
+		while (curwin->w_cursor.col > 0 && WHITECHAR(cc))
+		{
+		    dec_cursor();
+		    cc = gchar_cursor();
+
+		    // Increment count of how many whitespace chars in this
+		    // group; we only need to know if it's more than one.
+		    if (wcc < 2)
+		        wcc++;
+		}
+		if (curwin->w_cursor.col == 0 && WHITECHAR(cc))
+		    break;		// only spaces in front of text
+
+		// Don't break after a period when 'formatoptions' has 'p' and
+		// there are less than two spaces.
+		if (has_format_option(FO_PERIOD_ABBR) && cc == '.' && wcc < 2)
+		    continue;
+
+		// Don't break until after the comment leader
+		if (curwin->w_cursor.col < leader_len)
+		    break;
+		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
+		    // do not break "#a b" when 'tw' is 2
+		    if (curwin->w_cursor.col <= leader_len)
+			break;
+		    col = curwin->w_cursor.col;
+		    dec_cursor();
+		    cc = gchar_cursor();
+
+		    if (WHITECHAR(cc))
+			continue;	// one-letter, continue
+		    curwin->w_cursor.col = col;
+		}
+
+		inc_cursor();
+
+		end_foundcol = end_col + 1;
+		foundcol = curwin->w_cursor.col;
+		if (curwin->w_cursor.col <= (colnr_T)wantcol)
+		    break;
+	    }
+	    else if (cc >= 0x100 && fo_multibyte)
+	    {
+		// Break after or before a multi-byte character.
+		if (curwin->w_cursor.col != startcol)
+		{
+		    // Don't break until after the comment leader
+		    if (curwin->w_cursor.col < leader_len)
+			break;
+		    col = curwin->w_cursor.col;
+		    inc_cursor();
+		    // Don't change end_foundcol if already set.
+		    if (foundcol != curwin->w_cursor.col)
+		    {
+			foundcol = curwin->w_cursor.col;
+			end_foundcol = foundcol;
+			if (curwin->w_cursor.col <= (colnr_T)wantcol)
+			    break;
+		    }
+		    curwin->w_cursor.col = col;
+		}
+
+		if (curwin->w_cursor.col == 0)
+		    break;
+
+		col = curwin->w_cursor.col;
+
+		dec_cursor();
+		cc = gchar_cursor();
+
+		if (WHITECHAR(cc))
+		    continue;		// break with space
+		// Don't break until after the comment leader
+		if (curwin->w_cursor.col < leader_len)
+		    break;
+
+		curwin->w_cursor.col = col;
+
+		foundcol = curwin->w_cursor.col;
+		end_foundcol = foundcol;
+		if (curwin->w_cursor.col <= (colnr_T)wantcol)
+		    break;
+	    }
+	    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.
+	if (State & VREPLACE_FLAG)
+	    orig_col = startcol;	// Will start backspacing from here
+	else
+	    replace_offset = startcol - end_foundcol;
+
+	// 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))
+		    && (!fo_white_par || curwin->w_cursor.col < startcol))
+	    inc_cursor();
+	startcol -= curwin->w_cursor.col;
+	if (startcol < 0)
+	    startcol = 0;
+
+	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
+	{
+	    // 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)
+		+ (do_comments ? OPENLINE_DO_COM : 0)
+		+ ((flags & INSCHAR_COM_LIST) ? OPENLINE_COM_LIST : 0)
+		, ((flags & INSCHAR_COM_LIST) ? second_indent : old_indent));
+	if (!(flags & INSCHAR_COM_LIST))
+	    old_indent = 0;
+
+	replace_offset = 0;
+	if (first_line)
+	{
+	    if (!(flags & INSCHAR_COM_LIST))
+	    {
+		// This section is for auto-wrap of numeric lists.  When not
+		// in insert mode (i.e. format_lines()), the INSCHAR_COM_LIST
+		// flag will be set and open_line() will handle it (as seen
+		// above).  The code here (and in get_number_indent()) will
+		// recognize comments if needed...
+		if (second_indent < 0 && has_format_option(FO_Q_NUMBER))
+		    second_indent =
+				 get_number_indent(curwin->w_cursor.lnum - 1);
+		if (second_indent >= 0)
+		{
+		    if (State & VREPLACE_FLAG)
+			change_indent(INDENT_SET, second_indent,
+							    FALSE, NUL, TRUE);
+		    else
+			if (leader_len > 0 && second_indent - leader_len > 0)
+		    {
+			int i;
+			int padding = second_indent - leader_len;
+
+			// We started at the first_line of a numbered list
+			// that has a comment.  the open_line() function has
+			// inserted the proper comment leader and positioned
+			// the cursor at the end of the split line.  Now we
+			// add the additional whitespace needed after the
+			// comment leader for the numbered list.
+			for (i = 0; i < padding; i++)
+			    ins_str((char_u *)" ");
+		    }
+		    else
+		    {
+			(void)set_indent(second_indent, SIN_CHANGED);
+		    }
+		}
+	    }
+	    first_line = FALSE;
+	}
+
+	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
+	{
+	    // 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
+	set_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);
+
+#ifdef FEAT_LINEBREAK
+    curwin->w_p_lbr = has_lbr;
+#endif
+    if (!format_only && haveto_redraw)
+    {
+	update_topline();
+	redraw_curbuf_later(VALID);
+    }
+}
+
+/*
+ * Blank lines, and lines containing only the comment leader, are left
+ * untouched by the formatting.  The function returns TRUE in this
+ * case.  It also returns TRUE when a line starts with the end of a comment
+ * ('e' in comment flags), so that this line is skipped, and not joined to the
+ * previous line.  A new paragraph starts after a blank line, or when the
+ * comment leader changes -- webb.
+ */
+    static int
+fmt_check_par(
+    linenr_T	lnum,
+    int		*leader_len,
+    char_u	**leader_flags,
+    int		do_comments)
+{
+    char_u	*flags = NULL;	    // init for GCC
+    char_u	*ptr;
+
+    ptr = ml_get(lnum);
+    if (do_comments)
+	*leader_len = get_leader_len(ptr, leader_flags, FALSE, TRUE);
+    else
+	*leader_len = 0;
+
+    if (*leader_len > 0)
+    {
+	// Search for 'e' flag in comment leader flags.
+	flags = *leader_flags;
+	while (*flags && *flags != ':' && *flags != COM_END)
+	    ++flags;
+    }
+
+    return (*skipwhite(ptr + *leader_len) == NUL
+	    || (*leader_len > 0 && *flags == COM_END)
+	    || startPS(lnum, NUL, FALSE));
+}
+
+/*
+ * Return TRUE if line "lnum" ends in a white character.
+ */
+    static int
+ends_in_white(linenr_T lnum)
+{
+    char_u	*s = ml_get(lnum);
+    size_t	l;
+
+    if (*s == NUL)
+	return FALSE;
+    // Don't use STRLEN() inside VIM_ISWHITE(), SAS/C complains: "macro
+    // invocation may call function multiple times".
+    l = STRLEN(s) - 1;
+    return VIM_ISWHITE(s[l]);
+}
+
+/*
+ * Return TRUE if the two comment leaders given are the same.  "lnum" is
+ * the first line.  White-space is ignored.  Note that the whole of
+ * 'leader1' must match 'leader2_len' characters from 'leader2' -- webb
+ */
+    static int
+same_leader(
+    linenr_T lnum,
+    int	    leader1_len,
+    char_u  *leader1_flags,
+    int	    leader2_len,
+    char_u  *leader2_flags)
+{
+    int	    idx1 = 0, idx2 = 0;
+    char_u  *p;
+    char_u  *line1;
+    char_u  *line2;
+
+    if (leader1_len == 0)
+	return (leader2_len == 0);
+
+    // If first leader has 'f' flag, the lines can be joined only if the
+    // second line does not have a leader.
+    // If first leader has 'e' flag, the lines can never be joined.
+    // If fist leader has 's' flag, the lines can only be joined if there is
+    // some text after it and the second line has the 'm' flag.
+    if (leader1_flags != NULL)
+    {
+	for (p = leader1_flags; *p && *p != ':'; ++p)
+	{
+	    if (*p == COM_FIRST)
+		return (leader2_len == 0);
+	    if (*p == COM_END)
+		return FALSE;
+	    if (*p == COM_START)
+	    {
+		if (*(ml_get(lnum) + leader1_len) == NUL)
+		    return FALSE;
+		if (leader2_flags == NULL || leader2_len == 0)
+		    return FALSE;
+		for (p = leader2_flags; *p && *p != ':'; ++p)
+		    if (*p == COM_MIDDLE)
+			return TRUE;
+		return FALSE;
+	    }
+	}
+    }
+
+    // Get current line and next line, compare the leaders.
+    // The first line has to be saved, only one line can be locked at a time.
+    line1 = vim_strsave(ml_get(lnum));
+    if (line1 != NULL)
+    {
+	for (idx1 = 0; VIM_ISWHITE(line1[idx1]); ++idx1)
+	    ;
+	line2 = ml_get(lnum + 1);
+	for (idx2 = 0; idx2 < leader2_len; ++idx2)
+	{
+	    if (!VIM_ISWHITE(line2[idx2]))
+	    {
+		if (line1[idx1++] != line2[idx2])
+		    break;
+	    }
+	    else
+		while (VIM_ISWHITE(line1[idx1]))
+		    ++idx1;
+	}
+	vim_free(line1);
+    }
+    return (idx2 == leader2_len && idx1 == leader1_len);
+}
+
+/*
+ * Return TRUE when a paragraph starts in line "lnum".  Return FALSE when the
+ * previous line is in the same paragraph.  Used for auto-formatting.
+ */
+    static int
+paragraph_start(linenr_T lnum)
+{
+    char_u	*p;
+    int		leader_len = 0;		// leader len of current line
+    char_u	*leader_flags = NULL;	// flags for leader of current line
+    int		next_leader_len;	// leader len of next line
+    char_u	*next_leader_flags;	// flags for leader of next line
+    int		do_comments;		// format comments
+
+    if (lnum <= 1)
+	return TRUE;		// start of the file
+
+    p = ml_get(lnum - 1);
+    if (*p == NUL)
+	return TRUE;		// after empty line
+
+    do_comments = has_format_option(FO_Q_COMS);
+    if (fmt_check_par(lnum - 1, &leader_len, &leader_flags, do_comments))
+	return TRUE;		// after non-paragraph line
+
+    if (fmt_check_par(lnum, &next_leader_len, &next_leader_flags, do_comments))
+	return TRUE;		// "lnum" is not a paragraph line
+
+    if (has_format_option(FO_WHITE_PAR) && !ends_in_white(lnum - 1))
+	return TRUE;		// missing trailing space in previous line.
+
+    if (has_format_option(FO_Q_NUMBER) && (get_number_indent(lnum) > 0))
+	return TRUE;		// numbered item starts in "lnum".
+
+    if (!same_leader(lnum - 1, leader_len, leader_flags,
+					  next_leader_len, next_leader_flags))
+	return TRUE;		// change of comment leader.
+
+    return FALSE;
+}
+
+/*
+ * 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.
+ * The caller must have saved the cursor line for undo, following ones will be
+ * saved here.
+ */
+    void
+auto_format(
+    int		trailblank,	// when TRUE also format with trailing blank
+    int		prev_line)	// may start in previous line
+{
+    pos_T	pos;
+    colnr_T	len;
+    char_u	*old;
+    char_u	*new, *pnew;
+    int		wasatend;
+    int		cc;
+
+    if (!has_format_option(FO_AUTO))
+	return;
+
+    pos = curwin->w_cursor;
+    old = ml_get_curline();
+
+    // may remove added space
+    check_auto_format(FALSE);
+
+    // Don't format in Insert mode when the cursor is on a trailing blank, the
+    // user might insert normal text next.  Also skip formatting when "1" is
+    // in 'formatoptions' and there is a single character before the cursor.
+    // Otherwise the line would be broken and when typing another non-white
+    // next they are not joined back together.
+    wasatend = (pos.col == (colnr_T)STRLEN(old));
+    if (*old != NUL && !trailblank && wasatend)
+    {
+	dec_cursor();
+	cc = gchar_cursor();
+	if (!WHITECHAR(cc) && curwin->w_cursor.col > 0
+					  && has_format_option(FO_ONE_LETTER))
+	    dec_cursor();
+	cc = gchar_cursor();
+	if (WHITECHAR(cc))
+	{
+	    curwin->w_cursor = pos;
+	    return;
+	}
+	curwin->w_cursor = pos;
+    }
+
+    // With the 'c' flag in 'formatoptions' and 't' missing: only format
+    // comments.
+    if (has_format_option(FO_WRAP_COMS) && !has_format_option(FO_WRAP)
+				&& get_leader_len(old, NULL, FALSE, TRUE) == 0)
+	return;
+
+    // May start formatting in a previous line, so that after "x" a word is
+    // moved to the previous line if it fits there now.  Only when this is not
+    // the start of a paragraph.
+    if (prev_line && !paragraph_start(curwin->w_cursor.lnum))
+    {
+	--curwin->w_cursor.lnum;
+	if (u_save_cursor() == FAIL)
+	    return;
+    }
+
+    // Do the formatting and restore the cursor position.  "saved_cursor" will
+    // be adjusted for the text formatting.
+    saved_cursor = pos;
+    format_lines((linenr_T)-1, FALSE);
+    curwin->w_cursor = saved_cursor;
+    saved_cursor.lnum = 0;
+
+    if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count)
+    {
+	// "cannot happen"
+	curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
+	coladvance((colnr_T)MAXCOL);
+    }
+    else
+	check_cursor_col();
+
+    // Insert mode: If the cursor is now after the end of the line while it
+    // previously wasn't, the line was broken.  Because of the rule above we
+    // need to add a space when 'w' is in 'formatoptions' to keep a paragraph
+    // formatted.
+    if (!wasatend && has_format_option(FO_WHITE_PAR))
+    {
+	new = ml_get_curline();
+	len = (colnr_T)STRLEN(new);
+	if (curwin->w_cursor.col == len)
+	{
+	    pnew = vim_strnsave(new, len + 2);
+	    pnew[len] = ' ';
+	    pnew[len + 1] = NUL;
+	    ml_replace(curwin->w_cursor.lnum, pnew, FALSE);
+	    // remove the space later
+	    did_add_space = TRUE;
+	}
+	else
+	    // may remove added space
+	    check_auto_format(FALSE);
+    }
+
+    check_cursor();
+}
+
+/*
+ * When an extra space was added to continue a paragraph for auto-formatting,
+ * delete it now.  The space must be under the cursor, just after the insert
+ * position.
+ */
+    void
+check_auto_format(
+    int		end_insert)	    // TRUE when ending Insert mode
+{
+    int		c = ' ';
+    int		cc;
+
+    if (did_add_space)
+    {
+	cc = gchar_cursor();
+	if (!WHITECHAR(cc))
+	    // Somehow the space was removed already.
+	    did_add_space = FALSE;
+	else
+	{
+	    if (!end_insert)
+	    {
+		inc_cursor();
+		c = gchar_cursor();
+		dec_cursor();
+	    }
+	    if (c != NUL)
+	    {
+		// The space is no longer at the end of the line, delete it.
+		del_char(FALSE);
+		did_add_space = FALSE;
+	    }
+	}
+    }
+}
+
+/*
+ * Find out textwidth to be used for formatting:
+ *	if 'textwidth' option is set, use it
+ *	else if 'wrapmargin' option is set, use curwin->w_width - 'wrapmargin'
+ *	if invalid value, use 0.
+ *	Set default to window width (maximum 79) for "gq" operator.
+ */
+    int
+comp_textwidth(
+    int		ff)	// force formatting (for "gq" command)
+{
+    int		textwidth;
+
+    textwidth = curbuf->b_p_tw;
+    if (textwidth == 0 && curbuf->b_p_wm)
+    {
+	// The width is the window width minus 'wrapmargin' minus all the
+	// things that add to the margin.
+	textwidth = curwin->w_width - curbuf->b_p_wm;
+#ifdef FEAT_CMDWIN
+	if (cmdwin_type != 0)
+	    textwidth -= 1;
+#endif
+#ifdef FEAT_FOLDING
+	textwidth -= curwin->w_p_fdc;
+#endif
+#ifdef FEAT_SIGNS
+	if (signcolumn_on(curwin))
+	    textwidth -= 1;
+#endif
+	if (curwin->w_p_nu || curwin->w_p_rnu)
+	    textwidth -= 8;
+    }
+    if (textwidth < 0)
+	textwidth = 0;
+    if (ff && textwidth == 0)
+    {
+	textwidth = curwin->w_width - 1;
+	if (textwidth > 79)
+	    textwidth = 79;
+    }
+    return textwidth;
+}
+
+/*
+ * Implementation of the format operator 'gq'.
+ */
+    void
+op_format(
+    oparg_T	*oap,
+    int		keep_cursor)		// keep cursor on same text char
+{
+    long	old_line_count = curbuf->b_ml.ml_line_count;
+
+    // Place the cursor where the "gq" or "gw" command was given, so that "u"
+    // can put it back there.
+    curwin->w_cursor = oap->cursor_start;
+
+    if (u_save((linenr_T)(oap->start.lnum - 1),
+				       (linenr_T)(oap->end.lnum + 1)) == FAIL)
+	return;
+    curwin->w_cursor = oap->start;
+
+    if (oap->is_VIsual)
+	// When there is no change: need to remove the Visual selection
+	redraw_curbuf_later(INVERTED);
+
+    if (!cmdmod.lockmarks)
+	// Set '[ mark at the start of the formatted area
+	curbuf->b_op_start = oap->start;
+
+    // For "gw" remember the cursor position and put it back below (adjusted
+    // for joined and split lines).
+    if (keep_cursor)
+	saved_cursor = oap->cursor_start;
+
+    format_lines(oap->line_count, keep_cursor);
+
+    // Leave the cursor at the first non-blank of the last formatted line.
+    // If the cursor was moved one line back (e.g. with "Q}") go to the next
+    // line, so "." will do the next lines.
+    if (oap->end_adjusted && curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count)
+	++curwin->w_cursor.lnum;
+    beginline(BL_WHITE | BL_FIX);
+    old_line_count = curbuf->b_ml.ml_line_count - old_line_count;
+    msgmore(old_line_count);
+
+    if (!cmdmod.lockmarks)
+	// put '] mark on the end of the formatted area
+	curbuf->b_op_end = curwin->w_cursor;
+
+    if (keep_cursor)
+    {
+	curwin->w_cursor = saved_cursor;
+	saved_cursor.lnum = 0;
+    }
+
+    if (oap->is_VIsual)
+    {
+	win_T	*wp;
+
+	FOR_ALL_WINDOWS(wp)
+	{
+	    if (wp->w_old_cursor_lnum != 0)
+	    {
+		// When lines have been inserted or deleted, adjust the end of
+		// the Visual area to be redrawn.
+		if (wp->w_old_cursor_lnum > wp->w_old_visual_lnum)
+		    wp->w_old_cursor_lnum += old_line_count;
+		else
+		    wp->w_old_visual_lnum += old_line_count;
+	    }
+	}
+    }
+}
+
+#if defined(FEAT_EVAL) || defined(PROTO)
+/*
+ * Implementation of the format operator 'gq' for when using 'formatexpr'.
+ */
+    void
+op_formatexpr(oparg_T *oap)
+{
+    if (oap->is_VIsual)
+	// When there is no change: need to remove the Visual selection
+	redraw_curbuf_later(INVERTED);
+
+    if (fex_format(oap->start.lnum, oap->line_count, NUL) != 0)
+	// As documented: when 'formatexpr' returns non-zero fall back to
+	// internal formatting.
+	op_format(oap, FALSE);
+}
+
+    int
+fex_format(
+    linenr_T	lnum,
+    long	count,
+    int		c)	// character to be inserted
+{
+    int		use_sandbox = was_set_insecurely((char_u *)"formatexpr",
+								   OPT_LOCAL);
+    int		r;
+    char_u	*fex;
+
+    // Set v:lnum to the first line number and v:count to the number of lines.
+    // Set v:char to the character to be inserted (can be NUL).
+    set_vim_var_nr(VV_LNUM, lnum);
+    set_vim_var_nr(VV_COUNT, count);
+    set_vim_var_char(c);
+
+    // Make a copy, the option could be changed while calling it.
+    fex = vim_strsave(curbuf->b_p_fex);
+    if (fex == NULL)
+	return 0;
+
+    // Evaluate the function.
+    if (use_sandbox)
+	++sandbox;
+    r = (int)eval_to_number(fex);
+    if (use_sandbox)
+	--sandbox;
+
+    set_vim_var_string(VV_CHAR, NULL, -1);
+    vim_free(fex);
+
+    return r;
+}
+#endif
+
+/*
+ * Format "line_count" lines, starting at the cursor position.
+ * When "line_count" is negative, format until the end of the paragraph.
+ * Lines after the cursor line are saved for undo, caller must have saved the
+ * first line.
+ */
+    void
+format_lines(
+    linenr_T	line_count,
+    int		avoid_fex)		// don't use 'formatexpr'
+{
+    int		max_len;
+    int		is_not_par;		// current line not part of parag.
+    int		next_is_not_par;	// next line not part of paragraph
+    int		is_end_par;		// at end of paragraph
+    int		prev_is_end_par = FALSE;// prev. line not part of parag.
+    int		next_is_start_par = FALSE;
+    int		leader_len = 0;		// leader len of current line
+    int		next_leader_len;	// leader len of next line
+    char_u	*leader_flags = NULL;	// flags for leader of current line
+    char_u	*next_leader_flags;	// flags for leader of next line
+    int		do_comments;		// format comments
+    int		do_comments_list = 0;	// format comments with 'n' or '2'
+    int		advance = TRUE;
+    int		second_indent = -1;	// indent for second line (comment
+					// aware)
+    int		do_second_indent;
+    int		do_number_indent;
+    int		do_trail_white;
+    int		first_par_line = TRUE;
+    int		smd_save;
+    long	count;
+    int		need_set_indent = TRUE;	// set indent of next paragraph
+    int		force_format = FALSE;
+    int		old_State = State;
+
+    // length of a line to force formatting: 3 * 'tw'
+    max_len = comp_textwidth(TRUE) * 3;
+
+    // check for 'q', '2' and '1' in 'formatoptions'
+    do_comments = has_format_option(FO_Q_COMS);
+    do_second_indent = has_format_option(FO_Q_SECOND);
+    do_number_indent = has_format_option(FO_Q_NUMBER);
+    do_trail_white = has_format_option(FO_WHITE_PAR);
+
+    // Get info about the previous and current line.
+    if (curwin->w_cursor.lnum > 1)
+	is_not_par = fmt_check_par(curwin->w_cursor.lnum - 1
+				, &leader_len, &leader_flags, do_comments);
+    else
+	is_not_par = TRUE;
+    next_is_not_par = fmt_check_par(curwin->w_cursor.lnum
+			  , &next_leader_len, &next_leader_flags, do_comments);
+    is_end_par = (is_not_par || next_is_not_par);
+    if (!is_end_par && do_trail_white)
+	is_end_par = !ends_in_white(curwin->w_cursor.lnum - 1);
+
+    curwin->w_cursor.lnum--;
+    for (count = line_count; count != 0 && !got_int; --count)
+    {
+	// Advance to next paragraph.
+	if (advance)
+	{
+	    curwin->w_cursor.lnum++;
+	    prev_is_end_par = is_end_par;
+	    is_not_par = next_is_not_par;
+	    leader_len = next_leader_len;
+	    leader_flags = next_leader_flags;
+	}
+
+	// The last line to be formatted.
+	if (count == 1 || curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count)
+	{
+	    next_is_not_par = TRUE;
+	    next_leader_len = 0;
+	    next_leader_flags = NULL;
+	}
+	else
+	{
+	    next_is_not_par = fmt_check_par(curwin->w_cursor.lnum + 1
+			  , &next_leader_len, &next_leader_flags, do_comments);
+	    if (do_number_indent)
+		next_is_start_par =
+			   (get_number_indent(curwin->w_cursor.lnum + 1) > 0);
+	}
+	advance = TRUE;
+	is_end_par = (is_not_par || next_is_not_par || next_is_start_par);
+	if (!is_end_par && do_trail_white)
+	    is_end_par = !ends_in_white(curwin->w_cursor.lnum);
+
+	// Skip lines that are not in a paragraph.
+	if (is_not_par)
+	{
+	    if (line_count < 0)
+		break;
+	}
+	else
+	{
+	    // For the first line of a paragraph, check indent of second line.
+	    // Don't do this for comments and empty lines.
+	    if (first_par_line
+		    && (do_second_indent || do_number_indent)
+		    && prev_is_end_par
+		    && curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count)
+	    {
+		if (do_second_indent && !LINEEMPTY(curwin->w_cursor.lnum + 1))
+		{
+		    if (leader_len == 0 && next_leader_len == 0)
+		    {
+			// no comment found
+			second_indent =
+				   get_indent_lnum(curwin->w_cursor.lnum + 1);
+		    }
+		    else
+		    {
+			second_indent = next_leader_len;
+			do_comments_list = 1;
+		    }
+		}
+		else if (do_number_indent)
+		{
+		    if (leader_len == 0 && next_leader_len == 0)
+		    {
+			// no comment found
+			second_indent =
+				     get_number_indent(curwin->w_cursor.lnum);
+		    }
+		    else
+		    {
+			// get_number_indent() is now "comment aware"...
+			second_indent =
+				     get_number_indent(curwin->w_cursor.lnum);
+			do_comments_list = 1;
+		    }
+		}
+	    }
+
+	    // When the comment leader changes, it's the end of the paragraph.
+	    if (curwin->w_cursor.lnum >= curbuf->b_ml.ml_line_count
+		    || !same_leader(curwin->w_cursor.lnum,
+					leader_len, leader_flags,
+					   next_leader_len, next_leader_flags))
+		is_end_par = TRUE;
+
+	    // If we have got to the end of a paragraph, or the line is
+	    // getting long, format it.
+	    if (is_end_par || force_format)
+	    {
+		if (need_set_indent)
+		    // replace indent in first line with minimal number of
+		    // tabs and spaces, according to current options
+		    (void)set_indent(get_indent(), SIN_CHANGED);
+
+		// put cursor on last non-space
+		State = NORMAL;	// don't go past end-of-line
+		coladvance((colnr_T)MAXCOL);
+		while (curwin->w_cursor.col && vim_isspace(gchar_cursor()))
+		    dec_cursor();
+
+		// do the formatting, without 'showmode'
+		State = INSERT;	// for open_line()
+		smd_save = p_smd;
+		p_smd = FALSE;
+		insertchar(NUL, INSCHAR_FORMAT
+			+ (do_comments ? INSCHAR_DO_COM : 0)
+			+ (do_comments && do_comments_list
+						       ? INSCHAR_COM_LIST : 0)
+			+ (avoid_fex ? INSCHAR_NO_FEX : 0), second_indent);
+		State = old_State;
+		p_smd = smd_save;
+		second_indent = -1;
+		// at end of par.: need to set indent of next par.
+		need_set_indent = is_end_par;
+		if (is_end_par)
+		{
+		    // When called with a negative line count, break at the
+		    // end of the paragraph.
+		    if (line_count < 0)
+			break;
+		    first_par_line = TRUE;
+		}
+		force_format = FALSE;
+	    }
+
+	    // When still in same paragraph, join the lines together.  But
+	    // first delete the leader from the second line.
+	    if (!is_end_par)
+	    {
+		advance = FALSE;
+		curwin->w_cursor.lnum++;
+		curwin->w_cursor.col = 0;
+		if (line_count < 0 && u_save_cursor() == FAIL)
+		    break;
+		if (next_leader_len > 0)
+		{
+		    (void)del_bytes((long)next_leader_len, FALSE, FALSE);
+		    mark_col_adjust(curwin->w_cursor.lnum, (colnr_T)0, 0L,
+						    (long)-next_leader_len, 0);
+		}
+		else if (second_indent > 0)  // the "leader" for FO_Q_SECOND
+		{
+		    int indent = getwhitecols_curline();
+
+		    if (indent > 0)
+		    {
+			(void)del_bytes(indent, FALSE, FALSE);
+			mark_col_adjust(curwin->w_cursor.lnum,
+					       (colnr_T)0, 0L, (long)-indent, 0);
+		    }
+		}
+		curwin->w_cursor.lnum--;
+		if (do_join(2, TRUE, FALSE, FALSE, FALSE) == FAIL)
+		{
+		    beep_flush();
+		    break;
+		}
+		first_par_line = FALSE;
+		// If the line is getting long, format it next time
+		if (STRLEN(ml_get_curline()) > (size_t)max_len)
+		    force_format = TRUE;
+		else
+		    force_format = FALSE;
+	    }
+	}
+	line_breakcheck();
+    }
+}
--- a/src/version.c
+++ b/src/version.c
@@ -747,6 +747,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    674,
+/**/
     673,
 /**/
     672,