From 52ecd1e2d06673d6e06a5f2e2bd8a22425da6283 Mon Sep 17 00:00:00 2001 From: Connor Skees Date: Sat, 4 Jul 2020 12:38:09 -0400 Subject: [PATCH] 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. --- src/lib.rs | 164 +++++++++++------------ src/output.rs | 41 ++---- src/selector/extend/extended_selector.rs | 5 +- 3 files changed, 96 insertions(+), 114 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 26ad0eb..e7f3eb9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -136,34 +136,33 @@ pub fn from_path(p: &str) -> Result { 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::>() - .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::>() + .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 { 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::>() - .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::>() + .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 { 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::>() - .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::>() + .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())?) } diff --git a/src/output.rs b/src/output.rs index 5d5778e..102de14 100644 --- a/src/output.rs +++ b/src/output.rs @@ -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, extender: &mut Extender) -> SassResult { - Css::new().parse_stylesheet(s, extender) + pub(crate) fn from_stmts(s: Vec) -> SassResult { + Css::new().parse_stylesheet(s) } - fn parse_stmt(&mut self, stmt: Stmt, extender: &mut Extender) -> SassResult> { + fn parse_stmt(&mut self, stmt: Stmt) -> SassResult> { 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, extender: &mut Extender) -> SassResult { + fn parse_stylesheet(mut self, stmts: Vec) -> SassResult { 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 { + pub fn pretty_print(self, map: &CodeMap) -> SassResult { 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, 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) => { diff --git a/src/selector/extend/extended_selector.rs b/src/selector/extend/extended_selector.rs index a52f6e8..5e0ab22 100644 --- a/src/selector/extend/extended_selector.rs +++ b/src/selector/extend/extended_selector.rs @@ -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) {