summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLeo Tenenbaum <pommicket@gmail.com>2020-12-22 17:24:03 -0500
committerLeo Tenenbaum <pommicket@gmail.com>2020-12-22 17:24:03 -0500
commitbf906b2d5e1ddc5d77c06197d1105246e77b101a (patch)
treeb4e5ec39bdc077206b2e0cb0e35dd39c560a113e
parentc65b9f4b9faa734cb9c8804ccda0fa97f60e6381 (diff)
undo actually not quite done yet
-rw-r--r--base.h10
-rw-r--r--buffer.c108
-rw-r--r--main.c5
-rw-r--r--time.c65
4 files changed, 151 insertions, 37 deletions
diff --git a/base.h b/base.h
index b17a998..b4a9b00 100644
--- a/base.h
+++ b/base.h
@@ -24,16 +24,26 @@ typedef uint16_t u16;
typedef uint32_t u32;
typedef uint64_t u64;
+// (for u8 and u16, you can use %u)
#define U32_FMT "%" PRIu32
#define U64_FMT "%" PRIu64
+#define U8_MAX 0xff
+#define U16_MAX 0xffff
+#define U32_MAX 0xffffffff
+#define U64_MAX 0xffffffffffffffff
typedef int8_t i8;
typedef int16_t i16;
typedef int32_t i32;
typedef int64_t i64;
+// (for i8 and i16, you can use %d)
#define I32_FMT "%" PRId32
#define I64_FMT "%" PRId64
+#define I8_MAX 0x7f
+#define I16_MAX 0x7fff
+#define I32_MAX 0x7fffffff
+#define I64_MAX 0x7fffffffffffffff
typedef unsigned int uint;
typedef unsigned long ulong;
diff --git a/buffer.c b/buffer.c
index 8b1c097..a3426e7 100644
--- a/buffer.c
+++ b/buffer.c
@@ -1,10 +1,4 @@
// Text buffers - These store the contents of a file.
-#include <wctype.h>
-#include "unicode.h"
-#include "util.c"
-#include "text.h"
-#include "string32.c"
-#include "arr.c"
// a position in the buffer
typedef struct {
@@ -21,9 +15,10 @@ typedef struct {
// this refers to replacing prev_len characters (found in prev_text) at pos with new_len characters
typedef struct BufferEdit {
BufferPos pos;
- size_t new_len;
- size_t prev_len;
+ u32 new_len;
+ u32 prev_len;
char32_t *prev_text;
+ struct timespec time;
} BufferEdit;
typedef struct {
@@ -167,10 +162,13 @@ BufferPos buffer_end_pos(TextBuffer *buffer) {
}
-// get some number of characters of text from the given position in the buffer.
-static Status buffer_get_text_at_pos(TextBuffer *buffer, BufferPos pos, char32_t *text, size_t nchars) {
+// Get some number of characters of text from the given position in the buffer.
+// Returns the number of characters gotten.
+// You can pass NULL for text if you just want to know how many characters *could* be accessed before the
+// end of the file.
+static size_t buffer_get_text_at_pos(TextBuffer *buffer, BufferPos pos, char32_t *text, size_t nchars) {
if (!buffer_pos_valid(buffer, pos)) {
- return false;
+ return 0; // invalid position. no chars for you!
}
char32_t *p = text;
size_t chars_left = nchars;
@@ -179,24 +177,25 @@ static Status buffer_get_text_at_pos(TextBuffer *buffer, BufferPos pos, char32_t
while (chars_left) {
u32 chars_from_this_line = line->len - index;
if (chars_left <= chars_from_this_line) {
- memcpy(p, line->str, chars_left * sizeof *p);
+ if (p) memcpy(p, line->str, chars_left * sizeof *p);
chars_left = 0;
} else {
- memcpy(p, line->str, chars_from_this_line * sizeof *p);
- p += chars_from_this_line;
- *p++ = U'\n';
+ if (p) {
+ memcpy(p, line->str, chars_from_this_line * sizeof *p);
+ p += chars_from_this_line;
+ *p++ = U'\n';
+ }
chars_left -= chars_from_this_line+1;
}
index = 0;
++line;
if (chars_left && line == end) {
- // reached end of file before getting full text... this isn't good
- buffer_seterr(buffer, "Failed to fetch %zu characters at " U32_FMT ":" U32_FMT ".", nchars, pos.line+1, pos.index+1);
- return false;
+ // reached end of file before getting full text
+ break;
}
}
- return true;
+ return nchars - chars_left;
}
static BufferPos buffer_pos_advance(TextBuffer *buffer, BufferPos pos, size_t nchars) {
@@ -250,7 +249,7 @@ static i64 buffer_pos_diff(TextBuffer *buffer, BufferPos p1, BufferPos p2) {
return total * factor;
}
-static Status buffer_create_edit(TextBuffer *buffer, BufferEdit *edit, BufferPos start, size_t prev_len, size_t new_len) {
+static Status buffer_create_edit(TextBuffer *buffer, BufferEdit *edit, BufferPos start, u32 prev_len, u32 new_len) {
if (prev_len == 0)
edit->prev_text = NULL; // if there's no previous text, don't allocate anything
else
@@ -260,10 +259,8 @@ static Status buffer_create_edit(TextBuffer *buffer, BufferEdit *edit, BufferPos
edit->prev_len = prev_len;
edit->new_len = new_len;
if (prev_len) {
- if (!buffer_get_text_at_pos(buffer, start, edit->prev_text, prev_len)) {
- buffer_edit_free(edit);
- return false;
- }
+ size_t chars_gotten = buffer_get_text_at_pos(buffer, start, edit->prev_text, prev_len);
+ edit->prev_len = (u32)chars_gotten; // update the previous length, in case it went past the end of the file
}
return true;
} else {
@@ -273,7 +270,7 @@ static Status buffer_create_edit(TextBuffer *buffer, BufferEdit *edit, BufferPos
// add this edit to the undo history
// call this before actually changing buffer
-static void buffer_edit(TextBuffer *buffer, BufferPos start, size_t prev_len, size_t new_len) {
+static void buffer_edit(TextBuffer *buffer, BufferPos start, u32 prev_len, u32 new_len) {
BufferEdit edit = {0};
if (buffer_create_edit(buffer, &edit, start, prev_len, new_len)) {
buffer_append_edit(buffer, &edit);
@@ -950,6 +947,12 @@ static void buffer_insert_lines(TextBuffer *buffer, u32 where, u32 number) {
// inserts the given text, returning the position of the end of the text
BufferPos buffer_insert_text_at_pos(TextBuffer *buffer, BufferPos pos, String32 str) {
+ if (str.len > U32_MAX) {
+ buffer_seterr(buffer, "Inserting too much text (length: %zu).", str.len);
+ BufferPos ret = {0,0};
+ return ret;
+ }
+
if (buffer->store_undo_events) {
BufferEdit *last_edit = buffer->undo_history;
i64 where_in_last_edit = last_edit ? buffer_pos_diff(buffer, last_edit->pos, pos) : -1;
@@ -958,7 +961,7 @@ BufferPos buffer_insert_text_at_pos(TextBuffer *buffer, BufferPos pos, String32
last_edit->new_len += str.len;
} else {
// create a new edit for this insertion
- buffer_edit(buffer, pos, 0, str.len);
+ buffer_edit(buffer, pos, 0, (u32)str.len);
}
}
@@ -1062,18 +1065,52 @@ static void buffer_delete_lines(TextBuffer *buffer, u32 first_line_idx, u32 nlin
memmove(first_line, end, (size_t)(buffer->lines + buffer->nlines - end) * sizeof(Line));
}
-void buffer_delete_chars_at_pos(TextBuffer *buffer, BufferPos pos, i64 nchars) {
- assert(nchars >= 0);
- if (nchars <= 0) return;
+void buffer_delete_chars_at_pos(TextBuffer *buffer, BufferPos pos, i64 nchars_) {
+ if (nchars_ < 0) {
+ buffer_seterr(buffer, "Deleting negative characters (specifically, " I64_FMT ").", nchars_);
+ return;
+ }
+ if (nchars_ <= 0) return;
+ if (nchars_ > U32_MAX) nchars_ = U32_MAX;
+ u32 nchars = (u32)nchars_;
+
+ // Correct nchars in case it goes past the end of the file.
+ // Why do we need to correct it?
+ // When generating undo events, we allocate the given number of characters,
+ // even if that's not necessary (also see the allocating of `temp` below).
+ nchars = (u32)buffer_get_text_at_pos(buffer, pos, NULL, nchars);
if (buffer->store_undo_events) {
BufferEdit *last_edit = buffer->undo_history;
i64 where_in_last_edit = last_edit ? buffer_pos_diff(buffer, last_edit->pos, pos) : -1;
if (where_in_last_edit >= 0 && where_in_last_edit <= (i64)last_edit->new_len) {
+
// merge this edit into the last one.
+ #if 0
+ // ah this doesn't work
+ // some of the deleted text might be inside the edit.
+ // this is gonna be annoying
+
+ char32_t *temp = calloc(nchars, sizeof *temp);
+ if (temp) {
+ buffer_get_text_at_pos(buffer, pos, temp, nchars);
+ char32_t *prev_text = buffer_realloc(buffer, last_edit->prev_text,
+ (last_edit->prev_len + nchars) * sizeof *prev_text);
+ if (prev_text) {
+ // make space for our text
+ memmove(prev_text + where_in_last_edit + nchars, prev_text + where_in_last_edit,
+ last_edit->prev_len - (where_in_last_edit + nchars) * sizeof *prev_text);
+ // insert our text into new_text
+ memcpy(prev_text + where_in_last_edit, temp, nchars * sizeof *prev_text);
+ last_edit->prev_len += nchars;
+ last_edit->prev_text = prev_text;
+ }
+ free(temp);
+ }
+ #endif
} else {
// create a new edit
- buffer_edit(buffer, pos, (size_t)nchars, 0);
+ buffer_edit(buffer, pos, nchars, 0);
}
}
@@ -1090,12 +1127,9 @@ void buffer_delete_chars_at_pos(TextBuffer *buffer, BufferPos pos, i64 nchars) {
nchars -= last_line->len+1;
}
if (last_line == lines_end) {
- // delete everything to the end of the file
- for (u32 idx = line_idx + 1; idx < buffer->nlines; ++idx) {
- buffer_line_free(&buffer->lines[idx]);
- }
- buffer_shorten(buffer, line_idx + 1);
- } else {
+ assert(nchars == 0); // we already shortened nchars to go no further than the end of the file
+ }
+ {
// join last_line to line.
u32 last_line_chars_left = (u32)(last_line->len - nchars);
if (buffer_line_set_min_capacity(buffer, line, line->len + last_line_chars_left)) {
@@ -1111,7 +1145,7 @@ void buffer_delete_chars_at_pos(TextBuffer *buffer, BufferPos pos, i64 nchars) {
} else {
// just delete characters from this line
memmove(line->str + index, line->str + index + nchars, (size_t)(line->len - (nchars + index)) * sizeof(char32_t));
- line->len -= (u32)nchars;
+ line->len -= nchars;
}
}
diff --git a/main.c b/main.c
index 0a7b420..ea2c16f 100644
--- a/main.c
+++ b/main.c
@@ -8,7 +8,12 @@ no_warn_start
no_warn_end
#include <GL/gl.h>
#include <locale.h>
+#include <wctype.h>
+#include "unicode.h"
+#include "util.c"
#include "text.h"
+#include "string32.c"
+#include "arr.c"
#include "buffer.c"
static void die(char const *fmt, ...) {
diff --git a/time.c b/time.c
new file mode 100644
index 0000000..fb007ba
--- /dev/null
+++ b/time.c
@@ -0,0 +1,65 @@
+#include <time.h>
+#include <errno.h>
+#include <sys/stat.h>
+
+static struct timespec time_last_modified(char const *filename) {
+#if __unix__
+ struct stat statbuf = {};
+ stat(filename, &statbuf);
+ return statbuf.st_mtim;
+#else
+ // windows' _stat does not have st_mtim
+ struct _stat statbuf = {};
+ struct timespec ts = {};
+ _stat(filename, &statbuf);
+ ts.tv_sec = statbuf.st_mtime;
+ return ts;
+#endif
+}
+
+static int timespec_cmp(struct timespec a, struct timespec b) {
+ if (a.tv_sec > b.tv_sec) return 1;
+ if (a.tv_sec < b.tv_sec) return -1;
+ if (a.tv_nsec > b.tv_nsec) return 1;
+ if (a.tv_nsec < b.tv_nsec) return -1;
+ return 0;
+}
+
+static bool timespec_eq(struct timespec a, struct timespec b) {
+ return timespec_cmp(a, b) == 0;
+}
+
+static struct timespec timespec_max(struct timespec a, struct timespec b) {
+ return timespec_cmp(a, b) < 0 ? b : a;
+}
+
+// sleep for a certain number of nanoseconds
+static void sleep_ns(u64 ns) {
+#if __unix__
+ struct timespec rem = {}, req = {
+ (time_t)(ns / 1000000000),
+ (long)(ns % 1000000000)
+ };
+
+ while (nanosleep(&req, &rem) == EINTR) // sleep interrupted by signal
+ req = rem;
+#else
+ // windows....
+ Sleep((DWORD)(ns / 1000000));
+#endif
+}
+
+// sleep for microseconds
+static void time_sleep_us(u64 us) {
+ sleep_ns(us * 1000);
+}
+
+// sleep for milliseconds
+static void time_sleep_ms(u64 ms) {
+ sleep_ns(ms * 1000000);
+}
+
+// sleep for seconds
+static void time_sleep_s(u64 s) {
+ sleep_ns(s * 1000000000);
+}