allow !optional
in @extend
This commit is contained in:
parent
5634681fa2
commit
67cbf9591a
@ -15,7 +15,10 @@ use crate::{
|
|||||||
ComplexSelectorComponent, ExtendRule, ExtendedSelector, Extender, Selector, SelectorParser,
|
ComplexSelectorComponent, ExtendRule, ExtendedSelector, Extender, Selector, SelectorParser,
|
||||||
},
|
},
|
||||||
style::Style,
|
style::Style,
|
||||||
utils::{read_until_closing_curly_brace, read_until_semicolon_or_closing_curly_brace},
|
utils::{
|
||||||
|
peek_ident_no_interpolation, read_until_closing_curly_brace,
|
||||||
|
read_until_semicolon_or_closing_curly_brace,
|
||||||
|
},
|
||||||
value::Value,
|
value::Value,
|
||||||
Options, {Cow, Token},
|
Options, {Cow, Token},
|
||||||
};
|
};
|
||||||
@ -269,6 +272,7 @@ impl<'a> Parser<'a> {
|
|||||||
self.at_root = false;
|
self.at_root = false;
|
||||||
let selector = self
|
let selector = self
|
||||||
.parse_selector(!self.super_selectors.is_empty(), false, init)?
|
.parse_selector(!self.super_selectors.is_empty(), false, init)?
|
||||||
|
.0
|
||||||
.resolve_parent_selectors(
|
.resolve_parent_selectors(
|
||||||
self.super_selectors.last(),
|
self.super_selectors.last(),
|
||||||
!at_root || self.at_root_has_selector,
|
!at_root || self.at_root_has_selector,
|
||||||
@ -299,7 +303,7 @@ impl<'a> Parser<'a> {
|
|||||||
allows_parent: bool,
|
allows_parent: bool,
|
||||||
from_fn: bool,
|
from_fn: bool,
|
||||||
mut string: String,
|
mut string: String,
|
||||||
) -> SassResult<Selector> {
|
) -> SassResult<(Selector, bool)> {
|
||||||
let mut span = if let Some(tok) = self.toks.peek() {
|
let mut span = if let Some(tok) = self.toks.peek() {
|
||||||
tok.pos()
|
tok.pos()
|
||||||
} else {
|
} else {
|
||||||
@ -310,6 +314,8 @@ impl<'a> Parser<'a> {
|
|||||||
|
|
||||||
let mut found_curly = false;
|
let mut found_curly = false;
|
||||||
|
|
||||||
|
let mut optional = false;
|
||||||
|
|
||||||
// we resolve interpolation and strip comments
|
// we resolve interpolation and strip comments
|
||||||
while let Some(tok) = self.toks.next() {
|
while let Some(tok) = self.toks.next() {
|
||||||
span = span.merge(tok.pos());
|
span = span.merge(tok.pos());
|
||||||
@ -333,6 +339,16 @@ impl<'a> Parser<'a> {
|
|||||||
found_curly = true;
|
found_curly = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
'!' => {
|
||||||
|
if peek_ident_no_interpolation(self.toks, false, self.span_before)?.node
|
||||||
|
== "optional"
|
||||||
|
{
|
||||||
|
self.toks.truncate_iterator_to_cursor();
|
||||||
|
optional = true;
|
||||||
|
} else {
|
||||||
|
string.push('!');
|
||||||
|
}
|
||||||
|
}
|
||||||
c => string.push(c),
|
c => string.push(c),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -368,7 +384,7 @@ impl<'a> Parser<'a> {
|
|||||||
)
|
)
|
||||||
.parse()?;
|
.parse()?;
|
||||||
|
|
||||||
Ok(Selector(selector))
|
Ok((Selector(selector), optional))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Eat and return the contents of a comment.
|
/// Eat and return the contents of a comment.
|
||||||
@ -637,7 +653,7 @@ impl<'a> Parser<'a> {
|
|||||||
self.super_selectors.last().clone()
|
self.super_selectors.last().clone()
|
||||||
} else {
|
} else {
|
||||||
at_root_has_selector = true;
|
at_root_has_selector = true;
|
||||||
self.parse_selector(true, false, String::new())?
|
self.parse_selector(true, false, String::new())?.0
|
||||||
}
|
}
|
||||||
.resolve_parent_selectors(self.super_selectors.last(), false)?;
|
.resolve_parent_selectors(self.super_selectors.last(), false)?;
|
||||||
|
|
||||||
@ -692,7 +708,7 @@ impl<'a> Parser<'a> {
|
|||||||
// if !self.in_style_rule && !self.in_mixin && !self.in_content_block {
|
// if !self.in_style_rule && !self.in_mixin && !self.in_content_block {
|
||||||
// return Err(("@extend may only be used within style rules.", self.span_before).into());
|
// return Err(("@extend may only be used within style rules.", self.span_before).into());
|
||||||
// }
|
// }
|
||||||
let value = Parser {
|
let (value, is_optional) = Parser {
|
||||||
toks: &mut read_until_semicolon_or_closing_curly_brace(self.toks)?
|
toks: &mut read_until_semicolon_or_closing_curly_brace(self.toks)?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.peekmore(),
|
.peekmore(),
|
||||||
@ -712,17 +728,6 @@ impl<'a> Parser<'a> {
|
|||||||
}
|
}
|
||||||
.parse_selector(false, true, String::new())?;
|
.parse_selector(false, true, String::new())?;
|
||||||
|
|
||||||
let is_optional = if let Some(Token { kind: '!', .. }) = self.toks.peek() {
|
|
||||||
self.toks.next();
|
|
||||||
assert_eq!(
|
|
||||||
self.parse_identifier_no_interpolation(false)?.node,
|
|
||||||
"optional"
|
|
||||||
);
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
};
|
|
||||||
|
|
||||||
self.whitespace();
|
self.whitespace();
|
||||||
|
|
||||||
if let Some(Token { kind: ';', .. }) = self.toks.peek() {
|
if let Some(Token { kind: ';', .. }) = self.toks.peek() {
|
||||||
|
@ -457,7 +457,7 @@ impl Value {
|
|||||||
Some(v) => v,
|
Some(v) => v,
|
||||||
None => return Err((format!("${}: {} is not a valid selector: it must be a string, a list of strings, or a list of lists of strings.", name, self.inspect(parser.span_before)?), parser.span_before).into()),
|
None => return Err((format!("${}: {} is not a valid selector: it must be a string, a list of strings, or a list of lists of strings.", name, self.inspect(parser.span_before)?), parser.span_before).into()),
|
||||||
};
|
};
|
||||||
Parser {
|
Ok(Parser {
|
||||||
toks: &mut string
|
toks: &mut string
|
||||||
.chars()
|
.chars()
|
||||||
.map(|c| Token::new(parser.span_before, c))
|
.map(|c| Token::new(parser.span_before, c))
|
||||||
@ -478,7 +478,8 @@ impl Value {
|
|||||||
content_scopes: parser.content_scopes,
|
content_scopes: parser.content_scopes,
|
||||||
options: parser.options,
|
options: parser.options,
|
||||||
}
|
}
|
||||||
.parse_selector(allows_parent, true, String::new())
|
.parse_selector(allows_parent, true, String::new())?
|
||||||
|
.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn selector_string(self, span: Span) -> SassResult<Option<String>> {
|
fn selector_string(self, span: Span) -> SassResult<Option<String>> {
|
||||||
|
@ -1428,13 +1428,11 @@ test!(
|
|||||||
"a.bar {\n a: b;\n}\n\n.bar, b.foo {\n c: d;\n}\n"
|
"a.bar {\n a: b;\n}\n\n.bar, b.foo {\n c: d;\n}\n"
|
||||||
);
|
);
|
||||||
test!(
|
test!(
|
||||||
#[ignore = "!optional extend is not yet implemented"]
|
|
||||||
optional_extend_succeeds_when_extendee_doesnt_exist,
|
optional_extend_succeeds_when_extendee_doesnt_exist,
|
||||||
".foo {@extend .bar !optional}",
|
".foo {@extend .bar !optional}",
|
||||||
""
|
""
|
||||||
);
|
);
|
||||||
test!(
|
test!(
|
||||||
#[ignore = "!optional extend is not yet implemented"]
|
|
||||||
optional_extend_succeeds_when_extension_fails,
|
optional_extend_succeeds_when_extension_fails,
|
||||||
"a.bar {a: b}
|
"a.bar {a: b}
|
||||||
b.foo {@extend .bar !optional}
|
b.foo {@extend .bar !optional}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user