<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use api::{BorderRadius, ClipMode, HitTestFlags, HitTestItem, HitTestResult, ItemTag, LayerPoint};
use api::{LayerPrimitiveInfo, LayerRect, PipelineId, WorldPoint};
use clip::{ClipSource, ClipStore, rounded_rectangle_contains_point};
use clip_scroll_node::{ClipScrollNode, NodeType};
use clip_scroll_tree::{ClipChainIndex, ClipScrollNodeIndex, ClipScrollTree};
use internal_types::FastHashMap;
use prim_store::ScrollNodeAndClipChain;
use util::LayerToWorldFastTransform;

/// A copy of important clip scroll node data to use during hit testing. This a copy of
/// data from the ClipScrollTree that will persist as a new frame is under construction,
/// allowing hit tests consistent with the currently rendered frame.
pub struct HitTestClipScrollNode {
    /// The pipeline id of this node.
    pipeline_id: PipelineId,

    /// A particular point must be inside all of these regions to be considered clipped in
    /// for the purposes of a hit test.
    regions: Vec<HitTestRegion>,

    /// World transform for content transformed by this node.
    world_content_transform: LayerToWorldFastTransform,

    /// World viewport transform for content transformed by this node.
    world_viewport_transform: LayerToWorldFastTransform,

    /// Origin of the viewport of the node, used to calculate node-relative positions.
    node_origin: LayerPoint,
}

/// A description of a clip chain in the HitTester. This is used to describe
/// hierarchical clip scroll nodes as well as ClipChains, so that they can be
/// handled the same way during hit testing. Once we represent all ClipChains
/// using ClipChainDescriptors, we can get rid of this and just use the
/// ClipChainDescriptor here.
#[derive(Clone)]
struct HitTestClipChainDescriptor {
    parent: Option<ClipChainIndex>,
    clips: Vec<ClipScrollNodeIndex>,
}

impl HitTestClipChainDescriptor {
    fn empty() -> HitTestClipChainDescriptor {
        HitTestClipChainDescriptor {
            parent: None,
            clips: Vec::new(),
        }
    }
}

#[derive(Clone)]
pub struct HitTestingItem {
    rect: LayerRect,
    clip_rect: LayerRect,
    tag: ItemTag,
}

impl HitTestingItem {
    pub fn new(tag: ItemTag, info: &LayerPrimitiveInfo) -> HitTestingItem {
        HitTestingItem {
            rect: info.rect,
            clip_rect: info.clip_rect,
            tag: tag,
        }
    }
}

#[derive(Clone)]
pub struct HitTestingRun(pub Vec<HitTestingItem>, pub ScrollNodeAndClipChain);

enum HitTestRegion {
    Rectangle(LayerRect),
    RoundedRectangle(LayerRect, BorderRadius, ClipMode),
}

impl HitTestRegion {
    pub fn contains(&self, point: &LayerPoint) -> bool {
        match self {
            &HitTestRegion::Rectangle(ref rectangle) => rectangle.contains(point),
            &HitTestRegion::RoundedRectangle(rect, radii, ClipMode::Clip) =>
                rounded_rectangle_contains_point(point, &rect, &radii),
            &HitTestRegion::RoundedRectangle(rect, radii, ClipMode::ClipOut) =>
                !rounded_rectangle_contains_point(point, &rect, &radii),
        }
    }
}

pub struct HitTester {
    runs: Vec<HitTestingRun>,
    nodes: Vec<HitTestClipScrollNode>,
    clip_chains: Vec<HitTestClipChainDescriptor>,
    pipeline_root_nodes: FastHashMap<PipelineId, ClipScrollNodeIndex>,
}

impl HitTester {
    pub fn new(
        runs: &Vec<HitTestingRun>,
        clip_scroll_tree: &ClipScrollTree,
        clip_store: &ClipStore
    ) -> HitTester {
        let mut hit_tester = HitTester {
            runs: runs.clone(),
            nodes: Vec::new(),
            clip_chains: Vec::new(),
            pipeline_root_nodes: FastHashMap::default(),
        };
        hit_tester.read_clip_scroll_tree(clip_scroll_tree, clip_store);
        hit_tester
    }

    fn read_clip_scroll_tree(
        &mut self,
        clip_scroll_tree: &ClipScrollTree,
        clip_store: &ClipStore
    ) {
        self.nodes.clear();
        self.clip_chains.clear();
        self.clip_chains.resize(
            clip_scroll_tree.clip_chains.len(),
            HitTestClipChainDescriptor::empty()
        );

        for (index, node) in clip_scroll_tree.nodes.iter().enumerate() {
            let index = ClipScrollNodeIndex(index);

            // If we haven't already seen a node for this pipeline, record this one as the root
            // node.
            self.pipeline_root_nodes.entry(node.pipeline_id).or_insert(index);

            self.nodes.push(HitTestClipScrollNode {
                pipeline_id: node.pipeline_id,
                regions: get_regions_for_clip_scroll_node(node, clip_store),
                world_content_transform: node.world_content_transform,
                world_viewport_transform: node.world_viewport_transform,
                node_origin: node.local_viewport_rect.origin,
            });

            if let NodeType::Clip { clip_chain_index, .. } = node.node_type {
              let clip_chain = self.clip_chains.get_mut(clip_chain_index.0).unwrap();
              clip_chain.parent =
                  clip_scroll_tree.get_clip_chain(clip_chain_index).parent_index;
              clip_chain.clips = vec![index];
            }
        }

        for descriptor in &clip_scroll_tree.clip_chains_descriptors {
            let clip_chain = self.clip_chains.get_mut(descriptor.index.0).unwrap();
            clip_chain.parent = clip_scroll_tree.get_clip_chain(descriptor.index).parent_index;
            clip_chain.clips = descriptor.clips.clone();
        }
    }

    fn is_point_clipped_in_for_clip_chain(
        &self,
        point: WorldPoint,
        clip_chain_index: ClipChainIndex,
        test: &mut HitTest
    ) -> bool {
        if let Some(result) = test.get_from_clip_chain_cache(clip_chain_index) {
            return result;
        }

        let descriptor = &self.clip_chains[clip_chain_index.0];
        let parent_clipped_in = match descriptor.parent {
            None => true,
            Some(parent) => self.is_point_clipped_in_for_clip_chain(point, parent, test),
        };

        if !parent_clipped_in {
            test.set_in_clip_chain_cache(clip_chain_index, false);
            return false;
        }

        for clip_node_index in &descriptor.clips {
            if !self.is_point_clipped_in_for_node(point, *clip_node_index, test) {
                test.set_in_clip_chain_cache(clip_chain_index, false);
                return false;
            }
        }

        test.set_in_clip_chain_cache(clip_chain_index, true);
        true
    }

    fn is_point_clipped_in_for_node(
        &self,
        point: WorldPoint,
        node_index: ClipScrollNodeIndex,
        test: &mut HitTest
    ) -> bool {
        if let Some(point) = test.node_cache.get(&node_index) {
            return point.is_some();
        }

        let node = &self.nodes[node_index.0];
        let transform = node.world_viewport_transform;
        let transformed_point = match transform.inverse() {
            Some(inverted) => inverted.transform_point2d(&point),
            None => {
                test.node_cache.insert(node_index, None);
                return false;
            }
        };

        let point_in_layer = transformed_point - node.node_origin.to_vector();
        for region in &node.regions {
            if !region.contains(&transformed_point) {
                test.node_cache.insert(node_index, None);
                return false;
            }
        }

        test.node_cache.insert(node_index, Some(point_in_layer));
        true
    }

    pub fn hit_test(&self, mut test: HitTest) -> HitTestResult {
        let point = test.get_absolute_point(self);

        let mut result = HitTestResult::default();
        for &HitTestingRun(ref items, ref clip_and_scroll) in self.runs.iter().rev() {
            let scroll_node_id = clip_and_scroll.scroll_node_id;
            let scroll_node = &self.nodes[scroll_node_id.0];
            let pipeline_id = scroll_node.pipeline_id;
            match (test.pipeline_id, pipeline_id) {
                (Some(id), node_id) if node_id != id => continue,
                _ => {},
            }

            let transform = scroll_node.world_content_transform;
            let point_in_layer = match transform.inverse() {
                Some(inverted) => inverted.transform_point2d(&point),
                None => continue,
            };

            let mut clipped_in = false;
            for item in items.iter().rev() {
                if !item.rect.contains(&point_in_layer) ||
                    !item.clip_rect.contains(&point_in_layer) {
                    continue;
                }

                let clip_chain_index = clip_and_scroll.clip_chain_index;
                clipped_in |=
                    self.is_point_clipped_in_for_clip_chain(point, clip_chain_index, &mut test);
                if !clipped_in {
                    break;
                }

                // We need to trigger a lookup against the root reference frame here, because
                // items that are clipped by clip chains won't test against that part of the
                // hierarchy. If we don't have a valid point for this test, we are likely
                // in a situation where the reference frame has an univertible transform, but the
                // item's clip does not.
                let root_node_index = self.pipeline_root_nodes[&pipeline_id];
                if !self.is_point_clipped_in_for_node(point, root_node_index, &mut test) {
                    continue;
                }
                let point_in_viewport = match test.node_cache[&root_node_index] {
                    Some(point) => point,
                    None => continue,
                };

                result.items.push(HitTestItem {
                    pipeline: pipeline_id,
                    tag: item.tag,
                    point_in_viewport,
                    point_relative_to_item: point_in_layer - item.rect.origin.to_vector(),
                });
                if !test.flags.contains(HitTestFlags::FIND_ALL) {
                    return result;
                }
            }
        }

        result.items.dedup();
        result
    }

    pub fn get_pipeline_root(&self, pipeline_id: PipelineId) -> &HitTestClipScrollNode {
        &self.nodes[self.pipeline_root_nodes[&pipeline_id].0]
    }
}

fn get_regions_for_clip_scroll_node(
    node: &ClipScrollNode,
    clip_store: &ClipStore
) -> Vec<HitTestRegion> {
    let clips = match node.node_type {
        NodeType::Clip{ ref handle, .. } => clip_store.get(handle).clips(),
        _ => return Vec::new(),
    };

    clips.iter().map(|ref source| {
        match source.0 {
            ClipSource::Rectangle(ref rect) => HitTestRegion::Rectangle(*rect),
            ClipSource::RoundedRectangle(ref rect, ref radii, ref mode) =>
                HitTestRegion::RoundedRectangle(*rect, *radii, *mode),
            ClipSource::Image(ref mask) => HitTestRegion::Rectangle(mask.rect),
            ClipSource::BorderCorner(_) |
            ClipSource::LineDecoration(_) |
            ClipSource::BoxShadow(_) => {
                unreachable!("Didn't expect to hit test against BorderCorner / BoxShadow / LineDecoration");
            }
        }
    }).collect()
}

