Compare commits

..

18 Commits

Author SHA1 Message Date
Krzosa Karol
7720176e60 First hooks 2025-12-18 08:29:54 +01:00
Krzosa Karol
29002c965c Commands and keybindings 2025-12-16 23:07:16 +01:00
Krzosa Karol
5e7acd4a20 Binding parser, TestFunctions 2025-12-15 23:29:18 +01:00
Krzosa Karol
4e5a0f6a9b Remove NextActiveWindowID 2025-12-14 23:08:22 +01:00
Krzosa Karol
f297006dcb RegisterLua and ShowCommands, continue refactor 2025-12-14 21:54:37 +01:00
Krzosa Karol
85ca1a6a9e RegisterCommand 2025-12-14 17:32:48 +01:00
Krzosa Karol
5a12ab8d8c Linux add backtrace, fixing scaling / DPI problems 2025-12-14 17:05:55 +01:00
Krzosa Karol
2d79790d83 Remove init.project.lua 2025-12-14 12:04:03 +01:00
Krzosa Karol
a351d2eb41 restructuring 2025-12-14 10:32:42 +01:00
Krzosa Karol
0424ca62f2 Fix eval error 2025-12-14 08:18:17 +01:00
Krzosa Karol
83d5ddff9d Fix lua bug 2025-12-09 08:31:31 +01:00
Krzosa Karol
b600361278 Refactor Command_s 2025-12-08 09:23:07 +01:00
Krzosa Karol
9d29a1c187 Refactoring Command_s, NextWindowID 2025-12-08 09:16:31 +01:00
Krzosa Karol
20207e6040 jump history 2025-12-08 08:37:52 +01:00
Krzosa Karol
ef6a7be285 Command bar, undo merge time 2025-12-07 13:57:51 +01:00
Krzosa Karol
df84d1605d Bring back status bar with new design, fixing bugs 2025-12-07 10:40:30 +01:00
Krzosa Karol
88a5adaa0a Big refactor new layout 2025-12-06 19:12:06 +01:00
Krzosa Karol
e6e1ae0223 Switch to using arrays for Windows, Views, Buffers list, try to make DeleteWindow work 2025-12-05 08:58:46 +01:00
39 changed files with 2817 additions and 2591 deletions

0
build.bat Normal file → Executable file
View File

81
build.sh Normal file → Executable file
View File

@@ -3,79 +3,10 @@
mkdir build mkdir build
cd build cd build
if [ "$1" = "release" ]; then FLAGS="-nostdlib++ -fno-exceptions -fdiagnostics-absolute-paths -g -Wno-writable-strings -I../src -DDEBUG_BUILD=1"
profile_flags="-DDEBUG_BUILD=0 -O2" # clang -o metaprogram $FLAGS ../src/metaprogram/metaprogram.cpp
else # ./metaprogram
profile_flags="-DDEBUG_BUILD=1"
fi
if [ ! -f "lbaselib.o" ]; then I="-I../src/external/SDL/include -I../src/external/lua/src -I../src/external/glad"
clang -g -I../src/external/lua/src -I../src/external/glad \ clang -o te $FLAGS ../src/text_editor/text_editor.cpp $I -lSDL3 -lm -lbacktrace
../src/external/lua/src/lbaselib.c \ cp te ../data/te
../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

0
build_web.bat Normal file → Executable file
View File

0
build_web.sh Normal file → Executable file
View File

