blob: cd03f25d25842e86cdce8fe1fa0ddfb1a72303f5 [file] [log] [blame]
Bram Moolenaar4b471622019-01-31 13:48:09 +01001/* 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 * indent.c: Indentation related functions
12 */
13
14#include "vim.h"
15
Bram Moolenaare677df82019-09-02 22:31:11 +020016#if defined(FEAT_VARTABS) || defined(PROTO)
17
18/*
19 * Set the integer values corresponding to the string setting of 'vartabstop'.
20 * "array" will be set, caller must free it if needed.
Bram Moolenaarb7081e12021-09-04 18:47:28 +020021 * Return FAIL for an error.
Bram Moolenaare677df82019-09-02 22:31:11 +020022 */
23 int
24tabstop_set(char_u *var, int **array)
25{
Bram Moolenaarb7081e12021-09-04 18:47:28 +020026 int valcount = 1;
27 int t;
28 char_u *cp;
Bram Moolenaare677df82019-09-02 22:31:11 +020029
30 if (var[0] == NUL || (var[0] == '0' && var[1] == NUL))
31 {
32 *array = NULL;
Bram Moolenaarb7081e12021-09-04 18:47:28 +020033 return OK;
Bram Moolenaare677df82019-09-02 22:31:11 +020034 }
35
36 for (cp = var; *cp != NUL; ++cp)
37 {
38 if (cp == var || cp[-1] == ',')
39 {
40 char_u *end;
41
42 if (strtol((char *)cp, (char **)&end, 10) <= 0)
43 {
44 if (cp != end)
45 emsg(_(e_positive));
46 else
Bram Moolenaarb7081e12021-09-04 18:47:28 +020047 semsg(_(e_invarg2), cp);
48 return FAIL;
Bram Moolenaare677df82019-09-02 22:31:11 +020049 }
50 }
51
52 if (VIM_ISDIGIT(*cp))
53 continue;
54 if (cp[0] == ',' && cp > var && cp[-1] != ',' && cp[1] != NUL)
55 {
56 ++valcount;
57 continue;
58 }
Bram Moolenaarb7081e12021-09-04 18:47:28 +020059 semsg(_(e_invarg2), var);
60 return FAIL;
Bram Moolenaare677df82019-09-02 22:31:11 +020061 }
62
63 *array = ALLOC_MULT(int, valcount + 1);
64 if (*array == NULL)
Bram Moolenaarb7081e12021-09-04 18:47:28 +020065 return FAIL;
Bram Moolenaare677df82019-09-02 22:31:11 +020066 (*array)[0] = valcount;
67
68 t = 1;
69 for (cp = var; *cp != NUL;)
70 {
Bram Moolenaarb7081e12021-09-04 18:47:28 +020071 int n = atoi((char *)cp);
72
73 if (n < 0 || n > 9999)
74 {
75 semsg(_(e_invarg2), cp);
76 return FAIL;
77 }
78 (*array)[t++] = n;
79 while (*cp != NUL && *cp != ',')
Bram Moolenaare677df82019-09-02 22:31:11 +020080 ++cp;
81 if (*cp != NUL)
82 ++cp;
83 }
84
Bram Moolenaarb7081e12021-09-04 18:47:28 +020085 return OK;
Bram Moolenaare677df82019-09-02 22:31:11 +020086}
87
88/*
89 * Calculate the number of screen spaces a tab will occupy.
90 * If "vts" is set then the tab widths are taken from that array,
91 * otherwise the value of ts is used.
92 */
93 int
94tabstop_padding(colnr_T col, int ts_arg, int *vts)
95{
96 int ts = ts_arg == 0 ? 8 : ts_arg;
97 int tabcount;
98 colnr_T tabcol = 0;
99 int t;
100 int padding = 0;
101
102 if (vts == NULL || vts[0] == 0)
103 return ts - (col % ts);
104
105 tabcount = vts[0];
106
107 for (t = 1; t <= tabcount; ++t)
108 {
109 tabcol += vts[t];
110 if (tabcol > col)
111 {
112 padding = (int)(tabcol - col);
113 break;
114 }
115 }
116 if (t > tabcount)
117 padding = vts[tabcount] - (int)((col - tabcol) % vts[tabcount]);
118
119 return padding;
120}
121
122/*
123 * Find the size of the tab that covers a particular column.
124 */
125 int
126tabstop_at(colnr_T col, int ts, int *vts)
127{
128 int tabcount;
129 colnr_T tabcol = 0;
130 int t;
131 int tab_size = 0;
132
133 if (vts == 0 || vts[0] == 0)
134 return ts;
135
136 tabcount = vts[0];
137 for (t = 1; t <= tabcount; ++t)
138 {
139 tabcol += vts[t];
140 if (tabcol > col)
141 {
142 tab_size = vts[t];
143 break;
144 }
145 }
146 if (t > tabcount)
147 tab_size = vts[tabcount];
148
149 return tab_size;
150}
151
152/*
153 * Find the column on which a tab starts.
154 */
155 colnr_T
156tabstop_start(colnr_T col, int ts, int *vts)
157{
158 int tabcount;
159 colnr_T tabcol = 0;
160 int t;
161 int excess;
162
163 if (vts == NULL || vts[0] == 0)
164 return (col / ts) * ts;
165
166 tabcount = vts[0];
167 for (t = 1; t <= tabcount; ++t)
168 {
169 tabcol += vts[t];
170 if (tabcol > col)
171 return tabcol - vts[t];
172 }
173
174 excess = tabcol % vts[tabcount];
175 return excess + ((col - excess) / vts[tabcount]) * vts[tabcount];
176}
177
178/*
179 * Find the number of tabs and spaces necessary to get from one column
180 * to another.
181 */
182 void
183tabstop_fromto(
184 colnr_T start_col,
185 colnr_T end_col,
186 int ts_arg,
187 int *vts,
188 int *ntabs,
189 int *nspcs)
190{
191 int spaces = end_col - start_col;
192 colnr_T tabcol = 0;
193 int padding = 0;
194 int tabcount;
195 int t;
196 int ts = ts_arg == 0 ? curbuf->b_p_ts : ts_arg;
197
198 if (vts == NULL || vts[0] == 0)
199 {
200 int tabs = 0;
201 int initspc = 0;
202
203 initspc = ts - (start_col % ts);
204 if (spaces >= initspc)
205 {
206 spaces -= initspc;
207 tabs++;
208 }
209 tabs += spaces / ts;
210 spaces -= (spaces / ts) * ts;
211
212 *ntabs = tabs;
213 *nspcs = spaces;
214 return;
215 }
216
217 // Find the padding needed to reach the next tabstop.
218 tabcount = vts[0];
219 for (t = 1; t <= tabcount; ++t)
220 {
221 tabcol += vts[t];
222 if (tabcol > start_col)
223 {
224 padding = (int)(tabcol - start_col);
225 break;
226 }
227 }
228 if (t > tabcount)
229 padding = vts[tabcount] - (int)((start_col - tabcol) % vts[tabcount]);
230
231 // If the space needed is less than the padding no tabs can be used.
232 if (spaces < padding)
233 {
234 *ntabs = 0;
235 *nspcs = spaces;
236 return;
237 }
238
239 *ntabs = 1;
240 spaces -= padding;
241
242 // At least one tab has been used. See if any more will fit.
243 while (spaces != 0 && ++t <= tabcount)
244 {
245 padding = vts[t];
246 if (spaces < padding)
247 {
248 *nspcs = spaces;
249 return;
250 }
251 ++*ntabs;
252 spaces -= padding;
253 }
254
255 *ntabs += spaces / vts[tabcount];
256 *nspcs = spaces % vts[tabcount];
257}
258
259/*
260 * See if two tabstop arrays contain the same values.
261 */
Bram Moolenaar14c01f82019-10-09 22:53:08 +0200262 static int
Bram Moolenaare677df82019-09-02 22:31:11 +0200263tabstop_eq(int *ts1, int *ts2)
264{
265 int t;
266
267 if ((ts1 == 0 && ts2) || (ts1 && ts2 == 0))
268 return FALSE;
269 if (ts1 == ts2)
270 return TRUE;
271 if (ts1[0] != ts2[0])
272 return FALSE;
273
274 for (t = 1; t <= ts1[0]; ++t)
275 if (ts1[t] != ts2[t])
276 return FALSE;
277
278 return TRUE;
279}
280
Bram Moolenaar14c01f82019-10-09 22:53:08 +0200281# if defined(FEAT_BEVAL) || defined(PROTO)
Bram Moolenaare677df82019-09-02 22:31:11 +0200282/*
283 * Copy a tabstop array, allocating space for the new array.
284 */
285 int *
286tabstop_copy(int *oldts)
287{
288 int *newts;
289 int t;
290
291 if (oldts == NULL)
292 return NULL;
293 newts = ALLOC_MULT(int, oldts[0] + 1);
294 if (newts != NULL)
295 for (t = 0; t <= oldts[0]; ++t)
296 newts[t] = oldts[t];
297 return newts;
298}
Bram Moolenaar14c01f82019-10-09 22:53:08 +0200299# endif
Bram Moolenaare677df82019-09-02 22:31:11 +0200300
301/*
302 * Return a count of the number of tabstops.
303 */
304 int
305tabstop_count(int *ts)
306{
307 return ts != NULL ? ts[0] : 0;
308}
309
310/*
311 * Return the first tabstop, or 8 if there are no tabstops defined.
312 */
313 int
314tabstop_first(int *ts)
315{
316 return ts != NULL ? ts[1] : 8;
317}
318
319#endif
320
321/*
322 * Return the effective shiftwidth value for current buffer, using the
323 * 'tabstop' value when 'shiftwidth' is zero.
324 */
325 long
326get_sw_value(buf_T *buf)
327{
328 return get_sw_value_col(buf, 0);
329}
330
331/*
332 * Idem, using "pos".
333 */
334 static long
335get_sw_value_pos(buf_T *buf, pos_T *pos)
336{
337 pos_T save_cursor = curwin->w_cursor;
338 long sw_value;
339
340 curwin->w_cursor = *pos;
341 sw_value = get_sw_value_col(buf, get_nolist_virtcol());
342 curwin->w_cursor = save_cursor;
343 return sw_value;
344}
345
346/*
347 * Idem, using the first non-black in the current line.
348 */
349 long
350get_sw_value_indent(buf_T *buf)
351{
352 pos_T pos = curwin->w_cursor;
353
354 pos.col = getwhitecols_curline();
355 return get_sw_value_pos(buf, &pos);
356}
357
358/*
359 * Idem, using virtual column "col".
360 */
361 long
362get_sw_value_col(buf_T *buf, colnr_T col UNUSED)
363{
364 return buf->b_p_sw ? buf->b_p_sw :
Bram Moolenaar14c01f82019-10-09 22:53:08 +0200365#ifdef FEAT_VARTABS
Bram Moolenaare677df82019-09-02 22:31:11 +0200366 tabstop_at(col, buf->b_p_ts, buf->b_p_vts_array);
Bram Moolenaar14c01f82019-10-09 22:53:08 +0200367#else
Bram Moolenaare677df82019-09-02 22:31:11 +0200368 buf->b_p_ts;
Bram Moolenaar14c01f82019-10-09 22:53:08 +0200369#endif
Bram Moolenaare677df82019-09-02 22:31:11 +0200370}
371
372/*
373 * Return the effective softtabstop value for the current buffer, using the
374 * 'shiftwidth' value when 'softtabstop' is negative.
375 */
376 long
377get_sts_value(void)
378{
379 return curbuf->b_p_sts < 0 ? get_sw_value(curbuf) : curbuf->b_p_sts;
380}
Bram Moolenaar14c01f82019-10-09 22:53:08 +0200381
382/*
383 * Count the size (in window cells) of the indent in the current line.
384 */
385 int
386get_indent(void)
387{
388#ifdef FEAT_VARTABS
389 return get_indent_str_vtab(ml_get_curline(), (int)curbuf->b_p_ts,
390 curbuf->b_p_vts_array, FALSE);
391#else
392 return get_indent_str(ml_get_curline(), (int)curbuf->b_p_ts, FALSE);
393#endif
394}
395
396/*
397 * Count the size (in window cells) of the indent in line "lnum".
398 */
399 int
400get_indent_lnum(linenr_T lnum)
401{
402#ifdef FEAT_VARTABS
403 return get_indent_str_vtab(ml_get(lnum), (int)curbuf->b_p_ts,
404 curbuf->b_p_vts_array, FALSE);
405#else
406 return get_indent_str(ml_get(lnum), (int)curbuf->b_p_ts, FALSE);
407#endif
408}
409
410#if defined(FEAT_FOLDING) || defined(PROTO)
411/*
412 * Count the size (in window cells) of the indent in line "lnum" of buffer
413 * "buf".
414 */
415 int
416get_indent_buf(buf_T *buf, linenr_T lnum)
417{
418# ifdef FEAT_VARTABS
419 return get_indent_str_vtab(ml_get_buf(buf, lnum, FALSE),
420 (int)curbuf->b_p_ts, buf->b_p_vts_array, FALSE);
421# else
422 return get_indent_str(ml_get_buf(buf, lnum, FALSE), (int)buf->b_p_ts, FALSE);
423# endif
424}
425#endif
426
427/*
428 * count the size (in window cells) of the indent in line "ptr", with
429 * 'tabstop' at "ts"
430 */
431 int
432get_indent_str(
433 char_u *ptr,
434 int ts,
435 int list) // if TRUE, count only screen size for tabs
436{
437 int count = 0;
438
439 for ( ; *ptr; ++ptr)
440 {
441 if (*ptr == TAB)
442 {
Bram Moolenaareed9d462021-02-15 20:38:25 +0100443 if (!list || curwin->w_lcs_chars.tab1)
444 // count a tab for what it is worth
Bram Moolenaar14c01f82019-10-09 22:53:08 +0200445 count += ts - (count % ts);
446 else
447 // In list mode, when tab is not set, count screen char width
448 // for Tab, displays: ^I
449 count += ptr2cells(ptr);
450 }
451 else if (*ptr == ' ')
452 ++count; // count a space for one
453 else
454 break;
455 }
456 return count;
457}
458
459#ifdef FEAT_VARTABS
460/*
461 * Count the size (in window cells) of the indent in line "ptr", using
462 * variable tabstops.
463 * if "list" is TRUE, count only screen size for tabs.
464 */
465 int
466get_indent_str_vtab(char_u *ptr, int ts, int *vts, int list)
467{
468 int count = 0;
469
470 for ( ; *ptr; ++ptr)
471 {
472 if (*ptr == TAB) // count a tab for what it is worth
473 {
Bram Moolenaareed9d462021-02-15 20:38:25 +0100474 if (!list || curwin->w_lcs_chars.tab1)
Bram Moolenaar14c01f82019-10-09 22:53:08 +0200475 count += tabstop_padding(count, ts, vts);
476 else
477 // In list mode, when tab is not set, count screen char width
478 // for Tab, displays: ^I
479 count += ptr2cells(ptr);
480 }
481 else if (*ptr == ' ')
482 ++count; // count a space for one
483 else
484 break;
485 }
486 return count;
487}
488#endif
489
490/*
491 * Set the indent of the current line.
492 * Leaves the cursor on the first non-blank in the line.
493 * Caller must take care of undo.
494 * "flags":
495 * SIN_CHANGED: call changed_bytes() if the line was changed.
496 * SIN_INSERT: insert the indent in front of the line.
497 * SIN_UNDO: save line for undo before changing it.
498 * Returns TRUE if the line was changed.
499 */
500 int
501set_indent(
502 int size, // measured in spaces
503 int flags)
504{
505 char_u *p;
506 char_u *newline;
507 char_u *oldline;
508 char_u *s;
509 int todo;
510 int ind_len; // measured in characters
511 int line_len;
512 int doit = FALSE;
513 int ind_done = 0; // measured in spaces
514#ifdef FEAT_VARTABS
515 int ind_col = 0;
516#endif
517 int tab_pad;
518 int retval = FALSE;
519 int orig_char_len = -1; // number of initial whitespace chars when
520 // 'et' and 'pi' are both set
521
522 // First check if there is anything to do and compute the number of
523 // characters needed for the indent.
524 todo = size;
525 ind_len = 0;
526 p = oldline = ml_get_curline();
527
528 // Calculate the buffer size for the new indent, and check to see if it
529 // isn't already set
530
531 // if 'expandtab' isn't set: use TABs; if both 'expandtab' and
532 // 'preserveindent' are set count the number of characters at the
533 // beginning of the line to be copied
534 if (!curbuf->b_p_et || (!(flags & SIN_INSERT) && curbuf->b_p_pi))
535 {
536 // If 'preserveindent' is set then reuse as much as possible of
537 // the existing indent structure for the new indent
538 if (!(flags & SIN_INSERT) && curbuf->b_p_pi)
539 {
540 ind_done = 0;
541
542 // count as many characters as we can use
543 while (todo > 0 && VIM_ISWHITE(*p))
544 {
545 if (*p == TAB)
546 {
547#ifdef FEAT_VARTABS
548 tab_pad = tabstop_padding(ind_done, curbuf->b_p_ts,
549 curbuf->b_p_vts_array);
550#else
551 tab_pad = (int)curbuf->b_p_ts
552 - (ind_done % (int)curbuf->b_p_ts);
553#endif
554 // stop if this tab will overshoot the target
555 if (todo < tab_pad)
556 break;
557 todo -= tab_pad;
558 ++ind_len;
559 ind_done += tab_pad;
560 }
561 else
562 {
563 --todo;
564 ++ind_len;
565 ++ind_done;
566 }
567 ++p;
568 }
569
570#ifdef FEAT_VARTABS
571 // These diverge from this point.
572 ind_col = ind_done;
573#endif
574 // Set initial number of whitespace chars to copy if we are
575 // preserving indent but expandtab is set
576 if (curbuf->b_p_et)
577 orig_char_len = ind_len;
578
579 // Fill to next tabstop with a tab, if possible
580#ifdef FEAT_VARTABS
581 tab_pad = tabstop_padding(ind_done, curbuf->b_p_ts,
582 curbuf->b_p_vts_array);
583#else
584 tab_pad = (int)curbuf->b_p_ts - (ind_done % (int)curbuf->b_p_ts);
585#endif
586 if (todo >= tab_pad && orig_char_len == -1)
587 {
588 doit = TRUE;
589 todo -= tab_pad;
590 ++ind_len;
591 // ind_done += tab_pad;
592#ifdef FEAT_VARTABS
593 ind_col += tab_pad;
594#endif
595 }
596 }
597
598 // count tabs required for indent
599#ifdef FEAT_VARTABS
600 for (;;)
601 {
602 tab_pad = tabstop_padding(ind_col, curbuf->b_p_ts,
603 curbuf->b_p_vts_array);
604 if (todo < tab_pad)
605 break;
606 if (*p != TAB)
607 doit = TRUE;
608 else
609 ++p;
610 todo -= tab_pad;
611 ++ind_len;
612 ind_col += tab_pad;
613 }
614#else
615 while (todo >= (int)curbuf->b_p_ts)
616 {
617 if (*p != TAB)
618 doit = TRUE;
619 else
620 ++p;
621 todo -= (int)curbuf->b_p_ts;
622 ++ind_len;
623 // ind_done += (int)curbuf->b_p_ts;
624 }
625#endif
626 }
627 // count spaces required for indent
628 while (todo > 0)
629 {
630 if (*p != ' ')
631 doit = TRUE;
632 else
633 ++p;
634 --todo;
635 ++ind_len;
636 // ++ind_done;
637 }
638
639 // Return if the indent is OK already.
640 if (!doit && !VIM_ISWHITE(*p) && !(flags & SIN_INSERT))
641 return FALSE;
642
643 // Allocate memory for the new line.
644 if (flags & SIN_INSERT)
645 p = oldline;
646 else
647 p = skipwhite(p);
648 line_len = (int)STRLEN(p) + 1;
649
650 // If 'preserveindent' and 'expandtab' are both set keep the original
651 // characters and allocate accordingly. We will fill the rest with spaces
652 // after the if (!curbuf->b_p_et) below.
653 if (orig_char_len != -1)
654 {
655 newline = alloc(orig_char_len + size - ind_done + line_len);
656 if (newline == NULL)
657 return FALSE;
658 todo = size - ind_done;
659 ind_len = orig_char_len + todo; // Set total length of indent in
660 // characters, which may have been
661 // undercounted until now
662 p = oldline;
663 s = newline;
664 while (orig_char_len > 0)
665 {
666 *s++ = *p++;
667 orig_char_len--;
668 }
669
670 // Skip over any additional white space (useful when newindent is less
671 // than old)
672 while (VIM_ISWHITE(*p))
673 ++p;
674
675 }
676 else
677 {
678 todo = size;
679 newline = alloc(ind_len + line_len);
680 if (newline == NULL)
681 return FALSE;
682 s = newline;
683 }
684
685 // Put the characters in the new line.
686 // if 'expandtab' isn't set: use TABs
687 if (!curbuf->b_p_et)
688 {
689 // If 'preserveindent' is set then reuse as much as possible of
690 // the existing indent structure for the new indent
691 if (!(flags & SIN_INSERT) && curbuf->b_p_pi)
692 {
693 p = oldline;
694 ind_done = 0;
695
696 while (todo > 0 && VIM_ISWHITE(*p))
697 {
698 if (*p == TAB)
699 {
700#ifdef FEAT_VARTABS
701 tab_pad = tabstop_padding(ind_done, curbuf->b_p_ts,
702 curbuf->b_p_vts_array);
703#else
704 tab_pad = (int)curbuf->b_p_ts
705 - (ind_done % (int)curbuf->b_p_ts);
706#endif
707 // stop if this tab will overshoot the target
708 if (todo < tab_pad)
709 break;
710 todo -= tab_pad;
711 ind_done += tab_pad;
712 }
713 else
714 {
715 --todo;
716 ++ind_done;
717 }
718 *s++ = *p++;
719 }
720
721 // Fill to next tabstop with a tab, if possible
722#ifdef FEAT_VARTABS
723 tab_pad = tabstop_padding(ind_done, curbuf->b_p_ts,
724 curbuf->b_p_vts_array);
725#else
726 tab_pad = (int)curbuf->b_p_ts - (ind_done % (int)curbuf->b_p_ts);
727#endif
728 if (todo >= tab_pad)
729 {
730 *s++ = TAB;
731 todo -= tab_pad;
732#ifdef FEAT_VARTABS
733 ind_done += tab_pad;
734#endif
735 }
736
737 p = skipwhite(p);
738 }
739
740#ifdef FEAT_VARTABS
741 for (;;)
742 {
743 tab_pad = tabstop_padding(ind_done, curbuf->b_p_ts,
744 curbuf->b_p_vts_array);
745 if (todo < tab_pad)
746 break;
747 *s++ = TAB;
748 todo -= tab_pad;
749 ind_done += tab_pad;
750 }
751#else
752 while (todo >= (int)curbuf->b_p_ts)
753 {
754 *s++ = TAB;
755 todo -= (int)curbuf->b_p_ts;
756 }
757#endif
758 }
759 while (todo > 0)
760 {
761 *s++ = ' ';
762 --todo;
763 }
764 mch_memmove(s, p, (size_t)line_len);
765
766 // Replace the line (unless undo fails).
767 if (!(flags & SIN_UNDO) || u_savesub(curwin->w_cursor.lnum) == OK)
768 {
Bram Moolenaarcf306432020-06-29 20:40:37 +0200769 colnr_T old_offset = (colnr_T)(p - oldline);
770 colnr_T new_offset = (colnr_T)(s - newline);
771
772 // this may free "newline"
Bram Moolenaar14c01f82019-10-09 22:53:08 +0200773 ml_replace(curwin->w_cursor.lnum, newline, FALSE);
774 if (flags & SIN_CHANGED)
775 changed_bytes(curwin->w_cursor.lnum, 0);
776
777 // Correct saved cursor position if it is in this line.
778 if (saved_cursor.lnum == curwin->w_cursor.lnum)
779 {
Bram Moolenaarcf306432020-06-29 20:40:37 +0200780 if (saved_cursor.col >= old_offset)
Bram Moolenaar14c01f82019-10-09 22:53:08 +0200781 // cursor was after the indent, adjust for the number of
782 // bytes added/removed
Bram Moolenaarcf306432020-06-29 20:40:37 +0200783 saved_cursor.col += ind_len - old_offset;
784 else if (saved_cursor.col >= new_offset)
Bram Moolenaar14c01f82019-10-09 22:53:08 +0200785 // cursor was in the indent, and is now after it, put it back
786 // at the start of the indent (replacing spaces with TAB)
Bram Moolenaarcf306432020-06-29 20:40:37 +0200787 saved_cursor.col = new_offset;
Bram Moolenaar14c01f82019-10-09 22:53:08 +0200788 }
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +0100789#ifdef FEAT_PROP_POPUP
Bram Moolenaar14c01f82019-10-09 22:53:08 +0200790 {
Bram Moolenaarcf306432020-06-29 20:40:37 +0200791 int added = ind_len - old_offset;
Bram Moolenaar14c01f82019-10-09 22:53:08 +0200792
793 // When increasing indent this behaves like spaces were inserted at
794 // the old indent, when decreasing indent it behaves like spaces
795 // were deleted at the new indent.
796 adjust_prop_columns(curwin->w_cursor.lnum,
Bram Moolenaarcf306432020-06-29 20:40:37 +0200797 added > 0 ? old_offset : (colnr_T)ind_len, added, 0);
Bram Moolenaar14c01f82019-10-09 22:53:08 +0200798 }
799#endif
800 retval = TRUE;
801 }
802 else
803 vim_free(newline);
804
805 curwin->w_cursor.col = ind_len;
806 return retval;
807}
808
809/*
810 * Return the indent of the current line after a number. Return -1 if no
811 * number was found. Used for 'n' in 'formatoptions': numbered list.
812 * Since a pattern is used it can actually handle more than numbers.
813 */
814 int
815get_number_indent(linenr_T lnum)
816{
817 colnr_T col;
818 pos_T pos;
819
820 regmatch_T regmatch;
821 int lead_len = 0; // length of comment leader
822
823 if (lnum > curbuf->b_ml.ml_line_count)
824 return -1;
825 pos.lnum = 0;
826
827 // In format_lines() (i.e. not insert mode), fo+=q is needed too...
828 if ((State & INSERT) || has_format_option(FO_Q_COMS))
829 lead_len = get_leader_len(ml_get(lnum), NULL, FALSE, TRUE);
830
831 regmatch.regprog = vim_regcomp(curbuf->b_p_flp, RE_MAGIC);
832 if (regmatch.regprog != NULL)
833 {
834 regmatch.rm_ic = FALSE;
835
836 // vim_regexec() expects a pointer to a line. This lets us
837 // start matching for the flp beyond any comment leader...
838 if (vim_regexec(&regmatch, ml_get(lnum) + lead_len, (colnr_T)0))
839 {
840 pos.lnum = lnum;
841 pos.col = (colnr_T)(*regmatch.endp - ml_get(lnum));
842 pos.coladd = 0;
843 }
844 vim_regfree(regmatch.regprog);
845 }
846
847 if (pos.lnum == 0 || *ml_get_pos(&pos) == NUL)
848 return -1;
849 getvcol(curwin, &pos, &col, NULL, NULL);
850 return (int)col;
851}
852
853#if defined(FEAT_LINEBREAK) || defined(PROTO)
854/*
Bram Moolenaar7bae0b12019-11-21 22:14:18 +0100855 * This is called when 'breakindentopt' is changed and when a window is
856 * initialized.
857 */
858 int
859briopt_check(win_T *wp)
860{
861 char_u *p;
862 int bri_shift = 0;
863 long bri_min = 20;
864 int bri_sbr = FALSE;
Christian Brabandt4a0b85a2021-07-14 20:00:27 +0200865 int bri_list = 0;
Bram Moolenaar7bae0b12019-11-21 22:14:18 +0100866
867 p = wp->w_p_briopt;
868 while (*p != NUL)
869 {
870 if (STRNCMP(p, "shift:", 6) == 0
871 && ((p[6] == '-' && VIM_ISDIGIT(p[7])) || VIM_ISDIGIT(p[6])))
872 {
873 p += 6;
874 bri_shift = getdigits(&p);
875 }
876 else if (STRNCMP(p, "min:", 4) == 0 && VIM_ISDIGIT(p[4]))
877 {
878 p += 4;
879 bri_min = getdigits(&p);
880 }
881 else if (STRNCMP(p, "sbr", 3) == 0)
882 {
883 p += 3;
884 bri_sbr = TRUE;
885 }
Christian Brabandt4a0b85a2021-07-14 20:00:27 +0200886 else if (STRNCMP(p, "list:", 5) == 0)
887 {
888 p += 5;
889 bri_list = getdigits(&p);
890 }
Bram Moolenaar7bae0b12019-11-21 22:14:18 +0100891 if (*p != ',' && *p != NUL)
892 return FAIL;
893 if (*p == ',')
894 ++p;
895 }
896
Bram Moolenaarb81f56f2020-02-23 15:29:46 +0100897 wp->w_briopt_shift = bri_shift;
898 wp->w_briopt_min = bri_min;
899 wp->w_briopt_sbr = bri_sbr;
Christian Brabandt4a0b85a2021-07-14 20:00:27 +0200900 wp->w_briopt_list = bri_list;
Bram Moolenaar7bae0b12019-11-21 22:14:18 +0100901
902 return OK;
903}
904
905/*
Bram Moolenaar14c01f82019-10-09 22:53:08 +0200906 * Return appropriate space number for breakindent, taking influencing
907 * parameters into account. Window must be specified, since it is not
908 * necessarily always the current one.
909 */
910 int
911get_breakindent_win(
912 win_T *wp,
913 char_u *line) // start of the line
914{
915 static int prev_indent = 0; // cached indent value
916 static long prev_ts = 0L; // cached tabstop value
917 static char_u *prev_line = NULL; // cached pointer to line
918 static varnumber_T prev_tick = 0; // changedtick of cached value
919# ifdef FEAT_VARTABS
920 static int *prev_vts = NULL; // cached vartabs values
921# endif
922 int bri = 0;
923 // window width minus window margin space, i.e. what rests for text
924 const int eff_wwidth = wp->w_width
925 - ((wp->w_p_nu || wp->w_p_rnu)
926 && (vim_strchr(p_cpo, CPO_NUMCOL) == NULL)
927 ? number_width(wp) + 1 : 0);
928
929 // used cached indent, unless pointer or 'tabstop' changed
930 if (prev_line != line || prev_ts != wp->w_buffer->b_p_ts
931 || prev_tick != CHANGEDTICK(wp->w_buffer)
932# ifdef FEAT_VARTABS
933 || prev_vts != wp->w_buffer->b_p_vts_array
934# endif
935 )
936 {
937 prev_line = line;
938 prev_ts = wp->w_buffer->b_p_ts;
939 prev_tick = CHANGEDTICK(wp->w_buffer);
940# ifdef FEAT_VARTABS
941 prev_vts = wp->w_buffer->b_p_vts_array;
942 prev_indent = get_indent_str_vtab(line,
943 (int)wp->w_buffer->b_p_ts,
944 wp->w_buffer->b_p_vts_array, wp->w_p_list);
945# else
946 prev_indent = get_indent_str(line,
947 (int)wp->w_buffer->b_p_ts, wp->w_p_list);
948# endif
949 }
Bram Moolenaarb81f56f2020-02-23 15:29:46 +0100950 bri = prev_indent + wp->w_briopt_shift;
Bram Moolenaar14c01f82019-10-09 22:53:08 +0200951
Bram Moolenaar14c01f82019-10-09 22:53:08 +0200952 // Add offset for number column, if 'n' is in 'cpoptions'
953 bri += win_col_off2(wp);
954
Christian Brabandt4a0b85a2021-07-14 20:00:27 +0200955 // add additional indent for numbered lists
Maxim Kimf674b352021-07-22 11:46:59 +0200956 if (wp->w_briopt_list != 0)
Christian Brabandt4a0b85a2021-07-14 20:00:27 +0200957 {
958 regmatch_T regmatch;
959
960 regmatch.regprog = vim_regcomp(curbuf->b_p_flp,
961 RE_MAGIC + RE_STRING + RE_AUTO + RE_STRICT);
962 if (regmatch.regprog != NULL)
963 {
Dominique Pellea9187122021-08-29 22:12:56 +0200964 regmatch.rm_ic = FALSE;
Christian Brabandt4a0b85a2021-07-14 20:00:27 +0200965 if (vim_regexec(&regmatch, line, 0))
Maxim Kimf674b352021-07-22 11:46:59 +0200966 {
967 if (wp->w_briopt_list > 0)
968 bri += wp->w_briopt_list;
969 else
970 bri = (*regmatch.endp - *regmatch.startp);
971 }
Christian Brabandt4a0b85a2021-07-14 20:00:27 +0200972 vim_regfree(regmatch.regprog);
973 }
974 }
975
Maxim Kimf674b352021-07-22 11:46:59 +0200976 // indent minus the length of the showbreak string
977 if (wp->w_briopt_sbr)
978 bri -= vim_strsize(get_showbreak_value(wp));
979
980
Bram Moolenaar14c01f82019-10-09 22:53:08 +0200981 // never indent past left window margin
982 if (bri < 0)
983 bri = 0;
Christian Brabandt4a0b85a2021-07-14 20:00:27 +0200984
Bram Moolenaar14c01f82019-10-09 22:53:08 +0200985 // always leave at least bri_min characters on the left,
986 // if text width is sufficient
Bram Moolenaarb81f56f2020-02-23 15:29:46 +0100987 else if (bri > eff_wwidth - wp->w_briopt_min)
988 bri = (eff_wwidth - wp->w_briopt_min < 0)
989 ? 0 : eff_wwidth - wp->w_briopt_min;
Bram Moolenaar14c01f82019-10-09 22:53:08 +0200990
991 return bri;
992}
993#endif
994
995/*
996 * When extra == 0: Return TRUE if the cursor is before or on the first
997 * non-blank in the line.
998 * When extra == 1: Return TRUE if the cursor is before the first non-blank in
999 * the line.
1000 */
1001 int
1002inindent(int extra)
1003{
1004 char_u *ptr;
1005 colnr_T col;
1006
1007 for (col = 0, ptr = ml_get_curline(); VIM_ISWHITE(*ptr); ++col)
1008 ++ptr;
1009 if (col >= curwin->w_cursor.col + extra)
1010 return TRUE;
1011 else
1012 return FALSE;
1013}
1014
1015#if defined(FEAT_LISP) || defined(FEAT_CINDENT) || defined(PROTO)
1016/*
1017 * op_reindent - handle reindenting a block of lines.
1018 */
1019 void
1020op_reindent(oparg_T *oap, int (*how)(void))
1021{
1022 long i;
1023 char_u *l;
1024 int amount;
1025 linenr_T first_changed = 0;
1026 linenr_T last_changed = 0;
1027 linenr_T start_lnum = curwin->w_cursor.lnum;
1028
1029 // Don't even try when 'modifiable' is off.
1030 if (!curbuf->b_p_ma)
1031 {
Bram Moolenaar108010a2021-06-27 22:03:33 +02001032 emsg(_(e_cannot_make_changes_modifiable_is_off));
Bram Moolenaar14c01f82019-10-09 22:53:08 +02001033 return;
1034 }
1035
1036 for (i = oap->line_count; --i >= 0 && !got_int; )
1037 {
1038 // it's a slow thing to do, so give feedback so there's no worry that
1039 // the computer's just hung.
1040
1041 if (i > 1
1042 && (i % 50 == 0 || i == oap->line_count - 1)
1043 && oap->line_count > p_report)
1044 smsg(_("%ld lines to indent... "), i);
1045
1046 // Be vi-compatible: For lisp indenting the first line is not
1047 // indented, unless there is only one line.
1048# ifdef FEAT_LISP
1049 if (i != oap->line_count - 1 || oap->line_count == 1
1050 || how != get_lisp_indent)
1051# endif
1052 {
1053 l = skipwhite(ml_get_curline());
1054 if (*l == NUL) // empty or blank line
1055 amount = 0;
1056 else
1057 amount = how(); // get the indent for this line
1058
1059 if (amount >= 0 && set_indent(amount, SIN_UNDO))
1060 {
1061 // did change the indent, call changed_lines() later
1062 if (first_changed == 0)
1063 first_changed = curwin->w_cursor.lnum;
1064 last_changed = curwin->w_cursor.lnum;
1065 }
1066 }
1067 ++curwin->w_cursor.lnum;
1068 curwin->w_cursor.col = 0; // make sure it's valid
1069 }
1070
1071 // put cursor on first non-blank of indented line
1072 curwin->w_cursor.lnum = start_lnum;
1073 beginline(BL_SOL | BL_FIX);
1074
1075 // Mark changed lines so that they will be redrawn. When Visual
1076 // highlighting was present, need to continue until the last line. When
1077 // there is no change still need to remove the Visual highlighting.
1078 if (last_changed != 0)
1079 changed_lines(first_changed, 0,
1080 oap->is_VIsual ? start_lnum + oap->line_count :
1081 last_changed + 1, 0L);
1082 else if (oap->is_VIsual)
1083 redraw_curbuf_later(INVERTED);
1084
1085 if (oap->line_count > p_report)
1086 {
1087 i = oap->line_count - (i + 1);
1088 smsg(NGETTEXT("%ld line indented ",
1089 "%ld lines indented ", i), i);
1090 }
Bram Moolenaare1004402020-10-24 20:49:43 +02001091 if ((cmdmod.cmod_flags & CMOD_LOCKMARKS) == 0)
Bram Moolenaarf4a1d1c2019-11-16 13:50:25 +01001092 {
1093 // set '[ and '] marks
1094 curbuf->b_op_start = oap->start;
1095 curbuf->b_op_end = oap->end;
1096 }
Bram Moolenaar14c01f82019-10-09 22:53:08 +02001097}
1098#endif // defined(FEAT_LISP) || defined(FEAT_CINDENT)
1099
1100#if defined(FEAT_SMARTINDENT) || defined(FEAT_CINDENT) || defined(PROTO)
1101/*
1102 * Return TRUE if lines starting with '#' should be left aligned.
1103 */
1104 int
1105preprocs_left(void)
1106{
1107 return
1108# ifdef FEAT_SMARTINDENT
1109# ifdef FEAT_CINDENT
1110 (curbuf->b_p_si && !curbuf->b_p_cin) ||
1111# else
1112 curbuf->b_p_si
1113# endif
1114# endif
1115# ifdef FEAT_CINDENT
1116 (curbuf->b_p_cin && in_cinkeys('#', ' ', TRUE)
1117 && curbuf->b_ind_hash_comment == 0)
1118# endif
1119 ;
1120}
1121#endif
1122
1123#ifdef FEAT_SMARTINDENT
1124/*
1125 * Try to do some very smart auto-indenting.
1126 * Used when inserting a "normal" character.
1127 */
1128 void
1129ins_try_si(int c)
1130{
1131 pos_T *pos, old_pos;
1132 char_u *ptr;
1133 int i;
1134 int temp;
1135
1136 // do some very smart indenting when entering '{' or '}'
1137 if (((did_si || can_si_back) && c == '{') || (can_si && c == '}'))
1138 {
1139 // for '}' set indent equal to indent of line containing matching '{'
1140 if (c == '}' && (pos = findmatch(NULL, '{')) != NULL)
1141 {
1142 old_pos = curwin->w_cursor;
1143 // If the matching '{' has a ')' immediately before it (ignoring
1144 // white-space), then line up with the start of the line
1145 // containing the matching '(' if there is one. This handles the
1146 // case where an "if (..\n..) {" statement continues over multiple
1147 // lines -- webb
1148 ptr = ml_get(pos->lnum);
1149 i = pos->col;
1150 if (i > 0) // skip blanks before '{'
1151 while (--i > 0 && VIM_ISWHITE(ptr[i]))
1152 ;
1153 curwin->w_cursor.lnum = pos->lnum;
1154 curwin->w_cursor.col = i;
1155 if (ptr[i] == ')' && (pos = findmatch(NULL, '(')) != NULL)
1156 curwin->w_cursor = *pos;
1157 i = get_indent();
1158 curwin->w_cursor = old_pos;
1159 if (State & VREPLACE_FLAG)
1160 change_indent(INDENT_SET, i, FALSE, NUL, TRUE);
1161 else
1162 (void)set_indent(i, SIN_CHANGED);
1163 }
1164 else if (curwin->w_cursor.col > 0)
1165 {
1166 // when inserting '{' after "O" reduce indent, but not
1167 // more than indent of previous line
1168 temp = TRUE;
1169 if (c == '{' && can_si_back && curwin->w_cursor.lnum > 1)
1170 {
1171 old_pos = curwin->w_cursor;
1172 i = get_indent();
1173 while (curwin->w_cursor.lnum > 1)
1174 {
1175 ptr = skipwhite(ml_get(--(curwin->w_cursor.lnum)));
1176
1177 // ignore empty lines and lines starting with '#'.
1178 if (*ptr != '#' && *ptr != NUL)
1179 break;
1180 }
1181 if (get_indent() >= i)
1182 temp = FALSE;
1183 curwin->w_cursor = old_pos;
1184 }
1185 if (temp)
1186 shift_line(TRUE, FALSE, 1, TRUE);
1187 }
1188 }
1189
1190 // set indent of '#' always to 0
1191 if (curwin->w_cursor.col > 0 && can_si && c == '#')
1192 {
1193 // remember current indent for next line
1194 old_indent = get_indent();
1195 (void)set_indent(0, SIN_CHANGED);
1196 }
1197
1198 // Adjust ai_col, the char at this position can be deleted.
1199 if (ai_col > curwin->w_cursor.col)
1200 ai_col = curwin->w_cursor.col;
1201}
1202#endif
1203
1204/*
1205 * Insert an indent (for <Tab> or CTRL-T) or delete an indent (for CTRL-D).
1206 * Keep the cursor on the same character.
1207 * type == INDENT_INC increase indent (for CTRL-T or <Tab>)
1208 * type == INDENT_DEC decrease indent (for CTRL-D)
1209 * type == INDENT_SET set indent to "amount"
1210 * if round is TRUE, round the indent to 'shiftwidth' (only with _INC and _Dec).
1211 */
1212 void
1213change_indent(
1214 int type,
1215 int amount,
1216 int round,
1217 int replaced, // replaced character, put on replace stack
1218 int call_changed_bytes) // call changed_bytes()
1219{
1220 int vcol;
1221 int last_vcol;
1222 int insstart_less; // reduction for Insstart.col
1223 int new_cursor_col;
1224 int i;
1225 char_u *ptr;
1226 int save_p_list;
1227 int start_col;
1228 colnr_T vc;
1229 colnr_T orig_col = 0; // init for GCC
1230 char_u *new_line, *orig_line = NULL; // init for GCC
1231
1232 // VREPLACE mode needs to know what the line was like before changing
1233 if (State & VREPLACE_FLAG)
1234 {
1235 orig_line = vim_strsave(ml_get_curline()); // Deal with NULL below
1236 orig_col = curwin->w_cursor.col;
1237 }
1238
1239 // for the following tricks we don't want list mode
1240 save_p_list = curwin->w_p_list;
1241 curwin->w_p_list = FALSE;
1242 vc = getvcol_nolist(&curwin->w_cursor);
1243 vcol = vc;
1244
1245 // For Replace mode we need to fix the replace stack later, which is only
1246 // possible when the cursor is in the indent. Remember the number of
1247 // characters before the cursor if it's possible.
1248 start_col = curwin->w_cursor.col;
1249
1250 // determine offset from first non-blank
1251 new_cursor_col = curwin->w_cursor.col;
1252 beginline(BL_WHITE);
1253 new_cursor_col -= curwin->w_cursor.col;
1254
1255 insstart_less = curwin->w_cursor.col;
1256
1257 // If the cursor is in the indent, compute how many screen columns the
1258 // cursor is to the left of the first non-blank.
1259 if (new_cursor_col < 0)
1260 vcol = get_indent() - vcol;
1261
1262 if (new_cursor_col > 0) // can't fix replace stack
1263 start_col = -1;
1264
1265 // Set the new indent. The cursor will be put on the first non-blank.
1266 if (type == INDENT_SET)
1267 (void)set_indent(amount, call_changed_bytes ? SIN_CHANGED : 0);
1268 else
1269 {
1270 int save_State = State;
1271
1272 // Avoid being called recursively.
1273 if (State & VREPLACE_FLAG)
1274 State = INSERT;
1275 shift_line(type == INDENT_DEC, round, 1, call_changed_bytes);
1276 State = save_State;
1277 }
1278 insstart_less -= curwin->w_cursor.col;
1279
1280 // Try to put cursor on same character.
1281 // If the cursor is at or after the first non-blank in the line,
1282 // compute the cursor column relative to the column of the first
1283 // non-blank character.
1284 // If we are not in insert mode, leave the cursor on the first non-blank.
1285 // If the cursor is before the first non-blank, position it relative
1286 // to the first non-blank, counted in screen columns.
1287 if (new_cursor_col >= 0)
1288 {
1289 // When changing the indent while the cursor is touching it, reset
1290 // Insstart_col to 0.
1291 if (new_cursor_col == 0)
1292 insstart_less = MAXCOL;
1293 new_cursor_col += curwin->w_cursor.col;
1294 }
1295 else if (!(State & INSERT))
1296 new_cursor_col = curwin->w_cursor.col;
1297 else
1298 {
1299 // Compute the screen column where the cursor should be.
1300 vcol = get_indent() - vcol;
1301 curwin->w_virtcol = (colnr_T)((vcol < 0) ? 0 : vcol);
1302
1303 // Advance the cursor until we reach the right screen column.
1304 vcol = last_vcol = 0;
1305 new_cursor_col = -1;
1306 ptr = ml_get_curline();
1307 while (vcol <= (int)curwin->w_virtcol)
1308 {
1309 last_vcol = vcol;
1310 if (has_mbyte && new_cursor_col >= 0)
1311 new_cursor_col += (*mb_ptr2len)(ptr + new_cursor_col);
1312 else
1313 ++new_cursor_col;
1314 vcol += lbr_chartabsize(ptr, ptr + new_cursor_col, (colnr_T)vcol);
1315 }
1316 vcol = last_vcol;
1317
1318 // May need to insert spaces to be able to position the cursor on
1319 // the right screen column.
1320 if (vcol != (int)curwin->w_virtcol)
1321 {
1322 curwin->w_cursor.col = (colnr_T)new_cursor_col;
1323 i = (int)curwin->w_virtcol - vcol;
1324 ptr = alloc(i + 1);
1325 if (ptr != NULL)
1326 {
1327 new_cursor_col += i;
1328 ptr[i] = NUL;
1329 while (--i >= 0)
1330 ptr[i] = ' ';
1331 ins_str(ptr);
1332 vim_free(ptr);
1333 }
1334 }
1335
1336 // When changing the indent while the cursor is in it, reset
1337 // Insstart_col to 0.
1338 insstart_less = MAXCOL;
1339 }
1340
1341 curwin->w_p_list = save_p_list;
1342
1343 if (new_cursor_col <= 0)
1344 curwin->w_cursor.col = 0;
1345 else
1346 curwin->w_cursor.col = (colnr_T)new_cursor_col;
1347 curwin->w_set_curswant = TRUE;
1348 changed_cline_bef_curs();
1349
1350 // May have to adjust the start of the insert.
1351 if (State & INSERT)
1352 {
1353 if (curwin->w_cursor.lnum == Insstart.lnum && Insstart.col != 0)
1354 {
1355 if ((int)Insstart.col <= insstart_less)
1356 Insstart.col = 0;
1357 else
1358 Insstart.col -= insstart_less;
1359 }
1360 if ((int)ai_col <= insstart_less)
1361 ai_col = 0;
1362 else
1363 ai_col -= insstart_less;
1364 }
1365
1366 // For REPLACE mode, may have to fix the replace stack, if it's possible.
1367 // If the number of characters before the cursor decreased, need to pop a
1368 // few characters from the replace stack.
1369 // If the number of characters before the cursor increased, need to push a
1370 // few NULs onto the replace stack.
1371 if (REPLACE_NORMAL(State) && start_col >= 0)
1372 {
1373 while (start_col > (int)curwin->w_cursor.col)
1374 {
1375 replace_join(0); // remove a NUL from the replace stack
1376 --start_col;
1377 }
1378 while (start_col < (int)curwin->w_cursor.col || replaced)
1379 {
1380 replace_push(NUL);
1381 if (replaced)
1382 {
1383 replace_push(replaced);
1384 replaced = NUL;
1385 }
1386 ++start_col;
1387 }
1388 }
1389
1390 // For VREPLACE mode, we also have to fix the replace stack. In this case
1391 // it is always possible because we backspace over the whole line and then
1392 // put it back again the way we wanted it.
1393 if (State & VREPLACE_FLAG)
1394 {
1395 // If orig_line didn't allocate, just return. At least we did the job,
1396 // even if you can't backspace.
1397 if (orig_line == NULL)
1398 return;
1399
1400 // Save new line
1401 new_line = vim_strsave(ml_get_curline());
1402 if (new_line == NULL)
1403 return;
1404
1405 // We only put back the new line up to the cursor
1406 new_line[curwin->w_cursor.col] = NUL;
1407
1408 // Put back original line
1409 ml_replace(curwin->w_cursor.lnum, orig_line, FALSE);
1410 curwin->w_cursor.col = orig_col;
1411
1412 // Backspace from cursor to start of line
1413 backspace_until_column(0);
1414
1415 // Insert new stuff into line again
1416 ins_bytes(new_line);
1417
1418 vim_free(new_line);
1419 }
1420}
1421
1422/*
1423 * Copy the indent from ptr to the current line (and fill to size)
1424 * Leaves the cursor on the first non-blank in the line.
1425 * Returns TRUE if the line was changed.
1426 */
1427 int
1428copy_indent(int size, char_u *src)
1429{
1430 char_u *p = NULL;
1431 char_u *line = NULL;
1432 char_u *s;
1433 int todo;
1434 int ind_len;
1435 int line_len = 0;
1436 int tab_pad;
1437 int ind_done;
1438 int round;
1439#ifdef FEAT_VARTABS
1440 int ind_col;
1441#endif
1442
1443 // Round 1: compute the number of characters needed for the indent
1444 // Round 2: copy the characters.
1445 for (round = 1; round <= 2; ++round)
1446 {
1447 todo = size;
1448 ind_len = 0;
1449 ind_done = 0;
1450#ifdef FEAT_VARTABS
1451 ind_col = 0;
1452#endif
1453 s = src;
1454
1455 // Count/copy the usable portion of the source line
1456 while (todo > 0 && VIM_ISWHITE(*s))
1457 {
1458 if (*s == TAB)
1459 {
1460#ifdef FEAT_VARTABS
1461 tab_pad = tabstop_padding(ind_done, curbuf->b_p_ts,
1462 curbuf->b_p_vts_array);
1463#else
1464 tab_pad = (int)curbuf->b_p_ts
1465 - (ind_done % (int)curbuf->b_p_ts);
1466#endif
1467 // Stop if this tab will overshoot the target
1468 if (todo < tab_pad)
1469 break;
1470 todo -= tab_pad;
1471 ind_done += tab_pad;
1472#ifdef FEAT_VARTABS
1473 ind_col += tab_pad;
1474#endif
1475 }
1476 else
1477 {
1478 --todo;
1479 ++ind_done;
1480#ifdef FEAT_VARTABS
1481 ++ind_col;
1482#endif
1483 }
1484 ++ind_len;
1485 if (p != NULL)
1486 *p++ = *s;
1487 ++s;
1488 }
1489
1490 // Fill to next tabstop with a tab, if possible
1491#ifdef FEAT_VARTABS
1492 tab_pad = tabstop_padding(ind_done, curbuf->b_p_ts,
1493 curbuf->b_p_vts_array);
1494#else
1495 tab_pad = (int)curbuf->b_p_ts - (ind_done % (int)curbuf->b_p_ts);
1496#endif
1497 if (todo >= tab_pad && !curbuf->b_p_et)
1498 {
1499 todo -= tab_pad;
1500 ++ind_len;
1501#ifdef FEAT_VARTABS
1502 ind_col += tab_pad;
1503#endif
1504 if (p != NULL)
1505 *p++ = TAB;
1506 }
1507
1508 // Add tabs required for indent
1509 if (!curbuf->b_p_et)
1510 {
1511#ifdef FEAT_VARTABS
1512 for (;;)
1513 {
1514 tab_pad = tabstop_padding(ind_col, curbuf->b_p_ts,
1515 curbuf->b_p_vts_array);
1516 if (todo < tab_pad)
1517 break;
1518 todo -= tab_pad;
1519 ++ind_len;
1520 ind_col += tab_pad;
1521 if (p != NULL)
1522 *p++ = TAB;
1523 }
1524#else
1525 while (todo >= (int)curbuf->b_p_ts)
1526 {
1527 todo -= (int)curbuf->b_p_ts;
1528 ++ind_len;
1529 if (p != NULL)
1530 *p++ = TAB;
1531 }
1532#endif
1533 }
1534
1535 // Count/add spaces required for indent
1536 while (todo > 0)
1537 {
1538 --todo;
1539 ++ind_len;
1540 if (p != NULL)
1541 *p++ = ' ';
1542 }
1543
1544 if (p == NULL)
1545 {
1546 // Allocate memory for the result: the copied indent, new indent
1547 // and the rest of the line.
1548 line_len = (int)STRLEN(ml_get_curline()) + 1;
1549 line = alloc(ind_len + line_len);
1550 if (line == NULL)
1551 return FALSE;
1552 p = line;
1553 }
1554 }
1555
1556 // Append the original line
1557 mch_memmove(p, ml_get_curline(), (size_t)line_len);
1558
1559 // Replace the line
1560 ml_replace(curwin->w_cursor.lnum, line, FALSE);
1561
1562 // Put the cursor after the indent.
1563 curwin->w_cursor.col = ind_len;
1564 return TRUE;
1565}
1566
1567/*
1568 * ":retab".
1569 */
1570 void
1571ex_retab(exarg_T *eap)
1572{
1573 linenr_T lnum;
1574 int got_tab = FALSE;
1575 long num_spaces = 0;
1576 long num_tabs;
1577 long len;
1578 long col;
1579 long vcol;
Bram Moolenaaraa2f0ee2019-12-21 18:47:26 +01001580 long start_col = 0; // For start of white-space string
1581 long start_vcol = 0; // For start of white-space string
Bram Moolenaar14c01f82019-10-09 22:53:08 +02001582 long old_len;
1583 char_u *ptr;
Bram Moolenaaraa2f0ee2019-12-21 18:47:26 +01001584 char_u *new_line = (char_u *)1; // init to non-NULL
1585 int did_undo; // called u_save for current line
Bram Moolenaar14c01f82019-10-09 22:53:08 +02001586#ifdef FEAT_VARTABS
1587 int *new_vts_array = NULL;
Bram Moolenaaraa2f0ee2019-12-21 18:47:26 +01001588 char_u *new_ts_str; // string value of tab argument
Bram Moolenaar14c01f82019-10-09 22:53:08 +02001589#else
1590 int temp;
1591 int new_ts;
1592#endif
1593 int save_list;
Bram Moolenaaraa2f0ee2019-12-21 18:47:26 +01001594 linenr_T first_line = 0; // first changed line
1595 linenr_T last_line = 0; // last changed line
Bram Moolenaar14c01f82019-10-09 22:53:08 +02001596
1597 save_list = curwin->w_p_list;
Bram Moolenaaraa2f0ee2019-12-21 18:47:26 +01001598 curwin->w_p_list = 0; // don't want list mode here
Bram Moolenaar14c01f82019-10-09 22:53:08 +02001599
1600#ifdef FEAT_VARTABS
1601 new_ts_str = eap->arg;
Bram Moolenaarb7081e12021-09-04 18:47:28 +02001602 if (tabstop_set(eap->arg, &new_vts_array) == FAIL)
Bram Moolenaar14c01f82019-10-09 22:53:08 +02001603 return;
1604 while (vim_isdigit(*(eap->arg)) || *(eap->arg) == ',')
1605 ++(eap->arg);
1606
1607 // This ensures that either new_vts_array and new_ts_str are freshly
1608 // allocated, or new_vts_array points to an existing array and new_ts_str
1609 // is null.
1610 if (new_vts_array == NULL)
1611 {
1612 new_vts_array = curbuf->b_p_vts_array;
1613 new_ts_str = NULL;
1614 }
1615 else
1616 new_ts_str = vim_strnsave(new_ts_str, eap->arg - new_ts_str);
1617#else
1618 new_ts = getdigits(&(eap->arg));
1619 if (new_ts < 0)
1620 {
1621 emsg(_(e_positive));
1622 return;
1623 }
1624 if (new_ts == 0)
1625 new_ts = curbuf->b_p_ts;
1626#endif
1627 for (lnum = eap->line1; !got_int && lnum <= eap->line2; ++lnum)
1628 {
1629 ptr = ml_get(lnum);
1630 col = 0;
1631 vcol = 0;
1632 did_undo = FALSE;
1633 for (;;)
1634 {
1635 if (VIM_ISWHITE(ptr[col]))
1636 {
1637 if (!got_tab && num_spaces == 0)
1638 {
Bram Moolenaaraa2f0ee2019-12-21 18:47:26 +01001639 // First consecutive white-space
Bram Moolenaar14c01f82019-10-09 22:53:08 +02001640 start_vcol = vcol;
1641 start_col = col;
1642 }
1643 if (ptr[col] == ' ')
1644 num_spaces++;
1645 else
1646 got_tab = TRUE;
1647 }
1648 else
1649 {
1650 if (got_tab || (eap->forceit && num_spaces > 1))
1651 {
Bram Moolenaaraa2f0ee2019-12-21 18:47:26 +01001652 // Retabulate this string of white-space
Bram Moolenaar14c01f82019-10-09 22:53:08 +02001653
Bram Moolenaaraa2f0ee2019-12-21 18:47:26 +01001654 // len is virtual length of white string
Bram Moolenaar14c01f82019-10-09 22:53:08 +02001655 len = num_spaces = vcol - start_vcol;
1656 num_tabs = 0;
1657 if (!curbuf->b_p_et)
1658 {
1659#ifdef FEAT_VARTABS
1660 int t, s;
1661
1662 tabstop_fromto(start_vcol, vcol,
1663 curbuf->b_p_ts, new_vts_array, &t, &s);
1664 num_tabs = t;
1665 num_spaces = s;
1666#else
1667 temp = new_ts - (start_vcol % new_ts);
1668 if (num_spaces >= temp)
1669 {
1670 num_spaces -= temp;
1671 num_tabs++;
1672 }
1673 num_tabs += num_spaces / new_ts;
1674 num_spaces -= (num_spaces / new_ts) * new_ts;
1675#endif
1676 }
1677 if (curbuf->b_p_et || got_tab ||
1678 (num_spaces + num_tabs < len))
1679 {
1680 if (did_undo == FALSE)
1681 {
1682 did_undo = TRUE;
1683 if (u_save((linenr_T)(lnum - 1),
1684 (linenr_T)(lnum + 1)) == FAIL)
1685 {
Bram Moolenaaraa2f0ee2019-12-21 18:47:26 +01001686 new_line = NULL; // flag out-of-memory
Bram Moolenaar14c01f82019-10-09 22:53:08 +02001687 break;
1688 }
1689 }
1690
Bram Moolenaaraa2f0ee2019-12-21 18:47:26 +01001691 // len is actual number of white characters used
Bram Moolenaar14c01f82019-10-09 22:53:08 +02001692 len = num_spaces + num_tabs;
1693 old_len = (long)STRLEN(ptr);
1694 new_line = alloc(old_len - col + start_col + len + 1);
1695 if (new_line == NULL)
1696 break;
1697 if (start_col > 0)
1698 mch_memmove(new_line, ptr, (size_t)start_col);
1699 mch_memmove(new_line + start_col + len,
1700 ptr + col, (size_t)(old_len - col + 1));
1701 ptr = new_line + start_col;
1702 for (col = 0; col < len; col++)
1703 ptr[col] = (col < num_tabs) ? '\t' : ' ';
Bram Moolenaar0dcd39b2021-02-03 19:44:25 +01001704 if (ml_replace(lnum, new_line, FALSE) == OK)
1705 // "new_line" may have been copied
1706 new_line = curbuf->b_ml.ml_line_ptr;
Bram Moolenaar14c01f82019-10-09 22:53:08 +02001707 if (first_line == 0)
1708 first_line = lnum;
1709 last_line = lnum;
1710 ptr = new_line;
1711 col = start_col + len;
1712 }
1713 }
1714 got_tab = FALSE;
1715 num_spaces = 0;
1716 }
1717 if (ptr[col] == NUL)
1718 break;
1719 vcol += chartabsize(ptr + col, (colnr_T)vcol);
1720 if (has_mbyte)
1721 col += (*mb_ptr2len)(ptr + col);
1722 else
1723 ++col;
1724 }
Bram Moolenaaraa2f0ee2019-12-21 18:47:26 +01001725 if (new_line == NULL) // out of memory
Bram Moolenaar14c01f82019-10-09 22:53:08 +02001726 break;
1727 line_breakcheck();
1728 }
1729 if (got_int)
1730 emsg(_(e_interr));
1731
1732#ifdef FEAT_VARTABS
1733 // If a single value was given then it can be considered equal to
1734 // either the value of 'tabstop' or the value of 'vartabstop'.
1735 if (tabstop_count(curbuf->b_p_vts_array) == 0
1736 && tabstop_count(new_vts_array) == 1
1737 && curbuf->b_p_ts == tabstop_first(new_vts_array))
Bram Moolenaaraa2f0ee2019-12-21 18:47:26 +01001738 ; // not changed
Bram Moolenaar14c01f82019-10-09 22:53:08 +02001739 else if (tabstop_count(curbuf->b_p_vts_array) > 0
1740 && tabstop_eq(curbuf->b_p_vts_array, new_vts_array))
Bram Moolenaaraa2f0ee2019-12-21 18:47:26 +01001741 ; // not changed
Bram Moolenaar14c01f82019-10-09 22:53:08 +02001742 else
1743 redraw_curbuf_later(NOT_VALID);
1744#else
1745 if (curbuf->b_p_ts != new_ts)
1746 redraw_curbuf_later(NOT_VALID);
1747#endif
1748 if (first_line != 0)
1749 changed_lines(first_line, 0, last_line + 1, 0L);
1750
Bram Moolenaaraa2f0ee2019-12-21 18:47:26 +01001751 curwin->w_p_list = save_list; // restore 'list'
Bram Moolenaar14c01f82019-10-09 22:53:08 +02001752
1753#ifdef FEAT_VARTABS
Bram Moolenaaraa2f0ee2019-12-21 18:47:26 +01001754 if (new_ts_str != NULL) // set the new tabstop
Bram Moolenaar14c01f82019-10-09 22:53:08 +02001755 {
1756 // If 'vartabstop' is in use or if the value given to retab has more
1757 // than one tabstop then update 'vartabstop'.
1758 int *old_vts_ary = curbuf->b_p_vts_array;
1759
1760 if (tabstop_count(old_vts_ary) > 0 || tabstop_count(new_vts_array) > 1)
1761 {
1762 set_string_option_direct((char_u *)"vts", -1, new_ts_str,
1763 OPT_FREE|OPT_LOCAL, 0);
1764 curbuf->b_p_vts_array = new_vts_array;
1765 vim_free(old_vts_ary);
1766 }
1767 else
1768 {
1769 // 'vartabstop' wasn't in use and a single value was given to
1770 // retab then update 'tabstop'.
1771 curbuf->b_p_ts = tabstop_first(new_vts_array);
1772 vim_free(new_vts_array);
1773 }
1774 vim_free(new_ts_str);
1775 }
1776#else
1777 curbuf->b_p_ts = new_ts;
1778#endif
1779 coladvance(curwin->w_curswant);
1780
1781 u_clearline();
1782}
1783
1784#if (defined(FEAT_CINDENT) && defined(FEAT_EVAL)) || defined(PROTO)
1785/*
1786 * Get indent level from 'indentexpr'.
1787 */
1788 int
1789get_expr_indent(void)
1790{
1791 int indent = -1;
1792 char_u *inde_copy;
1793 pos_T save_pos;
1794 colnr_T save_curswant;
1795 int save_set_curswant;
1796 int save_State;
1797 int use_sandbox = was_set_insecurely((char_u *)"indentexpr",
1798 OPT_LOCAL);
1799
1800 // Save and restore cursor position and curswant, in case it was changed
1801 // via :normal commands
1802 save_pos = curwin->w_cursor;
1803 save_curswant = curwin->w_curswant;
1804 save_set_curswant = curwin->w_set_curswant;
1805 set_vim_var_nr(VV_LNUM, curwin->w_cursor.lnum);
1806 if (use_sandbox)
1807 ++sandbox;
Bram Moolenaar6adb9ea2020-04-30 22:31:18 +02001808 ++textwinlock;
Bram Moolenaar14c01f82019-10-09 22:53:08 +02001809
1810 // Need to make a copy, the 'indentexpr' option could be changed while
1811 // evaluating it.
1812 inde_copy = vim_strsave(curbuf->b_p_inde);
1813 if (inde_copy != NULL)
1814 {
1815 indent = (int)eval_to_number(inde_copy);
1816 vim_free(inde_copy);
1817 }
1818
1819 if (use_sandbox)
1820 --sandbox;
Bram Moolenaar6adb9ea2020-04-30 22:31:18 +02001821 --textwinlock;
Bram Moolenaar14c01f82019-10-09 22:53:08 +02001822
1823 // Restore the cursor position so that 'indentexpr' doesn't need to.
1824 // Pretend to be in Insert mode, allow cursor past end of line for "o"
1825 // command.
1826 save_State = State;
1827 State = INSERT;
1828 curwin->w_cursor = save_pos;
1829 curwin->w_curswant = save_curswant;
1830 curwin->w_set_curswant = save_set_curswant;
1831 check_cursor();
1832 State = save_State;
1833
Bram Moolenaar620c9592021-07-31 21:32:31 +02001834 // Reset did_throw, unless 'debug' has "throw" and inside a try/catch.
1835 if (did_throw && (vim_strchr(p_debug, 't') == NULL || trylevel == 0))
1836 {
1837 handle_did_throw();
1838 did_throw = FALSE;
1839 }
1840
Bram Moolenaar14c01f82019-10-09 22:53:08 +02001841 // If there is an error, just keep the current indent.
1842 if (indent < 0)
1843 indent = get_indent();
1844
1845 return indent;
1846}
1847#endif
1848
1849#if defined(FEAT_LISP) || defined(PROTO)
1850
1851 static int
1852lisp_match(char_u *p)
1853{
1854 char_u buf[LSIZE];
1855 int len;
1856 char_u *word = *curbuf->b_p_lw != NUL ? curbuf->b_p_lw : p_lispwords;
1857
1858 while (*word != NUL)
1859 {
1860 (void)copy_option_part(&word, buf, LSIZE, ",");
1861 len = (int)STRLEN(buf);
1862 if (STRNCMP(buf, p, len) == 0 && p[len] == ' ')
1863 return TRUE;
1864 }
1865 return FALSE;
1866}
1867
1868/*
1869 * When 'p' is present in 'cpoptions, a Vi compatible method is used.
1870 * The incompatible newer method is quite a bit better at indenting
1871 * code in lisp-like languages than the traditional one; it's still
1872 * mostly heuristics however -- Dirk van Deun, dirk@rave.org
1873 *
1874 * TODO:
1875 * Findmatch() should be adapted for lisp, also to make showmatch
1876 * work correctly: now (v5.3) it seems all C/C++ oriented:
1877 * - it does not recognize the #\( and #\) notations as character literals
1878 * - it doesn't know about comments starting with a semicolon
1879 * - it incorrectly interprets '(' as a character literal
1880 * All this messes up get_lisp_indent in some rare cases.
1881 * Update from Sergey Khorev:
1882 * I tried to fix the first two issues.
1883 */
1884 int
1885get_lisp_indent(void)
1886{
1887 pos_T *pos, realpos, paren;
1888 int amount;
1889 char_u *that;
1890 colnr_T col;
1891 colnr_T firsttry;
1892 int parencount, quotecount;
1893 int vi_lisp;
1894
1895 // Set vi_lisp to use the vi-compatible method
1896 vi_lisp = (vim_strchr(p_cpo, CPO_LISP) != NULL);
1897
1898 realpos = curwin->w_cursor;
1899 curwin->w_cursor.col = 0;
1900
1901 if ((pos = findmatch(NULL, '(')) == NULL)
1902 pos = findmatch(NULL, '[');
1903 else
1904 {
1905 paren = *pos;
1906 pos = findmatch(NULL, '[');
1907 if (pos == NULL || LT_POSP(pos, &paren))
1908 pos = &paren;
1909 }
1910 if (pos != NULL)
1911 {
1912 // Extra trick: Take the indent of the first previous non-white
1913 // line that is at the same () level.
1914 amount = -1;
1915 parencount = 0;
1916
1917 while (--curwin->w_cursor.lnum >= pos->lnum)
1918 {
1919 if (linewhite(curwin->w_cursor.lnum))
1920 continue;
1921 for (that = ml_get_curline(); *that != NUL; ++that)
1922 {
1923 if (*that == ';')
1924 {
1925 while (*(that + 1) != NUL)
1926 ++that;
1927 continue;
1928 }
1929 if (*that == '\\')
1930 {
1931 if (*(that + 1) != NUL)
1932 ++that;
1933 continue;
1934 }
1935 if (*that == '"' && *(that + 1) != NUL)
1936 {
1937 while (*++that && *that != '"')
1938 {
1939 // skipping escaped characters in the string
1940 if (*that == '\\')
1941 {
1942 if (*++that == NUL)
1943 break;
1944 if (that[1] == NUL)
1945 {
1946 ++that;
1947 break;
1948 }
1949 }
1950 }
1951 }
1952 if (*that == '(' || *that == '[')
1953 ++parencount;
1954 else if (*that == ')' || *that == ']')
1955 --parencount;
1956 }
1957 if (parencount == 0)
1958 {
1959 amount = get_indent();
1960 break;
1961 }
1962 }
1963
1964 if (amount == -1)
1965 {
1966 curwin->w_cursor.lnum = pos->lnum;
1967 curwin->w_cursor.col = pos->col;
1968 col = pos->col;
1969
1970 that = ml_get_curline();
1971
1972 if (vi_lisp && get_indent() == 0)
1973 amount = 2;
1974 else
1975 {
1976 char_u *line = that;
1977
1978 amount = 0;
1979 while (*that && col)
1980 {
1981 amount += lbr_chartabsize_adv(line, &that, (colnr_T)amount);
1982 col--;
1983 }
1984
1985 // Some keywords require "body" indenting rules (the
1986 // non-standard-lisp ones are Scheme special forms):
1987 //
1988 // (let ((a 1)) instead (let ((a 1))
1989 // (...)) of (...))
1990
1991 if (!vi_lisp && (*that == '(' || *that == '[')
1992 && lisp_match(that + 1))
1993 amount += 2;
1994 else
1995 {
1996 that++;
1997 amount++;
1998 firsttry = amount;
1999
2000 while (VIM_ISWHITE(*that))
2001 {
2002 amount += lbr_chartabsize(line, that, (colnr_T)amount);
2003 ++that;
2004 }
2005
2006 if (*that && *that != ';') // not a comment line
2007 {
2008 // test *that != '(' to accommodate first let/do
2009 // argument if it is more than one line
2010 if (!vi_lisp && *that != '(' && *that != '[')
2011 firsttry++;
2012
2013 parencount = 0;
2014 quotecount = 0;
2015
2016 if (vi_lisp
2017 || (*that != '"'
2018 && *that != '\''
2019 && *that != '#'
2020 && (*that < '0' || *that > '9')))
2021 {
2022 while (*that
2023 && (!VIM_ISWHITE(*that)
2024 || quotecount
2025 || parencount)
2026 && (!((*that == '(' || *that == '[')
2027 && !quotecount
2028 && !parencount
2029 && vi_lisp)))
2030 {
2031 if (*that == '"')
2032 quotecount = !quotecount;
2033 if ((*that == '(' || *that == '[')
2034 && !quotecount)
2035 ++parencount;
2036 if ((*that == ')' || *that == ']')
2037 && !quotecount)
2038 --parencount;
2039 if (*that == '\\' && *(that+1) != NUL)
2040 amount += lbr_chartabsize_adv(
2041 line, &that, (colnr_T)amount);
2042 amount += lbr_chartabsize_adv(
2043 line, &that, (colnr_T)amount);
2044 }
2045 }
2046 while (VIM_ISWHITE(*that))
2047 {
2048 amount += lbr_chartabsize(
2049 line, that, (colnr_T)amount);
2050 that++;
2051 }
2052 if (!*that || *that == ';')
2053 amount = firsttry;
2054 }
2055 }
2056 }
2057 }
2058 }
2059 else
2060 amount = 0; // no matching '(' or '[' found, use zero indent
2061
2062 curwin->w_cursor = realpos;
2063
2064 return amount;
2065}
2066#endif // FEAT_LISP
2067
2068#if defined(FEAT_LISP) || defined(FEAT_CINDENT) || defined(PROTO)
2069/*
2070 * Re-indent the current line, based on the current contents of it and the
2071 * surrounding lines. Fixing the cursor position seems really easy -- I'm very
2072 * confused what all the part that handles Control-T is doing that I'm not.
2073 * "get_the_indent" should be get_c_indent, get_expr_indent or get_lisp_indent.
2074 */
2075
2076 void
2077fixthisline(int (*get_the_indent)(void))
2078{
2079 int amount = get_the_indent();
2080
2081 if (amount >= 0)
2082 {
2083 change_indent(INDENT_SET, amount, FALSE, 0, TRUE);
2084 if (linewhite(curwin->w_cursor.lnum))
2085 did_ai = TRUE; // delete the indent if the line stays empty
2086 }
2087}
2088
2089 void
2090fix_indent(void)
2091{
2092 if (p_paste)
2093 return;
2094# ifdef FEAT_LISP
2095 if (curbuf->b_p_lisp && curbuf->b_p_ai)
2096 fixthisline(get_lisp_indent);
2097# endif
2098# if defined(FEAT_LISP) && defined(FEAT_CINDENT)
2099 else
2100# endif
2101# ifdef FEAT_CINDENT
2102 if (cindent_on())
2103 do_c_expr_indent();
2104# endif
2105}
2106#endif
2107
2108#if defined(FEAT_EVAL) || defined(PROTO)
2109/*
2110 * "indent()" function
2111 */
2112 void
2113f_indent(typval_T *argvars, typval_T *rettv)
2114{
2115 linenr_T lnum;
2116
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02002117 if (in_vim9script() && check_for_lnum_arg(argvars, 0) == FAIL)
2118 return;
2119
Bram Moolenaar14c01f82019-10-09 22:53:08 +02002120 lnum = tv_get_lnum(argvars);
2121 if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count)
2122 rettv->vval.v_number = get_indent_lnum(lnum);
2123 else
2124 rettv->vval.v_number = -1;
2125}
2126
2127/*
2128 * "lispindent(lnum)" function
2129 */
2130 void
2131f_lispindent(typval_T *argvars UNUSED, typval_T *rettv)
2132{
2133#ifdef FEAT_LISP
2134 pos_T pos;
2135 linenr_T lnum;
2136
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02002137 if (in_vim9script() && check_for_lnum_arg(argvars, 0) == FAIL)
2138 return;
2139
Bram Moolenaar14c01f82019-10-09 22:53:08 +02002140 pos = curwin->w_cursor;
2141 lnum = tv_get_lnum(argvars);
2142 if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count)
2143 {
2144 curwin->w_cursor.lnum = lnum;
2145 rettv->vval.v_number = get_lisp_indent();
2146 curwin->w_cursor = pos;
2147 }
2148 else
2149#endif
2150 rettv->vval.v_number = -1;
2151}
2152#endif