pub struct HitTest {
    pipeline_id: Option<PipelineId>,
    point: WorldPoint,
    flags: HitTestFlags,
    node_cache: FastHashMap<ClipScrollNodeIndex, Option<LayerPoint>>,
    clip_chain_cache: Vec<Option<bool>>,
}

impl HitTest {
    pub fn new(
        pipeline_id: Option<PipelineId>,
        point: WorldPoint,
        flags: HitTestFlags,
    ) -> HitTest {
        HitTest {
            pipeline_id,
            point,
            flags,
            node_cache: FastHashMap::default(),
            clip_chain_cache: Vec::new(),
        }
    }

    pub fn get_from_clip_chain_cache(&mut self, index: ClipChainIndex) -> Option<bool> {
        if index.0 >= self.clip_chain_cache.len() {
            None
        } else {
            self.clip_chain_cache[index.0]
        }
    }

    pub fn set_in_clip_chain_cache(&mut self, index: ClipChainIndex, value: bool) {
        if index.0 >= self.clip_chain_cache.len() {
            self.clip_chain_cache.resize(index.0 + 1, None);
        }
        self.clip_chain_cache[index.0] = Some(value);
    }

    pub fn get_absolute_point(&self, hit_tester: &HitTester) -> WorldPoint {
        if !self.flags.contains(HitTestFlags::POINT_RELATIVE_TO_PIPELINE_VIEWPORT) {
            return self.point;
        }

        let point =  &LayerPoint::new(self.point.x, self.point.y);
        self.pipeline_id.map(|id|
            hit_tester.get_pipeline_root(id).world_viewport_transform.transform_point2d(&point)
        ).unwrap_or_else(|| WorldPoint::new(self.point.x, self.point.y))
    }
}
<commit_msg>Handle backface-visibility:hidden in hit testing (Gecko bug 1447131)<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use api::{BorderRadius, ClipMode, HitTestFlags, HitTestItem, HitTestResult, ItemTag, LayerPoint};
use api::{LayerPrimitiveInfo, LayerRect, PipelineId, WorldPoint};
use clip::{ClipSource, ClipStore, rounded_rectangle_contains_point};
use clip_scroll_node::{ClipScrollNode, NodeType};
use clip_scroll_tree::{ClipChainIndex, ClipScrollNodeIndex, ClipScrollTree};
use internal_types::FastHashMap;
use prim_store::ScrollNodeAndClipChain;
use util::LayerToWorldFastTransform;

/// A copy of important clip scroll node data to use during hit testing. This a copy of
/// data from the ClipScrollTree that will persist as a new frame is under construction,
/// allowing hit tests consistent with the currently rendered frame.
pub struct HitTestClipScrollNode {
    /// The pipeline id of this node.
    pipeline_id: PipelineId,

    /// A particular point must be inside all of these regions to be considered clipped in
    /// for the purposes of a hit test.
    regions: Vec<HitTestRegion>,

    /// World transform for content transformed by this node.
    world_content_transform: LayerToWorldFastTransform,

    /// World viewport transform for content transformed by this node.
    world_viewport_transform: LayerToWorldFastTransform,

    /// Origin of the viewport of the node, used to calculate node-relative positions.
    node_origin: LayerPoint,
}

/// A description of a clip chain in the HitTester. This is used to describe
/// hierarchical clip scroll nodes as well as ClipChains, so that they can be
/// handled the same way during hit testing. Once we represent all ClipChains
/// using ClipChainDescriptors, we can get rid of this and just use the
/// ClipChainDescriptor here.
#[derive(Clone)]
struct HitTestClipChainDescriptor {
    parent: Option<ClipChainIndex>,
    clips: Vec<ClipScrollNodeIndex>,
}

impl HitTestClipChainDescriptor {
    fn empty() -> HitTestClipChainDescriptor {
        HitTestClipChainDescriptor {
            parent: None,
            clips: Vec::new(),
        }
    }
}

#[derive(Clone)]
pub struct HitTestingItem {
    rect: LayerRect,
    clip_rect: LayerRect,
    tag: ItemTag,
    is_backface_visible: bool,
}

impl HitTestingItem {
    pub fn new(tag: ItemTag, info: &LayerPrimitiveInfo) -> HitTestingItem {
        HitTestingItem {
            rect: info.rect,
            clip_rect: info.clip_rect,
            tag: tag,
            is_backface_visible: info.is_backface_visible,
        }
    }
}

#[derive(Clone)]
pub struct HitTestingRun(pub Vec<HitTestingItem>, pub ScrollNodeAndClipChain);

enum HitTestRegion {
    Rectangle(LayerRect),
    RoundedRectangle(LayerRect, BorderRadius, ClipMode),
}

impl HitTestRegion {
    pub fn contains(&self, point: &LayerPoint) -> bool {
        match self {
            &HitTestRegion::Rectangle(ref rectangle) => rectangle.contains(point),
            &HitTestRegion::RoundedRectangle(rect, radii, ClipMode::Clip) =>
                rounded_rectangle_contains_point(point, &rect, &radii),
            &HitTestRegion::RoundedRectangle(rect, radii, ClipMode::ClipOut) =>
                !rounded_rectangle_contains_point(point, &rect, &radii),
        }
    }
}

pub struct HitTester {
    runs: Vec<HitTestingRun>,
    nodes: Vec<HitTestClipScrollNode>,
    clip_chains: Vec<HitTestClipChainDescriptor>,
    pipeline_root_nodes: FastHashMap<PipelineId, ClipScrollNodeIndex>,
}

impl HitTester {
    pub fn new(
        runs: &Vec<HitTestingRun>,
        clip_scroll_tree: &ClipScrollTree,
        clip_store: &ClipStore
    ) -> HitTester {
        let mut hit_tester = HitTester {
            runs: runs.clone(),
            nodes: Vec::new(),
            clip_chains: Vec::new(),
            pipeline_root_nodes: FastHashMap::default(),
        };
        hit_tester.read_clip_scroll_tree(clip_scroll_tree, clip_store);
        hit_tester
    }

    fn read_clip_scroll_tree(
        &mut self,
        clip_scroll_tree: &ClipScrollTree,
        clip_store: &ClipStore
    ) {
        self.nodes.clear();
        self.clip_chains.clear();
        self.clip_chains.resize(
            clip_scroll_tree.clip_chains.len(),
            HitTestClipChainDescriptor::empty()
        );

        for (index, node) in clip_scroll_tree.nodes.iter().enumerate() {
            let index = ClipScrollNodeIndex(index);

            // If we haven't already seen a node for this pipeline, record this one as the root
            // node.
            self.pipeline_root_nodes.entry(node.pipeline_id).or_insert(index);

            self.nodes.push(HitTestClipScrollNode {
                pipeline_id: node.pipeline_id,
                regions: get_regions_for_clip_scroll_node(node, clip_store),
                world_content_transform: node.world_content_transform,
                world_viewport_transform: node.world_viewport_transform,
                node_origin: node.local_viewport_rect.origin,
            });

            if let NodeType::Clip { clip_chain_index, .. } = node.node_type {
              let clip_chain = self.clip_chains.get_mut(clip_chain_index.0).unwrap();
              clip_chain.parent =
                  clip_scroll_tree.get_clip_chain(clip_chain_index).parent_index;
              clip_chain.clips = vec![index];
            }
        }

        for descriptor in &clip_scroll_tree.clip_chains_descriptors {
            let clip_chain = self.clip_chains.get_mut(descriptor.index.0).unwrap();
            clip_chain.parent = clip_scroll_tree.get_clip_chain(descriptor.index).parent_index;
            clip_chain.clips = descriptor.clips.clone();
        }
    }

    fn is_point_clipped_in_for_clip_chain(
        &self,
        point: WorldPoint,
        clip_chain_index: ClipChainIndex,
        test: &mut HitTest
    ) -> bool {
        if let Some(result) = test.get_from_clip_chain_cache(clip_chain_index) {
            return result;
        }

        let descriptor = &self.clip_chains[clip_chain_index.0];
        let parent_clipped_in = match descriptor.parent {
            None => true,
            Some(parent) => self.is_point_clipped_in_for_clip_chain(point, parent, test),
        };

        if !parent_clipped_in {
            test.set_in_clip_chain_cache(clip_chain_index, false);
            return false;
        }

        for clip_node_index in &descriptor.clips {
            if !self.is_point_clipped_in_for_node(point, *clip_node_index, test) {
                test.set_in_clip_chain_cache(clip_chain_index, false);
                return false;
            }
        }

        test.set_in_clip_chain_cache(clip_chain_index, true);
        true
    }

    fn is_point_clipped_in_for_node(
        &self,
        point: WorldPoint,
        node_index: ClipScrollNodeIndex,
        test: &mut HitTest
    ) -> bool {
        if let Some(point) = test.node_cache.get(&node_index) {
            return point.is_some();
        }

        let node = &self.nodes[node_index.0];
        let transform = node.world_viewport_transform;
        let transformed_point = match transform.inverse() {
            Some(inverted) => inverted.transform_point2d(&point),
            None => {
                test.node_cache.insert(node_index, None);
                return false;
            }
        };

        let point_in_layer = transformed_point - node.node_origin.to_vector();
        for region in &node.regions {
            if !region.contains(&transformed_point) {
                test.node_cache.insert(node_index, None);
                return false;
            }
        }

        test.node_cache.insert(node_index, Some(point_in_layer));
        true
    }

