From da0b9ab2c76661c8aa2002cbf195122cf06ae039 Mon Sep 17 00:00:00 2001
From: Patrick Cleavelin <patrick@spacegirl.nl>
Date: Fri, 22 Mar 2024 21:00:21 -0500
Subject: [PATCH] start actual editor stuff

---
 debug.plist           |   8 +++
 justfile              |   2 +-
 src/buffer.h          | 154 ++++++++++++++++++++++++++++++++++++++++++
 src/compile_flags.txt |   1 +
 src/ed_array.h        |  36 ++++++++--
 src/gfx.h             |   1 -
 src/ht.h              |   5 +-
 src/main.c            |  11 ++-
 src/string.h          |  18 ++++-
 src/ui.h              |  13 +++-
 10 files changed, 236 insertions(+), 13 deletions(-)
 create mode 100644 debug.plist
 create mode 100644 src/buffer.h

diff --git a/debug.plist b/debug.plist
new file mode 100644
index 0000000..e09573c
--- /dev/null
+++ b/debug.plist
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "https://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+    <dict>
+        <key>com.apple.security.get-task-allow</key>
+        <true/>
+    </dict>
+</plist>
diff --git a/justfile b/justfile
index f62278e..9c355fb 100644
--- a/justfile
+++ b/justfile
@@ -3,7 +3,7 @@ alias r := run
 
 build: transpile_shaders_metal
     mkdir -p bin
