blob: 500e8895c818950f67d6c272ab3c31eb62ce2ecc [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);
59#ifdef FEAT_LINEBREAK
60 int has_lbr = curwin->w_p_lbr;
61
62 // make sure win_lbr_chartabsize() counts correctly
63 curwin->w_p_lbr = FALSE;
64#endif
65
66 // When 'ai' is off we don't want a space under the cursor to be
67 // deleted. Replace it with an 'x' temporarily.
68 if (!curbuf->b_p_ai && !(State & VREPLACE_FLAG))
69 {
70 cc = gchar_cursor();
71 if (VIM_ISWHITE(cc))
72 {
73 save_char = cc;
74 pchar_cursor('x');
75 }
76 }
77
78 // Repeat breaking lines, until the current line is not too long.
79 while (!got_int)
80 {
81 int startcol; // Cursor column at entry
82 int wantcol; // column at textwidth border
83 int foundcol; // column for start of spaces
84 int end_foundcol = 0; // column for start of word
85 colnr_T len;
86 colnr_T virtcol;
87 int orig_col = 0;
88 char_u *saved_text = NULL;
89 colnr_T col;
90 colnr_T end_col;
91 int wcc; // counter for whitespace chars
Bram Moolenaar6e371ec2021-12-12 14:16:39 +000092 int did_do_comment = FALSE;
kawaii-Code78019df2024-01-25 21:40:05 +010093 int first_pass;
Bram Moolenaar11abd092020-05-01 14:26:37 +020094
kawaii-Code78019df2024-01-25 21:40:05 +010095 // Cursor is currently at the end of line. No need to format
96 // if line length is less than textwidth (8 * textwidth for
97 // utf safety)
98 if (curwin->w_cursor.col < 8 * textwidth)
99 {
100 virtcol = get_nolist_virtcol()
101 + char2cells(c != NUL ? c : gchar_cursor());
102 if (virtcol <= (colnr_T)textwidth)
103 break;
104 }
Bram Moolenaar11abd092020-05-01 14:26:37 +0200105
106 if (no_leader)
107 do_comments = FALSE;
108 else if (!(flags & INSCHAR_FORMAT)
109 && has_format_option(FO_WRAP_COMS))
110 do_comments = TRUE;
111
112 // Don't break until after the comment leader
113 if (do_comments)
Bram Moolenaar48a8a832022-05-07 15:43:52 +0100114 {
115 char_u *line = ml_get_curline();
116
117 leader_len = get_leader_len(line, NULL, FALSE, TRUE);
Bram Moolenaar48a8a832022-05-07 15:43:52 +0100118 if (leader_len == 0 && curbuf->b_p_cin)
119 {
120 int comment_start;
121
122 // Check for a line comment after code.
123 comment_start = check_linecomment(line);
124 if (comment_start != MAXCOL)
125 {
126 leader_len = get_leader_len(
127 line + comment_start, NULL, FALSE, TRUE);
128 if (leader_len != 0)
129 leader_len += comment_start;
130 }
131 }
Bram Moolenaar48a8a832022-05-07 15:43:52 +0100132 }
Bram Moolenaar11abd092020-05-01 14:26:37 +0200133 else
134 leader_len = 0;
135
136 // If the line doesn't start with a comment leader, then don't
137 // start one in a following broken line. Avoids that a %word
138 // moved to the start of the next line causes all following lines
139 // to start with %.
140 if (leader_len == 0)
141 no_leader = TRUE;
142 if (!(flags & INSCHAR_FORMAT)
143 && leader_len == 0
144 && !has_format_option(FO_WRAP))
145
146 break;
147 if ((startcol = curwin->w_cursor.col) == 0)
148 break;
149
150 // find column of textwidth border
151 coladvance((colnr_T)textwidth);
152 wantcol = curwin->w_cursor.col;
153
kawaii-Code78019df2024-01-25 21:40:05 +0100154 // If startcol is large (a long line), formatting takes too much
155 // time. The algorithm is O(n^2), it walks from the end of the
156 // line to textwidth border every time for each line break.
157 //
158 // Ceil to 8 * textwidth to optimize.
159 curwin->w_cursor.col = startcol < 8 * textwidth ? startcol :
160 8 * textwidth;
161
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;
458 len = (colnr_T)STRLEN(ml_get_curline());
459 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;
534 // Don't use STRLEN() inside VIM_ISWHITE(), SAS/C complains: "macro
535 // invocation may call function multiple times".
536 l = STRLEN(s) - 1;
537 return VIM_ISWHITE(s[l]);
538}
539
540/*
541 * Return TRUE if the two comment leaders given are the same. "lnum" is
542 * the first line. White-space is ignored. Note that the whole of
543 * 'leader1' must match 'leader2_len' characters from 'leader2' -- webb
544 */
545 static int
546same_leader(
547 linenr_T lnum,
548 int leader1_len,
549 char_u *leader1_flags,
550 int leader2_len,
551 char_u *leader2_flags)
552{
553 int idx1 = 0, idx2 = 0;
554 char_u *p;
555 char_u *line1;
556 char_u *line2;
557
558 if (leader1_len == 0)
559 return (leader2_len == 0);
560
561 // If first leader has 'f' flag, the lines can be joined only if the
562 // second line does not have a leader.
563 // If first leader has 'e' flag, the lines can never be joined.
Dominique Pelleaf4a61a2021-12-27 17:21:41 +0000564 // If first leader has 's' flag, the lines can only be joined if there is
Bram Moolenaar11abd092020-05-01 14:26:37 +0200565 // some text after it and the second line has the 'm' flag.
566 if (leader1_flags != NULL)
567 {
568 for (p = leader1_flags; *p && *p != ':'; ++p)
569 {
570 if (*p == COM_FIRST)
571 return (leader2_len == 0);
572 if (*p == COM_END)
573 return FALSE;
574 if (*p == COM_START)
575 {
zeertzjq9cbf7912023-01-23 16:57:08 +0000576 int line_len = (int)STRLEN(ml_get(lnum));
Bram Moolenaar11977f92023-01-21 13:09:19 +0000577 if (line_len <= leader1_len)
Bram Moolenaar11abd092020-05-01 14:26:37 +0200578 return FALSE;
579 if (leader2_flags == NULL || leader2_len == 0)
580 return FALSE;
581 for (p = leader2_flags; *p && *p != ':'; ++p)
582 if (*p == COM_MIDDLE)
583 return TRUE;
584 return FALSE;
585 }
586 }
587 }
588
589 // Get current line and next line, compare the leaders.
590 // The first line has to be saved, only one line can be locked at a time.
591 line1 = vim_strsave(ml_get(lnum));
592 if (line1 != NULL)
593 {
594 for (idx1 = 0; VIM_ISWHITE(line1[idx1]); ++idx1)
595 ;
596 line2 = ml_get(lnum + 1);
597 for (idx2 = 0; idx2 < leader2_len; ++idx2)
598 {
599 if (!VIM_ISWHITE(line2[idx2]))
600 {
601 if (line1[idx1++] != line2[idx2])
602 break;
603 }
604 else
605 while (VIM_ISWHITE(line1[idx1]))
606 ++idx1;
607 }
608 vim_free(line1);
609 }
610 return (idx2 == leader2_len && idx1 == leader1_len);
611}
612
613/*
614 * Return TRUE when a paragraph starts in line "lnum". Return FALSE when the
615 * previous line is in the same paragraph. Used for auto-formatting.
616 */
617 static int
618paragraph_start(linenr_T lnum)
619{
620 char_u *p;
621 int leader_len = 0; // leader len of current line
622 char_u *leader_flags = NULL; // flags for leader of current line
623 int next_leader_len; // leader len of next line
624 char_u *next_leader_flags; // flags for leader of next line
625 int do_comments; // format comments
626
627 if (lnum <= 1)
628 return TRUE; // start of the file
629
630 p = ml_get(lnum - 1);
631 if (*p == NUL)
632 return TRUE; // after empty line
633
634 do_comments = has_format_option(FO_Q_COMS);
635 if (fmt_check_par(lnum - 1, &leader_len, &leader_flags, do_comments))
636 return TRUE; // after non-paragraph line
637
638 if (fmt_check_par(lnum, &next_leader_len, &next_leader_flags, do_comments))
639 return TRUE; // "lnum" is not a paragraph line
640
641 if (has_format_option(FO_WHITE_PAR) && !ends_in_white(lnum - 1))
642 return TRUE; // missing trailing space in previous line.
643
644 if (has_format_option(FO_Q_NUMBER) && (get_number_indent(lnum) > 0))
645 return TRUE; // numbered item starts in "lnum".
646
647 if (!same_leader(lnum - 1, leader_len, leader_flags,
648 next_leader_len, next_leader_flags))
649 return TRUE; // change of comment leader.
650
651 return FALSE;
652}
653
654/*
655 * Called after inserting or deleting text: When 'formatoptions' includes the
656 * 'a' flag format from the current line until the end of the paragraph.
657 * Keep the cursor at the same position relative to the text.
658 * The caller must have saved the cursor line for undo, following ones will be
659 * saved here.
660 */
661 void
662auto_format(
663 int trailblank, // when TRUE also format with trailing blank
664 int prev_line) // may start in previous line
665{
666 pos_T pos;
667 colnr_T len;
668 char_u *old;
669 char_u *new, *pnew;
670 int wasatend;
671 int cc;
672
673 if (!has_format_option(FO_AUTO))
674 return;
675
676 pos = curwin->w_cursor;
677 old = ml_get_curline();
678
679 // may remove added space
680 check_auto_format(FALSE);
681
682 // Don't format in Insert mode when the cursor is on a trailing blank, the
683 // user might insert normal text next. Also skip formatting when "1" is
684 // in 'formatoptions' and there is a single character before the cursor.
685 // Otherwise the line would be broken and when typing another non-white
686 // next they are not joined back together.
687 wasatend = (pos.col == (colnr_T)STRLEN(old));
688 if (*old != NUL && !trailblank && wasatend)
689 {
690 dec_cursor();
691 cc = gchar_cursor();
692 if (!WHITECHAR(cc) && curwin->w_cursor.col > 0
693 && has_format_option(FO_ONE_LETTER))
694 dec_cursor();
695 cc = gchar_cursor();
696 if (WHITECHAR(cc))
697 {
698 curwin->w_cursor = pos;
699 return;
700 }
701 curwin->w_cursor = pos;
702 }
703
704 // With the 'c' flag in 'formatoptions' and 't' missing: only format
705 // comments.
706 if (has_format_option(FO_WRAP_COMS) && !has_format_option(FO_WRAP)
707 && get_leader_len(old, NULL, FALSE, TRUE) == 0)
708 return;
709
710 // May start formatting in a previous line, so that after "x" a word is
711 // moved to the previous line if it fits there now. Only when this is not
712 // the start of a paragraph.
713 if (prev_line && !paragraph_start(curwin->w_cursor.lnum))
714 {
715 --curwin->w_cursor.lnum;
716 if (u_save_cursor() == FAIL)
717 return;
718 }
719
720 // Do the formatting and restore the cursor position. "saved_cursor" will
721 // be adjusted for the text formatting.
722 saved_cursor = pos;
723 format_lines((linenr_T)-1, FALSE);
724 curwin->w_cursor = saved_cursor;
725 saved_cursor.lnum = 0;
726
727 if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count)
728 {
729 // "cannot happen"
730 curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
731 coladvance((colnr_T)MAXCOL);
732 }
733 else
734 check_cursor_col();
735
736 // Insert mode: If the cursor is now after the end of the line while it
737 // previously wasn't, the line was broken. Because of the rule above we
738 // need to add a space when 'w' is in 'formatoptions' to keep a paragraph
739 // formatted.
740 if (!wasatend && has_format_option(FO_WHITE_PAR))
741 {
742 new = ml_get_curline();
743 len = (colnr_T)STRLEN(new);
744 if (curwin->w_cursor.col == len)
745 {
746 pnew = vim_strnsave(new, len + 2);
747 pnew[len] = ' ';
748 pnew[len + 1] = NUL;
749 ml_replace(curwin->w_cursor.lnum, pnew, FALSE);
750 // remove the space later
751 did_add_space = TRUE;
752 }
753 else
754 // may remove added space
755 check_auto_format(FALSE);
756 }
757
758 check_cursor();
759}
760
761/*
762 * When an extra space was added to continue a paragraph for auto-formatting,
763 * delete it now. The space must be under the cursor, just after the insert
764 * position.
765 */
766 void
767check_auto_format(
768 int end_insert) // TRUE when ending Insert mode
769{
770 int c = ' ';
771 int cc;
772
Yegappan Lakshmanan032713f2023-01-25 21:05:38 +0000773 if (!did_add_space)
774 return;
775
776 cc = gchar_cursor();
777 if (!WHITECHAR(cc))
778 // Somehow the space was removed already.
779 did_add_space = FALSE;
780 else
Bram Moolenaar11abd092020-05-01 14:26:37 +0200781 {
Yegappan Lakshmanan032713f2023-01-25 21:05:38 +0000782 if (!end_insert)
Bram Moolenaar11abd092020-05-01 14:26:37 +0200783 {
Yegappan Lakshmanan032713f2023-01-25 21:05:38 +0000784 inc_cursor();
785 c = gchar_cursor();
786 dec_cursor();
787 }
788 if (c != NUL)
789 {
790 // The space is no longer at the end of the line, delete it.
791 del_char(FALSE);
792 did_add_space = FALSE;
Bram Moolenaar11abd092020-05-01 14:26:37 +0200793 }
794 }
795}
796
797/*
798 * Find out textwidth to be used for formatting:
799 * if 'textwidth' option is set, use it
800 * else if 'wrapmargin' option is set, use curwin->w_width - 'wrapmargin'
801 * if invalid value, use 0.
802 * Set default to window width (maximum 79) for "gq" operator.
803 */
804 int
805comp_textwidth(
806 int ff) // force formatting (for "gq" command)
807{
808 int textwidth;
809
810 textwidth = curbuf->b_p_tw;
811 if (textwidth == 0 && curbuf->b_p_wm)
812 {
813 // The width is the window width minus 'wrapmargin' minus all the
814 // things that add to the margin.
815 textwidth = curwin->w_width - curbuf->b_p_wm;
Sean Dewar988f7432023-08-16 14:17:36 +0100816 if (curbuf == cmdwin_buf)
Bram Moolenaar11abd092020-05-01 14:26:37 +0200817 textwidth -= 1;
Bram Moolenaar11abd092020-05-01 14:26:37 +0200818#ifdef FEAT_FOLDING
819 textwidth -= curwin->w_p_fdc;
820#endif
821#ifdef FEAT_SIGNS
822 if (signcolumn_on(curwin))
823 textwidth -= 1;
824#endif
825 if (curwin->w_p_nu || curwin->w_p_rnu)
826 textwidth -= 8;
827 }
828 if (textwidth < 0)
829 textwidth = 0;
830 if (ff && textwidth == 0)
831 {
832 textwidth = curwin->w_width - 1;
833 if (textwidth > 79)
834 textwidth = 79;
835 }
836 return textwidth;
837}
838
839/*
840 * Implementation of the format operator 'gq'.
841 */
842 void
843op_format(
844 oparg_T *oap,
845 int keep_cursor) // keep cursor on same text char
846{
847 long old_line_count = curbuf->b_ml.ml_line_count;
848
849 // Place the cursor where the "gq" or "gw" command was given, so that "u"
850 // can put it back there.
851 curwin->w_cursor = oap->cursor_start;
852
853 if (u_save((linenr_T)(oap->start.lnum - 1),
854 (linenr_T)(oap->end.lnum + 1)) == FAIL)
855 return;
856 curwin->w_cursor = oap->start;
857
858 if (oap->is_VIsual)
859 // When there is no change: need to remove the Visual selection
Bram Moolenaara4d158b2022-08-14 14:17:45 +0100860 redraw_curbuf_later(UPD_INVERTED);
Bram Moolenaar11abd092020-05-01 14:26:37 +0200861
Bram Moolenaare1004402020-10-24 20:49:43 +0200862 if ((cmdmod.cmod_flags & CMOD_LOCKMARKS) == 0)
Bram Moolenaar11abd092020-05-01 14:26:37 +0200863 // Set '[ mark at the start of the formatted area
864 curbuf->b_op_start = oap->start;
865
866 // For "gw" remember the cursor position and put it back below (adjusted
867 // for joined and split lines).
868 if (keep_cursor)
869 saved_cursor = oap->cursor_start;
870
871 format_lines(oap->line_count, keep_cursor);
872
873 // Leave the cursor at the first non-blank of the last formatted line.
874 // If the cursor was moved one line back (e.g. with "Q}") go to the next
875 // line, so "." will do the next lines.
876 if (oap->end_adjusted && curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count)
877 ++curwin->w_cursor.lnum;
878 beginline(BL_WHITE | BL_FIX);
879 old_line_count = curbuf->b_ml.ml_line_count - old_line_count;
880 msgmore(old_line_count);
881
Bram Moolenaare1004402020-10-24 20:49:43 +0200882 if ((cmdmod.cmod_flags & CMOD_LOCKMARKS) == 0)
Bram Moolenaar11abd092020-05-01 14:26:37 +0200883 // put '] mark on the end of the formatted area
884 curbuf->b_op_end = curwin->w_cursor;
885
886 if (keep_cursor)
887 {
888 curwin->w_cursor = saved_cursor;
889 saved_cursor.lnum = 0;
Bram Moolenaar78d52882022-05-24 13:57:54 +0100890
891 // formatting may have made the cursor position invalid
892 check_cursor();
Bram Moolenaar11abd092020-05-01 14:26:37 +0200893 }
894
895 if (oap->is_VIsual)
896 {
897 win_T *wp;
898
899 FOR_ALL_WINDOWS(wp)
900 {
901 if (wp->w_old_cursor_lnum != 0)
902 {
903 // When lines have been inserted or deleted, adjust the end of
904 // the Visual area to be redrawn.
905 if (wp->w_old_cursor_lnum > wp->w_old_visual_lnum)
906 wp->w_old_cursor_lnum += old_line_count;
907 else
908 wp->w_old_visual_lnum += old_line_count;
909 }
910 }
911 }
912}
913
914#if defined(FEAT_EVAL) || defined(PROTO)
915/*
916 * Implementation of the format operator 'gq' for when using 'formatexpr'.
917 */
918 void
919op_formatexpr(oparg_T *oap)
920{
921 if (oap->is_VIsual)
922 // When there is no change: need to remove the Visual selection
Bram Moolenaara4d158b2022-08-14 14:17:45 +0100923 redraw_curbuf_later(UPD_INVERTED);
Bram Moolenaar11abd092020-05-01 14:26:37 +0200924
925 if (fex_format(oap->start.lnum, oap->line_count, NUL) != 0)
926 // As documented: when 'formatexpr' returns non-zero fall back to
927 // internal formatting.
928 op_format(oap, FALSE);
929}
930
931 int
932fex_format(
933 linenr_T lnum,
934 long count,
935 int c) // character to be inserted
936{
937 int use_sandbox = was_set_insecurely((char_u *)"formatexpr",
938 OPT_LOCAL);
939 int r;
940 char_u *fex;
Bram Moolenaar3ba685e2022-01-22 19:17:31 +0000941 sctx_T save_sctx = current_sctx;
Bram Moolenaar11abd092020-05-01 14:26:37 +0200942
943 // Set v:lnum to the first line number and v:count to the number of lines.
944 // Set v:char to the character to be inserted (can be NUL).
945 set_vim_var_nr(VV_LNUM, lnum);
946 set_vim_var_nr(VV_COUNT, count);
947 set_vim_var_char(c);
948
949 // Make a copy, the option could be changed while calling it.
950 fex = vim_strsave(curbuf->b_p_fex);
951 if (fex == NULL)
952 return 0;
Bram Moolenaar3ba685e2022-01-22 19:17:31 +0000953 current_sctx = curbuf->b_p_script_ctx[BV_FEX];
Bram Moolenaar11abd092020-05-01 14:26:37 +0200954
955 // Evaluate the function.
956 if (use_sandbox)
957 ++sandbox;
Bram Moolenaara4e0b972022-10-01 19:43:52 +0100958 r = (int)eval_to_number(fex, TRUE);
Bram Moolenaar11abd092020-05-01 14:26:37 +0200959 if (use_sandbox)
960 --sandbox;
961
962 set_vim_var_string(VV_CHAR, NULL, -1);
963 vim_free(fex);
Bram Moolenaar3ba685e2022-01-22 19:17:31 +0000964 current_sctx = save_sctx;
Bram Moolenaar11abd092020-05-01 14:26:37 +0200965
966 return r;
967}
968#endif
969
970/*
971 * Format "line_count" lines, starting at the cursor position.
972 * When "line_count" is negative, format until the end of the paragraph.
973 * Lines after the cursor line are saved for undo, caller must have saved the
974 * first line.
975 */
976 void
977format_lines(
978 linenr_T line_count,
979 int avoid_fex) // don't use 'formatexpr'
980{
981 int max_len;
982 int is_not_par; // current line not part of parag.
983 int next_is_not_par; // next line not part of paragraph
984 int is_end_par; // at end of paragraph
985 int prev_is_end_par = FALSE;// prev. line not part of parag.
986 int next_is_start_par = FALSE;
987 int leader_len = 0; // leader len of current line
988 int next_leader_len; // leader len of next line
989 char_u *leader_flags = NULL; // flags for leader of current line
Bram Moolenaar264d3dd2021-12-29 14:09:32 +0000990 char_u *next_leader_flags = NULL; // flags for leader of next line
Bram Moolenaar11abd092020-05-01 14:26:37 +0200991 int do_comments; // format comments
992 int do_comments_list = 0; // format comments with 'n' or '2'
993 int advance = TRUE;
994 int second_indent = -1; // indent for second line (comment
995 // aware)
996 int do_second_indent;
997 int do_number_indent;
998 int do_trail_white;
999 int first_par_line = TRUE;
1000 int smd_save;
1001 long count;
1002 int need_set_indent = TRUE; // set indent of next paragraph
Bram Moolenaarecabb512021-12-06 19:51:01 +00001003 linenr_T first_line = curwin->w_cursor.lnum;
Bram Moolenaar11abd092020-05-01 14:26:37 +02001004 int force_format = FALSE;
1005 int old_State = State;
1006
1007 // length of a line to force formatting: 3 * 'tw'
1008 max_len = comp_textwidth(TRUE) * 3;
1009
Christian Brabandt305127f2023-11-11 18:59:33 +01001010 // check for 'q', '2', 'n' and 'w' in 'formatoptions'
Bram Moolenaar11abd092020-05-01 14:26:37 +02001011 do_comments = has_format_option(FO_Q_COMS);
1012 do_second_indent = has_format_option(FO_Q_SECOND);
1013 do_number_indent = has_format_option(FO_Q_NUMBER);
1014 do_trail_white = has_format_option(FO_WHITE_PAR);
1015
1016 // Get info about the previous and current line.
1017 if (curwin->w_cursor.lnum > 1)
1018 is_not_par = fmt_check_par(curwin->w_cursor.lnum - 1
1019 , &leader_len, &leader_flags, do_comments);
1020 else
1021 is_not_par = TRUE;
1022 next_is_not_par = fmt_check_par(curwin->w_cursor.lnum
1023 , &next_leader_len, &next_leader_flags, do_comments);
1024 is_end_par = (is_not_par || next_is_not_par);
1025 if (!is_end_par && do_trail_white)
1026 is_end_par = !ends_in_white(curwin->w_cursor.lnum - 1);
1027
1028 curwin->w_cursor.lnum--;
1029 for (count = line_count; count != 0 && !got_int; --count)
1030 {
1031 // Advance to next paragraph.
1032 if (advance)
1033 {
1034 curwin->w_cursor.lnum++;
1035 prev_is_end_par = is_end_par;
1036 is_not_par = next_is_not_par;
1037 leader_len = next_leader_len;
1038 leader_flags = next_leader_flags;
1039 }
1040
1041 // The last line to be formatted.
1042 if (count == 1 || curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count)
1043 {
1044 next_is_not_par = TRUE;
1045 next_leader_len = 0;
1046 next_leader_flags = NULL;
1047 }
1048 else
1049 {
1050 next_is_not_par = fmt_check_par(curwin->w_cursor.lnum + 1
1051 , &next_leader_len, &next_leader_flags, do_comments);
1052 if (do_number_indent)
1053 next_is_start_par =
1054 (get_number_indent(curwin->w_cursor.lnum + 1) > 0);
1055 }
1056 advance = TRUE;
1057 is_end_par = (is_not_par || next_is_not_par || next_is_start_par);
1058 if (!is_end_par && do_trail_white)
1059 is_end_par = !ends_in_white(curwin->w_cursor.lnum);
1060
1061 // Skip lines that are not in a paragraph.
1062 if (is_not_par)
1063 {
1064 if (line_count < 0)
1065 break;
1066 }
1067 else
1068 {
1069 // For the first line of a paragraph, check indent of second line.
1070 // Don't do this for comments and empty lines.
1071 if (first_par_line
1072 && (do_second_indent || do_number_indent)
1073 && prev_is_end_par
1074 && curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count)
1075 {
1076 if (do_second_indent && !LINEEMPTY(curwin->w_cursor.lnum + 1))
1077 {
1078 if (leader_len == 0 && next_leader_len == 0)
1079 {
1080 // no comment found
1081 second_indent =
1082 get_indent_lnum(curwin->w_cursor.lnum + 1);
1083 }
1084 else
1085 {
1086 second_indent = next_leader_len;
1087 do_comments_list = 1;
1088 }
1089 }
1090 else if (do_number_indent)
1091 {
1092 if (leader_len == 0 && next_leader_len == 0)
1093 {
1094 // no comment found
1095 second_indent =
1096 get_number_indent(curwin->w_cursor.lnum);
1097 }
1098 else
1099 {
1100 // get_number_indent() is now "comment aware"...
1101 second_indent =
1102 get_number_indent(curwin->w_cursor.lnum);
1103 do_comments_list = 1;
1104 }
1105 }
1106 }
1107
1108 // When the comment leader changes, it's the end of the paragraph.
1109 if (curwin->w_cursor.lnum >= curbuf->b_ml.ml_line_count
1110 || !same_leader(curwin->w_cursor.lnum,
1111 leader_len, leader_flags,
1112 next_leader_len, next_leader_flags))
Bram Moolenaar264d3dd2021-12-29 14:09:32 +00001113 {
1114 // Special case: If the next line starts with a line comment
1115 // and this line has a line comment after some text, the
1116 // paragraph doesn't really end.
1117 if (next_leader_flags == NULL
1118 || STRNCMP(next_leader_flags, "://", 3) != 0
1119 || check_linecomment(ml_get_curline()) == MAXCOL)
Bram Moolenaar11abd092020-05-01 14:26:37 +02001120 is_end_par = TRUE;
Bram Moolenaar264d3dd2021-12-29 14:09:32 +00001121 }
Bram Moolenaar11abd092020-05-01 14:26:37 +02001122
1123 // If we have got to the end of a paragraph, or the line is
1124 // getting long, format it.
1125 if (is_end_par || force_format)
1126 {
1127 if (need_set_indent)
Christian Brabandt818ff252021-11-18 13:56:37 +00001128 {
1129 int indent = 0; // amount of indent needed
1130
Bram Moolenaarecabb512021-12-06 19:51:01 +00001131 // Replace indent in first line of a paragraph with minimal
1132 // number of tabs and spaces, according to current options.
1133 // For the very first formatted line keep the current
1134 // indent.
1135 if (curwin->w_cursor.lnum == first_line)
1136 indent = get_indent();
Bram Moolenaar8e145b82022-05-21 20:17:31 +01001137 else if (curbuf->b_p_lisp)
Christian Brabandt818ff252021-11-18 13:56:37 +00001138 indent = get_lisp_indent();
1139 else
Christian Brabandt818ff252021-11-18 13:56:37 +00001140 {
Christian Brabandt818ff252021-11-18 13:56:37 +00001141 if (cindent_on())
1142 {
1143 indent =
1144# ifdef FEAT_EVAL
1145 *curbuf->b_p_inde != NUL ? get_expr_indent() :
1146# endif
1147 get_c_indent();
1148 }
1149 else
Christian Brabandt818ff252021-11-18 13:56:37 +00001150 indent = get_indent();
1151 }
1152 (void)set_indent(indent, SIN_CHANGED);
1153 }
Bram Moolenaar11abd092020-05-01 14:26:37 +02001154
1155 // put cursor on last non-space
Bram Moolenaar24959102022-05-07 20:01:16 +01001156 State = MODE_NORMAL; // don't go past end-of-line
Bram Moolenaar11abd092020-05-01 14:26:37 +02001157 coladvance((colnr_T)MAXCOL);
1158 while (curwin->w_cursor.col && vim_isspace(gchar_cursor()))
1159 dec_cursor();
1160
1161 // do the formatting, without 'showmode'
Bram Moolenaar24959102022-05-07 20:01:16 +01001162 State = MODE_INSERT; // for open_line()
Bram Moolenaar11abd092020-05-01 14:26:37 +02001163 smd_save = p_smd;
1164 p_smd = FALSE;
1165 insertchar(NUL, INSCHAR_FORMAT
1166 + (do_comments ? INSCHAR_DO_COM : 0)
1167 + (do_comments && do_comments_list
1168 ? INSCHAR_COM_LIST : 0)
1169 + (avoid_fex ? INSCHAR_NO_FEX : 0), second_indent);
1170 State = old_State;
1171 p_smd = smd_save;
1172 second_indent = -1;
1173 // at end of par.: need to set indent of next par.
1174 need_set_indent = is_end_par;
1175 if (is_end_par)
1176 {
1177 // When called with a negative line count, break at the
1178 // end of the paragraph.
1179 if (line_count < 0)
1180 break;
1181 first_par_line = TRUE;
1182 }
1183 force_format = FALSE;
1184 }
1185
1186 // When still in same paragraph, join the lines together. But
1187 // first delete the leader from the second line.
1188 if (!is_end_par)
1189 {
1190 advance = FALSE;
1191 curwin->w_cursor.lnum++;
1192 curwin->w_cursor.col = 0;
1193 if (line_count < 0 && u_save_cursor() == FAIL)
1194 break;
1195 if (next_leader_len > 0)
1196 {
1197 (void)del_bytes((long)next_leader_len, FALSE, FALSE);
1198 mark_col_adjust(curwin->w_cursor.lnum, (colnr_T)0, 0L,
1199 (long)-next_leader_len, 0);
1200 }
1201 else if (second_indent > 0) // the "leader" for FO_Q_SECOND
1202 {
1203 int indent = getwhitecols_curline();
1204
1205 if (indent > 0)
1206 {
1207 (void)del_bytes(indent, FALSE, FALSE);
1208 mark_col_adjust(curwin->w_cursor.lnum,
Bram Moolenaar113d9de2022-08-08 15:49:18 +01001209 (colnr_T)0, 0L, (long)-indent, 0);
Bram Moolenaar11abd092020-05-01 14:26:37 +02001210 }
1211 }
1212 curwin->w_cursor.lnum--;
1213 if (do_join(2, TRUE, FALSE, FALSE, FALSE) == FAIL)
1214 {
1215 beep_flush();
1216 break;
1217 }
1218 first_par_line = FALSE;
1219 // If the line is getting long, format it next time
1220 if (STRLEN(ml_get_curline()) > (size_t)max_len)
1221 force_format = TRUE;
1222 else
1223 force_format = FALSE;
1224 }
1225 }
1226 line_breakcheck();
1227 }
1228}