    pub fn hit_test(&self, mut test: HitTest) -> HitTestResult {
        let point = test.get_absolute_point(self);

        let mut result = HitTestResult::default();
        for &HitTestingRun(ref items, ref clip_and_scroll) in self.runs.iter().rev() {
            let scroll_node_id = clip_and_scroll.scroll_node_id;
            let scroll_node = &self.nodes[scroll_node_id.0];
            let pipeline_id = scroll_node.pipeline_id;
            match (test.pipeline_id, pipeline_id) {
                (Some(id), node_id) if node_id != id => continue,
                _ => {},
            }

            let transform = scroll_node.world_content_transform;
            let mut facing_backwards: Option<bool> = None;  // will be computed on first use
            let point_in_layer = match transform.inverse() {
                Some(inverted) => inverted.transform_point2d(&point),
                None => continue,
            };

            let mut clipped_in = false;
            for item in items.iter().rev() {
                if !item.rect.contains(&point_in_layer) ||
                    !item.clip_rect.contains(&point_in_layer) {
                    continue;
                }

                let clip_chain_index = clip_and_scroll.clip_chain_index;
                clipped_in |=
                    self.is_point_clipped_in_for_clip_chain(point, clip_chain_index, &mut test);
                if !clipped_in {
                    break;
                }

                // We need to trigger a lookup against the root reference frame here, because
                // items that are clipped by clip chains won't test against that part of the
                // hierarchy. If we don't have a valid point for this test, we are likely
                // in a situation where the reference frame has an univertible transform, but the
                // item's clip does not.
                let root_node_index = self.pipeline_root_nodes[&pipeline_id];
                if !self.is_point_clipped_in_for_node(point, root_node_index, &mut test) {
                    continue;
                }
                let point_in_viewport = match test.node_cache[&root_node_index] {
                    Some(point) => point,
                    None => continue,
                };

                // Don't hit items with backface-visibility:hidden if they are facing the back.
                if !item.is_backface_visible {
                    if *facing_backwards.get_or_insert_with(|| transform.is_backface_visible()) {
                        continue;
                    }
                }

                result.items.push(HitTestItem {
                    pipeline: pipeline_id,
                    tag: item.tag,
                    point_in_viewport,
                    point_relative_to_item: point_in_layer - item.rect.origin.to_vector(),
                });
                if !test.flags.contains(HitTestFlags::FIND_ALL) {
                    return result;
                }
            }
        }

        result.items.dedup();
        result
    }

    pub fn get_pipeline_root(&self, pipeline_id: PipelineId) -> &HitTestClipScrollNode {
        &self.nodes[self.pipeline_root_nodes[&pipeline_id].0]
    }
}

fn get_regions_for_clip_scroll_node(
    node: &ClipScrollNode,
    clip_store: &ClipStore
) -> Vec<HitTestRegion> {
    let clips = match node.node_type {
        NodeType::Clip{ ref handle, .. } => clip_store.get(handle).clips(),
        _ => return Vec::new(),
    };

    clips.iter().map(|ref source| {
        match source.0 {
            ClipSource::Rectangle(ref rect) => HitTestRegion::Rectangle(*rect),
            ClipSource::RoundedRectangle(ref rect, ref radii, ref mode) =>
                HitTestRegion::RoundedRectangle(*rect, *radii, *mode),
            ClipSource::Image(ref mask) => HitTestRegion::Rectangle(mask.rect),
            ClipSource::BorderCorner(_) |
            ClipSource::LineDecoration(_) |
            ClipSource::BoxShadow(_) => {
                unreachable!("Didn't expect to hit test against BorderCorner / BoxShadow / LineDecoration");
            }
        }
    }).collect()
}

pub struct HitTest {
    pipeline_id: Option<PipelineId>,
    point: WorldPoint,
    flags: HitTestFlags,
    node_cache: FastHashMap<ClipScrollNodeIndex, Option<LayerPoint>>,
    clip_chain_cache: Vec<Option<bool>>,
}

impl HitTest {
    pub fn new(
        pipeline_id: Option<PipelineId>,
        point: WorldPoint,
        flags: HitTestFlags,
    ) -> HitTest {
        HitTest {
            pipeline_id,
            point,
            flags,
            node_cache: FastHashMap::default(),
            clip_chain_cache: Vec::new(),
        }
    }

    pub fn get_from_clip_chain_cache(&mut self, index: ClipChainIndex) -> Option<bool> {
        if index.0 >= self.clip_chain_cache.len() {
            None
        } else {
            self.clip_chain_cache[index.0]
        }
    }

    pub fn set_in_clip_chain_cache(&mut self, index: ClipChainIndex, value: bool) {
        if index.0 >= self.clip_chain_cache.len() {
            self.clip_chain_cache.resize(index.0 + 1, None);
        }
        self.clip_chain_cache[index.0] = Some(value);
    }

    pub fn get_absolute_point(&self, hit_tester: &HitTester) -> WorldPoint {
        if !self.flags.contains(HitTestFlags::POINT_RELATIVE_TO_PIPELINE_VIEWPORT) {
            return self.point;
        }

        let point =  &LayerPoint::new(self.point.x, self.point.y);
        self.pipeline_id.map(|id|
            hit_tester.get_pipeline_root(id).world_viewport_transform.transform_point2d(&point)
        ).unwrap_or_else(|| WorldPoint::new(self.point.x, self.point.y))
    }
}
<|endoftext|>


<commit_before>use std::{
    io::{stdin, stdout, BufRead, Write},
    thread::sleep,
    time::Duration,
};

use isahc::{
    auth::{Authentication, Credentials},
    config::Configurable,
    http::StatusCode,
    HttpClient, ReadResponseExt, Request,
};
use semver::{Identifier, Version};
use serde::Deserialize;
use serde_json::json;

use crate::{cargo::Package, cmd, util::ask_yes_no, GithubConfig, Metadata, Result};

const GITHUB_API_RUMA: &str = "https://api.github.com/repos/ruma/ruma";

/// Task to create a new release of the given crate.
#[derive(Debug)]
pub struct ReleaseTask {
    /// The metadata of the cargo workspace.
    metadata: Metadata,

    /// The crate to release.
    package: Package,

    /// The new version of the crate.
    version: Version,

    /// The http client to use for requests.
    http_client: HttpClient,

    /// The github configuration required to publish a release.
    config: GithubConfig,
}

impl ReleaseTask {
    /// Create a new `ReleaseTask` with the given `name` and `version`.
    pub(crate) fn new(name: String, version: Version) -> Result<Self> {
        let metadata = Metadata::load()?;

        let package = metadata
            .packages
            .clone()
            .into_iter()
            .find(|p| p.name == name)
            .ok_or(format!("Package {} not found in cargo metadata", name))?;

        let config = crate::Config::load()?.github;

        let http_client = HttpClient::new()?;

        Ok(Self { metadata, package, version, http_client, config })
    }

    /// Run the task to effectively create a release.
    pub(crate) fn run(&mut self) -> Result<()> {
        let title = &self.title();
        let prerelease = self.version.is_prerelease();
        let publish_only = self.package.name == "ruma-identifiers-validation";

        if let Some(name) = self.package.name.strip_suffix("-macros") {
            return Err(format!(
                "Macro crates are always released together with their parent crate.\n\
                 To release both {main_cr} and {macro_cr}, simply run\n\
                 \n\
                 cargo xtask release {main_cr}",
                main_cr = name,
                macro_cr = self.package.name,
            )
            .into());
        }

        println!(
            "Starting {} for {}…",
            match prerelease {
                true => "pre-release",
                false => "release",
            },
            title
        );

        if self.is_released()? {
            return Err("This crate version is already released".into());
        }

        let remote = Self::git_remote()?;

        println!("Checking status of git repository…");
        if !cmd!("git status -s -uno").read()?.is_empty()
            && !ask_yes_no("This git repository contains uncommitted changes. Continue?")?
        {
            return Ok(());
        }

        if self.package.version != self.version
            && !self.package.version.is_next(&self.version)
            && !ask_yes_no(&format!(
                "Version {} should not follow version {}. Do you really want to continue?",
                self.version, self.package.version,
            ))?
        {
            return Ok(());
        }

        let mut macros = self.macros();

        if let Some(m) = macros.as_mut() {
            println!("Found macros crate {}.", m.name);

            m.update_version(&self.version)?;
            m.update_dependants(&self.metadata)?;

            println!("Resuming release of {}…", self.title());
        }

        self.package.update_version(&self.version)?;
        self.package.update_dependants(&self.metadata)?;

        let changes = &self.package.changes(!prerelease)?;

        self.commit()?;

        if let Some(m) = macros {
            let published = m.publish(&self.http_client)?;

            if published {
                // Crate was published, instead of publishing skipped (because release already
                // existed).
                println!("Waiting 20 seconds for the release to make it into the crates.io index…");
                sleep(Duration::from_secs(20));
            }
        }

        self.package.publish(&self.http_client)?;

        let branch = cmd!("git rev-parse --abbrev-ref HEAD").read()?;
        if publish_only {
            println!("Pushing to remote repository…");
            cmd!("git push {remote} {branch}").run()?;

            println!("Crate published successfully!");
            return Ok(());
        }

        let tag = &self.tag_name();

        println!("Creating git tag…");
        if cmd!("git tag -l {tag}").read()?.is_empty() {
            cmd!("git tag -s {tag} -m {title} -m {changes}").read()?;
        } else if !ask_yes_no("This tag already exists. Skip this step and continue?")? {
            return Ok(());
        }

        println!("Pushing to remote repository…");
        if cmd!("git ls-remote --tags {remote} {tag}").read()?.is_empty() {
            cmd!("git push {remote} {branch} {tag}").run()?;
        } else if !ask_yes_no("This tag has already been pushed. Skip this step and continue?")? {
            return Ok(());
        }

        if prerelease {
            println!("Pre-release created successfully!");
            return Ok(());
        }

        println!("Creating release on GitHub…");
        let request_body = &json!({
            "tag_name": tag,
            "name": title,
            "body": changes.trim_softbreaks(),
        })
        .to_string();

        self.release(request_body)?;

        println!("Release created successfully!");

        Ok(())
    }

    /// Get the associated `-macros` crate of the current crate, if any.
    fn macros(&self) -> Option<Package> {
        self.metadata
            .packages
            .clone()
            .into_iter()
            .find(|p| p.name == format!("{}-macros", self.package.name))
    }

    /// Get the title of this release.
    fn title(&self) -> String {
        format!("{} {}", self.package.name, self.version)
    }

    /// Get the tag name for this release.
    fn tag_name(&self) -> String {
        format!("{}-{}", self.package.name, self.version)
    }

    /// Load the GitHub config from the config file.
    fn git_remote() -> Result<String> {
        let branch = cmd!("git rev-parse --abbrev-ref HEAD").read()?;
        let remote = cmd!("git config branch.{branch}.remote").read()?;

        if remote.is_empty() {
            return Err("Could not get current git remote".into());
        }

        Ok(remote)
    }

