-
Notifications
You must be signed in to change notification settings - Fork 398
/
ops.c
6077 lines (5594 loc) · 145 KB
/
ops.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/* vi:set ts=8 sts=4 sw=4:
*
* VIM - Vi IMproved by Bram Moolenaar
*
* Do ":help uganda" in Vim to read copying and usage conditions.
* Do ":help credits" in Vim to see a list of people who contributed.
* See README.txt for an overview of the Vim source code.
*/
/*
* ops.c: implementation of various operators: op_shift, op_delete, op_tilde,
* op_change, op_yank, do_put, do_join
*/
#include "vim.h"
/*
* Number of registers.
* 0 = unnamed register, for normal yanks and puts
* 1..9 = registers '1' to '9', for deletes
* 10..35 = registers 'a' to 'z'
* 36 = delete register '-'
* 37 = Selection register '*'. Only if FEAT_CLIPBOARD defined
* 38 = Clipboard register '+'. Only if FEAT_CLIPBOARD and FEAT_X11 defined
*/
/*
* Symbolic names for some registers.
*/
#define DELETION_REGISTER 36
#ifdef FEAT_CLIPBOARD
# define STAR_REGISTER 37
# ifdef FEAT_X11
# define PLUS_REGISTER 38
# else
# define PLUS_REGISTER STAR_REGISTER /* there is only one */
# endif
#endif
#ifdef FEAT_DND
# define TILDE_REGISTER (PLUS_REGISTER + 1)
#endif
#ifdef FEAT_CLIPBOARD
# ifdef FEAT_DND
# define NUM_REGISTERS (TILDE_REGISTER + 1)
# else
# define NUM_REGISTERS (PLUS_REGISTER + 1)
# endif
#else
# define NUM_REGISTERS 37
#endif
/*
* Each yank register is an array of pointers to lines.
*/
static struct yankreg
{
char_u **y_array; /* pointer to array of line pointers */
linenr_T y_size; /* number of lines in y_array */
char_u y_type; /* MLINE, MCHAR or MBLOCK */
#ifdef FEAT_VISUAL
colnr_T y_width; /* only set if y_type == MBLOCK */
#endif
} y_regs[NUM_REGISTERS];
static struct yankreg *y_current; /* ptr to current yankreg */
static int y_append; /* TRUE when appending */
static struct yankreg *y_previous = NULL; /* ptr to last written yankreg */
/*
* structure used by block_prep, op_delete and op_yank for blockwise operators
* also op_change, op_shift, op_insert, op_replace - AKelly
*/
struct block_def
{
int startspaces; /* 'extra' cols of first char */
int endspaces; /* 'extra' cols of first char */
int textlen; /* chars in block */
char_u *textstart; /* pointer to 1st char in block */
colnr_T textcol; /* cols of chars (at least part.) in block */
colnr_T start_vcol; /* start col of 1st char wholly inside block */
colnr_T end_vcol; /* start col of 1st char wholly after block */
#ifdef FEAT_VISUALEXTRA
int is_short; /* TRUE if line is too short to fit in block */
int is_MAX; /* TRUE if curswant==MAXCOL when starting */
int is_oneChar; /* TRUE if block within one character */
int pre_whitesp; /* screen cols of ws before block */
int pre_whitesp_c; /* chars of ws before block */
colnr_T end_char_vcols; /* number of vcols of post-block char */
#endif
colnr_T start_char_vcols; /* number of vcols of pre-block char */
};
#ifdef FEAT_VISUALEXTRA
static void shift_block __ARGS((oparg_T *oap, int amount));
static void block_insert __ARGS((oparg_T *oap, char_u *s, int b_insert, struct block_def*bdp));
#endif
static int stuff_yank __ARGS((int, char_u *));
static void put_reedit_in_typebuf __ARGS((void));
static int put_in_typebuf __ARGS((char_u *s, int colon));
static void stuffescaped __ARGS((char_u *arg, int literally));
#ifdef FEAT_MBYTE
static void mb_adjust_opend __ARGS((oparg_T *oap));
#endif
static void free_yank __ARGS((long));
static void free_yank_all __ARGS((void));
static int yank_copy_line __ARGS((struct block_def *bd, long y_idx));
#ifdef FEAT_CLIPBOARD
static void copy_yank_reg __ARGS((struct yankreg *reg));
# if defined(FEAT_VISUAL) || defined(FEAT_EVAL)
static void may_set_selection __ARGS((void));
# endif
#endif
static void dis_msg __ARGS((char_u *p, int skip_esc));
#ifdef FEAT_VISUAL
static void block_prep __ARGS((oparg_T *oap, struct block_def *, linenr_T, int));
#endif
#if defined(FEAT_CLIPBOARD) || defined(FEAT_EVAL)
static void str_to_reg __ARGS((struct yankreg *y_ptr, int type, char_u *str, long len, long blocklen));
#endif
static int ends_in_white __ARGS((linenr_T lnum));
#ifdef FEAT_COMMENTS
static int same_leader __ARGS((linenr_T lnum, int, char_u *, int, char_u *));
static int fmt_check_par __ARGS((linenr_T, int *, char_u **, int do_comments));
#else
static int fmt_check_par __ARGS((linenr_T));
#endif
/*
* The names of operators.
* IMPORTANT: Index must correspond with defines in vim.h!!!
* The third field indicates whether the operator always works on lines.
*/
static char opchars[][3] =
{
{NUL, NUL, FALSE}, /* OP_NOP */
{'d', NUL, FALSE}, /* OP_DELETE */
{'y', NUL, FALSE}, /* OP_YANK */
{'c', NUL, FALSE}, /* OP_CHANGE */
{'<', NUL, TRUE}, /* OP_LSHIFT */
{'>', NUL, TRUE}, /* OP_RSHIFT */
{'!', NUL, TRUE}, /* OP_FILTER */
{'g', '~', FALSE}, /* OP_TILDE */
{'=', NUL, TRUE}, /* OP_INDENT */
{'g', 'q', TRUE}, /* OP_FORMAT */
{':', NUL, TRUE}, /* OP_COLON */
{'g', 'U', FALSE}, /* OP_UPPER */
{'g', 'u', FALSE}, /* OP_LOWER */
{'J', NUL, TRUE}, /* DO_JOIN */
{'g', 'J', TRUE}, /* DO_JOIN_NS */
{'g', '?', FALSE}, /* OP_ROT13 */
{'r', NUL, FALSE}, /* OP_REPLACE */
{'I', NUL, FALSE}, /* OP_INSERT */
{'A', NUL, FALSE}, /* OP_APPEND */
{'z', 'f', TRUE}, /* OP_FOLD */
{'z', 'o', TRUE}, /* OP_FOLDOPEN */
{'z', 'O', TRUE}, /* OP_FOLDOPENREC */
{'z', 'c', TRUE}, /* OP_FOLDCLOSE */
{'z', 'C', TRUE}, /* OP_FOLDCLOSEREC */
{'z', 'd', TRUE}, /* OP_FOLDDEL */
{'z', 'D', TRUE}, /* OP_FOLDDELREC */
{'g', 'w', TRUE}, /* OP_FORMAT2 */
};
/*
* Translate a command name into an operator type.
* Must only be called with a valid operator name!
*/
int
get_op_type(char1, char2)
int char1;
int char2;
{
int i;
if (char1 == 'r') /* ignore second character */
return OP_REPLACE;
if (char1 == '~') /* when tilde is an operator */
return OP_TILDE;
for (i = 0; ; ++i)
if (opchars[i][0] == char1 && opchars[i][1] == char2)
break;
return i;
}
#if defined(FEAT_VISUAL) || defined(PROTO)
/*
* Return TRUE if operator "op" always works on whole lines.
*/
int
op_on_lines(op)
int op;
{
return opchars[op][2];
}
#endif
/*
* Get first operator command character.
* Returns 'g' or 'z' if there is another command character.
*/
int
get_op_char(optype)
int optype;
{
return opchars[optype][0];
}
/*
* Get second operator command character.
*/
int
get_extra_op_char(optype)
int optype;
{
return opchars[optype][1];
}
/*
* op_shift - handle a shift operation
*/
void
op_shift(oap, curs_top, amount)
oparg_T *oap;
int curs_top;
int amount;
{
long i;
int first_char;
char_u *s;
#ifdef FEAT_VISUAL
int block_col = 0;
#endif
if (u_save((linenr_T)(oap->start.lnum - 1),
(linenr_T)(oap->end.lnum + 1)) == FAIL)
return;
#ifdef FEAT_VISUAL
if (oap->block_mode)
block_col = curwin->w_cursor.col;
#endif
for (i = oap->line_count; --i >= 0; )
{
first_char = *ml_get_curline();
if (first_char == NUL) /* empty line */
curwin->w_cursor.col = 0;
#ifdef FEAT_VISUALEXTRA
else if (oap->block_mode)
shift_block(oap, amount);
#endif
else
/* Move the line right if it doesn't start with '#', 'smartindent'
* isn't set or 'cindent' isn't set or '#' isn't in 'cino'. */
#if defined(FEAT_SMARTINDENT) || defined(FEAT_CINDENT)
if (first_char != '#' || !preprocs_left())
#endif
{
shift_line(oap->op_type == OP_LSHIFT, p_sr, amount);
}
++curwin->w_cursor.lnum;
}
changed_lines(oap->start.lnum, 0, oap->end.lnum + 1, 0L);
#ifdef FEAT_VISUAL
if (oap->block_mode)
{
curwin->w_cursor.lnum = oap->start.lnum;
curwin->w_cursor.col = block_col;
}
else
#endif
if (curs_top) /* put cursor on first line, for ">>" */
{
curwin->w_cursor.lnum = oap->start.lnum;
beginline(BL_SOL | BL_FIX); /* shift_line() may have set cursor.col */
}
else
--curwin->w_cursor.lnum; /* put cursor on last line, for ":>" */
if (oap->line_count > p_report)
{
if (oap->op_type == OP_RSHIFT)
s = (char_u *)">";
else
s = (char_u *)"<";
if (oap->line_count == 1)
{
if (amount == 1)
sprintf((char *)IObuff, _("1 line %sed 1 time"), s);
else
sprintf((char *)IObuff, _("1 line %sed %d times"), s, amount);
}
else
{
if (amount == 1)
sprintf((char *)IObuff, _("%ld lines %sed 1 time"),
oap->line_count, s);
else
sprintf((char *)IObuff, _("%ld lines %sed %d times"),
oap->line_count, s, amount);
}
msg(IObuff);
}
/*
* Set "'[" and "']" marks.
*/
curbuf->b_op_start = oap->start;
curbuf->b_op_end.lnum = oap->end.lnum;
curbuf->b_op_end.col = (colnr_T)STRLEN(ml_get(oap->end.lnum));
if (curbuf->b_op_end.col > 0)
--curbuf->b_op_end.col;
}
/*
* shift the current line one shiftwidth left (if left != 0) or right
* leaves cursor on first blank in the line
*/
void
shift_line(left, round, amount)
int left;
int round;
int amount;
{
int count;
int i, j;
int p_sw = (int)curbuf->b_p_sw;
count = get_indent(); /* get current indent */
if (round) /* round off indent */
{
i = count / p_sw; /* number of p_sw rounded down */
j = count % p_sw; /* extra spaces */
if (j && left) /* first remove extra spaces */
--amount;
if (left)
{
i -= amount;
if (i < 0)
i = 0;
}
else
i += amount;
count = i * p_sw;
}
else /* original vi indent */
{
if (left)
{
count -= p_sw * amount;
if (count < 0)
count = 0;
}
else
count += p_sw * amount;
}
/* Set new indent */
#ifdef FEAT_VREPLACE
if (State & VREPLACE_FLAG)
change_indent(INDENT_SET, count, FALSE, NUL);
else
#endif
(void)set_indent(count, SIN_CHANGED);
}
#if defined(FEAT_VISUALEXTRA) || defined(PROTO)
/*
* Shift one line of the current block one shiftwidth right or left.
* Leaves cursor on first character in block.
*/
static void
shift_block(oap, amount)
oparg_T *oap;
int amount;
{
int left = (oap->op_type == OP_LSHIFT);
int oldstate = State;
int total, split;
char_u *newp, *oldp, *midp, *ptr;
int oldcol = curwin->w_cursor.col;
int p_sw = (int)curbuf->b_p_sw;
int p_ts = (int)curbuf->b_p_ts;
struct block_def bd;
int internal = 0;
int incr;
colnr_T vcol, col = 0, ws_vcol;
int i = 0, j = 0;
int len;
#ifdef FEAT_RIGHTLEFT
int old_p_ri = p_ri;
p_ri = 0; /* don't want revins in ident */
#endif
State = INSERT; /* don't want REPLACE for State */
block_prep(oap, &bd, curwin->w_cursor.lnum, TRUE);
if (bd.is_short)
return;
/* total is number of screen columns to be inserted/removed */
total = amount * p_sw;
oldp = ml_get_curline();
if (!left)
{
/*
* 1. Get start vcol
* 2. Total ws vcols
* 3. Divvy into TABs & spp
* 4. Construct new string
*/
total += bd.pre_whitesp; /* all virtual WS upto & incl a split TAB */
ws_vcol = bd.start_vcol - bd.pre_whitesp;
if (bd.startspaces)
{
#ifdef FEAT_MBYTE
if (has_mbyte)
bd.textstart += (*mb_ptr2len_check)(bd.textstart);
#endif
++bd.textstart;
}
for ( ; vim_iswhite(*bd.textstart); )
{
incr = lbr_chartabsize_adv(&bd.textstart, (colnr_T)(bd.start_vcol));
total += incr;
bd.start_vcol += incr;
}
/* OK, now total=all the VWS reqd, and textstart points at the 1st
* non-ws char in the block. */
if (!curbuf->b_p_et)
i = ((ws_vcol % p_ts) + total) / p_ts; /* number of tabs */
if (i)
j = ((ws_vcol % p_ts) + total) % p_ts; /* number of spp */
else
j = total;
/* if we're splitting a TAB, allow for it */
bd.textcol -= bd.pre_whitesp_c - (bd.startspaces != 0);
len = (int)STRLEN(bd.textstart) + 1;
newp = alloc_check((unsigned)(bd.textcol + i + j + len));
if (newp == NULL)
return;
vim_memset(newp, NUL, (size_t)(bd.textcol + i + j + len));
mch_memmove(newp, oldp, (size_t)bd.textcol);
copy_chars(newp + bd.textcol, (size_t)i, TAB);
copy_spaces(newp + bd.textcol + i, (size_t)j);
/* the end */
mch_memmove(newp + bd.textcol + i + j, bd.textstart, (size_t)len);
}
else /* left */
{
vcol = oap->start_vcol;
/* walk vcol past ws to be removed */
for (midp = oldp + bd.textcol;
vcol < (oap->start_vcol + total) && vim_iswhite(*midp); )
{
incr = lbr_chartabsize_adv(&midp, (colnr_T)vcol);
vcol += incr;
}
/* internal is the block-internal ws replacing a split TAB */
if (vcol > (oap->start_vcol + total))
{
/* we have to split the TAB *(midp-1) */
internal = vcol - (oap->start_vcol + total);
}
/* if 'expandtab' is not set, use TABs */
split = bd.startspaces + internal;
if (split > 0)
{
if (!curbuf->b_p_et)
{
for (ptr = oldp, col = 0; ptr < oldp+bd.textcol; )
col += lbr_chartabsize_adv(&ptr, (colnr_T)col);
/* col+1 now equals the start col of the first char of the
* block (may be < oap.start_vcol if we're splitting a TAB) */
i = ((col % p_ts) + split) / p_ts; /* number of tabs */
}
if (i)
j = ((col % p_ts) + split) % p_ts; /* number of spp */
else
j = split;
}
newp = alloc_check(bd.textcol + i + j + (unsigned)STRLEN(midp) + 1);
if (newp == NULL)
return;
vim_memset(newp, NUL, (size_t)(bd.textcol + i + j + STRLEN(midp) + 1));
/* copy first part we want to keep */
mch_memmove(newp, oldp, (size_t)bd.textcol);
/* Now copy any TABS and spp to ensure correct alignment! */
while (vim_iswhite(*midp))
{
if (*midp == TAB)
i++;
else /*space */
j++;
midp++;
}
/* We might have an extra TAB worth of spp now! */
if (j / p_ts && !curbuf->b_p_et)
{
i++;
j -= p_ts;
}
copy_chars(newp + bd.textcol, (size_t)i, TAB);
copy_spaces(newp + bd.textcol + i, (size_t)j);
/* the end */
mch_memmove(newp + STRLEN(newp), midp, (size_t)STRLEN(midp) + 1);
}
/* replace the line */
ml_replace(curwin->w_cursor.lnum, newp, FALSE);
changed_bytes(curwin->w_cursor.lnum, (colnr_T)bd.textcol);
State = oldstate;
curwin->w_cursor.col = oldcol;
#ifdef FEAT_RIGHTLEFT
p_ri = old_p_ri;
#endif
}
#endif
#ifdef FEAT_VISUALEXTRA
/*
* Insert string "s" (b_insert ? before : after) block :AKelly
* Caller must prepare for undo.
*/
static void
block_insert(oap, s, b_insert, bdp)
oparg_T *oap;
char_u *s;
int b_insert;
struct block_def *bdp;
{
int p_ts;
int count = 0; /* extra spaces to replace a cut TAB */
int spaces = 0; /* non-zero if cutting a TAB */
colnr_T offset; /* pointer along new line */
unsigned s_len; /* STRLEN(s) */
char_u *newp, *oldp; /* new, old lines */
linenr_T lnum; /* loop var */
int oldstate = State;
State = INSERT; /* don't want REPLACE for State */
s_len = (unsigned)STRLEN(s);
for (lnum = oap->start.lnum + 1; lnum <= oap->end.lnum; lnum++)
{
block_prep(oap, bdp, lnum, TRUE);
if (bdp->is_short && b_insert)
continue; /* OP_INSERT, line ends before block start */
oldp = ml_get(lnum);
if (b_insert)
{
p_ts = bdp->start_char_vcols;
spaces = bdp->startspaces;
if (spaces != 0)
count = p_ts - 1; /* we're cutting a TAB */
offset = bdp->textcol;
}
else /* append */
{
p_ts = bdp->end_char_vcols;
if (!bdp->is_short) /* spaces = padding after block */
{
spaces = (bdp->endspaces ? p_ts - bdp->endspaces : 0);
if (spaces != 0)
count = p_ts - 1; /* we're cutting a TAB */
offset = bdp->textcol + bdp->textlen - (spaces != 0);
}
else /* spaces = padding to block edge */
{
/* if $ used, just append to EOL (ie spaces==0) */
if (!bdp->is_MAX)
spaces = (oap->end_vcol - bdp->end_vcol) + 1;
count = spaces;
offset = bdp->textcol + bdp->textlen;
}
}
newp = alloc_check((unsigned)(STRLEN(oldp)) + s_len + count + 1);
if (newp == NULL)
continue;
/* copy up to shifted part */
mch_memmove(newp, oldp, (size_t)(offset));
oldp += offset;
/* insert pre-padding */
copy_spaces(newp + offset, (size_t)spaces);
/* copy the new text */
mch_memmove(newp + offset + spaces, s, (size_t)s_len);
offset += s_len;
if (spaces && !bdp->is_short)
{
/* insert post-padding */
copy_spaces(newp + offset + spaces, (size_t)(p_ts - spaces));
/* We're splitting a TAB, don't copy it. */
oldp++;
/* We allowed for that TAB, remember this now */
count++;
}
if (spaces > 0)
offset += count;
mch_memmove(newp + offset, oldp, (size_t)(STRLEN(oldp) + 1));
ml_replace(lnum, newp, FALSE);
if (lnum == oap->end.lnum)
{
/* Set "']" mark to the end of the block instead of the end of
* the insert in the first line. */
curbuf->b_op_end.lnum = oap->end.lnum;
curbuf->b_op_end.col = offset;
}
} /* for all lnum */
changed_lines(oap->start.lnum + 1, 0, oap->end.lnum + 1, 0L);
State = oldstate;
}
#endif
#if defined(FEAT_LISP) || defined(FEAT_CINDENT) || defined(PROTO)
/*
* op_reindent - handle reindenting a block of lines.
*/
void
op_reindent(oap, how)
oparg_T *oap;
int (*how) __ARGS((void));
{
long i;
char_u *l;
int count;
linenr_T first_changed = 0;
linenr_T last_changed = 0;
linenr_T start_lnum = curwin->w_cursor.lnum;
for (i = oap->line_count; --i >= 0 && !got_int; )
{
/* it's a slow thing to do, so give feedback so there's no worry that
* the computer's just hung. */
if (i > 1
&& (i % 50 == 0 || i == oap->line_count - 1)
&& oap->line_count > p_report)
smsg((char_u *)_("%ld lines to indent... "), i);
/*
* Be vi-compatible: For lisp indenting the first line is not
* indented, unless there is only one line.
*/
#ifdef FEAT_LISP
if (i != oap->line_count - 1 || oap->line_count == 1
|| how != get_lisp_indent)
#endif
{
l = skipwhite(ml_get_curline());
if (*l == NUL) /* empty or blank line */
count = 0;
else
count = how(); /* get the indent for this line */
if (set_indent(count, SIN_UNDO))
{
/* did change the indent, call changed_lines() later */
if (first_changed == 0)
first_changed = curwin->w_cursor.lnum;
last_changed = curwin->w_cursor.lnum;
}
}
++curwin->w_cursor.lnum;
}
/* put cursor on first non-blank of indented line */
curwin->w_cursor.lnum = start_lnum;
beginline(BL_SOL | BL_FIX);
/* Mark changed lines so that they will be redrawn. When Visual
* highlighting was present, need to continue until the last line. When
* there is no change still need to remove the Visual highlighting. */
if (last_changed != 0)
changed_lines(first_changed, 0,
#ifdef FEAT_VISUAL
oap->is_VIsual ? start_lnum + oap->line_count :
#endif
last_changed + 1, 0L);
#ifdef FEAT_VISUAL
else if (oap->is_VIsual)
redraw_curbuf_later(INVERTED);
#endif
if (oap->line_count > p_report)
{
i = oap->line_count - (i + 1);
if (i == 1)
MSG(_("1 line indented "));
else
smsg((char_u *)_("%ld lines indented "), i);
}
/* set '[ and '] marks */
curbuf->b_op_start = oap->start;
curbuf->b_op_end = oap->end;
}
#endif /* defined(FEAT_LISP) || defined(FEAT_CINDENT) */
#if defined(FEAT_EVAL) || defined(PROTO)
/*
* Keep the last expression line here, for repeating.
*/
static char_u *expr_line = NULL;
/*
* Get an expression for the "\"=expr1" or "CTRL-R =expr1"
* Returns '=' when OK, NUL otherwise.
*/
int
get_expr_register()
{
char_u *new_line;
new_line = getcmdline('=', 0L, 0);
if (new_line == NULL)
return NUL;
if (*new_line == NUL) /* use previous line */
vim_free(new_line);
else
set_expr_line(new_line);
return '=';
}
/*
* Set the expression for the '=' register.
* Argument must be an allocated string.
*/
void
set_expr_line(new_line)
char_u *new_line;
{
vim_free(expr_line);
expr_line = new_line;
}
/*
* Get the result of the '=' register expression.
* Returns a pointer to allocated memory, or NULL for failure.
*/
char_u *
get_expr_line()
{
char_u *expr_copy;
char_u *rv;
if (expr_line == NULL)
return NULL;
/* Make a copy of the expression, because evaluating it may cause it to be
* changed. */
expr_copy = vim_strsave(expr_line);
if (expr_copy == NULL)
return NULL;
rv = eval_to_string(expr_copy, NULL);
vim_free(expr_copy);
return rv;
}
#endif /* FEAT_EVAL */
/*
* Check if 'regname' is a valid name of a yank register.
* Note: There is no check for 0 (default register), caller should do this
*/
int
valid_yank_reg(regname, writing)
int regname;
int writing; /* if TRUE check for writable registers */
{
if ( (regname > 0 && ASCII_ISALNUM(regname))
|| (!writing && vim_strchr((char_u *)
#ifdef FEAT_EVAL
"/.%#:="
#else
"/.%#:"
#endif
, regname) != NULL)
|| regname == '"'
|| regname == '-'
|| regname == '_'
#ifdef FEAT_CLIPBOARD
|| regname == '*'
|| regname == '+'
#endif
#ifdef FEAT_DND
|| (!writing && regname == '~')
#endif
)
return TRUE;
return FALSE;
}
/*
* Set y_current and y_append, according to the value of "regname".
* Cannot handle the '_' register.
*
* If regname is 0 and writing, use register 0
* If regname is 0 and reading, use previous register
*/
void
get_yank_register(regname, writing)
int regname;
int writing;
{
int i;
y_append = FALSE;
if ((regname == 0 || regname == '"') && !writing && y_previous != NULL)
{
y_current = y_previous;
return;
}
i = regname;
if (VIM_ISDIGIT(i))
i -= '0';
else if (ASCII_ISLOWER(i))
i = CharOrdLow(i) + 10;
else if (ASCII_ISUPPER(i))
{
i = CharOrdUp(i) + 10;
y_append = TRUE;
}
else if (regname == '-')
i = DELETION_REGISTER;
#ifdef FEAT_CLIPBOARD
/* When selection is not available, use register 0 instead of '*' */
else if (clip_star.available && regname == '*')
i = STAR_REGISTER;
/* When clipboard is not available, use register 0 instead of '+' */
else if (clip_plus.available && regname == '+')
i = PLUS_REGISTER;
#endif
#ifdef FEAT_DND
else if (!writing && regname == '~')
i = TILDE_REGISTER;
#endif
else /* not 0-9, a-z, A-Z or '-': use register 0 */
i = 0;
y_current = &(y_regs[i]);
if (writing) /* remember the register we write into for do_put() */
y_previous = y_current;
}
#if defined(FEAT_CLIPBOARD) || defined(PROTO)
/*
* When "regname" is a clipboard register, obtain the selection. If it's not
* available return zero, otherwise return "regname".
*/
int
may_get_selection(regname)
int regname;
{
if (regname == '*')
{
if (!clip_star.available)
regname = 0;
else
clip_get_selection(&clip_star);
}
else if (regname == '+')
{
if (!clip_plus.available)
regname = 0;
else
clip_get_selection(&clip_plus);
}
return regname;
}
#endif
#if defined(FEAT_VISUAL) || defined(PROTO)
/*
* Obtain the contents of a "normal" register. The register is made empty.
* The returned pointer has allocated memory, use put_register() later.
*/
void *
get_register(name, copy)
int name;
int copy; /* make a copy, if FALSE make register empty. */
{
static struct yankreg *reg;
int i;
#ifdef FEAT_CLIPBOARD
/* When Visual area changed, may have to update selection. Obtain the
* selection too. */
if (name == '*' && clip_star.available && clip_isautosel())
{
clip_update_selection();
may_get_selection(name);
}
#endif
get_yank_register(name, 0);
reg = (struct yankreg *)alloc((unsigned)sizeof(struct yankreg));
if (reg != NULL)
{
*reg = *y_current;
if (copy)
{
/* If we run out of memory some or all of the lines are empty. */
if (reg->y_size == 0)
reg->y_array = NULL;
else
reg->y_array = (char_u **)alloc((unsigned)(sizeof(char_u *)
* reg->y_size));
if (reg->y_array != NULL)
{
for (i = 0; i < reg->y_size; ++i)
reg->y_array[i] = vim_strsave(y_current->y_array[i]);
}
}
else
y_current->y_array = NULL;
}
return (void *)reg;
}
/*
* Put "reg" into register "name". Free any previous contents.
*/
void
put_register(name, reg)
int name;
void *reg;
{
get_yank_register(name, 0);
free_yank_all();
*y_current = *(struct yankreg *)reg;
# ifdef FEAT_CLIPBOARD
/* Send text written to clipboard register to the clipboard. */
may_set_selection();
# endif
}
#endif
#if defined(FEAT_MOUSE) || defined(PROTO)
/*
* return TRUE if the current yank register has type MLINE
*/
int
yank_register_mline(regname)
int regname;
{
if (regname != 0 && !valid_yank_reg(regname, FALSE))
return FALSE;
if (regname == '_') /* black hole is always empty */
return FALSE;
get_yank_register(regname, FALSE);
return (y_current->y_type == MLINE);
}
#endif
/*
* start or stop recording into a yank register
*
* return FAIL for failure, OK otherwise
*/
int
do_record(c)
int c;
{
char_u *p;
static int regname;
struct yankreg *old_y_previous, *old_y_current;
int retval;
if (Recording == FALSE) /* start recording */
{
/* registers 0-9, a-z and " are allowed */
if (c < 0 || (!ASCII_ISALNUM(c) && c != '"'))
retval = FAIL;
else
{
Recording = TRUE;
showmode();
regname = c;
retval = OK;
}