diff --git a/DIAGNOSTIC_TESTS.yaml b/DIAGNOSTIC_TESTS.yaml index 9befd7a..9458277 100644 --- a/DIAGNOSTIC_TESTS.yaml +++ b/DIAGNOSTIC_TESTS.yaml @@ -157,12 +157,12 @@ input: | expected: | group_by expects 1 argument, got 0 - --> :11:36 - | + --> :11:36 + | 9 | href="https://edwardtufte.github.io/tufte-css/">Tufte CSS 10 |

11 |

© {{now | group_by }} {{params.author.name}}

- | ^^^^^^^^^ missing argument + | ^^^^^^^^ missing argument 12 | | --- @@ -303,7 +303,7 @@ input: | expected: | argument must not be zero - --> :1:5 + --> :1:16 | 1 |
    {{#items | first 0}}
  • {{.}}
  • {{/items}}
| ^^^^^^^ @@ -317,7 +317,7 @@ input: |
    {{#items | first -3}}
  • {{.}}
  • {{/items}}
expected: | first -3 is equivalent to last 3 - --> :1:5 + --> :1:16 | 1 |
    {{#items | first -3}}
  • {{.}}
  • {{/items}}
| ^^^^^^^^ consider using the clearer form diff --git a/mustache/pipes.odin b/mustache/pipes.odin index 9bf35f7..1e850bb 100644 --- a/mustache/pipes.odin +++ b/mustache/pipes.odin @@ -60,9 +60,10 @@ pipe_op_candidates :: proc(allocator := context.temp_allocator) -> []string { } Pipe_Filter :: struct { - op: string, - args: [dynamic; MAX_PIPE_ARGS]string, - op_pos: int, + op: string, + args: [dynamic; MAX_PIPE_ARGS]string, + op_pos: int, + end_pos: int, } Group :: struct { @@ -197,8 +198,9 @@ parse_pipeline :: proc( } filter := Pipe_Filter { - op = tokens[0], - op_pos = content_base + op_offset_in_content, + op = tokens[0], + op_pos = content_base + op_offset_in_content, + end_pos = content_base + op_offset_in_content + len(seg), } for j in 1 ..< len(tokens) { append(&filter.args, tokens[j]) @@ -297,7 +299,8 @@ apply_filter :: proc( switch op { case .Group_By: - return apply_group_by(value, filter.args[:], pos) + span := filter.end_pos - filter.op_pos + return apply_group_by(value, filter.args[:], filter.op_pos, span) case .Format: str, ok := reflect.as_string(value) if !ok { @@ -337,9 +340,11 @@ apply_filter :: proc( return any{new_clone(str2, context.temp_allocator), typeid_of(string)}, nil } case .First: - return take(value, filter.args[:], true, tmpl, pos, filter.op_pos) + span := filter.end_pos - filter.op_pos + return take(value, filter.args[:], true, tmpl, filter.op_pos, span) case .Last: - return take(value, filter.args[:], false, tmpl, pos, filter.op_pos) + span := filter.end_pos - filter.op_pos + return take(value, filter.args[:], false, tmpl, filter.op_pos, span) } return {}, nil } @@ -381,19 +386,27 @@ apply_format :: proc( } // Groups preserve first-appearance order from the input list. -apply_group_by :: proc(value: any, args: []string, pos: int) -> (result: any, err: Error) { +apply_group_by :: proc(value: any, args: []string, pos: int, span: int) -> (result: any, err: Error) { if len(args) != 1 { + hint := "" + if len(args) == 0 { + hint = "missing argument" + } else { + hint = "too many arguments" + } return nil, Error_Body { msg = fmt.tprintf("group_by expects 1 argument, got %d", len(args)), pos = pos, + span = span, kind = .Data, + hint = hint, } } field := args[0] elem_info, count, data := list_info(value) if elem_info == nil { - return nil, Error_Body{msg = "group_by expects a list", pos = pos, kind = .Data} + return nil, Error_Body{msg = "group_by expects a list", pos = pos, span = span, kind = .Data} } groups := make([dynamic]Group, 0, 8, context.temp_allocator) @@ -441,8 +454,8 @@ take :: proc( args: []string, from_start: bool, tmpl: Template, - tag_pos: int, op_pos: int, + span: int, ) -> ( any, Error, @@ -458,7 +471,8 @@ take :: proc( if !is_str { return nil, Error_Body { msg = "requires a list or string", - pos = tag_pos, + pos = op_pos, + span = span, kind = .Data, source = tmpl.source, path = tmpl.path, @@ -472,7 +486,8 @@ take :: proc( if !is_list && len(args) == 0 { warning = Error_Body { msg = "defaulting to n=1 for string", - pos = tag_pos, + pos = op_pos, + span = span, kind = .Warning, source = tmpl.source, path = tmpl.path, @@ -484,7 +499,8 @@ take :: proc( if !ok { return nil, Error_Body { msg = fmt.tprintf("invalid argument '%s'", args[0]), - pos = tag_pos, + pos = op_pos, + span = span, kind = .Data, source = tmpl.source, path = tmpl.path, @@ -496,7 +512,8 @@ take :: proc( if n == 0 { return nil, Error_Body { msg = "argument must not be zero", - pos = tag_pos, + pos = op_pos, + span = span, kind = .Data, source = tmpl.source, path = tmpl.path, @@ -508,7 +525,8 @@ take :: proc( alt_name := from_start ? "last" : "first" warning = Error_Body { msg = fmt.tprintf("%s %d is equivalent to %s %d", op_name, n, alt_name, 0 - n), - pos = tag_pos, + pos = op_pos, + span = span, kind = .Warning, source = tmpl.source, path = tmpl.path,