    /// Commit and push all the changes in the git repository.
    fn commit(&self) -> Result<()> {
        let stdin = stdin();

        let instructions = "Ready to commit the changes. [continue/abort/diff]: ";
        print!("{}", instructions);
        stdout().flush()?;

        let mut handle = stdin.lock();

        let mut input = String::new();
        loop {
            let eof = handle.read_line(&mut input)? == 0;
            if eof {
                return Err("User aborted commit".into());
            }

            match input.trim().to_ascii_lowercase().as_str() {
                "c" | "con" | "continue" => {
                    break;
                }
                "a" | "abort" => {
                    return Err("User aborted commit".into());
                }
                "d" | "diff" => {
                    cmd!("git diff").run()?;
                }
                _ => {
                    println!("Unknown command.");
                }
            }
            print!("{}", instructions);
            stdout().flush()?;

            input.clear();
        }

        let message = format!("Release {}", self.title());

        println!("Creating commit…");
        cmd!("git commit -a -m {message}").read()?;

        Ok(())
    }

    /// Check if the tag for the current version of the crate has been pushed on GitHub.
    fn is_released(&self) -> Result<bool> {
        let response = self.http_client.get(format!(
            "{}/releases/tags/{}",
            GITHUB_API_RUMA,
            self.tag_name()
        ))?;

        Ok(response.status() == StatusCode::OK)
    }

    /// Create the release on GitHub with the given `config` and `credentials`.
    fn release(&self, body: &str) -> Result<()> {
        let request = Request::post(format!("{}/releases", GITHUB_API_RUMA))
            .authentication(Authentication::basic())
            .credentials(Credentials::new(&self.config.user, &self.config.token))
            .header("Accept", "application/vnd.github.v3+json")
            .body(body)?;

        let mut response = self.http_client.send(request)?;

        if response.status() == StatusCode::CREATED {
            Ok(())
        } else {
            Err(format!("{}: {}", response.status(), response.text()?).into())
        }
    }
}

/// A tag from the `GET /repos/{owner}/{repo}/tags` endpoint of GitHub REST API.
#[derive(Debug, Deserialize)]
struct GithubTag {
    /// The name of the tag.
    name: String,
}

/// String manipulations for crate release.
trait StrExt {
    /// Remove soft line breaks as defined in CommonMark spec.
    fn trim_softbreaks(&self) -> String;
}

impl StrExt for str {
    fn trim_softbreaks(&self) -> String {
        let mut string = String::new();
        let mut s = self;

        while let Some(pos) = s.find('\n') {
            string.push_str(&s[..pos]);
            let pos_s = &s[pos..];

            if pos_s.starts_with("\n\n") {
                // Keep new paragraphs (multiple `\n`s).
                let next = pos_s.find(|c: char| c != '\n').unwrap_or(0);
                let (push, next_s) = pos_s.split_at(next);

                string.push_str(push);
                s = next_s;
            } else if s[..pos].ends_with("  ") || s[..pos].ends_with('\\') {
                // Keep hard line breaks (two spaces or a backslash before the line break).
                string.push('\n');
                s = &pos_s[1..];
            } else if let Some(p) = pos_s.find(|c: char| !c.is_ascii_whitespace()) {
                // Keep line break before list items (`\n` + whitespaces + `*` + whitespaces).
                // Remove line break and keep one space otherwise.
                let mut chars = pos_s.char_indices();
                let (_, char) = chars.find(|(i, _)| *i == p).unwrap();

                if char == '*' || char == '-' {
                    match chars.next() {
                        Some((_, next_char)) if next_char.is_ascii_whitespace() => {
                            string.push('\n');
                            s = &pos_s[1..];
                            continue;
                        }
                        _ => {}
                    }
                }

                string.push(' ');
                s = &pos_s[p..];
            }
        }

        string + s
    }
}

/// Extra Version increment methods for crate release.
trait VersionExt {
    /// Adds a pre-release label and number if there is none.
    fn add_pre_release(&mut self);

    /// Increments the pre-release number, if this is a pre-release.
    fn increment_pre_number(&mut self);

    /// Increments the pre-release label from `alpha` to `beta` if this is a pre-release and it is
    /// possible, otherwise does nothing.
    fn increment_pre_label(&mut self);

    /// If the given version can be the next after this one.
    ///
    /// This checks all the version bumps of the format MAJOR.MINOR.PATCH-PRE_LABEL.PRE_NUMBER, with
    /// PRE_LABEL = alpha or beta.
    fn is_next(&self, version: &Version) -> bool;
}

impl VersionExt for Version {
    fn add_pre_release(&mut self) {
        if !self.is_prerelease() {
            self.pre = vec![Identifier::AlphaNumeric("alpha".into()), Identifier::Numeric(1)];
        }
    }

    fn increment_pre_number(&mut self) {
        if self.is_prerelease() {
            if let Identifier::Numeric(n) = self.pre[1] {
                self.pre[1] = Identifier::Numeric(n + 1);
            }
        }
    }

    fn increment_pre_label(&mut self) {
        if self.is_prerelease() {
            match &self.pre[0] {
                Identifier::AlphaNumeric(n) if n == "alpha" => {
                    self.pre =
                        vec![Identifier::AlphaNumeric("beta".into()), Identifier::Numeric(1)];
                }
                _ => {}
            }
        }
    }

    fn is_next(&self, version: &Version) -> bool {
        let mut next = self.clone();

        if self.is_prerelease() {
            next.increment_pre_number();
            if next == *version {
                return true;
            }

            next.increment_pre_label();
            if next == *version {
                return true;
            }

            next.pre = vec![];
            if next == *version {
                return true;
            }
        } else {
            next.increment_patch();
            if next == *version {
                return true;
            }

            next.add_pre_release();
            if next == *version {
                return true;
            }

            next.increment_minor();
            if next == *version {
                return true;
            }

            next.add_pre_release();
            if next == *version {
                return true;
            }

            next.increment_major();
            if next == *version {
                return true;
            }

            next.add_pre_release();
            if next == *version {
                return true;
            }
        }

        false
    }
}
<commit_msg>xtask: Fix release creating an empty commit on existing release<commit_after>use std::{
    io::{stdin, stdout, BufRead, Write},
    thread::sleep,
    time::Duration,
};

use isahc::{
    auth::{Authentication, Credentials},
    config::Configurable,
    http::StatusCode,
    HttpClient, ReadResponseExt, Request,
};
use semver::{Identifier, Version};
use serde::Deserialize;
use serde_json::json;

use crate::{cargo::Package, cmd, util::ask_yes_no, GithubConfig, Metadata, Result};

const GITHUB_API_RUMA: &str = "https://api.github.com/repos/ruma/ruma";

/// Task to create a new release of the given crate.
#[derive(Debug)]
pub struct ReleaseTask {
    /// The metadata of the cargo workspace.
    metadata: Metadata,

    /// The crate to release.
    package: Package,

    /// The new version of the crate.
    version: Version,

    /// The http client to use for requests.
    http_client: HttpClient,

    /// The github configuration required to publish a release.
    config: GithubConfig,
}

impl ReleaseTask {
    /// Create a new `ReleaseTask` with the given `name` and `version`.
    pub(crate) fn new(name: String, version: Version) -> Result<Self> {
        let metadata = Metadata::load()?;

        let package = metadata
            .packages
            .clone()
            .into_iter()
            .find(|p| p.name == name)
            .ok_or(format!("Package {} not found in cargo metadata", name))?;

        let config = crate::Config::load()?.github;

        let http_client = HttpClient::new()?;

        Ok(Self { metadata, package, version, http_client, config })
    }

    /// Run the task to effectively create a release.
    pub(crate) fn run(&mut self) -> Result<()> {
        let title = &self.title();
        let prerelease = self.version.is_prerelease();
        let publish_only = self.package.name == "ruma-identifiers-validation";

        if let Some(name) = self.package.name.strip_suffix("-macros") {
            return Err(format!(
                "Macro crates are always released together with their parent crate.\n\
                 To release both {main_cr} and {macro_cr}, simply run\n\
                 \n\
                 cargo xtask release {main_cr}",
                main_cr = name,
                macro_cr = self.package.name,
            )
            .into());
        }

        println!(
            "Starting {} for {}…",
            match prerelease {
                true => "pre-release",
                false => "release",
            },
            title
        );

        if self.is_released()? {
            return Err("This crate version is already released".into());
        }

        let remote = Self::git_remote()?;

        println!("Checking status of git repository…");
        if !cmd!("git status -s -uno").read()?.is_empty()
            && !ask_yes_no("This git repository contains uncommitted changes. Continue?")?
        {
            return Ok(());
        }

        if self.package.version != self.version
            && !self.package.version.is_next(&self.version)
            && !ask_yes_no(&format!(
                "Version {} should not follow version {}. Do you really want to continue?",
                self.version, self.package.version,
            ))?
        {
            return Ok(());
        }

        let mut macros = self.macros();

        if self.package.version != self.version {
            if let Some(m) = macros.as_mut() {
                println!("Found macros crate {}.", m.name);

                m.update_version(&self.version)?;
                m.update_dependants(&self.metadata)?;

                println!("Resuming release of {}…", self.title());
            }

            self.package.update_version(&self.version)?;
            self.package.update_dependants(&self.metadata)?;
        }

        let changes = &self.package.changes(!prerelease)?;

        if self.package.version != self.version {
            self.commit()?;
        } else if !ask_yes_no(&format!(
            "Package is already version {}. Skip creating a commit and continue?",
            &self.version
        ))? {
            return Ok(());
        }

        if let Some(m) = macros {
            let published = m.publish(&self.http_client)?;

            if published {
                // Crate was published, instead of publishing skipped (because release already
                // existed).
                println!("Waiting 20 seconds for the release to make it into the crates.io index…");
                sleep(Duration::from_secs(20));
            }
        }

        self.package.publish(&self.http_client)?;

        let branch = cmd!("git rev-parse --abbrev-ref HEAD").read()?;
        if publish_only {
            println!("Pushing to remote repository…");
            cmd!("git push {remote} {branch}").run()?;

            println!("Crate published successfully!");
            return Ok(());
        }

        let tag = &self.tag_name();

        println!("Creating git tag…");
        if cmd!("git tag -l {tag}").read()?.is_empty() {
            cmd!("git tag -s {tag} -m {title} -m {changes}").read()?;
        } else if !ask_yes_no("This tag already exists. Skip this step and continue?")? {
            return Ok(());
        }

        println!("Pushing to remote repository…");
        if cmd!("git ls-remote --tags {remote} {tag}").read()?.is_empty() {
            cmd!("git push {remote} {branch} {tag}").run()?;
        } else if !ask_yes_no("This tag has already been pushed. Skip this step and continue?")? {
            return Ok(());
        }

        if prerelease {
            println!("Pre-release created successfully!");
            return Ok(());
        }

        println!("Creating release on GitHub…");
        let request_body = &json!({
            "tag_name": tag,
            "name": title,
            "body": changes.trim_softbreaks(),
        })
        .to_string();

        self.release(request_body)?;

        println!("Release created successfully!");

        Ok(())
    }

