111abd095SBram Moolenaar /* vi:set ts=8 sts=4 sw=4 noet:
211abd095SBram Moolenaar *
311abd095SBram Moolenaar * VIM - Vi IMproved by Bram Moolenaar
411abd095SBram Moolenaar *
511abd095SBram Moolenaar * Do ":help uganda" in Vim to read copying and usage conditions.
611abd095SBram Moolenaar * Do ":help credits" in Vim to see a list of people who contributed.
711abd095SBram Moolenaar * See README.txt for an overview of the Vim source code.
811abd095SBram Moolenaar */
911abd095SBram Moolenaar
1011abd095SBram Moolenaar /*
1111abd095SBram Moolenaar * textformat.c: text formatting functions
1211abd095SBram Moolenaar */
1311abd095SBram Moolenaar
1411abd095SBram Moolenaar #include "vim.h"
1511abd095SBram Moolenaar
1611abd095SBram Moolenaar static int did_add_space = FALSE; // auto_format() added an extra space
1711abd095SBram Moolenaar // under the cursor
1811abd095SBram Moolenaar
1911abd095SBram Moolenaar #define WHITECHAR(cc) (VIM_ISWHITE(cc) && (!enc_utf8 || !utf_iscomposing(utf_ptr2char(ml_get_cursor() + 1))))
2011abd095SBram Moolenaar
2111abd095SBram Moolenaar /*
2211abd095SBram Moolenaar * Return TRUE if format option 'x' is in effect.
2311abd095SBram Moolenaar * Take care of no formatting when 'paste' is set.
2411abd095SBram Moolenaar */
2511abd095SBram Moolenaar int
has_format_option(int x)2611abd095SBram Moolenaar has_format_option(int x)
2711abd095SBram Moolenaar {
2811abd095SBram Moolenaar if (p_paste)
2911abd095SBram Moolenaar return FALSE;
3011abd095SBram Moolenaar return (vim_strchr(curbuf->b_p_fo, x) != NULL);
3111abd095SBram Moolenaar }
3211abd095SBram Moolenaar
3311abd095SBram Moolenaar /*
3411abd095SBram Moolenaar * Format text at the current insert position.
3511abd095SBram Moolenaar *
3611abd095SBram Moolenaar * If the INSCHAR_COM_LIST flag is present, then the value of second_indent
3711abd095SBram Moolenaar * will be the comment leader length sent to open_line().
3811abd095SBram Moolenaar */
3911abd095SBram Moolenaar void
internal_format(int textwidth,int second_indent,int flags,int format_only,int c)4011abd095SBram Moolenaar internal_format(
4111abd095SBram Moolenaar int textwidth,
4211abd095SBram Moolenaar int second_indent,
4311abd095SBram Moolenaar int flags,
4411abd095SBram Moolenaar int format_only,
4511abd095SBram Moolenaar int c) // character to be inserted (can be NUL)
4611abd095SBram Moolenaar {
4711abd095SBram Moolenaar int cc;
48e52702f0SBram Moolenaar int skip_pos;
4911abd095SBram Moolenaar int save_char = NUL;
5011abd095SBram Moolenaar int haveto_redraw = FALSE;
5111abd095SBram Moolenaar int fo_ins_blank = has_format_option(FO_INS_BLANK);
5211abd095SBram Moolenaar int fo_multibyte = has_format_option(FO_MBYTE_BREAK);
53e52702f0SBram Moolenaar int fo_rigor_tw = has_format_option(FO_RIGOROUS_TW);
5411abd095SBram Moolenaar int fo_white_par = has_format_option(FO_WHITE_PAR);
5511abd095SBram Moolenaar int first_line = TRUE;
5611abd095SBram Moolenaar colnr_T leader_len;
5711abd095SBram Moolenaar int no_leader = FALSE;
5811abd095SBram Moolenaar int do_comments = (flags & INSCHAR_DO_COM);
5911abd095SBram Moolenaar #ifdef FEAT_LINEBREAK
6011abd095SBram Moolenaar int has_lbr = curwin->w_p_lbr;
6111abd095SBram Moolenaar
6211abd095SBram Moolenaar // make sure win_lbr_chartabsize() counts correctly
6311abd095SBram Moolenaar curwin->w_p_lbr = FALSE;
6411abd095SBram Moolenaar #endif
6511abd095SBram Moolenaar
6611abd095SBram Moolenaar // When 'ai' is off we don't want a space under the cursor to be
6711abd095SBram Moolenaar // deleted. Replace it with an 'x' temporarily.
6811abd095SBram Moolenaar if (!curbuf->b_p_ai && !(State & VREPLACE_FLAG))
6911abd095SBram Moolenaar {
7011abd095SBram Moolenaar cc = gchar_cursor();
7111abd095SBram Moolenaar if (VIM_ISWHITE(cc))
7211abd095SBram Moolenaar {
7311abd095SBram Moolenaar save_char = cc;
7411abd095SBram Moolenaar pchar_cursor('x');
7511abd095SBram Moolenaar }
7611abd095SBram Moolenaar }
7711abd095SBram Moolenaar
7811abd095SBram Moolenaar // Repeat breaking lines, until the current line is not too long.
7911abd095SBram Moolenaar while (!got_int)
8011abd095SBram Moolenaar {
8111abd095SBram Moolenaar int startcol; // Cursor column at entry
8211abd095SBram Moolenaar int wantcol; // column at textwidth border
8311abd095SBram Moolenaar int foundcol; // column for start of spaces
8411abd095SBram Moolenaar int end_foundcol = 0; // column for start of word
8511abd095SBram Moolenaar colnr_T len;
8611abd095SBram Moolenaar colnr_T virtcol;
8711abd095SBram Moolenaar int orig_col = 0;
8811abd095SBram Moolenaar char_u *saved_text = NULL;
8911abd095SBram Moolenaar colnr_T col;
9011abd095SBram Moolenaar colnr_T end_col;
9111abd095SBram Moolenaar int wcc; // counter for whitespace chars
9211abd095SBram Moolenaar
9311abd095SBram Moolenaar virtcol = get_nolist_virtcol()
9411abd095SBram Moolenaar + char2cells(c != NUL ? c : gchar_cursor());
9511abd095SBram Moolenaar if (virtcol <= (colnr_T)textwidth)
9611abd095SBram Moolenaar break;
9711abd095SBram Moolenaar
9811abd095SBram Moolenaar if (no_leader)
9911abd095SBram Moolenaar do_comments = FALSE;
10011abd095SBram Moolenaar else if (!(flags & INSCHAR_FORMAT)
10111abd095SBram Moolenaar && has_format_option(FO_WRAP_COMS))
10211abd095SBram Moolenaar do_comments = TRUE;
10311abd095SBram Moolenaar
10411abd095SBram Moolenaar // Don't break until after the comment leader
10511abd095SBram Moolenaar if (do_comments)
10611abd095SBram Moolenaar leader_len = get_leader_len(ml_get_curline(), NULL, FALSE, TRUE);
10711abd095SBram Moolenaar else
10811abd095SBram Moolenaar leader_len = 0;
10911abd095SBram Moolenaar
11011abd095SBram Moolenaar // If the line doesn't start with a comment leader, then don't
11111abd095SBram Moolenaar // start one in a following broken line. Avoids that a %word
11211abd095SBram Moolenaar // moved to the start of the next line causes all following lines
11311abd095SBram Moolenaar // to start with %.
11411abd095SBram Moolenaar if (leader_len == 0)
11511abd095SBram Moolenaar no_leader = TRUE;
11611abd095SBram Moolenaar if (!(flags & INSCHAR_FORMAT)
11711abd095SBram Moolenaar && leader_len == 0
11811abd095SBram Moolenaar && !has_format_option(FO_WRAP))
11911abd095SBram Moolenaar
12011abd095SBram Moolenaar break;
12111abd095SBram Moolenaar if ((startcol = curwin->w_cursor.col) == 0)
12211abd095SBram Moolenaar break;
12311abd095SBram Moolenaar
12411abd095SBram Moolenaar // find column of textwidth border
12511abd095SBram Moolenaar coladvance((colnr_T)textwidth);
12611abd095SBram Moolenaar wantcol = curwin->w_cursor.col;
12711abd095SBram Moolenaar
12811abd095SBram Moolenaar curwin->w_cursor.col = startcol;
12911abd095SBram Moolenaar foundcol = 0;
130e52702f0SBram Moolenaar skip_pos = 0;
13111abd095SBram Moolenaar
13211abd095SBram Moolenaar // Find position to break at.
13311abd095SBram Moolenaar // Stop at first entered white when 'formatoptions' has 'v'
13411abd095SBram Moolenaar while ((!fo_ins_blank && !has_format_option(FO_INS_VI))
13511abd095SBram Moolenaar || (flags & INSCHAR_FORMAT)
13611abd095SBram Moolenaar || curwin->w_cursor.lnum != Insstart.lnum
13711abd095SBram Moolenaar || curwin->w_cursor.col >= Insstart.col)
13811abd095SBram Moolenaar {
13911abd095SBram Moolenaar if (curwin->w_cursor.col == startcol && c != NUL)
14011abd095SBram Moolenaar cc = c;
14111abd095SBram Moolenaar else
14211abd095SBram Moolenaar cc = gchar_cursor();
14311abd095SBram Moolenaar if (WHITECHAR(cc))
14411abd095SBram Moolenaar {
14511abd095SBram Moolenaar // remember position of blank just before text
14611abd095SBram Moolenaar end_col = curwin->w_cursor.col;
14711abd095SBram Moolenaar
14811abd095SBram Moolenaar // find start of sequence of blanks
14911abd095SBram Moolenaar wcc = 0;
15011abd095SBram Moolenaar while (curwin->w_cursor.col > 0 && WHITECHAR(cc))
15111abd095SBram Moolenaar {
15211abd095SBram Moolenaar dec_cursor();
15311abd095SBram Moolenaar cc = gchar_cursor();
15411abd095SBram Moolenaar
15511abd095SBram Moolenaar // Increment count of how many whitespace chars in this
15611abd095SBram Moolenaar // group; we only need to know if it's more than one.
15711abd095SBram Moolenaar if (wcc < 2)
15811abd095SBram Moolenaar wcc++;
15911abd095SBram Moolenaar }
16011abd095SBram Moolenaar if (curwin->w_cursor.col == 0 && WHITECHAR(cc))
16111abd095SBram Moolenaar break; // only spaces in front of text
16211abd095SBram Moolenaar
16311abd095SBram Moolenaar // Don't break after a period when 'formatoptions' has 'p' and
16411abd095SBram Moolenaar // there are less than two spaces.
16511abd095SBram Moolenaar if (has_format_option(FO_PERIOD_ABBR) && cc == '.' && wcc < 2)
16611abd095SBram Moolenaar continue;
16711abd095SBram Moolenaar
16811abd095SBram Moolenaar // Don't break until after the comment leader
16911abd095SBram Moolenaar if (curwin->w_cursor.col < leader_len)
17011abd095SBram Moolenaar break;
17111abd095SBram Moolenaar if (has_format_option(FO_ONE_LETTER))
17211abd095SBram Moolenaar {
17311abd095SBram Moolenaar // do not break after one-letter words
17411abd095SBram Moolenaar if (curwin->w_cursor.col == 0)
17511abd095SBram Moolenaar break; // one-letter word at begin
17611abd095SBram Moolenaar // do not break "#a b" when 'tw' is 2
17711abd095SBram Moolenaar if (curwin->w_cursor.col <= leader_len)
17811abd095SBram Moolenaar break;
17911abd095SBram Moolenaar col = curwin->w_cursor.col;
18011abd095SBram Moolenaar dec_cursor();
18111abd095SBram Moolenaar cc = gchar_cursor();
18211abd095SBram Moolenaar
18311abd095SBram Moolenaar if (WHITECHAR(cc))
18411abd095SBram Moolenaar continue; // one-letter, continue
18511abd095SBram Moolenaar curwin->w_cursor.col = col;
18611abd095SBram Moolenaar }
18711abd095SBram Moolenaar
18811abd095SBram Moolenaar inc_cursor();
18911abd095SBram Moolenaar
19011abd095SBram Moolenaar end_foundcol = end_col + 1;
19111abd095SBram Moolenaar foundcol = curwin->w_cursor.col;
19211abd095SBram Moolenaar if (curwin->w_cursor.col <= (colnr_T)wantcol)
19311abd095SBram Moolenaar break;
19411abd095SBram Moolenaar }
195e52702f0SBram Moolenaar else if ((cc >= 0x100 || !utf_allow_break_before(cc)) && fo_multibyte)
19611abd095SBram Moolenaar {
197e52702f0SBram Moolenaar int ncc;
198e52702f0SBram Moolenaar int allow_break;
199e52702f0SBram Moolenaar
20011abd095SBram Moolenaar // Break after or before a multi-byte character.
20111abd095SBram Moolenaar if (curwin->w_cursor.col != startcol)
20211abd095SBram Moolenaar {
20311abd095SBram Moolenaar // Don't break until after the comment leader
20411abd095SBram Moolenaar if (curwin->w_cursor.col < leader_len)
20511abd095SBram Moolenaar break;
20611abd095SBram Moolenaar col = curwin->w_cursor.col;
20711abd095SBram Moolenaar inc_cursor();
208e52702f0SBram Moolenaar ncc = gchar_cursor();
209e52702f0SBram Moolenaar
210e52702f0SBram Moolenaar allow_break =
211e52702f0SBram Moolenaar (enc_utf8 && utf_allow_break(cc, ncc))
212e52702f0SBram Moolenaar || enc_dbcs;
213e52702f0SBram Moolenaar
214e52702f0SBram Moolenaar // If we have already checked this position, skip!
215e52702f0SBram Moolenaar if (curwin->w_cursor.col != skip_pos && allow_break)
21611abd095SBram Moolenaar {
21711abd095SBram Moolenaar foundcol = curwin->w_cursor.col;
21811abd095SBram Moolenaar end_foundcol = foundcol;
21911abd095SBram Moolenaar if (curwin->w_cursor.col <= (colnr_T)wantcol)
22011abd095SBram Moolenaar break;
22111abd095SBram Moolenaar }
22211abd095SBram Moolenaar curwin->w_cursor.col = col;
22311abd095SBram Moolenaar }
22411abd095SBram Moolenaar
22511abd095SBram Moolenaar if (curwin->w_cursor.col == 0)
22611abd095SBram Moolenaar break;
22711abd095SBram Moolenaar
228e52702f0SBram Moolenaar ncc = cc;
22911abd095SBram Moolenaar col = curwin->w_cursor.col;
23011abd095SBram Moolenaar
23111abd095SBram Moolenaar dec_cursor();
23211abd095SBram Moolenaar cc = gchar_cursor();
23311abd095SBram Moolenaar
23411abd095SBram Moolenaar if (WHITECHAR(cc))
23511abd095SBram Moolenaar continue; // break with space
236e52702f0SBram Moolenaar // Don't break until after the comment leader.
23711abd095SBram Moolenaar if (curwin->w_cursor.col < leader_len)
23811abd095SBram Moolenaar break;
23911abd095SBram Moolenaar
24011abd095SBram Moolenaar curwin->w_cursor.col = col;
241e52702f0SBram Moolenaar skip_pos = curwin->w_cursor.col;
24211abd095SBram Moolenaar
243e52702f0SBram Moolenaar allow_break =
244e52702f0SBram Moolenaar (enc_utf8 && utf_allow_break(cc, ncc))
245e52702f0SBram Moolenaar || enc_dbcs;
246e52702f0SBram Moolenaar
247e52702f0SBram Moolenaar // Must handle this to respect line break prohibition.
248e52702f0SBram Moolenaar if (allow_break)
249e52702f0SBram Moolenaar {
25011abd095SBram Moolenaar foundcol = curwin->w_cursor.col;
25111abd095SBram Moolenaar end_foundcol = foundcol;
252e52702f0SBram Moolenaar }
25311abd095SBram Moolenaar if (curwin->w_cursor.col <= (colnr_T)wantcol)
254e52702f0SBram Moolenaar {
255e52702f0SBram Moolenaar int ncc_allow_break =
256e52702f0SBram Moolenaar (enc_utf8 && utf_allow_break_before(ncc)) || enc_dbcs;
257e52702f0SBram Moolenaar
258e52702f0SBram Moolenaar if (allow_break)
25911abd095SBram Moolenaar break;
260e52702f0SBram Moolenaar if (!ncc_allow_break && !fo_rigor_tw)
261e52702f0SBram Moolenaar {
262e52702f0SBram Moolenaar // Enable at most 1 punct hang outside of textwidth.
263e52702f0SBram Moolenaar if (curwin->w_cursor.col == startcol)
264e52702f0SBram Moolenaar {
265e52702f0SBram Moolenaar // We are inserting a non-breakable char, postpone
266e52702f0SBram Moolenaar // line break check to next insert.
267e52702f0SBram Moolenaar end_foundcol = foundcol = 0;
268e52702f0SBram Moolenaar break;
269e52702f0SBram Moolenaar }
270e52702f0SBram Moolenaar
271e52702f0SBram Moolenaar // Neither cc nor ncc is NUL if we are here, so
272e52702f0SBram Moolenaar // it's safe to inc_cursor.
273e52702f0SBram Moolenaar col = curwin->w_cursor.col;
274e52702f0SBram Moolenaar
275e52702f0SBram Moolenaar inc_cursor();
276e52702f0SBram Moolenaar cc = ncc;
277e52702f0SBram Moolenaar ncc = gchar_cursor();
278e52702f0SBram Moolenaar // handle insert
279e52702f0SBram Moolenaar ncc = (ncc != NUL) ? ncc : c;
280e52702f0SBram Moolenaar
281e52702f0SBram Moolenaar allow_break =
282e52702f0SBram Moolenaar (enc_utf8 && utf_allow_break(cc, ncc))
283e52702f0SBram Moolenaar || enc_dbcs;
284e52702f0SBram Moolenaar
285e52702f0SBram Moolenaar if (allow_break)
286e52702f0SBram Moolenaar {
287e52702f0SBram Moolenaar // Break only when we are not at end of line.
288e52702f0SBram Moolenaar end_foundcol = foundcol =
289e52702f0SBram Moolenaar ncc == NUL? 0 : curwin->w_cursor.col;
290e52702f0SBram Moolenaar break;
291e52702f0SBram Moolenaar }
292e52702f0SBram Moolenaar curwin->w_cursor.col = col;
293e52702f0SBram Moolenaar }
294e52702f0SBram Moolenaar }
29511abd095SBram Moolenaar }
29611abd095SBram Moolenaar if (curwin->w_cursor.col == 0)
29711abd095SBram Moolenaar break;
29811abd095SBram Moolenaar dec_cursor();
29911abd095SBram Moolenaar }
30011abd095SBram Moolenaar
30111abd095SBram Moolenaar if (foundcol == 0) // no spaces, cannot break line
30211abd095SBram Moolenaar {
30311abd095SBram Moolenaar curwin->w_cursor.col = startcol;
30411abd095SBram Moolenaar break;
30511abd095SBram Moolenaar }
30611abd095SBram Moolenaar
30711abd095SBram Moolenaar // Going to break the line, remove any "$" now.
30811abd095SBram Moolenaar undisplay_dollar();
30911abd095SBram Moolenaar
31011abd095SBram Moolenaar // Offset between cursor position and line break is used by replace
31111abd095SBram Moolenaar // stack functions. VREPLACE does not use this, and backspaces
31211abd095SBram Moolenaar // over the text instead.
31311abd095SBram Moolenaar if (State & VREPLACE_FLAG)
31411abd095SBram Moolenaar orig_col = startcol; // Will start backspacing from here
31511abd095SBram Moolenaar else
31611abd095SBram Moolenaar replace_offset = startcol - end_foundcol;
31711abd095SBram Moolenaar
31811abd095SBram Moolenaar // adjust startcol for spaces that will be deleted and
31911abd095SBram Moolenaar // characters that will remain on top line
32011abd095SBram Moolenaar curwin->w_cursor.col = foundcol;
32111abd095SBram Moolenaar while ((cc = gchar_cursor(), WHITECHAR(cc))
32211abd095SBram Moolenaar && (!fo_white_par || curwin->w_cursor.col < startcol))
32311abd095SBram Moolenaar inc_cursor();
32411abd095SBram Moolenaar startcol -= curwin->w_cursor.col;
32511abd095SBram Moolenaar if (startcol < 0)
32611abd095SBram Moolenaar startcol = 0;
32711abd095SBram Moolenaar
32811abd095SBram Moolenaar if (State & VREPLACE_FLAG)
32911abd095SBram Moolenaar {
33011abd095SBram Moolenaar // In VREPLACE mode, we will backspace over the text to be
33111abd095SBram Moolenaar // wrapped, so save a copy now to put on the next line.
33211abd095SBram Moolenaar saved_text = vim_strsave(ml_get_cursor());
33311abd095SBram Moolenaar curwin->w_cursor.col = orig_col;
33411abd095SBram Moolenaar if (saved_text == NULL)
33511abd095SBram Moolenaar break; // Can't do it, out of memory
33611abd095SBram Moolenaar saved_text[startcol] = NUL;
33711abd095SBram Moolenaar
33811abd095SBram Moolenaar // Backspace over characters that will move to the next line
33911abd095SBram Moolenaar if (!fo_white_par)
34011abd095SBram Moolenaar backspace_until_column(foundcol);
34111abd095SBram Moolenaar }
34211abd095SBram Moolenaar else
34311abd095SBram Moolenaar {
34411abd095SBram Moolenaar // put cursor after pos. to break line
34511abd095SBram Moolenaar if (!fo_white_par)
34611abd095SBram Moolenaar curwin->w_cursor.col = foundcol;
34711abd095SBram Moolenaar }
34811abd095SBram Moolenaar
34911abd095SBram Moolenaar // Split the line just before the margin.
35011abd095SBram Moolenaar // Only insert/delete lines, but don't really redraw the window.
35111abd095SBram Moolenaar open_line(FORWARD, OPENLINE_DELSPACES + OPENLINE_MARKFIX
35211abd095SBram Moolenaar + (fo_white_par ? OPENLINE_KEEPTRAIL : 0)
35311abd095SBram Moolenaar + (do_comments ? OPENLINE_DO_COM : 0)
35411abd095SBram Moolenaar + ((flags & INSCHAR_COM_LIST) ? OPENLINE_COM_LIST : 0)
35511abd095SBram Moolenaar , ((flags & INSCHAR_COM_LIST) ? second_indent : old_indent));
35611abd095SBram Moolenaar if (!(flags & INSCHAR_COM_LIST))
35711abd095SBram Moolenaar old_indent = 0;
35811abd095SBram Moolenaar
35911abd095SBram Moolenaar replace_offset = 0;
36011abd095SBram Moolenaar if (first_line)
36111abd095SBram Moolenaar {
36211abd095SBram Moolenaar if (!(flags & INSCHAR_COM_LIST))
36311abd095SBram Moolenaar {
36411abd095SBram Moolenaar // This section is for auto-wrap of numeric lists. When not
36511abd095SBram Moolenaar // in insert mode (i.e. format_lines()), the INSCHAR_COM_LIST
36611abd095SBram Moolenaar // flag will be set and open_line() will handle it (as seen
36711abd095SBram Moolenaar // above). The code here (and in get_number_indent()) will
36811abd095SBram Moolenaar // recognize comments if needed...
36911abd095SBram Moolenaar if (second_indent < 0 && has_format_option(FO_Q_NUMBER))
37011abd095SBram Moolenaar second_indent =
37111abd095SBram Moolenaar get_number_indent(curwin->w_cursor.lnum - 1);
37211abd095SBram Moolenaar if (second_indent >= 0)
37311abd095SBram Moolenaar {
37411abd095SBram Moolenaar if (State & VREPLACE_FLAG)
37511abd095SBram Moolenaar change_indent(INDENT_SET, second_indent,
37611abd095SBram Moolenaar FALSE, NUL, TRUE);
37711abd095SBram Moolenaar else
37811abd095SBram Moolenaar if (leader_len > 0 && second_indent - leader_len > 0)
37911abd095SBram Moolenaar {
38011abd095SBram Moolenaar int i;
38111abd095SBram Moolenaar int padding = second_indent - leader_len;
38211abd095SBram Moolenaar
38311abd095SBram Moolenaar // We started at the first_line of a numbered list
38411abd095SBram Moolenaar // that has a comment. the open_line() function has
38511abd095SBram Moolenaar // inserted the proper comment leader and positioned
38611abd095SBram Moolenaar // the cursor at the end of the split line. Now we
38711abd095SBram Moolenaar // add the additional whitespace needed after the
38811abd095SBram Moolenaar // comment leader for the numbered list.
38911abd095SBram Moolenaar for (i = 0; i < padding; i++)
39011abd095SBram Moolenaar ins_str((char_u *)" ");
39111abd095SBram Moolenaar }
39211abd095SBram Moolenaar else
39311abd095SBram Moolenaar {
39411abd095SBram Moolenaar (void)set_indent(second_indent, SIN_CHANGED);
39511abd095SBram Moolenaar }
39611abd095SBram Moolenaar }
39711abd095SBram Moolenaar }
39811abd095SBram Moolenaar first_line = FALSE;
39911abd095SBram Moolenaar }
40011abd095SBram Moolenaar
40111abd095SBram Moolenaar if (State & VREPLACE_FLAG)
40211abd095SBram Moolenaar {
40311abd095SBram Moolenaar // In VREPLACE mode we have backspaced over the text to be
40411abd095SBram Moolenaar // moved, now we re-insert it into the new line.
40511abd095SBram Moolenaar ins_bytes(saved_text);
40611abd095SBram Moolenaar vim_free(saved_text);
40711abd095SBram Moolenaar }
40811abd095SBram Moolenaar else
40911abd095SBram Moolenaar {
41011abd095SBram Moolenaar // Check if cursor is not past the NUL off the line, cindent
41111abd095SBram Moolenaar // may have added or removed indent.
41211abd095SBram Moolenaar curwin->w_cursor.col += startcol;
41311abd095SBram Moolenaar len = (colnr_T)STRLEN(ml_get_curline());
41411abd095SBram Moolenaar if (curwin->w_cursor.col > len)
41511abd095SBram Moolenaar curwin->w_cursor.col = len;
41611abd095SBram Moolenaar }
41711abd095SBram Moolenaar
41811abd095SBram Moolenaar haveto_redraw = TRUE;
41911abd095SBram Moolenaar #ifdef FEAT_CINDENT
42011abd095SBram Moolenaar set_can_cindent(TRUE);
42111abd095SBram Moolenaar #endif
42211abd095SBram Moolenaar // moved the cursor, don't autoindent or cindent now
42311abd095SBram Moolenaar did_ai = FALSE;
42411abd095SBram Moolenaar #ifdef FEAT_SMARTINDENT
42511abd095SBram Moolenaar did_si = FALSE;
42611abd095SBram Moolenaar can_si = FALSE;
42711abd095SBram Moolenaar can_si_back = FALSE;
42811abd095SBram Moolenaar #endif
42911abd095SBram Moolenaar line_breakcheck();
43011abd095SBram Moolenaar }
43111abd095SBram Moolenaar
43211abd095SBram Moolenaar if (save_char != NUL) // put back space after cursor
43311abd095SBram Moolenaar pchar_cursor(save_char);
43411abd095SBram Moolenaar
43511abd095SBram Moolenaar #ifdef FEAT_LINEBREAK
43611abd095SBram Moolenaar curwin->w_p_lbr = has_lbr;
43711abd095SBram Moolenaar #endif
43811abd095SBram Moolenaar if (!format_only && haveto_redraw)
43911abd095SBram Moolenaar {
44011abd095SBram Moolenaar update_topline();
44111abd095SBram Moolenaar redraw_curbuf_later(VALID);
44211abd095SBram Moolenaar }
44311abd095SBram Moolenaar }
44411abd095SBram Moolenaar
44511abd095SBram Moolenaar /*
44611abd095SBram Moolenaar * Blank lines, and lines containing only the comment leader, are left
44711abd095SBram Moolenaar * untouched by the formatting. The function returns TRUE in this
44811abd095SBram Moolenaar * case. It also returns TRUE when a line starts with the end of a comment
44911abd095SBram Moolenaar * ('e' in comment flags), so that this line is skipped, and not joined to the
45011abd095SBram Moolenaar * previous line. A new paragraph starts after a blank line, or when the
45111abd095SBram Moolenaar * comment leader changes -- webb.
45211abd095SBram Moolenaar */
45311abd095SBram Moolenaar static int
fmt_check_par(linenr_T lnum,int * leader_len,char_u ** leader_flags,int do_comments)45411abd095SBram Moolenaar fmt_check_par(
45511abd095SBram Moolenaar linenr_T lnum,
45611abd095SBram Moolenaar int *leader_len,
45711abd095SBram Moolenaar char_u **leader_flags,
45811abd095SBram Moolenaar int do_comments)
45911abd095SBram Moolenaar {
46011abd095SBram Moolenaar char_u *flags = NULL; // init for GCC
46111abd095SBram Moolenaar char_u *ptr;
46211abd095SBram Moolenaar
46311abd095SBram Moolenaar ptr = ml_get(lnum);
46411abd095SBram Moolenaar if (do_comments)
46511abd095SBram Moolenaar *leader_len = get_leader_len(ptr, leader_flags, FALSE, TRUE);
46611abd095SBram Moolenaar else
46711abd095SBram Moolenaar *leader_len = 0;
46811abd095SBram Moolenaar
46911abd095SBram Moolenaar if (*leader_len > 0)
47011abd095SBram Moolenaar {
47111abd095SBram Moolenaar // Search for 'e' flag in comment leader flags.
47211abd095SBram Moolenaar flags = *leader_flags;
47311abd095SBram Moolenaar while (*flags && *flags != ':' && *flags != COM_END)
47411abd095SBram Moolenaar ++flags;
47511abd095SBram Moolenaar }
47611abd095SBram Moolenaar
47711abd095SBram Moolenaar return (*skipwhite(ptr + *leader_len) == NUL
47811abd095SBram Moolenaar || (*leader_len > 0 && *flags == COM_END)
47911abd095SBram Moolenaar || startPS(lnum, NUL, FALSE));
48011abd095SBram Moolenaar }
48111abd095SBram Moolenaar
48211abd095SBram Moolenaar /*
48311abd095SBram Moolenaar * Return TRUE if line "lnum" ends in a white character.
48411abd095SBram Moolenaar */
48511abd095SBram Moolenaar static int
ends_in_white(linenr_T lnum)48611abd095SBram Moolenaar ends_in_white(linenr_T lnum)
48711abd095SBram Moolenaar {
48811abd095SBram Moolenaar char_u *s = ml_get(lnum);
48911abd095SBram Moolenaar size_t l;
49011abd095SBram Moolenaar
49111abd095SBram Moolenaar if (*s == NUL)
49211abd095SBram Moolenaar return FALSE;
49311abd095SBram Moolenaar // Don't use STRLEN() inside VIM_ISWHITE(), SAS/C complains: "macro
49411abd095SBram Moolenaar // invocation may call function multiple times".
49511abd095SBram Moolenaar l = STRLEN(s) - 1;
49611abd095SBram Moolenaar return VIM_ISWHITE(s[l]);
49711abd095SBram Moolenaar }
49811abd095SBram Moolenaar
49911abd095SBram Moolenaar /*
50011abd095SBram Moolenaar * Return TRUE if the two comment leaders given are the same. "lnum" is
50111abd095SBram Moolenaar * the first line. White-space is ignored. Note that the whole of
50211abd095SBram Moolenaar * 'leader1' must match 'leader2_len' characters from 'leader2' -- webb
50311abd095SBram Moolenaar */
50411abd095SBram Moolenaar static int
same_leader(linenr_T lnum,int leader1_len,char_u * leader1_flags,int leader2_len,char_u * leader2_flags)50511abd095SBram Moolenaar same_leader(
50611abd095SBram Moolenaar linenr_T lnum,
50711abd095SBram Moolenaar int leader1_len,
50811abd095SBram Moolenaar char_u *leader1_flags,
50911abd095SBram Moolenaar int leader2_len,
51011abd095SBram Moolenaar char_u *leader2_flags)
51111abd095SBram Moolenaar {
51211abd095SBram Moolenaar int idx1 = 0, idx2 = 0;
51311abd095SBram Moolenaar char_u *p;
51411abd095SBram Moolenaar char_u *line1;
51511abd095SBram Moolenaar char_u *line2;
51611abd095SBram Moolenaar
51711abd095SBram Moolenaar if (leader1_len == 0)
51811abd095SBram Moolenaar return (leader2_len == 0);
51911abd095SBram Moolenaar
52011abd095SBram Moolenaar // If first leader has 'f' flag, the lines can be joined only if the
52111abd095SBram Moolenaar // second line does not have a leader.
52211abd095SBram Moolenaar // If first leader has 'e' flag, the lines can never be joined.
52311abd095SBram Moolenaar // If fist leader has 's' flag, the lines can only be joined if there is
52411abd095SBram Moolenaar // some text after it and the second line has the 'm' flag.
52511abd095SBram Moolenaar if (leader1_flags != NULL)
52611abd095SBram Moolenaar {
52711abd095SBram Moolenaar for (p = leader1_flags; *p && *p != ':'; ++p)
52811abd095SBram Moolenaar {
52911abd095SBram Moolenaar if (*p == COM_FIRST)
53011abd095SBram Moolenaar return (leader2_len == 0);
53111abd095SBram Moolenaar if (*p == COM_END)
53211abd095SBram Moolenaar return FALSE;
53311abd095SBram Moolenaar if (*p == COM_START)
53411abd095SBram Moolenaar {
53511abd095SBram Moolenaar if (*(ml_get(lnum) + leader1_len) == NUL)
53611abd095SBram Moolenaar return FALSE;
53711abd095SBram Moolenaar if (leader2_flags == NULL || leader2_len == 0)
53811abd095SBram Moolenaar return FALSE;
53911abd095SBram Moolenaar for (p = leader2_flags; *p && *p != ':'; ++p)
54011abd095SBram Moolenaar if (*p == COM_MIDDLE)
54111abd095SBram Moolenaar return TRUE;
54211abd095SBram Moolenaar return FALSE;
54311abd095SBram Moolenaar }
54411abd095SBram Moolenaar }
54511abd095SBram Moolenaar }
54611abd095SBram Moolenaar
54711abd095SBram Moolenaar // Get current line and next line, compare the leaders.
54811abd095SBram Moolenaar // The first line has to be saved, only one line can be locked at a time.
54911abd095SBram Moolenaar line1 = vim_strsave(ml_get(lnum));
55011abd095SBram Moolenaar if (line1 != NULL)
55111abd095SBram Moolenaar {
55211abd095SBram Moolenaar for (idx1 = 0; VIM_ISWHITE(line1[idx1]); ++idx1)
55311abd095SBram Moolenaar ;
55411abd095SBram Moolenaar line2 = ml_get(lnum + 1);
55511abd095SBram Moolenaar for (idx2 = 0; idx2 < leader2_len; ++idx2)
55611abd095SBram Moolenaar {
55711abd095SBram Moolenaar if (!VIM_ISWHITE(line2[idx2]))
55811abd095SBram Moolenaar {
55911abd095SBram Moolenaar if (line1[idx1++] != line2[idx2])
56011abd095SBram Moolenaar break;
56111abd095SBram Moolenaar }
56211abd095SBram Moolenaar else
56311abd095SBram Moolenaar while (VIM_ISWHITE(line1[idx1]))
56411abd095SBram Moolenaar ++idx1;
56511abd095SBram Moolenaar }
56611abd095SBram Moolenaar vim_free(line1);
56711abd095SBram Moolenaar }
56811abd095SBram Moolenaar return (idx2 == leader2_len && idx1 == leader1_len);
56911abd095SBram Moolenaar }
57011abd095SBram Moolenaar
57111abd095SBram Moolenaar /*
57211abd095SBram Moolenaar * Return TRUE when a paragraph starts in line "lnum". Return FALSE when the
57311abd095SBram Moolenaar * previous line is in the same paragraph. Used for auto-formatting.
57411abd095SBram Moolenaar */
57511abd095SBram Moolenaar static int
paragraph_start(linenr_T lnum)57611abd095SBram Moolenaar paragraph_start(linenr_T lnum)
57711abd095SBram Moolenaar {
57811abd095SBram Moolenaar char_u *p;
57911abd095SBram Moolenaar int leader_len = 0; // leader len of current line
58011abd095SBram Moolenaar char_u *leader_flags = NULL; // flags for leader of current line
58111abd095SBram Moolenaar int next_leader_len; // leader len of next line
58211abd095SBram Moolenaar char_u *next_leader_flags; // flags for leader of next line
58311abd095SBram Moolenaar int do_comments; // format comments
58411abd095SBram Moolenaar
58511abd095SBram Moolenaar if (lnum <= 1)
58611abd095SBram Moolenaar return TRUE; // start of the file
58711abd095SBram Moolenaar
58811abd095SBram Moolenaar p = ml_get(lnum - 1);
58911abd095SBram Moolenaar if (*p == NUL)
59011abd095SBram Moolenaar return TRUE; // after empty line
59111abd095SBram Moolenaar
59211abd095SBram Moolenaar do_comments = has_format_option(FO_Q_COMS);
59311abd095SBram Moolenaar if (fmt_check_par(lnum - 1, &leader_len, &leader_flags, do_comments))
59411abd095SBram Moolenaar return TRUE; // after non-paragraph line
59511abd095SBram Moolenaar
59611abd095SBram Moolenaar if (fmt_check_par(lnum, &next_leader_len, &next_leader_flags, do_comments))
59711abd095SBram Moolenaar return TRUE; // "lnum" is not a paragraph line
59811abd095SBram Moolenaar
59911abd095SBram Moolenaar if (has_format_option(FO_WHITE_PAR) && !ends_in_white(lnum - 1))
60011abd095SBram Moolenaar return TRUE; // missing trailing space in previous line.
60111abd095SBram Moolenaar
60211abd095SBram Moolenaar if (has_format_option(FO_Q_NUMBER) && (get_number_indent(lnum) > 0))
60311abd095SBram Moolenaar return TRUE; // numbered item starts in "lnum".
60411abd095SBram Moolenaar
60511abd095SBram Moolenaar if (!same_leader(lnum - 1, leader_len, leader_flags,
60611abd095SBram Moolenaar next_leader_len, next_leader_flags))
60711abd095SBram Moolenaar return TRUE; // change of comment leader.
60811abd095SBram Moolenaar
60911abd095SBram Moolenaar return FALSE;
61011abd095SBram Moolenaar }
61111abd095SBram Moolenaar
61211abd095SBram Moolenaar /*
61311abd095SBram Moolenaar * Called after inserting or deleting text: When 'formatoptions' includes the
61411abd095SBram Moolenaar * 'a' flag format from the current line until the end of the paragraph.
61511abd095SBram Moolenaar * Keep the cursor at the same position relative to the text.
61611abd095SBram Moolenaar * The caller must have saved the cursor line for undo, following ones will be
61711abd095SBram Moolenaar * saved here.
61811abd095SBram Moolenaar */
61911abd095SBram Moolenaar void
auto_format(int trailblank,int prev_line)62011abd095SBram Moolenaar auto_format(
62111abd095SBram Moolenaar int trailblank, // when TRUE also format with trailing blank
62211abd095SBram Moolenaar int prev_line) // may start in previous line
62311abd095SBram Moolenaar {
62411abd095SBram Moolenaar pos_T pos;
62511abd095SBram Moolenaar colnr_T len;
62611abd095SBram Moolenaar char_u *old;
62711abd095SBram Moolenaar char_u *new, *pnew;
62811abd095SBram Moolenaar int wasatend;
62911abd095SBram Moolenaar int cc;
63011abd095SBram Moolenaar
63111abd095SBram Moolenaar if (!has_format_option(FO_AUTO))
63211abd095SBram Moolenaar return;
63311abd095SBram Moolenaar
63411abd095SBram Moolenaar pos = curwin->w_cursor;
63511abd095SBram Moolenaar old = ml_get_curline();
63611abd095SBram Moolenaar
63711abd095SBram Moolenaar // may remove added space
63811abd095SBram Moolenaar check_auto_format(FALSE);
63911abd095SBram Moolenaar
64011abd095SBram Moolenaar // Don't format in Insert mode when the cursor is on a trailing blank, the
64111abd095SBram Moolenaar // user might insert normal text next. Also skip formatting when "1" is
64211abd095SBram Moolenaar // in 'formatoptions' and there is a single character before the cursor.
64311abd095SBram Moolenaar // Otherwise the line would be broken and when typing another non-white
64411abd095SBram Moolenaar // next they are not joined back together.
64511abd095SBram Moolenaar wasatend = (pos.col == (colnr_T)STRLEN(old));
64611abd095SBram Moolenaar if (*old != NUL && !trailblank && wasatend)
64711abd095SBram Moolenaar {
64811abd095SBram Moolenaar dec_cursor();
64911abd095SBram Moolenaar cc = gchar_cursor();
65011abd095SBram Moolenaar if (!WHITECHAR(cc) && curwin->w_cursor.col > 0
65111abd095SBram Moolenaar && has_format_option(FO_ONE_LETTER))
65211abd095SBram Moolenaar dec_cursor();
65311abd095SBram Moolenaar cc = gchar_cursor();
65411abd095SBram Moolenaar if (WHITECHAR(cc))
65511abd095SBram Moolenaar {
65611abd095SBram Moolenaar curwin->w_cursor = pos;
65711abd095SBram Moolenaar return;
65811abd095SBram Moolenaar }
65911abd095SBram Moolenaar curwin->w_cursor = pos;
66011abd095SBram Moolenaar }
66111abd095SBram Moolenaar
66211abd095SBram Moolenaar // With the 'c' flag in 'formatoptions' and 't' missing: only format
66311abd095SBram Moolenaar // comments.
66411abd095SBram Moolenaar if (has_format_option(FO_WRAP_COMS) && !has_format_option(FO_WRAP)
66511abd095SBram Moolenaar && get_leader_len(old, NULL, FALSE, TRUE) == 0)
66611abd095SBram Moolenaar return;
66711abd095SBram Moolenaar
66811abd095SBram Moolenaar // May start formatting in a previous line, so that after "x" a word is
66911abd095SBram Moolenaar // moved to the previous line if it fits there now. Only when this is not
67011abd095SBram Moolenaar // the start of a paragraph.
67111abd095SBram Moolenaar if (prev_line && !paragraph_start(curwin->w_cursor.lnum))
67211abd095SBram Moolenaar {
67311abd095SBram Moolenaar --curwin->w_cursor.lnum;
67411abd095SBram Moolenaar if (u_save_cursor() == FAIL)
67511abd095SBram Moolenaar return;
67611abd095SBram Moolenaar }
67711abd095SBram Moolenaar
67811abd095SBram Moolenaar // Do the formatting and restore the cursor position. "saved_cursor" will
67911abd095SBram Moolenaar // be adjusted for the text formatting.
68011abd095SBram Moolenaar saved_cursor = pos;
68111abd095SBram Moolenaar format_lines((linenr_T)-1, FALSE);
68211abd095SBram Moolenaar curwin->w_cursor = saved_cursor;
68311abd095SBram Moolenaar saved_cursor.lnum = 0;
68411abd095SBram Moolenaar
68511abd095SBram Moolenaar if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count)
68611abd095SBram Moolenaar {
68711abd095SBram Moolenaar // "cannot happen"
68811abd095SBram Moolenaar curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
68911abd095SBram Moolenaar coladvance((colnr_T)MAXCOL);
69011abd095SBram Moolenaar }
69111abd095SBram Moolenaar else
69211abd095SBram Moolenaar check_cursor_col();
69311abd095SBram Moolenaar
69411abd095SBram Moolenaar // Insert mode: If the cursor is now after the end of the line while it
69511abd095SBram Moolenaar // previously wasn't, the line was broken. Because of the rule above we
69611abd095SBram Moolenaar // need to add a space when 'w' is in 'formatoptions' to keep a paragraph
69711abd095SBram Moolenaar // formatted.
69811abd095SBram Moolenaar if (!wasatend && has_format_option(FO_WHITE_PAR))
69911abd095SBram Moolenaar {
70011abd095SBram Moolenaar new = ml_get_curline();
70111abd095SBram Moolenaar len = (colnr_T)STRLEN(new);
70211abd095SBram Moolenaar if (curwin->w_cursor.col == len)
70311abd095SBram Moolenaar {
70411abd095SBram Moolenaar pnew = vim_strnsave(new, len + 2);
70511abd095SBram Moolenaar pnew[len] = ' ';
70611abd095SBram Moolenaar pnew[len + 1] = NUL;
70711abd095SBram Moolenaar ml_replace(curwin->w_cursor.lnum, pnew, FALSE);
70811abd095SBram Moolenaar // remove the space later
70911abd095SBram Moolenaar did_add_space = TRUE;
71011abd095SBram Moolenaar }
71111abd095SBram Moolenaar else
71211abd095SBram Moolenaar // may remove added space
71311abd095SBram Moolenaar check_auto_format(FALSE);
71411abd095SBram Moolenaar }
71511abd095SBram Moolenaar
71611abd095SBram Moolenaar check_cursor();
71711abd095SBram Moolenaar }
71811abd095SBram Moolenaar
71911abd095SBram Moolenaar /*
72011abd095SBram Moolenaar * When an extra space was added to continue a paragraph for auto-formatting,
72111abd095SBram Moolenaar * delete it now. The space must be under the cursor, just after the insert
72211abd095SBram Moolenaar * position.
72311abd095SBram Moolenaar */
72411abd095SBram Moolenaar void
check_auto_format(int end_insert)72511abd095SBram Moolenaar check_auto_format(
72611abd095SBram Moolenaar int end_insert) // TRUE when ending Insert mode
72711abd095SBram Moolenaar {
72811abd095SBram Moolenaar int c = ' ';
72911abd095SBram Moolenaar int cc;
73011abd095SBram Moolenaar
73111abd095SBram Moolenaar if (did_add_space)
73211abd095SBram Moolenaar {
73311abd095SBram Moolenaar cc = gchar_cursor();
73411abd095SBram Moolenaar if (!WHITECHAR(cc))
73511abd095SBram Moolenaar // Somehow the space was removed already.
73611abd095SBram Moolenaar did_add_space = FALSE;
73711abd095SBram Moolenaar else
73811abd095SBram Moolenaar {
73911abd095SBram Moolenaar if (!end_insert)
74011abd095SBram Moolenaar {
74111abd095SBram Moolenaar inc_cursor();
74211abd095SBram Moolenaar c = gchar_cursor();
74311abd095SBram Moolenaar dec_cursor();
74411abd095SBram Moolenaar }
74511abd095SBram Moolenaar if (c != NUL)
74611abd095SBram Moolenaar {
74711abd095SBram Moolenaar // The space is no longer at the end of the line, delete it.
74811abd095SBram Moolenaar del_char(FALSE);
74911abd095SBram Moolenaar did_add_space = FALSE;
75011abd095SBram Moolenaar }
75111abd095SBram Moolenaar }
75211abd095SBram Moolenaar }
75311abd095SBram Moolenaar }
75411abd095SBram Moolenaar
75511abd095SBram Moolenaar /*
75611abd095SBram Moolenaar * Find out textwidth to be used for formatting:
75711abd095SBram Moolenaar * if 'textwidth' option is set, use it
75811abd095SBram Moolenaar * else if 'wrapmargin' option is set, use curwin->w_width - 'wrapmargin'
75911abd095SBram Moolenaar * if invalid value, use 0.
76011abd095SBram Moolenaar * Set default to window width (maximum 79) for "gq" operator.
76111abd095SBram Moolenaar */
76211abd095SBram Moolenaar int
comp_textwidth(int ff)76311abd095SBram Moolenaar comp_textwidth(
76411abd095SBram Moolenaar int ff) // force formatting (for "gq" command)
76511abd095SBram Moolenaar {
76611abd095SBram Moolenaar int textwidth;
76711abd095SBram Moolenaar
76811abd095SBram Moolenaar textwidth = curbuf->b_p_tw;
76911abd095SBram Moolenaar if (textwidth == 0 && curbuf->b_p_wm)
77011abd095SBram Moolenaar {
77111abd095SBram Moolenaar // The width is the window width minus 'wrapmargin' minus all the
77211abd095SBram Moolenaar // things that add to the margin.
77311abd095SBram Moolenaar textwidth = curwin->w_width - curbuf->b_p_wm;
77411abd095SBram Moolenaar #ifdef FEAT_CMDWIN
77511abd095SBram Moolenaar if (cmdwin_type != 0)
77611abd095SBram Moolenaar textwidth -= 1;
77711abd095SBram Moolenaar #endif
77811abd095SBram Moolenaar #ifdef FEAT_FOLDING
77911abd095SBram Moolenaar textwidth -= curwin->w_p_fdc;
78011abd095SBram Moolenaar #endif
78111abd095SBram Moolenaar #ifdef FEAT_SIGNS
78211abd095SBram Moolenaar if (signcolumn_on(curwin))
78311abd095SBram Moolenaar textwidth -= 1;
78411abd095SBram Moolenaar #endif
78511abd095SBram Moolenaar if (curwin->w_p_nu || curwin->w_p_rnu)
78611abd095SBram Moolenaar textwidth -= 8;
78711abd095SBram Moolenaar }
78811abd095SBram Moolenaar if (textwidth < 0)
78911abd095SBram Moolenaar textwidth = 0;
79011abd095SBram Moolenaar if (ff && textwidth == 0)
79111abd095SBram Moolenaar {
79211abd095SBram Moolenaar textwidth = curwin->w_width - 1;
79311abd095SBram Moolenaar if (textwidth > 79)
79411abd095SBram Moolenaar textwidth = 79;
79511abd095SBram Moolenaar }
79611abd095SBram Moolenaar return textwidth;
79711abd095SBram Moolenaar }
79811abd095SBram Moolenaar
79911abd095SBram Moolenaar /*
80011abd095SBram Moolenaar * Implementation of the format operator 'gq'.
80111abd095SBram Moolenaar */
80211abd095SBram Moolenaar void
op_format(oparg_T * oap,int keep_cursor)80311abd095SBram Moolenaar op_format(
80411abd095SBram Moolenaar oparg_T *oap,
80511abd095SBram Moolenaar int keep_cursor) // keep cursor on same text char
80611abd095SBram Moolenaar {
80711abd095SBram Moolenaar long old_line_count = curbuf->b_ml.ml_line_count;
80811abd095SBram Moolenaar
80911abd095SBram Moolenaar // Place the cursor where the "gq" or "gw" command was given, so that "u"
81011abd095SBram Moolenaar // can put it back there.
81111abd095SBram Moolenaar curwin->w_cursor = oap->cursor_start;
81211abd095SBram Moolenaar
81311abd095SBram Moolenaar if (u_save((linenr_T)(oap->start.lnum - 1),
81411abd095SBram Moolenaar (linenr_T)(oap->end.lnum + 1)) == FAIL)
81511abd095SBram Moolenaar return;
81611abd095SBram Moolenaar curwin->w_cursor = oap->start;
81711abd095SBram Moolenaar
81811abd095SBram Moolenaar if (oap->is_VIsual)
81911abd095SBram Moolenaar // When there is no change: need to remove the Visual selection
82011abd095SBram Moolenaar redraw_curbuf_later(INVERTED);
82111abd095SBram Moolenaar
822e1004401SBram Moolenaar if ((cmdmod.cmod_flags & CMOD_LOCKMARKS) == 0)
82311abd095SBram Moolenaar // Set '[ mark at the start of the formatted area
82411abd095SBram Moolenaar curbuf->b_op_start = oap->start;
82511abd095SBram Moolenaar
82611abd095SBram Moolenaar // For "gw" remember the cursor position and put it back below (adjusted
82711abd095SBram Moolenaar // for joined and split lines).
82811abd095SBram Moolenaar if (keep_cursor)
82911abd095SBram Moolenaar saved_cursor = oap->cursor_start;
83011abd095SBram Moolenaar
83111abd095SBram Moolenaar format_lines(oap->line_count, keep_cursor);
83211abd095SBram Moolenaar
83311abd095SBram Moolenaar // Leave the cursor at the first non-blank of the last formatted line.
83411abd095SBram Moolenaar // If the cursor was moved one line back (e.g. with "Q}") go to the next
83511abd095SBram Moolenaar // line, so "." will do the next lines.
83611abd095SBram Moolenaar if (oap->end_adjusted && curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count)
83711abd095SBram Moolenaar ++curwin->w_cursor.lnum;
83811abd095SBram Moolenaar beginline(BL_WHITE | BL_FIX);
83911abd095SBram Moolenaar old_line_count = curbuf->b_ml.ml_line_count - old_line_count;
84011abd095SBram Moolenaar msgmore(old_line_count);
84111abd095SBram Moolenaar
842e1004401SBram Moolenaar if ((cmdmod.cmod_flags & CMOD_LOCKMARKS) == 0)
84311abd095SBram Moolenaar // put '] mark on the end of the formatted area
84411abd095SBram Moolenaar curbuf->b_op_end = curwin->w_cursor;
84511abd095SBram Moolenaar
84611abd095SBram Moolenaar if (keep_cursor)
84711abd095SBram Moolenaar {
84811abd095SBram Moolenaar curwin->w_cursor = saved_cursor;
84911abd095SBram Moolenaar saved_cursor.lnum = 0;
85011abd095SBram Moolenaar }
85111abd095SBram Moolenaar
85211abd095SBram Moolenaar if (oap->is_VIsual)
85311abd095SBram Moolenaar {
85411abd095SBram Moolenaar win_T *wp;
85511abd095SBram Moolenaar
85611abd095SBram Moolenaar FOR_ALL_WINDOWS(wp)
85711abd095SBram Moolenaar {
85811abd095SBram Moolenaar if (wp->w_old_cursor_lnum != 0)
85911abd095SBram Moolenaar {
86011abd095SBram Moolenaar // When lines have been inserted or deleted, adjust the end of
86111abd095SBram Moolenaar // the Visual area to be redrawn.
86211abd095SBram Moolenaar if (wp->w_old_cursor_lnum > wp->w_old_visual_lnum)
86311abd095SBram Moolenaar wp->w_old_cursor_lnum += old_line_count;
86411abd095SBram Moolenaar else
86511abd095SBram Moolenaar wp->w_old_visual_lnum += old_line_count;
86611abd095SBram Moolenaar }
86711abd095SBram Moolenaar }
86811abd095SBram Moolenaar }
86911abd095SBram Moolenaar }
87011abd095SBram Moolenaar
87111abd095SBram Moolenaar #if defined(FEAT_EVAL) || defined(PROTO)
87211abd095SBram Moolenaar /*
87311abd095SBram Moolenaar * Implementation of the format operator 'gq' for when using 'formatexpr'.
87411abd095SBram Moolenaar */
87511abd095SBram Moolenaar void
op_formatexpr(oparg_T * oap)87611abd095SBram Moolenaar op_formatexpr(oparg_T *oap)
87711abd095SBram Moolenaar {
87811abd095SBram Moolenaar if (oap->is_VIsual)
87911abd095SBram Moolenaar // When there is no change: need to remove the Visual selection
88011abd095SBram Moolenaar redraw_curbuf_later(INVERTED);
88111abd095SBram Moolenaar
88211abd095SBram Moolenaar if (fex_format(oap->start.lnum, oap->line_count, NUL) != 0)
88311abd095SBram Moolenaar // As documented: when 'formatexpr' returns non-zero fall back to
88411abd095SBram Moolenaar // internal formatting.
88511abd095SBram Moolenaar op_format(oap, FALSE);
88611abd095SBram Moolenaar }
88711abd095SBram Moolenaar
88811abd095SBram Moolenaar int
fex_format(linenr_T lnum,long count,int c)88911abd095SBram Moolenaar fex_format(
89011abd095SBram Moolenaar linenr_T lnum,
89111abd095SBram Moolenaar long count,
89211abd095SBram Moolenaar int c) // character to be inserted
89311abd095SBram Moolenaar {
89411abd095SBram Moolenaar int use_sandbox = was_set_insecurely((char_u *)"formatexpr",
89511abd095SBram Moolenaar OPT_LOCAL);
89611abd095SBram Moolenaar int r;
89711abd095SBram Moolenaar char_u *fex;
89811abd095SBram Moolenaar
89911abd095SBram Moolenaar // Set v:lnum to the first line number and v:count to the number of lines.
90011abd095SBram Moolenaar // Set v:char to the character to be inserted (can be NUL).
90111abd095SBram Moolenaar set_vim_var_nr(VV_LNUM, lnum);
90211abd095SBram Moolenaar set_vim_var_nr(VV_COUNT, count);
90311abd095SBram Moolenaar set_vim_var_char(c);
90411abd095SBram Moolenaar
90511abd095SBram Moolenaar // Make a copy, the option could be changed while calling it.
90611abd095SBram Moolenaar fex = vim_strsave(curbuf->b_p_fex);
90711abd095SBram Moolenaar if (fex == NULL)
90811abd095SBram Moolenaar return 0;
90911abd095SBram Moolenaar
91011abd095SBram Moolenaar // Evaluate the function.
91111abd095SBram Moolenaar if (use_sandbox)
91211abd095SBram Moolenaar ++sandbox;
91311abd095SBram Moolenaar r = (int)eval_to_number(fex);
91411abd095SBram Moolenaar if (use_sandbox)
91511abd095SBram Moolenaar --sandbox;
91611abd095SBram Moolenaar
91711abd095SBram Moolenaar set_vim_var_string(VV_CHAR, NULL, -1);
91811abd095SBram Moolenaar vim_free(fex);
91911abd095SBram Moolenaar
92011abd095SBram Moolenaar return r;
92111abd095SBram Moolenaar }
92211abd095SBram Moolenaar #endif
92311abd095SBram Moolenaar
92411abd095SBram Moolenaar /*
92511abd095SBram Moolenaar * Format "line_count" lines, starting at the cursor position.
92611abd095SBram Moolenaar * When "line_count" is negative, format until the end of the paragraph.
92711abd095SBram Moolenaar * Lines after the cursor line are saved for undo, caller must have saved the
92811abd095SBram Moolenaar * first line.
92911abd095SBram Moolenaar */
93011abd095SBram Moolenaar void
format_lines(linenr_T line_count,int avoid_fex)93111abd095SBram Moolenaar format_lines(
93211abd095SBram Moolenaar linenr_T line_count,
93311abd095SBram Moolenaar int avoid_fex) // don't use 'formatexpr'
93411abd095SBram Moolenaar {
93511abd095SBram Moolenaar int max_len;
93611abd095SBram Moolenaar int is_not_par; // current line not part of parag.
93711abd095SBram Moolenaar int next_is_not_par; // next line not part of paragraph
93811abd095SBram Moolenaar int is_end_par; // at end of paragraph
93911abd095SBram Moolenaar int prev_is_end_par = FALSE;// prev. line not part of parag.
94011abd095SBram Moolenaar int next_is_start_par = FALSE;
94111abd095SBram Moolenaar int leader_len = 0; // leader len of current line
94211abd095SBram Moolenaar int next_leader_len; // leader len of next line
94311abd095SBram Moolenaar char_u *leader_flags = NULL; // flags for leader of current line
94411abd095SBram Moolenaar char_u *next_leader_flags; // flags for leader of next line
94511abd095SBram Moolenaar int do_comments; // format comments
94611abd095SBram Moolenaar int do_comments_list = 0; // format comments with 'n' or '2'
94711abd095SBram Moolenaar int advance = TRUE;
94811abd095SBram Moolenaar int second_indent = -1; // indent for second line (comment
94911abd095SBram Moolenaar // aware)
95011abd095SBram Moolenaar int do_second_indent;
95111abd095SBram Moolenaar int do_number_indent;
95211abd095SBram Moolenaar int do_trail_white;
95311abd095SBram Moolenaar int first_par_line = TRUE;
95411abd095SBram Moolenaar int smd_save;
95511abd095SBram Moolenaar long count;
95611abd095SBram Moolenaar int need_set_indent = TRUE; // set indent of next paragraph
95711abd095SBram Moolenaar int force_format = FALSE;
95811abd095SBram Moolenaar int old_State = State;
95911abd095SBram Moolenaar
96011abd095SBram Moolenaar // length of a line to force formatting: 3 * 'tw'
96111abd095SBram Moolenaar max_len = comp_textwidth(TRUE) * 3;
96211abd095SBram Moolenaar
96311abd095SBram Moolenaar // check for 'q', '2' and '1' in 'formatoptions'
96411abd095SBram Moolenaar do_comments = has_format_option(FO_Q_COMS);
96511abd095SBram Moolenaar do_second_indent = has_format_option(FO_Q_SECOND);
96611abd095SBram Moolenaar do_number_indent = has_format_option(FO_Q_NUMBER);
96711abd095SBram Moolenaar do_trail_white = has_format_option(FO_WHITE_PAR);
96811abd095SBram Moolenaar
96911abd095SBram Moolenaar // Get info about the previous and current line.
97011abd095SBram Moolenaar if (curwin->w_cursor.lnum > 1)
97111abd095SBram Moolenaar is_not_par = fmt_check_par(curwin->w_cursor.lnum - 1
97211abd095SBram Moolenaar , &leader_len, &leader_flags, do_comments);
97311abd095SBram Moolenaar else
97411abd095SBram Moolenaar is_not_par = TRUE;
97511abd095SBram Moolenaar next_is_not_par = fmt_check_par(curwin->w_cursor.lnum
97611abd095SBram Moolenaar , &next_leader_len, &next_leader_flags, do_comments);
97711abd095SBram Moolenaar is_end_par = (is_not_par || next_is_not_par);
97811abd095SBram Moolenaar if (!is_end_par && do_trail_white)
97911abd095SBram Moolenaar is_end_par = !ends_in_white(curwin->w_cursor.lnum - 1);
98011abd095SBram Moolenaar
98111abd095SBram Moolenaar curwin->w_cursor.lnum--;
98211abd095SBram Moolenaar for (count = line_count; count != 0 && !got_int; --count)
98311abd095SBram Moolenaar {
98411abd095SBram Moolenaar // Advance to next paragraph.
98511abd095SBram Moolenaar if (advance)
98611abd095SBram Moolenaar {
98711abd095SBram Moolenaar curwin->w_cursor.lnum++;
98811abd095SBram Moolenaar prev_is_end_par = is_end_par;
98911abd095SBram Moolenaar is_not_par = next_is_not_par;
99011abd095SBram Moolenaar leader_len = next_leader_len;
99111abd095SBram Moolenaar leader_flags = next_leader_flags;
99211abd095SBram Moolenaar }
99311abd095SBram Moolenaar
99411abd095SBram Moolenaar // The last line to be formatted.
99511abd095SBram Moolenaar if (count == 1 || curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count)
99611abd095SBram Moolenaar {
99711abd095SBram Moolenaar next_is_not_par = TRUE;
99811abd095SBram Moolenaar next_leader_len = 0;
99911abd095SBram Moolenaar next_leader_flags = NULL;
100011abd095SBram Moolenaar }
100111abd095SBram Moolenaar else
100211abd095SBram Moolenaar {
100311abd095SBram Moolenaar next_is_not_par = fmt_check_par(curwin->w_cursor.lnum + 1
100411abd095SBram Moolenaar , &next_leader_len, &next_leader_flags, do_comments);
100511abd095SBram Moolenaar if (do_number_indent)
100611abd095SBram Moolenaar next_is_start_par =
100711abd095SBram Moolenaar (get_number_indent(curwin->w_cursor.lnum + 1) > 0);
100811abd095SBram Moolenaar }
100911abd095SBram Moolenaar advance = TRUE;
101011abd095SBram Moolenaar is_end_par = (is_not_par || next_is_not_par || next_is_start_par);
101111abd095SBram Moolenaar if (!is_end_par && do_trail_white)
101211abd095SBram Moolenaar is_end_par = !ends_in_white(curwin->w_cursor.lnum);
101311abd095SBram Moolenaar
101411abd095SBram Moolenaar // Skip lines that are not in a paragraph.
101511abd095SBram Moolenaar if (is_not_par)
101611abd095SBram Moolenaar {
101711abd095SBram Moolenaar if (line_count < 0)
101811abd095SBram Moolenaar break;
101911abd095SBram Moolenaar }
102011abd095SBram Moolenaar else
102111abd095SBram Moolenaar {
102211abd095SBram Moolenaar // For the first line of a paragraph, check indent of second line.
102311abd095SBram Moolenaar // Don't do this for comments and empty lines.
102411abd095SBram Moolenaar if (first_par_line
102511abd095SBram Moolenaar && (do_second_indent || do_number_indent)
102611abd095SBram Moolenaar && prev_is_end_par
102711abd095SBram Moolenaar && curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count)
102811abd095SBram Moolenaar {
102911abd095SBram Moolenaar if (do_second_indent && !LINEEMPTY(curwin->w_cursor.lnum + 1))
103011abd095SBram Moolenaar {
103111abd095SBram Moolenaar if (leader_len == 0 && next_leader_len == 0)
103211abd095SBram Moolenaar {
103311abd095SBram Moolenaar // no comment found
103411abd095SBram Moolenaar second_indent =
103511abd095SBram Moolenaar get_indent_lnum(curwin->w_cursor.lnum + 1);
103611abd095SBram Moolenaar }
103711abd095SBram Moolenaar else
103811abd095SBram Moolenaar {
103911abd095SBram Moolenaar second_indent = next_leader_len;
104011abd095SBram Moolenaar do_comments_list = 1;
104111abd095SBram Moolenaar }
104211abd095SBram Moolenaar }
104311abd095SBram Moolenaar else if (do_number_indent)
104411abd095SBram Moolenaar {
104511abd095SBram Moolenaar if (leader_len == 0 && next_leader_len == 0)
104611abd095SBram Moolenaar {
104711abd095SBram Moolenaar // no comment found
104811abd095SBram Moolenaar second_indent =
104911abd095SBram Moolenaar get_number_indent(curwin->w_cursor.lnum);
105011abd095SBram Moolenaar }
105111abd095SBram Moolenaar else
105211abd095SBram Moolenaar {
105311abd095SBram Moolenaar // get_number_indent() is now "comment aware"...
105411abd095SBram Moolenaar second_indent =
105511abd095SBram Moolenaar get_number_indent(curwin->w_cursor.lnum);
105611abd095SBram Moolenaar do_comments_list = 1;
105711abd095SBram Moolenaar }
105811abd095SBram Moolenaar }
105911abd095SBram Moolenaar }
106011abd095SBram Moolenaar
106111abd095SBram Moolenaar // When the comment leader changes, it's the end of the paragraph.
106211abd095SBram Moolenaar if (curwin->w_cursor.lnum >= curbuf->b_ml.ml_line_count
106311abd095SBram Moolenaar || !same_leader(curwin->w_cursor.lnum,
106411abd095SBram Moolenaar leader_len, leader_flags,
106511abd095SBram Moolenaar next_leader_len, next_leader_flags))
106611abd095SBram Moolenaar is_end_par = TRUE;
106711abd095SBram Moolenaar
106811abd095SBram Moolenaar // If we have got to the end of a paragraph, or the line is
106911abd095SBram Moolenaar // getting long, format it.
107011abd095SBram Moolenaar if (is_end_par || force_format)
107111abd095SBram Moolenaar {
107211abd095SBram Moolenaar if (need_set_indent)
1073*818ff25cSChristian Brabandt {
1074*818ff25cSChristian Brabandt int indent = 0; // amount of indent needed
1075*818ff25cSChristian Brabandt
107611abd095SBram Moolenaar // replace indent in first line with minimal number of
107711abd095SBram Moolenaar // tabs and spaces, according to current options
1078*818ff25cSChristian Brabandt # ifdef FEAT_LISP
1079*818ff25cSChristian Brabandt if (curbuf->b_p_lisp)
1080*818ff25cSChristian Brabandt indent = get_lisp_indent();
1081*818ff25cSChristian Brabandt else
1082*818ff25cSChristian Brabandt # endif
1083*818ff25cSChristian Brabandt {
1084*818ff25cSChristian Brabandt #ifdef FEAT_CINDENT
1085*818ff25cSChristian Brabandt if (cindent_on())
1086*818ff25cSChristian Brabandt {
1087*818ff25cSChristian Brabandt indent =
1088*818ff25cSChristian Brabandt # ifdef FEAT_EVAL
1089*818ff25cSChristian Brabandt *curbuf->b_p_inde != NUL ? get_expr_indent() :
1090*818ff25cSChristian Brabandt # endif
1091*818ff25cSChristian Brabandt get_c_indent();
1092*818ff25cSChristian Brabandt }
1093*818ff25cSChristian Brabandt else
1094*818ff25cSChristian Brabandt #endif
1095*818ff25cSChristian Brabandt indent = get_indent();
1096*818ff25cSChristian Brabandt }
1097*818ff25cSChristian Brabandt (void)set_indent(indent, SIN_CHANGED);
1098*818ff25cSChristian Brabandt }
109911abd095SBram Moolenaar
110011abd095SBram Moolenaar // put cursor on last non-space
110111abd095SBram Moolenaar State = NORMAL; // don't go past end-of-line
110211abd095SBram Moolenaar coladvance((colnr_T)MAXCOL);
110311abd095SBram Moolenaar while (curwin->w_cursor.col && vim_isspace(gchar_cursor()))
110411abd095SBram Moolenaar dec_cursor();
110511abd095SBram Moolenaar
110611abd095SBram Moolenaar // do the formatting, without 'showmode'
110711abd095SBram Moolenaar State = INSERT; // for open_line()
110811abd095SBram Moolenaar smd_save = p_smd;
110911abd095SBram Moolenaar p_smd = FALSE;
111011abd095SBram Moolenaar insertchar(NUL, INSCHAR_FORMAT
111111abd095SBram Moolenaar + (do_comments ? INSCHAR_DO_COM : 0)
111211abd095SBram Moolenaar + (do_comments && do_comments_list
111311abd095SBram Moolenaar ? INSCHAR_COM_LIST : 0)
111411abd095SBram Moolenaar + (avoid_fex ? INSCHAR_NO_FEX : 0), second_indent);
111511abd095SBram Moolenaar State = old_State;
111611abd095SBram Moolenaar p_smd = smd_save;
111711abd095SBram Moolenaar second_indent = -1;
111811abd095SBram Moolenaar // at end of par.: need to set indent of next par.
111911abd095SBram Moolenaar need_set_indent = is_end_par;
112011abd095SBram Moolenaar if (is_end_par)
112111abd095SBram Moolenaar {
112211abd095SBram Moolenaar // When called with a negative line count, break at the
112311abd095SBram Moolenaar // end of the paragraph.
112411abd095SBram Moolenaar if (line_count < 0)
112511abd095SBram Moolenaar break;
112611abd095SBram Moolenaar first_par_line = TRUE;
112711abd095SBram Moolenaar }
112811abd095SBram Moolenaar force_format = FALSE;
112911abd095SBram Moolenaar }
113011abd095SBram Moolenaar
113111abd095SBram Moolenaar // When still in same paragraph, join the lines together. But
113211abd095SBram Moolenaar // first delete the leader from the second line.
113311abd095SBram Moolenaar if (!is_end_par)
113411abd095SBram Moolenaar {
113511abd095SBram Moolenaar advance = FALSE;
113611abd095SBram Moolenaar curwin->w_cursor.lnum++;
113711abd095SBram Moolenaar curwin->w_cursor.col = 0;
113811abd095SBram Moolenaar if (line_count < 0 && u_save_cursor() == FAIL)
113911abd095SBram Moolenaar break;
114011abd095SBram Moolenaar if (next_leader_len > 0)
114111abd095SBram Moolenaar {
114211abd095SBram Moolenaar (void)del_bytes((long)next_leader_len, FALSE, FALSE);
114311abd095SBram Moolenaar mark_col_adjust(curwin->w_cursor.lnum, (colnr_T)0, 0L,
114411abd095SBram Moolenaar (long)-next_leader_len, 0);
114511abd095SBram Moolenaar }
114611abd095SBram Moolenaar else if (second_indent > 0) // the "leader" for FO_Q_SECOND
114711abd095SBram Moolenaar {
114811abd095SBram Moolenaar int indent = getwhitecols_curline();
114911abd095SBram Moolenaar
115011abd095SBram Moolenaar if (indent > 0)
115111abd095SBram Moolenaar {
115211abd095SBram Moolenaar (void)del_bytes(indent, FALSE, FALSE);
115311abd095SBram Moolenaar mark_col_adjust(curwin->w_cursor.lnum,
115411abd095SBram Moolenaar (colnr_T)0, 0L, (long)-indent, 0);
115511abd095SBram Moolenaar }
115611abd095SBram Moolenaar }
115711abd095SBram Moolenaar curwin->w_cursor.lnum--;
115811abd095SBram Moolenaar if (do_join(2, TRUE, FALSE, FALSE, FALSE) == FAIL)
115911abd095SBram Moolenaar {
116011abd095SBram Moolenaar beep_flush();
116111abd095SBram Moolenaar break;
116211abd095SBram Moolenaar }
116311abd095SBram Moolenaar first_par_line = FALSE;
116411abd095SBram Moolenaar // If the line is getting long, format it next time
116511abd095SBram Moolenaar if (STRLEN(ml_get_curline()) > (size_t)max_len)
116611abd095SBram Moolenaar force_format = TRUE;
116711abd095SBram Moolenaar else
116811abd095SBram Moolenaar force_format = FALSE;
116911abd095SBram Moolenaar }
117011abd095SBram Moolenaar }
117111abd095SBram Moolenaar line_breakcheck();
117211abd095SBram Moolenaar }
117311abd095SBram Moolenaar }
1174