Skip to content

Commit

Permalink
feat: allow parser to handle infinitely nested plugin segments
Browse files Browse the repository at this point in the history
  • Loading branch information
arlyon committed Feb 20, 2023
1 parent 472c431 commit 1b96169
Show file tree
Hide file tree
Showing 3 changed files with 27 additions and 14 deletions.
6 changes: 5 additions & 1 deletion crates/tailwind-parse-macro/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,9 +104,13 @@ pub fn parser(_attr: TokenStream, input: TokenStream) -> TokenStream {
let subcommands = subcommand.variants.iter().map(|v| {
let kebab = format(v, &kebab);
let sub_name = subcommand.ident.clone();
if let Some((l, _)) = kebab.split_once('-') {

let mut split = kebab.rsplit_once('-');
while let Some((l, _)) = split {
has_subsegments.insert(l.to_string());
split = l.rsplit_once('-');
}

let sub_ident = v.ident.clone();
if optional.is_some() {
quote! {
Expand Down
3 changes: 2 additions & 1 deletion crates/tailwind-parse/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ mod test {
use std::assert_matches::assert_matches;

use crate::{
Border, Css, Directive, Display, Expression, Literal, Max, Plugin, Position, Subject,
Border, Css, Directive, Display, Expression, Grid, Literal, Max, Plugin, Position, Subject,
SubjectValue, TextDecoration, Value,
};

Expand Down Expand Up @@ -104,6 +104,7 @@ mod test {
#[test_case("border-[min-content min-content]", Plugin::Border(None), Some(SubjectValue::Css(Css("min-content min-content"))) ; "when spaces are in arbitrary css")]
#[test_case("line-through", Plugin::TextDecoration(TextDecoration::LineThrough), None ; "when we have a transparent plugin")]
#[test_case("table-cell", Plugin::Display(Display::TableCell), None ; "do not eagerly parse")]
#[test_case("grid-flow-col", Plugin::Grid(Some(Grid::FlowCol)), None; "handles multiple words")]
fn plugin(s: &str, p: Plugin, v: Option<SubjectValue>) {
let (rest, s) = Subject::parse(LocatedSpan::new_extra(s, DUMMY_SP)).unwrap();
let lit = match s {
Expand Down
32 changes: 20 additions & 12 deletions crates/tailwind-parse/src/plugin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -411,24 +411,32 @@ mod plugin {
/// this code is ugly
pub fn parse(s: NomSpan<'a>) -> IResult<NomSpan<'a>, Self, nom::error::Error<NomSpan<'a>>> {
let parse_segment = || take_while1(|c| c != '-' && c != ' ' && c != '[' && c != '!');

let (rest, segment) = parse_segment()(s)?;

// attempt to take the next one and parse
// this currently only goes one layer deep,
// but we may need to make this recursive
if Plugin::has_subsegments(&segment) && let Ok((rest, subsegment)) = preceded(tag("-"), parse_segment())(rest) {
let plugin_span = s
.slice(..subsegment.location_offset() + subsegment.len() - s.location_offset());
if let Ok(p) = plugin_span.parse::<Plugin>() {
return Ok((rest, p));
let next_segment = || preceded(tag("-"), parse_segment());

let (mut rest, mut segment) = parse_segment()(s)?;
let (mut prev_rest, mut prev_segment) = (rest, segment);

while Plugin::has_subsegments(&segment) {
if let Ok((rest2, subsegment)) = next_segment()(rest) {
prev_segment = segment;
prev_rest = rest;

rest = rest2;
segment = s.slice(
..subsegment.location_offset() + subsegment.len()
- segment.location_offset(),
);
} else {
break;
}
}

// try and parse a plugin and if there is no subcommand, return it
// attempt to parse a plugin, instead parsing the previous segment if it fails
// this is to allow for plugins with subcommands to be parsed
let plugin_parse = segment
.parse::<Plugin>()
.map(|p| (rest, p))
.or_else(|_| prev_segment.parse::<Plugin>().map(|p| (prev_rest, p)))
.map_err(|_| nom::Err::Error(Error::new(segment, nom::error::ErrorKind::Tag)));

if !plugin_parse
Expand Down

0 comments on commit 1b96169

Please sign in to comment.