4 #include "text-motions.h"
5 #include "text-objects.h"
9 static size_t op_delete(Vis
*vis
, Text
*txt
, OperatorContext
*c
) {
10 c
->reg
->linewise
= c
->linewise
;
11 register_put_range(vis
, c
->reg
, txt
, &c
->range
);
12 text_delete_range(txt
, &c
->range
);
13 size_t pos
= c
->range
.start
;
14 if (c
->linewise
&& pos
== text_size(txt
))
15 pos
= text_line_begin(txt
, text_line_prev(txt
, pos
));
19 static size_t op_change(Vis
*vis
, Text
*txt
, OperatorContext
*c
) {
20 op_delete(vis
, txt
, c
);
21 return c
->range
.start
;
24 static size_t op_yank(Vis
*vis
, Text
*txt
, OperatorContext
*c
) {
25 c
->reg
->linewise
= c
->linewise
;
26 register_put_range(vis
, c
->reg
, txt
, &c
->range
);
27 if (c
->reg
== &vis
->registers
[VIS_REG_DEFAULT
]) {
28 vis
->registers
[VIS_REG_ZERO
].linewise
= c
->reg
->linewise
;
29 register_put_range(vis
, &vis
->registers
[VIS_REG_ZERO
], txt
, &c
->range
);
31 return c
->linewise
? c
->pos
: c
->range
.start
;
34 static size_t op_put(Vis
*vis
, Text
*txt
, OperatorContext
*c
) {
37 bool sel
= text_range_size(&c
->range
) > 0;
38 bool sel_linewise
= sel
&& text_range_is_linewise(txt
, &c
->range
);
40 text_delete_range(txt
, &c
->range
);
41 pos
= c
->pos
= c
->range
.start
;
44 case VIS_OP_PUT_AFTER
:
45 case VIS_OP_PUT_AFTER_END
:
46 if (c
->reg
->linewise
&& !sel_linewise
)
47 pos
= text_line_next(txt
, pos
);
48 else if (!sel
&& text_byte_get(txt
, pos
, &b
) && b
!= '\r' && b
!= '\n')
49 pos
= text_char_next(txt
, pos
);
51 case VIS_OP_PUT_BEFORE
:
52 case VIS_OP_PUT_BEFORE_END
:
54 pos
= text_line_begin(txt
, pos
);
59 const char *data
= register_get(vis
, c
->reg
, &len
);
61 for (int i
= 0; i
< c
->count
; i
++) {
63 if (c
->reg
->linewise
&& pos
> 0 && text_byte_get(txt
, pos
-1, &nl
) && nl
!= '\n')
64 pos
+= text_insert_newline(txt
, pos
);
65 text_insert(txt
, pos
, data
, len
);
67 if (c
->reg
->linewise
&& pos
> 0 && text_byte_get(txt
, pos
-1, &nl
) && nl
!= '\n')
68 pos
+= text_insert_newline(txt
, pos
);
71 if (c
->reg
->linewise
) {
73 case VIS_OP_PUT_BEFORE_END
:
74 case VIS_OP_PUT_AFTER_END
:
75 pos
= text_line_start(txt
, pos
);
77 case VIS_OP_PUT_AFTER
:
78 pos
= text_line_start(txt
, text_line_next(txt
, c
->pos
));
80 case VIS_OP_PUT_BEFORE
:
81 pos
= text_line_start(txt
, c
->pos
);
86 case VIS_OP_PUT_AFTER
:
87 case VIS_OP_PUT_BEFORE
:
88 pos
= text_char_prev(txt
, pos
);
96 static size_t op_shift_right(Vis
*vis
, Text
*txt
, OperatorContext
*c
) {
98 spaces
[MIN(vis
->tabwidth
, LENGTH(spaces
) - 1)] = '\0';
99 const char *tab
= vis
->expandtab
? spaces
: "\t";
100 size_t tablen
= strlen(tab
);
101 size_t pos
= text_line_begin(txt
, c
->range
.end
), prev_pos
;
104 /* if range ends at the begin of a line, skip line break */
105 if (pos
== c
->range
.end
)
106 pos
= text_line_prev(txt
, pos
);
109 prev_pos
= pos
= text_line_begin(txt
, pos
);
110 text_insert(txt
, pos
, tab
, tablen
);
111 pos
= text_line_prev(txt
, pos
);
113 } while (pos
>= c
->range
.start
&& pos
!= prev_pos
);
115 return c
->pos
+ inserted
;
118 static size_t op_shift_left(Vis
*vis
, Text
*txt
, OperatorContext
*c
) {
119 size_t pos
= text_line_begin(txt
, c
->range
.end
), prev_pos
;
120 size_t tabwidth
= vis
->tabwidth
, tablen
;
123 /* if range ends at the begin of a line, skip line break */
124 if (pos
== c
->range
.end
)
125 pos
= text_line_prev(txt
, pos
);
130 prev_pos
= pos
= text_line_begin(txt
, pos
);
131 Iterator it
= text_iterator_get(txt
, pos
);
132 if (text_iterator_byte_get(&it
, &c
) && c
== '\t') {
135 for (len
= 0; text_iterator_byte_get(&it
, &c
) && c
== ' '; len
++)
136 text_iterator_byte_next(&it
, NULL
);
138 tablen
= MIN(len
, tabwidth
);
139 text_delete(txt
, pos
, tablen
);
140 pos
= text_line_prev(txt
, pos
);
142 } while (pos
>= c
->range
.start
&& pos
!= prev_pos
);
144 return c
->pos
- deleted
;
147 static size_t op_case_change(Vis
*vis
, Text
*txt
, OperatorContext
*c
) {
148 size_t len
= text_range_size(&c
->range
);
149 char *buf
= malloc(len
);
152 len
= text_bytes_get(txt
, c
->range
.start
, len
, buf
);
154 for (char *cur
= buf
; rem
> 0; cur
++, rem
--) {
155 int ch
= (unsigned char)*cur
;
157 if (c
->arg
->i
== VIS_OP_CASE_SWAP
)
158 *cur
= islower(ch
) ? toupper(ch
) : tolower(ch
);
159 else if (c
->arg
->i
== VIS_OP_CASE_UPPER
)
166 text_delete(txt
, c
->range
.start
, len
);
167 text_insert(txt
, c
->range
.start
, buf
, len
);
172 static size_t op_cursor(Vis
*vis
, Text
*txt
, OperatorContext
*c
) {
173 View
*view
= vis
->win
->view
;
174 Filerange r
= text_range_linewise(txt
, &c
->range
);
175 for (size_t line
= text_range_line_first(txt
, &r
); line
!= EPOS
; line
= text_range_line_next(txt
, &r
, line
)) {
177 if (c
->arg
->i
== VIS_OP_CURSOR_EOL
)
178 pos
= text_line_finish(txt
, line
);
180 pos
= text_line_start(txt
, line
);
181 view_cursors_new_force(view
, pos
);
186 static size_t op_join(Vis
*vis
, Text
*txt
, OperatorContext
*c
) {
187 size_t pos
= text_line_begin(txt
, c
->range
.end
), prev_pos
;
190 /* if operator and range are both linewise, skip last line break */
191 if (c
->linewise
&& text_range_is_linewise(txt
, &c
->range
)) {
192 size_t line_prev
= text_line_prev(txt
, pos
);
193 size_t line_prev_prev
= text_line_prev(txt
, line_prev
);
194 if (line_prev_prev
>= c
->range
.start
)
198 size_t len
= c
->arg
->s
? strlen(c
->arg
->s
) : 0;
202 size_t end
= text_line_start(txt
, pos
);
203 pos
= text_line_prev(txt
, end
);
204 if (pos
< c
->range
.start
|| end
<= pos
)
206 text_delete(txt
, pos
, end
- pos
);
208 if (text_byte_get(txt
, pos
-1, &prev
) && !isspace((unsigned char)prev
) &&
209 text_byte_get(txt
, pos
, &next
) && next
!= '\r' && next
!= '\n')
210 text_insert(txt
, pos
, c
->arg
->s
, len
);
212 mark
= text_mark_set(txt
, pos
);
213 } while (pos
!= prev_pos
);
215 size_t newpos
= text_mark_get(txt
, mark
);
216 return newpos
!= EPOS
? newpos
: c
->range
.start
;
219 static size_t op_modeswitch(Vis
*vis
, Text
*txt
, OperatorContext
*c
) {
220 return c
->newpos
!= EPOS
? c
->newpos
: c
->pos
;
223 static size_t op_replace(Vis
*vis
, Text
*txt
, OperatorContext
*c
) {
225 Iterator it
= text_iterator_get(txt
, c
->range
.start
);
226 while (it
. pos
< c
->range
.end
&& text_iterator_char_next(&it
, NULL
))
228 op_delete(vis
, txt
, c
);
229 size_t pos
= c
->range
.start
;
230 for (size_t len
= strlen(c
->arg
->s
); count
> 0; pos
+= len
, count
--)
231 text_insert(txt
, pos
, c
->arg
->s
, len
);
235 static size_t op_filter(Vis
*vis
, Text
*txt
, OperatorContext
*c
) {
236 return text_size(txt
) + 1; /* do not change cursor position, would destroy selection */
239 bool vis_operator(Vis
*vis
, enum VisOperator id
, ...) {
244 case VIS_OP_MODESWITCH
:
245 vis
->action
.mode
= va_arg(ap
, int);
247 case VIS_OP_CASE_LOWER
:
248 case VIS_OP_CASE_UPPER
:
249 case VIS_OP_CASE_SWAP
:
250 vis
->action
.arg
.i
= id
;
251 id
= VIS_OP_CASE_SWAP
;
253 case VIS_OP_CURSOR_SOL
:
254 case VIS_OP_CURSOR_EOL
:
255 vis
->action
.arg
.i
= id
;
256 id
= VIS_OP_CURSOR_SOL
;
258 case VIS_OP_PUT_AFTER
:
259 case VIS_OP_PUT_AFTER_END
:
260 case VIS_OP_PUT_BEFORE
:
261 case VIS_OP_PUT_BEFORE_END
:
262 vis
->action
.arg
.i
= id
;
263 id
= VIS_OP_PUT_AFTER
;
266 vis
->action
.arg
.s
= va_arg(ap
, char*);
269 vis
->action
.arg
.s
= va_arg(ap
, char*);
271 case VIS_OP_SHIFT_LEFT
:
272 case VIS_OP_SHIFT_RIGHT
:
273 vis_motion_type(vis
, VIS_MOTIONTYPE_LINEWISE
);
277 Macro
*macro
= &vis
->registers
[VIS_MACRO_OPERATOR
].buf
;
279 macro_append(macro
, va_arg(ap
, char*));
280 vis
->action
.arg
.s
= macro
->data
;
286 if (id
>= LENGTH(vis_operators
))
288 const Operator
*op
= &vis_operators
[id
];
289 if (vis
->mode
->visual
) {
295 /* switch to operator mode inorder to make operator options and
296 * text-object available */
297 vis_mode_switch(vis
, VIS_MODE_OPERATOR_PENDING
);
298 if (vis
->action
.op
== op
) {
299 /* hacky way to handle double operators i.e. things like
300 * dd, yy etc where the second char isn't a movement */
301 vis
->action
.type
= LINEWISE
;
302 vis_motion(vis
, VIS_MOVE_LINE_NEXT
);
307 /* put is not a real operator, does not need a range to operate on */
308 if (id
== VIS_OP_PUT_AFTER
)
309 vis_motion(vis
, VIS_MOVE_NOP
);
319 const Operator vis_operators
[] = {
320 [VIS_OP_DELETE
] = { op_delete
},
321 [VIS_OP_CHANGE
] = { op_change
},
322 [VIS_OP_YANK
] = { op_yank
},
323 [VIS_OP_PUT_AFTER
] = { op_put
},
324 [VIS_OP_SHIFT_RIGHT
] = { op_shift_right
},
325 [VIS_OP_SHIFT_LEFT
] = { op_shift_left
},
326 [VIS_OP_CASE_SWAP
] = { op_case_change
},
327 [VIS_OP_JOIN
] = { op_join
},
328 [VIS_OP_MODESWITCH
] = { op_modeswitch
},
329 [VIS_OP_REPLACE
] = { op_replace
},
330 [VIS_OP_CURSOR_SOL
] = { op_cursor
},
331 [VIS_OP_FILTER
] = { op_filter
},