1use super::{Parser, state::ParserState};
2use crate::{
3 Parse, Syntax,
4 ast::*,
5 error::{Error, ErrorKind, PResult},
6 pos::Span,
7 tokenizer::Token,
8};
9
10mod color_profile;
11mod container;
12mod counter_style;
13mod custom_media;
14mod custom_selector;
15mod document;
16mod font_feature_values;
17mod import;
18mod keyframes;
19mod layer;
20mod media;
21mod namespace;
22mod page;
23mod scope;
24mod supports;
25
26impl<'a> Parse<'a> for AtRule<'a> {
27 fn parse(input: &mut Parser<'a>) -> PResult<Self> {
28 let (at_keyword, at_keyword_span) = input.cursor.expect_at_keyword()?;
29
30 let at_rule_name = at_keyword.ident.name();
31 let (prelude, block, end) = if at_rule_name.eq_ignore_ascii_case("media") {
32 let prelude = match input
35 .try_parse_full_prelude(|p| MediaQueryList::parse(p).map(AtRulePrelude::Media))
36 {
37 Ok(prelude) => Some(prelude),
38 Err(_)
39 if matches!(
40 input.cursor.peek()?.token,
41 Token::LBrace(..) | Token::Indent(..)
42 ) =>
43 {
44 None
45 }
46 Err(_) => {
50 let raw = if input.syntax != Syntax::Css {
55 input.try_parse(|p| {
56 let raw = p.parse_raw_at_rule_prelude()?;
57 let has_substitution = matches!(
58 &raw,
59 UnknownAtRulePrelude::TokenSeq(seq)
60 if seq.tokens.iter().any(|t| match &t.token {
61 Token::HashLBrace(..) | Token::StrTemplate(..) => {
62 matches!(p.syntax, Syntax::Scss | Syntax::Sass)
63 }
64 Token::AtKeyword(..) | Token::AtLBraceVar(..) => {
65 p.syntax == Syntax::Less
66 }
67 _ => false,
68 })
69 );
70 if has_substitution {
71 Ok(raw)
72 } else {
73 let span = p.cursor.peek()?.span.clone();
74 Err(Error { kind: ErrorKind::TryParseError, span })
75 }
76 })
77 } else {
78 Err(Error {
79 kind: ErrorKind::TryParseError,
80 span: input.cursor.peek()?.span.clone(),
81 })
82 };
83 match raw {
84 Ok(raw) => Some(AtRulePrelude::Unknown(input.alloc(raw))),
85 Err(_) => Some(AtRulePrelude::Media(input.parse()?)),
86 }
87 }
88 };
89 let block = input.parse::<SimpleBlock>()?;
90 let end = block.span.end;
91 (prelude, Some(block), end)
92 } else if at_rule_name.eq_ignore_ascii_case("keyframes")
93 || at_rule_name.eq_ignore_ascii_case("-webkit-keyframes")
94 || at_rule_name.eq_ignore_ascii_case("-moz-keyframes")
95 || at_rule_name.eq_ignore_ascii_case("-ms-keyframes")
96 || at_rule_name.eq_ignore_ascii_case("-o-keyframes")
97 {
98 let prelude = match &input.cursor.peek()?.token {
102 Token::LBrace(..) => None,
103 _ => {
104 let typed = input.try_parse_full_prelude(KeyframesName::parse);
109 match typed {
110 Ok(name) => Some(AtRulePrelude::Keyframes(name)),
111 Err(_) => input
112 .parse_unknown_at_rule_prelude()?
113 .map(|prelude| AtRulePrelude::Unknown(input.alloc(prelude))),
114 }
115 }
116 };
117 let block = input
118 .with_state(ParserState { in_keyframes_at_rule: true, ..input.state.clone() })
119 .parse::<SimpleBlock>()?;
120 let end = block.span.end;
121 (prelude, Some(block), end)
122 } else if at_rule_name.eq_ignore_ascii_case("import") {
123 let (end, prelude) = match input.syntax {
124 Syntax::Css => {
125 let prelude = input.parse::<ImportPrelude>()?;
126 (prelude.span.end, AtRulePrelude::Import(input.alloc(prelude)))
127 }
128 Syntax::Scss | Syntax::Sass => {
129 if let Ok(prelude) = input.try_parse(ImportPrelude::parse) {
130 (prelude.span.end, AtRulePrelude::Import(input.alloc(prelude)))
131 } else {
132 let prelude = input.parse::<SassImportPrelude>()?;
133 (prelude.span.end, AtRulePrelude::SassImport(prelude))
134 }
135 }
136 Syntax::Less => {
137 if let Ok(prelude) = input.try_parse(ImportPrelude::parse) {
138 (prelude.span.end, AtRulePrelude::Import(input.alloc(prelude)))
139 } else {
140 let prelude = input.parse::<LessImportPrelude>()?;
141 (prelude.span.end, AtRulePrelude::LessImport(input.alloc(prelude)))
142 }
143 }
144 };
145 (Some(prelude), None, end)
146 } else if at_rule_name.eq_ignore_ascii_case("charset") {
147 if input.syntax == Syntax::Less
150 && matches!(input.cursor.peek()?.token, Token::StrTemplate(..))
151 {
152 let prelude = input.parse::<InterpolableStr>()?;
153 let end = prelude.span().end;
154 (
155 Some(AtRulePrelude::Unknown(input.alloc(
156 UnknownAtRulePrelude::ComponentValue(ComponentValue::InterpolableStr(
157 prelude,
158 )),
159 ))),
160 None,
161 end,
162 )
163 } else {
164 let prelude = input.parse::<Str>()?;
165 let end = prelude.span.end;
166 (Some(AtRulePrelude::Charset(prelude)), None, end)
167 }
168 } else if at_rule_name.eq_ignore_ascii_case("font-face") {
169 let block = input.parse::<SimpleBlock>()?;
170 let end = block.span.end;
171 (None, Some(block), end)
172 } else if at_rule_name.eq_ignore_ascii_case("supports") {
173 let prelude = Some(AtRulePrelude::Supports(input.parse()?));
174 let block = input.parse::<SimpleBlock>()?;
175 let end = block.span.end;
176 (prelude, Some(block), end)
177 } else if at_rule_name.eq_ignore_ascii_case("layer") {
178 let prelude = match input.try_parse(LayerNames::parse) {
179 Ok(names) => Some(AtRulePrelude::Layer(names)),
180 Err(_)
182 if input.syntax == Syntax::Less
183 && matches!(input.cursor.peek()?.token, Token::AtKeyword(..)) =>
184 {
185 let raw = input.parse_raw_at_rule_prelude()?;
186 Some(AtRulePrelude::Unknown(input.alloc(raw)))
187 }
188 Err(_) => None,
189 };
190 let block =
191 if matches!(input.cursor.peek()?.token, Token::LBrace(..) | Token::Indent(..)) {
192 Some(input.parse::<SimpleBlock>()?)
193 } else {
194 None
195 };
196 if let Some(block) = &block
197 && matches!(&prelude, Some(AtRulePrelude::Layer(names)) if names.names.len() > 1)
198 {
199 input.recoverable_errors.push(Error {
200 kind: ErrorKind::UnexpectedSimpleBlock,
201 span: block.span.clone(),
202 });
203 }
204 let end = block
205 .as_ref()
206 .map(|block| block.span.end)
207 .or_else(|| prelude.as_ref().map(|prelude| prelude.span().end))
208 .unwrap_or(at_keyword_span.end);
209 (prelude, block, end)
210 } else if at_rule_name.eq_ignore_ascii_case("container") {
211 let prelude =
216 match input.try_parse_full_prelude(|p| p.parse().map(AtRulePrelude::Container)) {
217 Ok(prelude) => prelude,
218 Err(error) => {
219 let is_query_list = input
220 .try_parse(|p| {
221 p.parse::<ContainerPrelude>()?;
222 if matches!(p.cursor.peek()?.token, Token::Comma(..)) {
223 Ok(())
224 } else {
225 let span = p.cursor.peek()?.span.clone();
226 Err(Error { kind: ErrorKind::TryParseError, span })
227 }
228 })
229 .is_ok();
230 let less_variable_name = input.syntax == Syntax::Less
233 && matches!(input.cursor.peek()?.token, Token::AtKeyword(..));
234 if !is_query_list && !less_variable_name {
235 return Err(error);
236 }
237 let prelude = input.parse_raw_at_rule_prelude()?;
238 AtRulePrelude::Unknown(input.alloc(prelude))
239 }
240 };
241 let block = input.parse::<SimpleBlock>()?;
242 let end = block.span.end;
243 (Some(prelude), Some(block), end)
244 } else if at_rule_name.eq_ignore_ascii_case("page") {
245 let prelude = input.try_parse(PageSelectorList::parse).map(AtRulePrelude::Page).ok();
246 let block = input.try_parse(SimpleBlock::parse).ok();
247 let end = block
248 .as_ref()
249 .map(|block| block.span.end)
250 .or_else(|| prelude.as_ref().map(|prelude| prelude.span().end))
251 .unwrap_or(at_keyword_span.end);
252 (prelude, block, end)
253 } else if at_rule_name.eq_ignore_ascii_case("namespace")
254 && input.syntax == Syntax::Less
255 && matches!(input.cursor.peek()?.token, Token::AtKeyword(..))
256 {
257 let raw = input.parse_raw_at_rule_prelude()?;
259 let end = raw.span().end;
260 (Some(AtRulePrelude::Unknown(input.alloc(raw))), None, end)
261 } else if at_rule_name.eq_ignore_ascii_case("namespace") {
262 let namespace = input.parse::<NamespacePrelude>()?;
263 let end = namespace.span.end;
264 (Some(AtRulePrelude::Namespace(input.alloc(namespace))), None, end)
265 } else if at_rule_name.eq_ignore_ascii_case("color-profile") {
266 let prelude = Some(AtRulePrelude::ColorProfile(input.parse()?));
267 let block = input.parse::<SimpleBlock>()?;
268 let end = block.span.end;
269 (prelude, Some(block), end)
270 } else if at_rule_name.eq_ignore_ascii_case("font-feature-values") {
271 let prelude = Some(AtRulePrelude::FontFeatureValues(input.parse()?));
272 let block = input.parse::<SimpleBlock>()?;
273 let end = block.span.end;
274 (prelude, Some(block), end)
275 } else if at_rule_name.eq_ignore_ascii_case("font-palette-values") {
276 let prelude = Some(AtRulePrelude::FontPaletteValues(input.parse_dashed_ident()?));
278 let block = input.parse::<SimpleBlock>()?;
279 let end = block.span.end;
280 (prelude, Some(block), end)
281 } else if at_rule_name.eq_ignore_ascii_case("counter-style") {
282 let prelude = Some(AtRulePrelude::CounterStyle(input.parse_counter_style_prelude()?));
283 let block = input.parse::<SimpleBlock>()?;
284 let end = block.span.end;
285 (prelude, Some(block), end)
286 } else if at_rule_name.eq_ignore_ascii_case("custom-media") {
287 let custom_media = input.parse::<CustomMedia>()?;
288 let end = custom_media.span.end;
289 (Some(AtRulePrelude::CustomMedia(input.alloc(custom_media))), None, end)
290 } else if at_rule_name.eq_ignore_ascii_case("custom-selector") {
291 let custom_selector_prelude = input.parse::<CustomSelectorPrelude>()?;
292 let end = custom_selector_prelude.span.end;
293 (Some(AtRulePrelude::CustomSelector(input.alloc(custom_selector_prelude))), None, end)
294 } else if at_rule_name.eq_ignore_ascii_case("position-try") {
295 let prelude = Some(AtRulePrelude::PositionTry(input.parse_dashed_ident()?));
297 let block = input.parse::<SimpleBlock>()?;
298 let end = block.span.end;
299 (prelude, Some(block), end)
300 } else if at_rule_name.eq_ignore_ascii_case("nest") {
301 let prelude = Some(AtRulePrelude::Nest(input.parse()?));
303 let block = input.parse::<SimpleBlock>()?;
304 let end = block.span.end;
305 (prelude, Some(block), end)
306 } else if at_rule_name.eq_ignore_ascii_case("property") {
307 let prelude = Some(AtRulePrelude::Property(input.parse_dashed_ident()?));
309 let block = input.parse::<SimpleBlock>()?;
310 let end = block.span.end;
311 (prelude, Some(block), end)
312 } else if at_rule_name.eq_ignore_ascii_case("scope") {
313 let prelude = if let Token::LParen(..) | Token::Ident(..) = input.cursor.peek()?.token {
314 let scope = input.parse()?;
315 Some(AtRulePrelude::Scope(input.alloc(scope)))
316 } else {
317 None
318 };
319 let block = input.parse::<SimpleBlock>()?;
320 let end = block.span.end;
321 (prelude, Some(block), end)
322 } else if at_rule_name.eq_ignore_ascii_case("document")
323 || at_rule_name.eq_ignore_ascii_case("-moz-document")
324 {
325 let prelude = match input.try_parse(|p| p.parse().map(AtRulePrelude::Document)) {
326 Ok(prelude) => prelude,
327 Err(error) => {
330 if matches!(
331 input.cursor.peek()?.token,
332 Token::LBrace(..) | Token::Indent(..) | Token::Semicolon(..)
333 ) {
334 return Err(error);
335 }
336 let prelude = input.parse_raw_at_rule_prelude()?;
337 AtRulePrelude::Unknown(input.alloc(prelude))
338 }
339 };
340 let block =
342 if matches!(input.cursor.peek()?.token, Token::LBrace(..) | Token::Indent(..)) {
343 Some(input.parse::<SimpleBlock>()?)
344 } else {
345 None
346 };
347 let end = block.as_ref().map_or(prelude.span().end, |block| block.span.end);
348 (Some(prelude), block, end)
349 } else if at_rule_name.eq_ignore_ascii_case("stylistic")
350 || at_rule_name.eq_ignore_ascii_case("historical-forms")
351 || at_rule_name.eq_ignore_ascii_case("styleset")
352 || at_rule_name.eq_ignore_ascii_case("character-variant")
353 || at_rule_name.eq_ignore_ascii_case("swash")
354 || at_rule_name.eq_ignore_ascii_case("ornaments")
355 || at_rule_name.eq_ignore_ascii_case("annotation")
356 || at_rule_name.eq_ignore_ascii_case("top-left-corner")
357 || at_rule_name.eq_ignore_ascii_case("top-left")
358 || at_rule_name.eq_ignore_ascii_case("top-center")
359 || at_rule_name.eq_ignore_ascii_case("top-right")
360 || at_rule_name.eq_ignore_ascii_case("top-right-corner")
361 || at_rule_name.eq_ignore_ascii_case("bottom-left-corner")
362 || at_rule_name.eq_ignore_ascii_case("bottom-left")
363 || at_rule_name.eq_ignore_ascii_case("bottom-center")
364 || at_rule_name.eq_ignore_ascii_case("bottom-right")
365 || at_rule_name.eq_ignore_ascii_case("bottom-right-corner")
366 || at_rule_name.eq_ignore_ascii_case("left-top")
367 || at_rule_name.eq_ignore_ascii_case("left-middle")
368 || at_rule_name.eq_ignore_ascii_case("left-bottom")
369 || at_rule_name.eq_ignore_ascii_case("right-top")
370 || at_rule_name.eq_ignore_ascii_case("right-middle")
371 || at_rule_name.eq_ignore_ascii_case("right-bottom")
372 || at_rule_name.eq_ignore_ascii_case("viewport")
373 || at_rule_name.eq_ignore_ascii_case("try")
374 || at_rule_name.eq_ignore_ascii_case("starting-style")
375 {
376 let block = input.parse::<SimpleBlock>()?;
377 let end = block.span.end;
378 (None, Some(block), end)
379 } else if at_rule_name == "plugin" && input.syntax == Syntax::Less {
380 let prelude = input.parse::<LessPlugin>()?;
381 let end = prelude.span.end;
382 (Some(AtRulePrelude::LessPlugin(input.alloc(prelude))), None, end)
383 } else if at_rule_name.eq_ignore_ascii_case("function")
384 && matches!(&input.cursor.peek()?.token, Token::Ident(ident) if ident.raw.starts_with("--"))
385 {
386 let prelude = input.parse_raw_at_rule_prelude()?;
392 let block = input
393 .with_state(ParserState { in_css_function_body: true, ..input.state.clone() })
394 .parse::<SimpleBlock>()?;
395 let end = block.span.end;
396 (Some(AtRulePrelude::Unknown(input.alloc(prelude))), Some(block), end)
397 } else if matches!(input.syntax, Syntax::Scss | Syntax::Sass) {
398 use super::state::{
399 SASS_CTX_ALLOW_DIV, SASS_CTX_ALLOW_KEYFRAME_BLOCK, SASS_CTX_IN_FUNCTION,
400 };
401 match &*at_rule_name {
402 "each" => {
403 let prelude = input.parse()?;
404 let block = input.parse::<SimpleBlock>()?;
405 let end = block.span.end;
406 (Some(AtRulePrelude::SassEach(input.alloc(prelude))), Some(block), end)
407 }
408 "while" => {
409 input.eat_sass_line_continuation()?;
410 let prelude = input.parse()?;
411 let block = input.parse::<SimpleBlock>()?;
412 let end = block.span.end;
413 (Some(AtRulePrelude::SassExpr(input.alloc(prelude))), Some(block), end)
414 }
415 "for" => {
416 let prelude = input.parse()?;
417 let block = input.parse::<SimpleBlock>()?;
418 let end = block.span.end;
419 (Some(AtRulePrelude::SassFor(input.alloc(prelude))), Some(block), end)
420 }
421 "mixin" => {
422 let prelude = input.parse()?;
423 let block = input
424 .with_state(ParserState {
425 sass_ctx: input.state.sass_ctx | SASS_CTX_ALLOW_KEYFRAME_BLOCK,
426 ..input.state.clone()
427 })
428 .parse::<SimpleBlock>()?;
429 let end = block.span.end;
430 (Some(AtRulePrelude::SassMixin(input.alloc(prelude))), Some(block), end)
431 }
432 "include" => {
433 let prelude = input.parse::<SassInclude>()?;
434 let block = if matches!(
435 input.cursor.peek()?.token,
436 Token::LBrace(..) | Token::Indent(..)
437 ) {
438 Some(
439 input
440 .with_state(ParserState {
441 sass_ctx: input.state.sass_ctx | SASS_CTX_ALLOW_KEYFRAME_BLOCK,
442 ..input.state.clone()
443 })
444 .parse::<SimpleBlock>()?,
445 )
446 } else {
447 None
448 };
449 let end =
450 block.as_ref().map(|block| block.span.end).unwrap_or(prelude.span.end);
451 (Some(AtRulePrelude::SassInclude(input.alloc(prelude))), block, end)
452 }
453 "content" => {
454 if matches!(input.cursor.peek()?.token, Token::LParen(..)) {
455 let prelude = input.parse::<SassContent>()?;
456 let end = prelude.span.end;
457 (Some(AtRulePrelude::SassContent(prelude)), None, end)
458 } else {
459 (None, None, input.cursor.tokenizer.current_offset())
460 }
461 }
462 "use" => {
463 let prelude = input.parse::<SassUse>()?;
464 let end = prelude.span.end;
465 (Some(AtRulePrelude::SassUse(input.alloc(prelude))), None, end)
466 }
467 "function" => {
468 let prelude = input.parse::<SassFunction>()?;
469 let block = input
470 .with_state(ParserState {
471 sass_ctx: input.state.sass_ctx | SASS_CTX_IN_FUNCTION,
472 ..input.state.clone()
473 })
474 .parse::<SimpleBlock>()?;
475 let end = block.span.end;
476 (Some(AtRulePrelude::SassFunction(input.alloc(prelude))), Some(block), end)
477 }
478 "return" => {
479 input.eat_sass_line_continuation()?;
480 let expr = input
481 .with_state(ParserState {
482 sass_ctx: input.state.sass_ctx | SASS_CTX_ALLOW_DIV,
483 ..input.state.clone()
484 })
485 .parse_maybe_sass_list(true)?;
486 let end = expr.span().end;
487 if input.state.sass_ctx & SASS_CTX_IN_FUNCTION == 0 {
488 input.recoverable_errors.push(Error {
489 kind: ErrorKind::ReturnOutsideFunction,
490 span: Span { start: at_keyword_span.start, end },
491 });
492 }
493 (Some(AtRulePrelude::SassExpr(input.alloc(expr))), None, end)
494 }
495 "extend" => {
496 let prelude = input.parse::<SassExtend>()?;
497 let end = prelude.span.end;
498 (Some(AtRulePrelude::SassExtend(input.alloc(prelude))), None, end)
499 }
500 "warn" | "error" | "debug" => {
501 input.eat_sass_line_continuation()?;
502 let expr = input.parse_maybe_sass_list(true)?;
503 let end = expr.span().end;
504 (Some(AtRulePrelude::SassExpr(input.alloc(expr))), None, end)
505 }
506 "forward" => {
507 let prelude = input.parse::<SassForward>()?;
508 let end = prelude.span.end;
509 (Some(AtRulePrelude::SassForward(input.alloc(prelude))), None, end)
510 }
511 "at-root" => {
512 let prelude = if !matches!(
513 input.cursor.peek()?.token,
514 Token::LBrace(..)
515 | Token::Indent(..)
516 | Token::Linebreak(..)
517 | Token::Dedent(..)
518 | Token::Eof(..)
519 ) {
520 Some(AtRulePrelude::SassAtRoot(input.parse()?))
521 } else {
522 None
523 };
524 let block = input
527 .with_state(ParserState {
528 in_keyframes_at_rule: false,
529 ..input.state.clone()
530 })
531 .parse::<SimpleBlock>()?;
532 let end = block.span.end;
533 (prelude, Some(block), end)
534 }
535 _ => {
536 let (prelude, block, end) = input.parse_unknown_at_rule()?;
537 (
538 prelude.map(|prelude| AtRulePrelude::Unknown(input.alloc(prelude))),
539 block,
540 end.unwrap_or(at_keyword_span.end),
541 )
542 }
543 }
544 } else {
545 let (prelude, block, end) = input.parse_unknown_at_rule()?;
546 (
547 prelude.map(|prelude| AtRulePrelude::Unknown(input.alloc(prelude))),
548 block,
549 end.unwrap_or(at_keyword_span.end),
550 )
551 };
552
553 let span = Span { start: at_keyword_span.start, end };
554 Ok(AtRule {
555 name: input.ident(
556 at_keyword.ident,
557 Span { start: at_keyword_span.start + 1, end: at_keyword_span.end },
558 ),
559 prelude,
560 block,
561 span,
562 })
563 }
564}
565
566impl<'a> Parser<'a> {
567 fn try_parse_full_prelude<T>(&mut self, f: impl FnOnce(&mut Self) -> PResult<T>) -> PResult<T> {
572 self.try_parse(|p| {
573 let value = f(p)?;
574 match &p.cursor.peek()?.token {
575 Token::LBrace(..)
576 | Token::Indent(..)
577 | Token::Semicolon(..)
578 | Token::Dedent(..)
579 | Token::Linebreak(..)
580 | Token::Eof(..) => Ok(value),
581 _ => {
582 let span = p.cursor.peek()?.span.clone();
583 Err(Error { kind: ErrorKind::TryParseError, span })
584 }
585 }
586 })
587 }
588
589 fn parse_raw_at_rule_prelude(&mut self) -> PResult<UnknownAtRulePrelude<'a>> {
594 let start = self.cursor.tokenizer.current_offset();
595 let mut tokens = self.vec();
596 let mut pairs: Vec<crate::util::PairedToken> = Vec::new();
597 loop {
598 match &self.cursor.peek()?.token {
599 Token::Semicolon(..)
600 | Token::Dedent(..)
601 | Token::Linebreak(..)
602 | Token::Indent(..)
603 | Token::Eof(..) => break,
604 Token::LBrace(..) if pairs.is_empty() => break,
605 Token::StrTemplate(..) => {
609 self.consume_str_template_tokens_into(&mut tokens)?;
610 continue;
611 }
612 token => {
613 if !crate::util::track_paired_token(token, &mut pairs) {
614 break;
615 }
616 }
617 }
618 tokens.push(self.cursor.bump()?);
619 }
620 let span = Span {
621 start: tokens.first().map_or(start, |token| token.span.start),
622 end: tokens.last().map_or(start, |token| token.span.end),
623 };
624 Ok(UnknownAtRulePrelude::TokenSeq(TokenSeq { tokens, span }))
625 }
626
627 pub(super) fn parse_unknown_at_rule(
628 &mut self,
629 ) -> PResult<(Option<UnknownAtRulePrelude<'a>>, Option<SimpleBlock<'a>>, Option<usize>)> {
630 let prelude = self.parse_unknown_at_rule_prelude()?;
631 let block = match &self.cursor.peek()?.token {
632 Token::LBrace(..) | Token::Indent(..) => {
636 let sass_ctx = if matches!(self.syntax, Syntax::Scss | Syntax::Sass) {
637 self.state.sass_ctx | super::state::SASS_CTX_ALLOW_KEYFRAME_BLOCK
638 } else {
639 self.state.sass_ctx
640 };
641 Some(
642 self.with_state(ParserState { sass_ctx, ..self.state.clone() })
643 .parse::<SimpleBlock>()?,
644 )
645 }
646 _ => None,
647 };
648 let end = block
649 .as_ref()
650 .map(|block| block.span.end)
651 .or_else(|| prelude.as_ref().map(|prelude| prelude.span().end));
652 Ok((prelude, block, end))
653 }
654
655 fn parse_unknown_at_rule_prelude(&mut self) -> PResult<Option<UnknownAtRulePrelude<'a>>> {
656 if let Ok(prelude) = self.try_parse(|parser| {
657 let mut tokens = parser.vec();
658 loop {
659 match &parser.cursor.peek()?.token {
660 Token::LBrace(..)
661 | Token::RBrace(..)
662 | Token::Semicolon(..)
663 | Token::Indent(..)
664 | Token::Dedent(..)
665 | Token::Linebreak(..)
666 | Token::Eof(..) => break,
667 Token::StrTemplate(..) | Token::HashLBrace(..) => {
668 return Err(Error {
669 kind: ErrorKind::TryParseError,
670 span: parser.cursor.bump()?.span,
671 });
672 }
673 _ => tokens.push(parser.cursor.bump()?),
674 }
675 }
676 if let Some((first, last)) = tokens.first().zip(tokens.last()) {
677 let span = Span { start: first.span.start, end: last.span.end };
678 Ok(Some(UnknownAtRulePrelude::TokenSeq(TokenSeq { tokens, span })))
679 } else {
680 Ok(None)
681 }
682 }) {
683 return Ok(prelude);
684 }
685
686 Ok(Some(UnknownAtRulePrelude::ComponentValue(match self.syntax {
687 Syntax::Css => self.parse()?,
688 Syntax::Scss | Syntax::Sass => {
689 self.parse_maybe_sass_list(true)?
690 }
691 Syntax::Less => self.parse_maybe_less_list(true)?,
692 })))
693 }
694}