<commit_before>use memory::Memory;
use std::boxed::Box;
use std::vec::Vec;

/// Simple slice-based memory implementation.
///
/// # Examples
///
/// ```
/// use osciemu::memory::Memory;
/// use osciemu::memory::SliceMemory;
///
/// let mut m = SliceMemory::new(16);
/// assert_eq!(m.get(0), 0);
/// m.set(8, 123);
/// assert_eq!(m.get(8), 123);
/// ```
///
/// To construct a `SliceMemory` from a `Vec`, use `into_boxed_slice()`.
///
/// ```
/// # use osciemu::memory::{Memory, SliceMemory};
///
/// let v : Vec<i32> = vec![0, 0xFFFFFFFF];
/// let m = SliceMemory(v.into_boxed_slice());
/// assert_eq!(m.get(0), 0);
/// assert_eq!(m.get(1), 0xFFFFFFFF);
/// ```
pub struct SliceMemory(pub Box<[i32]>);

impl SliceMemory {
    /// Creates a new `SliceMemory`.
    pub fn new(size: usize) -> SliceMemory {
        let mut v = Vec::with_capacity(size);
        for _ in 0..size {
            v.push(0)
        }
        SliceMemory(v.into_boxed_slice())
    }

    /// Initializes the new memory with the given `i32` slice data.
    pub fn from_slice(data: Box<[i32]>) -> SliceMemory {
        SliceMemory(data)
    }
}

impl Memory for SliceMemory {
    fn get(&self, addr: usize) -> i32 {
        self.0[addr]
    }

    fn set(&mut self, addr: usize, v: i32) {
        self.0[addr] = v;
    }

    fn size(&self) -> usize {
        self.0.len()
    }
}

#[cfg(test)]
mod tests {
    use memory::Memory;

    #[test]
    fn get_and_set() {
        let mut m = super::SliceMemory::new(16);
        for i in 0..4 {
            m.set(i, i as i32);
        }
        for i in 0..4 {
            assert_eq!(m.get(i), i as i32);
        }
    }

    #[test]
    fn from_slice() {
        let s = Box::new([0, 1, 2, 3, 4, 5, 6, 7]);
        let m = super::SliceMemory::from_slice(s);
        assert_eq!(m.get(0), 0);
        assert_eq!(m.get(4), 4);
    }

    #[test]
    fn size() {
        let m = super::SliceMemory::new(16);
        assert_eq!(m.size(), 16);

        let m = super::SliceMemory::from_slice(Box::new([1, 2, 3, 4]));
        assert_eq!(m.size(), 4);
    }
}
<commit_msg>docs(slicememory): Update documentation<commit_after>//! Use a slice as memory.
use memory::Memory;
use std::boxed::Box;
use std::vec::Vec;

/// Simple slice-based memory implementation.
///
/// # Examples
///
/// ```
/// use osciemu::memory::Memory;
/// use osciemu::memory::SliceMemory;
///
/// let mut m = SliceMemory::new(16);
/// assert_eq!(m.get(0), 0);
/// m.set(8, 123);
/// assert_eq!(m.get(8), 123);
/// ```
///
/// To construct a `SliceMemory` from a `Vec`, use `into_boxed_slice()`.
///
/// ```
/// # use osciemu::memory::{Memory, SliceMemory};
///
/// let v : Vec<i32> = vec![0, 0xFFFFFFFF];
/// let m = SliceMemory(v.into_boxed_slice());
/// assert_eq!(m.get(0), 0);
/// assert_eq!(m.get(1), 0xFFFFFFFF);
/// ```
pub struct SliceMemory(pub Box<[i32]>);

impl SliceMemory {
    /// Creates a new `SliceMemory`.
    pub fn new(size: usize) -> SliceMemory {
        let mut v = Vec::with_capacity(size);
        for _ in 0..size {
            v.push(0)
        }
        SliceMemory(v.into_boxed_slice())
    }

    /// Initializes the new memory with the given `i32` slice.
    pub fn from_slice(data: Box<[i32]>) -> SliceMemory {
        SliceMemory(data)
    }
}

impl Memory for SliceMemory {
    fn get(&self, addr: usize) -> i32 {
        self.0[addr]
    }

    fn set(&mut self, addr: usize, v: i32) {
        self.0[addr] = v;
    }

    fn size(&self) -> usize {
        self.0.len()
    }
}

#[cfg(test)]
mod tests {
    use memory::Memory;

    #[test]
    fn get_and_set() {
        let mut m = super::SliceMemory::new(16);
        for i in 0..4 {
            m.set(i, i as i32);
        }
        for i in 0..4 {
            assert_eq!(m.get(i), i as i32);
        }
    }

    #[test]
    fn from_slice() {
        let s = Box::new([0, 1, 2, 3, 4, 5, 6, 7]);
        let m = super::SliceMemory::from_slice(s);
        assert_eq!(m.get(0), 0);
        assert_eq!(m.get(4), 4);
    }

    #[test]
    fn size() {
        let m = super::SliceMemory::new(16);
        assert_eq!(m.size(), 16);

        let m = super::SliceMemory::from_slice(Box::new([1, 2, 3, 4]));
        assert_eq!(m.size(), 4);
    }
}
<|endoftext|>


<commit_before>//! List merge requests
//!
//! [https://docs.gitlab.com/ce/api/merge\_requests.html#list-merge-requests](https://docs.gitlab.com/ce/api/merge_requests.html#list-merge-requests)
//!
//! # List merge requests
//!
//! Get all merge requests for this project.
//! The `state` parameter can be used to get only merge requests with a given state (`opened`, `closed`, or `merged`) or all of them (`all`).
//! The pagination parameters `page` and `per_page` can be used to restrict the list of merge requests.
//!
//! ```text
//! GET /projects/ID/merge_requests
//! GET /projects/ID/merge_requests?state=opened
//! GET /projects/ID/merge_requests?state=all
//! GET /projects/ID/merge_requests?iid=42
//! GET /projects/ID/merge_requests?iid[]=42&iid[]=43
//! ```
//!
//! Parameters:
//!
//! - `id` (required) - The ID of a project
//! - `iid` (optional) - Return the request having the given `iid`
//! - `state` (optional) - Return `all` requests or just those that are `merged`, `opened` or `closed`
//! - `order_by` (optional) - Return requests ordered by `created_at` or `updated_at` fields. Default is `created_at`
//! - `sort` (optional) - Return requests sorted in `asc` or `desc` order. Default is `desc`
//!
//!

// use serde_urlencoded;

use BuildQuery;

pub mod single;

use ::errors::*;

#[cfg(feature = "serde_derive")]
include!("serde_types.in.rs");

#[cfg(feature = "serde_codegen")]
include!(concat!(env!("OUT_DIR"), "/merge_requests/serde_types.rs"));

#[derive(Debug, Clone)]
pub struct MergeRequestsLister<'a> {
    gl: &'a ::GitLab,
    id: i64,
    internal: MergeRequestsListerInternal,
}

#[allow(dead_code)]
impl<'a> MergeRequestsLister<'a> {
    pub fn new(gl: &'a ::GitLab, id: i64) -> MergeRequestsLister {
        MergeRequestsLister {
            gl: gl,
            id: id,
            internal: MergeRequestsListerInternal {
                iid: None,
                state: None,
                order_by: None,
                sort: None,
            },
        }
    }

