From ba7a8a8502db360fd09c0832e21873d6102e28f7 Mon Sep 17 00:00:00 2001 From: defname Date: Sat, 25 Oct 2025 12:14:59 +0200 Subject: [PATCH 01/12] propagate PATH_MAX by file.h --- src/io/file.c | 1 - src/io/file.h | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/src/io/file.c b/src/io/file.c index 86f8a18..9cf526a 100644 --- a/src/io/file.c +++ b/src/io/file.c @@ -20,7 +20,6 @@ #include #include // access() #include // dirname() -#include // PATH_MAX #include #include "common/config.h" #include "common/logging.h" diff --git a/src/io/file.h b/src/io/file.h index 733d676..fa0eb6d 100644 --- a/src/io/file.h +++ b/src/io/file.h @@ -16,6 +16,7 @@ #ifndef FILE_H #define FILE_H +#include // PATH_MAX #include "stdio.h" #include "common/string.h" From 5fc6a61b1a9fc8d78324e7ac18ffe9bfee7cb621 Mon Sep 17 00:00:00 2001 From: defname Date: Sat, 25 Oct 2025 12:15:48 +0200 Subject: [PATCH 02/12] create compile_commands.json (for correct using include paths in vscode) --- CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 57e2577..59b729e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,6 +5,8 @@ get_filename_component(PROJECT_NAME ${CMAKE_CURRENT_SOURCE_DIR} NAME) project(${PROJECT_NAME} C) set(CMAKE_C_STANDARD 99) +set(CMAKE_EXPORT_COMPILE_COMMANDS on) + # Set a default build type if none was specified if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) message(STATUS "Setting build type to 'Debug' as none was specified.") From dbd328ebb3647fa833ae3e89e27f84c3b400bbec Mon Sep 17 00:00:00 2001 From: defname Date: Sat, 25 Oct 2025 12:31:45 +0200 Subject: [PATCH 03/12] add create and destroy functions to SyntaxHighlighting --- src/syntax/highlighting.c | 14 ++++++++++++++ src/syntax/highlighting.h | 16 +++++++++------- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/src/syntax/highlighting.c b/src/syntax/highlighting.c index f9f0e06..635fb64 100644 --- a/src/syntax/highlighting.c +++ b/src/syntax/highlighting.c @@ -103,6 +103,20 @@ void SyntaxHighlighting_Deinit(SyntaxHighlighting *hl) { hl->def = NULL; } +SyntaxHighlighting *SyntaxHighlighting_Create(const SyntaxDefinition *def) { + SyntaxHighlighting *hl = malloc(sizeof(SyntaxHighlighting)); + if (!hl) { + logFatal("Cannot allocate memory for SyntaxHighlighting."); + } + SyntaxHighlighting_Init(hl, def); + return hl; +} + +void SyntaxHighlighting_Destroy(SyntaxHighlighting *hl) { + SyntaxHighlighting_Deinit(hl); + free(hl); +} + static bool regexec_with_cache(const regex_t *regex, const char *str, size_t offset, MatchCache *cache, regmatch_t *match) { if (cache->done) { return false; diff --git a/src/syntax/highlighting.h b/src/syntax/highlighting.h index 17e0bb9..efa9891 100644 --- a/src/syntax/highlighting.h +++ b/src/syntax/highlighting.h @@ -73,7 +73,7 @@ void SyntaxHighlightingString_Destroy(SyntaxHighlightingString *shs); void SyntaxHighlightingString_AddTag(SyntaxHighlightingString *shs, SyntaxHighlightingTag tag); void SyntaxHighlightingString_Clear(SyntaxHighlightingString *shs); -/**match +/** * @brief Holds the highlighting information for text of multiple `Strings`. */ typedef struct _SyntaxHighlighting { @@ -82,16 +82,18 @@ typedef struct _SyntaxHighlighting { } SyntaxHighlighting; -/** - * @brief Initialize syntax highlighting using def for definitions. - */ +/** @brief Initialize syntax highlighting using def for definitions. */ void SyntaxHighlighting_Init(SyntaxHighlighting *sh, const SyntaxDefinition *def); -/** - * @brief Deinitialize sh. - */ +/** @brief Deinitialize `sh`. */ void SyntaxHighlighting_Deinit(SyntaxHighlighting *sh); +/** @brief Instantiate `SyntaxHighlighting`. */ +SyntaxHighlighting *SyntaxHighlighting_Create(const SyntaxDefinition *def); + +/** @brief Destroy `sh` */ +void SyntaxHighlighting_Destroy(SyntaxHighlighting *sh); + /** * @brief Highlight a string according to given context. * From 57f1e167c2cae6c78a7ca589ab3d8f4a829f0e09 Mon Sep 17 00:00:00 2001 From: defname Date: Sat, 25 Oct 2025 12:36:16 +0200 Subject: [PATCH 04/12] SyntaxHighlighting now takes the ownership of the used SyntaxDefinition --- src/syntax/highlighting.c | 7 +++++-- src/syntax/highlighting.h | 19 ++++++++++++++----- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/src/syntax/highlighting.c b/src/syntax/highlighting.c index 635fb64..561c31f 100644 --- a/src/syntax/highlighting.c +++ b/src/syntax/highlighting.c @@ -90,7 +90,7 @@ void SyntaxHighlightingString_Clear(SyntaxHighlightingString *shs) { /*****************************************************************************/ /* SyntaxHighlighting */ -void SyntaxHighlighting_Init(SyntaxHighlighting *hl, const SyntaxDefinition *def) { +void SyntaxHighlighting_Init(SyntaxHighlighting *hl, SyntaxDefinition *def) { hl->def = def; hl->strings = Table_CreatePtr(); } @@ -99,11 +99,14 @@ void SyntaxHighlighting_Deinit(SyntaxHighlighting *hl) { if (hl->strings) { Table_Destroy(hl->strings); } + if (hl->def) { + SyntaxDefinition_Destroy(hl->def); + } hl->strings = NULL; hl->def = NULL; } -SyntaxHighlighting *SyntaxHighlighting_Create(const SyntaxDefinition *def) { +SyntaxHighlighting *SyntaxHighlighting_Create(SyntaxDefinition *def) { SyntaxHighlighting *hl = malloc(sizeof(SyntaxHighlighting)); if (!hl) { logFatal("Cannot allocate memory for SyntaxHighlighting."); diff --git a/src/syntax/highlighting.h b/src/syntax/highlighting.h index efa9891..c6dcc2e 100644 --- a/src/syntax/highlighting.h +++ b/src/syntax/highlighting.h @@ -77,19 +77,28 @@ void SyntaxHighlightingString_Clear(SyntaxHighlightingString *shs); * @brief Holds the highlighting information for text of multiple `Strings`. */ typedef struct _SyntaxHighlighting { - const SyntaxDefinition *def; //< SyntaxDefinition to use for highlighting + SyntaxDefinition *def; //< SyntaxDefinition to use for highlighting Table *strings; //< Table of (String -> SyntaxHighlightingString*) elements (holds the ownership of the SyntaxHighlightingString's) } SyntaxHighlighting; -/** @brief Initialize syntax highlighting using def for definitions. */ -void SyntaxHighlighting_Init(SyntaxHighlighting *sh, const SyntaxDefinition *def); +/** + * @brief Initialize syntax highlighting using def for definitions. + * + * Note that the ownership of `def` is taken!. + */ +void SyntaxHighlighting_Init(SyntaxHighlighting *sh, SyntaxDefinition *def); /** @brief Deinitialize `sh`. */ void SyntaxHighlighting_Deinit(SyntaxHighlighting *sh); -/** @brief Instantiate `SyntaxHighlighting`. */ -SyntaxHighlighting *SyntaxHighlighting_Create(const SyntaxDefinition *def); +/** + * @brief Instantiate `SyntaxHighlighting`. + * + * @param def The ownership of `def` is taken! + * @returns The newly created `SyntaxHighlighting` instance. +*/ +SyntaxHighlighting *SyntaxHighlighting_Create(SyntaxDefinition *def); /** @brief Destroy `sh` */ void SyntaxHighlighting_Destroy(SyntaxHighlighting *sh); From ded27e24c33168e70d66ef18d8b4bb06bcee067b Mon Sep 17 00:00:00 2001 From: defname Date: Sat, 25 Oct 2025 12:39:00 +0200 Subject: [PATCH 05/12] must not destroy SyntaxDefinition anymore since this is handled by SyntaxHighlighting --- tests/test_syntax_highlighting.c | 3 --- tests/test_syntax_textlayoutbindings.c | 1 - 2 files changed, 4 deletions(-) diff --git a/tests/test_syntax_highlighting.c b/tests/test_syntax_highlighting.c index 68b49ba..e7742f0 100644 --- a/tests/test_syntax_highlighting.c +++ b/tests/test_syntax_highlighting.c @@ -116,7 +116,6 @@ static void assert_highlight_tags( String_Deinit(&str); SyntaxHighlighting_Deinit(&hl); - SyntaxDefinition_Destroy(def); } const char *test_ini0 = @@ -154,7 +153,6 @@ void test_highlight_string_simple(void) { Stack_Destroy(open_blocks_at_begin); String_Deinit(&test1); SyntaxHighlighting_Deinit(&hl); - SyntaxDefinition_Destroy(def); } const char *test_ini1 = @@ -454,7 +452,6 @@ void test_stress(void) { String_Deinit(&test); SyntaxHighlighting_Deinit(&hl); - SyntaxDefinition_Destroy(def); } } diff --git a/tests/test_syntax_textlayoutbindings.c b/tests/test_syntax_textlayoutbindings.c index d7520e7..0209ef1 100644 --- a/tests/test_syntax_textlayoutbindings.c +++ b/tests/test_syntax_textlayoutbindings.c @@ -71,7 +71,6 @@ static void cleanup_fixture(TestFixture *fixture) { SyntaxHighlighting_Deinit(&fixture->sh); TextLayout_Deinit(&fixture->tl); TextBuffer_Deinit(&fixture->tb); - SyntaxDefinition_Destroy(fixture->def); free(fixture->lines); } From f42a9b9570503f8a91b4df9cd80642699b35ba28 Mon Sep 17 00:00:00 2001 From: defname Date: Sat, 25 Oct 2025 14:27:05 +0200 Subject: [PATCH 06/12] fix bug in find_project_file() --- src/io/file.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/io/file.c b/src/io/file.c index 9cf526a..50f6c27 100644 --- a/src/io/file.c +++ b/src/io/file.c @@ -67,12 +67,13 @@ File *File_Open(const char *filename, FileAccessType access) { // find project file char *find_project_file(const char *rel_path) { static char path[PATH_MAX]; - char exe_dir[PATH_MAX]; + char exe_abs_path[PATH_MAX]; + char *exe_dir = NULL; char *home = getenv("HOME"); // 1️. directory of the binary - if (realpath(Config_GetExePath(), exe_dir)) { - strcpy(exe_dir, dirname(exe_dir)); + if (realpath(Config_GetExePath(), exe_abs_path)) { + exe_dir = dirname(exe_abs_path); int n = snprintf(path, sizeof(path), "%s/%s", exe_dir, rel_path); n = n >= (int)sizeof(path) ? (int)sizeof(path) - 1 : n; path[n] = '\0'; From 1677fa3b6f829982011398386322c7d3c7aebc34 Mon Sep 17 00:00:00 2001 From: defname Date: Sat, 25 Oct 2025 14:29:20 +0200 Subject: [PATCH 07/12] implement the SyntaxHighlighting_LoadFromFile() function and tests --- src/syntax/loader.c | 68 ++++++++++++ src/syntax/loader.h | 43 ++++++++ tests/test_syntax_loader.c | 208 +++++++++++++++++++++++++++++++++++++ 3 files changed, 319 insertions(+) create mode 100644 src/syntax/loader.c create mode 100644 src/syntax/loader.h create mode 100644 tests/test_syntax_loader.c diff --git a/src/syntax/loader.c b/src/syntax/loader.c new file mode 100644 index 0000000..6e17b59 --- /dev/null +++ b/src/syntax/loader.c @@ -0,0 +1,68 @@ +#include "loader.h" +#include +#include "io/file.h" + + +void SyntaxHighlightingLoaderError_Deinit(SyntaxHighlightingLoaderError *error) { + SyntaxDefinitionError_Deinit(&error->def_error); + if (error->parsing_error.message) { + free(error->parsing_error.message); + } +} + + +SyntaxHighlighting *SyntaxHighlighting_LoadFromFile(const char *format, SyntaxHighlightingLoaderError *error) { + error->code = SYNTAX_LOADER_NO_ERROR; + error->parsing_error.message = NULL; // important for correct deinitialization + + if (!format) { + error->code = SYNTAX_LOADER_NO_FILENAME; + return NULL; + } + + // 1. load the ini file + char filename[PATH_MAX]; + snprintf(filename, PATH_MAX, "data/syntax/%s.ini", format); + File *file = File_OpenProjectFile(filename, FILE_ACCESS_READ); + if (!file) { + error->code = SYNTAX_LOADER_FILE_NOT_FOUND; + return NULL; + } + + // 2. read the ini file + char *ini = File_Read(file); + if (!ini) { + error->code = SYNTAX_LOADER_FILE_READ_ERROR; + File_Close(file); + return NULL; + } + File_Close(file); + + // 3. parse the ini file + IniParser parser; + IniParser_Init(&parser); + IniParser_SetText(&parser, ini); + Table *table = IniParser_Parse(&parser); + if (!table) { + error->code = SYNTAX_LOADER_PARSE_ERROR; + error->parsing_error = *IniParser_GetError(&parser); + error->parsing_error.message = strdup(IniParser_GetError(&parser)->message); + IniParser_Deinit(&parser); + free(ini); + return NULL; + } + IniParser_Deinit(&parser); + free(ini); + + // 4. create the syntax definition + SyntaxDefinition *def = SyntaxDefinition_FromTable(table, &error->def_error); + if (!def) { + Table_Destroy(table); + error->code = SYNTAX_LOADER_DEFINITION_ERROR; + return NULL; + } + Table_Destroy(table); + + // 5. create the highlighting engine + return SyntaxHighlighting_Create(def); +} \ No newline at end of file diff --git a/src/syntax/loader.h b/src/syntax/loader.h new file mode 100644 index 0000000..eb646a8 --- /dev/null +++ b/src/syntax/loader.h @@ -0,0 +1,43 @@ +#ifndef SYNTAX_LOADER_H +#define SYNTAX_LOADER_H + +#include "highlighting.h" +#include "common/iniparser.h" + +typedef enum { + SYNTAX_LOADER_NO_ERROR, + SYNTAX_LOADER_NO_FILENAME, + SYNTAX_LOADER_FILE_NOT_FOUND, + SYNTAX_LOADER_FILE_READ_ERROR, + SYNTAX_LOADER_PARSE_ERROR, + SYNTAX_LOADER_DEFINITION_ERROR +} SyntaxHighlightingLoaderErrorCode; + +typedef struct { + SyntaxHighlightingLoaderErrorCode code; + SyntaxDefinitionError def_error; + ParsingError parsing_error; +} SyntaxHighlightingLoaderError; + +void SyntaxHighlightingLoaderError_Deinit(SyntaxHighlightingLoaderError *error); + + +/** + * @brief Load a syntax definition and create the highlight engine. + * + * Load the syntax definition for `format` from data/sytnax/.ini + * (from inside a project's folder) and create the `SyntaxHighlight` instance. + * + * @param format Format to load (something like "ini", "md"). + * @param error Will be set in the case of an error and need to be deinitialized by the caller. + * @returns + * The newly created SyntaxHighlighting Instance (ownership transfers to the caller). + * NULL there is definition file or if an error occured. + * In the case of an error `error` will be set and need to be deinitialized by the caller. + * + * Note theat `format` needs to be exactly the same as the filename. + * The convention is to use lower case. + */ +SyntaxHighlighting *SyntaxHighlighting_LoadFromFile(const char *format, SyntaxHighlightingLoaderError *error); + +#endif \ No newline at end of file diff --git a/tests/test_syntax_loader.c b/tests/test_syntax_loader.c new file mode 100644 index 0000000..803b777 --- /dev/null +++ b/tests/test_syntax_loader.c @@ -0,0 +1,208 @@ +void test_init(); +void test_deinit(); + +#define TEST_INIT { test_init(); } +#define TEST_FINI { test_deinit(); } + +#include "acutest.h" +#include "io/file.h" +#include "syntax/loader.h" +#include "common/config.h" // to set a custom project folder + +/*****************************************************************/ +/* Functions to create a test environment */ +bool concat_paths(char *dst, const char *path1, const char *path2) { + if (!dst || !path1 || !path2) { + return false; + } + int l1 = strlen(path1); + if (path1[l1-1] == '/') { + l1--; + } + int l2 = strlen(path2); + if (path2[l2-1] == '/') { + l2--; + } + + int n = snprintf(dst, PATH_MAX, "%*s/%*s", l1-1, path1, l2-1, path2); + if (n >= PATH_MAX) { + return false; + } + dst[n] = '\0'; + return true; +} + +bool create_syntax_folder() { + char exe_abs_path[PATH_MAX]; + char data_dir[PATH_MAX]; + char syntax_dir[PATH_MAX]; + + TEST_CHECK(realpath(Config_GetExePath(), exe_abs_path)); + char *exe_dir = dirname(exe_abs_path); + + if (!concat_paths(data_dir, exe_dir, "data")) { + return false; + } + if (!concat_paths(syntax_dir, data_dir, "syntax")) { + return false; + } + + mkdir(data_dir, 0755); + mkdir(syntax_dir, 0755); + return true; +} + +bool remove_syntax_folder() { + char exe_abs_path[PATH_MAX]; + + TEST_CHECK(realpath(Config_GetExePath(), exe_abs_path)); + char *exe_dir = dirname(exe_abs_path); + char data_dir[PATH_MAX]; + char syntax_dir[PATH_MAX]; + + if (!concat_paths(data_dir, exe_dir, "data")) { + return false; + } + if (!concat_paths(syntax_dir, data_dir, "syntax")) { + return false; + } + + rmdir(syntax_dir); + rmdir(data_dir); + return true; +} + +void test_init() { + static char template[] = "/tmp/clieditor_XXXXXX"; + int fd = mkstemp(template); + TEST_ASSERT(fd != -1); + Config_Init(template); + close(fd); + + TEST_ASSERT(create_syntax_folder()); +} + +void test_deinit() { + remove_syntax_folder(); + unlink(Config_GetExePath()); + Config_Deinit(); +} + +void create_testfile(const char *filename, const char *content) { + char exe_abs_path[PATH_MAX]; + + TEST_CHECK(realpath(Config_GetExePath(), exe_abs_path)); + char *exe_dir = dirname(exe_abs_path); + + char filepath[PATH_MAX]; + snprintf(filepath, PATH_MAX, "%s/data/syntax/%s", exe_dir, filename); + FILE *fp = fopen(filepath, "w"); + TEST_ASSERT(fp); + fwrite(content, sizeof(char), strlen(content), fp); + fclose(fp); +} + + +void delete_testfile(const char *filename) { + char exe_abs_path[PATH_MAX]; + + TEST_CHECK(realpath(Config_GetExePath(), exe_abs_path)); + char *exe_dir = dirname(exe_abs_path); + + char filepath[PATH_MAX]; + snprintf(filepath, PATH_MAX, "%s/data/syntax/%s", exe_dir, filename); + unlink(filepath); +} + +/****************************************************************/ +/* Actual testing */ + +void test_basic(void) { + const char *ini = + "[meta]\n" + "name = TEST\n" + "\n" + "[block:root]\n" + "child_blocks = string\n" + "\n" + "[block:string]\n" + "start = '\n" + "end = '\n"; + + create_testfile("test.ini", ini); + SyntaxHighlightingLoaderError error; + SyntaxHighlighting *hl = SyntaxHighlighting_LoadFromFile("test", &error); + if (!hl) { + TEST_CHECK(hl); + TEST_MSG("SyntaxHighlightingLoaderErrorCode: %d", error.code); + TEST_MSG("message: %s", error.def_error.message.bytes); + SyntaxHighlightingLoaderError_Deinit(&error); + return; + } + SyntaxHighlighting_Destroy(hl); + delete_testfile("test.ini"); +} + +void test_no_filename(void) { + SyntaxHighlightingLoaderError error; + SyntaxHighlighting *hl = SyntaxHighlighting_LoadFromFile(NULL, &error); + TEST_CHECK(!hl); + TEST_CHECK(error.code == SYNTAX_LOADER_NO_FILENAME); + SyntaxHighlightingLoaderError_Deinit(&error); +} + +void test_file_not_found(void) { + SyntaxHighlightingLoaderError error; + SyntaxHighlighting *hl = SyntaxHighlighting_LoadFromFile("test", &error); + TEST_CHECK(!hl); + TEST_CHECK(error.code == SYNTAX_LOADER_FILE_NOT_FOUND); + SyntaxHighlightingLoaderError_Deinit(&error); +} + +void test_parse_error(void) { + const char *ini = + "[meta\n" // <---- INI format error + "name = TEST\n" + "\n" + "[block:root]\n" + "child_blocks = string\n" + "\n" + "[block:string]\n" + "start = '\n" + "end = '\n"; + create_testfile("test.ini", ini); + SyntaxHighlightingLoaderError error; + SyntaxHighlighting *hl = SyntaxHighlighting_LoadFromFile("test", &error); + TEST_CHECK(!hl); + TEST_CHECK(error.code == SYNTAX_LOADER_PARSE_ERROR); + SyntaxHighlightingLoaderError_Deinit(&error); + delete_testfile("test.ini"); +} + +void test_definition_error(void) { + const char *ini = + "[meta]\n" + "name = TEST\n" + "\n" + "[block:root]\n" + "child_blocks = string, blub\n" // <----- non-existing child + "\n" + "[block:string]\n" + "start = '\n" + "end = '\n"; + create_testfile("test.ini", ini); + SyntaxHighlightingLoaderError error; + SyntaxHighlighting *hl = SyntaxHighlighting_LoadFromFile("test", &error); + TEST_CHECK(!hl); + TEST_CHECK(error.code == SYNTAX_LOADER_DEFINITION_ERROR); + SyntaxHighlightingLoaderError_Deinit(&error); + delete_testfile("test.ini"); +} + +TEST_LIST = { + { "SyntaxLoader: Basic", test_basic }, + { "SyntaxLoader: No Filename", test_no_filename }, + { "SyntaxLoader: File Not Found", test_file_not_found }, + { "SyntaxLoader: Parse Error", test_parse_error }, + { NULL, NULL } +}; From d959c68580fcd989b66cc2719824743491daf0d8 Mon Sep 17 00:00:00 2001 From: defname Date: Sat, 25 Oct 2025 14:38:56 +0200 Subject: [PATCH 08/12] add NULL guard --- src/syntax/textlayoutbindings.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/syntax/textlayoutbindings.c b/src/syntax/textlayoutbindings.c index 5681678..835ec87 100644 --- a/src/syntax/textlayoutbindings.c +++ b/src/syntax/textlayoutbindings.c @@ -24,6 +24,10 @@ static void update_lines(SyntaxHighlightingBinding *binding, const Line *line, c } void SyntaxHighlightingBinding_Update(SyntaxHighlightingBinding *binding, const Line *line, const Line *last_line) { + if (!binding || !binding->sh || !binding->tl || !binding->tl->tb) { + return; + } + Line *prev_line = line->prev; Stack *open_blocks = NULL; if (prev_line) { From 475d15e9304a75cd1586b27bf5ab257b6199d5e7 Mon Sep 17 00:00:00 2001 From: defname Date: Sat, 25 Oct 2025 14:56:19 +0200 Subject: [PATCH 09/12] Initialize error properly in SyntaxHighlighting_LoadFromFile() --- src/syntax/loader.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/syntax/loader.c b/src/syntax/loader.c index 6e17b59..ed0e49f 100644 --- a/src/syntax/loader.c +++ b/src/syntax/loader.c @@ -1,7 +1,13 @@ #include "loader.h" #include +#include // snprintf #include "io/file.h" +void SyntaxHighlightingLoaderError_Init(SyntaxHighlightingLoaderError *error) { + memset(error, 0, sizeof(SyntaxHighlightingLoaderError)); + error->code = SYNTAX_LOADER_NO_ERROR; +} + void SyntaxHighlightingLoaderError_Deinit(SyntaxHighlightingLoaderError *error) { SyntaxDefinitionError_Deinit(&error->def_error); @@ -12,8 +18,10 @@ void SyntaxHighlightingLoaderError_Deinit(SyntaxHighlightingLoaderError *error) SyntaxHighlighting *SyntaxHighlighting_LoadFromFile(const char *format, SyntaxHighlightingLoaderError *error) { - error->code = SYNTAX_LOADER_NO_ERROR; - error->parsing_error.message = NULL; // important for correct deinitialization + if (!error) { + return NULL; + } + SyntaxHighlightingLoaderError_Init(error); if (!format) { error->code = SYNTAX_LOADER_NO_FILENAME; From 653e29b1803ce2576a7275bd4651432d19f873c4 Mon Sep 17 00:00:00 2001 From: defname Date: Sat, 25 Oct 2025 14:58:10 +0200 Subject: [PATCH 10/12] guard againsst line == NULL and handle last_line == NULL in SyntaxHighlightingBinding_Update() --- src/syntax/textlayoutbindings.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/syntax/textlayoutbindings.c b/src/syntax/textlayoutbindings.c index 835ec87..1c4b256 100644 --- a/src/syntax/textlayoutbindings.c +++ b/src/syntax/textlayoutbindings.c @@ -24,10 +24,14 @@ static void update_lines(SyntaxHighlightingBinding *binding, const Line *line, c } void SyntaxHighlightingBinding_Update(SyntaxHighlightingBinding *binding, const Line *line, const Line *last_line) { - if (!binding || !binding->sh || !binding->tl || !binding->tl->tb) { + if (!binding || !binding->sh || !binding->tl || !binding->tl->tb || !line) { return; } - + + if (!last_line) { + last_line = line; + } + Line *prev_line = line->prev; Stack *open_blocks = NULL; if (prev_line) { From ec86a6563778f5e7fdee59a910b67d8d5e269894 Mon Sep 17 00:00:00 2001 From: defname Date: Sat, 25 Oct 2025 15:01:53 +0200 Subject: [PATCH 11/12] fix bug in concat_paths() --- tests/test_syntax_loader.c | 33 +++++++++++++++------------------ 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/tests/test_syntax_loader.c b/tests/test_syntax_loader.c index 803b777..73fc495 100644 --- a/tests/test_syntax_loader.c +++ b/tests/test_syntax_loader.c @@ -5,6 +5,14 @@ void test_deinit(); #define TEST_FINI { test_deinit(); } #include "acutest.h" +#include +#include +#include +#include +#include +#include +#include +#include #include "io/file.h" #include "syntax/loader.h" #include "common/config.h" // to set a custom project folder @@ -12,24 +20,13 @@ void test_deinit(); /*****************************************************************/ /* Functions to create a test environment */ bool concat_paths(char *dst, const char *path1, const char *path2) { - if (!dst || !path1 || !path2) { - return false; - } - int l1 = strlen(path1); - if (path1[l1-1] == '/') { - l1--; - } - int l2 = strlen(path2); - if (path2[l2-1] == '/') { - l2--; - } - - int n = snprintf(dst, PATH_MAX, "%*s/%*s", l1-1, path1, l2-1, path2); - if (n >= PATH_MAX) { - return false; - } - dst[n] = '\0'; - return true; + if (!dst || !path1 || !path2) return false; + size_t cap = PATH_MAX; + size_t l1 = strlen(path1); + bool need_sep = (l1 > 0 && path1[l1 - 1] != '/'); + const char *p2 = (path2[0] == '/') ? path2 + 1 : path2; + int n = snprintf(dst, cap, "%s%s%s", path1, need_sep ? "/" : "", p2); + return n >= 0 && (size_t)n < cap; } bool create_syntax_folder() { From 4f6f7ec5ed65a03bcf614cac347f0323bccfb769 Mon Sep 17 00:00:00 2001 From: defname Date: Sat, 25 Oct 2025 15:02:40 +0200 Subject: [PATCH 12/12] add test_definition_error to TEST_LIST --- tests/test_syntax_loader.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_syntax_loader.c b/tests/test_syntax_loader.c index 73fc495..011aa1e 100644 --- a/tests/test_syntax_loader.c +++ b/tests/test_syntax_loader.c @@ -201,5 +201,6 @@ TEST_LIST = { { "SyntaxLoader: No Filename", test_no_filename }, { "SyntaxLoader: File Not Found", test_file_not_found }, { "SyntaxLoader: Parse Error", test_parse_error }, + { "SyntaxLoader: Definition Error", test_definition_error }, { NULL, NULL } };