feat: Added indent filter (#188)

This commit is contained in:
Ilya Puchka
2018-01-28 17:17:23 +01:00
committed by Kyle Fuller
parent 4827fb8e20
commit fa68ba9df8
7 changed files with 94 additions and 0 deletions

View File

@@ -10,6 +10,7 @@
- Added `split` filter - Added `split` filter
- Allow default string filters to be applied to arrays - Allow default string filters to be applied to arrays
- Similar filters are suggested when unknown filter is used - Similar filters are suggested when unknown filter is used
- Added `indent` filter
### Bug Fixes ### Bug Fixes

View File

@@ -58,6 +58,7 @@ class DefaultExtension: Extension {
registerFilter("lowercase", filter: lowercase) registerFilter("lowercase", filter: lowercase)
registerFilter("join", filter: joinFilter) registerFilter("join", filter: joinFilter)
registerFilter("split", filter: splitFilter) registerFilter("split", filter: splitFilter)
registerFilter("indent", filter: indentFilter)
} }
} }

View File

@@ -65,3 +65,49 @@ func splitFilter(value: Any?, arguments: [Any?]) throws -> Any? {
return value return value
} }
func indentFilter(value: Any?, arguments: [Any?]) throws -> Any? {
guard arguments.count <= 3 else {
throw TemplateSyntaxError("'indent' filter can take at most 3 arguments")
}
var indentWidth = 4
if arguments.count > 0 {
guard let value = arguments[0] as? Int else {
throw TemplateSyntaxError("'indent' filter width argument must be an Integer (\(String(describing: arguments[0])))")
}
indentWidth = value
}
var indentationChar = " "
if arguments.count > 1 {
guard let value = arguments[1] as? String else {
throw TemplateSyntaxError("'indent' filter indentation argument must be a String (\(String(describing: arguments[1]))")
}
indentationChar = value
}
var indentFirst = false
if arguments.count > 2 {
guard let value = arguments[2] as? Bool else {
throw TemplateSyntaxError("'indent' filter indentFirst argument must be a Bool")
}
indentFirst = value
}
let indentation = [String](repeating: indentationChar, count: indentWidth).joined(separator: "")
return indent(stringify(value), indentation: indentation, indentFirst: indentFirst)
}
func indent(_ content: String, indentation: String, indentFirst: Bool) -> String {
guard !indentation.isEmpty else { return content }
var lines = content.components(separatedBy: .newlines)
let firstLine = (indentFirst ? indentation : "") + lines.removeFirst()
let result = lines.reduce([firstLine]) { (result, line) in
return result + [(line.isEmpty ? "" : "\(indentation)\(line)")]
}
return result.joined(separator: "\n")
}

View File

@@ -70,6 +70,10 @@ public struct Variable : Equatable, Resolvable {
if let number = Number(variable) { if let number = Number(variable) {
return number return number
} }
// Boolean literal
if let bool = Bool(variable) {
return bool
}
for bit in lookup() { for bit in lookup() {
current = normalize(current) current = normalize(current)

View File

@@ -244,4 +244,30 @@ func testFilter() {
} }
describe("indent filter") {
$0.it("indents content") {
let template = Template(templateString: "{{ value|indent:2 }}")
let result = try template.render(Context(dictionary: ["value": "One\nTwo"]))
try expect(result) == "One\n Two"
}
$0.it("can indent with arbitrary character") {
let template = Template(templateString: "{{ value|indent:2,\"\t\" }}")
let result = try template.render(Context(dictionary: ["value": "One\nTwo"]))
try expect(result) == "One\n\t\tTwo"
}
$0.it("can indent first line") {
let template = Template(templateString: "{{ value|indent:2,\" \",true }}")
let result = try template.render(Context(dictionary: ["value": "One\nTwo"]))
try expect(result) == " One\n Two"
}
$0.it("does not indent empty lines") {
let template = Template(templateString: "{{ value|indent }}")
let result = try template.render(Context(dictionary: ["value": "One\n\n\nTwo\n\n"]))
try expect(result) == "One\n\n\n Two\n\n"
}
}
} }

View File

@@ -21,5 +21,14 @@ func testFilterTag() {
try expect(try template.render()).toThrow() try expect(try template.render()).toThrow()
} }
$0.it("can render filters with arguments") {
let ext = Extension()
ext.registerFilter("split", filter: {
return ($0 as! String).components(separatedBy: $1[0] as! String)
})
let env = Environment(extensions: [ext])
let result = try env.renderTemplate(string: "{% filter split:\",\"|join:\";\" %}{{ items|join:\",\" }}{% endfilter %}", context: ["items": [1, 2]])
try expect(result) == "1;2"
}
} }
} }

View File

@@ -71,6 +71,13 @@ func testVariable() {
try expect(result) == 3.14 try expect(result) == 3.14
} }
$0.it("can resolve boolean literal") {
try expect(Variable("true").resolve(context) as? Bool) == true
try expect(Variable("false").resolve(context) as? Bool) == false
try expect(Variable("0").resolve(context) as? Int) == 0
try expect(Variable("1").resolve(context) as? Int) == 1
}
$0.it("can resolve a string variable") { $0.it("can resolve a string variable") {
let variable = Variable("name") let variable = Variable("name")
let result = try variable.resolve(context) as? String let result = try variable.resolve(context) as? String