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(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 macro_operator_record(vis
);
22 return c
->range
.start
;
25 static size_t op_yank(Vis
*vis
, Text
*txt
, OperatorContext
*c
) {
26 c
->reg
->linewise
= c
->linewise
;
27 register_put(vis
, c
->reg
, txt
, &c
->range
);
28 if (c
->reg
== &vis
->registers
[VIS_REG_DEFAULT
])
29 register_put(vis
, &vis
->registers
[VIS_REG_ZERO
], txt
, &c
->range
);
33 static size_t op_put(Vis
*vis
, Text
*txt
, OperatorContext
*c
) {
35 bool sel
= text_range_size(&c
->range
) > 0;
36 bool sel_linewise
= sel
&& text_range_is_linewise(txt
, &c
->range
);
38 text_delete_range(txt
, &c
->range
);
39 pos
= c
->pos
= c
->range
.start
;
42 case VIS_OP_PUT_AFTER
:
43 case VIS_OP_PUT_AFTER_END
:
44 if (c
->reg
->linewise
&& !sel_linewise
)
45 pos
= text_line_next(txt
, pos
);
47 pos
= text_char_next(txt
, pos
);
49 case VIS_OP_PUT_BEFORE
:
50 case VIS_OP_PUT_BEFORE_END
:
52 pos
= text_line_begin(txt
, pos
);
57 const char *data
= register_get(vis
, c
->reg
, &len
);
59 for (int i
= 0; i
< c
->count
; i
++) {
60 text_insert(txt
, pos
, data
, len
);
64 if (c
->reg
->linewise
) {
66 case VIS_OP_PUT_BEFORE_END
:
67 case VIS_OP_PUT_AFTER_END
:
68 pos
= text_line_start(txt
, pos
);
70 case VIS_OP_PUT_AFTER
:
71 pos
= text_line_start(txt
, text_line_next(txt
, c
->pos
));
73 case VIS_OP_PUT_BEFORE
:
74 pos
= text_line_start(txt
, c
->pos
);
79 case VIS_OP_PUT_AFTER
:
80 case VIS_OP_PUT_BEFORE
:
81 pos
= text_char_prev(txt
, pos
);
89 static size_t op_shift_right(Vis
*vis
, Text
*txt
, OperatorContext
*c
) {
91 spaces
[MIN(vis
->tabwidth
, LENGTH(spaces
) - 1)] = '\0';
92 const char *tab
= vis
->expandtab
? spaces
: "\t";
93 size_t tablen
= strlen(tab
);
94 size_t pos
= text_line_begin(txt
, c
->range
.end
), prev_pos
;
97 /* if range ends at the begin of a line, skip line break */
98 if (pos
== c
->range
.end
)
99 pos
= text_line_prev(txt
, pos
);
102 prev_pos
= pos
= text_line_begin(txt
, pos
);
103 text_insert(txt
, pos
, tab
, tablen
);
104 pos
= text_line_prev(txt
, pos
);
106 } while (pos
>= c
->range
.start
&& pos
!= prev_pos
);
108 return c
->pos
+ inserted
;
111 static size_t op_shift_left(Vis
*vis
, Text
*txt
, OperatorContext
*c
) {
112 size_t pos
= text_line_begin(txt
, c
->range
.end
), prev_pos
;
113 size_t tabwidth
= vis
->tabwidth
, tablen
;
116 /* if range ends at the begin of a line, skip line break */
117 if (pos
== c
->range
.end
)
118 pos
= text_line_prev(txt
, pos
);
123 prev_pos
= pos
= text_line_begin(txt
, pos
);
124 Iterator it
= text_iterator_get(txt
, pos
);
125 if (text_iterator_byte_get(&it
, &c
) && c
== '\t') {
128 for (len
= 0; text_iterator_byte_get(&it
, &c
) && c
== ' '; len
++)
129 text_iterator_byte_next(&it
, NULL
);
131 tablen
= MIN(len
, tabwidth
);
132 text_delete(txt
, pos
, tablen
);
133 pos
= text_line_prev(txt
, pos
);
135 } while (pos
>= c
->range
.start
&& pos
!= prev_pos
);
137 return c
->pos
- deleted
;
140 static size_t op_case_change(Vis
*vis
, Text
*txt
, OperatorContext
*c
) {
141 size_t len
= text_range_size(&c
->range
);
142 char *buf
= malloc(len
);
145 len
= text_bytes_get(txt
, c
->range
.start
, len
, buf
);
147 for (char *cur
= buf
; rem
> 0; cur
++, rem
--) {
148 int ch
= (unsigned char)*cur
;
150 if (c
->arg
->i
== VIS_OP_CASE_SWAP
)
151 *cur
= islower(ch
) ? toupper(ch
) : tolower(ch
);
152 else if (c
->arg
->i
== VIS_OP_CASE_UPPER
)
159 text_delete(txt
, c
->range
.start
, len
);
160 text_insert(txt
, c
->range
.start
, buf
, len
);
165 static size_t op_cursor(Vis
*vis
, Text
*txt
, OperatorContext
*c
) {
166 View
*view
= vis
->win
->view
;
167 Filerange r
= text_range_linewise(txt
, &c
->range
);
168 for (size_t line
= text_range_line_first(txt
, &r
); line
!= EPOS
; line
= text_range_line_next(txt
, &r
, line
)) {
169 Cursor
*cursor
= view_cursors_new(view
);
172 if (c
->arg
->i
== VIS_OP_CURSOR_EOL
)
173 pos
= text_line_finish(txt
, line
);
175 pos
= text_line_start(txt
, line
);
176 view_cursors_to(cursor
, pos
);
182 static size_t op_join(Vis
*vis
, Text
*txt
, OperatorContext
*c
) {
183 size_t pos
= text_line_begin(txt
, c
->range
.end
), prev_pos
;
185 /* if operator and range are both linewise, skip last line break */
186 if (c
->linewise
&& text_range_is_linewise(txt
, &c
->range
)) {
187 size_t line_prev
= text_line_prev(txt
, pos
);
188 size_t line_prev_prev
= text_line_prev(txt
, line_prev
);
189 if (line_prev_prev
>= c
->range
.start
)
195 size_t end
= text_line_start(txt
, pos
);
196 pos
= text_char_next(txt
, text_line_finish(txt
, text_line_prev(txt
, end
)));
197 if (pos
>= c
->range
.start
&& end
> pos
) {
198 text_delete(txt
, pos
, end
- pos
);
199 text_insert(txt
, pos
, " ", 1);
203 } while (pos
!= prev_pos
);
205 return c
->range
.start
;
208 static size_t op_insert(Vis
*vis
, Text
*txt
, OperatorContext
*c
) {
209 macro_operator_record(vis
);
210 return c
->newpos
!= EPOS
? c
->newpos
: c
->pos
;
213 static size_t op_replace(Vis
*vis
, Text
*txt
, OperatorContext
*c
) {
214 macro_operator_record(vis
);
215 return c
->newpos
!= EPOS
? c
->newpos
: c
->pos
;
218 static size_t op_filter(Vis
*vis
, Text
*txt
, OperatorContext
*c
) {
220 macro_operator_record(vis
);
221 return text_size(txt
) + 1; /* do not change cursor position, would destroy selection */
224 bool vis_operator(Vis
*vis
, enum VisOperator id
, ...) {
229 case VIS_OP_CASE_LOWER
:
230 case VIS_OP_CASE_UPPER
:
231 case VIS_OP_CASE_SWAP
:
232 vis
->action
.arg
.i
= id
;
233 id
= VIS_OP_CASE_SWAP
;
235 case VIS_OP_CURSOR_SOL
:
236 case VIS_OP_CURSOR_EOL
:
237 vis
->action
.arg
.i
= id
;
238 id
= VIS_OP_CURSOR_SOL
;
240 case VIS_OP_PUT_AFTER
:
241 case VIS_OP_PUT_AFTER_END
:
242 case VIS_OP_PUT_BEFORE
:
243 case VIS_OP_PUT_BEFORE_END
:
244 vis
->action
.arg
.i
= id
;
245 id
= VIS_OP_PUT_AFTER
;
248 vis
->action
.arg
.s
= va_arg(ap
, char*);
250 case VIS_OP_SHIFT_LEFT
:
251 case VIS_OP_SHIFT_RIGHT
:
252 vis_motion_type(vis
, VIS_MOTIONTYPE_LINEWISE
);
257 if (id
>= LENGTH(vis_operators
))
259 const Operator
*op
= &vis_operators
[id
];
260 if (vis
->mode
->visual
) {
262 action_do(vis
, &vis
->action
);
266 /* switch to operator mode inorder to make operator options and
267 * text-object available */
268 vis_mode_switch(vis
, VIS_MODE_OPERATOR_PENDING
);
269 if (vis
->action
.op
== op
) {
270 /* hacky way to handle double operators i.e. things like
271 * dd, yy etc where the second char isn't a movement */
272 vis
->action
.type
= LINEWISE
;
273 vis_motion(vis
, VIS_MOVE_LINE_NEXT
);
278 /* put is not a real operator, does not need a range to operate on */
279 if (id
== VIS_OP_PUT_AFTER
)
280 vis_motion(vis
, VIS_MOVE_NOP
);
290 const Operator vis_operators
[] = {
291 [VIS_OP_DELETE
] = { op_delete
},
292 [VIS_OP_CHANGE
] = { op_change
},
293 [VIS_OP_YANK
] = { op_yank
},
294 [VIS_OP_PUT_AFTER
] = { op_put
},
295 [VIS_OP_SHIFT_RIGHT
] = { op_shift_right
},
296 [VIS_OP_SHIFT_LEFT
] = { op_shift_left
},
297 [VIS_OP_CASE_SWAP
] = { op_case_change
},
298 [VIS_OP_JOIN
] = { op_join
},
299 [VIS_OP_INSERT
] = { op_insert
},
300 [VIS_OP_REPLACE
] = { op_replace
},
301 [VIS_OP_CURSOR_SOL
] = { op_cursor
},
302 [VIS_OP_FILTER
] = { op_filter
},