-    cc -Ivendor/ -g -Wall -Wextra -framework Cocoa -framework QuartzCore -framework CoreImage -framework Metal -framework MetalKit -ObjC src/*.c -o bin/an_editor
+    cc -Ivendor/ -O0 -g -Wall -Wextra -framework Cocoa -framework QuartzCore -framework CoreImage -framework Metal -framework MetalKit -ObjC src/*.c -o bin/an_editor
     # cc -Ivendor/ -g -Wall -Wextra src/*.c -o bin/an_editor -lEGL -lGLESv2 -lGL -lm -lX11 -lXi -lXcursor
     # cc bin/*.o -o bin/an_editor -lEGL -lGLESv2 -lGL -lm -lX11 -lXi -lXcursor
 
diff --git a/src/buffer.h b/src/buffer.h
new file mode 100644
index 0000000..e93e38b
--- /dev/null
+++ b/src/buffer.h
@@ -0,0 +1,154 @@
+#ifndef ED_BUFFER_INCLUDED
+#define ED_BUFFER_INCLUDED
+#include "ed_array.h"
+#include "string.h"
+#include "ui.h"
+
+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) {
+    slice(uint8_t) data = {
+        .data = (uint8_t *)"Hello!\nThis is a test document!\nWith three lines.\nJust kidding, there actually four",
+        .len = 83,
+    };
+    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_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);
+        } while(ed_buffer_content_iter(buffer, &cursor));
+    }
+    ui_pop_parent(cx);
+}
+
+#endif
+#endif
diff --git a/src/compile_flags.txt b/src/compile_flags.txt
index 665d6c2..1169529 100644
--- a/src/compile_flags.txt
+++ b/src/compile_flags.txt
@@ -2,5 +2,6 @@
 -DED_HT_IMPLEMENTATION
 -DED_STRING_IMPLEMENTATION
 -DED_GFX_IMPLEMENTATION
+-DED_BUFFER_IMPLEMENTATION
 -I../vendor/
 -ObjC
diff --git a/src/ed_array.h b/src/ed_array.h
index dac74d2..150ff98 100644
--- a/src/ed_array.h
+++ b/src/ed_array.h
@@ -1,14 +1,16 @@
 #ifndef ED_ARRAY_INCLUDED
 #define ED_ARRAY_INCLUDED
+#include <stdio.h>
 #include <stdlib.h>
 #include <assert.h>
+#include <memory.h>
 
-#define array(T) struct T ## Array
+#define array(T) struct T ## _Array
 #define arrayTemplate(T) array(T) {\
     size_t size, capacity;\
     T *data;\
 };\
-array(T) T ## ArrayConstruct(size_t size) {\
+array(T) T ## _ArrayConstruct(size_t size) {\
     array(T) this;\
     this.size = 0;\
     this.capacity = size;\
@@ -18,7 +20,7 @@ array(T) T ## ArrayConstruct(size_t size) {\
     }\
     return this;\
 };\
-void T ## PushArray(array(T) *arr, T value) {\
+void T ## _PushArray(array(T) *arr, T value) {\
     if (arr->size+1 <= arr->capacity) {\
         arr->data[arr->size] = value;\
         arr->size += 1;\
@@ -27,7 +29,31 @@ void T ## PushArray(array(T) *arr, T value) {\
     }\
 };
 
-#define newArray(T, size) T ## ArrayConstruct(size)
-#define pushArray(T, arr, value) T ## PushArray(arr, (value))
+#define slice(T) struct T ## _Slice
+#define sliceTemplate(T) slice(T) {\
+    size_t len;\
+    T *data;\
+};\
+array(T) T ## _FromSlice(slice(T) s) {\
+    array(T) arr = T ## _ArrayConstruct(s.len);\
+    memcpy(arr.data, s.data, sizeof(T) * s.len);\
+    arr.size = s.len;\
+    return arr;\
+}\
+slice(T) T ## _SliceConstruct(void *data, size_t len) {\
+    slice(T) this;\
+    this.len = len;\
+    this.data = data;\
+    return this;\
+}
+
+#define newArray(T, size) T ## _ArrayConstruct(size)
+#define newArrayFromSlice(T, s) T ## _FromSlice(s)
+#define pushArray(T, arr, value) T ## _PushArray(arr, (value))
+
+#define newSlice(T, data, len) T ## _SliceConstruct(data, len)
+
+arrayTemplate(uint8_t);
+sliceTemplate(uint8_t);
 
 #endif
diff --git a/src/gfx.h b/src/gfx.h
index c65d501..d6594d0 100644
--- a/src/gfx.h
+++ b/src/gfx.h
@@ -260,7 +260,6 @@ void _metal_gfx_send_events(_metal_gfx_context *cx) {
         untilDate:[NSDate distantPast] inMode:NSDefaultRunLoopMode dequeue:YES];
 
     [cx->application sendEvent:event];
-    [cx->application updateWindows];
 }
 
 void _metal_gfx_present(_metal_gfx_context *cx) {
diff --git a/src/ht.h b/src/ht.h
index 81519dd..70efa4a 100644
--- a/src/ht.h
+++ b/src/ht.h
@@ -61,7 +61,10 @@ bool ht_set(ed_ht *ht, string key, void *value) {
 
     for (size_t i=hash; i<ht->capacity; ++i) {
         if (ht->key_slots[i].key.data == NULL || string_eq(ht->key_slots[i].key, key)) {
-            ht->key_slots[i].key = key;
+            if (ht->key_slots[i].key.data != NULL) {
+                free(ht->key_slots[i].key.data);
+            }
+            ht->key_slots[i].key = string_copy(key);
 
             memcpy(ht->value_slots+i*ht->value_size, value, ht->value_size);
             return true;
diff --git a/src/main.c b/src/main.c
index d1ac154..9d2091b 100644
--- a/src/main.c
+++ b/src/main.c
@@ -11,11 +11,13 @@
 #define ED_STRING_IMPLEMENTATION
 #define ED_HT_IMPLEMENTATION
 #define ED_UI_IMPLEMENTATION
+#define ED_BUFFER_IMPLEMENTATION
 #include "string.h"
 #include "ht.h"
 #include "ui.h"
 #include "ed_array.h"
 #include "gfx.h"
+#include "buffer.h"
 
 // static Arena default_arena = {0};
 // static Arena temporary_arena = {0};
@@ -56,6 +58,8 @@ static struct {
 
     ui_context ui_cx;
     gfx_context_t *gfx_cx;
+
+    ed_buffer test_buffer;
 } state;
 
 void queue_text(string text, float position[2]) {
@@ -177,6 +181,7 @@ void ed_init(_gfx_frame_func frame_func) {
 
     gfx_push_texture_buffer(state.gfx_cx, font_bitmap_size, font_bitmap_size, font_bitmap, font_bitmap_size*font_bitmap_size * sizeof(uint8_t));
 
+    state.test_buffer = ed_buffer_from_file(_String(""));
 }
 
 void render_ui_text(string text, float position[2]) {
@@ -190,14 +195,16 @@ void ed_frame() {
     state.ui_cx.frame_elements.data[0].size.computed_size[0] = state.gfx_cx->frame_width;
     state.ui_cx.frame_elements.data[0].size.computed_size[1] = state.gfx_cx->frame_height;
 
-    ui_element(&state.ui_cx, _String("Number 1"), UI_AXIS_VERTICAL, ui_make_size(ui_fill, ui_fill), 0);
+    ui_element(&state.ui_cx, _String("Number 1"), UI_AXIS_VERTICAL, ui_make_size(ui_children_sum, ui_fill), 0);
     ui_push_parent(&state.ui_cx);
     {
         ui_element(&state.ui_cx, _String("ui element 2"), UI_AXIS_HORIZONTAL, ui_make_size(ui_fit_text, ui_fill), UI_FLAG_DRAW_BACKGROUND|UI_FLAG_DRAW_TEXT);
         ui_element(&state.ui_cx, _String("ui element 2"), UI_AXIS_HORIZONTAL, ui_make_size(ui_fit_text, ui_fit_text), UI_FLAG_DRAW_BACKGROUND|UI_FLAG_DRAW_TEXT);
     }
     ui_pop_parent(&state.ui_cx);
-    ui_element(&state.ui_cx, _String("Look I don't have to hardcode any values"), UI_AXIS_VERTICAL, ui_make_size(ui_fit_text, ui_fit_text), UI_FLAG_DRAW_BACKGROUND|UI_FLAG_DRAW_TEXT);
+
+    // Test buffer
+    ed_buffer_render(&state.ui_cx, &state.test_buffer);
 
     ui_compute_layout(&state.ui_cx, 0);
 
diff --git a/src/string.h b/src/string.h
index f9186d3..26b6d64 100644
--- a/src/string.h
+++ b/src/string.h
@@ -1,13 +1,17 @@
 #ifndef ED_STRING_INCLUDED
 #define ED_STRING_INCLUDED
 
+#include <stdlib.h>
 #include <stddef.h>
 #include <stdint.h>
+#include <stdbool.h>
 
-#define _String(text) ((string) { .data = (uint8_t*) text, .len = sizeof(text) })
+#define _String(text) ((string) { .data = (uint8_t*) text, .len = sizeof(text), .owned = false })
 typedef struct {
     uint8_t *data;
     size_t len;
+
+    bool owned;
 } string;
 
 
@@ -22,5 +26,17 @@ bool string_eq(string a, string b) {
     return true;
 }
 
+string string_copy(string a) {
+    string new_string;
+    
+    new_string.data = malloc(a.len * sizeof(uint8_t));
+    new_string.len = a.len;
+    new_string.owned = true;
+
+    memcpy(new_string.data, a.data, new_string.len * sizeof(uint8_t));
+
+    return new_string;
+}
+
 #endif
 #endif
diff --git a/src/ui.h b/src/ui.h
index 6e9fcd7..b00a527 100644
--- a/src/ui.h
+++ b/src/ui.h
@@ -190,11 +190,12 @@ size_t ui_element(ui_context *cx, string label, ui_axis axis, ui_semantic_size s
         frame_data.size.computed_size[0] = cache_data->size.computed_size[0];
         frame_data.size.computed_size[1] = cache_data->size.computed_size[1];
     } else {
-        assert("couldn't insert into ui element cache" && ht_set(&cx->cached_elements, label, &(ui_element_cache_data) {
+        bool did_insert = ht_set(&cx->cached_elements, label, &(ui_element_cache_data) {
             .label = label,
             .size = { 0 },
             .last_instantiated_index = cx->frame_index,
-        }));
+        });
+        assert("couldn't insert into ui element cache" && did_insert);
     }
 
     pushArray(ui_element_frame_data, &cx->frame_elements, frame_data);
@@ -452,6 +453,14 @@ void ui_prune(ui_context *cx) {
         }
     }
 
+    size_t child_index = _elm(0)->first;
+    do {
+        __auto_type elm = _elm(child_index);
+        if (elm->label.owned) {
+            free(elm->label.data);
+        }
+    } while ((child_index = _next(child_index)) < SIZE_MAX);
+
     cx->frame_index += 1;
     cx->frame_elements.size = 1;
     cx->frame_elements.data[0].first = SIZE_MAX;