Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
782cfc5
add String_Split() and String_Trim() with tests
defname Oct 19, 2025
0f904b5
Merge branch 'main' into feat/71-syntax-highlighting
defname Oct 20, 2025
97db723
add example syntax definition
defname Oct 20, 2025
08ce38f
Feat/Implement Syntax Definitions (#84)
defname Oct 22, 2025
8cbe14e
add documentation
defname Oct 22, 2025
59c477a
Feat/Syntax Highlighting: Add module to generate Highlighting informa…
defname Oct 23, 2025
e349fc7
Cache regex matches and add ends_on option for Syntax definitions (#86)
defname Oct 23, 2025
c42caad
Merge branch 'main' into feat/71-syntax-highlighting
defname Oct 23, 2025
1f598f8
add Stack_CopyTo()
defname Oct 24, 2025
517e941
save open_blocks_at_begin and open_blocks_at_end to SyntaxHighlightin…
defname Oct 24, 2025
a9ed0c1
Add Syntax Highlighting binding for TextBuffer (#87)
defname Oct 25, 2025
30de205
Merge branch 'feat/71-syntax-highlighting' of https://github.com/defn…
defname Oct 25, 2025
2b0c6d3
make argv0 available through Config
defname Oct 25, 2025
b4f044a
Add File_Exits(), File_OpenConfig() and File_OpenProjectFile()
defname Oct 25, 2025
050a4e9
change defautl width
defname Oct 25, 2025
68014cf
fix wrong function calls
defname Oct 25, 2025
236b8f5
use new File_LoadConfig() function
defname Oct 25, 2025
913de9f
remove unused variable
defname Oct 25, 2025
dfe8e97
check if snprintf() truncated the path
defname Oct 25, 2025
b17766b
fix function declaration of File_OpenProjectFile()
defname Oct 25, 2025
9b1ddaf
Implement a loader function to load a complete SyntaxHighlighting eng…
defname Oct 25, 2025
df25499
Integrate SyntaxHighlighting to the editor (#89)
defname Oct 26, 2025
48becf0
Merge branch 'main' into feat/71-syntax-highlighting
defname Oct 26, 2025
13dec61
handle the case that no highlighting is selected
defname Oct 26, 2025
5e44e63
deinitialize IniParser at ealry exit if c == NULL in Config_LoadIni()
defname Oct 26, 2025
18ce3ff
remove double deinitialization of TextSelection in editor_destroy()
defname Oct 26, 2025
f00bc04
fix parse_arguments() and update print_help()
defname Oct 26, 2025
1221353
fix critical bug in Table_Set/Get(): change state of tombstones when …
defname Oct 26, 2025
242f5ee
anchor section block start to newline
defname Oct 26, 2025
b3414ac
fix comment
defname Oct 26, 2025
1c9ebee
fix off-by-one bug in Buffer_HasSpace()
defname Oct 26, 2025
c55881e
guard against NULL in SyntaxHighlightingBinding_UpdateAll()
defname Oct 26, 2025
d89ec89
update syntax definitions
defname Oct 26, 2025
2098964
fix off-by-one bug in SyntaxHighlightingString_AddTag()
defname Oct 26, 2025
9cde542
fix potential int overflow
defname Oct 26, 2025
4c9e5f2
add comment
defname Oct 26, 2025
b7275a6
minor change to const correctness in cpy_ptr
defname Oct 26, 2025
4d89c41
update readme
defname Oct 26, 2025
e6a4289
fix intendation
defname Oct 26, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .coderabbit.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,8 @@ reviews:

# Base branches (other than the default branch) to review. Accepts regex patterns. Use '.*' to match all branches.
# Default: []
base_branches: ['.*']
base_branches:
- ".*"

# Ignore reviewing pull requests by these usernames. These should match the Git platform usernames exactly, not the email addresses.
# Default: []
Expand Down
10 changes: 10 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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.")
Expand Down Expand Up @@ -52,7 +54,15 @@ target_link_libraries(${PROJECT_NAME} PRIVATE ${PROJECT_OBJECTS})

add_compile_definitions(_XOPEN_SOURCE=700)

# --- symlink zum data Ordner erstellen
execute_process(
COMMAND ${CMAKE_COMMAND} -E create_symlink
${CMAKE_SOURCE_DIR}/data
${CMAKE_BINARY_DIR}/data
)

# CTest-Unterstützung aktivieren
enable_testing()

# --- Add tools ---
add_subdirectory(tools)
Expand Down
154 changes: 100 additions & 54 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,82 +1,128 @@
# clieditor

A simple, terminal-based text editor written in C from scratch.
This project is an exercise in building a terminal user interface (TUI) application, managing low-level terminal I/O, and structuring a C application in a modular way.
The goal was to use **no third-party libraries** like `ncurses`.
A terminal-based text editor written in C from scratch. This project serves primarily as a personal learning exercise in building a TUI application, exploring low-level terminal I/O, and structuring a C application in a modular way, all without dependencies like `ncurses`.

**Still absolutely unfinished and work in progress**
While it includes modern features, it is a hobby project with known limitations and areas for improvement. It is not intended to be a production-ready editor.

![Screenshot](assets/screenshot.png)

## 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.

- **Efficient UTF-8 Support**: A revised string library that handles UTF-8 characters correctly and efficiently, with optimized indexing for large strings.
- **Modern Text Editing**: A gap buffer implementation for the current line allows for efficient character insertion and deletion.
- **Advanced Text Layout**: Supports line wrapping that correctly handles wide characters and tab expansion.
- **Asynchronous Events**: A timer system for timed events like cursor blinking and auto-hiding notifications.

## Building
Make sure to have `CMake` (you can also build it without though), a C compiler with the C standard library and POSIX C library available and run
```

Make sure to have `CMake`, a C compiler (like GCC or Clang) with the C standard library, and the POSIX C library available.

```bash
cmake -B build
cmake --build build
```

## Features
- A handwritten widget system with a status bar, main menu, notifications, and an editor widget forming the core of the UI.
- UTF-8 support
- Line wrapping with correct handling of wide characters and tabs
- A timer system for timed events such as cursor blinking and notifications
## Usage

## Design Overview
The code is organized into modules, each typically consisting of a data structure and a set of functions operating on it.
To open a file, simply pass its name as an argument:

### User Interface
The UI is built as a tree of widgets, all inheriting from the base `Widget` module.
The root of the widget tree is the `App` widget.
If a widget has focus, all its ancestors also have focus.
User input is passed from the root down to the deepest focused widget until one handles it.
Widgets can define handlers for common events such as input, drawing, or parent resize events.
```bash
./build/clieditor src/main.c
```

### Drawing
Each widget draws itself onto a `Canvas`, which consists of `Cell`s.
Canvases are layered and clipped together, and finally compared to the special `Screen` canvas.
Only changed `Cell`s are written to the `Terminal`, the module that directly interacts with the terminal device.
### Command-Line Options

### Editor
The editor itself is composed of several tightly integrated modules:
- **`TextBuffer`** — holds the complete text as a doubly-linked list of `Line`s, with a gap buffer for efficient editing.
- **`TextLayout`** — calculates which lines are visible on screen, where wrapping occurs, how tabs expand and where the cursor visually is on screen. It's also responsible also for scrolling.
- **`TextEdit`** — handles text modifications like inserting or deleting characters in the buffer and moving the cursor.
The `Editor` widget brings these parts together and manages user interaction and rendering.
- `clieditor <filename>`: Open a specific file.
- `clieditor -s <syntax> <filename>`: Open a file and force a specific syntax highlighting profile (e.g., `-s c ...`). The name corresponds to a `<syntax>.ini` file in the `data/syntax` directory.
- `clieditor -h`: Show the help message.

## Directory Structure
## Architecture Overview

### `common/`
Fundamental, reusable modules with no dependencies on the rest of the editor, e.g., UTF-8 utilities and logging.
The editor is designed with a clean separation of concerns, organized into distinct modules. This makes the codebase easier to navigate, maintain, and extend.

### `io/`
Handles all direct interaction with the system — terminal I/O, file reading, etc.
This is the only layer with platform-specific code.
- `main.c`: The application's entry point. It handles initialization, argument parsing, and runs the main event loop.
- `common/`: A collection of fundamental, reusable data structures and utilities. This includes the core `String` library, a hash `Table`, a dynamic `Stack`, the `iniparser`, and the `Config` manager.
- `io/`: The layer for all direct interaction with the system, such as terminal I/O (`Terminal`, `Screen`), user `Input`, file operations (`File`), and the event `Timer`.
- `display/`: An abstract rendering layer. It defines *what* to draw through primitives like `Widget`, `Canvas`, and `Cell`, but not *how* it's rendered on screen.
- `document/`: The core data model of the editor, managing the text content independently of the UI. This includes the `TextBuffer`, `TextLayout` for visual calculation, and `TextEdit` for modification logic.
- `syntax/`: The syntax highlighting engine. It includes the `SyntaxDefinition` loader, the `SyntaxHighlighting` processor, and bindings to connect it to the editor's text layout.
- `widgets/`: A library of all concrete UI components, built upon the `display` primitives. It is divided into `primitives` (like `Label`, `Menu`) and `components` (like `EditorView`, `BottomBar`), with `App` serving as the root of the widget hierarchy.

### `display/`
Abstract rendering layer — defines *what* to draw (via `Canvas`, `Cell`, and `Widget`), but not *how* it’s shown on screen.
## Core Concepts

### `document/`
The core data model of the editor. Manages text independently of the UI.
The editor is built on several key concepts that are crucial to understanding its design and its current limitations.

### `widgets/`
All concrete UI components, like the editor view, status bar, or labels.
The `App` widget forms the root of the widget hierarchy.
### The String System

### `main.c`
Entry point of the application. Initializes all systems, loads the file, and runs the main loop.
The editor uses a custom, UTF-8 aware string library designed for a balance of performance and memory efficiency.

## Questionable Design Choices
- **Representation**: A `String` is a standard `char*` byte buffer, keeping memory usage minimal.
- **Efficient Indexing**: To overcome the O(N) cost of character indexing in variable-length UTF-8 strings, the `String` struct maintains an auxiliary array (`multibytes`) that caches the byte offsets of multi-byte characters. This allows for O(log N) character-based indexing via binary search.

### UTF8 Support
I had no idea how UTF-8 actually worked in C, so I quickly threw together an `UTF8Char` struct and `UTF8String` containers built from it.
To be fair, they turned out to be quite robust and easy to use — which is the good part.
The bad part is that I now use at least 5 bytes per character, which blows up memory usage pretty fast.
For example, the release build needs around *600 MB* to load a *100 MB* file, which seems… a bit much.
### The Widget System

## License
The UI is a tree of widgets, with `App` at its root. This system was built entirely from scratch as a learning exercise.

This project is licensed under the GNU General Public License v3.0 (GPL-3.0).
- **Inheritance**: Polymorphism is achieved using a `WidgetOps` struct within the base `Widget`, which acts as a "vtable" pointing to a widget's specific implementations for `draw`, `on_input`, `on_resize`, etc.
- **Drawing**: The system uses a double-buffering strategy. Each widget draws to a `Canvas`, and the final canvas is compared against the previous frame to write only changed `Cell`s to the terminal.
- **Limitations**: As a from-scratch implementation, the widget, drawing, and focus systems are quite basic. They demonstrate core TUI concepts but could be significantly improved for more complex UI scenarios and are a known area of weakness.

### The Editor Engine

The core of the text editing functionality is split into three main parts:

1. **`TextBuffer`**: This is the data model. It stores the document's text as a doubly-linked list of `Line` structs. For efficient editing, it employs a **gap buffer** within the `String` of the *current line*. Insertions and deletions happen at the gap, which is almost always an O(1) operation.
- **Note**: This is a good example of the project's learning nature. The performance benefit of the gap buffer is currently limited, as other features (namely the syntax highlighter) frequently force the gap to be merged back into the text. Optimizing this interaction is a known area for future work.
2. **`TextLayout`**: This is the "view model". It is responsible for calculating how the text in the `TextBuffer` should be displayed, handling line wrapping, tab expansion, and scrolling.
3. **`TextEdit`**: This is the "controller". It provides an API for all text manipulations (e.g., `TextEdit_InsertChar`, `TextEdit_MoveUp`), modifying the `TextBuffer` and marking the `TextLayout` as dirty.

### Syntax Highlighting

The highlighting engine is designed to be extensible.

- **Declarative Definitions**: Syntax rules are defined in simple `.ini` files, consisting of a `[meta]` section and multiple `[block:...]` sections.
- **Regex-Based Blocks**: Each block is defined by a `start` regex and an optional `end` regex. Blocks can be nested by specifying `child_blocks`.
- **Stateful Parsing**: The engine (`SyntaxHighlighting`) processes text line by line, maintaining a stack of open blocks. It uses the context from the end of the previous line to correctly highlight constructs that span multiple lines.

You may obtain a copy of the license at:
## How to Read the Code

If you're new to the codebase, here's a recommended path:

1. **Start at `src/main.c`**: Understand the initialization sequence and the main event loop.
2. **Explore `common/`**: Look at `string.h` and `table.h`, as these are used everywhere.
3. **Understand the UI**: `display/widget.h` is the base for all UI elements, and `widgets/components/editor.h` is the main editor widget.
4. **Dive into the Editor Core**: `document/textbuffer.h` (storage), `document/textlayout.h` (display logic), and `syntax/highlighting.h` (highlighting logic).

## Coding Style

The project follows a consistent C coding style.

- **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`.
Comment on lines +102 to +105

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.

- **Object-Oriented C**: The code emulates object-oriented patterns. Modules are centered around a primary struct, and functions that operate on it take a pointer to that struct as their first argument (e.g., `void Widget_Draw(Widget *self, ...)`).
- **Memory Management**: All memory is managed manually. `_Create` functions `malloc` resources, and `_Destroy` functions `free` them. `_Init` and `_Deinit` pairs manage the contents of a struct without allocating/freeing the struct itself.
- **Formatting**: 4-space indentation. The opening brace `{` is always placed on the same line as the corresponding function declaration or control statement (`if`, `for`, `while`, etc.).

## Future Goals

This project is actively used for learning and experimentation. Here are some of the planned improvements and refactoring goals:

- **Adopt a Rope Data Structure**: Replace the current `TextBuffer` implementation (a linked list of lines) with a Rope. This more advanced data structure should offer better performance for large files and complex editing operations. This will also require a corresponding redesign of the syntax highlighting engine to work efficiently with the new structure.

- **Implement Common Editor Features**: While not the primary focus, implementing standard text editor functionalities such as searching, and find and replace, remains a long-term goal.

- **Simplify the Widget System**: Refactor the core widget system, particularly the drawing and focus management logic. The goal is to create a simpler, more robust implementation that is easier to maintain, since the application's UI needs are relatively modest and centered around the main editor component.

- **Refactor Application Setup**: Move the responsibility for creating and connecting widgets from the `main` function into the `App` module. This will centralize UI setup and make `App` the true root of the application, improving modularity.

- **Improve `const`-Correctness in the String Library**: Modify the `String` implementation to rebuild its internal UTF-8 indexing cache immediately after any modification. This change will allow read-only functions (like `String_GetChar`) to accept a `const String*`, enforcing better `const`-correctness throughout the API.

## License

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.

Binary file modified assets/screenshot.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
50 changes: 50 additions & 0 deletions data/syntax/ini.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# This is a simplified version of the INI file format supported by the projects IniParser module

[meta]
name = INI
file_extensions = ini

[block:root]
child_blocks = comment, section, assignment
color = 15

[block:comment]
# comments start with ";" or "#"
start = "(;|#)"
end = "$"
color = 242 # gray
child_blocks =

[block:section]
# section: [section_name]
start = "^\\[.*\\]"
end = "$"
color = 33 # cyan
child_blocks = comment

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

[block:number]
start = [0-9]+
color = 43

[block:string]
start = "\""
end = "\"|$"
color = 160
child_blocks = escape_chars

[block:bare_string]
start = "."
end = $
color = 67
ends_on = comment

[block:escape_chars]
start = "\\\\[\\\\\\nt'\"]"
color = 217

39 changes: 39 additions & 0 deletions data/syntax/md.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
[meta]
name = Markdown
file_extensions = md

[block:root]
child_blocks = title1, title2, title3, codeblock, code, emph, img
color = 15

[block:title1]
start = "^# (.*)$"
color = 33
child_blocks =

[block:title2]
start = "^## (.*)$"
color = 33

[block:title3]
start = "^### (.*)$"
color = 33

[block:code]
start = `[^`]*` # make pattern non-greedy by explicitly exclude ` from permitted characters
color = 64

Comment thread
defname marked this conversation as resolved.
[block:emph]
start = \*\*
end = \*\*
color = 45

[block:img]
start = !\[.*\]\([^\)]+\)
color = 92

[block:codeblock]
start = "^```"
end = "^```"
color = 64
child_blocks =
17 changes: 13 additions & 4 deletions src/common/buffer.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ void Buffer_Deinit(Buffer *buffer) {
buffer->buffer = NULL;
}

void Buffer_Clear(Buffer *buffer) {
buffer->count = 0;
buffer->cursor = 0;
}

void Buffer_Enqueue(Buffer *buffer, const void *element) {
if (buffer->count >= buffer->capacity) {
logError("Buffer overflow. Element dropped.");
Expand All @@ -65,19 +70,23 @@ bool Buffer_Dequeue(Buffer *buffer, void *out_element) {
return true;
}

bool Buffer_IsEmpty(Buffer *buffer) {
bool Buffer_IsEmpty(const Buffer *buffer) {
return buffer->count == 0;
}

size_t Buffer_Size(Buffer *buffer) {
size_t Buffer_Size(const Buffer *buffer) {
return buffer->count;
}

size_t Buffer_Capacity(Buffer *buffer) {
size_t Buffer_Capacity(const Buffer *buffer) {
return buffer->capacity;
}

bool Buffer_Peek(Buffer *buffer, size_t lookahead, void *out_element) {
bool Buffer_HasSpace(const Buffer *buffer) {
return buffer->count < buffer->capacity;
}
Comment thread
defname marked this conversation as resolved.

bool Buffer_Peek(const Buffer *buffer, size_t lookahead, void *out_element) {
if (lookahead >= buffer->count) {
logError("Buffer underflow on Peek (lookahead: %zu, count: %zu).", lookahead, buffer->count);
return false;
Expand Down
16 changes: 12 additions & 4 deletions src/common/buffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ void Buffer_Init(Buffer *buffer, size_t capacity, size_t element_size);
*/
void Buffer_Deinit(Buffer *buffer);

/**
* @brief Set buffer count to 0 and cursor to the first element.
*/
void Buffer_Clear(Buffer *buffer);

/**
* @brief Adds an element to the end (tail) of the buffer.
* If the buffer is full, the element is dropped.
Expand All @@ -70,13 +75,16 @@ bool Buffer_Dequeue(Buffer *buffer, void *out_element);
* @param buffer A pointer to the Buffer struct.
* @return true if the buffer contains no elements, false otherwise.
*/
bool Buffer_IsEmpty(Buffer *buffer);
bool Buffer_IsEmpty(const Buffer *buffer);

/** @brief Returns the current number of elements in the buffer. */
size_t Buffer_Size(Buffer *buffer);
size_t Buffer_Size(const Buffer *buffer);

/** @brief Returns the maximum capacity of the buffer. */
size_t Buffer_Capacity(Buffer *buffer);
size_t Buffer_Capacity(const Buffer *buffer);

/** @brief Return true if there are empty slots. */
bool Buffer_HasSpace(const Buffer *buffer);

Comment thread
defname marked this conversation as resolved.
/**
* @brief Peeks at an element in the buffer without removing it.
Expand All @@ -85,6 +93,6 @@ size_t Buffer_Capacity(Buffer *buffer);
* @param out_element A pointer to a variable where the peeked element will be copied.
* @return true on success, false if the lookahead index is out of bounds.
*/
bool Buffer_Peek(Buffer *buffer, size_t lookahead, void *out_element);
bool Buffer_Peek(const Buffer *buffer, size_t lookahead, void *out_element);

#endif
Loading