From 92139cf799329e12ac29843d700677b577efa24e Mon Sep 17 00:00:00 2001 From: krzosa Date: Tue, 29 Apr 2025 16:22:26 +0200 Subject: [PATCH] ALOT OF CHANGES --- build.bat | 18 +- build_file.cpp | 54 +- data/te.lua_project | 6 + src/basic/basic.h | 53 +- src/basic/filesystem.h | 102 +- src/basic/string16.cpp | 72 +- src/basic/win32.cpp | 954 +++++++------- src/build_tool/core/filesystem.c | 62 +- src/build_tool/process.cpp | 2 +- src/build_tool/standalone_libraries/string.c | 14 +- src/build_tool/standalone_libraries/string.h | 10 +- src/build_tool/standalone_libraries/unicode.c | 4 +- src/build_tool/standalone_libraries/unicode.h | 4 +- src/profiler/profiler.cpp | 2 +- src/text_editor/buffer.cpp | 1096 ++++++++++++++++- src/text_editor/buffer.h | 77 ++ src/text_editor/buffer_fuzzy_search.cpp | 53 - src/text_editor/buffer_helpers.cpp | 552 --------- src/text_editor/buffer_history.cpp | 209 ---- src/text_editor/buffer_multi_cursor.cpp | 163 --- src/text_editor/buffer_test_load.cpp | 29 +- src/text_editor/commands.cpp | 122 +- src/text_editor/commands_bindings.cpp | 41 +- src/text_editor/commands_clipboard.cpp | 6 +- src/text_editor/event.cpp | 4 + src/text_editor/generated.cpp | 54 +- src/text_editor/lua_api.cpp | 37 +- src/text_editor/management.cpp | 89 +- src/text_editor/notes_vcvarsall | 4 +- src/text_editor/process.cpp | 20 +- src/text_editor/prototype.cpp | 92 +- src/text_editor/serializer.cpp | 10 +- src/text_editor/text_editor.cpp | 51 +- src/text_editor/text_editor.h | 48 +- src/text_editor/title_bar.cpp | 22 +- src/text_editor/todo.txt | 16 +- src/text_editor/window.cpp | 40 +- src/text_editor/window_draw.cpp | 5 +- 38 files changed, 2230 insertions(+), 1967 deletions(-) create mode 100644 data/te.lua_project create mode 100644 src/text_editor/buffer.h delete mode 100644 src/text_editor/buffer_fuzzy_search.cpp delete mode 100644 src/text_editor/buffer_helpers.cpp delete mode 100644 src/text_editor/buffer_history.cpp delete mode 100644 src/text_editor/buffer_multi_cursor.cpp diff --git a/build.bat b/build.bat index 9df5cfb..77ce4c0 100644 --- a/build.bat +++ b/build.bat @@ -1,10 +1,10 @@ -@echo off - -if not exist build\build_tool.exe ( - mkdir build - cd build - cl -Fe:build_tool.exe ../src/build_tool/build_tool_main.cpp -FC -WX -W3 -wd4200 -wd4244 -diagnostics:column -nologo -Zi - cd .. -) - +@echo off + +if not exist build\build_tool.exe ( + mkdir build + cd build + cl -Fe:build_tool.exe ../src/build_tool/build_tool_main.cpp -FC -WX -W3 -wd4200 -wd4244 -diagnostics:column -nologo -Zi + cd .. +) + build\build_tool.exe \ No newline at end of file diff --git a/build_file.cpp b/build_file.cpp index 0836bb0..90f8f0a 100644 --- a/build_file.cpp +++ b/build_file.cpp @@ -187,6 +187,7 @@ SDLK_RIGHT = 1073741903 SDLK_LEFT = 1073741904 SDLK_Q = 113 SDLK_BACKSLASH = 0x5c +SDLK_RETURN = 13 SDLK_F1 = 0x4000003a SDLK_F2 = 0x4000003b @@ -394,6 +395,40 @@ function ApplyRules(s) return nil end +function IsWhitespace(w) + local result = w == string.byte('\n') or + w == string.byte(' ') or + w == string.byte('\t') or + w == string.byte('\v') or + w == string.byte('\r') + return result +end + +function IsSymbol(w) + local result = (w >= string.byte('!') and w <= string.byte('/')) or + (w >= string.byte(':') and w <= string.byte('@')) or + (w >= string.byte('[') and w <= string.byte('`')) or + (w >= string.byte('{') and w <= string.byte('~')) + return result +end + +function IsLoadWord(w) + local result = w == string.byte('(') or + w == string.byte(')') or + w == string.byte('/') or + w == string.byte('\\') or + w == string.byte(':') or + w == string.byte('+') or + w == string.byte('_') or + w == string.byte('.') or + w == string.byte('-') or + w == string.byte(',') + if not result then + result = not (IsSymbol(w) or IsWhitespace(w)) + end + return result +end + Coroutines = {} function AddCo(f) local i = #Coroutines + 1 @@ -414,18 +449,27 @@ function OnUpdate(e) Coroutines = new_co_list end +OnCommandCallbacks = {} +table.insert(OnCommandCallbacks, function (e) + if e.key == SDLK_F and e.ctrl == 1 and e.shift == 1 then + C("git grep -n "..GetLoadWord().." :/") end + if e.key == SDLK_L and e.ctrl == 1 then + Eval(GetLoadWord()) end + if e.key == SDLK_B and e.ctrl == 1 then + C(GetLoadWord()) end +end) + -- REMEBER: AS LITTLE ACTUAL CODE AS POSSIBLE IN LUA -- ONLY CONFIGURABLES function OnCommand(e) - if e.key == SDLK_F and e.ctrl == 1 and e.shift == 1 then - C("git grep -n "..GetLoadWord()) end - if e.key == SDLK_E and e.ctrl == 1 then - Eval(GetLoadWord()) end + for i = #OnCommandCallbacks,1,-1 do + on_command = OnCommandCallbacks[i] + on_command(e) + end end function OnInit() end - )=="; void GenerateConfig() { diff --git a/data/te.lua_project b/data/te.lua_project new file mode 100644 index 0000000..f2eb37b --- /dev/null +++ b/data/te.lua_project @@ -0,0 +1,6 @@ +function OnCommandTE(e) + if e.key == SDLK_B and e.ctrl == 1 then + C("cd D:/dev/text_editor && build.bat") end +end + +table.insert(OnCommandCallbacks, OnCommandTE) \ No newline at end of file diff --git a/src/basic/basic.h b/src/basic/basic.h index cfd1fe3..9a7c5fb 100644 --- a/src/basic/basic.h +++ b/src/basic/basic.h @@ -228,7 +228,7 @@ inline int64_t StringLen(char *string) { return i; } -inline int64_t WideLength(wchar_t *string) { +inline int64_t WideLength(char16_t *string) { if (!string) return 0; int64_t len = 0; while (*string++ != 0) @@ -248,9 +248,9 @@ struct Slice { Slice(const char *s) : data((char *)s), len(StringLen((char *)s)) {} Slice(const char *s, int64_t l) : data((char *)s), len(l) {} - Slice(wchar_t *s) : data(s), len(WideLength(s)) {} - Slice(const wchar_t *s) : data((wchar_t *)s), len(WideLength((wchar_t *)s)) {} - Slice(const wchar_t *s, int64_t l) : data((wchar_t *)s), len(l) {} + Slice(char16_t *s) : data(s), len(WideLength(s)) {} + Slice(const char16_t *s) : data((char16_t *)s), len(WideLength((char16_t *)s)) {} + Slice(const char16_t *s, int64_t l) : data((char16_t *)s), len(l) {} T &operator[](int64_t index) { Assert(index < len); @@ -782,7 +782,7 @@ struct UTF8Iter { }; using String = Slice; -using String16 = Slice; +using String16 = Slice; bool IsValid(UTF8Iter &iter); void Advance(UTF8Iter *iter); UTF8Iter IterateUTF8Ex(char *data, int64_t len); @@ -792,7 +792,7 @@ UTF8Iter IterateUTF8(String string); bool IsAlphabetic(char a); #define FmtString(string) (int)(string).len, (string).data bool AreEqual(String a, String b, unsigned ignore_case = false); -int64_t CreateWidecharFromChar(wchar_t *buffer, int64_t buffer_size, char *in, int64_t inlen); +int64_t CreateWidecharFromChar(char16_t *buffer, int64_t buffer_size, char *in, int64_t inlen); inline bool operator==(String a, String b) { return AreEqual(a, b); } inline bool operator!=(String a, String b) { return !AreEqual(a, b); } @@ -805,16 +805,17 @@ inline bool operator!=(String a, String b) { return !AreEqual(a, b); } String Format(Allocator allocator, const char *data, ...); String FormatV(Allocator allocator, const char *data, va_list args1); String ToString(Allocator allocator, String16 string); -String ToString(Allocator allocator, wchar_t *string, int64_t len); -String ToString(Allocator allocator, wchar_t *wstring); +String ToString(Allocator allocator, char16_t *string, int64_t len); +String ToString(Allocator allocator, char16_t *wstring); String16 ToString16(Allocator allocator, String string); -wchar_t *ToWidechar(Allocator allocator, String string); +char16_t *ToWidechar(Allocator allocator, String string); void NormalizePathInPlace(String s); String ChopLastSlash(String s); String ChopLastPeriod(String s); String SkipToLastSlash(String s); String SkipToLastPeriod(String s); String Copy(Allocator allocator, String string); +Int GetSize(Array array); /* Hash table implementation: Pointers to values @@ -1212,13 +1213,13 @@ UTF16Result UTF32ToUTF16(uint32_t codepoint) { return result; } - #define UTF__HANDLE_DECODE_ERROR(question_mark) \ + #define UTF__HANDLE_DECODE_ERROR(question_mark, I) \ { \ if (outlen < buffer_size - 1) buffer[outlen++] = (question_mark); \ - break; \ + i += I; \ } -int64_t CreateCharFromWidechar(char *buffer, int64_t buffer_size, wchar_t *in, int64_t inlen) { +int64_t CreateCharFromWidechar(char *buffer, int64_t buffer_size, char16_t *in, int64_t inlen) { int64_t outlen = 0; for (int64_t i = 0; i < inlen && in[i];) { UTF32Result decode = UTF16ToUTF32((uint16_t *)(in + i), (int64_t)(inlen - i)); @@ -1231,15 +1232,15 @@ int64_t CreateCharFromWidechar(char *buffer, int64_t buffer_size, wchar_t *in, i buffer[outlen++] = encode.out_str[j]; } } - } else UTF__HANDLE_DECODE_ERROR('?'); - } else UTF__HANDLE_DECODE_ERROR('?'); + } else UTF__HANDLE_DECODE_ERROR('?', 0); + } else UTF__HANDLE_DECODE_ERROR('?', 1); } buffer[outlen] = 0; return outlen; } -int64_t CreateWidecharFromChar(wchar_t *buffer, int64_t buffer_size, char *in, int64_t inlen) { +int64_t CreateWidecharFromChar(char16_t *buffer, int64_t buffer_size, char *in, int64_t inlen) { int64_t outlen = 0; for (int64_t i = 0; i < inlen;) { UTF32Result decode = UTF8ToUTF32((uint8_t *)(in + i), (int64_t)(inlen - i)); @@ -1252,8 +1253,8 @@ int64_t CreateWidecharFromChar(wchar_t *buffer, int64_t buffer_size, char *in, i buffer[outlen++] = encode.out_str[j]; } } - } else UTF__HANDLE_DECODE_ERROR(0x003f); - } else UTF__HANDLE_DECODE_ERROR(0x003f); + } else UTF__HANDLE_DECODE_ERROR(0x003f, 0); + } else UTF__HANDLE_DECODE_ERROR(0x003f, 1); } buffer[outlen] = 0; @@ -1546,6 +1547,12 @@ String Merge(Allocator allocator, Array list, String separator = " ") { return string; } +Int GetSize(Array array) { + Int result = 0; + For (array) result += it.len; + return result; +} + #include String FormatV(Allocator allocator, const char *data, va_list args1) { va_list args2; @@ -1565,20 +1572,20 @@ String Format(Allocator allocator, const char *data, ...) { } String16 ToString16(Allocator allocator, String string) { - Assert(sizeof(wchar_t) == 2); - wchar_t *buffer = (wchar_t *)AllocSize(allocator, sizeof(wchar_t) * (string.len + 1)); + Assert(sizeof(char16_t) == 2); + char16_t *buffer = (char16_t *)AllocSize(allocator, sizeof(char16_t) * (string.len + 1)); int64_t size = CreateWidecharFromChar(buffer, string.len + 1, string.data, string.len); String16 result = {buffer, size}; return result; } -wchar_t *ToWidechar(Allocator allocator, String string) { +char16_t *ToWidechar(Allocator allocator, String string) { String16 result = ToString16(allocator, string); return result.data; } String ToString(Allocator allocator, String16 string) { - Assert(sizeof(wchar_t) == 2); + Assert(sizeof(char16_t) == 2); int64_t buffer_size = (string.len + 1) * 2; char *buffer = (char *)AllocSize(allocator, buffer_size); @@ -1589,11 +1596,11 @@ String ToString(Allocator allocator, String16 string) { return result; } -String ToString(Allocator allocator, wchar_t *string, int64_t len) { +String ToString(Allocator allocator, char16_t *string, int64_t len) { return ToString(allocator, {string, len}); } -String ToString(Allocator allocator, wchar_t *wstring) { +String ToString(Allocator allocator, char16_t *wstring) { int64_t size = WideLength(wstring); String result = ToString(allocator, {wstring, size}); return result; diff --git a/src/basic/filesystem.h b/src/basic/filesystem.h index 7aa0dbf..61ec566 100644 --- a/src/basic/filesystem.h +++ b/src/basic/filesystem.h @@ -1,52 +1,52 @@ -#pragma once -#include "../basic/basic.h" - -struct FileIter { - bool is_valid; - bool is_directory; - String absolute_path; - String relative_path; - String filename; - - String path; - Allocator allocator; - Arena *arena; - TempArena temp; - union { - struct Win32_FileIter *w32; - void *dir; - }; -}; - -String ReadFile(Allocator arena, String path); -bool WriteFile(String path, String data); -String GetAbsolutePath(Allocator arena, String relative); -bool IsValid(const FileIter &it); -void Advance(FileIter *it); -FileIter IterateFiles(Allocator allocator, String path); -void InitOS(void (*error_proc)(const char *, ...)); - -String GetExePath(Allocator allocator); -String GetExeDir(Allocator allocator); -bool FileExists(String path); -bool IsDir(String path); -bool IsFile(String path); -String GetWorkingDir(Allocator arena); -bool IsAbsolute(String path); -int64_t GetFileModTime(String file); - -struct Process { - bool is_valid; - int exit_code; - char platform[6 * 8]; - - int64_t view_id; // text editor view - bool scroll_to_end; -}; - -Process SpawnProcess(String command_line, String working_dir, String write_stdin = {}); -bool IsValid(Process *process); -void KillProcess(Process *process); -String PollStdout(Allocator allocator, Process *process); -void WriteStdin(Process *process, String string); +#pragma once +#include "../basic/basic.h" + +struct FileIter { + bool is_valid; + bool is_directory; + String absolute_path; + String relative_path; + String filename; + + String path; + Allocator allocator; + Arena *arena; + TempArena temp; + union { + struct Win32_FileIter *w32; + void *dir; + }; +}; + +String ReadFile(Allocator arena, String path); +bool WriteFile(String path, String data); +String GetAbsolutePath(Allocator arena, String relative); +bool IsValid(const FileIter &it); +void Advance(FileIter *it); +FileIter IterateFiles(Allocator allocator, String path); +void InitOS(void (*error_proc)(const char *, ...)); + +String GetExePath(Allocator allocator); +String GetExeDir(Allocator allocator); +bool FileExists(String path); +bool IsDir(String path); +bool IsFile(String path); +String GetWorkingDir(Allocator arena); +bool IsAbsolute(String path); +int64_t GetFileModTime(String file); + +struct Process { + bool is_valid; + int exit_code; + char platform[6 * 8]; + + int64_t view_id; // text editor view + bool scroll_to_end; +}; + +Process SpawnProcess(String command_line, String working_dir, String write_stdin = {}, Array enviroment = {}); +bool IsValid(Process *process); +void KillProcess(Process *process); +String PollStdout(Allocator allocator, Process *process); +void WriteStdin(Process *process, String string); void CloseStdin(Process *process); \ No newline at end of file diff --git a/src/basic/string16.cpp b/src/basic/string16.cpp index 2a80471..3a288a7 100644 --- a/src/basic/string16.cpp +++ b/src/basic/string16.cpp @@ -1,68 +1,60 @@ -wchar_t ToLowerCase(wchar_t a) { +char16_t ToLowerCase(char16_t a) { if (a >= 'A' && a <= 'Z') a += 32; return a; } -wchar_t ToUpperCase(wchar_t a) { +char16_t ToUpperCase(char16_t a) { if (a >= 'a' && a <= 'z') a -= 32; return a; } -bool IsWhitespace(wchar_t w) { +bool IsWhitespace(char16_t w) { bool result = w == '\n' || w == ' ' || w == '\t' || w == '\v' || w == '\r'; return result; } -bool IsSymbol(wchar_t w) { +bool IsSymbol(char16_t w) { bool result = (w >= '!' && w <= '/') || (w >= ':' && w <= '@') || (w >= '[' && w <= '`') || (w >= '{' && w <= '~'); return result; } -bool IsNonWord(wchar_t w) { +bool IsNonWord(char16_t w) { if (w == '_') return false; bool result = IsSymbol(w) || IsWhitespace(w); return result; } -bool IsWord(wchar_t w) { +bool IsWord(char16_t w) { bool result = !IsNonWord(w); return result; } -bool IsLoadWord(wchar_t w) { - bool result = w == L'(' || w == L')' || w == L'/' || w == L'\\' || w == L':' || w == L'+' || w == L'_' || w == L'.' || w == L'-' || w == L','; - if (!result) { - result = !(IsSymbol(w) || IsWhitespace(w)); - } - return result; -} - -bool IsBrace(wchar_t c) { +bool IsBrace(char16_t c) { bool result = c == '{' || c == '}'; return result; } -bool IsParen(wchar_t c) { +bool IsParen(char16_t c) { bool result = c == '(' || c == ')'; return result; } -bool IsAlphabetic(wchar_t a) { +bool IsAlphabetic(char16_t a) { bool result = (a >= 'a' && a <= 'z') || (a >= 'A' && a <= 'Z'); return result; } -bool IsIdent(wchar_t a) { +bool IsIdent(char16_t a) { bool result = (a >= 'a' && a <= 'z') || (a >= 'A' && a <= 'Z') || a == '_'; return result; } -bool IsDigit(wchar_t a) { +bool IsDigit(char16_t a) { bool result = a >= '0' && a <= '9'; return result; } -bool IsAlphanumeric(wchar_t a) { +bool IsAlphanumeric(char16_t a) { bool result = IsDigit(a) || IsAlphabetic(a); return result; } @@ -70,8 +62,8 @@ bool IsAlphanumeric(wchar_t a) { bool AreEqual(String16 a, String16 b, unsigned ignore_case = false) { if (a.len != b.len) return false; for (int64_t i = 0; i < a.len; i++) { - wchar_t A = a.data[i]; - wchar_t B = b.data[i]; + char16_t A = a.data[i]; + char16_t B = b.data[i]; if (ignore_case) { A = ToLowerCase(A); B = ToLowerCase(B); @@ -84,8 +76,8 @@ bool AreEqual(String16 a, String16 b, unsigned ignore_case = false) { inline bool operator==(String16 a, String16 b) { return AreEqual(a, b); } inline bool operator!=(String16 a, String16 b) { return !AreEqual(a, b); } -wchar_t GetChar(String16 string, int64_t i) { - wchar_t result = 0; +char16_t GetChar(String16 string, int64_t i) { + char16_t result = 0; if (i < string.len) result = string.data[i]; return result; } @@ -99,14 +91,14 @@ String16 Merge(Allocator allocator, Array list, String16 separator = " int64_t base_size = (char_count + 1); int64_t sep_size = (node_count - 1) * separator.len; int64_t size = base_size + sep_size; - wchar_t *buff = (wchar_t *)AllocSize(allocator, sizeof(wchar_t) * (size + 1)); + char16_t *buff = (char16_t *)AllocSize(allocator, sizeof(char16_t) * (size + 1)); String16 string = {buff, 0}; For(list) { Assert(string.len + it.len <= size); - memcpy(string.data + string.len, it.data, it.len * sizeof(wchar_t)); + memcpy(string.data + string.len, it.data, it.len * sizeof(char16_t)); string.len += it.len; if (!IsLast(list, it)) { - memcpy(string.data + string.len, separator.data, separator.len * sizeof(wchar_t)); + memcpy(string.data + string.len, separator.data, separator.len * sizeof(char16_t)); string.len += separator.len; } } @@ -146,20 +138,20 @@ bool Seek(String16 string, String16 find, int64_t *index_out = NULL, SeekFlag fl String16 ChopLastSlash(String16 s) { String16 result = s; - Seek(s, L"/", &result.len, SeekFlag_MatchFindLast); + Seek(s, u"/", &result.len, SeekFlag_MatchFindLast); return result; } String16 ChopLastPeriod(String16 s) { String16 result = s; - Seek(s, L".", &result.len, SeekFlag_MatchFindLast); + Seek(s, u".", &result.len, SeekFlag_MatchFindLast); return result; } String16 SkipToLastSlash(String16 s) { int64_t pos; String16 result = s; - if (Seek(s, L"/", &pos, SeekFlag_MatchFindLast)) { + if (Seek(s, u"/", &pos, SeekFlag_MatchFindLast)) { result = Skip(result, pos + 1); } return result; @@ -168,7 +160,7 @@ String16 SkipToLastSlash(String16 s) { String16 SkipToLastPeriod(String16 s) { int64_t pos; String16 result = s; - if (Seek(s, L".", &pos, SeekFlag_MatchFindLast)) { + if (Seek(s, u".", &pos, SeekFlag_MatchFindLast)) { result = Skip(result, pos + 1); } return result; @@ -238,22 +230,22 @@ bool StartsWith(String16 a, String16 start, unsigned ignore_case = false) { } String16 Copy16(Allocator allocator, String16 string) { - wchar_t *copy = (wchar_t *)AllocSize(allocator, sizeof(wchar_t) * (string.len + 1)); + char16_t *copy = (char16_t *)AllocSize(allocator, sizeof(char16_t) * (string.len + 1)); memcpy(copy, string.data, string.len); copy[string.len] = 0; String16 result = {copy, string.len}; return result; } -String16 Copy16(Allocator allocator, wchar_t *string) { +String16 Copy16(Allocator allocator, char16_t *string) { String16 s = {string, (int64_t)WideLength(string)}; return Copy(allocator, s); } void NormalizePathInPlace(String16 s) { for (int64_t i = 0; i < s.len; i++) { - if (s.data[i] == L'\\') - s.data[i] = L'/'; + if (s.data[i] == u'\\') + s.data[i] = u'/'; } } @@ -306,14 +298,14 @@ String16 SkipWhitespace(String16 *string) { return begin; } -String16 Format16V(Allocator allocator, const wchar_t *data, va_list args1) { +String16 Format16V(Allocator allocator, const char16_t *data, va_list args1) { va_list args2; va_copy(args2, args1); - int64_t len = vswprintf(0, 0, data, args2); + int64_t len = vswprintf(0, 0, (wchar_t *)data, args2); // @todo: check this type va_end(args2); - wchar_t *result = (wchar_t *)AllocSize(allocator, sizeof(wchar_t) * (len + 1)); - vswprintf(result, (int)(len + 1), data, args1); + char16_t *result = (char16_t *)AllocSize(allocator, sizeof(char16_t) * (len + 1)); + vswprintf((wchar_t *)result, (int)(len + 1), (wchar_t *)data, args1); // @todo: check this type String16 res = {result, len}; return res; } @@ -324,7 +316,7 @@ String16 Format16V(Allocator allocator, const wchar_t *data, va_list args1) { String16 result = Format16V(allocator, data, args1); \ va_end(args1) -String16 Format16(Allocator allocator, const wchar_t *data, ...) { +String16 Format16(Allocator allocator, const char16_t *data, ...) { STRING16_FORMAT(allocator, data, result); return result; } \ No newline at end of file diff --git a/src/basic/win32.cpp b/src/basic/win32.cpp index 77c7bdb..175d2ba 100644 --- a/src/basic/win32.cpp +++ b/src/basic/win32.cpp @@ -1,469 +1,485 @@ -#include "filesystem.h" -#include "thread_queue.h" - -#ifndef NOMINMAX - #define NOMINMAX -#endif -#ifndef WIN32_LEAN_AND_MEAN - #define WIN32_LEAN_AND_MEAN -#endif -#include -#include -#include - -#include "win32_thread.cpp" - -void (*Error)(const char *, ...); - -// Basic begin -void *VReserve(size_t size) { - void *result = (uint8_t *)VirtualAlloc(0, size, MEM_RESERVE, PAGE_READWRITE); - return result; -} - -bool VCommit(void *p, size_t size) { - void *result = VirtualAlloc(p, size, MEM_COMMIT, PAGE_READWRITE); - return result ? true : false; -} - -bool VRelease(void *p) { - BOOL result = VirtualFree(p, 0, MEM_RELEASE); - return result ? true : false; -} - -bool VDecommit(void *p, size_t size) { - BOOL result = VirtualFree(p, size, MEM_DECOMMIT); - return result ? true : false; -} -// Basic end - -void InitOS(void (*error_proc)(const char *, ...)) { - Error = error_proc; -} - -String ReadFile(Allocator arena, String path) { - bool success = false; - String result = {}; - - Scratch scratch(arena); - String16 string16 = ToString16(scratch, path); - HANDLE handle = CreateFileW(string16.data, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - if (handle != INVALID_HANDLE_VALUE) { - LARGE_INTEGER file_size; - if (GetFileSizeEx(handle, &file_size)) { - if (file_size.QuadPart != 0) { - result.len = (int64_t)file_size.QuadPart; - result.data = (char *)AllocSize(arena, result.len + 1); - DWORD read; - if (ReadFile(handle, result.data, (DWORD)result.len, &read, NULL)) { // @todo: can only read 32 byte size files? - if (read == result.len) { - success = true; - result.data[result.len] = 0; - } - } - } - } - CloseHandle(handle); - } - - if (!success) { - Dealloc(arena, &result.data); - result = {}; - } - - return result; -} - -typedef struct Win32_FileIter { - HANDLE handle; - WIN32_FIND_DATAW data; -} Win32_FileIter; - -String GetAbsolutePath(Allocator arena, String relative) { - Scratch scratch(arena); - String16 wpath = ToString16(scratch, relative); - wchar_t *wpath_abs = AllocArray(scratch, wchar_t, 4096); - DWORD written = GetFullPathNameW(wpath.data, 4096, wpath_abs, 0); - if (written == 0) - return {}; - String path = ToString(arena, {(wchar_t *)wpath_abs, written}); - NormalizePathInPlace(path); - return path; -} - -bool IsValid(const FileIter &it) { - return it.is_valid; -} - -void Advance(FileIter *it) { - if (it->temp.arena) EndTemp(it->temp); - it->temp = BeginTemp(it->arena); - while (FindNextFileW(it->w32->handle, &it->w32->data) != 0) { - WIN32_FIND_DATAW *data = &it->w32->data; - - // Skip '.' and '..' - if (data->cFileName[0] == '.' && data->cFileName[1] == '.' && data->cFileName[2] == 0) continue; - if (data->cFileName[0] == '.' && data->cFileName[1] == 0) continue; - - it->is_directory = data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY; - it->filename = ToString(*it->arena, (wchar_t *)data->cFileName, WideLength(data->cFileName)); - const char *is_dir = it->is_directory ? "/" : ""; - const char *separator = it->path.data[it->path.len - 1] == '/' ? "" : "/"; - it->relative_path = Format(*it->arena, "%.*s%s%.*s%s", FmtString(it->path), separator, FmtString(it->filename), is_dir); - it->absolute_path = GetAbsolutePath(*it->arena, it->relative_path); - it->is_valid = true; - - if (it->is_directory) { - Assert(it->relative_path.data[it->relative_path.len - 1] == '/'); - Assert(it->absolute_path.data[it->absolute_path.len - 1] == '/'); - } - return; - } - - it->is_valid = false; - DWORD error = GetLastError(); - Assert(error == ERROR_NO_MORE_FILES); - FindClose(it->w32->handle); - Dealloc(it->allocator, &it->arena); -} - -FileIter IterateFiles(Allocator alo, String path) { - FileIter it = {0}; - it.allocator = alo; - it.arena = AllocArena(alo, MiB(2)); - it.path = path; - - String modified_path = Format(*it.arena, "%.*s\\*", FmtString(path)); - String16 modified_path16 = ToString16(*it.arena, modified_path); - - it.w32 = AllocType(*it.arena, Win32_FileIter); - it.w32->handle = FindFirstFileW(modified_path16.data, &it.w32->data); - if (it.w32->handle == INVALID_HANDLE_VALUE) { - it.is_valid = false; - return it; - } - - Advance(&it); - return it; -} - -#if _WIN32 - #include -double get_time_in_micros(void) { - static double invfreq; - if (!invfreq) { - LARGE_INTEGER frequency; - QueryPerformanceFrequency(&frequency); - invfreq = 1000000.0 / frequency.QuadPart; - } - LARGE_INTEGER counter; - QueryPerformanceCounter(&counter); - return counter.QuadPart * invfreq; -} -#else - #include -double get_time_in_micros(void) { - struct timespec spec; - clock_gettime(CLOCK_MONOTONIC, &spec); - return (((double)spec.tv_sec) * 1000000) + (((double)spec.tv_nsec) / 1000); -} -#endif - -bool WriteFile(String path, String data) { - bool result = false; - - Scratch scratch; - String16 wpath = ToString16(scratch, path); - - DWORD access = GENERIC_WRITE; - DWORD creation_disposition = CREATE_ALWAYS; - HANDLE handle = CreateFileW(wpath.data, access, 0, NULL, creation_disposition, FILE_ATTRIBUTE_NORMAL, NULL); - if (handle != INVALID_HANDLE_VALUE) { - DWORD bytes_written = 0; - Assert(data.len == (DWORD)data.len); // @Todo: can only read 32 byte size files? - BOOL error = WriteFile(handle, data.data, (DWORD)data.len, &bytes_written, NULL); - if (error == TRUE) { - if (bytes_written == data.len) { - result = true; - } - } - CloseHandle(handle); - } - - return result; -} - -String GetExePath(Allocator allocator) { - Scratch scratch(allocator); - wchar_t *wbuffer = AllocArray(scratch, wchar_t, 4096); - DWORD wsize = GetModuleFileNameW(0, wbuffer, 4096); - Assert(wsize != 0); - - String path = ToString(allocator, wbuffer, wsize); - NormalizePathInPlace(path); - return path; -} - -String GetExeDir(Allocator allocator) { - Scratch scratch((Arena *)allocator.object); - String path = GetExePath(scratch); - path = ChopLastSlash(path); - path = Copy(allocator, path); - return path; -} - -bool FileExists(String path) { - Scratch scratch; - String16 wbuff = ToString16(scratch, path); - DWORD attribs = GetFileAttributesW(wbuff.data); - bool result = attribs == INVALID_FILE_ATTRIBUTES ? false : true; - return result; -} - -bool IsDir(String path) { - Scratch scratch; - String16 wbuff = ToString16(scratch, path); - DWORD dwAttrib = GetFileAttributesW(wbuff.data); - return dwAttrib != INVALID_FILE_ATTRIBUTES && (dwAttrib & FILE_ATTRIBUTE_DIRECTORY); -} - -bool IsFile(String path) { - Scratch scratch; - String16 wbuff = ToString16(scratch, path); - DWORD dwAttrib = GetFileAttributesW(wbuff.data); - bool is_file = (dwAttrib & FILE_ATTRIBUTE_DIRECTORY) == 0; - return dwAttrib != INVALID_FILE_ATTRIBUTES && is_file; -} - -String GetWorkingDir(Allocator arena) { - Scratch scratch(arena); - wchar_t *wbuffer = AllocArray(scratch, wchar_t, 4096); - DWORD wsize = GetCurrentDirectoryW(4096, wbuffer); - Assert(wsize != 0); - wbuffer[wsize] = 0; - - String path = ToString(arena, wbuffer, wsize); - NormalizePathInPlace(path); - return path; -} - -bool IsAbsolute(String path) { - bool result = path.len > 3 && IsAlphabetic(path.data[0]) && path.data[1] == ':' && (path.data[2] == '/' || path.data[2] == '\\'); - return result; -} - -bool DeleteFile(String path) { - Scratch scratch; - String16 wpath = ToString16(scratch, path); - - BOOL success = DeleteFileW(wpath.data); - bool result = true; - if (success == 0) result = false; - return result; -} - -int64_t GetFileModTime(String file) { - Scratch scratch; - String16 string16 = ToString16(scratch, file); - WIN32_FIND_DATAW data; - HANDLE handle = FindFirstFileW(string16.data, &data); - if (handle != INVALID_HANDLE_VALUE) { - FindClose(handle); - FILETIME time = data.ftLastWriteTime; - int64_t result = (int64_t)time.dwHighDateTime << 32 | time.dwLowDateTime; - return result; - } else { - return -1; - } -} - -struct Win32Process { - HANDLE handle; - HANDLE child_stdout_read; - HANDLE child_stdout_write; - HANDLE child_stdin_read; - HANDLE child_stdin_write; -}; -static_assert(sizeof(Win32Process) < sizeof(Process::platform)); - -void Win32ReportError(String msg, String cmd) { - LPVOID lpMsgBuf; - FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpMsgBuf, 0, NULL); - defer { LocalFree(lpMsgBuf); }; - char *buff = (char *)lpMsgBuf; - size_t buffLen = strlen((const char *)buff); - if (buffLen > 0 && buff[buffLen - 1] == '\n') buff[buffLen - 1] = 0; - Error("%.*s: %.*s! %s", FmtString(msg), FmtString(cmd), (char *)lpMsgBuf); -} - -void Win32CloseProcess(Process *process) { - Win32Process *p = (Win32Process *)process->platform; - if (p->handle != INVALID_HANDLE_VALUE) CloseHandle(p->handle); - if (p->child_stdout_write != INVALID_HANDLE_VALUE) CloseHandle(p->child_stdout_write); - if (p->child_stdout_read != INVALID_HANDLE_VALUE) CloseHandle(p->child_stdout_read); - if (p->child_stdin_read != INVALID_HANDLE_VALUE) CloseHandle(p->child_stdin_read); - if (p->child_stdin_write != INVALID_HANDLE_VALUE) CloseHandle(p->child_stdin_write); - process->is_valid = false; -} - -static void Win32ProcessError(Process *process, String msg, String cmd) { - Win32Process *p = (Win32Process *)process->platform; - Win32ReportError(msg, cmd); - Win32CloseProcess(process); -} - -void WriteStdin(Process *process, String string) { - if (string.len == 0) return; - Assert(process->is_valid); - - Win32Process *p = (Win32Process *)process->platform; - Assert(p->child_stdin_write != INVALID_HANDLE_VALUE); - - DWORD written = 0; - bool write_error = WriteFile(p->child_stdin_write, string.data, (DWORD)string.len, &written, NULL) == 0; - - Assert(write_error == false); - Assert(written == string.len); -} - -void CloseStdin(Process *process) { - Win32Process *p = (Win32Process *)process->platform; - CloseHandle(p->child_stdin_write); - p->child_stdin_write = INVALID_HANDLE_VALUE; -} - -Process SpawnProcess(String command_line, String working_dir, String write_stdin) { - Process process = {}; - Win32Process *p = (Win32Process *)process.platform; - - Scratch scratch; - command_line = Format(scratch, "/C %.*s", FmtString(command_line)); - - p->handle = INVALID_HANDLE_VALUE; - p->child_stdout_write = INVALID_HANDLE_VALUE; - p->child_stdout_read = INVALID_HANDLE_VALUE; - p->child_stdin_read = INVALID_HANDLE_VALUE; - p->child_stdin_write = INVALID_HANDLE_VALUE; - - SECURITY_ATTRIBUTES security_atrb = {}; - security_atrb.nLength = sizeof(SECURITY_ATTRIBUTES); - security_atrb.bInheritHandle = TRUE; - - if (!CreatePipe(&p->child_stdout_read, &p->child_stdout_write, &security_atrb, 0)) { - Win32ProcessError(&process, "Failed to create process at create pipe stage", command_line); - return process; - } - - if (!SetHandleInformation(p->child_stdout_read, HANDLE_FLAG_INHERIT, 0)) { - Win32ProcessError(&process, "Failed to create process at create pipe stage", command_line); - return process; - } - - if (!CreatePipe(&p->child_stdin_read, &p->child_stdin_write, &security_atrb, 0)) { - Win32ProcessError(&process, "Failed to create process at create pipe stage", command_line); - return process; - } - - if (!SetHandleInformation(p->child_stdin_write, HANDLE_FLAG_INHERIT, 0)) { - Win32ProcessError(&process, "Failed to create process at create pipe stage", command_line); - return process; - } - - STARTUPINFOW startup = {}; - startup.cb = sizeof(STARTUPINFO); - startup.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES; - startup.hStdInput = p->child_stdin_read; - startup.hStdOutput = p->child_stdout_write; - startup.hStdError = p->child_stdout_write; - startup.wShowWindow = SW_HIDE; - - String16 cwd = ToString16(scratch, working_dir); - String16 cmd = ToString16(scratch, command_line); - void *env = NULL; - - DWORD dwCreationFlags = 0; - BOOL bInheritHandles = TRUE; - - PROCESS_INFORMATION info = {}; - if (!CreateProcessW(L"c:\\windows\\system32\\cmd.exe", cmd.data, 0, 0, bInheritHandles, dwCreationFlags, env, cwd.data, &startup, &info)) { - Win32ProcessError(&process, "failed to create process", command_line); - return process; - } - - // Close handles to the stdin and stdout pipes no longer needed by the child process. - // If they are not explicitly closed, there is no way to recognize that the child process has ended. - CloseHandle(info.hThread); - CloseHandle(p->child_stdin_read); - CloseHandle(p->child_stdout_write); - p->child_stdin_read = INVALID_HANDLE_VALUE; - p->child_stdout_write = INVALID_HANDLE_VALUE; - - p->handle = info.hProcess; - process.is_valid = true; - - if (write_stdin.len) { - WriteStdin(&process, write_stdin); - CloseStdin(&process); - } - - return process; -} - -bool IsValid(Process *process) { - if (process->is_valid == false) return false; - Win32Process *p = (Win32Process *)process->platform; - - if (WaitForSingleObject(p->handle, 0) != WAIT_OBJECT_0) { - return true; - } - - DWORD exit_code; - bool get_exit_code_failed = GetExitCodeProcess(p->handle, &exit_code) == 0; - if (get_exit_code_failed) { - exit_code = -1; - } - - process->exit_code = (int)exit_code; - Win32CloseProcess(process); - return false; -} - -void KillProcess(Process *process) { - Assert(process->is_valid); - Win32Process *p = (Win32Process *)process->platform; - - bool terminate_process_error = TerminateProcess(p->handle, -1) == 0; - if (terminate_process_error) { - Assert(0); - } - Win32CloseProcess(process); - process->exit_code = -1; -} - -String PollStdout(Allocator allocator, Process *process) { - Assert(process->is_valid); - Win32Process *p = (Win32Process *)process->platform; - - DWORD bytes_avail = 0; - bool peek_error = PeekNamedPipe(p->child_stdout_read, NULL, 0, NULL, &bytes_avail, NULL) == 0; - if (peek_error) { - return {}; - } else if (bytes_avail == 0) { - return {}; - } - - size_t buffer_size = ClampTop(bytes_avail, (DWORD)(4096 * 16)); - char *buffer = AllocArray(allocator, char, buffer_size); - - DWORD bytes_read = 0; - bool read_error = ReadFile(p->child_stdout_read, buffer, (DWORD)buffer_size, &bytes_read, 0) == 0; - if (read_error) { - Win32ReportError("Failed to read the stdout of child process", "ReadFile"); - Dealloc(allocator, &buffer); - Win32CloseProcess(process); - return {}; - } - - String result = {buffer, bytes_read}; - return result; -} +#include "filesystem.h" +#include "thread_queue.h" + +#ifndef NOMINMAX + #define NOMINMAX +#endif +#ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN +#endif +#include +#include +#include + +#include "win32_thread.cpp" + +void (*Error)(const char *, ...); + +// Basic begin +void *VReserve(size_t size) { + void *result = (uint8_t *)VirtualAlloc(0, size, MEM_RESERVE, PAGE_READWRITE); + return result; +} + +bool VCommit(void *p, size_t size) { + void *result = VirtualAlloc(p, size, MEM_COMMIT, PAGE_READWRITE); + return result ? true : false; +} + +bool VRelease(void *p) { + BOOL result = VirtualFree(p, 0, MEM_RELEASE); + return result ? true : false; +} + +bool VDecommit(void *p, size_t size) { + BOOL result = VirtualFree(p, size, MEM_DECOMMIT); + return result ? true : false; +} +// Basic end + +void InitOS(void (*error_proc)(const char *, ...)) { + Error = error_proc; + + SetConsoleOutputCP(65001); + SetConsoleCP(65001); +} + +String ReadFile(Allocator arena, String path) { + bool success = false; + String result = {}; + + Scratch scratch(arena); + String16 string16 = ToString16(scratch, path); + HANDLE handle = CreateFileW((wchar_t *)string16.data, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (handle != INVALID_HANDLE_VALUE) { + LARGE_INTEGER file_size; + if (GetFileSizeEx(handle, &file_size)) { + if (file_size.QuadPart != 0) { + result.len = (int64_t)file_size.QuadPart; + result.data = (char *)AllocSize(arena, result.len + 1); + DWORD read; + if (ReadFile(handle, result.data, (DWORD)result.len, &read, NULL)) { // @todo: can only read 32 byte size files? + if (read == result.len) { + success = true; + result.data[result.len] = 0; + } + } + } + } + CloseHandle(handle); + } + + if (!success) { + Dealloc(arena, &result.data); + result = {}; + } + + return result; +} + +typedef struct Win32_FileIter { + HANDLE handle; + WIN32_FIND_DATAW data; +} Win32_FileIter; + +String GetAbsolutePath(Allocator arena, String relative) { + Scratch scratch(arena); + String16 wpath = ToString16(scratch, relative); + wchar_t *wpath_abs = AllocArray(scratch, wchar_t, 4096); + DWORD written = GetFullPathNameW((wchar_t *)wpath.data, 4096, wpath_abs, 0); + if (written == 0) + return {}; + String path = ToString(arena, {(char16_t *)wpath_abs, written}); + NormalizePathInPlace(path); + return path; +} + +bool IsValid(const FileIter &it) { + return it.is_valid; +} + +void Advance(FileIter *it) { + if (it->temp.arena) EndTemp(it->temp); + it->temp = BeginTemp(it->arena); + while (FindNextFileW(it->w32->handle, &it->w32->data) != 0) { + WIN32_FIND_DATAW *data = &it->w32->data; + + // Skip '.' and '..' + if (data->cFileName[0] == '.' && data->cFileName[1] == '.' && data->cFileName[2] == 0) continue; + if (data->cFileName[0] == '.' && data->cFileName[1] == 0) continue; + + it->is_directory = data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY; + it->filename = ToString(*it->arena, (char16_t *)data->cFileName, WideLength((char16_t *)data->cFileName)); + const char *is_dir = it->is_directory ? "/" : ""; + const char *separator = it->path.data[it->path.len - 1] == '/' ? "" : "/"; + it->relative_path = Format(*it->arena, "%.*s%s%.*s%s", FmtString(it->path), separator, FmtString(it->filename), is_dir); + it->absolute_path = GetAbsolutePath(*it->arena, it->relative_path); + it->is_valid = true; + + if (it->is_directory) { + Assert(it->relative_path.data[it->relative_path.len - 1] == '/'); + Assert(it->absolute_path.data[it->absolute_path.len - 1] == '/'); + } + return; + } + + it->is_valid = false; + DWORD error = GetLastError(); + Assert(error == ERROR_NO_MORE_FILES); + FindClose(it->w32->handle); + Dealloc(it->allocator, &it->arena); +} + +FileIter IterateFiles(Allocator alo, String path) { + FileIter it = {0}; + it.allocator = alo; + it.arena = AllocArena(alo, MiB(2)); + it.path = path; + + String modified_path = Format(*it.arena, "%.*s\\*", FmtString(path)); + String16 modified_path16 = ToString16(*it.arena, modified_path); + + it.w32 = AllocType(*it.arena, Win32_FileIter); + it.w32->handle = FindFirstFileW((wchar_t *)modified_path16.data, &it.w32->data); + if (it.w32->handle == INVALID_HANDLE_VALUE) { + it.is_valid = false; + return it; + } + + Advance(&it); + return it; +} + +#if _WIN32 + #include +double get_time_in_micros(void) { + static double invfreq; + if (!invfreq) { + LARGE_INTEGER frequency; + QueryPerformanceFrequency(&frequency); + invfreq = 1000000.0 / frequency.QuadPart; + } + LARGE_INTEGER counter; + QueryPerformanceCounter(&counter); + return counter.QuadPart * invfreq; +} +#else + #include +double get_time_in_micros(void) { + struct timespec spec; + clock_gettime(CLOCK_MONOTONIC, &spec); + return (((double)spec.tv_sec) * 1000000) + (((double)spec.tv_nsec) / 1000); +} +#endif + +bool WriteFile(String path, String data) { + bool result = false; + + Scratch scratch; + String16 wpath = ToString16(scratch, path); + + DWORD access = GENERIC_WRITE; + DWORD creation_disposition = CREATE_ALWAYS; + HANDLE handle = CreateFileW((wchar_t *)wpath.data, access, 0, NULL, creation_disposition, FILE_ATTRIBUTE_NORMAL, NULL); + if (handle != INVALID_HANDLE_VALUE) { + DWORD bytes_written = 0; + Assert(data.len == (DWORD)data.len); // @Todo: can only read 32 byte size files? + BOOL error = WriteFile(handle, data.data, (DWORD)data.len, &bytes_written, NULL); + if (error == TRUE) { + if (bytes_written == data.len) { + result = true; + } + } + CloseHandle(handle); + } + + return result; +} + +String GetExePath(Allocator allocator) { + Scratch scratch(allocator); + wchar_t *wbuffer = AllocArray(scratch, wchar_t, 4096); + DWORD wsize = GetModuleFileNameW(0, wbuffer, 4096); + Assert(wsize != 0); + + String path = ToString(allocator, (char16_t *)wbuffer, wsize); + NormalizePathInPlace(path); + return path; +} + +String GetExeDir(Allocator allocator) { + Scratch scratch((Arena *)allocator.object); + String path = GetExePath(scratch); + path = ChopLastSlash(path); + path = Copy(allocator, path); + return path; +} + +bool FileExists(String path) { + Scratch scratch; + String16 wbuff = ToString16(scratch, path); + DWORD attribs = GetFileAttributesW((wchar_t *)wbuff.data); + bool result = attribs == INVALID_FILE_ATTRIBUTES ? false : true; + return result; +} + +bool IsDir(String path) { + Scratch scratch; + String16 wbuff = ToString16(scratch, path); + DWORD dwAttrib = GetFileAttributesW((wchar_t *)wbuff.data); + return dwAttrib != INVALID_FILE_ATTRIBUTES && (dwAttrib & FILE_ATTRIBUTE_DIRECTORY); +} + +bool IsFile(String path) { + Scratch scratch; + String16 wbuff = ToString16(scratch, path); + DWORD dwAttrib = GetFileAttributesW((wchar_t *)wbuff.data); + bool is_file = (dwAttrib & FILE_ATTRIBUTE_DIRECTORY) == 0; + return dwAttrib != INVALID_FILE_ATTRIBUTES && is_file; +} + +String GetWorkingDir(Allocator arena) { + Scratch scratch(arena); + wchar_t *wbuffer = AllocArray(scratch, wchar_t, 4096); + DWORD wsize = GetCurrentDirectoryW(4096, wbuffer); + Assert(wsize != 0); + wbuffer[wsize] = 0; + + String path = ToString(arena, (char16_t *)wbuffer, wsize); + NormalizePathInPlace(path); + return path; +} + +bool IsAbsolute(String path) { + bool result = path.len > 3 && IsAlphabetic(path.data[0]) && path.data[1] == ':' && (path.data[2] == '/' || path.data[2] == '\\'); + return result; +} + +bool DeleteFile(String path) { + Scratch scratch; + String16 wpath = ToString16(scratch, path); + + BOOL success = DeleteFileW((wchar_t *)wpath.data); + bool result = true; + if (success == 0) result = false; + return result; +} + +int64_t GetFileModTime(String file) { + Scratch scratch; + String16 string16 = ToString16(scratch, file); + WIN32_FIND_DATAW data; + HANDLE handle = FindFirstFileW((wchar_t *)string16.data, &data); + if (handle != INVALID_HANDLE_VALUE) { + FindClose(handle); + FILETIME time = data.ftLastWriteTime; + int64_t result = (int64_t)time.dwHighDateTime << 32 | time.dwLowDateTime; + return result; + } else { + return -1; + } +} + +struct Win32Process { + HANDLE handle; + HANDLE child_stdout_read; + HANDLE child_stdout_write; + HANDLE child_stdin_read; + HANDLE child_stdin_write; +}; +// static_assert(sizeof(Win32Process) < sizeof(Process::platform)); + +void Win32ReportError(String msg, String cmd) { + LPVOID lpMsgBuf; + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpMsgBuf, 0, NULL); + defer { LocalFree(lpMsgBuf); }; + char *buff = (char *)lpMsgBuf; + size_t buffLen = strlen((const char *)buff); + if (buffLen > 0 && buff[buffLen - 1] == '\n') buff[buffLen - 1] = 0; + Error("%.*s: %.*s! %s", FmtString(msg), FmtString(cmd), (char *)lpMsgBuf); +} + +void Win32CloseProcess(Process *process) { + Win32Process *p = (Win32Process *)process->platform; + if (p->handle != INVALID_HANDLE_VALUE) CloseHandle(p->handle); + if (p->child_stdout_write != INVALID_HANDLE_VALUE) CloseHandle(p->child_stdout_write); + if (p->child_stdout_read != INVALID_HANDLE_VALUE) CloseHandle(p->child_stdout_read); + if (p->child_stdin_read != INVALID_HANDLE_VALUE) CloseHandle(p->child_stdin_read); + if (p->child_stdin_write != INVALID_HANDLE_VALUE) CloseHandle(p->child_stdin_write); + process->is_valid = false; +} + +static void Win32ProcessError(Process *process, String msg, String cmd) { + Win32Process *p = (Win32Process *)process->platform; + Win32ReportError(msg, cmd); + Win32CloseProcess(process); +} + +void WriteStdin(Process *process, String string) { + if (string.len == 0) return; + Assert(process->is_valid); + + Win32Process *p = (Win32Process *)process->platform; + Assert(p->child_stdin_write != INVALID_HANDLE_VALUE); + + DWORD written = 0; + bool write_error = WriteFile(p->child_stdin_write, string.data, (DWORD)string.len, &written, NULL) == 0; + + Assert(write_error == false); + Assert(written == string.len); +} + +void CloseStdin(Process *process) { + Win32Process *p = (Win32Process *)process->platform; + CloseHandle(p->child_stdin_write); + p->child_stdin_write = INVALID_HANDLE_VALUE; +} + +Process SpawnProcess(String command_line, String working_dir, String write_stdin, Array enviroment) { + Process process = {}; + Win32Process *p = (Win32Process *)process.platform; + + Scratch scratch; + command_line = Format(scratch, "/C %.*s", FmtString(command_line)); + + p->handle = INVALID_HANDLE_VALUE; + p->child_stdout_write = INVALID_HANDLE_VALUE; + p->child_stdout_read = INVALID_HANDLE_VALUE; + p->child_stdin_read = INVALID_HANDLE_VALUE; + p->child_stdin_write = INVALID_HANDLE_VALUE; + + SECURITY_ATTRIBUTES security_atrb = {}; + security_atrb.nLength = sizeof(SECURITY_ATTRIBUTES); + security_atrb.bInheritHandle = TRUE; + + if (!CreatePipe(&p->child_stdout_read, &p->child_stdout_write, &security_atrb, 0)) { + Win32ProcessError(&process, "Failed to create process at create pipe stage", command_line); + return process; + } + + if (!SetHandleInformation(p->child_stdout_read, HANDLE_FLAG_INHERIT, 0)) { + Win32ProcessError(&process, "Failed to create process at create pipe stage", command_line); + return process; + } + + if (!CreatePipe(&p->child_stdin_read, &p->child_stdin_write, &security_atrb, 0)) { + Win32ProcessError(&process, "Failed to create process at create pipe stage", command_line); + return process; + } + + if (!SetHandleInformation(p->child_stdin_write, HANDLE_FLAG_INHERIT, 0)) { + Win32ProcessError(&process, "Failed to create process at create pipe stage", command_line); + return process; + } + + STARTUPINFOW startup = {}; + startup.cb = sizeof(STARTUPINFO); + startup.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES; + startup.hStdInput = p->child_stdin_read; + startup.hStdOutput = p->child_stdout_write; + startup.hStdError = p->child_stdout_write; + startup.wShowWindow = SW_HIDE; + + String16 cwd = ToString16(scratch, working_dir); + String16 cmd = ToString16(scratch, command_line); + + char *env = NULL; + if (enviroment.len) { + Int size = GetSize(enviroment) + enviroment.len + 1; + env = (char *)PushSize(scratch, size); + Int i = 0; + For(enviroment) { + MemoryCopy(env + i, it.data, it.len); + i += it.len; + + env[i++] = 0; + } + env[i++] = 0; + } + + DWORD dwCreationFlags = 0; + BOOL bInheritHandles = TRUE; + + PROCESS_INFORMATION info = {}; + if (!CreateProcessW(L"c:\\windows\\system32\\cmd.exe", (wchar_t *)cmd.data, 0, 0, bInheritHandles, dwCreationFlags, env, (wchar_t *)cwd.data, &startup, &info)) { + Win32ProcessError(&process, "failed to create process", command_line); + return process; + } + + // Close handles to the stdin and stdout pipes no longer needed by the child process. + // If they are not explicitly closed, there is no way to recognize that the child process has ended. + CloseHandle(info.hThread); + CloseHandle(p->child_stdin_read); + CloseHandle(p->child_stdout_write); + p->child_stdin_read = INVALID_HANDLE_VALUE; + p->child_stdout_write = INVALID_HANDLE_VALUE; + + p->handle = info.hProcess; + process.is_valid = true; + + if (write_stdin.len) { + WriteStdin(&process, write_stdin); + CloseStdin(&process); + } + + return process; +} + +bool IsValid(Process *process) { + if (process->is_valid == false) return false; + Win32Process *p = (Win32Process *)process->platform; + + if (WaitForSingleObject(p->handle, 0) != WAIT_OBJECT_0) { + return true; + } + + DWORD exit_code; + bool get_exit_code_failed = GetExitCodeProcess(p->handle, &exit_code) == 0; + if (get_exit_code_failed) { + exit_code = -1; + } + + process->exit_code = (int)exit_code; + Win32CloseProcess(process); + return false; +} + +void KillProcess(Process *process) { + Assert(process->is_valid); + Win32Process *p = (Win32Process *)process->platform; + + bool terminate_process_error = TerminateProcess(p->handle, -1) == 0; + if (terminate_process_error) { + Assert(0); + } + Win32CloseProcess(process); + process->exit_code = -1; +} + +String PollStdout(Allocator allocator, Process *process) { + Assert(process->is_valid); + Win32Process *p = (Win32Process *)process->platform; + + DWORD bytes_avail = 0; + bool peek_error = PeekNamedPipe(p->child_stdout_read, NULL, 0, NULL, &bytes_avail, NULL) == 0; + if (peek_error) { + return {}; + } else if (bytes_avail == 0) { + return {}; + } + + size_t buffer_size = ClampTop(bytes_avail, (DWORD)(4096 * 16)); + char *buffer = AllocArray(allocator, char, buffer_size); + + DWORD bytes_read = 0; + bool read_error = ReadFile(p->child_stdout_read, buffer, (DWORD)buffer_size, &bytes_read, 0) == 0; + if (read_error) { + Win32ReportError("Failed to read the stdout of child process", "ReadFile"); + Dealloc(allocator, &buffer); + Win32CloseProcess(process); + return {}; + } + + String result = {buffer, bytes_read}; + return result; +} diff --git a/src/build_tool/core/filesystem.c b/src/build_tool/core/filesystem.c index ce933f9..d238400 100644 --- a/src/build_tool/core/filesystem.c +++ b/src/build_tool/core/filesystem.c @@ -39,8 +39,8 @@ OS_API bool OS_IsAbsolute(S8_String path) { } OS_API S8_String OS_GetExePath(MA_Arena *arena) { - wchar_t wbuffer[1024]; - DWORD wsize = GetModuleFileNameW(0, wbuffer, MA_Lengthof(wbuffer)); + char16_t wbuffer[1024]; + DWORD wsize = GetModuleFileNameW(0, (wchar_t *)wbuffer, MA_Lengthof(wbuffer)); IO_Assert(wsize != 0); S8_String path = S8_FromWidecharEx(arena, wbuffer, wsize); @@ -58,8 +58,8 @@ OS_API S8_String OS_GetExeDir(MA_Arena *arena) { } OS_API S8_String OS_GetWorkingDir(MA_Arena *arena) { - wchar_t wbuffer[1024]; - DWORD wsize = GetCurrentDirectoryW(MA_Lengthof(wbuffer), wbuffer); + char16_t wbuffer[1024]; + DWORD wsize = GetCurrentDirectoryW(MA_Lengthof(wbuffer), (wchar_t *)wbuffer); IO_Assert(wsize != 0); IO_Assert(wsize < 1022); wbuffer[wsize++] = '/'; @@ -71,16 +71,16 @@ OS_API S8_String OS_GetWorkingDir(MA_Arena *arena) { } OS_API void OS_SetWorkingDir(S8_String path) { - wchar_t wpath[1024]; + char16_t wpath[1024]; UTF_CreateWidecharFromChar(wpath, MA_Lengthof(wpath), path.str, path.len); - SetCurrentDirectoryW(wpath); + SetCurrentDirectoryW((wchar_t *)wpath); } OS_API S8_String OS_GetAbsolutePath(MA_Arena *arena, S8_String relative) { - wchar_t wpath[1024]; + char16_t wpath[1024]; UTF_CreateWidecharFromChar(wpath, MA_Lengthof(wpath), relative.str, relative.len); - wchar_t wpath_abs[1024]; - DWORD written = GetFullPathNameW((wchar_t *)wpath, MA_Lengthof(wpath_abs), wpath_abs, 0); + char16_t wpath_abs[1024]; + DWORD written = GetFullPathNameW((wchar_t *)wpath, MA_Lengthof(wpath_abs), (wchar_t *)wpath_abs, 0); if (written == 0) return S8_MakeEmpty(); S8_String path = S8_FromWidecharEx(arena, wpath_abs, written); @@ -89,24 +89,24 @@ OS_API S8_String OS_GetAbsolutePath(MA_Arena *arena, S8_String relative) { } OS_API bool OS_FileExists(S8_String path) { - wchar_t wbuff[1024]; + char16_t wbuff[1024]; UTF_CreateWidecharFromChar(wbuff, MA_Lengthof(wbuff), path.str, path.len); - DWORD attribs = GetFileAttributesW(wbuff); + DWORD attribs = GetFileAttributesW((wchar_t *)wbuff); bool result = attribs == INVALID_FILE_ATTRIBUTES ? false : true; return result; } OS_API bool OS_IsDir(S8_String path) { - wchar_t wbuff[1024]; + char16_t wbuff[1024]; UTF_CreateWidecharFromChar(wbuff, MA_Lengthof(wbuff), path.str, path.len); - DWORD dwAttrib = GetFileAttributesW(wbuff); + DWORD dwAttrib = GetFileAttributesW((wchar_t *)wbuff); return dwAttrib != INVALID_FILE_ATTRIBUTES && (dwAttrib & FILE_ATTRIBUTE_DIRECTORY); } OS_API bool OS_IsFile(S8_String path) { - wchar_t wbuff[1024]; + char16_t wbuff[1024]; UTF_CreateWidecharFromChar(wbuff, MA_Lengthof(wbuff), path.str, path.len); - DWORD dwAttrib = GetFileAttributesW(wbuff); + DWORD dwAttrib = GetFileAttributesW((wchar_t *)wbuff); bool is_file = (dwAttrib & FILE_ATTRIBUTE_DIRECTORY) == 0; return dwAttrib != INVALID_FILE_ATTRIBUTES && is_file; } @@ -151,7 +151,7 @@ OS_API void OS_Advance(OS_FileIter *it) { if (data->cFileName[0] == '.' && data->cFileName[1] == 0) continue; it->is_directory = data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY; - it->filename = S8_FromWidecharEx(it->arena, data->cFileName, S8_WideLength(data->cFileName)); + it->filename = S8_FromWidecharEx(it->arena, (char16_t *)data->cFileName, S8_WideLength((char16_t *)data->cFileName)); const char *is_dir = it->is_directory ? "/" : ""; const char *separator = it->path.str[it->path.len - 1] == '/' ? "" : "/"; it->relative_path = S8_Format(it->arena, "%.*s%s%.*s%s", S8_Expand(it->path), separator, S8_Expand(it->filename), is_dir); @@ -177,12 +177,12 @@ OS_API OS_FileIter OS_IterateFiles(MA_Arena *scratch_arena, S8_String path) { it.path = path; S8_String modified_path = S8_Format(it.arena, "%.*s\\*", S8_Expand(path)); - wchar_t *wbuff = MA_PushArray(it.arena, wchar_t, modified_path.len + 1); + char16_t *wbuff = MA_PushArray(it.arena, char16_t, modified_path.len + 1); int64_t wsize = UTF_CreateWidecharFromChar(wbuff, modified_path.len + 1, modified_path.str, modified_path.len); IO_Assert(wsize); it.w32 = MA_PushStruct(it.arena, OS_Win32_FileIter); - it.w32->handle = FindFirstFileW(wbuff, &it.w32->data); + it.w32->handle = FindFirstFileW((wchar_t *)wbuff, &it.w32->data); if (it.w32->handle == INVALID_HANDLE_VALUE) { it.is_valid = false; return it; @@ -194,9 +194,9 @@ OS_API OS_FileIter OS_IterateFiles(MA_Arena *scratch_arena, S8_String path) { } OS_API OS_Result OS_MakeDir(S8_String path) { - wchar_t wpath[1024]; + char16_t wpath[1024]; UTF_CreateWidecharFromChar(wpath, MA_Lengthof(wpath), path.str, path.len); - BOOL success = CreateDirectoryW(wpath, NULL); + BOOL success = CreateDirectoryW((wchar_t *)wpath, NULL); OS_Result result = OS_SUCCESS; if (success == 0) { DWORD error = GetLastError(); @@ -212,14 +212,14 @@ OS_API OS_Result OS_MakeDir(S8_String path) { } OS_API OS_Result OS_CopyFile(S8_String from, S8_String to, bool overwrite) { - wchar_t wfrom[1024]; + char16_t wfrom[1024]; UTF_CreateWidecharFromChar(wfrom, MA_Lengthof(wfrom), from.str, from.len); - wchar_t wto[1024]; + char16_t wto[1024]; UTF_CreateWidecharFromChar(wto, MA_Lengthof(wto), to.str, to.len); BOOL fail_if_exists = !overwrite; - BOOL success = CopyFileW(wfrom, wto, fail_if_exists); + BOOL success = CopyFileW((wchar_t *)wfrom, (wchar_t *)wto, fail_if_exists); OS_Result result = OS_SUCCESS; if (success == FALSE) @@ -228,9 +228,9 @@ OS_API OS_Result OS_CopyFile(S8_String from, S8_String to, bool overwrite) { } OS_API OS_Result OS_DeleteFile(S8_String path) { - wchar_t wpath[1024]; + char16_t wpath[1024]; UTF_CreateWidecharFromChar(wpath, MA_Lengthof(wpath), path.str, path.len); - BOOL success = DeleteFileW(wpath); + BOOL success = DeleteFileW((wchar_t *)wpath); OS_Result result = OS_SUCCESS; if (success == 0) result = OS_PATH_NOT_FOUND; @@ -274,7 +274,7 @@ OS_API OS_Result OS_DeleteDir(S8_String path, unsigned flags) { } static OS_Result OS__WriteFile(S8_String path, S8_String data, bool append) { - wchar_t wpath[1024]; + char16_t wpath[1024]; UTF_CreateWidecharFromChar(wpath, MA_Lengthof(wpath), path.str, path.len); OS_Result result = OS_FAILURE; @@ -285,7 +285,7 @@ static OS_Result OS__WriteFile(S8_String path, S8_String data, bool append) { creation_disposition = OPEN_ALWAYS; } - HANDLE handle = CreateFileW(wpath, access, 0, NULL, creation_disposition, FILE_ATTRIBUTE_NORMAL, NULL); + HANDLE handle = CreateFileW((wchar_t *)wpath, access, 0, NULL, creation_disposition, FILE_ATTRIBUTE_NORMAL, NULL); if (handle != INVALID_HANDLE_VALUE) { DWORD bytes_written = 0; IO_Assert(data.len == (DWORD)data.len); // @Todo: can only read 32 byte size files? @@ -314,9 +314,9 @@ OS_API S8_String OS_ReadFile(MA_Arena *arena, S8_String path) { S8_String result = S8_MakeEmpty(); MA_Temp checkpoint = MA_BeginTemp(arena); - wchar_t wpath[1024]; + char16_t wpath[1024]; UTF_CreateWidecharFromChar(wpath, MA_Lengthof(wpath), path.str, path.len); - HANDLE handle = CreateFileW(wpath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + HANDLE handle = CreateFileW((wchar_t *)wpath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (handle != INVALID_HANDLE_VALUE) { LARGE_INTEGER file_size; if (GetFileSizeEx(handle, &file_size)) { @@ -347,9 +347,9 @@ OS_API int64_t OS_GetFileModTime(S8_String file) { FILETIME time = {0}; WIN32_FIND_DATAW data; - wchar_t wpath[1024]; + char16_t wpath[1024]; UTF_CreateWidecharFromChar(wpath, 1024, file.str, file.len); - HANDLE handle = FindFirstFileW(wpath, &data); + HANDLE handle = FindFirstFileW((wchar_t *)wpath, &data); if (handle != INVALID_HANDLE_VALUE) { FindClose(handle); time = data.ftLastWriteTime; diff --git a/src/build_tool/process.cpp b/src/build_tool/process.cpp index 47afcbf..6a1edb1 100644 --- a/src/build_tool/process.cpp +++ b/src/build_tool/process.cpp @@ -7,7 +7,7 @@ struct Process { Process RunEx(S8_String in_cmd) { MA_Scratch scratch; wchar_t *application_name = NULL; - wchar_t *cmd = S8_ToWidechar(scratch, in_cmd); + wchar_t *cmd = (wchar_t *)S8_ToWidechar(scratch, in_cmd); BOOL inherit_handles = FALSE; DWORD creation_flags = 0; void *enviroment = NULL; diff --git a/src/build_tool/standalone_libraries/string.c b/src/build_tool/standalone_libraries/string.c index a9ab569..ff34d17 100644 --- a/src/build_tool/standalone_libraries/string.c +++ b/src/build_tool/standalone_libraries/string.c @@ -410,7 +410,7 @@ S8_API int64_t S8_Length(char *string) { return len; } -S8_API int64_t S8_WideLength(wchar_t *string) { +S8_API int64_t S8_WideLength(char16_t *string) { int64_t len = 0; while (*string++ != 0) len++; @@ -533,20 +533,20 @@ S8_API S8_String S8_AddF(S8_Allocator allocator, S8_List *list, const char *str, #ifdef FIRST_UTF_HEADER S8_API S16_String S8_ToWidecharEx(S8_Allocator allocator, S8_String string) { - S8_ASSERT(sizeof(wchar_t) == 2); - wchar_t *buffer = (wchar_t *)S8_ALLOCATE(allocator, sizeof(wchar_t) * (string.len + 1)); + S8_ASSERT(sizeof(char16_t) == 2); + char16_t *buffer = (char16_t *)S8_ALLOCATE(allocator, sizeof(char16_t) * (string.len + 1)); int64_t size = UTF_CreateWidecharFromChar(buffer, string.len + 1, string.str, string.len); S16_String result = {buffer, size}; return result; } -S8_API wchar_t *S8_ToWidechar(S8_Allocator allocator, S8_String string) { +S8_API char16_t *S8_ToWidechar(S8_Allocator allocator, S8_String string) { S16_String result = S8_ToWidecharEx(allocator, string); return result.str; } -S8_API S8_String S8_FromWidecharEx(S8_Allocator allocator, wchar_t *wstring, int64_t wsize) { - S8_ASSERT(sizeof(wchar_t) == 2); +S8_API S8_String S8_FromWidecharEx(S8_Allocator allocator, char16_t *wstring, int64_t wsize) { + S8_ASSERT(sizeof(char16_t) == 2); int64_t buffer_size = (wsize + 1) * 2; char *buffer = (char *)S8_ALLOCATE(allocator, buffer_size); @@ -557,7 +557,7 @@ S8_API S8_String S8_FromWidecharEx(S8_Allocator allocator, wchar_t *wstring, int return result; } -S8_API S8_String S8_FromWidechar(S8_Allocator allocator, wchar_t *wstring) { +S8_API S8_String S8_FromWidechar(S8_Allocator allocator, char16_t *wstring) { int64_t size = S8_WideLength(wstring); S8_String result = S8_FromWidecharEx(allocator, wstring, size); return result; diff --git a/src/build_tool/standalone_libraries/string.h b/src/build_tool/standalone_libraries/string.h index 76b1d33..a370737 100644 --- a/src/build_tool/standalone_libraries/string.h +++ b/src/build_tool/standalone_libraries/string.h @@ -82,7 +82,7 @@ struct S8_List { }; typedef struct S16_String { - wchar_t *str; + char16_t *str; int64_t len; } S16_String; @@ -159,7 +159,7 @@ S8_API S8_String S8_GetNameNoExt(S8_String s); S8_API bool S8_IsPointerInside(S8_String string, char *p); S8_API S8_String S8_SkipToP(S8_String string, char *p); S8_API S8_String S8_SkipPast(S8_String string, S8_String a); -S8_API int64_t S8_WideLength(wchar_t *string); +S8_API int64_t S8_WideLength(char16_t *string); S8_API S8_String S8_MakeFromChar(char *string); S8_API S8_String S8_MakeEmpty(void); S8_API S8_List S8_MakeEmptyList(void); @@ -185,9 +185,9 @@ S8_API S8_Node *S8_Add(S8_Allocator allocator, S8_List *list, S8_String string) S8_API S8_String S8_AddF(S8_Allocator allocator, S8_List *list, const char *str, ...) S8__PrintfFormat(3, 4); S8_API S16_String S8_ToWidecharEx(S8_Allocator allocator, S8_String string); -S8_API wchar_t *S8_ToWidechar(S8_Allocator allocator, S8_String string); -S8_API S8_String S8_FromWidecharEx(S8_Allocator allocator, wchar_t *wstring, int64_t wsize); -S8_API S8_String S8_FromWidechar(S8_Allocator allocator, wchar_t *wstring); +S8_API char16_t *S8_ToWidechar(S8_Allocator allocator, S8_String string); +S8_API S8_String S8_FromWidecharEx(S8_Allocator allocator, char16_t *wstring, int64_t wsize); +S8_API S8_String S8_FromWidechar(S8_Allocator allocator, char16_t *wstring); #if defined(__cplusplus) inline S8_String operator""_s(const char *str, size_t size) { return {(char *)str, (int64_t)size}; } diff --git a/src/build_tool/standalone_libraries/unicode.c b/src/build_tool/standalone_libraries/unicode.c index 8cc03ab..c8e0465 100644 --- a/src/build_tool/standalone_libraries/unicode.c +++ b/src/build_tool/standalone_libraries/unicode.c @@ -136,7 +136,7 @@ UTF_API UTF16_Result UTF_ConvertUTF32ToUTF16(uint32_t codepoint) { break; \ } -UTF_API int64_t UTF_CreateCharFromWidechar(char *buffer, int64_t buffer_size, wchar_t *in, int64_t inlen) { +UTF_API int64_t UTF_CreateCharFromWidechar(char *buffer, int64_t buffer_size, char16_t *in, int64_t inlen) { int64_t outlen = 0; for (int64_t i = 0; i < inlen && in[i];) { UTF32_Result decode = UTF_ConvertUTF16ToUTF32((uint16_t *)(in + i), (int)(inlen - i)); @@ -159,7 +159,7 @@ UTF_API int64_t UTF_CreateCharFromWidechar(char *buffer, int64_t buffer_size, wc return outlen; } -UTF_API int64_t UTF_CreateWidecharFromChar(wchar_t *buffer, int64_t buffer_size, char *in, int64_t inlen) { +UTF_API int64_t UTF_CreateWidecharFromChar(char16_t *buffer, int64_t buffer_size, char *in, int64_t inlen) { int64_t outlen = 0; for (int64_t i = 0; i < inlen;) { UTF32_Result decode = UTF_ConvertUTF8ToUTF32(in + i, (int)(inlen - i)); diff --git a/src/build_tool/standalone_libraries/unicode.h b/src/build_tool/standalone_libraries/unicode.h index 0622e83..941f3bb 100644 --- a/src/build_tool/standalone_libraries/unicode.h +++ b/src/build_tool/standalone_libraries/unicode.h @@ -45,8 +45,8 @@ UTF_API UTF32_Result UTF_ConvertUTF16ToUTF32(uint16_t *c, int max_advance); UTF_API UTF8_Result UTF_ConvertUTF32ToUTF8(uint32_t codepoint); UTF_API UTF32_Result UTF_ConvertUTF8ToUTF32(char *c, int max_advance); UTF_API UTF16_Result UTF_ConvertUTF32ToUTF16(uint32_t codepoint); -UTF_API int64_t UTF_CreateCharFromWidechar(char *buffer, int64_t buffer_size, wchar_t *in, int64_t inlen); -UTF_API int64_t UTF_CreateWidecharFromChar(wchar_t *buffer, int64_t buffer_size, char *in, int64_t inlen); +UTF_API int64_t UTF_CreateCharFromWidechar(char *buffer, int64_t buffer_size, char16_t *in, int64_t inlen); +UTF_API int64_t UTF_CreateWidecharFromChar(char16_t *buffer, int64_t buffer_size, char *in, int64_t inlen); UTF_API void UTF8_Advance(UTF8_Iter *iter); UTF_API UTF8_Iter UTF8_IterateEx(char *str, int len); UTF_API UTF8_Iter UTF8_Iterate(char *str); diff --git a/src/profiler/profiler.cpp b/src/profiler/profiler.cpp index 1417396..68f3025 100644 --- a/src/profiler/profiler.cpp +++ b/src/profiler/profiler.cpp @@ -1,4 +1,4 @@ -#if DEBUG_BUILD +#if 0 #include "spall.h" static SpallProfile spall_ctx; diff --git a/src/text_editor/buffer.cpp b/src/text_editor/buffer.cpp index 2f9c825..1847c9f 100644 --- a/src/text_editor/buffer.cpp +++ b/src/text_editor/buffer.cpp @@ -1,9 +1,649 @@ -/* -https://code.visualstudio.com/blogs/2018/03/23/text-buffer-reimplementation -*/ #define BUFFER_DEBUG 0 -void Grow(Buffer *buffer, Int change_size) { +/////////////////////////////// +// helpers +/////////////////////////////// + +Int GetSize(Range range) { + Assert(range.max >= range.min); + Int result = range.max - range.min; + return result; +} + +Range Rng(Int a, Int b) { + Range result = {Min(a, b), Max(a, b)}; + return result; +} + +Range Rng(Int a = 0) { + Range result = {a, a}; + return result; +} + +String16 GetString(Buffer *buffer, Range range = {0, INT64_MAX}) { + range.min = Clamp(range.min, (Int)0, buffer->len); + range.max = Clamp(range.max, (Int)0, buffer->len); + String16 result = {(char16_t *)buffer->data + range.min, GetSize(range)}; + return result; +} + +String AllocCharString(Allocator allocator, Buffer *buffer, Range range = {0, INT64_MAX}) { + String16 string16 = GetString(buffer, range); + String result = ToString(allocator, string16); + return result; +} + +Int Clamp(const Buffer *buffer, Int pos) { + Int result = Clamp(pos, (Int)0, buffer->len); + return result; +} + +Range Clamp(const Buffer *buffer, Range range) { + Range result = {}; + result.min = Clamp(buffer, range.min); + result.max = Clamp(buffer, range.max); + return result; +} + +Range GetEndAsRange(Buffer *buffer) { + Range result = {buffer->len, buffer->len}; + return result; +} + +Range GetBeginAsRange(Buffer *buffer) { + Range result = {0, 0}; + return result; +} + +Range GetRange(Buffer *buffer) { + Range result = {0, buffer->len}; + return result; +} + +Int GetFront(Caret caret) { + Int result = caret.pos[caret.ifront]; + return result; +} + +Int GetBack(Caret caret) { + Int index = (caret.ifront + 1) % 2; + Int result = caret.pos[index]; + return result; +} + +Caret MakeCaret(Int pos) { + Caret result = {}; + result.range.min = result.range.max = pos; + return result; +} + +XY XYLine(Int line) { + XY result = {}; + result.line = line; + return result; +} + +char16_t GetChar(Buffer *buffer, Int pos) { + if (pos >= 0 && pos < buffer->len) return buffer->str[pos]; + return 0; +} + +Caret MakeCaret(Int front, Int back) { + Caret result = {}; + if (front >= back) { + result.range.min = back; + result.range.max = front; + result.ifront = 1; + } else { + result.range.min = front; + result.range.max = back; + result.ifront = 0; + } + return result; +} + +Caret SetBack(Caret caret, Int back) { + Int front = GetFront(caret); + Caret result = MakeCaret(front, back); + return result; +} + +Caret SetFront(Caret caret, Int front) { + Int back = GetBack(caret); + Caret result = MakeCaret(front, back); + return result; +} + +Caret SetFrontWithAnchor(Caret caret, Range anchor, Int p) { + if (anchor.min > p) { + caret = MakeCaret(p, anchor.max); + } else if (anchor.max < p) { + caret = MakeCaret(p, anchor.min); + } else { + caret = MakeCaret(anchor.max, anchor.min); + } + return caret; +} + +bool InBounds(const Buffer *buffer, Int pos) { + bool result = pos >= 0 && pos < buffer->len; + return result; +} + +bool InBounds(Range range, Int pos) { + bool result = pos >= range.min && pos < range.max; + return result; +} + +bool AreEqual(Range a, Range b) { + bool result = a.min == b.min && a.max == b.max; + return result; +} + +bool AreEqual(Caret a, Caret b) { + bool result = AreEqual(a.range, b.range) && a.ifront == b.ifront; + return result; +} + +bool AreOverlapping(Range a, Range b) { + bool r1 = (a.max >= b.min && a.max <= b.max) || (a.min >= b.min && a.min <= b.max); + bool r2 = (b.max >= a.min && b.max <= a.max) || (b.min >= a.min && b.min <= a.max); + return r1 || r2; +} + +bool AreOverlapping(Caret a, Caret b) { + bool result = AreOverlapping(a.range, b.range); + return result; +} + +bool InRange(Int a, Range b) { + bool result = a >= b.min && a < b.max; + return result; +} + +Range operator-(Range a, Int value) { + a.min -= value; + a.max -= value; + return a; +} + +Range operator-=(Range &range, Int value) { + range = range - value; + return range; +} + +Int LastLine(Buffer *buffer) { + Int result = buffer->line_starts.len - 1; + return result; +} + +const Int LAST_LINE = INT64_MAX; +Range GetLineRange(Buffer *buffer, Int line, Int *end_of_buffer = NULL) { + Range result = {buffer->line_starts[line], buffer->len}; + if (line + 1 < buffer->line_starts.len) { + result.max = buffer->line_starts[line + 1]; + } else if (end_of_buffer) { + *end_of_buffer = 1; + } + return result; +} + +Range GetLineRangeWithoutNL(Buffer *buffer, Int line) { + Int end_of_buffer = 0; + Range line_range = GetLineRange(buffer, line, &end_of_buffer); + line_range.max = line_range.max - 1 + end_of_buffer; + return line_range; +} + +String16 GetLineString(Buffer *buffer, Int line, Int *end_of_buffer = NULL) { + Range range = GetLineRange(buffer, line, end_of_buffer); + String16 string = GetString(buffer, range); + return string; +} + +String16 GetLineStringWithoutNL(Buffer *buffer, Int line) { + Range range = GetLineRangeWithoutNL(buffer, line); + String16 string = GetString(buffer, range); + return string; +} + +Int PosToLine(Buffer *buffer, Int pos) { + Add(&buffer->line_starts, buffer->len + 1); + + // binary search + Int low = 0; + + // -2 here because we use 2 indices and combine them into one line range so we + // don't want to access last item since that would index past array. + Int high = buffer->line_starts.len - 2; + Int result = 0; + + while (low <= high) { + Int mid = low + (high - low) / 2; + Range range = {buffer->line_starts[mid], buffer->line_starts[mid + 1]}; + if (pos >= range.min && pos < range.max) { + result = mid; + break; + } + + if (range.min < pos) { + low = mid + 1; + } else { + high = mid - 1; + } + } + + Pop(&buffer->line_starts); + return result; +} + +XY PosToXY(Buffer *buffer, Int pos) { + Int line = PosToLine(buffer, pos); + Range line_range = GetLineRange(buffer, line); + Int col = pos - line_range.min; + XY result = {col, line}; + return result; +} + +Int XYToPos(Buffer *buffer, XY xy) { + xy.line = Clamp(xy.line, (Int)0, buffer->line_starts.len - 1); + Range line_range = GetLineRange(buffer, xy.line); + Int pos = Clamp(xy.col + line_range.min, line_range.min, line_range.max); + return pos; +} + +Int XYToPosWithoutNL(Buffer *buffer, XY xy) { + xy.line = Clamp(xy.line, (Int)0, buffer->line_starts.len - 1); + Int end_of_buffer = 0; + Range line_range = GetLineRange(buffer, xy.line, &end_of_buffer); + Int pos = Clamp(xy.col + line_range.min, line_range.min, line_range.max - 1 + end_of_buffer); + return pos; +} + +Int XYToPosErrorOutOfBounds(Buffer *buffer, XY xy) { + if (xy.line < 0 || xy.line >= buffer->line_starts.len) return -1; + Range line_range = GetLineRange(buffer, xy.line); + Int pos = xy.col + line_range.min; + if (pos < line_range.min || pos > line_range.max) return -1; + return pos; +} + +Int GetWordStart(Buffer *buffer, Int pos) { + pos = Clamp(pos, (Int)0, buffer->len); + for (Int i = pos - 1; i >= 0; i -= 1) { + if (!IsWord(buffer->str[i])) break; + pos = i; + } + return pos; +} + +Int GetWordEnd(Buffer *buffer, Int pos) { + pos = Clamp(pos, (Int)0, buffer->len); + for (Int i = pos;; i += 1) { + pos = i; + // this is because buffer end terminates the loop + // too early and we cannot establish the proper range + // semantics - proper max is one past last index + if (!(i < buffer->len)) break; + if (!IsWord(buffer->str[i])) break; + } + return pos; +} + +bool CallIsLoadWord(char16_t w); +// bool IsLoadWord(char16_t w) { +// bool result = w == u'(' || w == u')' || w == u'/' || w == u'\\' || w == u':' || w == u'+' || w == u'_' || w == u'.' || w == u'-' || w == u','; +// if (!result) { +// result = !(IsSymbol(w) || IsWhitespace(w)); +// } +// return result; +// } + +Int GetLoadWordStart(Buffer *buffer, Int pos) { + pos = Clamp(pos, (Int)0, buffer->len); + for (Int i = pos - 1; i >= 0; i -= 1) { + if (!CallIsLoadWord(buffer->str[i])) break; + pos = i; + } + return pos; +} + +Int GetLoadWordEnd(Buffer *buffer, Int pos) { + pos = Clamp(pos, (Int)0, buffer->len); + for (Int i = pos;; i += 1) { + pos = i; + // this is because buffer end terminates the loop + // too early and we cannot establish the proper range + // semantics - proper max is one past last index + if (!(i < buffer->len)) break; + if (!CallIsLoadWord(buffer->str[i])) break; + } + return pos; +} + +Range EncloseLoadWord(Buffer *buffer, Int pos) { + Range result = {GetLoadWordStart(buffer, pos), GetLoadWordEnd(buffer, pos)}; + return result; +} + +Int GetExecWordStart(Buffer *buffer, Int pos) { + pos = Clamp(pos, (Int)0, buffer->len); + for (Int i = pos - 1; i >= 0; i -= 1) { + if (IsWhitespace(buffer->str[i])) break; + if (GetChar(buffer, i) == u'|' && GetChar(buffer, i + 1) == u'>') break; + pos = i; + } + return pos; +} + +Int GetExecWordEnd(Buffer *buffer, Int pos) { + pos = Clamp(pos, (Int)0, buffer->len); + for (Int i = pos;; i += 1) { + pos = i; + // this is because buffer end terminates the loop + // too early and we cannot establish the proper range + // semantics - proper max is one past last index + if (!(i < buffer->len)) break; + if (IsWhitespace(buffer->str[i])) break; + if (GetChar(buffer, i - 1) == u'<' && GetChar(buffer, i) == u'|') break; + } + return pos; +} + +Int GetNextWordEnd(Buffer *buffer, Int pos) { + pos = Clamp(pos, (Int)0, buffer->len); + char16_t prev = 0; + for (Int i = pos;; i += 1) { + pos = i; + // this is because buffer end terminates the loop + // too early and we cannot establish the proper range + // semantics - proper max is one past last index + if (!(i < buffer->len)) break; + if (prev == u'\n' || (prev && prev != buffer->str[i]) || IsWord(buffer->str[i])) { + break; + } + prev = buffer->str[i]; + } + Int result = prev == u'\n' ? pos : GetWordEnd(buffer, pos); + return result; +} + +Int GetPrevWordStart(Buffer *buffer, Int pos) { + pos = Clamp(pos, (Int)0, buffer->len); + char16_t prev = 0; + Int i = pos - 1; + for (; i >= 0; i -= 1) { + if (prev == u'\n' || (prev && prev != buffer->str[i]) || IsWord(buffer->str[i])) { + break; + } + pos = i; + prev = buffer->str[i]; + } + bool new_line = prev == u'\n'; + Int result = new_line ? pos : GetWordStart(buffer, pos); + return result; +} + +Int GetLineStart(Buffer *buffer, Int pos) { + pos = Clamp(pos, (Int)0, buffer->len); + Int line = PosToLine(buffer, pos); + Range range = GetLineRangeWithoutNL(buffer, line); + return range.min; +} + +Int GetLineEnd(Buffer *buffer, Int pos) { + pos = Clamp(pos, (Int)0, buffer->len); + Int line = PosToLine(buffer, pos); + Range range = GetLineRangeWithoutNL(buffer, line); + return range.max; +} + +Int GetFullLineStart(Buffer *buffer, Int pos) { + pos = Clamp(pos, (Int)0, buffer->len); + Int line = PosToLine(buffer, pos); + Range range = GetLineRange(buffer, line); + return range.min; +} + +Int GetFullLineEnd(Buffer *buffer, Int pos) { + pos = Clamp(pos, (Int)0, buffer->len); + Int line = PosToLine(buffer, pos); + Range range = GetLineRange(buffer, line); + return range.max; +} + +Int GetBufferEnd(Buffer *buffer) { + return buffer->len; +} + +Int GetBufferStart(Buffer *buffer) { + return 0; +} + +Int GetNextEmptyLineStart(Buffer *buffer, Int pos) { + Int result = pos; + Int next_line = PosToLine(buffer, pos) + 1; + for (Int line = next_line; line < buffer->line_starts.len; line += 1) { + Range line_range = GetLineRangeWithoutNL(buffer, line); + result = line_range.min; + + bool whitespace_line = true; + for (Int i = line_range.min; i < line_range.max; i += 1) { + if (!IsWhitespace(buffer->str[i])) { + whitespace_line = false; + break; + } + } + if (whitespace_line) break; + } + return result; +} + +Int GetPrevEmptyLineStart(Buffer *buffer, Int pos) { + Int result = pos; + Int next_line = PosToLine(buffer, pos) - 1; + for (Int line = next_line; line >= 0; line -= 1) { + Range line_range = GetLineRangeWithoutNL(buffer, line); + result = line_range.min; + + bool whitespace_line = true; + for (Int i = line_range.min; i < line_range.max; i += 1) { + if (!IsWhitespace(buffer->str[i])) { + whitespace_line = false; + break; + } + } + if (whitespace_line) break; + } + return result; +} + +Range EncloseWord(Buffer *buffer, Int pos) { + Range result = {GetWordStart(buffer, pos), GetWordEnd(buffer, pos)}; + return result; +} + +Int SkipSpaces(Buffer *buffer, Int seek) { + for (; seek < buffer->len; seek += 1) { + char16_t c = GetChar(buffer, seek); + if (c != u' ') break; + } + return seek; +} + +Int FindScopeEnd(Buffer *buffer, Int seek, Int max_seek, char16_t open, char16_t close) { + char16_t right = GetChar(buffer, seek); + if (right == open) { + int scope = 1; + Int i = seek + 1; + for (; i < seek + max_seek && i < buffer->len; i += 1) { + char16_t c = GetChar(buffer, i); + + if (open == close && c == '\\') { + i += 1; + } else if (open == close && c == open) { + scope -= 1; + } else if (c == open) { + scope += 1; + } else if (c == close) { + scope -= 1; + } + + if (c == u'\n' || scope == 0) break; + } + + if (scope == 0) seek = i; + } + return seek; +} + +Range EncloseScope(Buffer *buffer, Int pos, char16_t open, char16_t close) { + Range result = {pos, pos}; + for (Int i = pos; i >= 0; i -= 1) { + if (buffer->str[i] == open) { + result.min = i; + break; + } + } + for (Int i = pos; i < buffer->len; i += 1) { + if (buffer->str[i] == close) { + result.max = i + 1; + break; + } + } + return result; +} + +Range EncloseLine(Buffer *buffer, Int pos) { + Range result = {GetLineStart(buffer, pos), GetLineEnd(buffer, pos)}; + return result; +} + +Range EncloseFullLine(Buffer *buffer, Int pos) { + Range result = {GetFullLineStart(buffer, pos), GetFullLineEnd(buffer, pos)}; + return result; +} + +Int OffsetByLine(Buffer *buffer, Int pos, Int line_offset) { + XY xy = PosToXY(buffer, pos); + Int result = XYToPosWithoutNL(buffer, {xy.col, xy.line + line_offset}); + return result; +} + +Int GetNextChar(Buffer *buffer, Int pos) { + Int result = Clamp(pos + 1, (Int)0, buffer->len); + return result; +} + +Int GetPrevChar(Buffer *buffer, Int pos) { + Int result = Clamp(pos - 1, (Int)0, buffer->len); + return result; +} + +Int GetLineIndent(Buffer *buffer, Int line) { + String16 string = GetLineStringWithoutNL(buffer, line); + Int indent = 0; + for (Int i = 0; i < string.len; i += 1) { + if (IsWhitespace(string.data[i])) { + indent += 1; + } else { + break; + } + } + return indent; +} + +Int GetIndentAtPos(Buffer *buffer, Int pos) { + Int line = PosToLine(buffer, pos); + Int result = GetLineIndent(buffer, line); + return result; +} + +Range GetIndentRangeAtPos(Buffer *buffer, Int pos) { + Int line = PosToLine(buffer, pos); + Int indent = GetLineIndent(buffer, line); + Range range = GetLineRangeWithoutNL(buffer, line); + return {range.min, range.min + indent}; +} + + +Int FuzzyCloserWordBegin = 5; +Int FuzzyConsecutiveMultiplier = 3; +Int FuzzyRate(String16 string, String16 with) { + if (with.len == 0) return 0; + Int points = 0; + Int consecutive = 0; + Int with_i = 0; + for (Int i = 0; i < string.len; i++) { + if (string.data[i] == with[with_i]) { + Int closer_begin = ClampBottom((Int)0, FuzzyCloserWordBegin - i); + points += closer_begin; + consecutive++; + with_i += 1; + } else { + points += consecutive * FuzzyConsecutiveMultiplier; + consecutive = 0; + with_i = 0; + } + + if (with_i >= with.len) with_i = 0; + } + points += consecutive * FuzzyConsecutiveMultiplier; + return points; +} + +Array FuzzySearchLines(Allocator allocator, Buffer *buffer, Int line_min, Int line_max, String16 needle) { + if (line_min < 0 || line_min >= buffer->line_starts.len) return {}; + if (line_max < 0 || line_min > buffer->line_starts.len) return {}; + Array ratings = {allocator}; + for (Int i = line_min; i < line_max; i += 1) { + String16 s = GetLineStringWithoutNL(buffer, i); + Int rating = FuzzyRate(s, needle); + Add(&ratings, {i, rating}); + } + + // bubble sort + for (Int i = 0; i < ratings.len - 1; i++) { + for (Int j = 0; j < ratings.len - 1; j++) { + if (ratings[j].rating < ratings[j + 1].rating) { + FuzzyPair temp = ratings[j]; + ratings[j] = ratings[j + 1]; + ratings[j + 1] = temp; + } + } + } + + return ratings; +} + +Int FindRangeByPos(Array *ranges, Int pos) { + // binary search + Int low = 0; + Int high = ranges->len - 1; + Int result = -1; + + while (low <= high) { + Int mid = low + (high - low) / 2; + Range range = ranges->data[mid]; + if (pos >= range.min && pos < range.max) { + result = mid; + break; + } + + if (range.min < pos) { + low = mid + 1; + } else { + high = mid - 1; + } + } + + return result; +} +/////////////////////////////// +// Raw operations +/////////////////////////////// + +void RawGrow(Buffer *buffer, Int change_size) { Int new_size = buffer->len + change_size; if (new_size > buffer->cap) { Allocator alo = buffer->line_starts.allocator; @@ -17,7 +657,7 @@ void Grow(Buffer *buffer, Int change_size) { } } -void OffsetAllLinesForward(Buffer *buffer, Int line, Int *_offset) { +void RawOffsetAllLinesForward(Buffer *buffer, Int line, Int *_offset) { Int offset = *_offset; *_offset = 0; if (offset == 0) return; @@ -39,14 +679,14 @@ void UpdateLines(Buffer *buffer, Range range, String16 string) { Int lines_to_remove = min_line_number + 1; Int lines_to_remove_count = 0; for (Int i = range.min; i < range.max; i += 1) { - wchar_t c = buffer->data[i]; + char16_t c = buffer->data[i]; if (c == '\n') { lines_to_remove_count += 1; } line_offset -= 1; } RemoveManyByIndex(&ls, lines_to_remove, lines_to_remove_count); - OffsetAllLinesForward(buffer, min_line_number + 1, &line_offset); + RawOffsetAllLinesForward(buffer, min_line_number + 1, &line_offset); } // Update lines add @@ -56,30 +696,30 @@ void UpdateLines(Buffer *buffer, Range range, String16 string) { nl = min_line_number + 1; bool next_line_valid = nl < ls.len; - if (string[i] == L'\n') { + if (string[i] == u'\n') { Int new_line_pos = range.min + i + 1; line_offset += 1; Insert(&ls, new_line_pos, nl); - OffsetAllLinesForward(buffer, nl + 1, &line_offset); + RawOffsetAllLinesForward(buffer, nl + 1, &line_offset); min_line_number = nl; } else if (next_line_valid) { line_offset += 1; } } - OffsetAllLinesForward(buffer, nl, &line_offset); + RawOffsetAllLinesForward(buffer, nl, &line_offset); } -void ValidateLineStarts(Buffer *buffer) { +void RawValidateLineStarts(Buffer *buffer) { Int line = 0; for (Int i = 0; i < buffer->len; i += 1) { Int l = PosToLine(buffer, i); Assert(l == line); - if (buffer->data[i] == L'\n') line += 1; + if (buffer->data[i] == u'\n') line += 1; } } -void IKnowWhatImDoing_ReplaceText(Buffer *buffer, Range range, String16 string) { +void RawReplaceText(Buffer *buffer, Range range, String16 string) { ProfileFunction(); Assert(range.max >= range.min); Assert(range.max >= 0 && range.max <= buffer->len); @@ -91,7 +731,7 @@ void IKnowWhatImDoing_ReplaceText(Buffer *buffer, Range range, String16 string) Int size_to_add = string.len; Int change_size = size_to_add - size_to_remove; Assert(change_size + buffer->len >= 0); - Grow(buffer, change_size); + RawGrow(buffer, change_size); Int range_size = range.max - range.min; U16 *begin_remove = buffer->data + range.min; @@ -107,26 +747,436 @@ void IKnowWhatImDoing_ReplaceText(Buffer *buffer, Range range, String16 string) buffer->len = buffer->len + change_size; #if BUFFER_DEBUG - ValidateLineStarts(buffer); + RawValidateLineStarts(buffer); #endif } -void IKnowWhatImDoing_Append(Buffer *buffer, String16 string) { - IKnowWhatImDoing_ReplaceText(buffer, GetEndAsRange(buffer), string); +void RawAppend(Buffer *buffer, String16 string) { + RawReplaceText(buffer, GetEndAsRange(buffer), string); } -void IKnowWhatImDoing_Append(Buffer *buffer, String string) { +void RawAppend(Buffer *buffer, String string) { Scratch scratch(buffer->line_starts.allocator); - IKnowWhatImDoing_Append(buffer, ToString16(scratch, string)); + RawAppend(buffer, ToString16(scratch, string)); } -void IKnowWhatImDoing_Appendf(Buffer *buffer, const char *fmt, ...) { +void RawAppendf(Buffer *buffer, const char *fmt, ...) { Scratch scratch(buffer->line_starts.allocator); STRING_FORMAT(scratch, fmt, string); String16 string16 = ToString16(scratch, string); - IKnowWhatImDoing_ReplaceText(buffer, GetEndAsRange(buffer), string16); + RawReplaceText(buffer, GetEndAsRange(buffer), string16); } -void IKnowWhatImDoing_Append(Buffer *buffer, Int num) { - IKnowWhatImDoing_Appendf(buffer, "%d", num); +/////////////////////////////// +// multicursor +/////////////////////////////// + +void MergeSort(int64_t Count, Caret *First, Caret *Temp) { + ProfileFunction(); + // SortKey = range.min + if (Count == 1) { + // NOTE(casey): No work to do. + } else if (Count == 2) { + Caret *EntryA = First; + Caret *EntryB = First + 1; + if (EntryA->range.min > EntryB->range.min) { + Swap(EntryA, EntryB); + } + } else { + int64_t Half0 = Count / 2; + int64_t Half1 = Count - Half0; + + Assert(Half0 >= 1); + Assert(Half1 >= 1); + + Caret *InHalf0 = First; + Caret *InHalf1 = First + Half0; + Caret *End = First + Count; + + MergeSort(Half0, InHalf0, Temp); + MergeSort(Half1, InHalf1, Temp); + + Caret *ReadHalf0 = InHalf0; + Caret *ReadHalf1 = InHalf1; + + Caret *Out = Temp; + for (int64_t Index = 0; + Index < Count; + ++Index) { + if (ReadHalf0 == InHalf1) { + *Out++ = *ReadHalf1++; + } else if (ReadHalf1 == End) { + *Out++ = *ReadHalf0++; + } else if (ReadHalf0->range.min < ReadHalf1->range.min) { + *Out++ = *ReadHalf0++; + } else { + *Out++ = *ReadHalf1++; + } + } + Assert(Out == (Temp + Count)); + Assert(ReadHalf0 == InHalf1); + Assert(ReadHalf1 == End); + + // TODO(casey): Not really necessary if we ping-pong + for (int64_t Index = 0; + Index < Count; + ++Index) { + First[Index] = Temp[Index]; + } + } } + +void MergeSort(int64_t Count, Edit *First, Edit *Temp) { + ProfileFunction(); + // SortKey = range.min + if (Count == 1) { + // NOTE(casey): No work to do. + } else if (Count == 2) { + Edit *EntryA = First; + Edit *EntryB = First + 1; + if (EntryA->range.min > EntryB->range.min) { + Swap(EntryA, EntryB); + } + } else { + int64_t Half0 = Count / 2; + int64_t Half1 = Count - Half0; + + Assert(Half0 >= 1); + Assert(Half1 >= 1); + + Edit *InHalf0 = First; + Edit *InHalf1 = First + Half0; + Edit *End = First + Count; + + MergeSort(Half0, InHalf0, Temp); + MergeSort(Half1, InHalf1, Temp); + + Edit *ReadHalf0 = InHalf0; + Edit *ReadHalf1 = InHalf1; + + Edit *Out = Temp; + for (int64_t Index = 0; + Index < Count; + ++Index) { + if (ReadHalf0 == InHalf1) { + *Out++ = *ReadHalf1++; + } else if (ReadHalf1 == End) { + *Out++ = *ReadHalf0++; + } else if (ReadHalf0->range.min < ReadHalf1->range.min) { + *Out++ = *ReadHalf0++; + } else { + *Out++ = *ReadHalf1++; + } + } + Assert(Out == (Temp + Count)); + Assert(ReadHalf0 == InHalf1); + Assert(ReadHalf1 == End); + + // TODO(casey): Not really necessary if we ping-pong + for (int64_t Index = 0; + Index < Count; + ++Index) { + First[Index] = Temp[Index]; + } + } +} + +void ApplyEditsMultiCursor(Buffer *buffer, Array edits) { + ProfileFunction(); +#if BUFFER_DEBUG + Assert(buffer->line_starts.len); + Assert(edits.len); + For(edits) { + Assert(it.range.min >= 0); + Assert(it.range.max >= it.range.min); + Assert(it.range.max <= buffer->len); + } + + // Make sure edit ranges don't overlap + ForItem(it1, edits) { + ForItem(it2, edits) { + if (&it1 == &it2) continue; + + bool a2_inside = it2.range.min >= it1.range.min && it2.range.min < it1.range.max; + Assert(!a2_inside); + + bool b2_inside = it2.range.max > it1.range.min && it2.range.max <= it1.range.max; + Assert(!b2_inside); + } + } +#endif + + // We need to sort from lowest to highest based on range.min + { + Scratch scratch((Arena *)buffer->line_starts.allocator.object); + Array edits_copy = TightCopy(scratch, edits); + if (edits.len > 1) MergeSort(edits.len, edits_copy.data, edits.data); + edits = edits_copy; + } + +#if BUFFER_DEBUG + for (int64_t i = 0; i < edits.len - 1; i += 1) { + Assert(edits[i].range.min <= edits[i + 1].range.min); + } +#endif + + // @optimize: we can do all edits in one go with less memory copies probably + // or something else entirely + Int offset = 0; + For(edits) { + it.range.min += offset; + it.range.max += offset; + offset += it.string.len - GetSize(it.range); + RawReplaceText(buffer, it.range, it.string); + } +} + +void AddEdit(Array *e, Range range, String16 string) { + Add(e, {range, string}); +} + +/////////////////////////////// +// multicursor + history +/////////////////////////////// + +void SaveHistoryBeforeMergeCursor(Buffer *buffer, Array *stack, Array &carets) { + if (buffer->no_history) return; + HistoryEntry entry = {}; + entry.carets = TightCopy(GetSystemAllocator(), carets); + Add(stack, entry); +} + +void SaveHistoryBeforeApplyEdits(Buffer *buffer, Array *stack, Array &edits) { + ProfileFunction(); + if (buffer->no_history) return; + HistoryEntry *entry = GetLast(*stack); + Allocator sys_allocator = GetSystemAllocator(); + entry->edits = TightCopy(sys_allocator, edits); + + // Make reverse edits + For(entry->edits) { + Range new_range = {it.range.min, it.range.min + it.string.len}; + String16 string = GetString(buffer, it.range); + it.string = Copy(sys_allocator, string); + it.range = new_range; + } + + Scratch scratch; + Array temp_edits = TightCopy(scratch, entry->edits); + + // Fix reverse edits + ForItem(edit, edits) { + Int remove_size = GetSize(edit.range); + Int insert_size = edit.string.len; + Int offset = insert_size - remove_size; + + for (Int i = 0; i < entry->edits.len; i += 1) { + Edit &new_edit = entry->edits.data[i]; + Edit &old_edit = temp_edits.data[i]; + if (old_edit.range.min > edit.range.min) { + new_edit.range.min += offset; + new_edit.range.max += offset; + } + } + } +} + +void RedoEdit(Buffer *buffer, Array *carets) { + ProfileFunction(); + if (buffer->no_history) return; + if (buffer->redo_stack.len <= 0) return; + + HistoryEntry entry = Pop(&buffer->redo_stack); + + SaveHistoryBeforeMergeCursor(buffer, &buffer->undo_stack, *carets); + SaveHistoryBeforeApplyEdits(buffer, &buffer->undo_stack, entry.edits); + ApplyEditsMultiCursor(buffer, entry.edits); + + Dealloc(carets); + *carets = entry.carets; + + Allocator sys_allocator = GetSystemAllocator(); + For(entry.edits) Dealloc(sys_allocator, &it.string.data); + Dealloc(&entry.edits); +} + +void UndoEdit(Buffer *buffer, Array *carets) { + ProfileFunction(); + if (buffer->no_history) return; + if (buffer->undo_stack.len <= 0) return; + + HistoryEntry entry = Pop(&buffer->undo_stack); + + SaveHistoryBeforeMergeCursor(buffer, &buffer->redo_stack, *carets); + SaveHistoryBeforeApplyEdits(buffer, &buffer->redo_stack, entry.edits); + ApplyEditsMultiCursor(buffer, entry.edits); + + Dealloc(carets); + *carets = entry.carets; + + Allocator sys_allocator = GetSystemAllocator(); + For(entry.edits) Dealloc(sys_allocator, &it.string.data); + Dealloc(&entry.edits); +} + +void RawApplyEdits(Buffer *buffer, Array &edits) { + ProfileFunction(); + Assert(buffer->edit_phase == 1); + buffer->edit_phase += 1; + SaveHistoryBeforeApplyEdits(buffer, &buffer->undo_stack, edits); + ApplyEditsMultiCursor(buffer, edits); +} + +void DeallocHistoryEntries(Array *entries) { + For(*entries) { + Dealloc(&it.carets); + ForItem(edit, it.edits) Dealloc(it.edits.allocator, &edit.string.data); + Dealloc(&it.edits); + } + entries->len = 0; +} + +void ClearRedoStack(Buffer *buffer) { + DeallocHistoryEntries(&buffer->redo_stack); +} + +void DeallocHistoryArray(Array *entries) { + DeallocHistoryEntries(entries); + Dealloc(entries); +} + +// @note: !! +// We can invoke this before caret altering commands to save caret history +// and then call some editing command to edit which is not going to save carets +Array BeginEdit(Allocator allocator, Buffer *buffer, Array &carets) { + Assert(buffer->edit_phase == 0 || buffer->edit_phase == 1); + if (buffer->edit_phase == 0) { + buffer->edit_phase += 1; + Assert(carets.len); + SaveHistoryBeforeMergeCursor(buffer, &buffer->undo_stack, carets); + ClearRedoStack(buffer); + } + Array result = {allocator}; + return result; +} + +void SaveCaretHistoryBeforeBeginEdit(Buffer *buffer, Array &carets) { + BeginEdit({}, buffer, carets); +} + +void AssertRanges(Array carets) { + For(carets) { + Assert(it.range.max >= it.range.min); + } +} + +void AdjustCarets(Array edits, Array *carets) { + Scratch scratch; + Array new_carets = TightCopy(scratch, *carets); + ForItem(edit, edits) { + Int remove_size = GetSize(edit.range); + Int insert_size = edit.string.len; + Int offset = insert_size - remove_size; + + for (Int i = 0; i < carets->len; i += 1) { + Caret &old_cursor = carets->data[i]; + Caret &new_cursor = new_carets.data[i]; + + if (old_cursor.range.min > edit.range.min) { + new_cursor.range.min += offset; + new_cursor.range.max += offset; + } + } + } + + for (Int i = 0; i < carets->len; i += 1) carets->data[i] = new_carets[i]; +} + +void EndEdit(Buffer *buffer, Array *edits, Array *carets, bool kill_selection) { + ProfileFunction(); + RawApplyEdits(buffer, *edits); + + Assert(buffer->edit_phase == 2); + buffer->edit_phase -= 2; + +#if BUFFER_DEBUG + if (buffer->no_history == false) { + HistoryEntry *entry = GetLast(buffer->undo_stack); + Assert(entry->carets.len); + Assert(entry->edits.len); + for (Int i = 0; i < edits->len - 1; i += 1) { + Assert(edits->data[i].range.min <= edits->data[i + 1].range.min); + } + } +#endif + + // Adjust carets + // this one also moves the carets forward if they are aligned with edit + // + Scratch scratch; + Array new_carets = TightCopy(scratch, *carets); + ForItem(edit, *edits) { + Int remove_size = GetSize(edit.range); + Int insert_size = edit.string.len; + Int offset = insert_size - remove_size; + + for (Int i = 0; i < carets->len; i += 1) { + Caret &old_cursor = carets->data[i]; + Caret &new_cursor = new_carets.data[i]; + + if (old_cursor.range.min == edit.range.min) { + new_cursor.range.min += insert_size; + } else if (old_cursor.range.min > edit.range.min) { + new_cursor.range.min += offset; + } + + if (old_cursor.range.max == edit.range.max) { + new_cursor.range.max += insert_size; + } else if (old_cursor.range.max > edit.range.max) { + new_cursor.range.max += offset; + } + + Assert(new_cursor.range.max >= new_cursor.range.min); + } + } + + for (Int i = 0; i < carets->len; i += 1) { + carets->data[i] = new_carets[i]; + if (kill_selection) { + carets->data[i].range.max = carets->data[i].range.min; + } + } +} + + +// Merge carets that overlap, this needs to be handled before any edits to +// make sure overlapping edits won't happen. +// +// mouse_selection_anchor is special case for mouse handling ! +void MergeCarets(Buffer *buffer, Array *carets) { + ProfileFunction(); + For(*carets) it.range = Clamp(buffer, it.range); + Caret first_caret = carets->data[0]; + + Scratch scratch; + Array c1 = TightCopy(scratch, *carets); + if (carets->len > 1) MergeSort(carets->len, c1.data, carets->data); + carets->len = 0; + + Int first_caret_index = 0; + Add(carets, c1[0]); + for (Int i = 1; i < c1.len; i += 1) { + Caret &it = c1[i]; + Caret *last = GetLast(*carets); + + if (AreOverlapping(*last, it)) { + last->range.max = Max(last->range.max, it.range.max); + } else { + Add(carets, it); + } + + if (AreEqual(it, first_caret)) first_caret_index = carets->len - 1; + } + + Swap(&carets->data[first_caret_index], &carets->data[0]); +} \ No newline at end of file diff --git a/src/text_editor/buffer.h b/src/text_editor/buffer.h new file mode 100644 index 0000000..58b700a --- /dev/null +++ b/src/text_editor/buffer.h @@ -0,0 +1,77 @@ +struct Buffer; +struct BufferID { Int id; Buffer *o; }; +union Range { struct { Int min; Int max; }; Int e[2]; }; +struct Caret { union { Range range; Int pos[2]; }; Int ifront;}; +struct XY { Int col; Int line; }; + +struct Edit { + Range range; + String16 string; +}; + +struct HistoryEntry { + Array edits; + Array carets; +}; + +struct Buffer { + BufferID id; + String name; + Int change_id; + Int user_change_id; + Int file_mod_time; + + union { + U16 *data; + char16_t*str; + }; + Int len; + Int cap; + Array line_starts; + + Array undo_stack; + Array redo_stack; + int edit_phase; + struct { + int no_history : 1; + int no_line_starts : 1; + int dirty : 1; + int is_directory : 1; + int gc : 1; + int changed_on_disk : 1; + }; +}; + +struct FuzzyPair { + Int index; + Int rating; +}; + +enum { + KILL_SELECTION = 1, +}; + +void SaveCaretHistoryBeforeBeginEdit(Buffer *buffer, Array &carets); +Array BeginEdit(Allocator allocator, Buffer *buffer, Array &carets); +void EndEdit(Buffer *buffer, Array *edits, Array *carets, bool kill_selection = true); +void AddEdit(Array *e, Range range, String16 string); + +// Merge carets that overlap, this needs to be handled before any edits to +// make sure overlapping edits won't happen. +// +// mouse_selection_anchor is special case for mouse handling ! +void MergeCarets(Buffer *buffer, Array *carets); + +// was part of: Command_ReplaceWithoutMovingCarets, ReplaceTitleBarData +void AdjustCarets(Array edits, Array *carets); + +void RedoEdit(Buffer *buffer, Array *carets); +void UndoEdit(Buffer *buffer, Array *carets); + +// Raw buffer operations +void RawReplaceText(Buffer *buffer, Range range, String16 string); +void RawAppend(Buffer *buffer, String16 string); +void RawAppend(Buffer *buffer, String string); +void RawAppendf(Buffer *buffer, const char *fmt, ...); + +// @todo: buffer init, deinit \ No newline at end of file diff --git a/src/text_editor/buffer_fuzzy_search.cpp b/src/text_editor/buffer_fuzzy_search.cpp deleted file mode 100644 index bb1dc4a..0000000 --- a/src/text_editor/buffer_fuzzy_search.cpp +++ /dev/null @@ -1,53 +0,0 @@ -struct FuzzyPair { - Int index; - Int rating; -}; - -int64_t FuzzyCloserWordBegin = 5; -int64_t FuzzyConsecutiveMultiplier = 3; -int64_t FuzzyRate(String16 string, String16 with) { - if (with.len == 0) return 0; - int64_t points = 0; - int64_t consecutive = 0; - int64_t with_i = 0; - for (int64_t i = 0; i < string.len; i++) { - if (string.data[i] == with[with_i]) { - int64_t closer_begin = ClampBottom((int64_t)0, FuzzyCloserWordBegin - i); - points += closer_begin; - consecutive++; - with_i += 1; - } else { - points += consecutive * FuzzyConsecutiveMultiplier; - consecutive = 0; - with_i = 0; - } - - if (with_i >= with.len) with_i = 0; - } - points += consecutive * FuzzyConsecutiveMultiplier; - return points; -} - -Array FuzzySearchLines(Allocator allocator, Buffer *buffer, Int line_min, Int line_max, String16 needle) { - if (line_min < 0 || line_min >= buffer->line_starts.len) return {}; - if (line_max < 0 || line_min > buffer->line_starts.len) return {}; - Array ratings = {allocator}; - for (Int i = line_min; i < line_max; i += 1) { - String16 s = GetLineStringWithoutNL(buffer, i); - int64_t rating = FuzzyRate(s, needle); - Add(&ratings, {i, rating}); - } - - // bubble sort - for (int64_t i = 0; i < ratings.len - 1; i++) { - for (int64_t j = 0; j < ratings.len - 1; j++) { - if (ratings[j].rating < ratings[j + 1].rating) { - FuzzyPair temp = ratings[j]; - ratings[j] = ratings[j + 1]; - ratings[j + 1] = temp; - } - } - } - - return ratings; -} diff --git a/src/text_editor/buffer_helpers.cpp b/src/text_editor/buffer_helpers.cpp deleted file mode 100644 index 5c57133..0000000 --- a/src/text_editor/buffer_helpers.cpp +++ /dev/null @@ -1,552 +0,0 @@ -Int GetSize(Range range) { - Assert(range.max >= range.min); - Int result = range.max - range.min; - return result; -} - -Range Rng(Int a, Int b) { - Range result = {Min(a, b), Max(a, b)}; - return result; -} - -Range Rng(Int a = 0) { - Range result = {a, a}; - return result; -} - -String16 GetString(Buffer *buffer, Range range = {0, INT64_MAX}) { - range.min = Clamp(range.min, (Int)0, buffer->len); - range.max = Clamp(range.max, (Int)0, buffer->len); - String16 result = {(wchar_t *)buffer->data + range.min, GetSize(range)}; - return result; -} - -String AllocCharString(Allocator allocator, Buffer *buffer, Range range = {0, INT64_MAX}) { - String16 string16 = GetString(buffer, range); - String result = ToString(allocator, string16); - return result; -} - -Int Clamp(const Buffer *buffer, Int pos) { - Int result = Clamp(pos, (Int)0, buffer->len); - return result; -} - -Range Clamp(const Buffer *buffer, Range range) { - Range result = {}; - result.min = Clamp(buffer, range.min); - result.max = Clamp(buffer, range.max); - return result; -} - -Range GetEndAsRange(Buffer *buffer) { - Range result = {buffer->len, buffer->len}; - return result; -} - -Range GetBeginAsRange(Buffer *buffer) { - Range result = {0, 0}; - return result; -} - -Range GetRange(Buffer *buffer) { - Range result = {0, buffer->len}; - return result; -} - -Int GetFront(Caret caret) { - Int result = caret.pos[caret.ifront]; - return result; -} - -Int GetBack(Caret caret) { - Int index = (caret.ifront + 1) % 2; - Int result = caret.pos[index]; - return result; -} - -Caret MakeCaret(Int pos) { - Caret result = {}; - result.range.min = result.range.max = pos; - return result; -} - -XY XYLine(Int line) { - XY result = {}; - result.line = line; - return result; -} - -wchar_t GetChar(Buffer *buffer, Int pos) { - if (pos >= 0 && pos < buffer->len) return buffer->str[pos]; - return 0; -} - -Caret MakeCaret(Int front, Int back) { - Caret result = {}; - if (front >= back) { - result.range.min = back; - result.range.max = front; - result.ifront = 1; - } else { - result.range.min = front; - result.range.max = back; - result.ifront = 0; - } - return result; -} - -Caret SetBack(Caret caret, Int back) { - Int front = GetFront(caret); - Caret result = MakeCaret(front, back); - return result; -} - -Caret SetFront(Caret caret, Int front) { - Int back = GetBack(caret); - Caret result = MakeCaret(front, back); - return result; -} - -Caret SetFrontWithAnchor(Caret caret, Range anchor, Int p) { - if (anchor.min > p) { - caret = MakeCaret(p, anchor.max); - } else if (anchor.max < p) { - caret = MakeCaret(p, anchor.min); - } else { - caret = MakeCaret(anchor.max, anchor.min); - } - return caret; -} - -bool InBounds(const Buffer *buffer, Int pos) { - bool result = pos >= 0 && pos < buffer->len; - return result; -} - -bool InBounds(Range range, Int pos) { - bool result = pos >= range.min && pos < range.max; - return result; -} - -bool AreEqual(Range a, Range b) { - bool result = a.min == b.min && a.max == b.max; - return result; -} - -bool AreEqual(Caret a, Caret b) { - bool result = AreEqual(a.range, b.range) && a.ifront == b.ifront; - return result; -} - -bool AreOverlapping(Range a, Range b) { - bool r1 = (a.max >= b.min && a.max <= b.max) || (a.min >= b.min && a.min <= b.max); - bool r2 = (b.max >= a.min && b.max <= a.max) || (b.min >= a.min && b.min <= a.max); - return r1 || r2; -} - -bool AreOverlapping(Caret a, Caret b) { - bool result = AreOverlapping(a.range, b.range); - return result; -} - -bool InRange(Int a, Range b) { - bool result = a >= b.min && a < b.max; - return result; -} - -Range operator-(Range a, Int value) { - a.min -= value; - a.max -= value; - return a; -} - -Range operator-=(Range &range, Int value) { - range = range - value; - return range; -} - -Int LastLine(Buffer *buffer) { - Int result = buffer->line_starts.len - 1; - return result; -} - -const Int LAST_LINE = INT64_MAX; -Range GetLineRange(Buffer *buffer, Int line, Int *end_of_buffer = NULL) { - Range result = {buffer->line_starts[line], buffer->len}; - if (line + 1 < buffer->line_starts.len) { - result.max = buffer->line_starts[line + 1]; - } else if (end_of_buffer) { - *end_of_buffer = 1; - } - return result; -} - -Range GetLineRangeWithoutNL(Buffer *buffer, Int line) { - Int end_of_buffer = 0; - Range line_range = GetLineRange(buffer, line, &end_of_buffer); - line_range.max = line_range.max - 1 + end_of_buffer; - return line_range; -} - -String16 GetLineString(Buffer *buffer, Int line, Int *end_of_buffer = NULL) { - Range range = GetLineRange(buffer, line, end_of_buffer); - String16 string = GetString(buffer, range); - return string; -} - -String16 GetLineStringWithoutNL(Buffer *buffer, Int line) { - Range range = GetLineRangeWithoutNL(buffer, line); - String16 string = GetString(buffer, range); - return string; -} - -Int PosToLine(Buffer *buffer, Int pos) { - Add(&buffer->line_starts, buffer->len + 1); - - // binary search - Int low = 0; - - // -2 here because we use 2 indices and combine them into one line range so we - // don't want to access last item since that would index past array. - Int high = buffer->line_starts.len - 2; - Int result = 0; - - while (low <= high) { - Int mid = low + (high - low) / 2; - Range range = {buffer->line_starts[mid], buffer->line_starts[mid + 1]}; - if (pos >= range.min && pos < range.max) { - result = mid; - break; - } - - if (range.min < pos) { - low = mid + 1; - } else { - high = mid - 1; - } - } - - Pop(&buffer->line_starts); - return result; -} - -XY PosToXY(Buffer *buffer, Int pos) { - Int line = PosToLine(buffer, pos); - Range line_range = GetLineRange(buffer, line); - Int col = pos - line_range.min; - XY result = {col, line}; - return result; -} - -Int XYToPos(Buffer *buffer, XY xy) { - xy.line = Clamp(xy.line, (Int)0, buffer->line_starts.len - 1); - Range line_range = GetLineRange(buffer, xy.line); - Int pos = Clamp(xy.col + line_range.min, line_range.min, line_range.max); - return pos; -} - -Int XYToPosWithoutNL(Buffer *buffer, XY xy) { - xy.line = Clamp(xy.line, (Int)0, buffer->line_starts.len - 1); - Int end_of_buffer = 0; - Range line_range = GetLineRange(buffer, xy.line, &end_of_buffer); - Int pos = Clamp(xy.col + line_range.min, line_range.min, line_range.max - 1 + end_of_buffer); - return pos; -} - -Int XYToPosErrorOutOfBounds(Buffer *buffer, XY xy) { - if (xy.line < 0 || xy.line >= buffer->line_starts.len) return -1; - Range line_range = GetLineRange(buffer, xy.line); - Int pos = xy.col + line_range.min; - if (pos < line_range.min || pos > line_range.max) return -1; - return pos; -} - -Int GetWordStart(Buffer *buffer, Int pos) { - pos = Clamp(pos, (Int)0, buffer->len); - for (Int i = pos - 1; i >= 0; i -= 1) { - if (!IsWord(buffer->str[i])) break; - pos = i; - } - return pos; -} - -Int GetWordEnd(Buffer *buffer, Int pos) { - pos = Clamp(pos, (Int)0, buffer->len); - for (Int i = pos;; i += 1) { - pos = i; - // this is because buffer end terminates the loop - // too early and we cannot establish the proper range - // semantics - proper max is one past last index - if (!(i < buffer->len)) break; - if (!IsWord(buffer->str[i])) break; - } - return pos; -} - -Int GetLoadWordStart(Buffer *buffer, Int pos) { - pos = Clamp(pos, (Int)0, buffer->len); - for (Int i = pos - 1; i >= 0; i -= 1) { - if (!IsLoadWord(buffer->str[i])) break; - pos = i; - } - return pos; -} - -Int GetLoadWordEnd(Buffer *buffer, Int pos) { - pos = Clamp(pos, (Int)0, buffer->len); - for (Int i = pos;; i += 1) { - pos = i; - // this is because buffer end terminates the loop - // too early and we cannot establish the proper range - // semantics - proper max is one past last index - if (!(i < buffer->len)) break; - if (!IsLoadWord(buffer->str[i])) break; - } - return pos; -} - -Int GetExecWordStart(Buffer *buffer, Int pos) { - pos = Clamp(pos, (Int)0, buffer->len); - for (Int i = pos - 1; i >= 0; i -= 1) { - if (IsWhitespace(buffer->str[i])) break; - if (GetChar(buffer, i) == L'|' && GetChar(buffer, i + 1) == L'>') break; - pos = i; - } - return pos; -} - -Int GetExecWordEnd(Buffer *buffer, Int pos) { - pos = Clamp(pos, (Int)0, buffer->len); - for (Int i = pos;; i += 1) { - pos = i; - // this is because buffer end terminates the loop - // too early and we cannot establish the proper range - // semantics - proper max is one past last index - if (!(i < buffer->len)) break; - if (IsWhitespace(buffer->str[i])) break; - if (GetChar(buffer, i - 1) == L'<' && GetChar(buffer, i) == L'|') break; - } - return pos; -} - -Int GetNextWordEnd(Buffer *buffer, Int pos) { - pos = Clamp(pos, (Int)0, buffer->len); - wchar_t prev = 0; - for (Int i = pos;; i += 1) { - pos = i; - // this is because buffer end terminates the loop - // too early and we cannot establish the proper range - // semantics - proper max is one past last index - if (!(i < buffer->len)) break; - if (prev == L'\n' || (prev && prev != buffer->str[i]) || IsWord(buffer->str[i])) { - break; - } - prev = buffer->str[i]; - } - Int result = prev == L'\n' ? pos : GetWordEnd(buffer, pos); - return result; -} - -Int GetPrevWordStart(Buffer *buffer, Int pos) { - pos = Clamp(pos, (Int)0, buffer->len); - wchar_t prev = 0; - Int i = pos - 1; - for (; i >= 0; i -= 1) { - if (prev == L'\n' || (prev && prev != buffer->str[i]) || IsWord(buffer->str[i])) { - break; - } - pos = i; - prev = buffer->str[i]; - } - bool new_line = prev == L'\n'; - Int result = new_line ? pos : GetWordStart(buffer, pos); - return result; -} - -Int GetLineStart(Buffer *buffer, Int pos) { - pos = Clamp(pos, (Int)0, buffer->len); - Int line = PosToLine(buffer, pos); - Range range = GetLineRangeWithoutNL(buffer, line); - return range.min; -} - -Int GetLineEnd(Buffer *buffer, Int pos) { - pos = Clamp(pos, (Int)0, buffer->len); - Int line = PosToLine(buffer, pos); - Range range = GetLineRangeWithoutNL(buffer, line); - return range.max; -} - -Int GetFullLineStart(Buffer *buffer, Int pos) { - pos = Clamp(pos, (Int)0, buffer->len); - Int line = PosToLine(buffer, pos); - Range range = GetLineRange(buffer, line); - return range.min; -} - -Int GetFullLineEnd(Buffer *buffer, Int pos) { - pos = Clamp(pos, (Int)0, buffer->len); - Int line = PosToLine(buffer, pos); - Range range = GetLineRange(buffer, line); - return range.max; -} - -Int GetBufferEnd(Buffer *buffer) { - return buffer->len; -} - -Int GetBufferStart(Buffer *buffer) { - return 0; -} - -Int GetNextEmptyLineStart(Buffer *buffer, Int pos) { - Int result = pos; - Int next_line = PosToLine(buffer, pos) + 1; - for (Int line = next_line; line < buffer->line_starts.len; line += 1) { - Range line_range = GetLineRangeWithoutNL(buffer, line); - result = line_range.min; - - bool whitespace_line = true; - for (Int i = line_range.min; i < line_range.max; i += 1) { - if (!IsWhitespace(buffer->str[i])) { - whitespace_line = false; - break; - } - } - if (whitespace_line) break; - } - return result; -} - -Int GetPrevEmptyLineStart(Buffer *buffer, Int pos) { - Int result = pos; - Int next_line = PosToLine(buffer, pos) - 1; - for (Int line = next_line; line >= 0; line -= 1) { - Range line_range = GetLineRangeWithoutNL(buffer, line); - result = line_range.min; - - bool whitespace_line = true; - for (Int i = line_range.min; i < line_range.max; i += 1) { - if (!IsWhitespace(buffer->str[i])) { - whitespace_line = false; - break; - } - } - if (whitespace_line) break; - } - return result; -} - -Range EncloseWord(Buffer *buffer, Int pos) { - Range result = {GetWordStart(buffer, pos), GetWordEnd(buffer, pos)}; - return result; -} - -Range EncloseLoadWord(Buffer *buffer, Int pos) { - Range result = {GetLoadWordStart(buffer, pos), GetLoadWordEnd(buffer, pos)}; - return result; -} - -Int SkipSpaces(Buffer *buffer, Int seek) { - for (; seek < buffer->len; seek += 1) { - wchar_t c = GetChar(buffer, seek); - if (c != L' ') break; - } - return seek; -} - -Int FindScopeEnd(Buffer *buffer, Int seek, Int max_seek, wchar_t open, wchar_t close) { - wchar_t right = GetChar(buffer, seek); - if (right == open) { - int scope = 1; - Int i = seek + 1; - for (; i < seek + max_seek && i < buffer->len; i += 1) { - wchar_t c = GetChar(buffer, i); - - if (open == close && c == '\\') { - i += 1; - } else if (open == close && c == open) { - scope -= 1; - } else if (c == open) { - scope += 1; - } else if (c == close) { - scope -= 1; - } - - if (c == L'\n' || scope == 0) break; - } - - if (scope == 0) seek = i; - } - return seek; -} - -Range EncloseScope(Buffer *buffer, Int pos, wchar_t open, wchar_t close) { - Range result = {pos, pos}; - for (Int i = pos; i >= 0; i -= 1) { - if (buffer->str[i] == open) { - result.min = i; - break; - } - } - for (Int i = pos; i < buffer->len; i += 1) { - if (buffer->str[i] == close) { - result.max = i + 1; - break; - } - } - return result; -} - -Range EncloseLine(Buffer *buffer, Int pos) { - Range result = {GetLineStart(buffer, pos), GetLineEnd(buffer, pos)}; - return result; -} - -Range EncloseFullLine(Buffer *buffer, Int pos) { - Range result = {GetFullLineStart(buffer, pos), GetFullLineEnd(buffer, pos)}; - return result; -} - -Int OffsetByLine(Buffer *buffer, Int pos, Int line_offset) { - XY xy = PosToXY(buffer, pos); - Int result = XYToPosWithoutNL(buffer, {xy.col, xy.line + line_offset}); - return result; -} - -Int GetNextChar(Buffer *buffer, Int pos) { - Int result = Clamp(pos + 1, (Int)0, buffer->len); - return result; -} - -Int GetPrevChar(Buffer *buffer, Int pos) { - Int result = Clamp(pos - 1, (Int)0, buffer->len); - return result; -} - -Int GetLineIndent(Buffer *buffer, Int line) { - String16 string = GetLineStringWithoutNL(buffer, line); - Int indent = 0; - for (Int i = 0; i < string.len; i += 1) { - if (IsWhitespace(string.data[i])) { - indent += 1; - } else { - break; - } - } - return indent; -} - -Int GetIndentAtPos(Buffer *buffer, Int pos) { - Int line = PosToLine(buffer, pos); - Int result = GetLineIndent(buffer, line); - return result; -} - -Range GetIndentRangeAtPos(Buffer *buffer, Int pos) { - Int line = PosToLine(buffer, pos); - Int indent = GetLineIndent(buffer, line); - Range range = GetLineRangeWithoutNL(buffer, line); - return {range.min, range.min + indent}; -} diff --git a/src/text_editor/buffer_history.cpp b/src/text_editor/buffer_history.cpp deleted file mode 100644 index 5822957..0000000 --- a/src/text_editor/buffer_history.cpp +++ /dev/null @@ -1,209 +0,0 @@ -void SaveHistoryBeforeMergeCursor(Buffer *buffer, Array *stack, Array &carets) { - if (buffer->no_history) return; - HistoryEntry entry = {}; - entry.carets = TightCopy(GetSystemAllocator(), carets); - Add(stack, entry); -} - -void SaveHistoryBeforeApplyEdits(Buffer *buffer, Array *stack, Array &edits) { - ProfileFunction(); - if (buffer->no_history) return; - HistoryEntry *entry = GetLast(*stack); - Allocator sys_allocator = GetSystemAllocator(); - entry->edits = TightCopy(sys_allocator, edits); - - // Make reverse edits - For(entry->edits) { - Range new_range = {it.range.min, it.range.min + it.string.len}; - String16 string = GetString(buffer, it.range); - it.string = Copy(sys_allocator, string); - it.range = new_range; - } - - Scratch scratch; - Array temp_edits = TightCopy(scratch, entry->edits); - - // Fix reverse edits - ForItem(edit, edits) { - Int remove_size = GetSize(edit.range); - Int insert_size = edit.string.len; - Int offset = insert_size - remove_size; - - for (Int i = 0; i < entry->edits.len; i += 1) { - Edit &new_edit = entry->edits.data[i]; - Edit &old_edit = temp_edits.data[i]; - if (old_edit.range.min > edit.range.min) { - new_edit.range.min += offset; - new_edit.range.max += offset; - } - } - } -} - -void RedoEdit(Buffer *buffer, Array *carets) { - ProfileFunction(); - if (buffer->no_history) return; - if (buffer->redo_stack.len <= 0) return; - - HistoryEntry entry = Pop(&buffer->redo_stack); - - SaveHistoryBeforeMergeCursor(buffer, &buffer->undo_stack, *carets); - SaveHistoryBeforeApplyEdits(buffer, &buffer->undo_stack, entry.edits); - IKnowWhatImDoing__ApplyEditsMultiCursor(buffer, entry.edits); - - Dealloc(carets); - *carets = entry.carets; - - Allocator sys_allocator = GetSystemAllocator(); - For(entry.edits) Dealloc(sys_allocator, &it.string.data); - Dealloc(&entry.edits); -} - -void UndoEdit(Buffer *buffer, Array *carets) { - ProfileFunction(); - if (buffer->no_history) return; - if (buffer->undo_stack.len <= 0) return; - - HistoryEntry entry = Pop(&buffer->undo_stack); - - SaveHistoryBeforeMergeCursor(buffer, &buffer->redo_stack, *carets); - SaveHistoryBeforeApplyEdits(buffer, &buffer->redo_stack, entry.edits); - IKnowWhatImDoing__ApplyEditsMultiCursor(buffer, entry.edits); - - Dealloc(carets); - *carets = entry.carets; - - Allocator sys_allocator = GetSystemAllocator(); - For(entry.edits) Dealloc(sys_allocator, &it.string.data); - Dealloc(&entry.edits); -} - -void IKnowWhatImDoing_ApplyEdits(Buffer *buffer, Array &edits) { - ProfileFunction(); - Assert(buffer->edit_phase == 1); - buffer->edit_phase += 1; - SaveHistoryBeforeApplyEdits(buffer, &buffer->undo_stack, edits); - IKnowWhatImDoing__ApplyEditsMultiCursor(buffer, edits); -} - -void DeallocHistoryEntries(Array *entries) { - For(*entries) { - Dealloc(&it.carets); - ForItem(edit, it.edits) Dealloc(it.edits.allocator, &edit.string.data); - Dealloc(&it.edits); - } - entries->len = 0; -} - -void ClearRedoStack(Buffer *buffer) { - DeallocHistoryEntries(&buffer->redo_stack); -} - -void DeallocHistoryArray(Array *entries) { - DeallocHistoryEntries(entries); - Dealloc(entries); -} - -// @note: !! -// We can invoke this before caret altering commands to save caret history -// and then call some editing command to edit which is not going to save carets -Array BeginEdit(Allocator allocator, Buffer *buffer, Array &carets) { - Assert(buffer->edit_phase == 0 || buffer->edit_phase == 1); - if (buffer->edit_phase == 0) { - buffer->edit_phase += 1; - Assert(carets.len); - SaveHistoryBeforeMergeCursor(buffer, &buffer->undo_stack, carets); - ClearRedoStack(buffer); - } - Array result = {allocator}; - return result; -} - -void PreBeginEdit_SaveCaretHistory(Buffer *buffer, Array &carets) { - BeginEdit({}, buffer, carets); -} - -void AssertRanges(Array carets) { - For(carets) { - Assert(it.range.max >= it.range.min); - } -} - -void AdjustCarets(Array edits, Array *carets) { - Scratch scratch; - Array new_carets = TightCopy(scratch, *carets); - ForItem(edit, edits) { - Int remove_size = GetSize(edit.range); - Int insert_size = edit.string.len; - Int offset = insert_size - remove_size; - - for (Int i = 0; i < carets->len; i += 1) { - Caret &old_cursor = carets->data[i]; - Caret &new_cursor = new_carets.data[i]; - - if (old_cursor.range.min > edit.range.min) { - new_cursor.range.min += offset; - new_cursor.range.max += offset; - } - } - } - - for (Int i = 0; i < carets->len; i += 1) carets->data[i] = new_carets[i]; -} - -bool KILL_SELECTION = true; -void EndEdit(Buffer *buffer, Array *edits, Array *carets, bool kill_selection = true) { - ProfileFunction(); - IKnowWhatImDoing_ApplyEdits(buffer, *edits); - - Assert(buffer->edit_phase == 2); - buffer->edit_phase -= 2; - -#if BUFFER_DEBUG - if (buffer->no_history == false) { - HistoryEntry *entry = GetLast(buffer->undo_stack); - Assert(entry->carets.len); - Assert(entry->edits.len); - for (Int i = 0; i < edits->len - 1; i += 1) { - Assert(edits->data[i].range.min <= edits->data[i + 1].range.min); - } - } -#endif - - // Adjust carets - // this one also moves the carets forward if they are aligned with edit - // - Scratch scratch; - Array new_carets = TightCopy(scratch, *carets); - ForItem(edit, *edits) { - Int remove_size = GetSize(edit.range); - Int insert_size = edit.string.len; - Int offset = insert_size - remove_size; - - for (Int i = 0; i < carets->len; i += 1) { - Caret &old_cursor = carets->data[i]; - Caret &new_cursor = new_carets.data[i]; - - if (old_cursor.range.min == edit.range.min) { - new_cursor.range.min += insert_size; - } else if (old_cursor.range.min > edit.range.min) { - new_cursor.range.min += offset; - } - - if (old_cursor.range.max == edit.range.max) { - new_cursor.range.max += insert_size; - } else if (old_cursor.range.max > edit.range.max) { - new_cursor.range.max += offset; - } - - Assert(new_cursor.range.max >= new_cursor.range.min); - } - } - - for (Int i = 0; i < carets->len; i += 1) { - carets->data[i] = new_carets[i]; - if (kill_selection) { - carets->data[i].range.max = carets->data[i].range.min; - } - } -} diff --git a/src/text_editor/buffer_multi_cursor.cpp b/src/text_editor/buffer_multi_cursor.cpp deleted file mode 100644 index d5bcfc8..0000000 --- a/src/text_editor/buffer_multi_cursor.cpp +++ /dev/null @@ -1,163 +0,0 @@ -void MergeSort(int64_t Count, Caret *First, Caret *Temp) { - ProfileFunction(); - // SortKey = range.min - if (Count == 1) { - // NOTE(casey): No work to do. - } else if (Count == 2) { - Caret *EntryA = First; - Caret *EntryB = First + 1; - if (EntryA->range.min > EntryB->range.min) { - Swap(EntryA, EntryB); - } - } else { - int64_t Half0 = Count / 2; - int64_t Half1 = Count - Half0; - - Assert(Half0 >= 1); - Assert(Half1 >= 1); - - Caret *InHalf0 = First; - Caret *InHalf1 = First + Half0; - Caret *End = First + Count; - - MergeSort(Half0, InHalf0, Temp); - MergeSort(Half1, InHalf1, Temp); - - Caret *ReadHalf0 = InHalf0; - Caret *ReadHalf1 = InHalf1; - - Caret *Out = Temp; - for (int64_t Index = 0; - Index < Count; - ++Index) { - if (ReadHalf0 == InHalf1) { - *Out++ = *ReadHalf1++; - } else if (ReadHalf1 == End) { - *Out++ = *ReadHalf0++; - } else if (ReadHalf0->range.min < ReadHalf1->range.min) { - *Out++ = *ReadHalf0++; - } else { - *Out++ = *ReadHalf1++; - } - } - Assert(Out == (Temp + Count)); - Assert(ReadHalf0 == InHalf1); - Assert(ReadHalf1 == End); - - // TODO(casey): Not really necessary if we ping-pong - for (int64_t Index = 0; - Index < Count; - ++Index) { - First[Index] = Temp[Index]; - } - } -} - -void MergeSort(int64_t Count, Edit *First, Edit *Temp) { - ProfileFunction(); - // SortKey = range.min - if (Count == 1) { - // NOTE(casey): No work to do. - } else if (Count == 2) { - Edit *EntryA = First; - Edit *EntryB = First + 1; - if (EntryA->range.min > EntryB->range.min) { - Swap(EntryA, EntryB); - } - } else { - int64_t Half0 = Count / 2; - int64_t Half1 = Count - Half0; - - Assert(Half0 >= 1); - Assert(Half1 >= 1); - - Edit *InHalf0 = First; - Edit *InHalf1 = First + Half0; - Edit *End = First + Count; - - MergeSort(Half0, InHalf0, Temp); - MergeSort(Half1, InHalf1, Temp); - - Edit *ReadHalf0 = InHalf0; - Edit *ReadHalf1 = InHalf1; - - Edit *Out = Temp; - for (int64_t Index = 0; - Index < Count; - ++Index) { - if (ReadHalf0 == InHalf1) { - *Out++ = *ReadHalf1++; - } else if (ReadHalf1 == End) { - *Out++ = *ReadHalf0++; - } else if (ReadHalf0->range.min < ReadHalf1->range.min) { - *Out++ = *ReadHalf0++; - } else { - *Out++ = *ReadHalf1++; - } - } - Assert(Out == (Temp + Count)); - Assert(ReadHalf0 == InHalf1); - Assert(ReadHalf1 == End); - - // TODO(casey): Not really necessary if we ping-pong - for (int64_t Index = 0; - Index < Count; - ++Index) { - First[Index] = Temp[Index]; - } - } -} - -void IKnowWhatImDoing__ApplyEditsMultiCursor(Buffer *buffer, Array edits) { - ProfileFunction(); -#if BUFFER_DEBUG - Assert(buffer->line_starts.len); - Assert(edits.len); - For(edits) { - Assert(it.range.min >= 0); - Assert(it.range.max >= it.range.min); - Assert(it.range.max <= buffer->len); - } - - // Make sure edit ranges don't overlap - ForItem(it1, edits) { - ForItem(it2, edits) { - if (&it1 == &it2) continue; - - bool a2_inside = it2.range.min >= it1.range.min && it2.range.min < it1.range.max; - Assert(!a2_inside); - - bool b2_inside = it2.range.max > it1.range.min && it2.range.max <= it1.range.max; - Assert(!b2_inside); - } - } -#endif - - // We need to sort from lowest to highest based on range.min - { - Scratch scratch((Arena *)buffer->line_starts.allocator.object); - Array edits_copy = TightCopy(scratch, edits); - if (edits.len > 1) MergeSort(edits.len, edits_copy.data, edits.data); - edits = edits_copy; - } - -#if BUFFER_DEBUG - for (int64_t i = 0; i < edits.len - 1; i += 1) { - Assert(edits[i].range.min <= edits[i + 1].range.min); - } -#endif - - // @optimize: we can do all edits in one go with less memory copies probably - // or something else entirely - Int offset = 0; - For(edits) { - it.range.min += offset; - it.range.max += offset; - offset += it.string.len - GetSize(it.range); - IKnowWhatImDoing_ReplaceText(buffer, it.range, it.string); - } -} - -void AddEdit(Array *e, Range range, String16 string) { - Add(e, {range, string}); -} diff --git a/src/text_editor/buffer_test_load.cpp b/src/text_editor/buffer_test_load.cpp index ea8fa83..275dc90 100644 --- a/src/text_editor/buffer_test_load.cpp +++ b/src/text_editor/buffer_test_load.cpp @@ -1,12 +1,12 @@ void LoadBigText(Buffer *buffer, int size = 5000000) { for (int i = 0; i < size; i += 1) { - IKnowWhatImDoing_ReplaceText(buffer, GetEndAsRange(buffer), L"Line number or something of the sort which is here or there or maybe somewhere else\n"); + RawReplaceText(buffer, GetEndAsRange(buffer), u"Line number or something of the sort which is here or there or maybe somewhere else\n"); } } void LoadBigLine(Buffer *buffer, int size = 5000000) { for (int i = 0; i < size; i += 1) { - IKnowWhatImDoing_ReplaceText(buffer, GetEndAsRange(buffer), L"Line number or something of the sort which is here or there or maybe somewhere else | "); + RawReplaceText(buffer, GetEndAsRange(buffer), u"Line number or something of the sort which is here or there or maybe somewhere else | "); } } @@ -20,14 +20,14 @@ void LoadTextA(Buffer *buffer) { for (int i = 0; i < 1000; i += 1) { String s = Format(scratch, "line1: %d line2: %d line3: %d line4: %d line5: %d line6: %d line1: %d line2: %d line3: %d line4: %d line5: %d line6: %d\r\n", i, i, i, i, i, i, i, i, i, i, i, i); String16 s16 = ToString16(scratch, s); - IKnowWhatImDoing_ReplaceText(buffer, GetEndAsRange(buffer), s16); + RawReplaceText(buffer, GetEndAsRange(buffer), s16); } } void LoadLine(Buffer *buffer) { Scratch scratch; String s = "Line number and so on óźćż"; - IKnowWhatImDoing_ReplaceText(buffer, {}, ToString16(scratch, s)); + RawReplaceText(buffer, {}, ToString16(scratch, s)); } void LoadUnicode(Buffer *buffer) { @@ -38,26 +38,13 @@ See also Unicode 3.2 test page. commit 225d1ffc067da0723898ade68fb9492bbe308feb https://www.lua.org/manual/5.4/ -C:\dev\projects\text_editor\src\text_editor\buffer_history.cpp -C:/dev/projects/text_editor/src/text_editor/buffer_history.cpp +D:/dev/ +D:\dev\ )==="; Scratch scratch; - IKnowWhatImDoing_ReplaceText(buffer, {}, ToString16(scratch, text)); + RawReplaceText(buffer, {}, ToString16(scratch, text)); - text = R"===( - Hangul Compatibility Jamo -ㄱ ㄲ ㄳ ㄴ ㄵ ㄶ ㄷ ㄸ ㄹ ㄺ ㄻ ㄼ ㄽ ㄾ ㄿ ㅀ ㅁ ㅂ ㅃ ㅄ ㅅ ㅆ ㅇ ㅈ ㅉ ㅊ ㅋ ㅌ ㅍ ㅎ ㅏ ㅐ ㅑ ㅒ ㅓ ㅔ ㅕ ㅖ ㅗ ㅘ ㅙ ㅚ ㅛ ㅜ ㅝ ㅞ ㅟ ㅠ ㅡ ㅢ ㅣ ㅤ ㅥ ㅦ ㅧ ㅨ ㅩ ㅪ ㅫ ㅬ ㅭ ㅮ ㅯ ㅰ ㅱ ㅲ ㅳ ㅴ ㅵ ㅶ ㅷ ㅸ ㅹ ㅺ ㅻ ㅼ ㅽ ㅾ ㅿ ㆀ ㆁ ㆂ ㆃ ㆄ ㆅ ㆆ ㆇ ㆈ ㆉ ㆊ ㆋ ㆌ ㆍ ㆎ -Kanbun -㆐ ㆑ ㆒ ㆓ ㆔ ㆕ ㆖ ㆗ ㆘ ㆙ ㆚ ㆛ ㆜ ㆝ ㆞ ㆟ -Enclosed CJK Letters and Months -㈀ ㈁ ㈂ ㈃ ㈄ ㈅ ㈆ ㈇ ㈈ ㈉ ㈊ ㈋ ㈌ ㈍ ㈎ ㈏ ㈐ ㈑ ㈒ ㈓ ㈔ ㈕ ㈖ ㈗ ㈘ ㈙ ㈚ ㈛ ㈜ ㈠ ㈡ ㈢ ㈣ ㈤ ㈥ ㈦ ㈧ ㈨ ㈩ ㈪ ㈫ ㈬ ㈭ ㈮ ㈯ ㈰ ㈱ ㈲ ㈳ ㈴ ㈵ ㈶ ㈷ ㈸ ㈹ ㈺ ㈻ ㈼ ㈽ ㈾ ㈿ ㉀ ㉁ ㉂ ㉃ ㉠ ㉡ ㉢ ㉣ ㉤ ㉥ ㉦ ㉧ ㉨ ㉩ ㉪ ㉫ ㉬ ㉭ ㉮ ㉯ ㉰ ㉱ ㉲ ㉳ ㉴ ㉵ ㉶ ㉷ ㉸ ㉹ ㉺ ㉻ ㉿ ㊀ ㊁ ㊂ ㊃ ㊄ ㊅ ㊆ ㊇ ㊈ ㊉ ㊊ ㊋ ㊌ ㊍ ㊎ ㊏ ㊐ ㊑ ㊒ ㊓ ㊔ ㊕ ㊖ ㊗ ㊘ ㊙ ㊚ ㊛ ㊜ ㊝ ㊞ ㊟ ㊠ ㊡ ... -CJK Compatibility -㌀ ㌁ ㌂ ㌃ ㌄ ㌅ ㌆ ㌇ ㌈ ㌉ ㌊ ㌋ ㌌ ㌍ ㌎ ㌏ ㌐ ㌑ ㌒ ㌓ ㌔ ㌕ ㌖ ㌗ ㌘ ㌙ ㌚ ㌛ ㌜ ㌝ ㌞ ㌟ ㌠ ㌡ ㌢ ㌣ ㌤ ㌥ ㌦ ㌧ ㌨ ㌩ ㌪ ㌫ ㌬ ㌭ ㌮ ㌯ ㌰ ㌱ ㌲ ㌳ ㌴ ㌵ ㌶ ㌷ ㌸ ㌹ ㌺ ㌻ ㌼ ㌽ ㌾ ㌿ ㍀ ㍁ ㍂ ㍃ ㍄ ㍅ ㍆ ㍇ ㍈ ㍉ ㍊ ㍋ ㍌ ㍍ ㍎ ㍏ ㍐ ㍑ ㍒ ㍓ ㍔ ㍕ ㍖ ㍗ ㍘ ㍙ ㍚ ㍛ ㍜ ㍝ ㍞ ㍟ ㍠ ㍡ ㍢ ㍣ ㍤ ㍥ ㍦ ㍧ ㍨ ㍩ ㍪ ㍫ ㍬ ㍭ ㍮ ㍯ ㍰ ㍱ ㍲ ㍳ ㍴ ㍵ ㍶ ㍻ ㍼ ㍽ ㍾ ㍿ ㎀ ㎁ ㎂ ㎃ ... -CJK Unified Ideographs -一 丁 丂 七 丄 丅 丆 万 丈 三 上 下 丌 不 与 丏 丐 丑 丒 专 且 丕 世 丗 丘 丙 业 丛 东 丝 丞 丟 丠 両 丢 丣 两 严 並 丧 丨 丩 个 丫 丬 中 丮 丯 丰 丱 串 丳 临 丵 丶 丷 丸 丹 为 主 丼 丽 举 丿 乀 乁 乂 乃 乄 久 乆 乇 么 义 乊 之 乌 乍 乎 乏 乐 乑 乒 乓 乔 乕 乖 乗 乘 乙 乚 乛 乜 九 乞 也 习 乡 乢 乣 乤 乥 书 乧 乨 乩 乪 乫 乬 乭 乮 乯 买 乱 乲 乳 乴 乵 乶 乷 乸 乹 乺 乻 乼 乽 乾 乿 ... -Hangul Syllables -가 각 갂 갃 간 갅 갆 갇 갈 갉 갊 갋 갌 갍 갎 갏 감 갑 값 갓 갔 강 갖 갗 갘 같 갚 갛 개 객 갞 갟 갠 갡 갢 갣 갤 갥 갦 갧 갨 갩 갪 갫 갬 갭 갮 갯 갰 갱 갲 갳 갴 갵 갶 갷 갸 갹 갺 갻 갼 갽 갾 갿 걀 걁 걂 걃 걄 걅 걆 걇 걈 걉 걊 걋 걌 걍 걎 걏 걐 걑 걒 걓 걔 걕 걖 걗 걘 걙 걚 걛 걜 걝 걞 걟 걠 걡 걢 걣 걤 걥 걦 걧 걨 걩 걪 걫 걬 걭 걮 걯 거 걱 걲 걳 건 걵 걶 걷 걸 걹 걺 걻 걼 걽 걾 걿 ...)==="; - IKnowWhatImDoing_ReplaceText(buffer, GetEndAsRange(buffer), ToString16(scratch, text)); + // RawReplaceText(buffer, GetEndAsRange(buffer), ToString16(scratch, text)); } \ No newline at end of file diff --git a/src/text_editor/commands.cpp b/src/text_editor/commands.cpp index 8f1dc4a..dceb510 100644 --- a/src/text_editor/commands.cpp +++ b/src/text_editor/commands.cpp @@ -17,10 +17,10 @@ void ToggleFullscreen() { void CheckpointBeforeGoto(Window *window, View *view) { Buffer *buffer = GetBuffer(view->active_buffer); - if (buffer->gc == false) { + // if (buffer->gc == false) { Add(&window->goto_history, {buffer->id, view->carets[0]}); window->goto_redo.len = 0; - } + // } } void CheckpointBeforeGoto(Window *window) { @@ -152,7 +152,7 @@ void Command_Append(View *view, String16 string, bool scroll_to_end_if_cursor_on Command_SelectRangeOneCursor(view, GetEndAsRange(buffer)); Command_Replace(view, string); - Command_Replace(view, L"\n"); + Command_Replace(view, u"\n"); if (scroll_to_end) { view->carets[0] = MakeCaret(GetEndAsRange(buffer).min); @@ -387,7 +387,7 @@ void Command_MoveLine(View *view, int direction) { Buffer *buffer = GetBuffer(view->active_buffer); Array edits = BeginEdit(scratch, buffer, view->carets); - MergeCarets(view); + MergeCarets(buffer, &view->carets); Array saved_xy = {scratch}; For(view->carets) { Add(&saved_xy, {PosToXY(buffer, GetFront(it)), PosToXY(buffer, GetBack(it))}); @@ -427,7 +427,7 @@ void Command_MoveLine(View *view, int direction) { Array Command_ReplaceEx(Allocator scratch, View *view, String16 string) { Buffer *buffer = GetBuffer(view->active_buffer); Array edits = BeginEdit(scratch, buffer, view->carets); - MergeCarets(view); + MergeCarets(buffer, &view->carets); For(view->carets) AddEdit(&edits, it.range, string); EndEdit(buffer, &edits, &view->carets); return edits; @@ -445,7 +445,7 @@ void Command_DuplicateLine(View *view, int direction) { Buffer *buffer = GetBuffer(view->active_buffer); BeginEdit(scratch, buffer, view->carets); For(view->carets) it = MakeCaret(GetFront(it)); - MergeCarets(view); + MergeCarets(buffer, &view->carets); Array edits = {scratch}; For(view->carets) { @@ -460,30 +460,6 @@ void Command_DuplicateLine(View *view, int direction) { Command_Move(view, direction); } -Int FindRangeByPos(Array &ranges, Int pos) { - // binary search - Int low = 0; - Int high = ranges.len - 1; - Int result = -1; - - while (low <= high) { - Int mid = low + (high - low) / 2; - Range range = ranges[mid]; - if (pos >= range.min && pos < range.max) { - result = mid; - break; - } - - if (range.min < pos) { - low = mid + 1; - } else { - high = mid - 1; - } - } - - return result; -} - Array GetSelectedLinesSorted(Allocator allocator, View *view) { Scratch scratch(allocator); Buffer *buffer = GetBuffer(view->active_buffer); @@ -517,7 +493,7 @@ void Command_IndentSelectedLines(View *view, bool shift = false) { Buffer *buffer = GetBuffer(view->active_buffer); Array edits = BeginEdit(scratch, buffer, view->carets); - MergeCarets(view); + MergeCarets(buffer, &view->carets); Array line_ranges_to_indent = GetSelectedLinesSorted(scratch, view); For(line_ranges_to_indent) { @@ -525,7 +501,7 @@ void Command_IndentSelectedLines(View *view, bool shift = false) { Range pos_range_of_line = GetLineRange(buffer, i); if (!shift) { - String16 whitespace_string = {L" ", StyleIndentSize}; + String16 whitespace_string = {u" ", StyleIndentSize}; AddEdit(&edits, {pos_range_of_line.min, pos_range_of_line.min}, whitespace_string); } else { String16 string = GetString(buffer, pos_range_of_line); @@ -534,7 +510,7 @@ void Command_IndentSelectedLines(View *view, bool shift = false) { whitespace_len += 1; } - AddEdit(&edits, {pos_range_of_line.min, pos_range_of_line.min + whitespace_len}, L""); + AddEdit(&edits, {pos_range_of_line.min, pos_range_of_line.min + whitespace_len}, u""); } } } @@ -547,13 +523,13 @@ void Command_TrimTrailingWhitespace(View *view, bool dont_trim_lines_with_cursor Buffer *buffer = GetBuffer(view->active_buffer); Array edits = BeginEdit(scratch, buffer, view->carets); - MergeCarets(view); + MergeCarets(buffer, &view->carets); Array lines_to_skip_triming = {}; if (dont_trim_lines_with_cursor) lines_to_skip_triming = GetSelectedLinesSorted(scratch, view); for (Int i = 0; i < buffer->line_starts.len; i += 1) { - Int range_index = FindRangeByPos(lines_to_skip_triming, i); + Int range_index = FindRangeByPos(&lines_to_skip_triming, i); if (range_index != -1) continue; Range range = GetLineRangeWithoutNL(buffer, i); @@ -565,7 +541,7 @@ void Command_TrimTrailingWhitespace(View *view, bool dont_trim_lines_with_cursor } Range whitespace_range = {whitespace_end, range.max}; - AddEdit(&edits, whitespace_range, L""); + AddEdit(&edits, whitespace_range, u""); } EndEdit(buffer, &edits, &view->carets, !KILL_SELECTION); @@ -577,20 +553,20 @@ void Command_ConvertLineEndings(View *view, bool dont_trim_lines_with_cursor) { Buffer *buffer = GetBuffer(view->active_buffer); Array edits = BeginEdit(scratch, buffer, view->carets); - MergeCarets(view); + MergeCarets(buffer, &view->carets); Array lines_to_skip_triming = {}; if (dont_trim_lines_with_cursor) lines_to_skip_triming = GetSelectedLinesSorted(scratch, view); for (Int i = 0; i < buffer->line_starts.len; i += 1) { - Int range_index = FindRangeByPos(lines_to_skip_triming, i); + Int range_index = FindRangeByPos(&lines_to_skip_triming, i); if (range_index != -1) continue; Range range = GetLineRangeWithoutNL(buffer, i); - wchar_t cr = GetChar(buffer, range.max - 1); - wchar_t lf = GetChar(buffer, range.max); - if (cr == L'\r' && lf == L'\n') { - AddEdit(&edits, {range.max - 1, range.max}, L""); + char16_t cr = GetChar(buffer, range.max - 1); + char16_t lf = GetChar(buffer, range.max); + if (cr == u'\r' && lf == u'\n') { + AddEdit(&edits, {range.max - 1, range.max}, u""); } } EndEdit(buffer, &edits, &view->carets, !KILL_SELECTION); @@ -636,7 +612,7 @@ void Command_KillSelectedLines(View *view) { Buffer *buffer = GetBuffer(view->active_buffer); Array edits = BeginEdit(scratch, buffer, view->carets); - MergeCarets(view); + MergeCarets(buffer, &view->carets); Array lines = GetSelectedLinesSorted(scratch, view); For(lines) { @@ -646,7 +622,7 @@ void Command_KillSelectedLines(View *view) { Range line_range = GetLineRange(buffer, i); add_range.max = Max(line_range.max, add_range.max); } - AddEdit(&edits, add_range, L""); + AddEdit(&edits, add_range, u""); } For(view->carets) { @@ -689,7 +665,7 @@ void Command_Delete(View *view, int direction, bool ctrl = false) { } } - MergeCarets(view); + MergeCarets(buffer, &view->carets); For(view->carets) AddEdit(&edits, it.range, {}); EndEdit(buffer, &edits, &view->carets); } @@ -708,19 +684,18 @@ void Command_SelectAll(View *view, String16 needle) { Add(&view->carets, MakeCaret(pos + index + needle.len, pos + index)); pos += needle.len; } - MergeCarets(view); + MergeCarets(buffer, &view->carets); } -void Command_CreateCursorVertical(View *_view, int direction) { +void Command_CreateCursorVertical(View *view, int direction) { Assert(direction == DIR_UP || direction == DIR_DOWN); - View &view = *_view; - Buffer *buffer = GetBuffer(view.active_buffer); + Buffer *buffer = GetBuffer(view->active_buffer); Int line_offset = direction == DIR_UP ? -1 : 1; Scratch scratch; Array arr = {scratch}; - For(view.carets) { + For(view->carets) { if (PosToLine(buffer, it.range.min) == PosToLine(buffer, it.range.max)) { Int f = OffsetByLine(buffer, GetFront(it), line_offset); Int b = OffsetByLine(buffer, GetBack(it), line_offset); @@ -732,8 +707,8 @@ void Command_CreateCursorVertical(View *_view, int direction) { Add(&arr, caret); } } - For(arr) Add(&view.carets, it); - MergeCarets(_view); + For(arr) Add(&view->carets, it); + MergeCarets(buffer, &view->carets); } void Command_SelectRangeOneCursor(View *view, Caret caret) { @@ -750,41 +725,6 @@ void Command_SelectEntireBuffer(View *view) { Command_SelectRangeOneCursor(view, GetRange(buffer)); } -// Merge carets that overlap, this needs to be handled before any edits to -// make sure overlapping edits won't happen. -// -// mouse_selection_anchor is special case for mouse handling ! - -void MergeCarets(View *view) { - ProfileFunction(); - - Buffer *buffer = GetBuffer(view->active_buffer); - For(view->carets) it.range = Clamp(buffer, it.range); - Caret first_caret = view->carets.data[0]; - - Scratch scratch; - Array c1 = TightCopy(scratch, view->carets); - if (view->carets.len > 1) MergeSort(view->carets.len, c1.data, view->carets.data); - view->carets.len = 0; - - Int first_caret_index = 0; - Add(&view->carets, c1[0]); - for (Int i = 1; i < c1.len; i += 1) { - Caret &it = c1[i]; - Caret *last = GetLast(view->carets); - - if (AreOverlapping(*last, it)) { - last->range.max = Max(last->range.max, it.range.max); - } else { - Add(&view->carets, it); - } - - if (AreEqual(it, first_caret)) first_caret_index = view->carets.len - 1; - } - - Swap(&view->carets[first_caret_index], &view->carets[0]); -} - Caret FindPrev(Buffer *buffer, String16 needle, Caret caret) { Int pos = GetFront(caret); String16 medium = GetString(buffer, {0, pos}); @@ -825,7 +765,7 @@ void Command_IdentedNewLine(View *view) { Buffer *buffer = GetBuffer(view->active_buffer); Scratch scratch; Array edits = BeginEdit(scratch, buffer, view->carets); - MergeCarets(view); + MergeCarets(buffer, &view->carets); For(view->carets) { Int front = GetFront(it); Int line = PosToLine(buffer, front); @@ -865,7 +805,7 @@ void Command_GotoNextInList(Window *window, Int line_offset = 1) { view_goto->carets[0] = MakeCaret(line_range.min); line = Trim(line); - MergeCarets(view_goto); + MergeCarets(buffer_goto, &view_goto->carets); IF_DEBUG(AssertRanges(view_goto->carets)); if (line.len == 0) continue; @@ -888,8 +828,8 @@ void Command_FuzzySort(View *view, String16 needle) { For(ratings) { String16 s = GetLineStringWithoutNL(buffer, it.index); if (s.len == 0) continue; - IKnowWhatImDoing_ReplaceText(temp_buffer, GetEndAsRange(temp_buffer), s); - IKnowWhatImDoing_ReplaceText(temp_buffer, GetEndAsRange(temp_buffer), L"\n"); + RawReplaceText(temp_buffer, GetEndAsRange(temp_buffer), s); + RawReplaceText(temp_buffer, GetEndAsRange(temp_buffer), u"\n"); } Command_SelectEntireBuffer(view); diff --git a/src/text_editor/commands_bindings.cpp b/src/text_editor/commands_bindings.cpp index e75af79..8741666 100644 --- a/src/text_editor/commands_bindings.cpp +++ b/src/text_editor/commands_bindings.cpp @@ -119,11 +119,18 @@ void OnCommand(Event event) { } } - if (Ctrl() && Shift() && Mouse(RIGHT)) { + if (Mouse(X2)) { GotoForward(GetActiveMainSet().window); + } + if (Mouse(X1)) { + GotoBackward(GetActiveMainSet().window); + } + + if (Ctrl() && Shift() && Mouse(RIGHT)) { + } else if (Alt() && Ctrl() && Mouse(RIGHT)) { } else if (Ctrl() && Mouse(RIGHT)) { - GotoBackward(GetActiveMainSet().window); + } else if (Alt() && Mouse(RIGHT)) { } else if (Mouse(RIGHT)) { Vec2I mouse = MouseVec2I(); @@ -187,12 +194,14 @@ void OnCommand(Event event) { } } else if (event.clicks >= 2 && InBounds({caret.range.min - 1, caret.range.max + 1}, p)) { Range range = EncloseWord(active.buffer, p); - if (event.clicks >= 3) range = EncloseLoadWord(active.buffer, p); + if (event.clicks >= 3) { + range = EncloseFullLine(active.buffer, p); + } caret = MakeCaret(range.max, range.min); } else { caret = MakeCaret(p); } - MergeCarets(active.view); + MergeCarets(active.buffer, &active.view->carets); DocumentRangeAnchor = caret.range; } } @@ -332,9 +341,9 @@ void OnCommand(Event event) { } else if (CtrlPress(SDLK_V)) { Command_Paste(active.view); } else if (CtrlPress(SDLK_X)) { - PreBeginEdit_SaveCaretHistory(active.buffer, active.view->carets); + SaveCaretHistoryBeforeBeginEdit(active.buffer, active.view->carets); Command_Copy(active.view); - Command_Replace(active.view, L""); + Command_Replace(active.view, u""); } if (CtrlPress(SDLK_A)) { @@ -366,8 +375,12 @@ void OnCommand(Event event) { Command_MoveCursorsToSide(active.view, DIR_RIGHT); } - if (ShiftPress(SDLK_TAB)) { + if (CtrlShiftPress(SDLK_TAB)) { + GotoForward(GetActiveMainSet().window); + } else if (ShiftPress(SDLK_TAB)) { Command_IndentSelectedLines(active.view, SHIFT_PRESSED); + } else if (CtrlPress(SDLK_TAB)) { + GotoBackward(GetActiveMainSet().window); } else if (Press(SDLK_TAB)) { Command_IndentSelectedLines(active.view); } @@ -399,7 +412,7 @@ void OnCommand(Event event) { String16 string = GetString(active.buffer, active.view->carets[0].range); Caret caret = FindNext(active.buffer, string, active.view->carets[0]); Insert(&active.view->carets, caret, 0); - MergeCarets(active.view); + MergeCarets(active.buffer, &active.view->carets); } if (ShiftPress(SDLK_F3)) { @@ -456,11 +469,11 @@ void OnCommand(Event event) { } - if (CtrlShiftPress(SDLK_W)) { - GotoForward(GetActiveMainSet().window); - } else if (CtrlPress(SDLK_W)) { - GotoBackward(GetActiveMainSet().window); - } + // if (CtrlShiftPress(SDLK_W)) { + // GotoForward(GetActiveMainSet().window); + // } else if (CtrlPress(SDLK_W)) { + // GotoBackward(GetActiveMainSet().window); + // } if (CtrlPress(SDLK_Q)) { Caret caret = active.view->carets[0]; @@ -480,7 +493,7 @@ void OnCommand(Event event) { } } - MergeCarets(active.view); + MergeCarets(active.buffer, &active.view->carets); IF_DEBUG(AssertRanges(active.view->carets)); } diff --git a/src/text_editor/commands_clipboard.cpp b/src/text_editor/commands_clipboard.cpp index 3a86d30..1d16e9d 100644 --- a/src/text_editor/commands_clipboard.cpp +++ b/src/text_editor/commands_clipboard.cpp @@ -49,7 +49,7 @@ void Command_Copy(View *view) { } // @todo: maybe only add new line if there is no new line at the end, experiment with it - String16 final_string = Merge(sys_allocator, SavedClipboardCarets, L"\n"); + String16 final_string = Merge(sys_allocator, SavedClipboardCarets, u"\n"); MakeSureToUseSystemAllocator_SaveInClipboard(final_string, caret_strings); } @@ -64,7 +64,7 @@ void Command_Paste(View *view) { // Regular paste if (string != SavedClipboardString || SavedClipboardCarets.len != view->carets.len) { Array edits = BeginEdit(scratch, buffer, view->carets); - MergeCarets(view); + MergeCarets(buffer, &view->carets); For(view->carets) AddEdit(&edits, it.range, string); EndEdit(buffer, &edits, &view->carets); return; @@ -72,7 +72,7 @@ void Command_Paste(View *view) { // Multicursor paste Array edits = BeginEdit(scratch, buffer, view->carets); - MergeCarets(view); + MergeCarets(buffer, &view->carets); for (int64_t i = 0; i < view->carets.len; i += 1) { String16 string = SavedClipboardCarets[i]; Caret &it = view->carets[i]; diff --git a/src/text_editor/event.cpp b/src/text_editor/event.cpp index a5c3eb4..c9ee923 100644 --- a/src/text_editor/event.cpp +++ b/src/text_editor/event.cpp @@ -7,9 +7,13 @@ enum { EVENT_MOUSE_LEFT, EVENT_MOUSE_RIGHT, EVENT_MOUSE_MIDDLE, + EVENT_MOUSE_X1, + EVENT_MOUSE_X2, EVENT_MOUSE_LEFT_UP, EVENT_MOUSE_RIGHT_UP, EVENT_MOUSE_MIDDLE_UP, + EVENT_MOUSE_X1_UP, + EVENT_MOUSE_X2_UP, EVENT_MOUSE_WHEEL, EVENT_KEY_PRESS, diff --git a/src/text_editor/generated.cpp b/src/text_editor/generated.cpp index edc0329..329fb46 100644 --- a/src/text_editor/generated.cpp +++ b/src/text_editor/generated.cpp @@ -76,6 +76,7 @@ SDLK_RIGHT = 1073741903 SDLK_LEFT = 1073741904 SDLK_Q = 113 SDLK_BACKSLASH = 0x5c +SDLK_RETURN = 13 SDLK_F1 = 0x4000003a SDLK_F2 = 0x4000003b @@ -283,6 +284,40 @@ function ApplyRules(s) return nil end +function IsWhitespace(w) + local result = w == string.byte('\n') or + w == string.byte(' ') or + w == string.byte('\t') or + w == string.byte('\v') or + w == string.byte('\r') + return result +end + +function IsSymbol(w) + local result = (w >= string.byte('!') and w <= string.byte('/')) or + (w >= string.byte(':') and w <= string.byte('@')) or + (w >= string.byte('[') and w <= string.byte('`')) or + (w >= string.byte('{') and w <= string.byte('~')) + return result +end + +function IsLoadWord(w) + local result = w == string.byte('(') or + w == string.byte(')') or + w == string.byte('/') or + w == string.byte('\\') or + w == string.byte(':') or + w == string.byte('+') or + w == string.byte('_') or + w == string.byte('.') or + w == string.byte('-') or + w == string.byte(',') + if not result then + result = not (IsSymbol(w) or IsWhitespace(w)) + end + return result +end + Coroutines = {} function AddCo(f) local i = #Coroutines + 1 @@ -303,19 +338,28 @@ function OnUpdate(e) Coroutines = new_co_list end +OnCommandCallbacks = {} +table.insert(OnCommandCallbacks, function (e) + if e.key == SDLK_F and e.ctrl == 1 and e.shift == 1 then + C("git grep -n "..GetLoadWord().." :/") end + if e.key == SDLK_L and e.ctrl == 1 then + Eval(GetLoadWord()) end + if e.key == SDLK_B and e.ctrl == 1 then + C(GetLoadWord()) end +end) + -- REMEBER: AS LITTLE ACTUAL CODE AS POSSIBLE IN LUA -- ONLY CONFIGURABLES function OnCommand(e) - if e.key == SDLK_F and e.ctrl == 1 and e.shift == 1 then - C("git grep -n "..GetLoadWord()) end - if e.key == SDLK_E and e.ctrl == 1 then - Eval(GetLoadWord()) end + for i = #OnCommandCallbacks,1,-1 do + on_command = OnCommandCallbacks[i] + on_command(e) + end end function OnInit() end - )=="; void ReloadStyle() { ColorText = GetColor("Text", ColorText); diff --git a/src/text_editor/lua_api.cpp b/src/text_editor/lua_api.cpp index 04769ad..efd3462 100644 --- a/src/text_editor/lua_api.cpp +++ b/src/text_editor/lua_api.cpp @@ -14,7 +14,7 @@ String FieldString(lua_State *L, String name) { return result; } -void ExecInNewBuffer(BSet set, String cmd, String working_dir) { +void Command_ExecInNewBuffer(BSet set, String cmd, String working_dir) { CheckpointBeforeGoto(set.window); Scratch scratch; @@ -25,10 +25,19 @@ void ExecInNewBuffer(BSet set, String cmd, String working_dir) { buffer->gc = true; Command_SelectRangeOneCursor(view, Rng(0)); - Exec(view->id, false, cmd, working_dir); + Exec(view->id, true, cmd, working_dir); ActiveWindow = set.window->id; } +View *Command_ExecHidden(String buffer_name, String cmd, String working_dir) { + View *view = OpenBufferView(buffer_name); + Buffer *buffer = GetBuffer(view->active_buffer); + buffer->gc = true; + Command_SelectRangeOneCursor(view, Rng(0)); + Exec(view->id, true, cmd, working_dir); + return view; +} + void Open(String path) { Scratch scratch; @@ -62,7 +71,7 @@ void Open(String path) { } else if (FieldString(LuaState, "kind") == "exec") { String cmd = FieldString(LuaState, "cmd"); String working_dir = FieldString(LuaState, "working_dir"); - ExecInNewBuffer(main, cmd, working_dir); + Command_ExecInNewBuffer(main, cmd, working_dir); } else if (FieldString(LuaState, "kind") == "exec_console") { String cmd = FieldString(LuaState, "cmd"); String working_dir = FieldString(LuaState, "working_dir"); @@ -104,7 +113,7 @@ int Lua_C(lua_State *L) { String working_dir = GetActiveMainWindowBufferDir(); BSet set = GetActiveMainSet(); - ExecInNewBuffer(set, string, working_dir); + Command_ExecInNewBuffer(set, string, working_dir); return 0; } @@ -138,7 +147,7 @@ int Lua_Cmd(lua_State *L) { } else { set = active; } - ExecInNewBuffer(set, cmd, working_dir); + Command_ExecInNewBuffer(set, cmd, working_dir); return 0; } @@ -263,7 +272,7 @@ void ListFilesRecursive(Buffer *buffer, String filename) { if (it.is_directory) { ListFilesRecursive(buffer, it.absolute_path); } else { - IKnowWhatImDoing_Appendf(buffer, "%.*s\n", FmtString(it.absolute_path)); + RawAppendf(buffer, "%.*s\n", FmtString(it.absolute_path)); } } } @@ -443,6 +452,20 @@ void ReloadLuaConfigs() { } } +bool CallIsLoadWord(char16_t c) { + lua_getglobal(LuaState, "IsLoadWord"); + lua_pushinteger(LuaState, c); + if (lua_pcall(LuaState, 1, 1, 0) != 0) { + const char *error_message = lua_tostring(LuaState, -1); + ReportWarningf("Failed the call to IsLoadWord! %s", error_message); + lua_pop(LuaState, 1); + return false; + } + bool result = lua_toboolean(LuaState, -1); + lua_pop(LuaState, 1); + return result; +} + void CallOnCommand(Event *event) { lua_getglobal(LuaState, "OnCommand"); PushEvent(LuaState, event); @@ -505,7 +528,7 @@ void InitLuaConfig() { Buffer *lua_buffer = BufferOpenFile(lua_config_path); if (lua_buffer->len == 0) { String16 string16 = ToString16(scratch, BaseLuaConfig); - IKnowWhatImDoing_ReplaceText(lua_buffer, {}, string16); + RawReplaceText(lua_buffer, {}, string16); ReportConsolef("no config at: %.*s - creating config buffer", FmtString(lua_config_path)); } diff --git a/src/text_editor/management.cpp b/src/text_editor/management.cpp index 0ba3622..99b450b 100644 --- a/src/text_editor/management.cpp +++ b/src/text_editor/management.cpp @@ -12,6 +12,9 @@ ViewID NullViewID; WindowID DebugWindowID; // +debug BufferID DebugBufferID; +WindowID SearchWindowID; +BufferID SearchBufferID; + WindowID ActiveWindow; WindowID ScrollbarSelected = {-1}; @@ -253,7 +256,7 @@ String GetActiveMainWindowBufferDir() { return name; } -Int ConvertUTF8ToUTF16UnixLine(String string, wchar_t *buffer, Int buffer_cap) { +Int ConvertUTF8ToUTF16UnixLine(String string, char16_t *buffer, Int buffer_cap) { Int buffer_len = 0; Assert(buffer_cap > string.len * 2); for (Int i = 0; i < string.len;) { @@ -263,7 +266,7 @@ Int ConvertUTF8ToUTF16UnixLine(String string, wchar_t *buffer, Int buffer_cap) { } if (string.data[i] == '\t') { // @WARNING: DONT INCREASE THE SIZE CARELESSLY, WE NEED TO ADJUST BUFFER SIZE - for (Int i = 0; i < 4; i += 1) buffer[buffer_len++] = L' '; + for (Int i = 0; i < 4; i += 1) buffer[buffer_len++] = u' '; i += 1; continue; } @@ -284,7 +287,7 @@ Int ConvertUTF8ToUTF16UnixLine(String string, wchar_t *buffer, Int buffer_cap) { Assert(buffer_len < buffer_cap); } } else { - buffer[buffer_len++] = L'?'; + buffer[buffer_len++] = u'?'; } } return buffer_len; @@ -292,7 +295,7 @@ Int ConvertUTF8ToUTF16UnixLine(String string, wchar_t *buffer, Int buffer_cap) { String16 ToUnixString16(Allocator allocator, String string_) { Int cap = string_.len * 3; - wchar_t *string16_buffer = AllocArray(allocator, wchar_t, cap); + char16_t *string16_buffer = AllocArray(allocator, char16_t, cap); Int len = ConvertUTF8ToUTF16UnixLine(string_, string16_buffer, cap); String16 string = {string16_buffer, len}; return string; @@ -333,14 +336,14 @@ Buffer *BufferOpenFile(String path) { buffer->is_directory = true; for (FileIter it = IterateFiles(scratch, path); IsValid(it); Advance(&it)) { - IKnowWhatImDoing_Appendf(buffer, "%.*s\n", FmtString(it.filename)); + RawAppendf(buffer, "%.*s\n", FmtString(it.filename)); } } else { String string = ReadFile(scratch, path); buffer = CreateBuffer(sys_allocator, path, string.len * 4 + 4096); buffer->len = ConvertUTF8ToUTF16UnixLine(string, buffer->str, buffer->cap); buffer->file_mod_time = GetFileModTime(path); - UpdateLines(buffer, {}, String16{(wchar_t *)buffer->data, buffer->len}); + UpdateLines(buffer, {}, String16{(char16_t *)buffer->data, buffer->len}); } return buffer; @@ -419,46 +422,46 @@ Window *BufferIsCrumb(BufferID buffer_id) { void GarbageCollect() { Allocator sys_allocator = GetSystemAllocator(); - IterRemove(Views) { - IterRemovePrepare(Views); - View *view = it.o; - Buffer *buffer = GetBuffer(view->active_buffer); - bool gc = buffer->gc; - if (buffer->is_directory) { - Window *ref = BufferIsCrumb(buffer->id); - if (!ref) gc = true; - } + // IterRemove(Views) { + // IterRemovePrepare(Views); + // View *view = it.o; + // Buffer *buffer = GetBuffer(view->active_buffer); + // bool gc = buffer->gc; + // if (buffer->is_directory) { + // Window *ref = BufferIsCrumb(buffer->id); + // if (!ref) gc = true; + // } - Window *ref = ViewIsReferenced(view->id); - if (gc && !ref) { - Dealloc(&view->carets); - remove_item = true; + // Window *ref = ViewIsReferenced(view->id); + // if (gc && !ref) { + // Dealloc(&view->carets); + // remove_item = true; - IKnowWhatImDoing_Appendf(GCInfoBuffer, "view %.*s\n", FmtString(buffer->name)); - Dealloc(sys_allocator, &view); - } - } + // RawAppendf(GCInfoBuffer, "view %.*s\n", FmtString(buffer->name)); + // Dealloc(sys_allocator, &view); + // } + // } - IterRemove(Buffers) { - IterRemovePrepare(Buffers); - Buffer *buffer = it.o; - bool gc = buffer->gc; + // IterRemove(Buffers) { + // IterRemovePrepare(Buffers); + // Buffer *buffer = it.o; + // bool gc = buffer->gc; - View *ref = BufferIsReferenced(buffer->id); - if (gc && !ref) { - Dealloc(&buffer->line_starts); - DeallocHistoryArray(&buffer->undo_stack); - DeallocHistoryArray(&buffer->redo_stack); - Dealloc(buffer->line_starts.allocator, &buffer->data); - remove_item = true; + // View *ref = BufferIsReferenced(buffer->id); + // if (gc && !ref) { + // Dealloc(&buffer->line_starts); + // DeallocHistoryArray(&buffer->undo_stack); + // DeallocHistoryArray(&buffer->redo_stack); + // Dealloc(buffer->line_starts.allocator, &buffer->data); + // remove_item = true; - IKnowWhatImDoing_Appendf(GCInfoBuffer, "buffer %.*s\n", FmtString(buffer->name)); - Dealloc(sys_allocator, &buffer); - } else if (buffer->file_mod_time) { - int64_t new_file_mod_time = GetFileModTime(buffer->name); - if (buffer->file_mod_time != new_file_mod_time) { - buffer->changed_on_disk = true; - } - } - } + // RawAppendf(GCInfoBuffer, "buffer %.*s\n", FmtString(buffer->name)); + // Dealloc(sys_allocator, &buffer); + // } else if (buffer->file_mod_time) { + // int64_t new_file_mod_time = GetFileModTime(buffer->name); + // if (buffer->file_mod_time != new_file_mod_time) { + // buffer->changed_on_disk = true; + // } + // } + // } } diff --git a/src/text_editor/notes_vcvarsall b/src/text_editor/notes_vcvarsall index 5370885..13dc92c 100644 --- a/src/text_editor/notes_vcvarsall +++ b/src/text_editor/notes_vcvarsall @@ -1,6 +1,8 @@ -- this is going to output all the enviroment variables into the bar after it executes vcvarsall. -- Sentinel is for separating errors from data -local id = C '"C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvarsall.bat" amd64 && echo SOME_SENTINEL_VALUE && set' +"C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars64.bat" amd64 && echo SOME_SENTINEL_VALUE && set + +local id = '"C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvarsall.bat" amd64 && echo SOME_SENTINEL_VALUE && set' local buffer_id = GetBufferID(id) while ProcessIsRunning(id) do coroutine.yield() end diff --git a/src/text_editor/process.cpp b/src/text_editor/process.cpp index e51be56..609329f 100644 --- a/src/text_editor/process.cpp +++ b/src/text_editor/process.cpp @@ -1,4 +1,9 @@ Array ActiveProcesses = {}; +// @todo: I think I need to push strings onto the arena with alignment zero +// they should be of format a\0b\0c\0\0. Then it will probably work. It doesn' +// make a good api so it will have to be done in 2 steps. Platform dependent one +// and platform independent one +Array Enviroment = {}; // WARNING: seems that this maybe can't work reliably? // in case of 'git grep a' it's possible that child process spawns it's own @@ -19,7 +24,7 @@ void UpdateProcesses() { } void Exec(ViewID view, bool scroll_to_end, String cmd, String working_dir) { - Process process = SpawnProcess(cmd, working_dir); + Process process = SpawnProcess(cmd, working_dir, {}, Enviroment); process.view_id = view.id; process.scroll_to_end = scroll_to_end; if (process.is_valid) Add(&ActiveProcesses, process); @@ -33,10 +38,10 @@ void Exec(ViewID view, bool scroll_to_end, String16 cmd16, String working_dir) { Buffer *ExecAndWait(Allocator allocator, String cmd, String working_dir, String stdin_string = {}) { Buffer *temp_buffer = CreateTempBuffer(allocator, 4096 * 4); - for (Process process = SpawnProcess(cmd, working_dir, stdin_string); IsValid(&process);) { + for (Process process = SpawnProcess(cmd, working_dir, stdin_string, Enviroment); IsValid(&process);) { Scratch scratch(allocator); String poll = PollStdout(scratch, &process); - if (poll.len) IKnowWhatImDoing_Append(temp_buffer, poll); + if (poll.len) RawAppend(temp_buffer, poll); } return temp_buffer; @@ -55,4 +60,13 @@ void KillProcess(View *view) { // dont break because that will fuck with removal ... } } +} + +bool ProcessIsActive(ViewID view) { + For (ActiveProcesses) { + if (it.view_id == view.id) { + return true; + } + } + return false; } \ No newline at end of file diff --git a/src/text_editor/prototype.cpp b/src/text_editor/prototype.cpp index 5807f74..09fa163 100644 --- a/src/text_editor/prototype.cpp +++ b/src/text_editor/prototype.cpp @@ -1,55 +1,55 @@ -struct String16Builder { - Arena *arena; - size_t align; - size_t start_len; -}; +// struct String16Builder { +// Arena *arena; +// size_t align; +// size_t start_len; +// }; -String16Builder BeginString16(Arena *arena) { - String16Builder result = {arena, arena->align, arena->len}; - arena->align = 0; - return result; -} +// String16Builder BeginString16(Arena *arena) { +// String16Builder result = {arena, arena->align, arena->len}; +// arena->align = 0; +// return result; +// } -wchar_t *Add(String16Builder *sb, String16 string) { - wchar_t *r = AllocArray(*sb->arena, wchar_t, string.len); - MemoryCopy(r, string.data, string.len * sizeof(wchar_t)); - return r; -} +// char16_t *Add(String16Builder *sb, String16 string) { +// char16_t *r = AllocArray(*sb->arena, char16_t, string.len); +// MemoryCopy(r, string.data, string.len * sizeof(char16_t)); +// return r; +// } -String16 EndString16(String16Builder *sb) { - sb->arena->align = sb->align; - String16 result = {(wchar_t *)(sb->arena->data + sb->start_len), (Int)((sb->arena->len - sb->start_len) / sizeof(wchar_t))}; - return result; -} +// String16 EndString16(String16Builder *sb) { +// sb->arena->align = sb->align; +// String16 result = {(char16_t *)(sb->arena->data + sb->start_len), (Int)((sb->arena->len - sb->start_len) / sizeof(char16_t))}; +// return result; +// } -String16Builder &operator<<(String16Builder &sb, String16 string) { - Add(&sb, string); - return sb; -} +// String16Builder &operator<<(String16Builder &sb, String16 string) { +// Add(&sb, string); +// return sb; +// } -String16Builder &operator<<(String16Builder &sb, Int num) { - Scratch scratch(sb.arena); - String16 ss = Format16(scratch, L"%lld", (long long)num); - Add(&sb, ss); - return sb; -} +// String16Builder &operator<<(String16Builder &sb, Int num) { +// Scratch scratch(sb.arena); +// String16 ss = Format16(scratch, u"%lld", (long long)num); +// Add(&sb, ss); +// return sb; +// } -String16Builder &operator<<(String16Builder &sb, int num) { - return sb << (Int)num; -} +// String16Builder &operator<<(String16Builder &sb, int num) { +// return sb << (Int)num; +// } -String16Builder &operator<<(String16Builder &sb, double num) { - Scratch scratch(sb.arena); - String16 ss = Format16(scratch, L"%f", num); - Add(&sb, ss); - return sb; -} +// String16Builder &operator<<(String16Builder &sb, double num) { +// Scratch scratch(sb.arena); +// String16 ss = Format16(scratch, u"%f", num); +// Add(&sb, ss); +// return sb; +// } -void Prototype() { - Scratch scratch; - String16Builder sb = BeginString16(scratch); - sb << 32 << 32.0 << L"\n"; - String16 result = EndString16(&sb); +// void Prototype() { +// Scratch scratch; +// String16Builder sb = BeginString16(scratch); +// sb << 32 << 32.0 << u"\n"; +// String16 result = EndString16(&sb); - int a = 10; -} \ No newline at end of file +// int a = 10; +// } \ No newline at end of file diff --git a/src/text_editor/serializer.cpp b/src/text_editor/serializer.cpp index 8e04142..db91118 100644 --- a/src/text_editor/serializer.cpp +++ b/src/text_editor/serializer.cpp @@ -3,7 +3,7 @@ struct Serializer { }; void Serialize(Serializer *s, String name, Int *datum) { - IKnowWhatImDoing_Appendf(s->buffer, "%.*s = %lld, ", FmtString(name), (long long)*datum); + RawAppendf(s->buffer, "%.*s = %lld, ", FmtString(name), (long long)*datum); } void Serialize(Serializer *s, String name, uint32_t *datum) { @@ -25,18 +25,18 @@ void Serialize(Serializer *s, String name, int16_t *datum) { } void Serialize(Serializer *s, String name, float *datum) { - IKnowWhatImDoing_Appendf(s->buffer, "%.*s = %f, ", FmtString(name), *datum); + RawAppendf(s->buffer, "%.*s = %f, ", FmtString(name), *datum); } void Serialize(Serializer *s, String name, char **text) { String str = *text; - IKnowWhatImDoing_Appendf(s->buffer, "%.*s = \"%.*s\", ", FmtString(name), FmtString(str)); + RawAppendf(s->buffer, "%.*s = \"%.*s\", ", FmtString(name), FmtString(str)); } void SerializeBegin(Serializer *s) { - IKnowWhatImDoing_Appendf(s->buffer, "{"); + RawAppendf(s->buffer, "{"); } void SerializeEnd(Serializer *s) { - IKnowWhatImDoing_Appendf(s->buffer, "},\n"); + RawAppendf(s->buffer, "},\n"); } diff --git a/src/text_editor/text_editor.cpp b/src/text_editor/text_editor.cpp index db238c6..f940fda 100644 --- a/src/text_editor/text_editor.cpp +++ b/src/text_editor/text_editor.cpp @@ -25,12 +25,9 @@ int FullScreenPositionX, FullScreenPositionY; #include "render/font.cpp" #include "render/opengl.cpp" +#include "buffer.h" #include "text_editor.h" -#include "buffer_helpers.cpp" #include "buffer.cpp" -#include "buffer_multi_cursor.cpp" -#include "buffer_history.cpp" -#include "buffer_fuzzy_search.cpp" #include "buffer_test_load.cpp" #include "intern_table.cpp" @@ -99,17 +96,20 @@ Event TranslateSDLEvent(SDL_Event *input_event) { SDL_MouseButtonEvent &b = input_event->button; event.xmouse = (int16_t)b.x; event.ymouse = (int16_t)b.y; + event.clicks = b.clicks; if (b.button == SDL_BUTTON_LEFT) { event.kind = EVENT_MOUSE_LEFT; - event.clicks = b.clicks; } else if (b.button == SDL_BUTTON_RIGHT) { event.kind = EVENT_MOUSE_RIGHT; - event.clicks = b.clicks; } else if (b.button == SDL_BUTTON_MIDDLE) { event.kind = EVENT_MOUSE_MIDDLE; - event.clicks = b.clicks; + } else if (b.button == SDL_BUTTON_X1) { + event.kind = EVENT_MOUSE_X1; + } else if (b.button == SDL_BUTTON_X2) { + event.kind = EVENT_MOUSE_X2; } else { event.kind = EVENT_NONE; + event.clicks = 0; } } break; @@ -123,6 +123,10 @@ Event TranslateSDLEvent(SDL_Event *input_event) { event.kind = EVENT_MOUSE_RIGHT_UP; } else if (b.button == SDL_BUTTON_MIDDLE) { event.kind = EVENT_MOUSE_MIDDLE_UP; + } else if (b.button == SDL_BUTTON_X1) { + event.kind = EVENT_MOUSE_X1_UP; + } else if (b.button == SDL_BUTTON_X2) { + event.kind = EVENT_MOUSE_X2_UP; } else { event.kind = EVENT_NONE; } @@ -215,6 +219,30 @@ Array GetEventsForFrame(Allocator allocator) { return result; } +void Windows_SetupVCVarsall(mco_coro *co) { + Scratch scratch; + String working_dir = GetActiveMainWindowBufferDir(); + String buffer_name = GetUniqueBufferName(scratch, working_dir, "+cmd-"); + View *view = Command_ExecHidden(buffer_name, "\"C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Auxiliary/Build/vcvars64.bat\" && set", working_dir); + for (;;) { + if (!ProcessIsActive(view->id)) { + break; + } + Yield(co); + } + + Buffer *buffer = GetBuffer(view->active_buffer); + String16 string16 = GetString(buffer); + String string = ToString(scratch, string16); + Array lines = Split(scratch, string, "\n"); + For (lines) { + String s = Trim(it); + Array values = Split(scratch, s, "="); + if (values.len == 1) continue; + Add(&Enviroment, Copy(GetSystemAllocator(), s)); + } +} + #if _WIN32 int WinMain(void *hInstance, void *hPrevInstance, const char *lpCmdLine, int nShowCmd) #else @@ -228,7 +256,7 @@ int main(int argc, char **argv) BeginProfiler(); InitScratch(); InitArena(&Perm); - Prototype(); + // Prototype(); WorkingDir = GetWorkingDir(Perm); ExeDir = GetExeDir(Perm); @@ -262,7 +290,7 @@ int main(int argc, char **argv) SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8); Uint32 window_flags = SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_HIDDEN | SDL_WINDOW_HIGH_PIXEL_DENSITY; - SDLWindow = SDL_CreateWindow("Text editor", 1920, 1080, window_flags); + SDLWindow = SDL_CreateWindow("Text editor", 1280, 720, window_flags); if (SDLWindow == NULL) { ReportErrorf("Couldn't create window! %s", SDL_GetError()); return 1; @@ -270,8 +298,6 @@ int main(int argc, char **argv) SDL_GLContext gl_context = SDL_GL_CreateContext(SDLWindow); SDL_GL_MakeCurrent(SDLWindow, gl_context); - SDL_GL_SetSwapInterval(0); // Enable vsync - SDL_ShowWindow(SDLWindow); // Set icon @@ -321,6 +347,7 @@ int main(int argc, char **argv) // :Tests // StyleWaitForEvents = false; // AddCo(Test); + AddCo(Windows_SetupVCVarsall); Serializer ser = {EventBuffer}; while (AppIsRunning) { @@ -343,7 +370,7 @@ int main(int argc, char **argv) } WaitForEvents = StyleWaitForEvents; - if (IsDocumentSelectionValid() || IsScrollbarSelectionValid()) { + if (IsDocumentSelectionValid() || IsScrollbarSelectionValid() || ActiveProcesses.len) { WaitForEvents = false; } diff --git a/src/text_editor/text_editor.h b/src/text_editor/text_editor.h index 8203eb6..5da499c 100644 --- a/src/text_editor/text_editor.h +++ b/src/text_editor/text_editor.h @@ -1,52 +1,7 @@ -// clang-format off -struct Buffer; struct Window; struct View; -struct BufferID { Int id; Buffer *o; }; +struct Window; struct View; struct ViewID { Int id; View *o; }; struct WindowID { Int id; Window *o; }; // @todo: change name of object to something scary -union Range { struct { Int min; Int max; }; Int e[2]; }; -struct Caret { union { Range range; Int pos[2]; }; Int ifront;}; -struct XY { Int col; Int line; }; -// clang-format on - -struct Edit { - Range range; - String16 string; -}; - -struct HistoryEntry { - Array edits; - Array carets; -}; - -struct Buffer { - BufferID id; - String name; - Int change_id; - Int user_change_id; - Int file_mod_time; - - union { - U16 *data; - wchar_t *str; - }; - Int len; - Int cap; - Array line_starts; - - Array undo_stack; - Array redo_stack; - int edit_phase; - struct { - int no_history : 1; - int no_line_starts : 1; - int dirty : 1; - int is_directory : 1; - int gc : 1; - int changed_on_disk : 1; - }; -}; - struct View { ViewID id; BufferID active_buffer; @@ -128,7 +83,6 @@ bool Command_EvalLua(View *view, String16 string); Rect2I GetVisibleCells(Window *window); void AfterEdit(View *view, Array edits); Scroller ComputeScrollerRect(Window *window); -void MergeCarets(View *view); void Open(String path); void Open(String16 path); void UpdateScroll(Window *window, bool update_caret_scrolling); diff --git a/src/text_editor/title_bar.cpp b/src/text_editor/title_bar.cpp index e212532..b429a17 100644 --- a/src/text_editor/title_bar.cpp +++ b/src/text_editor/title_bar.cpp @@ -10,16 +10,16 @@ void UpdateDebugBuffer() { 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); - IKnowWhatImDoing_ReplaceText(buffer, GetRange(buffer), string); + RawReplaceText(buffer, GetRange(buffer), string); float xmouse, ymouse; SDL_GetMouseState(&xmouse, &ymouse); - IKnowWhatImDoing_Appendf(buffer, "mouse: [%f, %f]\n", xmouse, ymouse); - IKnowWhatImDoing_Appendf(buffer, "window count: %d view count: %d buffer count: %d\n", (int)Windows.len, (int)Views.len, (int)Buffers.len); - IKnowWhatImDoing_Appendf(buffer, "C:/Work/text_editor/src/text_editor/text_editor.cpp\n"); - IKnowWhatImDoing_Appendf(buffer, "working dir: %.*s\n", FmtString(WorkingDir)); - IKnowWhatImDoing_Appendf(buffer, "exe dir: %.*s\n", FmtString(ExeDir)); - IKnowWhatImDoing_Appendf(buffer, "config dir: %.*s\n", FmtString(ConfigDir)); + RawAppendf(buffer, "mouse: [%f, %f]\n", xmouse, ymouse); + RawAppendf(buffer, "window count: %d view count: %d buffer count: %d\n", (int)Windows.len, (int)Views.len, (int)Buffers.len); + RawAppendf(buffer, "C:/Work/text_editor/src/text_editor/text_editor.cpp\n"); + RawAppendf(buffer, "working dir: %.*s\n", FmtString(WorkingDir)); + RawAppendf(buffer, "exe dir: %.*s\n", FmtString(ExeDir)); + RawAppendf(buffer, "config dir: %.*s\n", FmtString(ConfigDir)); } void ReplaceTitleBarData(Window *window) { @@ -28,6 +28,10 @@ void ReplaceTitleBarData(Window *window) { if (window->id == ActiveWindow) return; + // @idea: maybe we could allow user to change window titles which would + // make them special in some way. + if (window->title_bar_window == SearchWindowID) return; + BSet main = GetMainSet(window); Scratch scratch; Caret caret = main.view->carets[0]; @@ -41,9 +45,9 @@ void ReplaceTitleBarData(Window *window) { String16 buffer_string = GetString(title.buffer); Range replace_range = {0, title.buffer->len}; - if (!Seek(buffer_string, L" |", &replace_range.max)) { + if (!Seek(buffer_string, u" |", &replace_range.max)) { Command_SelectRangeOneCursor(title.view, GetEndAsRange(title.buffer)); - Array edits = Command_ReplaceEx(scratch, title.view, L" |"); + Array edits = Command_ReplaceEx(scratch, title.view, u" |"); AdjustCarets(edits, &caret_copy); } diff --git a/src/text_editor/todo.txt b/src/text_editor/todo.txt index 2031b81..5061394 100644 --- a/src/text_editor/todo.txt +++ b/src/text_editor/todo.txt @@ -1,13 +1,7 @@ -- fix fucking conversions between strings! - - one approach would be to code only in widechar - - would need to replace stb_printf with system version - - big changes - - could possibly only convert deal with utf8 on read write, lua interfacing, process interfacing, - - - contain utf16? - - probably less efficient but hard to say, the worst part is still writing to disk and we need to do that regardless - - still, I would need to maintain 2 string libraries for 16 and 8 - - I start to feel like there is no easy answer if we want to have utf16 buffer +- dump text editor state to file, restore state +- help menu popup when for example in process buffer, on tile bar buffer and stuff like that +- Processing cmd.exe command encoding!!! +- shift click inside selection should move the selection - report errors (try showing a window) - proper lister - search @@ -16,6 +10,7 @@ - ask user if he really wants to quit even though he has an unsaved buffer - popup window - test the code editor: try writing in it, try browsing in it, create test tooling - Find matches using grep, change things in that buffer then apply those changes to all items + - need a way to pipe the buffer content as input into command, then it would be easy - group history entries so the you can rollback through multiple ones at once and not waste time on skipping whitespace trimming or deleting every character @@ -46,7 +41,6 @@ - change size of command window because it's wacky - combine glyph and selection rendering -- switch to previous view (ctrl + tab) - shift + ctrl + click should open a new window and then with alt it probably should kill it - layouting, resize windows - try to incorporate the acme square which allows you to put windows wherever and also scale the border diff --git a/src/text_editor/window.cpp b/src/text_editor/window.cpp index afe688f..823f00a 100644 --- a/src/text_editor/window.cpp +++ b/src/text_editor/window.cpp @@ -150,13 +150,35 @@ void InitWindows() { CreateTitlebar(window_id); } + { + Window *window = CreateWindow(); + WindowID window_id = window->id; + SearchWindowID = window->id; + window->draw_line_numbers = false; + window->absolute_position = true; + window->draw_scrollbar = false; + window->z = 2; + + Buffer *buffer = CreateBuffer(sys_allocator, BuffCWD("+search")); + SearchBufferID = buffer->id; + + View *view = CreateView(buffer->id); + window->active_view = view->id; + + Window *titlebar = CreateTitlebar(window_id); + BSet tb = GetBSet(titlebar); + RawAppendf(tb.buffer, "Search:"); + titlebar->z = 2; + + SetVisibility(window_id, false); + } + { Window *window = CreateWindow(); WindowID window_id = window->id; DebugWindowID = window->id; window->draw_line_numbers = false; window->absolute_position = true; - window->draw_line_numbers = false; window->draw_scrollbar = false; window->visible = false; window->z = 2; @@ -229,4 +251,20 @@ void LayoutWindows(int16_t wx, int16_t wy) { window->document_rect = window->total_rect; } + + { + Window *window = GetWindow(SearchWindowID); + Rect2 screen_rect = Rect0Size(wx, wy); + Vec2 size = GetSize(screen_rect); + + Rect2 rect = CutBottom(&screen_rect, (float)FontLineSpacing); + window->total_rect = ToRect2I(rect); + + Window *title_bar_window = GetWindow(window->title_bar_window); + BSet tb = GetBSet(title_bar_window); + title_bar_window->total_rect = CutLeft(&window->total_rect, tb.buffer->len * FontCharSpacing); + title_bar_window->document_rect = title_bar_window->total_rect; + + window->document_rect = window->total_rect; + } } \ No newline at end of file diff --git a/src/text_editor/window_draw.cpp b/src/text_editor/window_draw.cpp index 76d0b9b..6096d36 100644 --- a/src/text_editor/window_draw.cpp +++ b/src/text_editor/window_draw.cpp @@ -111,7 +111,8 @@ void DrawWindow(Window *window, Event &event) { Rect2 screen_rect = Rect0Size(event.xwindow, event.ywindow); SetScissor(screen_rect); - bool is_active = window->id == ActiveWindow || window->title_bar_window == ActiveWindow; + bool is_actib = window->id == ActiveWindow || window->title_bar_window == ActiveWindow; + bool is_active = window->id == ActiveWindow; Color color_whitespace_during_selection = ColorWhitespaceDuringSelection; Color color_background = ColorBackground; @@ -215,7 +216,7 @@ void DrawWindow(Window *window, Event &event) { DrawVisibleText(window, color_text); BeginProfileScope(draw_carets); - if (is_active) { + if (is_actib) { For(view->carets) { Int front = GetFront(it); XY fxy = PosToXY(buffer, front);