    /// Get the associated `-macros` crate of the current crate, if any.
    fn macros(&self) -> Option<Package> {
        self.metadata
            .packages
            .clone()
            .into_iter()
            .find(|p| p.name == format!("{}-macros", self.package.name))
    }

    /// Get the title of this release.
    fn title(&self) -> String {
        format!("{} {}", self.package.name, self.version)
    }

    /// Get the tag name for this release.
    fn tag_name(&self) -> String {
        format!("{}-{}", self.package.name, self.version)
    }

    /// Load the GitHub config from the config file.
    fn git_remote() -> Result<String> {
        let branch = cmd!("git rev-parse --abbrev-ref HEAD").read()?;
        let remote = cmd!("git config branch.{branch}.remote").read()?;

        if remote.is_empty() {
            return Err("Could not get current git remote".into());
        }

        Ok(remote)
    }

    /// Commit and push all the changes in the git repository.
    fn commit(&self) -> Result<()> {
        let stdin = stdin();

        let instructions = "Ready to commit the changes. [continue/abort/diff]: ";
        print!("{}", instructions);
        stdout().flush()?;

        let mut handle = stdin.lock();

        let mut input = String::new();
        loop {
            let eof = handle.read_line(&mut input)? == 0;
            if eof {
                return Err("User aborted commit".into());
            }

            match input.trim().to_ascii_lowercase().as_str() {
                "c" | "con" | "continue" => {
                    break;
                }
                "a" | "abort" => {
                    return Err("User aborted commit".into());
                }
                "d" | "diff" => {
                    cmd!("git diff").run()?;
                }
                _ => {
                    println!("Unknown command.");
                }
            }
            print!("{}", instructions);
            stdout().flush()?;

            input.clear();
        }

        let message = format!("Release {}", self.title());

        println!("Creating commit…");
        cmd!("git commit -a -m {message}").read()?;

        Ok(())
    }

    /// Check if the tag for the current version of the crate has been pushed on GitHub.
    fn is_released(&self) -> Result<bool> {
        let response = self.http_client.get(format!(
            "{}/releases/tags/{}",
            GITHUB_API_RUMA,
            self.tag_name()
        ))?;

        Ok(response.status() == StatusCode::OK)
    }

    /// Create the release on GitHub with the given `config` and `credentials`.
    fn release(&self, body: &str) -> Result<()> {
        let request = Request::post(format!("{}/releases", GITHUB_API_RUMA))
            .authentication(Authentication::basic())
            .credentials(Credentials::new(&self.config.user, &self.config.token))
            .header("Accept", "application/vnd.github.v3+json")
            .body(body)?;

        let mut response = self.http_client.send(request)?;

        if response.status() == StatusCode::CREATED {
            Ok(())
        } else {
            Err(format!("{}: {}", response.status(), response.text()?).into())
        }
    }
}

/// A tag from the `GET /repos/{owner}/{repo}/tags` endpoint of GitHub REST API.
#[derive(Debug, Deserialize)]
struct GithubTag {
    /// The name of the tag.
    name: String,
}

/// String manipulations for crate release.
trait StrExt {
    /// Remove soft line breaks as defined in CommonMark spec.
    fn trim_softbreaks(&self) -> String;
}

impl StrExt for str {
    fn trim_softbreaks(&self) -> String {
        let mut string = String::new();
        let mut s = self;

        while let Some(pos) = s.find('\n') {
            string.push_str(&s[..pos]);
            let pos_s = &s[pos..];

            if pos_s.starts_with("\n\n") {
                // Keep new paragraphs (multiple `\n`s).
                let next = pos_s.find(|c: char| c != '\n').unwrap_or(0);
                let (push, next_s) = pos_s.split_at(next);

                string.push_str(push);
                s = next_s;
            } else if s[..pos].ends_with("  ") || s[..pos].ends_with('\\') {
                // Keep hard line breaks (two spaces or a backslash before the line break).
                string.push('\n');
                s = &pos_s[1..];
            } else if let Some(p) = pos_s.find(|c: char| !c.is_ascii_whitespace()) {
                // Keep line break before list items (`\n` + whitespaces + `*` + whitespaces).
                // Remove line break and keep one space otherwise.
                let mut chars = pos_s.char_indices();
                let (_, char) = chars.find(|(i, _)| *i == p).unwrap();

                if char == '*' || char == '-' {
                    match chars.next() {
                        Some((_, next_char)) if next_char.is_ascii_whitespace() => {
                            string.push('\n');
                            s = &pos_s[1..];
                            continue;
                        }
                        _ => {}
                    }
                }

                string.push(' ');
                s = &pos_s[p..];
            }
        }

        string + s
    }
}

/// Extra Version increment methods for crate release.
trait VersionExt {
    /// Adds a pre-release label and number if there is none.
    fn add_pre_release(&mut self);

    /// Increments the pre-release number, if this is a pre-release.
    fn increment_pre_number(&mut self);

    /// Increments the pre-release label from `alpha` to `beta` if this is a pre-release and it is
    /// possible, otherwise does nothing.
    fn increment_pre_label(&mut self);

    /// If the given version can be the next after this one.
    ///
    /// This checks all the version bumps of the format MAJOR.MINOR.PATCH-PRE_LABEL.PRE_NUMBER, with
    /// PRE_LABEL = alpha or beta.
    fn is_next(&self, version: &Version) -> bool;
}

impl VersionExt for Version {
    fn add_pre_release(&mut self) {
        if !self.is_prerelease() {
            self.pre = vec![Identifier::AlphaNumeric("alpha".into()), Identifier::Numeric(1)];
        }
    }

    fn increment_pre_number(&mut self) {
        if self.is_prerelease() {
            if let Identifier::Numeric(n) = self.pre[1] {
                self.pre[1] = Identifier::Numeric(n + 1);
            }
        }
    }

    fn increment_pre_label(&mut self) {
        if self.is_prerelease() {
            match &self.pre[0] {
                Identifier::AlphaNumeric(n) if n == "alpha" => {
                    self.pre =
                        vec![Identifier::AlphaNumeric("beta".into()), Identifier::Numeric(1)];
                }
                _ => {}
            }
        }
    }

    fn is_next(&self, version: &Version) -> bool {
        let mut next = self.clone();

        if self.is_prerelease() {
            next.increment_pre_number();
            if next == *version {
                return true;
            }

            next.increment_pre_label();
            if next == *version {
                return true;
            }

            next.pre = vec![];
            if next == *version {
                return true;
            }
        } else {
            next.increment_patch();
            if next == *version {
                return true;
            }

            next.add_pre_release();
            if next == *version {
                return true;
            }

            next.increment_minor();
            if next == *version {
                return true;
            }

            next.add_pre_release();
            if next == *version {
                return true;
            }

            next.increment_major();
            if next == *version {
                return true;
            }

            next.add_pre_release();
            if next == *version {
                return true;
            }
        }

        false
    }
}
<|endoftext|>


<commit_before>use xshell::{cmd, cp, pushd, read_dir, write_file};

use crate::{codegen, date_iso, is_release_tag, project_root, Mode, Result};

pub struct ReleaseCmd {
    pub dry_run: bool,
}

impl ReleaseCmd {
    pub fn run(self) -> Result<()> {
        if !self.dry_run {
            cmd!("git switch release").run()?;
            cmd!("git fetch upstream --tags --force").run()?;
            cmd!("git reset --hard tags/nightly").run()?;
            cmd!("git push").run()?;
        }
        codegen::generate_assists_docs(Mode::Overwrite)?;
        codegen::generate_feature_docs(Mode::Overwrite)?;

        let website_root = project_root().join("../rust-analyzer.github.io");
        let changelog_dir = website_root.join("./thisweek/_posts");

        let today = date_iso()?;
        let commit = cmd!("git rev-parse HEAD").read()?;
        let changelog_n = read_dir(changelog_dir.as_path())?.len();

        let contents = format!(
            "\
= Changelog #{}
:sectanchors:
:page-layout: post

Commit: commit:{}[] +
Release: release:{}[]

== Sponsors

**Become a sponsor:** On https://opencollective.com/rust-analyzer/[OpenCollective] or
https://github.com/sponsors/rust-analyzer[GitHub Sponsors].

== New Features

* pr:[] .

== Fixes

== Internal Improvements
",
            changelog_n, commit, today
        );

        let path = changelog_dir.join(format!("{}-changelog-{}.adoc", today, changelog_n));
        write_file(&path, &contents)?;

        for &adoc in [
            "manual.adoc",
            "generated_assists.adoc",
            "generated_config.adoc",
            "generated_diagnostic.adoc",
            "generated_features.adoc",
        ]
        .iter()
        {
            let src = project_root().join("./docs/user/").join(adoc);
            let dst = website_root.join(adoc);
            cp(src, dst)?;
        }

        let tags = cmd!("git tag --list").read()?;
        let prev_tag = tags.lines().filter(|line| is_release_tag(line)).last().unwrap();

        let git_log = cmd!("git log {prev_tag}..HEAD --merges --reverse").read()?;
        let git_log_dst = website_root.join("git.log");
        write_file(git_log_dst, &git_log)?;

        Ok(())
    }
}

pub struct PromoteCmd {
    pub dry_run: bool,
}

