vis
a vi-like editor based on Plan 9's structural regular expressions
git clone https://9o.is/git/vis.git
vis-registers.c
(9134B)
1 #include <stdlib.h>
2 #include <string.h>
3
4 #include "vis-core.h"
5
6 static Buffer *register_buffer(Register *reg, size_t slot) {
7 Buffer *buf = array_get(®->values, slot);
8 if (buf)
9 return buf;
10 if (array_resize(®->values, slot) && (buf = array_get(®->values, slot)))
11 return buf;
12 Buffer new = {0};
13 if (!array_add(®->values, &new))
14 return NULL;
15 size_t capacity = reg->values.count;
16 for (size_t i = reg->values.len; i < capacity; i++) {
17 if (!array_add(®->values, &new))
18 return NULL;
19 }
20 return array_get(®->values, slot);
21 }
22
23 bool register_init(Register *reg) {
24 Buffer buf = {0};
25 array_init_sized(®->values, sizeof(Buffer));
26 return array_add(®->values, &buf);
27 }
28
29 void register_release(Register *reg) {
30 if (!reg)
31 return;
32 size_t n = reg->values.count;
33 for (size_t i = 0; i < n; i++)
34 buffer_release(array_get(®->values, i));
35 array_release(®->values);
36 }
37
38 const char *register_slot_get(Vis *vis, Register *reg, size_t slot, size_t *len) {
39 if (len)
40 *len = 0;
41 switch (reg->type) {
42 case REGISTER_NORMAL:
43 {
44 Buffer *buf = array_get(®->values, slot);
45 if (!buf)
46 return NULL;
47 buffer_terminate(buf);
48 if (len)
49 *len = buffer_length0(buf);
50 return buffer_content0(buf);
51 }
52 case REGISTER_NUMBER:
53 {
54 Buffer *buf = array_get(®->values, 0);
55 if (!buf)
56 return NULL;
57 buf->len = 0;
58 buffer_appendf(buf, "%zu", slot+1);
59 if (len)
60 *len = buffer_length0(buf);
61 return buffer_content0(buf);
62 }
63 case REGISTER_CLIPBOARD:
64 {
65 Buffer buferr = {0};
66 enum VisRegister id = reg - vis->registers;
67 const char *cmd[] = { VIS_CLIPBOARD, "--paste", "--selection", NULL, NULL };
68 Buffer *buf = array_get(®->values, slot);
69 if (!buf)
70 return NULL;
71 buf->len = 0;
72
73 if (id == VIS_REG_PRIMARY)
74 cmd[3] = "primary";
75 else
76 cmd[3] = "clipboard";
77 int status = vis_pipe(vis, vis->win->file,
78 &(Filerange){ .start = 0, .end = 0 },
79 cmd, buf, read_into_buffer, &buferr, read_into_buffer, false);
80
81 if (status != 0)
82 vis_info_show(vis, "Command failed %s", buffer_content0(&buferr));
83 buffer_release(&buferr);
84 if (len)
85 *len = buffer_length0(buf);
86 return buffer_content0(buf);
87 }
88 case REGISTER_BLACKHOLE:
89 default:
90 return NULL;
91 }
92 }
93
94 const char *register_get(Vis *vis, Register *reg, size_t *len) {
95 return register_slot_get(vis, reg, 0, len);
96 }
97
98 bool register_slot_put(Vis *vis, Register *reg, size_t slot, const char *data, size_t len) {
99 if (reg->type != REGISTER_NORMAL)
100 return false;
101 Buffer *buf = register_buffer(reg, slot);
102 return buf && buffer_put(buf, data, len);
103 }
104
105 bool register_put(Vis *vis, Register *reg, const char *data, size_t len) {
106 return register_slot_put(vis, reg, 0, data, len) &&
107 register_resize(reg, 1);
108 }
109
110 bool register_put0(Vis *vis, Register *reg, const char *data) {
111 return register_put(vis, reg, data, strlen(data)+1);
112 }
113
114 static bool register_slot_append_range(Register *reg, size_t slot, Text *txt, Filerange *range) {
115 switch (reg->type) {
116 case REGISTER_NORMAL:
117 {
118 Buffer *buf = register_buffer(reg, slot);
119 if (!buf)
120 return false;
121 size_t len = text_range_size(range);
122 if (len == SIZE_MAX || !buffer_grow(buf, len+1))
123 return false;
124 if (buf->len > 0 && buf->data[buf->len-1] == '\0')
125 buf->len--;
126 buf->len += text_bytes_get(txt, range->start, len, buf->data + buf->len);
127 return buffer_append(buf, "\0", 1);
128 }
129 default:
130 return false;
131 }
132 }
133
134 bool register_slot_put_range(Vis *vis, Register *reg, size_t slot, Text *txt, Filerange *range) {
135 if (reg->append)
136 return register_slot_append_range(reg, slot, txt, range);
137
138 switch (reg->type) {
139 case REGISTER_NORMAL:
140 {
141 Buffer *buf = register_buffer(reg, slot);
142 if (!buf)
143 return false;
144 size_t len = text_range_size(range);
145 if (len == SIZE_MAX || !buffer_reserve(buf, len+1))
146 return false;
147 buf->len = text_bytes_get(txt, range->start, len, buf->data);
148 return buffer_append(buf, "\0", 1);
149 }
150 case REGISTER_CLIPBOARD:
151 {
152 Buffer buferr = {0};
153 const char *cmd[] = { VIS_CLIPBOARD, "--copy", "--selection", NULL, NULL };
154 enum VisRegister id = reg - vis->registers;
155
156 if (id == VIS_REG_PRIMARY)
157 cmd[3] = "primary";
158 else
159 cmd[3] = "clipboard";
160
161 int status = vis_pipe(vis, vis->win->file, range,
162 cmd, NULL, NULL, &buferr, read_into_buffer, false);
163
164 if (status != 0)
165 vis_info_show(vis, "Command failed %s", buffer_content0(&buferr));
166 buffer_release(&buferr);
167 return status == 0;
168 }
169 case REGISTER_BLACKHOLE:
170 return true;
171 default:
172 return false;
173 }
174 }
175
176 bool register_put_range(Vis *vis, Register *reg, Text *txt, Filerange *range) {
177 return register_slot_put_range(vis, reg, 0, txt, range) &&
178 register_resize(reg, 1);
179 }
180
181 size_t vis_register_count(Vis *vis, Register *reg) {
182 if (reg->type == REGISTER_NUMBER)
183 return vis->win ? vis->win->view.selection_count : 0;
184 return reg->values.len;
185 }
186
187 bool register_resize(Register *reg, size_t count) {
188 return array_truncate(®->values, count);
189 }
190
191 enum VisRegister vis_register_from(Vis *vis, char reg) {
192
193 if (reg == '@')
194 return VIS_MACRO_LAST_RECORDED;
195
196 if ('a' <= reg && reg <= 'z')
197 return VIS_REG_a + reg - 'a';
198 if ('A' <= reg && reg <= 'Z')
199 return VIS_REG_A + reg - 'A';
200
201 for (size_t i = 0; i < LENGTH(vis_registers); i++) {
202 if (vis_registers[i].name == reg)
203 return i;
204 }
205 return VIS_REG_INVALID;
206 }
207
208 char vis_register_to(Vis *vis, enum VisRegister reg) {
209
210 if (reg == VIS_MACRO_LAST_RECORDED)
211 return '@';
212
213 if (VIS_REG_a <= reg && reg <= VIS_REG_z)
214 return 'a' + reg - VIS_REG_a;
215 if (VIS_REG_A <= reg && reg <= VIS_REG_Z)
216 return 'A' + reg - VIS_REG_A;
217
218 if (reg < LENGTH(vis_registers))
219 return vis_registers[reg].name;
220
221 return '\0';
222 }
223
224 void vis_register(Vis *vis, enum VisRegister reg) {
225 if (VIS_REG_A <= reg && reg <= VIS_REG_Z) {
226 vis->action.reg = &vis->registers[VIS_REG_a + reg - VIS_REG_A];
227 vis->action.reg->append = true;
228 } else if (reg < LENGTH(vis->registers)) {
229 vis->action.reg = &vis->registers[reg];
230 vis->action.reg->append = false;
231 }
232 }
233
234 enum VisRegister vis_register_used(Vis *vis) {
235 if (!vis->action.reg)
236 return VIS_REG_DEFAULT;
237 return vis->action.reg - vis->registers;
238 }
239
240 static Register *register_from(Vis *vis, enum VisRegister id) {
241 if (VIS_REG_A <= id && id <= VIS_REG_Z)
242 id = VIS_REG_a + id - VIS_REG_A;
243 if (id < LENGTH(vis->registers))
244 return &vis->registers[id];
245 return NULL;
246 }
247
248 bool vis_register_set(Vis *vis, enum VisRegister id, Array *data) {
249 Register *reg = register_from(vis, id);
250 if (!reg)
251 return false;
252 size_t len = data->len;
253 for (size_t i = 0; i < len; i++) {
254 Buffer *buf = register_buffer(reg, i);
255 if (!buf)
256 return false;
257 TextString *string = array_get(data, i);
258 if (!buffer_put(buf, string->data, string->len))
259 return false;
260 }
261 return register_resize(reg, len);
262 }
263
264 Array vis_register_get(Vis *vis, enum VisRegister id) {
265 Array data;
266 array_init_sized(&data, sizeof(TextString));
267 Register *reg = register_from(vis, id);
268 if (reg) {
269 size_t len = reg->values.len;
270 array_reserve(&data, len);
271 for (size_t i = 0; i < len; i++) {
272 Buffer *buf = array_get(®->values, i);
273 TextString string = {
274 .data = buf->data,
275 .len = buf->len,
276 };
277 array_add(&data, &string);
278 }
279 }
280 return data;
281 }
282
283 const RegisterDef vis_registers[] = {
284 [VIS_REG_DEFAULT] = { '"', VIS_HELP("Unnamed register") },
285 [VIS_REG_ZERO] = { '0', VIS_HELP("Yank register") },
286 [VIS_REG_1] = { '1', VIS_HELP("1st sub-expression match") },
287 [VIS_REG_2] = { '2', VIS_HELP("2nd sub-expression match") },
288 [VIS_REG_3] = { '3', VIS_HELP("3rd sub-expression match") },
289 [VIS_REG_4] = { '4', VIS_HELP("4th sub-expression match") },
290 [VIS_REG_5] = { '5', VIS_HELP("5th sub-expression match") },
291 [VIS_REG_6] = { '6', VIS_HELP("6th sub-expression match") },
292 [VIS_REG_7] = { '7', VIS_HELP("7th sub-expression match") },
293 [VIS_REG_8] = { '8', VIS_HELP("8th sub-expression match") },
294 [VIS_REG_9] = { '9', VIS_HELP("9th sub-expression match") },
295 [VIS_REG_AMPERSAND] = { '&', VIS_HELP("Last regex match") },
296 [VIS_REG_BLACKHOLE] = { '_', VIS_HELP("/dev/null register") },
297 [VIS_REG_PRIMARY] = { '*', VIS_HELP("Primary clipboard register, see vis-clipboard(1)") },
298 [VIS_REG_CLIPBOARD] = { '+', VIS_HELP("System clipboard register, see vis-clipboard(1)") },
299 [VIS_REG_DOT] = { '.', VIS_HELP("Last inserted text") },
300 [VIS_REG_SEARCH] = { '/', VIS_HELP("Last search pattern") },
301 [VIS_REG_COMMAND] = { ':', VIS_HELP("Last :-command") },
302 [VIS_REG_SHELL] = { '!', VIS_HELP("Last shell command given to either <, >, |, or !") },
303 [VIS_REG_NUMBER] = { '#', VIS_HELP("Register number") },
304 };