improve selector error handling
This commit is contained in:
parent
aea7c9c408
commit
a3a33db47a
@ -73,9 +73,12 @@ fn selector_nest(args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
|
|||||||
.map(|sel| sel.node.to_selector(parser, "selectors", true))
|
.map(|sel| sel.node.to_selector(parser, "selectors", true))
|
||||||
.collect::<SassResult<Vec<Selector>>>()?
|
.collect::<SassResult<Vec<Selector>>>()?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.fold(Selector::new(), |parent, child| {
|
.try_fold(
|
||||||
child.resolve_parent_selectors(&parent, true)
|
Selector::new(span),
|
||||||
})
|
|parent, child| -> SassResult<Selector> {
|
||||||
|
Ok(child.resolve_parent_selectors(&parent, true)?)
|
||||||
|
},
|
||||||
|
)?
|
||||||
.into_value())
|
.into_value())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -130,8 +133,9 @@ fn selector_append(args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value>
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect::<SassResult<Vec<ComplexSelector>>>()?,
|
.collect::<SassResult<Vec<ComplexSelector>>>()?,
|
||||||
|
span,
|
||||||
})
|
})
|
||||||
.resolve_parent_selectors(&parent, false))
|
.resolve_parent_selectors(&parent, false)?)
|
||||||
})?
|
})?
|
||||||
.into_value())
|
.into_value())
|
||||||
}
|
}
|
||||||
@ -148,7 +152,7 @@ fn selector_extend(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Va
|
|||||||
.arg(&mut args, 2, "extender")?
|
.arg(&mut args, 2, "extender")?
|
||||||
.to_selector(parser, "extender", false)?;
|
.to_selector(parser, "extender", false)?;
|
||||||
|
|
||||||
Ok(Extender::extend(selector.0, source.0, target.0)?.to_sass_list())
|
Ok(Extender::extend(selector.0, source.0, target.0, args.span())?.to_sass_list())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn selector_replace(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
|
fn selector_replace(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
|
||||||
@ -163,7 +167,7 @@ fn selector_replace(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<V
|
|||||||
parser
|
parser
|
||||||
.arg(&mut args, 2, "replacement")?
|
.arg(&mut args, 2, "replacement")?
|
||||||
.to_selector(parser, "replacement", false)?;
|
.to_selector(parser, "replacement", false)?;
|
||||||
Ok(Extender::replace(selector.0, source.0, target.0)?.to_sass_list())
|
Ok(Extender::replace(selector.0, source.0, target.0, args.span())?.to_sass_list())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn selector_unify(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
|
fn selector_unify(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
|
||||||
|
21
src/lib.rs
21
src/lib.rs
@ -143,8 +143,9 @@ fn raw_to_parse_error(map: &CodeMap, err: Error) -> Error {
|
|||||||
#[cfg(not(feature = "wasm"))]
|
#[cfg(not(feature = "wasm"))]
|
||||||
pub fn from_path(p: &str) -> Result<String> {
|
pub fn from_path(p: &str) -> Result<String> {
|
||||||
let mut map = CodeMap::new();
|
let mut map = CodeMap::new();
|
||||||
let mut extender = Extender::new();
|
|
||||||
let file = map.add_file(p.into(), String::from_utf8(fs::read(p)?)?);
|
let file = map.add_file(p.into(), String::from_utf8(fs::read(p)?)?);
|
||||||
|
let empty_span = file.span.subspan(0, 0);
|
||||||
|
let mut extender = Extender::new(empty_span);
|
||||||
Css::from_stmts(
|
Css::from_stmts(
|
||||||
Parser {
|
Parser {
|
||||||
toks: &mut Lexer::new(&file)
|
toks: &mut Lexer::new(&file)
|
||||||
@ -155,8 +156,8 @@ pub fn from_path(p: &str) -> Result<String> {
|
|||||||
path: p.as_ref(),
|
path: p.as_ref(),
|
||||||
scopes: &mut NeverEmptyVec::new(Scope::new()),
|
scopes: &mut NeverEmptyVec::new(Scope::new()),
|
||||||
global_scope: &mut Scope::new(),
|
global_scope: &mut Scope::new(),
|
||||||
super_selectors: &mut NeverEmptyVec::new(Selector::new()),
|
super_selectors: &mut NeverEmptyVec::new(Selector::new(empty_span)),
|
||||||
span_before: file.span.subspan(0, 0),
|
span_before: empty_span,
|
||||||
content: None,
|
content: None,
|
||||||
in_mixin: false,
|
in_mixin: false,
|
||||||
in_function: false,
|
in_function: false,
|
||||||
@ -188,8 +189,9 @@ pub fn from_path(p: &str) -> Result<String> {
|
|||||||
#[cfg(not(feature = "wasm"))]
|
#[cfg(not(feature = "wasm"))]
|
||||||
pub fn from_string(p: String) -> Result<String> {
|
pub fn from_string(p: String) -> Result<String> {
|
||||||
let mut map = CodeMap::new();
|
let mut map = CodeMap::new();
|
||||||
let mut extender = Extender::new();
|
|
||||||
let file = map.add_file("stdin".into(), p);
|
let file = map.add_file("stdin".into(), p);
|
||||||
|
let empty_span = file.span.subspan(0, 0);
|
||||||
|
let mut extender = Extender::new(empty_span);
|
||||||
Css::from_stmts(
|
Css::from_stmts(
|
||||||
Parser {
|
Parser {
|
||||||
toks: &mut Lexer::new(&file)
|
toks: &mut Lexer::new(&file)
|
||||||
@ -200,8 +202,8 @@ pub fn from_string(p: String) -> Result<String> {
|
|||||||
path: Path::new(""),
|
path: Path::new(""),
|
||||||
scopes: &mut NeverEmptyVec::new(Scope::new()),
|
scopes: &mut NeverEmptyVec::new(Scope::new()),
|
||||||
global_scope: &mut Scope::new(),
|
global_scope: &mut Scope::new(),
|
||||||
super_selectors: &mut NeverEmptyVec::new(Selector::new()),
|
super_selectors: &mut NeverEmptyVec::new(Selector::new(empty_span)),
|
||||||
span_before: file.span.subspan(0, 0),
|
span_before: empty_span,
|
||||||
content: None,
|
content: None,
|
||||||
in_mixin: false,
|
in_mixin: false,
|
||||||
in_function: false,
|
in_function: false,
|
||||||
@ -222,9 +224,10 @@ pub fn from_string(p: String) -> Result<String> {
|
|||||||
#[cfg(feature = "wasm")]
|
#[cfg(feature = "wasm")]
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
pub fn from_string(p: String) -> std::result::Result<String, JsValue> {
|
pub fn from_string(p: String) -> std::result::Result<String, JsValue> {
|
||||||
let mut extender = Extender::new();
|
|
||||||
let mut map = CodeMap::new();
|
let mut map = CodeMap::new();
|
||||||
let file = map.add_file("stdin".into(), p);
|
let file = map.add_file("stdin".into(), p);
|
||||||
|
let empty_span = file.span.subspan(0, 0);
|
||||||
|
let mut extender = Extender::new(empty_span);
|
||||||
Ok(Css::from_stmts(
|
Ok(Css::from_stmts(
|
||||||
Parser {
|
Parser {
|
||||||
toks: &mut Lexer::new(&file)
|
toks: &mut Lexer::new(&file)
|
||||||
@ -235,8 +238,8 @@ pub fn from_string(p: String) -> std::result::Result<String, JsValue> {
|
|||||||
path: Path::new(""),
|
path: Path::new(""),
|
||||||
scopes: &mut NeverEmptyVec::new(Scope::new()),
|
scopes: &mut NeverEmptyVec::new(Scope::new()),
|
||||||
global_scope: &mut Scope::new(),
|
global_scope: &mut Scope::new(),
|
||||||
super_selectors: &mut NeverEmptyVec::new(Selector::new()),
|
super_selectors: &mut NeverEmptyVec::new(Selector::new(empty_span)),
|
||||||
span_before: file.span.subspan(0, 0),
|
span_before: empty_span,
|
||||||
content: None,
|
content: None,
|
||||||
in_mixin: false,
|
in_mixin: false,
|
||||||
in_function: false,
|
in_function: false,
|
||||||
|
@ -89,10 +89,10 @@ impl Css {
|
|||||||
return Ok(Vec::new());
|
return Ok(Vec::new());
|
||||||
}
|
}
|
||||||
let selector = if extender.is_empty() {
|
let selector = if extender.is_empty() {
|
||||||
selector.resolve_parent_selectors(&super_selector, true)
|
selector.resolve_parent_selectors(&super_selector, true)?
|
||||||
} else {
|
} else {
|
||||||
Selector(extender.add_selector(
|
Selector(extender.add_selector(
|
||||||
selector.resolve_parent_selectors(&super_selector, true).0,
|
selector.resolve_parent_selectors(&super_selector, true)?.0,
|
||||||
None,
|
None,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
@ -267,7 +267,7 @@ impl<'a> Parser<'a> {
|
|||||||
self.super_selectors.push(selector.resolve_parent_selectors(
|
self.super_selectors.push(selector.resolve_parent_selectors(
|
||||||
&super_selector,
|
&super_selector,
|
||||||
!at_root || self.at_root_has_selector,
|
!at_root || self.at_root_has_selector,
|
||||||
));
|
)?);
|
||||||
let body = self.parse_stmt()?;
|
let body = self.parse_stmt()?;
|
||||||
self.scopes.pop();
|
self.scopes.pop();
|
||||||
self.super_selectors.pop();
|
self.super_selectors.pop();
|
||||||
@ -985,7 +985,7 @@ impl<'a> Parser<'a> {
|
|||||||
self.toks.next();
|
self.toks.next();
|
||||||
return Ok(Stmt::UnknownAtRule {
|
return Ok(Stmt::UnknownAtRule {
|
||||||
name,
|
name,
|
||||||
super_selector: Selector::new(),
|
super_selector: Selector::new(self.span_before),
|
||||||
params: String::new(),
|
params: String::new(),
|
||||||
body: Vec::new(),
|
body: Vec::new(),
|
||||||
});
|
});
|
||||||
@ -1029,7 +1029,7 @@ impl<'a> Parser<'a> {
|
|||||||
body = vec![Stmt::RuleSet {
|
body = vec![Stmt::RuleSet {
|
||||||
selector: self.super_selectors.last().clone(),
|
selector: self.super_selectors.last().clone(),
|
||||||
body,
|
body,
|
||||||
super_selector: Selector::new(),
|
super_selector: Selector::new(self.span_before),
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1037,7 +1037,7 @@ impl<'a> Parser<'a> {
|
|||||||
|
|
||||||
Ok(Stmt::UnknownAtRule {
|
Ok(Stmt::UnknownAtRule {
|
||||||
name,
|
name,
|
||||||
super_selector: Selector::new(),
|
super_selector: Selector::new(self.span_before),
|
||||||
params: params.trim().to_owned(),
|
params: params.trim().to_owned(),
|
||||||
body,
|
body,
|
||||||
})
|
})
|
||||||
@ -1082,14 +1082,14 @@ impl<'a> Parser<'a> {
|
|||||||
body = vec![Stmt::RuleSet {
|
body = vec![Stmt::RuleSet {
|
||||||
selector: self.super_selectors.last().clone(),
|
selector: self.super_selectors.last().clone(),
|
||||||
body,
|
body,
|
||||||
super_selector: Selector::new(),
|
super_selector: Selector::new(self.span_before),
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
body.append(&mut rules);
|
body.append(&mut rules);
|
||||||
|
|
||||||
Ok(Stmt::Media {
|
Ok(Stmt::Media {
|
||||||
super_selector: Selector::new(),
|
super_selector: Selector::new(self.span_before),
|
||||||
params: params.trim().to_owned(),
|
params: params.trim().to_owned(),
|
||||||
body,
|
body,
|
||||||
})
|
})
|
||||||
@ -1105,7 +1105,7 @@ impl<'a> Parser<'a> {
|
|||||||
at_root_has_selector = true;
|
at_root_has_selector = true;
|
||||||
self.parse_selector(true, false, String::new())?
|
self.parse_selector(true, false, String::new())?
|
||||||
}
|
}
|
||||||
.resolve_parent_selectors(self.super_selectors.last(), false);
|
.resolve_parent_selectors(self.super_selectors.last(), false)?;
|
||||||
|
|
||||||
self.whitespace();
|
self.whitespace();
|
||||||
|
|
||||||
@ -1142,18 +1142,23 @@ impl<'a> Parser<'a> {
|
|||||||
styles.push(s);
|
styles.push(s);
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
Stmt::RuleSet { selector, body, .. } if !at_root_has_selector => Some(Stmt::RuleSet {
|
Stmt::RuleSet { selector, body, .. } if !at_root_has_selector => {
|
||||||
super_selector: Selector::new(),
|
Some(Ok(Stmt::RuleSet {
|
||||||
selector: selector.resolve_parent_selectors(&at_rule_selector, false),
|
super_selector: Selector::new(self.span_before),
|
||||||
body,
|
selector: match selector.resolve_parent_selectors(&at_rule_selector, false) {
|
||||||
}),
|
Ok(v) => v,
|
||||||
_ => Some(s),
|
Err(e) => return Some(Err(e)),
|
||||||
|
},
|
||||||
|
body,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
_ => Some(Ok(s)),
|
||||||
})
|
})
|
||||||
.collect::<Vec<Stmt>>();
|
.collect::<SassResult<Vec<Stmt>>>()?;
|
||||||
let mut stmts = vec![Stmt::RuleSet {
|
let mut stmts = vec![Stmt::RuleSet {
|
||||||
selector: at_rule_selector,
|
selector: at_rule_selector,
|
||||||
body: styles,
|
body: styles,
|
||||||
super_selector: Selector::new(),
|
super_selector: Selector::new(self.span_before),
|
||||||
}];
|
}];
|
||||||
stmts.extend(raw_stmts);
|
stmts.extend(raw_stmts);
|
||||||
Ok(stmts)
|
Ok(stmts)
|
||||||
@ -1274,7 +1279,7 @@ impl<'a> Parser<'a> {
|
|||||||
body = vec![Stmt::RuleSet {
|
body = vec![Stmt::RuleSet {
|
||||||
selector: self.super_selectors.last().clone(),
|
selector: self.super_selectors.last().clone(),
|
||||||
body,
|
body,
|
||||||
super_selector: Selector::new(),
|
super_selector: Selector::new(self.span_before),
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
use std::fmt::{self, Display, Write};
|
use std::fmt::{self, Display, Write};
|
||||||
|
|
||||||
|
use crate::error::SassResult;
|
||||||
|
|
||||||
use super::{CompoundSelector, Pseudo, SelectorList, SimpleSelector, Specificity};
|
use super::{CompoundSelector, Pseudo, SelectorList, SimpleSelector, Specificity};
|
||||||
|
|
||||||
/// A complex selector.
|
/// A complex selector.
|
||||||
@ -264,7 +266,10 @@ impl ComplexSelectorComponent {
|
|||||||
matches!(self, Self::Combinator(..))
|
matches!(self, Self::Combinator(..))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn resolve_parent_selectors(self, parent: SelectorList) -> Option<Vec<ComplexSelector>> {
|
pub fn resolve_parent_selectors(
|
||||||
|
self,
|
||||||
|
parent: SelectorList,
|
||||||
|
) -> SassResult<Option<Vec<ComplexSelector>>> {
|
||||||
match self {
|
match self {
|
||||||
Self::Compound(c) => c.resolve_parent_selectors(parent),
|
Self::Compound(c) => c.resolve_parent_selectors(parent),
|
||||||
Self::Combinator(..) => todo!(),
|
Self::Combinator(..) => todo!(),
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
use std::fmt::{self, Write};
|
use std::fmt::{self, Write};
|
||||||
|
|
||||||
|
use crate::error::SassResult;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
ComplexSelector, ComplexSelectorComponent, Namespace, Pseudo, SelectorList, SimpleSelector,
|
ComplexSelector, ComplexSelectorComponent, Namespace, Pseudo, SelectorList, SimpleSelector,
|
||||||
Specificity,
|
Specificity,
|
||||||
@ -102,7 +104,10 @@ impl CompoundSelector {
|
|||||||
/// `SimpleSelector::Parent`s replaced with `parent`.
|
/// `SimpleSelector::Parent`s replaced with `parent`.
|
||||||
///
|
///
|
||||||
/// Returns `None` if `compound` doesn't contain any `SimpleSelector::Parent`s.
|
/// Returns `None` if `compound` doesn't contain any `SimpleSelector::Parent`s.
|
||||||
pub fn resolve_parent_selectors(self, parent: SelectorList) -> Option<Vec<ComplexSelector>> {
|
pub fn resolve_parent_selectors(
|
||||||
|
self,
|
||||||
|
parent: SelectorList,
|
||||||
|
) -> SassResult<Option<Vec<ComplexSelector>>> {
|
||||||
let contains_selector_pseudo = self.components.iter().any(|simple| {
|
let contains_selector_pseudo = self.components.iter().any(|simple| {
|
||||||
if let SimpleSelector::Pseudo(Pseudo {
|
if let SimpleSelector::Pseudo(Pseudo {
|
||||||
selector: Some(sel),
|
selector: Some(sel),
|
||||||
@ -116,7 +121,7 @@ impl CompoundSelector {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if !contains_selector_pseudo && !self.components[0].is_parent() {
|
if !contains_selector_pseudo && !self.components[0].is_parent() {
|
||||||
return None;
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
let resolved_members: Vec<SimpleSelector> = if contains_selector_pseudo {
|
let resolved_members: Vec<SimpleSelector> = if contains_selector_pseudo {
|
||||||
@ -127,64 +132,81 @@ impl CompoundSelector {
|
|||||||
if let SimpleSelector::Pseudo(mut pseudo) = simple {
|
if let SimpleSelector::Pseudo(mut pseudo) = simple {
|
||||||
if let Some(sel) = pseudo.selector.clone() {
|
if let Some(sel) = pseudo.selector.clone() {
|
||||||
if !sel.contains_parent_selector() {
|
if !sel.contains_parent_selector() {
|
||||||
return SimpleSelector::Pseudo(pseudo);
|
return Ok(SimpleSelector::Pseudo(pseudo));
|
||||||
}
|
}
|
||||||
pseudo.selector =
|
pseudo.selector =
|
||||||
Some(sel.resolve_parent_selectors(Some(parent.clone()), false));
|
Some(sel.resolve_parent_selectors(Some(parent.clone()), false)?);
|
||||||
SimpleSelector::Pseudo(pseudo)
|
Ok(SimpleSelector::Pseudo(pseudo))
|
||||||
} else {
|
} else {
|
||||||
SimpleSelector::Pseudo(pseudo)
|
Ok(SimpleSelector::Pseudo(pseudo))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
simple
|
Ok(simple)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect()
|
.collect::<SassResult<Vec<SimpleSelector>>>()?
|
||||||
} else {
|
} else {
|
||||||
self.components.clone()
|
self.components.clone()
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(SimpleSelector::Parent(suffix)) = self.components.first() {
|
if let Some(SimpleSelector::Parent(suffix)) = self.components.first() {
|
||||||
if self.components.len() == 1 && suffix.is_none() {
|
if self.components.len() == 1 && suffix.is_none() {
|
||||||
return Some(parent.components);
|
return Ok(Some(parent.components));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return Some(vec![ComplexSelector {
|
return Ok(Some(vec![ComplexSelector {
|
||||||
components: vec![ComplexSelectorComponent::Compound(CompoundSelector {
|
components: vec![ComplexSelectorComponent::Compound(CompoundSelector {
|
||||||
components: resolved_members,
|
components: resolved_members,
|
||||||
})],
|
})],
|
||||||
line_break: false,
|
line_break: false,
|
||||||
}]);
|
}]));
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(parent.components.into_iter().map(move |mut complex| {
|
let span = parent.span;
|
||||||
let last_component = complex.components.last();
|
|
||||||
let last = if let Some(ComplexSelectorComponent::Compound(c)) = last_component {
|
|
||||||
c.clone()
|
|
||||||
} else {
|
|
||||||
todo!("throw SassScriptException('Parent \"$complex\" is incompatible with this selector.');")
|
|
||||||
};
|
|
||||||
|
|
||||||
let last = if let Some(SimpleSelector::Parent(Some(suffix))) = self.components.first() {
|
Ok(Some(
|
||||||
let mut components = last.components;
|
parent
|
||||||
let mut end = components.pop().unwrap();
|
.components
|
||||||
end.add_suffix(suffix);
|
.into_iter()
|
||||||
components.push(end);
|
.map(move |mut complex| {
|
||||||
components.extend(resolved_members.clone().into_iter().skip(1));
|
let last_component = complex.components.last();
|
||||||
CompoundSelector { components }
|
let last = if let Some(ComplexSelectorComponent::Compound(c)) = last_component {
|
||||||
} else {
|
c.clone()
|
||||||
let mut components = last.components;
|
} else {
|
||||||
components.extend(resolved_members.clone().into_iter().skip(1));
|
return Err((
|
||||||
CompoundSelector { components }
|
format!("Parent \"{}\" is incompatible with this selector.", complex),
|
||||||
};
|
span,
|
||||||
|
)
|
||||||
|
.into());
|
||||||
|
};
|
||||||
|
|
||||||
complex.components.pop();
|
let last = if let Some(SimpleSelector::Parent(Some(suffix))) =
|
||||||
|
self.components.first()
|
||||||
|
{
|
||||||
|
let mut components = last.components;
|
||||||
|
let mut end = components.pop().unwrap();
|
||||||
|
end.add_suffix(suffix, span)?;
|
||||||
|
components.push(end);
|
||||||
|
components.extend(resolved_members.clone().into_iter().skip(1));
|
||||||
|
CompoundSelector { components }
|
||||||
|
} else {
|
||||||
|
let mut components = last.components;
|
||||||
|
components.extend(resolved_members.clone().into_iter().skip(1));
|
||||||
|
CompoundSelector { components }
|
||||||
|
};
|
||||||
|
|
||||||
let mut components = complex.components;
|
complex.components.pop();
|
||||||
components.push(ComplexSelectorComponent::Compound(last));
|
|
||||||
|
|
||||||
ComplexSelector { components, line_break: complex.line_break }
|
let mut components = complex.components;
|
||||||
}).collect())
|
components.push(ComplexSelectorComponent::Compound(last));
|
||||||
|
|
||||||
|
Ok(ComplexSelector {
|
||||||
|
components,
|
||||||
|
line_break: complex.line_break,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect::<SassResult<Vec<ComplexSelector>>>()?,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a `CompoundSelector` that matches only elements that are matched by
|
/// Returns a `CompoundSelector` that matches only elements that are matched by
|
||||||
|
@ -25,10 +25,10 @@ pub(crate) struct Extension {
|
|||||||
|
|
||||||
/// The media query context to which this extend is restricted, or `None` if
|
/// The media query context to which this extend is restricted, or `None` if
|
||||||
/// it can apply within any context.
|
/// it can apply within any context.
|
||||||
// todo: Option
|
|
||||||
pub media_context: Option<Vec<CssMediaQuery>>,
|
pub media_context: Option<Vec<CssMediaQuery>>,
|
||||||
|
|
||||||
/// The span in which `extender` was defined.
|
/// The span in which `extender` was defined.
|
||||||
|
// todo: no `Option<>`
|
||||||
pub span: Option<Span>,
|
pub span: Option<Span>,
|
||||||
|
|
||||||
pub left: Option<Box<Extension>>,
|
pub left: Option<Box<Extension>>,
|
||||||
|
@ -55,7 +55,7 @@ impl Default for ExtendMode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, Default)]
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
pub(crate) struct Extender {
|
pub(crate) struct Extender {
|
||||||
/// A map from all simple selectors in the stylesheet to the selector lists
|
/// A map from all simple selectors in the stylesheet to the selector lists
|
||||||
/// that contain them.
|
/// that contain them.
|
||||||
@ -100,6 +100,8 @@ pub(crate) struct Extender {
|
|||||||
|
|
||||||
/// The mode that controls this extender's behavior.
|
/// The mode that controls this extender's behavior.
|
||||||
mode: ExtendMode,
|
mode: ExtendMode,
|
||||||
|
|
||||||
|
span: Span,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Extender {
|
impl Extender {
|
||||||
@ -112,11 +114,12 @@ impl Extender {
|
|||||||
selector: SelectorList,
|
selector: SelectorList,
|
||||||
source: SelectorList,
|
source: SelectorList,
|
||||||
targets: SelectorList,
|
targets: SelectorList,
|
||||||
|
span: Span,
|
||||||
) -> SassResult<SelectorList> {
|
) -> SassResult<SelectorList> {
|
||||||
Self::extend_or_replace(selector, source, targets, ExtendMode::AllTargets)
|
Self::extend_or_replace(selector, source, targets, ExtendMode::AllTargets, span)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new() -> Self {
|
pub fn new(span: Span) -> Self {
|
||||||
Self {
|
Self {
|
||||||
selectors: HashMap::new(),
|
selectors: HashMap::new(),
|
||||||
extensions: HashMap::new(),
|
extensions: HashMap::new(),
|
||||||
@ -125,6 +128,7 @@ impl Extender {
|
|||||||
source_specificity: HashMap::new(),
|
source_specificity: HashMap::new(),
|
||||||
originals: HashSet::new(),
|
originals: HashSet::new(),
|
||||||
mode: ExtendMode::Normal,
|
mode: ExtendMode::Normal,
|
||||||
|
span,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -137,8 +141,9 @@ impl Extender {
|
|||||||
selector: SelectorList,
|
selector: SelectorList,
|
||||||
source: SelectorList,
|
source: SelectorList,
|
||||||
targets: SelectorList,
|
targets: SelectorList,
|
||||||
|
span: Span,
|
||||||
) -> SassResult<SelectorList> {
|
) -> SassResult<SelectorList> {
|
||||||
Self::extend_or_replace(selector, source, targets, ExtendMode::Replace)
|
Self::extend_or_replace(selector, source, targets, ExtendMode::Replace, span)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extend_or_replace(
|
fn extend_or_replace(
|
||||||
@ -146,6 +151,7 @@ impl Extender {
|
|||||||
source: SelectorList,
|
source: SelectorList,
|
||||||
targets: SelectorList,
|
targets: SelectorList,
|
||||||
mode: ExtendMode,
|
mode: ExtendMode,
|
||||||
|
span: Span,
|
||||||
) -> SassResult<SelectorList> {
|
) -> SassResult<SelectorList> {
|
||||||
let extenders: IndexMap<ComplexSelector, Extension> = source
|
let extenders: IndexMap<ComplexSelector, Extension> = source
|
||||||
.components
|
.components
|
||||||
@ -160,7 +166,7 @@ impl Extender {
|
|||||||
if complex.components.len() == 1 {
|
if complex.components.len() == 1 {
|
||||||
Ok(complex.components.first().unwrap().as_compound().clone())
|
Ok(complex.components.first().unwrap().as_compound().clone())
|
||||||
} else {
|
} else {
|
||||||
todo!("Can't extend complex selector $complex.")
|
return Err(("Can't extend complex selector $complex.", span).into());
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect::<SassResult<Vec<CompoundSelector>>>()?;
|
.collect::<SassResult<Vec<CompoundSelector>>>()?;
|
||||||
@ -176,7 +182,7 @@ impl Extender {
|
|||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let mut extender = Extender::with_mode(mode);
|
let mut extender = Extender::with_mode(mode, span);
|
||||||
|
|
||||||
if !selector.is_invisible() {
|
if !selector.is_invisible() {
|
||||||
extender
|
extender
|
||||||
@ -187,10 +193,10 @@ impl Extender {
|
|||||||
Ok(extender.extend_list(selector, &extensions, &None))
|
Ok(extender.extend_list(selector, &extensions, &None))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn with_mode(mode: ExtendMode) -> Self {
|
fn with_mode(mode: ExtendMode, span: Span) -> Self {
|
||||||
Self {
|
Self {
|
||||||
mode,
|
mode,
|
||||||
..Extender::default()
|
..Extender::new(span)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -225,6 +231,7 @@ impl Extender {
|
|||||||
|
|
||||||
SelectorList {
|
SelectorList {
|
||||||
components: self.trim(extended, |complex| self.originals.contains(complex)),
|
components: self.trim(extended, |complex| self.originals.contains(complex)),
|
||||||
|
span: self.span,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -550,7 +557,10 @@ impl Extender {
|
|||||||
media_query_context: &Option<Vec<CssMediaQuery>>,
|
media_query_context: &Option<Vec<CssMediaQuery>>,
|
||||||
) -> Option<Vec<Pseudo>> {
|
) -> Option<Vec<Pseudo>> {
|
||||||
let extended = self.extend_list(
|
let extended = self.extend_list(
|
||||||
pseudo.selector.clone().unwrap_or_else(SelectorList::new),
|
pseudo
|
||||||
|
.selector
|
||||||
|
.clone()
|
||||||
|
.unwrap_or_else(|| SelectorList::new(self.span)),
|
||||||
extensions,
|
extensions,
|
||||||
media_query_context,
|
media_query_context,
|
||||||
);
|
);
|
||||||
@ -657,6 +667,7 @@ impl Extender {
|
|||||||
.map(|complex| {
|
.map(|complex| {
|
||||||
pseudo.clone().with_selector(Some(SelectorList {
|
pseudo.clone().with_selector(Some(SelectorList {
|
||||||
components: vec![complex],
|
components: vec![complex],
|
||||||
|
span: self.span,
|
||||||
}))
|
}))
|
||||||
})
|
})
|
||||||
.collect::<Vec<Pseudo>>();
|
.collect::<Vec<Pseudo>>();
|
||||||
@ -668,6 +679,7 @@ impl Extender {
|
|||||||
} else {
|
} else {
|
||||||
Some(vec![pseudo.with_selector(Some(SelectorList {
|
Some(vec![pseudo.with_selector(Some(SelectorList {
|
||||||
components: complexes,
|
components: complexes,
|
||||||
|
span: self.span,
|
||||||
}))])
|
}))])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,10 +4,13 @@ use std::{
|
|||||||
mem,
|
mem,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use codemap::Span;
|
||||||
|
|
||||||
use super::{unify_complex, ComplexSelector, ComplexSelectorComponent};
|
use super::{unify_complex, ComplexSelector, ComplexSelectorComponent};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
common::{Brackets, ListSeparator, QuoteKind},
|
common::{Brackets, ListSeparator, QuoteKind},
|
||||||
|
error::SassResult,
|
||||||
value::Value,
|
value::Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -21,6 +24,7 @@ pub(crate) struct SelectorList {
|
|||||||
///
|
///
|
||||||
/// This is never empty.
|
/// This is never empty.
|
||||||
pub components: Vec<ComplexSelector>,
|
pub components: Vec<ComplexSelector>,
|
||||||
|
pub span: Span,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for SelectorList {
|
impl fmt::Display for SelectorList {
|
||||||
@ -58,9 +62,10 @@ impl SelectorList {
|
|||||||
.any(ComplexSelector::contains_parent_selector)
|
.any(ComplexSelector::contains_parent_selector)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const fn new() -> Self {
|
pub const fn new(span: Span) -> Self {
|
||||||
Self {
|
Self {
|
||||||
components: Vec::new(),
|
components: Vec::new(),
|
||||||
|
span,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,6 +131,7 @@ impl SelectorList {
|
|||||||
|
|
||||||
Some(Self {
|
Some(Self {
|
||||||
components: contents,
|
components: contents,
|
||||||
|
span: self.span.merge(other.span),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -137,28 +143,35 @@ impl SelectorList {
|
|||||||
/// The given `parent` may be `None`, indicating that this has no parents. If
|
/// The given `parent` may be `None`, indicating that this has no parents. If
|
||||||
/// so, this list is returned as-is if it doesn't contain any explicit
|
/// so, this list is returned as-is if it doesn't contain any explicit
|
||||||
/// `SimpleSelector::Parent`s. If it does, this returns a `SassError`.
|
/// `SimpleSelector::Parent`s. If it does, this returns a `SassError`.
|
||||||
// todo: return SassResult<Self> (the issue is figuring out the span)
|
pub fn resolve_parent_selectors(
|
||||||
pub fn resolve_parent_selectors(self, parent: Option<Self>, implicit_parent: bool) -> Self {
|
self,
|
||||||
|
parent: Option<Self>,
|
||||||
|
implicit_parent: bool,
|
||||||
|
) -> SassResult<Self> {
|
||||||
let parent = match parent {
|
let parent = match parent {
|
||||||
Some(p) => p,
|
Some(p) => p,
|
||||||
None => {
|
None => {
|
||||||
if !self.contains_parent_selector() {
|
if !self.contains_parent_selector() {
|
||||||
return self;
|
return Ok(self);
|
||||||
}
|
}
|
||||||
todo!("Top-level selectors may not contain the parent selector \"&\".")
|
return Err((
|
||||||
|
"Top-level selectors may not contain the parent selector \"&\".",
|
||||||
|
self.span,
|
||||||
|
)
|
||||||
|
.into());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Self {
|
Ok(Self {
|
||||||
components: flatten_vertically(
|
components: flatten_vertically(
|
||||||
self.components
|
self.components
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|complex| {
|
.map(|complex| {
|
||||||
if !complex.contains_parent_selector() {
|
if !complex.contains_parent_selector() {
|
||||||
if !implicit_parent {
|
if !implicit_parent {
|
||||||
return vec![complex];
|
return Ok(vec![complex]);
|
||||||
}
|
}
|
||||||
return parent
|
return Ok(parent
|
||||||
.clone()
|
.clone()
|
||||||
.components
|
.components
|
||||||
.into_iter()
|
.into_iter()
|
||||||
@ -170,7 +183,7 @@ impl SelectorList {
|
|||||||
line_break: complex.line_break || parent_complex.line_break,
|
line_break: complex.line_break || parent_complex.line_break,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect();
|
.collect());
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut new_complexes: Vec<Vec<ComplexSelectorComponent>> =
|
let mut new_complexes: Vec<Vec<ComplexSelectorComponent>> =
|
||||||
@ -181,7 +194,7 @@ impl SelectorList {
|
|||||||
if component.is_compound() {
|
if component.is_compound() {
|
||||||
let resolved = match component
|
let resolved = match component
|
||||||
.clone()
|
.clone()
|
||||||
.resolve_parent_selectors(parent.clone())
|
.resolve_parent_selectors(parent.clone())?
|
||||||
{
|
{
|
||||||
Some(r) => r,
|
Some(r) => r,
|
||||||
None => {
|
None => {
|
||||||
@ -213,7 +226,7 @@ impl SelectorList {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mut i = 0;
|
let mut i = 0;
|
||||||
new_complexes
|
Ok(new_complexes
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|new_complex| {
|
.map(|new_complex| {
|
||||||
i += 1;
|
i += 1;
|
||||||
@ -222,11 +235,12 @@ impl SelectorList {
|
|||||||
line_break: line_breaks[i - 1],
|
line_break: line_breaks[i - 1],
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect()
|
.collect())
|
||||||
})
|
})
|
||||||
.collect(),
|
.collect::<SassResult<Vec<Vec<ComplexSelector>>>>()?,
|
||||||
),
|
),
|
||||||
}
|
span: self.span,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_superselector(&self, other: &Self) -> bool {
|
pub fn is_superselector(&self, other: &Self) -> bool {
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
use crate::value::Value;
|
use codemap::Span;
|
||||||
|
|
||||||
|
use crate::{error::SassResult, value::Value};
|
||||||
|
|
||||||
pub(crate) use attribute::Attribute;
|
pub(crate) use attribute::Attribute;
|
||||||
pub(crate) use common::*;
|
pub(crate) use common::*;
|
||||||
@ -33,15 +35,19 @@ impl Selector {
|
|||||||
/// Small wrapper around `SelectorList`'s method that turns an empty parent selector
|
/// Small wrapper around `SelectorList`'s method that turns an empty parent selector
|
||||||
/// into `None`. This is a hack and in the future should be replaced.
|
/// into `None`. This is a hack and in the future should be replaced.
|
||||||
// todo: take Option<Self> for parent
|
// todo: take Option<Self> for parent
|
||||||
pub fn resolve_parent_selectors(&self, parent: &Self, implicit_parent: bool) -> Self {
|
pub fn resolve_parent_selectors(
|
||||||
Self(self.0.clone().resolve_parent_selectors(
|
&self,
|
||||||
|
parent: &Self,
|
||||||
|
implicit_parent: bool,
|
||||||
|
) -> SassResult<Self> {
|
||||||
|
Ok(Self(self.0.clone().resolve_parent_selectors(
|
||||||
if parent.is_empty() {
|
if parent.is_empty() {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
Some(parent.0.clone())
|
Some(parent.0.clone())
|
||||||
},
|
},
|
||||||
implicit_parent,
|
implicit_parent,
|
||||||
))
|
)?))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_super_selector(&self, other: &Self) -> bool {
|
pub fn is_super_selector(&self, other: &Self) -> bool {
|
||||||
@ -54,6 +60,7 @@ impl Selector {
|
|||||||
|
|
||||||
pub fn remove_placeholders(self) -> Selector {
|
pub fn remove_placeholders(self) -> Selector {
|
||||||
Self(SelectorList {
|
Self(SelectorList {
|
||||||
|
span: self.0.span,
|
||||||
components: self
|
components: self
|
||||||
.0
|
.0
|
||||||
.components
|
.components
|
||||||
@ -67,8 +74,8 @@ impl Selector {
|
|||||||
self.0.is_empty()
|
self.0.is_empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const fn new() -> Selector {
|
pub const fn new(span: Span) -> Selector {
|
||||||
Selector(SelectorList::new())
|
Selector(SelectorList::new(span))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn into_value(self) -> Value {
|
pub fn into_value(self) -> Value {
|
||||||
|
@ -103,7 +103,10 @@ impl<'a, 'b> SelectorParser<'a, 'b> {
|
|||||||
line_break = false;
|
line_break = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(SelectorList { components })
|
Ok(SelectorList {
|
||||||
|
components,
|
||||||
|
span: self.span,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eat_whitespace(&mut self) -> DevouredWhitespace {
|
fn eat_whitespace(&mut self) -> DevouredWhitespace {
|
||||||
@ -299,6 +302,7 @@ impl<'a, 'b> SelectorParser<'a, 'b> {
|
|||||||
selector: None,
|
selector: None,
|
||||||
is_syntactic_class: !element,
|
is_syntactic_class: !element,
|
||||||
argument: None,
|
argument: None,
|
||||||
|
span: self.span,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -351,6 +355,7 @@ impl<'a, 'b> SelectorParser<'a, 'b> {
|
|||||||
// todo: we can store the reference to this
|
// todo: we can store the reference to this
|
||||||
is_syntactic_class: !element,
|
is_syntactic_class: !element,
|
||||||
argument,
|
argument,
|
||||||
|
span: self.span,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,9 @@
|
|||||||
use std::fmt::{self, Write};
|
use std::fmt::{self, Write};
|
||||||
|
|
||||||
|
use codemap::Span;
|
||||||
|
|
||||||
|
use crate::error::SassResult;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
Attribute, ComplexSelector, ComplexSelectorComponent, CompoundSelector, Namespace,
|
Attribute, ComplexSelector, ComplexSelectorComponent, CompoundSelector, Namespace,
|
||||||
QualifiedName, SelectorList, Specificity,
|
QualifiedName, SelectorList, Specificity,
|
||||||
@ -119,8 +123,8 @@ impl SimpleSelector {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_suffix(&mut self, suffix: &str) {
|
pub fn add_suffix(&mut self, suffix: &str, span: Span) -> SassResult<()> {
|
||||||
match self {
|
Ok(match self {
|
||||||
Self::Type(name) => name.ident.push_str(suffix),
|
Self::Type(name) => name.ident.push_str(suffix),
|
||||||
Self::Placeholder(name)
|
Self::Placeholder(name)
|
||||||
| Self::Id(name)
|
| Self::Id(name)
|
||||||
@ -131,8 +135,9 @@ impl SimpleSelector {
|
|||||||
selector: None,
|
selector: None,
|
||||||
..
|
..
|
||||||
}) => name.push_str(suffix),
|
}) => name.push_str(suffix),
|
||||||
_ => todo!("Invalid parent selector"), //return Err((format!("Invalid parent selector \"{}\"", self), SPAN)),
|
// todo: add test for this?
|
||||||
}
|
_ => return Err((format!("Invalid parent selector \"{}\"", self), span).into()),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_universal(&self) -> bool {
|
pub fn is_universal(&self) -> bool {
|
||||||
@ -403,6 +408,8 @@ pub(crate) struct Pseudo {
|
|||||||
/// This is `None` if there's no selector. If `argument` and `selector` are
|
/// This is `None` if there's no selector. If `argument` and `selector` are
|
||||||
/// both non-`None`, the selector follows the argument.
|
/// both non-`None`, the selector follows the argument.
|
||||||
pub selector: Option<SelectorList>,
|
pub selector: Option<SelectorList>,
|
||||||
|
|
||||||
|
pub span: Span,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Pseudo {
|
impl fmt::Display for Pseudo {
|
||||||
@ -535,6 +542,7 @@ impl Pseudo {
|
|||||||
}
|
}
|
||||||
sel.is_superselector(&SelectorList {
|
sel.is_superselector(&SelectorList {
|
||||||
components: vec![complex.clone()],
|
components: vec![complex.clone()],
|
||||||
|
span: self.span,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
|
@ -153,3 +153,7 @@ error!(
|
|||||||
nothing_after_i_after_else,
|
nothing_after_i_after_else,
|
||||||
"@if true {} @else i", "Error: expected \"{\"."
|
"@if true {} @else i", "Error: expected \"{\"."
|
||||||
);
|
);
|
||||||
|
error!(
|
||||||
|
invalid_toplevel_selector,
|
||||||
|
"@if true { & { } }", "Error: Top-level selectors may not contain the parent selector \"&\"."
|
||||||
|
);
|
||||||
|
@ -685,3 +685,11 @@ error!(
|
|||||||
":#ab {}", "Error: Expected identifier."
|
":#ab {}", "Error: Expected identifier."
|
||||||
);
|
);
|
||||||
error!(nothing_after_colon, "a:{}", "Error: Expected identifier.");
|
error!(nothing_after_colon, "a:{}", "Error: Expected identifier.");
|
||||||
|
error!(
|
||||||
|
toplevel_parent_selector_after_combinator,
|
||||||
|
"~&{}", "Error: Top-level selectors may not contain the parent selector \"&\"."
|
||||||
|
);
|
||||||
|
error!(
|
||||||
|
toplevel_parent_selector_after_element,
|
||||||
|
"a&{}", "Error: \"&\" may only used at the beginning of a compound selector."
|
||||||
|
);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user