From cf2f7bfe72f896eb1615eded9bcc9c510f6cb604 Mon Sep 17 00:00:00 2001 From: Bodo Date: Mon, 20 Oct 2025 22:13:05 +0200 Subject: [PATCH 01/21] add TableIterator (#79) * add TableIterator * change include that provides ssize_t * add additional tests * make current slot pointer const * include stdbool.h for bool --- src/common/tableiterator.c | 24 +++++++++++ src/common/tableiterator.h | 17 ++++++++ tests/test_tableiterator.c | 88 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 129 insertions(+) create mode 100644 src/common/tableiterator.c create mode 100644 src/common/tableiterator.h create mode 100644 tests/test_tableiterator.c diff --git a/src/common/tableiterator.c b/src/common/tableiterator.c new file mode 100644 index 0000000..538d6d3 --- /dev/null +++ b/src/common/tableiterator.c @@ -0,0 +1,24 @@ +#include "tableiterator.h" + +TableIterator TableIterator_Begin(const Table *table) { + return (TableIterator){ + .table = table, + .index = -1, + .current = NULL + }; +} + +bool TableIterator_Next(TableIterator *it) { + if (!it || !it->table) { + return false; + } + for (size_t i = (size_t)(it->index + 1); i < it->table->capacity; i++) { + TableSlot *slot = &it->table->slots[i]; + if (slot->state == TABLE_SLOT_USED) { + it->index = i; + it->current = slot; + return true; + } + } + return false; +} diff --git a/src/common/tableiterator.h b/src/common/tableiterator.h new file mode 100644 index 0000000..bf7f06c --- /dev/null +++ b/src/common/tableiterator.h @@ -0,0 +1,17 @@ +#ifndef TABLEITERATOR_H +#define TABLEITERATOR_H + +#include +#include +#include "table.h" + +typedef struct _TableIterator { + const Table *table; + ssize_t index; + const TableSlot *current; +} TableIterator; + +TableIterator TableIterator_Begin(const Table *table); +bool TableIterator_Next(TableIterator *it); + +#endif \ No newline at end of file diff --git a/tests/test_tableiterator.c b/tests/test_tableiterator.c new file mode 100644 index 0000000..ae1aca0 --- /dev/null +++ b/tests/test_tableiterator.c @@ -0,0 +1,88 @@ +#include "acutest.h" +#include "common/tableiterator.h" +#include "common/table.h" + +void fill_table(const char **key, const char **values, size_t count) { + Table *table = Table_Create(); + + for (size_t i=0; istate == TABLE_SLOT_USED); + slots++; + } + TEST_CHECK(slots == count); + Table_Destroy(table); +} + +void test_standard(void) { + const char *keys[] = { + "key0", + "key1", + "key2", + "key3" + }; + + const char *values[] = { + "value0", + "value1", + "value2", + "value3" + }; + size_t count = sizeof(keys) / sizeof(keys[0]); + + fill_table(keys, values, count); + fill_table(keys, values, 0); + fill_table(keys, values, 1); +} + +void test_iterating_twice(void) { + Table *table = Table_Create(); + Table_Set(table, "key", "value", NULL); + Table_Set(table, "key2", "value2", NULL); + Table_Set(table, "key3", "value3", NULL); + + TableIterator it = TableIterator_Begin(table); + size_t count = 0; + while (TableIterator_Next(&it)) { + count++; + } + TEST_CHECK(count == 3); + + // run second time without reinitializing + count = 0; + while (TableIterator_Next(&it)) { + count++; + } + TEST_CHECK(count == 0); + + // run again with reinitializing + it = TableIterator_Begin(table); + count = 0; + while (TableIterator_Next(&it)) { + count++; + } + TEST_CHECK(count == 3); + + Table_Destroy(table); +} + +void test_edgecases(void) { + TableIterator it = TableIterator_Begin(NULL); + TEST_CHECK(!TableIterator_Next(&it)); + + TEST_CHECK(!TableIterator_Next(NULL)); +} + + +TEST_LIST = { + { "TableIterator: Standard", test_standard }, + { "TableIterator: Iterating twice", test_iterating_twice }, + { "TableIterator: Edgecases", test_edgecases }, + { NULL, NULL } +}; + From 6314f6698490ea3818cd0b1c57e21d6d9ba8a4c9 Mon Sep 17 00:00:00 2001 From: Bodo Date: Tue, 21 Oct 2025 16:50:51 +0200 Subject: [PATCH 02/21] 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() --- src/common/table.c | 12 ++++++++++++ tests/test_table.c | 8 +++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/common/table.c b/src/common/table.c index 3dd739e..737a5f5 100644 --- a/src/common/table.c +++ b/src/common/table.c @@ -220,6 +220,9 @@ void *Table_Get(const Table *table, const char *key) { if (!table) { logFatal("Invalid table in Table_Get()."); } + if (table->used == 0) { + return NULL; + } if (!key) { logWarn("Table_Get() was called with key == NULL."); return NULL; @@ -236,6 +239,9 @@ void Table_Delete(Table *table, const char *key) { if (!table) { logFatal("Invalid table in Table_Delete()."); } + if (table->used == 0) { + return; + } if (!key) { logWarn("Table_Delete() is called with key == NULL."); return; @@ -255,6 +261,9 @@ bool Table_Has(const Table *table, const char *key) { if (!table) { logFatal("Invalid table in Table_Has()."); } + if (table->used == 0) { + return false; + } if (!key) { logWarn("Table_Has() was called with key == NULL."); return false; @@ -267,6 +276,9 @@ bool Table_HasOwnership(const Table *table, const char *key) { if (!table) { logFatal("Invalid table in Table_HasOwnership()."); } + if (table->used == 0) { + return false; + } if (!key) { logWarn("Table_HasOwnership() was called with key == NULL."); return false; diff --git a/tests/test_table.c b/tests/test_table.c index b967aa0..4cc41c1 100644 --- a/tests/test_table.c +++ b/tests/test_table.c @@ -105,7 +105,13 @@ void test_edge_case(void) { } Table_Get(table, "key0"); - + Table_Destroy(table); + + // checks on empty table + table = Table_Create(); + TEST_CHECK(!Table_Get(table, "key")); + TEST_CHECK(!Table_Has(table, "key")); + TEST_CHECK(!Table_HasOwnership(table, "key")); Table_Destroy(table); } From 6802145c3819585d1a3e3d2db53f4f78f553e02f Mon Sep 17 00:00:00 2001 From: Bodo Date: Tue, 21 Oct 2025 20:21:26 +0200 Subject: [PATCH 03/21] 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. --- .coderabbit.yaml | 804 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 804 insertions(+) create mode 100644 .coderabbit.yaml diff --git a/.coderabbit.yaml b/.coderabbit.yaml new file mode 100644 index 0000000..a6587b8 --- /dev/null +++ b/.coderabbit.yaml @@ -0,0 +1,804 @@ +# CodeRabbit Configuration Template +# Complete reference: https://docs.coderabbit.ai/reference/configuration +# Copy this file to .coderabbit.yaml in your repository root + +# ============================================================================= +# GLOBAL SETTINGS +# These settings apply to your entire CodeRabbit configuration +# ============================================================================= + +# Set the language for reviews by using the corresponding ISO language code. +# Options: de, de-DE, de-AT, de-CH, en, en-US, en-AU, en-GB, en-CA, en-NZ, en-ZA, es, es-AR, fr, fr-CA, fr-CH, fr-BE, nl, nl-BE, pt-AO, pt, pt-BR, pt-MZ, pt-PT, ar, ast-ES, ast, be-BY, be, br-FR, br, ca-ES, ca, ca-ES-valencia, ca-ES-balear, da-DK, da, de-DE-x-simple-language, el-GR, el, eo, fa, ga-IE, ga, gl-ES, gl, it, ja-JP, ja, km-KH, km, ko-KR, ko, pl-PL, pl, ro-RO, ro, ru-RU, ru, sk-SK, sk, sl-SI, sl, sv, ta-IN, ta, tl-PH, tl, tr, uk-UA, uk, zh-CN, zh, crh-UA, crh, cs-CZ, cs, nb, no, nl-NL, de-DE-x-simple-language-DE, es-ES, it-IT, fa-IR, sv-SE, de-LU, fr-FR, bg-BG, bg, he-IL, he, hi-IN, hi, vi-VN, vi, th-TH, th, bn-BD, bn +# Default: "en-US" +language: "en-US" + +# Set the tone of reviews and chat. Example: 'You must use talk like Mr. T. I pity the fool who doesn't!' +# Default: "" +tone_instructions: "" + +# Enable early-access features. +# Default: false +early_access: false + +# Enable free tier features for users not on a paid plan. +# Default: true +enable_free_tier: true + +# ============================================================================= +# REVIEWS +# Settings related to reviews. +# ============================================================================= + +# Settings related to reviews. +# Default: {} +reviews: + + # Set the profile for reviews. Assertive profile yields more feedback, that may be considered nitpicky. + # Options: chill, assertive + # Default: "chill" + profile: "chill" + + # Approve the review once CodeRabbit’s comments are resolved and no pre-merge checks are in an error state. Note: In GitLab, all discussions must be resolved. + # Default: false + request_changes_workflow: false + + # Generate a high level summary of the changes in the PR/MR description. + # Default: true + high_level_summary: true + + # Placeholder in the PR/MR description that gets replaced with the high level summary. + # Default: "@coderabbitai summary" + high_level_summary_placeholder: "@coderabbitai summary" + + # Include the high level summary in the walkthrough comment. + # Default: false + high_level_summary_in_walkthrough: false + + # Add this keyword in the PR/MR title to auto-generate the title. + # Default: "@coderabbitai" + auto_title_placeholder: "@coderabbitai" + + # Auto Title Instructions - Custom instructions for auto-generating the PR/MR title. + # Default: "" + auto_title_instructions: "" + + # Post review details on each review. Additionally, post a review status when a review is skipped in certain cases. + # Default: true + review_status: true + + # Set the commit status to 'pending' when the review is in progress and 'success' when it is complete. + # Default: true + commit_status: true + + # Set the commit status to 'failure' when the PR cannot be reviewed by CodeRabbit for any reason. + # Default: false + fail_commit_status: false + + # Generate walkthrough in a markdown collapsible section. + # Default: false + collapse_walkthrough: false + + # Generate a summary of the changed files in the walkthrough. + # Default: true + changed_files_summary: true + + # Generate sequence diagrams in the walkthrough. + # Default: true + sequence_diagrams: true + + # Estimate the code review effort in the walkthrough. + # Default: true + estimate_code_review_effort: true + + # Generate an assessment of how well the changes address the linked issues in the walkthrough. + # Default: true + assess_linked_issues: true + + # Include possibly related issues in the walkthrough. + # Default: true + related_issues: true + + # Related PRs - Include possibly related pull requests in the walkthrough. + # Default: true + related_prs: true + + # Suggest labels based on the changes in the pull request in the walkthrough. + # Default: true + suggested_labels: true + + # Labeling Instructions - Provide guidelines for suggesting labels for the PR/MR. When specific labels or instructions are provided, only those labels are considered, though previous examples are still used to inform the suggestions. If no such labels are provided, suggestions are based solely on previous PR/MRs. + # Default: [] + labeling_instructions: [] + + # Automatically apply the suggested labels to the PR/MR. + # Default: false + auto_apply_labels: false + + # Suggest reviewers based on the changes in the pull request in the walkthrough. + # Default: true + suggested_reviewers: true + + # Automatically assign suggested reviewers to the pull request + # Default: false + auto_assign_reviewers: false + + # Post an in-progress fortune message while the review is in progress. + # Default: true + in_progress_fortune: true + + # Generate a poem in the walkthrough comment. + # Default: true + poem: true + + # Specify file patterns to include or exclude in a review using glob patterns (e.g., !dist/**, src/**). These patterns also apply to 'git sparse-checkout', including specified patterns and ignoring excluded ones (starting with '!') when cloning the repository. + # Default: [] + path_filters: [] + + # Path Instructions - Provide specific additional guidelines for code review based on file paths. + # Default: [] + path_instructions: [] + + # Abort the in-progress review if the pull request is closed or merged. + # Default: true + abort_on_close: true + + # Disable caching of code and dependencies. This will force CodeRabbit to download the code and dependencies fresh from the repository each time. + # Default: false + disable_cache: false + + # Configuration for auto review + # Default: {} + auto_review: + + # Automatic Review - Automatic code review + # Default: true + enabled: true + + # Automatic Incremental Review - Automatic incremental code review on each push + # Default: true + auto_incremental_review: true + + # Ignore reviewing if the title of the pull request contains any of these keywords (case-insensitive). + # Default: [] + ignore_title_keywords: [] + + # List of labels to control which PRs/MRs to review. Labels starting with '!' are negative matches. Examples: ['bug', 'feature'] - reviews PRs with 'bug' OR 'feature' label. ['!wip'] - reviews all PRs except those with 'wip' label. ['bug', '!wip'] - reviews PRs with 'bug' label but not if they have 'wip' label. + # Default: [] + labels: [] + + # Review draft PRs/MRs. + # Default: false + drafts: true + + # Base branches (other than the default branch) to review. Accepts regex patterns. Use '.*' to match all branches. + # Default: [] + base_branches: '.*' + + # Ignore reviewing pull requests by these usernames. These should match the Git platform usernames exactly, not the email addresses. + # Default: [] + ignore_usernames: [] + + # Configuration for finishing touches + # Default: {} + finishing_touches: + + # Docstrings - Options for generating Docstrings for your PRs/MRs. + # Default: {} + docstrings: + + # Docstrings - Allow CodeRabbit to generate docstrings for PRs/MRs. + # Default: true + enabled: true + + # Unit Tests - Options for generating unit tests for your PRs/MRs. + # Default: {} + unit_tests: + + # Unit Tests - Allow CodeRabbit to generate unit tests for PRs/MRs. + # Default: true + enabled: true + + # Configuration for pre merge checks + # Default: {} + pre_merge_checks: + + # Docstring Coverage - Checks if the code has sufficient docstrings. + # Default: {} + docstrings: + + # Mode - Determines how strictly the docstring coverage check is enforced. Warning will only generate a warning and does not require the user to resolve the check. Error requires the user to resolve issues before merging the pull request. If set to error and the request changes workflow is enabled, the pull request will be blocked until the issues are resolved. + # Options: off, warning, error + # Default: "warning" + mode: "warning" + + # Percentage threshold for docstring coverage check. + # Default: 80 + threshold: 80 + + # Title Check - Checks if the pull request title is appropriate and follows best practices. + # Default: {} + title: + + # Mode - Determines how strictly the title check is enforced. Warning will only generate a warning and does not require the user to resolve the check. Error requires the user to resolve issues before merging the pull request. If set to error and the request changes workflow is enabled, the pull request will be blocked until the issues are resolved. + # Options: off, warning, error + # Default: "warning" + mode: "warning" + + # Requirements - Requirements for the pull request title. Example: 'Title should be concise and descriptive, ideally under 50 characters.' + # Default: "" + requirements: "" + + # Description Check - Checks if the pull request description is appropriate and follows best practices. + # Default: {} + description: + + # Mode - Determines how strictly the description check is enforced. Warning will only generate a warning and does not require the user to resolve the check. Error requires the user to resolve issues before merging the pull request. If set to error and the request changes workflow is enabled, the pull request will be blocked until the issues are resolved. + # Options: off, warning, error + # Default: "warning" + mode: "warning" + + # Linked Issue Assessment - Checks if the pull request addresses the linked issues. Generate an assessment of how well the changes address the linked issues. + # Default: {} + issue_assessment: + + # Mode - Determines how strictly the issue assessment check is enforced. Warning will only generate a warning and does not require the user to resolve the check. Error requires the user to resolve issues before merging the pull request. If set to error and the request changes workflow is enabled, the pull request will be blocked until the issues are resolved. + # Options: off, warning, error + # Default: "warning" + mode: "warning" + + # Custom Pre-merge Checks - Add unique checks to enforce your team's standards before merging a pull request. Each check must have a unique name (up to 50 characters) and clear instructions (up to 10000 characters). Use these to automatically verify coding, security, documentation, or business rules and maintain code quality. + # Default: [] + custom_checks: [] + + # Tools that provide additional context to code reviews. + # Default: {} + tools: + + # Enable ast-grep - ast-grep is a code analysis tool that helps you to find patterns in your codebase using abstract syntax trees patterns. - v0.39.6 + # Default: {} + ast-grep: + + # List of rules directories. + # Default: [] + rule_dirs: [] + + # List of utils directories. + # Default: [] + util_dirs: [] + + # Use ast-grep essentials package. + # Default: true + essential_rules: true + + # Predefined packages to be used. + # Default: [] + packages: [] + + # ShellCheck is a static analysis tool that finds bugs in your shell scripts. + # Default: {} + shellcheck: + + # Enable ShellCheck - ShellCheck is a static analysis tool that finds bugs in your shell. - Enable ShellCheck integration. - v0.11.0 + # Default: true + enabled: true + + # Ruff is a Python linter and code formatter. + # Default: {} + ruff: + + # Enable Ruff - Ruff is a Python linter and code formatter. - Enable Ruff integration. - v0.14.1 + # Default: true + enabled: true + + # markdownlint-cli2 is a static analysis tool to enforce standards and consistency for Markdown files. + # Default: {} + markdownlint: + + # Enable markdownlint - markdownlint-cli2 is a static analysis tool to enforce standards and consistency for Markdown files. - Enable markdownlint integration. - v0.18.1 + # Default: true + enabled: true + + # GitHub Checks integration configuration. + # Default: {} + github-checks: + + # Enable GitHub Checks - Enable integration, defaults to true - Enable GitHub Checks integration. + # Default: true + enabled: true + + # Time in milliseconds to wait for all GitHub Checks to conclude. Default 90 seconds, max 15 minutes (900000ms). + # Default: 90000 + timeout_ms: 90000 + + # LanguageTool is a style and grammar checker for 30+ languages. + # Default: {} + languagetool: + + # Enable LanguageTool - Enable LanguageTool integration. + # Default: true + enabled: true + + # IDs of rules to be enabled. The rule won't run unless 'level' is set to a level that activates the rule. + # Default: [] + enabled_rules: [] + + # IDs of rules to be disabled. Note: EN_UNPAIRED_BRACKETS, and EN_UNPAIRED_QUOTES are always disabled. + # Default: [] + disabled_rules: [] + + # IDs of categories to be enabled. + # Default: [] + enabled_categories: [] + + # IDs of categories to be disabled. Note: TYPOS, TYPOGRAPHY, and CASING are always disabled. + # Default: [] + disabled_categories: [] + + # Only the rules and categories whose IDs are specified with 'enabledRules' or 'enabledCategories' are enabled. + # Default: false + enabled_only: false + + # If set to 'picky', additional rules will be activated, i.e. rules that you might only find useful when checking formal text. + # Options: default, picky + # Default: "default" + level: "default" + + # Biome is a fast formatter, linter, and analyzer for web projects. + # Default: {} + biome: + + # Enable Biome - Biome is a fast formatter, linter, and analyzer for web projects. - Enable Biome integration. - v2.1.2 + # Default: true + enabled: true + + # Hadolint is a Dockerfile linter. + # Default: {} + hadolint: + + # Enable Hadolint - Hadolint is a Dockerfile linter. - Enable Hadolint integration. - v2.14.0 + # Default: true + enabled: true + + # SwiftLint integration configuration object. + # Default: {} + swiftlint: + + # Enable SwiftLint - SwiftLint is a Swift linter. - Enable SwiftLint integration. - v0.57.0 + # Default: true + enabled: true + + # Optional path to the SwiftLint configuration file relative to the repository. This is useful when the configuration file is named differently than the default '.swiftlint.yml' or '.swiftlint.yaml'. + config_file: "example-value" + + # PHPStan is a tool to analyze PHP code. + # Default: {} + phpstan: + + # Enable PHPStan - PHPStan requires [config file](https://phpstan.org/config-reference#config-file) in your repository root. Please ensure that this file contains the `paths:` parameter. - v2.1.31 + # Default: true + enabled: true + + # Level - Specify the [rule level](https://phpstan.org/user-guide/rule-levels) to run. This setting is ignored if your configuration file already has a `level:` parameter. + # Options: default, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, max + # Default: "default" + level: "default" + + # PHPMD is a tool to find potential problems in PHP code. + # Default: {} + phpmd: + + # Enable PHPMD - PHPMD is a tool to find potential problems in PHP code. - v2.15.0 + # Default: true + enabled: true + + # PHP CodeSniffer is a PHP linter and coding standard checker. + # Default: {} + phpcs: + + # Enable PHP CodeSniffer - PHP CodeSniffer is a PHP linter and coding standard checker. - v3.7.2 + # Default: true + enabled: true + + # golangci-lint is a fast linters runner for Go. + # Default: {} + golangci-lint: + + # Enable golangci-lint - golangci-lint is a fast linters runner for Go. - Enable golangci-lint integration. - v2.5.0 + # Default: true + enabled: true + + # Optional path to the golangci-lint configuration file relative to the repository. Useful when the configuration file is named differently than the default '.golangci.yml', '.golangci.yaml', '.golangci.toml', '.golangci.json'. + config_file: "example-value" + + # YAMLlint is a linter for YAML files. + # Default: {} + yamllint: + + # Enable YAMLlint - YAMLlint is a linter for YAML files. - Enable YAMLlint integration. - v1.37.1 + # Default: true + enabled: true + + # Gitleaks is a secret scanner. + # Default: {} + gitleaks: + + # Enable Gitleaks - Gitleaks is a secret scanner. - Enable Gitleaks integration. - v8.28.0 + # Default: true + enabled: true + + # Checkov is a static code analysis tool for infrastructure-as-code files. + # Default: {} + checkov: + + # Enable Checkov - Checkov is a static code analysis tool for infrastructure-as-code files. - v3.2.334 + # Default: true + enabled: true + + # Detekt is a static code analysis tool for Kotlin files. + # Default: {} + detekt: + + # Enable detekt - detekt is a static code analysis tool for Kotlin files. - v1.23.8 + # Default: true + enabled: true + + # Optional path to the detekt configuration file relative to the repository. + config_file: "example-value" + + # ESLint is a static code analysis tool for JavaScript files. + # Default: {} + eslint: + + # Enable ESLint - ESLint is a static code analysis tool for JavaScript files. + # Default: true + enabled: true + + # Flake8 is a Python linter that wraps PyFlakes, pycodestyle and Ned Batchelder's McCabe script. + # Default: {} + flake8: + + # Enable Flake8 - Flake8 is a Python linter that wraps PyFlakes, pycodestyle and Ned Batchelder's McCabe script. - v7.3.0 + # Default: true + enabled: true + + # RuboCop is a Ruby static code analyzer (a.k.a. linter ) and code formatter. + # Default: {} + rubocop: + + # Enable RuboCop - RuboCop is a Ruby static code analyzer (a.k.a. linter ) and code formatter. - v1.81.1 + # Default: true + enabled: true + + # Buf offers linting for Protobuf files. + # Default: {} + buf: + + # Enable Buf - Buf offers linting for Protobuf files. - v1.58.0 + # Default: true + enabled: true + + # Regal is a linter and language server for Rego. + # Default: {} + regal: + + # Enable Regal - Regal is a linter and language server for Rego. - v0.36.1 + # Default: true + enabled: true + + # actionlint is a static checker for GitHub Actions workflow files. + # Default: {} + actionlint: + + # Enable actionlint - is a static checker for GitHub Actions workflow files. - v1.7.8 + # Default: true + enabled: true + + # PMD is an extensible multilanguage static code analyzer. It’s mainly concerned with Java. + # Default: {} + pmd: + + # Enable PMD - PMD is an extensible multilanguage static code analyzer. It’s mainly concerned with Java. - v7.17.0 + # Default: true + enabled: true + + # Optional path to the PMD configuration file relative to the repository. + config_file: "example-value" + + # Configuration for Clang to perform static analysis on C and C++ code + # Default: {} + clang: + + # Enable Clang for C/C++ static analysis and code quality checks - v14.0.6 + # Default: true + enabled: true + + # Cppcheck is a static code analysis tool for the C and C++ programming languages. + # Default: {} + cppcheck: + + # Enable Cppcheck - Cppcheck is a static code analysis tool for the C and C++ programming languages. - v2.18.0 + # Default: true + enabled: true + + # Semgrep is a static analysis tool designed to scan code for security vulnerabilities and code quality issues. + # Default: {} + semgrep: + + # Enable Semgrep - Semgrep is a static analysis tool designed to scan code for security vulnerabilities and code quality issues. - Enable Semgrep integration. - v1.140.0 + # Default: true + enabled: true + + # Optional path to the Semgrep configuration file relative to the repository. + config_file: "example-value" + + # CircleCI tool is a static checker for CircleCI config files. + # Default: {} + circleci: + + # Enable CircleCI - CircleCI tool is a static checker for CircleCI config files. - v0.1.33494 + # Default: true + enabled: true + + # Clippy is a collection of lints to catch common mistakes and improve your Rust code. + # Default: {} + clippy: + + # Enable Clippy - Clippy is a collection of lints to catch common mistakes and improve your Rust code. - Enable Clippy integration. + # Default: true + enabled: true + + # SQLFluff is an open source, dialect-flexible and configurable SQL linter. + # Default: {} + sqlfluff: + + # Enable SQLFluff - SQLFluff is an open source, dialect-flexible and configurable SQL linter. - v3.5.0 + # Default: true + enabled: true + + # Configuration for Prisma Schema linting to ensure schema file quality + # Default: {} + prismaLint: + + # Enable Prisma Schema linting - Prisma Schema linting helps maintain consistent and error-free schema files - v0.10.3 + # Default: true + enabled: true + + # Pylint is a Python static code analysis tool. + # Default: {} + pylint: + + # Enable Pylint - Pylint is a Python static code analysis tool. - v4.0.1 + # Default: true + enabled: true + + # Oxlint is a JavaScript/TypeScript linter for OXC written in Rust. + # Default: {} + oxc: + + # Enable Oxlint - Oxlint is a JavaScript/TypeScript linter for OXC written in Rust. - v1.23.0 + # Default: true + enabled: true + + # Configuration for Shopify Theme Check to ensure theme quality and best practices + # Default: {} + shopifyThemeCheck: + + # Enable Shopify Theme Check - A linter for Shopify themes that helps you follow Shopify theme & Liquid best practices - cli 3.84.2 - theme 3.58.2 + # Default: true + enabled: true + + # Configuration for Lua code linting to ensure code quality + # Default: {} + luacheck: + + # Enable Lua code linting - Luacheck helps maintain consistent and error-free Lua code - v1.2.0 + # Default: true + enabled: true + + # Brakeman is a static analysis security vulnerability scanner for Ruby on Rails applications. - v7.1.0 + # Default: {} + brakeman: + + # Enable Brakeman - Brakeman is a static analysis security vulnerability scanner for Ruby on Rails applications. - v7.1.0 + # Default: true + enabled: true + + # dotenv-linter is a tool for checking and fixing .env files for problems and best practices + # Default: {} + dotenvLint: + + # Enable dotenv-linter - dotenv-linter is a tool for checking and fixing .env files for problems and best practices - v4.0.0 + # Default: true + enabled: true + + # HTMLHint is a static code analysis tool for HTML files. + # Default: {} + htmlhint: + + # Enable HTMLHint - HTMLHint is a static code analysis tool for HTML files. - Enable HTMLHint integration. - v1.7.1 + # Default: true + enabled: true + + # checkmake is a linter for Makefiles. + # Default: {} + checkmake: + + # Enable checkmake - checkmake is a linter for Makefiles. - v0.2.2 + # Default: true + enabled: true + + # OSV Scanner is a tool for vulnerability package scanning. + # Default: {} + osvScanner: + + # Enable OSV Scanner - OSV Scanner is a tool for vulnerability package scanning - v2.2.3 + # Default: true + enabled: true + +# ============================================================================= +# CHAT +# Configuration for chat +# ============================================================================= + +# Configuration for chat +# Default: {} +chat: + + # Generate art in response to chat messages. CodeRabbit expresses emotions as either ASCII or Emoji art. + # Default: true + art: true + + # Enable the bot to reply automatically without requiring the user to tag it. + # Default: true + auto_reply: true + + # Configuration for integrations + # Default: {} + integrations: + + # Configuration for jira + # Default: {} + jira: + + # Jira - Enable the Jira integration for opening issues, etc. 'auto' disables the integration for public repositories. + # Options: auto, enabled, disabled + # Default: "auto" + usage: "auto" + + # Configuration for linear + # Default: {} + linear: + + # Linear - Enable the Linear integration for opening issues, etc. 'auto' disables the integration for public repositories. + # Options: auto, enabled, disabled + # Default: "auto" + usage: "auto" + +# ============================================================================= +# KNOWLEDGE BASE +# Configuration for knowledge base +# ============================================================================= + +# Configuration for knowledge base +# Default: {} +knowledge_base: + + # Opt Out - Disable all knowledge base features that require data retention. If you opt out after opting in, all of your existing knowledge base data will be removed from the system. + # Default: false + opt_out: false + + # Configuration for web search + # Default: {} + web_search: + + # Web Search - Enable the web search integration. + # Default: true + enabled: true + + # CodeRabbit will analyse and learn from your organization's code guidelines, which you can mention in the file patterns section. These guidelines will then be used to conduct thorough code reviews. + # Default: {} + code_guidelines: + + # Enabled - Enable CodeRabbit to enforce your organization's coding standards during reviews. + # Default: true + enabled: true + + # File Patterns - Specify files for your coding guideline documents in this section. CodeRabbit will scan these files to understand your team's standards and apply them during code reviews. Multiple files supported. File names are case-sensitive. Common files like: (**/.cursorrules, .github/copilot-instructions.md, .github/instructions/*.instructions.md, **/CLAUDE.md, **/GEMINI.md, **/.cursor/rules/*, **/.windsurfrules, **/.clinerules/*, **/.rules/*, **/AGENT.md, **/AGENTS.md) are included by default. + # Default: [] + filePatterns: [] + + # Configuration for learnings + # Default: {} + learnings: + + # Learnings - Specify the scope of learnings to use for the knowledge base. 'local' uses the repository's learnings, 'global' uses the organization's learnings, and 'auto' uses repository's learnings for public repositories and organization's learnings for private repositories. + # Options: local, global, auto + # Default: "auto" + scope: "auto" + + # Configuration for issues + # Default: {} + issues: + + # Issues - Specify the scope of git platform (GitHub/GitLab) issues to use for the knowledge base. 'local' uses the repository's issues, 'global' uses the organization's issues, and 'auto' uses repository's issues for public repositories and organization's issues for private repositories. + # Options: local, global, auto + # Default: "auto" + scope: "auto" + + # Configuration for jira + # Default: {} + jira: + + # Jira - Enable the Jira knowledge base integration. 'auto' disables the integration for public repositories. + # Options: auto, enabled, disabled + # Default: "auto" + usage: "auto" + + # Jira Project Keys - Specify the Jira project keys to use for the knowledge base. + # Default: [] + project_keys: [] + + # Configuration for linear + # Default: {} + linear: + + # Linear - Enable the Linear knowledge base integration. 'auto' disables the integration for public repositories. + # Options: auto, enabled, disabled + # Default: "auto" + usage: "auto" + + # Linear Team Keys - Specify the Linear team keys (identifiers) to use for the knowledge base. E.g. 'ENG' + # Default: [] + team_keys: [] + + # Configuration for pull requests + # Default: {} + pull_requests: + + # Pull Requests - Specify the scope of pull requests to use for the knowledge base. 'local' uses the repository's pull requests, 'global' uses the organization's pull requests, and 'auto' uses repository's pull requests for public repositories and organization's pull requests for private repositories. + # Options: local, global, auto + # Default: "auto" + scope: "auto" + + # Configuration for mcp + # Default: {} + mcp: + + # MCP - Enable the MCP knowledge base integration. 'auto' disables the integration for public repositories. + # Options: auto, enabled, disabled + # Default: "auto" + usage: "auto" + + # MCP Disabled Servers - Specify MCP server labels to disable (case-insensitive). These servers will be excluded from reviews and knowledge base queries. + # Default: [] + disabled_servers: [] + +# ============================================================================= +# CODE GENERATION +# Configuration for code generation +# ============================================================================= + +# Configuration for code generation +# Default: {} +code_generation: + + # Settings related to the generation of docstrings. + # Default: {"path_instructions":[]} + docstrings: + + # Set the language for docstrings by using the corresponding ISO language code. + # Options: de, de-DE, de-AT, de-CH, en, en-US, en-AU, en-GB, en-CA, en-NZ, en-ZA, es, es-AR, fr, fr-CA, fr-CH, fr-BE, nl, nl-BE, pt-AO, pt, pt-BR, pt-MZ, pt-PT, ar, ast-ES, ast, be-BY, be, br-FR, br, ca-ES, ca, ca-ES-valencia, ca-ES-balear, da-DK, da, de-DE-x-simple-language, el-GR, el, eo, fa, ga-IE, ga, gl-ES, gl, it, ja-JP, ja, km-KH, km, ko-KR, ko, pl-PL, pl, ro-RO, ro, ru-RU, ru, sk-SK, sk, sl-SI, sl, sv, ta-IN, ta, tl-PH, tl, tr, uk-UA, uk, zh-CN, zh, crh-UA, crh, cs-CZ, cs, nb, no, nl-NL, de-DE-x-simple-language-DE, es-ES, it-IT, fa-IR, sv-SE, de-LU, fr-FR, bg-BG, bg, he-IL, he, hi-IN, hi, vi-VN, vi, th-TH, th, bn-BD, bn + # Default: "en-US" + language: "en-US" + + # Path Instructions - Provide additional guidelines for docstring generation based on file paths. + # Default: [] + path_instructions: [] + + # Settings related to the generation of unit tests. + # Default: {"path_instructions":[]} + unit_tests: + + # Unit Test Generation - Provide additional guidelines for unit test generation based on file paths. + # Default: [] + path_instructions: [] From b7fce6cf6e08686eb46c51511e2a99935d7c78c8 Mon Sep 17 00:00:00 2001 From: defname Date: Fri, 24 Oct 2025 09:03:08 +0200 Subject: [PATCH 04/21] add SyntaxHighlightingBinding to connect SyntaxHighlighting with the TextBuffer/TextLayout --- src/syntax/textlayoutbindings.c | 36 +++++++++++++++++++++++++++++++++ src/syntax/textlayoutbindings.h | 17 ++++++++++++++++ 2 files changed, 53 insertions(+) create mode 100644 src/syntax/textlayoutbindings.c create mode 100644 src/syntax/textlayoutbindings.h diff --git a/src/syntax/textlayoutbindings.c b/src/syntax/textlayoutbindings.c new file mode 100644 index 0000000..7f4f34a --- /dev/null +++ b/src/syntax/textlayoutbindings.c @@ -0,0 +1,36 @@ +#include "textlayoutbindings.h" + +void SyntaxHighlightingBinding_Init(SyntaxHighlightingBinding *binding, const TextLayout *tl, SyntaxHighlighting *sh) { + binding->tl = tl; + binding->sh = sh; +} + +void SyntaxHighlightingBinding_Deinit(SyntaxHighlightingBinding *binding) { + binding->tl = NULL; + binding->sh = NULL; +} + +void SyntaxHighlightingBinding_Update(SyntaxHighlightingBinding *binding, const Line *line, const Line *last_line) { + Line *prev_line = line->prev; + Stack *open_blocks = NULL; + 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_Update(prev_line, last_line); + return; + } + } + if (line == binding->tl->tb->current_line) { + // this is super dirty cause the gap funcitonality is totally disabled this way!!! + // NEED A BETTER SOLUTION + TextBuffer_MergeGap(binding->tl->tb); + } + SyntaxHighlighting_HighlightString(binding->sh->strings, line->text, open_blocks); // open_blocks == NULL is handled by the function +} diff --git a/src/syntax/textlayoutbindings.h b/src/syntax/textlayoutbindings.h new file mode 100644 index 0000000..14cb1bb --- /dev/null +++ b/src/syntax/textlayoutbindings.h @@ -0,0 +1,17 @@ +#ifndef SYNTAX_TEXTLAYOUTBINDINGS_H +#define SYNTAX_TEXTLAYOUTBINDINGS_H + +#include "highlighting.h" +#include "document/textlayout.h" + +typedef struct _SyntaxHighlightingBinding { + const TextLayout *tl; + SyntaxHighlighting *sh; +} SyntaxHighlightingBinding; + +void SyntaxHighlightingBinding_Init(SyntaxHighlightingBinding *binding, const TextLayout *tl, SyntaxHighlighting *sh); +void SyntaxHighlightingBinding_Deinit(SyntaxHighlightingBinding *binding); + +void SyntaxHighlightingBinding_Update(SyntaxHighlightingBinding *binding, const Line *line, const Line *last_line); + +#endif \ No newline at end of file From c0f1c1122f554a3c6a169b1c6bec8c6fcff60973 Mon Sep 17 00:00:00 2001 From: defname Date: Fri, 24 Oct 2025 09:14:55 +0200 Subject: [PATCH 05/21] fix some obvious bugs in SyntaxHighlighingBinding --- src/syntax/textlayoutbindings.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/syntax/textlayoutbindings.c b/src/syntax/textlayoutbindings.c index 7f4f34a..16c3250 100644 --- a/src/syntax/textlayoutbindings.c +++ b/src/syntax/textlayoutbindings.c @@ -10,27 +10,40 @@ void SyntaxHighlightingBinding_Deinit(SyntaxHighlightingBinding *binding) { binding->sh = NULL; } +static void update_lines(SyntaxHighlightingBinding *binding, const Line *line, const Line *last_line, Stack *open_blocks) { + while (line) { + // open_blocks == NULL is also handled by the function + open_blocks = SyntaxHighlighting_HighlightString(binding->sh, &line->text, open_blocks); + if (line == last_line) { + break; + } + line = line->next; + } +} + void SyntaxHighlightingBinding_Update(SyntaxHighlightingBinding *binding, const Line *line, const Line *last_line) { Line *prev_line = line->prev; Stack *open_blocks = NULL; if (prev_line) { // check if highlighting for the previous line is already calculated - SyntaxHighlightingString *shs = Table_Get(binding->sh->strings, prev_line->text); + 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; + 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_Update(prev_line, last_line); + SyntaxHighlightingBinding_Update(binding, prev_line, last_line); return; } } if (line == binding->tl->tb->current_line) { // this is super dirty cause the gap funcitonality is totally disabled this way!!! // NEED A BETTER SOLUTION - TextBuffer_MergeGap(binding->tl->tb); + TextBuffer_MergeGap((TextBuffer*)binding->tl->tb); } - SyntaxHighlighting_HighlightString(binding->sh->strings, line->text, open_blocks); // open_blocks == NULL is handled by the function + + // update all lines until last_line (including) + update_lines(binding, line, last_line, open_blocks); } From 5d44d535f028f37e05205682cd6b27bac77cdf2c Mon Sep 17 00:00:00 2001 From: defname Date: Fri, 24 Oct 2025 12:23:59 +0200 Subject: [PATCH 06/21] fix memory leak --- src/syntax/textlayoutbindings.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/syntax/textlayoutbindings.c b/src/syntax/textlayoutbindings.c index 16c3250..8c60fd3 100644 --- a/src/syntax/textlayoutbindings.c +++ b/src/syntax/textlayoutbindings.c @@ -10,15 +10,19 @@ void SyntaxHighlightingBinding_Deinit(SyntaxHighlightingBinding *binding) { binding->sh = NULL; } -static void update_lines(SyntaxHighlightingBinding *binding, const Line *line, const Line *last_line, Stack *open_blocks) { +static void update_lines(SyntaxHighlightingBinding *binding, const Line *line, const Line *last_line, const Stack *open_blocks) { + Stack *open_blocks_begin = Stack_Copy(open_blocks); while (line) { // open_blocks == NULL is also handled by the function - open_blocks = SyntaxHighlighting_HighlightString(binding->sh, &line->text, open_blocks); + Stack *open_blocks_end = SyntaxHighlighting_HighlightString(binding->sh, &line->text, open_blocks_begin); + Stack_Destroy(open_blocks_begin); + open_blocks_begin = open_blocks_end; if (line == last_line) { break; } line = line->next; } + Stack_Destroy(open_blocks_begin); } void SyntaxHighlightingBinding_Update(SyntaxHighlightingBinding *binding, const Line *line, const Line *last_line) { From c28e50e67f1728ecab0cfa0a29dc83e0779bb963 Mon Sep 17 00:00:00 2001 From: defname Date: Fri, 24 Oct 2025 12:24:12 +0200 Subject: [PATCH 07/21] add basic test --- tests/test_syntax_textlayoutbindings.c | 130 +++++++++++++++++++++++++ 1 file changed, 130 insertions(+) create mode 100644 tests/test_syntax_textlayoutbindings.c diff --git a/tests/test_syntax_textlayoutbindings.c b/tests/test_syntax_textlayoutbindings.c new file mode 100644 index 0000000..c522b67 --- /dev/null +++ b/tests/test_syntax_textlayoutbindings.c @@ -0,0 +1,130 @@ +#include "acutest.h" +#include "syntax/textlayoutbindings.h" +#include "syntax/definition.h" +#include "common/iniparser.h" +#include "common/string.h" +#include "document/textlayout.h" + +typedef struct { + SyntaxDefinition *def; + const Line **lines; + size_t line_count; + SyntaxHighlighting sh; + TextBuffer tb; + TextLayout tl; + SyntaxHighlightingBinding binding; +} TestFixture; + +static void init_textbuffer(TextBuffer *tb, const char *lines[], size_t line_count) { + TextBuffer_Init(tb); + TEST_ASSERT(line_count > 0); + String s = String_FromCStr(lines[0], strlen(lines[0])); + String_Take(&tb->current_line->text, &s); + for (size_t i=1; itext, &s); + TextBuffer_InsertLineAtBottom(tb, new_line); + } + TEST_CHECK(tb->line_count == line_count); + TEST_MSG("%zu", tb->line_count); +} + + +static SyntaxDefinition *create_syntax_definition(const char *ini) { + IniParser parser; + IniParser_Init(&parser); + IniParser_SetText(&parser, ini); + Table *table = IniParser_Parse(&parser); + TEST_ASSERT(table != NULL); + SyntaxDefinitionError error; + SyntaxDefinition *def = SyntaxDefinition_FromTable(table, &error); + TEST_ASSERT(def != NULL); + IniParser_Deinit(&parser); + Table_Destroy(table); + return def; +} + +static void setup_lines(TestFixture *fixture) { + fixture->line_count = fixture->tb.line_count; + fixture->lines = malloc(sizeof(Line*) * fixture->line_count); + TEST_ASSERT(fixture->lines); + Line *line = TextBuffer_GetFirstLine(&fixture->tb); + size_t i = 0; + while (line) { + fixture->lines[i++] = line; + line = line->next; + } +} + +static void setup_fixture(TestFixture *fixture, const char *ini, const char *lines[], size_t line_count) { + init_textbuffer(&fixture->tb, lines, line_count); + TextLayout_Init(&fixture->tl, &fixture->tb, 10, 4); + fixture->def = create_syntax_definition(ini); + SyntaxHighlighting_Init(&fixture->sh, fixture->def); + SyntaxHighlightingBinding_Init(&fixture->binding, &fixture->tl, &fixture->sh); + setup_lines(fixture); +} + +static void cleanup_fixture(TestFixture *fixture) { + SyntaxHighlightingBinding_Deinit(&fixture->binding); + SyntaxHighlighting_Deinit(&fixture->sh); + TextLayout_Deinit(&fixture->tl); + TextBuffer_Deinit(&fixture->tb); + SyntaxDefinition_Destroy(fixture->def); + free(fixture->lines); +} + + + + +const char *test_ini = R"( +[meta] +name = TEST + +[block:root] +child_blocks=keyword, comment, string + +[block:keyword] +start = keyword + +[block:comment] +start = "#" +end = $ + +[block:string] +start = "'" +end = "'" +)"; + +void test_textlayout_bindings(void) { + // data + const char *lines[] = { + "First line", + "Second line", + }; + size_t lines_count = sizeof(lines) / sizeof(char*); + + // 1. Setup + TestFixture fixture; + setup_fixture(&fixture, test_ini, lines, lines_count); + TextBuffer *tb = &fixture.tb; + SyntaxHighlighting *sh = &fixture.sh; + SyntaxHighlightingBinding *binding = &fixture.binding; + + + // 2. Calculate + SyntaxHighlightingBinding_Update(binding, tb->current_line->next, tb->current_line->next); + + // 3. Check + TEST_CHECK(Table_Get(sh->strings, &fixture.lines[0]->text) != NULL); + TEST_CHECK(Table_Get(sh->strings, &fixture.lines[1]->text) != NULL); + + // 4. Cleanup + cleanup_fixture(&fixture); +} + +TEST_LIST = { + { "TextLayoutBindings: No Styling", test_textlayout_bindings }, + { NULL, NULL } +}; From f5e0b1453cf885dae243c3767d70bdbd9f708e10 Mon Sep 17 00:00:00 2001 From: defname Date: Fri, 24 Oct 2025 12:42:18 +0200 Subject: [PATCH 08/21] add Stack_Size() --- src/common/stack.c | 4 ++++ src/common/stack.h | 1 + 2 files changed, 5 insertions(+) diff --git a/src/common/stack.c b/src/common/stack.c index 2e8eb0f..16c2795 100644 --- a/src/common/stack.c +++ b/src/common/stack.c @@ -137,6 +137,10 @@ bool Stack_IsEmpty(const Stack *stack) { return stack->size == 0; } +size_t Stack_Size(const Stack *stack) { + return stack->size; +} + void Stack_Clear(Stack *stack) { if (!stack) { return; diff --git a/src/common/stack.h b/src/common/stack.h index 1e0b09c..4b01a9b 100644 --- a/src/common/stack.h +++ b/src/common/stack.h @@ -44,6 +44,7 @@ void *Stack_Peek(const Stack *stack); bool Stack_Has(const Stack *stack, const void *item); bool Stack_IsEmpty(const Stack *stack); +size_t Stack_Size(const Stack *stack); void Stack_Clear(Stack *stack); From 57bc8db5cd9e966be2823cd8e08176a172c8fb2d Mon Sep 17 00:00:00 2001 From: defname Date: Fri, 24 Oct 2025 13:51:58 +0200 Subject: [PATCH 09/21] rename test function --- tests/test_syntax_textlayoutbindings.c | 38 ++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/tests/test_syntax_textlayoutbindings.c b/tests/test_syntax_textlayoutbindings.c index c522b67..e362e01 100644 --- a/tests/test_syntax_textlayoutbindings.c +++ b/tests/test_syntax_textlayoutbindings.c @@ -97,7 +97,7 @@ start = "'" end = "'" )"; -void test_textlayout_bindings(void) { +void test_binding_basic(void) { // data const char *lines[] = { "First line", @@ -124,7 +124,41 @@ void test_textlayout_bindings(void) { cleanup_fixture(&fixture); } +void test_binding_basic2(void) { + // data + const char *lines[] = { + "First 'line", + "Second' // line", + }; + size_t lines_count = sizeof(lines) / sizeof(char*); + + // 1. Setup + TestFixture fixture; + setup_fixture(&fixture, test_ini, lines, lines_count); + TextBuffer *tb = &fixture.tb; + SyntaxHighlighting *sh = &fixture.sh; + SyntaxHighlightingBinding *binding = &fixture.binding; + + + // 2. Calculate + SyntaxHighlightingBinding_Update(binding, fixture.lines[1], fixture.lines[1]); + + // 3. Check + SyntaxHighlightingString *shs = Table_Get(sh->strings, &fixture.lines[0]->text); + TEST_CHECK(shs != NULL); + TEST_CHECK(shs->text == &fixture.lines[0]->text); + TEST_CHECK(shs->tags_count == 1); + TEST_CHECK(shs->tags[0].byte_offset == 6); + TEST_CHECK(shs->open_blocks_at_end.size == 2); + TEST_MSG("%zu", shs->open_blocks_at_end.size); + TEST_CHECK(Table_Get(sh->strings, &fixture.lines[1]->text) != NULL); + + // 4. Cleanup + cleanup_fixture(&fixture); +} + TEST_LIST = { - { "TextLayoutBindings: No Styling", test_textlayout_bindings }, + { "TextLayoutBindings: No Styling", test_binding_basic }, + { "TextLayoutBindings: String over two lines", test_binding_basic2 }, { NULL, NULL } }; From 1f598f89b466ac079f549a37446123c6d59970a9 Mon Sep 17 00:00:00 2001 From: defname Date: Fri, 24 Oct 2025 14:24:40 +0200 Subject: [PATCH 10/21] add Stack_CopyTo() --- src/common/stack.c | 62 +++++++++++++++++++++++++--------------------- src/common/stack.h | 3 ++- 2 files changed, 36 insertions(+), 29 deletions(-) diff --git a/src/common/stack.c b/src/common/stack.c index 2e8eb0f..4fee243 100644 --- a/src/common/stack.c +++ b/src/common/stack.c @@ -17,21 +17,23 @@ #include #include "logging.h" - -static void increase_capacity(Stack *stack) { - if (!stack) { +static void resize_capacity(Stack *stack, size_t new_capacity) { + if (!stack || stack->capacity >= new_capacity) { return; } - if (stack->capacity == 0) { - stack->capacity = STACK_INITIAL_CAPACITY; - } - else { - stack->capacity *= STACK_GROW_FACTOR; - } - stack->items = realloc(stack->items, stack->capacity * sizeof(void *)); + stack->items = realloc(stack->items, new_capacity * sizeof(void *)); if (!stack->items) { logFatal("Failed to reallocate stack items."); } + stack->capacity = new_capacity; +} + +static void increase_capacity(Stack *stack) { + if (!stack) { + return; + } + size_t new_cap = stack->capacity == 0 ? STACK_INITIAL_CAPACITY : stack->capacity * STACK_GROW_FACTOR; + resize_capacity(stack, new_cap); } void Stack_Init(Stack *stack) { @@ -72,28 +74,32 @@ Stack *Stack_Copy(const Stack *src) { if (!src) { return NULL; } - Stack *copy = malloc(sizeof(Stack)); - if (!copy) { - logFatal("Failed to allocate memory for stack copy."); - } - - size_t cap = src->capacity ? src->capacity : STACK_INITIAL_CAPACITY; // handle capacity == 0 + Stack *copy = Stack_Create(); + Stack_CopyTo(copy, src); + return copy; +} - // allocate memory - copy->items = malloc(cap * sizeof(void *)); - if (!copy->items) { - logFatal("Failed to allocate memory for stack copy items."); +void Stack_CopyTo(Stack *dst, const Stack *src) { + if (!dst || !src) { + return; + } + if (src->size == 0) { + // ealry exit if src is empty + Stack_Clear(dst); + return; } - // only copy items - memcpy(copy->items, src->items, src->size * sizeof(void *)); + if (src->size > dst->capacity) { + // resize dst to a multiple of STACK_INITIAL_SIZE + size_t new_cap = ((src->size % STACK_INITIAL_CAPACITY) + 1) * STACK_INITIAL_CAPACITY; + resize_capacity(dst, new_cap); + } + // copy items + memcpy(dst->items, src->items, src->size * sizeof(void *)); // set remainder to 0 - if (cap > src->size) { - memset(copy->items + src->size, 0, (cap - src->size) * sizeof(void *)); + if (dst->capacity > src->size) { + memset(dst->items + src->size, 0, (dst->capacity - src->size) * sizeof(void *)); } - copy->size = src->size; - copy->capacity = src->capacity; - - return copy; + dst->size = src->size; } void Stack_Push(Stack *stack, void *item) { diff --git a/src/common/stack.h b/src/common/stack.h index 1e0b09c..33bf584 100644 --- a/src/common/stack.h +++ b/src/common/stack.h @@ -35,7 +35,8 @@ void Stack_Deinit(Stack *stack); Stack *Stack_Create(); void Stack_Destroy(Stack *stack); -Stack *Stack_Copy(const Stack *stack); +Stack *Stack_Copy(const Stack *stack); //< create a new copy of stack +void Stack_CopyTo(Stack *dst, const Stack *src); //< copy src to the (already initialized) dst void Stack_Push(Stack *stack, void *item); void *Stack_Pop(Stack *stack); From 517e9416eb094d90040c03d86139278e1bb4eecf Mon Sep 17 00:00:00 2001 From: defname Date: Fri, 24 Oct 2025 14:25:22 +0200 Subject: [PATCH 11/21] save open_blocks_at_begin and open_blocks_at_end to SyntaxHighlightingString instances ins SyntaxHighlighting_HighlightString() --- src/syntax/highlighting.c | 16 +++++++++++----- src/syntax/highlighting.h | 5 +++-- tests/test_syntax_highlighting.c | 13 ++++--------- 3 files changed, 18 insertions(+), 16 deletions(-) diff --git a/src/syntax/highlighting.c b/src/syntax/highlighting.c index 837523a..b0385a5 100644 --- a/src/syntax/highlighting.c +++ b/src/syntax/highlighting.c @@ -26,6 +26,7 @@ SyntaxHighlightingString *SyntaxHighlightingString_Create(const String *text) { shs->tags = NULL; shs->tags_count = 0; shs->tags_capacity = 0; + Stack_Init(&shs->open_blocks_at_begin); Stack_Init(&shs->open_blocks_at_end); shs->tags = malloc(sizeof(SyntaxHighlightingTag) * SHS_TAGS_INITIAL_CAPACITY); @@ -44,7 +45,9 @@ void SyntaxHighlightingString_Destroy(SyntaxHighlightingString *shs) { shs->tags_count = 0; shs->tags_capacity = 0; shs->text = NULL; + Stack_Deinit(&shs->open_blocks_at_begin); Stack_Deinit(&shs->open_blocks_at_end); + free(shs); } @@ -168,7 +171,7 @@ static void init_match_cache(SyntaxHighlighting *sh) { } } -Stack *SyntaxHighlighting_HighlightString(SyntaxHighlighting *sh, const String *text, const Stack *open_blocks_at_begin) { +const Stack *SyntaxHighlighting_HighlightString(SyntaxHighlighting *sh, const String *text, const Stack *open_blocks_at_begin) { // check if there is already old infomation about text in the table SyntaxHighlightingString *shs = Table_Get(sh->strings, text); if (shs) { @@ -182,14 +185,17 @@ Stack *SyntaxHighlighting_HighlightString(SyntaxHighlighting *sh, const String * } // create a working copy of the stack - Stack *open_blocks; if (open_blocks_at_begin) { - open_blocks = Stack_Copy(open_blocks_at_begin); + Stack_CopyTo(&shs->open_blocks_at_begin, open_blocks_at_begin); + Stack_CopyTo(&shs->open_blocks_at_end, open_blocks_at_begin); } else { - open_blocks = Stack_Create(); - Stack_Push(open_blocks, sh->def->root); + Stack_Clear(&shs->open_blocks_at_begin); + Stack_Push(&shs->open_blocks_at_begin, sh->def->root); + Stack_CopyTo(&shs->open_blocks_at_end, &shs->open_blocks_at_begin); } + Stack *open_blocks = &shs->open_blocks_at_end; + // initialize the cache fields of the SyntaxBlockDefs init_match_cache(sh); diff --git a/src/syntax/highlighting.h b/src/syntax/highlighting.h index 5c1739e..02f367e 100644 --- a/src/syntax/highlighting.h +++ b/src/syntax/highlighting.h @@ -60,7 +60,8 @@ typedef struct _SyntaxHighlightingString { size_t tags_count; //< number of tags in `tags` size_t tags_capacity; //< capacity of `tags` - Stack open_blocks_at_end; //< Stack of (SyntaxBlockDef*) elements that are open at the end of `text` + Stack open_blocks_at_begin; //< Stack of (SyntaxBlockDef*) elements that are open + Stack open_blocks_at_end; //< Stack of (SyntaxBlockDef*) elements that are open at the end of `text` } SyntaxHighlightingString; #define SHS_TAGS_INITIAL_CAPACITY 16 @@ -103,6 +104,6 @@ void SyntaxHighlighting_Deinit(SyntaxHighlighting *sh); * @returns * A new created `Stack` containing all open blocks at the end of `text`. */ -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); #endif \ No newline at end of file diff --git a/tests/test_syntax_highlighting.c b/tests/test_syntax_highlighting.c index 54ecfe6..215bc9d 100644 --- a/tests/test_syntax_highlighting.c +++ b/tests/test_syntax_highlighting.c @@ -56,9 +56,10 @@ static void assert_highlight_tags( SyntaxHighlighting hl; SyntaxHighlighting_Init(&hl, def); - Stack *open_blocks_at_begin = Stack_Create(); - Stack_Push(open_blocks_at_begin, def->root); - Stack *open_blocks = SyntaxHighlighting_HighlightString(&hl, &str, open_blocks_at_begin); + const Stack *open_blocks = SyntaxHighlighting_HighlightString(&hl, &str, NULL); + + SyntaxHighlightingString *shs = Table_Get(hl.strings, &str); + TEST_ASSERT(open_blocks == &shs->open_blocks_at_end); TEST_CHECK(!Stack_IsEmpty(open_blocks)); TEST_MSG("Expected open_blocks to not be not empty."); @@ -69,8 +70,6 @@ static void assert_highlight_tags( TEST_CHECK(strcmp(open_blocks_expected[i], block->name) == 0); TEST_MSG("Expected open block #%zu to be '%s' but got '%s'.", i, open_blocks_expected[i], block->name); } - - SyntaxHighlightingString *shs = Table_Get(hl.strings, &str); TEST_CHECK(shs->tags_count == tags_count); TEST_MSG("Expected %zu tags but got %zu.", tags_count, shs->tags_count); @@ -83,8 +82,6 @@ static void assert_highlight_tags( TEST_MSG("Expected block #%zu to be '%s' but got '%s'.", i, tag_blocks[i], shs->tags[i].block->name); } - Stack_Destroy(open_blocks); - Stack_Destroy(open_blocks_at_begin); String_Deinit(&str); SyntaxHighlighting_Deinit(&hl); SyntaxDefinition_Destroy(def); @@ -122,7 +119,6 @@ void test_highlight_string_simple(void) { TEST_CHECK(shs->tags[1].byte_offset == 13); // end of 'string' TEST_CHECK(shs->tags[1].block == def->root); - Stack_Destroy(open_blocks); Stack_Destroy(open_blocks_at_begin); String_Deinit(&test1); SyntaxHighlighting_Deinit(&hl); @@ -380,7 +376,6 @@ void test_stress(void) { TEST_CHECK(open_blocks != NULL); TEST_CHECK(!Stack_IsEmpty(open_blocks)); - Stack_Destroy(open_blocks); String_Deinit(&test); SyntaxHighlighting_Deinit(&hl); SyntaxDefinition_Destroy(def); From f748bb5522e8693d79ad4d2c321bddd213f2b582 Mon Sep 17 00:00:00 2001 From: defname Date: Fri, 24 Oct 2025 16:44:27 +0200 Subject: [PATCH 12/21] adept code to the changed SyntaxHighlighingString scheme --- src/syntax/textlayoutbindings.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/syntax/textlayoutbindings.c b/src/syntax/textlayoutbindings.c index 8c60fd3..5681678 100644 --- a/src/syntax/textlayoutbindings.c +++ b/src/syntax/textlayoutbindings.c @@ -11,18 +11,16 @@ void SyntaxHighlightingBinding_Deinit(SyntaxHighlightingBinding *binding) { } static void update_lines(SyntaxHighlightingBinding *binding, const Line *line, const Line *last_line, const Stack *open_blocks) { - Stack *open_blocks_begin = Stack_Copy(open_blocks); + const Stack *open_blocks_begin = open_blocks; while (line) { // open_blocks == NULL is also handled by the function - Stack *open_blocks_end = SyntaxHighlighting_HighlightString(binding->sh, &line->text, open_blocks_begin); - Stack_Destroy(open_blocks_begin); + const Stack *open_blocks_end = SyntaxHighlighting_HighlightString(binding->sh, &line->text, open_blocks_begin); open_blocks_begin = open_blocks_end; if (line == last_line) { break; } line = line->next; } - Stack_Destroy(open_blocks_begin); } void SyntaxHighlightingBinding_Update(SyntaxHighlightingBinding *binding, const Line *line, const Line *last_line) { From c13c280a2ed7c4e2f2ad3569264dc76dedfae1b1 Mon Sep 17 00:00:00 2001 From: defname Date: Sat, 25 Oct 2025 08:11:42 +0200 Subject: [PATCH 13/21] improve tests --- tests/test_syntax_textlayoutbindings.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/tests/test_syntax_textlayoutbindings.c b/tests/test_syntax_textlayoutbindings.c index e362e01..ccf8f9d 100644 --- a/tests/test_syntax_textlayoutbindings.c +++ b/tests/test_syntax_textlayoutbindings.c @@ -144,13 +144,15 @@ void test_binding_basic2(void) { SyntaxHighlightingBinding_Update(binding, fixture.lines[1], fixture.lines[1]); // 3. Check - SyntaxHighlightingString *shs = Table_Get(sh->strings, &fixture.lines[0]->text); - TEST_CHECK(shs != NULL); - TEST_CHECK(shs->text == &fixture.lines[0]->text); - TEST_CHECK(shs->tags_count == 1); - TEST_CHECK(shs->tags[0].byte_offset == 6); - TEST_CHECK(shs->open_blocks_at_end.size == 2); - TEST_MSG("%zu", shs->open_blocks_at_end.size); + SyntaxHighlightingString *shs0 = Table_Get(sh->strings, &fixture.lines[0]->text); + SyntaxHighlightingString *shs1 = Table_Get(sh->strings, &fixture.lines[1]->text); + TEST_CHECK(shs0 != NULL); + TEST_CHECK(shs0->text == &fixture.lines[0]->text); + TEST_CHECK(shs0->tags_count == 1); + TEST_CHECK(shs0->tags[0].byte_offset == 6); + TEST_CHECK(shs0->open_blocks_at_end.size == 2); + TEST_MSG("%zu", shs0->open_blocks_at_end.size); + TEST_CHECK(shs0->open_blocks_at_end.size == shs1->open_blocks_at_begin.size); TEST_CHECK(Table_Get(sh->strings, &fixture.lines[1]->text) != NULL); // 4. Cleanup From 154937f61a51e2f80e3fc005bf97b943e821b4d8 Mon Sep 17 00:00:00 2001 From: defname Date: Sat, 25 Oct 2025 08:13:04 +0200 Subject: [PATCH 14/21] Compare the open_blocks_at_begin with the open_blocks parameter for an early exit int SyntaxHighlighting_HighlightString() --- src/syntax/highlighting.c | 21 +++++++++++++++++++-- src/syntax/highlighting.h | 5 ++++- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/src/syntax/highlighting.c b/src/syntax/highlighting.c index b0385a5..dd9ca01 100644 --- a/src/syntax/highlighting.c +++ b/src/syntax/highlighting.c @@ -171,15 +171,31 @@ static void init_match_cache(SyntaxHighlighting *sh) { } } +static bool stack_equal(const Stack *a, const Stack *b) { + if (a->size != b->size) { + return false; + } + for (size_t i=0; isize; i++) { + if (a->items[i] != b->items[i]) { + return false; + } + } + return true; +} + const Stack *SyntaxHighlighting_HighlightString(SyntaxHighlighting *sh, const String *text, const Stack *open_blocks_at_begin) { // check if there is already old infomation about text in the table SyntaxHighlightingString *shs = Table_Get(sh->strings, text); if (shs) { - // clear it if so + // early exit if nothing changed since the last calculation + if (stack_equal(&shs->open_blocks_at_begin, open_blocks_at_begin)) { + return &shs->open_blocks_at_end; + } + // if open_blocks_at_begin changed clear the old information SyntaxHighlightingString_Clear(shs); } else { - // otherwise create and store + // if there is no object present in sh->strings table create one and store it shs = SyntaxHighlightingString_Create(text); Table_Set(sh->strings, text, shs, (void(*)(void*))SyntaxHighlightingString_Destroy); } @@ -190,6 +206,7 @@ const Stack *SyntaxHighlighting_HighlightString(SyntaxHighlighting *sh, const St Stack_CopyTo(&shs->open_blocks_at_end, open_blocks_at_begin); } else { + // or create a new one if open_blocks_at_begin is NULL Stack_Clear(&shs->open_blocks_at_begin); Stack_Push(&shs->open_blocks_at_begin, sh->def->root); Stack_CopyTo(&shs->open_blocks_at_end, &shs->open_blocks_at_begin); diff --git a/src/syntax/highlighting.h b/src/syntax/highlighting.h index 02f367e..17e0bb9 100644 --- a/src/syntax/highlighting.h +++ b/src/syntax/highlighting.h @@ -96,13 +96,16 @@ void SyntaxHighlighting_Deinit(SyntaxHighlighting *sh); * @brief Highlight a string according to given context. * * Add a `SyntaxHighlightingString`to `hl->strings` or update an existing one. + * The table uses the pointer address of text as key, so make sure it does not change. + * open_blocks should only be NULL for the first line. In any other case if should be set to + * SyntaxHighlighlightingString->open_blocks_at_end of the previous line/text. * * @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. * * @returns - * A new created `Stack` containing all open blocks at the end of `text`. + * 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); From 558c925dbdcbe786a4d0c87c1bb319bcaea3bfc9 Mon Sep 17 00:00:00 2001 From: defname Date: Sat, 25 Oct 2025 08:37:37 +0200 Subject: [PATCH 15/21] add testcases and improve test framework's flexibility --- tests/test_syntax_highlighting.c | 100 +++++++++++++++++++++++++++---- 1 file changed, 89 insertions(+), 11 deletions(-) diff --git a/tests/test_syntax_highlighting.c b/tests/test_syntax_highlighting.c index 215bc9d..753090c 100644 --- a/tests/test_syntax_highlighting.c +++ b/tests/test_syntax_highlighting.c @@ -31,15 +31,26 @@ Table *build_blocks_table(SyntaxDefinition *def) { } typedef struct { - const char *ini; - const char *str; - size_t tags_count; - size_t tag_offsets[32]; - const char *tag_blocks[32]; - size_t open_blocks_count; - const char *open_blocks[32]; + const char *ini; //< text of an INI file for the syntax definition + const char *str; //< str to parse (single line) + size_t open_blocks_at_begin_count; //< number of open blocks at the begin + const char *open_blocks_at_begin[32]; //< list of open block names at the begin + size_t tags_count; //< expected count of tags + size_t tag_offsets[32]; //< list of expected offsets of the tags + const char *tag_blocks[32]; //< list of expected block names of the tags + size_t open_blocks_count; //< number of expected open blocks at the end + const char *open_blocks[32]; //< names of the expected open blocks at the end } TagTestCase; +static void get_blocks(const char **block_names, size_t count, SyntaxBlockDef **out, Table *blocks_table) { + for (size_t i=0; i blocks mapping table + Table *blocks_table = build_blocks_table(def); + + // map the open_blocks_at_begin (str) list to a list of SyntaxBlockDef instances + SyntaxBlockDef *open_blocks_at_begin[32]; + get_blocks(testcase.open_blocks_at_begin, testcase.open_blocks_at_begin_count, open_blocks_at_begin, blocks_table); - const Stack *open_blocks = SyntaxHighlighting_HighlightString(&hl, &str, NULL); + // create the open_blocks_at_begin_stack + Stack *open_blocks_at_begin_stack = Stack_Create(); + for (size_t i=0; iopen_blocks_at_end); @@ -64,8 +90,11 @@ static void assert_highlight_tags( TEST_CHECK(!Stack_IsEmpty(open_blocks)); TEST_MSG("Expected open_blocks to not be not empty."); + TEST_CHECK(open_blocks_count_expected == Stack_Size(open_blocks)); + TEST_MSG("Expected %zu open blocks but got %zu.", open_blocks_count_expected, Stack_Size(open_blocks)); + for (size_t i=0; iitems[open_blocks_count_expected-i-1]; TEST_CHECK(block != NULL); TEST_CHECK(strcmp(open_blocks_expected[i], block->name) == 0); TEST_MSG("Expected open block #%zu to be '%s' but got '%s'.", i, open_blocks_expected[i], block->name); @@ -82,6 +111,9 @@ static void assert_highlight_tags( TEST_MSG("Expected block #%zu to be '%s' but got '%s'.", i, tag_blocks[i], shs->tags[i].block->name); } + Stack_Destroy(open_blocks_at_begin_stack); + Table_Destroy(blocks_table); + String_Deinit(&str); SyntaxHighlighting_Deinit(&hl); SyntaxDefinition_Destroy(def); @@ -108,7 +140,7 @@ void test_highlight_string_simple(void) { String test1 = String_Format("root 'string' root"); Stack *open_blocks_at_begin = Stack_Create(); Stack_Push(open_blocks_at_begin, def->root); - Stack *open_blocks = SyntaxHighlighting_HighlightString(&hl, &test1, open_blocks_at_begin); + const Stack *open_blocks = SyntaxHighlighting_HighlightString(&hl, &test1, open_blocks_at_begin); TEST_CHECK(open_blocks != NULL); TEST_CHECK(Stack_Peek(open_blocks) == def->root); @@ -155,6 +187,8 @@ void test_basics(void) { { test_ini1, "foobar // blabla ' bla", // comment + 1, + { "root" }, 2, {7, 22}, {"comment", "root"}, @@ -164,6 +198,8 @@ void test_basics(void) { { test_ini1, "foo 'bar' foo", // string + 1, + { "root" }, 2, {4, 9}, {"string", "root"}, @@ -173,6 +209,8 @@ void test_basics(void) { { test_ini1, "foo keyword foo", // keyword + 1, + { "root" }, 2, {4, 11}, {"keyword", "root"}, @@ -182,6 +220,8 @@ void test_basics(void) { { test_ini1, "foo '//not a comment' foo", // string with comment inside (should be ignored) + 1, + { "root" }, 2, {4, 21}, {"string", "root"}, @@ -191,6 +231,8 @@ void test_basics(void) { { test_ini1, "foo 'not a keyword' foo", // keyword inside string + 1, + { "root" }, 2, {4, 19}, {"string", "root"}, @@ -200,6 +242,8 @@ void test_basics(void) { { test_ini1, "foo (no brackets) foo", + 1, + { "root" }, 2, {4, 17}, {"brackets", "root"}, @@ -209,6 +253,8 @@ void test_basics(void) { { test_ini1, "()", + 1, + { "root" }, 2, {0, 2}, {"brackets", "root"}, @@ -218,6 +264,8 @@ void test_basics(void) { { test_ini1, "(keyword)keyword", + 1, + { "root" }, 6, {0, 1, 8, 9, 9, 16}, {"brackets", "keyword", "brackets", "root", "keyword", "root"}, @@ -227,6 +275,8 @@ void test_basics(void) { { test_ini1, "", + 1, + { "root" }, 0, {}, {}, @@ -236,6 +286,8 @@ void test_basics(void) { { test_ini1, "(('not a keyword'))", + 1, + { "root" }, 6, {0, 1, 2, 17, 18, 19}, {"brackets", "brackets", "string", "brackets", "brackets", "root"}, @@ -279,6 +331,8 @@ void test_moderate(void) { { test_ini2, "foo=blub // should end assignment", // assignment with comment + 1, + { "root" }, 5, {0, 4, 9, 33, 33}, {"assignment", "value", "comment", "assignment", "root"}, @@ -300,12 +354,36 @@ void test_open_blocks(void) { { test_ini1, "(keyword", + 1, + { "root" }, 3, {0, 1, 8}, {"brackets", "keyword", "brackets"}, 2, {"brackets", "root"} }, + { + test_ini1, + "", + 2, + { "root", "brackets" }, + 0, + {}, + {}, + 2, + {"brackets", "root"} + }, + { + test_ini1, + ")", + 2, + { "root", "brackets" }, + 1, + {1}, + {"root"}, + 1, + { "root"} + }, }; size_t count = sizeof(cases) / sizeof(cases[0]); @@ -371,7 +449,7 @@ void test_stress(void) { TEST_CASE(str); String test = String_FromCStr(str, strlen(str)); - Stack *open_blocks = SyntaxHighlighting_HighlightString(&hl, &test, NULL); + const Stack *open_blocks = SyntaxHighlighting_HighlightString(&hl, &test, NULL); TEST_CHECK(open_blocks != NULL); TEST_CHECK(!Stack_IsEmpty(open_blocks)); From f412a0d261ff0b28b3326e1f23a2d6d87756c233 Mon Sep 17 00:00:00 2001 From: defname Date: Sat, 25 Oct 2025 08:52:09 +0200 Subject: [PATCH 16/21] fix coderabbit config --- .coderabbit.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.coderabbit.yaml b/.coderabbit.yaml index a6587b8..56adac6 100644 --- a/.coderabbit.yaml +++ b/.coderabbit.yaml @@ -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: [] From 0b223a6b149868f21366ac1ad7c5963a3f180624 Mon Sep 17 00:00:00 2001 From: defname Date: Sat, 25 Oct 2025 08:56:40 +0200 Subject: [PATCH 17/21] fix resizing in Stack_CopyTo() --- src/common/stack.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/common/stack.c b/src/common/stack.c index b3cc494..f065b52 100644 --- a/src/common/stack.c +++ b/src/common/stack.c @@ -84,13 +84,13 @@ void Stack_CopyTo(Stack *dst, const Stack *src) { return; } if (src->size == 0) { - // ealry exit if src is empty + // early exit if src is empty Stack_Clear(dst); return; } if (src->size > dst->capacity) { // resize dst to a multiple of STACK_INITIAL_SIZE - size_t new_cap = ((src->size % STACK_INITIAL_CAPACITY) + 1) * STACK_INITIAL_CAPACITY; + size_t new_cap = ((src->size + STACK_INITIAL_CAPACITY - 1) / STACK_INITIAL_CAPACITY) * STACK_INITIAL_CAPACITY; resize_capacity(dst, new_cap); } // copy items From cb33a8f0056202f22ac22e8832d5ae32674d080f Mon Sep 17 00:00:00 2001 From: defname Date: Sat, 25 Oct 2025 08:58:42 +0200 Subject: [PATCH 18/21] add NULL guard to SyntaxHighlighting_HighlightString() --- src/syntax/highlighting.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/syntax/highlighting.c b/src/syntax/highlighting.c index dd9ca01..751b765 100644 --- a/src/syntax/highlighting.c +++ b/src/syntax/highlighting.c @@ -188,7 +188,7 @@ const Stack *SyntaxHighlighting_HighlightString(SyntaxHighlighting *sh, const St SyntaxHighlightingString *shs = Table_Get(sh->strings, text); if (shs) { // early exit if nothing changed since the last calculation - if (stack_equal(&shs->open_blocks_at_begin, open_blocks_at_begin)) { + if (open_blocks_at_begin && stack_equal(&shs->open_blocks_at_begin, open_blocks_at_begin)) { return &shs->open_blocks_at_end; } // if open_blocks_at_begin changed clear the old information From 6b6703b97b902a8c8a1d5373a0e61daf83838121 Mon Sep 17 00:00:00 2001 From: defname Date: Sat, 25 Oct 2025 09:03:23 +0200 Subject: [PATCH 19/21] change multiline string to valid C string --- tests/test_syntax_textlayoutbindings.c | 36 +++++++++++++------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/tests/test_syntax_textlayoutbindings.c b/tests/test_syntax_textlayoutbindings.c index ccf8f9d..0b11099 100644 --- a/tests/test_syntax_textlayoutbindings.c +++ b/tests/test_syntax_textlayoutbindings.c @@ -78,24 +78,24 @@ static void cleanup_fixture(TestFixture *fixture) { -const char *test_ini = R"( -[meta] -name = TEST - -[block:root] -child_blocks=keyword, comment, string - -[block:keyword] -start = keyword - -[block:comment] -start = "#" -end = $ - -[block:string] -start = "'" -end = "'" -)"; +const char *test_ini = +"[meta]\n" +"name = TEST\n" +"\n" +"[block:root]\n" +"child_blocks=keyword, comment, string\n" +"\n" +"[block:keyword]\n" +"start = keyword\n" +"\n" +"[block:comment]\n" +"start = \"#\"\n" +"end = $\n" +"\n" +"[block:string]\n" +"start = \"'\"\n" +"end = \"'\"\n" +; void test_binding_basic(void) { // data From 811b0e207d3816d7375e54ce019e2be03c6142d0 Mon Sep 17 00:00:00 2001 From: defname Date: Sat, 25 Oct 2025 09:09:07 +0200 Subject: [PATCH 20/21] change multiline strings to valid C strings --- tests/test_syntax_highlighting.c | 86 ++++++++++++++++---------------- 1 file changed, 42 insertions(+), 44 deletions(-) diff --git a/tests/test_syntax_highlighting.c b/tests/test_syntax_highlighting.c index 753090c..68b49ba 100644 --- a/tests/test_syntax_highlighting.c +++ b/tests/test_syntax_highlighting.c @@ -157,29 +157,28 @@ void test_highlight_string_simple(void) { SyntaxDefinition_Destroy(def); } -const char *test_ini1 = R"( -[meta] -name = TEST - -[block:root] -child_blocks=string, comment, keyword, brackets - -[block:brackets] -start=\( -end=\) -child_blocks=string, keyword, brackets - -[block:keyword] -start=keyword - -[block:string] -start=' -end=' - -[block:comment] -start = // -end = $ -)"; +const char *test_ini1 = +"[meta]\n" +"name = TEST\n" +"\n" +"[block:root]\n" +"child_blocks=string, comment, keyword, brackets\n" +"\n" +"[block:brackets]\n" +"start=\\(\n" +"end=\\)\n" +"child_blocks=string, keyword, brackets\n" +"\n" +"[block:keyword]\n" +"start=keyword\n" +"\n" +"[block:string]\n" +"start='\n" +"end='\n" +"\n" +"[block:comment]\n" +"start = //\n" +"end = $\n"; void test_basics(void) { @@ -304,27 +303,26 @@ void test_basics(void) { } -const char *test_ini2 = R"( -[meta] -name = TEST2 - -[block:root] -child_blocks=assignment, comment - -[block:assignment] -start=^[a-zA-Z_:]+= -end=$ -child_blocks=value - -[block:value] -start=. -end=$ -ends_on=comment - -[block:comment] -start = // -end = $ -)"; +const char *test_ini2 = +"[meta]\n" +"name = TEST2\n" +"\n" +"[block:root]\n" +"child_blocks=assignment, comment\n" +"\n" +"[block:assignment]\n" +"start=^[a-zA-Z_:]+=\n" +"end=$\n" +"child_blocks=value\n" +"\n" +"[block:value]\n" +"start=.\n" +"end=$\n" +"ends_on=comment\n" +"\n" +"[block:comment]\n" +"start = //\n" +"end = $\n"; void test_moderate(void) { TagTestCase cases[] = { From b0a7167c73612336553da6b709780470b8c9be35 Mon Sep 17 00:00:00 2001 From: defname Date: Sat, 25 Oct 2025 09:11:26 +0200 Subject: [PATCH 21/21] set shs->tags_capacity correctly in SyntaxHighlioghtingString_Create() --- src/syntax/highlighting.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/syntax/highlighting.c b/src/syntax/highlighting.c index 751b765..f9f0e06 100644 --- a/src/syntax/highlighting.c +++ b/src/syntax/highlighting.c @@ -33,6 +33,7 @@ SyntaxHighlightingString *SyntaxHighlightingString_Create(const String *text) { if (!shs->tags) { logFatal("Cannot allocate memory for SyntaxHighlightingString tags."); } + shs->tags_capacity = SHS_TAGS_INITIAL_CAPACITY; return shs; }