#ifndef ED_BUFFER_INCLUDED #define ED_BUFFER_INCLUDED #include "ed_array.h" #include "string.h" #include "ui.h" #include typedef struct { size_t link_index; size_t content_index; } ed_buffer_offset; typedef struct { uint64_t column; uint64_t line; ed_buffer_offset offset; } ed_buffer_cursor; typedef enum { ED_BUFFER_CONTENT_LINK_TYPE_ORIGINAL, ED_BUFFER_CONTENT_LINK_TYPE_ADDED, } ed_buffer_content_link_t; typedef struct { ed_buffer_content_link_t type; size_t start; size_t end; size_t len; } ed_buffer_content_link; arrayTemplate(ed_buffer_content_link); typedef struct { array(uint8_t) original_content; array(uint8_t) added_content; array(ed_buffer_content_link) linked_content; } ed_buffer_data; typedef struct { ed_buffer_data data; ed_buffer_cursor cursor; } ed_buffer; #ifdef ED_BUFFER_IMPLEMENTATION static ed_buffer_data _ed_buffer_init_buffer_data(slice(uint8_t) data) { array(ed_buffer_content_link) linked_content = newArray(ed_buffer_content_link, 16); pushArray(ed_buffer_content_link, &linked_content, ((ed_buffer_content_link) { .type = ED_BUFFER_CONTENT_LINK_TYPE_ORIGINAL, .start = 0, .end = data.len, .len = data.len, })); return (ed_buffer_data) { .original_content = newArrayFromSlice(uint8_t, data), .added_content = newArray(uint8_t, 512), .linked_content = linked_content, }; } uint8_t ed_buffer_get_char_at(ed_buffer *buffer, ed_buffer_cursor cursor) { ed_buffer_content_link link = buffer->data.linked_content.data[cursor.offset.link_index]; uint8_t character; if (link.type == ED_BUFFER_CONTENT_LINK_TYPE_ORIGINAL) { character = buffer->data.original_content.data[link.start+cursor.offset.content_index]; } else if (link.type == ED_BUFFER_CONTENT_LINK_TYPE_ADDED) { character = buffer->data.added_content.data[link.start+cursor.offset.content_index]; } else { character = 0; } return character; } ed_buffer ed_buffer_from_file(string path) { char text[] = "bool ht_set(ed_ht *ht, string key, void *value) {\n" " ht_hash_t hash = ht_hash(key, ht->capacity);\n" "\n" " for (size_t i=hash; icapacity; ++i) {\n" " if (ht->key_slots[i].key.data == NULL || string_eq(ht->key_slots[i].key, key)) {\n" " if (ht->key_slots[i].key.data != NULL) {\n" " free(ht->key_slots[i].key.data);\n" " }\n" " ht->key_slots[i].key = string_copy(key);\n" "\n" " memcpy(ht->value_slots+i*ht->value_size, value, ht->value_size);\n" " return true;\n" " }\n" " }\n" " return false;\n" "}\n"; slice(uint8_t) data = { .data = text, .len = sizeof(text), }; ed_buffer buffer = { .data = _ed_buffer_init_buffer_data(data), .cursor = { .column = 0, .line = 0, .offset = { .link_index = 0, .content_index = 0, }, } }; return buffer; } bool ed_buffer_content_iter(ed_buffer *buffer, ed_buffer_cursor *cursor) { if (cursor->offset.link_index >= buffer->data.linked_content.size || cursor->offset.content_index >= (buffer->data.linked_content.data[cursor->offset.link_index].len)) { return false; } uint8_t character = ed_buffer_get_char_at(buffer, *cursor); ed_buffer_content_link link = buffer->data.linked_content.data[cursor->offset.link_index]; if (cursor->offset.content_index < link.len-1) { cursor->offset.content_index += 1; } else if (cursor->offset.link_index < buffer->data.linked_content.size-1) { cursor->offset.content_index = 0; cursor->offset.link_index += 1; } else { return false; } if (character == '\n') { cursor->column = 0; cursor->line += 1; } else { cursor->column += 1; } return true; } void ed_buffer_insert_text(ed_buffer *buffer, string text, ed_buffer_cursor cursor) { if (text.len == 0) { return; } size_t content_start = buffer->data.added_content.size; pushArrayMulti(uint8_t, &buffer->data.added_content, text.data, text.len); if (cursor.offset.content_index == 0) { insertArrayAt(ed_buffer_content_link, &buffer->data.linked_content, cursor.offset.link_index, ((ed_buffer_content_link) { .type = ED_BUFFER_CONTENT_LINK_TYPE_ADDED, .start = content_start, .end = content_start+text.len, .len = text.len, }) ); } else { ed_buffer_content_link end_link = buffer->data.linked_content.data[cursor.offset.link_index]; end_link.start += cursor.offset.content_index; end_link.len -= cursor.offset.content_index; buffer->data.linked_content.data[cursor.offset.link_index].end = end_link.start; buffer->data.linked_content.data[cursor.offset.link_index].len = buffer->data.linked_content.data[cursor.offset.link_index].end - buffer->data.linked_content.data[cursor.offset.link_index].start; // insert new content insertArrayAt(ed_buffer_content_link, &buffer->data.linked_content, cursor.offset.link_index+1, ((ed_buffer_content_link) { .type = ED_BUFFER_CONTENT_LINK_TYPE_ADDED, .start = content_start, .end = content_start+text.len, .len = text.len, }) ); // insert cut slice insertArrayAt(ed_buffer_content_link, &buffer->data.linked_content, cursor.offset.link_index+2, end_link ); } } void ed_buffer_render(ui_context *cx, ed_buffer *buffer) { ui_element(cx, _String("Buffer"), UI_AXIS_VERTICAL, ui_make_size(ui_fill, ui_fill), UI_FLAG_DRAW_BACKGROUND); ui_push_parent(cx); { ed_buffer_cursor cursor = buffer->cursor; do { uint8_t line_buffer[512]; size_t line_buffer_index = 0; do { uint8_t character = ed_buffer_get_char_at(buffer, cursor); if (character == '\n' || line_buffer_index >= 512) { break; } line_buffer[line_buffer_index] = character; line_buffer_index += 1; } while(ed_buffer_content_iter(buffer, &cursor)); uint8_t *str = malloc(line_buffer_index * sizeof(uint8_t)); memcpy(str, line_buffer, line_buffer_index * sizeof(uint8_t)); ui_element(cx, (string){ .data = str, .len = line_buffer_index, .owned = true }, UI_AXIS_HORIZONTAL, ui_make_size(ui_fit_text, ui_fit_text), UI_FLAG_DRAW_TEXT|UI_FLAG_DRAW_BACKGROUND ); } while(ed_buffer_content_iter(buffer, &cursor)); } ui_pop_parent(cx); } #endif #endif