vis: make paragraph textobjects linewise
[vis/gkirilov.git] / vis-registers.c
blob1875da95d42f9a2f636823b6d0d08bdbc1d0633b
1 #include <stdlib.h>
2 #include <string.h>
4 #include "vis-core.h"
6 static Buffer *register_buffer(Register *reg, size_t slot) {
7 Buffer *buf = array_get(&reg->values, slot);
8 if (buf)
9 return buf;
10 if (array_resize(&reg->values, slot) && (buf = array_get(&reg->values, slot)))
11 return buf;
12 Buffer new;
13 buffer_init(&new);
14 if (!array_add(&reg->values, &new))
15 return NULL;
16 size_t capacity = array_capacity(&reg->values);
17 for (size_t i = array_length(&reg->values); i < capacity; i++) {
18 if (!array_add(&reg->values, &new))
19 return NULL;
21 return array_get(&reg->values, slot);
24 static ssize_t read_buffer(void *context, char *data, size_t len) {
25 buffer_append(context, data, len);
26 return len;
29 bool register_init(Register *reg) {
30 Buffer buf;
31 buffer_init(&buf);
32 array_init_sized(&reg->values, sizeof(Buffer));
33 return array_add(&reg->values, &buf);
36 void register_release(Register *reg) {
37 if (!reg)
38 return;
39 size_t n = array_capacity(&reg->values);
40 for (size_t i = 0; i < n; i++)
41 buffer_release(array_get(&reg->values, i));
42 array_release(&reg->values);
45 const char *register_slot_get(Vis *vis, Register *reg, size_t slot, size_t *len) {
46 if (len)
47 *len = 0;
48 switch (reg->type) {
49 case REGISTER_NORMAL:
51 Buffer *buf = array_get(&reg->values, slot);
52 if (!buf)
53 return NULL;
54 buffer_terminate(buf);
55 if (len)
56 *len = buffer_length0(buf);
57 return buffer_content0(buf);
59 case REGISTER_NUMBER:
61 Buffer *buf = array_get(&reg->values, 0);
62 if (!buf)
63 return NULL;
64 buffer_printf(buf, "%zu", slot+1);
65 if (len)
66 *len = buffer_length0(buf);
67 return buffer_content0(buf);
69 case REGISTER_CLIPBOARD:
71 Buffer buferr;
72 enum VisRegister id = reg - vis->registers;
73 const char *cmd[] = { VIS_CLIPBOARD, "--paste", "--selection", NULL, NULL };
74 buffer_init(&buferr);
75 Buffer *buf = array_get(&reg->values, slot);
76 if (!buf)
77 return NULL;
78 buffer_clear(buf);
80 if (id == VIS_REG_PRIMARY)
81 cmd[3] = "primary";
82 else
83 cmd[3] = "clipboard";
84 int status = vis_pipe(vis, vis->win->file,
85 &(Filerange){ .start = 0, .end = 0 },
86 cmd, buf, read_buffer, &buferr, read_buffer, false);
88 if (status != 0)
89 vis_info_show(vis, "Command failed %s", buffer_content0(&buferr));
90 buffer_release(&buferr);
91 if (len)
92 *len = buffer_length0(buf);
93 return buffer_content0(buf);
95 case REGISTER_BLACKHOLE:
96 default:
97 return NULL;
101 const char *register_get(Vis *vis, Register *reg, size_t *len) {
102 return register_slot_get(vis, reg, 0, len);
105 bool register_slot_put(Vis *vis, Register *reg, size_t slot, const char *data, size_t len) {
106 if (reg->type != REGISTER_NORMAL)
107 return false;
108 Buffer *buf = register_buffer(reg, slot);
109 return buf && buffer_put(buf, data, len);
112 bool register_put(Vis *vis, Register *reg, const char *data, size_t len) {
113 return register_slot_put(vis, reg, 0, data, len) &&
114 register_resize(reg, 1);
117 bool register_put0(Vis *vis, Register *reg, const char *data) {
118 return register_put(vis, reg, data, strlen(data)+1);
121 static bool register_slot_append_range(Register *reg, size_t slot, Text *txt, Filerange *range) {
122 switch (reg->type) {
123 case REGISTER_NORMAL:
125 Buffer *buf = register_buffer(reg, slot);
126 if (!buf)
127 return false;
128 size_t len = text_range_size(range);
129 if (len == SIZE_MAX || !buffer_grow(buf, len+1))
130 return false;
131 if (buf->len > 0 && buf->data[buf->len-1] == '\0')
132 buf->len--;
133 buf->len += text_bytes_get(txt, range->start, len, buf->data + buf->len);
134 return buffer_append(buf, "\0", 1);
136 default:
137 return false;
141 bool register_slot_put_range(Vis *vis, Register *reg, size_t slot, Text *txt, Filerange *range) {
142 if (reg->append)
143 return register_slot_append_range(reg, slot, txt, range);
145 switch (reg->type) {
146 case REGISTER_NORMAL:
148 Buffer *buf = register_buffer(reg, slot);
149 if (!buf)
150 return false;
151 size_t len = text_range_size(range);
152 if (len == SIZE_MAX || !buffer_reserve(buf, len+1))
153 return false;
154 buf->len = text_bytes_get(txt, range->start, len, buf->data);
155 return buffer_append(buf, "\0", 1);
157 case REGISTER_CLIPBOARD:
159 Buffer buferr;
160 const char *cmd[] = { VIS_CLIPBOARD, "--copy", "--selection", NULL, NULL };
161 enum VisRegister id = reg - vis->registers;
162 buffer_init(&buferr);
164 if (id == VIS_REG_PRIMARY)
165 cmd[3] = "primary";
166 else
167 cmd[3] = "clipboard";
169 int status = vis_pipe(vis, vis->win->file, range,
170 cmd, NULL, NULL, &buferr, read_buffer, false);
172 if (status != 0)
173 vis_info_show(vis, "Command failed %s", buffer_content0(&buferr));
174 buffer_release(&buferr);
175 return status == 0;
177 case REGISTER_BLACKHOLE:
178 return true;
179 default:
180 return false;
184 bool register_put_range(Vis *vis, Register *reg, Text *txt, Filerange *range) {
185 return register_slot_put_range(vis, reg, 0, txt, range) &&
186 register_resize(reg, 1);
189 size_t vis_register_count(Vis *vis, Register *reg) {
190 if (reg->type == REGISTER_NUMBER)
191 return vis->win ? view_selections_count(vis->win->view) : 0;
192 return array_length(&reg->values);
195 bool register_resize(Register *reg, size_t count) {
196 return array_truncate(&reg->values, count);
199 enum VisRegister vis_register_from(Vis *vis, char reg) {
201 if (reg == '@')
202 return VIS_MACRO_LAST_RECORDED;
204 if ('a' <= reg && reg <= 'z')
205 return VIS_REG_a + reg - 'a';
206 if ('A' <= reg && reg <= 'Z')
207 return VIS_REG_A + reg - 'A';
209 for (size_t i = 0; i < LENGTH(vis_registers); i++) {
210 if (vis_registers[i].name == reg)
211 return i;
213 return VIS_REG_INVALID;
216 char vis_register_to(Vis *vis, enum VisRegister reg) {
218 if (reg == VIS_MACRO_LAST_RECORDED)
219 return '@';
221 if (VIS_REG_a <= reg && reg <= VIS_REG_z)
222 return 'a' + reg - VIS_REG_a;
223 if (VIS_REG_A <= reg && reg <= VIS_REG_Z)
224 return 'A' + reg - VIS_REG_A;
226 if (reg < LENGTH(vis_registers))
227 return vis_registers[reg].name;
229 return '\0';
232 void vis_register(Vis *vis, enum VisRegister reg) {
233 if (VIS_REG_A <= reg && reg <= VIS_REG_Z) {
234 vis->action.reg = &vis->registers[VIS_REG_a + reg - VIS_REG_A];
235 vis->action.reg->append = true;
236 } else if (reg < LENGTH(vis->registers)) {
237 vis->action.reg = &vis->registers[reg];
238 vis->action.reg->append = false;
242 enum VisRegister vis_register_used(Vis *vis) {
243 if (!vis->action.reg)
244 return VIS_REG_DEFAULT;
245 return vis->action.reg - vis->registers;
248 static Register *register_from(Vis *vis, enum VisRegister id) {
249 if (VIS_REG_A <= id && id <= VIS_REG_Z)
250 id = VIS_REG_a + id - VIS_REG_A;
251 if (id < LENGTH(vis->registers))
252 return &vis->registers[id];
253 return NULL;
256 bool vis_register_set(Vis *vis, enum VisRegister id, Array *data) {
257 Register *reg = register_from(vis, id);
258 if (!reg)
259 return false;
260 size_t len = array_length(data);
261 for (size_t i = 0; i < len; i++) {
262 Buffer *buf = register_buffer(reg, i);
263 if (!buf)
264 return false;
265 TextString *string = array_get(data, i);
266 if (!buffer_put(buf, string->data, string->len))
267 return false;
269 return register_resize(reg, len);
272 Array vis_register_get(Vis *vis, enum VisRegister id) {
273 Array data;
274 array_init_sized(&data, sizeof(TextString));
275 Register *reg = register_from(vis, id);
276 if (reg) {
277 size_t len = array_length(&reg->values);
278 array_reserve(&data, len);
279 for (size_t i = 0; i < len; i++) {
280 Buffer *buf = array_get(&reg->values, i);
281 TextString string = {
282 .data = buffer_content(buf),
283 .len = buffer_length(buf),
285 array_add(&data, &string);
288 return data;
291 const RegisterDef vis_registers[] = {
292 [VIS_REG_DEFAULT] = { '"', VIS_HELP("Unnamed register") },
293 [VIS_REG_ZERO] = { '0', VIS_HELP("Yank register") },
294 [VIS_REG_1] = { '1', VIS_HELP("1st sub-expression match") },
295 [VIS_REG_2] = { '2', VIS_HELP("2nd sub-expression match") },
296 [VIS_REG_3] = { '3', VIS_HELP("3rd sub-expression match") },
297 [VIS_REG_4] = { '4', VIS_HELP("4th sub-expression match") },
298 [VIS_REG_5] = { '5', VIS_HELP("5th sub-expression match") },
299 [VIS_REG_6] = { '6', VIS_HELP("6th sub-expression match") },
300 [VIS_REG_7] = { '7', VIS_HELP("7th sub-expression match") },
301 [VIS_REG_8] = { '8', VIS_HELP("8th sub-expression match") },
302 [VIS_REG_9] = { '9', VIS_HELP("9th sub-expression match") },
303 [VIS_REG_AMPERSAND] = { '&', VIS_HELP("Last regex match") },
304 [VIS_REG_BLACKHOLE] = { '_', VIS_HELP("/dev/null register") },
305 [VIS_REG_PRIMARY] = { '*', VIS_HELP("Primary clipboard register, see vis-clipboard(1)") },
306 [VIS_REG_CLIPBOARD] = { '+', VIS_HELP("System clipboard register, see vis-clipboard(1)") },
307 [VIS_REG_DOT] = { '.', VIS_HELP("Last inserted text") },
308 [VIS_REG_SEARCH] = { '/', VIS_HELP("Last search pattern") },
309 [VIS_REG_COMMAND] = { ':', VIS_HELP("Last :-command") },
310 [VIS_REG_SHELL] = { '!', VIS_HELP("Last shell command given to either <, >, |, or !") },
311 [VIS_REG_NUMBER] = { '#', VIS_HELP("Register number") },