diff --git a/src/renderer/context_element.rs b/src/renderer/context_element.rs index 992fa6b..ec2e5a0 100644 --- a/src/renderer/context_element.rs +++ b/src/renderer/context_element.rs @@ -21,6 +21,16 @@ pub trait Truthiness { pub trait Walkable { fn walk(&self, segment: &str) -> Result<&dyn ContextElement, WalkError>; + + /// If an element contains meta information and should not be + /// returned as the final result of a walk, this function should + /// return true. + /// + /// For example, the iteration context contains $idx and $len but + /// it should not be the result of a dot-reference like `{.}`. + fn is_pseudo_element(&self) -> bool { + false + } } pub trait Renderable { diff --git a/src/renderer/iteration_context.rs b/src/renderer/iteration_context.rs index acdd9a1..f6a3998 100644 --- a/src/renderer/iteration_context.rs +++ b/src/renderer/iteration_context.rs @@ -68,6 +68,10 @@ impl Walkable for IterationContext { _ => Err(WalkError::CantWalk), } } + + fn is_pseudo_element(&self) -> bool { + true + } } impl CompareContextElement for IterationContext { diff --git a/src/renderer/parameters_context.rs b/src/renderer/parameters_context.rs index 7a3e6b1..1d4dd8b 100644 --- a/src/renderer/parameters_context.rs +++ b/src/renderer/parameters_context.rs @@ -103,6 +103,10 @@ impl Walkable for ParametersContext { OwnedRValue::RVLiteral(literal) => Ok(literal), } } + + fn is_pseudo_element(&self) -> bool { + true + } } impl Clone for ParametersContext { diff --git a/src/renderer/renderer.rs b/src/renderer/renderer.rs index 0de9263..88617db 100644 --- a/src/renderer/renderer.rs +++ b/src/renderer/renderer.rs @@ -17,6 +17,7 @@ use crate::renderer::inline_partial_tree::InlinePartialTreeElement; use crate::renderer::iteration_context::IterationContext; use crate::renderer::parameters_context::ParametersContext; use crate::renderer::walking::walk_path; +use std::borrow::Borrow; use std::collections::HashMap; #[derive(Clone, Debug)] @@ -813,6 +814,15 @@ impl<'a> DustRenderer<'a> { Some(new_stack) } + fn get_index_of_first_non_pseudo_element<'b, B>(breadcrumbs: &'b Vec) -> Option + where + B: Borrow, + { + breadcrumbs + .iter() + .rposition(|b| !(*b).borrow().is_pseudo_element()) + } + fn new_breadcrumbs_partial<'b>( breadcrumbs: &'b Vec<&'b dyn ContextElement>, explicit_context_breadcrumbs: &'b Vec<&'b dyn ContextElement>, @@ -837,7 +847,10 @@ impl<'a> DustRenderer<'a> { // added after the current context but before the explicit // context. match explicit_context { - None => new_stack.insert(std::cmp::max(new_stack.len() - 1, 0), ctx), + None => new_stack.insert( + Self::get_index_of_first_non_pseudo_element(&new_stack).unwrap_or(0), + ctx, + ), _ => new_stack.push(ctx), } }); diff --git a/src/renderer/walking.rs b/src/renderer/walking.rs index 49dff7f..c0c2365 100644 --- a/src/renderer/walking.rs +++ b/src/renderer/walking.rs @@ -1,4 +1,5 @@ use crate::renderer::context_element::ContextElement; +use crate::renderer::context_element::Walkable; use crate::renderer::WalkError; use std::borrow::Borrow; @@ -34,6 +35,17 @@ where WalkResult::FullyWalked(output) } +pub fn get_first_non_pseudo_element<'a, B>(breadcrumbs: &'a Vec) -> Option<&B> +where + B: Borrow, +{ + breadcrumbs + .iter() + .rev() + .filter(|b| !(*b).borrow().is_pseudo_element()) + .next() +} + pub fn walk_path<'a, B, P>( breadcrumbs: &'a Vec, path: &Vec

, @@ -60,17 +72,18 @@ where .borrow() == "." { - return match walk_path_from_single_level( - breadcrumbs - .last() - .expect("Breadcrumbs should never be empty"), - &path[1..], - ) { - // If no walking was done at all or we partially walked - // then stop trying to find anything because '.' restricts - // us to the current scope - WalkResult::NoWalk | WalkResult::PartialWalk => Err(WalkError::CantWalk), - WalkResult::FullyWalked(new_context) => Ok(new_context), + let first_non_pseudo_element = get_first_non_pseudo_element(breadcrumbs); + return match first_non_pseudo_element { + None => Err(WalkError::CantWalk), + Some(current_context) => { + match walk_path_from_single_level(current_context, &path[1..]) { + // If no walking was done at all or we partially walked + // then stop trying to find anything because '.' restricts + // us to the current scope + WalkResult::NoWalk | WalkResult::PartialWalk => Err(WalkError::CantWalk), + WalkResult::FullyWalked(new_context) => Ok(new_context), + } + } }; } for context in breadcrumbs.iter().rev() {