Skip to content

Commit 8a70973

Browse files
pwilkinCISC
andauthored
Add Jinja support for "indent" string filter (ggml-org#19529)
* Add partial Jinja support for "indent" string filter * Fully implement indent * Add tests for all width variants. * Update tests/test-jinja.cpp Co-authored-by: Sigbjørn Skjæret <sigbjorn.skjaeret@scala.com> * Fix getline ignoring trailing newlines * Update common/jinja/value.cpp Co-authored-by: Sigbjørn Skjæret <sigbjorn.skjaeret@scala.com> * fix first indent condition --------- Co-authored-by: Sigbjørn Skjæret <sigbjorn.skjaeret@scala.com>
1 parent e7f2f95 commit 8a70973

2 files changed

Lines changed: 83 additions & 2 deletions

File tree

common/jinja/value.cpp

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
// for converting from JSON to jinja values
55
#include <nlohmann/json.hpp>
66

7+
#include <sstream>
78
#include <string>
89
#include <cctype>
910
#include <vector>
@@ -715,8 +716,46 @@ const func_builtins & value_string_t::get_builtins() const {
715716
return args.get_pos(0);
716717
}},
717718
{"tojson", tojson},
718-
{"indent", [](const func_args &) -> value {
719-
throw not_implemented_exception("String indent builtin not implemented");
719+
{"indent", [](const func_args &args) -> value {
720+
args.ensure_count(1, 4);
721+
value val_input = args.get_pos(0);
722+
value val_width = args.get_kwarg_or_pos("width", 1);
723+
const bool first = args.get_kwarg_or_pos("first", 2)->as_bool(); // undefined == false
724+
const bool blank = args.get_kwarg_or_pos("blank", 3)->as_bool(); // undefined == false
725+
if (!is_val<value_string>(val_input)) {
726+
throw raised_exception("indent() first argument must be a string");
727+
}
728+
std::string indent;
729+
if (is_val<value_int>(val_width)) {
730+
indent.assign(val_width->as_int(), ' ');
731+
} else if (is_val<value_string>(val_width)) {
732+
indent = val_width->as_string().str();
733+
} else {
734+
indent = " ";
735+
}
736+
std::string indented;
737+
std::string input = val_input->as_string().str();
738+
std::istringstream iss = std::istringstream(input);
739+
std::string line;
740+
while (std::getline(iss, line)) {
741+
if (!indented.empty()) {
742+
indented.push_back('\n');
743+
}
744+
if ((indented.empty() ? first : (!line.empty() || blank))) {
745+
indented += indent;
746+
}
747+
indented += line;
748+
}
749+
if (!input.empty() && input.back() == '\n') {
750+
indented.push_back('\n');
751+
if (blank) {
752+
indented += indent;
753+
}
754+
}
755+
756+
auto res = mk_val<value_string>(indented);
757+
res->val_str.mark_input_based_on(val_input->as_string());
758+
return res;
720759
}},
721760
{"join", [](const func_args &) -> value {
722761
throw not_implemented_exception("String join builtin not implemented");

tests/test-jinja.cpp

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -691,6 +691,48 @@ static void test_filters(testing & t) {
691691
"{\n \"a\": 1,\n \"b\": [\n 1,\n 2\n ]\n}"
692692
);
693693

694+
test_template(t, "indent",
695+
"{{ data|indent(2) }}",
696+
{{ "data", "foo\nbar" }},
697+
"foo\n bar"
698+
);
699+
700+
test_template(t, "indent first only",
701+
"{{ data|indent(width=3,first=true) }}",
702+
{{ "data", "foo\nbar" }},
703+
" foo\n bar"
704+
);
705+
706+
test_template(t, "indent blank lines and first line",
707+
"{{ data|indent(width=5,blank=true,first=true) }}",
708+
{{ "data", "foo\n\nbar" }},
709+
" foo\n \n bar"
710+
);
711+
712+
test_template(t, "indent with default width",
713+
"{{ data|indent() }}",
714+
{{ "data", "foo\nbar" }},
715+
"foo\n bar"
716+
);
717+
718+
test_template(t, "indent with no newline",
719+
"{{ data|indent }}",
720+
{{ "data", "foo" }},
721+
"foo"
722+
);
723+
724+
test_template(t, "indent with trailing newline",
725+
"{{ data|indent(blank=true) }}",
726+
{{ "data", "foo\n" }},
727+
"foo\n "
728+
);
729+
730+
test_template(t, "indent with string",
731+
"{{ data|indent(width='>>>>') }}",
732+
{{ "data", "foo\nbar" }},
733+
"foo\n>>>>bar"
734+
);
735+
694736
test_template(t, "chained filters",
695737
"{{ ' HELLO '|trim|lower }}",
696738
json::object(),

0 commit comments

Comments
 (0)