    pub fn single(self, merge_request_id: i64) -> single::MergeRequestLister<'a> {
        // assert_eq!(self, MergeRequestLister::new(self.gl));
        single::MergeRequestLister::new(self.gl, self.id, merge_request_id)
    }

    pub fn iid(&'a mut self, iid: Vec<i64>) -> &'a mut MergeRequestsLister {
        info!("Using 'idd' fails when there is more than one element!");
        if iid.len() > 1 {
            println!("Using 'idd' fails when there is more than one element!");
        }
        self.internal.iid = Some(iid);
        self
    }
    pub fn state(&'a mut self, state: State) -> &'a mut MergeRequestsLister {
        self.internal.state = Some(state);
        self
    }
    pub fn order_by(&'a mut self, order_by: ListingOrderBy) -> &'a mut MergeRequestsLister {
        self.internal.order_by = Some(order_by);
        self
    }
    fn sort(&'a mut self, sort: ::ListingSort) -> &'a mut MergeRequestsLister {
        self.internal.sort = Some(sort);
        self
    }

    /// Commit the lister: Query GitLab and return a list of projects.
    pub fn list(&self) -> Result<MergeRequests> {
        let query = self.build_query();
        debug!("query: {:?}", query);

        self.gl.get(&query).chain_err(|| format!("cannot get query {}", query))
    }
}

impl<'a> BuildQuery for MergeRequestsLister<'a> {
    fn build_query(&self) -> String {

        // NOTE: Can't use `serde_urlencoded` since it cannot serialize a Vec<T>
        //       See https://github.com/nox/serde_urlencoded/issues/6
        // let encoded = serde_urlencoded::to_string(&self.internal).unwrap();

        let mut query = format!("projects/{}/merge_requests", self.id);

        let amp_char = "&";
        let none_char = "";
        let mut split_char = &none_char;

        // Append a "?" only if at least one of the `Option` is `Some(_)` or if
        // strings contain something.
        query.push_str(match (&self.internal.iid,
                              &self.internal.state,
                              &self.internal.order_by,
                              &self.internal.sort) {
            (&None, &None, &None, &None) => "",
            _ => "?",
        });

        self.internal.iid.as_ref().map(|iid| {
            query.push_str(split_char);
            split_char = &amp_char;

            if iid.len() == 1 {
                query.push_str("iid=");
                query.push_str(&iid[0].to_string());
            } else {
                let mut array_split_char = &none_char;
                for iid in iid {
                    query.push_str(array_split_char);
                    query.push_str("iid[]=");
                    query.push_str(&iid.to_string());
                    array_split_char = &amp_char;
                }
            }
        });

        self.internal.state.map(|state| {
            query.push_str(split_char);
            split_char = &amp_char;

            query.push_str("state=");
            query.push_str(match state {
                State::Merged => "merged",
                State::Opened => "opened",
                State::Closed => "closed",
                State::All => "all",
            });
        });

        self.internal.order_by.map(|order_by| {
            query.push_str(split_char);
            split_char = &amp_char;

            query.push_str("order_by=");
            query.push_str(match order_by {
                ListingOrderBy::CreatedAt => "created_at",
                ListingOrderBy::UpdatedAt => "updated_at",
            });
        });

        self.internal.sort.map(|sort| {
            query.push_str(split_char);
            split_char = &amp_char;

            query.push_str("sort=");
            query.push_str(match sort {
                ::ListingSort::Asc => "asc",
                ::ListingSort::Desc => "desc",
            });
        });

        query
    }
}

#[cfg(test)]
mod tests {
    use BuildQuery;

    const TEST_PROJECT_ID: i64 = 123;

    #[test]
    fn build_query_default() {
        let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap();
        // let gl: ::GitLab = Default::default();

        let expected_string = format!("projects/{}/merge_requests", TEST_PROJECT_ID);
        let lister = gl.merge_requests(TEST_PROJECT_ID);
        let query = lister.build_query();
        assert_eq!(query, expected_string);

        let expected_string = format!("projects/{}/merge_requests", TEST_PROJECT_ID);
        let query = gl.merge_requests(TEST_PROJECT_ID).build_query();
        assert_eq!(query, expected_string);
    }

    #[test]
    fn build_query_iid() {
        let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap();
        // let gl: ::GitLab = Default::default();

        let expected_string = format!("projects/{}/merge_requests?iid=456", TEST_PROJECT_ID);
        let query = gl.merge_requests(TEST_PROJECT_ID).iid(vec![456]).build_query();
        assert_eq!(query, expected_string);

        let expected_string = format!("projects/123/merge_requests?iid[]=456&iid[]=789");
        let query = gl.merge_requests(TEST_PROJECT_ID).iid(vec![456, 789]).build_query();
        assert_eq!(query, expected_string);
    }

    #[test]
    fn build_query_state() {
        let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap();
        // let gl: ::GitLab = Default::default();

        let expected_string = format!("projects/{}/merge_requests?state=opened", TEST_PROJECT_ID);
        let query =
            gl.merge_requests(TEST_PROJECT_ID).state(::merge_requests::State::Opened).build_query();
        assert_eq!(query, expected_string);

        let expected_string = format!("projects/{}/merge_requests?state=closed", TEST_PROJECT_ID);
        let query =
            gl.merge_requests(TEST_PROJECT_ID).state(::merge_requests::State::Closed).build_query();
        assert_eq!(query, expected_string);
    }

    #[test]
    fn build_query_order_by() {
        let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap();
        // let gl: ::GitLab = Default::default();

        let expected_string = format!("projects/{}/merge_requests?order_by=created_at",
                                      TEST_PROJECT_ID);
        let query = gl.merge_requests(TEST_PROJECT_ID)
            .order_by(::merge_requests::ListingOrderBy::CreatedAt)
            .build_query();
        assert_eq!(query, expected_string);

        let expected_string = format!("projects/{}/merge_requests?order_by=updated_at",
                                      TEST_PROJECT_ID);
        let query = gl.merge_requests(TEST_PROJECT_ID)
            .order_by(::merge_requests::ListingOrderBy::UpdatedAt)
            .build_query();
        assert_eq!(query, expected_string);
    }

    #[test]
    fn build_query_sort() {
        let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap();
        // let gl: ::GitLab = Default::default();

        let expected_string = format!("projects/{}/merge_requests?sort=asc", TEST_PROJECT_ID);
        let query = gl.merge_requests(TEST_PROJECT_ID).sort(::ListingSort::Asc).build_query();
        assert_eq!(query, expected_string);

        let expected_string = format!("projects/{}/merge_requests?sort=desc", TEST_PROJECT_ID);
        let query = gl.merge_requests(TEST_PROJECT_ID).sort(::ListingSort::Desc).build_query();
        assert_eq!(query, expected_string);
    }

    #[test]
    fn build_query_multiple() {
        let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap();
        // let gl: ::GitLab = Default::default();

        let expected_string = format!("projects/{}/merge_requests?iid[]=456&iid[]=789&order_by=created_at&sort=asc",
                                      TEST_PROJECT_ID);
        let query = gl.merge_requests(TEST_PROJECT_ID)
            .iid(vec![456, 789])
            .sort(::ListingSort::Asc)
            .order_by(::merge_requests::ListingOrderBy::CreatedAt)
            .build_query();
        assert_eq!(query, expected_string);
    }
}
<commit_msg>Let `merge_requests::tests::build_query_state()` test `Merged` and `All` too<commit_after>//! List merge requests
//!
//! [https://docs.gitlab.com/ce/api/merge\_requests.html#list-merge-requests](https://docs.gitlab.com/ce/api/merge_requests.html#list-merge-requests)
//!
//! # List merge requests
//!
//! Get all merge requests for this project.
//! The `state` parameter can be used to get only merge requests with a given state (`opened`, `closed`, or `merged`) or all of them (`all`).
//! The pagination parameters `page` and `per_page` can be used to restrict the list of merge requests.
//!
//! ```text
//! GET /projects/ID/merge_requests
//! GET /projects/ID/merge_requests?state=opened
//! GET /projects/ID/merge_requests?state=all
//! GET /projects/ID/merge_requests?iid=42
//! GET /projects/ID/merge_requests?iid[]=42&iid[]=43
//! ```
//!
//! Parameters:
//!
//! - `id` (required) - The ID of a project
//! - `iid` (optional) - Return the request having the given `iid`
//! - `state` (optional) - Return `all` requests or just those that are `merged`, `opened` or `closed`
//! - `order_by` (optional) - Return requests ordered by `created_at` or `updated_at` fields. Default is `created_at`
//! - `sort` (optional) - Return requests sorted in `asc` or `desc` order. Default is `desc`
//!
//!

// use serde_urlencoded;

use BuildQuery;

pub mod single;

use ::errors::*;

#[cfg(feature = "serde_derive")]
include!("serde_types.in.rs");

#[cfg(feature = "serde_codegen")]
include!(concat!(env!("OUT_DIR"), "/merge_requests/serde_types.rs"));

#[derive(Debug, Clone)]
pub struct MergeRequestsLister<'a> {
    gl: &'a ::GitLab,
    id: i64,
    internal: MergeRequestsListerInternal,
}

#[allow(dead_code)]
impl<'a> MergeRequestsLister<'a> {
    pub fn new(gl: &'a ::GitLab, id: i64) -> MergeRequestsLister {
        MergeRequestsLister {
            gl: gl,
            id: id,
            internal: MergeRequestsListerInternal {
                iid: None,
                state: None,
                order_by: None,
                sort: None,
            },
        }
    }

    pub fn single(self, merge_request_id: i64) -> single::MergeRequestLister<'a> {
        // assert_eq!(self, MergeRequestLister::new(self.gl));
        single::MergeRequestLister::new(self.gl, self.id, merge_request_id)
    }

    pub fn iid(&'a mut self, iid: Vec<i64>) -> &'a mut MergeRequestsLister {
        info!("Using 'idd' fails when there is more than one element!");
        if iid.len() > 1 {
            println!("Using 'idd' fails when there is more than one element!");
        }
        self.internal.iid = Some(iid);
        self
    }
    pub fn state(&'a mut self, state: State) -> &'a mut MergeRequestsLister {
        self.internal.state = Some(state);
        self
    }
    pub fn order_by(&'a mut self, order_by: ListingOrderBy) -> &'a mut MergeRequestsLister {
        self.internal.order_by = Some(order_by);
        self
    }
    fn sort(&'a mut self, sort: ::ListingSort) -> &'a mut MergeRequestsLister {
        self.internal.sort = Some(sort);
        self
    }

    /// Commit the lister: Query GitLab and return a list of projects.
    pub fn list(&self) -> Result<MergeRequests> {
        let query = self.build_query();
        debug!("query: {:?}", query);

        self.gl.get(&query).chain_err(|| format!("cannot get query {}", query))
    }
}

impl<'a> BuildQuery for MergeRequestsLister<'a> {
    fn build_query(&self) -> String {

        // NOTE: Can't use `serde_urlencoded` since it cannot serialize a Vec<T>
        //       See https://github.com/nox/serde_urlencoded/issues/6
        // let encoded = serde_urlencoded::to_string(&self.internal).unwrap();

        let mut query = format!("projects/{}/merge_requests", self.id);

        let amp_char = "&";
        let none_char = "";
        let mut split_char = &none_char;

        // Append a "?" only if at least one of the `Option` is `Some(_)` or if
        // strings contain something.
        query.push_str(match (&self.internal.iid,
                              &self.internal.state,
                              &self.internal.order_by,
                              &self.internal.sort) {
            (&None, &None, &None, &None) => "",
            _ => "?",
        });

        self.internal.iid.as_ref().map(|iid| {
            query.push_str(split_char);
            split_char = &amp_char;

            if iid.len() == 1 {
                query.push_str("iid=");
                query.push_str(&iid[0].to_string());
            } else {
                let mut array_split_char = &none_char;
                for iid in iid {
                    query.push_str(array_split_char);
                    query.push_str("iid[]=");
                    query.push_str(&iid.to_string());
                    array_split_char = &amp_char;
                }
            }
        });

        self.internal.state.map(|state| {
            query.push_str(split_char);
            split_char = &amp_char;

            query.push_str("state=");
            query.push_str(match state {
                State::Merged => "merged",
                State::Opened => "opened",
                State::Closed => "closed",
                State::All => "all",
            });
        });

        self.internal.order_by.map(|order_by| {
            query.push_str(split_char);
            split_char = &amp_char;

            query.push_str("order_by=");
            query.push_str(match order_by {
                ListingOrderBy::CreatedAt => "created_at",
                ListingOrderBy::UpdatedAt => "updated_at",
            });
        });

        self.internal.sort.map(|sort| {
            query.push_str(split_char);
            split_char = &amp_char;

            query.push_str("sort=");
            query.push_str(match sort {
                ::ListingSort::Asc => "asc",
                ::ListingSort::Desc => "desc",
            });
        });

        query
    }
}

#[cfg(test)]
mod tests {
    use BuildQuery;

    const TEST_PROJECT_ID: i64 = 123;

    #[test]
    fn build_query_default() {
        let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap();
        // let gl: ::GitLab = Default::default();

        let expected_string = format!("projects/{}/merge_requests", TEST_PROJECT_ID);
        let lister = gl.merge_requests(TEST_PROJECT_ID);
        let query = lister.build_query();
        assert_eq!(query, expected_string);

        let expected_string = format!("projects/{}/merge_requests", TEST_PROJECT_ID);
        let query = gl.merge_requests(TEST_PROJECT_ID).build_query();
        assert_eq!(query, expected_string);
    }

    #[test]
    fn build_query_iid() {
        let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap();
        // let gl: ::GitLab = Default::default();

        let expected_string = format!("projects/{}/merge_requests?iid=456", TEST_PROJECT_ID);
        let query = gl.merge_requests(TEST_PROJECT_ID).iid(vec![456]).build_query();
        assert_eq!(query, expected_string);

        let expected_string = format!("projects/123/merge_requests?iid[]=456&iid[]=789");
        let query = gl.merge_requests(TEST_PROJECT_ID).iid(vec![456, 789]).build_query();
        assert_eq!(query, expected_string);
    }

    #[test]
    fn build_query_state() {
        let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap();
        // let gl: ::GitLab = Default::default();

        let expected_string = format!("projects/{}/merge_requests?state=merged", TEST_PROJECT_ID);
        let query =
            gl.merge_requests(TEST_PROJECT_ID).state(::merge_requests::State::Merged).build_query();
        assert_eq!(query, expected_string);

        let expected_string = format!("projects/{}/merge_requests?state=opened", TEST_PROJECT_ID);
        let query =
            gl.merge_requests(TEST_PROJECT_ID).state(::merge_requests::State::Opened).build_query();
        assert_eq!(query, expected_string);

        let expected_string = format!("projects/{}/merge_requests?state=closed", TEST_PROJECT_ID);
        let query =
            gl.merge_requests(TEST_PROJECT_ID).state(::merge_requests::State::Closed).build_query();
        assert_eq!(query, expected_string);

        let expected_string = format!("projects/{}/merge_requests?state=all", TEST_PROJECT_ID);
        let query =
            gl.merge_requests(TEST_PROJECT_ID).state(::merge_requests::State::All).build_query();
        assert_eq!(query, expected_string);
    }

    #[test]
    fn build_query_order_by() {
        let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap();
        // let gl: ::GitLab = Default::default();

        let expected_string = format!("projects/{}/merge_requests?order_by=created_at",
                                      TEST_PROJECT_ID);
        let query = gl.merge_requests(TEST_PROJECT_ID)
            .order_by(::merge_requests::ListingOrderBy::CreatedAt)
            .build_query();
        assert_eq!(query, expected_string);

        let expected_string = format!("projects/{}/merge_requests?order_by=updated_at",
                                      TEST_PROJECT_ID);
        let query = gl.merge_requests(TEST_PROJECT_ID)
            .order_by(::merge_requests::ListingOrderBy::UpdatedAt)
            .build_query();
        assert_eq!(query, expected_string);
    }

    #[test]
    fn build_query_sort() {
        let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap();
        // let gl: ::GitLab = Default::default();

        let expected_string = format!("projects/{}/merge_requests?sort=asc", TEST_PROJECT_ID);
        let query = gl.merge_requests(TEST_PROJECT_ID).sort(::ListingSort::Asc).build_query();
        assert_eq!(query, expected_string);

        let expected_string = format!("projects/{}/merge_requests?sort=desc", TEST_PROJECT_ID);
        let query = gl.merge_requests(TEST_PROJECT_ID).sort(::ListingSort::Desc).build_query();
        assert_eq!(query, expected_string);
    }

    #[test]
    fn build_query_multiple() {
        let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap();
        // let gl: ::GitLab = Default::default();

        let expected_string = format!("projects/{}/merge_requests?iid[]=456&iid[]=789&order_by=created_at&sort=asc",
                                      TEST_PROJECT_ID);
        let query = gl.merge_requests(TEST_PROJECT_ID)
            .iid(vec![456, 789])
            .sort(::ListingSort::Asc)
            .order_by(::merge_requests::ListingOrderBy::CreatedAt)
            .build_query();
        assert_eq!(query, expected_string);
    }
}
<|endoftext|>


<commit_before>mod atom;

pub(crate) use self::atom::match_arm_list;
pub(super) use self::atom::{literal, LITERAL_FIRST};
use super::*;

const EXPR_FIRST: TokenSet = LHS_FIRST;

pub(super) fn expr(p: &mut Parser) -> BlockLike {
    let r = Restrictions {
        forbid_structs: false,
        prefer_stmt: false,
    };
    expr_bp(p, r, 1)
}

pub(super) fn expr_stmt(p: &mut Parser) -> BlockLike {
    let r = Restrictions {
        forbid_structs: false,
        prefer_stmt: true,
    };
    expr_bp(p, r, 1)
}

fn expr_no_struct(p: &mut Parser) {
    let r = Restrictions {
        forbid_structs: true,
        prefer_stmt: false,
    };
    expr_bp(p, r, 1);
}

// test block
// fn a() {}
// fn b() { let _ = 1; }
// fn c() { 1; 2; }
// fn d() { 1; 2 }
pub(crate) fn block(p: &mut Parser) {
    if !p.at(L_CURLY) {
        p.error("expected a block");
        return;
    }
    let m = p.start();
    p.bump();
    // test nocontentexpr
    // fn foo(){
    //     ;;;some_expr();;;;{;;;};;;;Ok(())
    // }
    while p.eat(SEMI) {}
    while !p.at(EOF) && !p.at(R_CURLY) {
        match p.current() {
            LET_KW => let_stmt(p),
            _ => {
                // test block_items
                // fn a() { fn b() {} }
                let m = p.start();
                match items::maybe_item(p, items::ItemFlavor::Mod) {
                    items::MaybeItem::Item(kind) => {
                        m.complete(p, kind);
                    }
                    items::MaybeItem::Modifiers => {
                        m.abandon(p);
                        p.error("expected an item");
                    }
                    // test pub_expr
                    // fn foo() { pub 92; } //FIXME
                    items::MaybeItem::None => {
                        let is_blocklike = expressions::expr_stmt(p) == BlockLike::Block;
                        if p.at(R_CURLY) {
                            m.abandon(p);
                        } else {
                            // test no_semi_after_block
                            // fn foo() {
                            //     if true {}
                            //     loop {}
                            //     match () {}
                            //     while true {}
                            //     for _ in () {}
                            //     {}
                            //     {}
                            //     macro_rules! test {
                            //          () => {}
                            //     }
                            //     test!{}
                            // }
                            if is_blocklike {
                                p.eat(SEMI);
                            } else {
                                p.expect(SEMI);
                            }
                            m.complete(p, EXPR_STMT);
                        }
                    }
                }
            }
        }
        while p.eat(SEMI) {}
    }
    p.expect(R_CURLY);
    m.complete(p, BLOCK);

    // test let_stmt;
    // fn foo() {
    //     let a;
    //     let b: i32;
    //     let c = 92;
    //     let d: i32 = 92;
    // }
    fn let_stmt(p: &mut Parser) {
        assert!(p.at(LET_KW));
        let m = p.start();
        p.bump();
        patterns::pattern(p);
        if p.at(COLON) {
            types::ascription(p);
        }
        if p.eat(EQ) {
            expressions::expr(p);
        }
        p.expect(SEMI);
        m.complete(p, LET_STMT);
    }
}

#[derive(Clone, Copy)]
struct Restrictions {
    forbid_structs: bool,
    prefer_stmt: bool,
}

enum Op {
    Simple,
    Composite(SyntaxKind, u8),
}

fn current_op(p: &Parser) -> (u8, Op) {
    if let Some(t) = p.next3() {
        match t {
            (L_ANGLE, L_ANGLE, EQ) => return (1, Op::Composite(SHLEQ, 3)),
            (R_ANGLE, R_ANGLE, EQ) => return (1, Op::Composite(SHREQ, 3)),
            _ => (),
        }
    }

    if let Some(t) = p.next2() {
        match t {
            (PLUS, EQ) => return (1, Op::Composite(PLUSEQ, 2)),
            (MINUS, EQ) => return (1, Op::Composite(MINUSEQ, 2)),
            (STAR, EQ) => return (1, Op::Composite(STAREQ, 2)),
            (SLASH, EQ) => return (1, Op::Composite(SLASHEQ, 2)),
            (PIPE, EQ) => return (1, Op::Composite(PIPEEQ, 2)),
            (AMP, EQ) => return (1, Op::Composite(AMPEQ, 2)),
            (CARET, EQ) => return (1, Op::Composite(CARETEQ, 2)),
            (PIPE, PIPE) => return (3, Op::Composite(PIPEPIPE, 2)),
            (AMP, AMP) => return (4, Op::Composite(AMPAMP, 2)),
            (L_ANGLE, EQ) => return (5, Op::Composite(LTEQ, 2)),
            (R_ANGLE, EQ) => return (5, Op::Composite(GTEQ, 2)),
            (L_ANGLE, L_ANGLE) => return (9, Op::Composite(SHL, 2)),
            (R_ANGLE, R_ANGLE) => return (9, Op::Composite(SHR, 2)),
            _ => (),
        }
    }

    let bp = match p.current() {
        EQ => 1,
        DOTDOT | DOTDOTEQ => 2,
        EQEQ | NEQ | L_ANGLE | R_ANGLE => 5,
        PIPE => 6,
        CARET => 7,
        AMP => 8,
        MINUS | PLUS => 10,
        STAR | SLASH | PERCENT => 11,
        _ => 0,
    };
    (bp, Op::Simple)
}

// Parses expression with binding power of at least bp.
fn expr_bp(p: &mut Parser, r: Restrictions, bp: u8) -> BlockLike {
    let mut lhs = match lhs(p, r) {
        Some((lhs, blocklike)) => {
            // test stmt_bin_expr_ambiguity
            // fn foo() {
            //     let _ = {1} & 2;
            //     {1} &2;
            // }
            if r.prefer_stmt && blocklike.is_block() {
                return BlockLike::Block;
            }
            lhs
        }
        None => return BlockLike::NotBlock,
    };

    loop {
        let is_range = p.current() == DOTDOT || p.current() == DOTDOTEQ;
        let (op_bp, op) = current_op(p);
        if op_bp < bp {
            break;
        }
        let m = lhs.precede(p);
        match op {
            Op::Simple => p.bump(),
            Op::Composite(kind, n) => {
                p.bump_compound(kind, n);
            }
        }
        expr_bp(p, r, op_bp + 1);
        lhs = m.complete(p, if is_range { RANGE_EXPR } else { BIN_EXPR });
    }
    BlockLike::NotBlock
}

const LHS_FIRST: TokenSet = token_set_union![
    token_set![AMP, STAR, EXCL, DOTDOT, DOTDOTEQ, MINUS],
    atom::ATOM_EXPR_FIRST,
];

fn lhs(p: &mut Parser, r: Restrictions) -> Option<(CompletedMarker, BlockLike)> {
    let m;
    let kind = match p.current() {
        // test ref_expr
        // fn foo() {
        //     let _ = &1;
        //     let _ = &mut &f();
        // }
        AMP => {
            m = p.start();
            p.bump();
            p.eat(MUT_KW);
            REF_EXPR
        }
        // test unary_expr
        // fn foo() {
        //     **&1;
        //     !!true;
        //     --1;
        // }
        STAR | EXCL | MINUS => {
            m = p.start();
            p.bump();
            PREFIX_EXPR
        }
        // test full_range_expr
        // fn foo() { xs[..]; }
        DOTDOT | DOTDOTEQ => {
            m = p.start();
            p.bump();
            if p.at_ts(EXPR_FIRST) {
                expr_bp(p, r, 2);
            }
            return Some((m.complete(p, RANGE_EXPR), BlockLike::NotBlock));
        }
        _ => {
            let (lhs, blocklike) = atom::atom_expr(p, r)?;
            return Some((
                postfix_expr(p, lhs, !(r.prefer_stmt && blocklike.is_block())),
                blocklike,
            ));
        }
    };
    expr_bp(p, r, 255);
    Some((m.complete(p, kind), BlockLike::NotBlock))
}

fn postfix_expr(
    p: &mut Parser,
    mut lhs: CompletedMarker,
    // Calls are disallowed if the type is a block and we prefer statements because the call cannot be disambiguated from a tuple
    // E.g. `while true {break}();` is parsed as
    // `while true {break}; ();`
    mut allow_calls: bool,
) -> CompletedMarker {
    loop {
        lhs = match p.current() {
            // test stmt_postfix_expr_ambiguity
            // fn foo() {
            //     match () {
            //         _ => {}
            //         () => {}
            //         [] => {}
            //     }
            // }
            L_PAREN if allow_calls => call_expr(p, lhs),
            L_BRACK if allow_calls => index_expr(p, lhs),
            DOT if p.nth(1) == IDENT => {
                if p.nth(2) == L_PAREN || p.nth(2) == COLONCOLON {
                    method_call_expr(p, lhs)
                } else {
                    field_expr(p, lhs)
                }
            }
            DOT if p.nth(1) == INT_NUMBER => field_expr(p, lhs),
            // test postfix_range
            // fn foo() { let x = 1..; }
            DOTDOT | DOTDOTEQ if !EXPR_FIRST.contains(p.nth(1)) => {
                let m = lhs.precede(p);
                p.bump();
                m.complete(p, RANGE_EXPR)
            }
            QUESTION => try_expr(p, lhs),
            AS_KW => cast_expr(p, lhs),
            _ => break,
        };
        allow_calls = true
    }
    lhs
}

// test call_expr
// fn foo() {
//     let _ = f();
//     let _ = f()(1)(1, 2,);
// }
fn call_expr(p: &mut Parser, lhs: CompletedMarker) -> CompletedMarker {
    assert!(p.at(L_PAREN));
    let m = lhs.precede(p);
    arg_list(p);
    m.complete(p, CALL_EXPR)
}

// test index_expr
// fn foo() {
//     x[1][2];
// }
fn index_expr(p: &mut Parser, lhs: CompletedMarker) -> CompletedMarker {
    assert!(p.at(L_BRACK));
    let m = lhs.precede(p);
    p.bump();
    expr(p);
    p.expect(R_BRACK);
    m.complete(p, INDEX_EXPR)
}

// test method_call_expr
// fn foo() {
//     x.foo();
//     y.bar::<T>(1, 2,);
// }
fn method_call_expr(p: &mut Parser, lhs: CompletedMarker) -> CompletedMarker {
    assert!(p.at(DOT) && p.nth(1) == IDENT && (p.nth(2) == L_PAREN || p.nth(2) == COLONCOLON));
    let m = lhs.precede(p);
    p.bump();
    name_ref(p);
    type_args::opt_type_arg_list(p, true);
    if p.at(L_PAREN) {
        arg_list(p);
    }
    m.complete(p, METHOD_CALL_EXPR)
}

// test field_expr
// fn foo() {
//     x.foo;
//     x.0.bar;
// }
fn field_expr(p: &mut Parser, lhs: CompletedMarker) -> CompletedMarker {
    assert!(p.at(DOT) && (p.nth(1) == IDENT || p.nth(1) == INT_NUMBER));
    let m = lhs.precede(p);
    p.bump();
    if p.at(IDENT) {
        name_ref(p)
    } else {
        p.bump()
    }
    m.complete(p, FIELD_EXPR)
}

// test try_expr
// fn foo() {
//     x?;
// }
fn try_expr(p: &mut Parser, lhs: CompletedMarker) -> CompletedMarker {
    assert!(p.at(QUESTION));
    let m = lhs.precede(p);
    p.bump();
    m.complete(p, TRY_EXPR)
}

// test cast_expr
// fn foo() {
//     82 as i32;
//     81 as i8 + 1;
//     79 as i16 - 1;
// }
fn cast_expr(p: &mut Parser, lhs: CompletedMarker) -> CompletedMarker {
    assert!(p.at(AS_KW));
    let m = lhs.precede(p);
    p.bump();
    // Use type_no_bounds(), because cast expressions are not
    // allowed to have bounds.
    types::type_no_bounds(p);
    m.complete(p, CAST_EXPR)
}

fn arg_list(p: &mut Parser) {
    assert!(p.at(L_PAREN));
    let m = p.start();
    p.bump();
    while !p.at(R_PAREN) && !p.at(EOF) {
        if !p.at_ts(EXPR_FIRST) {
            p.error("expected expression");
            break;
        }
        expr(p);
        if !p.at(R_PAREN) && !p.expect(COMMA) {
            break;
        }
    }
    p.eat(R_PAREN);
    m.complete(p, ARG_LIST);
}

// test path_expr
// fn foo() {
//     let _ = a;
//     let _ = a::b;
//     let _ = ::a::<b>;
//     let _ = format!();
// }
fn path_expr(p: &mut Parser, r: Restrictions) -> (CompletedMarker, BlockLike) {
    assert!(paths::is_path_start(p) || p.at(L_ANGLE));
    let m = p.start();
    paths::expr_path(p);
    match p.current() {
        L_CURLY if !r.forbid_structs => {
            named_field_list(p);
            (m.complete(p, STRUCT_LIT), BlockLike::Block)
        }
        EXCL => {
            let block_like = items::macro_call_after_excl(p);
            return (m.complete(p, MACRO_CALL), block_like);
        }
        _ => (m.complete(p, PATH_EXPR), BlockLike::NotBlock),
    }
}

// test struct_lit
// fn foo() {
//     S {};
//     S { x, y: 32, };
//     S { x, y: 32, ..Default::default() };
// }
pub(crate) fn named_field_list(p: &mut Parser) {
    assert!(p.at(L_CURLY));
    let m = p.start();
    p.bump();
    while !p.at(EOF) && !p.at(R_CURLY) {
        match p.current() {
            IDENT => {
                let m = p.start();
                name_ref(p);
                if p.eat(COLON) {
                    expr(p);
                }
                m.complete(p, NAMED_FIELD);
            }
            DOTDOT => {
                p.bump();
                expr(p);
            }
            L_CURLY => error_block(p, "expected a field"),
            _ => p.err_and_bump("expected identifier"),
        }
        if !p.at(R_CURLY) {
            p.expect(COMMA);
        }
    }
    p.expect(R_CURLY);
    m.complete(p, NAMED_FIELD_LIST);
}
<commit_msg>Use a better way of skipping SEMIs<commit_after>mod atom;

pub(crate) use self::atom::match_arm_list;
pub(super) use self::atom::{literal, LITERAL_FIRST};
use super::*;

const EXPR_FIRST: TokenSet = LHS_FIRST;

pub(super) fn expr(p: &mut Parser) -> BlockLike {
    let r = Restrictions {
        forbid_structs: false,
        prefer_stmt: false,
    };
    expr_bp(p, r, 1)
}

pub(super) fn expr_stmt(p: &mut Parser) -> BlockLike {
    let r = Restrictions {
        forbid_structs: false,
        prefer_stmt: true,
    };
    expr_bp(p, r, 1)
}

fn expr_no_struct(p: &mut Parser) {
    let r = Restrictions {
        forbid_structs: true,
        prefer_stmt: false,
    };
    expr_bp(p, r, 1);
}

// test block
// fn a() {}
// fn b() { let _ = 1; }
// fn c() { 1; 2; }
// fn d() { 1; 2 }
pub(crate) fn block(p: &mut Parser) {
    if !p.at(L_CURLY) {
        p.error("expected a block");
        return;
    }
    let m = p.start();
    p.bump();

    while !p.at(EOF) && !p.at(R_CURLY) {
        match p.current() {
            LET_KW => let_stmt(p),
            // test nocontentexpr
            // fn foo(){
            //     ;;;some_expr();;;;{;;;};;;;Ok(())
            // }
            SEMI => p.bump(),
            _ => {
                // test block_items
                // fn a() { fn b() {} }
                let m = p.start();
                match items::maybe_item(p, items::ItemFlavor::Mod) {
                    items::MaybeItem::Item(kind) => {
                        m.complete(p, kind);
                    }
                    items::MaybeItem::Modifiers => {
                        m.abandon(p);
                        p.error("expected an item");
                    }
                    // test pub_expr
                    // fn foo() { pub 92; } //FIXME
                    items::MaybeItem::None => {
                        let is_blocklike = expressions::expr_stmt(p) == BlockLike::Block;
                        if p.at(R_CURLY) {
                            m.abandon(p);
                        } else {
                            // test no_semi_after_block
                            // fn foo() {
                            //     if true {}
                            //     loop {}
                            //     match () {}
                            //     while true {}
                            //     for _ in () {}
                            //     {}
                            //     {}
                            //     macro_rules! test {
                            //          () => {}
                            //     }
                            //     test!{}
                            // }
                            if is_blocklike {
                                p.eat(SEMI);
                            } else {
                                p.expect(SEMI);
                            }
                            m.complete(p, EXPR_STMT);
                        }
                    }
                }
            }
        }
    }
    p.expect(R_CURLY);
    m.complete(p, BLOCK);

    // test let_stmt;
    // fn foo() {
    //     let a;
    //     let b: i32;
    //     let c = 92;
    //     let d: i32 = 92;
    // }
    fn let_stmt(p: &mut Parser) {
        assert!(p.at(LET_KW));
        let m = p.start();
        p.bump();
        patterns::pattern(p);
        if p.at(COLON) {
            types::ascription(p);
        }
        if p.eat(EQ) {
            expressions::expr(p);
        }
        p.expect(SEMI);
        m.complete(p, LET_STMT);
    }
}

#[derive(Clone, Copy)]
struct Restrictions {
    forbid_structs: bool,
    prefer_stmt: bool,
}

enum Op {
    Simple,
    Composite(SyntaxKind, u8),
}

fn current_op(p: &Parser) -> (u8, Op) {
    if let Some(t) = p.next3() {
        match t {
            (L_ANGLE, L_ANGLE, EQ) => return (1, Op::Composite(SHLEQ, 3)),
            (R_ANGLE, R_ANGLE, EQ) => return (1, Op::Composite(SHREQ, 3)),
            _ => (),
        }
    }

    if let Some(t) = p.next2() {
        match t {
            (PLUS, EQ) => return (1, Op::Composite(PLUSEQ, 2)),
            (MINUS, EQ) => return (1, Op::Composite(MINUSEQ, 2)),
            (STAR, EQ) => return (1, Op::Composite(STAREQ, 2)),
            (SLASH, EQ) => return (1, Op::Composite(SLASHEQ, 2)),
            (PIPE, EQ) => return (1, Op::Composite(PIPEEQ, 2)),
            (AMP, EQ) => return (1, Op::Composite(AMPEQ, 2)),
            (CARET, EQ) => return (1, Op::Composite(CARETEQ, 2)),
            (PIPE, PIPE) => return (3, Op::Composite(PIPEPIPE, 2)),
            (AMP, AMP) => return (4, Op::Composite(AMPAMP, 2)),
            (L_ANGLE, EQ) => return (5, Op::Composite(LTEQ, 2)),
            (R_ANGLE, EQ) => return (5, Op::Composite(GTEQ, 2)),
            (L_ANGLE, L_ANGLE) => return (9, Op::Composite(SHL, 2)),
            (R_ANGLE, R_ANGLE) => return (9, Op::Composite(SHR, 2)),
            _ => (),
        }
    }

    let bp = match p.current() {
        EQ => 1,
        DOTDOT | DOTDOTEQ => 2,
        EQEQ | NEQ | L_ANGLE | R_ANGLE => 5,
        PIPE => 6,
        CARET => 7,
        AMP => 8,
        MINUS | PLUS => 10,
        STAR | SLASH | PERCENT => 11,
        _ => 0,
    };
    (bp, Op::Simple)
}

// Parses expression with binding power of at least bp.
fn expr_bp(p: &mut Parser, r: Restrictions, bp: u8) -> BlockLike {
    let mut lhs = match lhs(p, r) {
        Some((lhs, blocklike)) => {
            // test stmt_bin_expr_ambiguity
            // fn foo() {
            //     let _ = {1} & 2;
            //     {1} &2;
            // }
            if r.prefer_stmt && blocklike.is_block() {
                return BlockLike::Block;
            }
            lhs
        }
        None => return BlockLike::NotBlock,
    };

    loop {
        let is_range = p.current() == DOTDOT || p.current() == DOTDOTEQ;
        let (op_bp, op) = current_op(p);
        if op_bp < bp {
            break;
        }
        let m = lhs.precede(p);
        match op {
            Op::Simple => p.bump(),
            Op::Composite(kind, n) => {
                p.bump_compound(kind, n);
            }
        }
        expr_bp(p, r, op_bp + 1);
        lhs = m.complete(p, if is_range { RANGE_EXPR } else { BIN_EXPR });
    }
    BlockLike::NotBlock
}

const LHS_FIRST: TokenSet = token_set_union![
    token_set![AMP, STAR, EXCL, DOTDOT, DOTDOTEQ, MINUS],
    atom::ATOM_EXPR_FIRST,
];

fn lhs(p: &mut Parser, r: Restrictions) -> Option<(CompletedMarker, BlockLike)> {
    let m;
    let kind = match p.current() {
        // test ref_expr
        // fn foo() {
        //     let _ = &1;
        //     let _ = &mut &f();
        // }
        AMP => {
            m = p.start();
            p.bump();
            p.eat(MUT_KW);
            REF_EXPR
        }
        // test unary_expr
        // fn foo() {
        //     **&1;
        //     !!true;
        //     --1;
        // }
        STAR | EXCL | MINUS => {
            m = p.start();
            p.bump();
            PREFIX_EXPR
        }
        // test full_range_expr
        // fn foo() { xs[..]; }
        DOTDOT | DOTDOTEQ => {
            m = p.start();
            p.bump();
            if p.at_ts(EXPR_FIRST) {
                expr_bp(p, r, 2);
            }
            return Some((m.complete(p, RANGE_EXPR), BlockLike::NotBlock));
        }
        _ => {
            let (lhs, blocklike) = atom::atom_expr(p, r)?;
            return Some((
                postfix_expr(p, lhs, !(r.prefer_stmt && blocklike.is_block())),
                blocklike,
            ));
        }
    };
    expr_bp(p, r, 255);
    Some((m.complete(p, kind), BlockLike::NotBlock))
}

fn postfix_expr(
    p: &mut Parser,
    mut lhs: CompletedMarker,
    // Calls are disallowed if the type is a block and we prefer statements because the call cannot be disambiguated from a tuple
    // E.g. `while true {break}();` is parsed as
    // `while true {break}; ();`
    mut allow_calls: bool,
) -> CompletedMarker {
    loop {
        lhs = match p.current() {
            // test stmt_postfix_expr_ambiguity
            // fn foo() {
            //     match () {
            //         _ => {}
            //         () => {}
            //         [] => {}
            //     }
            // }
            L_PAREN if allow_calls => call_expr(p, lhs),
            L_BRACK if allow_calls => index_expr(p, lhs),
            DOT if p.nth(1) == IDENT => {
                if p.nth(2) == L_PAREN || p.nth(2) == COLONCOLON {
                    method_call_expr(p, lhs)
                } else {
                    field_expr(p, lhs)
                }
            }
            DOT if p.nth(1) == INT_NUMBER => field_expr(p, lhs),
            // test postfix_range
            // fn foo() { let x = 1..; }
            DOTDOT | DOTDOTEQ if !EXPR_FIRST.contains(p.nth(1)) => {
                let m = lhs.precede(p);
                p.bump();
                m.complete(p, RANGE_EXPR)
            }
            QUESTION => try_expr(p, lhs),
            AS_KW => cast_expr(p, lhs),
            _ => break,
        };
        allow_calls = true
    }
    lhs
}

// test call_expr
// fn foo() {
//     let _ = f();
//     let _ = f()(1)(1, 2,);
// }
fn call_expr(p: &mut Parser, lhs: CompletedMarker) -> CompletedMarker {
    assert!(p.at(L_PAREN));
    let m = lhs.precede(p);
    arg_list(p);
    m.complete(p, CALL_EXPR)
}

// test index_expr
// fn foo() {
//     x[1][2];
// }
fn index_expr(p: &mut Parser, lhs: CompletedMarker) -> CompletedMarker {
    assert!(p.at(L_BRACK));
    let m = lhs.precede(p);
    p.bump();
    expr(p);
    p.expect(R_BRACK);
    m.complete(p, INDEX_EXPR)
}

// test method_call_expr
// fn foo() {
//     x.foo();
//     y.bar::<T>(1, 2,);
// }
fn method_call_expr(p: &mut Parser, lhs: CompletedMarker) -> CompletedMarker {
    assert!(p.at(DOT) && p.nth(1) == IDENT && (p.nth(2) == L_PAREN || p.nth(2) == COLONCOLON));
    let m = lhs.precede(p);
    p.bump();
    name_ref(p);
    type_args::opt_type_arg_list(p, true);
    if p.at(L_PAREN) {
        arg_list(p);
    }
    m.complete(p, METHOD_CALL_EXPR)
}

// test field_expr
// fn foo() {
//     x.foo;
//     x.0.bar;
// }
fn field_expr(p: &mut Parser, lhs: CompletedMarker) -> CompletedMarker {
    assert!(p.at(DOT) && (p.nth(1) == IDENT || p.nth(1) == INT_NUMBER));
    let m = lhs.precede(p);
    p.bump();
    if p.at(IDENT) {
        name_ref(p)
    } else {
        p.bump()
    }
    m.complete(p, FIELD_EXPR)
}

// test try_expr
// fn foo() {
//     x?;
// }
fn try_expr(p: &mut Parser, lhs: CompletedMarker) -> CompletedMarker {
    assert!(p.at(QUESTION));
    let m = lhs.precede(p);
    p.bump();
    m.complete(p, TRY_EXPR)
}

// test cast_expr
// fn foo() {
//     82 as i32;
//     81 as i8 + 1;
//     79 as i16 - 1;
// }
fn cast_expr(p: &mut Parser, lhs: CompletedMarker) -> CompletedMarker {
    assert!(p.at(AS_KW));
    let m = lhs.precede(p);
    p.bump();
    // Use type_no_bounds(), because cast expressions are not
    // allowed to have bounds.
    types::type_no_bounds(p);
    m.complete(p, CAST_EXPR)
}

fn arg_list(p: &mut Parser) {
    assert!(p.at(L_PAREN));
    let m = p.start();
    p.bump();
    while !p.at(R_PAREN) && !p.at(EOF) {
        if !p.at_ts(EXPR_FIRST) {
            p.error("expected expression");
            break;
        }
        expr(p);
        if !p.at(R_PAREN) && !p.expect(COMMA) {
            break;
        }
    }
    p.eat(R_PAREN);
    m.complete(p, ARG_LIST);
}

// test path_expr
// fn foo() {
//     let _ = a;
//     let _ = a::b;
//     let _ = ::a::<b>;
//     let _ = format!();
// }
fn path_expr(p: &mut Parser, r: Restrictions) -> (CompletedMarker, BlockLike) {
    assert!(paths::is_path_start(p) || p.at(L_ANGLE));
    let m = p.start();
    paths::expr_path(p);
    match p.current() {
        L_CURLY if !r.forbid_structs => {
            named_field_list(p);
            (m.complete(p, STRUCT_LIT), BlockLike::Block)
        }
        EXCL => {
            let block_like = items::macro_call_after_excl(p);
            return (m.complete(p, MACRO_CALL), block_like);
        }
        _ => (m.complete(p, PATH_EXPR), BlockLike::NotBlock),
    }
}

// test struct_lit
// fn foo() {
//     S {};
//     S { x, y: 32, };
//     S { x, y: 32, ..Default::default() };
// }
pub(crate) fn named_field_list(p: &mut Parser) {
    assert!(p.at(L_CURLY));
    let m = p.start();
    p.bump();
    while !p.at(EOF) && !p.at(R_CURLY) {
        match p.current() {
            IDENT => {
                let m = p.start();
                name_ref(p);
                if p.eat(COLON) {
                    expr(p);
                }
                m.complete(p, NAMED_FIELD);
            }
            DOTDOT => {
                p.bump();
                expr(p);
            }
            L_CURLY => error_block(p, "expected a field"),
            _ => p.err_and_bump("expected identifier"),
        }
        if !p.at(R_CURLY) {
            p.expect(COMMA);
        }
    }
    p.expect(R_CURLY);
    m.complete(p, NAMED_FIELD_LIST);
}
<|endoftext|>


<commit_before>/* Fundamental layout structures and algorithms. */

use arc = std::arc;
use arc::ARC;
use au = gfx::geometry;
use au::au;
use core::dvec::DVec;
use core::to_str::ToStr;
use core::rand;
use css::styles::SpecifiedStyle;
use css::values::{BoxSizing, Length, Px, CSSDisplay, Specified, BgColor, BgColorTransparent};
use dl = gfx::display_list;
use dom::element::{ElementKind, HTMLDivElement, HTMLImageElement};
use dom::node::{Element, Node, NodeData, NodeKind, NodeTree};
use geom::rect::Rect;
use geom::size::Size2D;
use geom::point::Point2D;
use image::{Image, ImageHolder};
use layout::context::LayoutContext;
use layout::debug::BoxedDebugMethods;
use layout::flow::FlowContext;
use layout::text::TextBoxData;
use servo_text::text_run::TextRun;
use std::net::url::Url;
use task::spawn;
use util::color::Color;
use util::tree;
use vec::{push, push_all};

/**
Render boxes (`struct RenderBox`) are the leafs of the layout
tree. They cannot position themselves. In general, render boxes do not
have a simple correspondence with CSS boxes as in the specification:

 * Several render boxes may correspond to the same CSS box or DOM
   node. For example, a CSS text box broken across two lines is
   represented by two render boxes.

 * Some CSS boxes are not created at all, such as some anonymous block
   boxes induced by inline boxes with block-level sibling boxes. In
   that case, Servo uses an InlineFlow with BlockFlow siblings; the
   InlineFlow is block-level, but not a block container. It is
   positioned as if it were a block box, but its children are
   positioned according to inline flow.

Fundamental box types include:

 * GenericBox: an empty box that contributes only borders, margins,
padding, backgrounds. It is analogous to a CSS nonreplaced content box.

 * ImageBox: a box that represents a (replaced content) image and its
   accompanying borders, shadows, etc.

 * TextBox: a box representing a single run of text with a distinct
   style. A TextBox may be split into two or more render boxes across
   line breaks. Several TextBoxes may correspond to a single DOM text
   node. Split text boxes are implemented by referring to subsets of a
   master TextRun object.

*/

/* A box's kind influences how its styles are interpreted during
   layout.  For example, replaced content such as images are resized
   differently than tables, text, or other content.

   It also holds data specific to different box types, such as text.
*/
struct RenderBoxData {
    /* originating DOM node */
    node : Node,
    /* reference to containing flow context, which this box
       participates in */
    ctx  : @FlowContext,
    /* position of this box relative to owning flow */
    mut position : Rect<au>,
    font_size : Length,
    /* TODO (Issue #87): debug only */
    mut id: int
}

enum RenderBoxType {
    RenderBox_Generic,
    RenderBox_Image,
    RenderBox_Text,
}

pub enum RenderBox {
    GenericBox(RenderBoxData),
    ImageBox(RenderBoxData, ImageHolder),
    TextBox(RenderBoxData, TextBoxData),
    UnscannedTextBox(RenderBoxData, ~str)
}

enum InlineSpacerSide {
    LogicalBefore,
    LogicalAfter,
}

trait RenderBoxMethods {
    pure fn d(&self) -> &self/RenderBoxData;

    pure fn is_replaced() -> bool;
    pure fn can_split() -> bool;
    pure fn is_whitespace_only() -> bool;
    pure fn can_merge_with_box(@self, other: @RenderBox) -> bool;
    pure fn requires_inline_spacers() -> bool;
    pure fn content_box() -> Rect<au>;
    pure fn border_box() -> Rect<au>;

    fn get_min_width(&LayoutContext) -> au;
    fn get_pref_width(&LayoutContext) -> au;
    fn get_used_width() -> (au, au);
    fn get_used_height() -> (au, au);
    fn create_inline_spacer_for_side(&LayoutContext, InlineSpacerSide) -> Option<@RenderBox>;
    fn build_display_list(&dl::DisplayListBuilder, dirty: &Rect<au>,
                          offset: &Point2D<au>, &dl::DisplayList);
}

fn RenderBoxData(node: Node, ctx: @FlowContext, id: int) -> RenderBoxData {
    RenderBoxData {
        node : node,
        mut ctx  : ctx,
        mut position : au::zero_rect(),
        font_size: Px(0.0),
        id : id
    }
}

impl RenderBox : RenderBoxMethods {
    pure fn d(&self) -> &self/RenderBoxData {
        match *self {
            GenericBox(ref d)  => d,
            ImageBox(ref d, _) => d,
            TextBox(ref d, _)  => d,
            UnscannedTextBox(ref d, _) => d,
        }
    }

    pure fn is_replaced() -> bool {
        match self {
           ImageBox(*) => true, // TODO: form elements, etc
            _ => false
        }
    }

    pure fn can_split() -> bool {
        match self {
            TextBox(*) => true,
            _ => false
        }
    }

    pure fn is_whitespace_only() -> bool {
        match self {
            UnscannedTextBox(_, raw_text) => raw_text.is_whitespace(),
            _ => false
        }
    }

    pure fn can_merge_with_box(@self, other: @RenderBox) -> bool {
        assert !core::box::ptr_eq(self, other);

        match (self, other) {
            (@UnscannedTextBox(*), @UnscannedTextBox(*)) => true,
            (@TextBox(_,d1), @TextBox(_,d2)) => { core::box::ptr_eq(d1.run, d2.run) }
            (_, _) => false
        }
    }

    /** In general, these functions are transitively impure because they
     * may cause glyphs to be allocated. For now, it's impure because of
     * holder.get_image()
    */
    fn get_min_width(ctx: &LayoutContext) -> au {
        match self {
            // TODO: this should account for min/pref widths of the
            // box element in isolation. That includes
            // border/margin/padding but not child widths. The block
            // FlowContext will combine the width of this element and
            // that of its children to arrive at the context width.
            GenericBox(*) => au(0),
            // TODO: consult CSS 'width', margin, border.
            // TODO: If image isn't available, consult 'width'.
            ImageBox(_,i) => au::from_px(i.get_size().get_default(Size2D(0,0)).width),
            TextBox(_,d) => d.run.min_width_for_range(ctx, d.offset, d.length),
            UnscannedTextBox(*) => fail ~"Shouldn't see unscanned boxes here."
        }
    }

    fn get_pref_width(_ctx: &LayoutContext) -> au {
        match self {
            // TODO: this should account for min/pref widths of the
            // box element in isolation. That includes
            // border/margin/padding but not child widths. The block
            // FlowContext will combine the width of this element and
            // that of its children to arrive at the context width.
            GenericBox(*) => au(0),
            ImageBox(_,i) => au::from_px(i.get_size().get_default(Size2D(0,0)).width),

            // a text box cannot span lines, so assume that this is an unsplit text box.

            // TODO: If text boxes have been split to wrap lines, then
            // they could report a smaller pref width during incremental reflow.
            // maybe text boxes should report nothing, and the parent flow could
            // factor in min/pref widths of any text runs that it owns.
            TextBox(_,d) => {
                let mut max_line_width: au = au(0);
                do d.run.iter_natural_lines_for_range(d.offset, d.length) |line_offset, line_len| {
                    let mut line_width: au = au(0);
                    do d.run.glyphs.iter_glyphs_for_range(line_offset, line_len) |_char_i, glyph| {
                        line_width += glyph.advance()
                    };

                    if max_line_width < line_width {
                        max_line_width = line_width;
                    };
                    true
                }

                max_line_width
            },
            UnscannedTextBox(*) => fail ~"Shouldn't see unscanned boxes here."
        }
    }

    /* Returns the amount of left, right "fringe" used by this
    box. This should be based on margin, border, padding, width. */
    fn get_used_width() -> (au, au) {
        // TODO: this should actually do some computation!
        // See CSS 2.1, Section 10.3, 10.4.

        (au(0), au(0))
    }

    /* Returns the amount of left, right "fringe" used by this
    box. This should be based on margin, border, padding, width. */
    fn get_used_height() -> (au, au) {
        // TODO: this should actually do some computation!
        // See CSS 2.1, Section 10.5, 10.6.

        (au(0), au(0))
    }

    /* Whether "spacer" boxes are needed to stand in for this DOM node */
    pure fn requires_inline_spacers() -> bool {
        return false;
    }

    /* The box formed by the content edge, as defined in CSS 2.1 Section 8.1.
       Coordinates are relative to the owning flow. */
    pure fn content_box() -> Rect<au> {
        match self {
            ImageBox(_,i) => {
                let size = i.size();
                Rect {
                    origin: copy self.d().position.origin,
                    size:   Size2D(au::from_px(size.width),
                                   au::from_px(size.height))
                }
            },
            GenericBox(*) => {
                copy self.d().position
                /* FIXME: The following hits an ICE for whatever reason

                let origin = self.d().position.origin;
                let size   = self.d().position.size;
                let (offset_left, offset_right) = self.get_used_width();
                let (offset_top, offset_bottom) = self.get_used_height();

                Rect {
                    origin: Point2D(origin.x + offset_left, origin.y + offset_top),
                    size:   Size2D(size.width - (offset_left + offset_right),
                                   size.height - (offset_top + offset_bottom))
                }*/
            },
            TextBox(*) => {
                copy self.d().position
            },
            UnscannedTextBox(*) => fail ~"Shouldn't see unscanned boxes here."
        }
    }

    /* The box formed by the border edge, as defined in CSS 2.1 Section 8.1.
       Coordinates are relative to the owning flow. */
    pure fn border_box() -> Rect<au> {
        // TODO: actually compute content_box + padding + border
        self.content_box()
    }

    // TODO: implement this, generating spacer
    fn create_inline_spacer_for_side(_ctx: &LayoutContext, _side: InlineSpacerSide) -> Option<@RenderBox> {
        None
    }

    // TODO: to implement stacking contexts correctly, we need to
    // create a set of display lists, one per each layer of a stacking
    // context. (CSS 2.1, Section 9.9.1). Each box is passed the list
    // set representing the box's stacking context. When asked to
    // construct its constituent display items, each box puts its
    // DisplayItems into the correct stack layer (according to CSS 2.1
    // Appendix E).  and then builder flattens the list at the end.

    /* Methods for building a display list. This is a good candidate
       for a function pointer as the number of boxes explodes.

    # Arguments

    * `builder` - the display list builder which manages the coordinate system and options.
    * `dirty` - Dirty rectangle, in the coordinate system of the owning flow (self.ctx)
    * `origin` - Total offset from display list root flow to this box's owning flow
    * `list` - List to which items should be appended
    */
    fn build_display_list(_builder: &dl::DisplayListBuilder, dirty: &Rect<au>,
                          offset: &Point2D<au>, list: &dl::DisplayList) {
        if !self.d().position.intersects(dirty) {
            return;
        }

        let bounds : Rect<au> = Rect(self.d().position.origin.add(offset),
                                     copy self.d().position.size);

        // TODO: shouldn't need to unbox CSSValue by now
        let boxed_bgcolor = self.d().node.style().background_color;
        let bgcolor = match boxed_bgcolor {
            Specified(BgColor(c)) => c,
            Specified(BgColorTransparent) | _ => util::color::rgba(0,0,0,0.0)
        };

        match self {
            UnscannedTextBox(*) => fail ~"Shouldn't see unscanned boxes here.",
            TextBox(_,d) => {
                list.push(~dl::Text(bounds, ~(copy *d.run), d.offset, d.length))
            },
            // TODO: items for background, border, outline
            GenericBox(_) => {
                use std::cmp::FuzzyEq;
                if !bgcolor.alpha.fuzzy_eq(&0.0) {
                    list.push(~dl::SolidColor(bounds, bgcolor.red, bgcolor.green, bgcolor.blue));
                }
            },
            ImageBox(_,i) => {
                match i.get_image() {
                    Some(image) => list.push(~dl::Image(bounds, arc::clone(&image))),
                    /* No image data at all? Okay, add some fallback content instead. */
                    None => {
                        list.push(~dl::SolidColor(bounds, bgcolor.red, bgcolor.green, bgcolor.blue));
                    }
                }
            }
        }
    }
}

impl RenderBox : BoxedDebugMethods {
    fn dump(@self) {
        self.dump_indent(0u);
    }

    /* Dumps the node tree, for debugging, with indentation. */
    fn dump_indent(@self, indent: uint) {
        let mut s = ~"";
        for uint::range(0u, indent) |_i| {
            s += ~"    ";
        }

        s += self.debug_str();
        debug!("%s", s);
    }

    fn debug_str(@self) -> ~str {
        let repr = match self {
            @GenericBox(*) => ~"GenericBox",
            @ImageBox(*) => ~"ImageBox",
            @TextBox(_,d) => fmt!("TextBox(text=%s)", str::substr(d.run.text, d.offset, d.length)),
            @UnscannedTextBox(_,s) => fmt!("UnscannedTextBox(%s)", s)
        };

        fmt!("box b%?: %?", self.d().id, repr)
    }
}
<commit_msg>Apply background colors to all box types<commit_after>/* Fundamental layout structures and algorithms. */

use arc = std::arc;
use arc::ARC;
use au = gfx::geometry;
use au::au;
use core::dvec::DVec;
use core::to_str::ToStr;
use core::rand;
use css::styles::SpecifiedStyle;
use css::values::{BoxSizing, Length, Px, CSSDisplay, Specified, BgColor, BgColorTransparent};
use dl = gfx::display_list;
use dom::element::{ElementKind, HTMLDivElement, HTMLImageElement};
use dom::node::{Element, Node, NodeData, NodeKind, NodeTree};
use geom::rect::Rect;
use geom::size::Size2D;
use geom::point::Point2D;
use image::{Image, ImageHolder};
use layout::context::LayoutContext;
use layout::debug::BoxedDebugMethods;
use layout::flow::FlowContext;
use layout::text::TextBoxData;
use servo_text::text_run::TextRun;
use std::net::url::Url;
use task::spawn;
use util::color::Color;
use util::tree;
use vec::{push, push_all};

/**
Render boxes (`struct RenderBox`) are the leafs of the layout
tree. They cannot position themselves. In general, render boxes do not
have a simple correspondence with CSS boxes as in the specification:

 * Several render boxes may correspond to the same CSS box or DOM
   node. For example, a CSS text box broken across two lines is
   represented by two render boxes.

 * Some CSS boxes are not created at all, such as some anonymous block
   boxes induced by inline boxes with block-level sibling boxes. In
   that case, Servo uses an InlineFlow with BlockFlow siblings; the
   InlineFlow is block-level, but not a block container. It is
   positioned as if it were a block box, but its children are
   positioned according to inline flow.

Fundamental box types include:

 * GenericBox: an empty box that contributes only borders, margins,
padding, backgrounds. It is analogous to a CSS nonreplaced content box.

 * ImageBox: a box that represents a (replaced content) image and its
   accompanying borders, shadows, etc.

 * TextBox: a box representing a single run of text with a distinct
   style. A TextBox may be split into two or more render boxes across
   line breaks. Several TextBoxes may correspond to a single DOM text
   node. Split text boxes are implemented by referring to subsets of a
   master TextRun object.

*/

/* A box's kind influences how its styles are interpreted during
   layout.  For example, replaced content such as images are resized
   differently than tables, text, or other content.

   It also holds data specific to different box types, such as text.
*/
struct RenderBoxData {
    /* originating DOM node */
    node : Node,
    /* reference to containing flow context, which this box
       participates in */
    ctx  : @FlowContext,
    /* position of this box relative to owning flow */
    mut position : Rect<au>,
    font_size : Length,
    /* TODO (Issue #87): debug only */
    mut id: int
}

enum RenderBoxType {
    RenderBox_Generic,
    RenderBox_Image,
    RenderBox_Text,
}

pub enum RenderBox {
    GenericBox(RenderBoxData),
    ImageBox(RenderBoxData, ImageHolder),
    TextBox(RenderBoxData, TextBoxData),
    UnscannedTextBox(RenderBoxData, ~str)
}

enum InlineSpacerSide {
    LogicalBefore,
    LogicalAfter,
}

trait RenderBoxMethods {
    pure fn d(&self) -> &self/RenderBoxData;

    pure fn is_replaced() -> bool;
    pure fn can_split() -> bool;
    pure fn is_whitespace_only() -> bool;
    pure fn can_merge_with_box(@self, other: @RenderBox) -> bool;
    pure fn requires_inline_spacers() -> bool;
    pure fn content_box() -> Rect<au>;
    pure fn border_box() -> Rect<au>;

    fn get_min_width(&LayoutContext) -> au;
    fn get_pref_width(&LayoutContext) -> au;
    fn get_used_width() -> (au, au);
    fn get_used_height() -> (au, au);
    fn create_inline_spacer_for_side(&LayoutContext, InlineSpacerSide) -> Option<@RenderBox>;
    fn build_display_list(&dl::DisplayListBuilder, dirty: &Rect<au>,
                          offset: &Point2D<au>, &dl::DisplayList);
}

fn RenderBoxData(node: Node, ctx: @FlowContext, id: int) -> RenderBoxData {
    RenderBoxData {
        node : node,
        mut ctx  : ctx,
        mut position : au::zero_rect(),
        font_size: Px(0.0),
        id : id
    }
}

impl RenderBox : RenderBoxMethods {
    pure fn d(&self) -> &self/RenderBoxData {
        match *self {
            GenericBox(ref d)  => d,
            ImageBox(ref d, _) => d,
            TextBox(ref d, _)  => d,
            UnscannedTextBox(ref d, _) => d,
        }
    }

    pure fn is_replaced() -> bool {
        match self {
           ImageBox(*) => true, // TODO: form elements, etc
            _ => false
        }
    }

    pure fn can_split() -> bool {
        match self {
            TextBox(*) => true,
            _ => false
        }
    }

    pure fn is_whitespace_only() -> bool {
        match self {
            UnscannedTextBox(_, raw_text) => raw_text.is_whitespace(),
            _ => false
        }
    }

    pure fn can_merge_with_box(@self, other: @RenderBox) -> bool {
        assert !core::box::ptr_eq(self, other);

        match (self, other) {
            (@UnscannedTextBox(*), @UnscannedTextBox(*)) => true,
            (@TextBox(_,d1), @TextBox(_,d2)) => { core::box::ptr_eq(d1.run, d2.run) }
            (_, _) => false
        }
    }

    /** In general, these functions are transitively impure because they
     * may cause glyphs to be allocated. For now, it's impure because of
     * holder.get_image()
    */
    fn get_min_width(ctx: &LayoutContext) -> au {
        match self {
            // TODO: this should account for min/pref widths of the
            // box element in isolation. That includes
            // border/margin/padding but not child widths. The block
            // FlowContext will combine the width of this element and
            // that of its children to arrive at the context width.
            GenericBox(*) => au(0),
            // TODO: consult CSS 'width', margin, border.
            // TODO: If image isn't available, consult 'width'.
            ImageBox(_,i) => au::from_px(i.get_size().get_default(Size2D(0,0)).width),
            TextBox(_,d) => d.run.min_width_for_range(ctx, d.offset, d.length),
            UnscannedTextBox(*) => fail ~"Shouldn't see unscanned boxes here."
        }
    }

    fn get_pref_width(_ctx: &LayoutContext) -> au {
        match self {
            // TODO: this should account for min/pref widths of the
            // box element in isolation. That includes
            // border/margin/padding but not child widths. The block
            // FlowContext will combine the width of this element and
            // that of its children to arrive at the context width.
            GenericBox(*) => au(0),
            ImageBox(_,i) => au::from_px(i.get_size().get_default(Size2D(0,0)).width),

            // a text box cannot span lines, so assume that this is an unsplit text box.

            // TODO: If text boxes have been split to wrap lines, then
            // they could report a smaller pref width during incremental reflow.
            // maybe text boxes should report nothing, and the parent flow could
            // factor in min/pref widths of any text runs that it owns.
            TextBox(_,d) => {
                let mut max_line_width: au = au(0);
                do d.run.iter_natural_lines_for_range(d.offset, d.length) |line_offset, line_len| {
                    let mut line_width: au = au(0);
                    do d.run.glyphs.iter_glyphs_for_range(line_offset, line_len) |_char_i, glyph| {
                        line_width += glyph.advance()
                    };

                    if max_line_width < line_width {
                        max_line_width = line_width;
                    };
                    true
                }

                max_line_width
            },
            UnscannedTextBox(*) => fail ~"Shouldn't see unscanned boxes here."
        }
    }

    /* Returns the amount of left, right "fringe" used by this
    box. This should be based on margin, border, padding, width. */
    fn get_used_width() -> (au, au) {
        // TODO: this should actually do some computation!
        // See CSS 2.1, Section 10.3, 10.4.

        (au(0), au(0))
    }

    /* Returns the amount of left, right "fringe" used by this
    box. This should be based on margin, border, padding, width. */
    fn get_used_height() -> (au, au) {
        // TODO: this should actually do some computation!
        // See CSS 2.1, Section 10.5, 10.6.

        (au(0), au(0))
    }

    /* Whether "spacer" boxes are needed to stand in for this DOM node */
    pure fn requires_inline_spacers() -> bool {
        return false;
    }

    /* The box formed by the content edge, as defined in CSS 2.1 Section 8.1.
       Coordinates are relative to the owning flow. */
    pure fn content_box() -> Rect<au> {
        match self {
            ImageBox(_,i) => {
                let size = i.size();
                Rect {
                    origin: copy self.d().position.origin,
                    size:   Size2D(au::from_px(size.width),
                                   au::from_px(size.height))
                }
            },
            GenericBox(*) => {
                copy self.d().position
                /* FIXME: The following hits an ICE for whatever reason

                let origin = self.d().position.origin;
                let size   = self.d().position.size;
                let (offset_left, offset_right) = self.get_used_width();
                let (offset_top, offset_bottom) = self.get_used_height();

                Rect {
                    origin: Point2D(origin.x + offset_left, origin.y + offset_top),
                    size:   Size2D(size.width - (offset_left + offset_right),
                                   size.height - (offset_top + offset_bottom))
                }*/
            },
            TextBox(*) => {
                copy self.d().position
            },
            UnscannedTextBox(*) => fail ~"Shouldn't see unscanned boxes here."
        }
    }

    /* The box formed by the border edge, as defined in CSS 2.1 Section 8.1.
       Coordinates are relative to the owning flow. */
    pure fn border_box() -> Rect<au> {
        // TODO: actually compute content_box + padding + border
        self.content_box()
    }

    // TODO: implement this, generating spacer
    fn create_inline_spacer_for_side(_ctx: &LayoutContext, _side: InlineSpacerSide) -> Option<@RenderBox> {
        None
    }

    // TODO: to implement stacking contexts correctly, we need to
    // create a set of display lists, one per each layer of a stacking
    // context. (CSS 2.1, Section 9.9.1). Each box is passed the list
    // set representing the box's stacking context. When asked to
    // construct its constituent display items, each box puts its
    // DisplayItems into the correct stack layer (according to CSS 2.1
    // Appendix E).  and then builder flattens the list at the end.

    /* Methods for building a display list. This is a good candidate
       for a function pointer as the number of boxes explodes.

    # Arguments

    * `builder` - the display list builder which manages the coordinate system and options.
    * `dirty` - Dirty rectangle, in the coordinate system of the owning flow (self.ctx)
    * `origin` - Total offset from display list root flow to this box's owning flow
    * `list` - List to which items should be appended
    */
    fn build_display_list(_builder: &dl::DisplayListBuilder, dirty: &Rect<au>,
                          offset: &Point2D<au>, list: &dl::DisplayList) {
        if !self.d().position.intersects(dirty) {
            return;
        }

        let bounds : Rect<au> = Rect(self.d().position.origin.add(offset),
                                     copy self.d().position.size);

        self.add_bgcolor_to_list(list, bounds);

        match self {
            UnscannedTextBox(*) => fail ~"Shouldn't see unscanned boxes here.",
            TextBox(_,d) => {
                list.push(~dl::Text(bounds, ~(copy *d.run), d.offset, d.length))
            },
            // TODO: items for background, border, outline
            GenericBox(_) => {
            },
            ImageBox(_,i) => {
                match i.get_image() {
                    Some(image) => list.push(~dl::Image(bounds, arc::clone(&image))),
                    /* No image data at all? Okay, add some fallback content instead. */
                    None => ()
                }
            }
        }
    }

    fn add_bgcolor_to_list(list: &dl::DisplayList, bounds: Rect<au>) {
        use std::cmp::FuzzyEq;
        // TODO: shouldn't need to unbox CSSValue by now
        let boxed_bgcolor = self.d().node.style().background_color;
        let bgcolor = match boxed_bgcolor {
            Specified(BgColor(c)) => c,
            Specified(BgColorTransparent) | _ => util::color::rgba(0,0,0,0.0)
        };
        if !bgcolor.alpha.fuzzy_eq(&0.0) {
            list.push(~dl::SolidColor(bounds, bgcolor.red, bgcolor.green, bgcolor.blue));
        }
    }
}

impl RenderBox : BoxedDebugMethods {
    fn dump(@self) {
        self.dump_indent(0u);
    }

    /* Dumps the node tree, for debugging, with indentation. */
    fn dump_indent(@self, indent: uint) {
        let mut s = ~"";
        for uint::range(0u, indent) |_i| {
            s += ~"    ";
        }

        s += self.debug_str();
        debug!("%s", s);
    }

    fn debug_str(@self) -> ~str {
        let repr = match self {
            @GenericBox(*) => ~"GenericBox",
            @ImageBox(*) => ~"ImageBox",
            @TextBox(_,d) => fmt!("TextBox(text=%s)", str::substr(d.run.text, d.offset, d.length)),
            @UnscannedTextBox(_,s) => fmt!("UnscannedTextBox(%s)", s)
        };

        fmt!("box b%?: %?", self.d().id, repr)
    }
}
<|endoftext|>


<commit_before>pub mod store;

pub trait SessionStore<K, V>: Send + Share {
    fn set_key(&mut self, K);
    fn insert(&self, V);
    fn find(&self) -> Option<V>;
    fn swap(&self, V) -> Option<V>;
    fn upsert(&self, V, |&mut V|) -> V;
    fn remove(&self) -> bool;
}
<commit_msg>(doc) SessionStore.<commit_after>//! This module defines the trait necessary for a session storage struct.
///!
///! A default implementation can be found in the `store` module: `Session`.

pub mod store;

/// This trait must be implemented for any session storage struct.
///
/// A key is set using `set_key` when the `Session` middleware is first called.
///
/// All of the remaining methods act on the specific session
/// selected by that key. This isolates sessions to a specific request.
pub trait SessionStore<K, V>: Send {
    /// Set the key to identify a unique session.
    fn set_key(&mut self, key: K);
    /// Set the value of this session, replacing any previously set value.
    fn insert(&self, value: V);
    /// Retrieve the value of this session.
    ///
    /// Returns `None` if this session has not been set.
    fn find(&self) -> Option<V>;
    /// Swap the given value with the current value of this session.
    ///
    /// Returns the value being replaced.
    /// Returns `None` if this session was not yet set.
    fn swap(&self, value: V) -> Option<V>;
    /// Insert value, if not yet set, or update the current value of this session.
    ///
    /// Returns an owned copy of the set (current) value of this session.
    ///
    /// This is analagous to the `insert_or_update_with` method of `HashMap`.
    fn upsert(&self, value: V, mutator: |&mut V|) -> V;
    /// Remove the session stored at this key.
    fn remove(&self) -> bool;
}
<|endoftext|>


<commit_before>use ra_syntax::{
    ast::{self, AstNode},
    TextSize, T,
};

use crate::{AssistContext, AssistId, AssistKind, Assists};

// Assist: remove_dbg
//
// Removes `dbg!()` macro call.
//
// ```
// fn main() {
//     <|>dbg!(92);
// }
// ```
// ->
// ```
// fn main() {
//     92;
// }
// ```
pub(crate) fn remove_dbg(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
    let macro_call = ctx.find_node_at_offset::<ast::MacroCall>()?;

    if !is_valid_macrocall(&macro_call, "dbg")? {
        return None;
    }

    let macro_range = macro_call.syntax().text_range();

    let macro_content = {
        let macro_args = macro_call.token_tree()?.syntax().clone();

        let text = macro_args.text();
        let without_parens = TextSize::of('(')..text.len() - TextSize::of(')');
        text.slice(without_parens).to_string()
    };

    let target = macro_call.syntax().text_range();
    acc.add(AssistId("remove_dbg", AssistKind::Refactor), "Remove dbg!()", target, |builder| {
        builder.replace(macro_range, macro_content);
    })
}

/// Verifies that the given macro_call actually matches the given name
/// and contains proper ending tokens
fn is_valid_macrocall(macro_call: &ast::MacroCall, macro_name: &str) -> Option<bool> {
    let path = macro_call.path()?;
    let name_ref = path.segment()?.name_ref()?;

    // Make sure it is actually a dbg-macro call, dbg followed by !
    let excl = path.syntax().next_sibling_or_token()?;

    if name_ref.text() != macro_name || excl.kind() != T![!] {
        return None;
    }

    let node = macro_call.token_tree()?.syntax().clone();
    let first_child = node.first_child_or_token()?;
    let last_child = node.last_child_or_token()?;

    match (first_child.kind(), last_child.kind()) {
        (T!['('], T![')']) | (T!['['], T![']']) | (T!['{'], T!['}']) => Some(true),
        _ => Some(false),
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};

    #[test]
    fn test_remove_dbg() {
        check_assist(remove_dbg, "<|>dbg!(1 + 1)", "1 + 1");

        check_assist(remove_dbg, "dbg!<|>((1 + 1))", "(1 + 1)");

        check_assist(remove_dbg, "dbg!(1 <|>+ 1)", "1 + 1");

        check_assist(remove_dbg, "let _ = <|>dbg!(1 + 1)", "let _ = 1 + 1");

        check_assist(
            remove_dbg,
            "
fn foo(n: usize) {
    if let Some(_) = dbg!(n.<|>checked_sub(4)) {
        // ...
    }
}
",
            "
fn foo(n: usize) {
    if let Some(_) = n.checked_sub(4) {
        // ...
    }
}
",
        );
    }

    #[test]
    fn test_remove_dbg_with_brackets_and_braces() {
        check_assist(remove_dbg, "dbg![<|>1 + 1]", "1 + 1");
        check_assist(remove_dbg, "dbg!{<|>1 + 1}", "1 + 1");
    }

    #[test]
    fn test_remove_dbg_not_applicable() {
        check_assist_not_applicable(remove_dbg, "<|>vec![1, 2, 3]");
        check_assist_not_applicable(remove_dbg, "<|>dbg(5, 6, 7)");
        check_assist_not_applicable(remove_dbg, "<|>dbg!(5, 6, 7");
    }

    #[test]
    fn test_remove_dbg_target() {
        check_assist_target(
            remove_dbg,
            "
fn foo(n: usize) {
    if let Some(_) = dbg!(n.<|>checked_sub(4)) {
        // ...
    }
}
",
            "dbg!(n.checked_sub(4))",
        );
    }

    #[test]
    fn test_remove_dbg_keep_semicolon() {
        // https://github.com/rust-analyzer/rust-analyzer/issues/5129#issuecomment-651399779
        // not quite though
        let code = "
let res = <|>dbg!(1 * 20); // needless comment
";
        let expected = "
let res = 1 * 20; // needless comment
";
        check_assist(remove_dbg, code, expected);
    }

    #[test]
    fn test_remove_dbg_keep_expression() {
        let code = "
let res = <|>dbg!(a + b).foo();";
        let expected = "let res = (a + b).foo();";
        check_assist(remove_dbg, code, expected);
    }

    #[test]
    fn test_remove_dbg_from_inside_fn() {
        let code = "
fn square(x: u32) -> u32 {
    x * x
}

fn main() {
    let x = square(dbg<|>!(5 + 10));
    println!(\"{}\", x);
}";

        let expected = "
fn square(x: u32) -> u32 {
    x * x
}

fn main() {
    let x = square(5 + 10);
    println!(\"{}\", x);
}";
        check_assist_target(remove_dbg, code, "dbg!(5 + 10)");
        check_assist(remove_dbg, code, expected);
    }
}
<commit_msg>Fixed #5129<commit_after>use ra_syntax::{
    ast::{self, AstNode},
    TextRange, TextSize, T,
};

use crate::{AssistContext, AssistId, AssistKind, Assists};

// Assist: remove_dbg
//
// Removes `dbg!()` macro call.
//
// ```
// fn main() {
//     <|>dbg!(92);
// }
// ```
// ->
// ```
// fn main() {
//     92;
// }
// ```
pub(crate) fn remove_dbg(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
    let macro_call = ctx.find_node_at_offset::<ast::MacroCall>()?;

    if !is_valid_macrocall(&macro_call, "dbg")? {
        return None;
    }

    let semicolon_on_end = macro_call.semicolon_token().is_some();
    let is_leaf = macro_call.syntax().next_sibling().is_none();

    let macro_end = match semicolon_on_end {
        true => macro_call.syntax().text_range().end() - TextSize::of(';'),
        false => macro_call.syntax().text_range().end(),
    };

    // macro_range determines what will be deleted and replaced with macro_content
    let macro_range = TextRange::new(macro_call.syntax().text_range().start(), macro_end);
    let paste_instead_of_dbg = {
        let text = macro_call.token_tree()?.syntax().text();

        // leafines determines if we should include the parenthesis or not
        let slice_index: TextRange = match is_leaf {
            // leaf means - we can extract the contents of the dbg! in text
            true => TextRange::new(TextSize::of('('), text.len() - TextSize::of(')')),
            // not leaf - means we should keep the parens
            false => TextRange::new(TextSize::from(0 as u32), text.len()),
        };
        text.slice(slice_index).to_string()
    };

    let target = macro_call.syntax().text_range();
    acc.add(AssistId("remove_dbg", AssistKind::Refactor), "Remove dbg!()", target, |builder| {
        builder.replace(macro_range, paste_instead_of_dbg);
    })
}

/// Verifies that the given macro_call actually matches the given name
/// and contains proper ending tokens
fn is_valid_macrocall(macro_call: &ast::MacroCall, macro_name: &str) -> Option<bool> {
    let path = macro_call.path()?;
    let name_ref = path.segment()?.name_ref()?;

    // Make sure it is actually a dbg-macro call, dbg followed by !
    let excl = path.syntax().next_sibling_or_token()?;

    if name_ref.text() != macro_name || excl.kind() != T![!] {
        return None;
    }

    let node = macro_call.token_tree()?.syntax().clone();
    let first_child = node.first_child_or_token()?;
    let last_child = node.last_child_or_token()?;

    match (first_child.kind(), last_child.kind()) {
        (T!['('], T![')']) | (T!['['], T![']']) | (T!['{'], T!['}']) => Some(true),
        _ => Some(false),
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};

    #[test]
    fn test_remove_dbg() {
        check_assist(remove_dbg, "<|>dbg!(1 + 1)", "1 + 1");

        check_assist(remove_dbg, "dbg!<|>((1 + 1))", "(1 + 1)");

        check_assist(remove_dbg, "dbg!(1 <|>+ 1)", "1 + 1");

        check_assist(remove_dbg, "let _ = <|>dbg!(1 + 1)", "let _ = 1 + 1");

        check_assist(
            remove_dbg,
            "
fn foo(n: usize) {
    if let Some(_) = dbg!(n.<|>checked_sub(4)) {
        // ...
    }
}
",
            "
fn foo(n: usize) {
    if let Some(_) = n.checked_sub(4) {
        // ...
    }
}
",
        );
    }

    #[test]
    fn test_remove_dbg_with_brackets_and_braces() {
        check_assist(remove_dbg, "dbg![<|>1 + 1]", "1 + 1");
        check_assist(remove_dbg, "dbg!{<|>1 + 1}", "1 + 1");
    }

    #[test]
    fn test_remove_dbg_not_applicable() {
        check_assist_not_applicable(remove_dbg, "<|>vec![1, 2, 3]");
        check_assist_not_applicable(remove_dbg, "<|>dbg(5, 6, 7)");
        check_assist_not_applicable(remove_dbg, "<|>dbg!(5, 6, 7");
    }

    #[test]
    fn test_remove_dbg_target() {
        check_assist_target(
            remove_dbg,
            "
fn foo(n: usize) {
    if let Some(_) = dbg!(n.<|>checked_sub(4)) {
        // ...
    }
}
",
            "dbg!(n.checked_sub(4))",
        );
    }

    #[test]
    fn test_remove_dbg_keep_semicolon() {
        // https://github.com/rust-analyzer/rust-analyzer/issues/5129#issuecomment-651399779
        // not quite though
        // adding a comment at the end of the line makes
        // the ast::MacroCall to include the semicolon at the end
        let code = "
let res = <|>dbg!(1 * 20); // needless comment
";
        let expected = "
let res = 1 * 20; // needless comment
";
        check_assist(remove_dbg, code, expected);
    }

    #[test]
    fn test_remove_dbg_keep_expression() {
        let code = "
let res = <|>dbg!(a + b).foo();";
        let expected = "let res = (a + b).foo();";
        check_assist(remove_dbg, code, expected);
    }

    #[test]
    fn test_remove_dbg_from_inside_fn() {
        let code = "
fn square(x: u32) -> u32 {
    x * x
}

fn main() {
    let x = square(dbg<|>!(5 + 10));
    println!(\"{}\", x);
}";

        let expected = "
fn square(x: u32) -> u32 {
    x * x
}

fn main() {
    let x = square(5 + 10);
    println!(\"{}\", x);
}";
        check_assist_target(remove_dbg, code, "dbg!(5 + 10)");
        check_assist(remove_dbg, code, expected);
    }
}
<|endoftext|>