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_slot_put_range(vis
, c
->reg
, c
->reg_slot
, 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 bool linewise
= c
->linewise
|| text_range_is_linewise(txt
, &c
->range
);
21 op_delete(vis
, txt
, c
);
22 size_t pos
= c
->range
.start
;
24 size_t newpos
= vis_text_insert_nl(vis
, txt
, pos
> 0 ? pos
-1 : pos
);
31 static size_t op_yank(Vis
*vis
, Text
*txt
, OperatorContext
*c
) {
32 c
->reg
->linewise
= c
->linewise
;
33 register_slot_put_range(vis
, c
->reg
, c
->reg_slot
, txt
, &c
->range
);
34 if (c
->reg
== &vis
->registers
[VIS_REG_DEFAULT
]) {
35 vis
->registers
[VIS_REG_ZERO
].linewise
= c
->reg
->linewise
;
36 register_slot_put_range(vis
, &vis
->registers
[VIS_REG_ZERO
], c
->reg_slot
, txt
, &c
->range
);
38 return c
->linewise
? c
->pos
: c
->range
.start
;
41 static size_t op_put(Vis
*vis
, Text
*txt
, OperatorContext
*c
) {
44 bool sel
= text_range_size(&c
->range
) > 0;
45 bool sel_linewise
= sel
&& text_range_is_linewise(txt
, &c
->range
);
47 text_delete_range(txt
, &c
->range
);
48 pos
= c
->pos
= c
->range
.start
;
51 case VIS_OP_PUT_AFTER
:
52 case VIS_OP_PUT_AFTER_END
:
53 if (c
->reg
->linewise
&& !sel_linewise
)
54 pos
= text_line_next(txt
, pos
);
55 else if (!sel
&& text_byte_get(txt
, pos
, &b
) && b
!= '\n')
56 pos
= text_char_next(txt
, pos
);
58 case VIS_OP_PUT_BEFORE
:
59 case VIS_OP_PUT_BEFORE_END
:
61 pos
= text_line_begin(txt
, pos
);
66 const char *data
= register_slot_get(vis
, c
->reg
, c
->reg_slot
, &len
);
68 for (int i
= 0; i
< c
->count
; i
++) {
70 if (c
->reg
->linewise
&& pos
> 0 && text_byte_get(txt
, pos
-1, &nl
) && nl
!= '\n')
71 pos
+= text_insert(txt
, pos
, "\n", 1);
72 text_insert(txt
, pos
, data
, len
);
74 if (c
->reg
->linewise
&& pos
> 0 && text_byte_get(txt
, pos
-1, &nl
) && nl
!= '\n')
75 pos
+= text_insert(txt
, pos
, "\n", 1);
78 if (c
->reg
->linewise
) {
80 case VIS_OP_PUT_BEFORE_END
:
81 case VIS_OP_PUT_AFTER_END
:
82 pos
= text_line_start(txt
, pos
);
84 case VIS_OP_PUT_AFTER
:
85 pos
= text_line_start(txt
, text_line_next(txt
, c
->pos
));
87 case VIS_OP_PUT_BEFORE
:
88 pos
= text_line_start(txt
, c
->pos
);
93 case VIS_OP_PUT_AFTER
:
94 case VIS_OP_PUT_BEFORE
:
95 pos
= text_char_prev(txt
, pos
);
103 static size_t op_shift_right(Vis
*vis
, Text
*txt
, OperatorContext
*c
) {
104 char spaces
[9] = " ";
105 spaces
[MIN(vis
->tabwidth
, LENGTH(spaces
) - 1)] = '\0';
106 const char *tab
= vis
->expandtab
? spaces
: "\t";
107 size_t tablen
= strlen(tab
);
108 size_t pos
= text_line_begin(txt
, c
->range
.end
), prev_pos
;
109 size_t newpos
= c
->pos
;
111 /* if range ends at the begin of a line, skip line break */
112 if (pos
== c
->range
.end
)
113 pos
= text_line_prev(txt
, pos
);
114 bool multiple_lines
= text_line_prev(txt
, pos
) >= c
->range
.start
;
117 size_t end
= text_line_end(txt
, pos
);
118 prev_pos
= pos
= text_line_begin(txt
, end
);
119 if ((!multiple_lines
|| pos
!= end
) &&
120 text_insert(txt
, pos
, tab
, tablen
) && pos
<= c
->pos
)
122 pos
= text_line_prev(txt
, pos
);
123 } while (pos
>= c
->range
.start
&& pos
!= prev_pos
);
128 static size_t op_shift_left(Vis
*vis
, Text
*txt
, OperatorContext
*c
) {
129 size_t pos
= text_line_begin(txt
, c
->range
.end
), prev_pos
;
130 size_t tabwidth
= vis
->tabwidth
, tablen
;
131 size_t newpos
= c
->pos
;
133 /* if range ends at the begin of a line, skip line break */
134 if (pos
== c
->range
.end
)
135 pos
= text_line_prev(txt
, pos
);
140 prev_pos
= pos
= text_line_begin(txt
, pos
);
141 Iterator it
= text_iterator_get(txt
, pos
);
142 if (text_iterator_byte_get(&it
, &b
) && b
== '\t') {
145 for (len
= 0; text_iterator_byte_get(&it
, &b
) && b
== ' '; len
++)
146 text_iterator_byte_next(&it
, NULL
);
148 tablen
= MIN(len
, tabwidth
);
149 if (text_delete(txt
, pos
, tablen
) && pos
< c
->pos
) {
150 size_t delta
= c
->pos
- pos
;
157 pos
= text_line_prev(txt
, pos
);
158 } while (pos
>= c
->range
.start
&& pos
!= prev_pos
);
163 static size_t op_cursor(Vis
*vis
, Text
*txt
, OperatorContext
*c
) {
164 View
*view
= vis
->win
->view
;
165 Filerange r
= text_range_linewise(txt
, &c
->range
);
166 for (size_t line
= text_range_line_first(txt
, &r
); line
!= EPOS
; line
= text_range_line_next(txt
, &r
, line
)) {
168 if (c
->arg
->i
== VIS_OP_CURSOR_EOL
)
169 pos
= text_line_finish(txt
, line
);
171 pos
= text_line_start(txt
, line
);
172 view_selections_new_force(view
, pos
);
177 static size_t op_join(Vis
*vis
, Text
*txt
, OperatorContext
*c
) {
178 size_t pos
= text_line_begin(txt
, c
->range
.end
), prev_pos
;
181 /* if operator and range are both linewise, skip last line break */
182 if (c
->linewise
&& text_range_is_linewise(txt
, &c
->range
)) {
183 size_t line_prev
= text_line_prev(txt
, pos
);
184 size_t line_prev_prev
= text_line_prev(txt
, line_prev
);
185 if (line_prev_prev
>= c
->range
.start
)
189 size_t len
= c
->arg
->s
? strlen(c
->arg
->s
) : 0;
193 size_t end
= text_line_start(txt
, pos
);
194 pos
= text_line_prev(txt
, end
);
195 if (pos
< c
->range
.start
|| end
<= pos
)
197 text_delete(txt
, pos
, end
- pos
);
199 if (text_byte_get(txt
, pos
-1, &prev
) && !isspace((unsigned char)prev
) &&
200 text_byte_get(txt
, pos
, &next
) && next
!= '\n')
201 text_insert(txt
, pos
, c
->arg
->s
, len
);
203 mark
= text_mark_set(txt
, pos
);
204 } while (pos
!= prev_pos
);
206 size_t newpos
= text_mark_get(txt
, mark
);
207 return newpos
!= EPOS
? newpos
: c
->range
.start
;
210 static size_t op_modeswitch(Vis
*vis
, Text
*txt
, OperatorContext
*c
) {
211 return c
->newpos
!= EPOS
? c
->newpos
: c
->pos
;
214 static size_t op_replace(Vis
*vis
, Text
*txt
, OperatorContext
*c
) {
216 Iterator it
= text_iterator_get(txt
, c
->range
.start
);
217 while (it
. pos
< c
->range
.end
&& text_iterator_char_next(&it
, NULL
))
219 op_delete(vis
, txt
, c
);
220 size_t pos
= c
->range
.start
;
221 for (size_t len
= strlen(c
->arg
->s
); count
> 0; pos
+= len
, count
--)
222 text_insert(txt
, pos
, c
->arg
->s
, len
);
223 return c
->range
.start
;
226 int vis_operator_register(Vis
*vis
, VisOperatorFunction
*func
, void *context
) {
227 Operator
*op
= calloc(1, sizeof *op
);
231 op
->context
= context
;
232 if (array_add_ptr(&vis
->operators
, op
))
233 return VIS_OP_LAST
+ array_length(&vis
->operators
) - 1;
238 bool vis_operator(Vis
*vis
, enum VisOperator id
, ...) {
243 case VIS_OP_MODESWITCH
:
244 vis
->action
.mode
= va_arg(ap
, int);
246 case VIS_OP_CURSOR_SOL
:
247 case VIS_OP_CURSOR_EOL
:
248 vis
->action
.arg
.i
= id
;
249 id
= VIS_OP_CURSOR_SOL
;
251 case VIS_OP_PUT_AFTER
:
252 case VIS_OP_PUT_AFTER_END
:
253 case VIS_OP_PUT_BEFORE
:
254 case VIS_OP_PUT_BEFORE_END
:
255 vis
->action
.arg
.i
= id
;
256 id
= VIS_OP_PUT_AFTER
;
259 vis
->action
.arg
.s
= va_arg(ap
, char*);
261 case VIS_OP_SHIFT_LEFT
:
262 case VIS_OP_SHIFT_RIGHT
:
263 vis_motion_type(vis
, VIS_MOTIONTYPE_LINEWISE
);
267 Macro
*macro
= macro_get(vis
, VIS_REG_DOT
);
269 macro_append(macro
, va_arg(ap
, char*));
270 vis
->action
.arg
.s
= macro
->data
;
275 enum VisMode mode
= vis_mode_get(vis
);
276 enum VisRegister reg
= vis_register_used(vis
);
277 if (reg
== VIS_REG_DEFAULT
&& (mode
== VIS_MODE_INSERT
|| mode
== VIS_MODE_REPLACE
))
278 vis_register(vis
, VIS_REG_BLACKHOLE
);
285 const Operator
*op
= NULL
;
286 if (id
< LENGTH(vis_operators
))
287 op
= &vis_operators
[id
];
289 op
= array_get_ptr(&vis
->operators
, id
- VIS_OP_LAST
);
294 if (vis
->mode
->visual
) {
300 /* switch to operator mode inorder to make operator options and
301 * text-object available */
302 vis_mode_switch(vis
, VIS_MODE_OPERATOR_PENDING
);
303 if (vis
->action
.op
== op
) {
304 /* hacky way to handle double operators i.e. things like
305 * dd, yy etc where the second char isn't a movement */
306 vis_motion_type(vis
, VIS_MOTIONTYPE_LINEWISE
);
307 vis_motion(vis
, VIS_MOVE_LINE_NEXT
);
312 /* put is not a real operator, does not need a range to operate on */
313 if (id
== VIS_OP_PUT_AFTER
)
314 vis_motion(vis
, VIS_MOVE_NOP
);
324 const Operator vis_operators
[] = {
325 [VIS_OP_DELETE
] = { op_delete
},
326 [VIS_OP_CHANGE
] = { op_change
},
327 [VIS_OP_YANK
] = { op_yank
},
328 [VIS_OP_PUT_AFTER
] = { op_put
},
329 [VIS_OP_SHIFT_RIGHT
] = { op_shift_right
},
330 [VIS_OP_SHIFT_LEFT
] = { op_shift_left
},
331 [VIS_OP_JOIN
] = { op_join
},
332 [VIS_OP_MODESWITCH
] = { op_modeswitch
},
333 [VIS_OP_REPLACE
] = { op_replace
},
334 [VIS_OP_CURSOR_SOL
] = { op_cursor
},