emit charset only when output contains utf-8

This commit is contained in:
ConnorSkees 2020-04-05 23:20:47 -04:00
parent 74dab6872f
commit 13a96273e4
9 changed files with 37 additions and 38 deletions

View File

@ -1,11 +1,9 @@
use std::iter::Peekable; use std::iter::Peekable;
use std::str::Chars; use std::str::Chars;
use std::sync::atomic::{AtomicBool, Ordering};
use crate::common::Pos; use crate::common::Pos;
use crate::Token; use crate::Token;
pub static IS_UTF8: AtomicBool = AtomicBool::new(false);
pub const FORM_FEED: char = '\x0C'; pub const FORM_FEED: char = '\x0C';
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -31,10 +29,6 @@ impl<'a> Iterator for Lexer<'a> {
} }
} }
'\0' => return None, '\0' => return None,
c if !c.is_ascii() => {
IS_UTF8.store(true, Ordering::Relaxed);
c
}
c => c, c => c,
}; };
self.pos.next_char(); self.pos.next_char();

View File

@ -86,11 +86,11 @@ use std::path::Path;
use crate::atrule::{eat_include, AtRule, AtRuleKind, Function, Mixin}; use crate::atrule::{eat_include, AtRule, AtRuleKind, Function, Mixin};
use crate::common::Pos; use crate::common::Pos;
use crate::output::Css;
pub use crate::error::{SassError, SassResult}; pub use crate::error::{SassError, SassResult};
use crate::format::PrettyPrinter; use crate::format::PrettyPrinter;
use crate::imports::import; use crate::imports::import;
use crate::lexer::Lexer; use crate::lexer::Lexer;
use crate::output::Css;
use crate::scope::{insert_global_fn, insert_global_mixin, insert_global_var, Scope, GLOBAL_SCOPE}; use crate::scope::{insert_global_fn, insert_global_mixin, insert_global_var, Scope, GLOBAL_SCOPE};
use crate::selector::Selector; use crate::selector::Selector;
use crate::style::Style; use crate::style::Style;
@ -106,11 +106,11 @@ mod atrule;
mod builtin; mod builtin;
mod color; mod color;
mod common; mod common;
mod output;
mod error; mod error;
mod format; mod format;
mod imports; mod imports;
mod lexer; mod lexer;
mod output;
mod scope; mod scope;
mod selector; mod selector;
mod style; mod style;
@ -264,7 +264,7 @@ impl StyleSheet {
/// ``` /// ```
#[inline] #[inline]
pub fn print_as_css<W: Write>(self, buf: &mut W) -> SassResult<()> { pub fn print_as_css<W: Write>(self, buf: &mut W) -> SassResult<()> {
Css::from_stylesheet(self)?.pretty_print(buf, 0) Css::from_stylesheet(self)?.pretty_print(buf)
} }
} }

View File

