Support for trimming chars from generated slug's start/end.#175
Support for trimming chars from generated slug's start/end.#175mjamro wants to merge 2 commits intoctolkien:mainfrom
Conversation
There was a problem hiding this comment.
Pull request overview
This pull request adds support for trimming specific characters from the start and/or end of generated slugs, addressing issue #162 which requested the ability to remove trailing punctuation like dots from slugs.
Changes:
- Added three new configuration options:
TrimChars,TrimEndChars, andTrimStartCharstoSlugHelperConfiguration - Implemented trimming logic in
SlugHelper.GenerateSlug()method - Added comprehensive test coverage for the new trimming functionality
- Updated README.md with documentation and examples
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 8 comments.
| File | Description |
|---|---|
| src/Slugify.Core/SlugHelperConfiguration.cs | Added three new char array properties for trimming characters from both ends, end only, or start only |
| src/Slugify.Core/SlugHelper.cs | Implemented trimming operations in the slug generation pipeline |
| tests/Slugify.Core.Tests/SlugHelperTests.cs | Added three test methods to validate the trimming functionality |
| README.md | Added documentation section explaining the new trim options with examples |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| normalizedInput = Config.TrimChars.Length != 0 ? normalizedInput.Trim(Config.TrimChars) : normalizedInput; | ||
| normalizedInput = Config.TrimEndChars.Length != 0 ? normalizedInput.TrimEnd(Config.TrimEndChars) : normalizedInput; | ||
| normalizedInput = Config.TrimStartChars.Length != 0 ? normalizedInput.TrimStart(Config.TrimStartChars) : normalizedInput; |
There was a problem hiding this comment.
The trim operations are applied early in the pipeline, before string replacements and character filtering. This means they cannot trim characters that appear at the edges as a result of transformations. For example, if a string replacement adds a dot at the end, or if character filtering removes characters that expose trailing punctuation, the current implementation won't trim them. The MaximumLength feature (lines 92-100) sets a precedent by trimming trailing dashes AFTER all transformations. Consider moving these trim operations to after line 100 (before the return) to trim from the final slug, which would be more consistent and handle more use cases.
| Trim options to remove characters from the input before replacements and character filtering. This is useful to prevent generated slug to start/end with certain characters like dot. | ||
|
|
||
| - Options | ||
| - `TrimChars` — characters to trim from both ends (behaves like `string.Trim(char[])`). | ||
| - `TrimEndChars` — characters to trim from the end (behaves like `string.TrimEnd(char[])`). | ||
| - `TrimStartChars` — characters to trim from the start (behaves like `string.TrimStart(char[])`). | ||
|
|
||
| - Default value: all three are empty arrays (no extra trimming). |
There was a problem hiding this comment.
The documentation states these options remove characters "from the input before replacements and character filtering", but issue 162 describes wanting to remove trailing punctuation from the generated slug (i.e., the output). The current implementation trims early in the process, which may not match user expectations. Consider updating the documentation to clarify this behavior, or moving the trim operations to apply to the final slug output instead.
| [Theory] | ||
| [MemberData(nameof(GenerateStandardSluggers))] | ||
| public void TestTrimmingEndChars(ISlugHelper helper) | ||
| { | ||
| const string original = "hello._world._"; | ||
| const string expected = "hello._world"; | ||
|
|
||
| helper.Config = new SlugHelperConfiguration | ||
| { | ||
| TrimEndChars = [ '.', '_' ], | ||
| }; | ||
|
|
||
| Assert.Equal(expected, helper.GenerateSlug(original)); | ||
| } |
There was a problem hiding this comment.
The test only validates trimming characters that exist in the original input. Add a test case where trailing characters appear due to transformations, such as: input "test." with StringReplacements of {".": "-"} and TrimEndChars = ['-']. This would expose whether the trim operations correctly handle characters created during the slug generation process. The current implementation would fail such a test because trimming happens before replacements.
| normalizedInput = normalizedInput.Normalize(NormalizationForm.FormD); | ||
| normalizedInput = Config.TrimWhitespace ? normalizedInput.Trim() : normalizedInput; | ||
| normalizedInput = Config.ForceLowerCase ? normalizedInput.ToLowerInvariant() : normalizedInput; | ||
| normalizedInput = Config.TrimChars.Length != 0 ? normalizedInput.Trim(Config.TrimChars) : normalizedInput; |
There was a problem hiding this comment.
If a user sets both TrimChars and TrimEndChars (or TrimStartChars), the operations will be applied sequentially, which could lead to unexpected behavior. For example, if TrimChars = ['.'] and TrimEndChars = ['_'], after TrimChars removes dots from both ends, TrimEndChars would then remove underscores. Consider documenting this interaction, or detecting and warning about conflicting configurations, or applying only the most specific option when multiple are set.
| normalizedInput = Config.TrimChars.Length != 0 ? normalizedInput.Trim(Config.TrimChars) : normalizedInput; | |
| normalizedInput = Config.TrimChars.Length != 0 && Config.TrimEndChars.Length == 0 && Config.TrimStartChars.Length == 0 | |
| ? normalizedInput.Trim(Config.TrimChars) | |
| : normalizedInput; |
| /// List of characters to be trimmed (removed from both ends) from generated slug | ||
| /// </summary> | ||
| public char[] TrimChars { get; set; } = []; | ||
|
|
||
| /// <summary> | ||
| /// List of characters to be trimmed from end of the generated slug | ||
| /// </summary> | ||
| public char[] TrimEndChars { get; set; } = []; | ||
|
|
||
| /// <summary> | ||
| /// List of characters to be trimmed from start of the generated slug |
There was a problem hiding this comment.
The documentation says "List of characters to be trimmed (removed from both ends) from generated slug" but the trimming happens on the input, not the generated slug. Consider changing to "from the input string" or "before slug generation" for accuracy. Similarly for TrimEndChars and TrimStartChars documentation.
| /// List of characters to be trimmed (removed from both ends) from generated slug | |
| /// </summary> | |
| public char[] TrimChars { get; set; } = []; | |
| /// <summary> | |
| /// List of characters to be trimmed from end of the generated slug | |
| /// </summary> | |
| public char[] TrimEndChars { get; set; } = []; | |
| /// <summary> | |
| /// List of characters to be trimmed from start of the generated slug | |
| /// List of characters to be trimmed (removed from both ends) from the input string before slug generation. | |
| /// </summary> | |
| public char[] TrimChars { get; set; } = []; | |
| /// <summary> | |
| /// List of characters to be trimmed from the end of the input string before slug generation. | |
| /// </summary> | |
| public char[] TrimEndChars { get; set; } = []; | |
| /// <summary> | |
| /// List of characters to be trimmed from the start of the input string before slug generation. |
|
|
||
| ### `TrimChars`, `TrimEndChars`, `TrimStartChars` | ||
|
|
||
| Trim options to remove characters from the input before replacements and character filtering. This is useful to prevent generated slug to start/end with certain characters like dot. |
There was a problem hiding this comment.
Grammar issue: "to prevent generated slug to start/end" should be either "to prevent the generated slug from starting/ending" or "to prevent generated slugs from starting/ending".
| Trim options to remove characters from the input before replacements and character filtering. This is useful to prevent generated slug to start/end with certain characters like dot. | |
| Trim options to remove characters from the input before replacements and character filtering. This is useful to prevent the generated slug from starting or ending with certain characters like dot. |
Closes #162