From 9863f58322b0e8952915b736c424505e0684d33d Mon Sep 17 00:00:00 2001 From: Fury_101 Date: Sun, 12 Feb 2023 17:14:14 -0800 Subject: [PATCH 01/29] Added support for java and python. Also adds capability for adding other languages --- build.sh | 2 +- src/editor.c | 2 +- src/lexer.c | 61 ++++++++++++++++++++++++++++++++++++++++++++-------- src/lexer.h | 4 +++- 4 files changed, 57 insertions(+), 12 deletions(-) diff --git a/build.sh b/build.sh index 2c961d43..ff6f278a 100755 --- a/build.sh +++ b/build.sh @@ -12,4 +12,4 @@ if [ `uname` = "Darwin" ]; then CFLAGS+=" -framework OpenGL" fi -$CC $CFLAGS `pkg-config --cflags $PKGS` -o ded $SRC $LIBS `pkg-config --libs $PKGS` +$CC $CFLAGS `pkg-config --cflags $PKGS` -g -o ded $SRC $LIBS `pkg-config --libs $PKGS` diff --git a/src/editor.c b/src/editor.c index be76929a..aece3ad1 100644 --- a/src/editor.c +++ b/src/editor.c @@ -166,7 +166,7 @@ void editor_retokenize(Editor *e) // Syntax Highlighting { e->tokens.count = 0; - Lexer l = lexer_new(e->atlas, e->data.items, e->data.count); + Lexer l = lexer_new(e->atlas, e->data.items, e->data.count, e->file_path); Token t = lexer_next(&l); while (t.kind != TOKEN_END) { da_append(&e->tokens, t); diff --git a/src/lexer.c b/src/lexer.c index cfd9022c..b62d363d 100644 --- a/src/lexer.c +++ b/src/lexer.c @@ -2,6 +2,7 @@ #include #include #include +#include #include "common.h" #include "lexer.h" @@ -19,7 +20,12 @@ Literal_Token literal_tokens[] = { }; #define literal_tokens_count (sizeof(literal_tokens)/sizeof(literal_tokens[0])) -const char *keywords[] = { +const char *jKeywords[] = { + "abstract", "assert", "boolean", "break", "byte", "case", "catch", "char", "class", "const", "continue", "default", "do", "double", "else", "enum", "extends", "final", "finally", "float", "for", "goto", "if", "implements", "import", "instanceof", "int", "interface", "long", "native", "new", "package", "private", "protected", "public", "return", "short", "static", "super", "switch", "synchronized", "this", "throw", "throws", "transient", "try", "void", "volatile", "while", "non-sealed", "open", "opens", "permits", "provides", "record", "sealed", "to", "transitive", "uses", "var", "with", "yield", "true", "false", "null", "const", "goto", "strictfp", +}; +#define jKeywords_count (sizeof(jKeywords)/sizeof(jKeywords[0])) + +const char *cKeywords[] = { "auto", "break", "case", "char", "const", "continue", "default", "do", "double", "else", "enum", "extern", "float", "for", "goto", "if", "int", "long", "register", "return", "short", "signed", "sizeof", "static", "struct", "switch", "typedef", @@ -34,7 +40,12 @@ const char *keywords[] = { "template", "this", "thread_local", "throw", "true", "try", "typeid", "typename", "using", "virtual", "wchar_t", "xor", "xor_eq", }; -#define keywords_count (sizeof(keywords)/sizeof(keywords[0])) +#define cKeywords_count (sizeof(cKeywords)/sizeof(cKeywords[0])) + +const char *pyKeywords[] = { + "False", "None", "True", "and", "as", "assert", "async", "await", "break", "class", "continue", "def", "del", "elif", "else", "except", "finally", "for", "from", "global", "if", "import", "in", "is", "lambda", "nonlocal", "not", "or", "pass", "raise", "return", "try", "while", "with", "yield", +}; +#define pyKeywords_count (sizeof(pyKeywords)/sizeof(pyKeywords[0])) const char *token_kind_name(Token_Kind kind) { @@ -65,12 +76,16 @@ const char *token_kind_name(Token_Kind kind) return NULL; } -Lexer lexer_new(Free_Glyph_Atlas *atlas, const char *content, size_t content_len) +Lexer lexer_new(Free_Glyph_Atlas *atlas, const char *content, size_t content_len, String_Builder file_path) { Lexer l = {0}; l.atlas = atlas; l.content = content; l.content_len = content_len; + if (file_path.items != NULL) { + l.file_path.items = (char*) malloc(sizeof(char*) * (strlen(file_path.items) + 1)); + strcpy(l.file_path.items, file_path.items); + } return l; } @@ -202,15 +217,43 @@ Token lexer_next(Lexer *l) lexer_chop_char(l, 1); token.text_len += 1; } + + if (l->file_path.items == NULL) + return token; + + const char* file_ext; + const char* filename = l->file_path.items; + const char *dot = strrchr(filename, '.'); + if(!dot || dot == filename) + file_ext = ""; + else + file_ext = dot + 1; - for (size_t i = 0; i < keywords_count; ++i) { - size_t keyword_len = strlen(keywords[i]); - if (keyword_len == token.text_len && memcmp(keywords[i], token.text, keyword_len) == 0) { - token.kind = TOKEN_KEYWORD; - break; + if (strcmp(file_ext, "java") == 0) { + for (size_t i = 0; i < jKeywords_count; ++i) { + size_t keyword_len = strlen(jKeywords[i]); + if (keyword_len == token.text_len && memcmp(jKeywords[i], token.text, keyword_len) == 0) { + token.kind = TOKEN_KEYWORD; + break; + } + } + } else if (strcmp(file_ext, "py") == 0) { + for (size_t i = 0; i < pyKeywords_count; ++i) { + size_t keyword_len = strlen(pyKeywords[i]); + if (keyword_len == token.text_len && memcmp(pyKeywords[i], token.text, keyword_len) == 0) { + token.kind = TOKEN_KEYWORD; + break; + } + } + } else { + for (size_t i = 0; i < cKeywords_count; ++i) { + size_t keyword_len = strlen(cKeywords[i]); + if (keyword_len == token.text_len && memcmp(cKeywords[i], token.text, keyword_len) == 0) { + token.kind = TOKEN_KEYWORD; + break; + } } } - return token; } diff --git a/src/lexer.h b/src/lexer.h index 6ee721ed..90e0095e 100644 --- a/src/lexer.h +++ b/src/lexer.h @@ -4,6 +4,7 @@ #include #include "./la.h" #include "./free_glyph.h" +#include "./common.h" typedef enum { TOKEN_END = 0, @@ -37,9 +38,10 @@ typedef struct { size_t line; size_t bol; float x; + String_Builder file_path; } Lexer; -Lexer lexer_new(Free_Glyph_Atlas *atlas, const char *content, size_t content_len); +Lexer lexer_new(Free_Glyph_Atlas *atlas, const char *content, size_t content_len, String_Builder file_path); Token lexer_next(Lexer *l); #endif // LEXER_H_ From d25d562ec1b074fb6832c6e15128cb8c882cf85f Mon Sep 17 00:00:00 2001 From: SuperCraftAlex <63254202+SuperCraftAlex@users.noreply.github.com> Date: Mon, 20 Nov 2023 22:48:18 +0100 Subject: [PATCH 02/29] highlighting for different programming languages (refactored) --- src/lexer.c | 104 +++++++++++++++++++++++++++++++++++----------------- src/lexer.h | 8 ++++ 2 files changed, 78 insertions(+), 34 deletions(-) diff --git a/src/lexer.c b/src/lexer.c index b62d363d..a5c185dc 100644 --- a/src/lexer.c +++ b/src/lexer.c @@ -21,10 +21,25 @@ Literal_Token literal_tokens[] = { #define literal_tokens_count (sizeof(literal_tokens)/sizeof(literal_tokens[0])) const char *jKeywords[] = { - "abstract", "assert", "boolean", "break", "byte", "case", "catch", "char", "class", "const", "continue", "default", "do", "double", "else", "enum", "extends", "final", "finally", "float", "for", "goto", "if", "implements", "import", "instanceof", "int", "interface", "long", "native", "new", "package", "private", "protected", "public", "return", "short", "static", "super", "switch", "synchronized", "this", "throw", "throws", "transient", "try", "void", "volatile", "while", "non-sealed", "open", "opens", "permits", "provides", "record", "sealed", "to", "transitive", "uses", "var", "with", "yield", "true", "false", "null", "const", "goto", "strictfp", + "abstract", "assert", "boolean", "break", "byte", "case", "catch", "char", "class", + "const", "continue", "default", "do", "double", "else", "enum", "extends", "final", + "finally", "float", "for", "goto", "if", "implements", "import", "instanceof", "int", + "interface", "long", "native", "new", "package", "private", "protected", "public", + "return", "short", "static", "super", "switch", "synchronized", "this", "throw", + "throws", "transient", "try", "void", "volatile", "while", "non-sealed", "open", + "opens", "permits", "provides", "record", "sealed", "to", "transitive", "uses", "var", + "with", "yield", "true", "false", "null", "const", "goto", "strictfp", }; #define jKeywords_count (sizeof(jKeywords)/sizeof(jKeywords[0])) +const char *ktKeywords[] = { + "abstract", "break", "catch", "class", "const", "continue", "else", "enum", "is", "as", + "when", "val", "var", "for", "if", "import", "interface", "data", "external", "inner", + "package", "private", "protected", "return", "super", "when", "this", "throw", + "try", "while", "sealed", "open", "true", "false", "null", "fun", "typealias", +}; +#define ktKeywords_count (sizeof(ktKeywords)/sizeof(ktKeywords[0])) + const char *cKeywords[] = { "auto", "break", "case", "char", "const", "continue", "default", "do", "double", "else", "enum", "extern", "float", "for", "goto", "if", "int", "long", "register", @@ -43,7 +58,10 @@ const char *cKeywords[] = { #define cKeywords_count (sizeof(cKeywords)/sizeof(cKeywords[0])) const char *pyKeywords[] = { - "False", "None", "True", "and", "as", "assert", "async", "await", "break", "class", "continue", "def", "del", "elif", "else", "except", "finally", "for", "from", "global", "if", "import", "in", "is", "lambda", "nonlocal", "not", "or", "pass", "raise", "return", "try", "while", "with", "yield", + "False", "None", "True", "and", "as", "assert", "async", "await", "break", "class", + "continue", "def", "del", "elif", "else", "except", "finally", "for", "from", "global", + "if", "import", "in", "is", "lambda", "nonlocal", "not", "or", "pass", "raise", + "return", "try", "while", "with", "yield", }; #define pyKeywords_count (sizeof(pyKeywords)/sizeof(pyKeywords[0])) @@ -85,7 +103,29 @@ Lexer lexer_new(Free_Glyph_Atlas *atlas, const char *content, size_t content_len if (file_path.items != NULL) { l.file_path.items = (char*) malloc(sizeof(char*) * (strlen(file_path.items) + 1)); strcpy(l.file_path.items, file_path.items); + + File_Extension file_ext; + const char *filename = l.file_path.items; + const char *dot = strrchr(filename, '.'); + if (!dot || dot == filename) { + file_ext = FEXT_CPP; + } else { + const char *file_ext_str = dot + 1; + + if (strcmp(file_ext_str, "kt") == 0 || strcmp(file_ext_str, "kts") == 0) { + file_ext = FEXT_KOTLIN; + } else if (strcmp(file_ext_str, "py") == 0) { + file_ext = FEXT_PYTHON; + } else if (strcmp(file_ext_str, "java") == 0) { + file_ext = FEXT_JAVA; + } else { + file_ext = FEXT_CPP; + } + } + + l.file_ext = file_ext; } + return l; } @@ -218,42 +258,38 @@ Token lexer_next(Lexer *l) token.text_len += 1; } - if (l->file_path.items == NULL) - return token; + const char **keywords; + size_t keywords_count; + switch (l->file_ext) { + case FEXT_JAVA: + keywords = jKeywords; + keywords_count = jKeywords_count; + break; - const char* file_ext; - const char* filename = l->file_path.items; - const char *dot = strrchr(filename, '.'); - if(!dot || dot == filename) - file_ext = ""; - else - file_ext = dot + 1; + case FEXT_KOTLIN: + keywords = ktKeywords; + keywords_count = ktKeywords_count; + break; - if (strcmp(file_ext, "java") == 0) { - for (size_t i = 0; i < jKeywords_count; ++i) { - size_t keyword_len = strlen(jKeywords[i]); - if (keyword_len == token.text_len && memcmp(jKeywords[i], token.text, keyword_len) == 0) { - token.kind = TOKEN_KEYWORD; - break; - } - } - } else if (strcmp(file_ext, "py") == 0) { - for (size_t i = 0; i < pyKeywords_count; ++i) { - size_t keyword_len = strlen(pyKeywords[i]); - if (keyword_len == token.text_len && memcmp(pyKeywords[i], token.text, keyword_len) == 0) { - token.kind = TOKEN_KEYWORD; - break; - } - } - } else { - for (size_t i = 0; i < cKeywords_count; ++i) { - size_t keyword_len = strlen(cKeywords[i]); - if (keyword_len == token.text_len && memcmp(cKeywords[i], token.text, keyword_len) == 0) { - token.kind = TOKEN_KEYWORD; - break; - } + case FEXT_PYTHON: + keywords = pyKeywords; + keywords_count = pyKeywords_count; + break; + + default: + keywords = cKeywords; + keywords_count = cKeywords_count; + } + + + for (size_t i = 0; i < keywords_count; ++i) { + size_t keyword_len = strlen(keywords[i]); + if (keyword_len == token.text_len && memcmp(keywords[i], token.text, keyword_len) == 0) { + token.kind = TOKEN_KEYWORD; + break; } } + return token; } diff --git a/src/lexer.h b/src/lexer.h index 90e0095e..3801a358 100644 --- a/src/lexer.h +++ b/src/lexer.h @@ -6,6 +6,13 @@ #include "./free_glyph.h" #include "./common.h" +typedef enum { + FEXT_KOTLIN, + FEXT_JAVA, + FEXT_CPP, + FEXT_PYTHON, +} File_Extension; + typedef enum { TOKEN_END = 0, TOKEN_INVALID, @@ -39,6 +46,7 @@ typedef struct { size_t bol; float x; String_Builder file_path; + File_Extension file_ext; } Lexer; Lexer lexer_new(Free_Glyph_Atlas *atlas, const char *content, size_t content_len, String_Builder file_path); From a8ef42ddff69d5b9cf9f3017ce03b1d4834f62ec Mon Sep 17 00:00:00 2001 From: "alexander.nutz" Date: Wed, 3 Apr 2024 09:57:36 +0200 Subject: [PATCH 03/29] fix win build --- .gitmodules | 6 ++++++ build_msvc.bat | 7 ++++--- dependencies/freetype | 1 + dependencies/minirent | 1 + src/common.c | 6 +++++- src/main.c | 2 +- 6 files changed, 18 insertions(+), 5 deletions(-) create mode 100644 .gitmodules create mode 160000 dependencies/freetype create mode 160000 dependencies/minirent diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..1ef3fa5a --- /dev/null +++ b/.gitmodules @@ -0,0 +1,6 @@ +[submodule "dependencies/minirent"] + path = dependencies/minirent + url = https://github.com/alex-s168/minirent.git +[submodule "dependencies/freetype"] + path = dependencies/freetype + url = https://github.com/ubawurinna/freetype-windows-binaries.git diff --git a/build_msvc.bat b/build_msvc.bat index d3a87749..89481702 100644 --- a/build_msvc.bat +++ b/build_msvc.bat @@ -1,12 +1,13 @@ @echo off rem launch this from msvc-enabled console -set CFLAGS=/W4 /WX /std:c11 /wd4996 /wd5105 /FC /TC /Zi /nologo -set INCLUDES=/I dependencies\SDL2\include /I dependencies\GLFW\include /I dependencies\GLEW\include +set CFLAGS=/W4 /std:c11 /wd4996 /wd5105 /FC /TC /Zi /nologo +set INCLUDES=/I dependencies\SDL2\include /I dependencies\freetype\include /I dependencies\minirent /I dependencies\GLFW\include /I dependencies\GLEW\include set LIBS=dependencies\SDL2\lib\x64\SDL2.lib ^ dependencies\SDL2\lib\x64\SDL2main.lib ^ dependencies\GLFW\lib\glfw3.lib ^ dependencies\GLEW\lib\glew32s.lib ^ + "dependencies\freetype\release static\vs2015-2022\win64\freetype.lib" ^ opengl32.lib User32.lib Gdi32.lib Shell32.lib -cl.exe %CFLAGS% %INCLUDES% /Feded src\main.c src\la.c src\editor.c src\file_browser.c src\free_glyph.c src\simple_renderer.c src\common.c /link %LIBS% -SUBSYSTEM:windows +cl.exe %CFLAGS% %INCLUDES% /Feded src\main.c src\la.c src\editor.c src\file_browser.c src\free_glyph.c src\simple_renderer.c src\common.c src\lexer.c /link %LIBS% -SUBSYSTEM:windows diff --git a/dependencies/freetype b/dependencies/freetype new file mode 160000 index 00000000..d6fb49d1 --- /dev/null +++ b/dependencies/freetype @@ -0,0 +1 @@ +Subproject commit d6fb49d11a9d0011bf4ecfe7e570beaaa189838a diff --git a/dependencies/minirent b/dependencies/minirent new file mode 160000 index 00000000..dbbc305b --- /dev/null +++ b/dependencies/minirent @@ -0,0 +1 @@ +Subproject commit dbbc305ba37afe60003cc64c49691c2d32d0e8a7 diff --git a/src/common.c b/src/common.c index 46bcfc50..1dc11b96 100644 --- a/src/common.c +++ b/src/common.c @@ -133,7 +133,11 @@ Vec4f hex_to_vec4f(uint32_t color) Errno type_of_file(const char *file_path, File_Type *ft) { #ifdef _WIN32 -#error "TODO: type_of_file() is not implemented for Windows" + if (GetFileAttributesA(file_path) & FILE_ATTRIBUTE_DIRECTORY) { + *ft = FT_DIRECTORY; + } else { + *ft = FT_REGULAR; + } #else struct stat sb = {0}; if (stat(file_path, &sb) < 0) return errno; diff --git a/src/main.c b/src/main.c index eac9ac5f..fb464c29 100644 --- a/src/main.c +++ b/src/main.c @@ -148,7 +148,7 @@ int main(int argc, char **argv) if (GLEW_ARB_debug_output) { glEnable(GL_DEBUG_OUTPUT); - glDebugMessageCallback(MessageCallback, 0); + glDebugMessageCallback((GLDEBUGPROC) MessageCallback, 0); } else { fprintf(stderr, "WARNING: GLEW_ARB_debug_output is not available"); } From fcf147223a591658ca5f98245ebb6acde41bc732 Mon Sep 17 00:00:00 2001 From: "alexander.nutz" Date: Wed, 3 Apr 2024 10:09:36 +0200 Subject: [PATCH 04/29] fix --- setup_other.sh | 2 ++ src/main.c | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 setup_other.sh diff --git a/setup_other.sh b/setup_other.sh new file mode 100644 index 00000000..150cc5db --- /dev/null +++ b/setup_other.sh @@ -0,0 +1,2 @@ +cp dependencies/SDL2/lib/x64/SDL2.dll . +dos2unix shaders/* diff --git a/src/main.c b/src/main.c index fb464c29..90f53abd 100644 --- a/src/main.c +++ b/src/main.c @@ -112,7 +112,7 @@ int main(int argc, char **argv) SDL_Window *window = SDL_CreateWindow("ded", - 0, 0, + 100, 100, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_RESIZABLE | SDL_WINDOW_OPENGL); if (window == NULL) { From 96fa2c9f43e685c642a3896d69362ec46d24859e Mon Sep 17 00:00:00 2001 From: "alexander.nutz" Date: Wed, 3 Apr 2024 10:28:48 +0200 Subject: [PATCH 05/29] file create --- README.md | 13 ++++++++++++- src/common.c | 7 ++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e0f73d66..76704cb5 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ # Quick Start ## Dependencies +(Automatically downloaded on Windows) - [SDL2 2.0.9+](https://www.libsdl.org/) - [FreeType 2.13.0+](https://freetype.org/) @@ -18,10 +19,20 @@ $ ./ded src/main.c ``` ## Windows MSVC - +In any PowerShell or CMD console ```console > .\setup_dependencies.bat +``` +In a bash console +```console +$ ./setup_other.sh +``` +In a MSVC console (VS developer x64 prompt) +```console > .\build_msvc.bat +``` +Then +```console > .\ded.exe src\main.c ``` diff --git a/src/common.c b/src/common.c index 1dc11b96..9d46899d 100644 --- a/src/common.c +++ b/src/common.c @@ -95,7 +95,12 @@ Errno read_entire_file(const char *file_path, String_Builder *sb) FILE *f = NULL; f = fopen(file_path, "r"); - if (f == NULL) return_defer(errno); + if (f == NULL) { + f = fopen(file_path, "w"); + if (f == NULL) return_defer(errno); + fclose(f); + f = fopen(file_path, "r"); + } size_t size; Errno err = file_size(f, &size); From 77294230388581fc2c16ae386f53b347a10218a1 Mon Sep 17 00:00:00 2001 From: Junior Rantila Date: Sun, 3 Mar 2024 09:44:07 +0100 Subject: [PATCH 06/29] Only commit successful directory changes If you try to cd into a directory that you don't have permissions to open, the file browser would become blank with no way to recover since we had already changed the file browser state before knowing if the directory change would succeed. This patch ensures that we only commit the directory change after we've successfully read it. --- src/file_browser.c | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/src/file_browser.c b/src/file_browser.c index d4afb45e..7084356a 100644 --- a/src/file_browser.c +++ b/src/file_browser.c @@ -105,26 +105,32 @@ Errno fb_change_dir(File_Browser *fb) const char *dir_name = fb->files.items[fb->cursor]; - fb->dir_path.count -= 1; + String_Builder new_path = { 0 }; + da_append_many(&new_path, fb->dir_path.items, fb->dir_path.count); - // TODO: fb->dir_path grows indefinitely if we hit the root - sb_append_cstr(&fb->dir_path, "/"); - sb_append_cstr(&fb->dir_path, dir_name); - - String_Builder result = {0}; - normpath(sb_to_sv(fb->dir_path), &result); - da_move(&fb->dir_path, result); - sb_append_null(&fb->dir_path); + new_path.count -= 1; - printf("Changed dir to %s\n", fb->dir_path.items); + // TODO: fb->dir_path grows indefinitely if we hit the root + sb_append_cstr(&new_path, "/"); + sb_append_cstr(&new_path, dir_name); - fb->files.count = 0; - fb->cursor = 0; - Errno err = read_entire_dir(fb->dir_path.items, &fb->files); + String_Builder result = { 0 }; + normpath(sb_to_sv(new_path), &result); + da_move(&new_path, result); + sb_append_null(&new_path); + Files new_files = { 0 }; + Errno err = read_entire_dir(new_path.items, &new_files); if (err != 0) { return err; } + + da_move(&fb->files, new_files); + da_move(&fb->dir_path, new_path); + fb->cursor = 0; + + printf("Changed dir to %s\n", fb->dir_path.items); + qsort(fb->files.items, fb->files.count, sizeof(*fb->files.items), file_cmp); return 0; From 157135874af6509332d84d8889e9fa0389ed8f67 Mon Sep 17 00:00:00 2001 From: "alexander.nutz" Date: Wed, 3 Apr 2024 10:37:27 +0200 Subject: [PATCH 07/29] fullscreen --- src/main.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/main.c b/src/main.c index 90f53abd..a2212942 100644 --- a/src/main.c +++ b/src/main.c @@ -159,6 +159,7 @@ int main(int argc, char **argv) editor.atlas = &atlas; editor_retokenize(&editor); + bool is_fullscreen = false; bool quit = false; bool file_browser = false; while (!quit) { @@ -172,6 +173,14 @@ int main(int argc, char **argv) break; case SDL_KEYDOWN: { + switch (event.key.keysym.sym) { + case SDLK_F11: { + is_fullscreen = !is_fullscreen; + SDL_SetWindowFullscreen(window, is_fullscreen * SDL_WINDOW_FULLSCREEN_DESKTOP); + } + break; + } + if (file_browser) { switch (event.key.keysym.sym) { case SDLK_F3: { From 606daece5bde155eba59c64d26dfc46d68d11a4f Mon Sep 17 00:00:00 2001 From: Austin O'Donnell Date: Mon, 13 Mar 2023 00:01:10 -0400 Subject: [PATCH 08/29] Add the ability to delete entire selection area, and disable editor.selection when the area is empty after deletion. --- src/editor.c | 34 ++++++++++++++++++++++++++++++++++ src/editor.h | 1 + src/main.c | 14 ++++++++++++-- 3 files changed, 47 insertions(+), 2 deletions(-) diff --git a/src/editor.c b/src/editor.c index 8175ab5f..0dabb338 100644 --- a/src/editor.c +++ b/src/editor.c @@ -43,6 +43,40 @@ void editor_delete(Editor *e) editor_retokenize(e); } +void editor_delete_selection(Editor *e) +{ + assert(e->selection); + + if (e->cursor > e->select_begin) { + if (e->cursor > e->data.count) { + e->cursor = e->data.count; + } + if (e->cursor == 0) return; + + size_t nchars = e->cursor - e->select_begin; + memmove( + &e->data.items[e->cursor - nchars], + &e->data.items[e->cursor], + e->data.count - e->cursor + ); + + e->cursor -= nchars; + e->data.count -= nchars; + } else { + if (e->cursor >= e->data.count) return; + + size_t nchars = e->select_begin - e->cursor; + memmove( + &e->data.items[e->cursor], + &e->data.items[e->cursor + nchars], + e->data.count - e->cursor - nchars + ); + + e->data.count -= nchars; + } + editor_retokenize(e); +} + // TODO: make sure that you always have new line at the end of the file while saving // https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_206 diff --git a/src/editor.h b/src/editor.h index 4b5ddd58..5349c3b0 100644 --- a/src/editor.h +++ b/src/editor.h @@ -52,6 +52,7 @@ Errno editor_load_from_file(Editor *editor, const char *file_path); void editor_backspace(Editor *editor); void editor_delete(Editor *editor); +void editor_delete_selection(Editor *editor); size_t editor_cursor_row(const Editor *e); void editor_move_line_up(Editor *e); diff --git a/src/main.c b/src/main.c index a2212942..e5ec299e 100644 --- a/src/main.c +++ b/src/main.c @@ -263,7 +263,12 @@ int main(int argc, char **argv) } break; case SDLK_BACKSPACE: { - editor_backspace(&editor); + if (editor.selection) { + editor_delete_selection(&editor); + editor.selection = false; + } else { + editor_backspace(&editor); + } editor.last_stroke = SDL_GetTicks(); } break; @@ -302,7 +307,12 @@ int main(int argc, char **argv) break; case SDLK_DELETE: { - editor_delete(&editor); + if (editor.selection) { + editor_delete_selection(&editor); + editor.selection = false; + } else { + editor_delete(&editor); + } editor.last_stroke = SDL_GetTicks(); } break; From 8c3474f5021adbe74098550ee3391e6238e0f1dc Mon Sep 17 00:00:00 2001 From: "alexander.nutz" Date: Wed, 3 Apr 2024 12:58:14 +0200 Subject: [PATCH 09/29] pop ups --- src/editor.c | 95 +++++++++++++++++++++++++++++++++++++++---- src/editor.h | 23 +++++++++++ src/file_browser.c | 14 +++---- src/lexer.c | 4 +- src/main.c | 16 +++----- src/simple_renderer.c | 6 +-- src/simple_renderer.h | 12 ++++-- 7 files changed, 137 insertions(+), 33 deletions(-) diff --git a/src/editor.c b/src/editor.c index 0dabb338..3ecefd69 100644 --- a/src/editor.c +++ b/src/editor.c @@ -415,6 +415,48 @@ void editor_render(SDL_Window *window, Free_Glyph_Atlas *atlas, Simple_Renderer simple_renderer_flush(sr); } + // Render pop-ups + { + Simple_Camera oldCam = sr->cam; + sr->cam = (Simple_Camera) { + .pos = vec2f((float) w / 2 - 20.0f, -((float) h / 2) + 80.0f), + .scale = 1.0f, + .scale_vel = 0.0f, + .vel = vec2f(0, 0) + }; + simple_renderer_set_shader(sr, SHADER_FOR_TEXT); + + for (size_t i = 0; i < editor->popUps.count; i ++) { + PopUp *p = &editor->popUps.items[i]; + if (p->lasts == 0) + continue; + + if (p->when + p->lasts < SDL_GetTicks()) { + editor_remove_popup(editor, p->uid); + i --; + } + } + + for (size_t i = 0; i < editor->popUps.count; i ++) { + PopUp *p = &editor->popUps.items[i]; + + float t = (SDL_GetTicks() - p->when) / 300.0f; + if (t > 1) + t = 1; + else if (t < 0) + t = 0; + + Vec2f pos = vec2f( + lerpf(-180, 0, t), + -(i * 80.0f) + ); + free_glyph_atlas_render_line_sized(atlas, sr, p->msg, p->msg_size, &pos, p->color); + } + + simple_renderer_flush(sr); + sr->cam = oldCam; + } + // Update camera { if (max_line_len > 1000.0f) { @@ -429,18 +471,18 @@ void editor_render(SDL_Window *window, Free_Glyph_Atlas *atlas, Simple_Renderer if (target_scale > 3.0f) { target_scale = 3.0f; } else { - offset = cursor_pos.x - w/3/sr->camera_scale; + offset = cursor_pos.x - w/3/sr->cam.scale; if (offset < 0.0f) offset = 0.0f; - target = vec2f(w/3/sr->camera_scale + offset, cursor_pos.y); + target = vec2f(w/3/sr->cam.scale + offset, cursor_pos.y); } - sr->camera_vel = vec2f_mul( - vec2f_sub(target, sr->camera_pos), + sr->cam.vel = vec2f_mul( + vec2f_sub(target, sr->cam.pos), vec2fs(2.0f)); - sr->camera_scale_vel = (target_scale - sr->camera_scale) * 2.0f; + sr->cam.scale_vel = (target_scale - sr->cam.scale) * 2.0f; - sr->camera_pos = vec2f_add(sr->camera_pos, vec2f_mul(sr->camera_vel, vec2fs(DELTA_TIME))); - sr->camera_scale = sr->camera_scale + sr->camera_scale_vel * DELTA_TIME; + sr->cam.pos = vec2f_add(sr->cam.pos, vec2f_mul(sr->cam.vel, vec2fs(DELTA_TIME))); + sr->cam.scale = sr->cam.scale + sr->cam.scale_vel * DELTA_TIME; } } @@ -576,3 +618,42 @@ void editor_move_paragraph_down(Editor *e) } e->cursor = e->lines.items[row].begin; } + +Uint32 nextPopUpUid = 0; +Uint32 editor_add_popup(Editor *editor, PopUp *popUp) +{ + editor->popUps.count ++; + editor->popUps.items = realloc(editor->popUps.items, sizeof(PopUp) * editor->popUps.count); + memcpy(&editor->popUps.items[editor->popUps.count - 1], popUp, sizeof(PopUp)); + editor->popUps.items[editor->popUps.count - 1].uid = nextPopUpUid; + return nextPopUpUid ++; +} + +void editor_remove_popup(Editor *editor, Uint32 uid) +{ + for (size_t i = 0; i < editor->popUps.count; i ++) { + PopUp *p = &editor->popUps.items[i]; + if (p->uid != uid) + continue; + + memcpy(p, p + 1, (editor->popUps.count - i - 1) * sizeof(PopUp)); + editor->popUps.count --; + editor->popUps.items = realloc(editor->popUps.items, sizeof(PopUp) * editor->popUps.count); + break; + } +} + +void flash_error_str(Editor *editor, const char *str) +{ + fputs(str, stderr); + fputc('\n', stderr); + + PopUp p; + p.msg = str; + p.msg_size = strlen(str); + p.color = hex_to_vec4f(0xD20103FF); + p.when = SDL_GetTicks(); + p.lasts = 2000; + + (void) editor_add_popup(editor, &p); +} \ No newline at end of file diff --git a/src/editor.h b/src/editor.h index 5349c3b0..c84faab5 100644 --- a/src/editor.h +++ b/src/editor.h @@ -26,6 +26,20 @@ typedef struct { size_t capacity; } Tokens; +typedef struct { + Uint32 uid; + const char *msg; + size_t msg_size; + Vec4f color; + Uint32 when; + Uint32 lasts; +} PopUp; + +typedef struct { + PopUp *items; + size_t count; +} PopUps; + typedef struct { Free_Glyph_Atlas *atlas; @@ -33,6 +47,7 @@ typedef struct { Lines lines; Tokens tokens; String_Builder file_path; + PopUps popUps; bool searching; String_Builder search; @@ -50,6 +65,9 @@ Errno editor_save_as(Editor *editor, const char *file_path); Errno editor_save(const Editor *editor); Errno editor_load_from_file(Editor *editor, const char *file_path); +Uint32 editor_add_popup(Editor *editor, PopUp *popUp); +void editor_remove_popup(Editor *editor, Uint32 uid); + void editor_backspace(Editor *editor); void editor_delete(Editor *editor); void editor_delete_selection(Editor *editor); @@ -81,4 +99,9 @@ void editor_start_search(Editor *e); void editor_stop_search(Editor *e); bool editor_search_matches_at(Editor *e, size_t pos); +void flash_error_str(Editor *editor, const char *str); + +// TODO: display errors reported via flash_error right in the text editor window somehow +#define flash_error(editor, ...) do { static char buf[200]; sprintf(buf, __VA_ARGS__); flash_error_str(editor, buf); } while(0) + #endif // EDITOR_H_ diff --git a/src/file_browser.c b/src/file_browser.c index 7084356a..11670d8b 100644 --- a/src/file_browser.c +++ b/src/file_browser.c @@ -190,18 +190,18 @@ void fb_render(const File_Browser *fb, SDL_Window *window, Free_Glyph_Atlas *atl if (target_scale > 3.0f) { target_scale = 3.0f; } else { - offset = cursor_pos.x - w/3/sr->camera_scale; + offset = cursor_pos.x - w/3/sr->cam.scale; if (offset < 0.0f) offset = 0.0f; - target = vec2f(w/3/sr->camera_scale + offset, cursor_pos.y); + target = vec2f(w/3/sr->cam.scale + offset, cursor_pos.y); } - sr->camera_vel = vec2f_mul( - vec2f_sub(target, sr->camera_pos), + sr->cam.vel = vec2f_mul( + vec2f_sub(target, sr->cam.pos), vec2fs(2.0f)); - sr->camera_scale_vel = (target_scale - sr->camera_scale) * 2.0f; + sr->cam.scale_vel = (target_scale - sr->cam.scale) * 2.0f; - sr->camera_pos = vec2f_add(sr->camera_pos, vec2f_mul(sr->camera_vel, vec2fs(DELTA_TIME))); - sr->camera_scale = sr->camera_scale + sr->camera_scale_vel * DELTA_TIME; + sr->cam.pos = vec2f_add(sr->cam.pos, vec2f_mul(sr->cam.vel, vec2fs(DELTA_TIME))); + sr->cam.scale = sr->cam.scale + sr->cam.scale_vel * DELTA_TIME; } } diff --git a/src/lexer.c b/src/lexer.c index a5c185dc..dd0bf6f5 100644 --- a/src/lexer.c +++ b/src/lexer.c @@ -28,7 +28,7 @@ const char *jKeywords[] = { "return", "short", "static", "super", "switch", "synchronized", "this", "throw", "throws", "transient", "try", "void", "volatile", "while", "non-sealed", "open", "opens", "permits", "provides", "record", "sealed", "to", "transitive", "uses", "var", - "with", "yield", "true", "false", "null", "const", "goto", "strictfp", + "with", "yield", "true", "false", "null", "const", "goto", "strictfp", }; #define jKeywords_count (sizeof(jKeywords)/sizeof(jKeywords[0])) @@ -36,7 +36,7 @@ const char *ktKeywords[] = { "abstract", "break", "catch", "class", "const", "continue", "else", "enum", "is", "as", "when", "val", "var", "for", "if", "import", "interface", "data", "external", "inner", "package", "private", "protected", "return", "super", "when", "this", "throw", - "try", "while", "sealed", "open", "true", "false", "null", "fun", "typealias", + "try", "while", "sealed", "open", "true", "false", "null", "fun", "typealias", "in", }; #define ktKeywords_count (sizeof(ktKeywords)/sizeof(ktKeywords[0])) diff --git a/src/main.c b/src/main.c index e5ec299e..12dc5752 100644 --- a/src/main.c +++ b/src/main.c @@ -52,10 +52,6 @@ static Simple_Renderer sr = {0}; static Editor editor = {0}; static File_Browser fb = {0}; -// TODO: display errors reported via flash_error right in the text editor window somehow -#define flash_error(...) do { fprintf(stderr, __VA_ARGS__); fprintf(stderr, "\n"); } while(0) - - int main(int argc, char **argv) { Errno err; @@ -204,13 +200,13 @@ int main(int argc, char **argv) File_Type ft; err = type_of_file(file_path, &ft); if (err != 0) { - flash_error("Could not determine type of file %s: %s", file_path, strerror(err)); + flash_error(&editor, "Could not determine type of file %s: %s", file_path, strerror(err)); } else { switch (ft) { case FT_DIRECTORY: { err = fb_change_dir(&fb); if (err != 0) { - flash_error("Could not change directory to %s: %s", file_path, strerror(err)); + flash_error(&editor, "Could not change directory to %s: %s", file_path, strerror(err)); } } break; @@ -220,7 +216,7 @@ int main(int argc, char **argv) // And if you do, annoy the user about it. (just like all the other editors do) err = editor_load_from_file(&editor, file_path); if (err != 0) { - flash_error("Could not open file %s: %s", file_path, strerror(err)); + flash_error(&editor, "Could not open file %s: %s", file_path, strerror(err)); } else { file_browser = false; } @@ -228,7 +224,7 @@ int main(int argc, char **argv) break; case FT_OTHER: { - flash_error("%s is neither a regular file nor a directory. We can't open it.", file_path); + flash_error(&editor, "%s is neither a regular file nor a directory. We can't open it.", file_path); } break; @@ -277,11 +273,11 @@ int main(int argc, char **argv) if (editor.file_path.count > 0) { err = editor_save(&editor); if (err != 0) { - flash_error("Could not save currently edited file: %s", strerror(err)); + flash_error(&editor, "Could not save currently edited file: %s", strerror(err)); } } else { // TODO: ask the user for the path to save to in this situation - flash_error("Nowhere to save the text"); + flash_error(&editor, "Nowhere to save the text"); } } break; diff --git a/src/simple_renderer.c b/src/simple_renderer.c index 546678b7..8a7421e4 100644 --- a/src/simple_renderer.c +++ b/src/simple_renderer.c @@ -130,7 +130,7 @@ static void get_uniform_location(GLuint program, GLint locations[COUNT_UNIFORM_S void simple_renderer_init(Simple_Renderer *sr) { - sr->camera_scale = 3.0f; + sr->cam.scale = 3.0f; { glGenVertexArrays(1, &sr->vao); @@ -321,8 +321,8 @@ void simple_renderer_set_shader(Simple_Renderer *sr, Simple_Shader shader) get_uniform_location(sr->programs[sr->current_shader], sr->uniforms); glUniform2f(sr->uniforms[UNIFORM_SLOT_RESOLUTION], sr->resolution.x, sr->resolution.y); glUniform1f(sr->uniforms[UNIFORM_SLOT_TIME], sr->time); - glUniform2f(sr->uniforms[UNIFORM_SLOT_CAMERA_POS], sr->camera_pos.x, sr->camera_pos.y); - glUniform1f(sr->uniforms[UNIFORM_SLOT_CAMERA_SCALE], sr->camera_scale); + glUniform2f(sr->uniforms[UNIFORM_SLOT_CAMERA_POS], sr->cam.pos.x, sr->cam.pos.y); + glUniform1f(sr->uniforms[UNIFORM_SLOT_CAMERA_SCALE], sr->cam.scale); } void simple_renderer_flush(Simple_Renderer *sr) diff --git a/src/simple_renderer.h b/src/simple_renderer.h index b67bdb03..d2b398e1 100644 --- a/src/simple_renderer.h +++ b/src/simple_renderer.h @@ -43,6 +43,13 @@ typedef enum { COUNT_SIMPLE_SHADERS, } Simple_Shader; +typedef struct { + Vec2f pos; + float scale; + float scale_vel; + Vec2f vel; +} Simple_Camera; + typedef struct { GLuint vao; GLuint vbo; @@ -56,10 +63,7 @@ typedef struct { Vec2f resolution; float time; - Vec2f camera_pos; - float camera_scale; - float camera_scale_vel; - Vec2f camera_vel; + Simple_Camera cam; } Simple_Renderer; void simple_renderer_init(Simple_Renderer *sr); From acf712a8fd4bbacd1f64faba0b2e2009dde18131 Mon Sep 17 00:00:00 2001 From: "alexander.nutz" Date: Wed, 3 Apr 2024 14:24:32 +0200 Subject: [PATCH 10/29] ctrl+x, more warning stuff, search stuff --- src/editor.c | 110 ++++++++++++++++++++++++++++++++++----------------- src/editor.h | 9 ++++- src/lexer.c | 16 +++----- src/main.c | 56 ++++++++++++++++++++------ 4 files changed, 131 insertions(+), 60 deletions(-) diff --git a/src/editor.c b/src/editor.c index 3ecefd69..d85a8989 100644 --- a/src/editor.c +++ b/src/editor.c @@ -8,9 +8,9 @@ void editor_backspace(Editor *e) { - if (e->searching) { - if (e->search.count > 0) { - e->search.count -= 1; + if (e->input.active) { + if (e->input.text.count > 0) { + e->input.text.count -= 1; } } else { if (e->cursor > e->data.count) { @@ -198,34 +198,21 @@ void editor_insert_char(Editor *e, char x) void editor_insert_buf(Editor *e, char *buf, size_t buf_len) { - if (e->searching) { - sb_append_buf(&e->search, buf, buf_len); - bool matched = false; - for (size_t pos = e->cursor; pos < e->data.count; ++pos) { - if (editor_search_matches_at(e, pos)) { - e->cursor = pos; - matched = true; - break; - } - } - if (!matched) e->search.count -= buf_len; - } else { - if (e->cursor > e->data.count) { - e->cursor = e->data.count; - } + if (e->cursor > e->data.count) { + e->cursor = e->data.count; + } - for (size_t i = 0; i < buf_len; ++i) { - da_append(&e->data, '\0'); - } - memmove( - &e->data.items[e->cursor + buf_len], - &e->data.items[e->cursor], - e->data.count - e->cursor - buf_len - ); - memcpy(&e->data.items[e->cursor], buf, buf_len); - e->cursor += buf_len; - editor_retokenize(e); + for (size_t i = 0; i < buf_len; ++i) { + da_append(&e->data, '\0'); } + memmove( + &e->data.items[e->cursor + buf_len], + &e->data.items[e->cursor], + e->data.count - e->cursor - buf_len + ); + memcpy(&e->data.items[e->cursor], buf, buf_len); + e->cursor += buf_len; + editor_retokenize(e); } void editor_retokenize(Editor *e) @@ -355,12 +342,12 @@ void editor_render(SDL_Window *window, Free_Glyph_Atlas *atlas, Simple_Renderer // Render search { - if (editor->searching) { + if (editor->searching && editor_search_matches_at(editor, editor->cursor)) { simple_renderer_set_shader(sr, SHADER_FOR_COLOR); Vec4f selection_color = vec4f(.10, .10, .25, 1); Vec2f p1 = cursor_pos; Vec2f p2 = p1; - free_glyph_atlas_measure_line_sized(editor->atlas, editor->search.items, editor->search.count, &p2); + free_glyph_atlas_measure_line_sized(editor->atlas, editor->input.text.items, editor->input.text.count, &p2); simple_renderer_solid_rect(sr, p1, vec2f(p2.x - p1.x, FREE_GLYPH_FONT_SIZE), selection_color); simple_renderer_flush(sr); } @@ -457,6 +444,41 @@ void editor_render(SDL_Window *window, Free_Glyph_Atlas *atlas, Simple_Renderer sr->cam = oldCam; } + // Render input + { + if (editor->input.active) { + Simple_Camera oldCam = sr->cam; + sr->cam = (Simple_Camera) { + .pos = vec2f((float) w / 2, ((float) h / 2) - 60.0f), + .scale = 1.0f, + .scale_vel = 0.0f, + .vel = vec2f(0, 0) + }; + + { + simple_renderer_set_shader(sr, SHADER_FOR_COLOR); + Vec4f bg = vec4fs(1); + Vec2f p1 = vec2f(0, -60); + Vec2f s = vec2f(w, 120); + simple_renderer_solid_rect(sr, p1, s, bg); + simple_renderer_flush(sr); + } + + { + simple_renderer_set_shader(sr, SHADER_FOR_TEXT); + Vec4f color = vec4fs(0); + Vec2f pos = vec2f(20, -20); + free_glyph_atlas_render_line_sized(atlas, sr, + editor->input.text.items, + editor->input.text.count, + &pos, color); + simple_renderer_flush(sr); + } + + sr->cam = oldCam; + } + } + // Update camera { if (max_line_len > 1000.0f) { @@ -542,11 +564,15 @@ void editor_start_search(Editor *e) } } else { e->searching = true; + editor_start_input(e); if (e->selection) { + size_t begin = e->select_begin; + size_t end = e->cursor; + if (begin > end) SWAP(size_t, begin, end); + + sb_append_buf(&e->input.text, &e->data.items[begin], end - begin + 1); + e->cursor = begin; e->selection = false; - // TODO: put the selection into the search automatically - } else { - e->search.count = 0; } } } @@ -554,13 +580,14 @@ void editor_start_search(Editor *e) void editor_stop_search(Editor *e) { e->searching = false; + e->input.active = false; } bool editor_search_matches_at(Editor *e, size_t pos) { - if (e->data.count - pos < e->search.count) return false; - for (size_t i = 0; i < e->search.count; ++i) { - if (e->search.items[i] != e->data.items[pos + i]) { + if (e->data.count - pos < e->input.text.count) return false; + for (size_t i = 0; i < e->input.text.count; ++i) { + if (e->input.text.items[i] != e->data.items[pos + i]) { return false; } } @@ -656,4 +683,13 @@ void flash_error_str(Editor *editor, const char *str) p.lasts = 2000; (void) editor_add_popup(editor, &p); +} + +void editor_start_input(Editor *editor) +{ + editor->input.active = true; + if (editor->input.text.items) { + free(editor->input.text.items); + editor->input.text = (String_Builder){0}; + } } \ No newline at end of file diff --git a/src/editor.h b/src/editor.h index c84faab5..77f40c1e 100644 --- a/src/editor.h +++ b/src/editor.h @@ -40,6 +40,11 @@ typedef struct { size_t count; } PopUps; +typedef struct { + String_Builder text; + bool active; +} Input; + typedef struct { Free_Glyph_Atlas *atlas; @@ -48,9 +53,9 @@ typedef struct { Tokens tokens; String_Builder file_path; PopUps popUps; + Input input; bool searching; - String_Builder search; bool selection; size_t select_begin; @@ -68,6 +73,8 @@ Errno editor_load_from_file(Editor *editor, const char *file_path); Uint32 editor_add_popup(Editor *editor, PopUp *popUp); void editor_remove_popup(Editor *editor, Uint32 uid); +void editor_start_input(Editor *editor); + void editor_backspace(Editor *editor); void editor_delete(Editor *editor); void editor_delete_selection(Editor *editor); diff --git a/src/lexer.c b/src/lexer.c index dd0bf6f5..2208db93 100644 --- a/src/lexer.c +++ b/src/lexer.c @@ -100,30 +100,24 @@ Lexer lexer_new(Free_Glyph_Atlas *atlas, const char *content, size_t content_len l.atlas = atlas; l.content = content; l.content_len = content_len; + l.file_ext = FEXT_CPP; if (file_path.items != NULL) { l.file_path.items = (char*) malloc(sizeof(char*) * (strlen(file_path.items) + 1)); strcpy(l.file_path.items, file_path.items); - File_Extension file_ext; const char *filename = l.file_path.items; const char *dot = strrchr(filename, '.'); - if (!dot || dot == filename) { - file_ext = FEXT_CPP; - } else { + if (dot && dot != filename) { const char *file_ext_str = dot + 1; if (strcmp(file_ext_str, "kt") == 0 || strcmp(file_ext_str, "kts") == 0) { - file_ext = FEXT_KOTLIN; + l.file_ext = FEXT_KOTLIN; } else if (strcmp(file_ext_str, "py") == 0) { - file_ext = FEXT_PYTHON; + l.file_ext = FEXT_PYTHON; } else if (strcmp(file_ext_str, "java") == 0) { - file_ext = FEXT_JAVA; - } else { - file_ext = FEXT_CPP; + l.file_ext = FEXT_JAVA; } } - - l.file_ext = file_ext; } return l; diff --git a/src/main.c b/src/main.c index 12dc5752..7aba0621 100644 --- a/src/main.c +++ b/src/main.c @@ -273,7 +273,15 @@ int main(int argc, char **argv) if (editor.file_path.count > 0) { err = editor_save(&editor); if (err != 0) { - flash_error(&editor, "Could not save currently edited file: %s", strerror(err)); + flash_error(&editor, "Can't save: %s", strerror(err)); + } else { + PopUp p; + p.msg = "Saved file!"; + p.msg_size = strlen(p.msg); + p.color = hex_to_vec4f(0x7DDA58FF); + p.when = SDL_GetTicks(); + p.lasts = 1000; + (void) editor_add_popup(&editor, &p); } } else { // TODO: ask the user for the path to save to in this situation @@ -295,7 +303,13 @@ int main(int argc, char **argv) case SDLK_RETURN: { if (editor.searching) { editor_stop_search(&editor); + } else if (editor.input.active) { + editor.input.active = false; } else { + if (editor.selection) { + editor_delete_selection(&editor); + editor.selection = false; + } editor_insert_char(&editor, '\n'); editor.last_stroke = SDL_GetTicks(); } @@ -336,15 +350,19 @@ int main(int argc, char **argv) break; case SDLK_TAB: { - // TODO: indent on Tab instead of just inserting 4 spaces at the cursor - // That is insert the spaces at the beginning of the line. Shift+TAB should - // do unindent, that is remove 4 spaces from the beginning of the line. - // TODO: customizable indentation style - // - tabs/spaces - // - tab width - // - etc. - for (size_t i = 0; i < 4; ++i) { - editor_insert_char(&editor, ' '); + if (editor.searching) { + editor_start_search(&editor); + } else { + // TODO: indent on Tab instead of just inserting 4 spaces at the cursor + // That is insert the spaces at the beginning of the line. Shift+TAB should + // do unindent, that is remove 4 spaces from the beginning of the line. + // TODO: customizable indentation style + // - tabs/spaces + // - tab width + // - etc. + for (size_t i = 0; i < 4; ++i) { + editor_insert_char(&editor, ' '); + } } } break; @@ -356,6 +374,17 @@ int main(int argc, char **argv) } break; + case SDLK_x: { + if (event.key.keysym.mod & KMOD_CTRL) { + editor_clipboard_copy(&editor); + if (editor.selection) { + editor_delete_selection(&editor); + editor.selection = false; + } + } + } + break; + case SDLK_v: { if (event.key.keysym.mod & KMOD_CTRL) { editor_clipboard_paste(&editor); @@ -412,7 +441,9 @@ int main(int argc, char **argv) break; case SDL_TEXTINPUT: { - if (file_browser) { + if (editor.input.active) { + sb_append_cstr(&editor.input.text, event.text.text); + } else if (file_browser) { // Nothing for now // Once we have incremental search in the file browser this may become useful } else { @@ -423,6 +454,9 @@ int main(int argc, char **argv) } editor.last_stroke = SDL_GetTicks(); } + if (editor.searching && !editor_search_matches_at(&editor, editor.cursor)) { + editor_start_search(&editor); + } } break; } From ea034a7485fbed4368ed7a1314c57135360b83f7 Mon Sep 17 00:00:00 2001 From: "alexander.nutz" Date: Wed, 3 Apr 2024 14:37:33 +0200 Subject: [PATCH 11/29] more stuff --- src/editor.c | 25 +++++++++++++++++++++++-- src/editor.h | 7 +++++-- src/main.c | 33 ++++++++++++++++++--------------- 3 files changed, 46 insertions(+), 19 deletions(-) diff --git a/src/editor.c b/src/editor.c index d85a8989..5f0fe717 100644 --- a/src/editor.c +++ b/src/editor.c @@ -88,14 +88,33 @@ Errno editor_save_as(Editor *e, const char *file_path) e->file_path.count = 0; sb_append_cstr(&e->file_path, file_path); sb_append_null(&e->file_path); + + PopUp p; + p.msg = "Saved file!"; + p.msg_size = strlen(p.msg); + p.color = hex_to_vec4f(0x7DDA58FF); + p.when = SDL_GetTicks(); + p.lasts = 1000; + (void) editor_add_popup(e, &p); + return 0; } -Errno editor_save(const Editor *e) +Errno editor_save(Editor *e) { assert(e->file_path.count > 0); printf("Saving as %s...\n", e->file_path.items); - return write_entire_file(e->file_path.items, e->data.items, e->data.count); + Errno err = write_entire_file(e->file_path.items, e->data.items, e->data.count); + if (err == 0) { + PopUp p; + p.msg = "Saved file!"; + p.msg_size = strlen(p.msg); + p.color = hex_to_vec4f(0x7DDA58FF); + p.when = SDL_GetTicks(); + p.lasts = 1000; + (void) editor_add_popup(e, &p); + } + return err; } Errno editor_load_from_file(Editor *e, const char *file_path) @@ -581,6 +600,7 @@ void editor_stop_search(Editor *e) { e->searching = false; e->input.active = false; + e->input.onDone = NULL; } bool editor_search_matches_at(Editor *e, size_t pos) @@ -688,6 +708,7 @@ void flash_error_str(Editor *editor, const char *str) void editor_start_input(Editor *editor) { editor->input.active = true; + editor->input.onDone = NULL; if (editor->input.text.items) { free(editor->input.text.items); editor->input.text = (String_Builder){0}; diff --git a/src/editor.h b/src/editor.h index 77f40c1e..9059ccff 100644 --- a/src/editor.h +++ b/src/editor.h @@ -40,12 +40,15 @@ typedef struct { size_t count; } PopUps; +struct Editor_s; + typedef struct { String_Builder text; bool active; + void (*onDone)(struct Editor_s *); } Input; -typedef struct { +typedef struct Editor_s { Free_Glyph_Atlas *atlas; String_Builder data; @@ -67,7 +70,7 @@ typedef struct { } Editor; Errno editor_save_as(Editor *editor, const char *file_path); -Errno editor_save(const Editor *editor); +Errno editor_save(Editor *editor); Errno editor_load_from_file(Editor *editor, const char *file_path); Uint32 editor_add_popup(Editor *editor, PopUp *popUp); diff --git a/src/main.c b/src/main.c index 7aba0621..3457713a 100644 --- a/src/main.c +++ b/src/main.c @@ -52,6 +52,12 @@ static Simple_Renderer sr = {0}; static Editor editor = {0}; static File_Browser fb = {0}; +static void onSaveInputPath(Editor *e) +{ + sb_append_null(&e->input.text); + editor_save_as(e, e->input.text.items); +} + int main(int argc, char **argv) { Errno err; @@ -269,23 +275,18 @@ int main(int argc, char **argv) } break; - case SDLK_F2: { - if (editor.file_path.count > 0) { - err = editor_save(&editor); - if (err != 0) { - flash_error(&editor, "Can't save: %s", strerror(err)); + case SDLK_s: { + if (event.key.keysym.mod & KMOD_CTRL) { + // TODO: ctrl+shift+s + if (editor.file_path.count > 0) { + err = editor_save(&editor); + if (err != 0) { + flash_error(&editor, "Can't save: %s", strerror(err)); + } } else { - PopUp p; - p.msg = "Saved file!"; - p.msg_size = strlen(p.msg); - p.color = hex_to_vec4f(0x7DDA58FF); - p.when = SDL_GetTicks(); - p.lasts = 1000; - (void) editor_add_popup(&editor, &p); + editor_start_input(&editor); + editor.input.onDone = onSaveInputPath; } - } else { - // TODO: ask the user for the path to save to in this situation - flash_error(&editor, "Nowhere to save the text"); } } break; @@ -305,6 +306,8 @@ int main(int argc, char **argv) editor_stop_search(&editor); } else if (editor.input.active) { editor.input.active = false; + if (editor.input.onDone) + editor.input.onDone(&editor); } else { if (editor.selection) { editor_delete_selection(&editor); From e73c1cc135354b0b72e10692b38c8df683967c55 Mon Sep 17 00:00:00 2001 From: "alexander.nutz" Date: Wed, 3 Apr 2024 14:51:22 +0200 Subject: [PATCH 12/29] more stuff --- src/editor.c | 5 +++-- src/editor.h | 1 + src/main.c | 21 ++++++++++----------- 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/editor.c b/src/editor.c index 5f0fe717..fea8fb26 100644 --- a/src/editor.c +++ b/src/editor.c @@ -599,8 +599,8 @@ void editor_start_search(Editor *e) void editor_stop_search(Editor *e) { e->searching = false; - e->input.active = false; - e->input.onDone = NULL; + if (!e->input.required) + e->input.active = false; } bool editor_search_matches_at(Editor *e, size_t pos) @@ -709,6 +709,7 @@ void editor_start_input(Editor *editor) { editor->input.active = true; editor->input.onDone = NULL; + editor->input.required = false; if (editor->input.text.items) { free(editor->input.text.items); editor->input.text = (String_Builder){0}; diff --git a/src/editor.h b/src/editor.h index 9059ccff..b0c07d2e 100644 --- a/src/editor.h +++ b/src/editor.h @@ -45,6 +45,7 @@ struct Editor_s; typedef struct { String_Builder text; bool active; + bool required; void (*onDone)(struct Editor_s *); } Input; diff --git a/src/main.c b/src/main.c index 3457713a..cef72b8f 100644 --- a/src/main.c +++ b/src/main.c @@ -185,11 +185,6 @@ int main(int argc, char **argv) if (file_browser) { switch (event.key.keysym.sym) { - case SDLK_F3: { - file_browser = false; - } - break; - case SDLK_UP: { if (fb.cursor > 0) fb.cursor -= 1; } @@ -218,8 +213,6 @@ int main(int argc, char **argv) break; case FT_REGULAR: { - // TODO: before opening a new file make sure you don't have unsaved changes - // And if you do, annoy the user about it. (just like all the other editors do) err = editor_load_from_file(&editor, file_path); if (err != 0) { flash_error(&editor, "Could not open file %s: %s", file_path, strerror(err)); @@ -277,8 +270,10 @@ int main(int argc, char **argv) case SDLK_s: { if (event.key.keysym.mod & KMOD_CTRL) { - // TODO: ctrl+shift+s - if (editor.file_path.count > 0) { + if (event.key.keysym.mod & KMOD_SHIFT) { + editor_start_input(&editor); + editor.input.onDone = onSaveInputPath; + } else if (editor.file_path.count > 0) { err = editor_save(&editor); if (err != 0) { flash_error(&editor, "Can't save: %s", strerror(err)); @@ -286,13 +281,17 @@ int main(int argc, char **argv) } else { editor_start_input(&editor); editor.input.onDone = onSaveInputPath; + editor.input.required = true; } } } break; - case SDLK_F3: { - file_browser = true; + case SDLK_o: { + // TODO: annoy user if unsaved changes + if (event.key.keysym.mod & KMOD_CTRL) { + file_browser = true; + } } break; From f3d21acb2d859cc213a729b591dfaeff26927758 Mon Sep 17 00:00:00 2001 From: "alexander.nutz" Date: Wed, 3 Apr 2024 15:26:31 +0200 Subject: [PATCH 13/29] fix type_of_file, fix shader load ~joshuang321; other stuff --- src/common.c | 14 ++++++++++---- src/main.c | 19 ++++++++++++------- src/simple_renderer.c | 7 ++++--- 3 files changed, 26 insertions(+), 14 deletions(-) diff --git a/src/common.c b/src/common.c index 9d46899d..060525f7 100644 --- a/src/common.c +++ b/src/common.c @@ -94,12 +94,12 @@ Errno read_entire_file(const char *file_path, String_Builder *sb) Errno result = 0; FILE *f = NULL; - f = fopen(file_path, "r"); + f = fopen(file_path, "rb"); if (f == NULL) { f = fopen(file_path, "w"); if (f == NULL) return_defer(errno); fclose(f); - f = fopen(file_path, "r"); + f = fopen(file_path, "rb"); } size_t size; @@ -138,11 +138,17 @@ Vec4f hex_to_vec4f(uint32_t color) Errno type_of_file(const char *file_path, File_Type *ft) { #ifdef _WIN32 - if (GetFileAttributesA(file_path) & FILE_ATTRIBUTE_DIRECTORY) { + DWORD file_obj_type = GetFileAttributesA(file_path); + if (file_obj_type & FILE_ATTRIBUTE_DIRECTORY) { *ft = FT_DIRECTORY; - } else { + } + // I have no idea why, but a 'normal' file is considered an archive file? + else if (file_obj_type & FILE_ATTRIBUTE_ARCHIVE) { *ft = FT_REGULAR; } + else { + *ft = FT_OTHER; + } #else struct stat sb = {0}; if (stat(file_path, &sb) < 0) return errno; diff --git a/src/main.c b/src/main.c index cef72b8f..196413a3 100644 --- a/src/main.c +++ b/src/main.c @@ -442,6 +442,18 @@ int main(int argc, char **argv) } break; + case SDL_WINDOWEVENT: { + switch(event.window.event) { + case SDL_WINDOWEVENT_RESIZED: { + int w, h; + SDL_GetWindowSize(window, &w, &h); + glViewport(0, 0, w, h); + } + break; + } + } + break; + case SDL_TEXTINPUT: { if (editor.input.active) { sb_append_cstr(&editor.input.text, event.text.text); @@ -464,13 +476,6 @@ int main(int argc, char **argv) } } - { - int w, h; - SDL_GetWindowSize(window, &w, &h); - // TODO(#19): update the viewport and the resolution only on actual window change - glViewport(0, 0, w, h); - } - Vec4f bg = hex_to_vec4f(0x181818FF); glClearColor(bg.x, bg.y, bg.z, bg.w); glClear(GL_COLOR_BUFFER_BIT); diff --git a/src/simple_renderer.c b/src/simple_renderer.c index 8a7421e4..cb601376 100644 --- a/src/simple_renderer.c +++ b/src/simple_renderer.c @@ -29,10 +29,11 @@ static const char *shader_type_as_cstr(GLuint shader) } } -static bool compile_shader_source(const GLchar *source, GLenum shader_type, GLuint *shader) +static bool compile_shader_source(String_Builder source, GLenum shader_type, GLuint *shader) { *shader = glCreateShader(shader_type); - glShaderSource(*shader, 1, &source, NULL); + GLint source_count=(GLint)source.count; + glShaderSource(*shader, 1, &source.items, &source_count); glCompileShader(*shader); GLint compiled = 0; @@ -62,7 +63,7 @@ static bool compile_shader_file(const char *file_path, GLenum shader_type, GLuin } sb_append_null(&source); - if (!compile_shader_source(source.items, shader_type, shader)) { + if (!compile_shader_source(source, shader_type, shader)) { fprintf(stderr, "ERROR: failed to compile `%s` shader file\n", file_path); return_defer(false); } From a4465191bb23cd5b13d5434c06f3406737b2a262 Mon Sep 17 00:00:00 2001 From: "alexander.nutz" Date: Wed, 3 Apr 2024 17:28:04 +0200 Subject: [PATCH 14/29] more status bar stuff --- src/editor.c | 116 ++++++++++++++++++++++++++++------------- src/editor.h | 3 ++ src/lexer.c | 16 ++++++ src/lexer.h | 2 + src/main.c | 15 +++--- src/minirent.h | 136 ------------------------------------------------- 6 files changed, 109 insertions(+), 179 deletions(-) delete mode 100644 src/minirent.h diff --git a/src/editor.c b/src/editor.c index fea8fb26..02bb94af 100644 --- a/src/editor.c +++ b/src/editor.c @@ -259,6 +259,7 @@ void editor_retokenize(Editor *e) { e->tokens.count = 0; Lexer l = lexer_new(e->atlas, e->data.items, e->data.count, e->file_path); + e->file_ext = l.file_ext; Token t = lexer_next(&l); while (t.kind != TOKEN_END) { da_append(&e->tokens, t); @@ -421,12 +422,15 @@ void editor_render(SDL_Window *window, Free_Glyph_Atlas *atlas, Simple_Renderer simple_renderer_flush(sr); } + float scale = 0.6f; + float oscale = 1.0f / scale; + // Render pop-ups { Simple_Camera oldCam = sr->cam; sr->cam = (Simple_Camera) { - .pos = vec2f((float) w / 2 - 20.0f, -((float) h / 2) + 80.0f), - .scale = 1.0f, + .pos = vec2f((float) w / 2 * oscale - 20.0f * scale, -((float) h / 2 * oscale) + 80.0f * scale), + .scale = scale, .scale_vel = 0.0f, .vel = vec2f(0, 0) }; @@ -453,8 +457,8 @@ void editor_render(SDL_Window *window, Free_Glyph_Atlas *atlas, Simple_Renderer t = 0; Vec2f pos = vec2f( - lerpf(-180, 0, t), - -(i * 80.0f) + lerpf(-180 * oscale, 0, t), + -(i * 80.0f * scale) ); free_glyph_atlas_render_line_sized(atlas, sr, p->msg, p->msg_size, &pos, p->color); } @@ -463,40 +467,81 @@ void editor_render(SDL_Window *window, Free_Glyph_Atlas *atlas, Simple_Renderer sr->cam = oldCam; } - // Render input - { - if (editor->input.active) { - Simple_Camera oldCam = sr->cam; - sr->cam = (Simple_Camera) { - .pos = vec2f((float) w / 2, ((float) h / 2) - 60.0f), - .scale = 1.0f, - .scale_vel = 0.0f, - .vel = vec2f(0, 0) - }; - - { - simple_renderer_set_shader(sr, SHADER_FOR_COLOR); - Vec4f bg = vec4fs(1); - Vec2f p1 = vec2f(0, -60); - Vec2f s = vec2f(w, 120); - simple_renderer_solid_rect(sr, p1, s, bg); - simple_renderer_flush(sr); - } + // Render bottom bar + { +Simple_Camera oldCam = sr->cam; + sr->cam = (Simple_Camera) { + .pos = vec2f((float) w / 2 * oscale, ((float) h / 2 * oscale) - 60.0f * scale), + .scale = scale, + .scale_vel = 0.0f, + .vel = vec2f(0, 0) + }; - { - simple_renderer_set_shader(sr, SHADER_FOR_TEXT); - Vec4f color = vec4fs(0); - Vec2f pos = vec2f(20, -20); - free_glyph_atlas_render_line_sized(atlas, sr, - editor->input.text.items, - editor->input.text.count, - &pos, color); - simple_renderer_flush(sr); - } +{ + simple_renderer_set_shader(sr, SHADER_FOR_COLOR); + Vec4f bg = vec4fs(1); + Vec2f p1 = vec2f(0, -60.0f * scale); + Vec2f s = vec2f(w * oscale, 60.0f + 60.0f * scale); + simple_renderer_solid_rect(sr, p1, s, bg); + simple_renderer_flush(sr); + } + +float x = 20.0f * scale; - sr->cam = oldCam; + // Render input + if (editor->input.active) { + + + { + simple_renderer_set_shader(sr, SHADER_FOR_TEXT); + Vec4f color = hex_to_vec4f(0x7C7A7AFF); + Vec2f pos = vec2f(x, -20 * scale); + free_glyph_atlas_render_line_sized(atlas, sr, + editor->input.hint, + editor->input.hint_len, + &pos, color); + x = pos.x; + simple_renderer_flush(sr); + } + + { + simple_renderer_set_shader(sr, SHADER_FOR_TEXT); + Vec4f color = vec4fs(0); + Vec2f pos = vec2f(x, -20 * scale); + free_glyph_atlas_render_line_sized(atlas, sr, + editor->input.text.items, + editor->input.text.count, + &pos, color); + simple_renderer_flush(sr); } } + // Render additional info + else { + Simple_Camera oldCam = sr->cam; + sr->cam = (Simple_Camera) { + .pos = vec2f((float) w / 2 * oscale, ((float) h / 2 * oscale) - 60.0f * scale), + .scale = scale, + .scale_vel = 0.0f, + .vel = vec2f(0, 0) + }; + + static char str[200]; // TODO + sprintf(str, "%s %zu / %zu", file_ext_str(editor->file_ext), editor_cursor_row(editor) + 1, editor->lines.count); + +{ + simple_renderer_set_shader(sr, SHADER_FOR_TEXT); + Vec4f color = vec4fs(0); + Vec2f pos = vec2f(x, -20 * scale); + free_glyph_atlas_render_line_sized(atlas, sr, + str, + strlen(str), + &pos, color); + x = pos.x; + simple_renderer_flush(sr); + } + } +sr->cam = oldCam; + } // Update camera { @@ -584,6 +629,8 @@ void editor_start_search(Editor *e) } else { e->searching = true; editor_start_input(e); + e->input.hint = "find:"; + e->input.hint_len = strlen(e->input.hint); if (e->selection) { size_t begin = e->select_begin; size_t end = e->cursor; @@ -710,6 +757,7 @@ void editor_start_input(Editor *editor) editor->input.active = true; editor->input.onDone = NULL; editor->input.required = false; + editor->input.hint_len = 0; if (editor->input.text.items) { free(editor->input.text.items); editor->input.text = (String_Builder){0}; diff --git a/src/editor.h b/src/editor.h index b0c07d2e..27853189 100644 --- a/src/editor.h +++ b/src/editor.h @@ -47,6 +47,8 @@ typedef struct { bool active; bool required; void (*onDone)(struct Editor_s *); + const char *hint; + size_t hint_len; } Input; typedef struct Editor_s { @@ -58,6 +60,7 @@ typedef struct Editor_s { String_Builder file_path; PopUps popUps; Input input; + File_Extension file_ext; bool searching; diff --git a/src/lexer.c b/src/lexer.c index 2208db93..3f081ea7 100644 --- a/src/lexer.c +++ b/src/lexer.c @@ -123,6 +123,22 @@ Lexer lexer_new(Free_Glyph_Atlas *atlas, const char *content, size_t content_len return l; } +const char *file_ext_str(File_Extension ext) +{ + switch (ext) { + case FEXT_KOTLIN: + return "Kotlin"; + case FEXT_JAVA: + return "Java"; + case FEXT_CPP: + return "C++"; + case FEXT_PYTHON: + return "Python"; + default: + return "?"; + } +} + bool lexer_starts_with(Lexer *l, const char *prefix) { size_t prefix_len = strlen(prefix); diff --git a/src/lexer.h b/src/lexer.h index 3801a358..5993f3f5 100644 --- a/src/lexer.h +++ b/src/lexer.h @@ -49,6 +49,8 @@ typedef struct { File_Extension file_ext; } Lexer; +const char *file_ext_str(File_Extension ext); + Lexer lexer_new(Free_Glyph_Atlas *atlas, const char *content, size_t content_len, String_Builder file_path); Token lexer_next(Lexer *l); diff --git a/src/main.c b/src/main.c index 196413a3..5d7986e1 100644 --- a/src/main.c +++ b/src/main.c @@ -244,7 +244,6 @@ int main(int argc, char **argv) } else { editor_move_to_line_begin(&editor); } - editor.last_stroke = SDL_GetTicks(); } break; case SDLK_END: { @@ -254,7 +253,6 @@ int main(int argc, char **argv) } else { editor_move_to_line_end(&editor); } - editor.last_stroke = SDL_GetTicks(); } break; case SDLK_BACKSPACE: { @@ -264,7 +262,6 @@ int main(int argc, char **argv) } else { editor_backspace(&editor); } - editor.last_stroke = SDL_GetTicks(); } break; @@ -273,6 +270,8 @@ int main(int argc, char **argv) if (event.key.keysym.mod & KMOD_SHIFT) { editor_start_input(&editor); editor.input.onDone = onSaveInputPath; + editor.input.hint = "path:"; + editor.input.hint_len = strlen(editor.input.hint); } else if (editor.file_path.count > 0) { err = editor_save(&editor); if (err != 0) { @@ -282,6 +281,8 @@ int main(int argc, char **argv) editor_start_input(&editor); editor.input.onDone = onSaveInputPath; editor.input.required = true; + editor.input.hint = "path:"; + editor.input.hint_len = strlen(editor.input.hint); } } } @@ -313,7 +314,6 @@ int main(int argc, char **argv) editor.selection = false; } editor_insert_char(&editor, '\n'); - editor.last_stroke = SDL_GetTicks(); } } break; @@ -325,7 +325,6 @@ int main(int argc, char **argv) } else { editor_delete(&editor); } - editor.last_stroke = SDL_GetTicks(); } break; @@ -401,7 +400,6 @@ int main(int argc, char **argv) } else { editor_move_line_up(&editor); } - editor.last_stroke = SDL_GetTicks(); } break; @@ -412,7 +410,6 @@ int main(int argc, char **argv) } else { editor_move_line_down(&editor); } - editor.last_stroke = SDL_GetTicks(); } break; @@ -423,7 +420,6 @@ int main(int argc, char **argv) } else { editor_move_char_left(&editor); } - editor.last_stroke = SDL_GetTicks(); } break; @@ -434,10 +430,11 @@ int main(int argc, char **argv) } else { editor_move_char_right(&editor); } - editor.last_stroke = SDL_GetTicks(); } break; } + + editor.last_stroke = SDL_GetTicks(); } } break; diff --git a/src/minirent.h b/src/minirent.h deleted file mode 100644 index 40d3ca5a..00000000 --- a/src/minirent.h +++ /dev/null @@ -1,136 +0,0 @@ -// Copyright 2021 Alexey Kutepov -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// -// ============================================================ -// -// minirent — 0.0.1 — A subset of dirent interface for Windows. -// -// https://github.com/tsoding/minirent -// -// ============================================================ -// -// ChangeLog (https://semver.org/ is implied) -// -// 0.0.1 First Official Release - -#ifndef MINIRENT_H_ -#define MINIRENT_H_ - -#define WIN32_LEAN_AND_MEAN -#include "windows.h" - -struct dirent -{ - char d_name[MAX_PATH+1]; -}; - -typedef struct DIR DIR; - -DIR *opendir(const char *dirpath); -struct dirent *readdir(DIR *dirp); -int closedir(DIR *dirp); - -#endif // MINIRENT_H_ - -#ifdef MINIRENT_IMPLEMENTATION - -struct DIR -{ - HANDLE hFind; - WIN32_FIND_DATA data; - struct dirent *dirent; -}; - -DIR *opendir(const char *dirpath) -{ - assert(dirpath); - - char buffer[MAX_PATH]; - snprintf(buffer, MAX_PATH, "%s\\*", dirpath); - - DIR *dir = (DIR*)calloc(1, sizeof(DIR)); - - dir->hFind = FindFirstFile(buffer, &dir->data); - if (dir->hFind == INVALID_HANDLE_VALUE) { - // TODO: opendir should set errno accordingly on FindFirstFile fail - // https://docs.microsoft.com/en-us/windows/win32/api/errhandlingapi/nf-errhandlingapi-getlasterror - errno = ENOSYS; - goto fail; - } - - return dir; - -fail: - if (dir) { - free(dir); - } - - return NULL; -} - -struct dirent *readdir(DIR *dirp) -{ - assert(dirp); - - if (dirp->dirent == NULL) { - dirp->dirent = (struct dirent*)calloc(1, sizeof(struct dirent)); - } else { - if(!FindNextFile(dirp->hFind, &dirp->data)) { - if (GetLastError() != ERROR_NO_MORE_FILES) { - // TODO: readdir should set errno accordingly on FindNextFile fail - // https://docs.microsoft.com/en-us/windows/win32/api/errhandlingapi/nf-errhandlingapi-getlasterror - errno = ENOSYS; - } - - return NULL; - } - } - - memset(dirp->dirent->d_name, 0, sizeof(dirp->dirent->d_name)); - - strncpy( - dirp->dirent->d_name, - dirp->data.cFileName, - sizeof(dirp->dirent->d_name) - 1); - - return dirp->dirent; -} - -int closedir(DIR *dirp) -{ - assert(dirp); - - if(!FindClose(dirp->hFind)) { - // TODO: closedir should set errno accordingly on FindClose fail - // https://docs.microsoft.com/en-us/windows/win32/api/errhandlingapi/nf-errhandlingapi-getlasterror - errno = ENOSYS; - return -1; - } - - if (dirp->dirent) { - free(dirp->dirent); - } - free(dirp); - - return 0; -} - -#endif // MINIRENT_IMPLEMENTATION From e09621dbd9011ab91d8a43e363e530092b61a762 Mon Sep 17 00:00:00 2001 From: alex-s168 <63254202+alex-s168@users.noreply.github.com> Date: Wed, 3 Apr 2024 18:09:11 +0200 Subject: [PATCH 15/29] auto convert tabs to 4 spaces when load and append new line at save --- src/common.c | 20 +++++++++++++++----- src/main.c | 22 +++++++++++----------- 2 files changed, 26 insertions(+), 16 deletions(-) diff --git a/src/common.c b/src/common.c index 060525f7..e22b6104 100644 --- a/src/common.c +++ b/src/common.c @@ -70,6 +70,9 @@ Errno write_entire_file(const char *file_path, const char *buf, size_t buf_size) if (f == NULL) return_defer(errno); fwrite(buf, 1, buf_size, f); + if (buf[buf_size - 1] != '\n') { + fputc('\n', f); + } if (ferror(f)) return_defer(errno); defer: @@ -106,13 +109,20 @@ Errno read_entire_file(const char *file_path, String_Builder *sb) Errno err = file_size(f, &size); if (err != 0) return_defer(err); - if (sb->capacity < size) { - sb->capacity = size; - sb->items = realloc(sb->items, sb->capacity*sizeof(*sb->items)); - assert(sb->items != NULL && "Buy more RAM lol"); + int c; + while ((c = fgetc(f)) != EOF) { + if (c == '\t') { + da_append(sb, ' '); + da_append(sb, ' '); + da_append(sb, ' '); + da_append(sb, ' '); + } else if (c == '\r') { + continue; + } else { + da_append(sb, (char) c); + } } - fread(sb->items, size, 1, f); if (ferror(f)) return_defer(errno); sb->count = size; diff --git a/src/main.c b/src/main.c index 5d7986e1..dc4afd33 100644 --- a/src/main.c +++ b/src/main.c @@ -439,17 +439,17 @@ int main(int argc, char **argv) } break; - case SDL_WINDOWEVENT: { - switch(event.window.event) { - case SDL_WINDOWEVENT_RESIZED: { - int w, h; - SDL_GetWindowSize(window, &w, &h); - glViewport(0, 0, w, h); - } - break; - } - } - break; + case SDL_WINDOWEVENT: { + switch(event.window.event) { + case SDL_WINDOWEVENT_RESIZED: { + int w, h; + SDL_GetWindowSize(window, &w, &h); + glViewport(0, 0, w, h); + } + break; + } + } + break; case SDL_TEXTINPUT: { if (editor.input.active) { From c985cdd48f4be69affd2820a64b12cae423b92ff Mon Sep 17 00:00:00 2001 From: alex-s168 <63254202+alex-s168@users.noreply.github.com> Date: Wed, 3 Apr 2024 18:09:24 +0200 Subject: [PATCH 16/29] fix indenting --- src/editor.c | 126 +++++++++++++++++++++++++-------------------------- 1 file changed, 61 insertions(+), 65 deletions(-) diff --git a/src/editor.c b/src/editor.c index 02bb94af..dd8bde8f 100644 --- a/src/editor.c +++ b/src/editor.c @@ -422,8 +422,8 @@ void editor_render(SDL_Window *window, Free_Glyph_Atlas *atlas, Simple_Renderer simple_renderer_flush(sr); } - float scale = 0.6f; - float oscale = 1.0f / scale; + float scale = 0.6f; + float oscale = 1.0f / scale; // Render pop-ups { @@ -467,9 +467,9 @@ void editor_render(SDL_Window *window, Free_Glyph_Atlas *atlas, Simple_Renderer sr->cam = oldCam; } - // Render bottom bar - { -Simple_Camera oldCam = sr->cam; + // Render bottom bar + { + Simple_Camera oldCam = sr->cam; sr->cam = (Simple_Camera) { .pos = vec2f((float) w / 2 * oscale, ((float) h / 2 * oscale) - 60.0f * scale), .scale = scale, @@ -477,7 +477,7 @@ Simple_Camera oldCam = sr->cam; .vel = vec2f(0, 0) }; -{ + { simple_renderer_set_shader(sr, SHADER_FOR_COLOR); Vec4f bg = vec4fs(1); Vec2f p1 = vec2f(0, -60.0f * scale); @@ -486,62 +486,61 @@ Simple_Camera oldCam = sr->cam; simple_renderer_flush(sr); } -float x = 20.0f * scale; - - // Render input - if (editor->input.active) { - + float x = 20.0f * scale; + + // Render input + if (editor->input.active) { + { + simple_renderer_set_shader(sr, SHADER_FOR_TEXT); + Vec4f color = hex_to_vec4f(0x7C7A7AFF); + Vec2f pos = vec2f(x, -20 * scale); + free_glyph_atlas_render_line_sized(atlas, sr, + editor->input.hint, + editor->input.hint_len, + &pos, color); + x = pos.x; + simple_renderer_flush(sr); + } - { - simple_renderer_set_shader(sr, SHADER_FOR_TEXT); - Vec4f color = hex_to_vec4f(0x7C7A7AFF); - Vec2f pos = vec2f(x, -20 * scale); - free_glyph_atlas_render_line_sized(atlas, sr, - editor->input.hint, - editor->input.hint_len, - &pos, color); - x = pos.x; - simple_renderer_flush(sr); + { + simple_renderer_set_shader(sr, SHADER_FOR_TEXT); + Vec4f color = vec4fs(0); + Vec2f pos = vec2f(x, -20 * scale); + free_glyph_atlas_render_line_sized(atlas, sr, + editor->input.text.items, + editor->input.text.count, + &pos, color); + simple_renderer_flush(sr); + } } - - { - simple_renderer_set_shader(sr, SHADER_FOR_TEXT); - Vec4f color = vec4fs(0); - Vec2f pos = vec2f(x, -20 * scale); - free_glyph_atlas_render_line_sized(atlas, sr, - editor->input.text.items, - editor->input.text.count, - &pos, color); - simple_renderer_flush(sr); + // Render additional info + else { + Simple_Camera oldCam = sr->cam; + sr->cam = (Simple_Camera) { + .pos = vec2f((float) w / 2 * oscale, ((float) h / 2 * oscale) - 60.0f * scale), + .scale = scale, + .scale_vel = 0.0f, + .vel = vec2f(0, 0) + }; + + static char str[200]; // TODO + sprintf(str, "%s %zu / %zu", file_ext_str(editor->file_ext), editor_cursor_row(editor) + 1, editor->lines.count); + + { + simple_renderer_set_shader(sr, SHADER_FOR_TEXT); + Vec4f color = vec4fs(0); + Vec2f pos = vec2f(x, -20 * scale); + free_glyph_atlas_render_line_sized(atlas, sr, + str, + strlen(str), + &pos, color); + x = pos.x; + simple_renderer_flush(sr); + } } - } - // Render additional info - else { - Simple_Camera oldCam = sr->cam; - sr->cam = (Simple_Camera) { - .pos = vec2f((float) w / 2 * oscale, ((float) h / 2 * oscale) - 60.0f * scale), - .scale = scale, - .scale_vel = 0.0f, - .vel = vec2f(0, 0) - }; - - static char str[200]; // TODO - sprintf(str, "%s %zu / %zu", file_ext_str(editor->file_ext), editor_cursor_row(editor) + 1, editor->lines.count); -{ - simple_renderer_set_shader(sr, SHADER_FOR_TEXT); - Vec4f color = vec4fs(0); - Vec2f pos = vec2f(x, -20 * scale); - free_glyph_atlas_render_line_sized(atlas, sr, - str, - strlen(str), - &pos, color); - x = pos.x; - simple_renderer_flush(sr); - } - } -sr->cam = oldCam; - } + sr->cam = oldCam; + } // Update camera { @@ -629,8 +628,8 @@ void editor_start_search(Editor *e) } else { e->searching = true; editor_start_input(e); - e->input.hint = "find:"; - e->input.hint_len = strlen(e->input.hint); + e->input.hint = "find:"; + e->input.hint_len = strlen(e->input.hint); if (e->selection) { size_t begin = e->select_begin; size_t end = e->cursor; @@ -757,9 +756,6 @@ void editor_start_input(Editor *editor) editor->input.active = true; editor->input.onDone = NULL; editor->input.required = false; - editor->input.hint_len = 0; + editor->input.hint_len = 0; if (editor->input.text.items) { - free(editor->input.text.items); - editor->input.text = (String_Builder){0}; - } -} \ No newline at end of file + free(edi From 02d0c066cbc315b9bcb4417c98e231f5f1d3c698 Mon Sep 17 00:00:00 2001 From: alex-s168 <63254202+alex-s168@users.noreply.github.com> Date: Wed, 3 Apr 2024 18:12:21 +0200 Subject: [PATCH 17/29] ded fucked up end of file for some reason --- src/editor.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/editor.c b/src/editor.c index dd8bde8f..a7780674 100644 --- a/src/editor.c +++ b/src/editor.c @@ -758,4 +758,7 @@ void editor_start_input(Editor *editor) editor->input.required = false; editor->input.hint_len = 0; if (editor->input.text.items) { - free(edi + free(editor->input.text.items); + editor->input.text = (String_Builder){0}; + } +} From 8e9ddcaba22de648c32ed4dd8fe7c8dac804fd12 Mon Sep 17 00:00:00 2001 From: "alexander.nutz" Date: Thu, 4 Apr 2024 07:23:57 +0200 Subject: [PATCH 18/29] use better colors for bottom bar and fix segfault when saving empty file --- src/common.c | 2 +- src/editor.c | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/common.c b/src/common.c index e22b6104..2b634151 100644 --- a/src/common.c +++ b/src/common.c @@ -70,7 +70,7 @@ Errno write_entire_file(const char *file_path, const char *buf, size_t buf_size) if (f == NULL) return_defer(errno); fwrite(buf, 1, buf_size, f); - if (buf[buf_size - 1] != '\n') { + if (buf_size != 0 && buf[buf_size - 1] != '\n') { fputc('\n', f); } if (ferror(f)) return_defer(errno); diff --git a/src/editor.c b/src/editor.c index a7780674..c26cc0d6 100644 --- a/src/editor.c +++ b/src/editor.c @@ -92,7 +92,7 @@ Errno editor_save_as(Editor *e, const char *file_path) PopUp p; p.msg = "Saved file!"; p.msg_size = strlen(p.msg); - p.color = hex_to_vec4f(0x7DDA58FF); + p.color = hex_to_vec4f(0x009966FF); p.when = SDL_GetTicks(); p.lasts = 1000; (void) editor_add_popup(e, &p); @@ -109,7 +109,7 @@ Errno editor_save(Editor *e) PopUp p; p.msg = "Saved file!"; p.msg_size = strlen(p.msg); - p.color = hex_to_vec4f(0x7DDA58FF); + p.color = hex_to_vec4f(0x009966FF); p.when = SDL_GetTicks(); p.lasts = 1000; (void) editor_add_popup(e, &p); @@ -479,7 +479,7 @@ void editor_render(SDL_Window *window, Free_Glyph_Atlas *atlas, Simple_Renderer { simple_renderer_set_shader(sr, SHADER_FOR_COLOR); - Vec4f bg = vec4fs(1); + Vec4f bg = hex_to_vec4f(0xe7e7e7ff); Vec2f p1 = vec2f(0, -60.0f * scale); Vec2f s = vec2f(w * oscale, 60.0f + 60.0f * scale); simple_renderer_solid_rect(sr, p1, s, bg); @@ -492,7 +492,7 @@ void editor_render(SDL_Window *window, Free_Glyph_Atlas *atlas, Simple_Renderer if (editor->input.active) { { simple_renderer_set_shader(sr, SHADER_FOR_TEXT); - Vec4f color = hex_to_vec4f(0x7C7A7AFF); + Vec4f color = hex_to_vec4f(0x5b5b5bFF); Vec2f pos = vec2f(x, -20 * scale); free_glyph_atlas_render_line_sized(atlas, sr, editor->input.hint, @@ -504,7 +504,7 @@ void editor_render(SDL_Window *window, Free_Glyph_Atlas *atlas, Simple_Renderer { simple_renderer_set_shader(sr, SHADER_FOR_TEXT); - Vec4f color = vec4fs(0); + Vec4f color = hex_to_vec4f(0x181818FF); Vec2f pos = vec2f(x, -20 * scale); free_glyph_atlas_render_line_sized(atlas, sr, editor->input.text.items, @@ -523,12 +523,12 @@ void editor_render(SDL_Window *window, Free_Glyph_Atlas *atlas, Simple_Renderer .vel = vec2f(0, 0) }; - static char str[200]; // TODO + static char str[200]; sprintf(str, "%s %zu / %zu", file_ext_str(editor->file_ext), editor_cursor_row(editor) + 1, editor->lines.count); { simple_renderer_set_shader(sr, SHADER_FOR_TEXT); - Vec4f color = vec4fs(0); + Vec4f color = hex_to_vec4f(0x181818FF); Vec2f pos = vec2f(x, -20 * scale); free_glyph_atlas_render_line_sized(atlas, sr, str, @@ -744,7 +744,7 @@ void flash_error_str(Editor *editor, const char *str) PopUp p; p.msg = str; p.msg_size = strlen(str); - p.color = hex_to_vec4f(0xD20103FF); + p.color = hex_to_vec4f(0xff2400ff); p.when = SDL_GetTicks(); p.lasts = 2000; From e89add029057f1ba53499ec66a02b3834e7830fe Mon Sep 17 00:00:00 2001 From: "alexander.nutz" Date: Thu, 4 Apr 2024 07:36:48 +0200 Subject: [PATCH 19/29] fix some file loading shit --- src/common.c | 5 +++-- src/common.h | 4 +++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/common.c b/src/common.c index 2b634151..a944103f 100644 --- a/src/common.c +++ b/src/common.c @@ -70,7 +70,7 @@ Errno write_entire_file(const char *file_path, const char *buf, size_t buf_size) if (f == NULL) return_defer(errno); fwrite(buf, 1, buf_size, f); - if (buf_size != 0 && buf[buf_size - 1] != '\n') { + if (get_last(buf, buf_size) != '\n') { fputc('\n', f); } if (ferror(f)) return_defer(errno); @@ -103,6 +103,7 @@ Errno read_entire_file(const char *file_path, String_Builder *sb) if (f == NULL) return_defer(errno); fclose(f); f = fopen(file_path, "rb"); + if (f == NULL) return_defer(errno); } size_t size; @@ -110,7 +111,7 @@ Errno read_entire_file(const char *file_path, String_Builder *sb) if (err != 0) return_defer(err); int c; - while ((c = fgetc(f)) != EOF) { + while ((c = fgetc(f)) != EOF && c != '\0') { if (c == '\t') { da_append(sb, ' '); da_append(sb, ' '); diff --git a/src/common.h b/src/common.h index 761b093f..e1148cc3 100644 --- a/src/common.h +++ b/src/common.h @@ -88,7 +88,9 @@ typedef struct { size_t n = strlen(s); \ da_append_many(sb, s, n); \ } while (0) -#define sb_append_null(sb) da_append_many(sb, "", 1) +#define get_last(items, count) ((count) > 0 ? (items)[(count) - 1] : '\0') +#define sb_get_last(sb) get_last((sb).items, (sb).count) +#define sb_append_null(sb) da_append_many((sb), "", 1) #define sb_to_sv(sb) sv_from_parts((sb).items, (sb).count) From 51768eff81c116c55c50d43794b7f7bf99e58f09 Mon Sep 17 00:00:00 2001 From: "alexander.nutz" Date: Thu, 4 Apr 2024 07:43:26 +0200 Subject: [PATCH 20/29] "fix" file writing --- src/common.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/common.c b/src/common.c index a944103f..746f82d2 100644 --- a/src/common.c +++ b/src/common.c @@ -69,8 +69,11 @@ Errno write_entire_file(const char *file_path, const char *buf, size_t buf_size) f = fopen(file_path, "wb"); if (f == NULL) return_defer(errno); - fwrite(buf, 1, buf_size, f); - if (get_last(buf, buf_size) != '\n') { + // TODO: why are there extra nulls at the end of the buf (buf_size)?? + // that's why we strlen it here to get the size without these nulls + size_t real_buf_size = strlen(buf); + fwrite(buf, 1, real_buf_size, f); + if (get_last(buf, real_buf_size) != '\n') { fputc('\n', f); } if (ferror(f)) return_defer(errno); From db27eea4d03bba488ed21cd0308fdc31adcf6df6 Mon Sep 17 00:00:00 2001 From: "alexander.nutz" Date: Thu, 4 Apr 2024 08:02:44 +0200 Subject: [PATCH 21/29] ctrl+g = goto line(:col) --- src/editor.c | 18 +++++++++++++++++- src/editor.h | 1 + src/main.c | 39 +++++++++++++++++++++++++++++++++++++-- 3 files changed, 55 insertions(+), 3 deletions(-) diff --git a/src/editor.c b/src/editor.c index c26cc0d6..877c386e 100644 --- a/src/editor.c +++ b/src/editor.c @@ -148,6 +148,22 @@ size_t editor_cursor_row(const Editor *e) return e->lines.count - 1; } +void editor_goto(Editor *e, size_t line, size_t col) +{ + if (e->lines.count == 0) + line = 0; + else if (line >= e->lines.count) + line = e->lines.count - 1; + + Line target_line = e->lines.items[line]; + size_t target_line_size = target_line.end - target_line.begin; + if (target_line_size == 0) + col = 0; + else if (col >= target_line_size) + col = target_line_size - 1; + e->cursor = target_line.begin + col; +} + void editor_move_line_up(Editor *e) { editor_stop_search(e); @@ -628,7 +644,7 @@ void editor_start_search(Editor *e) } else { e->searching = true; editor_start_input(e); - e->input.hint = "find:"; + e->input.hint = "find: "; e->input.hint_len = strlen(e->input.hint); if (e->selection) { size_t begin = e->select_begin; diff --git a/src/editor.h b/src/editor.h index 27853189..235dc0aa 100644 --- a/src/editor.h +++ b/src/editor.h @@ -87,6 +87,7 @@ void editor_delete(Editor *editor); void editor_delete_selection(Editor *editor); size_t editor_cursor_row(const Editor *e); +void editor_goto(Editor *e, size_t line, size_t col); void editor_move_line_up(Editor *e); void editor_move_line_down(Editor *e); void editor_move_char_left(Editor *e); diff --git a/src/main.c b/src/main.c index dc4afd33..1552c3b6 100644 --- a/src/main.c +++ b/src/main.c @@ -58,6 +58,31 @@ static void onSaveInputPath(Editor *e) editor_save_as(e, e->input.text.items); } +static void onGoToLine(Editor *e) +{ + sb_append_null(&e->input.text); + const char *linestr = e->input.text.items; + char *end; + size_t linenum = strtol(linestr, &end, 10); + size_t colnum = 0; + if (*end == ':') { + char *end2; + colnum = strtol(end + 1, &end2, 10); + if (*end2 != '\0') + goto invalid_input; + } else if (*end != '\0') + goto invalid_input; + if (linenum == 0) + linenum = 1; + if (colnum == 0) + colnum = 1; + editor_goto(e, linenum - 1, colnum - 1); + return; + +invalid_input: + flash_error(e, "Invalid line(:col)!"); +} + int main(int argc, char **argv) { Errno err; @@ -270,7 +295,7 @@ int main(int argc, char **argv) if (event.key.keysym.mod & KMOD_SHIFT) { editor_start_input(&editor); editor.input.onDone = onSaveInputPath; - editor.input.hint = "path:"; + editor.input.hint = "path: "; editor.input.hint_len = strlen(editor.input.hint); } else if (editor.file_path.count > 0) { err = editor_save(&editor); @@ -281,7 +306,7 @@ int main(int argc, char **argv) editor_start_input(&editor); editor.input.onDone = onSaveInputPath; editor.input.required = true; - editor.input.hint = "path:"; + editor.input.hint = "path: "; editor.input.hint_len = strlen(editor.input.hint); } } @@ -393,6 +418,16 @@ int main(int argc, char **argv) } break; + case SDLK_g: { + if (event.key.keysym.mod & KMOD_CTRL) { + editor_start_input(&editor); + editor.input.onDone = onGoToLine; + editor.input.hint = "line: "; + editor.input.hint_len = strlen(editor.input.hint); + } + } + break; + case SDLK_UP: { editor_update_selection(&editor, event.key.keysym.mod & KMOD_SHIFT); if (event.key.keysym.mod & KMOD_CTRL) { From 90b73771657e3db129533a25e9c8bba073b24dde Mon Sep 17 00:00:00 2001 From: alex-s168 <63254202+alex-s168@users.noreply.github.com> Date: Thu, 4 Apr 2024 22:33:02 +0200 Subject: [PATCH 22/29] fix building and add miniconf --- .gitmodules | 3 +++ build.sh | 2 +- build_msvc.bat | 2 +- build_msys2_mingw64.sh | 4 ++-- dependencies/miniconf | 1 + src/common.c | 2 +- src/editor.c | 8 -------- 7 files changed, 9 insertions(+), 13 deletions(-) create mode 160000 dependencies/miniconf diff --git a/.gitmodules b/.gitmodules index 1ef3fa5a..1ec5a3b5 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,6 @@ [submodule "dependencies/freetype"] path = dependencies/freetype url = https://github.com/ubawurinna/freetype-windows-binaries.git +[submodule "dependencies/miniconf"] + path = dependencies/miniconf + url = https://github.com/alex-s168/miniconf.git diff --git a/build.sh b/build.sh index ff6f278a..55fe6db4 100755 --- a/build.sh +++ b/build.sh @@ -4,7 +4,7 @@ set -xe CC="${CXX:-cc}" PKGS="sdl2 glew freetype2" -CFLAGS="-Wall -Wextra -std=c11 -pedantic -ggdb" +CFLAGS="-Wall -Wextra -std=c11 -pedantic -ggdb -Idependencies/miniconf -Idependencies/minirent" LIBS=-lm SRC="src/main.c src/la.c src/editor.c src/file_browser.c src/free_glyph.c src/simple_renderer.c src/common.c src/lexer.c" diff --git a/build_msvc.bat b/build_msvc.bat index 89481702..53acde7b 100644 --- a/build_msvc.bat +++ b/build_msvc.bat @@ -2,7 +2,7 @@ rem launch this from msvc-enabled console set CFLAGS=/W4 /std:c11 /wd4996 /wd5105 /FC /TC /Zi /nologo -set INCLUDES=/I dependencies\SDL2\include /I dependencies\freetype\include /I dependencies\minirent /I dependencies\GLFW\include /I dependencies\GLEW\include +set INCLUDES=/I dependencies\SDL2\include /I dependencies\freetype\include /I dependencies\minirent /I dependencies\GLFW\include /I dependencies\GLEW\include /I dependencies\miniconf set LIBS=dependencies\SDL2\lib\x64\SDL2.lib ^ dependencies\SDL2\lib\x64\SDL2main.lib ^ dependencies\GLFW\lib\glfw3.lib ^ diff --git a/build_msys2_mingw64.sh b/build_msys2_mingw64.sh index aa156fc9..d704fa73 100644 --- a/build_msys2_mingw64.sh +++ b/build_msys2_mingw64.sh @@ -3,9 +3,9 @@ set -xe PKGS="--static sdl2 glew freetype2" -CFLAGS="-Wall -Wextra -pedantic -ggdb -DGLEW_STATIC `pkg-config --cflags $PKGS` -Isrc -Dassert(expression)=((void)0) " +CFLAGS="-Wall -Wextra -pedantic -ggdb -DGLEW_STATIC `pkg-config --cflags $PKGS` -Isrc -Idependencies/miniconf -Idependencies/minirent -Dassert(expression)=((void)0) " LIBS="-lm -lopengl32 `pkg-config --libs $PKGS`" -SRC="src/main.c src/la.c src/editor.c src/file_browser.c src/free_glyph.c src/simple_renderer.c src/common.c" +SRC="src/main.c src/la.c src/editor.c src/file_browser.c src/free_glyph.c src/simple_renderer.c src/common.c src/lexer.c"" OBJ=$(echo "$SRC" | sed "s/\.c/\.o/g") OBJ=$(echo "$OBJ" | sed "s/src\// /g") diff --git a/dependencies/miniconf b/dependencies/miniconf new file mode 160000 index 00000000..72a23f34 --- /dev/null +++ b/dependencies/miniconf @@ -0,0 +1 @@ +Subproject commit 72a23f34675aa529c39ea4952eba1a501f2e1768 diff --git a/src/common.c b/src/common.c index e22b6104..56dee517 100644 --- a/src/common.c +++ b/src/common.c @@ -99,7 +99,7 @@ Errno read_entire_file(const char *file_path, String_Builder *sb) f = fopen(file_path, "rb"); if (f == NULL) { - f = fopen(file_path, "w"); + f = fopen(file_path, "wb"); if (f == NULL) return_defer(errno); fclose(f); f = fopen(file_path, "rb"); diff --git a/src/editor.c b/src/editor.c index a7780674..226aa828 100644 --- a/src/editor.c +++ b/src/editor.c @@ -515,14 +515,6 @@ void editor_render(SDL_Window *window, Free_Glyph_Atlas *atlas, Simple_Renderer } // Render additional info else { - Simple_Camera oldCam = sr->cam; - sr->cam = (Simple_Camera) { - .pos = vec2f((float) w / 2 * oscale, ((float) h / 2 * oscale) - 60.0f * scale), - .scale = scale, - .scale_vel = 0.0f, - .vel = vec2f(0, 0) - }; - static char str[200]; // TODO sprintf(str, "%s %zu / %zu", file_ext_str(editor->file_ext), editor_cursor_row(editor) + 1, editor->lines.count); From 3f91efbc59a64b3dd4af515306eb6faef4351722 Mon Sep 17 00:00:00 2001 From: "alexander.nutz" Date: Fri, 5 Apr 2024 16:24:00 +0200 Subject: [PATCH 23/29] fix file write and read? --- src/common.c | 30 ++++++++++++------------------ src/main.c | 5 ----- 2 files changed, 12 insertions(+), 23 deletions(-) diff --git a/src/common.c b/src/common.c index 4ae67dd1..e292ec35 100644 --- a/src/common.c +++ b/src/common.c @@ -18,6 +18,8 @@ #include "./arena.h" #define SV_IMPLEMENTATION #include "sv.h" +#define FILELIB_IMPL +#include static Arena temporary_arena = {0}; @@ -72,6 +74,8 @@ Errno write_entire_file(const char *file_path, const char *buf, size_t buf_size) // TODO: why are there extra nulls at the end of the buf (buf_size)?? // that's why we strlen it here to get the size without these nulls size_t real_buf_size = strlen(buf); + if (buf_size < real_buf_size) + real_buf_size = buf_size; fwrite(buf, 1, real_buf_size, f); if (get_last(buf, real_buf_size) != '\n') { fputc('\n', f); @@ -97,22 +101,16 @@ static Errno file_size(FILE *file, size_t *size) Errno read_entire_file(const char *file_path, String_Builder *sb) { - Errno result = 0; - FILE *f = NULL; - - f = fopen(file_path, "rb"); + FILE *f = fopen(file_path, "r"); + // TODO: bruh if (f == NULL) { - f = fopen(file_path, "wb"); - if (f == NULL) return_defer(errno); + f = fopen(file_path, "w"); + if (f == NULL) return 1; fclose(f); - f = fopen(file_path, "rb"); - if (f == NULL) return_defer(errno); + f = fopen(file_path, "r"); + if (f == NULL) return 1; } - size_t size; - Errno err = file_size(f, &size); - if (err != 0) return_defer(err); - int c; while ((c = fgetc(f)) != EOF && c != '\0') { if (c == '\t') { @@ -127,12 +125,8 @@ Errno read_entire_file(const char *file_path, String_Builder *sb) } } - if (ferror(f)) return_defer(errno); - sb->count = size; - -defer: - if (f) fclose(f); - return result; + fclose(f); + return 0; } Vec4f hex_to_vec4f(uint32_t color) diff --git a/src/main.c b/src/main.c index 1552c3b6..4947e55f 100644 --- a/src/main.c +++ b/src/main.c @@ -22,12 +22,7 @@ #include "./lexer.h" #include "./sv.h" -// TODO: Save file dialog -// Needed when ded is ran without any file so it does not know where to save. - // TODO: An ability to create a new file -// TODO: Delete a word -// TODO: Delete selection // TODO: Undo/redo system void MessageCallback(GLenum source, From 8f6d65f69ee4ed4c4496130d9fe11929c06194c1 Mon Sep 17 00:00:00 2001 From: alex-s168 <63254202+alex-s168@users.noreply.github.com> Date: Fri, 5 Apr 2024 20:01:23 +0200 Subject: [PATCH 24/29] add minifmt; update miniconf; other small changes --- .gitmodules | 3 +++ build.sh | 2 +- build_msvc.bat | 2 +- build_msys2_mingw64.sh | 4 ++-- ded.miniconf | 0 default.miniconf | 0 dependencies/miniconf | 2 +- dependencies/minifmt | 1 + src/common.c | 27 ++++++--------------------- src/editor.c | 14 ++++++++++++++ src/editor.h | 9 +++++++++ src/main.c | 6 ++++++ 12 files changed, 44 insertions(+), 26 deletions(-) create mode 100644 ded.miniconf create mode 100644 default.miniconf create mode 160000 dependencies/minifmt diff --git a/.gitmodules b/.gitmodules index 1ec5a3b5..66709e6f 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,3 +7,6 @@ [submodule "dependencies/miniconf"] path = dependencies/miniconf url = https://github.com/alex-s168/miniconf.git +[submodule "dependencies/minifmt"] + path = dependencies/minifmt + url = https://github.com/alex-s168/minifmt.git diff --git a/build.sh b/build.sh index 55fe6db4..e0c07ec9 100755 --- a/build.sh +++ b/build.sh @@ -4,7 +4,7 @@ set -xe CC="${CXX:-cc}" PKGS="sdl2 glew freetype2" -CFLAGS="-Wall -Wextra -std=c11 -pedantic -ggdb -Idependencies/miniconf -Idependencies/minirent" +CFLAGS="-std=c11 -pedantic -ggdb -Idependencies/miniconf -Idependencies/minirent -Idependencies/minifmt" LIBS=-lm SRC="src/main.c src/la.c src/editor.c src/file_browser.c src/free_glyph.c src/simple_renderer.c src/common.c src/lexer.c" diff --git a/build_msvc.bat b/build_msvc.bat index 53acde7b..3150aee4 100644 --- a/build_msvc.bat +++ b/build_msvc.bat @@ -2,7 +2,7 @@ rem launch this from msvc-enabled console set CFLAGS=/W4 /std:c11 /wd4996 /wd5105 /FC /TC /Zi /nologo -set INCLUDES=/I dependencies\SDL2\include /I dependencies\freetype\include /I dependencies\minirent /I dependencies\GLFW\include /I dependencies\GLEW\include /I dependencies\miniconf +set INCLUDES=/I dependencies\SDL2\include /I dependencies\freetype\include /I dependencies\minirent /I dependencies\GLFW\include /I dependencies\GLEW\include /I dependencies\miniconf /I dependencies\minifmt set LIBS=dependencies\SDL2\lib\x64\SDL2.lib ^ dependencies\SDL2\lib\x64\SDL2main.lib ^ dependencies\GLFW\lib\glfw3.lib ^ diff --git a/build_msys2_mingw64.sh b/build_msys2_mingw64.sh index d704fa73..3623dfbb 100644 --- a/build_msys2_mingw64.sh +++ b/build_msys2_mingw64.sh @@ -3,9 +3,9 @@ set -xe PKGS="--static sdl2 glew freetype2" -CFLAGS="-Wall -Wextra -pedantic -ggdb -DGLEW_STATIC `pkg-config --cflags $PKGS` -Isrc -Idependencies/miniconf -Idependencies/minirent -Dassert(expression)=((void)0) " +CFLAGS="-Wall -Wextra -pedantic -ggdb -DGLEW_STATIC `pkg-config --cflags $PKGS` -Isrc -Idependencies/miniconf -Idependencies/minifmt -Idependencies/minirent -Dassert(expression)=((void)0) " LIBS="-lm -lopengl32 `pkg-config --libs $PKGS`" -SRC="src/main.c src/la.c src/editor.c src/file_browser.c src/free_glyph.c src/simple_renderer.c src/common.c src/lexer.c"" +SRC="src/main.c src/la.c src/editor.c src/file_browser.c src/free_glyph.c src/simple_renderer.c src/common.c src/lexer.c" OBJ=$(echo "$SRC" | sed "s/\.c/\.o/g") OBJ=$(echo "$OBJ" | sed "s/src\// /g") diff --git a/ded.miniconf b/ded.miniconf new file mode 100644 index 00000000..e69de29b diff --git a/default.miniconf b/default.miniconf new file mode 100644 index 00000000..e69de29b diff --git a/dependencies/miniconf b/dependencies/miniconf index 72a23f34..c8292d6d 160000 --- a/dependencies/miniconf +++ b/dependencies/miniconf @@ -1 +1 @@ -Subproject commit 72a23f34675aa529c39ea4952eba1a501f2e1768 +Subproject commit c8292d6d561bea49e606ef5414a1a6967ac331d0 diff --git a/dependencies/minifmt b/dependencies/minifmt new file mode 160000 index 00000000..6c41994f --- /dev/null +++ b/dependencies/minifmt @@ -0,0 +1 @@ +Subproject commit 6c41994fbff5cf428e06e1f170142921bfac088a diff --git a/src/common.c b/src/common.c index e292ec35..74d8f3fe 100644 --- a/src/common.c +++ b/src/common.c @@ -20,6 +20,9 @@ #include "sv.h" #define FILELIB_IMPL #include +#define MINICONF_IMPL +#include +#include static Arena temporary_arena = {0}; @@ -87,29 +90,11 @@ Errno write_entire_file(const char *file_path, const char *buf, size_t buf_size) return result; } -static Errno file_size(FILE *file, size_t *size) -{ - long saved = ftell(file); - if (saved < 0) return errno; - if (fseek(file, 0, SEEK_END) < 0) return errno; - long result = ftell(file); - if (result < 0) return errno; - if (fseek(file, saved, SEEK_SET) < 0) return errno; - *size = (size_t) result; - return 0; -} - Errno read_entire_file(const char *file_path, String_Builder *sb) { - FILE *f = fopen(file_path, "r"); - // TODO: bruh - if (f == NULL) { - f = fopen(file_path, "w"); - if (f == NULL) return 1; - fclose(f); - f = fopen(file_path, "r"); - if (f == NULL) return 1; - } + FILE *f = fopen(file_path, "r+"); + if (f == NULL) + return 1; int c; while ((c = fgetc(f)) != EOF && c != '\0') { diff --git a/src/editor.c b/src/editor.c index 5866da82..67967b82 100644 --- a/src/editor.c +++ b/src/editor.c @@ -770,3 +770,17 @@ void editor_start_input(Editor *editor) editor->input.text = (String_Builder){0}; } } + +bool editor_load_config(Editor *editor, const char *config_path) { + config_destroy(&editor->configs.cfg); + config_init(&editor->configs.cfg); + allocgroup_free(editor->configs.alloc); + + FILE *file = fopen(config_path, "r"); + if (file == NULL) + return false; + editor->configs.alloc = config_add_file(&editor->configs.cfg, file); + fclose(file); + + return true; +} \ No newline at end of file diff --git a/src/editor.h b/src/editor.h index 235dc0aa..c33e38b4 100644 --- a/src/editor.h +++ b/src/editor.h @@ -1,6 +1,7 @@ #ifndef EDITOR_H_ #define EDITOR_H_ +#include #include #include "common.h" #include "free_glyph.h" @@ -8,6 +9,7 @@ #include "lexer.h" #include +#include typedef struct { size_t begin; @@ -71,8 +73,15 @@ typedef struct Editor_s { Uint32 last_stroke; String_Builder clipboard; + + struct { + Config cfg; + AllocGroup alloc; + } configs; } Editor; +bool editor_load_config(Editor *editor, const char *config_path); + Errno editor_save_as(Editor *editor, const char *file_path); Errno editor_save(Editor *editor); Errno editor_load_from_file(Editor *editor, const char *file_path); diff --git a/src/main.c b/src/main.c index 4947e55f..4d33bd6a 100644 --- a/src/main.c +++ b/src/main.c @@ -90,6 +90,12 @@ int main(int argc, char **argv) return 1; } + const char *const conf_file_path = "ded.miniconf"; + if (!editor_load_config(&editor, conf_file_path)) { + fprintf(stderr, "ERROR: Could not read config file \"%s\"\n", conf_file_path); + return 1; + } + // TODO: users should be able to customize the font // const char *const font_file_path = "./fonts/VictorMono-Regular.ttf"; const char *const font_file_path = "./fonts/iosevka-regular.ttf"; From 14d625aaa8b1891d71bcf2e02194ad56c17d9670 Mon Sep 17 00:00:00 2001 From: alex-s168 <63254202+alex-s168@users.noreply.github.com> Date: Fri, 5 Apr 2024 21:52:10 +0200 Subject: [PATCH 25/29] config window, pop up, pop ups, input hints --- ded.miniconf | 70 +++++++++++++++ default.miniconf | 70 +++++++++++++++ src/common.c | 8 +- src/common.h | 2 - src/editor.c | 226 ++++++++++++++++++++++++++++++++++++++++++----- src/editor.h | 68 +++++++++++++- src/lexer.h | 2 + src/main.c | 59 +++++++++---- 8 files changed, 459 insertions(+), 46 deletions(-) diff --git a/ded.miniconf b/ded.miniconf index e69de29b..e5b30da4 100644 --- a/ded.miniconf +++ b/ded.miniconf @@ -0,0 +1,70 @@ +window: + font: "./fonts/iosevka-regular.ttf + title: "ded + x: 100 + y: 100 + w: 800 + h: 600 + +pop up: + scale: 0.6 + fade in: 300 + +pop ups: + save ok: + str: "Saved file! + color: x009966FF + last: 1000 + invalid goto: + str: "Invalid line(:col) + color: xFF2400FF + last: 2000 + can not cd: + str: "Can't change directory to "{path}"! + color: xFF2400FF + last: 2000 + can not open: + str: "Can't open file "{path}"! + color: xFF2400FF + last: 2000 + can not save: + str: "Can't save file: {err}! + color: xFF2400FF + last: 2000 + +input hints: + find: "find + save as: "path + save: "path + goto: "line + +editor: + background: x181818FF + bottom bar: + scale: 0.6 + background: xE7E7E7FF + input: + hint: + color: x5B5B5BFF + fmt: "({hint}) {msg} + content: + color: x181818FF + stats: + enabled: true + fmt: "{lang} {line}/{col} + color: x181818FF + tokens: + preproc: x95A99FFF + keyword: xFFDD33FF + comment: xCC8C3CFF + string: x73C936FF + cursor: + blink: + treshold: 500 + period: 1000 + color: xFFFFFFFF + width: 5.0 + selection: + color: x434343FF + search selection: + color: x191943FF \ No newline at end of file diff --git a/default.miniconf b/default.miniconf index e69de29b..e5b30da4 100644 --- a/default.miniconf +++ b/default.miniconf @@ -0,0 +1,70 @@ +window: + font: "./fonts/iosevka-regular.ttf + title: "ded + x: 100 + y: 100 + w: 800 + h: 600 + +pop up: + scale: 0.6 + fade in: 300 + +pop ups: + save ok: + str: "Saved file! + color: x009966FF + last: 1000 + invalid goto: + str: "Invalid line(:col) + color: xFF2400FF + last: 2000 + can not cd: + str: "Can't change directory to "{path}"! + color: xFF2400FF + last: 2000 + can not open: + str: "Can't open file "{path}"! + color: xFF2400FF + last: 2000 + can not save: + str: "Can't save file: {err}! + color: xFF2400FF + last: 2000 + +input hints: + find: "find + save as: "path + save: "path + goto: "line + +editor: + background: x181818FF + bottom bar: + scale: 0.6 + background: xE7E7E7FF + input: + hint: + color: x5B5B5BFF + fmt: "({hint}) {msg} + content: + color: x181818FF + stats: + enabled: true + fmt: "{lang} {line}/{col} + color: x181818FF + tokens: + preproc: x95A99FFF + keyword: xFFDD33FF + comment: xCC8C3CFF + string: x73C936FF + cursor: + blink: + treshold: 500 + period: 1000 + color: xFFFFFFFF + width: 5.0 + selection: + color: x434343FF + search selection: + color: x191943FF \ No newline at end of file diff --git a/src/common.c b/src/common.c index 74d8f3fe..d8682886 100644 --- a/src/common.c +++ b/src/common.c @@ -22,7 +22,8 @@ #include #define MINICONF_IMPL #include -#include +#define MINIFMT_IMPL +#include static Arena temporary_arena = {0}; @@ -69,9 +70,10 @@ Errno read_entire_dir(const char *dir_path, Files *files) Errno write_entire_file(const char *file_path, const char *buf, size_t buf_size) { Errno result = 0; - FILE *f = NULL; + if (buf == NULL) + return result; - f = fopen(file_path, "wb"); + FILE *f = fopen(file_path, "wb"); if (f == NULL) return_defer(errno); // TODO: why are there extra nulls at the end of the buf (buf_size)?? diff --git a/src/common.h b/src/common.h index e1148cc3..1ac08a5a 100644 --- a/src/common.h +++ b/src/common.h @@ -6,8 +6,6 @@ #include #include "./la.h" -#define SCREEN_WIDTH 800 -#define SCREEN_HEIGHT 600 #define FPS 60 #define DELTA_TIME (1.0f / FPS) #define CURSOR_OFFSET 0.13f diff --git a/src/editor.c b/src/editor.c index 67967b82..8a5fcb70 100644 --- a/src/editor.c +++ b/src/editor.c @@ -77,9 +77,6 @@ void editor_delete_selection(Editor *e) editor_retokenize(e); } -// TODO: make sure that you always have new line at the end of the file while saving -// https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_206 - Errno editor_save_as(Editor *e, const char *file_path) { printf("Saving as %s...\n", file_path); @@ -89,13 +86,12 @@ Errno editor_save_as(Editor *e, const char *file_path) sb_append_cstr(&e->file_path, file_path); sb_append_null(&e->file_path); - PopUp p; - p.msg = "Saved file!"; - p.msg_size = strlen(p.msg); - p.color = hex_to_vec4f(0x009966FF); - p.when = SDL_GetTicks(); - p.lasts = 1000; - (void) editor_add_popup(e, &p); + editor_configured_popup(e, "save ok", (PlaceholderList) { + .elems = (Placeholder[]) { + PLACEHOLDER_STR("path", file_path), + }, + .elems_size = 1 + }); return 0; } @@ -106,13 +102,12 @@ Errno editor_save(Editor *e) printf("Saving as %s...\n", e->file_path.items); Errno err = write_entire_file(e->file_path.items, e->data.items, e->data.count); if (err == 0) { - PopUp p; - p.msg = "Saved file!"; - p.msg_size = strlen(p.msg); - p.color = hex_to_vec4f(0x009966FF); - p.when = SDL_GetTicks(); - p.lasts = 1000; - (void) editor_add_popup(e, &p); + editor_configured_popup(e, "save ok", (PlaceholderList) { + .elems = (Placeholder[]) { + PLACEHOLDER_STR("path", e->file_path.items), + }, + .elems_size = 1 + }); } return err; } @@ -438,11 +433,11 @@ void editor_render(SDL_Window *window, Free_Glyph_Atlas *atlas, Simple_Renderer simple_renderer_flush(sr); } - float scale = 0.6f; - float oscale = 1.0f / scale; - // Render pop-ups { + float scale = (float) editor->configs.popup.scale; + float oscale = 1.0f / scale; + Simple_Camera oldCam = sr->cam; sr->cam = (Simple_Camera) { .pos = vec2f((float) w / 2 * oscale - 20.0f * scale, -((float) h / 2 * oscale) + 80.0f * scale), @@ -466,7 +461,7 @@ void editor_render(SDL_Window *window, Free_Glyph_Atlas *atlas, Simple_Renderer for (size_t i = 0; i < editor->popUps.count; i ++) { PopUp *p = &editor->popUps.items[i]; - float t = (SDL_GetTicks() - p->when) / 300.0f; + float t = (SDL_GetTicks() - p->when) / (float) editor->configs.popup.fade_in; if (t > 1) t = 1; else if (t < 0) @@ -485,6 +480,9 @@ void editor_render(SDL_Window *window, Free_Glyph_Atlas *atlas, Simple_Renderer // Render bottom bar { + float scale = 0.6f; + float oscale = 1.0f / scale; + Simple_Camera oldCam = sr->cam; sr->cam = (Simple_Camera) { .pos = vec2f((float) w / 2 * oscale, ((float) h / 2 * oscale) - 60.0f * scale), @@ -636,7 +634,7 @@ void editor_start_search(Editor *e) } else { e->searching = true; editor_start_input(e); - e->input.hint = "find: "; + e->input.hint = editor_configured_inline_hint(e, "find"); e->input.hint_len = strlen(e->input.hint); if (e->selection) { size_t begin = e->select_begin; @@ -737,6 +735,9 @@ void editor_remove_popup(Editor *editor, Uint32 uid) if (p->uid != uid) continue; + if (p->free_msg) + free((char *) p->msg); + memcpy(p, p + 1, (editor->popUps.count - i - 1) * sizeof(PopUp)); editor->popUps.count --; editor->popUps.items = realloc(editor->popUps.items, sizeof(PopUp) * editor->popUps.count); @@ -751,6 +752,7 @@ void flash_error_str(Editor *editor, const char *str) PopUp p; p.msg = str; + p.free_msg = false; p.msg_size = strlen(str); p.color = hex_to_vec4f(0xff2400ff); p.when = SDL_GetTicks(); @@ -771,6 +773,80 @@ void editor_start_input(Editor *editor) } } +void editor_configured_popup(Editor *editor, const char *type, PlaceholderList placeholders) { + Config cfg; + config_init(&cfg); + bool ok; + config_child(&cfg, editor->configs.popup_messages, type, &ok); + if (!ok) { + fprintf(stderr, "\"pop ups/%s\" not configured!\n", type); + goto err; + } + + PopUp popup; + + const char *fmt_str = config_get_str_at(cfg, "str", &ok); + if (!ok) { + fprintf(stderr, "\"pop ups/%s/str\" not configured!\n", type); + goto err; + } + + popup.color = hex_to_vec4f(config_get_long_at(cfg, "color", &ok)); + if (!ok) { + fprintf(stderr, "\"pop ups/%s/color\" not configured!\n", type); + goto err; + } + + popup.lasts = config_get_long_at(cfg, "last", &ok); + if (!ok) { + fprintf(stderr, "\"pop ups/%s/last\" not configured!\n", type); + goto err; + } + + { + size_t fmt_str_len = strlen(fmt_str); + char *fmt_str_copy = malloc(fmt_str_len + 1); + memcpy(fmt_str_copy, fmt_str, fmt_str_len + 1); + + Fmt fmt = fmt_compile(fmt_str_copy); + popup.msg = fmt_fmt_fmt(fmt, placeholders); + fmt_destroy(fmt); + + free(fmt_str_copy); + + if (popup.msg == NULL) { + fprintf(stderr, "\"pop ups/%s/str\" format error!\n", type); + goto err; + } + + popup.msg_size = strlen(popup.msg); + + popup.free_msg = true; + } + + popup.when = SDL_GetTicks(); + (void) editor_add_popup(editor, &popup); + + config_destroy(&cfg); + return; + +err: + flash_error(editor, "Config error! See program output for more info"); + config_destroy(&cfg); + return; +} + +const char *editor_configured_inline_hint(Editor *editor, const char *type) { + bool ok; + const char *str = config_get_str_at(editor->configs.input_hints, type, &ok); + if (!ok) { + fprintf(stderr, "\"input hints/%s\" not configured!\n", type); + flash_error(editor, "Config error! See program output for more info"); + return "???"; + } + return str; +} + bool editor_load_config(Editor *editor, const char *config_path) { config_destroy(&editor->configs.cfg); config_init(&editor->configs.cfg); @@ -782,5 +858,111 @@ bool editor_load_config(Editor *editor, const char *config_path) { editor->configs.alloc = config_add_file(&editor->configs.cfg, file); fclose(file); + { + Config window; + config_init(&window); + bool ok; + config_child(&window, editor->configs.cfg, "window", &ok); + if (!ok) { + fprintf(stderr, "\"window\" not found in config!"); + config_destroy(&window); + return false; + } + + editor->configs.window.font = config_get_str_at(window, "font", &ok); + if (!ok) { + fprintf(stderr, "\"window/font\" not found in config!"); + config_destroy(&window); + return false; + } + + editor->configs.window.title = config_get_str_at(window, "title", &ok); + if (!ok) { + fprintf(stderr, "\"window/title\" not found in config!"); + config_destroy(&window); + return false; + } + + editor->configs.window.x = config_get_long_at(window, "x", &ok); + if (!ok) { + fprintf(stderr, "\"window/x\" not found in config!"); + config_destroy(&window); + return false; + } + + editor->configs.window.y = config_get_long_at(window, "y", &ok); + if (!ok) { + fprintf(stderr, "\"window/y\" not found in config!"); + config_destroy(&window); + return false; + } + + editor->configs.window.w = config_get_long_at(window, "w", &ok); + if (!ok) { + fprintf(stderr, "\"window/w\" not found in config!"); + config_destroy(&window); + return false; + } + + editor->configs.window.h = config_get_long_at(window, "h", &ok); + if (!ok) { + fprintf(stderr, "\"window/h\" not found in config!"); + config_destroy(&window); + return false; + } + + config_destroy(&window); + } + + { + Config popup; + config_init(&popup); + bool ok; + config_child(&popup, editor->configs.cfg, "pop up", &ok); + if (!ok) { + fprintf(stderr, "\"pop up\" not found in config!"); + config_destroy(&popup); + return false; + } + + editor->configs.popup.scale = config_get_double_at(popup, "scale", &ok); + if (!ok) { + fprintf(stderr, "\"pop up/scale\" not found in config!"); + config_destroy(&popup); + return false; + } + + editor->configs.popup.fade_in = config_get_long_at(popup, "fade in", &ok); + if (!ok) { + fprintf(stderr, "\"pop up/fade in\" not found in config!"); + config_destroy(&popup); + return false; + } + + config_destroy(&popup); + } + + { + config_destroy(&editor->configs.popup_messages); + config_init(&editor->configs.popup_messages); + bool ok; + config_child(&editor->configs.popup_messages, editor->configs.cfg, "pop ups", &ok); + if (!ok) { + fprintf(stderr, "\"pop ups\" not found in config!"); + return false; + } + } + + { + config_destroy(&editor->configs.input_hints); + config_init(&editor->configs.input_hints); + bool ok; + config_child(&editor->configs.input_hints, editor->configs.cfg, "input hints", &ok); + if (!ok) { + fprintf(stderr, "\"input hints\" not found in config!"); + return false; + } + } + return true; } \ No newline at end of file diff --git a/src/editor.h b/src/editor.h index c33e38b4..d9631ca7 100644 --- a/src/editor.h +++ b/src/editor.h @@ -10,6 +10,7 @@ #include #include +#include typedef struct { size_t begin; @@ -32,6 +33,7 @@ typedef struct { Uint32 uid; const char *msg; size_t msg_size; + bool free_msg; Vec4f color; Uint32 when; Uint32 lasts; @@ -53,6 +55,65 @@ typedef struct { size_t hint_len; } Input; +typedef struct { + Config cfg; + AllocGroup alloc; + + struct { + const char *font; + const char *title; + size_t x; + size_t y; + size_t w; + size_t h; + } window; + + struct { + double scale; + long fade_in; + } popup; + + Config popup_messages; + + Config input_hints; + + struct { + Vec4f background; + + struct { + double scale; + Vec4f background; + + struct { + Vec4f color_hint; + Vec4f color; + Fmt fmt_hint; + } input; + + struct { + bool enabled; + Fmt fmt; + Vec4f color; + } stats; + } bottom; + + Vec4f tokens[TOKEN_KIND_SIZE]; + + struct { + long blink_threshold; + long blink_period; + Vec4f color; + double width; + } cursor; + + Vec4f selection; + + struct { + Vec4f selection; + } search; + } editor; +} EditorConfig; + typedef struct Editor_s { Free_Glyph_Atlas *atlas; @@ -74,10 +135,7 @@ typedef struct Editor_s { String_Builder clipboard; - struct { - Config cfg; - AllocGroup alloc; - } configs; + EditorConfig configs; } Editor; bool editor_load_config(Editor *editor, const char *config_path); @@ -88,6 +146,8 @@ Errno editor_load_from_file(Editor *editor, const char *file_path); Uint32 editor_add_popup(Editor *editor, PopUp *popUp); void editor_remove_popup(Editor *editor, Uint32 uid); +void editor_configured_popup(Editor *editor, const char *type, PlaceholderList placeholders); +const char *editor_configured_inline_hint(Editor *editor, const char *type); void editor_start_input(Editor *editor); diff --git a/src/lexer.h b/src/lexer.h index 5993f3f5..bd726ff5 100644 --- a/src/lexer.h +++ b/src/lexer.h @@ -28,6 +28,8 @@ typedef enum { TOKEN_STRING, } Token_Kind; +#define TOKEN_KIND_SIZE TOKEN_STRING - TOKEN_END + const char *token_kind_name(Token_Kind kind); typedef struct { diff --git a/src/main.c b/src/main.c index 4d33bd6a..933be2b7 100644 --- a/src/main.c +++ b/src/main.c @@ -75,7 +75,7 @@ static void onGoToLine(Editor *e) return; invalid_input: - flash_error(e, "Invalid line(:col)!"); + editor_configured_popup(e, "invalid goto", (PlaceholderList) { .elems_size = 0 }); } int main(int argc, char **argv) @@ -98,15 +98,15 @@ int main(int argc, char **argv) // TODO: users should be able to customize the font // const char *const font_file_path = "./fonts/VictorMono-Regular.ttf"; - const char *const font_file_path = "./fonts/iosevka-regular.ttf"; + const char *const font_file_path = editor.configs.window.font; FT_Face face; error = FT_New_Face(library, font_file_path, 0, &face); if (error == FT_Err_Unknown_File_Format) { - fprintf(stderr, "ERROR: `%s` has an unknown format\n", font_file_path); + fprintf(stderr, "ERROR: font file `%s` has an unknown format\n", font_file_path); return 1; } else if (error) { - fprintf(stderr, "ERROR: Could not load file `%s`\n", font_file_path); + fprintf(stderr, "ERROR: Could not load font file `%s`\n", font_file_path); return 1; } @@ -139,9 +139,9 @@ int main(int argc, char **argv) } SDL_Window *window = - SDL_CreateWindow("ded", - 100, 100, - SCREEN_WIDTH, SCREEN_HEIGHT, + SDL_CreateWindow(editor.configs.window.title, + editor.configs.window.x, editor.configs.window.y, + editor.configs.window.w, editor.configs.window.h, SDL_WINDOW_RESIZABLE | SDL_WINDOW_OPENGL); if (window == NULL) { fprintf(stderr, "ERROR: Could not create SDL window: %s\n", SDL_GetError()); @@ -227,13 +227,25 @@ int main(int argc, char **argv) File_Type ft; err = type_of_file(file_path, &ft); if (err != 0) { - flash_error(&editor, "Could not determine type of file %s: %s", file_path, strerror(err)); + editor_configured_popup(&editor, "can not open", (PlaceholderList) { + .elems = (Placeholder[]) { + PLACEHOLDER_STR("path", file_path), + PLACEHOLDER_STR("err", strerror(err)), + }, + .elems_size = 2 + }); } else { switch (ft) { case FT_DIRECTORY: { err = fb_change_dir(&fb); if (err != 0) { - flash_error(&editor, "Could not change directory to %s: %s", file_path, strerror(err)); + editor_configured_popup(&editor, "can not cd", (PlaceholderList) { + .elems = (Placeholder[]) { + PLACEHOLDER_STR("path", file_path), + PLACEHOLDER_STR("err", strerror(err)), + }, + .elems_size = 2 + }); } } break; @@ -241,7 +253,13 @@ int main(int argc, char **argv) case FT_REGULAR: { err = editor_load_from_file(&editor, file_path); if (err != 0) { - flash_error(&editor, "Could not open file %s: %s", file_path, strerror(err)); + editor_configured_popup(&editor, "can not open", (PlaceholderList) { + .elems = (Placeholder[]) { + PLACEHOLDER_STR("path", file_path), + PLACEHOLDER_STR("err", strerror(err)), + }, + .elems_size = 2 + }); } else { file_browser = false; } @@ -249,7 +267,13 @@ int main(int argc, char **argv) break; case FT_OTHER: { - flash_error(&editor, "%s is neither a regular file nor a directory. We can't open it.", file_path); + editor_configured_popup(&editor, "can not open", (PlaceholderList) { + .elems = (Placeholder[]) { + PLACEHOLDER_STR("path", file_path), + PLACEHOLDER_STR("err", strerror(err)), + }, + .elems_size = 2 + }); } break; @@ -296,18 +320,23 @@ int main(int argc, char **argv) if (event.key.keysym.mod & KMOD_SHIFT) { editor_start_input(&editor); editor.input.onDone = onSaveInputPath; - editor.input.hint = "path: "; + editor.input.hint = editor_configured_inline_hint(&editor, "save as"); editor.input.hint_len = strlen(editor.input.hint); } else if (editor.file_path.count > 0) { err = editor_save(&editor); if (err != 0) { - flash_error(&editor, "Can't save: %s", strerror(err)); + editor_configured_popup(&editor, "can not save", (PlaceholderList) { + .elems = (Placeholder[]) { + PLACEHOLDER_STR("err", strerror(err)), + }, + .elems_size = 1 + }); } } else { editor_start_input(&editor); editor.input.onDone = onSaveInputPath; editor.input.required = true; - editor.input.hint = "path: "; + editor.input.hint = editor_configured_inline_hint(&editor, "save"); editor.input.hint_len = strlen(editor.input.hint); } } @@ -423,7 +452,7 @@ int main(int argc, char **argv) if (event.key.keysym.mod & KMOD_CTRL) { editor_start_input(&editor); editor.input.onDone = onGoToLine; - editor.input.hint = "line: "; + editor.input.hint = editor_configured_inline_hint(&editor, "goto");; editor.input.hint_len = strlen(editor.input.hint); } } From 7154eee3341a5f0ecaa5cfe790386fcd356a61be Mon Sep 17 00:00:00 2001 From: alex-s168 <63254202+alex-s168@users.noreply.github.com> Date: Fri, 5 Apr 2024 22:51:05 +0200 Subject: [PATCH 26/29] config editor/background, editor/bottom bar --- ded.miniconf | 7 +- default.miniconf | 7 +- src/editor.c | 190 +++++++++++++++++++++++++++++++++++------------ src/editor.h | 5 ++ src/main.c | 2 +- 5 files changed, 157 insertions(+), 54 deletions(-) diff --git a/ded.miniconf b/ded.miniconf index e5b30da4..94e21683 100644 --- a/ded.miniconf +++ b/ded.miniconf @@ -45,13 +45,14 @@ editor: background: xE7E7E7FF input: hint: - color: x5B5B5BFF - fmt: "({hint}) {msg} + color: x5B5B5BFF + fmt: "({hint}) + spacing: 20.0 content: color: x181818FF stats: enabled: true - fmt: "{lang} {line}/{col} + fmt: "{lang} {row}:{col} / {max-row} color: x181818FF tokens: preproc: x95A99FFF diff --git a/default.miniconf b/default.miniconf index e5b30da4..94e21683 100644 --- a/default.miniconf +++ b/default.miniconf @@ -45,13 +45,14 @@ editor: background: xE7E7E7FF input: hint: - color: x5B5B5BFF - fmt: "({hint}) {msg} + color: x5B5B5BFF + fmt: "({hint}) + spacing: 20.0 content: color: x181818FF stats: enabled: true - fmt: "{lang} {line}/{col} + fmt: "{lang} {row}:{col} / {max-row} color: x181818FF tokens: preproc: x95A99FFF diff --git a/src/editor.c b/src/editor.c index 8a5fcb70..a1c777aa 100644 --- a/src/editor.c +++ b/src/editor.c @@ -164,7 +164,7 @@ void editor_move_line_up(Editor *e) editor_stop_search(e); size_t cursor_row = editor_cursor_row(e); - size_t cursor_col = e->cursor - e->lines.items[cursor_row].begin; + size_t cursor_col = EDITOR_CURSOR_COL(cursor_row,e); if (cursor_row > 0) { Line next_line = e->lines.items[cursor_row - 1]; size_t next_line_size = next_line.end - next_line.begin; @@ -178,7 +178,7 @@ void editor_move_line_down(Editor *e) editor_stop_search(e); size_t cursor_row = editor_cursor_row(e); - size_t cursor_col = e->cursor - e->lines.items[cursor_row].begin; + size_t cursor_col = EDITOR_CURSOR_COL(cursor_row,e); if (cursor_row < e->lines.count - 1) { Line next_line = e->lines.items[cursor_row + 1]; size_t next_line_size = next_line.end - next_line.begin; @@ -480,7 +480,7 @@ void editor_render(SDL_Window *window, Free_Glyph_Atlas *atlas, Simple_Renderer // Render bottom bar { - float scale = 0.6f; + float scale = (float) editor->configs.editor.bottom.scale; float oscale = 1.0f / scale; Simple_Camera oldCam = sr->cam; @@ -491,9 +491,9 @@ void editor_render(SDL_Window *window, Free_Glyph_Atlas *atlas, Simple_Renderer .vel = vec2f(0, 0) }; - { + if (editor->configs.editor.bottom.stats.enabled || editor->input.active) { simple_renderer_set_shader(sr, SHADER_FOR_COLOR); - Vec4f bg = hex_to_vec4f(0xe7e7e7ff); + Vec4f bg = editor->configs.editor.bottom.background; Vec2f p1 = vec2f(0, -60.0f * scale); Vec2f s = vec2f(w * oscale, 60.0f + 60.0f * scale); simple_renderer_solid_rect(sr, p1, s, bg); @@ -504,21 +504,35 @@ void editor_render(SDL_Window *window, Free_Glyph_Atlas *atlas, Simple_Renderer // Render input if (editor->input.active) { - { + do { + char *hint = fmt_fmt_fmt(editor->configs.editor.bottom.input.fmt_hint, (PlaceholderList) { + .elems = (Placeholder[]) { + PLACEHOLDER_STR("hint", editor->input.hint), + }, + .elems_size = 1 + }); + + if (hint == NULL) { + fprintf(stderr, "\"editor/bottom bar/input/hint/fmt\" format error!\n"); + break; + } + simple_renderer_set_shader(sr, SHADER_FOR_TEXT); - Vec4f color = hex_to_vec4f(0x5b5b5bFF); + Vec4f color = editor->configs.editor.bottom.input.color_hint; Vec2f pos = vec2f(x, -20 * scale); free_glyph_atlas_render_line_sized(atlas, sr, - editor->input.hint, - editor->input.hint_len, + hint, + strlen(hint), &pos, color); - x = pos.x; + x = pos.x + editor->configs.editor.bottom.input.spacing; simple_renderer_flush(sr); - } + + free(hint); + } while (0); { simple_renderer_set_shader(sr, SHADER_FOR_TEXT); - Vec4f color = hex_to_vec4f(0x181818FF); + Vec4f color = editor->configs.editor.bottom.input.color; Vec2f pos = vec2f(x, -20 * scale); free_glyph_atlas_render_line_sized(atlas, sr, editor->input.text.items, @@ -528,13 +542,30 @@ void editor_render(SDL_Window *window, Free_Glyph_Atlas *atlas, Simple_Renderer } } // Render additional info - else { - static char str[200]; - sprintf(str, "%s %zu / %zu", file_ext_str(editor->file_ext), editor_cursor_row(editor) + 1, editor->lines.count); + else if (editor->configs.editor.bottom.stats.enabled) do { + const size_t cursor_row = editor_cursor_row(editor); + const size_t cursor_col = EDITOR_CURSOR_COL(cursor_row,editor); + + char *str = fmt_fmt_fmt(editor->configs.editor.bottom.stats.fmt, (PlaceholderList) { + .elems = (Placeholder[]) { + PLACEHOLDER_STR("lang", file_ext_str(editor->file_ext)), + + PLACEHOLDER_LONG("row", cursor_row + 1), + PLACEHOLDER_LONG("max-row", editor->lines.count), + + PLACEHOLDER_LONG("col", cursor_col + 1) + }, + .elems_size = 4 + }); + + if (str == NULL) { + fprintf(stderr, "\"editor/bottom bar/stats/fmt\" format error!\n"); + break; + } { simple_renderer_set_shader(sr, SHADER_FOR_TEXT); - Vec4f color = hex_to_vec4f(0x181818FF); + Vec4f color = editor->configs.editor.bottom.stats.color; Vec2f pos = vec2f(x, -20 * scale); free_glyph_atlas_render_line_sized(atlas, sr, str, @@ -543,7 +574,9 @@ void editor_render(SDL_Window *window, Free_Glyph_Atlas *atlas, Simple_Renderer x = pos.x; simple_renderer_flush(sr); } - } + + free(str); + } while (0); sr->cam = oldCam; } @@ -869,47 +902,31 @@ bool editor_load_config(Editor *editor, const char *config_path) { return false; } +#define CHECK_NF(what) if (!ok) { \ + fprintf(stderr, "\"window/" what "\" not found in config!"); \ + config_destroy(&window); \ + return false; \ +} + editor->configs.window.font = config_get_str_at(window, "font", &ok); - if (!ok) { - fprintf(stderr, "\"window/font\" not found in config!"); - config_destroy(&window); - return false; - } + CHECK_NF("font") editor->configs.window.title = config_get_str_at(window, "title", &ok); - if (!ok) { - fprintf(stderr, "\"window/title\" not found in config!"); - config_destroy(&window); - return false; - } + CHECK_NF("title"); editor->configs.window.x = config_get_long_at(window, "x", &ok); - if (!ok) { - fprintf(stderr, "\"window/x\" not found in config!"); - config_destroy(&window); - return false; - } + CHECK_NF("x"); editor->configs.window.y = config_get_long_at(window, "y", &ok); - if (!ok) { - fprintf(stderr, "\"window/y\" not found in config!"); - config_destroy(&window); - return false; - } + CHECK_NF("y"); editor->configs.window.w = config_get_long_at(window, "w", &ok); - if (!ok) { - fprintf(stderr, "\"window/w\" not found in config!"); - config_destroy(&window); - return false; - } + CHECK_NF("w"); editor->configs.window.h = config_get_long_at(window, "h", &ok); - if (!ok) { - fprintf(stderr, "\"window/h\" not found in config!"); - config_destroy(&window); - return false; - } + CHECK_NF("h"); + +#undef CHECK_NF config_destroy(&window); } @@ -964,5 +981,84 @@ bool editor_load_config(Editor *editor, const char *config_path) { } } + Config edit; + config_init(&edit); + bool ok; + config_child(&edit, editor->configs.cfg, "editor", &ok); + if (!ok) { + fprintf(stderr, "\"editor\" not found in config!"); + config_destroy(&edit); + return false; + } + +#define CHECK_NF(what) if (!ok) { \ + fprintf(stderr, "\"editor/" what "\" not found in config!"); \ + config_destroy(&edit); \ + return false; \ +} + + editor->configs.editor.background = hex_to_vec4f(config_get_long_at(edit, "background", &ok)); + CHECK_NF("background") + + // TODO + // we should get each sub config manually but I'm lazy + + editor->configs.editor.bottom.scale = config_get_double_at(edit, "bottom bar/scale", &ok); + CHECK_NF("bottom bar/scale") + + editor->configs.editor.bottom.background = hex_to_vec4f(config_get_long_at(edit, "bottom bar/background", &ok)); + CHECK_NF("bottom bar/background") + + editor->configs.editor.bottom.input.color_hint = hex_to_vec4f(config_get_long_at(edit, "bottom bar/input/hint/color", &ok)); + CHECK_NF("bottom bar/input/hint/color") + + editor->configs.editor.bottom.input.spacing = config_get_double_at(edit, "bottom bar/input/hint/spacing", &ok); + CHECK_NF("bottom bar/input/hint/spacing") + + { + const char *fmt_str = config_get_str_at(edit, "bottom bar/input/hint/fmt", &ok); + CHECK_NF("bottom bar/input/hint/fmt") + + free(editor->configs.editor.bottom.input.fmt_str); + fmt_destroy(editor->configs.editor.bottom.input.fmt_hint); + + size_t fmt_str_len = strlen(fmt_str); + char *fmt_str_copy = malloc(fmt_str_len + 1); + memcpy(fmt_str_copy, fmt_str, fmt_str_len + 1); + + editor->configs.editor.bottom.input.fmt_hint = fmt_compile(fmt_str_copy); + editor->configs.editor.bottom.input.fmt_str = fmt_str_copy; + } + + editor->configs.editor.bottom.input.color = hex_to_vec4f(config_get_long_at(edit, "bottom bar/input/content/color", &ok)); + CHECK_NF("bottom bar/input/content/color") + + bool status_bar_enabled = config_get_bool_at(edit, "bottom bar/stats/enabled", &ok); + CHECK_NF("bottom bar/stats/enabled") + editor->configs.editor.bottom.stats.enabled = status_bar_enabled; + + if (status_bar_enabled) { + { + const char *fmt_str = config_get_str_at(edit, "bottom bar/stats/fmt", &ok); + CHECK_NF("bottom bar/stats/fmt") + + free(editor->configs.editor.bottom.stats.fmt_str); + fmt_destroy(editor->configs.editor.bottom.stats.fmt); + + size_t fmt_str_len = strlen(fmt_str); + char *fmt_str_copy = malloc(fmt_str_len + 1); + memcpy(fmt_str_copy, fmt_str, fmt_str_len + 1); + + editor->configs.editor.bottom.stats.fmt = fmt_compile(fmt_str_copy); + editor->configs.editor.bottom.stats.fmt_str = fmt_str_copy; + } + + editor->configs.editor.bottom.stats.color = hex_to_vec4f(config_get_long_at(edit, "bottom bar/stats/color", &ok)); + CHECK_NF("bottom bar/stats/color") + } + +#undef CHECK_NF + + config_destroy(&edit); return true; } \ No newline at end of file diff --git a/src/editor.h b/src/editor.h index d9631ca7..30f3cd60 100644 --- a/src/editor.h +++ b/src/editor.h @@ -87,12 +87,15 @@ typedef struct { struct { Vec4f color_hint; Vec4f color; + char *fmt_str; Fmt fmt_hint; + double spacing; } input; struct { bool enabled; Fmt fmt; + char *fmt_str; Vec4f color; } stats; } bottom; @@ -138,6 +141,8 @@ typedef struct Editor_s { EditorConfig configs; } Editor; +#define EDITOR_CURSOR_COL(cursor_row,editor) (editor->cursor - editor->lines.items[cursor_row].begin) + bool editor_load_config(Editor *editor, const char *config_path); Errno editor_save_as(Editor *editor, const char *file_path); diff --git a/src/main.c b/src/main.c index 933be2b7..71ed4ef1 100644 --- a/src/main.c +++ b/src/main.c @@ -538,7 +538,7 @@ int main(int argc, char **argv) } } - Vec4f bg = hex_to_vec4f(0x181818FF); + Vec4f bg = editor.configs.editor.background; glClearColor(bg.x, bg.y, bg.z, bg.w); glClear(GL_COLOR_BUFFER_BIT); From 4f5e38feadd64234d812d61408a221e732e7893a Mon Sep 17 00:00:00 2001 From: alex-s168 <63254202+alex-s168@users.noreply.github.com> Date: Fri, 5 Apr 2024 23:08:27 +0200 Subject: [PATCH 27/29] config editor/tokens --- ded.miniconf | 3 ++- default.miniconf | 3 ++- src/editor.c | 66 ++++++++++++++++++++++++++++++------------------ src/editor.h | 1 + src/lexer.c | 42 ++++++++++-------------------- src/lexer.h | 4 +-- 6 files changed, 63 insertions(+), 56 deletions(-) diff --git a/ded.miniconf b/ded.miniconf index 94e21683..32334880 100644 --- a/ded.miniconf +++ b/ded.miniconf @@ -40,6 +40,7 @@ input hints: editor: background: x181818FF + color: xFFFFFFFF bottom bar: scale: 0.6 background: xE7E7E7FF @@ -55,7 +56,7 @@ editor: fmt: "{lang} {row}:{col} / {max-row} color: x181818FF tokens: - preproc: x95A99FFF + preprocessor directive: x95A99FFF keyword: xFFDD33FF comment: xCC8C3CFF string: x73C936FF diff --git a/default.miniconf b/default.miniconf index 94e21683..32334880 100644 --- a/default.miniconf +++ b/default.miniconf @@ -40,6 +40,7 @@ input hints: editor: background: x181818FF + color: xFFFFFFFF bottom bar: scale: 0.6 background: xE7E7E7FF @@ -55,7 +56,7 @@ editor: fmt: "{lang} {row}:{col} / {max-row} color: x181818FF tokens: - preproc: x95A99FFF + preprocessor directive: x95A99FFF keyword: xFFDD33FF comment: xCC8C3CFF string: x73C936FF diff --git a/src/editor.c b/src/editor.c index a1c777aa..e89760d9 100644 --- a/src/editor.c +++ b/src/editor.c @@ -307,6 +307,10 @@ const char *editor_line_starts_with_one_of(Editor *e, size_t row, size_t col, co return NULL; } +static Vec4f token_color(Editor *editor, Token_Kind token) { + return editor->configs.editor.tokens[token]; +} + void editor_render(SDL_Window *window, Free_Glyph_Atlas *atlas, Simple_Renderer *sr, Editor *editor) { int w, h; @@ -390,23 +394,7 @@ void editor_render(SDL_Window *window, Free_Glyph_Atlas *atlas, Simple_Renderer for (size_t i = 0; i < editor->tokens.count; ++i) { Token token = editor->tokens.items[i]; Vec2f pos = token.position; - Vec4f color = vec4fs(1); - switch (token.kind) { - case TOKEN_PREPROC: - color = hex_to_vec4f(0x95A99FFF); - break; - case TOKEN_KEYWORD: - color = hex_to_vec4f(0xFFDD33FF); - break; - case TOKEN_COMMENT: - color = hex_to_vec4f(0xCC8C3CFF); - break; - case TOKEN_STRING: - color = hex_to_vec4f(0x73c936ff); - break; - default: - {} - } + Vec4f color = token_color(editor, token.kind); free_glyph_atlas_render_line_sized(atlas, sr, token.text, token.text_len, &pos, color); // TODO: the max_line_len should be calculated based on what's visible on the screen right now if (max_line_len < pos.x) max_line_len = pos.x; @@ -937,21 +925,21 @@ bool editor_load_config(Editor *editor, const char *config_path) { bool ok; config_child(&popup, editor->configs.cfg, "pop up", &ok); if (!ok) { - fprintf(stderr, "\"pop up\" not found in config!"); + fprintf(stderr, "\"pop up\" not found in config!\n"); config_destroy(&popup); return false; } editor->configs.popup.scale = config_get_double_at(popup, "scale", &ok); if (!ok) { - fprintf(stderr, "\"pop up/scale\" not found in config!"); + fprintf(stderr, "\"pop up/scale\" not found in config!\n"); config_destroy(&popup); return false; } editor->configs.popup.fade_in = config_get_long_at(popup, "fade in", &ok); if (!ok) { - fprintf(stderr, "\"pop up/fade in\" not found in config!"); + fprintf(stderr, "\"pop up/fade in\" not found in config!\n"); config_destroy(&popup); return false; } @@ -965,7 +953,7 @@ bool editor_load_config(Editor *editor, const char *config_path) { bool ok; config_child(&editor->configs.popup_messages, editor->configs.cfg, "pop ups", &ok); if (!ok) { - fprintf(stderr, "\"pop ups\" not found in config!"); + fprintf(stderr, "\"pop ups\" not found in config!\n"); return false; } } @@ -976,7 +964,7 @@ bool editor_load_config(Editor *editor, const char *config_path) { bool ok; config_child(&editor->configs.input_hints, editor->configs.cfg, "input hints", &ok); if (!ok) { - fprintf(stderr, "\"input hints\" not found in config!"); + fprintf(stderr, "\"input hints\" not found in config!\n"); return false; } } @@ -986,13 +974,13 @@ bool editor_load_config(Editor *editor, const char *config_path) { bool ok; config_child(&edit, editor->configs.cfg, "editor", &ok); if (!ok) { - fprintf(stderr, "\"editor\" not found in config!"); + fprintf(stderr, "\"editor\" not found in config!\n"); config_destroy(&edit); return false; } #define CHECK_NF(what) if (!ok) { \ - fprintf(stderr, "\"editor/" what "\" not found in config!"); \ + fprintf(stderr, "\"editor/" what "\" not found in config!\n"); \ config_destroy(&edit); \ return false; \ } @@ -1000,6 +988,9 @@ bool editor_load_config(Editor *editor, const char *config_path) { editor->configs.editor.background = hex_to_vec4f(config_get_long_at(edit, "background", &ok)); CHECK_NF("background") + editor->configs.editor.color = hex_to_vec4f(config_get_long_at(edit, "color", &ok)); + CHECK_NF("color") + // TODO // we should get each sub config manually but I'm lazy @@ -1057,6 +1048,33 @@ bool editor_load_config(Editor *editor, const char *config_path) { CHECK_NF("bottom bar/stats/color") } + { + Config tokens; + config_init(&tokens); + bool ok; + config_child(&tokens, edit, "tokens", &ok); + if (!ok) { + fprintf(stderr, "\"editor/tokens\" not found in config!"); + config_destroy(&tokens); + return false; + } + + for (size_t i = 0; i < TOKEN_KIND_SIZE; i ++) { + const char *name = token_kind_name[i]; + + long colorLong = config_get_long_at(tokens, name, &ok); + Vec4f color; + if (ok) + color = hex_to_vec4f(colorLong); + else + color = editor->configs.editor.color; + + editor->configs.editor.tokens[i] = color; + } + + config_destroy(&tokens); + } + #undef CHECK_NF config_destroy(&edit); diff --git a/src/editor.h b/src/editor.h index 30f3cd60..56f5b409 100644 --- a/src/editor.h +++ b/src/editor.h @@ -79,6 +79,7 @@ typedef struct { struct { Vec4f background; + Vec4f color; struct { double scale; diff --git a/src/lexer.c b/src/lexer.c index 3f081ea7..32245450 100644 --- a/src/lexer.c +++ b/src/lexer.c @@ -65,34 +65,20 @@ const char *pyKeywords[] = { }; #define pyKeywords_count (sizeof(pyKeywords)/sizeof(pyKeywords[0])) -const char *token_kind_name(Token_Kind kind) -{ - switch (kind) { - case TOKEN_END: - return "end of content"; - case TOKEN_INVALID: - return "invalid token"; - case TOKEN_PREPROC: - return "preprocessor directive"; - case TOKEN_SYMBOL: - return "symbol"; - case TOKEN_OPEN_PAREN: - return "open paren"; - case TOKEN_CLOSE_PAREN: - return "close paren"; - case TOKEN_OPEN_CURLY: - return "open curly"; - case TOKEN_CLOSE_CURLY: - return "close curly"; - case TOKEN_SEMICOLON: - return "semicolon"; - case TOKEN_KEYWORD: - return "keyword"; - default: - UNREACHABLE("token_kind_name"); - } - return NULL; -} +const char *token_kind_name[TOKEN_KIND_SIZE] = { + [TOKEN_END] = "end of content", + [TOKEN_INVALID] = "invalid token", + [TOKEN_PREPROC] = "preprocessor directive", + [TOKEN_SYMBOL] = "symbol", + [TOKEN_OPEN_PAREN] = "open paren", + [TOKEN_CLOSE_PAREN] = "close paren", + [TOKEN_OPEN_CURLY] = "open curly", + [TOKEN_CLOSE_CURLY] = "close curly", + [TOKEN_SEMICOLON] = "semicolon", + [TOKEN_KEYWORD] = "keyword", + [TOKEN_COMMENT] = "comment", + [TOKEN_STRING] = "string", +}; Lexer lexer_new(Free_Glyph_Atlas *atlas, const char *content, size_t content_len, String_Builder file_path) { diff --git a/src/lexer.h b/src/lexer.h index bd726ff5..16789ca9 100644 --- a/src/lexer.h +++ b/src/lexer.h @@ -28,9 +28,9 @@ typedef enum { TOKEN_STRING, } Token_Kind; -#define TOKEN_KIND_SIZE TOKEN_STRING - TOKEN_END +#define TOKEN_KIND_SIZE TOKEN_STRING + 1 -const char *token_kind_name(Token_Kind kind); +extern const char *token_kind_name[TOKEN_KIND_SIZE]; typedef struct { Token_Kind kind; From 4fc1d9af46c4844c8dfc91bba718412135155fa4 Mon Sep 17 00:00:00 2001 From: alex-s168 <63254202+alex-s168@users.noreply.github.com> Date: Fri, 5 Apr 2024 23:16:06 +0200 Subject: [PATCH 28/29] config done --- src/editor.c | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/src/editor.c b/src/editor.c index e89760d9..9e12204b 100644 --- a/src/editor.c +++ b/src/editor.c @@ -353,7 +353,7 @@ void editor_render(SDL_Window *window, Free_Glyph_Atlas *atlas, Simple_Renderer atlas, editor->data.items + select_begin_chr, select_end_chr - select_begin_chr, &select_end_scr); - Vec4f selection_color = vec4f(.25, .25, .25, 1); + Vec4f selection_color = editor->configs.editor.selection; simple_renderer_solid_rect(sr, select_begin_scr, vec2f(select_end_scr.x - select_begin_scr.x, FREE_GLYPH_FONT_SIZE), selection_color); } } @@ -379,7 +379,7 @@ void editor_render(SDL_Window *window, Free_Glyph_Atlas *atlas, Simple_Renderer { if (editor->searching && editor_search_matches_at(editor, editor->cursor)) { simple_renderer_set_shader(sr, SHADER_FOR_COLOR); - Vec4f selection_color = vec4f(.10, .10, .25, 1); + Vec4f selection_color = editor->configs.editor.search.selection; Vec2f p1 = cursor_pos; Vec2f p2 = p1; free_glyph_atlas_measure_line_sized(editor->atlas, editor->input.text.items, editor->input.text.count, &p2); @@ -405,9 +405,9 @@ void editor_render(SDL_Window *window, Free_Glyph_Atlas *atlas, Simple_Renderer // Render cursor simple_renderer_set_shader(sr, SHADER_FOR_COLOR); { - float CURSOR_WIDTH = 5.0f; - Uint32 CURSOR_BLINK_THRESHOLD = 500; - Uint32 CURSOR_BLINK_PERIOD = 1000; + float CURSOR_WIDTH = editor->configs.editor.cursor.width; + Uint32 CURSOR_BLINK_THRESHOLD = editor->configs.editor.cursor.blink_threshold; + Uint32 CURSOR_BLINK_PERIOD = editor->configs.editor.cursor.blink_period; Uint32 t = SDL_GetTicks() - editor->last_stroke; sr->verticies_count = 0; @@ -415,7 +415,7 @@ void editor_render(SDL_Window *window, Free_Glyph_Atlas *atlas, Simple_Renderer simple_renderer_solid_rect( sr, cursor_pos, vec2f(CURSOR_WIDTH, FREE_GLYPH_FONT_SIZE), - vec4fs(1)); + editor->configs.editor.cursor.color); } simple_renderer_flush(sr); @@ -1075,6 +1075,24 @@ bool editor_load_config(Editor *editor, const char *config_path) { config_destroy(&tokens); } + editor->configs.editor.cursor.blink_threshold = config_get_long_at(edit, "cursor/blink/treshold", &ok); + CHECK_NF("cursor/blink/treshold") + + editor->configs.editor.cursor.blink_period = config_get_long_at(edit, "cursor/blink/period", &ok); + CHECK_NF("cursor/blink/period") + + editor->configs.editor.cursor.color = hex_to_vec4f(config_get_long_at(edit, "cursor/color", &ok)); + CHECK_NF("cursor/color") + + editor->configs.editor.cursor.width = config_get_double_at(edit, "cursor/width", &ok); + CHECK_NF("cursor/width") + + editor->configs.editor.selection = hex_to_vec4f(config_get_long_at(edit, "selection/color", &ok)); + CHECK_NF("selection/color") + + editor->configs.editor.search.selection = hex_to_vec4f(config_get_long_at(edit, "search selection/color", &ok)); + CHECK_NF("search selection") + #undef CHECK_NF config_destroy(&edit); From 7652d96a6a59dcc640edb63a226002c25828bfb6 Mon Sep 17 00:00:00 2001 From: alex-s168 <63254202+alex-s168@users.noreply.github.com> Date: Thu, 11 Apr 2024 21:45:10 +0200 Subject: [PATCH 29/29] use minilibs instead of miniconf and minifmt --- .gitmodules | 9 +++------ build.sh | 2 +- build_msvc.bat | 3 ++- build_msys2_mingw64.sh | 2 +- dependencies/miniconf | 1 - dependencies/minifmt | 1 - dependencies/minilibs | 1 + 7 files changed, 8 insertions(+), 11 deletions(-) delete mode 160000 dependencies/miniconf delete mode 160000 dependencies/minifmt create mode 160000 dependencies/minilibs diff --git a/.gitmodules b/.gitmodules index 66709e6f..b1e43622 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,9 +4,6 @@ [submodule "dependencies/freetype"] path = dependencies/freetype url = https://github.com/ubawurinna/freetype-windows-binaries.git -[submodule "dependencies/miniconf"] - path = dependencies/miniconf - url = https://github.com/alex-s168/miniconf.git -[submodule "dependencies/minifmt"] - path = dependencies/minifmt - url = https://github.com/alex-s168/minifmt.git +[submodule "dependencies/minilibs"] + path = dependencies/minilibs + url = https://github.com/alex-s168/minilibs.git diff --git a/build.sh b/build.sh index e0c07ec9..4453e5b7 100755 --- a/build.sh +++ b/build.sh @@ -4,7 +4,7 @@ set -xe CC="${CXX:-cc}" PKGS="sdl2 glew freetype2" -CFLAGS="-std=c11 -pedantic -ggdb -Idependencies/miniconf -Idependencies/minirent -Idependencies/minifmt" +CFLAGS="-std=c11 -pedantic -ggdb -Idependencies/minilibs -Idependencies/minirent" LIBS=-lm SRC="src/main.c src/la.c src/editor.c src/file_browser.c src/free_glyph.c src/simple_renderer.c src/common.c src/lexer.c" diff --git a/build_msvc.bat b/build_msvc.bat index 3150aee4..ce58a4d6 100644 --- a/build_msvc.bat +++ b/build_msvc.bat @@ -2,7 +2,8 @@ rem launch this from msvc-enabled console set CFLAGS=/W4 /std:c11 /wd4996 /wd5105 /FC /TC /Zi /nologo -set INCLUDES=/I dependencies\SDL2\include /I dependencies\freetype\include /I dependencies\minirent /I dependencies\GLFW\include /I dependencies\GLEW\include /I dependencies\miniconf /I dependencies\minifmt +set INCLUDES=/I dependencies\SDL2\include /I dependencies\freetype\include /I dependencies\minirent /I dependencies\GLFW\include /I dependencies\GLEW\include /I dependencies\minilibs + set LIBS=dependencies\SDL2\lib\x64\SDL2.lib ^ dependencies\SDL2\lib\x64\SDL2main.lib ^ dependencies\GLFW\lib\glfw3.lib ^ diff --git a/build_msys2_mingw64.sh b/build_msys2_mingw64.sh index 3623dfbb..61e7e4c4 100644 --- a/build_msys2_mingw64.sh +++ b/build_msys2_mingw64.sh @@ -3,7 +3,7 @@ set -xe PKGS="--static sdl2 glew freetype2" -CFLAGS="-Wall -Wextra -pedantic -ggdb -DGLEW_STATIC `pkg-config --cflags $PKGS` -Isrc -Idependencies/miniconf -Idependencies/minifmt -Idependencies/minirent -Dassert(expression)=((void)0) " +CFLAGS="-Wall -Wextra -pedantic -ggdb -DGLEW_STATIC `pkg-config --cflags $PKGS` -Isrc -Idependencies/minilibs -Idependencies/minirent -Dassert(expression)=((void)0) " LIBS="-lm -lopengl32 `pkg-config --libs $PKGS`" SRC="src/main.c src/la.c src/editor.c src/file_browser.c src/free_glyph.c src/simple_renderer.c src/common.c src/lexer.c" OBJ=$(echo "$SRC" | sed "s/\.c/\.o/g") diff --git a/dependencies/miniconf b/dependencies/miniconf deleted file mode 160000 index c8292d6d..00000000 --- a/dependencies/miniconf +++ /dev/null @@ -1 +0,0 @@ -Subproject commit c8292d6d561bea49e606ef5414a1a6967ac331d0 diff --git a/dependencies/minifmt b/dependencies/minifmt deleted file mode 160000 index 6c41994f..00000000 --- a/dependencies/minifmt +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 6c41994fbff5cf428e06e1f170142921bfac088a diff --git a/dependencies/minilibs b/dependencies/minilibs new file mode 160000 index 00000000..f3a1f46e --- /dev/null +++ b/dependencies/minilibs @@ -0,0 +1 @@ +Subproject commit f3a1f46ea2da653e2d761537d1761f56fc556f92