optimize ExtendedSelector::into_selector
when there is only one reference to an `ExtendedSelector`, the selector will no longer do unnecessary cloning. this is a significant improvement as previously we were cloning *every* selector multiple times. note that this is optimization only occurs when the selector is being emitted.
This commit is contained in:
parent
ca861d488b
commit
52ecd1e2d0
164
src/lib.rs
164
src/lib.rs
@ -136,34 +136,33 @@ pub fn from_path(p: &str) -> Result<String> {
|
||||
let mut map = CodeMap::new();
|
||||
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(
|
||||
Parser {
|
||||
toks: &mut Lexer::new(&file)
|
||||
.collect::<Vec<Token>>()
|
||||
.into_iter()
|
||||
.peekmore(),
|
||||
map: &mut map,
|
||||
path: p.as_ref(),
|
||||
scopes: &mut NeverEmptyVec::new(Scope::new()),
|
||||
global_scope: &mut Scope::new(),
|
||||
super_selectors: &mut NeverEmptyVec::new(Selector::new(empty_span)),
|
||||
span_before: empty_span,
|
||||
content: &mut Vec::new(),
|
||||
in_mixin: false,
|
||||
in_function: false,
|
||||
in_control_flow: false,
|
||||
at_root: true,
|
||||
at_root_has_selector: false,
|
||||
extender: &mut extender,
|
||||
}
|
||||
.parse()
|
||||
.map_err(|e| raw_to_parse_error(&map, *e))?,
|
||||
&mut extender,
|
||||
)
|
||||
.map_err(|e| raw_to_parse_error(&map, *e))?
|
||||
.pretty_print(&map, &mut extender)
|
||||
.map_err(|e| raw_to_parse_error(&map, *e))
|
||||
|
||||
let stmts = Parser {
|
||||
toks: &mut Lexer::new(&file)
|
||||
.collect::<Vec<Token>>()
|
||||
.into_iter()
|
||||
.peekmore(),
|
||||
map: &mut map,
|
||||
path: p.as_ref(),
|
||||
scopes: &mut NeverEmptyVec::new(Scope::new()),
|
||||
global_scope: &mut Scope::new(),
|
||||
super_selectors: &mut NeverEmptyVec::new(Selector::new(empty_span)),
|
||||
span_before: empty_span,
|
||||
content: &mut Vec::new(),
|
||||
in_mixin: false,
|
||||
in_function: false,
|
||||
in_control_flow: false,
|
||||
at_root: true,
|
||||
at_root_has_selector: false,
|
||||
extender: &mut Extender::new(empty_span),
|
||||
}
|
||||
.parse()
|
||||
.map_err(|e| raw_to_parse_error(&map, *e))?;
|
||||
|
||||
Css::from_stmts(stmts)
|
||||
.map_err(|e| raw_to_parse_error(&map, *e))?
|
||||
.pretty_print(&map)
|
||||
.map_err(|e| raw_to_parse_error(&map, *e))
|
||||
}
|
||||
|
||||
/// Write CSS to `buf`, constructed from a string
|
||||
@ -182,34 +181,32 @@ pub fn from_string(p: String) -> Result<String> {
|
||||
let mut map = CodeMap::new();
|
||||
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(
|
||||
Parser {
|
||||
toks: &mut Lexer::new(&file)
|
||||
.collect::<Vec<Token>>()
|
||||
.into_iter()
|
||||
.peekmore(),
|
||||
map: &mut map,
|
||||
path: Path::new(""),
|
||||
scopes: &mut NeverEmptyVec::new(Scope::new()),
|
||||
global_scope: &mut Scope::new(),
|
||||
super_selectors: &mut NeverEmptyVec::new(Selector::new(empty_span)),
|
||||
span_before: empty_span,
|
||||
content: &mut Vec::new(),
|
||||
in_mixin: false,
|
||||
in_function: false,
|
||||
in_control_flow: false,
|
||||
at_root: true,
|
||||
at_root_has_selector: false,
|
||||
extender: &mut extender,
|
||||
}
|
||||
.parse()
|
||||
.map_err(|e| raw_to_parse_error(&map, *e))?,
|
||||
&mut extender,
|
||||
)
|
||||
.map_err(|e| raw_to_parse_error(&map, *e))?
|
||||
.pretty_print(&map, &mut extender)
|
||||
.map_err(|e| raw_to_parse_error(&map, *e))
|
||||
let stmts = Parser {
|
||||
toks: &mut Lexer::new(&file)
|
||||
.collect::<Vec<Token>>()
|
||||
.into_iter()
|
||||
.peekmore(),
|
||||
map: &mut map,
|
||||
path: Path::new(""),
|
||||
scopes: &mut NeverEmptyVec::new(Scope::new()),
|
||||
global_scope: &mut Scope::new(),
|
||||
super_selectors: &mut NeverEmptyVec::new(Selector::new(empty_span)),
|
||||
span_before: empty_span,
|
||||
content: &mut Vec::new(),
|
||||
in_mixin: false,
|
||||
in_function: false,
|
||||
in_control_flow: false,
|
||||
at_root: true,
|
||||
at_root_has_selector: false,
|
||||
extender: &mut Extender::new(empty_span),
|
||||
}
|
||||
.parse()
|
||||
.map_err(|e| raw_to_parse_error(&map, *e))?;
|
||||
|
||||
Css::from_stmts(stmts)
|
||||
.map_err(|e| raw_to_parse_error(&map, *e))?
|
||||
.pretty_print(&map)
|
||||
.map_err(|e| raw_to_parse_error(&map, *e))
|
||||
}
|
||||
|
||||
#[cfg(feature = "wasm")]
|
||||
@ -218,32 +215,31 @@ pub fn from_string(p: String) -> std::result::Result<String, JsValue> {
|
||||
let mut map = CodeMap::new();
|
||||
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(
|
||||
Parser {
|
||||
toks: &mut Lexer::new(&file)
|
||||
.collect::<Vec<Token>>()
|
||||
.into_iter()
|
||||
.peekmore(),
|
||||
map: &mut map,
|
||||
path: Path::new(""),
|
||||
scopes: &mut NeverEmptyVec::new(Scope::new()),
|
||||
global_scope: &mut Scope::new(),
|
||||
super_selectors: &mut NeverEmptyVec::new(Selector::new(empty_span)),
|
||||
span_before: empty_span,
|
||||
content: &mut Vec::new(),
|
||||
in_mixin: false,
|
||||
in_function: false,
|
||||
in_control_flow: false,
|
||||
at_root: true,
|
||||
at_root_has_selector: false,
|
||||
extender: &mut extender,
|
||||
}
|
||||
.parse()
|
||||
.map_err(|e| raw_to_parse_error(&map, *e).to_string())?,
|
||||
&mut extender,
|
||||
)
|
||||
.map_err(|e| raw_to_parse_error(&map, *e).to_string())?
|
||||
.pretty_print(&map, &mut extender)
|
||||
.map_err(|e| raw_to_parse_error(&map, *e).to_string())?)
|
||||
|
||||
let stmts = Parser {
|
||||
toks: &mut Lexer::new(&file)
|
||||
.collect::<Vec<Token>>()
|
||||
.into_iter()
|
||||
.peekmore(),
|
||||
map: &mut map,
|
||||
path: Path::new(""),
|
||||
scopes: &mut NeverEmptyVec::new(Scope::new()),
|
||||
global_scope: &mut Scope::new(),
|
||||
super_selectors: &mut NeverEmptyVec::new(Selector::new(empty_span)),
|
||||
span_before: empty_span,
|
||||
content: &mut Vec::new(),
|
||||
in_mixin: false,
|
||||
in_function: false,
|
||||
in_control_flow: false,
|
||||
at_root: true,
|
||||
at_root_has_selector: false,
|
||||
extender: &mut Extender::new(empty_span),
|
||||
}
|
||||
.parse()
|
||||
.map_err(|e| raw_to_parse_error(&map, *e).to_string())?;
|
||||
|
||||
Ok(Css::from_stmts(stmts)
|
||||
.map_err(|e| raw_to_parse_error(&map, *e).to_string())?
|
||||
.pretty_print(&map)
|
||||
.map_err(|e| raw_to_parse_error(&map, *e).to_string())?)
|
||||
}
|
||||
|
@ -7,7 +7,6 @@ use crate::{
|
||||
atrule::{media::MediaRule, SupportsRule, UnknownAtRule},
|
||||
error::SassResult,
|
||||
parse::Stmt,
|
||||
selector::Extender,
|
||||
selector::Selector,
|
||||
style::Style,
|
||||
};
|
||||
@ -76,11 +75,11 @@ impl Css {
|
||||
Css { blocks: Vec::new() }
|
||||
}
|
||||
|
||||
pub(crate) fn from_stmts(s: Vec<Stmt>, extender: &mut Extender) -> SassResult<Self> {
|
||||
Css::new().parse_stylesheet(s, extender)
|
||||
pub(crate) fn from_stmts(s: Vec<Stmt>) -> SassResult<Self> {
|
||||
Css::new().parse_stylesheet(s)
|
||||
}
|
||||
|
||||
fn parse_stmt(&mut self, stmt: Stmt, extender: &mut Extender) -> SassResult<Vec<Toplevel>> {
|
||||
fn parse_stmt(&mut self, stmt: Stmt) -> SassResult<Vec<Toplevel>> {
|
||||
Ok(match stmt {
|
||||
Stmt::RuleSet { selector, body } => {
|
||||
if body.is_empty() {
|
||||
@ -93,7 +92,7 @@ impl Css {
|
||||
let mut vals = vec![Toplevel::new_rule(selector)];
|
||||
for rule in body {
|
||||
match rule {
|
||||
Stmt::RuleSet { .. } => vals.extend(self.parse_stmt(rule, extender)?),
|
||||
Stmt::RuleSet { .. } => vals.extend(self.parse_stmt(rule)?),
|
||||
Stmt::Style(s) => vals.get_mut(0).unwrap().push_style(s),
|
||||
Stmt::Comment(s) => vals.get_mut(0).unwrap().push_comment(s),
|
||||
Stmt::Media(m) => {
|
||||
@ -117,7 +116,7 @@ impl Css {
|
||||
Stmt::Return(..) => unreachable!(),
|
||||
Stmt::AtRoot { body } => {
|
||||
body.into_iter().try_for_each(|r| -> SassResult<()> {
|
||||
vals.append(&mut self.parse_stmt(r, extender)?);
|
||||
vals.append(&mut self.parse_stmt(r)?);
|
||||
Ok(())
|
||||
})?
|
||||
}
|
||||
@ -150,10 +149,10 @@ impl Css {
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_stylesheet(mut self, stmts: Vec<Stmt>, extender: &mut Extender) -> SassResult<Css> {
|
||||
fn parse_stylesheet(mut self, stmts: Vec<Stmt>) -> SassResult<Css> {
|
||||
let mut is_first = true;
|
||||
for stmt in stmts {
|
||||
let v = self.parse_stmt(stmt, extender)?;
|
||||
let v = self.parse_stmt(stmt)?;
|
||||
// this is how we print newlines between unrelated styles
|
||||
// it could probably be refactored
|
||||
if !v.is_empty() {
|
||||
@ -169,9 +168,9 @@ impl Css {
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
pub fn pretty_print(self, map: &CodeMap, extender: &mut Extender) -> SassResult<String> {
|
||||
pub fn pretty_print(self, map: &CodeMap) -> SassResult<String> {
|
||||
let mut string = Vec::new();
|
||||
self._inner_pretty_print(&mut string, map, extender, 0)?;
|
||||
self._inner_pretty_print(&mut string, map, 0)?;
|
||||
if string.iter().any(|s| !s.is_ascii()) {
|
||||
return Ok(format!("@charset \"UTF-8\";\n{}", unsafe {
|
||||
String::from_utf8_unchecked(string)
|
||||
@ -184,7 +183,6 @@ impl Css {
|
||||
self,
|
||||
buf: &mut Vec<u8>,
|
||||
map: &CodeMap,
|
||||
extender: &mut Extender,
|
||||
nesting: usize,
|
||||
) -> SassResult<()> {
|
||||
let mut has_written = false;
|
||||
@ -231,12 +229,7 @@ impl Css {
|
||||
writeln!(buf, " {{")?;
|
||||
}
|
||||
|
||||
Css::from_stmts(body, extender)?._inner_pretty_print(
|
||||
buf,
|
||||
map,
|
||||
extender,
|
||||
nesting + 1,
|
||||
)?;
|
||||
Css::from_stmts(body)?._inner_pretty_print(buf, map, nesting + 1)?;
|
||||
writeln!(buf, "{}}}", padding)?;
|
||||
}
|
||||
Toplevel::Supports { params, body } => {
|
||||
@ -258,12 +251,7 @@ impl Css {
|
||||
writeln!(buf, " {{")?;
|
||||
}
|
||||
|
||||
Css::from_stmts(body, extender)?._inner_pretty_print(
|
||||
buf,
|
||||
map,
|
||||
extender,
|
||||
nesting + 1,
|
||||
)?;
|
||||
Css::from_stmts(body)?._inner_pretty_print(buf, map, nesting + 1)?;
|
||||
writeln!(buf, "{}}}", padding)?;
|
||||
}
|
||||
Toplevel::Media { query, body } => {
|
||||
@ -275,12 +263,7 @@ impl Css {
|
||||
writeln!(buf)?;
|
||||
}
|
||||
writeln!(buf, "{}@media {} {{", padding, query)?;
|
||||
Css::from_stmts(body, extender)?._inner_pretty_print(
|
||||
buf,
|
||||
map,
|
||||
extender,
|
||||
nesting + 1,
|
||||
)?;
|
||||
Css::from_stmts(body)?._inner_pretty_print(buf, map, nesting + 1)?;
|
||||
writeln!(buf, "{}}}", padding)?;
|
||||
}
|
||||
Toplevel::Style(s) => {
|
||||
|
@ -37,7 +37,10 @@ impl ExtendedSelector {
|
||||
}
|
||||
|
||||
pub fn into_selector(self) -> Selector {
|
||||
Selector(self.0.borrow().clone())
|
||||
Selector(match Rc::try_unwrap(self.0) {
|
||||
Ok(v) => v.into_inner(),
|
||||
Err(v) => v.borrow().clone(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn set_inner(&mut self, selector: SelectorList) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user