@ -1,11 +1,9 @@
//! # Convert from SCSS AST to CSS //! # Convert from SCSS AST to CSS
use std::fmt; use std::fmt;
use std::io::Write; use std::io::Write;
use std::sync::atomic::Ordering;
use crate::atrule::AtRule; use crate::atrule::AtRule;
use crate::error::SassResult; use crate::error::SassResult;
use crate::lexer::IS_UTF8;
use crate::{RuleSet, Selector, Stmt, Style, StyleSheet}; use crate::{RuleSet, Selector, Stmt, Style, StyleSheet};
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -121,12 +119,19 @@ impl Css {
Ok(self) Ok(self)
} }
pub fn pretty_print<W: Write>(self, buf: &mut W, nesting: usize) -> SassResult<()> { pub fn pretty_print<W: Write>(self, buf: &mut W) -> SassResult<()> {
let mut has_written = false; let mut string = Vec::new();
let padding = vec![' '; nesting * 2].iter().collect::<String>(); self._inner_pretty_print(&mut string, 0)?;
if IS_UTF8.swap(false, Ordering::Relaxed) { if string.iter().any(|s| !s.is_ascii()) {
writeln!(buf, "@charset \"UTF-8\";")?; writeln!(buf, "@charset \"UTF-8\";")?;
} }
write!(buf, "{}", String::from_utf8(string).unwrap())?;
Ok(())
}
fn _inner_pretty_print(self, buf: &mut Vec<u8>, nesting: usize) -> SassResult<()> {
let mut has_written = false;
let padding = vec![' '; nesting * 2].iter().collect::<String>();
for block in self.blocks { for block in self.blocks {
match block { match block {
Toplevel::RuleSet(selector, styles) => { Toplevel::RuleSet(selector, styles) => {
@ -152,7 +157,7 @@ impl Css {
writeln!(buf, "{}@{} {} {{", padding, u.name, u.params)?; writeln!(buf, "{}@{} {} {{", padding, u.name, u.params)?;
} }
Css::from_stylesheet(StyleSheet::from_stmts(u.body))? Css::from_stylesheet(StyleSheet::from_stmts(u.body))?
.pretty_print(buf, nesting + 1)?; ._inner_pretty_print(buf, nesting + 1)?;
writeln!(buf, "{}}}", padding)?; writeln!(buf, "{}}}", padding)?;
} }
_ => todo!("at-rule other than unknown at toplevel"), _ => todo!("at-rule other than unknown at toplevel"),

View File

@ -603,6 +603,8 @@ pub(crate) fn parse_quoted_string<I: Iterator<Item = Token>>(
let c = std::char::from_u32(u32::from_str_radix(&n, 16).unwrap()).unwrap(); let c = std::char::from_u32(u32::from_str_radix(&n, 16).unwrap()).unwrap();
if c.is_control() && c != '\t' && c != '\0' { if c.is_control() && c != '\t' && c != '\0' {
s.push_str(&format!("\\{}", n.to_ascii_lowercase())); s.push_str(&format!("\\{}", n.to_ascii_lowercase()));
} else if c == '\0' {
s.push('\u{FFFD}');
} else { } else {
s.push(c); s.push(c);
} }

View File

@ -4,13 +4,11 @@
mod macros; mod macros;
test!( test!(
#[ignore]
utf8_input, utf8_input,
"a {\n color: 🦆;\n}\n", "a {\n color: 🦆;\n}\n",
"@charset \"UTF-8\";\na {\n color: 🦆;\n}\n" "@charset \"UTF-8\";\na {\n color: 🦆;\n}\n"
); );
test!( test!(
#[ignore]
ascii_charset_utf8, ascii_charset_utf8,
"@charset \"UTF-8\";\na {\n color: red;\n}\n", "@charset \"UTF-8\";\na {\n color: red;\n}\n",
"a {\n color: red;\n}\n" "a {\n color: red;\n}\n"

View File

@ -47,25 +47,21 @@ test!(
"a {\n color: red;\n}\n" "a {\n color: red;\n}\n"
); );
test!( test!(
#[ignore]
utf8_ident_before_len, utf8_ident_before_len,
"a {\n color: length(😀red);\n}\n", "a {\n color: length(😀red);\n}\n",
"@charset \"UTF-8\";\na {\n color: 1;\n}\n" "a {\n color: 1;\n}\n"
); );
test!( test!(
#[ignore]
utf8_ident_before, utf8_ident_before,
"a {\n color: 😀red;\n}\n", "a {\n color: 😀red;\n}\n",
"@charset \"UTF-8\";\na {\n color: 😀red;\n}\n" "@charset \"UTF-8\";\na {\n color: 😀red;\n}\n"
); );
test!( test!(
#[ignore]
utf8_ident_after_len, utf8_ident_after_len,
"a {\n color: length(red😁)\n}\n", "a {\n color: length(red😁)\n}\n",
"@charset \"UTF-8\";\na {\n color: 1;\n}\n" "a {\n color: 1;\n}\n"
); );
test!( test!(
#[ignore]
utf8_ident_after, utf8_ident_after,
"a {\n color: red😁\n}\n", "a {\n color: red😁\n}\n",
"@charset \"UTF-8\";\na {\n color: red😁;\n}\n" "@charset \"UTF-8\";\na {\n color: red😁;\n}\n"

View File

@ -98,8 +98,8 @@ test!(
); );
test!( test!(
single_character_escape_sequence_has_space_after, single_character_escape_sequence_has_space_after,
"a {\n color: \\0;\n}\n", "a {\n color: \\a;\n}\n",
"a {\n color: \\0 ;\n}\n" "a {\n color: \\a ;\n}\n"
); );
test!( test!(
escapes_non_hex_in_string, escapes_non_hex_in_string,
@ -127,6 +127,16 @@ test!(
"a {\n color: foo == f\\6F\\6F;\n}\n", "a {\n color: foo == f\\6F\\6F;\n}\n",
"a {\n color: true;\n}\n" "a {\n color: true;\n}\n"
); );
test!(
quoted_escape_zero,
"a {\n color: \"\\0\";\n}\n",
"@charset \"UTF-8\";\na {\n color: \"<EFBFBD>\";\n}\n"
);
test!(
unquoted_escape_zero,
"a {\n color: \\0;\n}\n",
"a {\n color: \\0 ;\n}\n"
);
// test!( // test!(
// quote_escape, // quote_escape,
// "a {\n color: quote(\\b);\n}\n", // "a {\n color: quote(\\b);\n}\n",

View File

@ -119,10 +119,9 @@ test!(
"a {\n color: 7;\n}\n" "a {\n color: 7;\n}\n"
); );
test!( test!(
#[ignore]
str_len_double_wide, str_len_double_wide,
"a {\n color: str-length(\"👭\");\n}\n", "a {\n color: str-length(\"👭\");\n}\n",
"@charset \"UTF-8\";\na {\n color: 1;\n}\n" "a {\n color: 1;\n}\n"
); );
test!( test!(
str_len_combining, str_len_combining,
@ -215,7 +214,6 @@ test!(
"a {\n color: Xabcd;\n}\n" "a {\n color: Xabcd;\n}\n"
); );
test!( test!(
#[ignore]
str_insert_double_width_char, str_insert_double_width_char,
"a {\n color: str-insert(\"👭\", \"c\", 2);\n}\n", "a {\n color: str-insert(\"👭\", \"c\", 2);\n}\n",
"@charset \"UTF-8\";\na {\n color: \"👭c\";\n}\n" "@charset \"UTF-8\";\na {\n color: \"👭c\";\n}\n"

View File

@ -98,15 +98,11 @@ test!(
"a {\n $a: red\n}\n\nb {\n color: blue;\n}\n", "a {\n $a: red\n}\n\nb {\n color: blue;\n}\n",
"b {\n color: blue;\n}\n" "b {\n color: blue;\n}\n"
); );
// TODO: blocked on properly emitting @charset test!(
// right now, we emit @charset if a utf-8 character unicode_in_variables,
// is found *anywhere*, but ideally we would only emit "$vär: foo;\na {\n color: $vär;\n}\n",
// it if a utf-8 character is actually in the output "a {\n color: foo;\n}\n"
// test!( );
// unicode_in_variables,
// "$vär: foo;\na {\n color: $vär;\n}\n",
// "a {\n color: foo;\n}\n"
// );
test!( test!(
variable_does_not_include_interpolation, variable_does_not_include_interpolation,
"$input: foo;\na {\n color: $input#{\"literal\"};\n}\n", "$input: foo;\na {\n color: $input#{\"literal\"};\n}\n",