summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/buffer.c283
-rw-r--r--src/buffer.h33
-rw-r--r--src/buffers.c1
-rw-r--r--src/display.c4
-rw-r--r--src/main.c2
-rw-r--r--src/minibuffer.c3
-rw-r--r--src/text.c137
-rw-r--r--src/text.h7
8 files changed, 364 insertions, 106 deletions
diff --git a/src/buffer.c b/src/buffer.c
index b9ec8fc..71d486e 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -16,9 +16,13 @@
#include <unistd.h>
#include <wchar.h>
-struct modeline {
- uint8_t *buffer;
-};
+static struct modeline { uint8_t *buffer; } g_modeline = {0};
+
+#define KILL_RING_SZ 64
+static struct kill_ring {
+ struct text_chunk buffer[KILL_RING_SZ];
+ uint32_t curr_idx;
+} g_kill_ring = {.curr_idx = 0};
struct update_hook_result buffer_linenum_hook(struct buffer *buffer,
struct command_list *commands,
@@ -33,18 +37,20 @@ struct update_hook_result buffer_modeline_hook(struct buffer *buffer,
void *userdata);
struct buffer buffer_create(char *name, bool modeline) {
- struct buffer b =
- (struct buffer){.filename = NULL,
- .name = strdup(name),
- .text = text_create(10),
- .dot_col = 0,
- .dot_line = 0,
- .keymaps = calloc(10, sizeof(struct keymap)),
- .nkeymaps = 1,
- .scroll_col = 0,
- .scroll_line = 0,
- .update_hooks = {0},
- .nkeymaps_max = 10};
+ struct buffer b = (struct buffer){
+ .filename = NULL,
+ .name = strdup(name),
+ .text = text_create(10),
+ .dot = {0},
+ .mark = {0},
+ .mark_set = false,
+ .keymaps = calloc(10, sizeof(struct keymap)),
+ .nkeymaps = 1,
+ .scroll_col = 0,
+ .scroll_line = 0,
+ .update_hooks = {0},
+ .nkeymaps_max = 10,
+ };
b.keymaps[0] = keymap_create("buffer-default", 128);
struct binding bindings[] = {
@@ -71,14 +77,19 @@ struct buffer buffer_create(char *name, bool modeline) {
BINDING(DELETE, "delete-char"),
BINDING(Ctrl, 'D', "delete-char"),
BINDING(BACKSPACE, "backward-delete-char"),
+
+ BINDING(Ctrl, '@', "set-mark"),
+
+ BINDING(Ctrl, 'W', "cut"),
+ BINDING(Ctrl, 'Y', "paste"),
+ BINDING(Meta, 'w', "copy"),
};
keymap_bind_keys(&b.keymaps[0], bindings,
sizeof(bindings) / sizeof(bindings[0]));
if (modeline) {
- struct modeline *modeline = calloc(1, sizeof(struct modeline));
- modeline->buffer = malloc(1024);
- buffer_add_update_hook(&b, buffer_modeline_hook, modeline);
+ g_modeline.buffer = malloc(1024);
+ buffer_add_update_hook(&b, buffer_modeline_hook, &g_modeline);
}
if (modeline) {
@@ -93,11 +104,19 @@ void buffer_destroy(struct buffer *buffer) {
free(buffer->text);
free(buffer->name);
free(buffer->filename);
+ for (uint32_t keymapi = 0; keymapi < buffer->nkeymaps; ++keymapi) {
+ keymap_destroy(&buffer->keymaps[keymapi]);
+ }
+ free(buffer->keymaps);
+
+ if (g_modeline.buffer != NULL) {
+ free(g_modeline.buffer);
+ }
}
void buffer_clear(struct buffer *buffer) {
text_clear(buffer->text);
- buffer->dot_col = buffer->dot_line = 0;
+ buffer->dot.col = buffer->dot.line = 0;
}
bool buffer_is_empty(struct buffer *buffer) {
@@ -121,53 +140,125 @@ void buffer_add_keymap(struct buffer *buffer, struct keymap *keymap) {
}
bool movev(struct buffer *buffer, int rowdelta) {
- int64_t new_line = (int64_t)buffer->dot_line + rowdelta;
+ int64_t new_line = (int64_t)buffer->dot.line + rowdelta;
if (new_line < 0) {
- buffer->dot_line = 0;
+ buffer->dot.line = 0;
return false;
} else if (new_line > text_num_lines(buffer->text) - 1) {
- buffer->dot_line = text_num_lines(buffer->text) - 1;
+ buffer->dot.line = text_num_lines(buffer->text) - 1;
return false;
} else {
- buffer->dot_line = (uint32_t)new_line;
+ buffer->dot.line = (uint32_t)new_line;
// make sure column stays on the line
- uint32_t linelen = text_line_length(buffer->text, buffer->dot_line);
- buffer->dot_col = buffer->dot_col > linelen ? linelen : buffer->dot_col;
+ uint32_t linelen = text_line_length(buffer->text, buffer->dot.line);
+ buffer->dot.col = buffer->dot.col > linelen ? linelen : buffer->dot.col;
return true;
}
}
// move dot `coldelta` chars
void moveh(struct buffer *buffer, int coldelta) {
- int64_t new_col = (int64_t)buffer->dot_col + coldelta;
+ int64_t new_col = (int64_t)buffer->dot.col + coldelta;
- if (new_col > (int64_t)text_line_length(buffer->text, buffer->dot_line)) {
+ if (new_col > (int64_t)text_line_length(buffer->text, buffer->dot.line)) {
if (movev(buffer, 1)) {
- buffer->dot_col = 0;
+ buffer->dot.col = 0;
}
} else if (new_col < 0) {
if (movev(buffer, -1)) {
- buffer->dot_col = text_line_length(buffer->text, buffer->dot_line);
+ buffer->dot.col = text_line_length(buffer->text, buffer->dot.line);
}
} else {
- buffer->dot_col = new_col;
+ buffer->dot.col = new_col;
+ }
+}
+
+struct region {
+ struct buffer_location begin;
+ struct buffer_location end;
+};
+
+struct region to_region(struct buffer_location dot,
+ struct buffer_location mark) {
+ struct region reg = {.begin = mark, .end = dot};
+
+ if (dot.line < mark.line || (dot.line == mark.line && dot.col < mark.col)) {
+ reg.begin = dot;
+ reg.end = mark;
+ }
+
+ return reg;
+}
+
+bool region_has_size(struct buffer *buffer) {
+ return buffer->mark_set &&
+ (labs((int64_t)buffer->mark.line - (int64_t)buffer->dot.line + 1) *
+ labs((int64_t)buffer->mark.col - (int64_t)buffer->dot.col)) > 0;
+}
+
+struct text_chunk *copy_region(struct buffer *buffer, struct region region) {
+ g_kill_ring.curr_idx = g_kill_ring.curr_idx + 1 % KILL_RING_SZ;
+ struct text_chunk *curr = &g_kill_ring.buffer[g_kill_ring.curr_idx];
+ if (curr != NULL) {
+ free(curr->text);
+ }
+
+ struct text_chunk txt =
+ text_get_region(buffer->text, region.begin.line, region.begin.col,
+ region.end.line, region.end.col);
+ *curr = txt;
+ return curr;
+}
+
+void buffer_copy(struct buffer *buffer) {
+ if (region_has_size(buffer)) {
+ struct region reg = to_region(buffer->dot, buffer->mark);
+ struct text_chunk *curr = copy_region(buffer, reg);
+ buffer_clear_mark(buffer);
+ }
+}
+
+void buffer_paste(struct buffer *buffer) {
+ struct text_chunk *curr = &g_kill_ring.buffer[g_kill_ring.curr_idx];
+ if (curr != NULL) {
+ buffer_add_text(buffer, curr->text, curr->nbytes);
+ }
+}
+
+void buffer_cut(struct buffer *buffer) {
+ if (region_has_size(buffer)) {
+ struct region reg = to_region(buffer->dot, buffer->mark);
+ copy_region(buffer, reg);
+ text_delete(buffer->text, reg.begin.line, reg.begin.col, reg.end.line,
+ reg.end.col);
+ buffer_clear_mark(buffer);
}
}
void buffer_kill_line(struct buffer *buffer) {
- uint32_t nchars =
- text_line_length(buffer->text, buffer->dot_line) - buffer->dot_col;
+ uint32_t nchars = text_line_length(buffer->text, buffer->dot.line);
if (nchars == 0) {
nchars = 1;
}
- text_delete(buffer->text, buffer->dot_line, buffer->dot_col, nchars);
+ struct region reg = {
+ .begin = buffer->dot,
+ .end =
+ {
+ .line = buffer->dot.line,
+ .col = buffer->dot.col + nchars,
+ },
+ };
+ copy_region(buffer, reg);
+ text_delete(buffer->text, buffer->dot.line, buffer->dot.col, buffer->dot.line,
+ buffer->dot.col + nchars);
}
void buffer_forward_delete_char(struct buffer *buffer) {
- text_delete(buffer->text, buffer->dot_line, buffer->dot_col, 1);
+ text_delete(buffer->text, buffer->dot.line, buffer->dot.col, buffer->dot.line,
+ buffer->dot.col + 1);
}
void buffer_backward_delete_char(struct buffer *buffer) {
@@ -180,9 +271,9 @@ void buffer_forward_char(struct buffer *buffer) { moveh(buffer, 1); }
void buffer_forward_word(struct buffer *buffer) {
moveh(buffer, 1);
- struct text_chunk line = text_get_line(buffer->text, buffer->dot_line);
+ struct text_chunk line = text_get_line(buffer->text, buffer->dot.line);
uint32_t bytei =
- text_col_to_byteindex(buffer->text, buffer->dot_line, buffer->dot_col);
+ text_col_to_byteindex(buffer->text, buffer->dot.line, buffer->dot.col);
for (; bytei < line.nbytes; ++bytei) {
uint8_t b = line.text[bytei];
if (b == ' ' || b == '.') {
@@ -191,15 +282,15 @@ void buffer_forward_word(struct buffer *buffer) {
}
uint32_t target_col =
- text_byteindex_to_col(buffer->text, buffer->dot_line, bytei);
- moveh(buffer, target_col - buffer->dot_col);
+ text_byteindex_to_col(buffer->text, buffer->dot.line, bytei);
+ moveh(buffer, target_col - buffer->dot.col);
}
void buffer_backward_word(struct buffer *buffer) {
moveh(buffer, -1);
- struct text_chunk line = text_get_line(buffer->text, buffer->dot_line);
+ struct text_chunk line = text_get_line(buffer->text, buffer->dot.line);
uint32_t bytei =
- text_col_to_byteindex(buffer->text, buffer->dot_line, buffer->dot_col);
+ text_col_to_byteindex(buffer->text, buffer->dot.line, buffer->dot.col);
for (; bytei > 0; --bytei) {
uint8_t b = line.text[bytei];
if (b == ' ' || b == '.') {
@@ -208,18 +299,18 @@ void buffer_backward_word(struct buffer *buffer) {
}
uint32_t target_col =
- text_byteindex_to_col(buffer->text, buffer->dot_line, bytei);
- moveh(buffer, (int32_t)target_col - buffer->dot_col);
+ text_byteindex_to_col(buffer->text, buffer->dot.line, bytei);
+ moveh(buffer, (int32_t)target_col - buffer->dot.col);
}
void buffer_backward_line(struct buffer *buffer) { movev(buffer, -1); }
void buffer_forward_line(struct buffer *buffer) { movev(buffer, 1); }
void buffer_end_of_line(struct buffer *buffer) {
- buffer->dot_col = text_line_length(buffer->text, buffer->dot_line);
+ buffer->dot.col = text_line_length(buffer->text, buffer->dot.line);
}
-void buffer_beginning_of_line(struct buffer *buffer) { buffer->dot_col = 0; }
+void buffer_beginning_of_line(struct buffer *buffer) { buffer->dot.col = 0; }
struct buffer buffer_from_file(char *filename) {
struct buffer b = buffer_create(basename((char *)filename), true);
@@ -288,13 +379,13 @@ void buffer_to_file(struct buffer *buffer) {
int buffer_add_text(struct buffer *buffer, uint8_t *text, uint32_t nbytes) {
uint32_t lines_added, cols_added;
- text_insert_at(buffer->text, buffer->dot_line, buffer->dot_col, text, nbytes,
+ text_insert_at(buffer->text, buffer->dot.line, buffer->dot.col, text, nbytes,
&lines_added, &cols_added);
movev(buffer, lines_added);
if (lines_added > 0) {
// does not make sense to use position from another line
- buffer->dot_col = 0;
+ buffer->dot.col = 0;
}
moveh(buffer, cols_added);
@@ -306,6 +397,7 @@ void buffer_newline(struct buffer *buffer) {
}
void buffer_indent(struct buffer *buffer) {
+ // TODO: config
buffer_add_text(buffer, (uint8_t *)" ", 4);
}
@@ -322,12 +414,34 @@ uint32_t buffer_add_update_hook(struct buffer *buffer, update_hook_cb hook,
return buffer->update_hooks.nhooks - 1;
}
+void buffer_set_mark(struct buffer *buffer) {
+ buffer->mark_set
+ ? buffer_clear_mark(buffer)
+ : buffer_set_mark_at(buffer, buffer->dot.line, buffer->dot.col);
+}
+
+void buffer_clear_mark(struct buffer *buffer) {
+ buffer->mark_set = false;
+ minibuffer_echo_timeout(2, "mark cleared");
+}
+
+void buffer_set_mark_at(struct buffer *buffer, uint32_t line, uint32_t col) {
+ buffer->mark_set = true;
+ buffer->mark.line = line;
+ buffer->mark.col = col;
+ minibuffer_echo_timeout(2, "mark set");
+}
+
struct cmdbuf {
struct command_list *cmds;
uint32_t scroll_line;
uint32_t line_offset;
uint32_t col_offset;
uint32_t width;
+
+ struct region region;
+ bool mark_set;
+
struct line_render_hook *line_render_hooks;
uint32_t nlinerender_hooks;
};
@@ -341,8 +455,59 @@ void render_line(struct text_chunk *line, void *userdata) {
}
command_list_set_show_whitespace(cmdbuf->cmds, true);
- command_list_draw_text(cmdbuf->cmds, cmdbuf->col_offset, visual_line,
- line->text, line->nbytes);
+ struct buffer_location *begin = &cmdbuf->region.begin,
+ *end = &cmdbuf->region.end;
+ if (cmdbuf->mark_set && line->line >= begin->line &&
+ line->line <= end->line) {
+ uint32_t byte_offset = 0;
+ uint32_t col_offset = 0;
+
+ // draw any text on the line that should not be part of region
+ if (begin->line == line->line) {
+ if (begin->col > 0) {
+ uint32_t nbytes = utf8_nbytes(line->text, line->nbytes, begin->col);
+ command_list_draw_text(cmdbuf->cmds, cmdbuf->col_offset, visual_line,
+ line->text, nbytes);
+
+ byte_offset += nbytes;
+ }
+
+ col_offset = begin->col;
+ }
+
+ // activate region color
+ command_list_set_index_color_bg(cmdbuf->cmds, 5);
+
+ // draw any text on line that should be part of region
+ if (end->line == line->line) {
+ if (end->col > 0) {
+ uint32_t nbytes =
+ utf8_nbytes(line->text + byte_offset, line->nbytes - byte_offset,
+ end->col - col_offset);
+ command_list_draw_text(cmdbuf->cmds, cmdbuf->col_offset + col_offset,
+ visual_line, line->text + byte_offset, nbytes);
+ byte_offset += nbytes;
+ }
+
+ col_offset = end->col;
+ command_list_reset_color(cmdbuf->cmds);
+ }
+
+ // draw rest of line
+ if (line->nbytes - byte_offset > 0) {
+ command_list_draw_text(cmdbuf->cmds, cmdbuf->col_offset + col_offset,
+ visual_line, line->text + byte_offset,
+ line->nbytes - byte_offset);
+ }
+
+ // done rendering region
+ command_list_reset_color(cmdbuf->cmds);
+
+ } else {
+ command_list_draw_text(cmdbuf->cmds, cmdbuf->col_offset, visual_line,
+ line->text, line->nbytes);
+ }
+
command_list_set_show_whitespace(cmdbuf->cmds, false);
uint32_t col = line->nchars + cmdbuf->col_offset;
@@ -372,7 +537,7 @@ void scroll(struct buffer *buffer, int line_delta, int col_delta) {
int64_t new_col = (int64_t)buffer->scroll_col + col_delta;
if (new_col >= 0 &&
- new_col < text_line_length(buffer->text, buffer->dot_line)) {
+ new_col < text_line_length(buffer->text, buffer->dot.line)) {
buffer->scroll_col = (uint32_t)new_col;
}
}
@@ -385,10 +550,10 @@ void to_relative(struct buffer *buffer, uint32_t line, uint32_t col,
uint32_t visual_dot_col(struct buffer *buffer, uint32_t dot_col) {
uint32_t visual_dot_col = dot_col;
- struct text_chunk line = text_get_line(buffer->text, buffer->dot_line);
+ struct text_chunk line = text_get_line(buffer->text, buffer->dot.line);
for (uint32_t bytei = 0;
bytei <
- text_col_to_byteindex(buffer->text, buffer->dot_line, buffer->dot_col);
+ text_col_to_byteindex(buffer->text, buffer->dot.line, buffer->dot.col);
++bytei) {
if (line.text[bytei] == '\t') {
visual_dot_col += 3;
@@ -424,8 +589,8 @@ struct update_hook_result buffer_modeline_hook(struct buffer *buffer,
struct tm *lt = localtime(&now);
char left[128], right[128];
- snprintf(left, 128, " %-16s (%d, %d)", buffer->name, buffer->dot_line + 1,
- visual_dot_col(buffer, buffer->dot_col));
+ snprintf(left, 128, " %-16s (%d, %d)", buffer->name, buffer->dot.line + 1,
+ visual_dot_col(buffer, buffer->dot.col));
snprintf(right, 128, "(%.2f ms) %02d:%02d", frame_time / 1e6, lt->tm_hour,
lt->tm_min);
@@ -497,7 +662,7 @@ struct update_hook_result buffer_linenum_hook(struct buffer *buffer,
}
linenum_data.longest_nchars = longest_nchars;
- linenum_data.dot_line = buffer->dot_line;
+ linenum_data.dot_line = buffer->dot.line;
struct update_hook_result res = {0};
res.margins.left = longest_nchars + 2;
res.line_render_hook.callback = linenum_render_hook;
@@ -539,7 +704,7 @@ void buffer_update(struct buffer *buffer, uint32_t width, uint32_t height,
}
int64_t rel_line, rel_col;
- to_relative(buffer, buffer->dot_line, buffer->dot_col, &rel_line, &rel_col);
+ to_relative(buffer, buffer->dot.line, buffer->dot.col, &rel_line, &rel_col);
int line_delta = 0, col_delta = 0;
if (rel_line < 0) {
line_delta = -(int)height / 2;
@@ -563,6 +728,8 @@ void buffer_update(struct buffer *buffer, uint32_t width, uint32_t height,
.line_offset = total_margins.top,
.line_render_hooks = line_hooks,
.nlinerender_hooks = nlinehooks,
+ .mark_set = buffer->mark_set,
+ .region = to_region(buffer->dot, buffer->mark),
};
text_for_each_line(buffer->text, buffer->scroll_line, height, render_line,
&cmdbuf);
@@ -575,9 +742,9 @@ void buffer_update(struct buffer *buffer, uint32_t width, uint32_t height,
}
// update the visual cursor position
- to_relative(buffer, buffer->dot_line, buffer->dot_col, &rel_line, &rel_col);
- uint32_t visual_col = visual_dot_col(buffer, buffer->dot_col);
- to_relative(buffer, buffer->dot_line, visual_col, &rel_line, &rel_col);
+ to_relative(buffer, buffer->dot.line, buffer->dot.col, &rel_line, &rel_col);
+ uint32_t visual_col = visual_dot_col(buffer, buffer->dot.col);
+ to_relative(buffer, buffer->dot.line, visual_col, &rel_line, &rel_col);
*relline = rel_line < 0 ? 0 : (uint32_t)rel_line + total_margins.top;
*relcol = rel_col < 0 ? 0 : (uint32_t)rel_col + total_margins.left;
diff --git a/src/buffer.h b/src/buffer.h
index a565a6e..14389e7 100644
--- a/src/buffer.h
+++ b/src/buffer.h
@@ -74,6 +74,11 @@ struct update_hooks {
uint32_t nhooks;
};
+struct buffer_location {
+ uint32_t line;
+ uint32_t col;
+};
+
/**
* A buffer of text that can be modified, read from and written to disk.
*
@@ -81,13 +86,18 @@ struct update_hooks {
* implemented on top of it.
*/
struct buffer {
+
+ /** Buffer name */
char *name;
+ /** Associated filename, this is where the buffer will be saved to */
char *filename;
+ /** Text data structure */
struct text *text;
- uint32_t dot_line;
- uint32_t dot_col;
+ struct buffer_location dot;
+ struct buffer_location mark;
+ bool mark_set;
// local keymaps
struct keymap *keymaps;
@@ -124,6 +134,15 @@ void buffer_beginning_of_line(struct buffer *buffer);
void buffer_newline(struct buffer *buffer);
void buffer_indent(struct buffer *buffer);
+void buffer_set_mark(struct buffer *buffer);
+void buffer_clear_mark(struct buffer *buffer);
+void buffer_set_mark_at(struct buffer *buffer, uint32_t line, uint32_t col);
+
+void buffer_copy(struct buffer *buffer);
+void buffer_paste(struct buffer *buffer);
+void buffer_paste_older(struct buffer *buffer);
+void buffer_cut(struct buffer *buffer);
+
struct text_chunk buffer_get_line(struct buffer *buffer, uint32_t line);
uint32_t buffer_add_update_hook(struct buffer *buffer, update_hook_cb hook,
@@ -158,6 +177,11 @@ BUFFER_WRAPCMD(buffer_beginning_of_line);
BUFFER_WRAPCMD(buffer_newline);
BUFFER_WRAPCMD(buffer_indent);
BUFFER_WRAPCMD(buffer_to_file);
+BUFFER_WRAPCMD(buffer_set_mark);
+BUFFER_WRAPCMD(buffer_clear_mark);
+BUFFER_WRAPCMD(buffer_copy);
+BUFFER_WRAPCMD(buffer_cut);
+BUFFER_WRAPCMD(buffer_paste);
static struct command BUFFER_COMMANDS[] = {
{.name = "kill-line", .fn = buffer_kill_line_cmd},
@@ -174,4 +198,9 @@ static struct command BUFFER_COMMANDS[] = {
{.name = "newline", .fn = buffer_newline_cmd},
{.name = "indent", .fn = buffer_indent_cmd},
{.name = "buffer-write-to-file", .fn = buffer_to_file_cmd},
+ {.name = "set-mark", .fn = buffer_set_mark_cmd},
+ {.name = "clear-mark", .fn = buffer_clear_mark_cmd},
+ {.name = "copy", .fn = buffer_copy_cmd},
+ {.name = "cut", .fn = buffer_cut_cmd},
+ {.name = "paste", .fn = buffer_paste_cmd},
};
diff --git a/src/buffers.c b/src/buffers.c
index 479caa5..38b51b7 100644
--- a/src/buffers.c
+++ b/src/buffers.c
@@ -39,4 +39,5 @@ void buffers_destroy(struct buffers *buffers) {
}
buffers->nbuffers = 0;
+ free(buffers->buffers);
}
diff --git a/src/display.c b/src/display.c
index 2f147d5..39993ef 100644
--- a/src/display.c
+++ b/src/display.c
@@ -127,9 +127,9 @@ void putbyte(uint8_t c) {
void putbyte_ws(uint8_t c, bool show_whitespace) {
if (show_whitespace && c == '\t') {
- fputs("\x1b[90m → \x1b[0m", stdout);
+ fputs("\x1b[90m → \x1b[39m", stdout);
} else if (show_whitespace && c == ' ') {
- fputs("\x1b[90m·\x1b[0m", stdout);
+ fputs("\x1b[90m·\x1b[39m", stdout);
} else {
putbyte(c);
}
diff --git a/src/main.c b/src/main.c
index 3e4f334..0765344 100644
--- a/src/main.c
+++ b/src/main.c
@@ -309,7 +309,9 @@ int main(int argc, char *argv[]) {
display_clear(display);
display_destroy(display);
keymap_destroy(&global_keymap);
+ keymap_destroy(&ctrlx_map);
command_registry_destroy(&commands);
+ reactor_destroy(reactor);
frame_allocator_destroy(&frame_allocator);
return 0;
diff --git a/src/minibuffer.c b/src/minibuffer.c
index 5bf5043..41c669c 100644
--- a/src/minibuffer.c
+++ b/src/minibuffer.c
@@ -99,6 +99,8 @@ void minibuffer_init(struct buffer *buffer) {
}
g_minibuffer.buffer = buffer;
+ g_minibuffer.keymap = keymap_create("minibuffer", 10);
+
struct binding bindings[] = {
ANONYMOUS_BINDING(Ctrl, 'M', &execute_minibuffer_command),
ANONYMOUS_BINDING(Ctrl, 'I', &complete_minibuffer_command),
@@ -106,6 +108,7 @@ void minibuffer_init(struct buffer *buffer) {
keymap_bind_keys(&g_minibuffer.keymap, bindings,
sizeof(bindings) / sizeof(bindings[0]));
buffer_add_keymap(g_minibuffer.buffer, &g_minibuffer.keymap);
+
buffer_add_update_hook(g_minibuffer.buffer, update, &g_minibuffer);
}
diff --git a/src/text.c b/src/text.c
index ec80643..6e343ae 100644
--- a/src/text.c
+++ b/src/text.c
@@ -255,56 +255,44 @@ void text_insert_at(struct text *text, uint32_t line, uint32_t col,
*lines_added = line - start_line;
}
-void text_delete(struct text *text, uint32_t line, uint32_t col,
- uint32_t nchars) {
+void text_delete(struct text *text, uint32_t start_line, uint32_t start_col,
+ uint32_t end_line, uint32_t end_col) {
- struct line *lp = &text->lines[line];
- if (col > lp->nchars) {
+ struct line *firstline = &text->lines[start_line];
+ struct line *lastline = &text->lines[end_line];
+ if (start_col > firstline->nchars) {
return;
}
- // delete chars from current line
- uint32_t chars_initial_line =
- col + nchars > lp->nchars ? (lp->nchars - col) : nchars;
- uint32_t bytei = charidx_to_byteidx(lp, col);
- uint32_t nbytes =
- utf8_nbytes(lp->data + bytei, lp->nbytes - bytei, chars_initial_line);
-
- memcpy(lp->data + bytei, lp->data + bytei + nbytes,
- lp->nbytes - (bytei + nbytes));
-
- lp->nbytes -= nbytes;
- lp->nchars -= chars_initial_line;
- lp->flags |= LineChanged;
-
- uint32_t initial_line = line;
- uint32_t left_to_delete = nchars - chars_initial_line;
-
- // grab remaining chars from last line to delete from (if any)
- uint32_t src_col = 0;
- while (left_to_delete > 0 && line < text->nlines) {
- ++line;
- --left_to_delete; // newline char
-
- struct line *lp = &text->lines[line];
- uint32_t deleted_in_line =
- left_to_delete > lp->nchars ? lp->nchars : left_to_delete;
- src_col = deleted_in_line;
- left_to_delete -= deleted_in_line;
+ // handle deletion of newlines
+ if (end_col > lastline->nchars) {
+ ++end_line;
+ end_col = 0;
+ lastline = &text->lines[end_line];
}
- if (line != initial_line) {
- struct line *lp = &text->lines[line];
- uint32_t bytei = charidx_to_byteidx(lp, src_col);
- if (src_col < lp->nchars) {
- insert_at_col(&text->lines[initial_line], col, lp->data + bytei,
- lp->nbytes - bytei, lp->nchars - src_col);
- }
+ uint32_t bytei = utf8_nbytes(lastline->data, lastline->nbytes, end_col);
+ if (lastline == firstline) {
+ // in this case we can "overwrite"
+ uint32_t dstbytei =
+ utf8_nbytes(firstline->data, firstline->nbytes, start_col);
+ memcpy(firstline->data + dstbytei, lastline->data + bytei,
+ lastline->nbytes - bytei);
+ } else {
+ // otherwise we actually have to copy from the last line
+ insert_at_col(firstline, start_col, lastline->data + bytei,
+ lastline->nbytes - bytei, lastline->nchars - end_col);
}
- // delete all lines from current line + 1 to (and including) last line
- for (uint32_t li = initial_line + 1; li <= line && li < text->nlines; ++li) {
- delete_line(text, li);
+ firstline->nchars = start_col + (lastline->nchars - end_col);
+ firstline->nbytes =
+ utf8_nbytes(firstline->data, firstline->nbytes, start_col) +
+ (lastline->nbytes - bytei);
+
+ // delete full lines
+ for (uint32_t linei = start_line + 1;
+ linei <= end_line && linei < text->nlines; ++linei) {
+ delete_line(text, linei);
}
}
@@ -339,6 +327,71 @@ struct text_chunk text_get_line(struct text *text, uint32_t line) {
};
}
+struct copy_cmd {
+ uint32_t line;
+ uint32_t byteindex;
+ uint32_t nbytes;
+};
+
+struct text_chunk text_get_region(struct text *text, uint32_t start_line,
+ uint32_t start_col, uint32_t end_line,
+ uint32_t end_col) {
+ struct copy_cmd *copy_cmds = malloc(end_line - start_line + 1);
+
+ uint32_t total_chars = 0, total_bytes = 0;
+ for (uint32_t line = start_line; line <= end_line; ++line) {
+ struct line *l = &text->lines[line];
+ total_chars += l->nchars;
+ total_bytes += l->nbytes;
+
+ struct copy_cmd *cmd = &copy_cmds[line - start_line];
+ cmd->line = line;
+ cmd->byteindex = 0;
+ cmd->nbytes = l->nbytes;
+
+ ++line;
+ }
+
+ // correct first line
+ struct copy_cmd *cmd_first = &copy_cmds[start_line];
+ struct line *first_line = &text->lines[start_line];
+ uint32_t byteoff =
+ utf8_nbytes(first_line->data, first_line->nbytes, start_col);
+ cmd_first->byteindex += byteoff;
+ cmd_first->nbytes -= byteoff;
+ total_bytes -= byteoff;
+ total_chars -= start_col;
+
+ // correct last line
+ struct copy_cmd *cmd_last = &copy_cmds[end_line];
+ struct line *last_line = &text->lines[end_line];
+ uint32_t byteindex = utf8_nbytes(last_line->data, last_line->nbytes, end_col);
+ cmd_last->nbytes -= (last_line->nchars - end_col);
+ total_bytes -= (last_line->nbytes - byteindex);
+ total_chars -= (last_line->nchars - end_col);
+
+ struct text_chunk txt = {
+ .text = (uint8_t *)malloc(total_bytes + end_line - start_line),
+ .line = 0,
+ .nbytes = total_bytes,
+ .nchars = total_chars,
+ };
+
+ // copy data
+ for (uint32_t cmdi = 0, curr = 0; cmdi <= end_line - start_line; ++cmdi) {
+ struct copy_cmd *c = &copy_cmds[cmdi];
+ struct line *l = &text->lines[c->line];
+ memcpy(txt.text + curr, l->data + c->byteindex, c->nbytes);
+ curr += c->nbytes;
+
+ if (cmdi != end_line - start_line) {
+ txt.text[++curr] = '\n';
+ }
+ }
+
+ return txt;
+}
+
bool text_line_contains_unicode(struct text *text, uint32_t line) {
return text->lines[line].nbytes != text->lines[line].nchars;
}
diff --git a/src/text.h b/src/text.h
index 85eb522..12fe576 100644
--- a/src/text.h
+++ b/src/text.h
@@ -22,8 +22,8 @@ void text_insert_at(struct text *text, uint32_t line, uint32_t col,
void text_append(struct text *text, uint8_t *bytes, uint32_t nbytes,
uint32_t *lines_added, uint32_t *cols_added);
-void text_delete(struct text *text, uint32_t line, uint32_t col,
- uint32_t nchars);
+void text_delete(struct text *text, uint32_t start_line, uint32_t start_col,
+ uint32_t end_line, uint32_t end_col);
uint32_t text_num_lines(struct text *text);
uint32_t text_line_length(struct text *text, uint32_t lineidx);
@@ -46,5 +46,8 @@ void text_for_each_line(struct text *text, uint32_t line, uint32_t nlines,
void text_for_each_chunk(struct text *text, chunk_cb callback, void *userdata);
struct text_chunk text_get_line(struct text *text, uint32_t line);
+struct text_chunk text_get_region(struct text *text, uint32_t start_line,
+ uint32_t start_col, uint32_t end_line,
+ uint32_t end_col);
bool text_line_contains_unicode(struct text *text, uint32_t line);