View 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.VCVarsall = "C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Auxiliary/Build/vcvars64.bat"
Style.TrimWhitespaceOnSave = true Style.TrimWhitespaceOnSave = true
Style.ClangFormatOnSave = false Style.ClangFormatOnSave = false
Style.StyleUndoMergeTimeout = 0.3
INTERNET_BROWSER = 'C:/Program Files/BraveSoftware/Brave-Browser/Application/brave.exe' INTERNET_BROWSER = 'C:/Program Files/BraveSoftware/Brave-Browser/Application/brave.exe'
OS_WINDOWS = 0 OS_WINDOWS = 0
@@ -323,10 +324,6 @@ function MatchGotoBuild(s, meta)
end end
function MatchExec(s, meta) function MatchExec(s, meta)
if meta ~= "exec" then
return nil
end
if s:match(".exe$") or s:match(".bat$") or s:match(".sh$") then if s:match(".exe$") or s:match(".bat$") or s:match(".sh$") then
return {kind = "exec_console", cmd = s, working_dir = GetMainDir()} return {kind = "exec_console", cmd = s, working_dir = GetMainDir()}
end end
@@ -335,8 +332,7 @@ function MatchExec(s, meta)
return {kind = "exec_console", cmd = s:sub(2, -1), working_dir = GetMainDir()} return {kind = "exec_console", cmd = s:sub(2, -1), working_dir = GetMainDir()}
end end
Eval(s) return nil
return {kind = "skip"}
end end
BuiltinOnOpenMatchers = { BuiltinOnOpenMatchers = {

View File

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

View File

@@ -1,44 +1,31 @@
REDESIGN and DELETE CODE
- Reduce the amount of actions needed to navigate using keyboard
- Make mouse important but much less so
Needs to change:
- Make it more similar to sublime like editors
- Need Ctrl + P
- Clickable title bar may be cool or whatever but it's pretty bad
- Executing lua commands is clunky, need a real Ctrl+P and keybind actions, popups: remove a lot of the lua functionality, just for config files
- Window, View, Buffer + flags design (or is completely new kind based approach needed for Windows/Views?) - Window, View, Buffer + flags design (or is completely new kind based approach needed for Windows/Views?)
- How to make non-editable, informative, with different font size, title bar. Which might also contain tabs - How to make non-editable, informative, with different font size, title bar. Which might also contain tabs
- How to design clickable tree view in this way? - How to design clickable tree view in this way?
- How to design Command view? - How to design Command view?
- How to design popup view (input field)? - How to design popup view (input field)?
- How to design search view? or search and replace view? - How to design search view? or search and replace view?
- Window management, splitting, GC
Things I like:
- Basic editing
- Configurable Open
- Lua config files work pretty well
Splits: Splits:
- Buffer16 Buffer8?
- Why constraint that name of buffer needs to be unique? For Open() and default behavior but is this required? - 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 - 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? Commands TODO:
- window->document_rect is null - 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?
backlog
DESIGN try to make console less special, make stuff reusable etc. DESIGN try to make console less special, make stuff reusable etc.
DESIGN Config file versions, when loading should be checked, at the top of the file, what to do when old version? DESIGN Config file versions, when loading should be checked, at the top of the file, what to do when old version?
ISSUE Ctrl+Alt+Down (DuplicateLine) doesn't work on ubuntu ISSUE Ctrl+Alt+Down (DuplicateLine) doesn't work on ubuntu
@@ -71,7 +58,6 @@ FEATURE group history entries so the you can rollback through multiple ones at o
- code sections, visual demarkation if beginning of line has a very specific text + goto next / goto prev section hotkey! - code sections, visual demarkation if beginning of line has a very specific text + goto next / goto prev section hotkey!
- combine glyph and selection rendering - combine glyph and selection rendering
backlog
- expose a coroutine based scripting enviorment where user can execute shell commands wait for them and perform actions in very linear manner - expose a coroutine based scripting enviorment where user can execute shell commands wait for them and perform actions in very linear manner
- Test stdin writing code - Test stdin writing code
- Implement shell interaction (the valid cmd lines should start with '>' or '$', user can add more lines like this to expand the command size maybe?, if we have a case where we have a line with '>' but the last line doesn't have (just a space) then it should execute?) - Implement shell interaction (the valid cmd lines should start with '>' or '$', user can add more lines like this to expand the command size maybe?, if we have a case where we have a line with '>' but the last line doesn't have (just a space) then it should execute?)

View File

@@ -42,6 +42,8 @@ API bool VDecommit(void *p, size_t size) {
} }
#elif OS_LINUX || OS_MAC #elif OS_LINUX || OS_MAC
#include <sys/mman.h>
API void *VReserve(size_t size) { API void *VReserve(size_t size) {
void *result = mmap(0, size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, (off_t)0); void *result = mmap(0, size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, (off_t)0);

View File

@@ -37,8 +37,6 @@ For(arr.reverse_iter()) {
#define ForItem(it, array) for (auto &it : (array)) #define ForItem(it, array) for (auto &it : (array))
#define For(array) ForItem(it, array) #define For(array) ForItem(it, array)
template <class T> template <class T>
struct Slice { struct Slice {
T *data; T *data;

View File

@@ -213,3 +213,8 @@ inline uint64_t GetRandomU64(RandomSeed *state) {
x ^= x << 17; x ^= x << 17;
return state->a = x; 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)

View File

@@ -14,11 +14,58 @@
#include <stdlib.h> #include <stdlib.h>
#include <spawn.h> #include <spawn.h>
#include <poll.h> #include <poll.h>
#include <execinfo.h>
#include <backtrace.h>
API void (*Error)(const char *, ...); 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 *, ...)) { API void InitOS(void (*error_proc)(const char *, ...)) {
Error = error_proc; Error = error_proc;
RegisterCrashHandler();
} }
API String ReadFile(Allocator al, String path) { 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 *dir_char_ending = it->is_directory ? "/" : "";
const char *separator = it->path.data[it->path.len - 1] == '/' ? "" : "/"; 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); 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; it->is_valid = true;
return; return;
} }
@@ -759,7 +806,6 @@ API Process SpawnProcess(String command_line, String working_dir, String write_s
String16 cmd = ToString16(scratch, command_line); String16 cmd = ToString16(scratch, command_line);
char *env = NULL; char *env = NULL;
// TODO: FIX ARENA ALLOCATION USING PushSize, Prealloc maybe? Maybe we want a block arena
if (enviroment.len) { if (enviroment.len) {
Int size = GetSize(enviroment) + enviroment.len + 1; Int size = GetSize(enviroment) + enviroment.len + 1;
env = (char *)PushSize(scratch, size); env = (char *)PushSize(scratch, size);
@@ -1098,3 +1144,7 @@ API void CloseStdin(Process *process) {
} }
#endif #endif
API double GetTimeSeconds() {
return GetTimeMicros() / 1000000.0;
}

View File

@@ -388,7 +388,7 @@ GLuint UploadAtlas(Atlas *atlas) {
return tex; return tex;
} }
void ReloadFont() { void ReloadFont(String path, U32 size) {
if (PrimaryFont.texture_id) { if (PrimaryFont.texture_id) {
glDeleteTextures(1, &PrimaryFont.texture_id); glDeleteTextures(1, &PrimaryFont.texture_id);
Dealloc(&PrimaryFont.glyphs); Dealloc(&PrimaryFont.glyphs);
@@ -397,7 +397,7 @@ void ReloadFont() {
Scratch scratch; Scratch scratch;
Atlas atlas = CreateAtlas(scratch, {2048, 2048}); Atlas atlas = CreateAtlas(scratch, {2048, 2048});
PrimaryFont = CreateFont(&atlas, (uint32_t)ClampBottom(2u, (U32)StyleFontSize), StyleFont); PrimaryFont = CreateFont(&atlas, (uint32_t)ClampBottom(2u, (U32)size), path);
SecondaryFont = CreateFont(&atlas, 10, StyleFont); SecondaryFont = CreateFont(&atlas, 12, path);
SecondaryFont.texture_id = PrimaryFont.texture_id = UploadAtlas(&atlas); SecondaryFont.texture_id = PrimaryFont.texture_id = UploadAtlas(&atlas);
} }

View File

@@ -140,8 +140,8 @@ void PlayTestOpen(mco_coro *co) {
} }
void Test(mco_coro *co) { void Test(mco_coro *co) {
Wait(co); // First phase starts immediately but stuff is not initialized so Command_Open acts weird Wait(co); // First phase starts immediately but stuff is not initialized so Open acts weird
Command_Open(TestDir); Open(TestDir);
PlayTestOpen(co); PlayTestOpen(co);
Release(&TestArena); Release(&TestArena);

View File

@@ -1,6 +1,5 @@
#define BUFFER_DEBUG 0 #define BUFFER_DEBUG 0
API Range MakeRange(Int a, Int b) { API Range MakeRange(Int a, Int b) {
Range result = {Min(a, b), Max(a, b)}; Range result = {Min(a, b), Max(a, b)};
return result; return result;
@@ -94,6 +93,16 @@ API Int GetBack(Caret caret) {
return result; 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) { API Caret MakeCaret(Int pos) {
Caret result = {}; Caret result = {};
result.range.min = result.range.max = pos; 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) { void SaveHistoryBeforeMergeCursor(Buffer *buffer, Array<HistoryEntry> *stack, Array<Caret> &carets) {
if (buffer->no_history) return; if (buffer->no_history) return;
HistoryEntry entry = {}; HistoryEntry entry = {};
entry.carets = TightCopy(GetSystemAllocator(), carets); entry.time = GetTimeSeconds();
entry.carets = TightCopy(GetSystemAllocator(), carets);
Add(stack, entry); Add(stack, entry);
} }
void SaveHistoryBeforeApplyEdits(Buffer *buffer, Array<HistoryEntry> *stack, Array<Edit> &edits) { void SaveHistoryBeforeApplyEdits(Buffer *buffer, Array<HistoryEntry> *stack, Array<Edit> &edits) {
ProfileFunction(); ProfileFunction();
if (buffer->no_history) return; if (buffer->no_history) return;
HistoryEntry *entry = GetLast(*stack); HistoryEntry *entry = GetLast(*stack);
Allocator sys_allocator = GetSystemAllocator(); Allocator sys_allocator = GetSystemAllocator();
entry->edits = TightCopy(sys_allocator, edits); entry->edits = TightCopy(sys_allocator, edits);
// Make reverse edits // Make reverse edits
For(entry->edits) { For(entry->edits) {
@@ -992,8 +1002,16 @@ API void UndoEdit(Buffer *buffer, Array<Caret> *carets) {
Allocator sys_allocator = GetSystemAllocator(); Allocator sys_allocator = GetSystemAllocator();
For(entry.edits) Dealloc(sys_allocator, it.string.data); For(entry.edits) Dealloc(sys_allocator, it.string.data);
Dealloc(&entry.edits); 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) { API void DeallocHistoryEntries(Array<HistoryEntry> *entries) {
For(*entries) { For(*entries) {
Dealloc(&it.carets); Dealloc(&it.carets);
@@ -1003,6 +1021,21 @@ API void DeallocHistoryEntries(Array<HistoryEntry> *entries) {
entries->len = 0; 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) { void ClearRedoStack(Buffer *buffer) {
DeallocHistoryEntries(&buffer->redo_stack); DeallocHistoryEntries(&buffer->redo_stack);
} }
@@ -1170,8 +1203,6 @@ API void InitBuffer(Allocator allocator, Buffer *buffer, BufferID id = {}, Strin
} }
API void DeinitBuffer(Buffer *buffer) { API void DeinitBuffer(Buffer *buffer) {
Assert(buffer->next == NULL);
Assert(buffer->prev == NULL);
Allocator allocator = buffer->line_starts.allocator; Allocator allocator = buffer->line_starts.allocator;
Dealloc(allocator, buffer->data); Dealloc(allocator, buffer->data);
Dealloc(&buffer->line_starts); 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 // 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 // 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) { API Buffer *AllocBuffer(Allocator allocator, String name = "", Int size = 4096) {
Buffer *buffer = AllocType(allocator, Buffer); Buffer *buffer = AllocType(allocator, Buffer);
buffer->id = {BufferIDs++, buffer}; buffer->id = {BufferIDs++, buffer};
@@ -1380,6 +1410,165 @@ void RunBufferTest() {
DeinitBuffer(&buffer); DeinitBuffer(&buffer);
TrackingAllocatorCheck(); 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;
} }

View File

@@ -12,12 +12,11 @@ struct Edit {
struct HistoryEntry { struct HistoryEntry {
Array<Edit> edits; Array<Edit> edits;
Array<Caret> carets; Array<Caret> carets;
double time;
}; };
struct Buffer { struct Buffer {
BufferID id; BufferID id;
Buffer *next;
Buffer *prev;
String name; String name;
Int change_id; Int change_id;
Int user_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 RedoEdit(Buffer *buffer, Array<Caret> *carets);
API void UndoEdit(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 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

View File

@@ -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) { void OnCommand(Event event) {
ProfileFunction(); ProfileFunction();
// //
@@ -111,7 +94,7 @@ void OnCommand(Event event) {
view->scroll.y = (Int)(v * (double)s.line_count * (double)window->font->line_spacing); view->scroll.y = (Int)(v * (double)s.line_count * (double)window->font->line_spacing);
} }
if (DocumentSelected != ActiveWindow) { if (DocumentSelected != ActiveWindowID) {
DocumentSelected.id = -1; DocumentSelected.id = -1;
} else if (IsDocumentSelectionValid() && MouseUp()) { } else if (IsDocumentSelectionValid() && MouseUp()) {
Assert(ScrollbarSelected.id == -1); Assert(ScrollbarSelected.id == -1);
@@ -119,7 +102,6 @@ void OnCommand(Event event) {
} else if (IsDocumentSelectionValid()) { } else if (IsDocumentSelectionValid()) {
Assert(ScrollbarSelected.id == -1); Assert(ScrollbarSelected.id == -1);
BSet selected = GetBSet(DocumentSelected); BSet selected = GetBSet(DocumentSelected);
Vec2I mouse = MouseVec2I(); Vec2I mouse = MouseVec2I();
// Special case for full-screen where we can have document // Special case for full-screen where we can have document
// aligned with monitor screen in which case mouse cursor cannot // 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) { if (mouse.y == 0 && selected.window->document_rect.min.y == 0) {
float x, y; float x, y;
SDL_GetGlobalMouseState(&x, &y); SDL_GetGlobalMouseState(&x, &y);
x = roundf(DPIScale * x);
y = roundf(DPIScale * y);
if (y == 0) { if (y == 0) {
mouse.y = -10; 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 &caret = selected.view->carets[0];
caret = SetFrontWithAnchor(caret, DocumentAnchor, p); caret = SetFrontWithAnchor(caret, DocumentAnchor, p);
} }
if (ResizerSelected && Mouse(LEFT_UP)) { if (ResizerSelected.id != -1 && Mouse(LEFT_UP)) {
Assert(DocumentSelected.id == -1); Assert(DocumentSelected.id == -1);
Assert(ScrollbarSelected.id == -1); Assert(ScrollbarSelected.id == -1);
ResizerSelected = NULL; ResizerSelected.id = {-1};
} else if (ResizerSelected) { } else if (ResizerSelected.id != -1) {
Vec2I mouse = MouseVec2I(); Window *window = GetWindow(ResizerSelected);
mouse -= ResizerSelected->total_rect.min; if (window->layout) {
Vec2I size = GetSize(ResizerSelected->total_rect); Vec2I mouse = MouseVec2I();
Vec2 p = ToVec2(mouse) / ToVec2(size); Int offx = mouse.x - window->resizer_rect.min.x;
if (ResizerSelected->kind == WindowSplitKind_Vertical) { window->weight += (double)offx / (double)WindowCalcEvenResizerValue(event.xwindow);
ResizerSelected->value = p.x; window->weight = Clamp(window->weight, 0.1, 100.0);
} else {
ResizerSelected->value = p.y;
} }
} else { } 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 // Set active window on click
@@ -165,19 +156,12 @@ void OnCommand(Event event) {
} }
bool mouse_in_document = AreOverlapping(mouse, it->document_rect); bool mouse_in_document = AreOverlapping(mouse, it->document_rect);
if (mouse_in_document) { if (mouse_in_document) {
ActiveWindow = it->id; ActiveWindowID = it->id;
break; break;
} }
} }
} }
if (Mouse(X2)) {
GotoForward(GetActiveMainSet().window);
}
if (Mouse(X1)) {
GotoBackward(GetActiveMainSet().window);
}
if (Ctrl() && Shift() && Mouse(RIGHT)) { if (Ctrl() && Shift() && Mouse(RIGHT)) {
} else if (Alt() && Ctrl() && Mouse(RIGHT)) { } else if (Alt() && Ctrl() && Mouse(RIGHT)) {
@@ -185,8 +169,8 @@ void OnCommand(Event event) {
} else if (Alt() && Mouse(RIGHT)) { } else if (Alt() && Mouse(RIGHT)) {
} else if (Mouse(RIGHT)) { } else if (Mouse(RIGHT)) {
Vec2I mouse = MouseVec2I(); Vec2I mouse = MouseVec2I();
BSet active = GetActiveSet(); BSet active = GetBSet(ActiveWindowID);
bool mouse_in_document = AreOverlapping(mouse, active.window->document_rect); bool mouse_in_document = AreOverlapping(mouse, active.window->document_rect);
if (mouse_in_document) { if (mouse_in_document) {
Int p = ScreenSpaceToBufferPos(active.window, active.view, active.buffer, mouse); Int p = ScreenSpaceToBufferPos(active.window, active.view, active.buffer, mouse);
@@ -213,9 +197,7 @@ void OnCommand(Event event) {
} }
} }
if (Shift() && Ctrl() && Mouse(LEFT)) { if (Ctrl() && Mouse(LEFT)) {
MouseLoadWord(event, "exec");
} else if (Ctrl() && Mouse(LEFT)) {
MouseLoadWord(event); MouseLoadWord(event);
} else if (Mouse(LEFT)) { // Uses Alt and shift } else if (Mouse(LEFT)) { // Uses Alt and shift
Vec2I mouse = MouseVec2I(); Vec2I mouse = MouseVec2I();
@@ -223,12 +205,13 @@ void OnCommand(Event event) {
Assert(ScrollbarSelected.id == -1); Assert(ScrollbarSelected.id == -1);
Assert(DocumentSelected.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_document = AreOverlapping(mouse, active.window->document_rect);
bool mouse_in_line_numbers = AreOverlapping(mouse, active.window->line_numbers_rect); bool mouse_in_line_numbers = AreOverlapping(mouse, active.window->line_numbers_rect);
if (mouse_in_document || mouse_in_line_numbers) { if (mouse_in_document || mouse_in_line_numbers) {
CheckpointBeforeGoto(active.window);
DocumentSelected = active.window->id; DocumentSelected = active.window->id;
CheckpointBeforeGoto(active.window);
Int p = ScreenSpaceToBufferPos(active.window, active.view, active.buffer, mouse); Int p = ScreenSpaceToBufferPos(active.window, active.view, active.buffer, mouse);
if (Alt()) Insert(&active.view->carets, MakeCaret(p, p), 0); if (Alt()) Insert(&active.view->carets, MakeCaret(p, p), 0);
@@ -283,398 +266,54 @@ void OnCommand(Event event) {
} }
} }
BSet main = GetBSet(LastActiveLayoutWindowID);
BSet active = GetBSet(ActiveWindowID);
if (CtrlAltPress(SDLK_P)) { // @todo: somehow detect in post command that buffer changed but random buffer can get changed??? Not sure
Command_ListBuffers(); // maybe we
} 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();
Int buffer_change_id = active.buffer->change_id; Int buffer_change_id = active.buffer->change_id;
String event_text = event.text;
bool skip = CallOnCommand(&event); SkipRemainingCommands = false;
if (skip) { For (CommandFunctions) {
// :OnCommandEnding if (it.trigger && MatchEvent(it.trigger, &event)) {
MergeCarets(active.buffer, &active.view->carets); it.function();
IF_DEBUG(AssertRanges(active.view->carets)); if (SkipRemainingCommands) {
return; break;
}
}
} }
if (event.kind == EVENT_DROP_FILE) { if (event.kind == EVENT_DROP_FILE) {
WindowOpenBufferView(active.window, event.text); SkipRemainingCommands = false;
} For (OnDropFileHooks) {
it.function(&event_text);
if (CtrlAltPress(SDLK_DOWN)) { if (SkipRemainingCommands) {
Command_DuplicateLine(active.view, DIR_DOWN); break;
} 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);
} }
if (event.kind == EVENT_TEXT_INPUT) { if (event.kind == EVENT_TEXT_INPUT) {
Scratch scratch; SkipRemainingCommands = false;
String string = event.text; For (OnTextInputHooks) {
String16 string16 = ToString16(scratch, string); it.function(&event_text);
Command_Replace(active.view, string16); if (SkipRemainingCommands) {
} break;
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);
}
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);
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);
Buffer *temp_buffer = CreateTempBuffer(scratch, active.buffer->cap);
For(IterateInReverse(&ratings)) {
String16 s = GetLineStringWithoutNL(active.buffer, it.index);
if (s.len == 0) continue;
RawReplaceText(temp_buffer, GetBufferEndAsRange(temp_buffer), s);
RawReplaceText(temp_buffer, GetBufferEndAsRange(temp_buffer), u"\n");
}
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));
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); MergeCarets(active.buffer, &active.view->carets);
IF_DEBUG(AssertRanges(active.view->carets)); IF_DEBUG(AssertRanges(active.view->carets));
MergeCarets(main.buffer, &main.view->carets);
IF_DEBUG(AssertRanges(main.view->carets));
SkipRemainingCommands = false;
For (PostCommandHooks) {
it.function(NULL);
if (SkipRemainingCommands) {
return;
}
}
} }

View File

@@ -33,7 +33,7 @@ void SaveStringInClipboard(String16 string) {
_SetClipboardText(ToString(scratch, SavedClipboardString).data); _SetClipboardText(ToString(scratch, SavedClipboardString).data);
} }
void Command_Copy(View *view) { void ClipboardCopy(View *view) {
Scratch scratch; Scratch scratch;
Buffer *buffer = GetBuffer(view->active_buffer); Buffer *buffer = GetBuffer(view->active_buffer);
@@ -59,7 +59,7 @@ void Command_Copy(View *view) {
_SetClipboardText(ToString(scratch, SavedClipboardString).data); _SetClipboardText(ToString(scratch, SavedClipboardString).data);
} }
void Command_Paste(View *view) { void ClipboardPaste(View *view) {
Scratch scratch; Scratch scratch;
Buffer *buffer = GetBuffer(view->active_buffer); Buffer *buffer = GetBuffer(view->active_buffer);
const char *text = GetClipboardText(); const char *text = GetClipboardText();
@@ -86,3 +86,20 @@ void Command_Paste(View *view) {
} }
EndEdit(buffer, &edits, &view->carets, KILL_SELECTION); EndEdit(buffer, &edits, &view->carets, KILL_SELECTION);
} }
void Command_Paste() {
BSet active = GetBSet(ActiveWindowID);
ClipboardPaste(active.view);
} RegisterCommand(Command_Paste, "ctrl-v");
void Command_Copy() {
BSet active = GetBSet(ActiveWindowID);
ClipboardCopy(active.view);
} RegisterCommand(Command_Copy, "ctrl-c");
void Command_Cut() {
BSet active = GetBSet(ActiveWindowID);
SaveCaretHistoryBeforeBeginEdit(active.buffer, active.view->carets);
ClipboardCopy(active.view);
Replace(active.view, u"");
} RegisterCommand(Command_Cut, "ctrl-x");

View File

@@ -46,7 +46,7 @@ void DrawVisibleText(Window *window, Color tint) {
Rect2I visible = GetVisibleCells(window); 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) { 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); 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; 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) { 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); Rect2 screen_rect = Rect0Size(event.xwindow, event.ywindow);
SetScissor(screen_rect); SetScissor(screen_rect);
bool is_actib = window->id == ActiveWindow || window->title_bar_window == ActiveWindow || window->search_bar_window == ActiveWindow; bool is_active = window->id == ActiveWindowID;
bool is_active = window->id == ActiveWindow; bool active_layed_out_doc = window->id == LastActiveLayoutWindowID;
Color color_whitespace_during_selection = ColorWhitespaceDuringSelection; Color color_whitespace_during_selection = ColorWhitespaceDuringSelection;
Color color_background = ColorBackground; Color color_background = ColorBackground;
@@ -116,7 +116,8 @@ void DrawWindow(Window *window, Event &event) {
Color color_sub_caret = ColorSubCaret; Color color_sub_caret = ColorSubCaret;
Color color_text_line_numbers = ColorTextLineNumbers; Color color_text_line_numbers = ColorTextLineNumbers;
Color color_text = ColorText; Color color_text = ColorText;
if (window->is_title_bar || window->is_search_bar) {
if (window->draw_darker) {
if (is_active) { if (is_active) {
color_background = ColorTitleBarActiveBackground; color_background = ColorTitleBarActiveBackground;
} else { } else {
@@ -127,7 +128,12 @@ void DrawWindow(Window *window, Event &event) {
color_line_highlight = ColorTitleBarBackground; color_line_highlight = ColorTitleBarBackground;
} }
DrawRect(window->total_rect, color_background); 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; 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; 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 // Draw highlight
Int front = GetFront(it); Int front = GetFront(it);
@@ -226,8 +232,9 @@ void DrawWindow(Window *window, Event &event) {
EndProfileScope(); EndProfileScope();
DrawVisibleText(window, color_text); DrawVisibleText(window, color_text);
BeginProfileScope(draw_carets); // Draw caret "|" markings
if (is_actib) { if (is_active) {
BeginProfileScope(draw_carets);
For(view->carets) { For(view->carets) {
Int front = GetFront(it); Int front = GetFront(it);
XY fxy = PosToXY(buffer, front); 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); DrawCaret(window, fxy, 0.3f, main_caret ? color_main_caret : color_sub_caret);
} }
} }
EndProfileScope();
} }
EndProfileScope();
// Draw line numbers // Draw line numbers
if (window->draw_line_numbers) { if (window->draw_line_numbers) {
@@ -271,31 +278,22 @@ void DrawWindow(Window *window, Event &event) {
DrawRect(rect, color); DrawRect(rect, color);
} }
if (window->z == 1) { // color the floating object to make it stand out
if (window->z >= 1) {
SetScissor(window->total_rect); SetScissor(window->total_rect);
DrawRect(window->total_rect, {255, 255, 255, 25}); DrawRect(window->total_rect, {255, 255, 255, 25});
} }
if (!is_actib) { // darken the inactive windows
if (!is_active) {
SetScissor(screen_rect); SetScissor(screen_rect);
DrawRect(window->total_rect, ColorInactiveWindow); DrawRect(window->total_rect, ColorInactiveWindow);
} }
}
void DrawSplits(WindowSplit *split) { // Draw resizer rect
if (split == NULL) { {
return; 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);
} }

View File

@@ -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) { const char *SDLKeycodeToName(SDL_Keycode keycode) {
switch(keycode) { switch(keycode) {
case SDLK_UNKNOWN: return "SDLK_UNKNOWN"; break; 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 { struct Serializer {
Buffer *buffer; // for writing Buffer *buffer; // for writing
}; };
@@ -386,17 +553,6 @@ void Serialize(Serializer *s, Event *e) {
SerializeEnd(s); 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 Ctrl() event.ctrl
#define Alt() event.alt #define Alt() event.alt
#define Shift() event.shift #define Shift() event.shift

View File

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

View File

@@ -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.VCVarsall = "C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Auxiliary/Build/vcvars64.bat"
Style.TrimWhitespaceOnSave = true Style.TrimWhitespaceOnSave = true
Style.ClangFormatOnSave = false Style.ClangFormatOnSave = false
Style.StyleUndoMergeTimeout = 0.3
INTERNET_BROWSER = 'C:/Program Files/BraveSoftware/Brave-Browser/Application/brave.exe' INTERNET_BROWSER = 'C:/Program Files/BraveSoftware/Brave-Browser/Application/brave.exe'
OS_WINDOWS = 0 OS_WINDOWS = 0
@@ -324,10 +325,6 @@ function MatchGotoBuild(s, meta)
end end
function MatchExec(s, meta) function MatchExec(s, meta)
if meta ~= "exec" then
return nil
end
if s:match(".exe$") or s:match(".bat$") or s:match(".sh$") then if s:match(".exe$") or s:match(".bat$") or s:match(".sh$") then
return {kind = "exec_console", cmd = s, working_dir = GetMainDir()} return {kind = "exec_console", cmd = s, working_dir = GetMainDir()}
end end
@@ -336,8 +333,7 @@ function MatchExec(s, meta)
return {kind = "exec_console", cmd = s:sub(2, -1), working_dir = GetMainDir()} return {kind = "exec_console", cmd = s:sub(2, -1), working_dir = GetMainDir()}
end end
Eval(s) return nil
return {kind = "skip"}
end end
BuiltinOnOpenMatchers = { BuiltinOnOpenMatchers = {

View File

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

229
src/text_editor/globals.cpp Normal file
View File

@@ -0,0 +1,229 @@
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);
}
typedef void Function();
typedef void PFunction(void *param);
typedef int LuaFunction(lua_State *state);
struct FunctionData { String name; Function *function; };
struct LuaFunctionData { String name; LuaFunction *function; };
struct CommandData { String name; String binding; Function *function; struct Trigger *trigger; };
struct PFunctionData { String name; PFunction *function; };
bool SkipRemainingCommands;
Array<CommandData> CommandFunctions;
Array<LuaFunctionData> LuaFunctions;
Array<FunctionData> TestFunctions;
Array<PFunctionData> PostCommandHooks;
Array<PFunctionData> OnTextInputHooks;
Array<PFunctionData> OnDropFileHooks;
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}); } };
#define RegisterLua(NAME) Register_Lua RL_##NAME(NAME, #NAME)
struct Register_Command { Register_Command(Function *function, String name, String binding) { if (StartsWith(name, "Command_")) name = Skip(name, 8); Add(&CommandFunctions, {name, binding, function}); } };
#define RegisterCommand(name, binding) Register_Command RC__##name(name, #name, binding)
struct Register_Hook { Register_Hook(Array<PFunctionData> *functions, PFunction *function, String name) { if (StartsWith(name, "Hook_")) name = Skip(name, 5); Add(functions, {name, function}); } };
#define RegisterHook(functions, name) Register_Hook RC__##name(functions, (PFunction *)name, #name)
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;

View File

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

View File

@@ -4,33 +4,27 @@ int Lua_print(lua_State *L) {
View *null_view = GetView(NullViewID); View *null_view = GetView(NullViewID);
for (int i = 1; i <= nargs; i += 1) { for (int i = 1; i <= nargs; i += 1) {
String string = lua_tostring(L, i); 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); lua_pop(L, nargs);
return 0; return 0;
} } RegisterLua(Lua_print);
int Lua_Print(lua_State *L) { int Lua_Print(lua_State *L) {
Scratch scratch; Scratch scratch;
int nargs = lua_gettop(L); int nargs = lua_gettop(L);
for (int i = 1; i <= nargs; i += 1) { for (int i = 1; i <= nargs; i += 1) {
String string = lua_tostring(L, i); 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); lua_pop(L, nargs);
return 0; return 0;
} } RegisterLua(Lua_Print);
int Lua_Kill(lua_State *L) {
BSet main = GetActiveMainSet();
KillProcess(main.view);
return 0;
}
int Lua_GetLoadWord(lua_State *L) { int Lua_GetLoadWord(lua_State *L) {
BSet active = GetActiveSet(); BSet active = GetBSet(ActiveWindowID);
Range range = active.view->carets[0].range; Range range = active.view->carets[0].range;
if (GetSize(range) == 0) { if (GetSize(range) == 0) {
range = EncloseLoadWord(active.buffer, range.min); range = EncloseLoadWord(active.buffer, range.min);
@@ -40,7 +34,7 @@ int Lua_GetLoadWord(lua_State *L) {
String string = AllocCharString(scratch, active.buffer, range); String string = AllocCharString(scratch, active.buffer, range);
lua_pushlstring(L, string.data, string.len); lua_pushlstring(L, string.data, string.len);
return 1; return 1;
} } RegisterLua(Lua_GetLoadWord);
int Lua_BufferExists(lua_State *L) { int Lua_BufferExists(lua_State *L) {
String string = lua_tostring(L, 1); String string = lua_tostring(L, 1);
@@ -48,47 +42,47 @@ int Lua_BufferExists(lua_State *L) {
Buffer *buffer = GetBuffer(string); Buffer *buffer = GetBuffer(string);
lua_pushboolean(L, buffer != NULL); lua_pushboolean(L, buffer != NULL);
return 1; return 1;
} } RegisterLua(Lua_BufferExists);
int Lua_GetSelection(lua_State *L) { int Lua_GetSelection(lua_State *L) {
Scratch scratch; Scratch scratch;
BSet main = GetActiveMainSet(); BSet main = GetBSet(LastActiveLayoutWindowID);
String16 string16 = GetString(main.buffer, main.view->carets[0].range); String16 string16 = GetString(main.buffer, main.view->carets[0].range);
String string = ToString(scratch, string16); String string = ToString(scratch, string16);
lua_pushlstring(L, string.data, string.len); lua_pushlstring(L, string.data, string.len);
return 1; return 1;
} } RegisterLua(Lua_GetSelection);
int Lua_GetEntireBuffer(lua_State *L) { int Lua_GetEntireBuffer(lua_State *L) {
Scratch scratch; Scratch scratch;
BSet main = GetActiveMainSet(); BSet main = GetBSet(LastActiveLayoutWindowID);
String16 string16 = GetString(main.buffer); String16 string16 = GetString(main.buffer);
String string = ToString(scratch, string16); String string = ToString(scratch, string16);
lua_pushlstring(L, string.data, string.len); lua_pushlstring(L, string.data, string.len);
return 1; return 1;
} } RegisterLua(Lua_GetEntireBuffer);
int Lua_GetClipboard(lua_State *L) { int Lua_GetClipboard(lua_State *L) {
Scratch scratch; Scratch scratch;
String string = ToString(scratch, SavedClipboardString); String string = ToString(scratch, SavedClipboardString);
lua_pushlstring(L, string.data, string.len); lua_pushlstring(L, string.data, string.len);
return 1; return 1;
} } RegisterLua(Lua_GetClipboard);
int Lua_GetFilename(lua_State *L) { int Lua_GetFilename(lua_State *L) {
BSet main = GetActiveMainSet(); BSet main = GetBSet(LastActiveLayoutWindowID);
lua_pushlstring(L, main.buffer->name.data, main.buffer->name.len); lua_pushlstring(L, main.buffer->name.data, main.buffer->name.len);
return 1; return 1;
} } RegisterLua(Lua_GetFilename);
int Lua_GetLine(lua_State *L) { int Lua_GetLine(lua_State *L) {
BSet main = GetActiveMainSet(); BSet main = GetBSet(LastActiveLayoutWindowID);
Caret caret = main.view->carets[0]; Caret caret = main.view->carets[0];
Int front = GetFront(caret); Int front = GetFront(caret);
Int line = PosToLine(main.buffer, front); Int line = PosToLine(main.buffer, front);
lua_pushinteger(L, line + 1); lua_pushinteger(L, line + 1);
return 1; return 1;
} } RegisterLua(Lua_GetLine);
int Lua_FileExists(lua_State *L) { int Lua_FileExists(lua_State *L) {
String path = luaL_checkstring(L, 1); String path = luaL_checkstring(L, 1);
@@ -96,147 +90,186 @@ int Lua_FileExists(lua_State *L) {
bool exists = FileExists(path); bool exists = FileExists(path);
lua_pushboolean(L, exists); lua_pushboolean(L, exists);
return 1; return 1;
} } RegisterLua(Lua_FileExists);
int Lua_GetWorkDir(lua_State *L) { int Lua_GetWorkDir(lua_State *L) {
lua_pushlstring(L, WorkDir.data, WorkDir.len); lua_pushlstring(L, WorkDir.data, WorkDir.len);
return 1; return 1;
} } RegisterLua(Lua_GetWorkDir);
int Lua_GetExeDir(lua_State *L) { int Lua_GetExeDir(lua_State *L) {
Scratch scratch; Scratch scratch;
String exe_dir = GetExeDir(scratch); String exe_dir = GetExeDir(scratch);
lua_pushlstring(L, exe_dir.data, exe_dir.len); lua_pushlstring(L, exe_dir.data, exe_dir.len);
return 1; return 1;
} } RegisterLua(Lua_GetExeDir);
int Lua_GetMainDir(lua_State *L) { int Lua_GetMainDir(lua_State *L) {
String name = Command_GetMainDir(); String name = GetMainDir();
lua_pushlstring(L, name.data, name.len); lua_pushlstring(L, name.data, name.len);
return 1; return 1;
} } RegisterLua(Lua_GetMainDir);
int Lua_SplitSize(lua_State *L) { int Lua_ListCommands(lua_State *L) {
lua_Number num = lua_tonumber(L, 1); 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); lua_pop(L, 1);
BSet set = GetActiveMainSet(); lua_getfield(L, -1, "kind");
WindowSplit *split = set.window->split_ref; String kind = lua_tostring(L, -1);
split->parent->value = num; 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; return 0;
} } RegisterLua(Lua_Cmd);
int Lua_KillWindow(lua_State *L) { Int GetFieldAInt(lua_State *L, const char *name) {
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) {
lua_getfield(L, -1, name); lua_getfield(L, -1, name);
lua_Integer num = lua_tointeger(L, -1); lua_Integer num = lua_tointeger(L, -1);
lua_pop(L, 1); lua_pop(L, 1);
return (Int)num; return (Int)num;
} }
double GetFloat(lua_State *L, const char *name) { double GetFieldAFloat(lua_State *L, const char *name) {
lua_getfield(L, -1, name); lua_getfield(L, -1, name);
double num = lua_tonumber(L, -1); double num = lua_tonumber(L, -1);
lua_pop(L, 1); lua_pop(L, 1);
return num; 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); lua_getfield(L, -1, name);
const char *result = lua_tostring(L, -1); const char *result = lua_tostring(L, -1);
lua_pop(L, 1); lua_pop(L, 1);
return result; 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 // :Event
int Lua_Play(lua_State *L) { int Lua_Play(lua_State *L) {
if (!lua_istable(L, -1)) luaL_error(L, "expected a table of events"); 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); }; defer { lua_pop(L, 1); };
Event event = {}; 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 EVENT_FIELDS
#undef X #undef X
Add(&EventPlayback, event); Add(&EventPlayback, event);
} }
return 0; return 0;
} } RegisterLua(Lua_Play);
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;
return result; int Lua_TrimTrailingWhitespace(lua_State *L) {
} lua_Integer buffer_id = luaL_checkinteger(L, 1);
int trim_lines_with_caret = lua_toboolean(L, 2);
bool CallIsCode(String path, String meta = "") { lua_pop(L, 2);
lua_getglobal(LuaState, "IsCode"); Buffer *buffer = GetBuffer({buffer_id});
lua_pushlstring(LuaState, path.data, path.len); TrimTrailingWhitespace(buffer, trim_lines_with_caret);
lua_pushlstring(LuaState, meta.data, meta.len); return 0;
if (!CallLuaFunc("IsCode", 2, 1)) { } RegisterLua(Lua_TrimTrailingWhitespace);
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();
}

View File

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

View File

@@ -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 GetBSet(Window *window) {
BSet set = {window}; BSet set = {window};
set.view = GetView(set.window->active_view); set.view = GetView(set.window->active_view);
@@ -285,30 +11,6 @@ BSet GetBSet(WindowID window_id) {
return result; 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 GetConsoleSet() {
BSet result = {}; BSet result = {};
result.window = GetWindow(NullWindowID); result.window = GetWindow(NullWindowID);
@@ -317,15 +19,9 @@ BSet GetConsoleSet() {
return result; return result;
} }
BSet GetActiveTitleSet() { String GetCurrentFilename() {
Window *window = GetWindow(ActiveWindow); BSet main = GetBSet(LastActiveLayoutWindowID);
if (!window->is_title_bar) window = GetWindow(window->title_bar_window); return main.buffer->name;
return GetBSet(window);
}
String Command_GetFilename() {
BSet set = GetActiveMainSet();
return set.buffer->name;
} }
String GetDir(Buffer *buffer) { String GetDir(Buffer *buffer) {
@@ -333,156 +29,33 @@ String GetDir(Buffer *buffer) {
return name; return name;
} }
String Command_GetMainDir() { String GetMainDir() {
BSet set = GetActiveMainSet(); BSet main = GetBSet(LastActiveLayoutWindowID);
String name = ChopLastSlash(set.buffer->name); String name = ChopLastSlash(main.buffer->name);
return 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() { void GarbageCollect() {
Allocator sys_allocator = GetSystemAllocator(); 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) { if (it->file_mod_time) {
int64_t new_file_mod_time = GetFileModTime(it->name); int64_t new_file_mod_time = GetFileModTime(it->name);
if (it->file_mod_time != new_file_mod_time) { if (it->file_mod_time != new_file_mod_time) {
@@ -492,8 +65,8 @@ void GarbageCollect() {
} }
} }
for (View *it = FirstView, *next = NULL; it; it = next) { IterRemove(Views) {
next = it->next; IterRemovePrepare(Views);
Buffer *buffer = GetBuffer(it->active_buffer); Buffer *buffer = GetBuffer(it->active_buffer);
if (!buffer->garbage) { if (!buffer->garbage) {
@@ -505,13 +78,13 @@ void GarbageCollect() {
continue; continue;
} }
DLL_QUEUE_REMOVE(FirstView, LastView, it); remove_item = true;
Dealloc(&it->carets); Dealloc(&it->carets);
Dealloc(sys_allocator, it); Dealloc(sys_allocator, it);
} }
for (Buffer *it = FirstBuffer, *next = NULL; it; it = next) { IterRemove(Buffers) {
next = it->next; IterRemovePrepare(Buffers);
if (!it->garbage) { if (!it->garbage) {
continue; continue;
@@ -522,14 +95,17 @@ void GarbageCollect() {
continue; continue;
} }
DLL_QUEUE_REMOVE(FirstBuffer, LastBuffer, it); remove_item = true;
DeallocBuffer(it); DeallocBuffer(it);
} }
// for (Window *it = FirstWindow, *next = NULL; it; it = next) { IterRemove(Windows) {
// next = it->next; IterRemovePrepare(Windows);
// if (it->kill) { if (it->kill) {
// DestroyWindow(it); Dealloc(&it->goto_history);
// } Dealloc(&it->goto_redo);
// } Dealloc(sys_allocator, it);
remove_item = true;
}
}
} }

245
src/text_editor/parser.cpp Normal file
View 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() {
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);

View File

@@ -15,7 +15,7 @@ void UpdateProcesses() {
String poll = PollStdout(scratch, &it, false); String poll = PollStdout(scratch, &it, false);
if (poll.len) { if (poll.len) {
Command_Append(view, poll, it.scroll_to_end); Append(view, poll, it.scroll_to_end);
} }
if (!IsValid(&it)) { if (!IsValid(&it)) {
ReportDebugf("process %lld exit code = %d", it.id, it.exit_code); ReportDebugf("process %lld exit code = %d", it.id, it.exit_code);
@@ -60,7 +60,7 @@ void KillProcess(View *view) {
KillProcess(&it); KillProcess(&it);
remove_item = true; remove_item = true;
String string = "process was killed by user\n"; 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 ... // dont break because that will fuck with removal ...
} }
} }

View File

@@ -7,153 +7,46 @@
#include "external/glad/glad.h" #include "external/glad/glad.h"
#include "external/stb_truetype.h" #include "external/stb_truetype.h"
#include "external/stb_truetype.c" #include "external/stb_truetype.c"
#if OS_LINUX
#define MCO_USE_UCONTEXT
#endif
#define MINICORO_IMPL #define MINICORO_IMPL
#include "external/minicoro.h" #include "external/minicoro.h"
#define LUA_USE_LONGJMP #define LUA_USE_LONGJMP
#include "external/luaunity.c" #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/generated_font.cpp"
#include "render/font.cpp" #include "render/font.cpp"
#include "render/opengl.cpp" #include "render/opengl.cpp"
#include "buffer.h" #include "buffer.h"
#include "view.h"
#include "window.h"
#include "text_editor.h" #include "text_editor.h"
#include "intern_table.cpp" #include "lua.h"
#include "buffer.cpp"
#include "management.cpp" #include "globals.cpp"
#include "lua.cpp"
#include "buffer.cpp"
#include "view.cpp"
#include "window.cpp" #include "window.cpp"
#include "management.cpp"
#include "process.cpp" #include "process.cpp"
#include "event.cpp" #include "event.cpp"
#include "parser.cpp"
#include "lua_api.cpp"
#include "commands.cpp" #include "commands.cpp"
#include "lua_api.cpp"
#include "commands_clipboard.cpp" #include "commands_clipboard.cpp"
#include "commands_bindings.cpp" #include "commands_bindings.cpp"
#include "title_bar.cpp" #include "title_bar.cpp"
#include "lua_api_generated.cpp"
#include "generated_config.cpp" #include "generated_config.cpp"
#include "generated.cpp"
#include "window_draw.cpp" #include "draw.cpp"
#include "coroutines.cpp" #include "coroutines.cpp"
#include "test/tests.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 #if OS_WASM
EM_JS(void, JS_SetMouseCursor, (const char *cursor_str), { EM_JS(void, JS_SetMouseCursor, (const char *cursor_str), {
@@ -194,12 +87,21 @@ void SetMouseCursor(Event event) {
Array<Window *> order = GetWindowZOrder(scratch); Array<Window *> order = GetWindowZOrder(scratch);
Vec2I mouse = MouseVec2I(); Vec2I mouse = MouseVec2I();
if (ResizerSelected) { if (ResizerSelected.id != -1) {
WindowSplit *split = ResizerSelected; Window *window = GetWindow(ResizerSelected);
if (split->kind == WindowSplitKind_Vertical) { 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); SetMouseCursor(SDL_SYSTEM_CURSOR_EW_RESIZE);
} else { } else {
Assert(split->kind == WindowSplitKind_Horizontal);
SetMouseCursor(SDL_SYSTEM_CURSOR_NS_RESIZE); SetMouseCursor(SDL_SYSTEM_CURSOR_NS_RESIZE);
} }
return; return;
@@ -218,18 +120,6 @@ void SetMouseCursor(Event event) {
return; 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); SetMouseCursor(SDL_SYSTEM_CURSOR_DEFAULT);
} }
@@ -245,15 +135,15 @@ void Update(Event event) {
view->update_scroll = true; view->update_scroll = true;
} }
OnCommandEvent = &event;
OnCommand(event); OnCommand(event);
for (Window *it = FirstWindow; it; it = it->next) { OnCommandEvent = NULL;
if (it->is_title_bar) ReplaceTitleBarData(it);
}
UpdateProcesses(); UpdateProcesses();
CoUpdate(&event); CoUpdate(&event);
ReloadLuaConfigs(); ReloadLuaConfigs();
CallLuaOnUpdate(&event); StatusBarUpdate();
UpdateDebugBuffer(); UpdateDebugBuffer();
CallLuaOnUpdate(&event);
GarbageCollect(); GarbageCollect();
For(IterateInReverse(&order)) { 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) { void Windows_SetupVCVarsall(mco_coro *co) {
View *view = NULL; View *view = NULL;
{ {
@@ -301,7 +161,7 @@ void Windows_SetupVCVarsall(mco_coro *co) {
String working_dir = WorkDir; String working_dir = WorkDir;
String buffer_name = GetUniqueBufferName(working_dir, "vcvarsall-"); String buffer_name = GetUniqueBufferName(working_dir, "vcvarsall-");
String cmd = Format(scratch, "\"%S\" && set", StyleVCVarsall); String cmd = Format(scratch, "\"%S\" && set", StyleVCVarsall);
view = Command_ExecHidden(buffer_name, cmd, working_dir); view = ExecHidden(buffer_name, cmd, working_dir);
} }
for (;;) { for (;;) {
if (!ProcessIsActive(view->id)) { if (!ProcessIsActive(view->id)) {
@@ -340,7 +200,7 @@ void MainLoop() {
if (it.xwindow == 0 || it.ywindow == 0) { if (it.xwindow == 0 || it.ywindow == 0) {
int xwindow, ywindow; int xwindow, ywindow;
SDL_GetWindowSize(SDLWindow, &xwindow, &ywindow); SDL_GetWindowSizeInPixels(SDLWindow, &xwindow, &ywindow);
it.xwindow = xwindow; it.xwindow = xwindow;
it.ywindow = ywindow; it.ywindow = ywindow;
} }
@@ -368,7 +228,6 @@ void MainLoop() {
SetMouseCursor(*event); SetMouseCursor(*event);
LayoutWindows(event->xwindow, event->ywindow); // This is here to render changes in title bar size without a frame of delay 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); BeginFrameRender(event->xwindow, event->ywindow);
DrawSplits(&WindowSplits);
Array<Window *> order = GetWindowZOrder(scratch); Array<Window *> order = GetWindowZOrder(scratch);
For(IterateInReverse(&order)) { For(IterateInReverse(&order)) {
@@ -379,7 +238,6 @@ void MainLoop() {
SDL_GL_SwapWindow(SDLWindow); SDL_GL_SwapWindow(SDLWindow);
} }
#if _WIN32 #if _WIN32
int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
#else #else
@@ -396,18 +254,21 @@ int main(int argc, char **argv)
if (1) { if (1) {
RunArenaTest(); RunArenaTest();
RunBufferTest(); For (TestFunctions) {
it.function();
}
// ReportErrorf("Testing DONE\n"); // ReportErrorf("Testing DONE\n");
// return 0; // return 0;
} }
#if !OS_WINDOWS #if !OS_WINDOWS
for (int i = 0; environ[i]; i += 1) { for (int i = 0; environ[i]; i += 1) {
Add(&Enviroment, Copy(GetSystemAllocator(), environ[i])); Add(&Enviroment, Copy(Perm, environ[i]));
} }
#endif #endif
WorkDir = GetWorkingDir(SysAllocator); WorkDir = GetWorkingDir(Perm);
{ {
String sdl_config_path = SDL_GetPrefPath("krzosa", "text_editor"); String sdl_config_path = SDL_GetPrefPath("krzosa", "text_editor");
if (sdl_config_path.len && sdl_config_path.data[sdl_config_path.len - 1] == '\\') { 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] == '/') { if (sdl_config_path.len && sdl_config_path.data[sdl_config_path.len - 1] == '/') {
sdl_config_path = Chop(sdl_config_path, 1); // chop '/' 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); SDL_free(sdl_config_path.data);
} }
@@ -455,7 +316,7 @@ int main(int argc, char **argv)
int yhalf = 30; int yhalf = 30;
Uint32 window_flags = SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_HIGH_PIXEL_DENSITY; 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) { if (SDLWindow == NULL) {
ReportErrorf("Couldn't create window! %s", SDL_GetError()); ReportErrorf("Couldn't create window! %s", SDL_GetError());
return 1; return 1;
@@ -489,10 +350,15 @@ int main(int argc, char **argv)
InitBuffers(); InitBuffers();
InitRender(); InitRender();
ReloadFont(); ReloadFont(StyleFont, (U32)StyleFontSize);
InitWindows(); InitWindows();
InitOS(ReportWarningf); 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) { for (int i = 1; i < argc; i += 1) {
String it = argv[i]; String it = argv[i];

View File

@@ -1,97 +1,61 @@
struct Window; struct View; struct WindowSplit; #define EVENT_KINDS \
struct ViewID { Int id; View *o; }; X(EVENT_NONE) \
struct WindowID { Int id; Window *o; }; 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 { enum EventKind {
ViewID id; #define X(TYPE) TYPE,
BufferID active_buffer; EVENT_KINDS
View *next; #undef X
View *prev; EVENT_KIND_COUNT,
Vec2I scroll; EVENT_KIND_INVALID = 111,
Array<Caret> carets;
// window | view
Caret main_caret_on_begin_frame;
bool update_scroll;
bool fuzzy_search;
String16 prev_search_line;
}; };
struct GotoCrumb { const char *EventKindStrings[] = {
ViewID view_id; #define X(TYPE) #TYPE,
Caret caret; EVENT_KINDS
#undef X
}; };
struct Window { #define EVENT_FIELDS \
WindowID id; X(EventKind, Int, kind) \
ViewID active_view; X(SDL_Keycode, Int, key) \
Window *next; X(int16_t, Int, xwindow) \
Window *prev; X(int16_t, Int, ywindow) \
WindowSplit *split_ref; 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; struct Event {
Int title_bar_last_buffer_change_id; // @todo: bring back the changes to title bar? #define X(TYPE, KIND, NAME) TYPE NAME;
EVENT_FIELDS
WindowID search_bar_window; #undef X
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 BSet { struct BSet {
Window *window; struct Window *window;
View *view; View *view;
Buffer *buffer; Buffer *buffer;
}; };
@@ -106,42 +70,38 @@ Allocator SysAllocator = {SystemAllocatorProc};
String ConfigDir; String ConfigDir;
float DPIScale = 1.0f; float DPIScale = 1.0f;
Rect2I GetVisibleCells(Window *window);
void AfterEdit(View *view, Array<Edit> edits); void AfterEdit(View *view, Array<Edit> edits);
Scroller ComputeScrollerRect(Window *window); Scroller ComputeScrollerRect(Window *window);
BSet Command_Open(String path, String meta = ""); BSet Open(String path, String meta = "");
BSet Command_Open(String16 path, String meta = ""); BSet Open(String16 path, String meta = "");
void UpdateScroll(Window *window, bool update_caret_scrolling); void UpdateScroll(Window *window, bool update_caret_scrolling);
void Command_SelectEntireBuffer(View *view); void SelectEntireBuffer(View *view);
void Command_Replace(View *view, String16 string); void Replace(View *view, String16 string);
void Command_SelectRangeOneCursor(View *view, Range range); void SelectRange(View *view, Range range);
void Command_Append(View *view, String16 string, bool scroll_to_end_if_cursor_on_last_line); void 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); void Append(View *view, String string, bool scroll_to_end_if_cursor_on_last_line);
Array<Edit> Command_ReplaceEx(Allocator scratch, View *view, String16 string); Array<Edit> ReplaceEx(Allocator scratch, View *view, String16 string);
void Command_Eval(String string); void Eval(String string);
void Command_Eval(String16 string); void Eval(String16 string);
String Command_GetMainDir();
void ReportDebugf(const char *fmt, ...); void ReportDebugf(const char *fmt, ...);
void ReplaceWithoutMovingCarets(Buffer *buffer, Range range, String16 string); void ReplaceWithoutMovingCarets(Buffer *buffer, Range range, String16 string);
void Command_Copy(View *view); void ClipboardCopy(View *view);
void Command_Paste(View *view); void ClipboardPaste(View *view);
void ReportConsolef(const char *fmt, ...); void ReportConsolef(const char *fmt, ...);
void ReportErrorf(const char *fmt, ...); void ReportErrorf(const char *fmt, ...);
void ReportWarningf(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); Buffer *CreateBuffer(Allocator allocator, String name, Int size = 4096);
View *CreateView(BufferID active_buffer); View *CreateView(BufferID active_buffer);
void ReopenBuffer(Buffer *buffer); void ReopenBuffer(Buffer *buffer);
inline Buffer *FindBuffer(String name); 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==(BufferID a, BufferID b) { return a.id == b.id; }
inline bool operator==(ViewID a, ViewID 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!=(BufferID a, BufferID b) { return a.id != b.id; }
inline bool operator!=(ViewID a, ViewID b) { return a.id != b.id; } inline bool operator!=(ViewID a, ViewID b) { return a.id != b.id; }

View File

@@ -5,7 +5,7 @@ void UpdateDebugBuffer() {
View *view = GetView(window->active_view); View *view = GetView(window->active_view);
if (view->active_buffer.id == buffer->id.id) return; if (view->active_buffer.id == buffer->id.id) return;
BSet main = GetActiveMainSet(); BSet main = GetBSet(LastActiveLayoutWindowID);
Scratch scratch; 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); 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; float xmouse, ymouse;
SDL_GetMouseState(&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, "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, "String name = %S\n", main.buffer->name);
RawAppendf(buffer, "Int change_id = %lld\n", (long long)main.buffer->change_id); RawAppendf(buffer, "Int change_id = %lld\n", (long long)main.buffer->change_id);
RawAppendf(buffer, "Int user_change_id = %lld\n", (long long)main.buffer->user_change_id); RawAppendf(buffer, "Int 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); 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; Scratch scratch;
BSet main = GetMainSet(window); BSet main = GetBSet(LastActiveLayoutWindowID);
BSet title = GetBSet(window); BSet title = GetBSet(status_bar_window);
title.view->scroll.y = 0; title.view->scroll.y = 0;
String16 buffer_string = GetString(title.buffer); String16 buffer_string = GetString(title.buffer);
@@ -54,11 +57,12 @@ void ReplaceTitleBarData(Window *window) {
bool found_separator = Seek(buffer_string, u" |", &replace_range.max); bool found_separator = Seek(buffer_string, u" |", &replace_range.max);
// Parse the title and line // Parse the title and line
if (window->id == ActiveWindow) { if (title.window->id == ActiveWindowID) {
if (title.buffer->change_id == title.window->title_bar_last_buffer_change_id) { if (title.buffer->change_id == title.window->status_bar_last_buffer_change_id) {
return; return;
} }
String16 buffer_name = GetString(title.buffer, replace_range); String16 buffer_name = GetString(title.buffer, replace_range);
buffer_name = Skip(buffer_name, 1);
buffer_name = Trim(buffer_name); buffer_name = Trim(buffer_name);
Int column = ChopNumber(&buffer_name); Int column = ChopNumber(&buffer_name);
@@ -70,64 +74,41 @@ void ReplaceTitleBarData(Window *window) {
column = 0; 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}); Int buffer_pos = XYToPos(main.buffer, {column, line});
Caret &caret = main.view->carets[0]; Caret &caret = main.view->carets[0];
if (GetFront(caret) != buffer_pos) { if (GetFront(caret) != buffer_pos) {
caret = MakeCaret(buffer_pos); caret = MakeCaret(buffer_pos);
} }
title.window->status_bar_last_buffer_change_id = title.buffer->change_id;
return; return;
} }
Caret caret = main.view->carets[0]; Caret caret = main.view->carets[0];
XY xy = PosToXY(main.buffer, GetFront(caret)); 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 // add separator at the end of buffer
if (!found_separator) { if (!found_separator) {
Command_SelectRangeOneCursor(title.view, GetBufferEndAsRange(title.buffer)); SelectRange(title.view, GetBufferEndAsRange(title.buffer));
Array<Edit> edits = Command_ReplaceEx(scratch, title.view, u" |"); Array<Edit> edits = ReplaceEx(scratch, title.view, u" |");
AdjustCarets(edits, &caret_copy);
} }
// replace data up to separator with filename and stuff // replace data up to separator with filename and stuff
const char *reopen = main.buffer->changed_on_disk ? " Reopen()" : ""; 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) { For (ActiveProcesses) {
if (it.view_id == main.view->id.id) { 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 = ToString16(scratch, s);
String16 string_to_replace = GetString(title.buffer, replace_range); String16 string_to_replace = GetString(title.buffer, replace_range);
if (string_to_replace != string) { if (string_to_replace != string) {
Command_SelectRangeOneCursor(title.view, replace_range); SelectRange(title.view, replace_range);
Array<Edit> edits = Command_ReplaceEx(scratch, title.view, string); Array<Edit> edits = ReplaceEx(scratch, title.view, string);
Command_SelectRangeOneCursor(title.view, MakeRange(0));
AdjustCarets(edits, &caret_copy);
} }
SelectRange(title.view, MakeRange(0));
ResetHistory(title.buffer);
} }

77
src/text_editor/view.cpp Normal file
View 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
View 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);

View File

@@ -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 *> GetWindowZOrder(Allocator allocator) {
Array<Window *> order = {allocator}; Array<Window *> order = {allocator};
for (Window *it = FirstWindow; it; it = it->next) if (it->z == 2) Add(&order, it); For(Windows) if (it->z == 2) Add(&order, it);
for (Window *it = FirstWindow; it; it = it->next) if (it->z == 1) Add(&order, it); For(Windows) 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 == 0) Add(&order, it);
return order; return order;
} }
Window *CreateSearchBar(WindowID parent_window_id) { Int GetExpandingBarSize(Window *window) {
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) {
View *view = GetView(window->active_view); View *view = GetView(window->active_view);
Buffer *buffer = GetBuffer(view->active_buffer); Buffer *buffer = GetBuffer(view->active_buffer);
float result = (float)buffer->line_starts.len * window->font->line_spacing; float result = (float)buffer->line_starts.len * window->font->line_spacing;
return (Int)result; return (Int)result;
} }
WindowSplit *CreateSplitForWindow(WindowSplit *parent, Window *window) { View *WindowOpenBufferView(Window *new_parent_window, String name) {
WindowSplit *split = AllocType(SysAllocator, WindowSplit); View *view = FindView(name);
split->kind = WindowSplitKind_Window; if (!view) {
split->window = window; View *result = OpenBufferView(name);
split->parent = parent; new_parent_window->active_view = result->id;
window->split_ref = split; return result;
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;
} }
}
void SetVisibility(WindowID window_id, bool v) { Window *window = FindWindow(view->id);
Window *window = GetWindow(window_id); if (!window) {
window->visible = v; new_parent_window->active_view = view->id;
return view;
if (window->title_bar_window.id != 0) {
Window *title_bar = GetWindow(window->title_bar_window);
title_bar->visible = v;
} }
if (window->search_bar_window.id != 0) { if (window == new_parent_window) {
Window *search_bar = GetWindow(window->search_bar_window); return view;
search_bar->visible = v;
} }
}
bool ToggleVisibility(WindowID window_id) { Assert(window->active_view.id == view->id.id);
Window *window = GetWindow(window_id); View *result = OpenBufferView(name);
bool visible = !window->visible; new_parent_window->active_view = result->id;
SetVisibility(window_id, visible); return result;
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));
} }
void InitWindows() { void InitWindows() {
WindowSplit *split = &WindowSplits; Scratch scratch;
split->kind = WindowSplitKind_Horizontal;
split->value = (double)0.9;
CreateWind();
CreateWind();
// COMMAND BAR
{ {
Window *window = CreateWind(); Window *window = CreateWind();
window->active_view = NullViewID; CommandBarWindowID = window->id;
Assert(window->id.id == 0); Buffer *buffer = CreateBuffer(SysAllocator, "command_bar");
CreateTitlebar(window->id); View *view = CreateView(buffer->id);
CreateSearchBar(window->id);
split->right = CreateSplitForWindow(split, window);
}
{
Window *window = CreateWind();
Buffer *buffer = ScratchBuffer;
View *view = CreateView(buffer->id);
window->active_view = view->id; window->active_view = view->id;
CreateTitlebar(window->id); window->draw_line_numbers = false;
CreateSearchBar(window->id); window->draw_scrollbar = false;
ActiveWindow = window->id; window->draw_darker = true;
window->draw_line_highlight = false;
split->left = CreateSplitForWindow(split, window); 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(); Window *window = CreateWind();
DebugWindowID = window->id; DebugWindowID = window->id;
@@ -190,6 +171,7 @@ void InitWindows() {
window->draw_scrollbar = false; window->draw_scrollbar = false;
window->visible = false; window->visible = false;
window->z = 2; window->z = 2;
window->layout = false;
Buffer *buffer = CreateBuffer(SysAllocator, GetUniqueBufferName(WorkDir, "debug")); Buffer *buffer = CreateBuffer(SysAllocator, GetUniqueBufferName(WorkDir, "debug"));
DebugBufferID = buffer->id; DebugBufferID = buffer->id;
@@ -199,86 +181,139 @@ void InitWindows() {
DebugViewID = view->id; DebugViewID = view->id;
window->active_view = view->id; window->active_view = view->id;
Window *titlebar = CreateTitlebar(window->id); window->visible = false;
Window *searchbar = CreateSearchBar(window->id);
titlebar->z = 2;
searchbar->z = 2;
SetVisibility(window->id, false);
} }
} }
void LayoutWindowSplit(WindowSplit *split, Rect2I rect) { void CalcNiceties(Window *n) {
float scrollbar_size = (10.f * DPIScale); float scrollbar_size = (10.f * DPIScale);
float resizer_size = (float)PrimaryFont.char_spacing*0.5f; 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) { double WindowCalcEvenResizerValue(Int screen_size_x, Int *out_count = NULL) {
Window *it = split->window; double w = 0;
float line_numbers_size = (float)it->font->char_spacing * 10; Int c = 0;
Assert(it->split_ref); ForItem(n, Windows) {
it->total_rect = rect; if (n->layout) {
w += n->weight;
Window *title_bar_window = GetWindow(it->title_bar_window); c += 1;
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");
} }
if (out_count) *out_count = c;
return (double)screen_size_x / w;
} }
void LayoutWindows(int16_t wx, int16_t wy) { void LayoutWindows(int16_t wx, int16_t wy) {
Rect2I screen_rect = RectI0Size(wx, wy); Rect2I screen_rect = RectI0Size(wx, wy);
LayoutWindowSplit(&WindowSplits, screen_rect);
// 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 = &copy_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 = &copy_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 = &copy_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); Rect2 screen_rect = Rect0Size(wx, wy);
Vec2 size = GetSize(screen_rect); Vec2 size = GetSize(screen_rect);
Rect2 a = CutLeft(&screen_rect, 0.3f * size.x); Rect2 a = CutRight(&screen_rect, 0.3f * size.x);
Rect2 b = CutBottom(&a, 0.4f * size.y); Rect2 b = CutTop(&a, 0.4f * size.y);
Rect2 c = Shrink(b, 20); Rect2 c = Shrink(b, 20);
n->document_rect = n->total_rect = ToRect2I(c);
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;
} }
// 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
View 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);