///|
/// A block definition and the template it came from.
priv struct Frame {
block : @syntax.Block
template : String
}
///|
/// One render in flight: the output, the scopes, the block definitions the
/// inheritance chain filed, and where in which template it stands.
priv struct Run {
env : Env
mut call : @spec.Call
mut out : StringBuilder
mut scopes : Array[Map[String, @spec.Value]]
mut template : String
mut line : Int32
mut terse : Bool
mut xml : Bool
depth : Ref[Int32]
once : Map[String, String]
mut blocks : Map[String, Array[Frame]]
mut braking : Bool
mut skipping : Bool
mut extended : Bool
mut parents : Array[Template]
}
///|
fn Run::new(
env : Env,
template : String,
autoescape : Bool,
context : Map[String, @spec.Value],
) -> Run {
{
env,
call: env.call(autoescape),
out: StringBuilder(),
scopes: [context],
template,
line: 1,
terse: false,
xml: false,
depth: Ref(0),
once: Map([]),
blocks: Map([]),
braking: false,
skipping: false,
extended: false,
parents: [],
}
}
///|
fn Run::autoescape(self : Run) -> Bool {
self.call.autoescape
}
///|
fn Run::set_autoescape(self : Run, on : Bool) -> Unit {
self.call = self.call.escaping(on)
}
///|
/// The value a name has here: innermost scope first, then the globals,
/// else undefined.
fn Run::lookup(self : Run, name : String) -> @spec.Value {
for i = self.scopes.length() - 1; i >= 0; i = i - 1 {
match self.scopes[i].get(name) {
Some(v) => return v
None => ()
}
}
match self.env.globals.get(name) {
Some(v) => v
None => Undefined(name)
}
}
///|
fn Run::bind(self : Run, name : String, value : @spec.Value) -> Unit {
self.scopes[self.scopes.length() - 1][name] = value
}
///|
fn Run::push(self : Run, scope : Map[String, @spec.Value]) -> Unit {
self.scopes.push(scope)
}
///|
fn Run::pop(self : Run) -> Unit {
ignore(self.scopes.pop())
}
///|
/// Run `body` inside a new scope.
fn Run::scoped(
self : Run,
scope : Map[String, @spec.Value],
body : () -> Unit raise @spec.Refused,
) -> Unit raise @spec.Refused {
self.push(scope)
defer self.pop()
body()
}
///|
/// Every name visible here, innermost winning.
fn Run::visible(self : Run) -> Map[String, @spec.Value] {
let flat = Map([])
for scope in self.scopes {
for k, v in scope {
flat[k] = v
}
}
flat
}
///|
/// Run `body` with output going to a fresh buffer, and return what it wrote.
fn Run::capture(
self : Run,
body : () -> Unit raise @spec.Refused,
) -> String raise @spec.Refused {
let outer = self.out
let extended = self.extended
self.out = StringBuilder()
self.extended = false
defer {
self.out = outer
self.extended = extended
}
body()
self.out.to_string()
}
///|
/// Count one more level of nesting, refused past the bound.
fn Run::enter(self : Run) -> Unit raise @spec.Refused {
self.depth.val += 1
let limit = self.env.settings.limits.depth
if self.depth.val > limit {
let got = self.depth.val
self.depth.val -= 1
raise Exceeded(template="", line=0, limit~, got~)
}
}
///|
fn Run::leave(self : Run) -> Unit {
self.depth.val -= 1
}
///|
/// Write a value as output: escaped when escaping is on and it is not
/// already safe.
fn Run::emit(self : Run, value : @spec.Value) -> Unit raise @spec.Refused {
match value {
Markup(s) => self.out.write_string(s)
_ => {
let text = self.call.text(value)
self.out.write_string(
if self.autoescape() {
@spec.escape(text)
} else {
text
},
)
}
}
}
///|
/// Text built from rendered output: safe when escaping is on.
fn Run::rendered(self : Run, text : String) -> @spec.Value {
if self.autoescape() {
Markup(text)
} else {
Text(text)
}
}