impl PromoteCmd {
    pub fn run(self) -> Result<()> {
        let _dir = pushd("../rust-rust-analyzer")?;
        cmd!("git switch master").run()?;
        cmd!("git fetch upstream").run()?;
        cmd!("git reset --hard upstream/master").run()?;
        cmd!("git submodule update --recursive").run()?;

        let branch = format!("rust-analyzer-{}", date_iso()?);
        cmd!("git switch -c {branch}").run()?;
        {
            let _dir = pushd("src/tools/rust-analyzer")?;
            cmd!("git fetch origin").run()?;
            cmd!("git reset --hard origin/release").run()?;
        }
        cmd!("git add src/tools/rust-analyzer").run()?;
        cmd!("git commit -m':arrow_up: rust-analyzer'").run()?;
        if !self.dry_run {
            cmd!("git push").run()?;
            cmd!("xdg-open https://github.com/matklad/rust/pull/new/{branch}?body=r%3F%20%40ghost")
                .run()?;
        }
        Ok(())
    }
}
<commit_msg>Track remote branch on xtask promote<commit_after>use xshell::{cmd, cp, pushd, read_dir, write_file};

use crate::{codegen, date_iso, is_release_tag, project_root, Mode, Result};

pub struct ReleaseCmd {
    pub dry_run: bool,
}

impl ReleaseCmd {
    pub fn run(self) -> Result<()> {
        if !self.dry_run {
            cmd!("git switch release").run()?;
            cmd!("git fetch upstream --tags --force").run()?;
            cmd!("git reset --hard tags/nightly").run()?;
            cmd!("git push").run()?;
        }
        codegen::generate_assists_docs(Mode::Overwrite)?;
        codegen::generate_feature_docs(Mode::Overwrite)?;

        let website_root = project_root().join("../rust-analyzer.github.io");
        let changelog_dir = website_root.join("./thisweek/_posts");

        let today = date_iso()?;
        let commit = cmd!("git rev-parse HEAD").read()?;
        let changelog_n = read_dir(changelog_dir.as_path())?.len();

        let contents = format!(
            "\
= Changelog #{}
:sectanchors:
:page-layout: post

Commit: commit:{}[] +
Release: release:{}[]

== Sponsors

**Become a sponsor:** On https://opencollective.com/rust-analyzer/[OpenCollective] or
https://github.com/sponsors/rust-analyzer[GitHub Sponsors].

== New Features

* pr:[] .

== Fixes

== Internal Improvements
",
            changelog_n, commit, today
        );

        let path = changelog_dir.join(format!("{}-changelog-{}.adoc", today, changelog_n));
        write_file(&path, &contents)?;

        for &adoc in [
            "manual.adoc",
            "generated_assists.adoc",
            "generated_config.adoc",
            "generated_diagnostic.adoc",
            "generated_features.adoc",
        ]
        .iter()
        {
            let src = project_root().join("./docs/user/").join(adoc);
            let dst = website_root.join(adoc);
            cp(src, dst)?;
        }

        let tags = cmd!("git tag --list").read()?;
        let prev_tag = tags.lines().filter(|line| is_release_tag(line)).last().unwrap();

        let git_log = cmd!("git log {prev_tag}..HEAD --merges --reverse").read()?;
        let git_log_dst = website_root.join("git.log");
        write_file(git_log_dst, &git_log)?;

        Ok(())
    }
}

pub struct PromoteCmd {
    pub dry_run: bool,
}

impl PromoteCmd {
    pub fn run(self) -> Result<()> {
        let _dir = pushd("../rust-rust-analyzer")?;
        cmd!("git switch master").run()?;
        cmd!("git fetch upstream").run()?;
        cmd!("git reset --hard upstream/master").run()?;
        cmd!("git submodule update --recursive").run()?;

        let branch = format!("rust-analyzer-{}", date_iso()?);
        cmd!("git switch -c {branch}").run()?;
        {
            let _dir = pushd("src/tools/rust-analyzer")?;
            cmd!("git fetch origin").run()?;
            cmd!("git reset --hard origin/release").run()?;
        }
        cmd!("git add src/tools/rust-analyzer").run()?;
        cmd!("git commit -m':arrow_up: rust-analyzer'").run()?;
        if !self.dry_run {
            cmd!("git push -u").run()?;
            cmd!("xdg-open https://github.com/matklad/rust/pull/new/{branch}?body=r%3F%20%40ghost")
                .run()?;
        }
        Ok(())
    }
}
<|endoftext|>


<commit_before>/*
   Copyright 2017 Miguel Peláez <kernelfreeze@greenlab.games>
   Copyright 2017 Raúl Salas <raulsalas.martin@greenlab.games>

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at
       http://www.apache.org/licenses/LICENSE-2.0
   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

use allegro::bitmap::Bitmap;
use client::Client;
use std::fs;
use std::path::PathBuf;
use std::collections::HashMap;
use client::allegro_font::Font;
use client::allegro_font::*;
use client::allegro_ttf::{TtfAddon, TtfFlags};
use allegro::Flag;

pub enum ResourseType {
    LitecraftTexture,
    MinecraftTexture,
}

pub struct ResourceManager<'a> {
    textures: HashMap<&'a str, Bitmap>,
    minecraft_font: Option<Font>,
    litecraft_font: Option<Font>,
    load_queue: Vec<(&'static str, ResourseType)>,
}

impl<'a> ResourceManager<'a> {
    pub fn new() -> ResourceManager<'a> {
        ResourceManager {
            textures: HashMap::new(),
            minecraft_font: None,
            litecraft_font: None,
            load_queue: vec![
                ("menu_1", ResourseType::LitecraftTexture),
                ("menu_2", ResourseType::LitecraftTexture),
                ("menu_3", ResourseType::LitecraftTexture),
                ("menu_4", ResourseType::LitecraftTexture),
            ],
        }
    }

    pub fn get_minecraft_font(&self) -> &Font {
        match self.minecraft_font {
            Some(ref font) => font,
            None => panic!("I don't have a font!"),
        }
    }

    pub fn get_litecraft_font(&self) -> &Font {
        match self.litecraft_font {
            Some(ref font) => font,
            None => panic!("I don't have a font!"),
        }
    }

    pub fn get_texture(&self, name: &str) -> &Bitmap {
        self.textures.get(name).unwrap()
    }

    pub fn load(client: &mut Client) {
        info!("Loading Resource Manager");

        let font_addon = FontAddon::init(&client.core).unwrap();
        let ttf_addon = TtfAddon::init(&font_addon).unwrap();

        info!("Loading default font");
        let font = ttf_addon
            .load_ttf_font(
                ResourceManager::get_asset("litecraft", "fonts", String::from("minecraft"), "ttf")
                    .as_str(),
                16,
                TtfFlags::zero(),
            )
            .unwrap();

        client.resource_manager.minecraft_font = Some(font);
        info!("Loading litecraft font");
        let font = ttf_addon
            .load_ttf_font(
                ResourceManager::get_asset("litecraft", "fonts", String::from("litecraft"), "ttf")
                    .as_str(),
                22,
                TtfFlags::zero(),
            )
            .unwrap();
        client.resource_manager.litecraft_font = Some(font);

        ResourceManager::load_litecraft_texture(client, "logo");
        ResourceManager::load_litecraft_texture(client, "background");

        // Set our awesome logo ;3
        let logo = client.resource_manager.get_texture("logo");
        client.display.set_icon(logo);
    }

    pub fn load_assets(client: &mut Client) -> bool {
        match client.resource_manager.load_queue.pop() {
            Some(resource) => {
                match resource.1 {
                    ResourseType::LitecraftTexture => {
                        ResourceManager::load_litecraft_texture(client, resource.0)
                    },
                    ResourseType::MinecraftTexture => {
                        ResourceManager::load_minecraft_texture(client, resource.0)
                    }
                }
                true
            }
            None => false,
        }
    }

    fn load_minecraft_texture(client: &mut Client, name: &'static str) {
        ResourceManager::load_texture(client, "minecraft", name);
    }

    fn load_litecraft_texture(client: &mut Client, name: &'static str) {
        ResourceManager::load_texture(client, "litecraft", name);
    }

    fn load_texture(client: &mut Client, domain: &str, name: &'static str) {
        info!("Loading {} texture '{}'", domain, name);

        let bmp = Bitmap::load(
            &client.core,
            ResourceManager::get_asset(domain, "textures", name.to_string(), "png").as_str(),
        );

        match bmp {
            Ok(texture) => client.resource_manager.textures.insert(name, texture),
            Err(error) => {
                error!("I can't load texture '{}'. Error: {:?}", name, error);
                panic!("Fatal error. See log for details.");
            }
        };
    }

    fn get_asset_path(domain: &str, class: &str, path: String, extension: &str) -> PathBuf {
        let path = PathBuf::from(format!(
            "./assets/{}/{}/{}.{}",
            domain,
            class,
            path,
            extension
        ));
        fs::canonicalize(path).unwrap()
    }

    fn get_asset(domain: &str, class: &str, path: String, extension: &str) -> String {
        ResourceManager::get_asset_path(domain, class, path, extension)
            .into_os_string()
            .into_string()
            .unwrap()
    }
}<commit_msg>woba loba dop dop<commit_after>/*
   Copyright 2017 Miguel Peláez <kernelfreeze@greenlab.games>
   Copyright 2017 Raúl Salas <raulsalas.martin@greenlab.games>

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at
       http://www.apache.org/licenses/LICENSE-2.0
   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

use allegro::bitmap::Bitmap;
use client::Client;
use std::fs;
use std::path::PathBuf;
use std::collections::HashMap;
use client::allegro_font::Font;
use client::allegro_font::*;
use client::allegro_ttf::{TtfAddon, TtfFlags};
use allegro::Flag;

pub enum ResourseType {
    LitecraftTexture,
    MinecraftTexture,
}

pub struct ResourceManager<'a> {
    textures: HashMap<&'a str, Bitmap>,
    minecraft_font: Option<Font>,
    litecraft_font: Option<Font>,
    load_queue: Vec<(&'static str, ResourseType)>,
}

impl<'a> ResourceManager<'a> {
    pub fn new() -> ResourceManager<'a> {
        ResourceManager {
            textures: HashMap::new(),
            minecraft_font: None,
            litecraft_font: None,
            load_queue: vec![
                ("menu_1", ResourseType::LitecraftTexture),
                ("menu_2", ResourseType::LitecraftTexture),
                ("menu_3", ResourseType::LitecraftTexture),
                ("menu_4", ResourseType::LitecraftTexture),
                ("menu_5", ResourseType::LitecraftTexture),
                ("menu_6", ResourseType::LitecraftTexture),
                ("menu_7", ResourseType::LitecraftTexture),
                ("menu_8", ResourseType::LitecraftTexture),
                ("menu_9", ResourseType::LitecraftTexture),
            ],
        }
    }

    pub fn get_minecraft_font(&self) -> &Font {
        match self.minecraft_font {
            Some(ref font) => font,
            None => panic!("I don't have a font!"),
        }
    }

    pub fn get_litecraft_font(&self) -> &Font {
        match self.litecraft_font {
            Some(ref font) => font,
            None => panic!("I don't have a font!"),
        }
    }

    pub fn get_texture(&self, name: &str) -> &Bitmap {
        self.textures.get(name).unwrap()
    }

    pub fn load(client: &mut Client) {
        info!("Loading Resource Manager");

        let font_addon = FontAddon::init(&client.core).unwrap();
        let ttf_addon = TtfAddon::init(&font_addon).unwrap();

        info!("Loading default font");
        let font = ttf_addon
            .load_ttf_font(
                ResourceManager::get_asset("litecraft", "fonts", String::from("minecraft"), "ttf")
                    .as_str(),
                16,
                TtfFlags::zero(),
            )
            .unwrap();

        client.resource_manager.minecraft_font = Some(font);
        info!("Loading litecraft font");
        let font = ttf_addon
            .load_ttf_font(
                ResourceManager::get_asset("litecraft", "fonts", String::from("litecraft"), "ttf")
                    .as_str(),
                22,
                TtfFlags::zero(),
            )
            .unwrap();
        client.resource_manager.litecraft_font = Some(font);

        ResourceManager::load_litecraft_texture(client, "logo");
        ResourceManager::load_litecraft_texture(client, "background");

        // Set our awesome logo ;3
        let logo = client.resource_manager.get_texture("logo");
        client.display.set_icon(logo);
    }

    pub fn load_assets(client: &mut Client) -> bool {
        match client.resource_manager.load_queue.pop() {
            Some(resource) => {
                match resource.1 {
                    ResourseType::LitecraftTexture => {
                        ResourceManager::load_litecraft_texture(client, resource.0)
                    },
                    ResourseType::MinecraftTexture => {
                        ResourceManager::load_minecraft_texture(client, resource.0)
                    }
                }
                true
            }
            None => false,
        }
    }

    fn load_minecraft_texture(client: &mut Client, name: &'static str) {
        ResourceManager::load_texture(client, "minecraft", name);
    }

    fn load_litecraft_texture(client: &mut Client, name: &'static str) {
        ResourceManager::load_texture(client, "litecraft", name);
    }

    fn load_texture(client: &mut Client, domain: &str, name: &'static str) {
        info!("Loading {} texture '{}'", domain, name);

        let bmp = Bitmap::load(
            &client.core,
            ResourceManager::get_asset(domain, "textures", name.to_string(), "png").as_str(),
        );

        match bmp {
            Ok(texture) => client.resource_manager.textures.insert(name, texture),
            Err(error) => {
                error!("I can't load texture '{}'. Error: {:?}", name, error);
                panic!("Fatal error. See log for details.");
            }
        };
    }

    fn get_asset_path(domain: &str, class: &str, path: String, extension: &str) -> PathBuf {
        let path = PathBuf::from(format!(
            "./assets/{}/{}/{}.{}",
            domain,
            class,
            path,
            extension
        ));
        fs::canonicalize(path).unwrap()
    }

    fn get_asset(domain: &str, class: &str, path: String, extension: &str) -> String {
        ResourceManager::get_asset_path(domain, class, path, extension)
            .into_os_string()
            .into_string()
            .unwrap()
    }
}<|endoftext|>


<commit_before>use flowgger::config::Config;

pub mod tcp_input;
#[cfg(feature = "coroutines")]
pub mod tcpco_input;

pub use super::Input;

const DEFAULT_FRAMING: &'static str = "line";
const DEFAULT_LISTEN: &'static str = "0.0.0.0:6514";
const DEFAULT_THREADS: usize = 1;
const DEFAULT_TIMEOUT: u64 = 3600;

#[derive(Clone)]
pub struct TcpConfig {
    framing: String,
    threads: usize
}

#[cfg(feature = "coroutines")]
fn get_default_threads(config: &Config) -> usize {
    config.lookup("input.tcp_threads").map_or(DEFAULT_THREADS, |x| x.as_integer().
        expect("input.tcp_threads must be an unsigned integer") as usize)
}

#[cfg(not(feature = "coroutines"))]
fn get_default_threads(_config: &Config) -> usize {
    1
}

pub fn config_parse(config: &Config) -> (TcpConfig, String, u64) {
    let listen = config.lookup("input.listen").map_or(DEFAULT_LISTEN, |x|x.as_str().
        expect("input.listen must be an ip:port string")).to_owned();
    let threads = get_default_threads(&config);
    let timeout = config.lookup("input.timeout").map_or(DEFAULT_TIMEOUT, |x| x.as_integer().
        expect("input.timeout must be an integer") as u64);
    let framing = if config.lookup("input.framed").map_or(false, |x| x.as_bool().
        expect("input.framed must be a boolean")) {
        "syslen"
    } else {
        DEFAULT_FRAMING
    };
    let framing = config.lookup("input.framing").map_or(framing, |x| x.as_str().
        expect(r#"input.framing must be a string set to "line", "nul" or "syslen""#)).to_owned();
    let tcp_config = TcpConfig {
        framing: framing,
        threads: threads
    };
    (tcp_config, listen, timeout)
}
<commit_msg>Don't define tcp/DEFAULT_THREADS when coroutines are disabled<commit_after>use flowgger::config::Config;

pub mod tcp_input;
#[cfg(feature = "coroutines")]
pub mod tcpco_input;

pub use super::Input;

const DEFAULT_FRAMING: &'static str = "line";
const DEFAULT_LISTEN: &'static str = "0.0.0.0:514";
#[cfg(feature = "coroutines")]
const DEFAULT_THREADS: usize = 1;
const DEFAULT_TIMEOUT: u64 = 3600;

#[derive(Clone)]
pub struct TcpConfig {
    framing: String,
    threads: usize
}

#[cfg(feature = "coroutines")]
fn get_default_threads(config: &Config) -> usize {
    config.lookup("input.tcp_threads").map_or(DEFAULT_THREADS, |x| x.as_integer().
        expect("input.tcp_threads must be an unsigned integer") as usize)
}

#[cfg(not(feature = "coroutines"))]
fn get_default_threads(_config: &Config) -> usize {
    1
}

pub fn config_parse(config: &Config) -> (TcpConfig, String, u64) {
    let listen = config.lookup("input.listen").map_or(DEFAULT_LISTEN, |x|x.as_str().
        expect("input.listen must be an ip:port string")).to_owned();
    let threads = get_default_threads(&config);
    let timeout = config.lookup("input.timeout").map_or(DEFAULT_TIMEOUT, |x| x.as_integer().
        expect("input.timeout must be an integer") as u64);
    let framing = if config.lookup("input.framed").map_or(false, |x| x.as_bool().
        expect("input.framed must be a boolean")) {
        "syslen"
    } else {
        DEFAULT_FRAMING
    };
    let framing = config.lookup("input.framing").map_or(framing, |x| x.as_str().
        expect(r#"input.framing must be a string set to "line", "nul" or "syslen""#)).to_owned();
    let tcp_config = TcpConfig {
        framing: framing,
        threads: threads
    };
    (tcp_config, listen, timeout)
}
<|endoftext|>


<commit_before>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

/*!
Syntax extension to generate FourCCs.

Once loaded, fourcc!() is called with a single 4-character string,
and an optional ident that is either `big`, `little`, or `target`.
The ident represents endianness, and specifies in which direction
the characters should be read. If the ident is omitted, it is assumed
to be `big`, i.e. left-to-right order. It returns a u32.

# Examples

To load the extension and use it:

```rust,ignore
#[phase(plugin)]
extern crate fourcc;

fn main() {
    let val = fourcc!("\xC0\xFF\xEE!");
    assert_eq!(val, 0xC0FFEE21u32);
    let little_val = fourcc!("foo ", little);
    assert_eq!(little_val, 0x21EEFFC0u32);
}
```

# References

* [Wikipedia: FourCC](http://en.wikipedia.org/wiki/FourCC)

*/

#![crate_id = "fourcc#0.11.0"] // NOTE: remove after stage0
#![crate_name = "fourcc"]
#![experimental]
#![crate_type = "rlib"]
#![crate_type = "dylib"]
#![license = "MIT/ASL2"]
#![doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
       html_favicon_url = "http://www.rust-lang.org/favicon.ico",
       html_root_url = "http://doc.rust-lang.org/0.11.0/")]
#![allow(unused_attribute)] // NOTE: remove after stage0

#![feature(plugin_registrar, managed_boxes)]

extern crate syntax;
extern crate rustc;

use syntax::ast;
use syntax::attr::contains;
use syntax::codemap::{Span, mk_sp};
use syntax::ext::base;
use syntax::ext::base::{ExtCtxt, MacExpr};
use syntax::ext::build::AstBuilder;
use syntax::parse::token;
use syntax::parse::token::InternedString;
use rustc::plugin::Registry;

use std::gc::Gc;

#[plugin_registrar]
pub fn plugin_registrar(reg: &mut Registry) {
    reg.register_macro("fourcc", expand_syntax_ext);
}

pub fn expand_syntax_ext(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
                         -> Box<base::MacResult> {
    let (expr, endian) = parse_tts(cx, tts);

    let little = match endian {
        None => false,
        Some(Ident{ident, span}) => match token::get_ident(ident).get() {
            "little" => true,
            "big" => false,
            "target" => target_endian_little(cx, sp),
            _ => {
                cx.span_err(span, "invalid endian directive in fourcc!");
                target_endian_little(cx, sp)
            }
        }
    };

    let s = match expr.node {
        // expression is a literal
        ast::ExprLit(ref lit) => match lit.node {
            // string literal
            ast::LitStr(ref s, _) => {
                if s.get().char_len() != 4 {
                    cx.span_err(expr.span, "string literal with len != 4 in fourcc!");
                }
                s
            }
            _ => {
                cx.span_err(expr.span, "unsupported literal in fourcc!");
                return base::DummyResult::expr(sp)
            }
        },
        _ => {
            cx.span_err(expr.span, "non-literal in fourcc!");
            return base::DummyResult::expr(sp)
        }
    };

    let mut val = 0u32;
    for codepoint in s.get().chars().take(4) {
        let byte = if codepoint as u32 > 0xFF {
            cx.span_err(expr.span, "fourcc! literal character out of range 0-255");
            0u8
        } else {
            codepoint as u8
        };

        val = if little {
            (val >> 8) | ((byte as u32) << 24)
        } else {
            (val << 8) | (byte as u32)
        };
    }
    let e = cx.expr_lit(sp, ast::LitUint(val as u64, ast::TyU32));
    MacExpr::new(e)
}

struct Ident {
    ident: ast::Ident,
    span: Span
}

fn parse_tts(cx: &ExtCtxt,
             tts: &[ast::TokenTree]) -> (Gc<ast::Expr>, Option<Ident>) {
    let p = &mut cx.new_parser_from_tts(tts);
    let ex = p.parse_expr();
    let id = if p.token == token::EOF {
        None
    } else {
        p.expect(&token::COMMA);
        let lo = p.span.lo;
        let ident = p.parse_ident();
        let hi = p.last_span.hi;
        Some(Ident{ident: ident, span: mk_sp(lo, hi)})
    };
    if p.token != token::EOF {
        p.unexpected();
    }
    (ex, id)
}

fn target_endian_little(cx: &ExtCtxt, sp: Span) -> bool {
    let meta = cx.meta_name_value(sp, InternedString::new("target_endian"),
        ast::LitStr(InternedString::new("little"), ast::CookedStr));
    contains(cx.cfg().as_slice(), meta)
}

// FIXME (10872): This is required to prevent an LLVM assert on Windows
#[test]
fn dummy_test() { }
<commit_msg>Register new snapshots<commit_after>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

/*!
Syntax extension to generate FourCCs.

Once loaded, fourcc!() is called with a single 4-character string,
and an optional ident that is either `big`, `little`, or `target`.
The ident represents endianness, and specifies in which direction
the characters should be read. If the ident is omitted, it is assumed
to be `big`, i.e. left-to-right order. It returns a u32.

# Examples

To load the extension and use it:

```rust,ignore
#[phase(plugin)]
extern crate fourcc;

fn main() {
    let val = fourcc!("\xC0\xFF\xEE!");
    assert_eq!(val, 0xC0FFEE21u32);
    let little_val = fourcc!("foo ", little);
    assert_eq!(little_val, 0x21EEFFC0u32);
}
```

# References

* [Wikipedia: FourCC](http://en.wikipedia.org/wiki/FourCC)

*/

#![crate_name = "fourcc"]
#![experimental]
#![crate_type = "rlib"]
#![crate_type = "dylib"]
#![license = "MIT/ASL2"]
#![doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
       html_favicon_url = "http://www.rust-lang.org/favicon.ico",
       html_root_url = "http://doc.rust-lang.org/0.11.0/")]

#![feature(plugin_registrar, managed_boxes)]

extern crate syntax;
extern crate rustc;

use syntax::ast;
use syntax::attr::contains;
use syntax::codemap::{Span, mk_sp};
use syntax::ext::base;
use syntax::ext::base::{ExtCtxt, MacExpr};
use syntax::ext::build::AstBuilder;
use syntax::parse::token;
use syntax::parse::token::InternedString;
use rustc::plugin::Registry;

use std::gc::Gc;

#[plugin_registrar]
pub fn plugin_registrar(reg: &mut Registry) {
    reg.register_macro("fourcc", expand_syntax_ext);
}

pub fn expand_syntax_ext(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
                         -> Box<base::MacResult> {
    let (expr, endian) = parse_tts(cx, tts);

    let little = match endian {
        None => false,
        Some(Ident{ident, span}) => match token::get_ident(ident).get() {
            "little" => true,
            "big" => false,
            "target" => target_endian_little(cx, sp),
            _ => {
                cx.span_err(span, "invalid endian directive in fourcc!");
                target_endian_little(cx, sp)
            }
        }
    };

    let s = match expr.node {
        // expression is a literal
        ast::ExprLit(ref lit) => match lit.node {
            // string literal
            ast::LitStr(ref s, _) => {
                if s.get().char_len() != 4 {
                    cx.span_err(expr.span, "string literal with len != 4 in fourcc!");
                }
                s
            }
            _ => {
                cx.span_err(expr.span, "unsupported literal in fourcc!");
                return base::DummyResult::expr(sp)
            }
        },
        _ => {
            cx.span_err(expr.span, "non-literal in fourcc!");
            return base::DummyResult::expr(sp)
        }
    };

    let mut val = 0u32;
    for codepoint in s.get().chars().take(4) {
        let byte = if codepoint as u32 > 0xFF {
            cx.span_err(expr.span, "fourcc! literal character out of range 0-255");
            0u8
        } else {
            codepoint as u8
        };

        val = if little {
            (val >> 8) | ((byte as u32) << 24)
        } else {
            (val << 8) | (byte as u32)
        };
    }
    let e = cx.expr_lit(sp, ast::LitUint(val as u64, ast::TyU32));
    MacExpr::new(e)
}

struct Ident {
    ident: ast::Ident,
    span: Span
}

fn parse_tts(cx: &ExtCtxt,
             tts: &[ast::TokenTree]) -> (Gc<ast::Expr>, Option<Ident>) {
    let p = &mut cx.new_parser_from_tts(tts);
    let ex = p.parse_expr();
    let id = if p.token == token::EOF {
        None
    } else {
        p.expect(&token::COMMA);
        let lo = p.span.lo;
        let ident = p.parse_ident();
        let hi = p.last_span.hi;
        Some(Ident{ident: ident, span: mk_sp(lo, hi)})
    };
    if p.token != token::EOF {
        p.unexpected();
    }
    (ex, id)
}

fn target_endian_little(cx: &ExtCtxt, sp: Span) -> bool {
    let meta = cx.meta_name_value(sp, InternedString::new("target_endian"),
        ast::LitStr(InternedString::new("little"), ast::CookedStr));
    contains(cx.cfg().as_slice(), meta)
}

// FIXME (10872): This is required to prevent an LLVM assert on Windows
#[test]
fn dummy_test() { }
<|endoftext|>


<commit_before>// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use middle::ty;

use std::cell::Cell;
use syntax::ast;
use syntax::codemap::Span;
use syntax::owned_slice::OwnedSlice;

/// Defines strategies for handling regions that are omitted.  For
/// example, if one writes the type `&Foo`, then the lifetime of
/// this reference has been omitted. When converting this
/// type, the generic functions in astconv will invoke `anon_regions`
/// on the provided region-scope to decide how to translate this
/// omitted region.
///
/// It is not always legal to omit regions, therefore `anon_regions`
/// can return `Err(())` to indicate that this is not a scope in which
/// regions can legally be omitted.
pub trait RegionScope {
    fn anon_regions(&self,
                    span: Span,
                    count: uint)
                    -> Result<Vec<ty::Region> , ()>;
}

// A scope in which all regions must be explicitly named
pub struct ExplicitRscope;

impl RegionScope for ExplicitRscope {
    fn anon_regions(&self,
                    _span: Span,
                    _count: uint)
                    -> Result<Vec<ty::Region> , ()> {
        Err(())
    }
}

/// A scope in which we generate anonymous, late-bound regions for
/// omitted regions. This occurs in function signatures.
pub struct BindingRscope {
    binder_id: ast::NodeId,
    anon_bindings: Cell<uint>,
}

impl BindingRscope {
    pub fn new(binder_id: ast::NodeId) -> BindingRscope {
        BindingRscope {
            binder_id: binder_id,
            anon_bindings: Cell::new(0),
        }
    }
}

impl RegionScope for BindingRscope {
    fn anon_regions(&self,
                    _: Span,
                    count: uint)
                    -> Result<Vec<ty::Region> , ()> {
        let idx = self.anon_bindings.get();
        self.anon_bindings.set(idx + count);
        Ok(Vec::from_fn(count, |i| ty::ReLateBound(self.binder_id,
                                                   ty::BrAnon(idx + i))))
    }
}

pub fn bound_type_regions(defs: &[ty::RegionParameterDef])
                          -> OwnedSlice<ty::Region> {
    assert!(defs.iter().all(|def| def.def_id.krate == ast::LOCAL_CRATE));
    defs.iter().enumerate().map(
        |(i, def)| ty::ReEarlyBound(def.def_id.node, i, def.name)).collect()
}
<commit_msg>middle: typeck: rscope: remove dead code<commit_after>// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use middle::ty;

use std::cell::Cell;
use syntax::ast;
use syntax::codemap::Span;

/// Defines strategies for handling regions that are omitted.  For
/// example, if one writes the type `&Foo`, then the lifetime of
/// this reference has been omitted. When converting this
/// type, the generic functions in astconv will invoke `anon_regions`
/// on the provided region-scope to decide how to translate this
/// omitted region.
///
/// It is not always legal to omit regions, therefore `anon_regions`
/// can return `Err(())` to indicate that this is not a scope in which
/// regions can legally be omitted.
pub trait RegionScope {
    fn anon_regions(&self,
                    span: Span,
                    count: uint)
                    -> Result<Vec<ty::Region> , ()>;
}

// A scope in which all regions must be explicitly named
pub struct ExplicitRscope;

impl RegionScope for ExplicitRscope {
    fn anon_regions(&self,
                    _span: Span,
                    _count: uint)
                    -> Result<Vec<ty::Region> , ()> {
        Err(())
    }
}

/// A scope in which we generate anonymous, late-bound regions for
/// omitted regions. This occurs in function signatures.
pub struct BindingRscope {
    binder_id: ast::NodeId,
    anon_bindings: Cell<uint>,
}

impl BindingRscope {
    pub fn new(binder_id: ast::NodeId) -> BindingRscope {
        BindingRscope {
            binder_id: binder_id,
            anon_bindings: Cell::new(0),
        }
    }
}

impl RegionScope for BindingRscope {
    fn anon_regions(&self,
                    _: Span,
                    count: uint)
                    -> Result<Vec<ty::Region> , ()> {
        let idx = self.anon_bindings.get();
        self.anon_bindings.set(idx + count);
        Ok(Vec::from_fn(count, |i| ty::ReLateBound(self.binder_id,
                                                   ty::BrAnon(idx + i))))
    }
}
<|endoftext|>