Mercurial > vim
comparison src/textformat.c @ 20237:918245588b50 v8.2.0674
patch 8.2.0674: some source files are too big
Commit: https://github.com/vim/vim/commit/11abd095210fc84e5dcee87b9baed86061caefe4
Author: Bram Moolenaar <Bram@vim.org>
Date: Fri May 1 14:26:37 2020 +0200
patch 8.2.0674: some source files are too big
Problem: Some source files are too big.
Solution: Move text formatting functions to a new file. (Yegappan
Lakshmanan, closes #6021)
author | Bram Moolenaar <Bram@vim.org> |
---|---|
date | Fri, 01 May 2020 14:30:04 +0200 |
parents | |
children | cea8ae407452 |
comparison
equal
deleted
inserted
replaced
20236:366b1c4f8c49 | 20237:918245588b50 |
---|---|
1 /* 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 | |
16 static 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 | |
26 has_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 | |
40 internal_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; | |
48 int save_char = NUL; | |
49 int haveto_redraw = FALSE; | |
50 int fo_ins_blank = has_format_option(FO_INS_BLANK); | |
51 int fo_multibyte = has_format_option(FO_MBYTE_BREAK); | |
52 int fo_white_par = has_format_option(FO_WHITE_PAR); | |
53 int first_line = TRUE; | |
54 colnr_T leader_len; | |
55 int no_leader = FALSE; | |
56 int do_comments = (flags & INSCHAR_DO_COM); | |
57 #ifdef FEAT_LINEBREAK | |
58 int has_lbr = curwin->w_p_lbr; | |
59 | |
60 // make sure win_lbr_chartabsize() counts correctly | |
61 curwin->w_p_lbr = FALSE; | |
62 #endif | |
63 | |
64 // When 'ai' is off we don't want a space under the cursor to be | |
65 // deleted. Replace it with an 'x' temporarily. | |
66 if (!curbuf->b_p_ai && !(State & VREPLACE_FLAG)) | |
67 { | |
68 cc = gchar_cursor(); | |
69 if (VIM_ISWHITE(cc)) | |
70 { | |
71 save_char = cc; | |
72 pchar_cursor('x'); | |
73 } | |
74 } | |
75 | |
76 // Repeat breaking lines, until the current line is not too long. | |
77 while (!got_int) | |
78 { | |
79 int startcol; // Cursor column at entry | |
80 int wantcol; // column at textwidth border | |
81 int foundcol; // column for start of spaces | |
82 int end_foundcol = 0; // column for start of word | |
83 colnr_T len; | |
84 colnr_T virtcol; | |
85 int orig_col = 0; | |
86 char_u *saved_text = NULL; | |
87 colnr_T col; | |
88 colnr_T end_col; | |
89 int wcc; // counter for whitespace chars | |
90 | |
91 virtcol = get_nolist_virtcol() | |
92 + char2cells(c != NUL ? c : gchar_cursor()); | |
93 if (virtcol <= (colnr_T)textwidth) | |
94 break; | |
95 | |
96 if (no_leader) | |
97 do_comments = FALSE; | |
98 else if (!(flags & INSCHAR_FORMAT) | |
99 && has_format_option(FO_WRAP_COMS)) | |
100 do_comments = TRUE; | |
101 | |
102 // Don't break until after the comment leader | |
103 if (do_comments) | |
104 leader_len = get_leader_len(ml_get_curline(), NULL, FALSE, TRUE); | |
105 else | |
106 leader_len = 0; | |
107 | |
108 // If the line doesn't start with a comment leader, then don't | |
109 // start one in a following broken line. Avoids that a %word | |
110 // moved to the start of the next line causes all following lines | |
111 // to start with %. | |
112 if (leader_len == 0) | |
113 no_leader = TRUE; | |
114 if (!(flags & INSCHAR_FORMAT) | |
115 && leader_len == 0 | |
116 && !has_format_option(FO_WRAP)) | |
117 | |
118 break; | |
119 if ((startcol = curwin->w_cursor.col) == 0) | |
120 break; | |
121 | |
122 // find column of textwidth border | |
123 coladvance((colnr_T)textwidth); | |
124 wantcol = curwin->w_cursor.col; | |
125 | |
126 curwin->w_cursor.col = startcol; | |
127 foundcol = 0; | |
128 | |
129 // Find position to break at. | |
130 // Stop at first entered white when 'formatoptions' has 'v' | |
131 while ((!fo_ins_blank && !has_format_option(FO_INS_VI)) | |
132 || (flags & INSCHAR_FORMAT) | |
133 || curwin->w_cursor.lnum != Insstart.lnum | |
134 || curwin->w_cursor.col >= Insstart.col) | |
135 { | |
136 if (curwin->w_cursor.col == startcol && c != NUL) | |
137 cc = c; | |
138 else | |
139 cc = gchar_cursor(); | |
140 if (WHITECHAR(cc)) | |
141 { | |
142 // remember position of blank just before text | |
143 end_col = curwin->w_cursor.col; | |
144 | |
145 // find start of sequence of blanks | |
146 wcc = 0; | |
147 while (curwin->w_cursor.col > 0 && WHITECHAR(cc)) | |
148 { | |
149 dec_cursor(); | |
150 cc = gchar_cursor(); | |
151 | |
152 // Increment count of how many whitespace chars in this | |
153 // group; we only need to know if it's more than one. | |
154 if (wcc < 2) | |
155 wcc++; | |
156 } | |
157 if (curwin->w_cursor.col == 0 && WHITECHAR(cc)) | |
158 break; // only spaces in front of text | |
159 | |
160 // Don't break after a period when 'formatoptions' has 'p' and | |
161 // there are less than two spaces. | |
162 if (has_format_option(FO_PERIOD_ABBR) && cc == '.' && wcc < 2) | |
163 continue; | |
164 | |
165 // Don't break until after the comment leader | |
166 if (curwin->w_cursor.col < leader_len) | |
167 break; | |
168 if (has_format_option(FO_ONE_LETTER)) | |
169 { | |
170 // do not break after one-letter words | |
171 if (curwin->w_cursor.col == 0) | |
172 break; // one-letter word at begin | |
173 // do not break "#a b" when 'tw' is 2 | |
174 if (curwin->w_cursor.col <= leader_len) | |
175 break; | |
176 col = curwin->w_cursor.col; | |
177 dec_cursor(); | |
178 cc = gchar_cursor(); | |
179 | |
180 if (WHITECHAR(cc)) | |
181 continue; // one-letter, continue | |
182 curwin->w_cursor.col = col; | |
183 } | |
184 | |
185 inc_cursor(); | |
186 | |
187 end_foundcol = end_col + 1; | |
188 foundcol = curwin->w_cursor.col; | |
189 if (curwin->w_cursor.col <= (colnr_T)wantcol) | |
190 break; | |
191 } | |
192 else if (cc >= 0x100 && fo_multibyte) | |
193 { | |
194 // Break after or before a multi-byte character. | |
195 if (curwin->w_cursor.col != startcol) | |
196 { | |
197 // Don't break until after the comment leader | |
198 if (curwin->w_cursor.col < leader_len) | |
199 break; | |
200 col = curwin->w_cursor.col; | |
201 inc_cursor(); | |
202 // Don't change end_foundcol if already set. | |
203 if (foundcol != curwin->w_cursor.col) | |
204 { | |
205 foundcol = curwin->w_cursor.col; | |
206 end_foundcol = foundcol; | |
207 if (curwin->w_cursor.col <= (colnr_T)wantcol) | |
208 break; | |
209 } | |
210 curwin->w_cursor.col = col; | |
211 } | |
212 | |
213 if (curwin->w_cursor.col == 0) | |
214 break; | |
215 | |
216 col = curwin->w_cursor.col; | |
217 | |
218 dec_cursor(); | |
219 cc = gchar_cursor(); | |
220 | |
221 if (WHITECHAR(cc)) | |
222 continue; // break with space | |
223 // Don't break until after the comment leader | |
224 if (curwin->w_cursor.col < leader_len) | |
225 break; | |
226 | |
227 curwin->w_cursor.col = col; | |
228 | |
229 foundcol = curwin->w_cursor.col; | |
230 end_foundcol = foundcol; | |
231 if (curwin->w_cursor.col <= (colnr_T)wantcol) | |
232 break; | |
233 } | |
234 if (curwin->w_cursor.col == 0) | |
235 break; | |
236 dec_cursor(); | |
237 } | |
238 | |
239 if (foundcol == 0) // no spaces, cannot break line | |
240 { | |
241 curwin->w_cursor.col = startcol; | |
242 break; | |
243 } | |
244 | |
245 // Going to break the line, remove any "$" now. | |
246 undisplay_dollar(); | |
247 | |
248 // Offset between cursor position and line break is used by replace | |
249 // stack functions. VREPLACE does not use this, and backspaces | |
250 // over the text instead. | |
251 if (State & VREPLACE_FLAG) | |
252 orig_col = startcol; // Will start backspacing from here | |
253 else | |
254 replace_offset = startcol - end_foundcol; | |
255 | |
256 // adjust startcol for spaces that will be deleted and | |
257 // characters that will remain on top line | |
258 curwin->w_cursor.col = foundcol; | |
259 while ((cc = gchar_cursor(), WHITECHAR(cc)) | |
260 && (!fo_white_par || curwin->w_cursor.col < startcol)) | |
261 inc_cursor(); | |
262 startcol -= curwin->w_cursor.col; | |
263 if (startcol < 0) | |
264 startcol = 0; | |
265 | |
266 if (State & VREPLACE_FLAG) | |
267 { | |
268 // In VREPLACE mode, we will backspace over the text to be | |
269 // wrapped, so save a copy now to put on the next line. | |
270 saved_text = vim_strsave(ml_get_cursor()); | |
271 curwin->w_cursor.col = orig_col; | |
272 if (saved_text == NULL) | |
273 break; // Can't do it, out of memory | |
274 saved_text[startcol] = NUL; | |
275 | |
276 // Backspace over characters that will move to the next line | |
277 if (!fo_white_par) | |
278 backspace_until_column(foundcol); | |
279 } | |
280 else | |
281 { | |
282 // put cursor after pos. to break line | |
283 if (!fo_white_par) | |
284 curwin->w_cursor.col = foundcol; | |
285 } | |
286 | |
287 // Split the line just before the margin. | |
288 // Only insert/delete lines, but don't really redraw the window. | |
289 open_line(FORWARD, OPENLINE_DELSPACES + OPENLINE_MARKFIX | |
290 + (fo_white_par ? OPENLINE_KEEPTRAIL : 0) | |
291 + (do_comments ? OPENLINE_DO_COM : 0) | |
292 + ((flags & INSCHAR_COM_LIST) ? OPENLINE_COM_LIST : 0) | |
293 , ((flags & INSCHAR_COM_LIST) ? second_indent : old_indent)); | |
294 if (!(flags & INSCHAR_COM_LIST)) | |
295 old_indent = 0; | |
296 | |
297 replace_offset = 0; | |
298 if (first_line) | |
299 { | |
300 if (!(flags & INSCHAR_COM_LIST)) | |
301 { | |
302 // This section is for auto-wrap of numeric lists. When not | |
303 // in insert mode (i.e. format_lines()), the INSCHAR_COM_LIST | |
304 // flag will be set and open_line() will handle it (as seen | |
305 // above). The code here (and in get_number_indent()) will | |
306 // recognize comments if needed... | |
307 if (second_indent < 0 && has_format_option(FO_Q_NUMBER)) | |
308 second_indent = | |
309 get_number_indent(curwin->w_cursor.lnum - 1); | |
310 if (second_indent >= 0) | |
311 { | |
312 if (State & VREPLACE_FLAG) | |
313 change_indent(INDENT_SET, second_indent, | |
314 FALSE, NUL, TRUE); | |
315 else | |
316 if (leader_len > 0 && second_indent - leader_len > 0) | |
317 { | |
318 int i; | |
319 int padding = second_indent - leader_len; | |
320 | |
321 // We started at the first_line of a numbered list | |
322 // that has a comment. the open_line() function has | |
323 // inserted the proper comment leader and positioned | |
324 // the cursor at the end of the split line. Now we | |
325 // add the additional whitespace needed after the | |
326 // comment leader for the numbered list. | |
327 for (i = 0; i < padding; i++) | |
328 ins_str((char_u *)" "); | |
329 } | |
330 else | |
331 { | |
332 (void)set_indent(second_indent, SIN_CHANGED); | |
333 } | |
334 } | |
335 } | |
336 first_line = FALSE; | |
337 } | |
338 | |
339 if (State & VREPLACE_FLAG) | |
340 { | |
341 // In VREPLACE mode we have backspaced over the text to be | |
342 // moved, now we re-insert it into the new line. | |
343 ins_bytes(saved_text); | |
344 vim_free(saved_text); | |
345 } | |
346 else | |
347 { | |
348 // Check if cursor is not past the NUL off the line, cindent | |
349 // may have added or removed indent. | |
350 curwin->w_cursor.col += startcol; | |
351 len = (colnr_T)STRLEN(ml_get_curline()); | |
352 if (curwin->w_cursor.col > len) | |
353 curwin->w_cursor.col = len; | |
354 } | |
355 | |
356 haveto_redraw = TRUE; | |
357 #ifdef FEAT_CINDENT | |
358 set_can_cindent(TRUE); | |
359 #endif | |
360 // moved the cursor, don't autoindent or cindent now | |
361 did_ai = FALSE; | |
362 #ifdef FEAT_SMARTINDENT | |
363 did_si = FALSE; | |
364 can_si = FALSE; | |
365 can_si_back = FALSE; | |
366 #endif | |
367 line_breakcheck(); | |
368 } | |
369 | |
370 if (save_char != NUL) // put back space after cursor | |
371 pchar_cursor(save_char); | |
372 | |
373 #ifdef FEAT_LINEBREAK | |
374 curwin->w_p_lbr = has_lbr; | |
375 #endif | |
376 if (!format_only && haveto_redraw) | |
377 { | |
378 update_topline(); | |
379 redraw_curbuf_later(VALID); | |
380 } | |
381 } | |
382 | |
383 /* | |
384 * Blank lines, and lines containing only the comment leader, are left | |
385 * untouched by the formatting. The function returns TRUE in this | |
386 * case. It also returns TRUE when a line starts with the end of a comment | |
387 * ('e' in comment flags), so that this line is skipped, and not joined to the | |
388 * previous line. A new paragraph starts after a blank line, or when the | |
389 * comment leader changes -- webb. | |
390 */ | |
391 static int | |
392 fmt_check_par( | |
393 linenr_T lnum, | |
394 int *leader_len, | |
395 char_u **leader_flags, | |
396 int do_comments) | |
397 { | |
398 char_u *flags = NULL; // init for GCC | |
399 char_u *ptr; | |
400 | |
401 ptr = ml_get(lnum); | |
402 if (do_comments) | |
403 *leader_len = get_leader_len(ptr, leader_flags, FALSE, TRUE); | |
404 else | |
405 *leader_len = 0; | |
406 | |
407 if (*leader_len > 0) | |
408 { | |
409 // Search for 'e' flag in comment leader flags. | |
410 flags = *leader_flags; | |
411 while (*flags && *flags != ':' && *flags != COM_END) | |
412 ++flags; | |
413 } | |
414 | |
415 return (*skipwhite(ptr + *leader_len) == NUL | |
416 || (*leader_len > 0 && *flags == COM_END) | |
417 || startPS(lnum, NUL, FALSE)); | |
418 } | |
419 | |
420 /* | |
421 * Return TRUE if line "lnum" ends in a white character. | |
422 */ | |
423 static int | |
424 ends_in_white(linenr_T lnum) | |
425 { | |
426 char_u *s = ml_get(lnum); | |
427 size_t l; | |
428 | |
429 if (*s == NUL) | |
430 return FALSE; | |
431 // Don't use STRLEN() inside VIM_ISWHITE(), SAS/C complains: "macro | |
432 // invocation may call function multiple times". | |
433 l = STRLEN(s) - 1; | |
434 return VIM_ISWHITE(s[l]); | |
435 } | |
436 | |
437 /* | |
438 * Return TRUE if the two comment leaders given are the same. "lnum" is | |
439 * the first line. White-space is ignored. Note that the whole of | |
440 * 'leader1' must match 'leader2_len' characters from 'leader2' -- webb | |
441 */ | |
442 static int | |
443 same_leader( | |
444 linenr_T lnum, | |
445 int leader1_len, | |
446 char_u *leader1_flags, | |
447 int leader2_len, | |
448 char_u *leader2_flags) | |
449 { | |
450 int idx1 = 0, idx2 = 0; | |
451 char_u *p; | |
452 char_u *line1; | |
453 char_u *line2; | |
454 | |
455 if (leader1_len == 0) | |
456 return (leader2_len == 0); | |
457 | |
458 // If first leader has 'f' flag, the lines can be joined only if the | |
459 // second line does not have a leader. | |
460 // If first leader has 'e' flag, the lines can never be joined. | |
461 // If fist leader has 's' flag, the lines can only be joined if there is | |
462 // some text after it and the second line has the 'm' flag. | |
463 if (leader1_flags != NULL) | |
464 { | |
465 for (p = leader1_flags; *p && *p != ':'; ++p) | |
466 { | |
467 if (*p == COM_FIRST) | |
468 return (leader2_len == 0); | |
469 if (*p == COM_END) | |
470 return FALSE; | |
471 if (*p == COM_START) | |
472 { | |
473 if (*(ml_get(lnum) + leader1_len) == NUL) | |
474 return FALSE; | |
475 if (leader2_flags == NULL || leader2_len == 0) | |
476 return FALSE; | |
477 for (p = leader2_flags; *p && *p != ':'; ++p) | |
478 if (*p == COM_MIDDLE) | |
479 return TRUE; | |
480 return FALSE; | |
481 } | |
482 } | |
483 } | |
484 | |
485 // Get current line and next line, compare the leaders. | |
486 // The first line has to be saved, only one line can be locked at a time. | |
487 line1 = vim_strsave(ml_get(lnum)); | |
488 if (line1 != NULL) | |
489 { | |
490 for (idx1 = 0; VIM_ISWHITE(line1[idx1]); ++idx1) | |
491 ; | |
492 line2 = ml_get(lnum + 1); | |
493 for (idx2 = 0; idx2 < leader2_len; ++idx2) | |
494 { | |
495 if (!VIM_ISWHITE(line2[idx2])) | |
496 { | |
497 if (line1[idx1++] != line2[idx2]) | |
498 break; | |
499 } | |
500 else | |
501 while (VIM_ISWHITE(line1[idx1])) | |
502 ++idx1; | |
503 } | |
504 vim_free(line1); | |
505 } | |
506 return (idx2 == leader2_len && idx1 == leader1_len); | |
507 } | |
508 | |
509 /* | |
510 * Return TRUE when a paragraph starts in line "lnum". Return FALSE when the | |
511 * previous line is in the same paragraph. Used for auto-formatting. | |
512 */ | |
513 static int | |
514 paragraph_start(linenr_T lnum) | |
515 { | |
516 char_u *p; | |
517 int leader_len = 0; // leader len of current line | |
518 char_u *leader_flags = NULL; // flags for leader of current line | |
519 int next_leader_len; // leader len of next line | |
520 char_u *next_leader_flags; // flags for leader of next line | |
521 int do_comments; // format comments | |
522 | |
523 if (lnum <= 1) | |
524 return TRUE; // start of the file | |
525 | |
526 p = ml_get(lnum - 1); | |
527 if (*p == NUL) | |
528 return TRUE; // after empty line | |
529 | |
530 do_comments = has_format_option(FO_Q_COMS); | |
531 if (fmt_check_par(lnum - 1, &leader_len, &leader_flags, do_comments)) | |
532 return TRUE; // after non-paragraph line | |
533 | |
534 if (fmt_check_par(lnum, &next_leader_len, &next_leader_flags, do_comments)) | |
535 return TRUE; // "lnum" is not a paragraph line | |
536 | |
537 if (has_format_option(FO_WHITE_PAR) && !ends_in_white(lnum - 1)) | |
538 return TRUE; // missing trailing space in previous line. | |
539 | |
540 if (has_format_option(FO_Q_NUMBER) && (get_number_indent(lnum) > 0)) | |
541 return TRUE; // numbered item starts in "lnum". | |
542 | |
543 if (!same_leader(lnum - 1, leader_len, leader_flags, | |
544 next_leader_len, next_leader_flags)) | |
545 return TRUE; // change of comment leader. | |
546 | |
547 return FALSE; | |
548 } | |
549 | |
550 /* | |
551 * Called after inserting or deleting text: When 'formatoptions' includes the | |
552 * 'a' flag format from the current line until the end of the paragraph. | |
553 * Keep the cursor at the same position relative to the text. | |
554 * The caller must have saved the cursor line for undo, following ones will be | |
555 * saved here. | |
556 */ | |
557 void | |
558 auto_format( | |
559 int trailblank, // when TRUE also format with trailing blank | |
560 int prev_line) // may start in previous line | |
561 { | |
562 pos_T pos; | |
563 colnr_T len; | |
564 char_u *old; | |
565 char_u *new, *pnew; | |
566 int wasatend; | |
567 int cc; | |
568 | |
569 if (!has_format_option(FO_AUTO)) | |
570 return; | |
571 | |
572 pos = curwin->w_cursor; | |
573 old = ml_get_curline(); | |
574 | |
575 // may remove added space | |
576 check_auto_format(FALSE); | |
577 | |
578 // Don't format in Insert mode when the cursor is on a trailing blank, the | |
579 // user might insert normal text next. Also skip formatting when "1" is | |
580 // in 'formatoptions' and there is a single character before the cursor. | |
581 // Otherwise the line would be broken and when typing another non-white | |
582 // next they are not joined back together. | |
583 wasatend = (pos.col == (colnr_T)STRLEN(old)); | |
584 if (*old != NUL && !trailblank && wasatend) | |
585 { | |
586 dec_cursor(); | |
587 cc = gchar_cursor(); | |
588 if (!WHITECHAR(cc) && curwin->w_cursor.col > 0 | |
589 && has_format_option(FO_ONE_LETTER)) | |
590 dec_cursor(); | |
591 cc = gchar_cursor(); | |
592 if (WHITECHAR(cc)) | |
593 { | |
594 curwin->w_cursor = pos; | |
595 return; | |
596 } | |
597 curwin->w_cursor = pos; | |
598 } | |
599 | |
600 // With the 'c' flag in 'formatoptions' and 't' missing: only format | |
601 // comments. | |
602 if (has_format_option(FO_WRAP_COMS) && !has_format_option(FO_WRAP) | |
603 && get_leader_len(old, NULL, FALSE, TRUE) == 0) | |
604 return; | |
605 | |
606 // May start formatting in a previous line, so that after "x" a word is | |
607 // moved to the previous line if it fits there now. Only when this is not | |
608 // the start of a paragraph. | |
609 if (prev_line && !paragraph_start(curwin->w_cursor.lnum)) | |
610 { | |
611 --curwin->w_cursor.lnum; | |
612 if (u_save_cursor() == FAIL) | |
613 return; | |
614 } | |
615 | |
616 // Do the formatting and restore the cursor position. "saved_cursor" will | |
617 // be adjusted for the text formatting. | |
618 saved_cursor = pos; | |
619 format_lines((linenr_T)-1, FALSE); | |
620 curwin->w_cursor = saved_cursor; | |
621 saved_cursor.lnum = 0; | |
622 | |
623 if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count) | |
624 { | |
625 // "cannot happen" | |
626 curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count; | |
627 coladvance((colnr_T)MAXCOL); | |
628 } | |
629 else | |
630 check_cursor_col(); | |
631 | |
632 // Insert mode: If the cursor is now after the end of the line while it | |
633 // previously wasn't, the line was broken. Because of the rule above we | |
634 // need to add a space when 'w' is in 'formatoptions' to keep a paragraph | |
635 // formatted. | |
636 if (!wasatend && has_format_option(FO_WHITE_PAR)) | |
637 { | |
638 new = ml_get_curline(); | |
639 len = (colnr_T)STRLEN(new); | |
640 if (curwin->w_cursor.col == len) | |
641 { | |
642 pnew = vim_strnsave(new, len + 2); | |
643 pnew[len] = ' '; | |
644 pnew[len + 1] = NUL; | |
645 ml_replace(curwin->w_cursor.lnum, pnew, FALSE); | |
646 // remove the space later | |
647 did_add_space = TRUE; | |
648 } | |
649 else | |
650 // may remove added space | |
651 check_auto_format(FALSE); | |
652 } | |
653 | |
654 check_cursor(); | |
655 } | |
656 | |
657 /* | |
658 * When an extra space was added to continue a paragraph for auto-formatting, | |
659 * delete it now. The space must be under the cursor, just after the insert | |
660 * position. | |
661 */ | |
662 void | |
663 check_auto_format( | |
664 int end_insert) // TRUE when ending Insert mode | |
665 { | |
666 int c = ' '; | |
667 int cc; | |
668 | |
669 if (did_add_space) | |
670 { | |
671 cc = gchar_cursor(); | |
672 if (!WHITECHAR(cc)) | |
673 // Somehow the space was removed already. | |
674 did_add_space = FALSE; | |
675 else | |
676 { | |
677 if (!end_insert) | |
678 { | |
679 inc_cursor(); | |
680 c = gchar_cursor(); | |
681 dec_cursor(); | |
682 } | |
683 if (c != NUL) | |
684 { | |
685 // The space is no longer at the end of the line, delete it. | |
686 del_char(FALSE); | |
687 did_add_space = FALSE; | |
688 } | |
689 } | |
690 } | |
691 } | |
692 | |
693 /* | |
694 * Find out textwidth to be used for formatting: | |
695 * if 'textwidth' option is set, use it | |
696 * else if 'wrapmargin' option is set, use curwin->w_width - 'wrapmargin' | |
697 * if invalid value, use 0. | |
698 * Set default to window width (maximum 79) for "gq" operator. | |
699 */ | |
700 int | |
701 comp_textwidth( | |
702 int ff) // force formatting (for "gq" command) | |
703 { | |
704 int textwidth; | |
705 | |
706 textwidth = curbuf->b_p_tw; | |
707 if (textwidth == 0 && curbuf->b_p_wm) | |
708 { | |
709 // The width is the window width minus 'wrapmargin' minus all the | |
710 // things that add to the margin. | |
711 textwidth = curwin->w_width - curbuf->b_p_wm; | |
712 #ifdef FEAT_CMDWIN | |
713 if (cmdwin_type != 0) | |
714 textwidth -= 1; | |
715 #endif | |
716 #ifdef FEAT_FOLDING | |
717 textwidth -= curwin->w_p_fdc; | |
718 #endif | |
719 #ifdef FEAT_SIGNS | |
720 if (signcolumn_on(curwin)) | |
721 textwidth -= 1; | |
722 #endif | |
723 if (curwin->w_p_nu || curwin->w_p_rnu) | |
724 textwidth -= 8; | |
725 } | |
726 if (textwidth < 0) | |
727 textwidth = 0; | |
728 if (ff && textwidth == 0) | |
729 { | |
730 textwidth = curwin->w_width - 1; | |
731 if (textwidth > 79) | |
732 textwidth = 79; | |
733 } | |
734 return textwidth; | |
735 } | |
736 | |
737 /* | |
738 * Implementation of the format operator 'gq'. | |
739 */ | |
740 void | |
741 op_format( | |
742 oparg_T *oap, | |
743 int keep_cursor) // keep cursor on same text char | |
744 { | |
745 long old_line_count = curbuf->b_ml.ml_line_count; | |
746 | |
747 // Place the cursor where the "gq" or "gw" command was given, so that "u" | |
748 // can put it back there. | |
749 curwin->w_cursor = oap->cursor_start; | |
750 | |
751 if (u_save((linenr_T)(oap->start.lnum - 1), | |
752 (linenr_T)(oap->end.lnum + 1)) == FAIL) | |
753 return; | |
754 curwin->w_cursor = oap->start; | |
755 | |
756 if (oap->is_VIsual) | |
757 // When there is no change: need to remove the Visual selection | |
758 redraw_curbuf_later(INVERTED); | |
759 | |
760 if (!cmdmod.lockmarks) | |
761 // Set '[ mark at the start of the formatted area | |
762 curbuf->b_op_start = oap->start; | |
763 | |
764 // For "gw" remember the cursor position and put it back below (adjusted | |
765 // for joined and split lines). | |
766 if (keep_cursor) | |
767 saved_cursor = oap->cursor_start; | |
768 | |
769 format_lines(oap->line_count, keep_cursor); | |
770 | |
771 // Leave the cursor at the first non-blank of the last formatted line. | |
772 // If the cursor was moved one line back (e.g. with "Q}") go to the next | |
773 // line, so "." will do the next lines. | |
774 if (oap->end_adjusted && curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count) | |
775 ++curwin->w_cursor.lnum; | |
776 beginline(BL_WHITE | BL_FIX); | |
777 old_line_count = curbuf->b_ml.ml_line_count - old_line_count; | |
778 msgmore(old_line_count); | |
779 | |
780 if (!cmdmod.lockmarks) | |
781 // put '] mark on the end of the formatted area | |
782 curbuf->b_op_end = curwin->w_cursor; | |
783 | |
784 if (keep_cursor) | |
785 { | |
786 curwin->w_cursor = saved_cursor; | |
787 saved_cursor.lnum = 0; | |
788 } | |
789 | |
790 if (oap->is_VIsual) | |
791 { | |
792 win_T *wp; | |
793 | |
794 FOR_ALL_WINDOWS(wp) | |
795 { | |
796 if (wp->w_old_cursor_lnum != 0) | |
797 { | |
798 // When lines have been inserted or deleted, adjust the end of | |
799 // the Visual area to be redrawn. | |
800 if (wp->w_old_cursor_lnum > wp->w_old_visual_lnum) | |
801 wp->w_old_cursor_lnum += old_line_count; | |
802 else | |
803 wp->w_old_visual_lnum += old_line_count; | |
804 } | |
805 } | |
806 } | |
807 } | |
808 | |
809 #if defined(FEAT_EVAL) || defined(PROTO) | |
810 /* | |
811 * Implementation of the format operator 'gq' for when using 'formatexpr'. | |
812 */ | |
813 void | |
814 op_formatexpr(oparg_T *oap) | |
815 { | |
816 if (oap->is_VIsual) | |
817 // When there is no change: need to remove the Visual selection | |
818 redraw_curbuf_later(INVERTED); | |
819 | |
820 if (fex_format(oap->start.lnum, oap->line_count, NUL) != 0) | |
821 // As documented: when 'formatexpr' returns non-zero fall back to | |
822 // internal formatting. | |
823 op_format(oap, FALSE); | |
824 } | |
825 | |
826 int | |
827 fex_format( | |
828 linenr_T lnum, | |
829 long count, | |
830 int c) // character to be inserted | |
831 { | |
832 int use_sandbox = was_set_insecurely((char_u *)"formatexpr", | |
833 OPT_LOCAL); | |
834 int r; | |
835 char_u *fex; | |
836 | |
837 // Set v:lnum to the first line number and v:count to the number of lines. | |
838 // Set v:char to the character to be inserted (can be NUL). | |
839 set_vim_var_nr(VV_LNUM, lnum); | |
840 set_vim_var_nr(VV_COUNT, count); | |
841 set_vim_var_char(c); | |
842 | |
843 // Make a copy, the option could be changed while calling it. | |
844 fex = vim_strsave(curbuf->b_p_fex); | |
845 if (fex == NULL) | |
846 return 0; | |
847 | |
848 // Evaluate the function. | |
849 if (use_sandbox) | |
850 ++sandbox; | |
851 r = (int)eval_to_number(fex); | |
852 if (use_sandbox) | |
853 --sandbox; | |
854 | |
855 set_vim_var_string(VV_CHAR, NULL, -1); | |
856 vim_free(fex); | |
857 | |
858 return r; | |
859 } | |
860 #endif | |
861 | |
862 /* | |
863 * Format "line_count" lines, starting at the cursor position. | |
864 * When "line_count" is negative, format until the end of the paragraph. | |
865 * Lines after the cursor line are saved for undo, caller must have saved the | |
866 * first line. | |
867 */ | |
868 void | |
869 format_lines( | |
870 linenr_T line_count, | |
871 int avoid_fex) // don't use 'formatexpr' | |
872 { | |
873 int max_len; | |
874 int is_not_par; // current line not part of parag. | |
875 int next_is_not_par; // next line not part of paragraph | |
876 int is_end_par; // at end of paragraph | |
877 int prev_is_end_par = FALSE;// prev. line not part of parag. | |
878 int next_is_start_par = FALSE; | |
879 int leader_len = 0; // leader len of current line | |
880 int next_leader_len; // leader len of next line | |
881 char_u *leader_flags = NULL; // flags for leader of current line | |
882 char_u *next_leader_flags; // flags for leader of next line | |
883 int do_comments; // format comments | |
884 int do_comments_list = 0; // format comments with 'n' or '2' | |
885 int advance = TRUE; | |
886 int second_indent = -1; // indent for second line (comment | |
887 // aware) | |
888 int do_second_indent; | |
889 int do_number_indent; | |
890 int do_trail_white; | |
891 int first_par_line = TRUE; | |
892 int smd_save; | |
893 long count; | |
894 int need_set_indent = TRUE; // set indent of next paragraph | |
895 int force_format = FALSE; | |
896 int old_State = State; | |
897 | |
898 // length of a line to force formatting: 3 * 'tw' | |
899 max_len = comp_textwidth(TRUE) * 3; | |
900 | |
901 // check for 'q', '2' and '1' in 'formatoptions' | |
902 do_comments = has_format_option(FO_Q_COMS); | |
903 do_second_indent = has_format_option(FO_Q_SECOND); | |
904 do_number_indent = has_format_option(FO_Q_NUMBER); | |
905 do_trail_white = has_format_option(FO_WHITE_PAR); | |
906 | |
907 // Get info about the previous and current line. | |
908 if (curwin->w_cursor.lnum > 1) | |
909 is_not_par = fmt_check_par(curwin->w_cursor.lnum - 1 | |
910 , &leader_len, &leader_flags, do_comments); | |
911 else | |
912 is_not_par = TRUE; | |
913 next_is_not_par = fmt_check_par(curwin->w_cursor.lnum | |
914 , &next_leader_len, &next_leader_flags, do_comments); | |
915 is_end_par = (is_not_par || next_is_not_par); | |
916 if (!is_end_par && do_trail_white) | |
917 is_end_par = !ends_in_white(curwin->w_cursor.lnum - 1); | |
918 | |
919 curwin->w_cursor.lnum--; | |
920 for (count = line_count; count != 0 && !got_int; --count) | |
921 { | |
922 // Advance to next paragraph. | |
923 if (advance) | |
924 { | |
925 curwin->w_cursor.lnum++; | |
926 prev_is_end_par = is_end_par; | |
927 is_not_par = next_is_not_par; | |
928 leader_len = next_leader_len; | |
929 leader_flags = next_leader_flags; | |
930 } | |
931 | |
932 // The last line to be formatted. | |
933 if (count == 1 || curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count) | |
934 { | |
935 next_is_not_par = TRUE; | |
936 next_leader_len = 0; | |
937 next_leader_flags = NULL; | |
938 } | |
939 else | |
940 { | |
941 next_is_not_par = fmt_check_par(curwin->w_cursor.lnum + 1 | |
942 , &next_leader_len, &next_leader_flags, do_comments); | |
943 if (do_number_indent) | |
944 next_is_start_par = | |
945 (get_number_indent(curwin->w_cursor.lnum + 1) > 0); | |
946 } | |
947 advance = TRUE; | |
948 is_end_par = (is_not_par || next_is_not_par || next_is_start_par); | |
949 if (!is_end_par && do_trail_white) | |
950 is_end_par = !ends_in_white(curwin->w_cursor.lnum); | |
951 | |
952 // Skip lines that are not in a paragraph. | |
953 if (is_not_par) | |
954 { | |
955 if (line_count < 0) | |
956 break; | |
957 } | |
958 else | |
959 { | |
960 // For the first line of a paragraph, check indent of second line. | |
961 // Don't do this for comments and empty lines. | |
962 if (first_par_line | |
963 && (do_second_indent || do_number_indent) | |
964 && prev_is_end_par | |
965 && curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count) | |
966 { | |
967 if (do_second_indent && !LINEEMPTY(curwin->w_cursor.lnum + 1)) | |
968 { | |
969 if (leader_len == 0 && next_leader_len == 0) | |
970 { | |
971 // no comment found | |
972 second_indent = | |
973 get_indent_lnum(curwin->w_cursor.lnum + 1); | |
974 } | |
975 else | |
976 { | |
977 second_indent = next_leader_len; | |
978 do_comments_list = 1; | |
979 } | |
980 } | |
981 else if (do_number_indent) | |
982 { | |
983 if (leader_len == 0 && next_leader_len == 0) | |
984 { | |
985 // no comment found | |
986 second_indent = | |
987 get_number_indent(curwin->w_cursor.lnum); | |
988 } | |
989 else | |
990 { | |
991 // get_number_indent() is now "comment aware"... | |
992 second_indent = | |
993 get_number_indent(curwin->w_cursor.lnum); | |
994 do_comments_list = 1; | |
995 } | |
996 } | |
997 } | |
998 | |
999 // When the comment leader changes, it's the end of the paragraph. | |
1000 if (curwin->w_cursor.lnum >= curbuf->b_ml.ml_line_count | |
1001 || !same_leader(curwin->w_cursor.lnum, | |
1002 leader_len, leader_flags, | |
1003 next_leader_len, next_leader_flags)) | |
1004 is_end_par = TRUE; | |
1005 | |
1006 // If we have got to the end of a paragraph, or the line is | |
1007 // getting long, format it. | |
1008 if (is_end_par || force_format) | |
1009 { | |
1010 if (need_set_indent) | |
1011 // replace indent in first line with minimal number of | |
1012 // tabs and spaces, according to current options | |
1013 (void)set_indent(get_indent(), SIN_CHANGED); | |
1014 | |
1015 // put cursor on last non-space | |
1016 State = NORMAL; // don't go past end-of-line | |
1017 coladvance((colnr_T)MAXCOL); | |
1018 while (curwin->w_cursor.col && vim_isspace(gchar_cursor())) | |
1019 dec_cursor(); | |
1020 | |
1021 // do the formatting, without 'showmode' | |
1022 State = INSERT; // for open_line() | |
1023 smd_save = p_smd; | |
1024 p_smd = FALSE; | |
1025 insertchar(NUL, INSCHAR_FORMAT | |
1026 + (do_comments ? INSCHAR_DO_COM : 0) | |
1027 + (do_comments && do_comments_list | |
1028 ? INSCHAR_COM_LIST : 0) | |
1029 + (avoid_fex ? INSCHAR_NO_FEX : 0), second_indent); | |
1030 State = old_State; | |
1031 p_smd = smd_save; | |
1032 second_indent = -1; | |
1033 // at end of par.: need to set indent of next par. | |
1034 need_set_indent = is_end_par; | |
1035 if (is_end_par) | |
1036 { | |
1037 // When called with a negative line count, break at the | |
1038 // end of the paragraph. | |
1039 if (line_count < 0) | |
1040 break; | |
1041 first_par_line = TRUE; | |
1042 } | |
1043 force_format = FALSE; | |
1044 } | |
1045 | |
1046 // When still in same paragraph, join the lines together. But | |
1047 // first delete the leader from the second line. | |
1048 if (!is_end_par) | |
1049 { | |
1050 advance = FALSE; | |
1051 curwin->w_cursor.lnum++; | |
1052 curwin->w_cursor.col = 0; | |
1053 if (line_count < 0 && u_save_cursor() == FAIL) | |
1054 break; | |
1055 if (next_leader_len > 0) | |
1056 { | |
1057 (void)del_bytes((long)next_leader_len, FALSE, FALSE); | |
1058 mark_col_adjust(curwin->w_cursor.lnum, (colnr_T)0, 0L, | |
1059 (long)-next_leader_len, 0); | |
1060 } | |
1061 else if (second_indent > 0) // the "leader" for FO_Q_SECOND | |
1062 { | |
1063 int indent = getwhitecols_curline(); | |
1064 | |
1065 if (indent > 0) | |
1066 { | |
1067 (void)del_bytes(indent, FALSE, FALSE); | |
1068 mark_col_adjust(curwin->w_cursor.lnum, | |
1069 (colnr_T)0, 0L, (long)-indent, 0); | |
1070 } | |
1071 } | |
1072 curwin->w_cursor.lnum--; | |
1073 if (do_join(2, TRUE, FALSE, FALSE, FALSE) == FAIL) | |
1074 { | |
1075 beep_flush(); | |
1076 break; | |
1077 } | |
1078 first_par_line = FALSE; | |
1079 // If the line is getting long, format it next time | |
1080 if (STRLEN(ml_get_curline()) > (size_t)max_len) | |
1081 force_format = TRUE; | |
1082 else | |
1083 force_format = FALSE; | |
1084 } | |
1085 } | |
1086 line_breakcheck(); | |
1087 } | |
1088 } |