Skip to content

Feat/71 Syntax Highlighting - #90

Merged
defname merged 39 commits into
mainfrom
feat/71-syntax-highlighting
Oct 26, 2025
Merged

defname merged 39 commits into
mainfrom
feat/71-syntax-highlighting

Conversation

@defname

@defname defname commented Oct 26, 2025 •

Copy link
Copy Markdown
Owner

Summary by CodeRabbit

  • New Features

    • Full syntax highlighting with pluggable definitions, runtime loader, editor integration, and built-in INI/Markdown profiles; per-line highlighting/state preserved across lines.
    • CLI: -h help and -s syntax selection; improved config/project file discovery and config open helper.
  • Build & Testing

    • CMake: export compile commands, default build type, enable testing improvements.
    • New comprehensive tests for syntax definition, highlighting, loader, bindings, and pointer-key table behavior.
  • UI

    • Bottom bar width adapts to parent/window size.
  • Documentation

    • README greatly expanded with usage, architecture, and future goals.

* add SyntaxBlockDef and SyntaxDefinition declarations

* add TableIterator (#79)

* add TableIterator

* change include that provides ssize_t

* add additional tests

* make current slot pointer const

* include stdbool.h for bool

* Add table iteration API and improve parsing robustness (#83)

* add TableIterator (#79)

* add TableIterator

* change include that provides ssize_t

* add additional tests

* make current slot pointer const

* include stdbool.h for bool

* WIP: implement definition loader

* add Table_GetUsage()

* allow ':' in INI file key names

* fix location of tests

* fix typo in Doxygen comment

* add NULL guard to String_Take()

* Add early-exit guards to table lookup functions (#80)

* Fix bug in reading operations on an empty table

* add check for empty table also to Table_Delete()

* add functiond to create SyntaxDefinition_FromTable() with tests. More tests needed

* additional tests (still not enough)

* fix: free correct regex in regex_end branch in SyntaxBlockDef_FromTable()

* fix: wrong numbe of arguments in String_Format() call in SymtaxBlockDef_FromTable()

* add explanation

* fix correct recognition of root block

* fix memory lealk

* add NULL guard for strdup in init_definition()

* fix last fix

* add additional test
…tion (#85)

* add Stack_Copy() and Stack_IsEmpty()

* fix: return copy in Stack_Copy()

* implement syntax highlighting

* add Table_CreateCustom() and Table_CreatePtr()

* fix minor typing bugs

* add Stack_Create(), Stack_Destroy()

* fix but in find_first_child to actually return the child with the first occurence

* automatically add patterns to end only_start blocks and to start/end root

* fix memory leak produced in last commit

* fix tests

* fix bug in SyntaxHighllighting_HighlightString(): check if child or block end comes first

* add basic tests

* use root block it open_blocks parameter is NULL in SyntaxHighlighting_HighlightString()

* add random tests

* handle src->capacity == 0 in Stack_Copy()

* add NULL pointer guard before using the key_free_func() function pointer in the Table module

* fix error message for end_regex compilation error in SyntaxBlockDef_FromTable()

* correct inlcudes

* fix typo

* add missing includes

* change examples in documentation

* change "key == NULL" to "!key" for non pointer keys

* do not shadow parameter match in find_first_block()

* change first parameter name in SyntaxHighlighting_HighlightString() for consitency
* add const to parameter if function does not modify it. add Buffer_Has_Space(), Buffer_Clear()

* cache regex results for better performance

* add "ends_on" (INI) property to SyntaxBlockDef

* implement ends_on in the SyntaxHighlight modul

* remove the check if the ends_on block is allowed inside the surrounding block. this leads to more flexibility and better performance

* add additional (but just a single) test for the new feature
…gString instances ins SyntaxHighlighting_HighlightString()
* add TableIterator (#79)

* add TableIterator

* change include that provides ssize_t

* add additional tests

* make current slot pointer const

* include stdbool.h for bool

* Add early-exit guards to table lookup functions (#80)

* Fix bug in reading operations on an empty table

* add check for empty table also to Table_Delete()

* Add CodeRabbit configuration template

Added a comprehensive configuration template for CodeRabbit, including global settings, review settings, chat configurations, knowledge base settings, and code generation options.

* add SyntaxHighlightingBinding to connect SyntaxHighlighting with the TextBuffer/TextLayout

* fix some obvious bugs in SyntaxHighlighingBinding

* fix memory leak

* add basic test

* add Stack_Size()

* rename test function

* add Stack_CopyTo()

* save open_blocks_at_begin and open_blocks_at_end to SyntaxHighlightingString instances ins SyntaxHighlighting_HighlightString()

* adept code to the changed SyntaxHighlighingString scheme

* improve tests

* Compare the open_blocks_at_begin with the open_blocks parameter for an early exit int SyntaxHighlighting_HighlightString()

* add testcases and improve test framework's flexibility

* fix coderabbit config

* fix resizing in Stack_CopyTo()

* add NULL guard to SyntaxHighlighting_HighlightString()

* change multiline string to valid C string

* change multiline strings to valid C strings

* set shs->tags_capacity correctly in SyntaxHighlioghtingString_Create()
…ine from an INI file Enable compile_commands.json and improve syntax handling (#88)

* propagate PATH_MAX by file.h

* create compile_commands.json (for correct using include paths in vscode)

* add create and destroy functions to SyntaxHighlighting

* SyntaxHighlighting now takes the ownership of the used SyntaxDefinition

* must not destroy SyntaxDefinition anymore since this is handled by SyntaxHighlighting

* fix bug in find_project_file()

* implement the SyntaxHighlighting_LoadFromFile() function and tests

* add NULL guard

* Initialize error properly in SyntaxHighlighting_LoadFromFile()

* guard againsst line == NULL and handle last_line == NULL in SyntaxHighlightingBinding_Update()

* fix bug in concat_paths()

* add test_definition_error to TEST_LIST
* wip

* link to the data directory in the build directory

* example syntax definitions

* massive changes to get syntax highlighting working... need to optimization and better organisation, lot of work arounds

* comment out TESTFILE definition

* destroy SyntaxHighlighting instance properly

* fix Table_Get() call in draw_visual_line() to use the correct key

* improve highlighting

* add Config_SetSyntax(), Config_GetSyntax()

* use getopt() to parse cmd arguments, take syntax type from cmd arguments

* Trigger a full SyntaxHighlighting update once in the beginning

* add hint that byte offset is used

* update screenshot

* improve markdown highlighting

* remove TESTFILE debugging stuff
@coderabbitai

coderabbitai Bot commented Oct 26, 2025 •

Copy link
Copy Markdown
Contributor

Walkthrough

Adds a full INI-based syntax-highlighting subsystem (definition, loader, highlighter, textlayout binding) and editor integration; refactors Table to use generic void* keys with ownership callbacks; adds Buffer/Stack/Config/File utilities, syntax data files, tests, CMake updates, and README rewrite.

Changes

Cohort / File(s) Summary
Build & CI
CMakeLists.txt, tests/CMakeLists.txt, \.coderabbit\.yaml
Export compile commands, set default build type, create data symlink, enable tests/tools subdirs, change test invocation to target file, and reformat YAML list syntax.
Syntax data
data/syntax/ini.ini, data/syntax/md.ini
Add INI and Markdown syntax definition files for highlighting.
Buffer
src/common/buffer.c, src/common/buffer.h
Add Buffer_Clear and Buffer_HasSpace; make IsEmpty/Size/Capacity/Peek const-correct.
Stack
src/common/stack.c, src/common/stack.h
Add Stack_Create/Destroy/Copy/CopyTo, resize helper, and Stack_IsEmpty/Stack_Size accessors.
String
src/common/string.c, src/common/string.h
Add null-pointer guard in String_Take; ensure capacity accounts for NUL in append; minor doc tweak.
Generic Table API
src/common/table.c, src/common/table.h
Replace char* keys with void* keys, add hash/compare/copy/free callbacks, Table_CreateCustom/Table_CreatePtr, change Set/Get/Delete/Has signatures, add Table_GetUsage and ownership-aware slot deinit.
Config & INI parser
src/common/config.c, src/common/config.h, src/common/iniparser.c, src/common/iniparser.h
Config_Init now takes argv0; add exe_path and syntax getters/setters and Config_GetColor; switch filename bounds to PATH_MAX; allow ':' in INI key chars.
File I/O helpers
src/io/file.c, src/io/file.h
Add File_Exists, find_project_file, File_OpenConfig, File_OpenProjectFile for project/config path resolution.
Syntax definition module
src/syntax/definition.c, src/syntax/definition.h
New SyntaxDefinition/SyntaxBlockDef APIs: build/validate blocks from tables, compile regexes, link children/ends_on, and propagate detailed errors with lifecycle functions.
Syntax highlighting core
src/syntax/highlighting.c, src/syntax/highlighting.h
New highlighting engine with tag storage, regex-match caching, stack-based traversal, per-string tag APIs, and public lifecycle/highlight entry point.
Syntax loader
src/syntax/loader.c, src/syntax/loader.h
New loader SyntaxHighlighting_LoadFromFile with loader error types; reads data/syntax/.ini, parses, builds definition, and creates highlighting.
TextLayout binding
src/syntax/textlayoutbindings.c, src/syntax/textlayoutbindings.h
New SyntaxHighlightingBinding tying highlighting to TextLayout with Init/Deinit/Update/UpdateLine/UpdateAll and open-block propagation across lines.
Editor integration
src/widgets/components/editor.c, src/widgets/components/editor.h
Add SyntaxHighlightingBinding to Editor, init/deinit binding, trigger updates after input, and apply per-character syntax colors during line drawing.
Widgets
src/widgets/components/bottombar.c
Initialize BottomBar width from parent->width instead of a fixed macro.
Main
src/main.c
Pass argv0 to Config_Init, add -s syntax option, load highlighting via loader, bind to editor, and use File_OpenConfig for config open.
Tests
tests/* (multiple files)
Add/extend tests for syntax definition, highlighting, loader, textlayout bindings, pointer-key table; minor formatting tweak in test_string.c.
Docs
README.md
Major rewrite: expanded architecture, core concepts, build instructions, and future goals; documentation-only changes.

Sequence Diagram(s)

sequenceDiagram
    participant Main as main.c
    participant Loader as syntax/loader
    participant File as io/file
    participant Parser as iniparser
    participant Def as syntax/definition
    participant Highlight as syntax/highlighting
    participant Editor as editor

    Main->>Main: Config_Init(argv0)
    Main->>Loader: SyntaxHighlighting_LoadFromFile(format, &err)
    Loader->>File: File_OpenProjectFile(path)
    File-->>Loader: file contents / NULL
    Loader->>Parser: IniParser_Parse(contents)
    Parser-->>Loader: Table / parse error
    Loader->>Def: SyntaxDefinition_FromTable(Table)
    Def-->>Loader: SyntaxDefinition / definition error
    Loader->>Highlight: SyntaxHighlighting_Create(def)
    Highlight-->>Loader: SyntaxHighlighting
    Loader-->>Main: SyntaxHighlighting (or NULL + error)
    Main->>Editor: Editor->sh_binding <- SyntaxHighlighting
    Editor->>Highlight: SyntaxHighlighting_HighlightString(text, open_blocks)
    Highlight-->>Editor: tags + open_blocks_at_end
Loading
sequenceDiagram
    participant User as Caller
    participant Table as Table API

    User->>Table: Table_CreatePtr()
    Table-->>User: Table (with ptr callbacks)
    User->>Table: Table_Set(table, key_ptr, value)
    Table->>Table: hash_func -> find_slot -> key_copy_func -> store
    User->>Table: Table_Get(table, key_ptr)
    Table->>Table: hash_func -> key_cmp_func -> return value
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

  • Areas needing focused review:
    • src/syntax/highlighting.c — regex caching, stack traversal logic, tag offsets, and memory/bounds safety.
    • src/syntax/definition.{c,h} and src/syntax/loader.{c,h} — regex compile/cleanup, table-to-block mapping, and error propagation.
    • src/common/table.{c,h} — API signature changes, ownership semantics, and call-site updates.
    • Editor and textlayout binding changes — open-block propagation correctness and per-character drawing performance/edge cases.

Possibly related PRs

Poem

🐰 I hopped through tables, keys set free,
I stitched bright blocks with regex glee,
Buffers cleared and stacks stand tall,
Tags trace words and colors fall,
A rabbit cheers — code springs for all! 🎨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 8.07% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The PR title "Feat/71 Syntax Highlighting" directly and clearly describes the primary objective of the changeset. The pull request introduces a complete syntax highlighting system, including new modules for syntax definition parsing, the highlighting engine, file loading with error handling, and integration into the editor. The title accurately captures this main feature addition with the feature/issue reference "Feat/71" and the specific feature name "Syntax Highlighting." The changes span infrastructure updates to core editor components, data files, tests, and build configuration—all in service of implementing this syntax highlighting feature. The title is concise, specific, and meaningful for scanning commit history.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/71-syntax-highlighting

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 22

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (7)
src/common/string.h (1)

34-36: Header should include sys/types.h for ssize_t.

This header declares functions using ssize_t but doesn’t include its definition. Make the header self-contained.

 #include <stdlib.h>
 #include <stdbool.h>
+#include <sys/types.h>  // ssize_t
src/common/config.c (1)

69-76: Leak on parse failure in Config_LoadIni.

Call IniParser_Deinit(&ini) before returning when IniParser_Parse() fails.

 Table *c = IniParser_Parse(&ini);
 if (!c) {
-    return;
+    IniParser_Deinit(&ini);
+    return;
 }
src/widgets/components/editor.c (2)

39-44: Remove duplicate TextSelection_Deinit

TextSelection_Deinit(&editor->ts) is called twice (Lines 40 and 43). Drop one to avoid confusion.

 static void editor_destroy(Widget *self) {
     Editor *editor = AS_EDITOR(self);
     Timer_Stop(editor->cursor_timer);
     SyntaxHighlightingBinding_Deinit(&editor->sh_binding);
-    TextSelection_Deinit(&editor->ts);
     TextLayout_Deinit(&editor->tl);
     TextEdit_Deinit(&editor->te);
     TextSelection_Deinit(&editor->ts);
 }

69-106: Add bounds check for syntax tag lookup and fix redundant assignment

The code assumes ch points into line->src->text.bytes when computing byte offsets, but VisualLine_GetChar() can return pointers from line->gap->text when a gap exists on the current line—causing undefined behavior. Additionally, line 116 has a redundant assignment: size_t byte_offset = byte_offset = ...

Apply safe bounds checking before pointer arithmetic and clean up the assignment. Requires adding #include <string.h> for strlen().

 #include "common/utf8_helper.h"
+#include <string.h>
 #include "document/textedit.h"
@@
     SyntaxHighlighting *sh = editor->sh_binding.sh;
     SyntaxHighlightingString *shs = !sh ? NULL : Table_Get(sh->strings, &line->src->text);
@@
-    // 2 --- Draw characters loop
+    // 2 --- Draw characters loop
+    const char *src_begin = line->src->text.bytes;
+    const char *src_end = src_begin ? src_begin + strlen(src_begin) : NULL;
     for (int i=0; i<line->length; i++) {
@@
-        // This will not always work if is_gap_line and the gap is not merged!!
-        size_t byte_offset = byte_offset = ch - line->src->text.bytes;
-        
-        const SyntaxHighlightingTag *tag = SyntaxHighlightingString_GetTag(shs, byte_offset);
-        if (tag) {
-            canvas->current_style.fg = tag->block->color;
-            line_style.fg = tag->block->color;
-        }
+        // Only look up tags when character pointer lies within the source text buffer.
+        if (shs && src_begin && src_end && ch >= src_begin && ch < src_end) {
+            size_t byte_offset = (size_t)(ch - src_begin);
+            const SyntaxHighlightingTag *tag = SyntaxHighlightingString_GetTag(shs, byte_offset);
+            if (tag) {
+                canvas->current_style.fg = tag->block->color;
+                line_style.fg = tag->block->color;
+            }
+        }
src/main.c (2)

52-85: Fix argument parsing: avoid pre-getopt filename assignment and unreachable code.

Current logic sets filename to argv[1] even when it's an option and calls help multiple times. Simplify around getopt and handle remaining args cleanly.

 static void parse_arguments(int argc, char *argv[]) {
-    // For now, we only handle a single filename argument.
-    if (argc >= 2) {
-        Config_SetFilename(argv[1]);
-    } else {
-        print_help(argv[0]);
-        // No file provided, could set a default or leave it empty.
-        Config_SetFilename(NULL);
-    }
-
-    int opt;
-    while ((opt = getopt(argc, argv, "hs:")) != -1) {
+    Config_SetFilename(NULL);
+    int opt;
+    while ((opt = getopt(argc, argv, "hs:")) != -1) {
         switch (opt) {
             case 'h':
                 print_help(argv[0]);
-                return;
+                return; // unreachable after exit(0), but keeps intent clear
             case 's':
                 Config_SetSyntax(optarg);
                 break;
             case '?':
                 fprintf(stderr, "Unknown option: -%c\n", optopt);
                 exit(1);
         }
     }
 
-    // Verbleibende Argumente:
-    for (int i = optind; i < argc; i++) {
-        Config_SetFilename(argv[i]);
-        return;
-    }
-
-    print_help(argv[0]);
+    // remaining non-option args: first one is the filename
+    if (optind < argc) {
+        Config_SetFilename(argv[optind]);
+        return;
+    }
+    print_help(argv[0]);
 }

99-118: Cast entry to const char before strcmp.*

entry is a void* user pointer; strict C requires explicit cast for strcmp.

-    if (strcmp(entry, "exit") == 0) {
+    if (strcmp((const char*)entry, "exit") == 0) {
         exit(0);
     }
-    if (strcmp(entry, "save") == 0) {
+    if (strcmp((const char*)entry, "save") == 0) {
         File *file = File_Open(Config_GetFilename(), FILE_ACCESS_WRITE);
src/common/table.c (1)

237-263: Critical: inserting into a tombstone never sets key/state (corrupts table).

When find_slot returns a TOMBSTONE for a new key, Table_Set doesn’t set slot->state to USED nor copy the key. This can also make Table_Get return a value from a tombstone. Fix by treating EMPTY or TOMBSTONE identically on insert, and tighten the state check in Table_Get.

Apply:

 void Table_Set(Table *table, const void *key, void *value, void (*destructor)(void *value)) {
@@
-    if (slot->state == TABLE_SLOT_EMPTY) {
+    if (slot->state != TABLE_SLOT_USED) {  // EMPTY or TOMBSTONE -> new insert
         slot->state = TABLE_SLOT_USED;
         slot->key = table->key_copy_func(key);
         if (!slot->key) {
-            logFatal("No memory for string copy in Table_Set().");
+            logFatal("No memory for key copy in Table_Set().");
         }
         table->used++;
     }
@@
 }
 
-void *Table_Get(const Table *table, const void *key) {
+void *Table_Get(const Table *table, const void *key) {
@@
-    if (slot->state == TABLE_SLOT_EMPTY) {
-        return NULL;
-    }
-    return slot->value;
+    if (slot->state != TABLE_SLOT_USED) {
+        return NULL;
+    }
+    return slot->value;
 }
🧹 Nitpick comments (32)
src/common/iniparser.h (1)

60-61: Allowing ‘:’ in keys — verify scanner and ambiguity.

If iniparser.c tokenization uses a mirrored KEY_CHAR set, ensure it’s updated too. Also confirm no ambiguity with assignment (still ‘=’) and that section names with ‘:’ parse as intended. Recommend adding tests for keys like scope:name and sections [lang:cpp].

CMakeLists.txt (1)

57-62: Symlink to data may fail on Windows; add portable fallback.

create_symlink can require admin/dev-mode on Windows and fail silently here. Provide a fallback copy to keep tests/data available cross‑platform.

-execute_process(
-    COMMAND ${CMAKE_COMMAND} -E create_symlink
-        ${CMAKE_SOURCE_DIR}/data
-        ${CMAKE_BINARY_DIR}/data
-)
+execute_process(
+  COMMAND ${CMAKE_COMMAND} -E create_symlink
+          ${CMAKE_SOURCE_DIR}/data
+          ${CMAKE_BINARY_DIR}/data
+  RESULT_VARIABLE _symlink_result
+)
+if(NOT _symlink_result EQUAL 0)
+  message(WARNING "Symlink to data/ failed; falling back to copy.")
+  file(COPY ${CMAKE_SOURCE_DIR}/data DESTINATION ${CMAKE_BINARY_DIR})
+endif()
src/io/file.h (2)

19-19: limits.h error in tool output — keep include, but add portable fallback if needed.

limits.h is the right header for PATH_MAX on POSIX; the clang error likely indicates an environment issue. If you want a portable fallback, gate it:

-#include <limits.h>     // PATH_MAX
+#if defined(_WIN32)
+  #include <windows.h>
+  #ifndef PATH_MAX
+    #define PATH_MAX MAX_PATH
+  #endif
+#else
+  #include <limits.h>   // PATH_MAX
+#endif

Please confirm CI toolchain has standard headers available (POSIX or MSVC).


56-64: Clarify search order in docs and error signaling.

Document actual precedence implemented by find_project_file() and consider returning a richer status (e.g., errno or enum) for better diagnostics when lookup fails.

src/widgets/components/bottombar.c (1)

71-71: Initialize from parent safely and reuse on_resize for consistency.
Guard parent and drive initial geometry via your resize handler to keep logic in one place.

 void BottomBar_Init(BottomBar *self, Widget *parent) {
   Widget_Init(AS_WIDGET(self), parent, &bottombar_ops);
-  self->base.x = 0;
-  self->base.y = 0;
-  self->base.width = parent->width;
-  self->base.height = 1;
+  self->base.x = 0;
+  self->base.y = 0;
+  self->base.width = parent ? parent->width : 0;
+  self->base.height = 1;
+  if (parent) {
+    bottombar_handle_resize(AS_WIDGET(self), parent->width, parent->height);
+  }

Ensure parent is always non-NULL here; otherwise this guard is required.

tests/test_table.c (1)

118-129: Avoid integer-to-pointer casts in tests; use real addresses.
Casting small integers to pointers is implementation-defined. Use distinct object addresses to avoid UB.

 void test_ptr_table(void) {
   Table *table = Table_CreatePtr();
-
-  Table_Set(table, (void*)1, strdup("Foobar"), free);
-  Table_Set(table, (void*)2, strdup("Blub"), free);
-
-  TEST_CHECK(strcmp(Table_Get(table, (void*)1), "Foobar") == 0);
-  TEST_CHECK(strcmp(Table_Get(table, (void*)2), "Blub") == 0);
+  int k1, k2;
+  void *key1 = (void*)&k1;
+  void *key2 = (void*)&k2;
+  Table_Set(table, key1, strdup("Foobar"), free);
+  Table_Set(table, key2, strdup("Blub"), free);
+
+  TEST_CHECK(strcmp(Table_Get(table, key1), "Foobar") == 0);
+  TEST_CHECK(strcmp(Table_Get(table, key2), "Blub") == 0);
 
   Table_Destroy(table);
 }

Run this on both 32- and 64-bit to ensure no pointer-size assumptions leak into the test.

Also applies to: 136-136

data/syntax/ini.ini (4)

8-8: Duplicate child entry in root.

comment appears twice; dedupe to avoid redundant scans and potential double-links.

-[block:root]
-child_blocks = comment, section, comment, assignment
+[block:root]
+child_blocks = comment, section, assignment

26-30: Tighten assignment matcher.

Anchor at BOL and use explicit whitespace to reduce false positives mid‑line.

-start = [ \t]*[.\-_:a-zA-Z0-9]+[ \t]*=[ \t]*
+start = "^[ \t]*[.\-_:a-zA-Z0-9]+[ \t]*=[ \t]*"
 end = $
 color=82
 child_blocks= number, string, bare_string, comment

35-40: Quote handling in string block: prefer explicit anchors.

Be explicit and consistent with quoting; ensure the end includes EOL fallback.

-start = "\""
-end = "\"|$"
+start = "\""
+end = "\"|$"

If the parser does not process backslash escapes inside quotes, switch to single quotes for the whole value and use raw double quote inside: start = '"' and end = '"|$'. Please confirm parser semantics.


41-46: Constrain bare_string to avoid eating comments/quotes.

Using . with end $ will swallow everything; since you already ends_on = comment, also exclude ; # " to reduce over-match.

-start = "."
-end = $
+start = "[^;#\"]+"
+end = "$"
 color = 67
 ends_on = comment
src/common/stack.h (1)

35-40: API surface looks consistent with implementation.

Constructors/copy helpers align with stack.c. Consider moving STACK_GROW_FACTOR to .c if not part of the public contract.

data/syntax/md.ini (1)

9-14: Remove stray comment under title1.

The comment about INI comments is unrelated/noise here.

 [block:title1]
-# comments start with ";" or "#"
 start = "^# (.*)$"
 color = 33
 child_blocks =
src/io/file.c (1)

95-108: Sanity‑check rel_path to reduce traversal risk.

If rel_path can be user‑provided, normalize or reject .. components to keep lookups within expected roots.

Would you like a small helper (rejects absolute paths and .. segments) added to file.c?

tests/test_syntax_definition.c (1)

176-184: Consider adding cases for duplicate child_blocks and ends_on order.

Add tests to confirm duplicates are ignored (or explicitly allowed) and that ends_on order is preserved.

I can draft two minimal tests mirroring your existing style.

src/common/config.c (1)

42-50: Init path looks good; minor null‑guard suggestion.

If argv0 can be NULL in tests, consider defaulting exe_path to empty string.

src/common/buffer.c (1)

47-55: Avoid non‑standard void arithmetic.*

Cast to char * for portable pointer math.

-    memcpy(buffer->buffer + new_pos * buffer->element_size, element, buffer->element_size);
+    memcpy((char*)buffer->buffer + new_pos * buffer->element_size, element, buffer->element_size);
-        void *source = buffer->buffer + buffer->cursor * buffer->element_size;
+        void *source = (char*)buffer->buffer + buffer->cursor * buffer->element_size;
-    memcpy(out_element, buffer->buffer + peek_pos * buffer->element_size, buffer->element_size);
+    memcpy(out_element, (char*)buffer->buffer + peek_pos * buffer->element_size, buffer->element_size);

Also applies to: 64-66, 98-99

src/syntax/loader.h (1)

22-41: Export Init prototype and fix documentation typos

The implementation provides SyntaxHighlightingLoaderError_Init but the header doesn’t declare it. Also fix typos in the doc comment.

 typedef struct {
     SyntaxHighlightingLoaderErrorCode code;
     SyntaxDefinitionError def_error;
     ParsingError parsing_error;
 } SyntaxHighlightingLoaderError;
 
+void SyntaxHighlightingLoaderError_Init(SyntaxHighlightingLoaderError *error);
 void SyntaxHighlightingLoaderError_Deinit(SyntaxHighlightingLoaderError *error);
@@
- * Load the syntax definition for `format` from data/sytnax/<format>.ini
+ * Load the syntax definition for `format` from data/syntax/<format>.ini
@@
- * NULL there is definition file or if an error occured.
+ * NULL if there is no definition file or if an error occurred.
@@
- * Note theat `format` needs to be exactly the same as the filename.
+ * Note that `format` needs to be exactly the same as the filename.
src/syntax/loader.c (1)

31-38: Optional: sanitize format to prevent path traversal

format is interpolated into a relative path. Consider restricting to [A-Za-z0-9_-] to avoid “../” or separators.

     // 1. load the ini file
     char filename[PATH_MAX];
+    for (const char *p = format; *p; ++p) {
+        unsigned char c = (unsigned char)*p;
+        if (!(c == '_' || c == '-' || (c >= '0' && c <= '9') ||
+              (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'))) {
+            error->code = SYNTAX_LOADER_NO_FILENAME;
+            return NULL;
+        }
+    }
     snprintf(filename, PATH_MAX, "data/syntax/%s.ini", format);

If you prefer allowing dots (e.g., “c++”), adjust the predicate accordingly and add tests in tests/test_syntax_loader.c.

src/common/stack.c (1)

142-148: Make Stack_IsEmpty/Stack_Size null-safe.

Small guard avoids UB at call sites.

 bool Stack_IsEmpty(const Stack *stack) {
-    return stack->size == 0;
+    return !stack || stack->size == 0;
 }
 
 size_t Stack_Size(const Stack *stack) {
-    return stack->size;
+    return stack ? stack->size : 0;
 }
src/main.c (1)

47-50: Update help text to reflect supported options.

-static void print_help(const char *program_name) {
-    fprintf(stderr, "Usage:\n  %s <filename>\n", program_name);
+static void print_help(const char *program_name) {
+    fprintf(stderr, "Usage:\n  %s [-s <syntax>] <filename>\n  %s -h\n", program_name, program_name);
     exit(0);
 }
tests/test_syntax_highlighting.c (1)

58-59: Minor test hygiene: avoid String_Format for literals; fix message text.

  • Prefer String_FromCStr for non-formatted literals to skip a vsnprintf pass.
  • Clarify the assertion message.
-    String str = String_Format(testcase.str);
+    String str = String_FromCStr(testcase.str, strlen(testcase.str));
@@
-    TEST_CHECK(!Stack_IsEmpty(open_blocks));
-    TEST_MSG("Expected open_blocks to not be not empty.");
+    TEST_CHECK(!Stack_IsEmpty(open_blocks));
+    TEST_MSG("Expected open_blocks to be non-empty.");

Also applies to: 91-93

src/common/table.c (2)

30-41: Destructor vs key free order.

If a value destructor needs the key, freeing the key first is risky. Prefer destroying value before freeing the key. Low likelihood, but safer ordering.

-    if (slot->key && free_key_func) {
-        free_key_func(slot->key);
-    }
-    if (slot->destructor) {
+    if (slot->destructor) {
         slot->destructor(slot->value);
     }
+    if (slot->key && free_key_func) {
+        free_key_func(slot->key);
+    }

169-176: Initialize function pointers to NULL in Table_Init.

Defensive: prevents accidental use of uninitialized callbacks when Table_Init is used directly.

 void Table_Init(Table *table) {
@@
     table->slots = NULL;
     table->capacity = 0;
     table->used = 0;
+    table->hash_func = NULL;
+    table->key_cmp_func = NULL;
+    table->key_copy_func = NULL;
+    table->key_free_func = NULL;
 }
src/syntax/definition.h (1)

151-161: Doc typos and clarity.

“strcut”, “occures”, and pluralization nits. Consider fixing to reduce confusion in public API docs.

src/syntax/textlayoutbindings.c (4)

26-45: Make open_blocks param const; stop when state repeats (good).

The function doesn’t mutate open_blocks; take const Stack * to document intent. Logic to stop when state matches is solid.

-static void update_following_lines(SyntaxHighlightingBinding *binding, const Line *first_line, const Line *last_line, Stack *open_blocks) {
-    const Stack *open_blocks_begin = open_blocks;
+static void update_following_lines(SyntaxHighlightingBinding *binding, const Line *first_line, const Line *last_line, const Stack *open_blocks) {
+    const Stack *open_blocks_begin = open_blocks;

56-71: Avoid recursion for previous-line priming.

Recursive call on long documents can blow the stack. Convert to an iterative walk up to the nearest highlighted ancestor line.

-    if (prev_line) {
-        // check if highlighting for the previous line is already calculated
-        SyntaxHighlightingString *shs = Table_Get(binding->sh->strings, &prev_line->text);
-        if (shs) {
-            // use the open_blocks from the end of previous line
-            open_blocks = &shs->open_blocks_at_end;
-        }
-        else {
-            // highlighting for the line is not calculated so far
-            // so run this function for thr previous line
-            SyntaxHighlightingBinding_UpdateLine(binding, prev_line, last_line);
-            return;
-        }
-    }
+    if (prev_line) {
+        const Line *seek = prev_line;
+        const Stack *found = NULL;
+        while (seek) {
+            SyntaxHighlightingString *shs_prev = Table_Get(binding->sh->strings, &seek->text);
+            if (shs_prev) { found = &shs_prev->open_blocks_at_end; break; }
+            seek = seek->prev;
+        }
+        if (found) {
+            open_blocks = (Stack*)found; // cast to reuse variable; function only reads it
+        } else {
+            // prime from the begin of file
+            seek = TextBuffer_GetFirstLine(binding->tl->tb);
+            update_following_lines(binding, seek, prev_line, NULL);
+            SyntaxHighlightingString *shs_prev = Table_Get(binding->sh->strings, &prev_line->text);
+            open_blocks = shs_prev ? &shs_prev->open_blocks_at_end : NULL;
+        }
+    }

72-77: Merging the gap on update disables gap-buffer benefits.

This forces compaction on every current-line update. If acceptable for now, gate it behind a config or limit to visible updates; otherwise, compute highlighting over a logical view of current line without merging.


57-58: Remove dead code and commented-out frees.

open_blocks is only used as a const input; the commented Stack_Destroy is irrelevant and misleading.

Also applies to: 81-83

src/common/table.h (1)

71-90: API surface looks good for generic keys. Minor doc polish.

Small nits in comments (“occure”, “ligit”, “with custom keys” spacing). Consider clarifying comparator contract: “key_cmp_func(a,b) must return 0 when equal, non‑zero otherwise.”

src/syntax/highlighting.c (2)

71-79: Off‑by‑one in capacity check.

tags_count + 1 >= tags_capacity reallocates one step too early. Check >= against current count instead.

-    if (shs->tags_count + 1 >= shs->tags_capacity) {
+    if (shs->tags_count >= shs->tags_capacity) {
         increase_tags_capacity(shs);
     }

89-100: Index type for reverse scan can overflow int.

Use size_t for indices and a reverse loop that avoids signed casts.

-    for (int i=(int)shs->tags_count-1; i>=0; i--) {
-        if (shs->tags[i].byte_offset == offset) {
-            return &shs->tags[i];
-        }
-    }
+    for (size_t i = shs->tags_count; i > 0; i--) {
+        const size_t idx = i - 1;
+        if (shs->tags[idx].byte_offset == offset) {
+            return &shs->tags[idx];
+        }
+    }
src/syntax/highlighting.h (1)

50-51: Consider whether SyntaxHighlightingTag_Deinit is necessary.

The implementation shows that Deinit simply calls Init, which only resets fields to NULL/0. Since the struct doesn't own any resources (as documented on line 42), the Deinit function provides no cleanup value. Consider removing it or adding a comment explaining why resetting fields is important for this type.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2417025 and 13dec61.

⛔ Files ignored due to path filters (1)
  • assets/screenshot.png is excluded by !**/*.png
📒 Files selected for processing (37)
  • .coderabbit.yaml (1 hunks)
  • CMakeLists.txt (2 hunks)
  • data/syntax/ini.ini (1 hunks)
  • data/syntax/md.ini (1 hunks)
  • src/common/buffer.c (2 hunks)
  • src/common/buffer.h (3 hunks)
  • src/common/config.c (4 hunks)
  • src/common/config.h (1 hunks)
  • src/common/iniparser.c (1 hunks)
  • src/common/iniparser.h (1 hunks)
  • src/common/stack.c (3 hunks)
  • src/common/stack.h (1 hunks)
  • src/common/string.c (2 hunks)
  • src/common/string.h (1 hunks)
  • src/common/table.c (14 hunks)
  • src/common/table.h (4 hunks)
  • src/io/file.c (3 hunks)
  • src/io/file.h (3 hunks)
  • src/main.c (7 hunks)
  • src/syntax/definition.c (1 hunks)
  • src/syntax/definition.h (1 hunks)
  • src/syntax/highlighting.c (1 hunks)
  • src/syntax/highlighting.h (1 hunks)
  • src/syntax/loader.c (1 hunks)
  • src/syntax/loader.h (1 hunks)
  • src/syntax/textlayoutbindings.c (1 hunks)
  • src/syntax/textlayoutbindings.h (1 hunks)
  • src/widgets/components/bottombar.c (1 hunks)
  • src/widgets/components/editor.c (6 hunks)
  • src/widgets/components/editor.h (2 hunks)
  • tests/CMakeLists.txt (1 hunks)
  • tests/test_string.c (1 hunks)
  • tests/test_syntax_definition.c (1 hunks)
  • tests/test_syntax_highlighting.c (1 hunks)
  • tests/test_syntax_loader.c (1 hunks)
  • tests/test_syntax_textlayoutbindings.c (1 hunks)
  • tests/test_table.c (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-10-25T12:50:44.423Z
Learnt from: defname
PR: defname/clieditor#88
File: src/io/file.h:19-19
Timestamp: 2025-10-25T12:50:44.423Z
Learning: In src/io/file.h, the inclusion of <limits.h> to expose PATH_MAX is intentional so that users of the File module have access to PATH_MAX for working with file paths.

Applied to files:

  • src/io/file.h
🧬 Code graph analysis (22)
tests/test_syntax_definition.c (4)
src/common/table.c (2)
  • Table_Create (194-200)
  • Table_Destroy (228-234)
src/common/typedtable.c (2)
  • TypedTable_SetStringCopy (54-60)
  • TypedTable_SetNumber (39-44)
src/syntax/definition.c (5)
  • SyntaxBlockDef_FromTable (73-132)
  • SyntaxBlockDef_Destroy (56-70)
  • SyntaxDefinitionError_Deinit (36-38)
  • SyntaxDefinition_FromTable (329-375)
  • SyntaxDefinition_Destroy (377-391)
src/common/iniparser.c (4)
  • IniParser_Init (362-367)
  • IniParser_SetText (389-391)
  • IniParser_Parse (401-421)
  • IniParser_Deinit (369-372)
src/common/stack.h (1)
src/common/stack.c (6)
  • Stack_Create (56-63)
  • Stack_Destroy (65-71)
  • Stack_Copy (73-80)
  • Stack_CopyTo (82-103)
  • Stack_IsEmpty (142-144)
  • Stack_Size (146-148)
src/common/buffer.h (1)
src/common/buffer.c (6)
  • Buffer_Clear (42-45)
  • Buffer_IsEmpty (73-75)
  • Buffer_Size (77-79)
  • Buffer_Capacity (81-83)
  • Buffer_HasSpace (85-87)
  • Buffer_Peek (89-100)
src/syntax/definition.c (4)
src/common/string.c (9)
  • String_AsCStr (331-333)
  • String_Take (318-329)
  • String_Deinit (264-281)
  • String_Format (404-431)
  • String_FromView (433-435)
  • String_Trim (591-612)
  • String_Length (301-303)
  • String_FromCStr (335-351)
  • String_Split (614-669)
src/common/typedtable.c (3)
  • TypedTable_GetString (100-109)
  • TypedTable_GetNumber (89-98)
  • TypedTable_GetTable (122-131)
src/common/table.c (5)
  • Table_GetUsage (336-341)
  • Table_Set (237-263)
  • Table_Get (265-282)
  • Table_Create (194-200)
  • Table_Destroy (228-234)
src/common/tableiterator.c (2)
  • TableIterator_Begin (3-9)
  • TableIterator_Next (11-24)
src/syntax/highlighting.c (3)
src/common/stack.c (7)
  • Stack_Init (39-44)
  • Stack_Deinit (46-54)
  • Stack_Clear (150-155)
  • Stack_CopyTo (82-103)
  • Stack_Push (105-114)
  • Stack_Peek (123-128)
  • Stack_Pop (116-121)
src/common/table.c (4)
  • Table_CreatePtr (202-208)
  • Table_Destroy (228-234)
  • Table_Get (265-282)
  • Table_Set (237-263)
src/syntax/definition.c (1)
  • SyntaxDefinition_Destroy (377-391)
src/syntax/loader.c (5)
src/syntax/definition.c (2)
  • SyntaxDefinitionError_Deinit (36-38)
  • SyntaxDefinition_FromTable (329-375)
src/io/file.c (3)
  • File_OpenProjectFile (122-128)
  • File_Read (169-193)
  • File_Close (130-141)
src/common/iniparser.c (5)
  • IniParser_Init (362-367)
  • IniParser_SetText (389-391)
  • IniParser_Parse (401-421)
  • IniParser_GetError (397-399)
  • IniParser_Deinit (369-372)
src/common/table.c (1)
  • Table_Destroy (228-234)
src/syntax/highlighting.c (1)
  • SyntaxHighlighting_Create (124-131)
tests/test_syntax_loader.c (3)
src/common/config.c (3)
  • Config_GetExePath (93-95)
  • Config_Init (42-52)
  • Config_Deinit (54-62)
src/syntax/loader.c (2)
  • SyntaxHighlighting_LoadFromFile (20-76)
  • SyntaxHighlightingLoaderError_Deinit (12-17)
src/syntax/highlighting.c (1)
  • SyntaxHighlighting_Destroy (133-139)
src/io/file.h (1)
src/io/file.c (3)
  • File_Exists (38-43)
  • File_OpenConfig (114-120)
  • File_OpenProjectFile (122-128)
src/syntax/definition.h (1)
src/syntax/definition.c (6)
  • SyntaxDefinitionError_Deinit (36-38)
  • SyntaxBlockDef_Create (41-53)
  • SyntaxBlockDef_Destroy (56-70)
  • SyntaxBlockDef_FromTable (73-132)
  • SyntaxDefinition_FromTable (329-375)
  • SyntaxDefinition_Destroy (377-391)
src/common/config.h (1)
src/common/config.c (5)
  • Config_GetExePath (93-95)
  • Config_SetFilename (97-104)
  • Config_GetFilename (106-108)
  • Config_SetSyntax (111-114)
  • Config_GetSyntax (116-118)
src/syntax/loader.h (1)
src/syntax/loader.c (2)
  • SyntaxHighlightingLoaderError_Deinit (12-17)
  • SyntaxHighlighting_LoadFromFile (20-76)
src/syntax/textlayoutbindings.h (1)
src/syntax/textlayoutbindings.c (5)
  • SyntaxHighlightingBinding_Init (3-7)
  • SyntaxHighlightingBinding_Deinit (9-12)
  • SyntaxHighlightingBinding_UpdateLine (47-83)
  • SyntaxHighlightingBinding_Update (85-97)
  • SyntaxHighlightingBinding_UpdateAll (99-110)
src/io/file.c (1)
src/common/config.c (1)
  • Config_GetExePath (93-95)
tests/test_syntax_highlighting.c (6)
src/common/iniparser.c (4)
  • IniParser_Init (362-367)
  • IniParser_SetText (389-391)
  • IniParser_Parse (401-421)
  • IniParser_Deinit (369-372)
src/syntax/definition.c (1)
  • SyntaxDefinition_FromTable (329-375)
src/common/table.c (4)
  • Table_Destroy (228-234)
  • Table_Create (194-200)
  • Table_Set (237-263)
  • Table_Get (265-282)
src/common/string.c (3)
  • String_Format (404-431)
  • String_Deinit (264-281)
  • String_FromCStr (335-351)
src/syntax/highlighting.c (3)
  • SyntaxHighlighting_Init (105-108)
  • SyntaxHighlighting_HighlightString (210-323)
  • SyntaxHighlighting_Deinit (110-122)
src/common/stack.c (6)
  • Stack_Create (56-63)
  • Stack_Push (105-114)
  • Stack_IsEmpty (142-144)
  • Stack_Size (146-148)
  • Stack_Destroy (65-71)
  • Stack_Peek (123-128)
src/widgets/components/editor.c (7)
src/syntax/textlayoutbindings.c (4)
  • SyntaxHighlightingBinding_Deinit (9-12)
  • SyntaxHighlightingBinding_Update (85-97)
  • SyntaxHighlightingBinding_UpdateAll (99-110)
  • SyntaxHighlightingBinding_Init (3-7)
src/document/textselection.c (1)
  • TextSelection_Deinit (25-30)
src/document/textlayout.c (3)
  • TextLayout_GetVisualLine (389-401)
  • VisualLine_GetChar (97-125)
  • TextLayout_GetCursorLayoutInfo (416-473)
src/common/table.c (1)
  • Table_Get (265-282)
src/common/stack.c (1)
  • Stack_Peek (123-128)
src/syntax/highlighting.c (1)
  • SyntaxHighlightingString_GetTag (89-100)
src/common/utf8_helper.c (1)
  • utf8_to_codepoint (57-96)
tests/test_table.c (1)
src/common/table.c (4)
  • Table_CreatePtr (202-208)
  • Table_Set (237-263)
  • Table_Get (265-282)
  • Table_Destroy (228-234)
src/syntax/highlighting.h (1)
src/syntax/highlighting.c (12)
  • SyntaxHighlightingTag_Init (7-11)
  • SyntaxHighlightingTag_Deinit (13-15)
  • SyntaxHighlightingString_Create (20-39)
  • SyntaxHighlightingString_Destroy (41-53)
  • SyntaxHighlightingString_AddTag (71-79)
  • SyntaxHighlightingString_Clear (81-87)
  • SyntaxHighlightingString_GetTag (89-100)
  • SyntaxHighlighting_Init (105-108)
  • SyntaxHighlighting_Deinit (110-122)
  • SyntaxHighlighting_Create (124-131)
  • SyntaxHighlighting_Destroy (133-139)
  • SyntaxHighlighting_HighlightString (210-323)
src/common/stack.c (2)
src/common/table.c (1)
  • increase_capacity (121-167)
src/display/widget.c (1)
  • increase_capacity (86-106)
tests/test_syntax_textlayoutbindings.c (9)
src/document/textbuffer.c (4)
  • TextBuffer_Init (29-34)
  • TextBuffer_InsertLineAtBottom (90-94)
  • TextBuffer_GetFirstLine (105-111)
  • TextBuffer_Deinit (36-48)
src/common/string.c (2)
  • String_FromCStr (335-351)
  • String_Take (318-329)
src/document/line.c (1)
  • Line_Create (21-31)
src/common/iniparser.c (4)
  • IniParser_Init (362-367)
  • IniParser_SetText (389-391)
  • IniParser_Parse (401-421)
  • IniParser_Deinit (369-372)
src/syntax/definition.c (1)
  • SyntaxDefinition_FromTable (329-375)
src/common/table.c (2)
  • Table_Destroy (228-234)
  • Table_Get (265-282)
src/document/textlayout.c (2)
  • TextLayout_Init (128-140)
  • TextLayout_Deinit (142-155)
src/syntax/highlighting.c (2)
  • SyntaxHighlighting_Init (105-108)
  • SyntaxHighlighting_Deinit (110-122)
src/syntax/textlayoutbindings.c (3)
  • SyntaxHighlightingBinding_Init (3-7)
  • SyntaxHighlightingBinding_Deinit (9-12)
  • SyntaxHighlightingBinding_UpdateLine (47-83)
src/syntax/textlayoutbindings.c (4)
src/common/table.c (1)
  • Table_Get (265-282)
src/syntax/highlighting.c (1)
  • SyntaxHighlighting_HighlightString (210-323)
src/document/textbuffer.c (3)
  • TextBuffer_MergeGap (60-77)
  • TextBuffer_GetLastLine (113-119)
  • TextBuffer_GetFirstLine (105-111)
src/document/textlayout.c (1)
  • TextLayout_GetVisualLine (389-401)
src/common/table.h (1)
src/common/table.c (13)
  • TableSlot_Init (23-28)
  • TableSlot_Deinit (30-41)
  • Table_Init (169-176)
  • Table_Deinit (178-192)
  • Table_Create (194-200)
  • Table_CreatePtr (202-208)
  • Table_CreateCustom (210-226)
  • Table_Set (237-263)
  • Table_Get (265-282)
  • Table_Delete (284-304)
  • Table_Has (306-319)
  • Table_HasOwnership (321-334)
  • Table_GetUsage (336-341)
src/main.c (4)
src/common/config.c (4)
  • Config_SetSyntax (111-114)
  • Config_SetFilename (97-104)
  • Config_Init (42-52)
  • Config_GetSyntax (116-118)
src/syntax/highlighting.c (1)
  • SyntaxHighlighting_Destroy (133-139)
src/syntax/loader.c (2)
  • SyntaxHighlighting_LoadFromFile (20-76)
  • SyntaxHighlightingLoaderError_Deinit (12-17)
src/io/file.c (1)
  • File_OpenConfig (114-120)
🪛 Clang (14.0.6)
src/io/file.h

[error] 19-19: 'limits.h' file not found

(clang-diagnostic-error)

src/syntax/definition.h

[error] 68-68: 'stdint.h' file not found

(clang-diagnostic-error)

src/syntax/textlayoutbindings.h

[error] 4-4: 'stdbool.h' file not found

(clang-diagnostic-error)

src/syntax/highlighting.h

[error] 30-30: 'stddef.h' file not found

(clang-diagnostic-error)

src/common/table.h

[error] 33-33: 'stdint.h' file not found

(clang-diagnostic-error)

🔇 Additional comments (24)
src/common/string.h (1)

181-181: Doc correction looks right; confirm behavior matches.

Change clarifies that String_Take deinitializes src. Please confirm implementation actually deinitializes the source to avoid double-free/aliasing surprises.

.coderabbit.yaml (1)

175-176: LGTM — style-only YAML change.

Block-style list for base_branches is semantically identical to ['.*'] and valid per schema.

CMakeLists.txt (2)

8-9: Nice DX improvement.

CMAKE_EXPORT_COMPILE_COMMANDS=ON helps tooling (clangd, static analyzers).


64-66: CTest enablement — good.

enable_testing() placement at root is correct; subdir tests will be discoverable.

src/common/buffer.h (2)

51-55: Clear API is useful.

Resetting count and cursor is correct and matches implementation.


78-85: Const-correctness improvements — good.

IsEmpty, Size, Capacity, Peek taking const Buffer* aligns with read-only usage.

Also applies to: 96-96

tests/test_string.c (1)

254-254: No functional change; safe to merge.
Extra blank line only. Nothing to do.

src/common/iniparser.c (1)

145-145: Allowing ':' in keys looks good; add targeted tests and doc note.
This enables namespaced keys/sections. Please:

  • Add tests for keys with colon (both in sections and assignments).
  • Add a negative test to ensure "key: value" (colon-as-assignment) still errors as intended.
src/common/string.c (1)

538-540: Good catch: reserve space for the trailing null.
Updating the capacity check to include +1 prevents off-by-one overwrites on append.

Consider adding a regression test appending a view that exactly fills capacity to ensure no overflow and correct termination.

src/common/config.h (1)

32-32: Config_GetExePath API LGTM.
Returning const char* is appropriate; document lifetime (valid until Config_Deinit).

tests/CMakeLists.txt (1)

24-24: Code change verified and approved.

The verification confirms that enable_testing() is properly invoked in the root CMakeLists.txt at line 65, which validates the use of $<TARGET_FILE:...> generator expressions in the add_test call. The configuration is correct.

src/widgets/components/editor.h (1)

25-25: Lifecycle management of sh_binding is properly handled.

Verification confirms that SyntaxHighlightingBinding_Init is called during Editor_Init (line 459) and SyntaxHighlightingBinding_Deinit is called during Editor destruction (line 39). The by-value embedding is reasonable for a tightly-coupled binding component. Consider a pointer-based approach only if decoupling from the TextLayout header becomes necessary in the future; the current design avoids heap allocation overhead.

src/common/stack.h (1)

47-49: Nice const‑correct accessors.

Stack_IsEmpty/Stack_Size signatures are appropriate.

src/io/file.c (1)

38-43: File_Exists is fine for read paths.

R_OK is sensible given current read‑only usage. If you later use it for write discovery, consider W_OK or F_OK.

src/common/buffer.c (1)

42-46: Buffer_Clear is a welcome addition.

Matches typical ring buffer semantics.

data/syntax/ini.ini (1)

47-50: Revert suggested change—original escape pattern is correct.

The original pattern correctly matches all escape sequences. After INI string parsing (\\ → \), "\\\\[\\\\\\nt'\"]" becomes regex \\[\\nt'"], which matches \ followed by any of: \, n, t, ', ". The suggested change would remove the ability to match \\ (escaped backslash), breaking functionality.

Likely an incorrect or invalid review comment.

src/main.c (1)

214-217: Ensure binding is fully initialized, not just sh assigned.

Confirm EditorView_Create (or Editor) calls SyntaxHighlightingBinding_Init and sets tl; otherwise call it here to avoid partial state.

Would you confirm where SyntaxHighlightingBinding_Init is invoked for editor->editor->sh_binding? If not initialized, we should do:

SyntaxHighlightingBinding_Init(&editor->editor->sh_binding, editor->editor->layout, highlighting);
src/syntax/definition.c (1)

41-71: Solid construction, linking, and cleanup paths.

Block creation, error propagation, and cleanup look correct; no issues found.

Also applies to: 142-218, 329-375

src/syntax/highlighting.c (2)

195-208: Good: cache reset for all blocks before a run.

This avoids cross‑line contamination. Nice.


110-122: Lifecycle ownership is clear and safe.

SyntaxHighlighting_Deinit destroys table and def; Destroy wraps Deinit. LGTM.

Also applies to: 133-139

src/syntax/definition.h (1)

74-85: ****

The code is correct. NO_ERROR in src/syntax/definition.c:26 is a macro that properly initializes the entire error structure: #define NO_ERROR ((SyntaxDefinitionError){ .code = SYNTAXDEFINITION_NO_ERROR, .message = (String){0} }). It correctly uses the enum constant from the header and compiles without issues. The assignment at line 74 is valid and idiomatic.

Likely an incorrect or invalid review comment.

src/syntax/highlighting.h (3)

76-85: Note the performance consideration for SyntaxHighlightingString_GetTag.

The implementation uses a linear search through tags (O(n)), which the developer has already flagged with a TODO comment about needing "massive optimization". For strings with many syntax elements, this could become a bottleneck if called frequently during rendering.


30-30: No action needed—static analysis warning is confirmed as a false positive.

The build system exists and multiple files in the codebase, including the file under review, successfully include stddef.h. This confirms the warning stems from tool configuration rather than an actual missing header.


117-132: Fix parameter name inconsistency in documentation.

The documentation on line 127 refers to the parameter as open_blocks_at_begin, but the function signature on line 132 uses open_blocks. The implementation file uses open_blocks_at_begin. Standardize the naming for clarity.

  * @param hl The `SyntaxHighlighting` instance to use.
  * @param text The text to hightlight.
- * @param open_blocks An pointer to a `Stack` instance that holds the open blocks at the beginning of string. If NULL it's assumed that the root block is current.
+ * @param open_blocks_at_begin A pointer to a `Stack` instance that holds the open blocks at the beginning of string. If NULL it's assumed that the root block is current.
  * 
  * @returns
  * A reference to the `Stack` containing all open blocks at the end of `text`. 
  */
-const Stack *SyntaxHighlighting_HighlightString(SyntaxHighlighting *sh, const String *text, const Stack *open_blocks);
+const Stack *SyntaxHighlighting_HighlightString(SyntaxHighlighting *sh, const String *text, const Stack *open_blocks_at_begin);

Also note: line 126 has a typo "hightlight" → "highlight" and line 127 has "An pointer" → "A pointer".

Likely an incorrect or invalid review comment.

Comment thread data/syntax/ini.ini Outdated
Comment thread data/syntax/md.ini
Comment thread src/common/buffer.c
Comment thread src/common/buffer.h
Comment thread src/common/config.c
Comment thread src/syntax/loader.c
Comment thread src/syntax/textlayoutbindings.c
Comment thread src/syntax/textlayoutbindings.h
Comment thread tests/test_syntax_loader.c
Comment thread tests/test_syntax_textlayoutbindings.c

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/common/table.c (1)

237-263: Update error message to be generic.

Line 253 says "No memory for string copy" but this table now supports arbitrary key types, not just strings.

Apply this diff:

-            logFatal("No memory for string copy in Table_Set().");
+            logFatal("Key copy failed in Table_Set().");
♻️ Duplicate comments (4)
src/common/table.c (1)

54-57: hash_ptr has UB on 32‑bit platforms (shift by 32).

This issue was already identified in a previous review. The right-shift by 32 is undefined behavior when uintptr_t is 32 bits.

src/common/config.c (1)

112-119: Own the syntax string (avoid dangling pointer).

This issue was previously flagged and remains unresolved. Storing a raw pointer to the caller's string (e.g., from optarg in main.c line 64) creates a dangling pointer risk. The string should be owned via duplication, and freed in Config_Deinit().

See the previous review comment for a suggested fix.

src/main.c (1)

156-178: Fix String formatting and ensure unconditional error deinit.

This issue was previously flagged and remains unresolved:

  1. Line 170: Passing error.def_error.message (a String) to "%s" is undefined behavior. Use String_AsCStr(&error.def_error.message).
  2. Line 176: SyntaxHighlightingLoaderError_Deinit is only called on failure. It must be called unconditionally (also on success) to avoid leaks.

Apply this fix:

         case SYNTAX_LOADER_DEFINITION_ERROR:
-            logError("Syntax definition error.\n%s", error.def_error.message);
+            logError("Syntax definition error.\n%s", String_AsCStr(&error.def_error.message));
             break;
         default:
             logError("Could not load Syntaxdefinition (Code: %d)", error.code);
             break;
     }
-    SyntaxHighlightingLoaderError_Deinit(&error);
     highlighting = NULL;
 }
+SyntaxHighlightingLoaderError_Deinit(&error);
src/syntax/textlayoutbindings.c (1)

99-113: Add NULL guard for sh like Update does.

This issue was previously flagged. Line 100 lacks a binding->sh check that Update includes (line 86). While UpdateLine guards against NULL sh, failing fast here improves robustness and consistency.

Apply this fix:

 void SyntaxHighlightingBinding_UpdateAll(SyntaxHighlightingBinding *binding, bool force) {
-    if (!binding || !binding->tl || !binding->tl->tb) {
+    if (!binding || !binding->sh || !binding->tl || !binding->tl->tb) {
         return;
     }
     if (!force && !binding->need_full_update) {
🧹 Nitpick comments (4)
src/common/table.c (1)

194-200: Consider wrapper functions for better type safety.

The function pointer casts bypass the type system—hash_string expects const char* but the callback signature is const void*. While this is a common C pattern for generic containers, it relies entirely on runtime discipline. If a caller passes non-string keys to a string table, it will crash or corrupt memory.

To improve safety, consider using thin wrapper functions:

static uint32_t hash_string_wrapper(const void *p) {
    return hash_string((const char *)p);
}

static int strcmp_wrapper(const void *a, const void *b) {
    return strcmp((const char *)a, (const char *)b);
}

static void *strdup_wrapper(const void *p) {
    return strdup((const char *)p);
}

Table *Table_Create() {
    return Table_CreateCustom(
        hash_string_wrapper,
        strcmp_wrapper,
        strdup_wrapper,
        free);
}

This makes the type conversions explicit and easier to audit.

src/syntax/textlayoutbindings.c (2)

26-45: Consider removing or clarifying the commented-out code.

Line 39 contains //Stack_Destroy(open_blocks_end); which is correctly not called since open_blocks_end points to the internal stack within shs. Consider removing this commented line or adding a clarifying comment explaining why destruction is not needed.

     const Stack *open_blocks_end = SyntaxHighlighting_HighlightString(binding->sh, &line->text, open_blocks_begin);
     open_blocks_begin = open_blocks_end;
-    //Stack_Destroy(open_blocks_end);
+    // Note: open_blocks_end points to internal shs->open_blocks_at_end, not owned by us

72-76: Gap merging approach acknowledged as suboptimal.

The forced gap merge (line 75) with const-cast is acknowledged as "super dirty" by the inline comments. While this ensures correctness for current highlighting logic (including pointer arithmetic in editor.c line 115), consider refactoring to a gap-aware solution that doesn't require merging, preserving the gap buffer's performance benefits.

Potential approaches:

  • Implement gap-aware byte offset calculation that doesn't require pointer arithmetic.
  • Highlight on a merged copy rather than modifying the text buffer.
src/syntax/highlighting.c (1)

90-101: Acknowledged optimization opportunity.

The linear search (O(n)) for tag lookup is flagged by the TODO comment (line 91). Consider implementing binary search or an index structure if profiling shows this is a bottleneck.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 13dec61 and 4c9e5f2.

📒 Files selected for processing (9)
  • data/syntax/ini.ini (1 hunks)
  • data/syntax/md.ini (1 hunks)
  • src/common/buffer.c (2 hunks)
  • src/common/config.c (5 hunks)
  • src/common/table.c (14 hunks)
  • src/main.c (6 hunks)
  • src/syntax/highlighting.c (1 hunks)
  • src/syntax/textlayoutbindings.c (1 hunks)
  • src/widgets/components/editor.c (6 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • data/syntax/md.ini
  • data/syntax/ini.ini
🧰 Additional context used
🧬 Code graph analysis (5)
src/syntax/highlighting.c (3)
src/common/stack.c (7)
  • Stack_Init (39-44)
  • Stack_Deinit (46-54)
  • Stack_Clear (150-155)
  • Stack_CopyTo (82-103)
  • Stack_Push (105-114)
  • Stack_Peek (123-128)
  • Stack_Pop (116-121)
src/common/table.c (4)
  • Table_CreatePtr (202-208)
  • Table_Destroy (228-234)
  • Table_Get (265-282)
  • Table_Set (237-263)
src/syntax/definition.c (1)
  • SyntaxDefinition_Destroy (377-391)
src/main.c (4)
src/common/config.c (4)
  • Config_SetSyntax (112-115)
  • Config_SetFilename (98-105)
  • Config_Init (42-52)
  • Config_GetSyntax (117-119)
src/syntax/highlighting.c (1)
  • SyntaxHighlighting_Destroy (134-140)
src/syntax/loader.c (2)
  • SyntaxHighlighting_LoadFromFile (20-76)
  • SyntaxHighlightingLoaderError_Deinit (12-17)
src/io/file.c (1)
  • File_OpenConfig (114-120)
src/syntax/textlayoutbindings.c (4)
src/common/table.c (1)
  • Table_Get (265-282)
src/syntax/highlighting.c (1)
  • SyntaxHighlighting_HighlightString (211-324)
src/document/textbuffer.c (3)
  • TextBuffer_MergeGap (60-77)
  • TextBuffer_GetLastLine (113-119)
  • TextBuffer_GetFirstLine (105-111)
src/document/textlayout.c (1)
  • TextLayout_GetVisualLine (389-401)
src/widgets/components/editor.c (7)
src/syntax/textlayoutbindings.c (4)
  • SyntaxHighlightingBinding_Deinit (9-12)
  • SyntaxHighlightingBinding_Update (85-97)
  • SyntaxHighlightingBinding_UpdateAll (99-113)
  • SyntaxHighlightingBinding_Init (3-7)
src/document/textselection.c (1)
  • TextSelection_Deinit (25-30)
src/document/textlayout.c (3)
  • TextLayout_GetVisualLine (389-401)
  • VisualLine_GetChar (97-125)
  • TextLayout_GetCursorLayoutInfo (416-473)
src/common/table.c (1)
  • Table_Get (265-282)
src/common/stack.c (1)
  • Stack_Peek (123-128)
src/syntax/highlighting.c (1)
  • SyntaxHighlightingString_GetTag (90-101)
src/common/utf8_helper.c (1)
  • utf8_to_codepoint (57-96)
src/common/config.c (1)
src/common/iniparser.c (1)
  • IniParser_Deinit (369-372)
🔇 Additional comments (34)
src/common/buffer.c (3)

42-45: LGTM! Clean buffer reset implementation.

The function correctly resets the buffer to an empty state by zeroing the count and cursor. The underlying memory is preserved, which is appropriate for reuse.


73-89: LGTM! Excellent const-correctness improvements.

The addition of const qualifiers to Buffer_IsEmpty, Buffer_Size, Buffer_Capacity, and Buffer_Peek appropriately reflects that these operations are read-only. This strengthens the API contract and enables usage with const buffers.


85-87: LGTM! The off-by-one issue has been correctly fixed.

The current implementation correctly uses buffer->count < buffer->capacity, which is consistent with Buffer_Enqueue's capacity check on line 48. This allows the buffer to utilize its full capacity rather than stopping at capacity - 1. The issue raised in the previous review has been properly addressed.

src/common/table.c (9)

30-41: LGTM!

The callback-based key deallocation is correctly implemented with proper NULL checks for both the key and the free function.


59-61: LGTM!

The pointer comparison correctly implements the equality-check semantics required by the table (returns 0 when equal, non-zero otherwise).


67-98: LGTM!

The generic key implementation correctly delegates to the callback functions for hashing and comparison.


121-167: LGTM!

The capacity expansion correctly preserves the callback functions and uses them for key cleanup during rehashing.


178-192: LGTM!

Table cleanup correctly delegates key deallocation to the configured callback.


202-208: LGTM!

Pointer-key table configuration is correct, with NULL for the free function since pointer keys are not owned.


210-226: LGTM!

The custom constructor provides the necessary flexibility for arbitrary key types while maintaining a clean API.


265-334: LGTM!

The public API correctly handles generic keys and uses the configured callbacks appropriately.


336-341: LGTM!

The new usage query function is straightforward and correctly handles NULL input.

src/common/config.c (2)

42-52: LGTM!

The updated signature correctly initializes exe_path from argv[0], which remains valid for the program's lifetime. The syntax field is properly initialized to NULL.


98-105: LGTM!

Proper bounds checking with PATH_MAX - 1 and explicit null-termination ensure safe string handling.

src/widgets/components/editor.c (5)

36-43: LGTM!

Proper cleanup sequence: syntax highlighting binding is deinitialized before text selection and layout teardown.


107-122: Verify gap-merging guarantee across the highlighting pipeline.

The pointer arithmetic byte_offset = ch - line->src->text.bytes (line 115) assumes ch points within line->src->text.bytes. The inline comments acknowledge this fragility: "This will not always work if is_gap_line and the gap is not merged!!" While TextBuffer_MergeGap is called in textlayoutbindings.c (line 75), this creates tight coupling and risks undefined behavior if the gap-merging contract is violated in future changes.

Consider one of the following to reduce fragility:

  • Add a runtime assertion confirming the gap is merged before calculating byte_offset.
  • Refactor to avoid pointer arithmetic by using a gap-aware index calculation helper.

347-356: LGTM!

Triggering SyntaxHighlightingBinding_Update after successful input handling ensures highlighting stays synchronized with text changes.


379-394: LGTM!

Calling SyntaxHighlightingBinding_UpdateAll with force=false ensures highlighting is initialized once, respecting the internal need_full_update flag. Placement after cursor correction is appropriate.


432-459: LGTM!

Initializing the binding with NULL highlighting is appropriate here, as the actual SyntaxHighlighting instance is assigned later in main.c (line 211) after loading the syntax definition.

src/main.c (4)

139-141: LGTM!

Correctly passes argv[0] to the updated Config_Init signature.


197-205: LGTM!

Using File_OpenConfig simplifies config file access and aligns with the new helper API.


209-212: LGTM!

Assigning the loaded highlighting to editor->editor->sh_binding.sh completes the binding initialization.


119-130: LGTM!

SyntaxHighlighting_Destroy in finish() ensures proper cleanup. The function handles NULL safely.

src/syntax/textlayoutbindings.c (2)

3-12: LGTM!

Straightforward initialization and deinitialization of the binding, storing references and managing the update flag.


85-97: LGTM!

Comprehensive NULL guards and correct determination of the visible line range for incremental updates.

src/syntax/highlighting.c (9)

7-16: LGTM!

Simple tag lifecycle management with future extensibility in mind.


21-54: LGTM!

Proper allocation, initialization, and cleanup of SyntaxHighlightingString with appropriate error handling.


56-70: LGTM!

Standard capacity growth pattern with appropriate error handling.


72-88: LGTM!

AddTag and Clear correctly manage tags and stack state. Efficient reuse of allocated capacity.


106-140: LGTM!

Clean lifecycle management with proper ownership transfer of the syntax definition.


142-159: LGTM!

Clever regex match caching avoids redundant regex execution by reusing matches when the search offset moves forward. The offset arithmetic correctly translates cached absolute positions to new relative positions.


161-193: LGTM!

Helper functions correctly find the earliest matching block using cached regex execution.


196-209: LGTM!

Proper cache initialization before highlighting ensures clean state.


211-324: LGTM!

The core highlighting algorithm correctly implements a stack-based state machine that:

  • Identifies the earliest match among child start, ends_on, and current block end
  • Maintains open-block state via stack operations
  • Generates tags at precise byte offsets
  • Returns open-block state for cross-line propagation

The design assumes well-formed syntax definitions where the root block's end pattern never matches (line 321-322 comment). This is an appropriate contract.

Comment thread src/common/table.c
Comment on lines +63 to +65
static void *cpy_ptr(const void *p) {
return (void*)p;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Minor const-correctness issue: casting away const.

The cast (void*)p discards the const qualifier. While harmless here since pointer keys aren't modified, consider returning (void *)(uintptr_t)p to make the intent clearer.

🤖 Prompt for AI Agents
In src/common/table.c around lines 63 to 65, the helper cpy_ptr currently casts
away const via (void*)p; change it to return (void*)(uintptr_t)p to preserve
intent without discarding const, and ensure uintptr_t is available by adding
#include <stdint.h> at the top of the file if not already present; no other
logic changes required.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (2)
src/common/table.c (2)

54-57: UB on 32-bit platforms remains unfixed.

This issue was previously flagged: (x >> 32) is undefined when uintptr_t is 32 bits. The shift count must not equal or exceed the bit-width of the operand.


63-65: Const-correctness issue remains unaddressed.

This was previously noted: (void*)p discards the const qualifier. Use (void*)(uintptr_t)p to make the intent clearer.

🧹 Nitpick comments (1)
src/common/table.c (1)

210-226: Consider validating mandatory callback parameters.

Table_CreateCustom does not check whether hash_func, key_cmp_func, or key_copy_func are non-NULL. If any are NULL, the table will crash when used (e.g., lines 75, 89, 251). Either add validation with logFatal for these mandatory parameters, or document the precondition that callers must provide non-NULL callbacks.

Apply this diff to add validation:

 Table *Table_CreateCustom(
     uint32_t (*hash_func)(const void *p),
     int (*key_cmp_func)(const void*, const void *),
     void *(*key_copy_func)(const void *p),
     void (*key_free_func)(void *p)
 ) {
+    if (!hash_func || !key_cmp_func || !key_copy_func) {
+        logFatal("hash_func, key_cmp_func, and key_copy_func must be non-NULL in Table_CreateCustom.");
+    }
     Table *table = malloc(sizeof(Table));
     if (!table) {
         logFatal("Failed to allocate memory for table.");
     }
     Table_Init(table);
     table->hash_func = hash_func;
     table->key_cmp_func = key_cmp_func;
     table->key_copy_func = key_copy_func;
     table->key_free_func = key_free_func;
     return table;
 }
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4c9e5f2 and b7275a6.

📒 Files selected for processing (1)
  • src/common/table.c (14 hunks)
🔇 Additional comments (5)
src/common/table.c (5)

30-41: LGTM: Clean parameterization of key deallocation.

The updated signature correctly delegates key cleanup to the caller-supplied callback, maintaining consistency with the generic-key design.


59-61: LGTM: Comparison semantics are correct.

Returns 0 when pointers are equal, non-zero otherwise, matching the expected comparison function contract.


194-208: LGTM: Constructor refactoring is well-structured.

Both Table_Create and Table_CreatePtr correctly delegate to Table_CreateCustom with appropriate callbacks. The function pointer casts in Table_Create (lines 196-199) are safe because runtime arguments will match the original string-based types.


121-167: LGTM: Resize correctly preserves callback context.

increase_capacity properly propagates all four callbacks to the resized table (lines 126-131) and correctly frees old keys via key_free_func (lines 151-153) before deallocating slots. The ownership transfer in shallow_copy_table (line 117) is handled correctly.


237-341: LGTM: Public API correctly adopts generic-key semantics.

All public functions (Table_Set, Table_Get, Table_Delete, Table_Has, Table_HasOwnership) now accept const void *key and delegate key operations to the stored callbacks. The new Table_GetUsage accessor is straightforward and correct.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (3)
README.md (3)

59-59: Fix hyphenation in compound adjective.

"UTF-8 aware" should be hyphenated as "UTF-8-aware" when used as a compound adjective before a noun.

-The editor uses a custom, UTF-8 aware string library designed for a balance of performance and memory efficiency.
+The editor uses a custom, UTF-8-aware string library designed for a balance of performance and memory efficiency.

102-106: Correct markdown list indentation.

The nested list items under "Naming Conventions" use 4-space indentation, but markdown lists should use 2-space indentation for consistency.

 -   **Naming Conventions**:
-    -   Types and structs are `PascalCase` (e.g., `TextBuffer`).
-    -   Public functions are `Module_Function` (e.g., `TextBuffer_Init`).
-    -   Enums and macros are `ALL_CAPS`.
+    -   Types and structs are `PascalCase` (e.g., `TextBuffer`).
+    -   Public functions are `Module_Function` (e.g., `TextBuffer_Init`).
+    -   Enums and macros are `ALL_CAPS`.

128-128: Format bare URL as markdown link.

The GPL-3.0 license URL should be properly formatted as a markdown link rather than a bare URL.

-You may obtain a copy of the license at: https://www.gnu.org/licenses/gpl-3.0.html
+You may obtain a copy of the license at: [https://www.gnu.org/licenses/gpl-3.0.html](https://www.gnu.org/licenses/gpl-3.0.html)
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b7275a6 and 4d89c41.

📒 Files selected for processing (1)
  • README.md (1 hunks)
🧰 Additional context used
🪛 LanguageTool
README.md

[grammar] ~59-~59: Use a hyphen to join words.
Context: ... System The editor uses a custom, UTF-8 aware string library designed for a bala...

(QB_NEW_EN_HYPHEN)

🪛 markdownlint-cli2 (0.18.1)
README.md

103-103: Unordered list indentation
Expected: 2; Actual: 4

(MD007, ul-indent)


104-104: Unordered list indentation
Expected: 2; Actual: 4

(MD007, ul-indent)


105-105: Unordered list indentation
Expected: 2; Actual: 4

(MD007, ul-indent)


128-128: Bare URL used

(MD034, no-bare-urls)

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4d89c41 and e6a4289.

📒 Files selected for processing (1)
  • README.md (1 hunks)
🧰 Additional context used
🪛 LanguageTool
README.md

[grammar] ~59-~59: Use a hyphen to join words.
Context: ... System The editor uses a custom, UTF-8 aware string library designed for a bala...

(QB_NEW_EN_HYPHEN)

🪛 markdownlint-cli2 (0.18.1)
README.md

103-103: Inconsistent indentation for list items at the same level
Expected: 0; Actual: 2

(MD005, list-indent)


103-103: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


104-104: Inconsistent indentation for list items at the same level
Expected: 0; Actual: 2

(MD005, list-indent)


104-104: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


105-105: Inconsistent indentation for list items at the same level
Expected: 0; Actual: 2

(MD005, list-indent)


105-105: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


128-128: Bare URL used

(MD034, no-bare-urls)

🔇 Additional comments (1)
README.md (1)

1-128: Comprehensive architectural documentation; well-structured rewrite.

The README has been thoroughly rewritten to document the new syntax highlighting subsystem and overall architecture. The structure is clear and logical:

  • Features are well-articulated with an emphasis on implementation choices (gap buffer, regex-based highlighting, UTF-8 efficiency).
  • Architecture Overview provides good module separation and clarifies the role of the new syntax/ module.
  • Core Concepts explain key design decisions (String System caching, Widget System inheritance, Editor Engine layering, Syntax Highlighting declarative definitions).
  • Practical sections (How to Read, Coding Style, Future Goals) are detailed and actionable.
  • Usage documentation includes the new -s syntax flag with clear examples.

The documentation aligns well with the PR's objectives and provides a solid foundation for onboarding and future development.

Comment thread README.md
## Features

- **Handwritten UI Toolkit**: A custom-built widget system from scratch, featuring a status bar, main menu, notifications, and an editor widget.
- **Syntax Highlighting**: A flexible, regex-based highlighting engine. Syntaxes are defined in simple `.ini` files, making it easy to add new languages.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Hyphenate compound adjective "UTF-8-aware".

When "UTF-8-aware" precedes a noun, it should be hyphenated to form a compound adjective. This appears in two places: line 12 (Features section) and line 59 (Core Concepts section).

- -   **Syntax Highlighting**: A flexible, regex-based highlighting engine. Syntaxes are defined in simple `.ini` files, making it easy to add new languages.
+ -   **Syntax Highlighting**: A flexible, regex-based highlighting engine. Syntaxes are defined in simple `.ini` files, making it easy to add new languages.
- -   **Efficient UTF-8 Support**: A revised string library that handles UTF-8 characters correctly and efficiently, with optimized indexing for large strings.
+ -   **Efficient UTF-8-aware Support**: A revised string library that handles UTF-8 characters correctly and efficiently, with optimized indexing for large strings.

And on line 59:

- The editor uses a custom, UTF-8 aware string library designed for a balance of performance and memory efficiency.
+ The editor uses a custom, UTF-8-aware string library designed for a balance of performance and memory efficiency.

Also applies to: 59-59

🤖 Prompt for AI Agents
In README.md around lines 12 and 59, the phrase "UTF-8 aware" should be
hyphenated as "UTF-8-aware" when used as a compound adjective preceding a noun;
update both occurrences to "UTF-8-aware" to follow compound adjective
hyphenation rules.

Comment thread README.md
Comment on lines +102 to +105
- **Naming Conventions**:
- Types and structs are `PascalCase` (e.g., `TextBuffer`).
- Public functions are `Module_Function` (e.g., `TextBuffer_Init`).
- Enums and macros are `ALL_CAPS`.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix markdown list indentation.

The nested list items under "Naming Conventions" are indented with 2 spaces, but markdown expects them to align with their parent list markers (0 indentation). This causes markdownlint to flag inconsistent indentation.

 -   **Naming Conventions**:
-  -   Types and structs are `PascalCase` (e.g., `TextBuffer`).
-  -   Public functions are `Module_Function` (e.g., `TextBuffer_Init`).
-  -   Enums and macros are `ALL_CAPS`.
+ -   Types and structs are `PascalCase` (e.g., `TextBuffer`).
+ -   Public functions are `Module_Function` (e.g., `TextBuffer_Init`).
+ -   Enums and macros are `ALL_CAPS`.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- **Naming Conventions**:
- Types and structs are `PascalCase` (e.g., `TextBuffer`).
- Public functions are `Module_Function` (e.g., `TextBuffer_Init`).
- Enums and macros are `ALL_CAPS`.
- **Naming Conventions**:
- Types and structs are `PascalCase` (e.g., `TextBuffer`).
- Public functions are `Module_Function` (e.g., `TextBuffer_Init`).
- Enums and macros are `ALL_CAPS`.
🧰 Tools
🪛 markdownlint-cli2 (0.18.1)

103-103: Inconsistent indentation for list items at the same level
Expected: 0; Actual: 2

(MD005, list-indent)


103-103: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


104-104: Inconsistent indentation for list items at the same level
Expected: 0; Actual: 2

(MD005, list-indent)


104-104: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


105-105: Inconsistent indentation for list items at the same level
Expected: 0; Actual: 2

(MD005, list-indent)


105-105: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)

🤖 Prompt for AI Agents
In README.md around lines 102 to 105, the nested list under "Naming Conventions"
is indented with two leading spaces causing markdownlint to flag inconsistent
indentation; remove the extra two spaces so the nested list items align directly
under the parent list marker (i.e., start the lines with the list marker at
column 0), preserving the backtick formatting for examples.

Comment thread README.md
This project is licensed under the GNU General Public License v3.0 (GPL-3.0).

https://www.gnu.org/licenses/gpl-3.0.html
You may obtain a copy of the license at: https://www.gnu.org/licenses/gpl-3.0.html

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Wrap bare URL in markdown link syntax.

Line 128 contains a bare URL. Per markdownlint (MD034), URLs should be wrapped in markdown link syntax or angle brackets.

- You may obtain a copy of the license at: https://www.gnu.org/licenses/gpl-3.0.html
+ You may obtain a copy of the license at: <https://www.gnu.org/licenses/gpl-3.0.html>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
You may obtain a copy of the license at: https://www.gnu.org/licenses/gpl-3.0.html
You may obtain a copy of the license at: <https://www.gnu.org/licenses/gpl-3.0.html>
🧰 Tools
🪛 markdownlint-cli2 (0.18.1)

128-128: Bare URL used

(MD034, no-bare-urls)

🤖 Prompt for AI Agents
In README.md around line 128, the license URL is a bare link which violates
markdownlint MD034; replace the bare URL with markdown link syntax by wrapping
the URL in square brackets with descriptive text and the URL in parentheses
(e.g., [GNU GPL v3](https://www.gnu.org/licenses/gpl-3.0.html)) or wrap the bare
URL in angle brackets (<https://...>) so the line becomes a proper markdown
link.

@defname
defname merged commit e966ff0 into main Oct 26, 2025
2 checks passed
@defname
defname deleted the feat/71-syntax-highlighting branch October 26, 2025 16:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant