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