Compare commits
17 Commits
05f0197d50
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
29002c965c | ||
|
|
5e7acd4a20 | ||
|
|
4e5a0f6a9b | ||
|
|
f297006dcb | ||
|
|
85ca1a6a9e | ||
|
|
5a12ab8d8c | ||
|
|
2d79790d83 | ||
|
|
a351d2eb41 | ||
|
|
0424ca62f2 | ||
|
|
83d5ddff9d | ||
|
|
b600361278 | ||
|
|
9d29a1c187 | ||
|
|
20207e6040 | ||
|
|
ef6a7be285 | ||
|
|
df84d1605d | ||
|
|
88a5adaa0a | ||
|
|
e6e1ae0223 |
81
build.sh
Normal file → Executable file
81
build.sh
Normal file → Executable file
@@ -3,79 +3,10 @@
|
||||
mkdir build
|
||||
cd build
|
||||
|
||||
if [ "$1" = "release" ]; then
|
||||
profile_flags="-DDEBUG_BUILD=0 -O2"
|
||||
else
|
||||
profile_flags="-DDEBUG_BUILD=1"
|
||||
fi
|
||||
FLAGS="-nostdlib++ -fno-exceptions -fdiagnostics-absolute-paths -g -Wno-writable-strings -I../src -DDEBUG_BUILD=1"
|
||||
# clang -o metaprogram $FLAGS ../src/metaprogram/metaprogram.cpp
|
||||
# ./metaprogram
|
||||
|
||||
if [ ! -f "lbaselib.o" ]; then
|
||||
clang -g -I../src/external/lua/src -I../src/external/glad \
|
||||
../src/external/lua/src/lbaselib.c \
|
||||
../src/external/lua/src/lctype.c \
|
||||
../src/external/lua/src/ldo.c \
|
||||
../src/external/lua/src/lgc.c \
|
||||
../src/external/lua/src/liolib.c \
|
||||
../src/external/lua/src/lmem.c \
|
||||
../src/external/lua/src/lopcodes.c \
|
||||
../src/external/lua/src/lstate.c \
|
||||
../src/external/lua/src/ltable.c \
|
||||
../src/external/lua/src/lundump.c \
|
||||
../src/external/lua/src/lzio.c \
|
||||
../src/external/lua/src/lapi.c \
|
||||
../src/external/lua/src/lcode.c \
|
||||
../src/external/lua/src/ldblib.c \
|
||||
../src/external/lua/src/ldump.c \
|
||||
../src/external/lua/src/llex.c \
|
||||
../src/external/lua/src/loadlib.c \
|
||||
../src/external/lua/src/loslib.c \
|
||||
../src/external/lua/src/lstring.c \
|
||||
../src/external/lua/src/ltablib.c \
|
||||
../src/external/lua/src/lutf8lib.c \
|
||||
../src/external/lua/src/lauxlib.c \
|
||||
../src/external/lua/src/lcorolib.c \
|
||||
../src/external/lua/src/ldebug.c \
|
||||
../src/external/lua/src/lfunc.c \
|
||||
../src/external/lua/src/linit.c \
|
||||
../src/external/lua/src/lmathlib.c \
|
||||
../src/external/lua/src/lobject.c \
|
||||
../src/external/lua/src/lparser.c \
|
||||
../src/external/lua/src/lstrlib.c \
|
||||
../src/external/lua/src/ltm.c \
|
||||
../src/external/lua/src/lvm.c \
|
||||
../src/external/glad/glad.c \
|
||||
-c
|
||||
fi
|
||||
|
||||
if [ ! -f "metaprogram.exe" ]; then
|
||||
clang ../src/metaprogram/metaprogram.cpp ../src/basic/unix.cpp -o metaprogram.exe \
|
||||
-nostdlib++ -fno-exceptions -fdiagnostics-absolute-paths -g \
|
||||
-Wno-writable-strings \
|
||||
-I../src
|
||||
fi
|
||||
./metaprogram.exe
|
||||
|
||||
clang ../src/text_editor/text_editor.cpp ../src/basic/unix.cpp -o te_linux.exe \
|
||||
-nostdlib++ -fno-exceptions -fdiagnostics-absolute-paths -g \
|
||||
$profile_flags \
|
||||
-Wno-writable-strings \
|
||||
-I../src/external/SDL/include \
|
||||
-I../src/external/lua/src \
|
||||
-I../src/external/glad \
|
||||
-I../src/ \
|
||||
-lm \
|
||||
../src/external/SDL/build/libSDL3.a \
|
||||
lbaselib.o lctype.o ldo.o lgc.o liolib.o lmem.o \
|
||||
lopcodes.o lstate.o ltable.o lundump.o lzio.o lapi.o lcode.o ldblib.o ldump.o \
|
||||
llex.o loadlib.o loslib.o lstring.o ltablib.o lutf8lib.o lauxlib.o lcorolib.o ldebug.o \
|
||||
lfunc.o linit.o lmathlib.o lobject.o lparser.o lstrlib.o ltm.o lvm.o \
|
||||
glad.o \
|
||||
|
||||
|
||||
if [ "$1" = "release" ]; then
|
||||
cp te_linux.exe ../data/te
|
||||
echo written ../data/te
|
||||
else
|
||||
cp te_linux.exe ../data/te_debug
|
||||
echo written ../data/te_debug
|
||||
fi
|
||||
I="-I../src/external/SDL/include -I../src/external/lua/src -I../src/external/glad"
|
||||
clang -o te $FLAGS ../src/text_editor/text_editor.cpp $I -lSDL3 -lm -lbacktrace
|
||||
cp te ../data/te
|
||||
0
build_web.bat
Normal file → Executable file
0
build_web.bat
Normal file → Executable file
0
build_web.sh
Normal file → Executable file
0
build_web.sh
Normal file → Executable file
@@ -68,6 +68,7 @@ Style.Font = GetExeDir().."/CascadiaMono.ttf"
|
||||
Style.VCVarsall = "C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Auxiliary/Build/vcvars64.bat"
|
||||
Style.TrimWhitespaceOnSave = true
|
||||
Style.ClangFormatOnSave = false
|
||||
Style.StyleUndoMergeTimeout = 0.3
|
||||
|
||||
INTERNET_BROWSER = 'C:/Program Files/BraveSoftware/Brave-Browser/Application/brave.exe'
|
||||
OS_WINDOWS = 0
|
||||
@@ -323,10 +324,6 @@ function MatchGotoBuild(s, meta)
|
||||
end
|
||||
|
||||
function MatchExec(s, meta)
|
||||
if meta ~= "exec" then
|
||||
return nil
|
||||
end
|
||||
|
||||
if s:match(".exe$") or s:match(".bat$") or s:match(".sh$") then
|
||||
return {kind = "exec_console", cmd = s, working_dir = GetMainDir()}
|
||||
end
|
||||
@@ -335,8 +332,7 @@ function MatchExec(s, meta)
|
||||
return {kind = "exec_console", cmd = s:sub(2, -1), working_dir = GetMainDir()}
|
||||
end
|
||||
|
||||
Eval(s)
|
||||
return {kind = "skip"}
|
||||
return nil
|
||||
end
|
||||
|
||||
BuiltinOnOpenMatchers = {
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
FKey[2] = "remedybg.exe build\\te.exe"
|
||||
FKey[5] = "remedybg.exe continue-execution"
|
||||
FKey[9] = function () return 'remedybg.exe add-breakpoint-at-file '..GetFilename()..' '..tostring(GetLine()) end
|
||||
FKey[10] = function () return 'remedybg.exe run-to-cursor '..GetFilename()..' '..tostring(GetLine()) end
|
||||
|
||||
|
||||
function OnInit()
|
||||
Open("init.project.lua")
|
||||
Split(VERTICAL)
|
||||
Open("src/text_editor/todo.txt")
|
||||
Split(HORIZONTAL)
|
||||
Open("src/text_editor/commands.cpp")
|
||||
SplitSize(0.15)
|
||||
end
|
||||
@@ -13,7 +13,6 @@ Needs to change:
|
||||
- How to design Command view?
|
||||
- How to design popup view (input field)?
|
||||
- How to design search view? or search and replace view?
|
||||
- Window management, splitting, GC
|
||||
|
||||
Things I like:
|
||||
- Basic editing
|
||||
@@ -24,19 +23,47 @@ Splits:
|
||||
- Buffer16 Buffer8?
|
||||
- Why constraint that name of buffer needs to be unique? For Open() and default behavior but is this required?
|
||||
- Try to add Tracking Allocator and rewrite the app, free all memory at the end of the app and check all is well
|
||||
- SpawnProcess wrong memory allocation there
|
||||
- Trying to fix the testing, Command_Open doesn't work on first frame because window is not metricly OK?
|
||||
- window->document_rect is null
|
||||
|
||||
|
||||
|
||||
|
||||
- move titlebar, search to splits?
|
||||
|
||||
|
||||
Commands TODO:
|
||||
- Search
|
||||
- Ctrl + F
|
||||
- Next occurence: Enter
|
||||
- Prev occurence: Shift + Enter
|
||||
- next occurence: F3
|
||||
- prev occurence: Shift + F3
|
||||
- Console: OK concept but constrain
|
||||
- Turned off by default
|
||||
- Special: non editable, hotkeys don't work etc.
|
||||
- I'M SETTING ACTIVE WINDOW AFTER COMMAND!!! DO WE CONTINUE WITH THIS?
|
||||
|
||||
- CONSIDER AUTOMATING: CheckpointBeforeGoto
|
||||
- GotoBackward how to handle that in case we want to automate and create on every move?
|
||||
|
||||
## Hooks and bindings
|
||||
|
||||
```
|
||||
|
||||
struct Hook {
|
||||
String name;
|
||||
String trigger;
|
||||
HookFunction function;
|
||||
};
|
||||
|
||||
void Command_New() {
|
||||
...
|
||||
} RegisterCommand(Command_New, "ctrl-n", NOT_VISIBLE_IN_LISTING);
|
||||
// How do we handle Command_Delete variations????
|
||||
|
||||
void Hook_FormatOnSave() {
|
||||
} RegisterHook(Hook_FormatOnSave, "onsave");
|
||||
|
||||
|
||||
Array<Hook> Hooks;
|
||||
|
||||
|
||||
```
|
||||
|
||||
|
||||
DESIGN try to make console less special, make stuff reusable etc.
|
||||
|
||||
@@ -42,6 +42,8 @@ API bool VDecommit(void *p, size_t size) {
|
||||
}
|
||||
|
||||
#elif OS_LINUX || OS_MAC
|
||||
#include <sys/mman.h>
|
||||
|
||||
|
||||
API void *VReserve(size_t size) {
|
||||
void *result = mmap(0, size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, (off_t)0);
|
||||
|
||||
@@ -37,8 +37,6 @@ For(arr.reverse_iter()) {
|
||||
#define ForItem(it, array) for (auto &it : (array))
|
||||
#define For(array) ForItem(it, array)
|
||||
|
||||
|
||||
|
||||
template <class T>
|
||||
struct Slice {
|
||||
T *data;
|
||||
|
||||
@@ -212,4 +212,9 @@ inline uint64_t GetRandomU64(RandomSeed *state) {
|
||||
x ^= x >> 7;
|
||||
x ^= x << 17;
|
||||
return state->a = x;
|
||||
}
|
||||
}
|
||||
|
||||
#define STRINGIFY_(x) x
|
||||
#define STRINGIFY(x) STRINGIFY_(x)
|
||||
#define CONCAT_(a, b) a ## b
|
||||
#define CONCAT(a, b) CONCAT_(a, b)
|
||||
|
||||
@@ -14,11 +14,58 @@
|
||||
#include <stdlib.h>
|
||||
#include <spawn.h>
|
||||
#include <poll.h>
|
||||
#include <execinfo.h>
|
||||
#include <backtrace.h>
|
||||
|
||||
API void (*Error)(const char *, ...);
|
||||
|
||||
struct backtrace_state *backtrace_state = NULL;
|
||||
|
||||
void BacktraceOnError(void *data, const char *msg, int errnum) {
|
||||
Error("libbacktrace error: %s (errnum: %d)\n", msg, errnum);
|
||||
}
|
||||
|
||||
int BacktraceOnPrint(void *data, uintptr_t pc, const char *filename, int lineno, const char *function) {
|
||||
bool printed = false;
|
||||
if (filename != NULL) {
|
||||
char buffer[512];
|
||||
char *f = realpath(filename, buffer);
|
||||
printf("%s:%d:1: ", f, lineno);
|
||||
printed = true;
|
||||
}
|
||||
if (function != NULL) {
|
||||
printf("%s", function);
|
||||
printed = true;
|
||||
}
|
||||
if (printed) {
|
||||
printf("\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void CrashHandler(int signal, siginfo_t* info, void* context) {
|
||||
backtrace_full(backtrace_state, 2, BacktraceOnPrint, BacktraceOnError, NULL);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void RegisterCrashHandler(void) {
|
||||
backtrace_state = backtrace_create_state(NULL, 1, BacktraceOnError, NULL);
|
||||
|
||||
struct sigaction sa;
|
||||
sa.sa_sigaction = CrashHandler;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_flags = SA_RESTART | SA_SIGINFO;
|
||||
|
||||
sigaction(SIGSEGV, &sa, NULL);
|
||||
sigaction(SIGABRT, &sa, NULL);
|
||||
sigaction(SIGBUS, &sa, NULL);
|
||||
sigaction(SIGILL, &sa, NULL);
|
||||
sigaction(SIGFPE, &sa, NULL);
|
||||
}
|
||||
|
||||
API void InitOS(void (*error_proc)(const char *, ...)) {
|
||||
Error = error_proc;
|
||||
RegisterCrashHandler();
|
||||
}
|
||||
|
||||
API String ReadFile(Allocator al, String path) {
|
||||
@@ -171,9 +218,9 @@ API void Advance(FileIter *it) {
|
||||
|
||||
const char *dir_char_ending = it->is_directory ? "/" : "";
|
||||
const char *separator = it->path.data[it->path.len - 1] == '/' ? "" : "/";
|
||||
it->relative_path = Format(it->allocator, "%.*s%s%s%s", FmtString(it->path), separator, file->d_name, dir_char_ending);
|
||||
it->relative_path = Format(it->allocator, "%S%s%s%s", it->path, separator, file->d_name, dir_char_ending);
|
||||
it->absolute_path = GetAbsolutePath(it->allocator, it->relative_path);
|
||||
if (it->is_directory) it->absolute_path = Format(it->allocator, "%.*s/", FmtString(it->absolute_path));
|
||||
if (it->is_directory) it->absolute_path = Format(it->allocator, "%S/", it->absolute_path);
|
||||
it->is_valid = true;
|
||||
return;
|
||||
}
|
||||
@@ -759,7 +806,6 @@ API Process SpawnProcess(String command_line, String working_dir, String write_s
|
||||
String16 cmd = ToString16(scratch, command_line);
|
||||
|
||||
char *env = NULL;
|
||||
// TODO: FIX ARENA ALLOCATION USING PushSize, Prealloc maybe? Maybe we want a block arena
|
||||
if (enviroment.len) {
|
||||
Int size = GetSize(enviroment) + enviroment.len + 1;
|
||||
env = (char *)PushSize(scratch, size);
|
||||
@@ -1098,3 +1144,7 @@ API void CloseStdin(Process *process) {
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
API double GetTimeSeconds() {
|
||||
return GetTimeMicros() / 1000000.0;
|
||||
}
|
||||
@@ -388,7 +388,7 @@ GLuint UploadAtlas(Atlas *atlas) {
|
||||
return tex;
|
||||
}
|
||||
|
||||
void ReloadFont() {
|
||||
void ReloadFont(String path, U32 size) {
|
||||
if (PrimaryFont.texture_id) {
|
||||
glDeleteTextures(1, &PrimaryFont.texture_id);
|
||||
Dealloc(&PrimaryFont.glyphs);
|
||||
@@ -397,7 +397,7 @@ void ReloadFont() {
|
||||
|
||||
Scratch scratch;
|
||||
Atlas atlas = CreateAtlas(scratch, {2048, 2048});
|
||||
PrimaryFont = CreateFont(&atlas, (uint32_t)ClampBottom(2u, (U32)StyleFontSize), StyleFont);
|
||||
SecondaryFont = CreateFont(&atlas, 10, StyleFont);
|
||||
PrimaryFont = CreateFont(&atlas, (uint32_t)ClampBottom(2u, (U32)size), path);
|
||||
SecondaryFont = CreateFont(&atlas, 12, path);
|
||||
SecondaryFont.texture_id = PrimaryFont.texture_id = UploadAtlas(&atlas);
|
||||
}
|
||||
|
||||
@@ -140,8 +140,8 @@ void PlayTestOpen(mco_coro *co) {
|
||||
}
|
||||
|
||||
void Test(mco_coro *co) {
|
||||
Wait(co); // First phase starts immediately but stuff is not initialized so Command_Open acts weird
|
||||
Command_Open(TestDir);
|
||||
Wait(co); // First phase starts immediately but stuff is not initialized so Open acts weird
|
||||
Open(TestDir);
|
||||
PlayTestOpen(co);
|
||||
Release(&TestArena);
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#define BUFFER_DEBUG 0
|
||||
|
||||
|
||||
API Range MakeRange(Int a, Int b) {
|
||||
Range result = {Min(a, b), Max(a, b)};
|
||||
return result;
|
||||
@@ -94,6 +93,16 @@ API Int GetBack(Caret caret) {
|
||||
return result;
|
||||
}
|
||||
|
||||
API Int GetMax(Caret caret) {
|
||||
Int result = Max(caret.pos[0], caret.pos[1]);
|
||||
return result;
|
||||
}
|
||||
|
||||
API Int GetMin(Caret caret) {
|
||||
Int result = Min(caret.pos[0], caret.pos[1]);
|
||||
return result;
|
||||
}
|
||||
|
||||
API Caret MakeCaret(Int pos) {
|
||||
Caret result = {};
|
||||
result.range.min = result.range.max = pos;
|
||||
@@ -917,16 +926,17 @@ API void AddEdit(Array<Edit> *e, Range range, String16 string) {
|
||||
void SaveHistoryBeforeMergeCursor(Buffer *buffer, Array<HistoryEntry> *stack, Array<Caret> &carets) {
|
||||
if (buffer->no_history) return;
|
||||
HistoryEntry entry = {};
|
||||
entry.carets = TightCopy(GetSystemAllocator(), carets);
|
||||
entry.time = GetTimeSeconds();
|
||||
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);
|
||||
HistoryEntry *entry = GetLast(*stack);
|
||||
Allocator sys_allocator = GetSystemAllocator();
|
||||
entry->edits = TightCopy(sys_allocator, edits);
|
||||
|
||||
// Make reverse edits
|
||||
For(entry->edits) {
|
||||
@@ -992,8 +1002,16 @@ API void UndoEdit(Buffer *buffer, Array<Caret> *carets) {
|
||||
Allocator sys_allocator = GetSystemAllocator();
|
||||
For(entry.edits) Dealloc(sys_allocator, it.string.data);
|
||||
Dealloc(&entry.edits);
|
||||
|
||||
if (buffer->undo_stack.len > 0) {
|
||||
HistoryEntry *next = GetLast(buffer->undo_stack);
|
||||
if (entry.time - next->time <= StyleUndoMergeTimeout) {
|
||||
UndoEdit(buffer, carets);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
API void DeallocHistoryEntries(Array<HistoryEntry> *entries) {
|
||||
For(*entries) {
|
||||
Dealloc(&it.carets);
|
||||
@@ -1003,6 +1021,21 @@ API void DeallocHistoryEntries(Array<HistoryEntry> *entries) {
|
||||
entries->len = 0;
|
||||
}
|
||||
|
||||
API void ResetHistory(Buffer *buffer) {
|
||||
DeallocHistoryEntries(&buffer->redo_stack);
|
||||
DeallocHistoryEntries(&buffer->undo_stack);
|
||||
}
|
||||
|
||||
API void ResetBuffer(Buffer *buffer) {
|
||||
ResetHistory(buffer);
|
||||
buffer->change_id += 1;
|
||||
buffer->line_starts.len = 0;
|
||||
buffer->len = 0;
|
||||
if (!buffer->no_line_starts) {
|
||||
Add(&buffer->line_starts, (Int)0);
|
||||
}
|
||||
}
|
||||
|
||||
void ClearRedoStack(Buffer *buffer) {
|
||||
DeallocHistoryEntries(&buffer->redo_stack);
|
||||
}
|
||||
@@ -1170,8 +1203,6 @@ API void InitBuffer(Allocator allocator, Buffer *buffer, BufferID id = {}, Strin
|
||||
}
|
||||
|
||||
API void DeinitBuffer(Buffer *buffer) {
|
||||
Assert(buffer->next == NULL);
|
||||
Assert(buffer->prev == NULL);
|
||||
Allocator allocator = buffer->line_starts.allocator;
|
||||
Dealloc(allocator, buffer->data);
|
||||
Dealloc(&buffer->line_starts);
|
||||
@@ -1181,7 +1212,6 @@ API void DeinitBuffer(Buffer *buffer) {
|
||||
|
||||
// Indexing starts from 0 not 1 because this routine creates also the zero buffer
|
||||
// which is the buffer that often is defaulted to in case of errors
|
||||
Int BufferIDs;
|
||||
API Buffer *AllocBuffer(Allocator allocator, String name = "", Int size = 4096) {
|
||||
Buffer *buffer = AllocType(allocator, Buffer);
|
||||
buffer->id = {BufferIDs++, buffer};
|
||||
@@ -1203,7 +1233,7 @@ API Buffer *CreateTempBuffer(Allocator allocator, Int size = 4096) {
|
||||
return result;
|
||||
}
|
||||
|
||||
void RunBufferTest() {
|
||||
void RunBufferTest(void *param) {
|
||||
{
|
||||
Scratch scratch;
|
||||
Buffer buffer = {};
|
||||
@@ -1380,6 +1410,165 @@ void RunBufferTest() {
|
||||
DeinitBuffer(&buffer);
|
||||
TrackingAllocatorCheck();
|
||||
}
|
||||
} RegisterFunction(&TestFunctions, RunBufferTest);
|
||||
|
||||
///////////////////////////////
|
||||
// Management
|
||||
inline BufferID AllocBufferID(Buffer *buffer) {
|
||||
return {BufferIDs++, buffer};
|
||||
}
|
||||
|
||||
}
|
||||
inline Buffer *GetBuffer(BufferID id) {
|
||||
For(Buffers) {
|
||||
if (it->id == id) return it;
|
||||
}
|
||||
return Buffers[0];
|
||||
}
|
||||
|
||||
inline Buffer *FindBuffer(BufferID id) {
|
||||
For(Buffers) {
|
||||
if (it->id == id) return it;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
inline Buffer *GetBuffer(String name) {
|
||||
For(Buffers) {
|
||||
if (it->name == name) return it;
|
||||
}
|
||||
return Buffers[0];
|
||||
}
|
||||
|
||||
inline Buffer *FindBuffer(String name) {
|
||||
For(Buffers) {
|
||||
if (it->name == name) return it;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
inline bool IsNull(Buffer *buffer) {
|
||||
return buffer->id.id == 0;
|
||||
}
|
||||
|
||||
Buffer *CreateBuffer(Allocator allocator, String name, Int size) {
|
||||
Buffer *result = AllocBuffer(allocator, name, size);
|
||||
Add(&Buffers, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
String GetUniqueBufferName(String working_dir, String prepend_name, String extension = ".log") {
|
||||
Scratch scratch;
|
||||
String buffer_name = {};
|
||||
for (int i = 1; i < INT_MAX; i += 1) {
|
||||
buffer_name = Format(scratch, "%S/%S%d%S", working_dir, prepend_name, i, extension);
|
||||
buffer_name = GetAbsolutePath(scratch, buffer_name);
|
||||
Buffer *exists = FindBuffer(buffer_name);
|
||||
if (!exists && !FileExists(buffer_name)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
buffer_name = Intern(&GlobalInternTable, buffer_name);
|
||||
return buffer_name;
|
||||
}
|
||||
|
||||
void InitBuffers() {
|
||||
Allocator sys_allocator = GetSystemAllocator();
|
||||
Scratch scratch;
|
||||
Buffer *null_buffer = CreateBuffer(sys_allocator, GetUniqueBufferName(GetWorkingDir(scratch), "console"));
|
||||
View *null_view = CreateView(null_buffer->id);
|
||||
Assert(null_buffer->id == NullBufferID && null_view->id == NullViewID);
|
||||
TraceBuffer = CreateBuffer(sys_allocator, GetUniqueBufferName(WorkDir, "trace"));
|
||||
TraceView = CreateView(TraceBuffer->id);
|
||||
GCInfoBuffer = CreateBuffer(sys_allocator, GetUniqueBufferName(WorkDir, "gc"));
|
||||
EventBuffer = CreateBuffer(sys_allocator, GetUniqueBufferName(WorkDir, "events"));
|
||||
ScratchBuffer = BufferOpenFile(GetUniqueBufferName(WorkDir, "scratch"));
|
||||
EventBuffer->no_history = true;
|
||||
GCInfoBuffer->no_history = true;
|
||||
}
|
||||
|
||||
Int ConvertUTF8ToUTF16UnixLine(String string, char16_t *buffer, Int buffer_cap) {
|
||||
if (string.len == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
Int buffer_len = 0;
|
||||
Assert(buffer_cap > string.len * 2);
|
||||
for (Int i = 0; i < string.len;) {
|
||||
if (string.data[i] == '\r') {
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
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++] = u' ';
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
uint32_t u32 = '?';
|
||||
UTF32Result decode = UTF8ToUTF32((uint8_t *)(string.data + i), (int64_t)(string.len - i));
|
||||
if (!decode.error) {
|
||||
i += decode.advance;
|
||||
u32 = decode.out_str;
|
||||
} else {
|
||||
i += 1;
|
||||
}
|
||||
|
||||
UTF16Result encode = UTF32ToUTF16(u32);
|
||||
if (!encode.error) {
|
||||
for (int64_t encode_i = 0; encode_i < encode.len; encode_i += 1) {
|
||||
buffer[buffer_len++] = encode.out_str[encode_i];
|
||||
Assert(buffer_len < buffer_cap);
|
||||
}
|
||||
} else {
|
||||
buffer[buffer_len++] = u'?';
|
||||
}
|
||||
}
|
||||
return buffer_len;
|
||||
}
|
||||
|
||||
String16 ToUnixString16(Allocator allocator, String string_) {
|
||||
Int cap = string_.len * 3;
|
||||
char16_t *string16_buffer = AllocArray(allocator, char16_t, cap);
|
||||
Int len = ConvertUTF8ToUTF16UnixLine(string_, string16_buffer, cap);
|
||||
String16 string = {string16_buffer, len};
|
||||
return string;
|
||||
}
|
||||
|
||||
Buffer *BufferOpenFile(String path) {
|
||||
Allocator sys_allocator = GetSystemAllocator();
|
||||
Scratch scratch;
|
||||
|
||||
path = GetAbsolutePath(scratch, path);
|
||||
Buffer *buffer = GetBuffer(path);
|
||||
if (!IsNull(buffer) || (IsNull(buffer) && buffer->name == path)) {
|
||||
return buffer;
|
||||
}
|
||||
|
||||
if (!FileExists(path)) {
|
||||
buffer = CreateBuffer(sys_allocator, path);
|
||||
} else if (IsDir(path)) {
|
||||
ReportWarningf("failed to open, it's a directory: %S", path);
|
||||
return GetBuffer(NullBufferID);
|
||||
} 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{(char16_t *)buffer->data, buffer->len});
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
bool BufferIsReferenced(BufferID buffer_id) {
|
||||
if (buffer_id == NullBufferID) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (FindView(buffer_id)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -12,12 +12,11 @@ struct Edit {
|
||||
struct HistoryEntry {
|
||||
Array<Edit> edits;
|
||||
Array<Caret> carets;
|
||||
double time;
|
||||
};
|
||||
|
||||
struct Buffer {
|
||||
BufferID id;
|
||||
Buffer *next;
|
||||
Buffer *prev;
|
||||
String name;
|
||||
Int change_id;
|
||||
Int user_change_id;
|
||||
@@ -158,6 +157,8 @@ API void AssertRanges(Array<Caret> carets);
|
||||
|
||||
API void RedoEdit(Buffer *buffer, Array<Caret> *carets);
|
||||
API void UndoEdit(Buffer *buffer, Array<Caret> *carets);
|
||||
API void ResetHistory(Buffer *buffer);
|
||||
|
||||
API void DeallocHistoryArray(Array<HistoryEntry> *entries);
|
||||
API void DeallocHistoryEntries(Array<HistoryEntry> *entries);
|
||||
API void DeallocHistoryEntries(Array<HistoryEntry> *entries);
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -46,23 +46,6 @@ void UpdateScroll(Window *window, bool update_caret_scrolling) {
|
||||
}
|
||||
}
|
||||
|
||||
void ResizerDetectMouse(Event event, WindowSplit *split) {
|
||||
if (split == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
Vec2I mouse = MouseVec2I();
|
||||
bool mouse_in_rect = AreOverlapping(mouse, split->resizer_rect);
|
||||
if (mouse_in_rect) {
|
||||
ResizerHover = split;
|
||||
if (Mouse(LEFT)) {
|
||||
ResizerSelected = split;
|
||||
}
|
||||
}
|
||||
ResizerDetectMouse(event, split->left);
|
||||
ResizerDetectMouse(event, split->right);
|
||||
}
|
||||
|
||||
void OnCommand(Event event) {
|
||||
ProfileFunction();
|
||||
//
|
||||
@@ -111,7 +94,7 @@ void OnCommand(Event event) {
|
||||
view->scroll.y = (Int)(v * (double)s.line_count * (double)window->font->line_spacing);
|
||||
}
|
||||
|
||||
if (DocumentSelected != ActiveWindow) {
|
||||
if (DocumentSelected != ActiveWindowID) {
|
||||
DocumentSelected.id = -1;
|
||||
} else if (IsDocumentSelectionValid() && MouseUp()) {
|
||||
Assert(ScrollbarSelected.id == -1);
|
||||
@@ -119,7 +102,6 @@ void OnCommand(Event event) {
|
||||
} else if (IsDocumentSelectionValid()) {
|
||||
Assert(ScrollbarSelected.id == -1);
|
||||
BSet selected = GetBSet(DocumentSelected);
|
||||
|
||||
Vec2I mouse = MouseVec2I();
|
||||
// Special case for full-screen where we can have document
|
||||
// aligned with monitor screen in which case mouse cursor cannot
|
||||
@@ -127,33 +109,42 @@ void OnCommand(Event event) {
|
||||
if (mouse.y == 0 && selected.window->document_rect.min.y == 0) {
|
||||
float x, y;
|
||||
SDL_GetGlobalMouseState(&x, &y);
|
||||
x = roundf(DPIScale * x);
|
||||
y = roundf(DPIScale * y);
|
||||
if (y == 0) {
|
||||
mouse.y = -10;
|
||||
}
|
||||
}
|
||||
|
||||
Int p = ScreenSpaceToBufferPos(selected.window, selected.view, selected.buffer, mouse);
|
||||
Int p = ScreenSpaceToBufferPos(selected.window, selected.view, selected.buffer, mouse);
|
||||
Caret &caret = selected.view->carets[0];
|
||||
|
||||
caret = SetFrontWithAnchor(caret, DocumentAnchor, p);
|
||||
}
|
||||
|
||||
if (ResizerSelected && Mouse(LEFT_UP)) {
|
||||
if (ResizerSelected.id != -1 && Mouse(LEFT_UP)) {
|
||||
Assert(DocumentSelected.id == -1);
|
||||
Assert(ScrollbarSelected.id == -1);
|
||||
ResizerSelected = NULL;
|
||||
} else if (ResizerSelected) {
|
||||
Vec2I mouse = MouseVec2I();
|
||||
mouse -= ResizerSelected->total_rect.min;
|
||||
Vec2I size = GetSize(ResizerSelected->total_rect);
|
||||
Vec2 p = ToVec2(mouse) / ToVec2(size);
|
||||
if (ResizerSelected->kind == WindowSplitKind_Vertical) {
|
||||
ResizerSelected->value = p.x;
|
||||
} else {
|
||||
ResizerSelected->value = p.y;
|
||||
ResizerSelected.id = {-1};
|
||||
} else if (ResizerSelected.id != -1) {
|
||||
Window *window = GetWindow(ResizerSelected);
|
||||
if (window->layout) {
|
||||
Vec2I mouse = MouseVec2I();
|
||||
Int offx = mouse.x - window->resizer_rect.min.x;
|
||||
window->weight += (double)offx / (double)WindowCalcEvenResizerValue(event.xwindow);
|
||||
window->weight = Clamp(window->weight, 0.1, 100.0);
|
||||
}
|
||||
} else {
|
||||
ResizerDetectMouse(event, &WindowSplits);
|
||||
ResizerHover = {-1};
|
||||
For(Windows) {
|
||||
Vec2I mouse = MouseVec2I();
|
||||
bool mouse_in_rect = AreOverlapping(mouse, it->resizer_rect);
|
||||
if (mouse_in_rect) {
|
||||
ResizerHover = it->id;
|
||||
if (Mouse(LEFT)) {
|
||||
ResizerSelected = it->id;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set active window on click
|
||||
@@ -165,19 +156,12 @@ void OnCommand(Event event) {
|
||||
}
|
||||
bool mouse_in_document = AreOverlapping(mouse, it->document_rect);
|
||||
if (mouse_in_document) {
|
||||
ActiveWindow = it->id;
|
||||
ActiveWindowID = it->id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Mouse(X2)) {
|
||||
GotoForward(GetActiveMainSet().window);
|
||||
}
|
||||
if (Mouse(X1)) {
|
||||
GotoBackward(GetActiveMainSet().window);
|
||||
}
|
||||
|
||||
if (Ctrl() && Shift() && Mouse(RIGHT)) {
|
||||
|
||||
} else if (Alt() && Ctrl() && Mouse(RIGHT)) {
|
||||
@@ -185,8 +169,8 @@ void OnCommand(Event event) {
|
||||
|
||||
} else if (Alt() && Mouse(RIGHT)) {
|
||||
} else if (Mouse(RIGHT)) {
|
||||
Vec2I mouse = MouseVec2I();
|
||||
BSet active = GetActiveSet();
|
||||
Vec2I mouse = MouseVec2I();
|
||||
BSet active = GetBSet(ActiveWindowID);
|
||||
bool mouse_in_document = AreOverlapping(mouse, active.window->document_rect);
|
||||
if (mouse_in_document) {
|
||||
Int p = ScreenSpaceToBufferPos(active.window, active.view, active.buffer, mouse);
|
||||
@@ -213,9 +197,7 @@ void OnCommand(Event event) {
|
||||
}
|
||||
}
|
||||
|
||||
if (Shift() && Ctrl() && Mouse(LEFT)) {
|
||||
MouseLoadWord(event, "exec");
|
||||
} else if (Ctrl() && Mouse(LEFT)) {
|
||||
if (Ctrl() && Mouse(LEFT)) {
|
||||
MouseLoadWord(event);
|
||||
} else if (Mouse(LEFT)) { // Uses Alt and shift
|
||||
Vec2I mouse = MouseVec2I();
|
||||
@@ -223,12 +205,13 @@ void OnCommand(Event event) {
|
||||
Assert(ScrollbarSelected.id == -1);
|
||||
Assert(DocumentSelected.id == -1);
|
||||
|
||||
BSet active = GetActiveSet();
|
||||
BSet active = GetBSet(ActiveWindowID); // using next to make sure mouse works on first click after switching the window
|
||||
bool mouse_in_document = AreOverlapping(mouse, active.window->document_rect);
|
||||
bool mouse_in_line_numbers = AreOverlapping(mouse, active.window->line_numbers_rect);
|
||||
if (mouse_in_document || mouse_in_line_numbers) {
|
||||
CheckpointBeforeGoto(active.window);
|
||||
DocumentSelected = active.window->id;
|
||||
CheckpointBeforeGoto(active.window);
|
||||
|
||||
|
||||
Int p = ScreenSpaceToBufferPos(active.window, active.view, active.buffer, mouse);
|
||||
if (Alt()) Insert(&active.view->carets, MakeCaret(p, p), 0);
|
||||
@@ -283,293 +266,44 @@ void OnCommand(Event event) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (CtrlAltPress(SDLK_P)) {
|
||||
Command_ListBuffers();
|
||||
} else if (CtrlPress(SDLK_P)) {
|
||||
Command_ListCode();
|
||||
}
|
||||
|
||||
if (CtrlShiftPress(SDLK_BACKSLASH)) {
|
||||
SplitWindow(WindowSplitKind_Horizontal);
|
||||
} else if (CtrlPress(SDLK_BACKSLASH)) {
|
||||
SplitWindow(WindowSplitKind_Vertical);
|
||||
}
|
||||
|
||||
if (CtrlPress(SDLK_0)) {
|
||||
ToggleVisibility(DebugWindowID);
|
||||
}
|
||||
|
||||
if (CtrlPress(SDLK_GRAVE)) {
|
||||
if (ActiveWindow != NullWindowID) {
|
||||
ActiveWindow = NullWindowID;
|
||||
} else {
|
||||
if (WindowSplits.value + 0.01 < 0.9) {
|
||||
WindowSplits.value = (double)0.9;
|
||||
} else {
|
||||
WindowSplits.value = (double)0.6;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (CtrlPress(SDLK_1)) {
|
||||
ActiveWindow = GetOverlappingWindow({0,0}, GetWindow(ActiveWindow))->id;
|
||||
}
|
||||
if (CtrlPress(SDLK_2)) {
|
||||
Window *first = GetOverlappingWindow({0,0}, GetWindow(ActiveWindow));
|
||||
Vec2I p = GetSideOfWindow(first, DIR_RIGHT);
|
||||
ActiveWindow = GetOverlappingWindow(p, GetWindow(ActiveWindow))->id;
|
||||
}
|
||||
if (CtrlPress(SDLK_3)) {
|
||||
Window *first = GetOverlappingWindow({0,0});
|
||||
if (first) {
|
||||
Window *second = GetOverlappingWindow(GetSideOfWindow(first, DIR_RIGHT));
|
||||
if (second) {
|
||||
Window *third = GetOverlappingWindow(GetSideOfWindow(second, DIR_RIGHT));
|
||||
if (third) {
|
||||
ActiveWindow = third->id;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BSet main = GetActiveMainSet();
|
||||
BSet active = GetActiveSet();
|
||||
BSet main = GetBSet(LastActiveLayoutWindowID);
|
||||
BSet active = GetBSet(ActiveWindowID);
|
||||
Int buffer_change_id = active.buffer->change_id;
|
||||
|
||||
bool skip = CallOnCommand(&event);
|
||||
if (skip) {
|
||||
// :OnCommandEnding
|
||||
MergeCarets(active.buffer, &active.view->carets);
|
||||
IF_DEBUG(AssertRanges(active.view->carets));
|
||||
return;
|
||||
CommandContext ctx = {};
|
||||
For (CommandFunctions) {
|
||||
if (it.trigger && MatchEvent(it.trigger, &event)) {
|
||||
it.function(&ctx);
|
||||
MergeCarets(active.buffer, &active.view->carets);
|
||||
IF_DEBUG(AssertRanges(active.view->carets));
|
||||
if (ctx.out_skip_rem_cmds) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// @todo: do we need the context for commands, maybe skip cmds should be global?
|
||||
// @todo: hook drop file
|
||||
if (event.kind == EVENT_DROP_FILE) {
|
||||
WindowOpenBufferView(active.window, event.text);
|
||||
}
|
||||
|
||||
if (CtrlAltPress(SDLK_DOWN)) {
|
||||
Command_DuplicateLine(active.view, DIR_DOWN);
|
||||
} else if (AltShiftPress(SDLK_DOWN)) {
|
||||
Command_CreateCursorVertical(active.view, DIR_DOWN);
|
||||
} else if (CtrlShiftPress(SDLK_DOWN)) {
|
||||
Command_Move(active.view, DIR_DOWN, CTRL_PRESSED, SHIFT_PRESS);
|
||||
} else if (AltPress(SDLK_DOWN)) {
|
||||
Command_MoveLine(active.view, DIR_DOWN);
|
||||
} else if (CtrlPress(SDLK_DOWN)) {
|
||||
Command_Move(active.view, DIR_DOWN, CTRL_PRESSED);
|
||||
} else if (ShiftPress(SDLK_DOWN)) {
|
||||
Command_Move(active.view, DIR_DOWN, false, SHIFT_PRESS);
|
||||
} else if (Press(SDLK_DOWN)) {
|
||||
Command_Move(active.view, DIR_DOWN);
|
||||
}
|
||||
|
||||
if (CtrlAltPress(SDLK_UP)) {
|
||||
Command_DuplicateLine(active.view, DIR_UP);
|
||||
} else if (AltShiftPress(SDLK_UP)) {
|
||||
Command_CreateCursorVertical(active.view, DIR_UP);
|
||||
} else if (CtrlShiftPress(SDLK_UP)) {
|
||||
Command_Move(active.view, DIR_UP, CTRL_PRESSED, SHIFT_PRESS);
|
||||
} else if (AltPress(SDLK_UP)) {
|
||||
Command_MoveLine(active.view, DIR_UP);
|
||||
} else if (CtrlPress(SDLK_UP)) {
|
||||
Command_Move(active.view, DIR_UP, CTRL_PRESSED);
|
||||
} else if (ShiftPress(SDLK_UP)) {
|
||||
Command_Move(active.view, DIR_UP, false, SHIFT_PRESS);
|
||||
} else if (Press(SDLK_UP)) {
|
||||
Command_Move(active.view, DIR_UP);
|
||||
}
|
||||
|
||||
if (CtrlShiftPress(SDLK_LEFT)) {
|
||||
Command_Move(active.view, DIR_LEFT, CTRL_PRESSED, SHIFT_PRESS);
|
||||
} else if (CtrlPress(SDLK_LEFT)) {
|
||||
Command_Move(active.view, DIR_LEFT, CTRL_PRESSED);
|
||||
} else if (ShiftPress(SDLK_LEFT)) {
|
||||
Command_Move(active.view, DIR_LEFT, false, SHIFT_PRESS);
|
||||
} else if (AltPress(SDLK_LEFT)) {
|
||||
ActiveWindow = SwitchWindow(DIR_LEFT)->id;
|
||||
} else if (Press(SDLK_LEFT)) {
|
||||
Command_Move(active.view, DIR_LEFT);
|
||||
}
|
||||
|
||||
if (CtrlShiftPress(SDLK_RIGHT)) {
|
||||
Command_Move(active.view, DIR_RIGHT, CTRL_PRESSED, SHIFT_PRESS);
|
||||
} else if (CtrlPress(SDLK_RIGHT)) {
|
||||
Command_Move(active.view, DIR_RIGHT, CTRL_PRESSED);
|
||||
} else if (ShiftPress(SDLK_RIGHT)) {
|
||||
Command_Move(active.view, DIR_RIGHT, false, SHIFT_PRESS);
|
||||
} else if (AltPress(SDLK_RIGHT)) {
|
||||
ActiveWindow = SwitchWindow(DIR_RIGHT)->id;
|
||||
} else if (Press(SDLK_RIGHT)) {
|
||||
Command_Move(active.view, DIR_RIGHT);
|
||||
}
|
||||
|
||||
if (CtrlShiftPress(SDLK_Z)) {
|
||||
RedoEdit(active.buffer, &active.view->carets);
|
||||
} else if (CtrlPress(SDLK_Z)) {
|
||||
UndoEdit(active.buffer, &active.view->carets);
|
||||
}
|
||||
|
||||
if (CtrlPress(SDLK_C)) {
|
||||
Command_Copy(active.view);
|
||||
} else if (CtrlPress(SDLK_V)) {
|
||||
Command_Paste(active.view);
|
||||
} else if (CtrlPress(SDLK_X)) {
|
||||
SaveCaretHistoryBeforeBeginEdit(active.buffer, active.view->carets);
|
||||
Command_Copy(active.view);
|
||||
Command_Replace(active.view, u"");
|
||||
}
|
||||
|
||||
if (CtrlPress(SDLK_A)) {
|
||||
Command_SelectEntireBuffer(active.view);
|
||||
active.view->update_scroll = false;
|
||||
}
|
||||
|
||||
if (ShiftPress(SDLK_PAGEUP)) {
|
||||
Command_MoveCursorsByPageSize(active.window, DIR_UP, SHIFT_PRESS);
|
||||
} else if (CtrlPress(SDLK_PAGEUP)) {
|
||||
Command_SelectRangeOneCursor(active.view, MakeRange(0));
|
||||
} else if (Press(SDLK_PAGEUP)) {
|
||||
Command_MoveCursorsByPageSize(active.window, DIR_UP);
|
||||
}
|
||||
|
||||
if (ShiftPress(SDLK_PAGEDOWN)) {
|
||||
Command_MoveCursorsByPageSize(active.window, DIR_DOWN, SHIFT_PRESS);
|
||||
} else if (CtrlPress(SDLK_PAGEDOWN)) {
|
||||
Command_SelectRangeOneCursor(active.view, MakeRange(active.buffer->len));
|
||||
} else if (Press(SDLK_PAGEDOWN)) {
|
||||
Command_MoveCursorsByPageSize(active.window, DIR_DOWN);
|
||||
}
|
||||
|
||||
if (ShiftPress(SDLK_HOME)) {
|
||||
Command_MoveCursorsToSide(active.view, DIR_LEFT, SHIFT_PRESS);
|
||||
} else if (Press(SDLK_HOME)) {
|
||||
Command_MoveCursorsToSide(active.view, DIR_LEFT);
|
||||
}
|
||||
|
||||
if (ShiftPress(SDLK_END)) {
|
||||
Command_MoveCursorsToSide(active.view, DIR_RIGHT, SHIFT_PRESS);
|
||||
} else if (Press(SDLK_END)) {
|
||||
Command_MoveCursorsToSide(active.view, DIR_RIGHT);
|
||||
}
|
||||
|
||||
if (CtrlShiftPress(SDLK_TAB)) {
|
||||
GotoForward(main.window);
|
||||
} else if (ShiftPress(SDLK_TAB)) {
|
||||
Command_IndentSelectedLines(active.view, SHIFT_PRESS);
|
||||
} else if (CtrlPress(SDLK_TAB)) {
|
||||
GotoBackward(main.window);
|
||||
} else if (Press(SDLK_TAB)) {
|
||||
Command_IndentSelectedLines(active.view);
|
||||
}
|
||||
|
||||
if (CtrlPress(SDLK_LEFTBRACKET)) {
|
||||
Command_IndentSelectedLines(active.view, SHIFT_PRESS);
|
||||
}
|
||||
if (CtrlPress(SDLK_RIGHTBRACKET)) {
|
||||
Command_IndentSelectedLines(active.view);
|
||||
}
|
||||
|
||||
if (CtrlShiftPress(SDLK_K)) {
|
||||
Command_KillSelectedLines(active.view);
|
||||
}
|
||||
|
||||
if (CtrlPress(SDLK_BACKSPACE)) {
|
||||
Command_Delete(active.view, DIR_LEFT, CTRL_PRESSED);
|
||||
} else if (Press(SDLK_BACKSPACE)) {
|
||||
Command_Delete(active.view, DIR_LEFT);
|
||||
}
|
||||
|
||||
if (CtrlPress(SDLK_DELETE)) {
|
||||
Command_Delete(active.view, DIR_RIGHT, CTRL_PRESSED);
|
||||
} else if (Press(SDLK_DELETE)) {
|
||||
Command_Delete(active.view, DIR_RIGHT);
|
||||
}
|
||||
|
||||
// @todo: hook on textinput
|
||||
if (event.kind == EVENT_TEXT_INPUT) {
|
||||
Scratch scratch;
|
||||
String string = event.text;
|
||||
String string = event.text;
|
||||
String16 string16 = ToString16(scratch, string);
|
||||
Command_Replace(active.view, string16);
|
||||
Replace(active.view, string16);
|
||||
}
|
||||
|
||||
if (CtrlPress(SDLK_D)) {
|
||||
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.buffer, &active.view->carets);
|
||||
}
|
||||
|
||||
if (CtrlShiftPress(SDLK_N)) {
|
||||
Scratch scratch;
|
||||
String16 search_string = GetSearchString(main.window);
|
||||
Caret caret = FindPrev(main.buffer, search_string, main.view->carets[0]);
|
||||
BSet search = GetBSet(main.window->search_bar_window);
|
||||
search.window->search_bar_anchor = caret;
|
||||
Command_SelectRangeOneCursor(main.view, caret);
|
||||
} else if (CtrlPress(SDLK_N)) {
|
||||
Scratch scratch;
|
||||
String16 search_string = GetSearchString(main.window);
|
||||
Caret caret = FindNext(main.buffer, search_string, main.view->carets[0]);
|
||||
BSet search = GetBSet(main.window->search_bar_window);
|
||||
search.window->search_bar_anchor = caret;
|
||||
Command_SelectRangeOneCursor(main.view, caret);
|
||||
}
|
||||
|
||||
if (CtrlPress(SDLK_EQUALS)) {
|
||||
StyleFontSize += 1;
|
||||
ReloadFont();
|
||||
}
|
||||
if (CtrlPress(SDLK_MINUS)) {
|
||||
if (StyleFontSize > 4) {
|
||||
StyleFontSize -= 1;
|
||||
ReloadFont();
|
||||
}
|
||||
}
|
||||
|
||||
if (CtrlPress(SDLK_E)) {
|
||||
Command_GotoNextInList(active.window, 1);
|
||||
} else if (AltPress(SDLK_E)) {
|
||||
Command_GotoNextInList(active.window, -1);
|
||||
}
|
||||
|
||||
if (CtrlPress(SDLK_F)) {
|
||||
if (!active.window->is_search_bar) {
|
||||
BSet search = GetBSet(main.window->search_bar_window);
|
||||
String16 string = GetString(main.buffer, main.view->carets[0].range);
|
||||
if (string.len) {
|
||||
Command_SelectEntireBuffer(search.view);
|
||||
Command_Replace(search.view, string);
|
||||
}
|
||||
Command_SelectEntireBuffer(search.view);
|
||||
search.window->visible = 1;
|
||||
search.window->search_bar_anchor = main.view->carets[0];
|
||||
ActiveWindow = search.window->id;
|
||||
}
|
||||
}
|
||||
|
||||
if (CtrlShiftPress(SDLK_RETURN)) {
|
||||
Command_MoveCursorsToSide(active.view, DIR_LEFT);
|
||||
Command_IdentedNewLine(active.view);
|
||||
Command_Move(active.view, DIR_UP);
|
||||
} else if (CtrlPress(SDLK_RETURN)) {
|
||||
Command_MoveCursorsToSide(active.view, DIR_RIGHT);
|
||||
Command_IdentedNewLine(active.view);
|
||||
} else if (Press(SDLK_RETURN)) {
|
||||
Command_IdentedNewLine(active.view);
|
||||
}
|
||||
|
||||
|
||||
// @todo: hook PostCommand?
|
||||
// @todo: Detect if edited
|
||||
if (active.view->fuzzy_search) {
|
||||
if (!ProcessIsActive(active.view->id)) {
|
||||
Scratch scratch;
|
||||
String16 last_line_string = GetLineStringWithoutNL(active.buffer, active.buffer->line_starts.len - 1);
|
||||
Scratch scratch;
|
||||
String16 last_line_string = GetLineStringWithoutNL(active.buffer, active.buffer->line_starts.len - 1);
|
||||
if (active.view->prev_search_line != last_line_string) {
|
||||
active.view->prev_search_line = last_line_string;
|
||||
Array<FuzzyPair> ratings = FuzzySearchLines(scratch, active.buffer, 0, active.buffer->line_starts.len - 1, last_line_string);
|
||||
Array<FuzzyPair> ratings = FuzzySearchLines(scratch, active.buffer, 0, active.buffer->line_starts.len - 1, last_line_string);
|
||||
|
||||
Buffer *temp_buffer = CreateTempBuffer(scratch, active.buffer->cap);
|
||||
For(IterateInReverse(&ratings)) {
|
||||
@@ -581,99 +315,14 @@ void OnCommand(Event event) {
|
||||
RawReplaceText(temp_buffer, GetBufferEndAsRange(temp_buffer), last_line_string);
|
||||
|
||||
Caret caret = active.view->carets[0];
|
||||
Command_SelectEntireBuffer(active.view);
|
||||
Command_Replace(active.view, GetString(temp_buffer));
|
||||
SaveCaretHistoryBeforeBeginEdit(active.buffer, active.view->carets);
|
||||
SelectEntireBuffer(active.view);
|
||||
Replace(active.view, GetString(temp_buffer));
|
||||
active.view->carets[0] = caret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if (CtrlPress(SDLK_N)) {
|
||||
// Command_New();
|
||||
// }
|
||||
|
||||
if (CtrlPress(SDLK_S)) {
|
||||
SaveBuffer(active.buffer);
|
||||
}
|
||||
|
||||
if (CtrlPress(SDLK_PERIOD)) {
|
||||
String name = ChopLastSlash(main.buffer->name);
|
||||
if (EndsWith(main.buffer->name, "dirlisting")) {
|
||||
name = ChopLastSlash(name);
|
||||
}
|
||||
Command_Open(name);
|
||||
}
|
||||
|
||||
if (CtrlPress(SDLK_T)) {
|
||||
ActiveWindow = GetWindow(active.window->title_bar_window)->id;
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (CtrlShiftPress(SDLK_L)) {
|
||||
EncloseSpace(active.view);
|
||||
} else if (CtrlPress(SDLK_L)) {
|
||||
EncloseLine(active.view);
|
||||
}
|
||||
|
||||
if (CtrlShiftPress(SDLK_G)) {
|
||||
} else if (CtrlPress(SDLK_G)) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
if (CtrlPress(SDLK_SEMICOLON) || CtrlShiftPress(SDLK_Q)) {
|
||||
Command_Open(FetchLoadWord(), "exec");
|
||||
}
|
||||
else if (CtrlPress(SDLK_Q)) {
|
||||
if (active.view->fuzzy_search) {
|
||||
bool success = false;
|
||||
Range range = active.view->carets[0].range;
|
||||
if (GetSize(range) == 0) {
|
||||
Int line = PosToLine(active.buffer, range.min);
|
||||
if ((active.buffer->line_starts.len - 1) == line) {
|
||||
line = ClampBottom(0ll, line - 1ll);
|
||||
}
|
||||
|
||||
String16 string = GetLineStringWithoutNL(active.buffer, line);
|
||||
Int idx = 0;
|
||||
if (Seek(string, u"||", &idx)) {
|
||||
string = Skip(string, idx + 3);
|
||||
Command_Open(string);
|
||||
success = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
Command_Open(FetchLoadWord());
|
||||
}
|
||||
|
||||
{
|
||||
Range rng = GetLineRangeWithoutNL(active.buffer, active.buffer->line_starts.len - 1);
|
||||
GetLast(active.window->goto_history)->caret = MakeCaret(rng.max, rng.min);
|
||||
}
|
||||
} else {
|
||||
Command_Open(FetchLoadWord());
|
||||
}
|
||||
}
|
||||
|
||||
if (Press(SDLK_ESCAPE)) {
|
||||
if (active.window->is_search_bar) {
|
||||
ActiveWindow = main.window->id;
|
||||
active.window->visible = 0;
|
||||
} else if (active.window->deactivate_on_escape) {
|
||||
ActiveWindow = main.window->id;
|
||||
} else {
|
||||
active.view->carets.len = 1;
|
||||
active.view->carets[0] = MakeCaret(GetFront(active.view->carets[0]));
|
||||
}
|
||||
}
|
||||
|
||||
if (active.window->is_search_bar && buffer_change_id != active.buffer->change_id) {
|
||||
main.view->carets[0] = active.window->search_bar_anchor;
|
||||
Command_Find(main.view, GetSearchString(main.window), true);
|
||||
}
|
||||
|
||||
// :OnCommandEnding
|
||||
MergeCarets(active.buffer, &active.view->carets);
|
||||
IF_DEBUG(AssertRanges(active.view->carets));
|
||||
|
||||
@@ -33,7 +33,7 @@ void SaveStringInClipboard(String16 string) {
|
||||
_SetClipboardText(ToString(scratch, SavedClipboardString).data);
|
||||
}
|
||||
|
||||
void Command_Copy(View *view) {
|
||||
void ClipboardCopy(View *view) {
|
||||
Scratch scratch;
|
||||
Buffer *buffer = GetBuffer(view->active_buffer);
|
||||
|
||||
@@ -59,7 +59,7 @@ void Command_Copy(View *view) {
|
||||
_SetClipboardText(ToString(scratch, SavedClipboardString).data);
|
||||
}
|
||||
|
||||
void Command_Paste(View *view) {
|
||||
void ClipboardPaste(View *view) {
|
||||
Scratch scratch;
|
||||
Buffer *buffer = GetBuffer(view->active_buffer);
|
||||
const char *text = GetClipboardText();
|
||||
@@ -86,3 +86,20 @@ void Command_Paste(View *view) {
|
||||
}
|
||||
EndEdit(buffer, &edits, &view->carets, KILL_SELECTION);
|
||||
}
|
||||
|
||||
void Command_Paste(CommandContext *ctx) {
|
||||
BSet active = GetBSet(ActiveWindowID);
|
||||
ClipboardPaste(active.view);
|
||||
} RegisterCommand(Command_Paste, "ctrl-v");
|
||||
|
||||
void Command_Copy(CommandContext *ctx) {
|
||||
BSet active = GetBSet(ActiveWindowID);
|
||||
ClipboardCopy(active.view);
|
||||
} RegisterCommand(Command_Copy, "ctrl-c");
|
||||
|
||||
void Command_Cut(CommandContext *ctx) {
|
||||
BSet active = GetBSet(ActiveWindowID);
|
||||
SaveCaretHistoryBeforeBeginEdit(active.buffer, active.view->carets);
|
||||
ClipboardCopy(active.view);
|
||||
Replace(active.view, u"");
|
||||
} RegisterCommand(Command_Cut, "ctrl-x");
|
||||
@@ -46,7 +46,7 @@ void DrawVisibleText(Window *window, Color tint) {
|
||||
Rect2I visible = GetVisibleCells(window);
|
||||
for (Int line_index = visible.min.y; line_index < visible.max.y && line_index >= 0 && line_index < buffer->line_starts.len; line_index += 1) {
|
||||
String16 line_string = GetLineString(buffer, line_index);
|
||||
Vec2I pos = Vec2I{visible.min.x, line_index} * (Int)window->font->line_spacing - view->scroll + window->document_rect.min;
|
||||
Vec2I pos = Vec2I{visible.min.x, line_index} * Vec2I{(Int)window->font->char_spacing, (Int)window->font->line_spacing} - view->scroll + window->document_rect.min;
|
||||
|
||||
float text_offset_x = 0;
|
||||
for (Int col_index = visible.min.x; col_index < visible.max.x && col_index >= 0 && col_index < line_string.len; col_index += 1) {
|
||||
@@ -105,8 +105,8 @@ void DrawWindow(Window *window, Event &event) {
|
||||
Rect2 screen_rect = Rect0Size(event.xwindow, event.ywindow);
|
||||
SetScissor(screen_rect);
|
||||
|
||||
bool is_actib = window->id == ActiveWindow || window->title_bar_window == ActiveWindow || window->search_bar_window == ActiveWindow;
|
||||
bool is_active = window->id == ActiveWindow;
|
||||
bool is_active = window->id == ActiveWindowID;
|
||||
bool active_layed_out_doc = window->id == LastActiveLayoutWindowID;
|
||||
|
||||
Color color_whitespace_during_selection = ColorWhitespaceDuringSelection;
|
||||
Color color_background = ColorBackground;
|
||||
@@ -116,7 +116,8 @@ void DrawWindow(Window *window, Event &event) {
|
||||
Color color_sub_caret = ColorSubCaret;
|
||||
Color color_text_line_numbers = ColorTextLineNumbers;
|
||||
Color color_text = ColorText;
|
||||
if (window->is_title_bar || window->is_search_bar) {
|
||||
|
||||
if (window->draw_darker) {
|
||||
if (is_active) {
|
||||
color_background = ColorTitleBarActiveBackground;
|
||||
} else {
|
||||
@@ -127,7 +128,12 @@ void DrawWindow(Window *window, Event &event) {
|
||||
color_line_highlight = ColorTitleBarBackground;
|
||||
}
|
||||
|
||||
|
||||
DrawRect(window->total_rect, color_background);
|
||||
if (window->draw_darker) {
|
||||
Rect2I rect = window->total_rect;
|
||||
DrawRect(CutTop(&rect, 1), ColorResizerOutline);
|
||||
}
|
||||
|
||||
Rect2I combined_document_line_number = window->document_rect;
|
||||
if (window->draw_line_numbers) combined_document_line_number.min.x = window->line_numbers_rect.min.x;
|
||||
@@ -182,7 +188,7 @@ void DrawWindow(Window *window, Event &event) {
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (!window->is_title_bar && !window->is_search_bar) {
|
||||
} else if (window->draw_line_highlight) {
|
||||
//
|
||||
// Draw highlight
|
||||
Int front = GetFront(it);
|
||||
@@ -226,8 +232,9 @@ void DrawWindow(Window *window, Event &event) {
|
||||
EndProfileScope();
|
||||
DrawVisibleText(window, color_text);
|
||||
|
||||
BeginProfileScope(draw_carets);
|
||||
if (is_actib) {
|
||||
// Draw caret "|" markings
|
||||
if (is_active) {
|
||||
BeginProfileScope(draw_carets);
|
||||
For(view->carets) {
|
||||
Int front = GetFront(it);
|
||||
XY fxy = PosToXY(buffer, front);
|
||||
@@ -236,8 +243,8 @@ void DrawWindow(Window *window, Event &event) {
|
||||
DrawCaret(window, fxy, 0.3f, main_caret ? color_main_caret : color_sub_caret);
|
||||
}
|
||||
}
|
||||
EndProfileScope();
|
||||
}
|
||||
EndProfileScope();
|
||||
|
||||
// Draw line numbers
|
||||
if (window->draw_line_numbers) {
|
||||
@@ -271,31 +278,22 @@ void DrawWindow(Window *window, Event &event) {
|
||||
DrawRect(rect, color);
|
||||
}
|
||||
|
||||
if (window->z == 1) {
|
||||
// color the floating object to make it stand out
|
||||
if (window->z >= 1) {
|
||||
SetScissor(window->total_rect);
|
||||
DrawRect(window->total_rect, {255, 255, 255, 25});
|
||||
}
|
||||
|
||||
if (!is_actib) {
|
||||
// darken the inactive windows
|
||||
if (!is_active) {
|
||||
SetScissor(screen_rect);
|
||||
DrawRect(window->total_rect, ColorInactiveWindow);
|
||||
}
|
||||
}
|
||||
|
||||
void DrawSplits(WindowSplit *split) {
|
||||
if (split == NULL) {
|
||||
return;
|
||||
// Draw resizer rect
|
||||
{
|
||||
Rect2I rect = window->resizer_rect;
|
||||
DrawRect(rect, ColorResizerBackground);
|
||||
DrawRect(CutRight(&rect, 1), ColorResizerOutline);
|
||||
}
|
||||
Rect2I rect = split->resizer_rect;
|
||||
DrawRect(split->resizer_rect, ColorResizerBackground);
|
||||
if (split->kind == WindowSplitKind_Vertical) {
|
||||
Rect2I s = CutRight(&rect, 1);
|
||||
DrawRect(s, ColorResizerOutline);
|
||||
} else if (split->kind == WindowSplitKind_Horizontal) {
|
||||
Rect2I s = CutBottom(&rect, 1);
|
||||
DrawRect(s, ColorResizerOutline);
|
||||
}
|
||||
|
||||
DrawSplits(split->left);
|
||||
DrawSplits(split->right);
|
||||
}
|
||||
@@ -1,60 +1,3 @@
|
||||
#define EVENT_KINDS \
|
||||
X(EVENT_NONE) \
|
||||
X(EVENT_UPDATE) \
|
||||
X(EVENT_QUIT) \
|
||||
X(EVENT_MOUSE_LEFT) \
|
||||
X(EVENT_MOUSE_RIGHT) \
|
||||
X(EVENT_MOUSE_MIDDLE) \
|
||||
X(EVENT_MOUSE_X1) \
|
||||
X(EVENT_MOUSE_X2) \
|
||||
X(EVENT_MOUSE_LEFT_UP) \
|
||||
X(EVENT_MOUSE_RIGHT_UP) \
|
||||
X(EVENT_MOUSE_MIDDLE_UP) \
|
||||
X(EVENT_MOUSE_X1_UP) \
|
||||
X(EVENT_MOUSE_X2_UP) \
|
||||
X(EVENT_MOUSE_WHEEL) \
|
||||
X(EVENT_KEY_PRESS) \
|
||||
X(EVENT_TEXT_INPUT) \
|
||||
X(EVENT_DROP_FILE)
|
||||
|
||||
enum EventKind {
|
||||
#define X(TYPE) TYPE,
|
||||
EVENT_KINDS
|
||||
#undef X
|
||||
EVENT_KIND_COUNT,
|
||||
EVENT_KIND_INVALID = 111,
|
||||
};
|
||||
|
||||
const char *EventKindStrings[] = {
|
||||
#define X(TYPE) #TYPE,
|
||||
EVENT_KINDS
|
||||
#undef X
|
||||
};
|
||||
|
||||
#define EVENT_FIELDS \
|
||||
X(EventKind, Int, kind) \
|
||||
X(SDL_Keycode, Int, key) \
|
||||
X(int16_t, Int, xwindow) \
|
||||
X(int16_t, Int, ywindow) \
|
||||
X(int16_t, Int, xmouse) \
|
||||
X(int16_t, Int, ymouse) \
|
||||
X(uint8_t, Int, clicks) \
|
||||
X(uint8_t, Int, shift) \
|
||||
X(uint8_t, Int, ctrl) \
|
||||
X(uint8_t, Int, alt) \
|
||||
X(uint8_t, Int, super) \
|
||||
X(float, Float, xwheel) \
|
||||
X(float, Float, ywheel) \
|
||||
X(char *, String, text)
|
||||
#define EVENT_FIELD_COUNT 14
|
||||
|
||||
struct Event {
|
||||
#define X(TYPE, KIND, NAME) TYPE NAME;
|
||||
EVENT_FIELDS
|
||||
#undef X
|
||||
};
|
||||
Array<Event> EventPlayback;
|
||||
|
||||
const char *SDLKeycodeToName(SDL_Keycode keycode) {
|
||||
switch(keycode) {
|
||||
case SDLK_UNKNOWN: return "SDLK_UNKNOWN"; break;
|
||||
@@ -317,6 +260,230 @@ const char *SDLKeycodeToName(SDL_Keycode keycode) {
|
||||
}
|
||||
}
|
||||
|
||||
struct { String string; EventKind value; } MouseConversionTable[] = {
|
||||
{"mousex1", EVENT_MOUSE_X1},
|
||||
{"mousex2", EVENT_MOUSE_X2},
|
||||
{"mouseleft", EVENT_MOUSE_LEFT},
|
||||
{"mouseright", EVENT_MOUSE_RIGHT},
|
||||
{"mousemiddle", EVENT_MOUSE_MIDDLE},
|
||||
};
|
||||
|
||||
struct { String string; SDL_Keycode value; } SDLKeycodeConversionTable[] = {
|
||||
{"enter", SDLK_RETURN},
|
||||
{"escape", SDLK_ESCAPE},
|
||||
{"backspace", SDLK_BACKSPACE},
|
||||
{"tab", SDLK_TAB},
|
||||
{"space", SDLK_SPACE},
|
||||
{"minus", SDLK_MINUS},
|
||||
{"period", SDLK_PERIOD},
|
||||
{"slash", SDLK_SLASH},
|
||||
{"0", SDLK_0},
|
||||
{"1", SDLK_1},
|
||||
{"2", SDLK_2},
|
||||
{"3", SDLK_3},
|
||||
{"4", SDLK_4},
|
||||
{"5", SDLK_5},
|
||||
{"6", SDLK_6},
|
||||
{"7", SDLK_7},
|
||||
{"8", SDLK_8},
|
||||
{"9", SDLK_9},
|
||||
{"semicolon", SDLK_SEMICOLON},
|
||||
{"less", SDLK_LESS},
|
||||
{"equals", SDLK_EQUALS},
|
||||
{"greater", SDLK_GREATER},
|
||||
{"leftbracket", SDLK_LEFTBRACKET},
|
||||
{"backslash", SDLK_BACKSLASH},
|
||||
{"rightbracket", SDLK_RIGHTBRACKET},
|
||||
{"grave", SDLK_GRAVE},
|
||||
{"a", SDLK_A},
|
||||
{"b", SDLK_B},
|
||||
{"c", SDLK_C},
|
||||
{"d", SDLK_D},
|
||||
{"e", SDLK_E},
|
||||
{"f", SDLK_F},
|
||||
{"g", SDLK_G},
|
||||
{"h", SDLK_H},
|
||||
{"i", SDLK_I},
|
||||
{"j", SDLK_J},
|
||||
{"k", SDLK_K},
|
||||
{"l", SDLK_L},
|
||||
{"m", SDLK_M},
|
||||
{"n", SDLK_N},
|
||||
{"o", SDLK_O},
|
||||
{"p", SDLK_P},
|
||||
{"q", SDLK_Q},
|
||||
{"r", SDLK_R},
|
||||
{"s", SDLK_S},
|
||||
{"t", SDLK_T},
|
||||
{"u", SDLK_U},
|
||||
{"v", SDLK_V},
|
||||
{"w", SDLK_W},
|
||||
{"x", SDLK_X},
|
||||
{"y", SDLK_Y},
|
||||
{"z", SDLK_Z},
|
||||
{"delete", SDLK_DELETE},
|
||||
{"capslock", SDLK_CAPSLOCK},
|
||||
{"f1", SDLK_F1},
|
||||
{"f2", SDLK_F2},
|
||||
{"f3", SDLK_F3},
|
||||
{"f4", SDLK_F4},
|
||||
{"f5", SDLK_F5},
|
||||
{"f6", SDLK_F6},
|
||||
{"f7", SDLK_F7},
|
||||
{"f8", SDLK_F8},
|
||||
{"f9", SDLK_F9},
|
||||
{"f10", SDLK_F10},
|
||||
{"f11", SDLK_F11},
|
||||
{"f12", SDLK_F12},
|
||||
{"insert", SDLK_INSERT},
|
||||
{"home", SDLK_HOME},
|
||||
{"pageup", SDLK_PAGEUP},
|
||||
{"end", SDLK_END},
|
||||
{"pagedown", SDLK_PAGEDOWN},
|
||||
{"right", SDLK_RIGHT},
|
||||
{"left", SDLK_LEFT},
|
||||
{"down", SDLK_DOWN},
|
||||
{"up", SDLK_UP},
|
||||
};
|
||||
|
||||
void FillEventWithBasicData(Event *event) {
|
||||
SDL_Keymod mod = SDL_GetModState();
|
||||
event->shift = (mod & SDL_KMOD_SHIFT) != 0;
|
||||
event->ctrl = (mod & SDL_KMOD_CTRL) != 0;
|
||||
event->alt = (mod & SDL_KMOD_ALT) != 0;
|
||||
event->super = (mod & SDL_KMOD_GUI) != 0;
|
||||
|
||||
float xmouse, ymouse;
|
||||
SDL_GetMouseState(&xmouse, &ymouse);
|
||||
event->xmouse = (int16_t)roundf(DPIScale * xmouse);
|
||||
event->ymouse = (int16_t)roundf(DPIScale * ymouse);
|
||||
|
||||
int xwindow, ywindow;
|
||||
SDL_GetWindowSizeInPixels(SDLWindow, &xwindow, &ywindow);
|
||||
event->xwindow = xwindow;
|
||||
event->ywindow = ywindow;
|
||||
event->text = "";
|
||||
}
|
||||
|
||||
Event TranslateSDLEvent(SDL_Event *input_event) {
|
||||
ProfileFunction();
|
||||
Event event = {};
|
||||
FillEventWithBasicData(&event);
|
||||
|
||||
switch (input_event->type) {
|
||||
case SDL_EVENT_QUIT: {
|
||||
event.kind = EVENT_QUIT;
|
||||
} break;
|
||||
|
||||
case SDL_EVENT_KEY_DOWN: {
|
||||
event.kind = EVENT_KEY_PRESS;
|
||||
SDL_KeyboardEvent &key = input_event->key;
|
||||
event.key = key.key;
|
||||
} break;
|
||||
|
||||
case SDL_EVENT_TEXT_INPUT: {
|
||||
event.kind = EVENT_TEXT_INPUT;
|
||||
SDL_TextInputEvent &b = input_event->text;
|
||||
String string = b.text;
|
||||
event.text = Intern(&GlobalInternTable, string).data;
|
||||
} break;
|
||||
|
||||
case SDL_EVENT_MOUSE_BUTTON_DOWN: {
|
||||
SDL_MouseButtonEvent &b = input_event->button;
|
||||
event.xmouse = (int16_t)roundf(DPIScale * b.x);
|
||||
event.ymouse = (int16_t)roundf(DPIScale * b.y);
|
||||
event.clicks = b.clicks;
|
||||
if (b.button == SDL_BUTTON_LEFT) {
|
||||
event.kind = EVENT_MOUSE_LEFT;
|
||||
} else if (b.button == SDL_BUTTON_RIGHT) {
|
||||
event.kind = EVENT_MOUSE_RIGHT;
|
||||
} else if (b.button == SDL_BUTTON_MIDDLE) {
|
||||
event.kind = EVENT_MOUSE_MIDDLE;
|
||||
} 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;
|
||||
|
||||
case SDL_EVENT_MOUSE_BUTTON_UP: {
|
||||
SDL_MouseButtonEvent &b = input_event->button;
|
||||
event.xmouse = (int16_t)roundf(DPIScale * b.x);
|
||||
event.ymouse = (int16_t)roundf(DPIScale * b.y);
|
||||
if (b.button == SDL_BUTTON_LEFT) {
|
||||
event.kind = EVENT_MOUSE_LEFT_UP;
|
||||
} else if (b.button == SDL_BUTTON_RIGHT) {
|
||||
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;
|
||||
}
|
||||
} break;
|
||||
|
||||
case SDL_EVENT_MOUSE_WHEEL: {
|
||||
event.kind = EVENT_MOUSE_WHEEL;
|
||||
SDL_MouseWheelEvent &b = input_event->wheel;
|
||||
event.xmouse = (int16_t)roundf(DPIScale * b.x);
|
||||
event.ymouse = (int16_t)roundf(DPIScale * b.y);
|
||||
event.xwheel = b.x;
|
||||
event.ywheel = b.y;
|
||||
} break;
|
||||
|
||||
case SDL_EVENT_MOUSE_MOTION: {
|
||||
event.kind = EVENT_UPDATE;
|
||||
} break;
|
||||
|
||||
case SDL_EVENT_DROP_FILE: {
|
||||
event.kind = EVENT_DROP_FILE;
|
||||
SDL_DropEvent &b = input_event->drop;
|
||||
String string = b.data;
|
||||
event.text = Intern(&GlobalInternTable, string).data;
|
||||
} break;
|
||||
default: {
|
||||
};
|
||||
}
|
||||
|
||||
return event;
|
||||
}
|
||||
|
||||
Array<Event> GetEventsForFrame(Allocator allocator) {
|
||||
Array<Event> result = {allocator};
|
||||
if (EventPlayback.len) {
|
||||
result = TightCopy(allocator, EventPlayback);
|
||||
EventPlayback.len = 0;
|
||||
}
|
||||
|
||||
SDL_Event event;
|
||||
if (WaitForEvents) {
|
||||
SDL_WaitEvent(&event);
|
||||
Event ev = TranslateSDLEvent(&event);
|
||||
if (ev.kind != EVENT_NONE) Add(&result, ev);
|
||||
}
|
||||
|
||||
while (SDL_PollEvent(&event)) {
|
||||
Event ev = TranslateSDLEvent(&event);
|
||||
if (ev.kind != EVENT_NONE) Add(&result, ev);
|
||||
}
|
||||
|
||||
if (result.len == 0) {
|
||||
Event event = {};
|
||||
FillEventWithBasicData(&event);
|
||||
event.kind = EVENT_UPDATE;
|
||||
Add(&result, event);
|
||||
}
|
||||
|
||||
Assert(result.len);
|
||||
return result;
|
||||
}
|
||||
|
||||
struct Serializer {
|
||||
Buffer *buffer; // for writing
|
||||
};
|
||||
@@ -386,17 +553,6 @@ void Serialize(Serializer *s, Event *e) {
|
||||
SerializeEnd(s);
|
||||
}
|
||||
|
||||
const int DIR_RIGHT = 0;
|
||||
const int DIR_LEFT = 1;
|
||||
const int DIR_DOWN = 2;
|
||||
const int DIR_UP = 3;
|
||||
const int DIR_COUNT = 4;
|
||||
const bool CTRL_PRESSED = true;
|
||||
const bool SHIFT_PRESS = true;
|
||||
|
||||
bool AppIsRunning = true;
|
||||
bool WaitForEvents = true;
|
||||
|
||||
#define Ctrl() event.ctrl
|
||||
#define Alt() event.alt
|
||||
#define Shift() event.shift
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
void ReloadStyle() {
|
||||
ColorText = GetColor("Text", ColorText);
|
||||
ColorLoadTextHighlight = GetColor("LoadTextHighlight", ColorLoadTextHighlight);
|
||||
ColorBackground = GetColor("Background", ColorBackground);
|
||||
ColorInactiveWindow = GetColor("InactiveWindow", ColorInactiveWindow);
|
||||
ColorTextLineNumbers = GetColor("TextLineNumbers", ColorTextLineNumbers);
|
||||
ColorLineHighlight = GetColor("LineHighlight", ColorLineHighlight);
|
||||
ColorMainCaret = GetColor("MainCaret", ColorMainCaret);
|
||||
ColorSubCaret = GetColor("SubCaret", ColorSubCaret);
|
||||
ColorSelection = GetColor("Selection", ColorSelection);
|
||||
ColorWhitespaceDuringSelection = GetColor("WhitespaceDuringSelection", ColorWhitespaceDuringSelection);
|
||||
ColorMouseUnderline = GetColor("MouseUnderline", ColorMouseUnderline);
|
||||
ColorCaretUnderline = GetColor("CaretUnderline", ColorCaretUnderline);
|
||||
ColorFuzzySearchLineHighlight = GetColor("FuzzySearchLineHighlight", ColorFuzzySearchLineHighlight);
|
||||
ColorScrollbarBackground = GetColor("ScrollbarBackground", ColorScrollbarBackground);
|
||||
ColorScrollbarScroller = GetColor("ScrollbarScroller", ColorScrollbarScroller);
|
||||
ColorScrollbarScrollerSelected = GetColor("ScrollbarScrollerSelected", ColorScrollbarScrollerSelected);
|
||||
ColorTitleBarText = GetColor("TitleBarText", ColorTitleBarText);
|
||||
ColorTitleBarBackground = GetColor("TitleBarBackground", ColorTitleBarBackground);
|
||||
ColorTitleBarActiveBackground = GetColor("TitleBarActiveBackground", ColorTitleBarActiveBackground);
|
||||
ColorTitleBarSelection = GetColor("TitleBarSelection", ColorTitleBarSelection);
|
||||
ColorResizerBackground = GetColor("ResizerBackground", ColorResizerBackground);
|
||||
ColorResizerOutline = GetColor("ResizerOutline", ColorResizerOutline);
|
||||
StyleWaitForEvents = GetStyleInt("WaitForEvents", StyleWaitForEvents);
|
||||
StyleDrawLineNumbers = GetStyleInt("DrawLineNumbers", StyleDrawLineNumbers);
|
||||
StyleDrawScrollbar = GetStyleInt("DrawScrollbar", StyleDrawScrollbar);
|
||||
StyleIndentSize = GetStyleInt("IndentSize", StyleIndentSize);
|
||||
StyleFontSize = GetStyleInt("FontSize", StyleFontSize);
|
||||
StyleFontFilter = GetStyleInt("FontFilter", StyleFontFilter);
|
||||
StyleFont = GetStyleString("Font", StyleFont);
|
||||
StyleVCVarsall = GetStyleString("VCVarsall", StyleVCVarsall);
|
||||
}
|
||||
@@ -69,6 +69,7 @@ Style.Font = GetExeDir().."/CascadiaMono.ttf"
|
||||
Style.VCVarsall = "C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Auxiliary/Build/vcvars64.bat"
|
||||
Style.TrimWhitespaceOnSave = true
|
||||
Style.ClangFormatOnSave = false
|
||||
Style.StyleUndoMergeTimeout = 0.3
|
||||
|
||||
INTERNET_BROWSER = 'C:/Program Files/BraveSoftware/Brave-Browser/Application/brave.exe'
|
||||
OS_WINDOWS = 0
|
||||
@@ -324,10 +325,6 @@ function MatchGotoBuild(s, meta)
|
||||
end
|
||||
|
||||
function MatchExec(s, meta)
|
||||
if meta ~= "exec" then
|
||||
return nil
|
||||
end
|
||||
|
||||
if s:match(".exe$") or s:match(".bat$") or s:match(".sh$") then
|
||||
return {kind = "exec_console", cmd = s, working_dir = GetMainDir()}
|
||||
end
|
||||
@@ -336,8 +333,7 @@ function MatchExec(s, meta)
|
||||
return {kind = "exec_console", cmd = s:sub(2, -1), working_dir = GetMainDir()}
|
||||
end
|
||||
|
||||
Eval(s)
|
||||
return {kind = "skip"}
|
||||
return nil
|
||||
end
|
||||
|
||||
BuiltinOnOpenMatchers = {
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
Color GruvboxDark0Hard = {0x1d, 0x20, 0x21, 0xff};
|
||||
Color GruvboxDark0 = {0x28, 0x28, 0x28, 0xff};
|
||||
Color GruvboxDark0Soft = {0x32, 0x30, 0x2f, 0xff};
|
||||
Color GruvboxDark1 = {0x3c, 0x38, 0x36, 0xff};
|
||||
Color GruvboxDark2 = {0x50, 0x49, 0x45, 0xff};
|
||||
Color GruvboxDark3 = {0x66, 0x5c, 0x54, 0xff};
|
||||
Color GruvboxDark4 = {0x7c, 0x6f, 0x64, 0xff};
|
||||
Color GruvboxGray245 = {0x92, 0x83, 0x74, 0xff};
|
||||
Color GruvboxGray244 = {0x92, 0x83, 0x74, 0xff};
|
||||
Color GruvboxLight0Hard = {0xf9, 0xf5, 0xd7, 0xff};
|
||||
Color GruvboxLight0 = {0xfb, 0xf1, 0xc7, 0xff};
|
||||
Color GruvboxLight0Soft = {0xf2, 0xe5, 0xbc, 0xff};
|
||||
Color GruvboxLight1 = {0xeb, 0xdb, 0xb2, 0xff};
|
||||
Color GruvboxLight2 = {0xd5, 0xc4, 0xa1, 0xff};
|
||||
Color GruvboxLight3 = {0xbd, 0xae, 0x93, 0xff};
|
||||
Color GruvboxLight4 = {0xa8, 0x99, 0x84, 0xff};
|
||||
Color GruvboxBrightRed = {0xfb, 0x49, 0x34, 0xff};
|
||||
Color GruvboxBrightGreen = {0xb8, 0xbb, 0x26, 0xff};
|
||||
Color GruvboxBrightYellow = {0xfa, 0xbd, 0x2f, 0xff};
|
||||
Color GruvboxBrightBlue = {0x83, 0xa5, 0x98, 0xff};
|
||||
Color GruvboxBrightPurple = {0xd3, 0x86, 0x9b, 0xff};
|
||||
Color GruvboxBrightAqua = {0x8e, 0xc0, 0x7c, 0xff};
|
||||
Color GruvboxBrightOrange = {0xfe, 0x80, 0x19, 0xff};
|
||||
Color GruvboxNeutralRed = {0xcc, 0x24, 0x1d, 0xff};
|
||||
Color GruvboxNeutralGreen = {0x98, 0x97, 0x1a, 0xff};
|
||||
Color GruvboxNeutralYellow = {0xd7, 0x99, 0x21, 0xff};
|
||||
Color GruvboxNeutralBlue = {0x45, 0x85, 0x88, 0xff};
|
||||
Color GruvboxNeutralPurple = {0xb1, 0x62, 0x86, 0xff};
|
||||
Color GruvboxNeutralAqua = {0x68, 0x9d, 0x6a, 0xff};
|
||||
Color GruvboxNeutralOrange = {0xd6, 0x5d, 0x0e, 0xff};
|
||||
Color GruvboxFadedRed = {0x9d, 0x00, 0x06, 0xff};
|
||||
Color GruvboxFadedGreen = {0x79, 0x74, 0x0e, 0xff};
|
||||
Color GruvboxFadedYellow = {0xb5, 0x76, 0x14, 0xff};
|
||||
Color GruvboxFadedBlue = {0x07, 0x66, 0x78, 0xff};
|
||||
Color GruvboxFadedPurple = {0x8f, 0x3f, 0x71, 0xff};
|
||||
Color GruvboxFadedAqua = {0x42, 0x7b, 0x58, 0xff};
|
||||
Color GruvboxFadedOrange = {0xaf, 0x3a, 0x03, 0xff};
|
||||
Color ColorText = GruvboxDark0Hard;
|
||||
Color ColorLoadTextHighlight = {0x00, 0x00, 0x00, 0x0F};
|
||||
Color ColorBackground = GruvboxLight0Hard;
|
||||
Color ColorInactiveWindow = {0x00, 0x00, 0x00, 0x0F};
|
||||
Color ColorTextLineNumbers = GruvboxDark4;
|
||||
Color ColorLineHighlight = GruvboxLight0Soft;
|
||||
Color ColorMainCaret = GruvboxDark0Hard;
|
||||
Color ColorSubCaret = GruvboxGray245;
|
||||
Color ColorSelection = GruvboxLight1;
|
||||
Color ColorWhitespaceDuringSelection = GruvboxLight4;
|
||||
Color ColorMouseUnderline = GruvboxDark0Hard;
|
||||
Color ColorCaretUnderline = GruvboxGray245;
|
||||
Color ColorFuzzySearchLineHighlight = GruvboxDark0;
|
||||
Color ColorScrollbarBackground = GruvboxLight2;
|
||||
Color ColorScrollbarScroller = GruvboxLight1;
|
||||
Color ColorScrollbarScrollerSelected = GruvboxLight0Hard;
|
||||
Color ColorTitleBarText = GruvboxDark2;
|
||||
Color ColorTitleBarBackground = GruvboxLight1;
|
||||
Color ColorTitleBarActiveBackground = {0xfe, 0xfe, 0xfe, 0xfe};
|
||||
Color ColorTitleBarSelection = GruvboxLight3;
|
||||
Color ColorResizerBackground = GruvboxLight0Hard;
|
||||
Color ColorResizerOutline = GruvboxLight3;
|
||||
Int StyleWaitForEvents = 1;
|
||||
Int StyleDrawLineNumbers = 1;
|
||||
Int StyleDrawScrollbar = 1;
|
||||
Int StyleIndentSize = 4;
|
||||
Int StyleFontSize = 15;
|
||||
Int StyleFontFilter = 0;
|
||||
String StyleFont = "/home/krz/text_editor/package/CascadiaMono.ttf";
|
||||
String StyleVCVarsall = "C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Auxiliary/Build/vcvars64.bat";
|
||||
222
src/text_editor/globals.cpp
Normal file
222
src/text_editor/globals.cpp
Normal file
@@ -0,0 +1,222 @@
|
||||
SDL_Window *SDLWindow;
|
||||
bool IsInFullscreen;
|
||||
int FullScreenSizeX, FullScreenSizeY;
|
||||
int FullScreenPositionX, FullScreenPositionY;
|
||||
bool Testing = false;
|
||||
bool AppIsRunning = true;
|
||||
bool WaitForEvents = true;
|
||||
|
||||
WindowID WindowIDs;
|
||||
ViewID ViewIDs;
|
||||
Int BufferIDs;
|
||||
|
||||
Array<Window *> Windows;
|
||||
Array<View *> Views;
|
||||
Array<Buffer *> Buffers;
|
||||
|
||||
// console
|
||||
BufferID NullBufferID;
|
||||
ViewID NullViewID;
|
||||
WindowID NullWindowID;
|
||||
|
||||
// hidden floating window
|
||||
WindowID DebugWindowID;
|
||||
ViewID DebugViewID;
|
||||
BufferID DebugBufferID;
|
||||
|
||||
WindowID CommandBarWindowID;
|
||||
WindowID StatusBarWindowID;
|
||||
WindowID SearchBarWindowID;
|
||||
ViewID SearchViewID;
|
||||
BufferID SearchBufferID;
|
||||
|
||||
WindowID ActiveWindowID;
|
||||
WindowID LastActiveLayoutWindowID;
|
||||
WindowID ScrollbarSelected = {-1};
|
||||
WindowID DocumentSelected = {-1};
|
||||
WindowID ResizerSelected = {-1};
|
||||
WindowID ResizerHover = {-1};
|
||||
Caret DocumentAnchor;
|
||||
|
||||
Buffer *LuaProjectBuffer;
|
||||
Buffer *LuaConfigBuffer;
|
||||
Buffer *GCInfoBuffer;
|
||||
Buffer *EventBuffer;
|
||||
Buffer *ScratchBuffer;
|
||||
Buffer *TraceBuffer;
|
||||
View *TraceView;
|
||||
|
||||
String WorkDir;
|
||||
RandomSeed UniqueBufferNameSeed = {};
|
||||
Array<Event> EventPlayback;
|
||||
lua_State *LuaState = NULL;
|
||||
BlockArena Perm;
|
||||
Event *OnCommandEvent;
|
||||
|
||||
// clipboard
|
||||
BlockArena ClipboardArena;
|
||||
String16 SavedClipboardString;
|
||||
Array<String16> SavedClipboardCarets = {SysAllocator};
|
||||
|
||||
struct InternTable {
|
||||
HashTable<String> strings; // general allocator
|
||||
BlockArena arena;
|
||||
};
|
||||
|
||||
String Intern(InternTable *table, String string) {
|
||||
String *value = table->strings.get(string);
|
||||
if (!value) {
|
||||
String copy = Copy(table->arena, string);
|
||||
table->strings.put(copy, copy);
|
||||
return copy;
|
||||
}
|
||||
return *value;
|
||||
}
|
||||
|
||||
// We use the intern table to optimize for space, I don't want to worry about freeing
|
||||
// buffer names and event text data so let it all accumulate and interning will at least
|
||||
// optimize worst offenders (like event text)
|
||||
InternTable GlobalInternTable;
|
||||
|
||||
///////////////////////////////
|
||||
// CONFIG
|
||||
Color GruvboxDark0Hard = {0x1d, 0x20, 0x21, 0xff};
|
||||
Color GruvboxDark0 = {0x28, 0x28, 0x28, 0xff};
|
||||
Color GruvboxDark0Soft = {0x32, 0x30, 0x2f, 0xff};
|
||||
Color GruvboxDark1 = {0x3c, 0x38, 0x36, 0xff};
|
||||
Color GruvboxDark2 = {0x50, 0x49, 0x45, 0xff};
|
||||
Color GruvboxDark3 = {0x66, 0x5c, 0x54, 0xff};
|
||||
Color GruvboxDark4 = {0x7c, 0x6f, 0x64, 0xff};
|
||||
Color GruvboxGray245 = {0x92, 0x83, 0x74, 0xff};
|
||||
Color GruvboxGray244 = {0x92, 0x83, 0x74, 0xff};
|
||||
Color GruvboxLight0Hard = {0xf9, 0xf5, 0xd7, 0xff};
|
||||
Color GruvboxLight0 = {0xfb, 0xf1, 0xc7, 0xff};
|
||||
Color GruvboxLight0Soft = {0xf2, 0xe5, 0xbc, 0xff};
|
||||
Color GruvboxLight1 = {0xeb, 0xdb, 0xb2, 0xff};
|
||||
Color GruvboxLight2 = {0xd5, 0xc4, 0xa1, 0xff};
|
||||
Color GruvboxLight3 = {0xbd, 0xae, 0x93, 0xff};
|
||||
Color GruvboxLight4 = {0xa8, 0x99, 0x84, 0xff};
|
||||
Color GruvboxBrightRed = {0xfb, 0x49, 0x34, 0xff};
|
||||
Color GruvboxBrightGreen = {0xb8, 0xbb, 0x26, 0xff};
|
||||
Color GruvboxBrightYellow = {0xfa, 0xbd, 0x2f, 0xff};
|
||||
Color GruvboxBrightBlue = {0x83, 0xa5, 0x98, 0xff};
|
||||
Color GruvboxBrightPurple = {0xd3, 0x86, 0x9b, 0xff};
|
||||
Color GruvboxBrightAqua = {0x8e, 0xc0, 0x7c, 0xff};
|
||||
Color GruvboxBrightOrange = {0xfe, 0x80, 0x19, 0xff};
|
||||
Color GruvboxNeutralRed = {0xcc, 0x24, 0x1d, 0xff};
|
||||
Color GruvboxNeutralGreen = {0x98, 0x97, 0x1a, 0xff};
|
||||
Color GruvboxNeutralYellow = {0xd7, 0x99, 0x21, 0xff};
|
||||
Color GruvboxNeutralBlue = {0x45, 0x85, 0x88, 0xff};
|
||||
Color GruvboxNeutralPurple = {0xb1, 0x62, 0x86, 0xff};
|
||||
Color GruvboxNeutralAqua = {0x68, 0x9d, 0x6a, 0xff};
|
||||
Color GruvboxNeutralOrange = {0xd6, 0x5d, 0x0e, 0xff};
|
||||
Color GruvboxFadedRed = {0x9d, 0x00, 0x06, 0xff};
|
||||
Color GruvboxFadedGreen = {0x79, 0x74, 0x0e, 0xff};
|
||||
Color GruvboxFadedYellow = {0xb5, 0x76, 0x14, 0xff};
|
||||
Color GruvboxFadedBlue = {0x07, 0x66, 0x78, 0xff};
|
||||
Color GruvboxFadedPurple = {0x8f, 0x3f, 0x71, 0xff};
|
||||
Color GruvboxFadedAqua = {0x42, 0x7b, 0x58, 0xff};
|
||||
Color GruvboxFadedOrange = {0xaf, 0x3a, 0x03, 0xff};
|
||||
Color ColorText = GruvboxDark0Hard;
|
||||
Color ColorLoadTextHighlight = {0x00, 0x00, 0x00, 0x0F};
|
||||
Color ColorBackground = GruvboxLight0Hard;
|
||||
Color ColorInactiveWindow = {0x00, 0x00, 0x00, 0x0F};
|
||||
Color ColorTextLineNumbers = GruvboxDark4;
|
||||
Color ColorLineHighlight = GruvboxLight0Soft;
|
||||
Color ColorMainCaret = GruvboxDark0Hard;
|
||||
Color ColorSubCaret = GruvboxGray245;
|
||||
Color ColorSelection = GruvboxLight1;
|
||||
Color ColorWhitespaceDuringSelection = GruvboxLight4;
|
||||
Color ColorMouseUnderline = GruvboxDark0Hard;
|
||||
Color ColorCaretUnderline = GruvboxGray245;
|
||||
Color ColorFuzzySearchLineHighlight = GruvboxDark0;
|
||||
Color ColorScrollbarBackground = GruvboxLight2;
|
||||
Color ColorScrollbarScroller = GruvboxLight1;
|
||||
Color ColorScrollbarScrollerSelected = GruvboxLight0Hard;
|
||||
Color ColorTitleBarText = GruvboxDark2;
|
||||
Color ColorTitleBarBackground = GruvboxLight1;
|
||||
Color ColorTitleBarActiveBackground = {0xfe, 0xfe, 0xfe, 0xfe};
|
||||
Color ColorTitleBarSelection = GruvboxLight3;
|
||||
Color ColorResizerBackground = GruvboxLight0Hard;
|
||||
Color ColorResizerOutline = GruvboxLight3;
|
||||
Int StyleWaitForEvents = 1;
|
||||
Int StyleDrawLineNumbers = 1;
|
||||
Int StyleDrawScrollbar = 1;
|
||||
Int StyleIndentSize = 4;
|
||||
Int StyleFontSize = 15;
|
||||
Int StyleFontFilter = 0;
|
||||
String StyleFont = "/home/krz/text_editor/package/CascadiaMono.ttf";
|
||||
String StyleVCVarsall = "C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Auxiliary/Build/vcvars64.bat";
|
||||
double StyleUndoMergeTimeout = 0.3;
|
||||
|
||||
void ReloadStyle() {
|
||||
ColorText = GetColor("Text", ColorText);
|
||||
ColorLoadTextHighlight = GetColor("LoadTextHighlight", ColorLoadTextHighlight);
|
||||
ColorBackground = GetColor("Background", ColorBackground);
|
||||
ColorInactiveWindow = GetColor("InactiveWindow", ColorInactiveWindow);
|
||||
ColorTextLineNumbers = GetColor("TextLineNumbers", ColorTextLineNumbers);
|
||||
ColorLineHighlight = GetColor("LineHighlight", ColorLineHighlight);
|
||||
ColorMainCaret = GetColor("MainCaret", ColorMainCaret);
|
||||
ColorSubCaret = GetColor("SubCaret", ColorSubCaret);
|
||||
ColorSelection = GetColor("Selection", ColorSelection);
|
||||
ColorWhitespaceDuringSelection = GetColor("WhitespaceDuringSelection", ColorWhitespaceDuringSelection);
|
||||
ColorMouseUnderline = GetColor("MouseUnderline", ColorMouseUnderline);
|
||||
ColorCaretUnderline = GetColor("CaretUnderline", ColorCaretUnderline);
|
||||
ColorFuzzySearchLineHighlight = GetColor("FuzzySearchLineHighlight", ColorFuzzySearchLineHighlight);
|
||||
ColorScrollbarBackground = GetColor("ScrollbarBackground", ColorScrollbarBackground);
|
||||
ColorScrollbarScroller = GetColor("ScrollbarScroller", ColorScrollbarScroller);
|
||||
ColorScrollbarScrollerSelected = GetColor("ScrollbarScrollerSelected", ColorScrollbarScrollerSelected);
|
||||
ColorTitleBarText = GetColor("TitleBarText", ColorTitleBarText);
|
||||
ColorTitleBarBackground = GetColor("TitleBarBackground", ColorTitleBarBackground);
|
||||
ColorTitleBarActiveBackground = GetColor("TitleBarActiveBackground", ColorTitleBarActiveBackground);
|
||||
ColorTitleBarSelection = GetColor("TitleBarSelection", ColorTitleBarSelection);
|
||||
ColorResizerBackground = GetColor("ResizerBackground", ColorResizerBackground);
|
||||
ColorResizerOutline = GetColor("ResizerOutline", ColorResizerOutline);
|
||||
StyleWaitForEvents = GetStyleInt("WaitForEvents", StyleWaitForEvents);
|
||||
StyleDrawLineNumbers = GetStyleInt("DrawLineNumbers", StyleDrawLineNumbers);
|
||||
StyleDrawScrollbar = GetStyleInt("DrawScrollbar", StyleDrawScrollbar);
|
||||
StyleIndentSize = GetStyleInt("IndentSize", StyleIndentSize);
|
||||
StyleFontSize = GetStyleInt("FontSize", StyleFontSize);
|
||||
StyleFontFilter = GetStyleInt("FontFilter", StyleFontFilter);
|
||||
StyleFont = GetStyleString("Font", StyleFont);
|
||||
StyleVCVarsall = GetStyleString("VCVarsall", StyleVCVarsall);
|
||||
StyleUndoMergeTimeout = GetStyleFloat("UndoMergeTimeout", StyleUndoMergeTimeout);
|
||||
}
|
||||
|
||||
struct CommandContext {
|
||||
bool out_skip_rem_cmds;
|
||||
};
|
||||
|
||||
typedef void Function(void *param);
|
||||
typedef void CommandFunction(CommandContext *ctx);
|
||||
typedef int LuaFunction(lua_State *state);
|
||||
struct FunctionData { String name; Function *function; };
|
||||
struct LuaFunctionData { String name; LuaFunction *function; };
|
||||
struct CommandData { String name; String binding; CommandFunction *function; struct Trigger *trigger; };
|
||||
|
||||
Array<CommandData> CommandFunctions;
|
||||
Array<LuaFunctionData> LuaFunctions;
|
||||
Array<FunctionData> TestFunctions;
|
||||
|
||||
struct Register_Function {
|
||||
Register_Function(Array<FunctionData> *functions, String name, Function *f) {
|
||||
int64_t pos = 0;
|
||||
if (Seek(name, "_", &pos, 0)) {
|
||||
name = Skip(name, pos + 1);
|
||||
}
|
||||
Add(functions, {name, f});
|
||||
}
|
||||
};
|
||||
#define RegisterFunction(functions, name) Register_Function RF__##name(functions, #name, name)
|
||||
struct Register_Lua { Register_Lua(LuaFunction *function, String name) { if (StartsWith(name, "Lua_")) name = Skip(name, 4); Add(&LuaFunctions, {name, function}); } };
|
||||
struct Register_Command { Register_Command(CommandFunction *function, String name, String binding) { if (StartsWith(name, "Command_")) name = Skip(name, 8); Add(&CommandFunctions, {name, binding, function}); } };
|
||||
#define RegisterLua(NAME) Register_Lua RL_##NAME(NAME, #NAME)
|
||||
#define RegisterCommand(name, binding) Register_Command RC__##name(name, #name, binding)
|
||||
|
||||
const int DIR_RIGHT = 0;
|
||||
const int DIR_LEFT = 1;
|
||||
const int DIR_DOWN = 2;
|
||||
const int DIR_UP = 3;
|
||||
const int DIR_COUNT = 4;
|
||||
const bool CTRL_PRESSED = true;
|
||||
const bool SHIFT_PRESS = true;
|
||||
@@ -1,19 +0,0 @@
|
||||
struct InternTable {
|
||||
HashTable<String> strings; // general allocator
|
||||
BlockArena arena;
|
||||
};
|
||||
|
||||
String Intern(InternTable *table, String string) {
|
||||
String *value = table->strings.get(string);
|
||||
if (!value) {
|
||||
String copy = Copy(table->arena, string);
|
||||
table->strings.put(copy, copy);
|
||||
return copy;
|
||||
}
|
||||
return *value;
|
||||
}
|
||||
|
||||
// We use the intern table to optimize for space, I don't want to worry about freeing
|
||||
// buffer names and event text data so let it all accumulate and interning will at least
|
||||
// optimize worst offenders (like event text)
|
||||
InternTable GlobalInternTable;
|
||||
294
src/text_editor/lua.cpp
Normal file
294
src/text_editor/lua.cpp
Normal file
@@ -0,0 +1,294 @@
|
||||
static void HookLuaForceExit(lua_State *L, lua_Debug *debug) {
|
||||
SDL_PumpEvents();
|
||||
int numkeys = 0;
|
||||
const bool *keys = SDL_GetKeyboardState(&numkeys);
|
||||
if (keys[SDL_SCANCODE_F9])
|
||||
luaL_error(L, "lua execution got interrupted");
|
||||
}
|
||||
|
||||
API double GetStyleFloat(String name, double default_float) {
|
||||
double result = default_float;
|
||||
lua_getglobal(LuaState, "Style");
|
||||
defer { lua_pop(LuaState, 1); };
|
||||
if (lua_istable(LuaState, -1)) {
|
||||
lua_pushlstring(LuaState, name.data, name.len);
|
||||
lua_gettable(LuaState, -2);
|
||||
defer { lua_pop(LuaState, 1); };
|
||||
if (lua_isnumber(LuaState, -1) || lua_isboolean(LuaState, -1) || lua_isinteger(LuaState, -1)) {
|
||||
lua_Number num = lua_tonumber(LuaState, -1);
|
||||
result = (double)num;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
API Int GetStyleInt(String name, Int default_int) {
|
||||
Int result = default_int;
|
||||
lua_getglobal(LuaState, "Style");
|
||||
defer { lua_pop(LuaState, 1); };
|
||||
if (lua_istable(LuaState, -1)) {
|
||||
lua_pushlstring(LuaState, name.data, name.len);
|
||||
lua_gettable(LuaState, -2);
|
||||
defer { lua_pop(LuaState, 1); };
|
||||
if (lua_isnumber(LuaState, -1) || lua_isboolean(LuaState, -1) || lua_isinteger(LuaState, -1)) {
|
||||
lua_Integer num = lua_tointeger(LuaState, -1);
|
||||
result = (Int)num;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
API String GetStyleString(String name, String default_string) {
|
||||
String result = default_string;
|
||||
lua_getglobal(LuaState, "Style");
|
||||
defer { lua_pop(LuaState, 1); };
|
||||
if (lua_istable(LuaState, -1)) {
|
||||
lua_pushlstring(LuaState, name.data, name.len);
|
||||
lua_gettable(LuaState, -2);
|
||||
defer { lua_pop(LuaState, 1); };
|
||||
if (lua_isstring(LuaState, -1)) {
|
||||
const char *string = lua_tostring(LuaState, -1);
|
||||
result = Intern(&GlobalInternTable, string);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
API Color GetColor(String name, Color default_color) {
|
||||
Color result = default_color;
|
||||
lua_getglobal(LuaState, "Color");
|
||||
defer { lua_pop(LuaState, 1); };
|
||||
if (lua_istable(LuaState, -1)) {
|
||||
lua_pushlstring(LuaState, name.data, name.len);
|
||||
lua_gettable(LuaState, -2);
|
||||
defer { lua_pop(LuaState, 1); };
|
||||
if (lua_isnumber(LuaState, -1)) {
|
||||
lua_Integer num = lua_tointeger(LuaState, -1);
|
||||
result.r = (uint8_t)((0xFF000000 & num) >> 24);
|
||||
result.g = (uint8_t)((0x00FF0000 & num) >> 16);
|
||||
result.b = (uint8_t)((0x0000FF00 & num) >> 8);
|
||||
result.a = (uint8_t)((0x000000FF & num) >> 0);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
API String GetFieldString(lua_State *L, String name) {
|
||||
String result = {};
|
||||
if (lua_istable(L, -1)) {
|
||||
lua_pushlstring(L, name.data, name.len);
|
||||
lua_gettable(L, -2);
|
||||
defer { lua_pop(L, 1); };
|
||||
if (lua_isstring(L, -1)) {
|
||||
result = lua_tostring(L, -1);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void ReloadStyle();
|
||||
extern String BaseLuaConfig;
|
||||
|
||||
API void LoadLuaBuffer(Buffer *lua_buffer) {
|
||||
if (!lua_buffer) return;
|
||||
ReportConsolef("reloading config: %S", lua_buffer->name);
|
||||
|
||||
Scratch scratch;
|
||||
String string = AllocCharString(scratch, lua_buffer);
|
||||
if (luaL_dostring(LuaState, string.data) == LUA_OK) {
|
||||
if (lua_isstring(LuaState, -1)) {
|
||||
const char *text = lua_tostring(LuaState, -1);
|
||||
ReportConsolef(text);
|
||||
lua_pop(LuaState, 1);
|
||||
}
|
||||
} else {
|
||||
const char *error_message = lua_tostring(LuaState, -1);
|
||||
ReportWarningf("Failed to load user config! %s", error_message);
|
||||
lua_pop(LuaState, 1);
|
||||
}
|
||||
lua_buffer->user_change_id = lua_buffer->change_id;
|
||||
}
|
||||
|
||||
API void ReloadLuaConfigs(bool reload) {
|
||||
if (LuaConfigBuffer && !LuaConfigBuffer->dirty && LuaConfigBuffer->change_id != LuaConfigBuffer->user_change_id) {
|
||||
reload = true;
|
||||
}
|
||||
if (LuaProjectBuffer && !LuaProjectBuffer->dirty && LuaProjectBuffer->change_id != LuaProjectBuffer->user_change_id) {
|
||||
reload = true;
|
||||
}
|
||||
if (reload == false) {
|
||||
return;
|
||||
}
|
||||
|
||||
LoadLuaBuffer(LuaConfigBuffer);
|
||||
LoadLuaBuffer(LuaProjectBuffer);
|
||||
|
||||
ReloadStyle();
|
||||
ReloadFont(StyleFont, (U32)StyleFontSize);
|
||||
For(Windows) {
|
||||
it->draw_scrollbar = StyleDrawScrollbar;
|
||||
it->draw_line_numbers = StyleDrawLineNumbers;
|
||||
}
|
||||
}
|
||||
|
||||
API bool CallLuaFunc(char *func_name, int arg_count, int ret_count) {
|
||||
if (lua_pcall(LuaState, arg_count, ret_count, 0) != 0) {
|
||||
const char *error_message = lua_tostring(LuaState, -1);
|
||||
ReportWarningf("Failed to call a lua function: %s! %s", func_name, error_message);
|
||||
lua_pop(LuaState, 1);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void CallLuaOnInit() {
|
||||
lua_getglobal(LuaState, "OnInit");
|
||||
CallLuaFunc("OnInit", 0, 0);
|
||||
}
|
||||
|
||||
API void InitLuaConfig() {
|
||||
LuaState = luaL_newstate();
|
||||
luaL_openlibs(LuaState);
|
||||
lua_sethook(LuaState, HookLuaForceExit, LUA_MASKCOUNT, 100000000);
|
||||
|
||||
For(LuaFunctions) {
|
||||
lua_pushcfunction(LuaState, it.function);
|
||||
lua_setglobal(LuaState, it.name.data);
|
||||
}
|
||||
|
||||
#if OS_WINDOWS
|
||||
lua_pushinteger(LuaState, 0);
|
||||
#else
|
||||
lua_pushinteger(LuaState, 1);
|
||||
#endif
|
||||
lua_setglobal(LuaState, "OS_VALUE");
|
||||
|
||||
// Init base config, test that it works and initialize the lua stuff
|
||||
if (!luaL_dostring(LuaState, BaseLuaConfig.data) == LUA_OK) {
|
||||
const char *error_message = lua_tostring(LuaState, -1);
|
||||
ReportErrorf("Failed to load base lua config! %s", error_message);
|
||||
lua_pop(LuaState, 1);
|
||||
Assert(!"Invalid codepath");
|
||||
}
|
||||
|
||||
// Init user config
|
||||
Buffer *lua_buffer = NULL;
|
||||
|
||||
Scratch scratch;
|
||||
String lua_config_exe = Format(scratch, "%S/init.lua", GetExeDir(scratch));
|
||||
if (FileExists(lua_config_exe)) {
|
||||
lua_buffer = BufferOpenFile(lua_config_exe);
|
||||
}
|
||||
|
||||
if (lua_buffer == NULL) {
|
||||
String lua_config_remote = Format(scratch, "%S/init.lua", ConfigDir);
|
||||
// #if DEBUG_BUILD
|
||||
// // WARNING! Delete config to make sure we are running this code more frequently
|
||||
// SDL_RemovePath(lua_config_remote.data);
|
||||
// ReportConsolef("deleting config for debug purposes!");
|
||||
// #endif
|
||||
lua_buffer = BufferOpenFile(lua_config_remote);
|
||||
if (lua_buffer->len == 0) {
|
||||
String16 string16 = ToString16(scratch, BaseLuaConfig);
|
||||
RawReplaceText(lua_buffer, {}, string16);
|
||||
ReportConsolef("no config at: %S - creating config buffer", lua_config_remote);
|
||||
}
|
||||
}
|
||||
LuaConfigBuffer = lua_buffer;
|
||||
ReloadLuaConfigs(true);
|
||||
CallLuaOnInit();
|
||||
}
|
||||
|
||||
struct OnOpenResult {
|
||||
String kind;
|
||||
String file_path;
|
||||
Int line, col;
|
||||
String working_dir;
|
||||
String cmd;
|
||||
};
|
||||
|
||||
OnOpenResult CallOnOpen(Allocator allocator, String path, String meta) {
|
||||
lua_getglobal(LuaState, "OnOpen");
|
||||
lua_pushlstring(LuaState, path.data, path.len);
|
||||
lua_pushlstring(LuaState, meta.data, meta.len);
|
||||
if (!CallLuaFunc("OnOpen", 2, 1)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
String file_path = GetFieldString(LuaState, "file_path");
|
||||
String line_string = GetFieldString(LuaState, "line");
|
||||
String col_string = GetFieldString(LuaState, "col");
|
||||
String cmd = GetFieldString(LuaState, "cmd");
|
||||
String working_dir = GetFieldString(LuaState, "working_dir");
|
||||
String kind = GetFieldString(LuaState, "kind");
|
||||
|
||||
OnOpenResult result = {};
|
||||
result.cmd = cmd;
|
||||
result.working_dir = working_dir;
|
||||
result.file_path = file_path;
|
||||
if (!IsAbsolute(result.file_path)) {
|
||||
String GetMainDir();
|
||||
String dir = GetMainDir();
|
||||
result.file_path = Format(allocator, "%S/%S", dir, result.file_path);
|
||||
}
|
||||
if (col_string.len) {
|
||||
result.col = strtoll(col_string.data, NULL, 10);
|
||||
} else {
|
||||
result.col = -1;
|
||||
}
|
||||
if (line_string.len) {
|
||||
result.line = strtoll(line_string.data, NULL, 10);
|
||||
} else {
|
||||
result.line = -1;
|
||||
}
|
||||
result.kind = kind;
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool CallIsCode(String path, String meta = "") {
|
||||
lua_getglobal(LuaState, "IsCode");
|
||||
lua_pushlstring(LuaState, path.data, path.len);
|
||||
lua_pushlstring(LuaState, meta.data, meta.len);
|
||||
if (!CallLuaFunc("IsCode", 2, 1)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool result = lua_toboolean(LuaState, -1);
|
||||
lua_pop(LuaState, 1);
|
||||
return result;
|
||||
}
|
||||
|
||||
void CallOnSave(BufferID buffer_id) {
|
||||
lua_getglobal(LuaState, "OnSave");
|
||||
lua_pushinteger(LuaState, buffer_id.id);
|
||||
CallLuaFunc("OnSave", 1, 0);
|
||||
}
|
||||
|
||||
void PushEvent(lua_State *L, Event *event) {
|
||||
lua_createtable(L, 0, EVENT_FIELD_COUNT);
|
||||
#define lua_pushInt lua_pushinteger
|
||||
#define lua_pushString lua_pushstring
|
||||
#define lua_pushFloat lua_pushnumber
|
||||
#define X(TYPE, KIND, NAME) \
|
||||
lua_push##KIND(L, event->NAME); \
|
||||
lua_setfield(L, -2, #NAME);
|
||||
EVENT_FIELDS
|
||||
#undef X
|
||||
}
|
||||
|
||||
bool CallOnCommand(Event *event) {
|
||||
lua_getglobal(LuaState, "OnCommand");
|
||||
PushEvent(LuaState, event);
|
||||
CallLuaFunc("OnCommand", 1, 1);
|
||||
bool result = lua_toboolean(LuaState, -1);
|
||||
lua_pop(LuaState, 1);
|
||||
return result;
|
||||
}
|
||||
|
||||
void CallLuaOnUpdate(Event *event) {
|
||||
lua_getglobal(LuaState, "OnUpdate");
|
||||
PushEvent(LuaState, event);
|
||||
CallLuaFunc("OnUpdate", 1, 0);
|
||||
}
|
||||
9
src/text_editor/lua.h
Normal file
9
src/text_editor/lua.h
Normal file
@@ -0,0 +1,9 @@
|
||||
API double GetStyleFloat(String name, double default_float);
|
||||
API Int GetStyleInt(String name, Int default_int);
|
||||
API String GetStyleString(String name, String default_string);
|
||||
API Color GetColor(String name, Color default_color);
|
||||
API String GetFieldString(lua_State *L, String name);
|
||||
API void LoadLuaBuffer(Buffer *lua_buffer);
|
||||
API void ReloadLuaConfigs(bool reload = false);
|
||||
API bool CallLuaFunc(char *func_name, int arg_count, int ret_count);
|
||||
API void InitLuaConfig();
|
||||
@@ -4,33 +4,27 @@ int Lua_print(lua_State *L) {
|
||||
View *null_view = GetView(NullViewID);
|
||||
for (int i = 1; i <= nargs; i += 1) {
|
||||
String string = lua_tostring(L, i);
|
||||
Command_Appendf(null_view, "%S ", string);
|
||||
Appendf(null_view, "%S ", string);
|
||||
}
|
||||
Command_Appendf(null_view, "\n");
|
||||
Appendf(null_view, "\n");
|
||||
lua_pop(L, nargs);
|
||||
return 0;
|
||||
}
|
||||
} RegisterLua(Lua_print);
|
||||
|
||||
int Lua_Print(lua_State *L) {
|
||||
Scratch scratch;
|
||||
int nargs = lua_gettop(L);
|
||||
for (int i = 1; i <= nargs; i += 1) {
|
||||
String string = lua_tostring(L, i);
|
||||
Command_Appendf(TraceView, "%S ", string);
|
||||
Appendf(TraceView, "%S ", string);
|
||||
}
|
||||
Command_Appendf(TraceView, "\n");
|
||||
Appendf(TraceView, "\n");
|
||||
lua_pop(L, nargs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Lua_Kill(lua_State *L) {
|
||||
BSet main = GetActiveMainSet();
|
||||
KillProcess(main.view);
|
||||
return 0;
|
||||
}
|
||||
} RegisterLua(Lua_Print);
|
||||
|
||||
int Lua_GetLoadWord(lua_State *L) {
|
||||
BSet active = GetActiveSet();
|
||||
BSet active = GetBSet(ActiveWindowID);
|
||||
Range range = active.view->carets[0].range;
|
||||
if (GetSize(range) == 0) {
|
||||
range = EncloseLoadWord(active.buffer, range.min);
|
||||
@@ -40,7 +34,7 @@ int Lua_GetLoadWord(lua_State *L) {
|
||||
String string = AllocCharString(scratch, active.buffer, range);
|
||||
lua_pushlstring(L, string.data, string.len);
|
||||
return 1;
|
||||
}
|
||||
} RegisterLua(Lua_GetLoadWord);
|
||||
|
||||
int Lua_BufferExists(lua_State *L) {
|
||||
String string = lua_tostring(L, 1);
|
||||
@@ -48,47 +42,47 @@ int Lua_BufferExists(lua_State *L) {
|
||||
Buffer *buffer = GetBuffer(string);
|
||||
lua_pushboolean(L, buffer != NULL);
|
||||
return 1;
|
||||
}
|
||||
} RegisterLua(Lua_BufferExists);
|
||||
|
||||
int Lua_GetSelection(lua_State *L) {
|
||||
Scratch scratch;
|
||||
BSet main = GetActiveMainSet();
|
||||
BSet main = GetBSet(LastActiveLayoutWindowID);
|
||||
String16 string16 = GetString(main.buffer, main.view->carets[0].range);
|
||||
String string = ToString(scratch, string16);
|
||||
lua_pushlstring(L, string.data, string.len);
|
||||
return 1;
|
||||
}
|
||||
} RegisterLua(Lua_GetSelection);
|
||||
|
||||
int Lua_GetEntireBuffer(lua_State *L) {
|
||||
Scratch scratch;
|
||||
BSet main = GetActiveMainSet();
|
||||
BSet main = GetBSet(LastActiveLayoutWindowID);
|
||||
String16 string16 = GetString(main.buffer);
|
||||
String string = ToString(scratch, string16);
|
||||
lua_pushlstring(L, string.data, string.len);
|
||||
return 1;
|
||||
}
|
||||
} RegisterLua(Lua_GetEntireBuffer);
|
||||
|
||||
int Lua_GetClipboard(lua_State *L) {
|
||||
Scratch scratch;
|
||||
String string = ToString(scratch, SavedClipboardString);
|
||||
lua_pushlstring(L, string.data, string.len);
|
||||
return 1;
|
||||
}
|
||||
} RegisterLua(Lua_GetClipboard);
|
||||
|
||||
int Lua_GetFilename(lua_State *L) {
|
||||
BSet main = GetActiveMainSet();
|
||||
BSet main = GetBSet(LastActiveLayoutWindowID);
|
||||
lua_pushlstring(L, main.buffer->name.data, main.buffer->name.len);
|
||||
return 1;
|
||||
}
|
||||
} RegisterLua(Lua_GetFilename);
|
||||
|
||||
int Lua_GetLine(lua_State *L) {
|
||||
BSet main = GetActiveMainSet();
|
||||
BSet main = GetBSet(LastActiveLayoutWindowID);
|
||||
Caret caret = main.view->carets[0];
|
||||
Int front = GetFront(caret);
|
||||
Int line = PosToLine(main.buffer, front);
|
||||
lua_pushinteger(L, line + 1);
|
||||
return 1;
|
||||
}
|
||||
} RegisterLua(Lua_GetLine);
|
||||
|
||||
int Lua_FileExists(lua_State *L) {
|
||||
String path = luaL_checkstring(L, 1);
|
||||
@@ -96,147 +90,186 @@ int Lua_FileExists(lua_State *L) {
|
||||
bool exists = FileExists(path);
|
||||
lua_pushboolean(L, exists);
|
||||
return 1;
|
||||
}
|
||||
} RegisterLua(Lua_FileExists);
|
||||
|
||||
int Lua_GetWorkDir(lua_State *L) {
|
||||
lua_pushlstring(L, WorkDir.data, WorkDir.len);
|
||||
return 1;
|
||||
}
|
||||
} RegisterLua(Lua_GetWorkDir);
|
||||
|
||||
int Lua_GetExeDir(lua_State *L) {
|
||||
Scratch scratch;
|
||||
String exe_dir = GetExeDir(scratch);
|
||||
lua_pushlstring(L, exe_dir.data, exe_dir.len);
|
||||
return 1;
|
||||
}
|
||||
} RegisterLua(Lua_GetExeDir);
|
||||
|
||||
int Lua_GetMainDir(lua_State *L) {
|
||||
String name = Command_GetMainDir();
|
||||
String name = GetMainDir();
|
||||
lua_pushlstring(L, name.data, name.len);
|
||||
return 1;
|
||||
}
|
||||
} RegisterLua(Lua_GetMainDir);
|
||||
|
||||
int Lua_SplitSize(lua_State *L) {
|
||||
lua_Number num = lua_tonumber(L, 1);
|
||||
int Lua_ListCommands(lua_State *L) {
|
||||
BSet main = GetBSet(LastActiveLayoutWindowID);
|
||||
BeginJump(&main);
|
||||
int i = 0;
|
||||
For (LuaFunctions) {
|
||||
Appendf(main.view, "%20S() ", it.name);
|
||||
if (((i + 1) % 6) == 0) {
|
||||
Appendf(main.view, "\n");
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
EndJump(main);
|
||||
ActiveWindowID = main.window->id;
|
||||
return 0;
|
||||
} RegisterLua(Lua_ListCommands);
|
||||
|
||||
int Lua_GetBufferList(lua_State *L) {
|
||||
lua_createtable(L, 0, (int)Buffers.len);
|
||||
|
||||
int i = 1;
|
||||
For(Buffers) {
|
||||
lua_pushinteger(L, i++);
|
||||
lua_pushlstring(L, it->name.data, it->name.len);
|
||||
lua_settable(L, -3); /* 3rd element from the stack top */
|
||||
}
|
||||
/* We still have table left on top of the Lua stack. */
|
||||
return 1;
|
||||
} RegisterLua(Lua_GetBufferList);
|
||||
|
||||
int Lua_Eval(lua_State *L) {
|
||||
String string = lua_tostring(L, 1);
|
||||
lua_pop(L, 1);
|
||||
Eval(string);
|
||||
return 0;
|
||||
} RegisterLua(Lua_Eval);
|
||||
|
||||
int Lua_ApplyClangFormat(lua_State *L) {
|
||||
lua_Integer buffer_id = luaL_checkinteger(L, 1);
|
||||
lua_pop(L, 1);
|
||||
Buffer *buffer = GetBuffer({buffer_id});
|
||||
ApplyClangFormat(buffer);
|
||||
return 0;
|
||||
} RegisterLua(Lua_ApplyClangFormat);
|
||||
|
||||
int Lua_GetBufferNameByID(lua_State *L) {
|
||||
lua_Integer buffer_id = luaL_checkinteger(L, 1);
|
||||
lua_pop(L, 1);
|
||||
Buffer *buffer = GetBuffer({buffer_id});
|
||||
lua_pushlstring(L, buffer->name.data, buffer->name.len);
|
||||
return 1;
|
||||
} RegisterLua(Lua_GetBufferNameByID);
|
||||
|
||||
|
||||
int Lua_ConvertLineEndingsToLF(lua_State *L) {
|
||||
lua_Integer buffer_id = luaL_checkinteger(L, 1);
|
||||
int trim_lines_with_caret = lua_toboolean(L, 2);
|
||||
lua_pop(L, 2);
|
||||
Buffer *buffer = GetBuffer({buffer_id});
|
||||
ConvertLineEndingsToLF(buffer, trim_lines_with_caret);
|
||||
return 0;
|
||||
} RegisterLua(Lua_ConvertLineEndingsToLF);
|
||||
|
||||
int Lua_New(lua_State *L) {
|
||||
String name = lua_tostring(L, 1);
|
||||
lua_pop(L, 1);
|
||||
BSet main = GetBSet(LastActiveLayoutWindowID);
|
||||
New(main.window, name);
|
||||
return 0;
|
||||
} RegisterLua(Lua_New);
|
||||
|
||||
int Lua_NewDir(lua_State *L) {
|
||||
String name = lua_tostring(L, 1);
|
||||
lua_pop(L, 1);
|
||||
BSet main = GetBSet(LastActiveLayoutWindowID);
|
||||
NewDir(main.window, name);
|
||||
return 0;
|
||||
} RegisterLua(Lua_NewDir);
|
||||
|
||||
int Lua_C(lua_State *L) {
|
||||
String string = lua_tostring(L, 1);
|
||||
lua_pop(L, 1);
|
||||
Exec(string, GetMainDir());
|
||||
return 0;
|
||||
} RegisterLua(Lua_C);
|
||||
|
||||
int Lua_Open(lua_State *L) {
|
||||
Scratch scratch;
|
||||
String path = luaL_checkstring(L, 1);
|
||||
lua_pop(L, 1);
|
||||
Open(path);
|
||||
return 0;
|
||||
} RegisterLua(Lua_Open);
|
||||
|
||||
int Lua_Cmd(lua_State *L) {
|
||||
if (!lua_istable(L, -1)) luaL_error(L, "expected a table as argument");
|
||||
defer { lua_pop(L, 1); };
|
||||
|
||||
lua_getfield(L, -1, "working_dir");
|
||||
String working_dir = lua_tostring(L, -1);
|
||||
lua_pop(L, 1);
|
||||
if (working_dir == "") {
|
||||
working_dir = GetMainDir();
|
||||
}
|
||||
|
||||
lua_getfield(L, -1, "cmd");
|
||||
if (!lua_isstring(L, -1)) luaL_error(L, "expected a string for cmd param");
|
||||
String cmd = lua_tostring(L, -1);
|
||||
lua_pop(L, 1);
|
||||
|
||||
BSet set = GetActiveMainSet();
|
||||
WindowSplit *split = set.window->split_ref;
|
||||
split->parent->value = num;
|
||||
lua_getfield(L, -1, "kind");
|
||||
String kind = lua_tostring(L, -1);
|
||||
lua_pop(L, 1);
|
||||
|
||||
BSet main = GetBSet(LastActiveLayoutWindowID);
|
||||
if (kind == "console") {
|
||||
BSet set = GetConsoleSet();
|
||||
main.window->active_goto_list = set.view->id;
|
||||
main.window->goto_list_pos = set.buffer->len;
|
||||
SelectRange(set.view, MakeRange(set.buffer->len));
|
||||
BeginJump(&set);
|
||||
Exec(set.view->id, true, cmd, working_dir);
|
||||
EndJump(set);
|
||||
} else if (kind == "fuzzy") {
|
||||
JumpGarbageBuffer(&main);
|
||||
Exec(main.view->id, true, cmd, working_dir);
|
||||
main.view->fuzzy_search = true;
|
||||
ActiveWindowID = main.window->id;
|
||||
} else {
|
||||
JumpGarbageBuffer(&main);
|
||||
main.window->active_goto_list = main.view->id;
|
||||
main.window->goto_list_pos = 0;
|
||||
Exec(main.view->id, true, cmd, working_dir);
|
||||
ActiveWindowID = main.window->id;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
} RegisterLua(Lua_Cmd);
|
||||
|
||||
int Lua_KillWindow(lua_State *L) {
|
||||
BSet set = GetActiveMainSet();
|
||||
set.window->kill = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void HookLuaForceExit(lua_State *L, lua_Debug *debug) {
|
||||
SDL_PumpEvents();
|
||||
int numkeys = 0;
|
||||
const bool *keys = SDL_GetKeyboardState(&numkeys);
|
||||
if (keys[SDL_SCANCODE_F9])
|
||||
luaL_error(L, "lua execution got interrupted");
|
||||
}
|
||||
|
||||
Int GetStyleInt(String name, Int default_int) {
|
||||
Int result = default_int;
|
||||
lua_getglobal(LuaState, "Style");
|
||||
defer { lua_pop(LuaState, 1); };
|
||||
if (lua_istable(LuaState, -1)) {
|
||||
lua_pushlstring(LuaState, name.data, name.len);
|
||||
lua_gettable(LuaState, -2);
|
||||
defer { lua_pop(LuaState, 1); };
|
||||
if (lua_isnumber(LuaState, -1) || lua_isboolean(LuaState, -1) || lua_isinteger(LuaState, -1)) {
|
||||
lua_Integer num = lua_tointeger(LuaState, -1);
|
||||
result = (Int)num;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
String GetStyleString(String name, String default_string) {
|
||||
String result = default_string;
|
||||
lua_getglobal(LuaState, "Style");
|
||||
defer { lua_pop(LuaState, 1); };
|
||||
if (lua_istable(LuaState, -1)) {
|
||||
lua_pushlstring(LuaState, name.data, name.len);
|
||||
lua_gettable(LuaState, -2);
|
||||
defer { lua_pop(LuaState, 1); };
|
||||
if (lua_isstring(LuaState, -1)) {
|
||||
const char *string = lua_tostring(LuaState, -1);
|
||||
result = Intern(&GlobalInternTable, string);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Color GetColor(String name, Color default_color) {
|
||||
Color result = default_color;
|
||||
lua_getglobal(LuaState, "Color");
|
||||
defer { lua_pop(LuaState, 1); };
|
||||
if (lua_istable(LuaState, -1)) {
|
||||
lua_pushlstring(LuaState, name.data, name.len);
|
||||
lua_gettable(LuaState, -2);
|
||||
defer { lua_pop(LuaState, 1); };
|
||||
if (lua_isnumber(LuaState, -1)) {
|
||||
lua_Integer num = lua_tointeger(LuaState, -1);
|
||||
result.r = (uint8_t)((0xFF000000 & num) >> 24);
|
||||
result.g = (uint8_t)((0x00FF0000 & num) >> 16);
|
||||
result.b = (uint8_t)((0x0000FF00 & num) >> 8);
|
||||
result.a = (uint8_t)((0x000000FF & num) >> 0);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
String GetFieldString(lua_State *L, String name) {
|
||||
String result = {};
|
||||
if (lua_istable(L, -1)) {
|
||||
lua_pushlstring(L, name.data, name.len);
|
||||
lua_gettable(L, -2);
|
||||
defer { lua_pop(L, 1); };
|
||||
if (lua_isstring(L, -1)) {
|
||||
result = lua_tostring(L, -1);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Int GetInt(lua_State *L, const char *name) {
|
||||
Int GetFieldAInt(lua_State *L, const char *name) {
|
||||
lua_getfield(L, -1, name);
|
||||
lua_Integer num = lua_tointeger(L, -1);
|
||||
lua_pop(L, 1);
|
||||
return (Int)num;
|
||||
}
|
||||
|
||||
double GetFloat(lua_State *L, const char *name) {
|
||||
double GetFieldAFloat(lua_State *L, const char *name) {
|
||||
lua_getfield(L, -1, name);
|
||||
double num = lua_tonumber(L, -1);
|
||||
lua_pop(L, 1);
|
||||
return num;
|
||||
}
|
||||
|
||||
const char *GetString(lua_State *L, const char *name) {
|
||||
const char *GetFieldAString(lua_State *L, const char *name) {
|
||||
lua_getfield(L, -1, name);
|
||||
const char *result = lua_tostring(L, -1);
|
||||
|
||||
lua_pop(L, 1);
|
||||
return result;
|
||||
}
|
||||
|
||||
void PushEvent(lua_State *L, Event *event) {
|
||||
lua_createtable(L, 0, EVENT_FIELD_COUNT);
|
||||
#define lua_pushInt lua_pushinteger
|
||||
#define lua_pushString lua_pushstring
|
||||
#define lua_pushFloat lua_pushnumber
|
||||
#define X(TYPE, KIND, NAME) \
|
||||
lua_push##KIND(L, event->NAME); \
|
||||
lua_setfield(L, -2, #NAME);
|
||||
EVENT_FIELDS
|
||||
#undef X
|
||||
}
|
||||
|
||||
// :Event
|
||||
int Lua_Play(lua_State *L) {
|
||||
if (!lua_istable(L, -1)) luaL_error(L, "expected a table of events");
|
||||
@@ -249,212 +282,21 @@ int Lua_Play(lua_State *L) {
|
||||
defer { lua_pop(L, 1); };
|
||||
|
||||
Event event = {};
|
||||
#define X(TYPE, KIND, NAME) event.NAME = (TYPE)Get##KIND(L, #NAME);
|
||||
#define X(TYPE, KIND, NAME) event.NAME = (TYPE)GetFieldA##KIND(L, #NAME);
|
||||
EVENT_FIELDS
|
||||
#undef X
|
||||
Add(&EventPlayback, event);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ReloadStyle();
|
||||
extern String BaseLuaConfig;
|
||||
|
||||
void LoadLuaBuffer(Buffer *lua_buffer) {
|
||||
if (!lua_buffer) return;
|
||||
ReportConsolef("reloading config: %S", lua_buffer->name);
|
||||
|
||||
Scratch scratch;
|
||||
String string = AllocCharString(scratch, lua_buffer);
|
||||
if (luaL_dostring(LuaState, string.data) == LUA_OK) {
|
||||
if (lua_isstring(LuaState, -1)) {
|
||||
const char *text = lua_tostring(LuaState, -1);
|
||||
ReportConsolef(text);
|
||||
lua_pop(LuaState, 1);
|
||||
}
|
||||
} else {
|
||||
const char *error_message = lua_tostring(LuaState, -1);
|
||||
ReportWarningf("Failed to load user config! %s", error_message);
|
||||
lua_pop(LuaState, 1);
|
||||
}
|
||||
lua_buffer->user_change_id = lua_buffer->change_id;
|
||||
}
|
||||
|
||||
void ReloadLuaConfigs(bool reload = false) {
|
||||
if (LuaConfigBuffer && !LuaConfigBuffer->dirty && LuaConfigBuffer->change_id != LuaConfigBuffer->user_change_id) {
|
||||
reload = true;
|
||||
}
|
||||
if (LuaProjectBuffer && !LuaProjectBuffer->dirty && LuaProjectBuffer->change_id != LuaProjectBuffer->user_change_id) {
|
||||
reload = true;
|
||||
}
|
||||
if (reload == false) {
|
||||
return;
|
||||
}
|
||||
|
||||
LoadLuaBuffer(LuaConfigBuffer);
|
||||
LoadLuaBuffer(LuaProjectBuffer);
|
||||
|
||||
ReloadStyle();
|
||||
ReloadFont();
|
||||
for (Window *it = FirstWindow; it; it = it->next) {
|
||||
if (it->is_title_bar || it->is_search_bar) {
|
||||
continue;
|
||||
}
|
||||
it->draw_scrollbar = StyleDrawScrollbar;
|
||||
it->draw_line_numbers = StyleDrawLineNumbers;
|
||||
}
|
||||
}
|
||||
|
||||
bool CallLuaFunc(char *func_name, int arg_count, int ret_count) {
|
||||
if (lua_pcall(LuaState, arg_count, ret_count, 0) != 0) {
|
||||
const char *error_message = lua_tostring(LuaState, -1);
|
||||
ReportWarningf("Failed to call a lua function: %s! %s", func_name, error_message);
|
||||
lua_pop(LuaState, 1);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
struct OnOpenResult {
|
||||
String kind;
|
||||
String file_path;
|
||||
Int line, col;
|
||||
String working_dir;
|
||||
String cmd;
|
||||
};
|
||||
|
||||
OnOpenResult CallOnOpen(Allocator allocator, String path, String meta) {
|
||||
lua_getglobal(LuaState, "OnOpen");
|
||||
lua_pushlstring(LuaState, path.data, path.len);
|
||||
lua_pushlstring(LuaState, meta.data, meta.len);
|
||||
if (!CallLuaFunc("OnOpen", 2, 1)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
String file_path = GetFieldString(LuaState, "file_path");
|
||||
String line_string = GetFieldString(LuaState, "line");
|
||||
String col_string = GetFieldString(LuaState, "col");
|
||||
String cmd = GetFieldString(LuaState, "cmd");
|
||||
String working_dir = GetFieldString(LuaState, "working_dir");
|
||||
String kind = GetFieldString(LuaState, "kind");
|
||||
|
||||
OnOpenResult result = {};
|
||||
result.cmd = cmd;
|
||||
result.working_dir = working_dir;
|
||||
result.file_path = file_path;
|
||||
if (!IsAbsolute(result.file_path)) {
|
||||
String dir = Command_GetMainDir();
|
||||
result.file_path = Format(allocator, "%S/%S", dir, result.file_path);
|
||||
}
|
||||
if (col_string.len) {
|
||||
result.col = strtoll(col_string.data, NULL, 10);
|
||||
} else {
|
||||
result.col = -1;
|
||||
}
|
||||
if (line_string.len) {
|
||||
result.line = strtoll(line_string.data, NULL, 10);
|
||||
} else {
|
||||
result.line = -1;
|
||||
}
|
||||
result.kind = kind;
|
||||
} RegisterLua(Lua_Play);
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool CallIsCode(String path, String meta = "") {
|
||||
lua_getglobal(LuaState, "IsCode");
|
||||
lua_pushlstring(LuaState, path.data, path.len);
|
||||
lua_pushlstring(LuaState, meta.data, meta.len);
|
||||
if (!CallLuaFunc("IsCode", 2, 1)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool result = lua_toboolean(LuaState, -1);
|
||||
lua_pop(LuaState, 1);
|
||||
return result;
|
||||
}
|
||||
|
||||
void CallOnSave(BufferID buffer_id) {
|
||||
lua_getglobal(LuaState, "OnSave");
|
||||
lua_pushinteger(LuaState, buffer_id.id);
|
||||
CallLuaFunc("OnSave", 1, 0);
|
||||
}
|
||||
|
||||
bool CallOnCommand(Event *event) {
|
||||
lua_getglobal(LuaState, "OnCommand");
|
||||
PushEvent(LuaState, event);
|
||||
CallLuaFunc("OnCommand", 1, 1);
|
||||
bool result = lua_toboolean(LuaState, -1);
|
||||
lua_pop(LuaState, 1);
|
||||
return result;
|
||||
}
|
||||
|
||||
void CallLuaOnUpdate(Event *event) {
|
||||
lua_getglobal(LuaState, "OnUpdate");
|
||||
PushEvent(LuaState, event);
|
||||
CallLuaFunc("OnUpdate", 1, 0);
|
||||
}
|
||||
|
||||
void CallLuaOnInit() {
|
||||
lua_getglobal(LuaState, "OnInit");
|
||||
CallLuaFunc("OnInit", 0, 0);
|
||||
}
|
||||
|
||||
void InitLuaConfig() {
|
||||
LuaState = luaL_newstate();
|
||||
luaL_openlibs(LuaState);
|
||||
lua_sethook(LuaState, HookLuaForceExit, LUA_MASKCOUNT, 100000000);
|
||||
|
||||
for (int i = 0; LuaFunctions[i].name; i += 1) {
|
||||
lua_pushcfunction(LuaState, LuaFunctions[i].func);
|
||||
lua_setglobal(LuaState, LuaFunctions[i].name);
|
||||
}
|
||||
|
||||
#if OS_WINDOWS
|
||||
lua_pushinteger(LuaState, 0);
|
||||
#else
|
||||
lua_pushinteger(LuaState, 1);
|
||||
#endif
|
||||
lua_setglobal(LuaState, "OS_VALUE");
|
||||
|
||||
// Init base config, test that it works and initialize the lua stuff
|
||||
if (!luaL_dostring(LuaState, BaseLuaConfig.data) == LUA_OK) {
|
||||
const char *error_message = lua_tostring(LuaState, -1);
|
||||
ReportErrorf("Failed to load base lua config! %s", error_message);
|
||||
lua_pop(LuaState, 1);
|
||||
Assert(!"Invalid codepath");
|
||||
}
|
||||
|
||||
// Init user config
|
||||
Buffer *lua_buffer = NULL;
|
||||
|
||||
Scratch scratch;
|
||||
String lua_config_exe = Format(scratch, "%S/init.lua", GetExeDir(scratch));
|
||||
if (FileExists(lua_config_exe)) {
|
||||
lua_buffer = BufferOpenFile(lua_config_exe);
|
||||
}
|
||||
|
||||
if (lua_buffer == NULL) {
|
||||
String lua_config_remote = Format(scratch, "%S/init.lua", ConfigDir);
|
||||
// #if DEBUG_BUILD
|
||||
// // WARNING! Delete config to make sure we are running this code more frequently
|
||||
// SDL_RemovePath(lua_config_remote.data);
|
||||
// ReportConsolef("deleting config for debug purposes!");
|
||||
// #endif
|
||||
lua_buffer = BufferOpenFile(lua_config_remote);
|
||||
if (lua_buffer->len == 0) {
|
||||
String16 string16 = ToString16(scratch, BaseLuaConfig);
|
||||
RawReplaceText(lua_buffer, {}, string16);
|
||||
ReportConsolef("no config at: %S - creating config buffer", lua_config_remote);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
LuaConfigBuffer = lua_buffer;
|
||||
ReloadLuaConfigs(true);
|
||||
CallLuaOnInit();
|
||||
}
|
||||
int Lua_TrimTrailingWhitespace(lua_State *L) {
|
||||
lua_Integer buffer_id = luaL_checkinteger(L, 1);
|
||||
int trim_lines_with_caret = lua_toboolean(L, 2);
|
||||
lua_pop(L, 2);
|
||||
Buffer *buffer = GetBuffer({buffer_id});
|
||||
TrimTrailingWhitespace(buffer, trim_lines_with_caret);
|
||||
return 0;
|
||||
} RegisterLua(Lua_TrimTrailingWhitespace);
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
luaL_Reg LuaFunctions[] = {
|
||||
{"print", Lua_print},
|
||||
{"Print", Lua_Print},
|
||||
{"SaveAll", Lua_SaveAll},
|
||||
{"Kill", Lua_Kill},
|
||||
{"GetLoadWord", Lua_GetLoadWord},
|
||||
{"BufferExists", Lua_BufferExists},
|
||||
{"GetSelection", Lua_GetSelection},
|
||||
{"GetEntireBuffer", Lua_GetEntireBuffer},
|
||||
{"GetClipboard", Lua_GetClipboard},
|
||||
{"GetFilename", Lua_GetFilename},
|
||||
{"GetLine", Lua_GetLine},
|
||||
{"FileExists", Lua_FileExists},
|
||||
{"GetWorkDir", Lua_GetWorkDir},
|
||||
{"GetExeDir", Lua_GetExeDir},
|
||||
{"GetMainDir", Lua_GetMainDir},
|
||||
{"SplitSize", Lua_SplitSize},
|
||||
{"KillWindow", Lua_KillWindow},
|
||||
{"Play", Lua_Play},
|
||||
{"TrimTrailingWhitespace", Lua_TrimTrailingWhitespace},
|
||||
{"ConvertLineEndingsToLF", Lua_ConvertLineEndingsToLF},
|
||||
{"ApplyClangFormat", Lua_ApplyClangFormat},
|
||||
{"GetBufferNameByID", Lua_GetBufferNameByID},
|
||||
{"Save", Lua_Save},
|
||||
{"Reopen", Lua_Reopen},
|
||||
{"New", Lua_New},
|
||||
{"NewDir", Lua_NewDir},
|
||||
{"ToggleFullscreen", Lua_ToggleFullscreen},
|
||||
{"ListCode", Lua_ListCode},
|
||||
{"C", Lua_C},
|
||||
{"Open", Lua_Open},
|
||||
{"Cmd", Lua_Cmd},
|
||||
{"ListBuffers", Lua_ListBuffers},
|
||||
{"ListViews", Lua_ListViews},
|
||||
{"Eval", Lua_Eval},
|
||||
{"SetProjectFile", Lua_SetProjectFile},
|
||||
{"SetWorkDir", Lua_SetWorkDir},
|
||||
{"ListCommands", Lua_ListCommands},
|
||||
{"GetBufferList", Lua_GetBufferList},
|
||||
{"Split", Lua_Split},
|
||||
{NULL, NULL},
|
||||
};
|
||||
@@ -1,277 +1,3 @@
|
||||
WindowID WindowIDs;
|
||||
ViewID ViewIDs;
|
||||
|
||||
Window *FirstWindow;
|
||||
Window *LastWindow;
|
||||
Int WindowCount;
|
||||
|
||||
View *FirstView;
|
||||
View *LastView;
|
||||
Int ViewCount;
|
||||
|
||||
Buffer *FirstBuffer;
|
||||
Buffer *LastBuffer;
|
||||
Int BufferCount;
|
||||
|
||||
// console
|
||||
BufferID NullBufferID;
|
||||
ViewID NullViewID;
|
||||
WindowID NullWindowID;
|
||||
|
||||
// hidden floating window
|
||||
WindowID DebugWindowID;
|
||||
ViewID DebugViewID;
|
||||
BufferID DebugBufferID;
|
||||
|
||||
WindowSplit WindowSplits;
|
||||
WindowID ActiveWindow;
|
||||
|
||||
WindowSplit *ResizerSelected = NULL;
|
||||
WindowSplit *ResizerHover = NULL;
|
||||
WindowID ScrollbarSelected = {-1};
|
||||
WindowID DocumentSelected = {-1};
|
||||
Caret DocumentAnchor;
|
||||
|
||||
Buffer *LuaProjectBuffer;
|
||||
Buffer *LuaConfigBuffer;
|
||||
Buffer *GCInfoBuffer;
|
||||
Buffer *EventBuffer;
|
||||
Buffer *ScratchBuffer;
|
||||
Buffer *TraceBuffer;
|
||||
View *TraceView;
|
||||
|
||||
String WorkDir;
|
||||
RandomSeed UniqueBufferNameSeed = {};
|
||||
|
||||
// lua
|
||||
lua_State *LuaState = NULL;
|
||||
String16 LuaCommandResult = {};
|
||||
extern luaL_Reg LuaFunctions[];
|
||||
|
||||
// clipboard
|
||||
BlockArena ClipboardArena;
|
||||
String16 SavedClipboardString;
|
||||
Array<String16> SavedClipboardCarets = {SysAllocator};
|
||||
|
||||
|
||||
String GetUniqueBufferName(String working_dir, String prepend_name, String extension = ".log") {
|
||||
Scratch scratch;
|
||||
String buffer_name = {};
|
||||
for (int i = 1; i < INT_MAX; i += 1) {
|
||||
buffer_name = Format(scratch, "%S/%S%d%S", working_dir, prepend_name, i, extension);
|
||||
buffer_name = GetAbsolutePath(scratch, buffer_name);
|
||||
Buffer *exists = FindBuffer(buffer_name);
|
||||
if (!exists && !FileExists(buffer_name)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
buffer_name = Intern(&GlobalInternTable, buffer_name);
|
||||
return buffer_name;
|
||||
}
|
||||
|
||||
void InitBuffers() {
|
||||
Allocator sys_allocator = GetSystemAllocator();
|
||||
Scratch scratch;
|
||||
Buffer *null_buffer = CreateBuffer(sys_allocator, GetUniqueBufferName(GetWorkingDir(scratch), "console"));
|
||||
View *null_view = CreateView(null_buffer->id);
|
||||
Assert(null_buffer->id == NullBufferID && null_view->id == NullViewID);
|
||||
|
||||
TraceBuffer = CreateBuffer(sys_allocator, GetUniqueBufferName(WorkDir, "trace"));
|
||||
TraceView = CreateView(TraceBuffer->id);
|
||||
GCInfoBuffer = CreateBuffer(sys_allocator, GetUniqueBufferName(WorkDir, "gc"));
|
||||
EventBuffer = CreateBuffer(sys_allocator, GetUniqueBufferName(WorkDir, "events"));
|
||||
ScratchBuffer = BufferOpenFile(GetUniqueBufferName(WorkDir, "scratch"));
|
||||
|
||||
EventBuffer->no_history = true;
|
||||
GCInfoBuffer->no_history = true;
|
||||
}
|
||||
|
||||
inline bool IsDocumentSelectionValid() {
|
||||
if (DocumentSelected.id == -1) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool IsScrollbarSelectionValid() {
|
||||
if (ScrollbarSelected.id == -1) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
inline ViewID AllocViewID(View *view) { return {ViewIDs.id++, view}; }
|
||||
inline WindowID AllocWindowID(Window *window) { return {WindowIDs.id++, window}; }
|
||||
inline BufferID AllocBufferID(Buffer *buffer) { return {BufferIDs++, buffer}; }
|
||||
|
||||
inline Window *GetWindow(WindowID id) {
|
||||
for (Window *it = FirstWindow; it; it = it->next) {
|
||||
if (it->id == id) return it;
|
||||
}
|
||||
return FirstWindow;
|
||||
}
|
||||
|
||||
inline Buffer *GetBuffer(BufferID id) {
|
||||
for (Buffer *it = FirstBuffer; it; it = it->next) {
|
||||
if (it->id == id) return it;
|
||||
}
|
||||
return FirstBuffer;
|
||||
}
|
||||
|
||||
inline Buffer *FindBuffer(BufferID id) {
|
||||
for (Buffer *it = FirstBuffer; it; it = it->next) {
|
||||
if (it->id == id) return it;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
inline Buffer *GetBuffer(String name) {
|
||||
for (Buffer *it = FirstBuffer; it; it = it->next) {
|
||||
if (it->name == name) return it;
|
||||
}
|
||||
return FirstBuffer;
|
||||
}
|
||||
|
||||
inline Buffer *FindBuffer(String name) {
|
||||
for (Buffer *it = FirstBuffer; it; it = it->next) {
|
||||
if (it->name == name) return it;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
inline View *GetView(ViewID id) {
|
||||
for (View *it = FirstView; it; it = it->next) {
|
||||
if (it->id == id) return it;
|
||||
}
|
||||
return FirstView;
|
||||
}
|
||||
|
||||
inline bool IsNull(Buffer *buffer) { return buffer->id.id == NullBufferID.id; }
|
||||
inline Window *GetActiveWind() { return GetWindow(ActiveWindow); }
|
||||
|
||||
Buffer *CreateBuffer(Allocator allocator, String name, Int size) {
|
||||
Buffer *result = AllocBuffer(allocator, name, size);
|
||||
DLL_QUEUE_ADD(FirstBuffer, LastBuffer, result);
|
||||
BufferCount += 1;
|
||||
return result;
|
||||
}
|
||||
|
||||
Window *CreateWind(bool create_command_buffer = true) {
|
||||
Window *w = AllocType(SysAllocator, Window);
|
||||
w->font = &PrimaryFont;
|
||||
w->visible = true;
|
||||
w->draw_scrollbar = StyleDrawScrollbar;
|
||||
w->draw_line_numbers = StyleDrawLineNumbers;
|
||||
w->id = AllocWindowID(w);
|
||||
DLL_QUEUE_ADD(FirstWindow, LastWindow, w);
|
||||
WindowCount += 1;
|
||||
return w;
|
||||
}
|
||||
|
||||
// void DestroyWindow(Window *window) {
|
||||
// WindowSplit *split = window->split_ref;
|
||||
// if (split->parent == NULL) {
|
||||
// Assert(split->kind == WindowSplitKind_Window);
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// WindowSplit *p = split->parent;
|
||||
// WindowSplit *valid_node = p->left == split ? p->right : p->left;
|
||||
// if (p->parent == NULL) {
|
||||
// // @todo:
|
||||
// } else {
|
||||
// valid_node->parent = p->parent;
|
||||
// *p = *valid_node;
|
||||
// }
|
||||
//
|
||||
// // @leak window
|
||||
// DLL_QUEUE_REMOVE(FirstWindow, LastWindow, window);
|
||||
// WindowCount -= 1;
|
||||
//
|
||||
// if (window->search_bar_window.id) {
|
||||
// Window *s = GetWindow(window->search_bar_window);
|
||||
// DLL_QUEUE_REMOVE(FirstWindow, LastWindow, s);
|
||||
// WindowCount -= 1;
|
||||
// }
|
||||
//
|
||||
// if (window->title_bar_window.id) {
|
||||
// Window *s = GetWindow(window->title_bar_window);
|
||||
// DLL_QUEUE_REMOVE(FirstWindow, LastWindow, s);
|
||||
// WindowCount -= 1;
|
||||
// }
|
||||
// }
|
||||
|
||||
View *CreateView(BufferID active_buffer) {
|
||||
Allocator al = GetSystemAllocator();
|
||||
View *view = AllocType(al, View);
|
||||
view->id = AllocViewID(view);
|
||||
view->active_buffer = active_buffer;
|
||||
view->carets.allocator = al;
|
||||
Add(&view->carets, {0, 0});
|
||||
DLL_QUEUE_ADD(FirstView, LastView, view);
|
||||
ViewCount += 1;
|
||||
return view;
|
||||
}
|
||||
|
||||
View *FindView(ViewID view_id, View *default_view = NULL) {
|
||||
for (View *it = FirstView; it; it = it->next) {
|
||||
if (it->id == view_id) {
|
||||
return it;
|
||||
}
|
||||
}
|
||||
return default_view;
|
||||
}
|
||||
|
||||
View *FindView(BufferID buffer_id, View *default_view = NULL) {
|
||||
for (View *it = FirstView; it; it = it->next) {
|
||||
if (it->active_buffer == buffer_id) {
|
||||
return it;
|
||||
}
|
||||
}
|
||||
return default_view;
|
||||
}
|
||||
|
||||
Window *FindWindow(ViewID view_id, Window *default_window = NULL) {
|
||||
for (Window *it = FirstWindow; it; it = it->next) {
|
||||
if (it->active_view == view_id) {
|
||||
return it;
|
||||
}
|
||||
}
|
||||
return default_window;
|
||||
}
|
||||
|
||||
Window *FindWindow(String buffer_name, Window *default_window = NULL) {
|
||||
for (Window *it = FirstWindow; it; it = it->next) {
|
||||
View *it_view = GetView(it->active_view);
|
||||
Buffer *it_buffer = GetBuffer(it_view->active_buffer);
|
||||
if (it_buffer->name == buffer_name) {
|
||||
return it;
|
||||
}
|
||||
}
|
||||
return default_window;
|
||||
}
|
||||
|
||||
Window *FindWindow(BufferID buffer_id) {
|
||||
for (Window *it = FirstWindow; it; it = it->next) {
|
||||
View *view = GetView(it->active_view);
|
||||
if (view->active_buffer == buffer_id) return it;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
View *FindView(String name, View *default_view = NULL) {
|
||||
for (View *it = FirstView; it; it = it->next) {
|
||||
Buffer *buffer = GetBuffer(it->active_buffer);
|
||||
if (buffer->name == name) {
|
||||
return it;
|
||||
}
|
||||
}
|
||||
return default_view;
|
||||
}
|
||||
|
||||
Window *GetTitlebarWindow(WindowID id) {
|
||||
Window *window = GetWindow(id);
|
||||
if (!window->is_title_bar) window = GetWindow(window->title_bar_window);
|
||||
Assert(window->is_title_bar);
|
||||
return window;
|
||||
}
|
||||
|
||||
BSet GetBSet(Window *window) {
|
||||
BSet set = {window};
|
||||
set.view = GetView(set.window->active_view);
|
||||
@@ -285,30 +11,6 @@ BSet GetBSet(WindowID window_id) {
|
||||
return result;
|
||||
}
|
||||
|
||||
BSet GetTitleSet(Window *window) {
|
||||
if (!window->is_title_bar) window = GetWindow(window->title_bar_window);
|
||||
BSet result = GetBSet(window);
|
||||
return result;
|
||||
}
|
||||
|
||||
BSet GetMainSet(Window *window) {
|
||||
if (window->is_title_bar) window = GetWindow(window->title_bar_window);
|
||||
BSet result = GetBSet(window);
|
||||
return result;
|
||||
}
|
||||
|
||||
BSet GetActiveSet() {
|
||||
Window *window = GetWindow(ActiveWindow);
|
||||
return GetBSet(window);
|
||||
}
|
||||
|
||||
BSet GetActiveMainSet() {
|
||||
Window *window = GetWindow(ActiveWindow);
|
||||
if (window->is_title_bar) window = GetWindow(window->title_bar_window);
|
||||
if (window->is_search_bar) window = GetWindow(window->search_bar_window);
|
||||
return GetBSet(window);
|
||||
}
|
||||
|
||||
BSet GetConsoleSet() {
|
||||
BSet result = {};
|
||||
result.window = GetWindow(NullWindowID);
|
||||
@@ -317,15 +19,9 @@ BSet GetConsoleSet() {
|
||||
return result;
|
||||
}
|
||||
|
||||
BSet GetActiveTitleSet() {
|
||||
Window *window = GetWindow(ActiveWindow);
|
||||
if (!window->is_title_bar) window = GetWindow(window->title_bar_window);
|
||||
return GetBSet(window);
|
||||
}
|
||||
|
||||
String Command_GetFilename() {
|
||||
BSet set = GetActiveMainSet();
|
||||
return set.buffer->name;
|
||||
String GetCurrentFilename() {
|
||||
BSet main = GetBSet(LastActiveLayoutWindowID);
|
||||
return main.buffer->name;
|
||||
}
|
||||
|
||||
String GetDir(Buffer *buffer) {
|
||||
@@ -333,156 +29,33 @@ String GetDir(Buffer *buffer) {
|
||||
return name;
|
||||
}
|
||||
|
||||
String Command_GetMainDir() {
|
||||
BSet set = GetActiveMainSet();
|
||||
String name = ChopLastSlash(set.buffer->name);
|
||||
String GetMainDir() {
|
||||
BSet main = GetBSet(LastActiveLayoutWindowID);
|
||||
String name = ChopLastSlash(main.buffer->name);
|
||||
return name;
|
||||
}
|
||||
|
||||
Int ConvertUTF8ToUTF16UnixLine(String string, char16_t *buffer, Int buffer_cap) {
|
||||
if (string.len == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
Int buffer_len = 0;
|
||||
Assert(buffer_cap > string.len * 2);
|
||||
for (Int i = 0; i < string.len;) {
|
||||
if (string.data[i] == '\r') {
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
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++] = u' ';
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
uint32_t u32 = '?';
|
||||
UTF32Result decode = UTF8ToUTF32((uint8_t *)(string.data + i), (int64_t)(string.len - i));
|
||||
if (!decode.error) {
|
||||
i += decode.advance;
|
||||
u32 = decode.out_str;
|
||||
} else {
|
||||
i += 1;
|
||||
}
|
||||
|
||||
UTF16Result encode = UTF32ToUTF16(u32);
|
||||
if (!encode.error) {
|
||||
for (int64_t encode_i = 0; encode_i < encode.len; encode_i += 1) {
|
||||
buffer[buffer_len++] = encode.out_str[encode_i];
|
||||
Assert(buffer_len < buffer_cap);
|
||||
}
|
||||
} else {
|
||||
buffer[buffer_len++] = u'?';
|
||||
}
|
||||
}
|
||||
return buffer_len;
|
||||
}
|
||||
|
||||
String16 ToUnixString16(Allocator allocator, String string_) {
|
||||
Int cap = string_.len * 3;
|
||||
char16_t *string16_buffer = AllocArray(allocator, char16_t, cap);
|
||||
Int len = ConvertUTF8ToUTF16UnixLine(string_, string16_buffer, cap);
|
||||
String16 string = {string16_buffer, len};
|
||||
return string;
|
||||
}
|
||||
|
||||
Buffer *BufferOpenFile(String path) {
|
||||
Allocator sys_allocator = GetSystemAllocator();
|
||||
Scratch scratch;
|
||||
|
||||
path = GetAbsolutePath(scratch, path);
|
||||
Buffer *buffer = GetBuffer(path);
|
||||
if (!IsNull(buffer) || (IsNull(buffer) && buffer->name == path)) {
|
||||
return buffer;
|
||||
}
|
||||
|
||||
if (!FileExists(path)) {
|
||||
buffer = CreateBuffer(sys_allocator, path);
|
||||
} else if (IsDir(path)) {
|
||||
ReportWarningf("failed to open, it's a directory: %S", path);
|
||||
return GetBuffer(NullBufferID);
|
||||
} 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{(char16_t *)buffer->data, buffer->len});
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
View *OpenBufferView(String name) {
|
||||
Buffer *buffer = BufferOpenFile(name);
|
||||
View *view = CreateView(buffer->id);
|
||||
return view;
|
||||
}
|
||||
|
||||
View *WindowOpenBufferView(Window *new_parent_window, String name) {
|
||||
View *view = FindView(name);
|
||||
if (!view) {
|
||||
View *result = OpenBufferView(name);
|
||||
new_parent_window->active_view = result->id;
|
||||
return result;
|
||||
}
|
||||
|
||||
Window *window = FindWindow(view->id);
|
||||
if (!window) {
|
||||
new_parent_window->active_view = view->id;
|
||||
return view;
|
||||
}
|
||||
if (window == new_parent_window) {
|
||||
return view;
|
||||
}
|
||||
|
||||
Assert(window->active_view.id == view->id.id);
|
||||
View *result = OpenBufferView(name);
|
||||
new_parent_window->active_view = result->id;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool ViewIsCrumb(ViewID view_id) {
|
||||
for (Window *window = FirstWindow; window; window = window->next) {
|
||||
For(window->goto_history) if (it.view_id == view_id) return true;
|
||||
For(window->goto_redo) if (it.view_id == view_id) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ViewIsReferenced(ViewID view) {
|
||||
if (view == NullViewID) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ViewIsCrumb(view)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (Window *it = FirstWindow; it; it = it->next) {
|
||||
if (it->active_view == view || it->active_goto_list == view) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BufferIsReferenced(BufferID buffer_id) {
|
||||
if (buffer_id == NullBufferID) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (FindView(buffer_id)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void GarbageCollect() {
|
||||
Allocator sys_allocator = GetSystemAllocator();
|
||||
for (Buffer *it = FirstBuffer; it; it = it->next) {
|
||||
|
||||
For (Windows) {
|
||||
if (it->sync_visibility_with_focus) {
|
||||
if (it->id == ActiveWindowID) {
|
||||
it->visible = true;
|
||||
} else {
|
||||
it->visible = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Window *window = GetWindow(ActiveWindowID);
|
||||
if (ActiveWindowID.id != LastActiveLayoutWindowID.id) {
|
||||
if (window->layout) {
|
||||
LastActiveLayoutWindowID = ActiveWindowID;
|
||||
}
|
||||
}
|
||||
|
||||
For(Buffers) {
|
||||
if (it->file_mod_time) {
|
||||
int64_t new_file_mod_time = GetFileModTime(it->name);
|
||||
if (it->file_mod_time != new_file_mod_time) {
|
||||
@@ -492,8 +65,8 @@ void GarbageCollect() {
|
||||
}
|
||||
}
|
||||
|
||||
for (View *it = FirstView, *next = NULL; it; it = next) {
|
||||
next = it->next;
|
||||
IterRemove(Views) {
|
||||
IterRemovePrepare(Views);
|
||||
|
||||
Buffer *buffer = GetBuffer(it->active_buffer);
|
||||
if (!buffer->garbage) {
|
||||
@@ -505,13 +78,13 @@ void GarbageCollect() {
|
||||
continue;
|
||||
}
|
||||
|
||||
DLL_QUEUE_REMOVE(FirstView, LastView, it);
|
||||
remove_item = true;
|
||||
Dealloc(&it->carets);
|
||||
Dealloc(sys_allocator, it);
|
||||
}
|
||||
|
||||
for (Buffer *it = FirstBuffer, *next = NULL; it; it = next) {
|
||||
next = it->next;
|
||||
IterRemove(Buffers) {
|
||||
IterRemovePrepare(Buffers);
|
||||
|
||||
if (!it->garbage) {
|
||||
continue;
|
||||
@@ -522,14 +95,17 @@ void GarbageCollect() {
|
||||
continue;
|
||||
}
|
||||
|
||||
DLL_QUEUE_REMOVE(FirstBuffer, LastBuffer, it);
|
||||
remove_item = true;
|
||||
DeallocBuffer(it);
|
||||
}
|
||||
|
||||
// for (Window *it = FirstWindow, *next = NULL; it; it = next) {
|
||||
// next = it->next;
|
||||
// if (it->kill) {
|
||||
// DestroyWindow(it);
|
||||
// }
|
||||
// }
|
||||
IterRemove(Windows) {
|
||||
IterRemovePrepare(Windows);
|
||||
if (it->kill) {
|
||||
Dealloc(&it->goto_history);
|
||||
Dealloc(&it->goto_redo);
|
||||
Dealloc(sys_allocator, it);
|
||||
remove_item = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
245
src/text_editor/parser.cpp
Normal file
245
src/text_editor/parser.cpp
Normal file
@@ -0,0 +1,245 @@
|
||||
struct Lexer {
|
||||
Allocator allocator;
|
||||
char *at;
|
||||
char *start;
|
||||
char *end;
|
||||
char *name;
|
||||
int line, column;
|
||||
};
|
||||
|
||||
enum TriggerKind {
|
||||
TriggerKind_Error,
|
||||
TriggerKind_Key,
|
||||
TriggerKind_Mouse,
|
||||
TriggerKind_Binary,
|
||||
};
|
||||
|
||||
struct Trigger {
|
||||
TriggerKind kind;
|
||||
Trigger *left;
|
||||
Trigger *right;
|
||||
SDL_Keycode key;
|
||||
struct {
|
||||
EventKind event_kind : 8;
|
||||
U32 ctrl : 1;
|
||||
U32 alt : 1;
|
||||
U32 shift : 1;
|
||||
};
|
||||
};
|
||||
|
||||
void Advance(Lexer *lex) {
|
||||
if (lex->at < lex->end) {
|
||||
if (lex->at[0] == '\n') {
|
||||
lex->line += 1;
|
||||
lex->column = 0;
|
||||
} else {
|
||||
lex->column += 1;
|
||||
}
|
||||
lex->at += 1;
|
||||
}
|
||||
}
|
||||
|
||||
void Advance(Lexer *lex, int n) {
|
||||
for (int i = 0; i < n; i += 1) Advance(lex);
|
||||
}
|
||||
|
||||
char At(Lexer *lex) {
|
||||
if (lex->at < lex->end) {
|
||||
return lex->at[0];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
String AsString(Lexer *lex) {
|
||||
String result = {lex->at, lex->end - lex->at};
|
||||
return result;
|
||||
}
|
||||
|
||||
void EatWhitespace(Lexer *lex) {
|
||||
while (IsWhitespace(At(lex))) {
|
||||
Advance(lex);
|
||||
}
|
||||
}
|
||||
|
||||
Trigger *TriggerBinary(Lexer *lex, Trigger *left, Trigger *right, int key) {
|
||||
Trigger *result = AllocType(lex->allocator, Trigger);
|
||||
result->kind = TriggerKind_Binary;
|
||||
result->key = key;
|
||||
result->left = left;
|
||||
result->right = right;
|
||||
return result;
|
||||
}
|
||||
|
||||
Trigger *ParseKeyAtom(Lexer *lex) {
|
||||
Trigger *result = AllocType(lex->allocator, Trigger);
|
||||
result->kind = TriggerKind_Key;
|
||||
EatWhitespace(lex);
|
||||
for (;;) {
|
||||
String lex_string = AsString(lex);
|
||||
if (StartsWith(lex_string, "ctrl")) {
|
||||
result->ctrl = true;
|
||||
Advance(lex, 4);
|
||||
} else if (StartsWith(lex_string, "shift")) {
|
||||
result->shift = true;
|
||||
Advance(lex, 5);
|
||||
} else if (StartsWith(lex_string, "alt")) {
|
||||
result->alt = true;
|
||||
Advance(lex, 3);
|
||||
} else if (IsAlphanumeric(At(lex))) {
|
||||
char *start = lex->at;
|
||||
while (IsAlphanumeric(At(lex))) {
|
||||
Advance(lex);
|
||||
}
|
||||
String a = {start, lex->at - start};
|
||||
Advance(lex);
|
||||
|
||||
bool found = false;
|
||||
for (int i = 0; !found && i < Lengthof(MouseConversionTable); i += 1) {
|
||||
if (a == MouseConversionTable[i].string) {
|
||||
result->event_kind = MouseConversionTable[i].value;
|
||||
result->kind = TriggerKind_Mouse;
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; !found && i < Lengthof(SDLKeycodeConversionTable); i += 1) {
|
||||
if (a == SDLKeycodeConversionTable[i].string) {
|
||||
result->key = SDLKeycodeConversionTable[i].value;
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
result->kind = TriggerKind_Error;
|
||||
ReportErrorf("%s:%d:%d: Failed to parse key trigger, unexpected identifier: '%d'", lex->name, lex->line, lex->column, result->key);
|
||||
return result;
|
||||
}
|
||||
} else {
|
||||
result->kind = TriggerKind_Error;
|
||||
ReportErrorf("%s:%d:%d: Failed to parse key trigger, unexpected character: '%c'", lex->name, lex->line, lex->column, At(lex));
|
||||
return result;
|
||||
}
|
||||
|
||||
if (At(lex) == '-') {
|
||||
Advance(lex);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Trigger *ParseKeyChord(Lexer *lex) {
|
||||
Trigger *left = ParseKeyAtom(lex);
|
||||
EatWhitespace(lex);
|
||||
while (IsAlphanumeric(At(lex))) {
|
||||
int op = At(lex);
|
||||
left = TriggerBinary(lex, left, ParseKeyChord(lex), ' ');
|
||||
EatWhitespace(lex);
|
||||
}
|
||||
return left;
|
||||
}
|
||||
|
||||
Trigger *ParseKeyExpr(Lexer *lex) {
|
||||
Trigger *left = ParseKeyChord(lex);
|
||||
EatWhitespace(lex);
|
||||
while (At(lex) == '|') {
|
||||
Advance(lex);
|
||||
left = TriggerBinary(lex, left, ParseKeyExpr(lex), '|');
|
||||
EatWhitespace(lex);
|
||||
}
|
||||
return left;
|
||||
}
|
||||
|
||||
Trigger *ParseKey(Allocator allocator, String key, char *debug_name) {
|
||||
Lexer lex = {allocator, key.data, key.data, key.data + key.len, debug_name};
|
||||
Trigger *result = ParseKeyExpr(&lex);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool MatchEvent(Trigger *trigger, Event *event) {
|
||||
if (trigger->kind == TriggerKind_Key) {
|
||||
if (trigger->key == event->key && trigger->ctrl == event->ctrl && trigger->alt == event->alt && trigger->shift == event->shift) {
|
||||
return true;
|
||||
}
|
||||
} else if (trigger->kind == TriggerKind_Mouse) {
|
||||
if (trigger->event_kind == event->kind) {
|
||||
return true;
|
||||
}
|
||||
} else if (trigger->kind == TriggerKind_Binary) {
|
||||
if (trigger->key == ' ') {
|
||||
return false;
|
||||
} else if (trigger->key == '|') {
|
||||
bool ok = MatchEvent(trigger->left, event);
|
||||
if (ok) return ok;
|
||||
ok = MatchEvent(trigger->right, event);
|
||||
if (ok) return ok;
|
||||
} ElseInvalidCodepath();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void TestParser(void *param) {
|
||||
Scratch scratch;
|
||||
{
|
||||
char *cmd = "ctrl-b";
|
||||
Lexer base_lex = {scratch, cmd, cmd, cmd + strlen(cmd), "keybinding"};
|
||||
Trigger *trigger = ParseKeyExpr(&base_lex);
|
||||
Assert(trigger->kind == TriggerKind_Key);
|
||||
Assert(trigger->key == SDLK_B);
|
||||
Assert(trigger->ctrl);
|
||||
Assert(trigger->shift == 0);
|
||||
}
|
||||
|
||||
{
|
||||
char *cmd = "ctrl-b shift-ctrl-a";
|
||||
Lexer base_lex = {scratch, cmd, cmd, cmd + strlen(cmd), "keybinding"};
|
||||
Trigger *trigger = ParseKeyExpr(&base_lex);
|
||||
Assert(trigger->kind == TriggerKind_Binary);
|
||||
Assert(trigger->key == ' ');
|
||||
Assert(trigger->left->kind == TriggerKind_Key);
|
||||
Assert(trigger->left->key == SDLK_B);
|
||||
Assert(trigger->left->ctrl);
|
||||
Assert(trigger->left->shift == 0);
|
||||
Assert(trigger->right->kind == TriggerKind_Key);
|
||||
Assert(trigger->right->key == SDLK_A);
|
||||
Assert(trigger->right->ctrl);
|
||||
Assert(trigger->right->shift);
|
||||
}
|
||||
|
||||
{
|
||||
char *cmd = "ctrl-b shift-ctrl-a | ctrl-c | ctrl-d";
|
||||
Lexer base_lex = {scratch, cmd, cmd, cmd + strlen(cmd), "keybinding"};
|
||||
Trigger *trigger = ParseKeyExpr(&base_lex);
|
||||
Assert(trigger->kind == TriggerKind_Binary);
|
||||
Assert(trigger->key == '|');
|
||||
|
||||
Assert(trigger->left->kind == TriggerKind_Binary);
|
||||
Assert(trigger->left->key == ' ');
|
||||
|
||||
Assert(trigger->right->kind == TriggerKind_Binary);
|
||||
Assert(trigger->right->key == '|');
|
||||
|
||||
|
||||
Event event = {};
|
||||
event.kind = EVENT_KEY_PRESS;
|
||||
event.key = SDLK_D;
|
||||
event.ctrl = 1;
|
||||
bool ok = MatchEvent(trigger, &event);
|
||||
Assert(ok);
|
||||
|
||||
event.key = SDLK_F1;
|
||||
ok = MatchEvent(trigger, &event);
|
||||
Assert(ok == 0);
|
||||
|
||||
event.ctrl = 1;
|
||||
event.shift = 1;
|
||||
event.key = SDLK_A;
|
||||
ok = MatchEvent(trigger, &event);
|
||||
Assert(!ok);
|
||||
}
|
||||
|
||||
|
||||
} RegisterFunction(&TestFunctions, TestParser);
|
||||
@@ -15,7 +15,7 @@ void UpdateProcesses() {
|
||||
|
||||
String poll = PollStdout(scratch, &it, false);
|
||||
if (poll.len) {
|
||||
Command_Append(view, poll, it.scroll_to_end);
|
||||
Append(view, poll, it.scroll_to_end);
|
||||
}
|
||||
if (!IsValid(&it)) {
|
||||
ReportDebugf("process %lld exit code = %d", it.id, it.exit_code);
|
||||
@@ -60,7 +60,7 @@ void KillProcess(View *view) {
|
||||
KillProcess(&it);
|
||||
remove_item = true;
|
||||
String string = "process was killed by user\n";
|
||||
Command_Append(view, string, it.scroll_to_end);
|
||||
Append(view, string, it.scroll_to_end);
|
||||
// dont break because that will fuck with removal ...
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,153 +7,46 @@
|
||||
#include "external/glad/glad.h"
|
||||
#include "external/stb_truetype.h"
|
||||
#include "external/stb_truetype.c"
|
||||
#if OS_LINUX
|
||||
#define MCO_USE_UCONTEXT
|
||||
#endif
|
||||
#define MINICORO_IMPL
|
||||
#include "external/minicoro.h"
|
||||
#define LUA_USE_LONGJMP
|
||||
#include "external/luaunity.c"
|
||||
|
||||
SDL_Window *SDLWindow;
|
||||
bool IsInFullscreen;
|
||||
int FullScreenSizeX, FullScreenSizeY;
|
||||
int FullScreenPositionX, FullScreenPositionY;
|
||||
bool Testing = true;
|
||||
|
||||
#include "generated_variables.cpp"
|
||||
#include "render/generated_font.cpp"
|
||||
#include "render/font.cpp"
|
||||
#include "render/opengl.cpp"
|
||||
|
||||
#include "buffer.h"
|
||||
#include "view.h"
|
||||
#include "window.h"
|
||||
#include "text_editor.h"
|
||||
#include "intern_table.cpp"
|
||||
#include "buffer.cpp"
|
||||
#include "lua.h"
|
||||
|
||||
#include "management.cpp"
|
||||
#include "globals.cpp"
|
||||
#include "lua.cpp"
|
||||
#include "buffer.cpp"
|
||||
#include "view.cpp"
|
||||
#include "window.cpp"
|
||||
#include "management.cpp"
|
||||
#include "process.cpp"
|
||||
#include "event.cpp"
|
||||
#include "parser.cpp"
|
||||
|
||||
#include "lua_api.cpp"
|
||||
#include "commands.cpp"
|
||||
#include "lua_api.cpp"
|
||||
#include "commands_clipboard.cpp"
|
||||
#include "commands_bindings.cpp"
|
||||
#include "title_bar.cpp"
|
||||
|
||||
#include "lua_api_generated.cpp"
|
||||
#include "generated_config.cpp"
|
||||
#include "generated.cpp"
|
||||
|
||||
#include "window_draw.cpp"
|
||||
#include "draw.cpp"
|
||||
#include "coroutines.cpp"
|
||||
#include "test/tests.cpp"
|
||||
|
||||
void FillEventWithBasicData(Event *event) {
|
||||
SDL_Keymod mod = SDL_GetModState();
|
||||
event->shift = (mod & SDL_KMOD_SHIFT) != 0;
|
||||
event->ctrl = (mod & SDL_KMOD_CTRL) != 0;
|
||||
event->alt = (mod & SDL_KMOD_ALT) != 0;
|
||||
event->super = (mod & SDL_KMOD_GUI) != 0;
|
||||
|
||||
float xmouse, ymouse;
|
||||
SDL_GetMouseState(&xmouse, &ymouse);
|
||||
event->xmouse = (int16_t)xmouse;
|
||||
event->ymouse = (int16_t)ymouse;
|
||||
|
||||
int xwindow, ywindow;
|
||||
SDL_GetWindowSize(SDLWindow, &xwindow, &ywindow);
|
||||
event->xwindow = xwindow;
|
||||
event->ywindow = ywindow;
|
||||
event->text = "";
|
||||
}
|
||||
|
||||
Event TranslateSDLEvent(SDL_Event *input_event) {
|
||||
ProfileFunction();
|
||||
Event event = {};
|
||||
FillEventWithBasicData(&event);
|
||||
|
||||
switch (input_event->type) {
|
||||
case SDL_EVENT_QUIT: {
|
||||
event.kind = EVENT_QUIT;
|
||||
} break;
|
||||
|
||||
case SDL_EVENT_KEY_DOWN: {
|
||||
event.kind = EVENT_KEY_PRESS;
|
||||
SDL_KeyboardEvent &key = input_event->key;
|
||||
event.key = key.key;
|
||||
} break;
|
||||
|
||||
case SDL_EVENT_TEXT_INPUT: {
|
||||
event.kind = EVENT_TEXT_INPUT;
|
||||
SDL_TextInputEvent &b = input_event->text;
|
||||
String string = b.text;
|
||||
event.text = Intern(&GlobalInternTable, string).data;
|
||||
} break;
|
||||
|
||||
case SDL_EVENT_MOUSE_BUTTON_DOWN: {
|
||||
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;
|
||||
} else if (b.button == SDL_BUTTON_RIGHT) {
|
||||
event.kind = EVENT_MOUSE_RIGHT;
|
||||
} else if (b.button == SDL_BUTTON_MIDDLE) {
|
||||
event.kind = EVENT_MOUSE_MIDDLE;
|
||||
} 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;
|
||||
|
||||
case SDL_EVENT_MOUSE_BUTTON_UP: {
|
||||
SDL_MouseButtonEvent &b = input_event->button;
|
||||
event.xmouse = (int16_t)b.x;
|
||||
event.ymouse = (int16_t)b.y;
|
||||
if (b.button == SDL_BUTTON_LEFT) {
|
||||
event.kind = EVENT_MOUSE_LEFT_UP;
|
||||
} else if (b.button == SDL_BUTTON_RIGHT) {
|
||||
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;
|
||||
}
|
||||
} break;
|
||||
|
||||
case SDL_EVENT_MOUSE_WHEEL: {
|
||||
event.kind = EVENT_MOUSE_WHEEL;
|
||||
SDL_MouseWheelEvent &b = input_event->wheel;
|
||||
event.xmouse = (int16_t)b.mouse_x;
|
||||
event.ymouse = (int16_t)b.mouse_y;
|
||||
event.xwheel = b.x;
|
||||
event.ywheel = b.y;
|
||||
} break;
|
||||
|
||||
case SDL_EVENT_MOUSE_MOTION: {
|
||||
event.kind = EVENT_UPDATE;
|
||||
} break;
|
||||
|
||||
case SDL_EVENT_DROP_FILE: {
|
||||
event.kind = EVENT_DROP_FILE;
|
||||
SDL_DropEvent &b = input_event->drop;
|
||||
String string = b.data;
|
||||
event.text = Intern(&GlobalInternTable, string).data;
|
||||
} break;
|
||||
default: {
|
||||
};
|
||||
}
|
||||
|
||||
return event;
|
||||
}
|
||||
|
||||
#if OS_WASM
|
||||
EM_JS(void, JS_SetMouseCursor, (const char *cursor_str), {
|
||||
@@ -194,12 +87,21 @@ void SetMouseCursor(Event event) {
|
||||
Array<Window *> order = GetWindowZOrder(scratch);
|
||||
Vec2I mouse = MouseVec2I();
|
||||
|
||||
if (ResizerSelected) {
|
||||
WindowSplit *split = ResizerSelected;
|
||||
if (split->kind == WindowSplitKind_Vertical) {
|
||||
if (ResizerSelected.id != -1) {
|
||||
Window *window = GetWindow(ResizerSelected);
|
||||
if (window->layout) {
|
||||
SetMouseCursor(SDL_SYSTEM_CURSOR_EW_RESIZE);
|
||||
} else {
|
||||
SetMouseCursor(SDL_SYSTEM_CURSOR_NS_RESIZE);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (ResizerHover.id != -1) {
|
||||
Window *window = GetWindow(ResizerHover);
|
||||
if (window->layout) {
|
||||
SetMouseCursor(SDL_SYSTEM_CURSOR_EW_RESIZE);
|
||||
} else {
|
||||
Assert(split->kind == WindowSplitKind_Horizontal);
|
||||
SetMouseCursor(SDL_SYSTEM_CURSOR_NS_RESIZE);
|
||||
}
|
||||
return;
|
||||
@@ -218,18 +120,6 @@ void SetMouseCursor(Event event) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (ResizerHover) {
|
||||
WindowSplit *split = ResizerHover;
|
||||
if (split->kind == WindowSplitKind_Vertical) {
|
||||
SetMouseCursor(SDL_SYSTEM_CURSOR_EW_RESIZE);
|
||||
} else {
|
||||
Assert(split->kind == WindowSplitKind_Horizontal);
|
||||
SetMouseCursor(SDL_SYSTEM_CURSOR_NS_RESIZE);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
SetMouseCursor(SDL_SYSTEM_CURSOR_DEFAULT);
|
||||
}
|
||||
|
||||
@@ -245,15 +135,15 @@ void Update(Event event) {
|
||||
view->update_scroll = true;
|
||||
}
|
||||
|
||||
OnCommandEvent = &event;
|
||||
OnCommand(event);
|
||||
for (Window *it = FirstWindow; it; it = it->next) {
|
||||
if (it->is_title_bar) ReplaceTitleBarData(it);
|
||||
}
|
||||
OnCommandEvent = NULL;
|
||||
UpdateProcesses();
|
||||
CoUpdate(&event);
|
||||
ReloadLuaConfigs();
|
||||
CallLuaOnUpdate(&event);
|
||||
StatusBarUpdate();
|
||||
UpdateDebugBuffer();
|
||||
CallLuaOnUpdate(&event);
|
||||
GarbageCollect();
|
||||
|
||||
For(IterateInReverse(&order)) {
|
||||
@@ -264,36 +154,6 @@ void Update(Event event) {
|
||||
}
|
||||
}
|
||||
|
||||
Array<Event> GetEventsForFrame(Allocator allocator) {
|
||||
Array<Event> result = {allocator};
|
||||
if (EventPlayback.len) {
|
||||
result = TightCopy(allocator, EventPlayback);
|
||||
EventPlayback.len = 0;
|
||||
}
|
||||
|
||||
SDL_Event event;
|
||||
if (WaitForEvents) {
|
||||
SDL_WaitEvent(&event);
|
||||
Event ev = TranslateSDLEvent(&event);
|
||||
if (ev.kind != EVENT_NONE) Add(&result, ev);
|
||||
}
|
||||
|
||||
while (SDL_PollEvent(&event)) {
|
||||
Event ev = TranslateSDLEvent(&event);
|
||||
if (ev.kind != EVENT_NONE) Add(&result, ev);
|
||||
}
|
||||
|
||||
if (result.len == 0) {
|
||||
Event event = {};
|
||||
FillEventWithBasicData(&event);
|
||||
event.kind = EVENT_UPDATE;
|
||||
Add(&result, event);
|
||||
}
|
||||
|
||||
Assert(result.len);
|
||||
return result;
|
||||
}
|
||||
|
||||
void Windows_SetupVCVarsall(mco_coro *co) {
|
||||
View *view = NULL;
|
||||
{
|
||||
@@ -301,7 +161,7 @@ void Windows_SetupVCVarsall(mco_coro *co) {
|
||||
String working_dir = WorkDir;
|
||||
String buffer_name = GetUniqueBufferName(working_dir, "vcvarsall-");
|
||||
String cmd = Format(scratch, "\"%S\" && set", StyleVCVarsall);
|
||||
view = Command_ExecHidden(buffer_name, cmd, working_dir);
|
||||
view = ExecHidden(buffer_name, cmd, working_dir);
|
||||
}
|
||||
for (;;) {
|
||||
if (!ProcessIsActive(view->id)) {
|
||||
@@ -340,7 +200,7 @@ void MainLoop() {
|
||||
|
||||
if (it.xwindow == 0 || it.ywindow == 0) {
|
||||
int xwindow, ywindow;
|
||||
SDL_GetWindowSize(SDLWindow, &xwindow, &ywindow);
|
||||
SDL_GetWindowSizeInPixels(SDLWindow, &xwindow, &ywindow);
|
||||
it.xwindow = xwindow;
|
||||
it.ywindow = ywindow;
|
||||
}
|
||||
@@ -368,7 +228,6 @@ void MainLoop() {
|
||||
SetMouseCursor(*event);
|
||||
LayoutWindows(event->xwindow, event->ywindow); // This is here to render changes in title bar size without a frame of delay
|
||||
BeginFrameRender(event->xwindow, event->ywindow);
|
||||
DrawSplits(&WindowSplits);
|
||||
|
||||
Array<Window *> order = GetWindowZOrder(scratch);
|
||||
For(IterateInReverse(&order)) {
|
||||
@@ -379,7 +238,6 @@ void MainLoop() {
|
||||
SDL_GL_SwapWindow(SDLWindow);
|
||||
}
|
||||
|
||||
|
||||
#if _WIN32
|
||||
int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
|
||||
#else
|
||||
@@ -396,18 +254,21 @@ int main(int argc, char **argv)
|
||||
|
||||
if (1) {
|
||||
RunArenaTest();
|
||||
RunBufferTest();
|
||||
For (TestFunctions) {
|
||||
it.function(NULL);
|
||||
}
|
||||
|
||||
// ReportErrorf("Testing DONE\n");
|
||||
// return 0;
|
||||
}
|
||||
|
||||
#if !OS_WINDOWS
|
||||
for (int i = 0; environ[i]; i += 1) {
|
||||
Add(&Enviroment, Copy(GetSystemAllocator(), environ[i]));
|
||||
Add(&Enviroment, Copy(Perm, environ[i]));
|
||||
}
|
||||
#endif
|
||||
|
||||
WorkDir = GetWorkingDir(SysAllocator);
|
||||
WorkDir = GetWorkingDir(Perm);
|
||||
{
|
||||
String sdl_config_path = SDL_GetPrefPath("krzosa", "text_editor");
|
||||
if (sdl_config_path.len && sdl_config_path.data[sdl_config_path.len - 1] == '\\') {
|
||||
@@ -416,7 +277,7 @@ int main(int argc, char **argv)
|
||||
if (sdl_config_path.len && sdl_config_path.data[sdl_config_path.len - 1] == '/') {
|
||||
sdl_config_path = Chop(sdl_config_path, 1); // chop '/'
|
||||
}
|
||||
ConfigDir = NormalizePath(SysAllocator, sdl_config_path);
|
||||
ConfigDir = NormalizePath(Perm, sdl_config_path);
|
||||
SDL_free(sdl_config_path.data);
|
||||
}
|
||||
|
||||
@@ -455,7 +316,7 @@ int main(int argc, char **argv)
|
||||
int yhalf = 30;
|
||||
|
||||
Uint32 window_flags = SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_HIGH_PIXEL_DENSITY;
|
||||
SDLWindow = SDL_CreateWindow("Text editor", whalf, hhalf, window_flags);
|
||||
SDLWindow = SDL_CreateWindow("Text editor", whalf, hhalf, window_flags);
|
||||
if (SDLWindow == NULL) {
|
||||
ReportErrorf("Couldn't create window! %s", SDL_GetError());
|
||||
return 1;
|
||||
@@ -489,10 +350,15 @@ int main(int argc, char **argv)
|
||||
|
||||
InitBuffers();
|
||||
InitRender();
|
||||
ReloadFont();
|
||||
ReloadFont(StyleFont, (U32)StyleFontSize);
|
||||
InitWindows();
|
||||
InitOS(ReportWarningf);
|
||||
|
||||
For (CommandFunctions) {
|
||||
if (it.binding.len != 0) {
|
||||
it.trigger = ParseKey(Perm, it.binding, it.name.data);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 1; i < argc; i += 1) {
|
||||
String it = argv[i];
|
||||
|
||||
@@ -1,97 +1,61 @@
|
||||
struct Window; struct View; struct WindowSplit;
|
||||
struct ViewID { Int id; View *o; };
|
||||
struct WindowID { Int id; Window *o; };
|
||||
#define EVENT_KINDS \
|
||||
X(EVENT_NONE) \
|
||||
X(EVENT_UPDATE) \
|
||||
X(EVENT_QUIT) \
|
||||
X(EVENT_MOUSE_LEFT) \
|
||||
X(EVENT_MOUSE_RIGHT) \
|
||||
X(EVENT_MOUSE_MIDDLE) \
|
||||
X(EVENT_MOUSE_X1) \
|
||||
X(EVENT_MOUSE_X2) \
|
||||
X(EVENT_MOUSE_LEFT_UP) \
|
||||
X(EVENT_MOUSE_RIGHT_UP) \
|
||||
X(EVENT_MOUSE_MIDDLE_UP) \
|
||||
X(EVENT_MOUSE_X1_UP) \
|
||||
X(EVENT_MOUSE_X2_UP) \
|
||||
X(EVENT_MOUSE_WHEEL) \
|
||||
X(EVENT_KEY_PRESS) \
|
||||
X(EVENT_TEXT_INPUT) \
|
||||
X(EVENT_DROP_FILE)
|
||||
|
||||
struct View {
|
||||
ViewID id;
|
||||
BufferID active_buffer;
|
||||
View *next;
|
||||
View *prev;
|
||||
Vec2I scroll;
|
||||
Array<Caret> carets;
|
||||
|
||||
// window | view
|
||||
Caret main_caret_on_begin_frame;
|
||||
bool update_scroll;
|
||||
|
||||
bool fuzzy_search;
|
||||
String16 prev_search_line;
|
||||
enum EventKind {
|
||||
#define X(TYPE) TYPE,
|
||||
EVENT_KINDS
|
||||
#undef X
|
||||
EVENT_KIND_COUNT,
|
||||
EVENT_KIND_INVALID = 111,
|
||||
};
|
||||
|
||||
struct GotoCrumb {
|
||||
ViewID view_id;
|
||||
Caret caret;
|
||||
const char *EventKindStrings[] = {
|
||||
#define X(TYPE) #TYPE,
|
||||
EVENT_KINDS
|
||||
#undef X
|
||||
};
|
||||
|
||||
struct Window {
|
||||
WindowID id;
|
||||
ViewID active_view;
|
||||
Window *next;
|
||||
Window *prev;
|
||||
WindowSplit *split_ref;
|
||||
#define EVENT_FIELDS \
|
||||
X(EventKind, Int, kind) \
|
||||
X(SDL_Keycode, Int, key) \
|
||||
X(int16_t, Int, xwindow) \
|
||||
X(int16_t, Int, ywindow) \
|
||||
X(int16_t, Int, xmouse) \
|
||||
X(int16_t, Int, ymouse) \
|
||||
X(uint8_t, Int, clicks) \
|
||||
X(uint8_t, Int, shift) \
|
||||
X(uint8_t, Int, ctrl) \
|
||||
X(uint8_t, Int, alt) \
|
||||
X(uint8_t, Int, super) \
|
||||
X(float, Float, xwheel) \
|
||||
X(float, Float, ywheel) \
|
||||
X(char *, String, text)
|
||||
#define EVENT_FIELD_COUNT 14
|
||||
|
||||
WindowID title_bar_window;
|
||||
Int title_bar_last_buffer_change_id; // @todo: bring back the changes to title bar?
|
||||
|
||||
WindowID search_bar_window;
|
||||
|
||||
Rect2I total_rect;
|
||||
Rect2I document_rect;
|
||||
Rect2I scrollbar_rect;
|
||||
Rect2I line_numbers_rect;
|
||||
|
||||
Array<GotoCrumb> goto_history;
|
||||
Array<GotoCrumb> goto_redo;
|
||||
|
||||
ViewID active_goto_list;
|
||||
Int goto_list_pos;
|
||||
|
||||
Font *font;
|
||||
Caret search_bar_anchor;
|
||||
double mouse_scroller_offset;
|
||||
int z;
|
||||
|
||||
struct {
|
||||
bool draw_scrollbar : 1;
|
||||
bool draw_line_numbers : 1;
|
||||
bool visible : 1;
|
||||
|
||||
bool is_title_bar : 1;
|
||||
bool is_search_bar : 1;
|
||||
|
||||
bool deactivate_on_escape : 1;
|
||||
|
||||
bool kill : 1;
|
||||
};
|
||||
};
|
||||
|
||||
enum WindowSplitKind {
|
||||
WindowSplitKind_Window,
|
||||
WindowSplitKind_Vertical,
|
||||
WindowSplitKind_Horizontal,
|
||||
};
|
||||
|
||||
struct WindowSplit {
|
||||
WindowSplitKind kind;
|
||||
WindowSplit *left;
|
||||
WindowSplit *right;
|
||||
WindowSplit *parent;
|
||||
Rect2I total_rect;
|
||||
Rect2I resizer_rect;
|
||||
|
||||
Window *window;
|
||||
double value;
|
||||
};
|
||||
|
||||
struct Scroller {
|
||||
Rect2 rect;
|
||||
double begin;
|
||||
double end;
|
||||
Int line_count;
|
||||
struct Event {
|
||||
#define X(TYPE, KIND, NAME) TYPE NAME;
|
||||
EVENT_FIELDS
|
||||
#undef X
|
||||
};
|
||||
|
||||
struct BSet {
|
||||
Window *window;
|
||||
struct Window *window;
|
||||
View *view;
|
||||
Buffer *buffer;
|
||||
};
|
||||
@@ -106,42 +70,38 @@ Allocator SysAllocator = {SystemAllocatorProc};
|
||||
String ConfigDir;
|
||||
float DPIScale = 1.0f;
|
||||
|
||||
Rect2I GetVisibleCells(Window *window);
|
||||
void AfterEdit(View *view, Array<Edit> edits);
|
||||
Scroller ComputeScrollerRect(Window *window);
|
||||
BSet Command_Open(String path, String meta = "");
|
||||
BSet Command_Open(String16 path, String meta = "");
|
||||
BSet Open(String path, String meta = "");
|
||||
BSet Open(String16 path, String meta = "");
|
||||
void UpdateScroll(Window *window, bool update_caret_scrolling);
|
||||
|
||||
void Command_SelectEntireBuffer(View *view);
|
||||
void Command_Replace(View *view, String16 string);
|
||||
void Command_SelectRangeOneCursor(View *view, Range range);
|
||||
void Command_Append(View *view, String16 string, bool scroll_to_end_if_cursor_on_last_line);
|
||||
void Command_Append(View *view, String string, bool scroll_to_end_if_cursor_on_last_line);
|
||||
Array<Edit> Command_ReplaceEx(Allocator scratch, View *view, String16 string);
|
||||
void Command_Eval(String string);
|
||||
void Command_Eval(String16 string);
|
||||
String Command_GetMainDir();
|
||||
void SelectEntireBuffer(View *view);
|
||||
void Replace(View *view, String16 string);
|
||||
void SelectRange(View *view, Range range);
|
||||
void Append(View *view, String16 string, bool scroll_to_end_if_cursor_on_last_line);
|
||||
void Append(View *view, String string, bool scroll_to_end_if_cursor_on_last_line);
|
||||
Array<Edit> ReplaceEx(Allocator scratch, View *view, String16 string);
|
||||
void Eval(String string);
|
||||
void Eval(String16 string);
|
||||
void ReportDebugf(const char *fmt, ...);
|
||||
|
||||
void ReplaceWithoutMovingCarets(Buffer *buffer, Range range, String16 string);
|
||||
void Command_Copy(View *view);
|
||||
void Command_Paste(View *view);
|
||||
void ReplaceWithoutMovingCarets(Buffer *buffer, Range range, String16 string);
|
||||
void ClipboardCopy(View *view);
|
||||
void ClipboardPaste(View *view);
|
||||
|
||||
void ReportConsolef(const char *fmt, ...);
|
||||
void ReportErrorf(const char *fmt, ...);
|
||||
void ReportWarningf(const char *fmt, ...);
|
||||
void Command_Appendf(View *view, const char *fmt, ...);
|
||||
void Appendf(View *view, const char *fmt, ...);
|
||||
|
||||
Buffer *CreateBuffer(Allocator allocator, String name, Int size = 4096);
|
||||
View *CreateView(BufferID active_buffer);
|
||||
void ReopenBuffer(Buffer *buffer);
|
||||
inline Buffer *FindBuffer(String name);
|
||||
|
||||
inline bool operator==(WindowID a, WindowID b) { return a.id == b.id; }
|
||||
inline bool operator==(BufferID a, BufferID b) { return a.id == b.id; }
|
||||
inline bool operator==(ViewID a, ViewID b) { return a.id == b.id; }
|
||||
inline bool operator!=(WindowID a, WindowID b) { return a.id != b.id; }
|
||||
inline bool operator!=(BufferID a, BufferID b) { return a.id != b.id; }
|
||||
inline bool operator!=(ViewID a, ViewID b) { return a.id != b.id; }
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ void UpdateDebugBuffer() {
|
||||
View *view = GetView(window->active_view);
|
||||
if (view->active_buffer.id == buffer->id.id) return;
|
||||
|
||||
BSet main = GetActiveMainSet();
|
||||
BSet main = GetBSet(LastActiveLayoutWindowID);
|
||||
|
||||
Scratch scratch;
|
||||
String s = Format(scratch, "wid: %d\nvid: %d\nbid: %d\nframe: %lld\n", (int)main.window->id.id, (int)main.view->id.id, (int)main.buffer->id.id, (long long)FrameID);
|
||||
@@ -14,11 +14,9 @@ void UpdateDebugBuffer() {
|
||||
|
||||
float xmouse, ymouse;
|
||||
SDL_GetMouseState(&xmouse, &ymouse);
|
||||
RawAppendf(buffer, "mouse: [%f, %f]\n", xmouse, ymouse);
|
||||
RawAppendf(buffer, "mouse: [%f, %f]\n", roundf(DPIScale * xmouse), roundf(DPIScale * ymouse));
|
||||
|
||||
RawAppendf(buffer, "BufferID id = %d\n", main.buffer->id.id);
|
||||
RawAppendf(buffer, "Buffer *next = %zu\n", main.buffer->next);
|
||||
RawAppendf(buffer, "Buffer *prev = %zu\n", main.buffer->prev);
|
||||
RawAppendf(buffer, "String name = %S\n", main.buffer->name);
|
||||
RawAppendf(buffer, "Int change_id = %lld\n", (long long)main.buffer->change_id);
|
||||
RawAppendf(buffer, "Int user_change_id = %lld\n", (long long)main.buffer->user_change_id);
|
||||
@@ -43,10 +41,15 @@ void UpdateDebugBuffer() {
|
||||
RawAppendf(buffer, "int garbage = %d\n", main.buffer->garbage);
|
||||
}
|
||||
|
||||
void ReplaceTitleBarData(Window *window) {
|
||||
void StatusBarUpdate() {
|
||||
Window *status_bar_window = GetWindow(StatusBarWindowID, NULL);
|
||||
if (status_bar_window == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
Scratch scratch;
|
||||
BSet main = GetMainSet(window);
|
||||
BSet title = GetBSet(window);
|
||||
BSet main = GetBSet(LastActiveLayoutWindowID);
|
||||
BSet title = GetBSet(status_bar_window);
|
||||
title.view->scroll.y = 0;
|
||||
|
||||
String16 buffer_string = GetString(title.buffer);
|
||||
@@ -54,11 +57,12 @@ void ReplaceTitleBarData(Window *window) {
|
||||
bool found_separator = Seek(buffer_string, u" |", &replace_range.max);
|
||||
|
||||
// Parse the title and line
|
||||
if (window->id == ActiveWindow) {
|
||||
if (title.buffer->change_id == title.window->title_bar_last_buffer_change_id) {
|
||||
if (title.window->id == ActiveWindowID) {
|
||||
if (title.buffer->change_id == title.window->status_bar_last_buffer_change_id) {
|
||||
return;
|
||||
}
|
||||
String16 buffer_name = GetString(title.buffer, replace_range);
|
||||
buffer_name = Skip(buffer_name, 1);
|
||||
buffer_name = Trim(buffer_name);
|
||||
|
||||
Int column = ChopNumber(&buffer_name);
|
||||
@@ -70,64 +74,41 @@ void ReplaceTitleBarData(Window *window) {
|
||||
column = 0;
|
||||
}
|
||||
|
||||
String name = ToString(scratch, buffer_name);
|
||||
if (name != main.buffer->name) {
|
||||
name = GetAbsolutePath(scratch, name);
|
||||
if (FindBuffer(name)) {
|
||||
title.window->title_bar_last_buffer_change_id = title.buffer->change_id;
|
||||
ReportConsolef("there is already buffer with name: %S", name);
|
||||
return;
|
||||
}
|
||||
|
||||
if (name != main.buffer->name) {
|
||||
main.buffer->name = Intern(&GlobalInternTable, name);
|
||||
title.window->title_bar_last_buffer_change_id = title.buffer->change_id;
|
||||
main.buffer->file_mod_time = 0;
|
||||
main.buffer->changed_on_disk = false;
|
||||
main.buffer->dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
Int buffer_pos = XYToPos(main.buffer, {column, line});
|
||||
Caret &caret = main.view->carets[0];
|
||||
if (GetFront(caret) != buffer_pos) {
|
||||
caret = MakeCaret(buffer_pos);
|
||||
}
|
||||
title.window->status_bar_last_buffer_change_id = title.buffer->change_id;
|
||||
return;
|
||||
}
|
||||
|
||||
Caret caret = main.view->carets[0];
|
||||
XY xy = PosToXY(main.buffer, GetFront(caret));
|
||||
|
||||
Array<Caret> caret_copy = Copy(GetSystemAllocator(), title.view->carets);
|
||||
defer {
|
||||
Dealloc(&title.view->carets);
|
||||
title.view->carets = caret_copy;
|
||||
};
|
||||
|
||||
// add separator at the end of buffer
|
||||
if (!found_separator) {
|
||||
Command_SelectRangeOneCursor(title.view, GetBufferEndAsRange(title.buffer));
|
||||
Array<Edit> edits = Command_ReplaceEx(scratch, title.view, u" |");
|
||||
AdjustCarets(edits, &caret_copy);
|
||||
SelectRange(title.view, GetBufferEndAsRange(title.buffer));
|
||||
Array<Edit> edits = ReplaceEx(scratch, title.view, u" |");
|
||||
}
|
||||
|
||||
|
||||
// replace data up to separator with filename and stuff
|
||||
const char *reopen = main.buffer->changed_on_disk ? " Reopen()" : "";
|
||||
String s = Format(scratch, "%S:%lld:%lld%s", main.buffer->name, (long long)xy.line + 1ll, (long long)xy.col + 1ll, reopen);
|
||||
String s = Format(scratch, "# %S:%lld:%lld%s", main.buffer->name, (long long)xy.line + 1ll, (long long)xy.col + 1ll, reopen);
|
||||
For (ActiveProcesses) {
|
||||
if (it.view_id == main.view->id.id) {
|
||||
s = Format(scratch, "%S %lld", s, (long long)it.id);
|
||||
s = Format(scratch, "%S %lld Kill()", s, (long long)it.id);
|
||||
}
|
||||
}
|
||||
|
||||
String16 string = ToString16(scratch, s);
|
||||
String16 string_to_replace = GetString(title.buffer, replace_range);
|
||||
if (string_to_replace != string) {
|
||||
Command_SelectRangeOneCursor(title.view, replace_range);
|
||||
Array<Edit> edits = Command_ReplaceEx(scratch, title.view, string);
|
||||
Command_SelectRangeOneCursor(title.view, MakeRange(0));
|
||||
AdjustCarets(edits, &caret_copy);
|
||||
SelectRange(title.view, replace_range);
|
||||
Array<Edit> edits = ReplaceEx(scratch, title.view, string);
|
||||
}
|
||||
|
||||
SelectRange(title.view, MakeRange(0));
|
||||
ResetHistory(title.buffer);
|
||||
}
|
||||
77
src/text_editor/view.cpp
Normal file
77
src/text_editor/view.cpp
Normal file
@@ -0,0 +1,77 @@
|
||||
API ViewID AllocViewID(View *view) {
|
||||
return {ViewIDs.id++, view};
|
||||
}
|
||||
|
||||
API View *CreateView(BufferID active_buffer) {
|
||||
Allocator al = GetSystemAllocator();
|
||||
View *view = AllocType(al, View);
|
||||
view->id = AllocViewID(view);
|
||||
view->active_buffer = active_buffer;
|
||||
view->carets.allocator = al;
|
||||
Add(&view->carets, {0, 0});
|
||||
Add(&Views, view);
|
||||
return view;
|
||||
}
|
||||
|
||||
API View *FindView(ViewID view_id, View *default_view) {
|
||||
For(Views) {
|
||||
if (it->id == view_id) {
|
||||
return it;
|
||||
}
|
||||
}
|
||||
return default_view;
|
||||
}
|
||||
|
||||
API View *FindView(BufferID buffer_id, View *default_view) {
|
||||
For(Views) {
|
||||
if (it->active_buffer == buffer_id) {
|
||||
return it;
|
||||
}
|
||||
}
|
||||
return default_view;
|
||||
}
|
||||
|
||||
API View *FindView(String name, View *default_view) {
|
||||
For(Views) {
|
||||
Buffer *buffer = GetBuffer(it->active_buffer);
|
||||
if (buffer->name == name) {
|
||||
return it;
|
||||
}
|
||||
}
|
||||
return default_view;
|
||||
}
|
||||
|
||||
API View *GetView(ViewID id) {
|
||||
return FindView(id, Views[0]);
|
||||
}
|
||||
|
||||
API View *OpenBufferView(String name) {
|
||||
Buffer *buffer = BufferOpenFile(name);
|
||||
View *view = CreateView(buffer->id);
|
||||
return view;
|
||||
}
|
||||
|
||||
API bool ViewIsCrumb(ViewID view_id) {
|
||||
ForItem(window, Windows) {
|
||||
For(window->goto_history) if (it.view_id == view_id) return true;
|
||||
For(window->goto_redo) if (it.view_id == view_id) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
API bool ViewIsReferenced(ViewID view) {
|
||||
if (view == NullViewID) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ViewIsCrumb(view)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
For(Windows) {
|
||||
if (it->active_view == view) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
32
src/text_editor/view.h
Normal file
32
src/text_editor/view.h
Normal file
@@ -0,0 +1,32 @@
|
||||
struct View;
|
||||
struct ViewID { Int id; View *o; };
|
||||
|
||||
struct View {
|
||||
ViewID id;
|
||||
BufferID active_buffer;
|
||||
Vec2I scroll;
|
||||
Array<Caret> carets;
|
||||
|
||||
// window | view
|
||||
Caret main_caret_on_begin_frame;
|
||||
bool update_scroll;
|
||||
|
||||
bool fuzzy_search;
|
||||
String16 prev_search_line;
|
||||
};
|
||||
|
||||
struct GotoCrumb {
|
||||
ViewID view_id;
|
||||
Caret caret;
|
||||
double time;
|
||||
};
|
||||
|
||||
API ViewID AllocViewID(View *view);
|
||||
API View *CreateView(BufferID active_buffer);
|
||||
API View *FindView(ViewID view_id, View *default_view = NULL);
|
||||
API View *FindView(BufferID buffer_id, View *default_view = NULL);
|
||||
API View *FindView(String name, View *default_view = NULL);
|
||||
API View *GetView(ViewID id);
|
||||
API View *OpenBufferView(String name);
|
||||
API bool ViewIsCrumb(ViewID view_id);
|
||||
API bool ViewIsReferenced(ViewID view);
|
||||
@@ -1,188 +1,169 @@
|
||||
inline WindowID AllocWindowID(Window *window) {
|
||||
return {WindowIDs.id++, window};
|
||||
}
|
||||
|
||||
Window *CreateWind() {
|
||||
Allocator allocator = GetSystemAllocator();
|
||||
Window *w = AllocType(allocator, Window);
|
||||
w->font = &PrimaryFont;
|
||||
w->visible = true;
|
||||
w->layout = true;
|
||||
w->draw_scrollbar = StyleDrawScrollbar;
|
||||
w->draw_line_numbers = StyleDrawLineNumbers;
|
||||
w->draw_line_highlight = true;
|
||||
w->jump_history = true;
|
||||
w->id = AllocWindowID(w);
|
||||
w->weight = 1.0;
|
||||
Add(&Windows, w);
|
||||
return w;
|
||||
}
|
||||
|
||||
inline Window *GetWindow(WindowID id, Window *default_window = Windows[0]) {
|
||||
For(Windows) {
|
||||
if (it->id == id) return it;
|
||||
}
|
||||
return default_window;
|
||||
}
|
||||
|
||||
Window *FindWindow(ViewID view_id, Window *default_window = NULL) {
|
||||
For(Windows) {
|
||||
if (it->active_view == view_id) {
|
||||
return it;
|
||||
}
|
||||
}
|
||||
return default_window;
|
||||
}
|
||||
|
||||
Window *FindWindow(String buffer_name, Window *default_window = NULL) {
|
||||
For(Windows) {
|
||||
View *it_view = GetView(it->active_view);
|
||||
Buffer *it_buffer = GetBuffer(it_view->active_buffer);
|
||||
if (it_buffer->name == buffer_name) {
|
||||
return it;
|
||||
}
|
||||
}
|
||||
return default_window;
|
||||
}
|
||||
|
||||
Window *FindWindow(BufferID buffer_id) {
|
||||
For(Windows) {
|
||||
View *view = GetView(it->active_view);
|
||||
if (view->active_buffer == buffer_id) return it;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
inline Window *GetActiveWind() {
|
||||
return GetWindow(ActiveWindowID);
|
||||
}
|
||||
|
||||
inline bool IsDocumentSelectionValid() {
|
||||
if (DocumentSelected.id == -1) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool IsScrollbarSelectionValid() {
|
||||
if (ScrollbarSelected.id == -1) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
Array<Window *> GetWindowZOrder(Allocator allocator) {
|
||||
Array<Window *> order = {allocator};
|
||||
for (Window *it = FirstWindow; it; it = it->next) if (it->z == 2) Add(&order, it);
|
||||
for (Window *it = FirstWindow; it; it = it->next) if (it->z == 1) Add(&order, it);
|
||||
for (Window *it = FirstWindow; it; it = it->next) if (it->z == 0) Add(&order, it);
|
||||
For(Windows) if (it->z == 2) Add(&order, it);
|
||||
For(Windows) if (it->z == 1) Add(&order, it);
|
||||
For(Windows) if (it->z == 0) Add(&order, it);
|
||||
return order;
|
||||
}
|
||||
|
||||
Window *CreateSearchBar(WindowID parent_window_id) {
|
||||
Window *window = CreateWind(false);
|
||||
window->draw_scrollbar = false;
|
||||
window->deactivate_on_escape = true;
|
||||
window->is_search_bar = true;
|
||||
|
||||
static int BarCount;
|
||||
Allocator sys_allocator = GetSystemAllocator();
|
||||
String name = Format(sys_allocator, "%S/searchbar%d", WorkDir, ++BarCount);
|
||||
|
||||
Buffer *b = CreateBuffer(sys_allocator, name);
|
||||
View *v = CreateView(b->id);
|
||||
window->active_view = v->id;
|
||||
|
||||
Window *parent_window = GetWindow(parent_window_id);
|
||||
parent_window->search_bar_window = window->id;
|
||||
window->search_bar_window = parent_window->id;
|
||||
window->z = parent_window->z + 1;
|
||||
window->visible = false;
|
||||
|
||||
return window;
|
||||
}
|
||||
|
||||
Window *CreateTitlebar(WindowID parent_window_id) {
|
||||
Window *window = CreateWind(false);
|
||||
window->font = &SecondaryFont;
|
||||
window->draw_scrollbar = false;
|
||||
window->deactivate_on_escape = true;
|
||||
window->is_title_bar = true;
|
||||
|
||||
static int TitlebarCount;
|
||||
Allocator sys_allocator = GetSystemAllocator();
|
||||
String name = Format(sys_allocator, "%S/titlebar%d", WorkDir, ++TitlebarCount);
|
||||
|
||||
Buffer *b = CreateBuffer(sys_allocator, name);
|
||||
View *v = CreateView(b->id);
|
||||
window->active_view = v->id;
|
||||
|
||||
Window *parent_window = GetWindow(parent_window_id);
|
||||
parent_window->title_bar_window = window->id;
|
||||
window->title_bar_window = parent_window->id;
|
||||
|
||||
void ReplaceTitleBarData(Window * window);
|
||||
ReplaceTitleBarData(window);
|
||||
|
||||
return window;
|
||||
}
|
||||
|
||||
Int GetTitleBarSize(Window *window) {
|
||||
Int GetExpandingBarSize(Window *window) {
|
||||
View *view = GetView(window->active_view);
|
||||
Buffer *buffer = GetBuffer(view->active_buffer);
|
||||
float result = (float)buffer->line_starts.len * window->font->line_spacing;
|
||||
return (Int)result;
|
||||
}
|
||||
|
||||
WindowSplit *CreateSplitForWindow(WindowSplit *parent, Window *window) {
|
||||
WindowSplit *split = AllocType(SysAllocator, WindowSplit);
|
||||
split->kind = WindowSplitKind_Window;
|
||||
split->window = window;
|
||||
split->parent = parent;
|
||||
window->split_ref = split;
|
||||
return split;
|
||||
}
|
||||
|
||||
void SplitWindowEx(WindowSplit **node, WindowSplit *split, Window *target, Window *new_window, WindowSplitKind kind) {
|
||||
if (split->kind == WindowSplitKind_Horizontal || split->kind == WindowSplitKind_Vertical) {
|
||||
SplitWindowEx(&split->left, split->left, target, new_window, kind);
|
||||
SplitWindowEx(&split->right, split->right, target, new_window, kind);
|
||||
} else {
|
||||
Assert(split->kind == WindowSplitKind_Window);
|
||||
if (target != split->window) {
|
||||
return;
|
||||
}
|
||||
|
||||
WindowSplit *hori = AllocType(SysAllocator, WindowSplit);
|
||||
hori->parent = node[0]->parent;
|
||||
hori->kind = kind;
|
||||
hori->value = 0.5;
|
||||
hori->left = *node;
|
||||
hori->right = CreateSplitForWindow(hori, new_window);
|
||||
*node = hori;
|
||||
View *WindowOpenBufferView(Window *new_parent_window, String name) {
|
||||
View *view = FindView(name);
|
||||
if (!view) {
|
||||
View *result = OpenBufferView(name);
|
||||
new_parent_window->active_view = result->id;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
void SetVisibility(WindowID window_id, bool v) {
|
||||
Window *window = GetWindow(window_id);
|
||||
window->visible = v;
|
||||
|
||||
if (window->title_bar_window.id != 0) {
|
||||
Window *title_bar = GetWindow(window->title_bar_window);
|
||||
title_bar->visible = v;
|
||||
Window *window = FindWindow(view->id);
|
||||
if (!window) {
|
||||
new_parent_window->active_view = view->id;
|
||||
return view;
|
||||
}
|
||||
if (window->search_bar_window.id != 0) {
|
||||
Window *search_bar = GetWindow(window->search_bar_window);
|
||||
search_bar->visible = v;
|
||||
if (window == new_parent_window) {
|
||||
return view;
|
||||
}
|
||||
}
|
||||
|
||||
bool ToggleVisibility(WindowID window_id) {
|
||||
Window *window = GetWindow(window_id);
|
||||
bool visible = !window->visible;
|
||||
SetVisibility(window_id, visible);
|
||||
return visible;
|
||||
}
|
||||
|
||||
void LoadBigText(Buffer *buffer, int size = 5000000) {
|
||||
for (int i = 0; i < size; i += 1) {
|
||||
RawReplaceText(buffer, GetBufferEndAsRange(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) {
|
||||
RawReplaceText(buffer, GetBufferEndAsRange(buffer), u"Line number or something of the sort which is here or there or maybe somewhere else | ");
|
||||
}
|
||||
}
|
||||
|
||||
void LoadBigTextAndBigLine(Buffer *buffer, int size = 2500000) {
|
||||
LoadBigLine(buffer, size);
|
||||
LoadBigText(buffer, size);
|
||||
}
|
||||
|
||||
void LoadTextA(Buffer *buffer) {
|
||||
Scratch scratch;
|
||||
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);
|
||||
RawReplaceText(buffer, GetBufferEndAsRange(buffer), s16);
|
||||
}
|
||||
}
|
||||
|
||||
void LoadLine(Buffer *buffer) {
|
||||
Scratch scratch;
|
||||
String s = "Line number and so on óźćż";
|
||||
RawReplaceText(buffer, {}, ToString16(scratch, s));
|
||||
}
|
||||
|
||||
void LoadTestBufferMessage(Buffer *buffer) {
|
||||
String text = R"===(
|
||||
|
||||
commit 225d1ffc067da0723898ade68fb9492bbe308feb
|
||||
https://www.lua.org/manual/5.4/
|
||||
|
||||
)===";
|
||||
Scratch scratch;
|
||||
RawReplaceText(buffer, {}, ToString16(scratch, text));
|
||||
|
||||
// RawReplaceText(buffer, GetBufferEndAsRange(buffer), ToString16(scratch, text));
|
||||
Assert(window->active_view.id == view->id.id);
|
||||
View *result = OpenBufferView(name);
|
||||
new_parent_window->active_view = result->id;
|
||||
return result;
|
||||
}
|
||||
|
||||
void InitWindows() {
|
||||
WindowSplit *split = &WindowSplits;
|
||||
split->kind = WindowSplitKind_Horizontal;
|
||||
split->value = (double)0.9;
|
||||
Scratch scratch;
|
||||
|
||||
CreateWind();
|
||||
CreateWind();
|
||||
|
||||
// COMMAND BAR
|
||||
{
|
||||
Window *window = CreateWind();
|
||||
window->active_view = NullViewID;
|
||||
Assert(window->id.id == 0);
|
||||
CreateTitlebar(window->id);
|
||||
CreateSearchBar(window->id);
|
||||
|
||||
split->right = CreateSplitForWindow(split, window);
|
||||
}
|
||||
|
||||
{
|
||||
Window *window = CreateWind();
|
||||
Buffer *buffer = ScratchBuffer;
|
||||
View *view = CreateView(buffer->id);
|
||||
CommandBarWindowID = window->id;
|
||||
Buffer *buffer = CreateBuffer(SysAllocator, "command_bar");
|
||||
View *view = CreateView(buffer->id);
|
||||
window->active_view = view->id;
|
||||
CreateTitlebar(window->id);
|
||||
CreateSearchBar(window->id);
|
||||
ActiveWindow = window->id;
|
||||
|
||||
split->left = CreateSplitForWindow(split, window);
|
||||
window->draw_line_numbers = false;
|
||||
window->draw_scrollbar = false;
|
||||
window->draw_darker = true;
|
||||
window->draw_line_highlight = false;
|
||||
window->layout = false;
|
||||
window->visible = false;
|
||||
window->sync_visibility_with_focus = true;
|
||||
window->lose_focus_on_escape = true;
|
||||
window->jump_history = false;
|
||||
view->fuzzy_search = true;
|
||||
}
|
||||
|
||||
// SEARCH BAR
|
||||
{
|
||||
Window *window = CreateWind();
|
||||
SearchBarWindowID = window->id;
|
||||
Buffer *buffer = CreateBuffer(SysAllocator, "search_bar");
|
||||
SearchBufferID = buffer->id;
|
||||
View *view = CreateView(buffer->id);
|
||||
SearchViewID = view->id;
|
||||
window->active_view = view->id;
|
||||
window->draw_line_numbers = false;
|
||||
window->draw_scrollbar = false;
|
||||
window->draw_darker = true;
|
||||
window->draw_line_highlight = false;
|
||||
window->layout = false;
|
||||
window->visible = false;
|
||||
window->lose_focus_on_escape = true;
|
||||
}
|
||||
|
||||
// STATUS BAR at the bottom
|
||||
{
|
||||
Window *window = CreateWind();
|
||||
StatusBarWindowID = window->id;
|
||||
Buffer *buffer = CreateBuffer(SysAllocator, "status_bar");
|
||||
View *view = CreateView(buffer->id);
|
||||
window->active_view = view->id;
|
||||
window->font = &SecondaryFont;
|
||||
window->draw_line_numbers = false;
|
||||
window->draw_scrollbar = false;
|
||||
window->draw_line_highlight = false;
|
||||
window->draw_darker = true;
|
||||
window->layout = false;
|
||||
}
|
||||
|
||||
// DEBUG WINDOW
|
||||
{
|
||||
Window *window = CreateWind();
|
||||
DebugWindowID = window->id;
|
||||
@@ -190,6 +171,7 @@ void InitWindows() {
|
||||
window->draw_scrollbar = false;
|
||||
window->visible = false;
|
||||
window->z = 2;
|
||||
window->layout = false;
|
||||
|
||||
Buffer *buffer = CreateBuffer(SysAllocator, GetUniqueBufferName(WorkDir, "debug"));
|
||||
DebugBufferID = buffer->id;
|
||||
@@ -199,86 +181,139 @@ void InitWindows() {
|
||||
DebugViewID = view->id;
|
||||
window->active_view = view->id;
|
||||
|
||||
Window *titlebar = CreateTitlebar(window->id);
|
||||
Window *searchbar = CreateSearchBar(window->id);
|
||||
titlebar->z = 2;
|
||||
searchbar->z = 2;
|
||||
|
||||
SetVisibility(window->id, false);
|
||||
window->visible = false;
|
||||
}
|
||||
}
|
||||
|
||||
void LayoutWindowSplit(WindowSplit *split, Rect2I rect) {
|
||||
float scrollbar_size = (10.f * DPIScale);
|
||||
float resizer_size = (float)PrimaryFont.char_spacing*0.5f;
|
||||
void CalcNiceties(Window *n) {
|
||||
float scrollbar_size = (10.f * DPIScale);
|
||||
float line_numbers_size = (float)n->font->char_spacing * 10.f;
|
||||
if (n->draw_scrollbar) n->scrollbar_rect = CutRight(&n->document_rect, (Int)scrollbar_size);
|
||||
if (n->draw_line_numbers) n->line_numbers_rect = CutLeft(&n->document_rect, (Int)line_numbers_size);
|
||||
}
|
||||
|
||||
if (split->kind == WindowSplitKind_Window) {
|
||||
Window *it = split->window;
|
||||
float line_numbers_size = (float)it->font->char_spacing * 10;
|
||||
Assert(it->split_ref);
|
||||
it->total_rect = rect;
|
||||
|
||||
Window *title_bar_window = GetWindow(it->title_bar_window);
|
||||
title_bar_window->total_rect = CutBottom(&it->total_rect, GetTitleBarSize(title_bar_window));
|
||||
title_bar_window->document_rect = title_bar_window->total_rect;
|
||||
|
||||
Rect2I save_rect = it->document_rect;
|
||||
CutLeft(&save_rect, GetSize(save_rect).x/2);
|
||||
Window *search_bar_window = GetWindow(it->search_bar_window);
|
||||
search_bar_window->total_rect = CutTop(&save_rect, GetTitleBarSize(search_bar_window));
|
||||
search_bar_window->document_rect = search_bar_window->total_rect;
|
||||
|
||||
it->document_rect = it->total_rect;
|
||||
if (it->draw_scrollbar) it->scrollbar_rect = CutRight(&it->document_rect, (Int)scrollbar_size);
|
||||
if (it->draw_line_numbers) it->line_numbers_rect = CutLeft(&it->document_rect, (Int)line_numbers_size);
|
||||
} else if (split->kind == WindowSplitKind_Vertical) {
|
||||
Rect2I rect2 = {0};
|
||||
split->total_rect = rect;
|
||||
rect2 = CutLeft(&rect, (Int)round((double)GetSize(rect).x * split->value));
|
||||
split->resizer_rect = CutRight(&rect2, (Int)resizer_size);
|
||||
|
||||
LayoutWindowSplit(split->left, rect2);
|
||||
LayoutWindowSplit(split->right, rect);
|
||||
} else if (split->kind == WindowSplitKind_Horizontal) {
|
||||
Rect2I rect2 = {0};
|
||||
split->total_rect = rect;
|
||||
rect2 = CutTop(&rect, (Int)round((double)GetSize(rect).y * split->value));
|
||||
split->resizer_rect = CutTop(&rect, (Int)resizer_size);
|
||||
|
||||
LayoutWindowSplit(split->left, rect2);
|
||||
LayoutWindowSplit(split->right, rect);
|
||||
} else {
|
||||
Assert(!"Invalid codepath");
|
||||
double WindowCalcEvenResizerValue(Int screen_size_x, Int *out_count = NULL) {
|
||||
double w = 0;
|
||||
Int c = 0;
|
||||
ForItem(n, Windows) {
|
||||
if (n->layout) {
|
||||
w += n->weight;
|
||||
c += 1;
|
||||
}
|
||||
}
|
||||
if (out_count) *out_count = c;
|
||||
return (double)screen_size_x / w;
|
||||
}
|
||||
|
||||
void LayoutWindows(int16_t wx, int16_t wy) {
|
||||
Rect2I screen_rect = RectI0Size(wx, wy);
|
||||
LayoutWindowSplit(&WindowSplits, screen_rect);
|
||||
Rect2I screen_rect = RectI0Size(wx, wy);
|
||||
|
||||
// layout debug window
|
||||
// Command bar
|
||||
{
|
||||
Window *window = GetWindow(DebugWindowID);
|
||||
Window *n = GetWindow(CommandBarWindowID);
|
||||
Rect2I *rect = &screen_rect;
|
||||
Rect2I copy_rect = screen_rect;
|
||||
if (!n->visible) {
|
||||
rect = ©_rect;
|
||||
}
|
||||
Int barsize = Clamp((Int)n->font->line_spacing*10, (Int)0, (Int)wx - 100);
|
||||
n->document_rect = n->total_rect = CutBottom(rect, barsize);
|
||||
}
|
||||
|
||||
// bar at the bottom
|
||||
{
|
||||
Window *n = GetWindow(StatusBarWindowID);
|
||||
Rect2I *rect = &screen_rect;
|
||||
Rect2I copy_rect = screen_rect;
|
||||
if (!n->visible) {
|
||||
rect = ©_rect;
|
||||
}
|
||||
Int barsize = GetExpandingBarSize(n);
|
||||
n->document_rect = n->total_rect = CutBottom(rect, barsize);
|
||||
}
|
||||
|
||||
// search bar
|
||||
{
|
||||
Window *n = GetWindow(SearchBarWindowID);
|
||||
Rect2I *rect = &screen_rect;
|
||||
Rect2I copy_rect = screen_rect;
|
||||
if (!n->visible) {
|
||||
rect = ©_rect;
|
||||
}
|
||||
Int barsize = GetExpandingBarSize(n);
|
||||
n->document_rect = n->total_rect = CutBottom(rect, barsize);
|
||||
}
|
||||
|
||||
// floating debug window
|
||||
{
|
||||
Window *n = GetWindow(DebugWindowID);
|
||||
Rect2 screen_rect = Rect0Size(wx, wy);
|
||||
Vec2 size = GetSize(screen_rect);
|
||||
|
||||
Rect2 a = CutLeft(&screen_rect, 0.3f * size.x);
|
||||
Rect2 b = CutBottom(&a, 0.4f * size.y);
|
||||
Rect2 a = CutRight(&screen_rect, 0.3f * size.x);
|
||||
Rect2 b = CutTop(&a, 0.4f * size.y);
|
||||
Rect2 c = Shrink(b, 20);
|
||||
|
||||
window->total_rect = ToRect2I(c);
|
||||
|
||||
Window *title_bar_window = GetWindow(window->title_bar_window);
|
||||
title_bar_window->total_rect = CutBottom(&window->total_rect, GetTitleBarSize(title_bar_window));
|
||||
title_bar_window->document_rect = title_bar_window->total_rect;
|
||||
|
||||
Rect2I save_rect = window->document_rect;
|
||||
CutLeft(&save_rect, GetSize(save_rect).x/2);
|
||||
Window *search_bar_window = GetWindow(window->search_bar_window);
|
||||
search_bar_window->total_rect = CutTop(&save_rect, GetTitleBarSize(search_bar_window));
|
||||
search_bar_window->document_rect = search_bar_window->total_rect;
|
||||
|
||||
window->document_rect = window->total_rect;
|
||||
n->document_rect = n->total_rect = ToRect2I(c);
|
||||
}
|
||||
|
||||
}
|
||||
// Column layout
|
||||
Int c = 0;
|
||||
double size = WindowCalcEvenResizerValue(wx, &c);
|
||||
if (c == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
ForItem(n, Windows) {
|
||||
if (!n->layout) {
|
||||
continue;
|
||||
}
|
||||
|
||||
n->total_rect = n->document_rect = CutLeft(&screen_rect, (Int)(size * n->weight));
|
||||
if (i != (c - 1)) {
|
||||
Int resizer_size = (Int)(PrimaryFont.char_spacing*0.5f);
|
||||
n->resizer_rect = CutRight(&n->document_rect, resizer_size);
|
||||
} else {
|
||||
n->resizer_rect = {};
|
||||
}
|
||||
CalcNiceties(n);
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
Window *GetOverlappingWindow(Vec2I p, Window *default_window = NULL) {
|
||||
For(Windows) {
|
||||
if (AreOverlapping(p, it->total_rect)) {
|
||||
return it;
|
||||
}
|
||||
}
|
||||
return default_window;
|
||||
}
|
||||
|
||||
Vec2I GetSideOfWindow(Window *window, int direction) {
|
||||
Vec2I p = {};
|
||||
Rect2I rect = window->total_rect;
|
||||
float resizer_size = (float)window->font->char_spacing*0.5f; // @check_codebase_when_changing
|
||||
if (direction == DIR_LEFT) {
|
||||
p.x = rect.min.x - (Int)(resizer_size + window->font->char_spacing);
|
||||
p.y = rect.min.y + (rect.max.y / 2);
|
||||
} else if (direction == DIR_RIGHT) {
|
||||
p.x = rect.max.x + (Int)(resizer_size + window->font->char_spacing);
|
||||
p.y = rect.min.y + (rect.max.y / 2);
|
||||
} else if (direction == DIR_UP) {
|
||||
p.x = rect.min.x + (rect.max.x / 2);
|
||||
p.y = rect.min.y - (Int)(resizer_size + window->font->line_spacing);
|
||||
} else {
|
||||
Assert(direction == DIR_DOWN);
|
||||
p.x = rect.min.x + (rect.max.x / 2);
|
||||
p.y = rect.max.y + (Int)(resizer_size + window->font->line_spacing);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
Window *SwitchWindow(int direction) {
|
||||
Window *window = GetWindow(ActiveWindowID);
|
||||
Vec2I p = GetSideOfWindow(window, direction);
|
||||
Window *result = GetOverlappingWindow(p, window);
|
||||
return result;
|
||||
}
|
||||
|
||||
50
src/text_editor/window.h
Normal file
50
src/text_editor/window.h
Normal file
@@ -0,0 +1,50 @@
|
||||
struct Window;
|
||||
struct WindowID { Int id; Window *o; };
|
||||
|
||||
struct Window {
|
||||
WindowID id;
|
||||
ViewID active_view;
|
||||
|
||||
Rect2I total_rect;
|
||||
Rect2I document_rect;
|
||||
Rect2I scrollbar_rect;
|
||||
Rect2I line_numbers_rect;
|
||||
Rect2I resizer_rect;
|
||||
|
||||
Font *font;
|
||||
double mouse_scroller_offset;
|
||||
int z;
|
||||
double weight;
|
||||
Int status_bar_last_buffer_change_id;
|
||||
|
||||
Array<GotoCrumb> goto_history;
|
||||
Array<GotoCrumb> goto_redo;
|
||||
|
||||
ViewID active_goto_list;
|
||||
Int goto_list_pos;
|
||||
|
||||
struct {
|
||||
bool draw_scrollbar : 1;
|
||||
bool draw_line_numbers : 1;
|
||||
bool draw_darker : 1;
|
||||
bool draw_line_highlight : 1;
|
||||
bool visible : 1;
|
||||
bool layout : 1;
|
||||
bool kill : 1;
|
||||
bool sync_visibility_with_focus : 1;
|
||||
bool lose_focus_on_escape : 1;
|
||||
bool jump_history : 1;
|
||||
bool eval_command : 1;
|
||||
};
|
||||
};
|
||||
|
||||
struct Scroller {
|
||||
Rect2 rect;
|
||||
double begin;
|
||||
double end;
|
||||
Int line_count;
|
||||
};
|
||||
|
||||
inline bool operator==(WindowID a, WindowID b) { return a.id == b.id; }
|
||||
inline bool operator!=(WindowID a, WindowID b) { return a.id != b.id; }
|
||||
Rect2I GetVisibleCells(Window *window);
|
||||
Reference in New Issue
Block a user