ALOT OF CHANGES

This commit is contained in:
krzosa
2025-04-29 16:22:26 +02:00
parent ed9ec06eee
commit 92139cf799
38 changed files with 2230 additions and 1967 deletions

View File

@@ -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<char>;
using String16 = Slice<wchar_t>;
using String16 = Slice<char16_t>;
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<String> 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<String> list, String separator = " ") {
return string;
}
Int GetSize(Array<String> array) {
Int result = 0;
For (array) result += it.len;
return result;
}
#include <stdio.h>
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;

View File

@@ -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<String> 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);

View File

@@ -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<String16> 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;
}

View File

@@ -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 <windows.h>
#include <stdio.h>
#include <intrin.h>
#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 <Windows.h>
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 <unistd.h>
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 <windows.h>
#include <stdio.h>
#include <intrin.h>
#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 <Windows.h>
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 <unistd.h>
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<String> 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;
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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}; }

View File

@@ -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));

View File

@@ -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);

View File

@@ -1,4 +1,4 @@
#if DEBUG_BUILD
#if 0
#include "spall.h"
static SpallProfile spall_ctx;

File diff suppressed because it is too large Load Diff

77
src/text_editor/buffer.h Normal file
View File

@@ -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<Edit> edits;
Array<Caret> 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<Int> line_starts;
Array<HistoryEntry> undo_stack;
Array<HistoryEntry> 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<Caret> &carets);
Array<Edit> BeginEdit(Allocator allocator, Buffer *buffer, Array<Caret> &carets);
void EndEdit(Buffer *buffer, Array<Edit> *edits, Array<Caret> *carets, bool kill_selection = true);
void AddEdit(Array<Edit> *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<Caret> *carets);
// was part of: Command_ReplaceWithoutMovingCarets, ReplaceTitleBarData
void AdjustCarets(Array<Edit> edits, Array<Caret> *carets);
void RedoEdit(Buffer *buffer, Array<Caret> *carets);
void UndoEdit(Buffer *buffer, Array<Caret> *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

View File

@@ -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<FuzzyPair> 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<FuzzyPair> 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;
}

View File

@@ -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};
}

View File

@@ -1,209 +0,0 @@
void SaveHistoryBeforeMergeCursor(Buffer *buffer, Array<HistoryEntry> *stack, Array<Caret> &carets) {
if (buffer->no_history) return;
HistoryEntry entry = {};
entry.carets = TightCopy(GetSystemAllocator(), carets);
Add(stack, entry);
}
void SaveHistoryBeforeApplyEdits(Buffer *buffer, Array<HistoryEntry> *stack, Array<Edit> &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<Edit> 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<Caret> *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<Caret> *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<Edit> &edits) {
ProfileFunction();
Assert(buffer->edit_phase == 1);
buffer->edit_phase += 1;
SaveHistoryBeforeApplyEdits(buffer, &buffer->undo_stack, edits);
IKnowWhatImDoing__ApplyEditsMultiCursor(buffer, edits);
}
void DeallocHistoryEntries(Array<HistoryEntry> *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<HistoryEntry> *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<Edit> BeginEdit(Allocator allocator, Buffer *buffer, Array<Caret> &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<Edit> result = {allocator};
return result;
}
void PreBeginEdit_SaveCaretHistory(Buffer *buffer, Array<Caret> &carets) {
BeginEdit({}, buffer, carets);
}
void AssertRanges(Array<Caret> carets) {
For(carets) {
Assert(it.range.max >= it.range.min);
}
}
void AdjustCarets(Array<Edit> edits, Array<Caret> *carets) {
Scratch scratch;
Array<Caret> 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<Edit> *edits, Array<Caret> *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<Caret> 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;
}
}
}

View File

@@ -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<Edit> 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<Edit> 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<Edit> *e, Range range, String16 string) {
Add(e, {range, string});
}

View File

@@ -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));
}

View File

@@ -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<Edit> edits = BeginEdit(scratch, buffer, view->carets);
MergeCarets(view);
MergeCarets(buffer, &view->carets);
Array<XYPair> 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<Edit> Command_ReplaceEx(Allocator scratch, View *view, String16 string) {
Buffer *buffer = GetBuffer(view->active_buffer);
Array<Edit> 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<Edit> edits = {scratch};
For(view->carets) {
@@ -460,30 +460,6 @@ void Command_DuplicateLine(View *view, int direction) {
Command_Move(view, direction);
}
Int FindRangeByPos(Array<Range> &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<Range> 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<Edit> edits = BeginEdit(scratch, buffer, view->carets);
MergeCarets(view);
MergeCarets(buffer, &view->carets);
Array<Range> 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<Edit> edits = BeginEdit(scratch, buffer, view->carets);
MergeCarets(view);
MergeCarets(buffer, &view->carets);
Array<Range> 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<Edit> edits = BeginEdit(scratch, buffer, view->carets);
MergeCarets(view);
MergeCarets(buffer, &view->carets);
Array<Range> 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<Edit> edits = BeginEdit(scratch, buffer, view->carets);
MergeCarets(view);
MergeCarets(buffer, &view->carets);
Array<Range> 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<Caret> 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<Caret> 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<Edit> 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);

View File

@@ -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));
}

View File

@@ -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<Edit> 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<Edit> 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];

View File

@@ -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,

View File

@@ -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);

View File

@@ -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));
}

View File

@@ -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;
// }
// }
// }
}

View File

@@ -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

View File

@@ -1,4 +1,9 @@
Array<Process> 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<String> 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;
}

View File

@@ -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;
}
// int a = 10;
// }

View File

@@ -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");
}

View File

@@ -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<Event> 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<String> lines = Split(scratch, string, "\n");
For (lines) {
String s = Trim(it);
Array<String> 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;
}

View File

@@ -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<Edit> edits;
Array<Caret> 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<Int> line_starts;
Array<HistoryEntry> undo_stack;
Array<HistoryEntry> 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<Edit> 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);

View File

@@ -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<Edit> edits = Command_ReplaceEx(scratch, title.view, L" |");
Array<Edit> edits = Command_ReplaceEx(scratch, title.view, u" |");
AdjustCarets(edits, &caret_copy);
}

View File

@@ -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

View File

@@ -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;
}
}

View File

@@ -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);