Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stylo: Add support for grid-template-{rows,columns} #16067

Merged
merged 12 commits into from May 18, 2017

Add parsing/serialization for repeat() function

  • Loading branch information
wafflespeanut committed May 18, 2017
commit 81b4e64dfc8af9d0c2bbfd8e52e3421ee694f7e0
@@ -4,7 +4,7 @@

//! Necessary types for [grid](https://drafts.csswg.org/css-grid/).

use cssparser::{Parser, Token};
use cssparser::{Parser, Token, serialize_identifier};
use parser::{Parse, ParserContext};
use std::ascii::AsciiExt;
use std::fmt;
@@ -294,7 +294,7 @@ impl<L: ToCss> ToCss for TrackSize<L> {
TrackSize::MinMax(ref infexible, ref flexible) => {
try!(dest.write_str("minmax("));
try!(infexible.to_css(dest));
try!(dest.write_str(","));
try!(dest.write_str(", "));
try!(flexible.to_css(dest));
dest.write_str(")")
},
@@ -364,6 +364,24 @@ pub fn parse_line_names(input: &mut Parser) -> Result<Vec<String>, ()> {
})
}

fn concat_serialize_idents<W>(prefix: &str, suffix: &str,
slice: &[String], sep: &str, dest: &mut W) -> fmt::Result
where W: fmt::Write
{
if let Some((ref first, rest)) = slice.split_first() {
dest.write_str(prefix)?;
serialize_identifier(first, dest)?;
for thing in rest {
dest.write_str(sep)?;
serialize_identifier(thing, dest)?;
}

dest.write_str(suffix)?;
}

Ok(())
}

/// The initial argument of the `repeat` function.
///
/// https://drafts.csswg.org/css-grid/#typedef-track-repeat
@@ -408,3 +426,125 @@ impl ToCss for RepeatCount {

impl ComputedValueAsSpecified for RepeatCount {}
no_viewport_percentage!(RepeatCount);

/// The type of `repeat` function (only used in parsing).
///
/// https://drafts.csswg.org/css-grid/#typedef-track-repeat
#[derive(Clone, Copy, PartialEq, Debug)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
enum RepeatType {
/// [`<auto-repeat>`](https://drafts.csswg.org/css-grid/#typedef-auto-repeat)
Auto,
/// [`<track-repeat>`](https://drafts.csswg.org/css-grid/#typedef-track-repeat)
Normal,

This comment has been minimized.

@canova

canova Apr 11, 2017

Member

Links of these two variants looks opposite. Aren't they?

This comment has been minimized.

@wafflespeanut

wafflespeanut Apr 11, 2017

Author Member

Good catch! :)

/// [`<fixed-repeat>`](https://drafts.csswg.org/css-grid/#typedef-fixed-repeat)
Fixed,
}

/// The structure containing `<line-names>` and `<track-size>` values.
///
/// It can also hold `repeat()` function parameters, which expands into the respective
/// values in its computed form.
#[derive(Clone, PartialEq, Debug)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct TrackRepeat<L> {
/// The number of times for the value to be repeated (could also be `auto-fit` or `auto-fill`)
pub count: RepeatCount,
/// `<line-names>` accompanying `<track_size>` values.
///
/// If there's no `<line-names>`, then it's represented by an empty vector.
/// For N `<track-size>` values, there will be N+1 `<line-names>`, and so this vector's
/// length is always one value more than that of the `<track-size>`.
pub line_names: Vec<Vec<String>>,
/// `<track-size>` values.
pub track_sizes: Vec<TrackSize<L>>,
}

impl TrackRepeat<LengthOrPercentage> {
fn parse_with_repeat_type(context: &ParserContext, input: &mut Parser)
-> Result<(TrackRepeat<LengthOrPercentage>, RepeatType), ()> {
input.try(|i| i.expect_function_matching("repeat")).and_then(|_| {
input.parse_nested_block(|input| {
let count = RepeatCount::parse(context, input)?;
input.expect_comma()?;

let is_auto = count == RepeatCount::AutoFit || count == RepeatCount::AutoFill;
let mut repeat_type = if is_auto {
RepeatType::Auto
} else { // <fixed-size> is a subset of <track_size>, so it should work for both
RepeatType::Fixed
};

let mut names = vec![];
let mut values = vec![];
let mut current_names;

loop {
current_names = input.try(parse_line_names).unwrap_or(vec![]);
if let Ok(track_size) = input.try(|i| TrackSize::parse(context, i)) {
if !track_size.is_fixed() {
if is_auto {
return Err(()) // should be <fixed-size> for <auto-repeat>
}

if repeat_type == RepeatType::Fixed {
repeat_type = RepeatType::Normal // <track-size> for sure
}
}

values.push(track_size);
names.push(current_names);
} else {
if values.is_empty() {
return Err(()) // expecting at least one <track-size>
}

names.push(current_names); // final `<line-names>`
break // no more <track-size>, breaking
}
}

let repeat = TrackRepeat {
count: count,
track_sizes: values,
line_names: names,
};

Ok((repeat, repeat_type))
})
})
}
}

impl<L: ToCss> ToCss for TrackRepeat<L> {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
dest.write_str("repeat(")?;
self.count.to_css(dest)?;
dest.write_str(", ")?;

let mut line_names_iter = self.line_names.iter();
for (i, (ref size, ref names)) in self.track_sizes.iter()
.zip(&mut line_names_iter).enumerate() {
if i > 0 {
dest.write_str(" ")?;
}

concat_serialize_idents("[", "] ", names, " ", dest)?;
size.to_css(dest)?;
}

if let Some(line_names_last) = line_names_iter.next() {
concat_serialize_idents(" [", "]", line_names_last, " ", dest)?;
}

dest.write_str(")")?;
Ok(())
}
}

impl HasViewportPercentage for TrackRepeat<LengthOrPercentage> {
#[inline]
fn has_viewport_percentage(&self) -> bool {
self.track_sizes.iter().any(|ref v| v.has_viewport_percentage())
}
}
@@ -74,6 +74,7 @@ macro_rules! impl_to_css_for_predefined_type {

impl_to_css_for_predefined_type!(f32);
impl_to_css_for_predefined_type!(i32);
impl_to_css_for_predefined_type!(u16);
impl_to_css_for_predefined_type!(u32);
impl_to_css_for_predefined_type!(::cssparser::Token<'a>);
impl_to_css_for_predefined_type!(::cssparser::RGBA);
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.