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(&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 = {0};
     13 	if (!array_add(&reg->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(&reg->values, &new))
     18 			return NULL;
     19 	}
     20 	return array_get(&reg->values, slot);
     21 }
     22 
     23 bool register_init(Register *reg) {
     24 	Buffer buf = {0};
     25 	array_init_sized(&reg->values, sizeof(Buffer));
     26 	return array_add(&reg->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(&reg->values, i));
     35 	array_release(&reg->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(&reg->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(&reg->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(&reg->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(&reg->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(&reg->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 };