Compare commits
12 Commits
4ad9e6bf20
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
20d71722ea | ||
|
|
cf383c9772 | ||
|
|
82dceaca68 | ||
|
|
4f4940bcd3 | ||
|
|
aff3403404 | ||
|
|
b04b566681 | ||
|
|
aa85d9420a | ||
|
|
4c21026842 | ||
|
|
cab882de60 | ||
|
|
fadf4cd698 | ||
|
|
94ee03800d | ||
|
|
207fc65fec |
121
README.md
121
README.md
@@ -1,9 +1,118 @@
|
||||
# te, a text editor
|
||||
# te
|
||||
|
||||

|
||||
A from-scratch text editor that aims to stay fast, hackable, and practical for day-to-day coding.
|
||||
|
||||
In order to build on windows you need to call build.bat from
|
||||
the x64 Native Command Tools prompt that ships with Visual Studio.
|
||||

|
||||
|
||||
For Linux you will need clang, SDL3 and libbacktrace libraries to be installed and
|
||||
available on your system. Just call build.sh when these are available.
|
||||
## What this project is
|
||||
|
||||
`te` is a native desktop editor written as a single C++ executable (SDL3 + OpenGL + custom editor core).
|
||||
It is built around a straightforward single-threaded architecture, with coroutines used for asynchronous workflows (searching, UI flows, process jobs) so behavior stays easy to reason about and debug.
|
||||
|
||||
The codebase includes its own:
|
||||
|
||||
- text buffer + undo/redo engine
|
||||
- window/view layout system
|
||||
- command system + keybinding parser
|
||||
- fuzzy selection UI
|
||||
- process execution and output streaming
|
||||
- built-in font fallback (baked into the binary)
|
||||
|
||||
## Highlights
|
||||
|
||||
- VS Code-style keybindings and command palette (`Ctrl+Shift+P`)
|
||||
- Multi-cursor editing (keyboard and mouse workflows)
|
||||
- Fuzzy-open patterns for commands, files, and open buffers
|
||||
- Project-wide search (`Ctrl+Shift+F`) and replace-all workflow
|
||||
- Shell integration directly from editor input:
|
||||
- `:Command` to run editor commands
|
||||
- `:Set ...` to configure options and keybindings
|
||||
- `!cmd` / `!!cmd` to execute shell commands
|
||||
- `py:...` to execute Python snippets via discovered Python interpreter
|
||||
- Build panel integration (`Ctrl+B`, `Alt+B`, etc.) with configurable build commands
|
||||
- Config-driven behavior (font, colors, project commands, keymaps, paths)
|
||||
|
||||
## Build
|
||||
|
||||
### Linux
|
||||
|
||||
Requirements:
|
||||
|
||||
- `clang`
|
||||
- `cmake`
|
||||
- `libbacktrace`
|
||||
- SDL3 (the build script can clone and install SDL when missing)
|
||||
|
||||
Build commands:
|
||||
|
||||
```bash
|
||||
./build.sh # debug build (default)
|
||||
./build.sh release # optimized build
|
||||
./build.sh slow # extra asserts/instrumentation path
|
||||
```
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
./build/te
|
||||
./build/te path/to/file.cpp
|
||||
```
|
||||
|
||||
### Windows
|
||||
|
||||
Use the **x64 Native Tools Command Prompt for Visual Studio**.
|
||||
|
||||
Requirements:
|
||||
|
||||
- Visual Studio C++ toolchain (`cl`, `msbuild`)
|
||||
- `cmake`
|
||||
|
||||
Build commands:
|
||||
|
||||
```bat
|
||||
build.bat
|
||||
build.bat release
|
||||
build.bat slow
|
||||
```
|
||||
|
||||
Run:
|
||||
|
||||
```bat
|
||||
build\te.exe
|
||||
build\te.exe path\to\file.cpp
|
||||
```
|
||||
|
||||
## First steps in the editor
|
||||
|
||||
- `Ctrl+N`: new buffer
|
||||
- `Ctrl+O`: open current file's folder / directory navigation view
|
||||
- `Ctrl+P`: fuzzy list of open buffers
|
||||
- `Ctrl+Shift+P`: all commands
|
||||
- `Ctrl+Q` or `F12`: open thing under caret (path / link / command)
|
||||
- `Ctrl+F`, `F3`, `Shift+F3`: in-buffer search flow
|
||||
- `Ctrl+Shift+F`: interactive search across open project buffers
|
||||
- `Alt+Shift+Up/Down`: add cursors vertically
|
||||
- `Ctrl+B`: run Build1 command (configurable)
|
||||
|
||||
## Configuration
|
||||
|
||||
On startup, `te` loads a config file from SDL's app preference directory (`config.te`).
|
||||
You can also pass additional `.te` files as CLI arguments.
|
||||
|
||||
Useful examples:
|
||||
|
||||
```text
|
||||
:Set FontSize 16
|
||||
:Set PathToFont '/path/to/font.ttf'
|
||||
:Set Build1OnUnix 'sh build.sh release'
|
||||
:Set Build1OnWindows 'build.bat release'
|
||||
:Set TextColor ff202020
|
||||
:Set BackgroundColor fffdf6e3
|
||||
```
|
||||
|
||||
## Notes on architecture
|
||||
|
||||
- Single-threaded main loop (event/update/render)
|
||||
- Coroutines for async editor tasks instead of multi-threaded complexity
|
||||
- "Plugin" modules are integrated in the source tree and compiled in
|
||||
- Focus on predictable behavior and low-latency interaction
|
||||
|
||||
8
build.sh
8
build.sh
@@ -5,6 +5,7 @@ if [ ! -v release ]; then debug=1; fi
|
||||
if [ -v debug ]; then echo "[debug build]"; fi
|
||||
if [ -v release ]; then echo "[release build]"; fi
|
||||
if [ -v slow ]; then echo "[slow build]"; fi
|
||||
if [ -v addr ]; then echo "[address sanitizer build]"; fi
|
||||
|
||||
mkdir -p build
|
||||
|
||||
@@ -13,7 +14,8 @@ if [ ! -e "src/external/SDL" ]; then
|
||||
git clone https://github.com/libsdl-org/SDL.git
|
||||
cd SDL
|
||||
git checkout release-3.2.30
|
||||
# cmake -S . -B build_linux -DCMAKE_BUILD_TYPE=Release -DSDL_PIPEWIRE=OFF
|
||||
# We need older version of SDL3 because there is a bug on wayland that
|
||||
# doubles click events and it's kind of unusable
|
||||
cmake -S . -B build_linux -DSDL_PIPEWIRE=OFF -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
|
||||
cd build_linux
|
||||
sudo make -j16 install
|
||||
@@ -27,8 +29,10 @@ flags="-Wall -Wextra -Werror -Wformat=2 -Wundef -Wshadow -Wno-missing-field-init
|
||||
-g -fdiagnostics-absolute-paths \
|
||||
-nostdlib++ -fno-exceptions"
|
||||
|
||||
if [ -v debug ]; then flags="$flags -fsanitize=address,undefined -fno-omit-frame-pointer -DDEBUG_BUILD=1"; fi
|
||||
if [ -v debug ]; then flags="$flags -fsanitize=undefined -fno-omit-frame-pointer -DDEBUG_BUILD=1"; fi
|
||||
if [ -v release ]; then flags="$flags -DDEBUG_BUILD=0 -O2"; fi
|
||||
if [ -v slow ]; then flags="$flags -DSLOW_BUILD=1"; fi
|
||||
if [ -v addr ]; then flags="$flags -fsanitize=address"; fi
|
||||
|
||||
time clang -o te $flags ../src/text_editor.cpp $I -lSDL3 -lm -lbacktrace
|
||||
./te :RunTests
|
||||
@@ -18,6 +18,8 @@ typedef void OSErrorReport(const char *, ...);
|
||||
#include <poll.h>
|
||||
#include <execinfo.h>
|
||||
#include <backtrace.h>
|
||||
#include <sys/resource.h>
|
||||
|
||||
|
||||
API void (*Error)(const char *, ...);
|
||||
|
||||
|
||||
@@ -256,4 +256,4 @@ void CMD_SelectComment() {
|
||||
}
|
||||
MergeCarets(active.buffer, &active.view->carets);
|
||||
|
||||
} RegisterCommand(CMD_SelectComment, "ctrl-shift-l", "Find /* and */ and select the content in between");
|
||||
} RegisterCommand(CMD_SelectComment, "ctrl-semicolon", "Find /* and */ and select the content in between");
|
||||
@@ -80,7 +80,8 @@ void UpdateCoroutines(Event *event) {
|
||||
#endif
|
||||
|
||||
double took = GetTimeSeconds() - start;
|
||||
if (took > (0.016666 / 3.0)) {
|
||||
bool dont_loop_on_coroutines_when_budget_is_ok_exit_immediately = Testing;
|
||||
if (dont_loop_on_coroutines_when_budget_is_ok_exit_immediately || (took > (0.016666 / 3.0))) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
SDL_Window *SDLWindow;
|
||||
SDL_GLContext SDL_WindowGLContext;
|
||||
SDL_Cursor *SDL_MouseCursor;
|
||||
SDL_SystemCursor SDL_MouseCursorLastID;
|
||||
bool IsInFullscreen;
|
||||
int FullScreenSizeX, FullScreenSizeY;
|
||||
int FullScreenPositionX, FullScreenPositionY;
|
||||
@@ -10,6 +13,7 @@ bool SearchWordBoundary = false;
|
||||
bool BreakOnError = false;
|
||||
Int ErrorCount;
|
||||
bool DebugTraceBufferInits = false;
|
||||
bool Testing = false;
|
||||
|
||||
Allocator SysAllocator = {SystemAllocatorProc};
|
||||
float DPIScale = 1.0f;
|
||||
@@ -61,6 +65,8 @@ Vec2I MouseMiddleAnchor;
|
||||
|
||||
RandomSeed UniqueBufferNameSeed = {};
|
||||
Array<Event> EventPlayback;
|
||||
Array<Event> MacroPlayback;
|
||||
bool RecordingMacro = false;
|
||||
BlockArena Perm;
|
||||
|
||||
// clipboard
|
||||
|
||||
@@ -37,7 +37,7 @@ void CMD_DedentSelectedLines() {
|
||||
void CMD_DuplicateLineDown() {
|
||||
BSet active = GetBSet(ActiveWindowID);
|
||||
DuplicateLine(active.view, DIR_DOWN);
|
||||
} RegisterCommand(CMD_DuplicateLineDown, "ctrl-alt-down", "");
|
||||
} RegisterCommand(CMD_DuplicateLineDown, "ctrl-shift-alt-down", "");
|
||||
|
||||
void CMD_CreateCursorDown() {
|
||||
BSet active = GetBSet(ActiveWindowID);
|
||||
@@ -72,7 +72,7 @@ void CMD_MoveDown() {
|
||||
void CMD_DuplicateLineUp() {
|
||||
BSet active = GetBSet(ActiveWindowID);
|
||||
DuplicateLine(active.view, DIR_UP);
|
||||
} RegisterCommand(CMD_DuplicateLineUp, "ctrl-alt-up", "");
|
||||
} RegisterCommand(CMD_DuplicateLineUp, "ctrl-shift-alt-up", "");
|
||||
|
||||
void CMD_CreateCursorUp() {
|
||||
BSet active = GetBSet(ActiveWindowID);
|
||||
@@ -234,6 +234,12 @@ void CMD_NewLine() {
|
||||
IndentedNewLine(active.view);
|
||||
} RegisterCommand(CMD_NewLine, "enter | shift-enter", "");
|
||||
|
||||
void CMD_SearchAllOccurences() {
|
||||
BSet active = GetBSet(ActiveWindowID);
|
||||
String16 needle = GetString(active.buffer, active.view->carets[0].range);
|
||||
SelectAllOccurences(active.view, needle);
|
||||
} RegisterCommand(CMD_SearchAllOccurences, "ctrl-shift-l", "Use the selected word as needle and selects all the possible occurences in current buffer");
|
||||
|
||||
void CMD_CreateCaretOnNextFind() {
|
||||
BSet active = GetBSet(ActiveWindowID);
|
||||
String16 string = GetString(active.buffer, active.view->carets[0].range);
|
||||
@@ -247,3 +253,21 @@ void CMD_ClearCarets() {
|
||||
active.view->carets.len = 1;
|
||||
active.view->carets[0] = MakeCaret(GetFront(active.view->carets[0]));
|
||||
} RegisterCommand(CMD_ClearCarets, "escape", "Clear all carets and reset to 1 caret, also do some windowing stuff that closes things on escape");
|
||||
|
||||
void CMD_ToggleMacroRecording() {
|
||||
if (RecordingMacro) {
|
||||
RecordingMacro = false;
|
||||
Pop(&MacroPlayback); // Remove the ctrl-m from macro playback thing
|
||||
} else {
|
||||
RecordingMacro = true;
|
||||
MacroPlayback.len = 0;
|
||||
}
|
||||
} RegisterCommand(CMD_ToggleMacroRecording, "ctrl-m", "Start recording a macro");
|
||||
|
||||
void CMD_PlayMacro() {
|
||||
if (RecordingMacro) {
|
||||
return;
|
||||
}
|
||||
|
||||
For (MacroPlayback) Add(&EventPlayback, it);
|
||||
} RegisterCommand(CMD_PlayMacro, "alt-m", "Start playing back a macro recording");
|
||||
@@ -88,17 +88,17 @@ void Set(String string) {
|
||||
string = Skip(string, 1);
|
||||
String quote = SkipUntil(&string, {&c, 1});
|
||||
ExpectP(At(string, 0) == c, ":Set %S <error here>, unclosed quote", name);
|
||||
ReportConsolef(":Set %S %c%S%c", name, c, quote, c);
|
||||
// ReportConsolef(":Set %S %c%S%c", name, c, quote, c);
|
||||
*var->string = Intern(&GlobalInternTable, quote);
|
||||
} else if (var->type == VariableType_Int) {
|
||||
ExpectP(IsDigit(At(string, 0)), "Expected an integer to follow the command name, instead got: %S", string);
|
||||
Int number = SkipInt(&string);
|
||||
ReportConsolef(":Set %S %lld", name, number);
|
||||
// ReportConsolef(":Set %S %lld", name, number);
|
||||
*var->i = number;
|
||||
} else if (var->type == VariableType_Float) {
|
||||
ExpectP(IsDigit(At(string, 0)), "Expected float to follow the command name, instead got: %S", string);
|
||||
Float number = SkipFloat(&string);
|
||||
ReportConsolef(":Set %S %f", name, number);
|
||||
// ReportConsolef(":Set %S %f", name, number);
|
||||
*var->f = number;
|
||||
} else if (var->type == VariableType_Color) {
|
||||
ExpectP(IsHexDigit(At(string, 0)), "Expected hex integer to follow the command name, instead got: %S", string);
|
||||
@@ -107,7 +107,7 @@ void Set(String string) {
|
||||
string = Skip(string, 1);
|
||||
begin.len += 1;
|
||||
}
|
||||
ReportConsolef(":Set %S %S", name, begin);
|
||||
// ReportConsolef(":Set %S %S", name, begin);
|
||||
var->color->value = (uint32_t)strtoll(begin.data, NULL, 16);
|
||||
} ElseInvalidCodepath();
|
||||
|
||||
|
||||
@@ -48,7 +48,13 @@ void UpdateDebugWindow() {
|
||||
return;
|
||||
}
|
||||
|
||||
RawReplaceText(set.buffer, GetRange(set.buffer), u"Active buffers and views:\n");
|
||||
RawReplaceText(set.buffer, GetRange(set.buffer), u"");
|
||||
#if OS_POSIX
|
||||
struct rusage usage;
|
||||
getrusage(RUSAGE_SELF, &usage);
|
||||
RawAppendf(set.buffer, "MemoryUsage: %lld\n", usage.ru_maxrss);
|
||||
#endif
|
||||
|
||||
For (Views) {
|
||||
Buffer *buffer = GetBuffer(it->active_buffer);
|
||||
RawAppendf(set.buffer, "view->id:%lld, buffer->id:%lld, buffer->name:%S\n", (long long)it->id.id, (long long)buffer->id.id, buffer->name);
|
||||
|
||||
@@ -8,6 +8,16 @@ void CMD_OpenUpFolder() {
|
||||
Open(name);
|
||||
} RegisterCommand(CMD_OpenUpFolder, "ctrl-o", "Open current's file directory or up directory in other cases");
|
||||
|
||||
void CMD_OpenSystemFileBrowser() {
|
||||
Scratch scratch;
|
||||
String string = GetPrimaryDirectory();
|
||||
#if OS_WINDOWS
|
||||
Open(Format(scratch, "!!explorer %S", string));
|
||||
#else
|
||||
Open(Format(scratch, "!!xdg-open %S & disown", string));
|
||||
#endif
|
||||
} RegisterCommand(CMD_OpenSystemFileBrowser, "ctrl-alt-r", "Opens the directory of current file in the systems file browser");
|
||||
|
||||
void InsertDirectoryNavigation(Buffer *buffer) {
|
||||
Assert(buffer->is_dir);
|
||||
Scratch scratch;
|
||||
|
||||
@@ -15,6 +15,7 @@ void New(Window *window, String name = "") {
|
||||
name = GetUniqueBufferName(dir, "new");
|
||||
}
|
||||
WindowOpenBufferView(window, name);
|
||||
RunGCThisFrame = true;
|
||||
}
|
||||
|
||||
void CMD_New() {
|
||||
|
||||
@@ -1,14 +1,33 @@
|
||||
bool Testing = true;
|
||||
#if PLUGIN_TESTS
|
||||
|
||||
|
||||
void Wait(mco_coro *co) {
|
||||
Add(&EventPlayback, {EVENT_KIND_INVALID});
|
||||
for (Event *event = Yield(co); event->kind != EVENT_KIND_INVALID; event = Yield(co)) {
|
||||
{Event ev = {};ev.kind = EVENT_KIND_INVALID; Add(&EventPlayback, ev);}
|
||||
Event *event = NULL;
|
||||
for (event = Yield(co); event->kind != EVENT_KIND_INVALID; event = Yield(co)) {
|
||||
}
|
||||
}
|
||||
|
||||
void CO_FirstTest(mco_coro *co) {
|
||||
void Wait(mco_coro *co, int updates) {
|
||||
for (int i = 0; i < updates; i += 1) {
|
||||
{Event ev = {};ev.kind = EVENT_UPDATE; Add(&EventPlayback, ev);}
|
||||
}
|
||||
Wait(co);
|
||||
}
|
||||
|
||||
void OpenCloseCodeTest(mco_coro *co) {
|
||||
Int initial_buffers_count = Buffers.len;
|
||||
CO_OpenCode(co);
|
||||
Assert(Buffers.len > initial_buffers_count);
|
||||
CO_CloseAll(co);
|
||||
Wait(co);
|
||||
Assert(initial_buffers_count - 1 == Buffers.len);
|
||||
}
|
||||
|
||||
void CO_RunTests(mco_coro *co) {
|
||||
Testing = true;
|
||||
WaitForEvents = false;
|
||||
|
||||
{Event ev = {};ev.kind = EVENT_KEY_PRESS; ev.key = SDLK_RETURN; ev.xwindow = 1412; ev.ywindow = 1032; ev.xmouse = 1234; ev.ymouse = 594; Add(&EventPlayback, ev);}
|
||||
{Event ev = {};ev.kind = EVENT_KEY_PRESS; ev.key = SDLK_RETURN; ev.xwindow = 1412; ev.ywindow = 1032; ev.xmouse = 1234; ev.ymouse = 594; Add(&EventPlayback, ev);}
|
||||
{Event ev = {};ev.kind = EVENT_KEY_PRESS; ev.key = SDLK_RETURN; ev.xwindow = 1412; ev.ywindow = 1032; ev.xmouse = 1234; ev.ymouse = 594; Add(&EventPlayback, ev);}
|
||||
@@ -37,4 +56,68 @@ void CO_FirstTest(mco_coro *co) {
|
||||
{Event ev = {};ev.kind = EVENT_TEXT_INPUT; ev.xwindow = 1412; ev.ywindow = 1032; ev.xmouse = 1234; ev.ymouse = 594; ev.text = "f"; Add(&EventPlayback, ev);}
|
||||
Wait(co);
|
||||
|
||||
} RegisterCoroutineCommand(CO_FirstTest, "", "Basic tests");
|
||||
String16 result = uR"FOO(
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Memes and stuff)FOO";
|
||||
BSet set = GetBSet(PrimaryWindowID);
|
||||
Assert(AreEqual(result, GetString(set.buffer)));
|
||||
|
||||
// Test the box selection
|
||||
CMD_New();
|
||||
{Event ev = {};ev.kind = EVENT_KEY_PRESS; ev.key = SDLK_RETURN; ev.xwindow = 1412; ev.ywindow = 1032; ev.xmouse = 1234; ev.ymouse = 594; Add(&EventPlayback, ev);}
|
||||
{Event ev = {};ev.kind = EVENT_KEY_PRESS; ev.key = SDLK_RETURN; ev.xwindow = 1412; ev.ywindow = 1032; ev.xmouse = 1234; ev.ymouse = 594; Add(&EventPlayback, ev);}
|
||||
{Event ev = {};ev.kind = EVENT_KEY_PRESS; ev.key = SDLK_RETURN; ev.xwindow = 1412; ev.ywindow = 1032; ev.xmouse = 1234; ev.ymouse = 594; Add(&EventPlayback, ev);}
|
||||
{Event ev = {};ev.kind = EVENT_KEY_PRESS; ev.key = SDLK_RETURN; ev.xwindow = 1412; ev.ywindow = 1032; ev.xmouse = 1234; ev.ymouse = 594; Add(&EventPlayback, ev);}
|
||||
{Event ev = {};ev.kind = EVENT_KEY_PRESS; ev.key = SDLK_RETURN; ev.xwindow = 1412; ev.ywindow = 1032; ev.xmouse = 1234; ev.ymouse = 594; Add(&EventPlayback, ev);}
|
||||
{Event ev = {};ev.kind = EVENT_KEY_PRESS; ev.key = SDLK_RETURN; ev.xwindow = 1412; ev.ywindow = 1032; ev.xmouse = 1234; ev.ymouse = 594; Add(&EventPlayback, ev);}
|
||||
{Event ev = {};ev.kind = EVENT_KEY_PRESS; ev.key = SDLK_RETURN; ev.xwindow = 1412; ev.ywindow = 1032; ev.xmouse = 1234; ev.ymouse = 594; Add(&EventPlayback, ev);}
|
||||
{Event ev = {};ev.kind = EVENT_KEY_PRESS; ev.key = SDLK_LSHIFT; ev.xwindow = 1412; ev.ywindow = 1032; ev.xmouse = 1234; ev.ymouse = 594; ev.shift = 1; Add(&EventPlayback, ev);}
|
||||
{Event ev = {};ev.kind = EVENT_TEXT_INPUT; ev.xwindow = 1412; ev.ywindow = 1032; ev.xmouse = 1234; ev.ymouse = 594; ev.shift = 1; ev.text = "M"; Add(&EventPlayback, ev);}
|
||||
{Event ev = {};ev.kind = EVENT_TEXT_INPUT; ev.xwindow = 1412; ev.ywindow = 1032; ev.xmouse = 1234; ev.ymouse = 594; ev.text = "e"; Add(&EventPlayback, ev);}
|
||||
{Event ev = {};ev.kind = EVENT_TEXT_INPUT; ev.xwindow = 1412; ev.ywindow = 1032; ev.xmouse = 1234; ev.ymouse = 594; ev.text = "m"; Add(&EventPlayback, ev);}
|
||||
{Event ev = {};ev.kind = EVENT_TEXT_INPUT; ev.xwindow = 1412; ev.ywindow = 1032; ev.xmouse = 1234; ev.ymouse = 594; ev.text = "e"; Add(&EventPlayback, ev);}
|
||||
{Event ev = {};ev.kind = EVENT_TEXT_INPUT; ev.xwindow = 1412; ev.ywindow = 1032; ev.xmouse = 1234; ev.ymouse = 594; ev.text = "s"; Add(&EventPlayback, ev);}
|
||||
{Event ev = {};ev.kind = EVENT_KEY_PRESS; ev.key = SDLK_SPACE; ev.xwindow = 1412; ev.ywindow = 1032; ev.xmouse = 1234; ev.ymouse = 594; Add(&EventPlayback, ev);}
|
||||
{Event ev = {};ev.kind = EVENT_TEXT_INPUT; ev.xwindow = 1412; ev.ywindow = 1032; ev.xmouse = 1234; ev.ymouse = 594; ev.text = " "; Add(&EventPlayback, ev);}
|
||||
{Event ev = {};ev.kind = EVENT_KEY_PRESS; ev.key = SDLK_LSHIFT; ev.xwindow = 1412; ev.ywindow = 1032; ev.xmouse = 1234; ev.ymouse = 594; ev.shift = 1; Add(&EventPlayback, ev);}
|
||||
{Event ev = {};ev.kind = EVENT_TEXT_INPUT; ev.xwindow = 1412; ev.ywindow = 1032; ev.xmouse = 1234; ev.ymouse = 594; ev.text = "a"; Add(&EventPlayback, ev);}
|
||||
{Event ev = {};ev.kind = EVENT_TEXT_INPUT; ev.xwindow = 1412; ev.ywindow = 1032; ev.xmouse = 1234; ev.ymouse = 594; ev.text = "n"; Add(&EventPlayback, ev);}
|
||||
{Event ev = {};ev.kind = EVENT_TEXT_INPUT; ev.xwindow = 1412; ev.ywindow = 1032; ev.xmouse = 1234; ev.ymouse = 594; ev.text = "d"; Add(&EventPlayback, ev);}
|
||||
{Event ev = {};ev.kind = EVENT_KEY_PRESS; ev.key = SDLK_SPACE; ev.xwindow = 1412; ev.ywindow = 1032; ev.xmouse = 1234; ev.ymouse = 594; Add(&EventPlayback, ev);}
|
||||
{Event ev = {};ev.kind = EVENT_TEXT_INPUT; ev.xwindow = 1412; ev.ywindow = 1032; ev.xmouse = 1234; ev.ymouse = 594; ev.text = " "; Add(&EventPlayback, ev);}
|
||||
{Event ev = {};ev.kind = EVENT_TEXT_INPUT; ev.xwindow = 1412; ev.ywindow = 1032; ev.xmouse = 1234; ev.ymouse = 594; ev.text = "s"; Add(&EventPlayback, ev);}
|
||||
{Event ev = {};ev.kind = EVENT_TEXT_INPUT; ev.xwindow = 1412; ev.ywindow = 1032; ev.xmouse = 1234; ev.ymouse = 594; ev.text = "t"; Add(&EventPlayback, ev);}
|
||||
{Event ev = {};ev.kind = EVENT_TEXT_INPUT; ev.xwindow = 1412; ev.ywindow = 1032; ev.xmouse = 1234; ev.ymouse = 594; ev.text = "u"; Add(&EventPlayback, ev);}
|
||||
{Event ev = {};ev.kind = EVENT_TEXT_INPUT; ev.xwindow = 1412; ev.ywindow = 1032; ev.xmouse = 1234; ev.ymouse = 594; ev.text = "f"; Add(&EventPlayback, ev);}
|
||||
{Event ev = {};ev.kind = EVENT_TEXT_INPUT; ev.xwindow = 1412; ev.ywindow = 1032; ev.xmouse = 1234; ev.ymouse = 594; ev.text = "f"; Add(&EventPlayback, ev);}
|
||||
Wait(co);
|
||||
|
||||
result = uR"FOO(
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Memes and stuff)FOO";
|
||||
set = GetBSet(PrimaryWindowID);
|
||||
Assert(AreEqual(result, GetString(set.buffer)));
|
||||
|
||||
if (ErrorCount != 0) {
|
||||
Scratch scratch;
|
||||
String string = AllocCharString(scratch, LogBuffer);
|
||||
printf("TEXT EDITOR ERRORS!\n==========================\n%.*s", (int)string.len, string.data);
|
||||
}
|
||||
printf("RunTests OK\n");
|
||||
fflush(stdout);
|
||||
void CMD_QuitWithoutSaving();
|
||||
CMD_QuitWithoutSaving();
|
||||
} RegisterCoroutineCommand(CO_RunTests, "", "Basic tests");
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -9,14 +9,14 @@ void CMD_Prev() {
|
||||
main.window->skip_checkpoint = true;
|
||||
JumpBack(main.window);
|
||||
NextActiveWindowID = main.window->id;
|
||||
} RegisterCommand(CMD_Prev, "alt-q | mousex1", "Go to previous position (either previous view that was open or caret position) in the primary window");
|
||||
} RegisterCommand(CMD_Prev, "alt-q | mousex1 | ctrl-alt-minus", "Go to previous position (either previous view that was open or caret position) in the primary window");
|
||||
|
||||
void CMD_Next() {
|
||||
BSet main = GetBSet(PrimaryWindowID);
|
||||
main.window->skip_checkpoint = true;
|
||||
JumpForward(main.window);
|
||||
NextActiveWindowID = main.window->id;
|
||||
} RegisterCommand(CMD_Next, "alt-shift-q | mousex2", "Go to next position, after backtracking, in the primary window");
|
||||
} RegisterCommand(CMD_Next, "alt-shift-q | mousex2 | ctrl-shift-minus", "Go to next position, after backtracking, in the primary window");
|
||||
|
||||
void CMD_FocusLeftWindow() {
|
||||
NextActiveWindowID = SwitchWindow(DIR_LEFT)->id;
|
||||
@@ -74,9 +74,9 @@ void CMD_CloseWindow() {
|
||||
void CMD_GotoNextInList() {
|
||||
BSet main = GetBSet(PrimaryWindowID);
|
||||
GotoNextInList(main.window, 1);
|
||||
} RegisterCommand(CMD_GotoNextInList, "ctrl-e", "For example: when jumping from build panel to build error, a jump point is setup, user can click this button to go over to the next compiler error");
|
||||
} RegisterCommand(CMD_GotoNextInList, "ctrl-e | f8", "For example: when jumping from build panel to build error, a jump point is setup, user can click this button to go over to the next compiler error");
|
||||
|
||||
void CMD_GotoPrevInList() {
|
||||
BSet main = GetBSet(PrimaryWindowID);
|
||||
GotoNextInList(main.window, -1);
|
||||
} RegisterCommand(CMD_GotoPrevInList, "alt-e", "For example: when jumping from build panel to build error, a jump point is setup, user can click this button to go over to the previous compiler error");
|
||||
} RegisterCommand(CMD_GotoPrevInList, "alt-e | shift-f8", "For example: when jumping from build panel to build error, a jump point is setup, user can click this button to go over to the previous compiler error");
|
||||
|
||||
@@ -31,9 +31,7 @@ BlockArena RenderArena;
|
||||
Rect2 CurrentScissor;
|
||||
|
||||
Font PrimaryFont;
|
||||
Font SecondaryFont;
|
||||
|
||||
// ---------- shaders (ES3 / WebGL2) ----------
|
||||
static const char *glsl_vshader_es3 = R"==(#version 300 es
|
||||
precision highp float;
|
||||
|
||||
@@ -82,7 +80,6 @@ void GLDebugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLs
|
||||
}
|
||||
}
|
||||
|
||||
// ---------- helper: compile/link ----------
|
||||
static GLuint CompileShaderSrc(GLenum kind, const char *src) {
|
||||
GLuint s = glCreateShader(kind);
|
||||
glShaderSource(s, 1, &src, NULL);
|
||||
@@ -137,7 +134,6 @@ Shader CreateShaderES3(const char *vsrc, const char *fsrc) {
|
||||
return out;
|
||||
}
|
||||
|
||||
// ---------- InitRender for ES3 ----------
|
||||
void InitRender() {
|
||||
#if !OS_WASM
|
||||
glDebugMessageCallback(&GLDebugCallback, NULL);
|
||||
@@ -185,7 +181,6 @@ void BeginFrameRender(float wx, float wy) {
|
||||
CurrentScissor = Rect0Size(wx, wy);
|
||||
}
|
||||
|
||||
// ---------- EndFrameRender for ES3 ----------
|
||||
void EndFrameRender(float wx, float wy, Color color) {
|
||||
ProfileFunction();
|
||||
glEnable(GL_BLEND);
|
||||
@@ -391,6 +386,9 @@ void ReloadFont(String path, U32 size) {
|
||||
Scratch scratch;
|
||||
Atlas atlas = CreateAtlas(scratch, {2048, 2048});
|
||||
PrimaryFont = CreateFont(&atlas, (uint32_t)ClampBottom(2u, (U32)size), path);
|
||||
SecondaryFont = CreateFont(&atlas, 12, path);
|
||||
SecondaryFont.texture_id = PrimaryFont.texture_id = UploadAtlas(&atlas);
|
||||
PrimaryFont.texture_id = UploadAtlas(&atlas);
|
||||
}
|
||||
|
||||
void CleanupRender() {
|
||||
Dealloc(&PrimaryFont.glyphs);
|
||||
}
|
||||
@@ -1,56 +1,57 @@
|
||||
/*
|
||||
- [x] list_functions.sh and rg in general in the color mode is prinitng differently from terminal as well as output is truncated!
|
||||
- [ ] Syntax for executing commands from root of project
|
||||
- [ ] Fuzzy search over executed command ouput
|
||||
|
||||
## Basics
|
||||
- [ ] Ctrl+Shift+ArrowDown at the end of buffer, doesn't capture characters on last line without new line, same at the top
|
||||
- [ ] When inserting parenthesis and selection is there, put the parens on both sides?
|
||||
- [ ] KillProcess in console !!! - should also kill all the children ...........
|
||||
- [x] ctrl-e with these short main.c:290: breaks a little, need to first click ctrl-e and then alt-e to jump
|
||||
- [ ] Use command window without special fuzzy search features to type commands and stuff for executing shell etc..
|
||||
- [ ] I noticed WordComplete getting busted after using editor for a while and returning cutdown words but not sure how to repro ...
|
||||
- [ ] Rewrite WordComplete to use CoroutineCreate, maybe try reducing globals to just the coroutine itself
|
||||
- [ ] WorkComplete, Bounded search, after a while it becomes problematic with many buffers
|
||||
|
||||
## Monaco like design for familiarity
|
||||
- [ ] ctrl-tab - switch file lister with instant hold release semantics?
|
||||
- [ ] SearchAndReplace how to do better?
|
||||
- [ ] ctrl-t find workspace symbols? how can we do it?
|
||||
- [ ] Snippet design?
|
||||
|
||||
## Refactor
|
||||
- [ ] Make a platform layer and separate SDL stuff out
|
||||
- [x] ReplaceAll - heap-use-after-free address, how to debug? I think would be nice to iterate all buffer ids and their addresses along with the state
|
||||
- [ ] Report a regression in SDL in newer versions that make it so that events are doubled when unpressing the held button
|
||||
- [ ] Remove -lbacktrace and add my backtrace library thing
|
||||
- [ ] Refactor build.sh to accept commands and remove build_web.sh
|
||||
- [ ] GetWindowZOrder to IterateWindowsInZOrder
|
||||
- [ ] Investigate reworking history API, tagging modification blocks with carets?
|
||||
- [ ] How to enable framerate to be unlimited and not break scrolling?
|
||||
- [ ] When 2 views of same buffer are open, the view with caret below the caret which modifies the view - starts moving and getting messed up
|
||||
- [ ] The lexing / parsing code for config / bindings appears sloppy would be nice to clean it up but I don't have any ideas
|
||||
- [ ] Redesign `:QueryFile`
|
||||
|
||||
- [x] BRO, the caret teleports on linux when I press the arrow for too long
|
||||
- [ ] Report SDL newest vs SDL previous version on wayland
|
||||
## Features
|
||||
- [ ] KillProcess in console !!! - should also kill all the children ...........
|
||||
- [ ] BeginLog and EndLog, show all logs in the UI thing at the end
|
||||
- [ ] Fuzzy search over executed command ouput
|
||||
- [ ] Command window but for executing commands without special fuzzy stuff?
|
||||
- [ ] Implement Regex and jumps using regex so as to allow for using ctags
|
||||
- [ ] Add UndoKinds (SnapshotUndo, DiffUndo), I want to enable history in fuzzy search buffers without a huge memory cost
|
||||
- [ ] General parser / data description thing, rewrite other parsers using this, rewrite env handling
|
||||
- [ ] DirNav, update lister when files change on disk
|
||||
- [ ] Investigate ways to bind "open" commands to keys from config
|
||||
- [ ] Ability to access and set clipboard as well as affect selection in the open scripts
|
||||
- [ ] Syntax for executing commands from root of project, or maybe commands should be executed from root of the CurrentDirectory
|
||||
|
||||
- [ ] Cleanups
|
||||
- [ ] How to enable framerate to be unlimited and not break scrolling?
|
||||
- [x] When dragging a file into the editor, would be nice if the file opened in the window user dropped the file into. Not the active window.
|
||||
- [ ] When 2 views of same buffer are open, the view with caret below the caret which modifies the view - starts moving and getting messed up
|
||||
- [ ] Reduce number of created buffers to one per window, should be enough
|
||||
- [ ] GetWindowZOrder to IterateWindowsInZOrder
|
||||
- [ ] Rework history API, tagging modification blocks with carets?
|
||||
- [ ] The lexing / parsing code for config / bindings appears sloppy would be nice to clean it up but I don't have any ideas
|
||||
- [ ] Directory tree doesn't make much sense! Maybe just consolidate into one folder? create nice names - the raddbg idea didn't pan out well here
|
||||
## Test
|
||||
- [ ] Test BlockArena correctnsess - random allocations, writes and undos, try to crash
|
||||
|
||||
- [ ] General parser / data description thing
|
||||
- [ ] Rewrite other parsers using this
|
||||
- [ ] Rewrite Env handling (to not be OS specific)
|
||||
|
||||
- New error mechanism - we were losing errors when ReportError was called multiple times, I still want that but I don't want to lose errors, so turn it into a summary list of errors
|
||||
- [ ] BeginLog EndLog, and then show all logs as a list in the UI thing
|
||||
- [ ] Undo kinds (to enable history in fuzzy buffers)
|
||||
- [ ] Add undo kind. Snapshot kind, so that history is possible in weird buffers without paying a huge memory cost. The idea is that we would store the exact buffer state to replace with, editor would just save history of first line etc.
|
||||
|
||||
- [ ] Macros
|
||||
- [ ] Regex
|
||||
- [ ] ctags based indexing
|
||||
- [ ] WordComplete
|
||||
- [ ] Rewrite WordComplete to use CoroutineCreate, maybe try reducing globals to just the coroutine itself
|
||||
- [ ] More bounded? seems like it might be problematic on a bigger project but so far it isn't (even for SDL or raddbg)
|
||||
## Low
|
||||
- [ ] Shell / terminal buffer plugin (keep the shell alive and talk with it)
|
||||
- [ ] Directory Navigation
|
||||
- [ ] Remake lister when files change on disk
|
||||
- [ ] When saving apply all the modifications instead (like deleting files, renaming etc.) or maybe that's not even needed considering we are integrating shell commands
|
||||
- [ ] OpenCode
|
||||
- [ ] Hangs the editor on big files
|
||||
- [ ] Open
|
||||
- [ ] Way to bind "open" commands to keys from config
|
||||
- [ ] Ability to access and set clipboard as well as affect selection in the open scripts
|
||||
- [ ] QueryFile
|
||||
- [ ] Indicate to user that he is choosing a file
|
||||
- [ ] Define clear rules for opt out (like switching to different window) and kill or views that were for choosing?
|
||||
- [ ] Fix somehow OpenCode hanging the editor on big files
|
||||
|
||||
-----------------------------------------------------------------------
|
||||
|
||||
## :QueryFile problems
|
||||
- User doesn't see that he in a special mode
|
||||
- Coroutine is boundless here and the boundries of the mode are too lossely defined, it makes it strange when you learn that you are still in this mode
|
||||
- How do we kill all the views/buffers we entered? Do we care?
|
||||
|
||||
|
||||
*/
|
||||
#define PLUGIN_PROFILER 1
|
||||
@@ -91,6 +92,7 @@
|
||||
#define PLUGIN_REMEDYBG OS_WINDOWS
|
||||
#define PLUGIN_FILE_COMMANDS 1
|
||||
#define PLUGIN_WORD_COMPLETE 1
|
||||
#define PLUGIN_TESTS 1
|
||||
|
||||
#include "plugin_directory_navigation.h"
|
||||
#include "plugin_search_window.h"
|
||||
@@ -154,16 +156,13 @@ void SetMouseCursor(SDL_SystemCursor id) {
|
||||
}
|
||||
#else
|
||||
void SetMouseCursor(SDL_SystemCursor id) {
|
||||
static SDL_Cursor *SDL_MouseCursor;
|
||||
static SDL_SystemCursor last_id;
|
||||
|
||||
if (SDL_MouseCursor == NULL || last_id != id) {
|
||||
if (SDL_MouseCursor == NULL || SDL_MouseCursorLastID != id) {
|
||||
if (SDL_MouseCursor != NULL) {
|
||||
SDL_DestroyCursor(SDL_MouseCursor);
|
||||
}
|
||||
SDL_MouseCursor = SDL_CreateSystemCursor(id);
|
||||
SDL_SetCursor(SDL_MouseCursor);
|
||||
last_id = id;
|
||||
SDL_MouseCursorLastID = id;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -216,9 +215,6 @@ void SetMouseCursor(Event event) {
|
||||
}
|
||||
|
||||
void CMD_QuitWithoutSaving() {
|
||||
#if PLUGIN_REMEDYBG
|
||||
QuitDebugger();
|
||||
#endif
|
||||
AppIsRunning = false;
|
||||
} RegisterCommand(CMD_QuitWithoutSaving, "", "Self explanatory");
|
||||
|
||||
@@ -308,6 +304,27 @@ void OnCommand(Event event) {
|
||||
Int p = ScreenSpaceToBufferPos(selected.window, selected.view, selected.buffer, mouse);
|
||||
Caret &caret = selected.view->carets[0];
|
||||
caret = SetFrontWithAnchor(caret, DocumentAnchor, p);
|
||||
|
||||
if (event.alt && event.shift) {
|
||||
Int front = GetFront(DocumentAnchor);
|
||||
XY from = PosToXY(selected.buffer, front);
|
||||
XY to = ScreenSpaceToXY(selected.window, selected.view, mouse);
|
||||
Int min_line = Min(from.y, to.y);
|
||||
Int max_line = Max(from.y, to.y);
|
||||
Int min_col = Min(from.x, to.x);
|
||||
Int max_col = Max(from.x, to.x);
|
||||
|
||||
selected.view->carets.len = 0;
|
||||
for (Int line = min_line; line <= max_line; line += 1) {
|
||||
XY left_xy = {min_col, line};
|
||||
XY right_xy = {max_col, line};
|
||||
Int left = XYToPosWithoutNL(selected.buffer, left_xy);
|
||||
Int right = XYToPosWithoutNL(selected.buffer, right_xy);
|
||||
Caret new_selection = MakeCaret(left, right);
|
||||
Add(&selected.view->carets, new_selection);
|
||||
}
|
||||
MergeCarets(selected.buffer, &selected.view->carets);
|
||||
}
|
||||
}
|
||||
|
||||
if (ResizerSelected.id != -1 && Mouse(LEFT_UP)) {
|
||||
@@ -414,8 +431,11 @@ void OnCommand(Event event) {
|
||||
DocumentSelected = active.window->id;
|
||||
|
||||
Int p = ScreenSpaceToBufferPos(active.window, active.view, active.buffer, mouse);
|
||||
if (event.alt) Insert(&active.view->carets, MakeCaret(p, p), 0);
|
||||
if (!event.alt && !event.shift) active.view->carets.len = 1;
|
||||
if (event.alt) {
|
||||
Insert(&active.view->carets, MakeCaret(p, p), 0);
|
||||
} else if (!event.alt && !event.shift) {
|
||||
active.view->carets.len = 1;
|
||||
}
|
||||
|
||||
Caret &caret = active.view->carets[0];
|
||||
if (event.shift) {
|
||||
@@ -547,6 +567,7 @@ void GarbageCollect() {
|
||||
return;
|
||||
}
|
||||
RunGCThisFrame = false;
|
||||
ReportConsolef("GarbageCollect");
|
||||
|
||||
ProfileFunction();
|
||||
Allocator sys_allocator = GetSystemAllocator();
|
||||
@@ -568,8 +589,8 @@ void GarbageCollect() {
|
||||
continue;
|
||||
}
|
||||
|
||||
bool ref = ViewIsReferenced(it);
|
||||
if (ref) {
|
||||
Int ref = ViewIsReferenced(it);
|
||||
if (ref < 25) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -840,6 +861,9 @@ void MainLoop() {
|
||||
FrameEvents.len = 0;
|
||||
GetEventsForFrame(&FrameEvents);
|
||||
For (FrameEvents) {
|
||||
if (RecordingMacro) {
|
||||
Add(&MacroPlayback, it);
|
||||
}
|
||||
#if PLUGIN_RECORD_EVENTS
|
||||
if (it.kind != EVENT_UPDATE && !Testing) {
|
||||
Serialize(EventBuffer, &it);
|
||||
@@ -1012,8 +1036,8 @@ int main(int argc, char **argv, char **envp)
|
||||
}
|
||||
SDL_SetWindowPosition(SDLWindow, xhalf, yhalf);
|
||||
|
||||
SDL_GLContext gl_context = SDL_GL_CreateContext(SDLWindow);
|
||||
SDL_GL_MakeCurrent(SDLWindow, gl_context);
|
||||
SDL_WindowGLContext = SDL_GL_CreateContext(SDLWindow);
|
||||
SDL_GL_MakeCurrent(SDLWindow, SDL_WindowGLContext);
|
||||
SDL_ShowWindow(SDLWindow);
|
||||
|
||||
// Set icon
|
||||
@@ -1136,6 +1160,10 @@ int main(int argc, char **argv, char **envp)
|
||||
}
|
||||
#endif
|
||||
|
||||
#if PLUGIN_REMEDYBG
|
||||
QuitDebugger();
|
||||
#endif
|
||||
CleanupRender();
|
||||
SDL_DestroyWindow(SDLWindow);
|
||||
SDL_Quit();
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ void JumpTempBuffer(BSet *set, String buffer_name) {
|
||||
set->view = WindowOpenBufferView(set->window, buffer_name);
|
||||
set->buffer = GetBuffer(set->view->active_buffer);
|
||||
set->buffer->temp = true;
|
||||
RunGCThisFrame = true;
|
||||
}
|
||||
|
||||
void AddCommand(Array<Command> *arr, String name, Trigger *trigger, CMDFunction *function) {
|
||||
|
||||
26
src/view.cpp
26
src/view.cpp
@@ -103,6 +103,22 @@ API bool ViewIsCrumb(ViewID view_id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
API Int GetViewReferencedInHistoryRating(ViewID view_id) {
|
||||
ForItem (window, Windows) {
|
||||
For (window->goto_redo) if (it.view_id == view_id) return 1;
|
||||
Int i = 0;
|
||||
For (window->goto_history) {
|
||||
i += 1;
|
||||
if (it.view_id == view_id) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
bool ViewIsActive(ViewID id) {
|
||||
For (Windows) {
|
||||
if (it->active_view == id) {
|
||||
@@ -112,16 +128,16 @@ bool ViewIsActive(ViewID id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
API bool ViewIsReferenced(View *view) {
|
||||
API Int ViewIsReferenced(View *view) {
|
||||
if (view->special) {
|
||||
return true;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (ViewIsCrumb(view->id)) {
|
||||
return true;
|
||||
if (ViewIsActive(view->id)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return ViewIsActive(view->id);
|
||||
return GetViewReferencedInHistoryRating(view->id);
|
||||
}
|
||||
|
||||
bool BufferIsReferenced(Buffer *buffer) {
|
||||
|
||||
@@ -176,20 +176,22 @@ Window *SwitchWindow(int direction) {
|
||||
return result;
|
||||
}
|
||||
|
||||
Int ScreenSpaceToBufferPos(Window *window, View *view, Buffer *buffer, Vec2I mouse) {
|
||||
XY ScreenSpaceToXY(Window *window, View *view, Vec2I mouse) {
|
||||
Vec2I mworld = mouse - window->document_rect.min + view->scroll;
|
||||
double px = (double)mworld.x / (double)window->font->char_spacing;
|
||||
double py = (double)mworld.y / (double)window->font->line_spacing;
|
||||
XY xy = {(Int)round(px), (Int)floor(py)};
|
||||
return xy;
|
||||
}
|
||||
|
||||
Int ScreenSpaceToBufferPos(Window *window, View *view, Buffer *buffer, Vec2I mouse) {
|
||||
XY xy = ScreenSpaceToXY(window, view, mouse);
|
||||
Int result = XYToPosWithoutNL(buffer, xy);
|
||||
return result;
|
||||
}
|
||||
|
||||
Int ScreenSpaceToBufferPosErrorOutOfBounds(Window *window, View *view, Buffer *buffer, Vec2I mouse) {
|
||||
Vec2I mworld = mouse - window->document_rect.min + view->scroll;
|
||||
double px = (double)mworld.x / (double)window->font->char_spacing;
|
||||
double py = (double)mworld.y / (double)window->font->line_spacing;
|
||||
XY xy = {(Int)round(px), (Int)floor(py)};
|
||||
XY xy = ScreenSpaceToXY(window, view, mouse);
|
||||
Int result = XYToPosErrorOutOfBounds(buffer, xy);
|
||||
return result;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user