fix(css): Fixup (#2138)

This commit is contained in:
강동윤 2021-08-23 23:03:08 +09:00 committed by GitHub
parent c05a724d84
commit 81061a91bb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 282 additions and 399 deletions

View File

@ -115,6 +115,8 @@ jobs:
- swc_css_ast
- swc_css_codegen
- swc_css_parser
- swc_css_utils
- swc_css_visit
- swc_ecma_ast
- swc_ecma_codegen
- swc_ecma_codegen_macros

2
Cargo.lock generated
View File

@ -2337,7 +2337,7 @@ dependencies = [
[[package]]
name = "swc_css_codegen_macros"
version = "0.1.0"
version = "0.2.0"
dependencies = [
"pmutil",
"proc-macro2",

View File

@ -14,7 +14,7 @@ bitflags = "1.3.2"
swc_atoms = {version = "0.2.7", path = "../../atoms"}
swc_common = {version = "0.11.6", path = "../../common"}
swc_css_ast = {version = "0.4.0", path = "../ast/"}
swc_css_codegen_macros = {version = "0.1.0", path = "macros/"}
swc_css_codegen_macros = {version = "0.2.0", path = "macros/"}
[dev-dependencies]
swc_css_parser = {version = "0.4.0", path = "../parser"}

View File

@ -6,7 +6,7 @@ edition = "2018"
license = "Apache-2.0/MIT"
name = "swc_css_codegen_macros"
repository = "https://github.com/swc-project/swc.git"
version = "0.1.0"
version = "0.2.0"
[lib]
proc-macro = true

View File

@ -1,133 +0,0 @@
use pmutil::ToTokensExt;
use quote::quote_spanned;
use swc_macros_common::prelude::*;
use syn::{
fold::Fold,
parse::{Parse, Parser},
spanned::Spanned,
token::Token,
*,
};
pub(crate) struct InjectSelf {
pub parser: Option<Ident>,
}
#[cfg(procmacro2_semver_exempt)]
fn get_joined_span(t: &dyn ToTokens) -> Span {
let tts: TokenStream = t.dump().into();
let (mut first, mut last) = (None, None);
for tt in tts {
match first {
None => first = Some(tt.span()),
_ => {}
}
last = Some(tt.span());
}
let cs = Span::call_site();
first.unwrap_or(cs).join(last.unwrap_or(cs)).unwrap_or(cs)
}
#[cfg(not(procmacro2_semver_exempt))]
fn get_joined_span(t: &dyn ToTokens) -> Span {
let tts: TokenStream = t.dump();
let mut first = None;
for tt in tts {
if first.is_none() {
first = Some(tt.span());
}
// last = Some(tt.span());
}
let cs = Span::call_site();
// first.unwrap_or(cs).join(last.unwrap_or(cs)).unwrap_or(cs)
first.unwrap_or(cs)
}
fn parse_args<T, P>(tokens: TokenStream) -> Punctuated<T, P>
where
T: Parse,
P: Parse + Token,
{
let parser = Punctuated::parse_separated_nonempty;
parser.parse2(tokens).expect("failed parse args")
}
impl Fold for InjectSelf {
fn fold_signature(&mut self, i: Signature) -> Signature {
self.parser = i.inputs.first().cloned().and_then(|arg| match arg {
FnArg::Receiver(Receiver {
self_token,
mutability: Some(..),
..
})
| FnArg::Receiver(Receiver { self_token, .. }) => {
Some(Ident::new("self", self_token.span()))
}
_ => None,
});
i
}
fn fold_macro(&mut self, i: Macro) -> Macro {
let parser = match self.parser {
Some(ref s) => s.clone(),
_ => {
// If we are not in parser, don't do anything.
return i;
}
};
let name = i.path.dump().to_string();
let span = get_joined_span(&i.path);
match &*name {
"smallvec" | "write" | "writeln" | "vec" | "unreachable" | "tok" | "op" | "js_word" => {
i
}
"println" | "print" | "format" | "assert" | "assert_eq" | "assert_ne"
| "debug_assert" | "debug_assert_eq" | "debug_assert_ne" | "dbg" => {
let mut args: Punctuated<Expr, token::Comma> = parse_args(i.tokens);
args = args
.into_pairs()
.map(|el| el.map_item(|expr| self.fold_expr(expr)))
.collect();
Macro {
tokens: args.dump(),
..i
}
}
"trace" | "debug" | "info" | "warn" | "error" => i,
//TODO
"unimplemented" | "todo" => i,
//TODO: Collect expect and give that list to unexpected
"keyword" | "emit" | "punct" | "semi" | "formatting_semi" | "space"
| "formatting_space" | "operator" | "opt" | "opt_leading_space" => {
let tokens = if i.tokens.is_empty() {
quote_spanned!(span => #parser)
} else {
let args: Punctuated<Expr, token::Comma> = parse_args(i.tokens);
let args = args
.into_pairs()
.map(|el| el.map_item(|expr| self.fold_expr(expr)))
.map(|arg| arg.dump())
.flatten();
quote_spanned!(span => #parser,)
.into_iter()
.chain(args)
.collect()
};
Macro { tokens, ..i }
}
_ => {
unimplemented!("Macro: {:#?}", i);
}
}
}
}

View File

@ -1,9 +1,7 @@
extern crate proc_macro;
use pmutil::{smart_quote, Quote, ToTokensExt};
use syn::{fold::Fold, FnArg, ImplItemMethod, Type, TypeReference};
mod fold;
use syn::{FnArg, ImplItemMethod, Type, TypeReference};
#[proc_macro_attribute]
pub fn emitter(
@ -11,7 +9,6 @@ pub fn emitter(
item: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
let item: ImplItemMethod = syn::parse(item).expect("failed to parse input as an item");
let item = fold::InjectSelf { parser: None }.fold_impl_item_method(item);
let item = expand(item);
item.dump().into()

View File

@ -20,6 +20,7 @@ where
#[derive(Debug, Default, Clone, Copy)]
pub(crate) struct Ctx {
pub semi_after_property: bool,
pub escape_first_dash: bool,
}
pub(super) struct WithCtx<'w, I: 'w + CssWriter> {

View File

@ -48,8 +48,8 @@ where
#[emitter]
fn emit_rule(&mut self, n: &Rule) -> Result {
match n {
Rule::Style(n) => emit!(n),
Rule::AtRule(n) => emit!(n),
Rule::Style(n) => emit!(self, n),
Rule::AtRule(n) => emit!(self, n),
}
}
@ -57,229 +57,229 @@ where
fn emit_style_rule(&mut self, n: &StyleRule) -> Result {
self.emit_list(&n.selectors, ListFormat::CommaDelimited)?;
space!();
space!(self);
emit!(n.block);
emit!(self, n.block);
}
#[emitter]
fn emit_at_rule(&mut self, n: &AtRule) -> Result {
match n {
AtRule::Charset(n) => emit!(n),
AtRule::Import(n) => emit!(n),
AtRule::FontFace(n) => emit!(n),
AtRule::Keyframes(n) => emit!(n),
AtRule::Media(n) => emit!(n),
AtRule::Supports(n) => emit!(n),
AtRule::Page(n) => emit!(n),
AtRule::Namespace(n) => emit!(n),
AtRule::Viewport(n) => emit!(n),
AtRule::Document(n) => emit!(n),
AtRule::Unknown(n) => emit!(n),
AtRule::Charset(n) => emit!(self, n),
AtRule::Import(n) => emit!(self, n),
AtRule::FontFace(n) => emit!(self, n),
AtRule::Keyframes(n) => emit!(self, n),
AtRule::Media(n) => emit!(self, n),
AtRule::Supports(n) => emit!(self, n),
AtRule::Page(n) => emit!(self, n),
AtRule::Namespace(n) => emit!(self, n),
AtRule::Viewport(n) => emit!(self, n),
AtRule::Document(n) => emit!(self, n),
AtRule::Unknown(n) => emit!(self, n),
}
}
#[emitter]
fn emit_charset_rule(&mut self, n: &CharsetRule) -> Result {
punct!("@");
keyword!("charset");
punct!(self, "@");
keyword!(self, "charset");
space!();
space!(self);
emit!(n.charset);
emit!(self, n.charset);
semi!();
semi!(self);
}
#[emitter]
fn emit_import_rule(&mut self, n: &ImportRule) -> Result {
punct!("@");
keyword!("import");
punct!(self, "@");
keyword!(self, "import");
space!();
emit!(n.src);
space!(self);
emit!(self, n.src);
if let Some(query) = &n.condition {
space!();
emit!(query);
space!(self);
emit!(self, query);
}
semi!();
semi!(self);
}
#[emitter]
fn emit_font_face_rule(&mut self, n: &FontFaceRule) -> Result {
punct!("@");
keyword!("font-face");
space!();
punct!(self, "@");
keyword!(self, "font-face");
space!(self);
emit!(n.block);
emit!(self, n.block);
}
#[emitter]
fn emit_keyframes_rule(&mut self, n: &KeyframesRule) -> Result {
punct!("@");
keyword!("keyframes");
space!();
punct!(self, "@");
keyword!(self, "keyframes");
space!(self);
emit!(n.id);
emit!(self, n.id);
if !n.id.value.is_empty() {
space!();
space!(self);
}
punct!("{");
punct!(self, "{");
self.emit_list(&n.blocks, ListFormat::NotDelimited)?;
punct!("}");
punct!(self, "}");
}
#[emitter]
fn emit_keyfram_block(&mut self, n: &KeyframeBlock) -> Result {
self.emit_list(&n.selector, ListFormat::CommaDelimited)?;
space!();
space!(self);
emit!(n.rule);
emit!(self, n.rule);
}
#[emitter]
fn emit_keyframe_selector(&mut self, n: &KeyframeSelector) -> Result {
match n {
KeyframeSelector::Id(n) => emit!(n),
KeyframeSelector::Percent(n) => emit!(n),
KeyframeSelector::Id(n) => emit!(self, n),
KeyframeSelector::Percent(n) => emit!(self, n),
}
}
#[emitter]
fn emit_media_rule(&mut self, n: &MediaRule) -> Result {
punct!("@");
keyword!("media");
space!();
punct!(self, "@");
keyword!(self, "media");
space!(self);
emit!(n.query);
emit!(self, n.query);
space!();
space!(self);
punct!("{");
punct!(self, "{");
self.emit_list(&n.rules, ListFormat::NotDelimited | ListFormat::MultiLine)?;
punct!("}");
punct!(self, "}");
}
#[emitter]
fn emit_supports_rule(&mut self, n: &SupportsRule) -> Result {
punct!("@");
keyword!("supports");
space!();
punct!(self, "@");
keyword!(self, "supports");
space!(self);
emit!(n.query);
emit!(self, n.query);
space!();
space!(self);
punct!("{");
punct!(self, "{");
self.emit_list(&n.rules, ListFormat::NotDelimited)?;
punct!("}");
punct!(self, "}");
}
#[emitter]
fn emit_page_rule(&mut self, n: &PageRule) -> Result {
punct!("@");
keyword!("page");
space!();
punct!(self, "@");
keyword!(self, "page");
space!(self);
self.emit_list(&n.prelude, ListFormat::CommaDelimited)?;
emit!(n.block);
emit!(self, n.block);
}
#[emitter]
fn emit_page_selector(&mut self, n: &PageSelector) -> Result {
emit!(n.ident);
emit!(self, n.ident);
if let Some(pseudo) = &n.pseudo {
punct!(":");
emit!(pseudo);
punct!(self, ":");
emit!(self, pseudo);
}
}
#[emitter]
fn emit_namespace_rule(&mut self, n: &NamespaceRule) -> Result {
punct!("@");
keyword!("namespace");
space!();
punct!(self, "@");
keyword!(self, "namespace");
space!(self);
emit!(n.prefix);
emit!(self, n.prefix);
space!();
space!(self);
emit!(n.value);
emit!(self, n.value);
}
#[emitter]
fn emit_viewport_rule(&mut self, n: &ViewportRule) -> Result {
punct!("@");
keyword!("viewport");
space!();
punct!(self, "@");
keyword!(self, "viewport");
space!(self);
emit!(n.block);
emit!(self, n.block);
}
#[emitter]
fn emit_document_rule(&mut self, n: &DocumentRule) -> Result {
punct!("@");
keyword!("document");
space!();
punct!(self, "@");
keyword!(self, "document");
space!(self);
self.emit_list(&n.selectors, ListFormat::CommaDelimited)?;
space!();
space!(self);
punct!("{");
punct!(self, "{");
self.emit_list(&n.block, ListFormat::NotDelimited)?;
punct!("}");
punct!(self, "}");
}
#[emitter]
fn emit_fn_value(&mut self, n: &FnValue) -> Result {
emit!(n.name);
emit!(self, n.name);
punct!("(");
punct!(self, "(");
self.emit_list(&n.args, ListFormat::CommaDelimited)?;
punct!(")");
punct!(self, ")");
}
#[emitter]
fn emit_value(&mut self, n: &Value) -> Result {
match n {
Value::Paren(n) => emit!(n),
Value::Unit(n) => emit!(n),
Value::Number(n) => emit!(n),
Value::Percent(n) => emit!(n),
Value::Hash(n) => emit!(n),
Value::Text(n) => emit!(n),
Value::Str(n) => emit!(n),
Value::Fn(n) => emit!(n),
Value::Bin(n) => emit!(n),
Value::Array(n) => emit!(n),
Value::Space(n) => emit!(n),
Value::Brace(n) => emit!(n),
Value::Lazy(n) => emit!(n),
Value::AtText(n) => emit!(n),
Value::Url(n) => emit!(n),
Value::Comma(n) => emit!(n),
Value::Paren(n) => emit!(self, n),
Value::Unit(n) => emit!(self, n),
Value::Number(n) => emit!(self, n),
Value::Percent(n) => emit!(self, n),
Value::Hash(n) => emit!(self, n),
Value::Text(n) => emit!(self, n),
Value::Str(n) => emit!(self, n),
Value::Fn(n) => emit!(self, n),
Value::Bin(n) => emit!(self, n),
Value::Array(n) => emit!(self, n),
Value::Space(n) => emit!(self, n),
Value::Brace(n) => emit!(self, n),
Value::Lazy(n) => emit!(self, n),
Value::AtText(n) => emit!(self, n),
Value::Url(n) => emit!(self, n),
Value::Comma(n) => emit!(self, n),
}
}
#[emitter]
fn emit_unknown_at_rule(&mut self, n: &UnknownAtRule) -> Result {
punct!("@");
emit!(n.name);
space!();
punct!(self, "@");
emit!(self, n.name);
space!(self);
emit!(n.tokens)
emit!(self, n.tokens)
}
#[emitter]
fn emit_str(&mut self, n: &Str) -> Result {
// TODO: Handle escapes.
punct!("'");
punct!(self, "'");
if n.value.chars().any(|c| c == '\n') {
for c in n.value.chars() {
@ -296,96 +296,97 @@ where
} else {
self.wr.write_raw(Some(n.span), &n.value)?;
}
punct!("'");
punct!(self, "'");
}
#[emitter]
fn emit_media_query(&mut self, n: &MediaQuery) -> Result {
match n {
MediaQuery::Text(n) => emit!(n),
MediaQuery::And(n) => emit!(n),
MediaQuery::Or(n) => emit!(n),
MediaQuery::Not(n) => emit!(n),
MediaQuery::Only(n) => emit!(n),
MediaQuery::Text(n) => emit!(self, n),
MediaQuery::And(n) => emit!(self, n),
MediaQuery::Or(n) => emit!(self, n),
MediaQuery::Not(n) => emit!(self, n),
MediaQuery::Only(n) => emit!(self, n),
MediaQuery::Property(n) => {
punct!("(");
emit!(n);
punct!(")");
punct!(self, "(");
emit!(self, n);
punct!(self, ")");
}
MediaQuery::Comma(n) => emit!(n),
MediaQuery::Comma(n) => emit!(self, n),
}
}
#[emitter]
fn emit_decl_block(&mut self, n: &DeclBlock) -> Result {
punct!("{");
punct!(self, "{");
self.emit_list(
&n.properties,
ListFormat::SemiDelimited | ListFormat::MultiLine,
)?;
punct!("}");
punct!(self, "}");
}
#[emitter]
fn emit_property(&mut self, n: &Property) -> Result {
emit!(n.name);
punct!(":");
formatting_space!();
emit!(self, n.name);
punct!(self, ":");
formatting_space!(self);
self.emit_list(
&n.values,
ListFormat::SpaceDelimited | ListFormat::SingleLine,
)?;
if let Some(tok) = n.important {
formatting_space!();
punct!(tok, "!");
formatting_space!(self);
punct!(self, tok, "!");
self.wr.write_ident(Some(tok), "important", false)?;
}
if self.ctx.semi_after_property {
punct!(";");
punct!(self, ";");
}
}
#[emitter]
fn emit_text(&mut self, n: &Text) -> Result {
self.wr.write_ident(Some(n.span), &n.value, false)?;
self.wr
.write_ident(Some(n.span), &n.value, self.ctx.escape_first_dash)?;
}
#[emitter]
fn emit_keyframe_block_rule(&mut self, n: &KeyframeBlockRule) -> Result {
match n {
KeyframeBlockRule::Decl(n) => emit!(n),
KeyframeBlockRule::AtRule(n) => emit!(n),
KeyframeBlockRule::Decl(n) => emit!(self, n),
KeyframeBlockRule::AtRule(n) => emit!(self, n),
}
}
#[emitter]
fn emit_percent_value(&mut self, n: &PercentValue) -> Result {
emit!(n.value);
punct!("%");
emit!(self, n.value);
punct!(self, "%");
}
#[emitter]
fn emit_support_query(&mut self, n: &SupportQuery) -> Result {
match n {
SupportQuery::Not(n) => emit!(n),
SupportQuery::And(n) => emit!(n),
SupportQuery::Or(n) => emit!(n),
SupportQuery::Not(n) => emit!(self, n),
SupportQuery::And(n) => emit!(self, n),
SupportQuery::Or(n) => emit!(self, n),
SupportQuery::Property(n) => {
punct!("(");
emit!(n);
punct!(")");
punct!(self, "(");
emit!(self, n);
punct!(self, ")");
}
SupportQuery::Paren(n) => emit!(n),
SupportQuery::Paren(n) => emit!(self, n),
}
}
#[emitter]
fn emit_page_rule_block(&mut self, n: &PageRuleBlock) -> Result {
punct!("{");
punct!(self, "{");
self.wr.write_newline()?;
@ -402,28 +403,28 @@ where
self.wr.decrease_indent();
punct!("}");
punct!(self, "}");
}
#[emitter]
fn emit_page_rule_block_item(&mut self, n: &PageRuleBlockItem) -> Result {
match n {
PageRuleBlockItem::Property(n) => emit!(n),
PageRuleBlockItem::Nested(n) => emit!(n),
PageRuleBlockItem::Property(n) => emit!(self, n),
PageRuleBlockItem::Nested(n) => emit!(self, n),
}
}
#[emitter]
fn emit_paren_value(&mut self, n: &ParenValue) -> Result {
punct!("(");
emit!(n.value);
punct!(")");
punct!(self, "(");
emit!(self, n.value);
punct!(self, ")");
}
#[emitter]
fn emit_unit_value(&mut self, n: &UnitValue) -> Result {
emit!(n.value);
emit!(n.unit);
emit!(self, n.value);
emit!(self, n.unit);
}
#[emitter]
@ -433,27 +434,27 @@ where
#[emitter]
fn emit_hash_value(&mut self, n: &HashValue) -> Result {
punct!("#");
punct!(self, "#");
self.wr.write_hash_value(Some(n.span), &n.value)?;
}
#[emitter]
fn emit_bin_value(&mut self, n: &BinValue) -> Result {
emit!(n.left);
space!();
punct!(n.op.as_str());
space!();
emit!(n.right);
emit!(self, n.left);
space!(self);
punct!(self, n.op.as_str());
space!(self);
emit!(self, n.right);
}
#[emitter]
fn emit_array_value(&mut self, n: &ArrayValue) -> Result {
punct!("[");
punct!(self, "[");
self.emit_list(&n.values, ListFormat::CommaDelimited)?;
punct!("]");
punct!(self, "]");
}
#[emitter]
@ -468,9 +469,9 @@ where
#[emitter]
fn emit_brace_value(&mut self, n: &BraceValue) -> Result {
punct!("{");
emit!(n.value);
punct!("}");
punct!(self, "{");
emit!(self, n.value);
punct!(self, "}");
}
#[emitter]
@ -479,23 +480,24 @@ where
let span = *span;
match token {
Token::AtKeyword(name) => {
punct!(span, "@");
punct!(self, span, "@");
punct!(self, span, "@");
self.wr.write_ident(Some(span), &name, false)?;
}
Token::LParen => {
punct!(span, "(");
punct!(self, span, "(");
}
Token::RParen => {
punct!(span, ")");
punct!(self, span, ")");
}
Token::LBracket => {
punct!(span, "[");
punct!(self, span, "[");
}
Token::RBracket => {
punct!(span, "]");
punct!(self, span, "]");
}
Token::Percent => {
punct!(span, "%");
punct!(self, span, "%");
}
Token::Num(n) => {
self.wr.write_raw(Some(span), &n.value.to_string())?;
@ -504,82 +506,84 @@ where
self.wr.write_ident(Some(span), &n, true)?;
}
Token::Str { value } => {
punct!("'");
punct!(self, "'");
self.wr.write_raw(Some(span), &value)?;
punct!("'");
punct!(self, "'");
}
Token::Url { value } => {
self.wr.write_ident(Some(span), "url", false)?;
punct!("(");
punct!(self, "(");
punct!(self, "(");
self.wr.write_raw(None, &value)?;
punct!(")");
punct!(self, ")");
}
Token::Comma => {
punct!(span, ",");
punct!(self, span, ",");
}
Token::Semi => {
punct!(span, ";");
punct!(self, span, ";");
}
Token::Bang => {
punct!(span, "!");
punct!(self, span, "!");
}
Token::LBrace => {
punct!(span, "{");
punct!(self, span, "{");
}
Token::RBrace => {
punct!(span, "}");
punct!(self, span, "}");
}
Token::Colon => {
punct!(span, ":");
punct!(self, span, ":");
}
Token::Asterisk => {
punct!(span, "*");
punct!(self, span, "*");
}
Token::Dot => {
punct!(span, ".");
punct!(self, span, ".");
}
Token::Hash { value, .. } => {
punct!("#");
punct!(self, "#");
punct!(self, "#");
self.wr.write_ident(Some(span), &value, true)?;
}
Token::WhiteSpace => {
space!();
space!(self);
}
Token::CDC => {
punct!(span, "-->");
punct!(self, span, "-->");
}
Token::CDO => {
punct!(span, "<!--");
punct!(self, span, "<!--");
}
Token::Ampersand => {
punct!(span, "&");
punct!(self, span, "&");
}
Token::Bar => {
punct!(span, "|");
punct!(self, span, "|");
}
Token::Dollar => {
punct!(span, "$");
punct!(self, span, "$");
}
Token::Caret => {
punct!(span, "^");
punct!(self, span, "^");
}
Token::Tilde => {
punct!(span, "~");
punct!(self, span, "~");
}
Token::Equals => {
punct!(span, "=");
punct!(self, span, "=");
}
Token::Plus => {
punct!(span, "+");
punct!(self, span, "+");
}
Token::Minus => {
punct!(span, "-");
punct!(self, span, "-");
}
Token::Div => {
punct!(span, "/");
punct!(self, span, "/");
}
Token::GreaterThan => {
punct!(span, ">");
punct!(self, span, ">");
}
}
}
@ -587,55 +591,55 @@ where
#[emitter]
fn emit_at_text_value(&mut self, n: &AtTextValue) -> Result {
punct!("@");
emit!(n.name);
space!();
punct!(self, "@");
emit!(self, n.name);
space!(self);
emit!(n.block);
emit!(self, n.block);
}
#[emitter]
fn emit_url_value(&mut self, n: &UrlValue) -> Result {
keyword!("url");
punct!("(");
keyword!(self, "url");
punct!(self, "(");
self.wr.write_raw(Some(n.span), &n.url)?;
punct!(")");
punct!(self, ")");
}
#[emitter]
fn emit_and_media_query(&mut self, n: &AndMediaQuery) -> Result {
emit!(n.left);
space!();
emit!(self, n.left);
space!(self);
keyword!("and");
keyword!(self, "and");
space!();
emit!(n.right);
space!(self);
emit!(self, n.right);
}
#[emitter]
fn emit_or_media_query(&mut self, n: &OrMediaQuery) -> Result {
emit!(n.left);
space!();
emit!(self, n.left);
space!(self);
keyword!("or");
keyword!(self, "or");
space!();
emit!(n.right);
space!(self);
emit!(self, n.right);
}
#[emitter]
fn emit_not_media_query(&mut self, n: &NotMediaQuery) -> Result {
keyword!("not");
space!();
emit!(n.query);
keyword!(self, "not");
space!(self);
emit!(self, n.query);
}
#[emitter]
fn emit_only_media_query(&mut self, n: &OnlyMediaQuery) -> Result {
keyword!("only");
space!();
emit!(n.query);
keyword!(self, "only");
space!(self);
emit!(self, n.query);
}
#[emitter]
@ -645,46 +649,46 @@ where
#[emitter]
fn emit_not_support_query(&mut self, n: &NotSupportQuery) -> Result {
keyword!("not");
space!();
keyword!(self, "not");
space!(self);
emit!(n.query);
emit!(self, n.query);
}
#[emitter]
fn emit_and_support_query(&mut self, n: &AndSupportQuery) -> Result {
emit!(n.left);
space!();
emit!(self, n.left);
space!(self);
keyword!("and");
keyword!(self, "and");
space!();
emit!(n.right);
space!(self);
emit!(self, n.right);
}
#[emitter]
fn emit_or_support_query(&mut self, n: &OrSupportQuery) -> Result {
emit!(n.left);
space!();
emit!(self, n.left);
space!(self);
keyword!("or");
keyword!(self, "or");
space!();
emit!(n.right);
space!(self);
emit!(self, n.right);
}
#[emitter]
fn emit_paren_support_query(&mut self, n: &ParenSupportQuery) -> Result {
punct!("(");
emit!(n.query);
punct!(")");
punct!(self, "(");
emit!(self, n.query);
punct!(self, ")");
}
#[emitter]
fn emit_nested_page_rule(&mut self, n: &NestedPageRule) -> Result {
self.emit_list(&n.prelude, ListFormat::CommaDelimited)?;
emit!(n.block);
emit!(self, n.block);
}
#[emitter]
@ -695,14 +699,18 @@ where
#[emitter]
fn emit_compound_selector(&mut self, n: &CompoundSelector) -> Result {
if n.has_nest_prefix {
punct!("&");
punct!(self, "&");
}
if let Some(combinator) = &n.combinator {
self.wr.write_punct(None, combinator.as_str())?;
}
emit!(n.type_selector);
let ctx = Ctx {
escape_first_dash: true,
..self.ctx
};
emit!(&mut *self.with_ctx(ctx), n.type_selector);
self.emit_list(&n.subclass_selectors, ListFormat::NotDelimited)?;
}
@ -710,11 +718,11 @@ where
#[emitter]
fn emit_subclass_selector(&mut self, n: &SubclassSelector) -> Result {
match n {
SubclassSelector::Id(n) => emit!(n),
SubclassSelector::Class(n) => emit!(n),
SubclassSelector::Attr(n) => emit!(n),
SubclassSelector::Pseudo(n) => emit!(n),
SubclassSelector::At(n) => emit!(n),
SubclassSelector::Id(n) => emit!(self, n),
SubclassSelector::Class(n) => emit!(self, n),
SubclassSelector::Attr(n) => emit!(self, n),
SubclassSelector::Pseudo(n) => emit!(self, n),
SubclassSelector::At(n) => emit!(self, n),
}
}
@ -730,67 +738,75 @@ where
#[emitter]
fn emit_namespaced_name(&mut self, n: &NamespacedName) -> Result {
if let Some(prefix) = &n.prefix {
emit!(prefix);
punct!("|");
emit!(self, prefix);
punct!(self, "|");
}
emit!(n.name);
emit!(self, n.name);
}
#[emitter]
fn emit_id_selector(&mut self, n: &IdSelector) -> Result {
punct!("#");
emit!(n.text);
punct!(self, "#");
let ctx = Ctx {
escape_first_dash: true,
..self.ctx
};
emit!(&mut *self.with_ctx(ctx), n.text);
}
#[emitter]
fn emit_class_selector(&mut self, n: &ClassSelector) -> Result {
punct!(".");
emit!(n.text);
punct!(self, ".");
let ctx = Ctx {
escape_first_dash: true,
..self.ctx
};
emit!(&mut *self.with_ctx(ctx), n.text);
}
#[emitter]
fn emit_attr_selector(&mut self, n: &AttrSelector) -> Result {
punct!("[");
punct!(self, "[");
emit!(n.name);
emit!(self, n.name);
if let Some(op) = n.op {
space!();
space!(self);
self.wr.write_punct(None, op.as_str())?;
space!();
space!(self);
}
emit!(n.value);
emit!(self, n.value);
if let Some(m) = &n.modifier {
space!();
space!(self);
self.wr.write_raw_char(None, *m)?;
}
punct!("]");
punct!(self, "]");
}
#[emitter]
fn emit_pseudo_selector(&mut self, n: &PseudoSelector) -> Result {
punct!(":");
punct!(self, ":");
if n.is_element {
punct!(":");
punct!(self, ":");
}
emit!(n.name);
emit!(self, n.name);
if !n.args.tokens.is_empty() {
punct!("(");
emit!(n.args);
punct!(")");
punct!(self, "(");
emit!(self, n.args);
punct!(self, ")");
}
}
#[emitter]
fn emit_at_selector(&mut self, n: &AtSelector) -> Result {
punct!("@");
emit!(n.text);
punct!(self, "@");
emit!(self, n.text);
}
fn emit_list<N>(&mut self, nodes: &[N], format: ListFormat) -> Result

View File

@ -45,7 +45,7 @@ where
Ok(())
}
fn write_escaped(&mut self, s: &str, escape_dash: bool) -> Result {
fn write_escaped(&mut self, s: &str, escape_first_dash: bool) -> Result {
for (idx, c) in s.chars().enumerate() {
match c {
' ' | ',' | ':' | '~' | '+' | '.' | '#' | '\x00'..='\x1f' => {
@ -53,7 +53,7 @@ where
self.w.write_char('\\')?;
}
'-' if escape_dash => {
'-' if escape_first_dash && idx == 0 => {
self.col += 1;
self.w.write_char('\\')?;
}
@ -80,9 +80,9 @@ impl<W> CssWriter for BasicCssWriter<'_, W>
where
W: Write,
{
fn write_ident(&mut self, _span: Option<Span>, s: &str, escape_dash: bool) -> Result {
fn write_ident(&mut self, _span: Option<Span>, s: &str, escape_first_dash: bool) -> Result {
self.apply_indent()?;
self.write_escaped(s, escape_dash)?;
self.write_escaped(s, escape_first_dash)?;
Ok(())
}

View File

@ -7,7 +7,7 @@ pub mod basic;
#[auto_impl(&mut, Box)]
pub trait CssWriter {
/// Implementor should handle escapes.
fn write_ident(&mut self, span: Option<Span>, s: &str, escape_dash: bool) -> Result;
fn write_ident(&mut self, span: Option<Span>, s: &str, escape_first_dash: bool) -> Result;
/// `punct` should not contain newline.
fn write_punct(&mut self, span: Option<Span>, punct: &str) -> Result;