summaryrefslogtreecommitdiff
path: root/src/dged/buffer.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/dged/buffer.c')
-rw-r--r--src/dged/buffer.c1270
1 files changed, 425 insertions, 845 deletions
diff --git a/src/dged/buffer.c b/src/dged/buffer.c
index c826401..117fab5 100644
--- a/src/dged/buffer.c
+++ b/src/dged/buffer.c
@@ -29,7 +29,7 @@ struct modeline {
#define KILL_RING_SZ 64
static struct kill_ring {
struct text_chunk buffer[KILL_RING_SZ];
- struct buffer_location last_paste;
+ struct location last_paste;
bool paste_up_to_date;
uint32_t curr_idx;
uint32_t paste_idx;
@@ -46,67 +46,6 @@ static struct create_hook {
} g_create_hooks[MAX_CREATE_HOOKS];
static uint32_t g_num_create_hooks = 0;
-struct update_hook_result buffer_linenum_hook(struct buffer_view *view,
- struct command_list *commands,
- uint32_t width, uint32_t height,
- uint64_t frame_time,
- void *userdata);
-
-struct update_hook_result buffer_modeline_hook(struct buffer_view *view,
- struct command_list *commands,
- uint32_t width, uint32_t height,
- uint64_t frame_time,
- void *userdata);
-
-struct buffer_view buffer_view_create(struct buffer *buffer, bool modeline,
- bool line_numbers) {
- struct buffer_view view = {
- .dot = {0},
- .mark = {0},
- .mark_set = false,
- .scroll = {0},
- .buffer = buffer,
- .modeline = NULL,
- .line_numbers = line_numbers,
- };
-
- if (modeline) {
- view.modeline = calloc(1, sizeof(struct modeline));
- view.modeline->buffer = malloc(1024);
- view.modeline->sz = 1024;
- view.modeline->buffer[0] = '\0';
- }
-
- return view;
-}
-
-struct buffer_view buffer_view_clone(struct buffer_view *view) {
- struct buffer_view c = {
- .dot = view->dot,
- .mark = view->mark,
- .mark_set = view->mark_set,
- .scroll = view->scroll,
- .buffer = view->buffer,
- .modeline = NULL,
- .line_numbers = view->line_numbers,
- };
-
- if (view->modeline) {
- c.modeline = calloc(1, sizeof(struct modeline));
- c.modeline->buffer = malloc(view->modeline->sz);
- memcpy(c.modeline->buffer, view->modeline->buffer, view->modeline->sz);
- }
-
- return c;
-}
-
-void buffer_view_destroy(struct buffer_view *view) {
- if (view->modeline != NULL) {
- free(view->modeline->buffer);
- free(view->modeline);
- }
-}
-
uint32_t buffer_add_create_hook(create_hook_cb hook, void *userdata) {
if (g_num_create_hooks < MAX_CREATE_HOOKS) {
g_create_hooks[g_num_create_hooks] = (struct create_hook){
@@ -119,56 +58,6 @@ uint32_t buffer_add_create_hook(create_hook_cb hook, void *userdata) {
return g_num_create_hooks - 1;
}
-struct buffer create_internal(char *name, char *filename) {
- struct buffer b = (struct buffer){
- .filename = filename,
- .name = strdup(name),
- .text = text_create(10),
- .modified = false,
- .readonly = false,
- .lang =
- filename != NULL ? lang_from_filename(filename) : lang_from_id("fnd"),
- .last_write = {0},
- };
-
- VEC_INIT(&b.text_properties, 32);
-
- undo_init(&b.undo, 100);
-
- return b;
-}
-
-struct buffer buffer_create(char *name) {
-
- struct buffer b = create_internal(name, NULL);
-
- for (uint32_t hooki = 0; hooki < g_num_create_hooks; ++hooki) {
- g_create_hooks[hooki].callback(&b, g_create_hooks[hooki].userdata);
- }
-
- return b;
-}
-
-void buffer_destroy(struct buffer *buffer) {
- VEC_DESTROY(&buffer->text_properties);
- text_destroy(buffer->text);
- buffer->text = NULL;
-
- free(buffer->name);
- buffer->name = NULL;
-
- free(buffer->filename);
- buffer->filename = NULL;
-
- undo_destroy(&buffer->undo);
-}
-
-void buffer_clear(struct buffer_view *view) {
- text_clear(view->buffer->text);
- view->dot.col = view->dot.line = 0;
- view->scroll.col = view->scroll.line = 0;
-}
-
void buffer_static_init() {
settings_register_setting(
"editor.tab-width",
@@ -187,335 +76,95 @@ void buffer_static_teardown() {
}
}
-bool buffer_is_empty(struct buffer *buffer) {
- return text_num_lines(buffer->text) == 0;
-}
-
-bool buffer_is_modified(struct buffer *buffer) { return buffer->modified; }
-
-bool buffer_is_readonly(struct buffer *buffer) { return buffer->readonly; }
-
-void buffer_set_readonly(struct buffer *buffer, bool readonly) {
- buffer->readonly = readonly;
-}
-
-bool buffer_is_backed(struct buffer *buffer) {
- return buffer->filename != NULL;
-}
-
-void delete_with_undo(struct buffer *buffer, struct buffer_location start,
- struct buffer_location end) {
- if (buffer->readonly) {
- minibuffer_echo_timeout(4, "buffer is read-only");
- return;
- }
-
- struct text_chunk txt =
- text_get_region(buffer->text, start.line, start.col, end.line, end.col);
-
- undo_push_delete(
- &buffer->undo,
- (struct undo_delete){.data = txt.text,
- .nbytes = txt.nbytes,
- .pos = {.row = start.line, .col = start.col}});
- undo_push_boundary(&buffer->undo,
- (struct undo_boundary){.save_point = false});
+static struct buffer create_internal(const char *name, char *filename) {
+ struct buffer b = (struct buffer){
+ .filename = filename,
+ .name = strdup(name),
+ .text = text_create(10),
+ .modified = false,
+ .readonly = false,
+ .lang =
+ filename != NULL ? lang_from_filename(filename) : lang_from_id("fnd"),
+ .last_write = {0},
+ };
- text_delete(buffer->text, start.line, start.col, end.line, end.col);
- buffer->modified = true;
-}
+ VEC_INIT(&b.update_hooks, 32);
-void buffer_goto_beginning(struct buffer_view *view) {
- view->dot.col = 0;
- view->dot.line = 0;
-}
+ undo_init(&b.undo, 100);
-void buffer_goto_end(struct buffer_view *view) {
- view->dot.line = text_num_lines(view->buffer->text);
- view->dot.col = 0;
+ return b;
}
-bool movev(struct buffer_view *view, int rowdelta) {
- int64_t new_line = (int64_t)view->dot.line + rowdelta;
-
+static bool movev(struct buffer *buffer, int64_t linedelta,
+ struct location *location) {
+ int64_t new_line = (int64_t)location->line + linedelta;
if (new_line < 0) {
- view->dot.line = 0;
+ location->line = 0;
return false;
- } else if (new_line > text_num_lines(view->buffer->text)) {
- view->dot.line = text_num_lines(view->buffer->text);
+ } else if (new_line > text_num_lines(buffer->text)) {
+ // allow addition of an extra line by going past the bottom
+ location->line = text_num_lines(buffer->text);
return false;
} else {
- view->dot.line = (uint32_t)new_line;
+ location->line = (uint32_t)new_line;
// make sure column stays on the line
- uint32_t linelen = text_line_length(view->buffer->text, view->dot.line);
- view->dot.col = view->dot.col > linelen ? linelen : view->dot.col;
+ uint32_t linelen = text_line_length(buffer->text, location->line);
+ location->col = location->col > linelen ? linelen : location->col;
return true;
}
}
// move dot `coldelta` chars
-bool moveh(struct buffer_view *view, int coldelta) {
- int64_t new_col = (int64_t)view->dot.col + coldelta;
-
- if (new_col > (int64_t)text_line_length(view->buffer->text, view->dot.line)) {
- if (movev(view, 1)) {
- view->dot.col = 0;
+static bool moveh(struct buffer *buffer, int64_t coldelta,
+ struct location *location) {
+ int64_t new_col = (int64_t)location->col + coldelta;
+ if (new_col > (int64_t)text_line_length(buffer->text, location->line)) {
+ if (movev(buffer, 1, location)) {
+ location->col = 0;
}
} else if (new_col < 0) {
- if (movev(view, -1)) {
- view->dot.col = text_line_length(view->buffer->text, view->dot.line);
+ if (movev(buffer, -1, location)) {
+ location->col = text_line_length(buffer->text, location->line);
} else {
return false;
}
} else {
- view->dot.col = new_col;
+ location->col = new_col;
}
return true;
}
-void buffer_goto(struct buffer_view *view, uint32_t line, uint32_t col) {
- int64_t linedelta = (int64_t)line - (int64_t)view->dot.line;
- movev(view, linedelta);
-
- int64_t coldelta = (int64_t)col - (int64_t)view->dot.col;
- moveh(view, coldelta);
-}
-
-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;
-}
-
-struct region buffer_get_region(struct buffer_view *view) {
- return to_region(view->dot, view->mark);
-}
-
-bool buffer_region_has_size(struct buffer_view *view) {
- return view->mark_set &&
- (labs((int64_t)view->mark.line - (int64_t)view->dot.line) +
- labs((int64_t)view->mark.col - (int64_t)view->dot.col)) > 0;
-}
-
-struct text_chunk *copy_region(struct buffer *buffer, struct region region) {
- struct text_chunk *curr = &g_kill_ring.buffer[g_kill_ring.curr_idx];
- g_kill_ring.curr_idx = (g_kill_ring.curr_idx + 1) % KILL_RING_SZ;
-
- if (curr->allocated) {
- 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_view *view) {
- if (buffer_region_has_size(view)) {
- struct region reg = buffer_get_region(view);
- struct text_chunk *curr = copy_region(view->buffer, reg);
- buffer_clear_mark(view);
- }
-}
-
-void paste(struct buffer_view *view, uint32_t ring_idx) {
- if (ring_idx > 0) {
- struct text_chunk *curr = &g_kill_ring.buffer[ring_idx - 1];
- if (curr->text != NULL) {
- g_kill_ring.last_paste = view->mark_set ? view->mark : view->dot;
- buffer_add_text(view, curr->text, curr->nbytes);
- g_kill_ring.paste_up_to_date = true;
- }
- }
-}
-
-void buffer_paste(struct buffer_view *view) {
- g_kill_ring.paste_idx = g_kill_ring.curr_idx;
- paste(view, g_kill_ring.curr_idx);
-}
-
-void buffer_paste_older(struct buffer_view *view) {
- if (g_kill_ring.paste_up_to_date) {
-
- // remove previous paste
- struct text_chunk *curr = &g_kill_ring.buffer[g_kill_ring.curr_idx];
- delete_with_undo(view->buffer, g_kill_ring.last_paste, view->dot);
-
- // place ourselves right
- view->dot = g_kill_ring.last_paste;
-
- // paste older
- if (g_kill_ring.paste_idx - 1 > 0) {
- --g_kill_ring.paste_idx;
- } else {
- g_kill_ring.paste_idx = g_kill_ring.curr_idx;
- }
-
- paste(view, g_kill_ring.paste_idx);
-
- } else {
- buffer_paste(view);
- }
-}
-
-void buffer_cut(struct buffer_view *view) {
- if (buffer_region_has_size(view)) {
- struct region reg = buffer_get_region(view);
- copy_region(view->buffer, reg);
- delete_with_undo(view->buffer, reg.begin, reg.end);
- buffer_clear_mark(view);
- view->dot = reg.begin;
- }
-}
-
-bool maybe_delete_region(struct buffer_view *view) {
- if (buffer_region_has_size(view)) {
- struct region reg = buffer_get_region(view);
- delete_with_undo(view->buffer, reg.begin, reg.end);
- buffer_clear_mark(view);
- view->dot = reg.begin;
- return true;
- }
-
- return false;
-}
-
-void buffer_kill_line(struct buffer_view *view) {
- uint32_t nchars =
- text_line_length(view->buffer->text, view->dot.line) - view->dot.col;
- if (nchars == 0) {
- nchars = 1;
- }
-
- struct region reg = {
- .begin = view->dot,
- .end =
- {
- .line = view->dot.line,
- .col = view->dot.col + nchars,
- },
- };
-
- copy_region(view->buffer, reg);
- delete_with_undo(view->buffer, view->dot,
- (struct buffer_location){
- .line = view->dot.line,
- .col = view->dot.col + nchars,
- });
-}
-
-void buffer_forward_delete_char(struct buffer_view *view) {
- if (maybe_delete_region(view)) {
- return;
- }
-
- delete_with_undo(view->buffer, view->dot,
- (struct buffer_location){
- .line = view->dot.line,
- .col = view->dot.col + 1,
- });
-}
-
-void buffer_backward_delete_char(struct buffer_view *view) {
- if (maybe_delete_region(view)) {
- return;
- }
-
- if (moveh(view, -1)) {
- buffer_forward_delete_char(view);
- }
-}
-
-void buffer_forward_delete_word(struct buffer_view *view) {
- if (maybe_delete_region(view)) {
- return;
- }
-
- struct buffer_location start = view->dot;
- buffer_forward_word(view);
- struct buffer_location end = view->dot;
-
- buffer_goto(view, start.line, start.col);
-
- delete_with_undo(view->buffer, start, end);
-}
-
-void buffer_backward_delete_word(struct buffer_view *view) {
- if (maybe_delete_region(view)) {
+static void delete_with_undo(struct buffer *buffer, struct location start,
+ struct location end) {
+ if (buffer->readonly) {
+ minibuffer_echo_timeout(4, "buffer is read-only");
return;
}
- struct buffer_location end = view->dot;
- buffer_backward_word(view);
- struct buffer_location start = view->dot;
+ struct text_chunk txt =
+ text_get_region(buffer->text, start.line, start.col, end.line, end.col);
- buffer_goto(view, start.line, start.col);
+ undo_push_delete(
+ &buffer->undo,
+ (struct undo_delete){.data = txt.text,
+ .nbytes = txt.nbytes,
+ .pos = {.row = start.line, .col = start.col}});
+ undo_push_boundary(&buffer->undo,
+ (struct undo_boundary){.save_point = false});
- delete_with_undo(view->buffer, start, end);
+ text_delete(buffer->text, start.line, start.col, end.line, end.col);
+ buffer->modified = true;
}
-void buffer_backward_char(struct buffer_view *view) { moveh(view, -1); }
-void buffer_forward_char(struct buffer_view *view) { moveh(view, 1); }
-
-struct buffer_location find_next(struct buffer_view *view, uint8_t chars[],
- uint32_t nchars, int direction) {
- struct text_chunk line = text_get_line(view->buffer->text, view->dot.line);
- int64_t bytei =
- text_col_to_byteindex(view->buffer->text, view->dot.line, view->dot.col);
- while (bytei < line.nbytes && bytei > 0 &&
- (line.text[bytei] == ' ' || line.text[bytei] == '.')) {
- bytei += direction;
- }
-
- for (; bytei < line.nbytes && bytei > 0; bytei += direction) {
- uint8_t b = line.text[bytei];
- if (b == ' ' || b == '.') {
- break;
- }
+static void maybe_delete_region(struct buffer *buffer, struct region region) {
+ if (region_has_size(region)) {
+ delete_with_undo(buffer, region.begin, region.end);
}
-
- uint32_t target_col =
- text_byteindex_to_col(view->buffer->text, view->dot.line, bytei);
- return (struct buffer_location){.line = view->dot.line, .col = target_col};
}
-void buffer_forward_word(struct buffer_view *view) {
- moveh(view, 1);
- uint8_t chars[] = {' ', '.'};
- view->dot = find_next(view, chars, 2, 1);
-}
-
-void buffer_backward_word(struct buffer_view *view) {
- moveh(view, -1);
- uint8_t chars[] = {' ', '.'};
- view->dot = find_next(view, chars, 2, -1);
-}
-
-void buffer_backward_line(struct buffer_view *view) { movev(view, -1); }
-void buffer_forward_line(struct buffer_view *view) { movev(view, 1); }
-
-void buffer_end_of_line(struct buffer_view *view) {
- view->dot.col = text_line_length(view->buffer->text, view->dot.line);
-}
-
-void buffer_beginning_of_line(struct buffer_view *view) { view->dot.col = 0; }
-
-void buffer_read_from_file(struct buffer *b) {
+static void buffer_read_from_file(struct buffer *b) {
struct stat sb;
char *fullname = to_abspath(b->filename);
if (stat(fullname, &sb) == 0) {
@@ -554,10 +203,56 @@ void buffer_read_from_file(struct buffer *b) {
undo_push_boundary(&b->undo, (struct undo_boundary){.save_point = true});
}
-struct buffer buffer_from_file(char *filename) {
- char *full_filename = to_abspath(filename);
- struct buffer b = create_internal(basename((char *)filename), full_filename);
- buffer_read_from_file(&b);
+static void write_line(struct text_chunk *chunk, void *userdata) {
+ FILE *file = (FILE *)userdata;
+ fwrite(chunk->text, 1, chunk->nbytes, file);
+
+ // final newline is not optional!
+ fputc('\n', file);
+}
+
+static struct location find_next(struct buffer *buffer, struct location from,
+ uint8_t chars[], uint32_t nchars,
+ int direction) {
+ struct text_chunk line = text_get_line(buffer->text, from.line);
+ int64_t bytei = text_col_to_byteindex(buffer->text, from.line, from.col);
+ while (bytei < line.nbytes && bytei > 0 &&
+ (line.text[bytei] == ' ' || line.text[bytei] == '.')) {
+ bytei += direction;
+ }
+
+ for (; bytei < line.nbytes && bytei > 0; bytei += direction) {
+ uint8_t b = line.text[bytei];
+ if (b == ' ' || b == '.') {
+ break;
+ }
+ }
+
+ uint32_t target_col = text_byteindex_to_col(buffer->text, from.line, bytei);
+ return (struct location){.line = from.line, .col = target_col};
+}
+
+static struct text_chunk *copy_region(struct buffer *buffer,
+ struct region region) {
+ struct text_chunk *curr = &g_kill_ring.buffer[g_kill_ring.curr_idx];
+ g_kill_ring.curr_idx = (g_kill_ring.curr_idx + 1) % KILL_RING_SZ;
+
+ if (curr->allocated) {
+ 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;
+}
+
+/* --------------------- buffer methods -------------------- */
+
+struct buffer buffer_create(const char *name) {
+
+ struct buffer b = create_internal(name, NULL);
for (uint32_t hooki = 0; hooki < g_num_create_hooks; ++hooki) {
g_create_hooks[hooki].callback(&b, g_create_hooks[hooki].userdata);
@@ -566,12 +261,16 @@ struct buffer buffer_from_file(char *filename) {
return b;
}
-void write_line(struct text_chunk *chunk, void *userdata) {
- FILE *file = (FILE *)userdata;
- fwrite(chunk->text, 1, chunk->nbytes, file);
+struct buffer buffer_from_file(const char *path) {
+ char *full_path = to_abspath(path);
+ struct buffer b = create_internal(basename((char *)path), full_path);
+ buffer_read_from_file(&b);
- // final newline is not optional!
- fputc('\n', file);
+ for (uint32_t hooki = 0; hooki < g_num_create_hooks; ++hooki) {
+ g_create_hooks[hooki].callback(&b, g_create_hooks[hooki].userdata);
+ }
+
+ return b;
}
void buffer_to_file(struct buffer *buffer) {
@@ -609,10 +308,9 @@ void buffer_to_file(struct buffer *buffer) {
undo_push_boundary(&buffer->undo, (struct undo_boundary){.save_point = true});
}
-void buffer_write_to(struct buffer *buffer, const char *filename) {
+void buffer_set_filename(struct buffer *buffer, const char *filename) {
buffer->filename = to_abspath(filename);
buffer->modified = true;
- buffer_to_file(buffer);
}
void buffer_reload(struct buffer *buffer) {
@@ -635,141 +333,152 @@ void buffer_reload(struct buffer *buffer) {
}
}
-struct search_data {
- VEC(struct match) matches;
- const char *pattern;
-};
-
-// TODO: maybe should live in text
-void search_line(struct text_chunk *chunk, void *userdata) {
- struct search_data *data = (struct search_data *)userdata;
- size_t pattern_len = strlen(data->pattern);
- uint32_t pattern_nchars = utf8_nchars((uint8_t *)data->pattern, pattern_len);
-
- char *line = malloc(chunk->nbytes + 1);
- strncpy(line, chunk->text, chunk->nbytes);
- line[chunk->nbytes] = '\0';
- char *hit = NULL;
- uint32_t byteidx = 0;
- while ((hit = strstr(line + byteidx, data->pattern)) != NULL) {
- byteidx = hit - line;
- uint32_t begin = utf8_nchars(chunk->text, byteidx);
- struct match match = (struct match){
- .begin = {.col = begin, .line = chunk->line},
- .end = {.col = begin + pattern_nchars - 1, .line = chunk->line},
- };
-
- VEC_PUSH(&data->matches, match);
-
- // proceed to after match
- byteidx += pattern_len;
- }
-
- free(line);
-}
-
-void buffer_find(struct buffer *buffer, const char *pattern,
- struct match **matches, uint32_t *nmatches) {
+void buffer_destroy(struct buffer *buffer) {
+ text_destroy(buffer->text);
+ buffer->text = NULL;
- struct search_data data = (struct search_data){.pattern = pattern};
- VEC_INIT(&data.matches, 16);
- text_for_each_line(buffer->text, 0, text_num_lines(buffer->text), search_line,
- &data);
+ free(buffer->name);
+ buffer->name = NULL;
- *matches = VEC_ENTRIES(&data.matches);
- *nmatches = VEC_SIZE(&data.matches);
-}
+ free(buffer->filename);
+ buffer->filename = NULL;
-void buffer_set_text(struct buffer *buffer, uint8_t *text, uint32_t nbytes) {
- text_clear(buffer->text);
- uint32_t lines, cols;
- text_append(buffer->text, text, nbytes, &lines, &cols);
+ undo_destroy(&buffer->undo);
}
-int buffer_add_text(struct buffer_view *view, uint8_t *text, uint32_t nbytes) {
- if (view->buffer->readonly) {
+struct location buffer_add(struct buffer *buffer, struct location at,
+ uint8_t *text, uint32_t nbytes) {
+ if (buffer->readonly) {
minibuffer_echo_timeout(4, "buffer is read-only");
- return 0;
+ return at;
}
// invalidate last paste
g_kill_ring.paste_up_to_date = false;
- /* If we currently have a selection active,
- * replace it with the text to insert. */
- maybe_delete_region(view);
-
- struct buffer_location initial = view->dot;
+ struct location initial = at;
+ struct location final = at;
uint32_t lines_added, cols_added;
- text_insert_at(view->buffer->text, initial.line, initial.col, text, nbytes,
+ text_insert_at(buffer->text, initial.line, initial.col, text, nbytes,
&lines_added, &cols_added);
// move to after inserted text
- movev(view, lines_added);
+ movev(buffer, lines_added, &final);
if (lines_added > 0) {
// does not make sense to use position from another line
- view->dot.col = 0;
+ final.col = 0;
}
- moveh(view, cols_added);
+ moveh(buffer, cols_added, &final);
- struct buffer_location final = view->dot;
undo_push_add(
- &view->buffer->undo,
+ &buffer->undo,
(struct undo_add){.begin = {.row = initial.line, .col = initial.col},
.end = {.row = final.line, .col = final.col}});
if (lines_added > 0) {
- undo_push_boundary(&view->buffer->undo,
+ undo_push_boundary(&buffer->undo,
(struct undo_boundary){.save_point = false});
}
- view->buffer->modified = true;
- return lines_added;
+ buffer->modified = true;
+ return final;
}
-void buffer_newline(struct buffer_view *view) {
- buffer_add_text(view, (uint8_t *)"\n", 1);
+struct location buffer_set_text(struct buffer *buffer, uint8_t *text,
+ uint32_t nbytes) {
+ uint32_t lines, cols;
+
+ text_clear(buffer->text);
+ text_append(buffer->text, text, nbytes, &lines, &cols);
+ return buffer_clamp(buffer, lines, cols);
}
-void buffer_indent(struct buffer_view *view) {
- uint32_t tab_width = view->buffer->lang.tab_width;
- buffer_add_text(view, (uint8_t *)" ",
- tab_width > 16 ? 16 : tab_width);
+void buffer_clear(struct buffer *buffer) { text_clear(buffer->text); }
+
+bool buffer_is_empty(struct buffer *buffer) {
+ return text_num_lines(buffer->text) == 0;
}
-uint32_t buffer_add_update_hook(struct buffer *buffer, update_hook_cb hook,
- void *userdata) {
- struct update_hook *h =
- &buffer->update_hooks.hooks[buffer->update_hooks.nhooks];
- h->callback = hook;
- h->userdata = userdata;
+bool buffer_is_modified(struct buffer *buffer) { return buffer->modified; }
+bool buffer_is_readonly(struct buffer *buffer) { return buffer->readonly; }
- ++buffer->update_hooks.nhooks;
+void buffer_set_readonly(struct buffer *buffer, bool readonly) {
+ buffer->readonly = readonly;
+}
- // TODO: cant really have this if we actually want to remove a hook
- return buffer->update_hooks.nhooks - 1;
+bool buffer_is_backed(struct buffer *buffer) {
+ return buffer->filename != NULL;
+}
+
+struct location buffer_previous_char(struct buffer *buffer,
+ struct location dot) {
+ moveh(buffer, -1, &dot);
+ return dot;
+}
+
+struct location buffer_previous_word(struct buffer *buffer,
+ struct location dot) {
+ moveh(buffer, -1, &dot);
+ uint8_t chars[] = {' ', '.'};
+ return find_next(buffer, dot, chars, 2, -1);
}
-void buffer_set_mark(struct buffer_view *view) {
- view->mark_set ? buffer_clear_mark(view)
- : buffer_set_mark_at(view, view->dot.line, view->dot.col);
+struct location buffer_previous_line(struct buffer *buffer,
+ struct location dot) {
+ movev(buffer, -1, &dot);
+ return dot;
}
-void buffer_clear_mark(struct buffer_view *view) {
- view->mark_set = false;
- minibuffer_echo_timeout(2, "mark cleared");
+struct location buffer_next_char(struct buffer *buffer, struct location dot) {
+ moveh(buffer, 1, &dot);
+ return dot;
+}
+
+struct location buffer_next_word(struct buffer *buffer, struct location dot) {
+ moveh(buffer, 1, &dot);
+ uint8_t chars[] = {' ', '.'};
+ return find_next(buffer, dot, chars, 2, 1);
}
-void buffer_set_mark_at(struct buffer_view *view, uint32_t line, uint32_t col) {
- view->mark_set = true;
- view->mark.line = line;
- view->mark.col = col;
- minibuffer_echo_timeout(2, "mark set");
+struct location buffer_next_line(struct buffer *buffer, struct location dot) {
+ movev(buffer, 1, &dot);
+ return dot;
}
-void buffer_undo(struct buffer_view *view) {
- struct undo_stack *undo = &view->buffer->undo;
+struct location buffer_clamp(struct buffer *buffer, int64_t line, int64_t col) {
+ struct location location = {.line = 0, .col = 0};
+ movev(buffer, line, &location);
+ moveh(buffer, col, &location);
+
+ return location;
+}
+
+struct location buffer_end(struct buffer *buffer) {
+ uint32_t nlines = buffer_num_lines(buffer);
+ return (struct location){nlines, buffer_num_chars(buffer, nlines)};
+}
+
+uint32_t buffer_num_lines(struct buffer *buffer) {
+ return text_num_lines(buffer->text);
+}
+
+uint32_t buffer_num_chars(struct buffer *buffer, uint32_t line) {
+ return text_line_length(buffer->text, line);
+}
+
+struct location buffer_newline(struct buffer *buffer, struct location at) {
+ return buffer_add(buffer, at, (uint8_t *)"\n", 1);
+}
+
+struct location buffer_indent(struct buffer *buffer, struct location at) {
+ uint32_t tab_width = buffer->lang.tab_width;
+ buffer_add(buffer, at, (uint8_t *)" ",
+ tab_width > 16 ? 16 : tab_width);
+}
+
+struct location buffer_undo(struct buffer *buffer, struct location dot) {
+ struct undo_stack *undo = &buffer->undo;
undo_begin(undo);
// fetch and handle records
@@ -783,62 +492,213 @@ void buffer_undo(struct buffer_view *view) {
undo_next(undo, &records, &nrecords);
+ struct location pos = dot;
undo_push_boundary(undo, (struct undo_boundary){.save_point = false});
for (uint32_t reci = 0; reci < nrecords; ++reci) {
struct undo_record *rec = &records[reci];
switch (rec->type) {
+
case Undo_Boundary: {
struct undo_boundary *b = &rec->boundary;
if (b->save_point) {
- view->buffer->modified = false;
+ buffer->modified = false;
}
break;
}
+
case Undo_Add: {
struct undo_add *add = &rec->add;
- delete_with_undo(view->buffer,
- (struct buffer_location){
- .line = add->begin.row,
- .col = add->begin.col,
- },
- (struct buffer_location){
- .line = add->end.row,
- .col = add->end.col,
- });
+ pos =
+ buffer_delete(buffer, (struct region){.begin =
+ (struct location){
+ .line = add->begin.row,
+ .col = add->begin.col,
+ },
+ .end = (struct location){
+ .line = add->end.row,
+ .col = add->end.col,
+ }});
- buffer_goto(view, add->begin.row, add->begin.col);
break;
}
+
case Undo_Delete: {
struct undo_delete *del = &rec->delete;
- buffer_goto(view, del->pos.row, del->pos.col);
- buffer_add_text(view, del->data, del->nbytes);
+ pos = buffer_add(buffer,
+ (struct location){
+ .line = del->pos.row,
+ .col = del->pos.col,
+ },
+ del->data, del->nbytes);
break;
}
}
}
+
undo_push_boundary(undo, (struct undo_boundary){.save_point = false});
free(records);
undo_end(undo);
+
+ return pos;
+}
+
+/* --------------- searching and supporting types ---------------- */
+struct search_data {
+ VEC(struct region) matches;
+ const char *pattern;
+};
+
+// TODO: maybe should live in text
+static void search_line(struct text_chunk *chunk, void *userdata) {
+ struct search_data *data = (struct search_data *)userdata;
+ size_t pattern_len = strlen(data->pattern);
+ uint32_t pattern_nchars = utf8_nchars((uint8_t *)data->pattern, pattern_len);
+
+ char *line = malloc(chunk->nbytes + 1);
+ strncpy(line, chunk->text, chunk->nbytes);
+ line[chunk->nbytes] = '\0';
+ char *hit = NULL;
+ uint32_t byteidx = 0;
+ while ((hit = strstr(line + byteidx, data->pattern)) != NULL) {
+ byteidx = hit - line;
+ uint32_t begin = utf8_nchars(chunk->text, byteidx);
+ struct region match =
+ region_new((struct location){.col = begin, .line = chunk->line},
+ (struct location){.col = begin + pattern_nchars - 1,
+ .line = chunk->line});
+ VEC_PUSH(&data->matches, match);
+
+ // proceed to after match
+ byteidx += pattern_len;
+ }
+
+ free(line);
+}
+
+void buffer_find(struct buffer *buffer, const char *pattern,
+ struct region **matches, uint32_t *nmatches) {
+
+ struct search_data data = (struct search_data){.pattern = pattern};
+ VEC_INIT(&data.matches, 16);
+ text_for_each_line(buffer->text, 0, text_num_lines(buffer->text), search_line,
+ &data);
+
+ *matches = VEC_ENTRIES(&data.matches);
+ *nmatches = VEC_SIZE(&data.matches);
+}
+
+struct location buffer_copy(struct buffer *buffer, struct region region) {
+ if (region_has_size(region)) {
+ struct text_chunk *curr = copy_region(buffer, region);
+ }
+
+ return region.begin;
+}
+
+struct location buffer_cut(struct buffer *buffer, struct region region) {
+ if (region_has_size(region)) {
+ copy_region(buffer, region);
+ buffer_delete(buffer, region);
+ }
+
+ return region.begin;
+}
+
+struct location buffer_delete(struct buffer *buffer, struct region region) {
+ if (buffer->readonly) {
+ minibuffer_echo_timeout(4, "buffer is read-only");
+ return region.end;
+ }
+
+ if (!region_has_size(region)) {
+ return region.begin;
+ }
+
+ struct text_chunk txt =
+ text_get_region(buffer->text, region.begin.line, region.begin.col,
+ region.end.line, region.end.col);
+
+ undo_push_delete(&buffer->undo,
+ (struct undo_delete){.data = txt.text,
+ .nbytes = txt.nbytes,
+ .pos = {.row = region.begin.line,
+ .col = region.begin.col}});
+ undo_push_boundary(&buffer->undo,
+ (struct undo_boundary){.save_point = false});
+
+ text_delete(buffer->text, region.begin.line, region.begin.col,
+ region.end.line, region.end.col);
+ buffer->modified = true;
+
+ return region.begin;
+}
+
+static struct location paste(struct buffer *buffer, struct location at,
+ uint32_t ring_idx) {
+ struct location new_loc = at;
+ if (ring_idx > 0) {
+ struct text_chunk *curr = &g_kill_ring.buffer[ring_idx - 1];
+ if (curr->text != NULL) {
+ g_kill_ring.last_paste = at;
+ new_loc = buffer_add(buffer, at, curr->text, curr->nbytes);
+ g_kill_ring.paste_up_to_date = true;
+ }
+ }
+
+ return new_loc;
+}
+
+struct location buffer_paste(struct buffer *buffer, struct location at) {
+ g_kill_ring.paste_idx = g_kill_ring.curr_idx;
+ return paste(buffer, at, g_kill_ring.curr_idx);
+}
+
+struct location buffer_paste_older(struct buffer *buffer, struct location at) {
+ if (g_kill_ring.paste_up_to_date) {
+
+ // remove previous paste
+ struct text_chunk *curr = &g_kill_ring.buffer[g_kill_ring.curr_idx];
+ delete_with_undo(buffer, g_kill_ring.last_paste, at);
+
+ // paste older
+ if (g_kill_ring.paste_idx - 1 > 0) {
+ --g_kill_ring.paste_idx;
+ } else {
+ g_kill_ring.paste_idx = g_kill_ring.curr_idx;
+ }
+
+ paste(buffer, g_kill_ring.last_paste, g_kill_ring.paste_idx);
+
+ } else {
+ buffer_paste(buffer, at);
+ }
+}
+
+struct text_chunk buffer_line(struct buffer *buffer, uint32_t line) {
+ return text_get_line(buffer->text, line);
+}
+
+uint32_t buffer_add_update_hook(struct buffer *buffer, update_hook_cb hook,
+ void *userdata) {
+ VEC_APPEND(&buffer->update_hooks, struct update_hook_entry * e);
+ struct update_hook *h = &e->hook;
+ h->callback = hook;
+ h->userdata = userdata;
+
+ // TODO: cant really have this if we actually want to remove a hook
+ return VEC_SIZE(&buffer->update_hooks) - 1;
}
struct cmdbuf {
struct command_list *cmds;
- struct buffer_location scroll;
- uint32_t line_offset;
- uint32_t left_margin;
+ struct location origin;
uint32_t width;
-
- struct region region;
- bool mark_set;
+ uint32_t height;
bool show_ws;
- struct line_render_hook *line_render_hooks;
- uint32_t nlinerender_hooks;
-
struct buffer *buffer;
};
@@ -865,33 +725,26 @@ static uint32_t visual_string_width(uint8_t *txt, uint32_t len,
void render_line(struct text_chunk *line, void *userdata) {
struct cmdbuf *cmdbuf = (struct cmdbuf *)userdata;
- uint32_t visual_line = line->line - cmdbuf->scroll.line + cmdbuf->line_offset;
-
- for (uint32_t hooki = 0; hooki < cmdbuf->nlinerender_hooks; ++hooki) {
- struct line_render_hook *hook = &cmdbuf->line_render_hooks[hooki];
- hook->callback(line, visual_line, cmdbuf->cmds, hook->userdata);
- }
+ uint32_t visual_line = line->line - cmdbuf->origin.line;
command_list_set_show_whitespace(cmdbuf->cmds, cmdbuf->show_ws);
- struct buffer_location *begin = &cmdbuf->region.begin,
- *end = &cmdbuf->region.end;
// calculate scroll offsets
uint32_t scroll_bytes =
- utf8_nbytes(line->text, line->nbytes, cmdbuf->scroll.col);
+ utf8_nbytes(line->text, line->nbytes, cmdbuf->origin.col);
uint32_t text_nbytes_scroll =
scroll_bytes > line->nbytes ? 0 : line->nbytes - scroll_bytes;
uint8_t *text = line->text + scroll_bytes;
- uint32_t visual_col_start = cmdbuf->left_margin;
- uint32_t cur_visual_col = visual_col_start;
+ uint32_t visual_col_start = 0;
+ uint32_t cur_visual_col = 0;
uint32_t start_byte = 0, text_nbytes = 0;
struct text_property *properties[16] = {0};
struct text_property *prev_properties[16] = {0};
uint32_t prev_nproperties;
for (uint32_t cur_byte = start_byte, coli = 0;
cur_byte < text_nbytes_scroll && cur_visual_col < cmdbuf->width &&
- coli < line->nchars - cmdbuf->scroll.col;
+ coli < line->nchars - cmdbuf->origin.col;
++coli) {
uint32_t bytes_remaining = text_nbytes_scroll - cur_byte;
@@ -900,10 +753,9 @@ void render_line(struct text_chunk *line, void *userdata) {
// calculate character properties
uint32_t nproperties = 0;
- buffer_get_text_properties(
- cmdbuf->buffer,
- (struct buffer_location){.line = line->line, .col = coli}, properties,
- 16, &nproperties);
+ text_get_properties(cmdbuf->buffer->text,
+ (struct location){.line = line->line, .col = coli + cmdbuf->origin.col},
+ properties, 16, &nproperties);
// handle changes to properties
uint32_t nnew_props = 0;
@@ -967,327 +819,55 @@ void render_line(struct text_chunk *line, void *userdata) {
}
}
-void scroll(struct buffer_view *view, int line_delta, int col_delta) {
- uint32_t nlines = text_num_lines(view->buffer->text);
- int64_t new_line = (int64_t)view->scroll.line + line_delta;
- if (new_line >= 0 && new_line < nlines) {
- view->scroll.line = (uint32_t)new_line;
- } else if (new_line < 0) {
- view->scroll.line = 0;
- }
-
- int64_t new_col = (int64_t)view->scroll.col + col_delta;
- if (new_col >= 0 &&
- new_col < text_line_length(view->buffer->text, view->dot.line)) {
- view->scroll.col = (uint32_t)new_col;
- } else if (new_col < 0) {
- view->scroll.col = 0;
- }
-}
-
-void to_relative(struct buffer_view *view, uint32_t line, uint32_t col,
- int64_t *rel_line, int64_t *rel_col) {
- *rel_col = (int64_t)col - (int64_t)view->scroll.col;
- *rel_line = (int64_t)line - (int64_t)view->scroll.line;
-}
-
-uint32_t visual_dot_col(struct buffer_view *view, uint32_t dot_col) {
- struct text_chunk line = text_get_line(view->buffer->text, view->dot.line);
- return visual_string_width(line.text, line.nbytes, view->scroll.col, dot_col);
-}
-
-void render_modeline(struct modeline *modeline, struct buffer_view *view,
- struct command_list *commands, uint32_t window_id,
- uint32_t width, uint32_t height, uint64_t frame_time) {
- char buf[width * 4];
-
- static uint64_t samples[10] = {0};
- static uint32_t samplei = 0;
- static uint64_t avg = 0;
-
- // calc a moving average with a window of the last 10 frames
- ++samplei;
- samplei %= 10;
- avg += 0.1 * (frame_time - samples[samplei]);
- samples[samplei] = frame_time;
-
- time_t now = time(NULL);
- struct tm *lt = localtime(&now);
- char left[128], right[128];
-
- snprintf(left, 128, " %c%c %d:%-16s (%d, %d) (%s)",
- view->buffer->modified ? '*' : '-',
- view->buffer->readonly ? '%' : '-', window_id, view->buffer->name,
- view->dot.line + 1, visual_dot_col(view, view->dot.col),
- view->buffer->lang.name);
- snprintf(right, 128, "(%.2f ms) %02d:%02d", frame_time / 1e6, lt->tm_hour,
- lt->tm_min);
-
- snprintf(buf, width * 4, "%s%*s%s", left,
- (int)(width - (strlen(left) + strlen(right))), "", right);
-
- if (strcmp(buf, (char *)modeline->buffer) != 0) {
- modeline->buffer = realloc(modeline->buffer, width * 4);
- modeline->sz = width * 4;
- strcpy((char *)modeline->buffer, buf);
- }
-
- command_list_set_index_color_bg(commands, 8);
- command_list_draw_text(commands, 0, height - 1, modeline->buffer,
- strlen((char *)modeline->buffer));
- command_list_reset_color(commands);
-}
-
-struct linenumdata {
- uint32_t longest_nchars;
- uint32_t dot_line;
-} linenum_data;
-
-void linenum_render_hook(struct text_chunk *line_data, uint32_t line,
- struct command_list *commands, void *userdata) {
- struct linenumdata *data = (struct linenumdata *)userdata;
- static char buf[16];
- command_list_set_index_color_bg(commands, 8);
- command_list_set_index_color_fg(commands,
- line_data->line == data->dot_line ? 15 : 7);
- uint32_t chars =
- snprintf(buf, 16, "%*d", data->longest_nchars + 1, line_data->line + 1);
- command_list_draw_text_copy(commands, 0, line, (uint8_t *)buf, chars);
- command_list_reset_color(commands);
- command_list_draw_text(commands, data->longest_nchars + 1, line,
- (uint8_t *)" ", 1);
-}
-
-void clear_empty_linenum_lines(uint32_t line, struct command_list *commands,
- void *userdata) {
- struct linenumdata *data = (struct linenumdata *)userdata;
- uint32_t longest_nchars = data->longest_nchars;
- command_list_draw_repeated(commands, 0, line, ' ', longest_nchars + 2);
-}
-
-uint32_t longest_linenum(struct buffer *buffer) {
- uint32_t total_lines = text_num_lines(buffer->text);
- uint32_t longest_nchars = 10;
- if (total_lines < 10) {
- longest_nchars = 1;
- } else if (total_lines < 100) {
- longest_nchars = 2;
- } else if (total_lines < 1000) {
- longest_nchars = 3;
- } else if (total_lines < 10000) {
- longest_nchars = 4;
- } else if (total_lines < 100000) {
- longest_nchars = 5;
- } else if (total_lines < 1000000) {
- longest_nchars = 6;
- } else if (total_lines < 10000000) {
- longest_nchars = 7;
- } else if (total_lines < 100000000) {
- longest_nchars = 8;
- } else if (total_lines < 1000000000) {
- longest_nchars = 9;
- }
-
- return longest_nchars;
-}
-
-void buffer_update(struct buffer_view *view, uint32_t window_id, uint32_t width,
- uint32_t height, struct command_list *commands,
- uint64_t frame_time, uint32_t *relline, uint32_t *relcol) {
- if (width == 0 || height == 0) {
+void buffer_update(struct buffer *buffer, struct buffer_update_params *params) {
+ if (params->width == 0 || params->height == 0) {
return;
}
- uint32_t total_width = width, total_height = height;
- struct margin total_margins = {0};
- struct line_render_hook line_hooks[16 + 1];
- uint32_t nlinehooks = 0;
- for (uint32_t hooki = 0; hooki < view->buffer->update_hooks.nhooks; ++hooki) {
- struct update_hook *h = &view->buffer->update_hooks.hooks[hooki];
- struct update_hook_result res =
- h->callback(view, commands, width, height, frame_time, h->userdata);
-
- if (res.line_render_hook.callback != NULL) {
- line_hooks[nlinehooks] = res.line_render_hook;
- ++nlinehooks;
- }
-
- total_margins.left += res.margins.left;
- total_margins.right += res.margins.right;
- total_margins.bottom += res.margins.bottom;
- total_margins.top += res.margins.top;
-
- height -= total_margins.top + total_margins.bottom;
- width -= total_margins.left + total_margins.right;
- }
-
- if (view->line_numbers) {
- linenum_data.longest_nchars = longest_linenum(view->buffer);
- linenum_data.dot_line = view->dot.line;
- line_hooks[nlinehooks].callback = linenum_render_hook;
- line_hooks[nlinehooks].empty_callback = clear_empty_linenum_lines;
- line_hooks[nlinehooks].userdata = &linenum_data;
- ++nlinehooks;
-
- total_margins.left += linenum_data.longest_nchars + 2;
- }
-
- if (view->modeline != NULL) {
- render_modeline(view->modeline, view, commands, window_id, width, height,
- frame_time);
- total_margins.bottom += 1;
- }
-
- height -= total_margins.top + total_margins.bottom;
- width -= total_margins.left + total_margins.right;
-
- int64_t rel_line, rel_col;
- to_relative(view, view->dot.line, view->dot.col, &rel_line, &rel_col);
- int line_delta = 0, col_delta = 0;
- if (rel_line < 0) {
- line_delta = rel_line - ((int)height / 2);
- } else if (rel_line >= height) {
- line_delta = (rel_line - height) + height / 2;
+ VEC_FOR_EACH(&buffer->update_hooks, struct update_hook_entry * entry) {
+ struct update_hook *h = &entry->hook;
+ h->callback(buffer, params->width, params->height, h->userdata);
}
- if (rel_col < 0) {
- col_delta = rel_col - ((int)width / 2);
- } else if (rel_col >= width) {
- col_delta = (rel_col - width) + width / 2;
- }
-
- scroll(view, line_delta, col_delta);
-
struct setting *show_ws = settings_get("editor.show-whitespace");
- if (buffer_region_has_size(view)) {
- struct region reg = to_region(view->dot, view->mark);
- buffer_add_text_property(view->buffer, reg.begin, reg.end,
- (struct text_property){
- .type = TextProperty_Colors,
- .colors =
- (struct text_property_colors){
- .set_bg = true,
- .bg = 5,
- .set_fg = false,
- },
- });
- }
-
struct cmdbuf cmdbuf = (struct cmdbuf){
- .cmds = commands,
- .scroll = view->scroll,
- .left_margin = total_margins.left,
- .width = total_width,
- .line_offset = total_margins.top,
- .line_render_hooks = line_hooks,
- .nlinerender_hooks = nlinehooks,
- .mark_set = view->mark_set,
- .region = to_region(view->dot, view->mark),
+ .cmds = params->commands,
+ .origin = params->origin,
+ .width = params->width,
+ .height = params->height,
.show_ws = show_ws != NULL ? show_ws->value.bool_value : true,
- .buffer = view->buffer,
+ .buffer = buffer,
};
- text_for_each_line(view->buffer->text, view->scroll.line, height, render_line,
- &cmdbuf);
+ text_for_each_line(buffer->text, params->origin.line, params->height,
+ render_line, &cmdbuf);
// draw empty lines
- uint32_t nlines = text_num_lines(view->buffer->text);
- for (uint32_t linei = nlines - view->scroll.line + total_margins.top;
- linei < height; ++linei) {
-
- for (uint32_t hooki = 0; hooki < nlinehooks; ++hooki) {
- struct line_render_hook *hook = &line_hooks[hooki];
- if (hook->empty_callback != NULL) {
- hook->empty_callback(linei, commands, hook->userdata);
- }
- }
-
- command_list_draw_repeated(commands, total_margins.left, linei, ' ',
- total_width - total_margins.left);
+ uint32_t nlines = text_num_lines(buffer->text);
+ for (uint32_t linei = nlines - params->origin.line; linei < params->height;
+ ++linei) {
+ command_list_draw_repeated(params->commands, 0, linei, ' ', params->width);
}
-
- // update the visual cursor position
- to_relative(view, view->dot.line, view->dot.col, &rel_line, &rel_col);
- uint32_t visual_col = visual_dot_col(view, view->dot.col);
-
- // TODO: fix this shit, should not need to add scroll_col back here
- // only to subtract it in the function
- to_relative(view, view->dot.line, visual_col + view->scroll.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;
-}
-
-struct text_chunk buffer_get_line(struct buffer *buffer, uint32_t line) {
- return text_get_line(buffer->text, line);
-}
-
-void buffer_view_scroll_down(struct buffer_view *view, uint32_t height) {
- buffer_goto(view, view->dot.line + height, view->dot.col);
- scroll(view, height, 0);
-}
-
-void buffer_view_scroll_up(struct buffer_view *view, uint32_t height) {
- buffer_goto(view, view->dot.line - height, view->dot.col);
- scroll(view, -height, 0);
}
-void buffer_clear_text_properties(struct buffer *buffer) {
- VEC_CLEAR(&buffer->text_properties);
-}
-
-void buffer_add_text_property(struct buffer *buffer,
- struct buffer_location start,
- struct buffer_location end,
+void buffer_add_text_property(struct buffer *buffer, struct location start,
+ struct location end,
struct text_property property) {
- struct text_property_entry entry = {
- .start = start,
- .end = end,
- .property = property,
- };
- VEC_PUSH(&buffer->text_properties, entry);
+ text_add_property(
+ buffer->text, (struct location){.line = start.line, .col = start.col},
+ (struct location){.line = end.line, .col = end.col}, property);
}
-bool buffer_location_is_between(struct buffer_location location,
- struct buffer_location start,
- struct buffer_location end) {
- if (location.line >= start.line && location.line <= end.line) {
- if (location.line == end.line && location.col <= end.col &&
- location.line == start.line && location.col >= start.col) {
- // only one line
- return true;
- } else if (location.line == start.line && location.line != end.line &&
- location.col >= start.col) {
- // we are on the first line
- return true;
- } else if (location.line == end.line && location.line != start.line &&
- location.col <= end.col) {
- // we are on the last line
- return true;
- } else if (location.line != end.line && location.line != start.line) {
- // we are on lines in between
- return true;
- }
- }
- return false;
-}
-
-void buffer_get_text_properties(struct buffer *buffer,
- struct buffer_location location,
+void buffer_get_text_properties(struct buffer *buffer, struct location location,
struct text_property **properties,
uint32_t max_nproperties,
uint32_t *nproperties) {
- uint32_t nres = 0;
- VEC_FOR_EACH(&buffer->text_properties, struct text_property_entry * prop) {
- if (buffer_location_is_between(location, prop->start, prop->end)) {
- properties[nres] = &prop->property;
- ++nres;
+ text_get_properties(
+ buffer->text,
+ (struct location){.line = location.line, .col = location.col}, properties,
+ max_nproperties, nproperties);
+}
- if (nres == max_nproperties) {
- break;
- }
- }
- }
- *nproperties = nres;
+void buffer_clear_text_properties(struct buffer *buffer) {
+ text_clear_properties(buffer->text);
}