Skip to content

Conversation

@osteele
Copy link
Owner

@osteele osteele commented Nov 14, 2025

  • Add proper Unicode handling using runes instead of regex
  • Fix performance issues with strings longer than 1000 characters
  • Add support for slicing arrays/slices, not just strings
  • Improve bounds checking: clamp out-of-bounds negative indices to 0
  • Add comprehensive test coverage:
    • Unicode strings (Japanese characters)
    • Very long strings (10,000+ characters)
    • Out-of-bounds indices (positive and negative)
    • Array slicing from split operations

Resolves #72

Checklist

  • I have read the contribution guidelines.
  • make test passes.
  • make lint passes.
  • New and changed code is covered by tests.
  • Performance improvements include benchmarks.
  • Changes match the documented (not just the implemented) behavior of Shopify.

- Add proper Unicode handling using runes instead of regex
- Fix performance issues with strings longer than 1000 characters
- Add support for slicing arrays/slices, not just strings
- Improve bounds checking: clamp out-of-bounds negative indices to 0
- Add comprehensive test coverage:
  - Unicode strings (Japanese characters)
  - Very long strings (10,000+ characters)
  - Out-of-bounds indices (positive and negative)
  - Array slicing from split operations

Co-authored-by: Olivier Favre <olivier@wonderpush.com>
Resolves #72
@osteele osteele requested a review from Copilot November 14, 2025 12:38
@osteele osteele merged commit 2449340 into main Nov 14, 2025
12 checks passed
@osteele osteele deleted the claude/rebase-review-liquid-pr-011CUrSNw4Gv4suTCJNi3erS branch November 14, 2025 12:41
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR enhances the slice filter to add proper Unicode support, improve performance with long strings, and extend functionality to support array/slice slicing in addition to string slicing.

Key changes:

  • Replaced string-specific implementation with a generic interface{}-based approach that handles strings, byte arrays, and slices
  • Added proper Unicode handling by using runes instead of byte indices for string slicing
  • Improved bounds checking by clamping out-of-bounds negative indices to 0 instead of returning empty results

Reviewed Changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
filters/standard_filters.go Refactored slice filter to accept interface{} instead of string, added support for slicing arrays/slices using values.Convert, improved negative index bounds checking to clamp to 0
filters/standard_filters_test.go Added comprehensive test coverage for Unicode strings (Japanese characters), very long strings (10,000+ characters), out-of-bounds indices (both positive and negative), and array slicing from split operations

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +207 to +242
if start < 0 {
start = len(runes) + start
if start < 0 {
start = 0
}
}
if start > len(runes) {
start = len(runes)
}
end := start + n
if end > len(runes) {
end = len(runes)
}
return string(runes[start:end])
}

end := start + n
if end > len(ss) {
end = len(ss)
// Are we in the slice case?
// A type test cannot suffice because []T and []U are different types, so we must use conversion.
var slice []interface{}
if sliceIface, err := values.Convert(v, reflect.TypeOf(slice)); err == nil {
var ok bool
if slice, ok = sliceIface.([]interface{}); ok {
n := length(1)
if start < 0 {
start = len(slice) + start
if start < 0 {
start = 0
}
}
if start > len(slice) {
start = len(slice)
}
end := start + n
if end > len(slice) {
end = len(slice)
}
return slice[start:end]
Copy link

Copilot AI Nov 14, 2025

Choose a reason for hiding this comment

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

The bounds checking logic (lines 207-220 for strings and 229-242 for slices) is duplicated. Consider extracting this into a helper function to reduce code duplication and improve maintainability. For example:

func clampSliceBounds(start, length, size int) (int, int) {
    if start < 0 {
        start = size + start
        if start < 0 {
            start = 0
        }
    }
    if start > size {
        start = size
    }
    end := start + length
    if end > size {
        end = size
    }
    return start, end
}

This would make the code more maintainable and reduce the chance of inconsistencies between the two implementations.

Copilot uses AI. Check for mistakes.

if start < 0 {
start = len(ss) + start
fd.AddFilter("slice", func(v interface{}, start int, length func(int) int) interface{} {
Copy link

Copilot AI Nov 14, 2025

Choose a reason for hiding this comment

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

The code uses interface{} instead of any, which is inconsistent with the rest of the codebase. All other filters in this file use any (e.g., lines 31, 38, 71, 78, 111, 290, 298). Consider using any for consistency:

fd.AddFilter("slice", func(v any, start int, length func(int) int) any {
Suggested change
fd.AddFilter("slice", func(v interface{}, start int, length func(int) int) interface{} {
fd.AddFilter("slice", func(v any, start int, length func(int) int) any {

Copilot uses AI. Check for mistakes.
Comment on lines +224 to +227
var slice []interface{}
if sliceIface, err := values.Convert(v, reflect.TypeOf(slice)); err == nil {
var ok bool
if slice, ok = sliceIface.([]interface{}); ok {
Copy link

Copilot AI Nov 14, 2025

Choose a reason for hiding this comment

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

The code uses []interface{} instead of []any, which is inconsistent with the rest of the codebase. All other filters in this file use []any (e.g., lines 44, 53, 58, 71, 78, 303). Consider using []any for consistency:

var slice []any
if sliceIface, err := values.Convert(v, reflect.TypeOf(slice)); err == nil {
    var ok bool
    if slice, ok = sliceIface.([]any); ok {
Suggested change
var slice []interface{}
if sliceIface, err := values.Convert(v, reflect.TypeOf(slice)); err == nil {
var ok bool
if slice, ok = sliceIface.([]interface{}); ok {
var slice []any
if sliceIface, err := values.Convert(v, reflect.TypeOf(slice)); err == nil {
var ok bool
if slice, ok = sliceIface.([]any); ok {

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants