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