diff --git a/src/backup/todo.txt b/src/backup/todo.txt index 115dac2..200f520 100644 --- a/src/backup/todo.txt +++ b/src/backup/todo.txt @@ -1,9 +1,7 @@ - What precise workflow do I need for me to be viable to use this? - From a user (novice) point of view, how does it look like? -- Close all (ask to save) - - Close buffer instead of window (ask to save) -- Open all in the folder and ctrl + p like in VSCode (without special buffers) +- ctrl + p like in VSCode (without special buffers) - Guide on the first page for new users with links to configs, tutorials - Window, View, Buffer + flags design (or is completely new kind based approach needed for Windows/Views?) diff --git a/src/basic/basic_os.cpp b/src/basic/basic_os.cpp index 472b523..2b2d56e 100644 --- a/src/basic/basic_os.cpp +++ b/src/basic/basic_os.cpp @@ -1147,4 +1147,4 @@ API void CloseStdin(Process *process) { API double GetTimeSeconds() { return GetTimeMicros() / 1000000.0; -} \ No newline at end of file +} diff --git a/src/text_editor/buffer.cpp b/src/text_editor/buffer.cpp index 5752cc2..28a12ea 100644 --- a/src/text_editor/buffer.cpp +++ b/src/text_editor/buffer.cpp @@ -1436,6 +1436,18 @@ bool IsNull(Buffer *buffer) { return buffer->id.id == 0; } +void Close(BufferID id) { + Buffer *buffer = GetBuffer(id, NULL); + if (buffer) { + if (buffer->id.id == 0) { + return; + } + + buffer->close = true; + RunGCThisFrame = true; + } +} + Buffer *CreateBuffer(Allocator allocator, String name, Int size) { Buffer *result = AllocBuffer(allocator, name, size); Add(&Buffers, result); @@ -1467,15 +1479,13 @@ void InitBuffers() { Assert(null_buffer->id == NullBufferID && null_view->id == NullViewID); TraceBuffer = CreateBuffer(sys_allocator, GetUniqueBufferName(WorkDir, "trace")); TraceBuffer->special = true; - TraceView = CreateView(TraceBuffer->id); + TraceBuffer->no_history = true; GCInfoBuffer = CreateBuffer(sys_allocator, GetUniqueBufferName(WorkDir, "gc")); GCInfoBuffer->special = true; GCInfoBuffer->no_history = true; EventBuffer = CreateBuffer(sys_allocator, GetUniqueBufferName(WorkDir, "events")); EventBuffer->no_history = true; EventBuffer->special = true; - ScratchBuffer = BufferOpenFile(GetUniqueBufferName(WorkDir, "scratch")); - ScratchBuffer->special = true; } Int ConvertUTF8ToUTF16UnixLine(String string, char16_t *buffer, Int buffer_cap) { @@ -1558,7 +1568,7 @@ bool BufferIsReferenced(BufferID buffer_id) { return true; } - if (FindView(buffer_id)) { + if (FindView(buffer_id, NULL)) { return true; } diff --git a/src/text_editor/buffer.h b/src/text_editor/buffer.h index b62de58..4736a26 100644 --- a/src/text_editor/buffer.h +++ b/src/text_editor/buffer.h @@ -40,7 +40,7 @@ struct Buffer { unsigned changed_on_disk : 1; unsigned garbage : 1; unsigned dont_try_to_save_in_bulk_ops : 1; - unsigned kill : 1; + unsigned close : 1; unsigned special : 1; }; }; diff --git a/src/text_editor/commands.cpp b/src/text_editor/commands.cpp index 0cb5d65..83ce1ce 100644 --- a/src/text_editor/commands.cpp +++ b/src/text_editor/commands.cpp @@ -248,7 +248,7 @@ void ReportWarningf(const char *fmt, ...) { void ReportDebugf(const char *fmt, ...) { Scratch scratch; STRING_FORMAT(scratch, fmt, string); - Appendf(TraceView, "%S\n", string); + RawAppendf(TraceBuffer, "%S\n", string); } void MoveCursorByPageSize(Window *window, int direction, bool shift = false) { @@ -427,29 +427,6 @@ void NewDir(Window *window, String name = "") { Open(name); } -String CodeEndings[] = { ".c", ".cpp", ".h", ".hpp", ".py", ".lua", ".cxx", ".hxx", }; - -void ListFilesRecursive(Buffer *buffer, String dir) { - Scratch scratch(buffer->line_starts.allocator); - for (FileIter it = IterateFiles(scratch, dir); IsValid(it); Advance(&it)) { - if (it.filename == ".git") { - continue; - } - if (it.is_directory) { - ListFilesRecursive(buffer, it.absolute_path); - } else { - bool match = false; - ForItem (ending, CodeEndings) { - if (EndsWith(it.absolute_path, ending)) { - match = true; - break; - } - } - if (match) RawAppendf(buffer, "%-80S || %S\n", it.filename, it.absolute_path); - } - } -} - View *ExecHidden(String buffer_name, String cmd, String working_dir) { View *view = OpenBufferView(buffer_name); Buffer *buffer = GetBuffer(view->active_buffer); @@ -668,14 +645,66 @@ void Command_SetWorkDir() { WorkDir = ChopLastSlash(main.buffer->name); } RegisterCommand(Command_SetWorkDir, ""); +String CodeEndings[] = { ".c", ".cpp", ".h", ".hpp", ".py", ".lua", ".cxx", ".hxx", }; + +void ListFilesRecursive(Buffer *buffer, String dir) { + Scratch scratch(buffer->line_starts.allocator); + for (FileIter it = IterateFiles(scratch, dir); IsValid(it); Advance(&it)) { + if (it.filename == ".git") { + continue; + } + if (it.is_directory) { + ListFilesRecursive(buffer, it.absolute_path); + } else { + bool match = false; + ForItem (ending, CodeEndings) { + if (EndsWith(it.absolute_path, ending)) { + match = true; + break; + } + } + if (match) RawAppendf(buffer, "%-80S || %S\n", it.filename, it.absolute_path); + } + } +} + +void OpenCodeRecursive(String dir) { + Scratch scratch; + for (FileIter it = IterateFiles(scratch, dir); IsValid(it); Advance(&it)) { + if (it.filename == ".git") { + continue; + } + if (it.filename == "SDL") { + continue; + } + if (it.is_directory) { + OpenCodeRecursive(it.absolute_path); + } else { + bool match = false; + ForItem (ending, CodeEndings) { + if (EndsWith(it.absolute_path, ending)) { + match = true; + break; + } + } + if (match) { + BufferOpenFile(it.absolute_path); + } + } + } +} + +void Command_OpenCode() { + OpenCodeRecursive(WorkDir); +} RegisterCommand(Command_OpenCode, ""); + void Command_KillProcess() { BSet main = GetBSet(LastActiveLayoutWindowID); KillProcess(main.view); } RegisterCommand(Command_KillProcess, ""); void Command_CloseWindow() { - BSet main = GetBSet(LastActiveLayoutWindowID); - main.window->kill = true; + Close(LastActiveLayoutWindowID); } RegisterCommand(Command_CloseWindow, ""); SaveResult TrySavingBuffer(Buffer *buffer) { @@ -717,7 +746,7 @@ void Command_Close() { } main.window->active_view = FindInactiveView(); - main.view->kill = true; + Close(main.view->id); bool ref = false; For (Views) { @@ -731,7 +760,7 @@ void Command_Close() { } if (!ref) { - main.buffer->kill = true; + Close(main.buffer->id); } } RegisterCommand(Command_Close, "ctrl-w"); @@ -747,13 +776,13 @@ void Command_CloseAll() { if (it->special) { continue; } - it->kill = true; + Close(it->id); } For (Buffers) { if (it->special) { continue; } - it->kill = true; + Close(it->id); } } } RegisterCommand(Command_CloseAll, ""); diff --git a/src/text_editor/coroutines.cpp b/src/text_editor/coroutines.cpp index f60ea9b..d854551 100644 --- a/src/text_editor/coroutines.cpp +++ b/src/text_editor/coroutines.cpp @@ -17,6 +17,7 @@ mco_coro *CoAdd(CoroutineProc *proc) { } void CoUpdate(Event *event) { + ProfileFunction(); IterRemove(ActiveCoroutines) { IterRemovePrepare(ActiveCoroutines); @@ -45,4 +46,4 @@ Event *CoYield(mco_coro *co) { Assert(ok == MCO_SUCCESS); return event; -} \ No newline at end of file +} diff --git a/src/text_editor/draw.cpp b/src/text_editor/draw.cpp index f7052ce..06d8764 100644 --- a/src/text_editor/draw.cpp +++ b/src/text_editor/draw.cpp @@ -100,6 +100,7 @@ void DrawUnderline(Window *window, View *view, Buffer *buffer, Range range, Colo } void DrawWindow(Window *window, Event &event) { + ProfileFunction(); View *view = GetView(window->active_view); Buffer *buffer = GetBuffer(view->active_buffer); Rect2 screen_rect = Rect0Size(event.xwindow, event.ywindow); diff --git a/src/text_editor/globals.cpp b/src/text_editor/globals.cpp index 113e88f..1827dc1 100644 --- a/src/text_editor/globals.cpp +++ b/src/text_editor/globals.cpp @@ -5,6 +5,7 @@ int FullScreenPositionX, FullScreenPositionY; bool Testing = false; bool AppIsRunning = true; bool WaitForEvents = true; +bool RunGCThisFrame; WindowID WindowIDs; ViewID ViewIDs; @@ -40,7 +41,6 @@ Caret DocumentAnchor; Buffer *GCInfoBuffer; Buffer *EventBuffer; -Buffer *ScratchBuffer; Buffer *TraceBuffer; View *TraceView; @@ -50,8 +50,8 @@ Array EventPlayback; BlockArena Perm; // clipboard -BlockArena ClipboardArena; -String16 SavedClipboardString; +BlockArena ClipboardArena; +String16 SavedClipboardString; Array SavedClipboardCarets = {SysAllocator}; struct InternTable { diff --git a/src/text_editor/process.cpp b/src/text_editor/process.cpp index eccabe2..39bd12a 100644 --- a/src/text_editor/process.cpp +++ b/src/text_editor/process.cpp @@ -5,6 +5,7 @@ // 'less' program which errors out and doesn't print anything // @todo: maybe I should ask someone smarter about this! void UpdateProcesses() { + ProfileFunction(); IterRemove(ActiveProcesses) { IterRemovePrepare(ActiveProcesses); Scratch scratch; @@ -70,4 +71,4 @@ bool ProcessIsActive(ViewID view) { } } return false; -} \ No newline at end of file +} diff --git a/src/text_editor/profiler.h b/src/text_editor/profiler.h index e5eb1ed..beb5304 100644 --- a/src/text_editor/profiler.h +++ b/src/text_editor/profiler.h @@ -1,12 +1,373 @@ -#if 0 - #include "spall.h" +#if 1 +// SPDX-FileCopyrightText: © 2023 Phillip Trudeau-Tavara +// SPDX-License-Identifier: MIT + +/* + +TODO: Optional Helper APIs: + + - Compression API: would require a mutexed lockable context (yuck...) + - Either using a ZIP library, a name cache + TIDPID cache, or both (but ZIP is likely more than enough!!!) + - begin()/end() writes compressed chunks to a caller-determined destination + - The destination can be the buffered-writing API or a custom user destination + - Ultimately need to take a lock with some granularity... can that be the caller's responsibility? + + - Counter Event: should allow tracking arbitrary named values with a single event, for memory and frame profiling + + - Ring-buffer API + spall_ring_init + spall_ring_emit_begin + spall_ring_emit_end + spall_ring_flush +*/ + +#ifndef SPALL_H +#define SPALL_H + +#if !defined(_MSC_VER) || defined(__clang__) +#define SPALL_NOINSTRUMENT __attribute__((no_instrument_function)) +#define SPALL_FORCEINLINE __attribute__((always_inline)) +#else +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS +#endif +#define SPALL_NOINSTRUMENT // Can't noinstrument on MSVC! +#define SPALL_FORCEINLINE __forceinline +#endif + +#include +#include +#include +#include + +#define SPALL_FN static inline SPALL_NOINSTRUMENT +#define SPALL_MIN(a, b) (((a) < (b)) ? (a) : (b)) +#define SPALL_MAX(a, b) (((a) > (b)) ? (a) : (b)) + +#pragma pack(push, 1) + +typedef struct SpallHeader { + uint64_t magic_header; // = 0x0BADF00D + uint64_t version; // = 3 + double timestamp_unit; + uint64_t must_be_0; +} SpallHeader; + +typedef enum { + SpallEventType_Invalid = 0, + SpallEventType_Custom_Data = 1, // Basic readers can skip this. + SpallEventType_StreamOver = 2, + + SpallEventType_Begin = 3, + SpallEventType_End = 4, + SpallEventType_Instant = 5, + + SpallEventType_Overwrite_Timestamp = 6, // Retroactively change timestamp units - useful for incrementally improving RDTSC frequency. + SpallEventType_Pad_Skip = 7, + + SpallEventType_NameProcess = 8, + SpallEventType_NameThread = 9, +} SpallEventType; + +typedef struct SpallBufferHeader { + uint32_t size; + uint32_t tid; + uint32_t pid; + uint64_t first_ts; +} SpallBufferHeader; + +typedef struct SpallBeginEvent { + uint8_t type; // = SpallEventType_Begin + uint64_t when; + + uint8_t name_length; + uint8_t args_length; +} SpallBeginEvent; + +typedef struct SpallBeginEventMax { + SpallBeginEvent event; + char name_bytes[255]; + char args_bytes[255]; +} SpallBeginEventMax; + +typedef struct SpallEndEvent { + uint8_t type; // = SpallEventType_End + uint64_t when; +} SpallEndEvent; + +typedef struct SpallPadSkipEvent { + uint8_t type; // = SpallEventType_Pad_Skip + uint32_t size; +} SpallPadSkipEvent; + +typedef struct SpallNameContainerEvent { + uint8_t type; // = SpallEventType_NameThread/Process + uint8_t name_length; +} SpallNameContainerEvent; + +typedef struct SpallNameContainerEventMax { + SpallNameContainerEvent event; + char name_bytes[255]; +} SpallNameContainerEventMax; + +#pragma pack(pop) + +typedef struct SpallProfile SpallProfile; + +// Important!: If you define your own callbacks, mark them SPALL_NOINSTRUMENT! +typedef bool (*SpallWriteCallback)(SpallProfile *self, const void *data, size_t length); +typedef bool (*SpallFlushCallback)(SpallProfile *self); +typedef void (*SpallCloseCallback)(SpallProfile *self); + +struct SpallProfile { + double timestamp_unit; + SpallWriteCallback write; + SpallFlushCallback flush; + SpallCloseCallback close; + void *data; +}; + +// Important!: If you are writing Begin/End events, then do NOT write +// events for the same PID + TID pair on different buffers!!! +typedef struct SpallBuffer { + void *data; + size_t length; + uint32_t tid; + uint32_t pid; + + // Internal data - don't assign this + size_t head; + uint64_t first_ts; +} SpallBuffer; + +#ifdef __cplusplus +extern "C" { +#endif + +SPALL_FN SPALL_FORCEINLINE bool spall__file_write(SpallProfile *ctx, const void *p, size_t n) { + if (fwrite(p, n, 1, (FILE *)ctx->data) != 1) return false; + return true; +} +SPALL_FN bool spall__file_flush(SpallProfile *ctx) { + if (fflush((FILE *)ctx->data)) return false; + return true; +} +SPALL_FN void spall__file_close(SpallProfile *ctx) { + fclose((FILE *)ctx->data); + ctx->data = NULL; +} + +SPALL_FN SPALL_FORCEINLINE bool spall__buffer_flush(SpallProfile *ctx, SpallBuffer *wb, uint64_t ts) { + wb->first_ts = SPALL_MAX(wb->first_ts, ts); + + SpallBufferHeader hdr; + hdr.size = (uint32_t)(wb->head - sizeof(SpallBufferHeader)); + hdr.pid = wb->pid; + hdr.tid = wb->tid; + hdr.first_ts = wb->first_ts; + + memcpy(wb->data, &hdr, sizeof(hdr)); + + if (!ctx->write(ctx, wb->data, wb->head)) return false; + if (!ctx->flush(ctx)) return false; + wb->head = sizeof(SpallBufferHeader); + return true; +} + +SPALL_FN bool spall_buffer_flush(SpallProfile *ctx, SpallBuffer *wb) { + if (!spall__buffer_flush(ctx, wb, 0)) return false; + return true; +} + +SPALL_FN bool spall_buffer_quit(SpallProfile *ctx, SpallBuffer *wb) { + if (!spall_buffer_flush(ctx, wb)) return false; + return true; +} + +SPALL_FN size_t spall_build_header(void *buffer, size_t rem_size, double timestamp_unit) { + size_t header_size = sizeof(SpallHeader); + if (header_size > rem_size) { + return 0; + } + + SpallHeader *header = (SpallHeader *)buffer; + header->magic_header = 0x0BADF00D; + header->version = 3; + header->timestamp_unit = timestamp_unit; + header->must_be_0 = 0; + return header_size; +} +SPALL_FN SPALL_FORCEINLINE size_t spall_build_begin(void *buffer, size_t rem_size, const char *name, int32_t name_len, const char *args, int32_t args_len, uint64_t when) { + SpallBeginEventMax *ev = (SpallBeginEventMax *)buffer; + uint8_t trunc_name_len = (uint8_t)SPALL_MIN(name_len, 255); // will be interpreted as truncated in the app (?) + uint8_t trunc_args_len = (uint8_t)SPALL_MIN(args_len, 255); // will be interpreted as truncated in the app (?) + + size_t ev_size = sizeof(SpallBeginEvent) + trunc_name_len + trunc_args_len; + if (ev_size > rem_size) { + return 0; + } + + ev->event.type = SpallEventType_Begin; + ev->event.when = when; + ev->event.name_length = trunc_name_len; + ev->event.args_length = trunc_args_len; + memcpy(ev->name_bytes, name, trunc_name_len); + memcpy(ev->name_bytes + trunc_name_len, args, trunc_args_len); + + return ev_size; +} +SPALL_FN SPALL_FORCEINLINE size_t spall_build_end(void *buffer, size_t rem_size, uint64_t when) { + size_t ev_size = sizeof(SpallEndEvent); + if (ev_size > rem_size) { + return 0; + } + + SpallEndEvent *ev = (SpallEndEvent *)buffer; + ev->type = SpallEventType_End; + ev->when = when; + + return ev_size; +} +SPALL_FN SPALL_FORCEINLINE size_t spall_build_name(void *buffer, size_t rem_size, const char *name, int32_t name_len, SpallEventType type) { + SpallNameContainerEventMax *ev = (SpallNameContainerEventMax *)buffer; + uint8_t trunc_name_len = (uint8_t)SPALL_MIN(name_len, 255); // will be interpreted as truncated in the app (?) + + size_t ev_size = sizeof(SpallNameContainerEvent) + trunc_name_len; + if (ev_size > rem_size) { + return 0; + } + + ev->event.type = type; + ev->event.name_length = trunc_name_len; + memcpy(ev->name_bytes, name, trunc_name_len); + + return ev_size; +} + +SPALL_FN void spall_quit(SpallProfile *ctx) { + if (!ctx) return; + if (ctx->close) ctx->close(ctx); + + memset(ctx, 0, sizeof(*ctx)); +} + +SPALL_FN bool spall_init_callbacks(double timestamp_unit, + SpallWriteCallback write, + SpallFlushCallback flush, + SpallCloseCallback close, + void *userdata, + SpallProfile *ctx) { + + if (timestamp_unit < 0) return false; + + memset(ctx, 0, sizeof(*ctx)); + ctx->timestamp_unit = timestamp_unit; + ctx->data = userdata; + ctx->write = write; + ctx->flush = flush; + ctx->close = close; + + SpallHeader header; + size_t len = spall_build_header(&header, sizeof(header), timestamp_unit); + if (!ctx->write(ctx, &header, len)) { + spall_quit(ctx); + return false; + } + + return true; +} + +SPALL_FN bool spall_init_file(const char* filename, double timestamp_unit, SpallProfile *ctx) { + if (!filename) return false; + + FILE *f = fopen(filename, "wb"); // TODO: handle utf8 and long paths on windows + if (f) { // basically freopen() but we don't want to force users to lug along another macro define + fclose(f); + f = fopen(filename, "ab"); + } + if (!f) { return false; } + + return spall_init_callbacks(timestamp_unit, spall__file_write, spall__file_flush, spall__file_close, (void *)f, ctx); +} + +SPALL_FN bool spall_flush(SpallProfile *ctx) { + if (!ctx->flush(ctx)) return false; + return true; +} + +SPALL_FN bool spall_buffer_init(SpallProfile *ctx, SpallBuffer *wb) { + // Fails if buffer is not big enough to contain at least one event! + if (wb->length < sizeof(SpallBufferHeader) + sizeof(SpallBeginEventMax)) { + return false; + } + + wb->head = sizeof(SpallBufferHeader); + return true; +} + + +SPALL_FN SPALL_FORCEINLINE bool spall_buffer_begin_args(SpallProfile *ctx, SpallBuffer *wb, const char *name, int32_t name_len, const char *args, int32_t args_len, uint64_t when) { + if ((wb->head + sizeof(SpallBeginEventMax)) > wb->length) { + if (!spall__buffer_flush(ctx, wb, when)) { + return false; + } + } + + wb->head += spall_build_begin((char *)wb->data + wb->head, wb->length - wb->head, name, name_len, args, args_len, when); + + return true; +} + +SPALL_FN bool spall_buffer_begin(SpallProfile *ctx, SpallBuffer *wb, const char *name, int32_t name_len, uint64_t when) { + return spall_buffer_begin_args(ctx, wb, name, name_len, "", 0, when); +} + +SPALL_FN bool spall_buffer_end(SpallProfile *ctx, SpallBuffer *wb, uint64_t when) { + if ((wb->head + sizeof(SpallEndEvent)) > wb->length) { + if (!spall__buffer_flush(ctx, wb, when)) { + return false; + } + } + + wb->head += spall_build_end((char *)wb->data + wb->head, wb->length - wb->head, when); + return true; +} + +SPALL_FN bool spall_buffer_name_thread(SpallProfile *ctx, SpallBuffer *wb, const char *name, int32_t name_len) { + if ((wb->head + sizeof(SpallNameContainerEvent)) > wb->length) { + if (!spall__buffer_flush(ctx, wb, 0)) { + return false; + } + } + + wb->head += spall_build_name((char *)wb->data + wb->head, wb->length - wb->head, name, name_len, SpallEventType_NameThread); + return true; +} + +SPALL_FN bool spall_buffer_name_process(SpallProfile *ctx, SpallBuffer *wb, const char *name, int32_t name_len) { + if ((wb->head + sizeof(SpallNameContainerEvent)) > wb->length) { + if (!spall__buffer_flush(ctx, wb, 0)) { + return false; + } + } + + wb->head += spall_build_name((char *)wb->data + wb->head, wb->length - wb->head, name, name_len, SpallEventType_NameProcess); + return true; +} + +#ifdef __cplusplus +} +#endif + +#endif // SPALL_H + static SpallProfile spall_ctx; static SpallBuffer spall_buffer; double GetTimeMicros(void); void BeginProfiler() { - spall_ctx = spall_init_file("hello_world.spall", 1); + spall_init_file("te.spall", 1, &spall_ctx); int buffer_size = 1 * 1024 * 1024; unsigned char *buffer = (unsigned char *)malloc(buffer_size); @@ -25,14 +386,14 @@ void _BeginProfileScope(const char *name, int len) { spall_buffer_begin(&spall_ctx, &spall_buffer, name, // name of your name len, // name len minus the null terminator - GetTimeMicros() // timestamp in microseconds -- start of your timing block + (uint64_t)GetTimeMicros() // timestamp in microseconds -- start of your timing block ); } #define BeginProfileScope(name) _BeginProfileScope(#name, sizeof(#name) - 1) void EndProfileScope() { spall_buffer_end(&spall_ctx, &spall_buffer, - GetTimeMicros() // timestamp in microseconds -- end of your timing block + (uint64_t)GetTimeMicros() // timestamp in microseconds -- end of your timing block ); } #define ProfileScope(name) ProfileScope_ PROFILE_SCOPE_VAR_##name(#name, sizeof(#name) - 1) @@ -48,4 +409,4 @@ struct ProfileScope_ { #define EndProfiler() #define BeginProfileScope(name) #define EndProfileScope() -#endif \ No newline at end of file +#endif diff --git a/src/text_editor/text_editor.cpp b/src/text_editor/text_editor.cpp index a4ebed6..737be9c 100644 --- a/src/text_editor/text_editor.cpp +++ b/src/text_editor/text_editor.cpp @@ -412,32 +412,20 @@ void OnCommand(Event event) { if (event.kind == EVENT_QUIT) { Command_Quit(); } + + IF_DEBUG(AssertRanges(main.view->carets)); + IF_DEBUG(AssertRanges(active.view->carets)); } void GarbageCollect() { + if (RunGCThisFrame == false) { + return; + } + RunGCThisFrame = false; + + ProfileFunction(); Allocator sys_allocator = GetSystemAllocator(); - For (Views) { - IF_DEBUG(AssertRanges(it->carets)); - } - - For (Windows) { - if (it->sync_visibility_with_focus) { - if (it->id == ActiveWindowID) { - it->visible = true; - } else { - it->visible = false; - } - } - } - - Window *window = GetWindow(ActiveWindowID); - if (ActiveWindowID.id != LastActiveLayoutWindowID.id) { - if (window->layout) { - LastActiveLayoutWindowID = ActiveWindowID; - } - } - For(Buffers) { if (it->file_mod_time) { int64_t new_file_mod_time = GetFileModTime(it->name); @@ -451,8 +439,12 @@ void GarbageCollect() { IterRemove(Views) { IterRemovePrepare(Views); + if (it->close && it->id.id == 0) { + InvalidCodepath(); + } + Buffer *buffer = GetBuffer(it->active_buffer); - if (it->kill == 0) { + if (!it->close) { if (!buffer->garbage) { continue; } @@ -472,13 +464,11 @@ void GarbageCollect() { IterRemove(Buffers) { IterRemovePrepare(Buffers); - - if (it->id.id == 0) { - it->kill = 0; - continue; + if (it->close && it->id.id == 0) { + InvalidCodepath(); } - if (it->kill == 0) { + if (!it->close) { if (!it->garbage) { continue; } @@ -496,7 +486,11 @@ void GarbageCollect() { IterRemove(Windows) { IterRemovePrepare(Windows); - if (it->kill) { + if (it->close && it->id.id == 0) { + InvalidCodepath(); + } + + if (it->close) { RawAppendf(GCInfoBuffer, "Wind %d %S\n", (int)it->id.id); Dealloc(&it->goto_history); Dealloc(&it->goto_redo); @@ -512,10 +506,12 @@ void Update(Event event) { Scratch scratch; Array order = GetWindowZOrder(scratch); For(order) { - if (!it->visible) continue; - View *view = GetView(it->active_view); + if (!it->visible) { + continue; + } + View *view = GetView(it->active_view); view->main_caret_on_begin_frame = view->carets[0]; - view->update_scroll = true; + view->update_scroll = true; } OnCommand(event); @@ -524,7 +520,27 @@ void Update(Event event) { CommandWindowUpdate(); UpdateProcesses(); CoUpdate(&event); - GarbageCollect(); + + { + ProfileScope(WindowEndOfFrameVisibilityAndLastActive); + For (Windows) { + if (it->sync_visibility_with_focus) { + if (it->id == ActiveWindowID) { + it->visible = true; + } else { + it->visible = false; + } + } + } + + Window *window = GetWindow(ActiveWindowID); + if (ActiveWindowID.id != LastActiveLayoutWindowID.id) { + if (window->layout) { + LastActiveLayoutWindowID = ActiveWindowID; + } + } + } + For(IterateInReverse(&order)) { if (!it->visible) continue; @@ -532,6 +548,8 @@ void Update(Event event) { View *view = GetView(it->active_view); UpdateScroll(it, !AreEqual(view->main_caret_on_begin_frame, view->carets[0]) && view->update_scroll); } + + GarbageCollect(); } void Windows_SetupVCVarsall(mco_coro *co) { @@ -564,9 +582,9 @@ void Windows_SetupVCVarsall(mco_coro *co) { } void MainLoop() { + ProfileFunction(); + Scratch scratch; FrameID += 1; - - Scratch scratch; Array frame_events = GetEventsForFrame(scratch); Serializer ser = {EventBuffer}; For(frame_events) { diff --git a/src/text_editor/view.cpp b/src/text_editor/view.cpp index 1130e82..a9802ea 100644 --- a/src/text_editor/view.cpp +++ b/src/text_editor/view.cpp @@ -80,6 +80,17 @@ API bool ViewIsReferenced(ViewID view) { return ViewIsActive(view); } +void Close(ViewID id) { + View *view = GetView(id); + if (view) { + if (view->id.id == 0) { + return; + } + view->close = true; + RunGCThisFrame = true; + } +} + ViewID FindInactiveView(ViewID default_view = NullViewID) { For (Views) { if (!ViewIsActive(it->id)) return it->id; diff --git a/src/text_editor/view.h b/src/text_editor/view.h index ef3352f..c0a534b 100644 --- a/src/text_editor/view.h +++ b/src/text_editor/view.h @@ -13,7 +13,7 @@ struct View { String16 prev_search_line; struct { - unsigned kill : 1; + unsigned close : 1; unsigned special : 1; }; }; diff --git a/src/text_editor/window.cpp b/src/text_editor/window.cpp index c96166f..c63a1cb 100644 --- a/src/text_editor/window.cpp +++ b/src/text_editor/window.cpp @@ -18,13 +18,24 @@ Window *CreateWind() { return w; } -inline Window *GetWindow(WindowID id, Window *default_window = Windows[0]) { +Window *GetWindow(WindowID id, Window *default_window = Windows[0]) { For(Windows) { if (it->id == id) return it; } return default_window; } +void Close(WindowID id) { + Window *window = GetWindow(id, NULL); + if (window) { + if (window->id.id == 0) { + return; + } + window->close = true; + RunGCThisFrame = true; + } +} + Window *FindWindow(ViewID view_id, Window *default_window = NULL) { For(Windows) { if (it->active_view == view_id) { @@ -36,7 +47,7 @@ Window *FindWindow(ViewID view_id, Window *default_window = NULL) { Window *FindWindow(String buffer_name, Window *default_window = NULL) { For(Windows) { - View *it_view = GetView(it->active_view); + View *it_view = GetView(it->active_view); Buffer *it_buffer = GetBuffer(it_view->active_buffer); if (it_buffer->name == buffer_name) { return it; @@ -136,6 +147,7 @@ double WindowCalcEvenResizerValue(Int screen_size_x, Int *out_count = NULL) { } void LayoutWindows(int16_t wx, int16_t wy) { + ProfileFunction(); Rect2I screen_rect = RectI0Size(wx, wy); CommandWindowLayout(&screen_rect, wx, wy); @@ -237,8 +249,10 @@ void CheckpointBeforeGoto(Window *window) { GotoCrumb GetCrumb(Array *cr) { for (; cr->len;) { GotoCrumb c = Pop(cr); - View *view = FindView(c.view_id); - if (view) return c; + View *view = GetView(c.view_id); + if (view) { + return c; + } } return {}; } diff --git a/src/text_editor/window.h b/src/text_editor/window.h index b5feba5..613b76c 100644 --- a/src/text_editor/window.h +++ b/src/text_editor/window.h @@ -30,7 +30,7 @@ struct Window { bool draw_line_highlight : 1; bool visible : 1; bool layout : 1; - bool kill : 1; + bool close : 1; bool sync_visibility_with_focus : 1; bool lose_focus_on_escape : 1; bool jump_history : 1; @@ -60,4 +60,4 @@ void StatusWindowInit(); void StatusWindowLayout(Rect2I *rect, Int wx, Int wy); void DebugWindowInit(); -void DebugWindowLayout(Rect2I *rect, Int wx, Int wy); \ No newline at end of file +void DebugWindowLayout(Rect2I *rect, Int wx, Int wy); diff --git a/src/text_editor/window_command.cpp b/src/text_editor/window_command.cpp index dde4d21..14362fe 100644 --- a/src/text_editor/window_command.cpp +++ b/src/text_editor/window_command.cpp @@ -28,6 +28,7 @@ void CommandWindowLayout(Rect2I *rect, Int wx, Int wy) { } void CommandWindowUpdate() { + ProfileFunction(); BSet active = GetBSet(ActiveWindowID); if (active.window->id == CommandBarWindowID) { if (!ProcessIsActive(active.view->id)) { diff --git a/src/text_editor/window_debug.cpp b/src/text_editor/window_debug.cpp index bdb213a..26f6e9d 100644 --- a/src/text_editor/window_debug.cpp +++ b/src/text_editor/window_debug.cpp @@ -31,46 +31,51 @@ void DebugWindowLayout(Rect2I *rect, Int wx, Int wy) { } void DebugWindowUpdate() { - Buffer *buffer = GetBuffer(DebugBufferID); + ProfileFunction(); + BSet set = GetBSet(DebugWindowID); + if (!set.window->visible) { + return; + } - Window *window = GetActiveWind(); - View *view = GetView(window->active_view); - if (view->active_buffer.id == buffer->id.id) return; + BSet active = GetBSet(ActiveWindowID); + if (active.buffer->id.id == set.buffer->id.id) { + return; + } BSet main = GetBSet(LastActiveLayoutWindowID); Scratch scratch; String s = Format(scratch, "wid: %d\nvid: %d\nbid: %d\nframe: %lld\n", (int)main.window->id.id, (int)main.view->id.id, (int)main.buffer->id.id, (long long)FrameID); String16 string = ToString16(scratch, s); - RawReplaceText(buffer, GetRange(buffer), string); + RawReplaceText(set.buffer, GetRange(set.buffer), string); float xmouse, ymouse; SDL_GetMouseState(&xmouse, &ymouse); - RawAppendf(buffer, "mouse: [%f, %f]\n", roundf(DPIScale * xmouse), roundf(DPIScale * ymouse)); + RawAppendf(set.buffer, "mouse: [%f, %f]\n", roundf(DPIScale * xmouse), roundf(DPIScale * ymouse)); - RawAppendf(buffer, "BufferID id = %d\n", main.buffer->id.id); - RawAppendf(buffer, "String name = %S\n", main.buffer->name); - RawAppendf(buffer, "Int change_id = %lld\n", (long long)main.buffer->change_id); - RawAppendf(buffer, "Int user_change_id = %lld\n", (long long)main.buffer->user_change_id); - RawAppendf(buffer, "Int file_mod_time = %lld\n", (long long)main.buffer->file_mod_time); - RawAppendf(buffer, "\n"); + RawAppendf(set.buffer, "BufferID id = %d\n", main.buffer->id.id); + RawAppendf(set.buffer, "String name = %S\n", main.buffer->name); + RawAppendf(set.buffer, "Int change_id = %lld\n", (long long)main.buffer->change_id); + RawAppendf(set.buffer, "Int user_change_id = %lld\n", (long long)main.buffer->user_change_id); + RawAppendf(set.buffer, "Int file_mod_time = %lld\n", (long long)main.buffer->file_mod_time); + RawAppendf(set.buffer, "\n"); - RawAppendf(buffer, "U16 *data = %zu\n", main.buffer->data); - RawAppendf(buffer, "Int len = %lld\n", (long long)main.buffer->len); - RawAppendf(buffer, "Int cap = %lld\n", (long long)main.buffer->cap); - RawAppendf(buffer, "Array line_starts = {len = %lld, cap = %lld, data = %zu}\n", (long long)main.buffer->line_starts.len, (long long)main.buffer->line_starts.cap, main.buffer->line_starts.data); - RawAppendf(buffer, "\n"); + RawAppendf(set.buffer, "U16 *data = %zu\n", main.buffer->data); + RawAppendf(set.buffer, "Int len = %lld\n", (long long)main.buffer->len); + RawAppendf(set.buffer, "Int cap = %lld\n", (long long)main.buffer->cap); + RawAppendf(set.buffer, "Array line_starts = {len = %lld, cap = %lld, data = %zu}\n", (long long)main.buffer->line_starts.len, (long long)main.buffer->line_starts.cap, main.buffer->line_starts.data); + RawAppendf(set.buffer, "\n"); - RawAppendf(buffer, "Array undo_stack = {len = %lld, cap = %lld, data = %zu}\n", (long long)main.buffer->undo_stack.len, (long long)main.buffer->undo_stack.cap, main.buffer->undo_stack.data); - RawAppendf(buffer, "Array redo_stack = {len = %lld, cap = %lld, data = %zu}\n", (long long)main.buffer->redo_stack.len, (long long)main.buffer->redo_stack.cap, main.buffer->redo_stack.data); - RawAppendf(buffer, "int edit_phase = %d", main.buffer->edit_phase); - RawAppendf(buffer, "\n"); + RawAppendf(set.buffer, "Array undo_stack = {len = %lld, cap = %lld, data = %zu}\n", (long long)main.buffer->undo_stack.len, (long long)main.buffer->undo_stack.cap, main.buffer->undo_stack.data); + RawAppendf(set.buffer, "Array redo_stack = {len = %lld, cap = %lld, data = %zu}\n", (long long)main.buffer->redo_stack.len, (long long)main.buffer->redo_stack.cap, main.buffer->redo_stack.data); + RawAppendf(set.buffer, "int edit_phase = %d", main.buffer->edit_phase); + RawAppendf(set.buffer, "\n"); - RawAppendf(buffer, "int no_history = %d\n", main.buffer->no_history); - RawAppendf(buffer, "int no_line_starts = %d\n", main.buffer->no_line_starts); - RawAppendf(buffer, "int dirty = %d\n", main.buffer->dirty); - RawAppendf(buffer, "int changed_on_disk = %d\n", main.buffer->changed_on_disk); - RawAppendf(buffer, "int garbage = %d\n", main.buffer->garbage); + RawAppendf(set.buffer, "int no_history = %d\n", main.buffer->no_history); + RawAppendf(set.buffer, "int no_line_starts = %d\n", main.buffer->no_line_starts); + RawAppendf(set.buffer, "int dirty = %d\n", main.buffer->dirty); + RawAppendf(set.buffer, "int changed_on_disk = %d\n", main.buffer->changed_on_disk); + RawAppendf(set.buffer, "int garbage = %d\n", main.buffer->garbage); } void Command_ToggleDebug() { diff --git a/src/text_editor/window_status.cpp b/src/text_editor/window_status.cpp index a207bed..eeb9c6a 100644 --- a/src/text_editor/window_status.cpp +++ b/src/text_editor/window_status.cpp @@ -25,6 +25,7 @@ void StatusWindowLayout(Rect2I *rect, Int wx, Int wy) { } void StatusWindowUpdate() { + ProfileFunction(); Window *status_bar_window = GetWindow(StatusBarWindowID, NULL); if (status_bar_window == NULL) { return;