blob: 41ec2cfe4be98085802ab05a06183d7526c0c8f8 [file] [log] [blame]
Bram Moolenaar11abd092020-05-01 14:26:37 +02001/* vi:set ts=8 sts=4 sw=4 noet:
2 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 *
5 * Do ":help uganda" in Vim to read copying and usage conditions.
6 * Do ":help credits" in Vim to see a list of people who contributed.
7 * See README.txt for an overview of the Vim source code.
8 */
9
10/*
11 * textformat.c: text formatting functions
12 */
13
14#include "vim.h"
15
16static int did_add_space = FALSE; // auto_format() added an extra space
17 // under the cursor
18
19#define WHITECHAR(cc) (VIM_ISWHITE(cc) && (!enc_utf8 || !utf_iscomposing(utf_ptr2char(ml_get_cursor() + 1))))
20
21/*
22 * Return TRUE if format option 'x' is in effect.
23 * Take care of no formatting when 'paste' is set.
24 */
25 int
26has_format_option(int x)
27{
28 if (p_paste)
29 return FALSE;
30 return (vim_strchr(curbuf->b_p_fo, x) != NULL);
31}
32
33/*
34 * Format text at the current insert position.
35 *
36 * If the INSCHAR_COM_LIST flag is present, then the value of second_indent
37 * will be the comment leader length sent to open_line().
38 */
39 void
40internal_format(
41 int textwidth,
42 int second_indent,
43 int flags,
44 int format_only,
45 int c) // character to be inserted (can be NUL)
46{
47 int cc;
Bram Moolenaare52702f2020-06-04 18:22:13 +020048 int skip_pos;
Bram Moolenaar11abd092020-05-01 14:26:37 +020049 int save_char = NUL;
50 int haveto_redraw = FALSE;
51 int fo_ins_blank = has_format_option(FO_INS_BLANK);
52 int fo_multibyte = has_format_option(FO_MBYTE_BREAK);
Bram Moolenaare52702f2020-06-04 18:22:13 +020053 int fo_rigor_tw = has_format_option(FO_RIGOROUS_TW);
Bram Moolenaar11abd092020-05-01 14:26:37 +020054 int fo_white_par = has_format_option(FO_WHITE_PAR);
55 int first_line = TRUE;
56 colnr_T leader_len;
57 int no_leader = FALSE;
58 int do_comments = (flags & INSCHAR_DO_COM);
Christian Brabandtbba79802024-04-11 22:54:44 +020059 int safe_tw = trim_to_int(8 * (vimlong_T)textwidth);
Bram Moolenaar11abd092020-05-01 14:26:37 +020060#ifdef FEAT_LINEBREAK
61 int has_lbr = curwin->w_p_lbr;
62
63 // make sure win_lbr_chartabsize() counts correctly
64 curwin->w_p_lbr = FALSE;
65#endif
66
67 // When 'ai' is off we don't want a space under the cursor to be
68 // deleted. Replace it with an 'x' temporarily.
69 if (!curbuf->b_p_ai && !(State & VREPLACE_FLAG))
70 {
71 cc = gchar_cursor();
72 if (VIM_ISWHITE(cc))
73 {
74 save_char = cc;
75 pchar_cursor('x');
76 }
77 }
78
79 // Repeat breaking lines, until the current line is not too long.
80 while (!got_int)
81 {
82 int startcol; // Cursor column at entry
83 int wantcol; // column at textwidth border
84 int foundcol; // column for start of spaces
85 int end_foundcol = 0; // column for start of word
86 colnr_T len;
87 colnr_T virtcol;
88 int orig_col = 0;
89 char_u *saved_text = NULL;
90 colnr_T col;
91 colnr_T end_col;
92 int wcc; // counter for whitespace chars
Bram Moolenaar6e371ec2021-12-12 14:16:39 +000093 int did_do_comment = FALSE;
kawaii-Code78019df2024-01-25 21:40:05 +010094 int first_pass;
Bram Moolenaar11abd092020-05-01 14:26:37 +020095
kawaii-Code78019df2024-01-25 21:40:05 +010096 // Cursor is currently at the end of line. No need to format
97 // if line length is less than textwidth (8 * textwidth for
98 // utf safety)
Christian Brabandtbba79802024-04-11 22:54:44 +020099 if (curwin->w_cursor.col < safe_tw)
kawaii-Code78019df2024-01-25 21:40:05 +0100100 {
101 virtcol = get_nolist_virtcol()
102 + char2cells(c != NUL ? c : gchar_cursor());
103 if (virtcol <= (colnr_T)textwidth)
104 break;
105 }
Bram Moolenaar11abd092020-05-01 14:26:37 +0200106
107 if (no_leader)
108 do_comments = FALSE;
109 else if (!(flags & INSCHAR_FORMAT)
110 && has_format_option(FO_WRAP_COMS))
111 do_comments = TRUE;
112
113 // Don't break until after the comment leader
114 if (do_comments)
Bram Moolenaar48a8a832022-05-07 15:43:52 +0100115 {
116 char_u *line = ml_get_curline();
117
118 leader_len = get_leader_len(line, NULL, FALSE, TRUE);
Bram Moolenaar48a8a832022-05-07 15:43:52 +0100119 if (leader_len == 0 && curbuf->b_p_cin)
120 {
121 int comment_start;
122
123 // Check for a line comment after code.
124 comment_start = check_linecomment(line);
125 if (comment_start != MAXCOL)
126 {
127 leader_len = get_leader_len(
128 line + comment_start, NULL, FALSE, TRUE);
129 if (leader_len != 0)
130 leader_len += comment_start;
131 }
132 }
Bram Moolenaar48a8a832022-05-07 15:43:52 +0100133 }
Bram Moolenaar11abd092020-05-01 14:26:37 +0200134 else
135 leader_len = 0;
136
137 // If the line doesn't start with a comment leader, then don't
138 // start one in a following broken line. Avoids that a %word
139 // moved to the start of the next line causes all following lines
140 // to start with %.
141 if (leader_len == 0)
142 no_leader = TRUE;
143 if (!(flags & INSCHAR_FORMAT)
144 && leader_len == 0
145 && !has_format_option(FO_WRAP))
146
147 break;
148 if ((startcol = curwin->w_cursor.col) == 0)
149 break;
150
151 // find column of textwidth border
152 coladvance((colnr_T)textwidth);
153 wantcol = curwin->w_cursor.col;
154
kawaii-Code78019df2024-01-25 21:40:05 +0100155 // If startcol is large (a long line), formatting takes too much
156 // time. The algorithm is O(n^2), it walks from the end of the
157 // line to textwidth border every time for each line break.
158 //
159 // Ceil to 8 * textwidth to optimize.
Christian Brabandtbba79802024-04-11 22:54:44 +0200160 curwin->w_cursor.col = startcol < safe_tw ? startcol : safe_tw;
kawaii-Code78019df2024-01-25 21:40:05 +0100161
Bram Moolenaar11abd092020-05-01 14:26:37 +0200162 foundcol = 0;
Bram Moolenaare52702f2020-06-04 18:22:13 +0200163 skip_pos = 0;
kawaii-Code78019df2024-01-25 21:40:05 +0100164 first_pass = TRUE;
Bram Moolenaar11abd092020-05-01 14:26:37 +0200165
166 // Find position to break at.
167 // Stop at first entered white when 'formatoptions' has 'v'
168 while ((!fo_ins_blank && !has_format_option(FO_INS_VI))
169 || (flags & INSCHAR_FORMAT)
170 || curwin->w_cursor.lnum != Insstart.lnum
171 || curwin->w_cursor.col >= Insstart.col)
172 {
kawaii-Code78019df2024-01-25 21:40:05 +0100173 if (first_pass && c != NUL)
174 {
Bram Moolenaar11abd092020-05-01 14:26:37 +0200175 cc = c;
kawaii-Code78019df2024-01-25 21:40:05 +0100176 first_pass = FALSE;
177 }
Bram Moolenaar11abd092020-05-01 14:26:37 +0200178 else
179 cc = gchar_cursor();
180 if (WHITECHAR(cc))
181 {
182 // remember position of blank just before text
183 end_col = curwin->w_cursor.col;
184
185 // find start of sequence of blanks
186 wcc = 0;
187 while (curwin->w_cursor.col > 0 && WHITECHAR(cc))
188 {
189 dec_cursor();
190 cc = gchar_cursor();
191
192 // Increment count of how many whitespace chars in this
193 // group; we only need to know if it's more than one.
194 if (wcc < 2)
Bram Moolenaar6ed545e2022-05-09 20:09:23 +0100195 wcc++;
Bram Moolenaar11abd092020-05-01 14:26:37 +0200196 }
197 if (curwin->w_cursor.col == 0 && WHITECHAR(cc))
198 break; // only spaces in front of text
199
200 // Don't break after a period when 'formatoptions' has 'p' and
201 // there are less than two spaces.
202 if (has_format_option(FO_PERIOD_ABBR) && cc == '.' && wcc < 2)
203 continue;
204
205 // Don't break until after the comment leader
206 if (curwin->w_cursor.col < leader_len)
207 break;
208 if (has_format_option(FO_ONE_LETTER))
209 {
210 // do not break after one-letter words
211 if (curwin->w_cursor.col == 0)
212 break; // one-letter word at begin
213 // do not break "#a b" when 'tw' is 2
214 if (curwin->w_cursor.col <= leader_len)
215 break;
216 col = curwin->w_cursor.col;
217 dec_cursor();
218 cc = gchar_cursor();
219
220 if (WHITECHAR(cc))
221 continue; // one-letter, continue
222 curwin->w_cursor.col = col;
223 }
224
225 inc_cursor();
226
227 end_foundcol = end_col + 1;
228 foundcol = curwin->w_cursor.col;
229 if (curwin->w_cursor.col <= (colnr_T)wantcol)
230 break;
231 }
Bram Moolenaar264d3dd2021-12-29 14:09:32 +0000232 else if ((cc >= 0x100 || !utf_allow_break_before(cc))
233 && fo_multibyte)
Bram Moolenaar11abd092020-05-01 14:26:37 +0200234 {
Bram Moolenaare52702f2020-06-04 18:22:13 +0200235 int ncc;
236 int allow_break;
237
Bram Moolenaar11abd092020-05-01 14:26:37 +0200238 // Break after or before a multi-byte character.
239 if (curwin->w_cursor.col != startcol)
240 {
241 // Don't break until after the comment leader
242 if (curwin->w_cursor.col < leader_len)
243 break;
244 col = curwin->w_cursor.col;
245 inc_cursor();
Bram Moolenaare52702f2020-06-04 18:22:13 +0200246 ncc = gchar_cursor();
247
248 allow_break =
249 (enc_utf8 && utf_allow_break(cc, ncc))
250 || enc_dbcs;
251
252 // If we have already checked this position, skip!
253 if (curwin->w_cursor.col != skip_pos && allow_break)
Bram Moolenaar11abd092020-05-01 14:26:37 +0200254 {
255 foundcol = curwin->w_cursor.col;
256 end_foundcol = foundcol;
257 if (curwin->w_cursor.col <= (colnr_T)wantcol)
258 break;
259 }
260 curwin->w_cursor.col = col;
261 }
262
263 if (curwin->w_cursor.col == 0)
264 break;
265
Bram Moolenaare52702f2020-06-04 18:22:13 +0200266 ncc = cc;
Bram Moolenaar11abd092020-05-01 14:26:37 +0200267 col = curwin->w_cursor.col;
268
269 dec_cursor();
270 cc = gchar_cursor();
271
272 if (WHITECHAR(cc))
273 continue; // break with space
Bram Moolenaare52702f2020-06-04 18:22:13 +0200274 // Don't break until after the comment leader.
Bram Moolenaar11abd092020-05-01 14:26:37 +0200275 if (curwin->w_cursor.col < leader_len)
276 break;
277
278 curwin->w_cursor.col = col;
Bram Moolenaare52702f2020-06-04 18:22:13 +0200279 skip_pos = curwin->w_cursor.col;
Bram Moolenaar11abd092020-05-01 14:26:37 +0200280
Bram Moolenaare52702f2020-06-04 18:22:13 +0200281 allow_break =
282 (enc_utf8 && utf_allow_break(cc, ncc))
283 || enc_dbcs;
284
285 // Must handle this to respect line break prohibition.
286 if (allow_break)
287 {
288 foundcol = curwin->w_cursor.col;
289 end_foundcol = foundcol;
290 }
Bram Moolenaar11abd092020-05-01 14:26:37 +0200291 if (curwin->w_cursor.col <= (colnr_T)wantcol)
Bram Moolenaare52702f2020-06-04 18:22:13 +0200292 {
293 int ncc_allow_break =
294 (enc_utf8 && utf_allow_break_before(ncc)) || enc_dbcs;
295
296 if (allow_break)
297 break;
298 if (!ncc_allow_break && !fo_rigor_tw)
299 {
300 // Enable at most 1 punct hang outside of textwidth.
301 if (curwin->w_cursor.col == startcol)
302 {
303 // We are inserting a non-breakable char, postpone
304 // line break check to next insert.
305 end_foundcol = foundcol = 0;
306 break;
307 }
308
309 // Neither cc nor ncc is NUL if we are here, so
310 // it's safe to inc_cursor.
311 col = curwin->w_cursor.col;
312
313 inc_cursor();
314 cc = ncc;
315 ncc = gchar_cursor();
316 // handle insert
317 ncc = (ncc != NUL) ? ncc : c;
318
319 allow_break =
320 (enc_utf8 && utf_allow_break(cc, ncc))
321 || enc_dbcs;
322
323 if (allow_break)
324 {
325 // Break only when we are not at end of line.
326 end_foundcol = foundcol =
327 ncc == NUL? 0 : curwin->w_cursor.col;
328 break;
329 }
330 curwin->w_cursor.col = col;
331 }
332 }
Bram Moolenaar11abd092020-05-01 14:26:37 +0200333 }
334 if (curwin->w_cursor.col == 0)
335 break;
336 dec_cursor();
337 }
338
339 if (foundcol == 0) // no spaces, cannot break line
340 {
341 curwin->w_cursor.col = startcol;
342 break;
343 }
344
345 // Going to break the line, remove any "$" now.
346 undisplay_dollar();
347
348 // Offset between cursor position and line break is used by replace
Bram Moolenaar24959102022-05-07 20:01:16 +0100349 // stack functions. MODE_VREPLACE does not use this, and backspaces
Bram Moolenaar11abd092020-05-01 14:26:37 +0200350 // over the text instead.
351 if (State & VREPLACE_FLAG)
352 orig_col = startcol; // Will start backspacing from here
353 else
354 replace_offset = startcol - end_foundcol;
355
356 // adjust startcol for spaces that will be deleted and
357 // characters that will remain on top line
358 curwin->w_cursor.col = foundcol;
359 while ((cc = gchar_cursor(), WHITECHAR(cc))
360 && (!fo_white_par || curwin->w_cursor.col < startcol))
361 inc_cursor();
362 startcol -= curwin->w_cursor.col;
363 if (startcol < 0)
364 startcol = 0;
365
366 if (State & VREPLACE_FLAG)
367 {
Bram Moolenaar24959102022-05-07 20:01:16 +0100368 // In MODE_VREPLACE state, we will backspace over the text to be
Bram Moolenaar11abd092020-05-01 14:26:37 +0200369 // wrapped, so save a copy now to put on the next line.
370 saved_text = vim_strsave(ml_get_cursor());
371 curwin->w_cursor.col = orig_col;
372 if (saved_text == NULL)
373 break; // Can't do it, out of memory
374 saved_text[startcol] = NUL;
375
376 // Backspace over characters that will move to the next line
377 if (!fo_white_par)
378 backspace_until_column(foundcol);
379 }
380 else
381 {
382 // put cursor after pos. to break line
383 if (!fo_white_par)
384 curwin->w_cursor.col = foundcol;
385 }
386
387 // Split the line just before the margin.
388 // Only insert/delete lines, but don't really redraw the window.
389 open_line(FORWARD, OPENLINE_DELSPACES + OPENLINE_MARKFIX
390 + (fo_white_par ? OPENLINE_KEEPTRAIL : 0)
391 + (do_comments ? OPENLINE_DO_COM : 0)
Bram Moolenaar7e667782022-05-23 13:10:48 +0100392 + OPENLINE_FORMAT
Bram Moolenaar11abd092020-05-01 14:26:37 +0200393 + ((flags & INSCHAR_COM_LIST) ? OPENLINE_COM_LIST : 0)
Bram Moolenaar6e371ec2021-12-12 14:16:39 +0000394 , ((flags & INSCHAR_COM_LIST) ? second_indent : old_indent),
395 &did_do_comment);
Bram Moolenaar11abd092020-05-01 14:26:37 +0200396 if (!(flags & INSCHAR_COM_LIST))
397 old_indent = 0;
398
Bram Moolenaar6e371ec2021-12-12 14:16:39 +0000399 // If a comment leader was inserted, may also do this on a following
400 // line.
401 if (did_do_comment)
402 no_leader = FALSE;
403
Bram Moolenaar11abd092020-05-01 14:26:37 +0200404 replace_offset = 0;
405 if (first_line)
406 {
407 if (!(flags & INSCHAR_COM_LIST))
408 {
409 // This section is for auto-wrap of numeric lists. When not
410 // in insert mode (i.e. format_lines()), the INSCHAR_COM_LIST
411 // flag will be set and open_line() will handle it (as seen
412 // above). The code here (and in get_number_indent()) will
413 // recognize comments if needed...
414 if (second_indent < 0 && has_format_option(FO_Q_NUMBER))
415 second_indent =
416 get_number_indent(curwin->w_cursor.lnum - 1);
417 if (second_indent >= 0)
418 {
419 if (State & VREPLACE_FLAG)
420 change_indent(INDENT_SET, second_indent,
421 FALSE, NUL, TRUE);
422 else
423 if (leader_len > 0 && second_indent - leader_len > 0)
424 {
425 int i;
426 int padding = second_indent - leader_len;
427
428 // We started at the first_line of a numbered list
429 // that has a comment. the open_line() function has
430 // inserted the proper comment leader and positioned
431 // the cursor at the end of the split line. Now we
432 // add the additional whitespace needed after the
433 // comment leader for the numbered list.
434 for (i = 0; i < padding; i++)
435 ins_str((char_u *)" ");
436 }
437 else
438 {
439 (void)set_indent(second_indent, SIN_CHANGED);
440 }
441 }
442 }
443 first_line = FALSE;
444 }
445
446 if (State & VREPLACE_FLAG)
447 {
Bram Moolenaar24959102022-05-07 20:01:16 +0100448 // In MODE_VREPLACE state we have backspaced over the text to be
Bram Moolenaar11abd092020-05-01 14:26:37 +0200449 // moved, now we re-insert it into the new line.
450 ins_bytes(saved_text);
451 vim_free(saved_text);
452 }
453 else
454 {
455 // Check if cursor is not past the NUL off the line, cindent
456 // may have added or removed indent.
457 curwin->w_cursor.col += startcol;
zeertzjq94b7c322024-03-12 21:50:32 +0100458 len = ml_get_curline_len();
Bram Moolenaar11abd092020-05-01 14:26:37 +0200459 if (curwin->w_cursor.col > len)
460 curwin->w_cursor.col = len;
461 }
462
463 haveto_redraw = TRUE;
Bram Moolenaar11abd092020-05-01 14:26:37 +0200464 set_can_cindent(TRUE);
Bram Moolenaar11abd092020-05-01 14:26:37 +0200465 // moved the cursor, don't autoindent or cindent now
466 did_ai = FALSE;
Bram Moolenaar11abd092020-05-01 14:26:37 +0200467 did_si = FALSE;
468 can_si = FALSE;
469 can_si_back = FALSE;
Bram Moolenaar11abd092020-05-01 14:26:37 +0200470 line_breakcheck();
471 }
472
473 if (save_char != NUL) // put back space after cursor
474 pchar_cursor(save_char);
475
476#ifdef FEAT_LINEBREAK
477 curwin->w_p_lbr = has_lbr;
478#endif
479 if (!format_only && haveto_redraw)
480 {
481 update_topline();
Bram Moolenaara4d158b2022-08-14 14:17:45 +0100482 redraw_curbuf_later(UPD_VALID);
Bram Moolenaar11abd092020-05-01 14:26:37 +0200483 }
484}
485
486/*
487 * Blank lines, and lines containing only the comment leader, are left
488 * untouched by the formatting. The function returns TRUE in this
489 * case. It also returns TRUE when a line starts with the end of a comment
490 * ('e' in comment flags), so that this line is skipped, and not joined to the
491 * previous line. A new paragraph starts after a blank line, or when the
492 * comment leader changes -- webb.
493 */
494 static int
495fmt_check_par(
496 linenr_T lnum,
497 int *leader_len,
498 char_u **leader_flags,
499 int do_comments)
500{
501 char_u *flags = NULL; // init for GCC
502 char_u *ptr;
503
504 ptr = ml_get(lnum);
505 if (do_comments)
506 *leader_len = get_leader_len(ptr, leader_flags, FALSE, TRUE);
507 else
508 *leader_len = 0;
509
510 if (*leader_len > 0)
511 {
512 // Search for 'e' flag in comment leader flags.
513 flags = *leader_flags;
514 while (*flags && *flags != ':' && *flags != COM_END)
515 ++flags;
516 }
517
518 return (*skipwhite(ptr + *leader_len) == NUL
519 || (*leader_len > 0 && *flags == COM_END)
520 || startPS(lnum, NUL, FALSE));
521}
522
523/*
524 * Return TRUE if line "lnum" ends in a white character.
525 */
526 static int
527ends_in_white(linenr_T lnum)
528{
529 char_u *s = ml_get(lnum);
530 size_t l;
531
532 if (*s == NUL)
533 return FALSE;
zeertzjq94b7c322024-03-12 21:50:32 +0100534 l = ml_get_len(lnum) - 1;
Bram Moolenaar11abd092020-05-01 14:26:37 +0200535 return VIM_ISWHITE(s[l]);
536}
537
538/*
539 * Return TRUE if the two comment leaders given are the same. "lnum" is
540 * the first line. White-space is ignored. Note that the whole of
541 * 'leader1' must match 'leader2_len' characters from 'leader2' -- webb
542 */
543 static int
544same_leader(
545 linenr_T lnum,
546 int leader1_len,
547 char_u *leader1_flags,
548 int leader2_len,
549 char_u *leader2_flags)
550{
551 int idx1 = 0, idx2 = 0;
552 char_u *p;
553 char_u *line1;
554 char_u *line2;
555
556 if (leader1_len == 0)
557 return (leader2_len == 0);
558
559 // If first leader has 'f' flag, the lines can be joined only if the
560 // second line does not have a leader.
561 // If first leader has 'e' flag, the lines can never be joined.
Dominique Pelleaf4a61a2021-12-27 17:21:41 +0000562 // If first leader has 's' flag, the lines can only be joined if there is
Bram Moolenaar11abd092020-05-01 14:26:37 +0200563 // some text after it and the second line has the 'm' flag.
564 if (leader1_flags != NULL)
565 {
566 for (p = leader1_flags; *p && *p != ':'; ++p)
567 {
568 if (*p == COM_FIRST)
569 return (leader2_len == 0);
570 if (*p == COM_END)
571 return FALSE;
572 if (*p == COM_START)
573 {
zeertzjq94b7c322024-03-12 21:50:32 +0100574 int line_len = ml_get_len(lnum);
Bram Moolenaar11977f92023-01-21 13:09:19 +0000575 if (line_len <= leader1_len)
Bram Moolenaar11abd092020-05-01 14:26:37 +0200576 return FALSE;
577 if (leader2_flags == NULL || leader2_len == 0)
578 return FALSE;
579 for (p = leader2_flags; *p && *p != ':'; ++p)
580 if (*p == COM_MIDDLE)
581 return TRUE;
582 return FALSE;
583 }
584 }
585 }
586
587 // Get current line and next line, compare the leaders.
588 // The first line has to be saved, only one line can be locked at a time.
589 line1 = vim_strsave(ml_get(lnum));
590 if (line1 != NULL)
591 {
592 for (idx1 = 0; VIM_ISWHITE(line1[idx1]); ++idx1)
593 ;
594 line2 = ml_get(lnum + 1);
595 for (idx2 = 0; idx2 < leader2_len; ++idx2)
596 {
597 if (!VIM_ISWHITE(line2[idx2]))
598 {
599 if (line1[idx1++] != line2[idx2])
600 break;
601 }
602 else
603 while (VIM_ISWHITE(line1[idx1]))
604 ++idx1;
605 }
606 vim_free(line1);
607 }
608 return (idx2 == leader2_len && idx1 == leader1_len);
609}
610
611/*
612 * Return TRUE when a paragraph starts in line "lnum". Return FALSE when the
613 * previous line is in the same paragraph. Used for auto-formatting.
614 */
615 static int
616paragraph_start(linenr_T lnum)
617{
618 char_u *p;
619 int leader_len = 0; // leader len of current line
620 char_u *leader_flags = NULL; // flags for leader of current line
621 int next_leader_len; // leader len of next line
622 char_u *next_leader_flags; // flags for leader of next line
623 int do_comments; // format comments
624
625 if (lnum <= 1)
626 return TRUE; // start of the file
627
628 p = ml_get(lnum - 1);
629 if (*p == NUL)
630 return TRUE; // after empty line
631
632 do_comments = has_format_option(FO_Q_COMS);
633 if (fmt_check_par(lnum - 1, &leader_len, &leader_flags, do_comments))
634 return TRUE; // after non-paragraph line
635
636 if (fmt_check_par(lnum, &next_leader_len, &next_leader_flags, do_comments))
637 return TRUE; // "lnum" is not a paragraph line
638
639 if (has_format_option(FO_WHITE_PAR) && !ends_in_white(lnum - 1))
640 return TRUE; // missing trailing space in previous line.
641
642 if (has_format_option(FO_Q_NUMBER) && (get_number_indent(lnum) > 0))
643 return TRUE; // numbered item starts in "lnum".
644
645 if (!same_leader(lnum - 1, leader_len, leader_flags,
646 next_leader_len, next_leader_flags))
647 return TRUE; // change of comment leader.
648
649 return FALSE;
650}
651
652/*
653 * Called after inserting or deleting text: When 'formatoptions' includes the
654 * 'a' flag format from the current line until the end of the paragraph.
655 * Keep the cursor at the same position relative to the text.
656 * The caller must have saved the cursor line for undo, following ones will be
657 * saved here.
658 */
659 void
660auto_format(
661 int trailblank, // when TRUE also format with trailing blank
662 int prev_line) // may start in previous line
663{
664 pos_T pos;
665 colnr_T len;
666 char_u *old;
667 char_u *new, *pnew;
668 int wasatend;
669 int cc;
670
671 if (!has_format_option(FO_AUTO))
672 return;
673
674 pos = curwin->w_cursor;
675 old = ml_get_curline();
676
677 // may remove added space
678 check_auto_format(FALSE);
679
680 // Don't format in Insert mode when the cursor is on a trailing blank, the
681 // user might insert normal text next. Also skip formatting when "1" is
682 // in 'formatoptions' and there is a single character before the cursor.
683 // Otherwise the line would be broken and when typing another non-white
684 // next they are not joined back together.
zeertzjq94b7c322024-03-12 21:50:32 +0100685 wasatend = (pos.col == ml_get_curline_len());
Bram Moolenaar11abd092020-05-01 14:26:37 +0200686 if (*old != NUL && !trailblank && wasatend)
687 {
688 dec_cursor();
689 cc = gchar_cursor();
690 if (!WHITECHAR(cc) && curwin->w_cursor.col > 0
691 && has_format_option(FO_ONE_LETTER))
692 dec_cursor();
693 cc = gchar_cursor();
694 if (WHITECHAR(cc))
695 {
696 curwin->w_cursor = pos;
697 return;
698 }
699 curwin->w_cursor = pos;
700 }
701
702 // With the 'c' flag in 'formatoptions' and 't' missing: only format
703 // comments.
704 if (has_format_option(FO_WRAP_COMS) && !has_format_option(FO_WRAP)
705 && get_leader_len(old, NULL, FALSE, TRUE) == 0)
706 return;
707
708 // May start formatting in a previous line, so that after "x" a word is
709 // moved to the previous line if it fits there now. Only when this is not
710 // the start of a paragraph.
711 if (prev_line && !paragraph_start(curwin->w_cursor.lnum))
712 {
713 --curwin->w_cursor.lnum;
714 if (u_save_cursor() == FAIL)
715 return;
716 }
717
718 // Do the formatting and restore the cursor position. "saved_cursor" will
719 // be adjusted for the text formatting.
720 saved_cursor = pos;
721 format_lines((linenr_T)-1, FALSE);
722 curwin->w_cursor = saved_cursor;
723 saved_cursor.lnum = 0;
724
725 if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count)
726 {
727 // "cannot happen"
728 curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
729 coladvance((colnr_T)MAXCOL);
730 }
731 else
732 check_cursor_col();
733
734 // Insert mode: If the cursor is now after the end of the line while it
735 // previously wasn't, the line was broken. Because of the rule above we
736 // need to add a space when 'w' is in 'formatoptions' to keep a paragraph
737 // formatted.
738 if (!wasatend && has_format_option(FO_WHITE_PAR))
739 {
740 new = ml_get_curline();
zeertzjq94b7c322024-03-12 21:50:32 +0100741 len = ml_get_curline_len();
Bram Moolenaar11abd092020-05-01 14:26:37 +0200742 if (curwin->w_cursor.col == len)
743 {
744 pnew = vim_strnsave(new, len + 2);
745 pnew[len] = ' ';
746 pnew[len + 1] = NUL;
747 ml_replace(curwin->w_cursor.lnum, pnew, FALSE);
748 // remove the space later
749 did_add_space = TRUE;
750 }
751 else
752 // may remove added space
753 check_auto_format(FALSE);
754 }
755
756 check_cursor();
757}
758
759/*
760 * When an extra space was added to continue a paragraph for auto-formatting,
761 * delete it now. The space must be under the cursor, just after the insert
762 * position.
763 */
764 void
765check_auto_format(
766 int end_insert) // TRUE when ending Insert mode
767{
768 int c = ' ';
769 int cc;
770
Yegappan Lakshmanan032713f2023-01-25 21:05:38 +0000771 if (!did_add_space)
772 return;
773
774 cc = gchar_cursor();
775 if (!WHITECHAR(cc))
776 // Somehow the space was removed already.
777 did_add_space = FALSE;
778 else
Bram Moolenaar11abd092020-05-01 14:26:37 +0200779 {
Yegappan Lakshmanan032713f2023-01-25 21:05:38 +0000780 if (!end_insert)
Bram Moolenaar11abd092020-05-01 14:26:37 +0200781 {
Yegappan Lakshmanan032713f2023-01-25 21:05:38 +0000782 inc_cursor();
783 c = gchar_cursor();
784 dec_cursor();
785 }
786 if (c != NUL)
787 {
788 // The space is no longer at the end of the line, delete it.
789 del_char(FALSE);
790 did_add_space = FALSE;
Bram Moolenaar11abd092020-05-01 14:26:37 +0200791 }
792 }
793}
794
795/*
796 * Find out textwidth to be used for formatting:
797 * if 'textwidth' option is set, use it
798 * else if 'wrapmargin' option is set, use curwin->w_width - 'wrapmargin'
799 * if invalid value, use 0.
800 * Set default to window width (maximum 79) for "gq" operator.
801 */
802 int
803comp_textwidth(
804 int ff) // force formatting (for "gq" command)
805{
806 int textwidth;
807
808 textwidth = curbuf->b_p_tw;
809 if (textwidth == 0 && curbuf->b_p_wm)
810 {
811 // The width is the window width minus 'wrapmargin' minus all the
812 // things that add to the margin.
813 textwidth = curwin->w_width - curbuf->b_p_wm;
Sean Dewar988f7432023-08-16 14:17:36 +0100814 if (curbuf == cmdwin_buf)
Bram Moolenaar11abd092020-05-01 14:26:37 +0200815 textwidth -= 1;
Bram Moolenaar11abd092020-05-01 14:26:37 +0200816#ifdef FEAT_FOLDING
817 textwidth -= curwin->w_p_fdc;
818#endif
819#ifdef FEAT_SIGNS
820 if (signcolumn_on(curwin))
821 textwidth -= 1;
822#endif
823 if (curwin->w_p_nu || curwin->w_p_rnu)
824 textwidth -= 8;
825 }
826 if (textwidth < 0)
827 textwidth = 0;
828 if (ff && textwidth == 0)
829 {
830 textwidth = curwin->w_width - 1;
831 if (textwidth > 79)
832 textwidth = 79;
833 }
834 return textwidth;
835}
836
837/*
838 * Implementation of the format operator 'gq'.
839 */
840 void
841op_format(
842 oparg_T *oap,
843 int keep_cursor) // keep cursor on same text char
844{
845 long old_line_count = curbuf->b_ml.ml_line_count;
846
847 // Place the cursor where the "gq" or "gw" command was given, so that "u"
848 // can put it back there.
849 curwin->w_cursor = oap->cursor_start;
850
851 if (u_save((linenr_T)(oap->start.lnum - 1),
852 (linenr_T)(oap->end.lnum + 1)) == FAIL)
853 return;
854 curwin->w_cursor = oap->start;
855
856 if (oap->is_VIsual)
857 // When there is no change: need to remove the Visual selection
Bram Moolenaara4d158b2022-08-14 14:17:45 +0100858 redraw_curbuf_later(UPD_INVERTED);
Bram Moolenaar11abd092020-05-01 14:26:37 +0200859
Bram Moolenaare1004402020-10-24 20:49:43 +0200860 if ((cmdmod.cmod_flags & CMOD_LOCKMARKS) == 0)
Bram Moolenaar11abd092020-05-01 14:26:37 +0200861 // Set '[ mark at the start of the formatted area
862 curbuf->b_op_start = oap->start;
863
864 // For "gw" remember the cursor position and put it back below (adjusted
865 // for joined and split lines).
866 if (keep_cursor)
867 saved_cursor = oap->cursor_start;
868
869 format_lines(oap->line_count, keep_cursor);
870
871 // Leave the cursor at the first non-blank of the last formatted line.
872 // If the cursor was moved one line back (e.g. with "Q}") go to the next
873 // line, so "." will do the next lines.
874 if (oap->end_adjusted && curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count)
875 ++curwin->w_cursor.lnum;
876 beginline(BL_WHITE | BL_FIX);
877 old_line_count = curbuf->b_ml.ml_line_count - old_line_count;
878 msgmore(old_line_count);
879
Bram Moolenaare1004402020-10-24 20:49:43 +0200880 if ((cmdmod.cmod_flags & CMOD_LOCKMARKS) == 0)
Bram Moolenaar11abd092020-05-01 14:26:37 +0200881 // put '] mark on the end of the formatted area
882 curbuf->b_op_end = curwin->w_cursor;
883
884 if (keep_cursor)
885 {
886 curwin->w_cursor = saved_cursor;
887 saved_cursor.lnum = 0;
Bram Moolenaar78d52882022-05-24 13:57:54 +0100888
889 // formatting may have made the cursor position invalid
890 check_cursor();
Bram Moolenaar11abd092020-05-01 14:26:37 +0200891 }
892
893 if (oap->is_VIsual)
894 {
895 win_T *wp;
896
897 FOR_ALL_WINDOWS(wp)
898 {
899 if (wp->w_old_cursor_lnum != 0)
900 {
901 // When lines have been inserted or deleted, adjust the end of
902 // the Visual area to be redrawn.
903 if (wp->w_old_cursor_lnum > wp->w_old_visual_lnum)
904 wp->w_old_cursor_lnum += old_line_count;
905 else
906 wp->w_old_visual_lnum += old_line_count;
907 }
908 }
909 }
910}
911
912#if defined(FEAT_EVAL) || defined(PROTO)
913/*
914 * Implementation of the format operator 'gq' for when using 'formatexpr'.
915 */
916 void
917op_formatexpr(oparg_T *oap)
918{
919 if (oap->is_VIsual)
920 // When there is no change: need to remove the Visual selection
Bram Moolenaara4d158b2022-08-14 14:17:45 +0100921 redraw_curbuf_later(UPD_INVERTED);
Bram Moolenaar11abd092020-05-01 14:26:37 +0200922
923 if (fex_format(oap->start.lnum, oap->line_count, NUL) != 0)
924 // As documented: when 'formatexpr' returns non-zero fall back to
925 // internal formatting.
926 op_format(oap, FALSE);
927}
928
929 int
930fex_format(
931 linenr_T lnum,
932 long count,
933 int c) // character to be inserted
934{
935 int use_sandbox = was_set_insecurely((char_u *)"formatexpr",
936 OPT_LOCAL);
937 int r;
938 char_u *fex;
Bram Moolenaar3ba685e2022-01-22 19:17:31 +0000939 sctx_T save_sctx = current_sctx;
Bram Moolenaar11abd092020-05-01 14:26:37 +0200940
941 // Set v:lnum to the first line number and v:count to the number of lines.
942 // Set v:char to the character to be inserted (can be NUL).
943 set_vim_var_nr(VV_LNUM, lnum);
944 set_vim_var_nr(VV_COUNT, count);
945 set_vim_var_char(c);
946
947 // Make a copy, the option could be changed while calling it.
948 fex = vim_strsave(curbuf->b_p_fex);
949 if (fex == NULL)
950 return 0;
Bram Moolenaar3ba685e2022-01-22 19:17:31 +0000951 current_sctx = curbuf->b_p_script_ctx[BV_FEX];
Bram Moolenaar11abd092020-05-01 14:26:37 +0200952
953 // Evaluate the function.
954 if (use_sandbox)
955 ++sandbox;
Bram Moolenaara4e0b972022-10-01 19:43:52 +0100956 r = (int)eval_to_number(fex, TRUE);
Bram Moolenaar11abd092020-05-01 14:26:37 +0200957 if (use_sandbox)
958 --sandbox;
959
960 set_vim_var_string(VV_CHAR, NULL, -1);
961 vim_free(fex);
Bram Moolenaar3ba685e2022-01-22 19:17:31 +0000962 current_sctx = save_sctx;
Bram Moolenaar11abd092020-05-01 14:26:37 +0200963
964 return r;
965}
966#endif
967
968/*
969 * Format "line_count" lines, starting at the cursor position.
970 * When "line_count" is negative, format until the end of the paragraph.
971 * Lines after the cursor line are saved for undo, caller must have saved the
972 * first line.
973 */
974 void
975format_lines(
976 linenr_T line_count,
977 int avoid_fex) // don't use 'formatexpr'
978{
979 int max_len;
980 int is_not_par; // current line not part of parag.
981 int next_is_not_par; // next line not part of paragraph
982 int is_end_par; // at end of paragraph
983 int prev_is_end_par = FALSE;// prev. line not part of parag.
984 int next_is_start_par = FALSE;
985 int leader_len = 0; // leader len of current line
986 int next_leader_len; // leader len of next line
987 char_u *leader_flags = NULL; // flags for leader of current line
Bram Moolenaar264d3dd2021-12-29 14:09:32 +0000988 char_u *next_leader_flags = NULL; // flags for leader of next line
Bram Moolenaar11abd092020-05-01 14:26:37 +0200989 int do_comments; // format comments
990 int do_comments_list = 0; // format comments with 'n' or '2'
991 int advance = TRUE;
992 int second_indent = -1; // indent for second line (comment
993 // aware)
994 int do_second_indent;
995 int do_number_indent;
996 int do_trail_white;
997 int first_par_line = TRUE;
998 int smd_save;
999 long count;
1000 int need_set_indent = TRUE; // set indent of next paragraph
Bram Moolenaarecabb512021-12-06 19:51:01 +00001001 linenr_T first_line = curwin->w_cursor.lnum;
Bram Moolenaar11abd092020-05-01 14:26:37 +02001002 int force_format = FALSE;
1003 int old_State = State;
1004
1005 // length of a line to force formatting: 3 * 'tw'
1006 max_len = comp_textwidth(TRUE) * 3;
1007
Christian Brabandt305127f2023-11-11 18:59:33 +01001008 // check for 'q', '2', 'n' and 'w' in 'formatoptions'
Bram Moolenaar11abd092020-05-01 14:26:37 +02001009 do_comments = has_format_option(FO_Q_COMS);
1010 do_second_indent = has_format_option(FO_Q_SECOND);
1011 do_number_indent = has_format_option(FO_Q_NUMBER);
1012 do_trail_white = has_format_option(FO_WHITE_PAR);
1013
1014 // Get info about the previous and current line.
1015 if (curwin->w_cursor.lnum > 1)
1016 is_not_par = fmt_check_par(curwin->w_cursor.lnum - 1
1017 , &leader_len, &leader_flags, do_comments);
1018 else
1019 is_not_par = TRUE;
1020 next_is_not_par = fmt_check_par(curwin->w_cursor.lnum
1021 , &next_leader_len, &next_leader_flags, do_comments);
1022 is_end_par = (is_not_par || next_is_not_par);
1023 if (!is_end_par && do_trail_white)
1024 is_end_par = !ends_in_white(curwin->w_cursor.lnum - 1);
1025
1026 curwin->w_cursor.lnum--;
1027 for (count = line_count; count != 0 && !got_int; --count)
1028 {
1029 // Advance to next paragraph.
1030 if (advance)
1031 {
1032 curwin->w_cursor.lnum++;
1033 prev_is_end_par = is_end_par;
1034 is_not_par = next_is_not_par;
1035 leader_len = next_leader_len;
1036 leader_flags = next_leader_flags;
1037 }
1038
1039 // The last line to be formatted.
1040 if (count == 1 || curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count)
1041 {
1042 next_is_not_par = TRUE;
1043 next_leader_len = 0;
1044 next_leader_flags = NULL;
1045 }
1046 else
1047 {
1048 next_is_not_par = fmt_check_par(curwin->w_cursor.lnum + 1
1049 , &next_leader_len, &next_leader_flags, do_comments);
1050 if (do_number_indent)
1051 next_is_start_par =
1052 (get_number_indent(curwin->w_cursor.lnum + 1) > 0);
1053 }
1054 advance = TRUE;
1055 is_end_par = (is_not_par || next_is_not_par || next_is_start_par);
1056 if (!is_end_par && do_trail_white)
1057 is_end_par = !ends_in_white(curwin->w_cursor.lnum);
1058
1059 // Skip lines that are not in a paragraph.
1060 if (is_not_par)
1061 {
1062 if (line_count < 0)
1063 break;
1064 }
1065 else
1066 {
1067 // For the first line of a paragraph, check indent of second line.
1068 // Don't do this for comments and empty lines.
1069 if (first_par_line
1070 && (do_second_indent || do_number_indent)
1071 && prev_is_end_par
1072 && curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count)
1073 {
1074 if (do_second_indent && !LINEEMPTY(curwin->w_cursor.lnum + 1))
1075 {
1076 if (leader_len == 0 && next_leader_len == 0)
1077 {
1078 // no comment found
1079 second_indent =
1080 get_indent_lnum(curwin->w_cursor.lnum + 1);
1081 }
1082 else
1083 {
1084 second_indent = next_leader_len;
1085 do_comments_list = 1;
1086 }
1087 }
1088 else if (do_number_indent)
1089 {
1090 if (leader_len == 0 && next_leader_len == 0)
1091 {
1092 // no comment found
1093 second_indent =
1094 get_number_indent(curwin->w_cursor.lnum);
1095 }
1096 else
1097 {
1098 // get_number_indent() is now "comment aware"...
1099 second_indent =
1100 get_number_indent(curwin->w_cursor.lnum);
1101 do_comments_list = 1;
1102 }
1103 }
1104 }
1105
1106 // When the comment leader changes, it's the end of the paragraph.
1107 if (curwin->w_cursor.lnum >= curbuf->b_ml.ml_line_count
1108 || !same_leader(curwin->w_cursor.lnum,
1109 leader_len, leader_flags,
1110 next_leader_len, next_leader_flags))
Bram Moolenaar264d3dd2021-12-29 14:09:32 +00001111 {
1112 // Special case: If the next line starts with a line comment
1113 // and this line has a line comment after some text, the
1114 // paragraph doesn't really end.
1115 if (next_leader_flags == NULL
1116 || STRNCMP(next_leader_flags, "://", 3) != 0
1117 || check_linecomment(ml_get_curline()) == MAXCOL)
Bram Moolenaar11abd092020-05-01 14:26:37 +02001118 is_end_par = TRUE;
Bram Moolenaar264d3dd2021-12-29 14:09:32 +00001119 }
Bram Moolenaar11abd092020-05-01 14:26:37 +02001120
1121 // If we have got to the end of a paragraph, or the line is
1122 // getting long, format it.
1123 if (is_end_par || force_format)
1124 {
1125 if (need_set_indent)
Christian Brabandt818ff252021-11-18 13:56:37 +00001126 {
1127 int indent = 0; // amount of indent needed
1128
Bram Moolenaarecabb512021-12-06 19:51:01 +00001129 // Replace indent in first line of a paragraph with minimal
1130 // number of tabs and spaces, according to current options.
1131 // For the very first formatted line keep the current
1132 // indent.
1133 if (curwin->w_cursor.lnum == first_line)
1134 indent = get_indent();
Bram Moolenaar8e145b82022-05-21 20:17:31 +01001135 else if (curbuf->b_p_lisp)
Christian Brabandt818ff252021-11-18 13:56:37 +00001136 indent = get_lisp_indent();
1137 else
Christian Brabandt818ff252021-11-18 13:56:37 +00001138 {
Christian Brabandt818ff252021-11-18 13:56:37 +00001139 if (cindent_on())
1140 {
1141 indent =
1142# ifdef FEAT_EVAL
1143 *curbuf->b_p_inde != NUL ? get_expr_indent() :
1144# endif
1145 get_c_indent();
1146 }
1147 else
Christian Brabandt818ff252021-11-18 13:56:37 +00001148 indent = get_indent();
1149 }
1150 (void)set_indent(indent, SIN_CHANGED);
1151 }
Bram Moolenaar11abd092020-05-01 14:26:37 +02001152
1153 // put cursor on last non-space
Bram Moolenaar24959102022-05-07 20:01:16 +01001154 State = MODE_NORMAL; // don't go past end-of-line
Bram Moolenaar11abd092020-05-01 14:26:37 +02001155 coladvance((colnr_T)MAXCOL);
1156 while (curwin->w_cursor.col && vim_isspace(gchar_cursor()))
1157 dec_cursor();
1158
1159 // do the formatting, without 'showmode'
Bram Moolenaar24959102022-05-07 20:01:16 +01001160 State = MODE_INSERT; // for open_line()
Bram Moolenaar11abd092020-05-01 14:26:37 +02001161 smd_save = p_smd;
1162 p_smd = FALSE;
1163 insertchar(NUL, INSCHAR_FORMAT
1164 + (do_comments ? INSCHAR_DO_COM : 0)
1165 + (do_comments && do_comments_list
1166 ? INSCHAR_COM_LIST : 0)
1167 + (avoid_fex ? INSCHAR_NO_FEX : 0), second_indent);
1168 State = old_State;
1169 p_smd = smd_save;
1170 second_indent = -1;
1171 // at end of par.: need to set indent of next par.
1172 need_set_indent = is_end_par;
1173 if (is_end_par)
1174 {
1175 // When called with a negative line count, break at the
1176 // end of the paragraph.
1177 if (line_count < 0)
1178 break;
1179 first_par_line = TRUE;
1180 }
1181 force_format = FALSE;
1182 }
1183
1184 // When still in same paragraph, join the lines together. But
1185 // first delete the leader from the second line.
1186 if (!is_end_par)
1187 {
1188 advance = FALSE;
1189 curwin->w_cursor.lnum++;
1190 curwin->w_cursor.col = 0;
1191 if (line_count < 0 && u_save_cursor() == FAIL)
1192 break;
1193 if (next_leader_len > 0)
1194 {
1195 (void)del_bytes((long)next_leader_len, FALSE, FALSE);
1196 mark_col_adjust(curwin->w_cursor.lnum, (colnr_T)0, 0L,
1197 (long)-next_leader_len, 0);
1198 }
1199 else if (second_indent > 0) // the "leader" for FO_Q_SECOND
1200 {
1201 int indent = getwhitecols_curline();
1202
1203 if (indent > 0)
1204 {
1205 (void)del_bytes(indent, FALSE, FALSE);
1206 mark_col_adjust(curwin->w_cursor.lnum,
Bram Moolenaar113d9de2022-08-08 15:49:18 +01001207 (colnr_T)0, 0L, (long)-indent, 0);
Bram Moolenaar11abd092020-05-01 14:26:37 +02001208 }
1209 }
1210 curwin->w_cursor.lnum--;
1211 if (do_join(2, TRUE, FALSE, FALSE, FALSE) == FAIL)
1212 {
1213 beep_flush();
1214 break;
1215 }
1216 first_par_line = FALSE;
1217 // If the line is getting long, format it next time
zeertzjq94b7c322024-03-12 21:50:32 +01001218 if (ml_get_curline_len() > max_len)
Bram Moolenaar11abd092020-05-01 14:26:37 +02001219 force_format = TRUE;
1220 else
1221 force_format = FALSE;
1222 }
1223 }
1224 line_breakcheck();
1225 }
1226}