<commit_before>//! Defines common interfaces for interacting with statistical distributions and provides
//! concrete implementations for a variety of distributions.

use rand::Rng;
use super::result::Result;

pub use self::bernoulli::Bernoulli;
pub use self::binomial::Binomial;
pub use self::chi::Chi;
pub use self::chi_squared::ChiSquared;
pub use self::discrete_uniform::DiscreteUniform;
pub use self::exponential::Exponential;
pub use self::gamma::Gamma;
pub use self::log_normal::LogNormal;
pub use self::normal::Normal;
pub use self::poisson::Poisson;
pub use self::student_t::StudentT;
pub use self::triangular::Triangular;
pub use self::uniform::Uniform;
pub use self::weibull::Weibull;

mod bernoulli;
mod binomial;
mod chi;
mod chi_squared;
mod discrete_uniform;
mod exponential;
mod gamma;
mod log_normal;
mod normal;
mod poisson;
mod student_t;
mod triangular;
mod uniform;
mod weibull;

/// The `Distribution` trait is used to specify an interface
/// for sampling distributions
///
/// # Examples
///
/// A trivial implementation that just samples from the supplied
/// random number generator
///
/// ```
/// use rand::StdRng;
///
/// struct Foo;
///
/// impl Distribution for Foo {
///     fn sample<R: Rng>(&self, r: &mut R) -> f64 {
///         r.next_f64()
///     }
/// }
/// ```
pub trait Distribution {
    /// Draws a random sample using the supplied random number generator
    fn sample<R: Rng>(&self, r: &mut R) -> f64;
}

/// The `Univariate` trait extends the `Distribution`
/// trait provides an interface for interacting with
/// univariate statistical distributions.
///
/// # Remarks
///
/// All methods provided by the `Univariate` trait are unchecked, meaning
/// they can panic if in an invalid state or encountering invalid input
/// depending on the implementing distribution. The `CheckedUnivariate`
/// trait provides a panic-safe interface for univariate distributions
pub trait Univariate : Distribution {
    /// Returns the mean for a given distribution. May panic depending
    /// on the implementor.
    ///
    /// # Examples
    ///
    /// ```
    /// use statrs::distribution::Uniform;
    ///
    /// let n = Uniform::new(0, 1).unwrap();
    /// assert_eq!(0.5, n.mean());
    /// ```
    fn mean(&self) -> f64;

    /// Returns the variance for a given distribution. May panic depending
    /// on the implementor.
    ///
    /// # Examples
    ///
    /// ```
    /// use statrs::distribution::Uniform;
    ///
    /// let n = Uniform::new(0, 1).unwrap();
    /// assert_eq!(1.0 / 12.0, n.variance());
    /// ```
    fn variance(&self) -> f64;

    /// Returns the standard deviation for a given distribution. May panic depending
    /// on the implementor.
    ///
    /// # Examples
    ///
    /// ```
    /// use statrs::distribution::Uniform;
    ///
    /// let n = Uniform::new(0, 1).unwrap();
    /// assert_eq!((1f64 / 12f64).sqrt(), n.std_dev());
    /// ```
    fn std_dev(&self) -> f64;

    /// Returns the entropy for a given distribution. May panic depending
    /// on the implementor.
    ///
    /// # Examples
    ///
    /// ```
    /// use statrs::distribution::Uniform;
    ///
    /// let n = Uniform::new(0, 1).unwrap();
    /// assert_eq!(0.0, n.entropy());
    /// ```
    fn entropy(&self) -> f64;

    /// Returns the skewness for a given distribution. May panic depending
    /// on the implementor.
    ///
    /// # Examples
    ///
    /// ```
    /// use statrs::distribution::Uniform;
    ///
    /// let n = Uniform::new(0, 1).unwrap();
    /// assert_eq!(0.0, n.skewness());
    /// ```
    fn skewness(&self) -> f64;

    /// Returns the median for a given distribution. May panic depending
    /// on the implementor.
    ///
    /// # Examples
    ///
    /// ```
    /// use statrs::distribution::Uniform;
    ///
    /// let n = Uniform::new(0, 1).unwrap();
    /// assert_eq!(0.5, n.median());
    /// ```
    fn median(&self) -> f64;

    /// Returns the cumulative distribution function calculated
    /// at `x` for a given distribution. May panic depending
    /// on the implementor.
    ///
    /// # Examples
    ///
    /// ```
    /// use statrs::distribution::Uniform;
    ///
    /// let n = Uniform::new(0, 1).unwrap();
    /// assert_eq!(0.5, n.cdf(0.5));
    /// ```
    fn cdf(&self, x: f64) -> f64;
}

/// The `CheckedUnivariate` trait extends the `Distribution` trait and
/// provides a checked interface for interacting with univariate statistical
/// distributions. This means implementors should return an `Err` instead of
/// panicking on an invalid state or input.
pub trait CheckedUnivariate : Distribution {
    /// Returns the mean for a given distribution or a `StatsError`
    /// depending on the implementor. This method should not panic
    /// but return a `StatsError` instead.
    ///
    /// # Examples
    ///
    /// ```
    /// use statrs::distribution::Uniform;
    ///
    /// let n = Uniform::new(0, 1).unwrap();
    /// assert_eq!(0.5, n.mean().unwrap());
    /// ```
    fn mean(&self) -> Result<f64>;

    /// Returns the variance for a given distribution or a `StatsError`
    /// depending on the implementor. This method should not panic
    /// but return a `StatsError` instead.
    ///
    /// # Examples
    ///
    /// ```
    /// use statrs::distribution::Uniform;
    ///
    /// let n = Uniform::new(0, 1).unwrap();
    /// assert_eq!(1.0 / 12.0, n.variance().unwrap());
    /// ```
    fn variance(&self) -> Result<f64>;

    /// Returns the standard deviation for a given distribution or
    /// a `StatsError` depending on the implementor. This method should
    /// not panic but return a `StatsError` instead.
    ///
    /// # Examples
    ///
    /// ```
    /// use statrs::distribution::Uniform;
    ///
    /// let n = Uniform::new(0, 1).unwrap();
    /// assert_eq!((1f64 / 12f64).sqrt(), n.std_dev().unwrap());
    /// ```
    fn std_dev(&self) -> Result<f64>;

    /// Returns the entropy for a given distribution or a `StatsError`
    /// depending on the implementor. This method should not panic but
    /// return a `StatsError` instead.
    ///
    /// # Examples
    ///
    /// ```
    /// use statrs::distribution::Uniform;
    ///
    /// let n = Uniform::new(0, 1).unwrap();
    /// assert_eq!(0.0, n.entropy().unwrap());
    /// ```
    fn entropy(&self) -> Result<f64>;

    /// Returns the skewness for a given distribution or a `StatsError`
    /// depending on the implementor. This method should not panic but
    /// return a `StatsError` instead.
    ///
    /// # Examples
    ///
    /// ```
    /// use statrs::distribution::Uniform;
    ///
    /// let n = Uniform::new(0, 1).unwrap();
    /// assert_eq!(0.0, n.skewness().unwrap());
    /// ```
    fn skewness(&self) -> Result<f64>;

    /// Returns the median for a given distribution or a `StatsError`
    /// depending on the implementor. This method should not panic but
    /// return a `StatsError` instead.
    ///
    /// # Examples
    ///
    /// ```
    /// use statrs::distribution::Uniform;
    ///
    /// let n = Uniform::new(0, 1).unwrap();
    /// assert_eq!(0.5, n.median());
    /// ```
    fn median(&self) -> Result<f64>;

    /// Returns the cumulative distribution function calculated
    /// at `x` for a given distribution or a `StatsError` depending
    /// on the implementor. This method should not panic but return a
    /// `StatsError` instead. `StatsError`s should be returned for argument
    /// bound violations.
    ///
    /// # Examples
    ///
    /// ```
    /// use statrs::distribution::Uniform;
    ///
    /// let n = Uniform::new(0, 1).unwrap();
    /// assert_eq!(0.5, n.cdf(0.5));
    /// ```
    fn cdf(&self) -> Result<f64>;
}

/// The `Continuous` trait extends the `Univariate`
/// trait and provides an interface for interacting with continuous
/// univariate statistical distributions
///
/// # Remarks
///
/// All methods provided by the `Continuous` trait are unchecked, meaning
/// they can panic if in an invalid state or encountering invalid input
/// depending on the implementing distribution. The `CheckedContinuous`
/// trait provides a panic-safe interface for continuous distributions
pub trait Continuous : Univariate {
    fn mode(&self) -> f64;
    fn min(&self) -> f64;
    fn max(&self) -> f64;
    fn pdf(&self, x: f64) -> f64;
    fn ln_pdf(&self, x: f64) -> f64;
}

/// The `CheckedContinous` trait extends the `CheckedUnivariate`
/// trait and provides a checked interface for interacting with
/// continous univariate statistical distributions. This means
/// implementors should return an `Err` instead of panicking on
/// an invalid state or input.
pub trait CheckedContinuous : CheckedUnivariate {
    fn mode(&self) -> Result<f64>;
    fn min(&self) -> Result<f64>;
    fn max(&self) -> Result<f64>;
    fn pdf(&self, x: f64) -> Result<f64>;
    fn ln_pdf(&self, x: f64) -> Result<f64>;
}

/// The `Discrete` trait extends the `Univariate`
/// trait and provides an interface for interacting with discrete
/// univariate statistical distributions
///
/// # Remakrs
///
/// All methods provided by the `Discrete` trait are unchecked, meaning
/// they can panic if in an invalid state or encountering invalid input
/// depending on the implementing distribution. The `CheckedDiscrete` trait
/// provides a panic-safe interface for discrete distributions
pub trait Discrete : Univariate {
    fn mode(&self) -> i64;
    fn min(&self) -> i64;
    fn max(&self) -> i64;
    fn pmf(&self, x: i64) -> f64;
    fn ln_pmf(&self, x: i64) -> f64;
}

/// The `CheckedDiscrete` trait extends the `CheckedUnivariate` trait
/// and provides a checked interface for interacting with discrete
/// univariate statistical distributions. This means implementors
/// should return an `Err` instead of panicking on an invalid state
/// or input
pub trait CheckedDiscrete : CheckedUnivariate {
    fn mode(&self) -> Result<i64>;
    fn min(&self) -> Result<i64>;
    fn max(&self) -> Result<i64>;
    fn pmf(&self, x: i64) -> Result<f64>;
    fn ln_pmf(&self, x: i64) -> Result<f64>;
}<commit_msg>fix doc bug with instantiating uniform<commit_after>//! Defines common interfaces for interacting with statistical distributions and provides
//! concrete implementations for a variety of distributions.

use rand::Rng;
use super::result::Result;

pub use self::bernoulli::Bernoulli;
pub use self::binomial::Binomial;
pub use self::chi::Chi;
pub use self::chi_squared::ChiSquared;
pub use self::discrete_uniform::DiscreteUniform;
pub use self::exponential::Exponential;
pub use self::gamma::Gamma;
pub use self::log_normal::LogNormal;
pub use self::normal::Normal;
pub use self::poisson::Poisson;
pub use self::student_t::StudentT;
pub use self::triangular::Triangular;
pub use self::uniform::Uniform;
pub use self::weibull::Weibull;

mod bernoulli;
mod binomial;
mod chi;
mod chi_squared;
mod discrete_uniform;
mod exponential;
mod gamma;
mod log_normal;
mod normal;
mod poisson;
mod student_t;
mod triangular;
mod uniform;
mod weibull;

/// The `Distribution` trait is used to specify an interface
/// for sampling distributions
///
/// # Examples
///
/// A trivial implementation that just samples from the supplied
/// random number generator
///
/// ```
/// use rand::StdRng;
///
/// struct Foo;
///
/// impl Distribution for Foo {
///     fn sample<R: Rng>(&self, r: &mut R) -> f64 {
///         r.next_f64()
///     }
/// }
/// ```
pub trait Distribution {
    /// Draws a random sample using the supplied random number generator
    fn sample<R: Rng>(&self, r: &mut R) -> f64;
}

/// The `Univariate` trait extends the `Distribution`
/// trait provides an interface for interacting with
/// univariate statistical distributions.
///
/// # Remarks
///
/// All methods provided by the `Univariate` trait are unchecked, meaning
/// they can panic if in an invalid state or encountering invalid input
/// depending on the implementing distribution. The `CheckedUnivariate`
/// trait provides a panic-safe interface for univariate distributions
pub trait Univariate : Distribution {
    /// Returns the mean for a given distribution. May panic depending
    /// on the implementor.
    ///
    /// # Examples
    ///
    /// ```
    /// use statrs::distribution::Uniform;
    ///
    /// let n = Uniform::new(0.0, 1.0).unwrap();
    /// assert_eq!(0.5, n.mean());
    /// ```
    fn mean(&self) -> f64;

    /// Returns the variance for a given distribution. May panic depending
    /// on the implementor.
    ///
    /// # Examples
    ///
    /// ```
    /// use statrs::distribution::Uniform;
    ///
    /// let n = Uniform::new(0.0, 1.0).unwrap();
    /// assert_eq!(1.0 / 12.0, n.variance());
    /// ```
    fn variance(&self) -> f64;

    /// Returns the standard deviation for a given distribution. May panic depending
    /// on the implementor.
    ///
    /// # Examples
    ///
    /// ```
    /// use statrs::distribution::Uniform;
    ///
    /// let n = Uniform::new(0.0, 1.0).unwrap();
    /// assert_eq!((1f64 / 12f64).sqrt(), n.std_dev());
    /// ```
    fn std_dev(&self) -> f64;

    /// Returns the entropy for a given distribution. May panic depending
    /// on the implementor.
    ///
    /// # Examples
    ///
    /// ```
    /// use statrs::distribution::Uniform;
    ///
    /// let n = Uniform::new(0.0, 1.0).unwrap();
    /// assert_eq!(0.0, n.entropy());
    /// ```
    fn entropy(&self) -> f64;

    /// Returns the skewness for a given distribution. May panic depending
    /// on the implementor.
    ///
    /// # Examples
    ///
    /// ```
    /// use statrs::distribution::Uniform;
    ///
    /// let n = Uniform::new(0.0, 1.0).unwrap();
    /// assert_eq!(0.0, n.skewness());
    /// ```
    fn skewness(&self) -> f64;

    /// Returns the median for a given distribution. May panic depending
    /// on the implementor.
    ///
    /// # Examples
    ///
    /// ```
    /// use statrs::distribution::Uniform;
    ///
    /// let n = Uniform::new(0.0, 1.0).unwrap();
    /// assert_eq!(0.5, n.median());
    /// ```
    fn median(&self) -> f64;

    /// Returns the cumulative distribution function calculated
    /// at `x` for a given distribution. May panic depending
    /// on the implementor.
    ///
    /// # Examples
    ///
    /// ```
    /// use statrs::distribution::Uniform;
    ///
    /// let n = Uniform::new(0.0, 1.0).unwrap();
    /// assert_eq!(0.5, n.cdf(0.5));
    /// ```
    fn cdf(&self, x: f64) -> f64;
}

/// The `CheckedUnivariate` trait extends the `Distribution` trait and
/// provides a checked interface for interacting with univariate statistical
/// distributions. This means implementors should return an `Err` instead of
/// panicking on an invalid state or input.
pub trait CheckedUnivariate : Distribution {
    /// Returns the mean for a given distribution or a `StatsError`
    /// depending on the implementor. This method should not panic
    /// but return a `StatsError` instead.
    ///
    /// # Examples
    ///
    /// ```
    /// use statrs::distribution::Uniform;
    ///
    /// let n = Uniform::new(0.0, 1.0).unwrap();
    /// assert_eq!(0.5, n.mean().unwrap());
    /// ```
    fn mean(&self) -> Result<f64>;

    /// Returns the variance for a given distribution or a `StatsError`
    /// depending on the implementor. This method should not panic
    /// but return a `StatsError` instead.
    ///
    /// # Examples
    ///
    /// ```
    /// use statrs::distribution::Uniform;
    ///
    /// let n = Uniform::new(0.0, 1.0).unwrap();
    /// assert_eq!(1.0 / 12.0, n.variance().unwrap());
    /// ```
    fn variance(&self) -> Result<f64>;

    /// Returns the standard deviation for a given distribution or
    /// a `StatsError` depending on the implementor. This method should
    /// not panic but return a `StatsError` instead.
    ///
    /// # Examples
    ///
    /// ```
    /// use statrs::distribution::Uniform;
    ///
    /// let n = Uniform::new(0.0, 1.0).unwrap();
    /// assert_eq!((1f64 / 12f64).sqrt(), n.std_dev().unwrap());
    /// ```
    fn std_dev(&self) -> Result<f64>;

    /// Returns the entropy for a given distribution or a `StatsError`
    /// depending on the implementor. This method should not panic but
    /// return a `StatsError` instead.
    ///
    /// # Examples
    ///
    /// ```
    /// use statrs::distribution::Uniform;
    ///
    /// let n = Uniform::new(0.0, 1.0).unwrap();
    /// assert_eq!(0.0, n.entropy().unwrap());
    /// ```
    fn entropy(&self) -> Result<f64>;

    /// Returns the skewness for a given distribution or a `StatsError`
    /// depending on the implementor. This method should not panic but
    /// return a `StatsError` instead.
    ///
    /// # Examples
    ///
    /// ```
    /// use statrs::distribution::Uniform;
    ///
    /// let n = Uniform::new(0.0, 1.0).unwrap();
    /// assert_eq!(0.0, n.skewness().unwrap());
    /// ```
    fn skewness(&self) -> Result<f64>;

    /// Returns the median for a given distribution or a `StatsError`
    /// depending on the implementor. This method should not panic but
    /// return a `StatsError` instead.
    ///
    /// # Examples
    ///
    /// ```
    /// use statrs::distribution::Uniform;
    ///
    /// let n = Uniform::new(0.0, 1.0).unwrap();
    /// assert_eq!(0.5, n.median());
    /// ```
    fn median(&self) -> Result<f64>;

    /// Returns the cumulative distribution function calculated
    /// at `x` for a given distribution or a `StatsError` depending
    /// on the implementor. This method should not panic but return a
    /// `StatsError` instead. `StatsError`s should be returned for argument
    /// bound violations.
    ///
    /// # Examples
    ///
    /// ```
    /// use statrs::distribution::Uniform;
    ///
    /// let n = Uniform::new(0.0, 1.0).unwrap();
    /// assert_eq!(0.5, n.cdf(0.5));
    /// ```
    fn cdf(&self) -> Result<f64>;
}

/// The `Continuous` trait extends the `Univariate`
/// trait and provides an interface for interacting with continuous
/// univariate statistical distributions
///
/// # Remarks
///
/// All methods provided by the `Continuous` trait are unchecked, meaning
/// they can panic if in an invalid state or encountering invalid input
/// depending on the implementing distribution. The `CheckedContinuous`
/// trait provides a panic-safe interface for continuous distributions
pub trait Continuous : Univariate {
    fn mode(&self) -> f64;
    fn min(&self) -> f64;
    fn max(&self) -> f64;
    fn pdf(&self, x: f64) -> f64;
    fn ln_pdf(&self, x: f64) -> f64;
}

/// The `CheckedContinous` trait extends the `CheckedUnivariate`
/// trait and provides a checked interface for interacting with
/// continous univariate statistical distributions. This means
/// implementors should return an `Err` instead of panicking on
/// an invalid state or input.
pub trait CheckedContinuous : CheckedUnivariate {
    fn mode(&self) -> Result<f64>;
    fn min(&self) -> Result<f64>;
    fn max(&self) -> Result<f64>;
    fn pdf(&self, x: f64) -> Result<f64>;
    fn ln_pdf(&self, x: f64) -> Result<f64>;
}

/// The `Discrete` trait extends the `Univariate`
/// trait and provides an interface for interacting with discrete
/// univariate statistical distributions
///
/// # Remakrs
///
/// All methods provided by the `Discrete` trait are unchecked, meaning
/// they can panic if in an invalid state or encountering invalid input
/// depending on the implementing distribution. The `CheckedDiscrete` trait
/// provides a panic-safe interface for discrete distributions
pub trait Discrete : Univariate {
    fn mode(&self) -> i64;
    fn min(&self) -> i64;
    fn max(&self) -> i64;
    fn pmf(&self, x: i64) -> f64;
    fn ln_pmf(&self, x: i64) -> f64;
}

/// The `CheckedDiscrete` trait extends the `CheckedUnivariate` trait
/// and provides a checked interface for interacting with discrete
/// univariate statistical distributions. This means implementors
/// should return an `Err` instead of panicking on an invalid state
/// or input
pub trait CheckedDiscrete : CheckedUnivariate {
    fn mode(&self) -> Result<i64>;
    fn min(&self) -> Result<i64>;
    fn max(&self) -> Result<i64>;
    fn pmf(&self, x: i64) -> Result<f64>;
    fn ln_pmf(&self, x: i64) -> Result<f64>;
}<|endoftext|>


<commit_before>
use color::Color;
use frame::{
    Framing,
    Frame,
    NoFrame,
};
use graphics::{
    AddColor,
    AddEllipse,
    AddLine,
    AddRoundBorder,
    Context,
    Draw,
};
use label;
use label::{
    NoLabel,
    Label,
    FontSize,
    Labeling,
};
use mouse_state::{
    MouseState,
    Up,
    Down,
};
use opengl_graphics::Gl;
use piston::{
    RenderArgs,
};
use piston::input::keyboard::{
    Key,
    Backspace,
};
use point::Point;
use rectangle;
use rectangle::{
    TopLeft,
    TopRight,
    BottomLeft,
    BottomRight,
};
use ui_context::{
    UIID,
    UIContext,
};
use utils::{
    clamp,
    map_range,
    val_to_string,
};
use widget::TextBox;

pub type Idx = uint;
pub type CursorX = f64;

/// Represents the state of the text_box widget.
#[deriving(Show, PartialEq)]
pub struct State(WidgetState, Capturing);

/// Represents the next tier of state.
#[deriving(Show, PartialEq)]
pub enum WidgetState {
    Normal,
    Highlighted(Element),
    Clicked(Element),
}

/// Whether the textbox is currently captured or not.
#[deriving(Show, PartialEq)]
pub enum Capturing {
    Uncaptured,
    Captured(Idx, CursorX),
}

/// Represents an element of the TextBox widget.
#[deriving(Show, PartialEq)]
pub enum Element {
    Nill,
    Rect,
    Text(Idx, CursorX),
}

impl State {
    /// Return the associated Rectangle state.
    fn as_rectangle_state(&self) -> rectangle::State {
        match self {
            &State(state, capturing) => match capturing {
                Captured(_, _) => rectangle::Normal,
                Uncaptured => match state {
                    Normal => rectangle::Normal,
                    Highlighted(_) => rectangle::Highlighted,
                    Clicked(_) => rectangle::Clicked,
                },
            }
        }
    }
}

widget_fns!(TextBox, State, TextBox(State(Normal, Uncaptured)))

static TEXT_PADDING: f64 = 5f64;

/// Draw the text_box widget.
pub fn draw(args: &RenderArgs,
            gl: &mut Gl,
            uic: &mut UIContext,
            ui_id: UIID,
            pos: Point<f64>,
            width: f64,
            height: f64,
            font_size: FontSize,
            frame: Framing,
            color: Color,
            text: &mut String,
            callback: |&mut String|) {

    let mouse = uic.get_mouse_state();
    let state = *get_state(uic, ui_id);

    // Rect.
    let frame_w = match frame { Frame(w, _) => w, NoFrame => 0.0 };
    let frame_w2 = frame_w * 2.0;
    let pad_pos = pos + Point::new(frame_w, frame_w, 0.0);
    let pad_w = width - frame_w2;
    let pad_h = height - frame_w2;
    let text_pos = Point::new(pad_pos.x + TEXT_PADDING,
                              pad_pos.y + (pad_h - font_size as f64) / 2.0,
                              0.0);
    let text_w = label::width(uic, font_size, text.as_slice());
    let over_elem = over_elem(uic, pos, mouse.pos, width, height,
                              pad_pos, pad_w, pad_h,
                              text_pos, text_w, font_size, text.as_slice());
    let mut new_state = get_new_state(over_elem, state, mouse);

    rectangle::draw(args, gl, new_state.as_rectangle_state(), pos, width, height, frame, color);
    label::draw(args, gl, uic, text_pos, font_size, color.plain_contrast(), text.as_slice());

    let new_state = match new_state { State(w_state, capturing) => match capturing {
        Uncaptured => new_state,
        Captured(idx, cursor_x) => {
            let context = Context::abs(args.width as f64, args.height as f64);
            let (r, g, b, a) = color.plain_contrast().as_tuple();
            context
                .line(cursor_x, pad_pos.y, cursor_x, pad_pos.y + pad_h)
                .round_border_width(1f64)
                .rgba(r, g, b, a)
                .draw(gl);
            let mut new_idx = idx;
            let mut new_cursor_x = cursor_x;

            // Check for control keys.
            let pressed_keys = uic.get_pressed_keys();
            for key in pressed_keys.iter() {
                println!("Key: {}", key);
                match *key {
                    Backspace => {
                        if text.len() > 0u
                        && text.len() >= idx
                        && idx > 0u {
                            let rem_idx = idx - 1u;
                            new_cursor_x = {
                                let c = uic.get_character(font_size,
                                                          text.as_slice().char_at(rem_idx));
                                cursor_x - (c.glyph.advance().x >> 16) as f64
                            };
                            let new_text = String::from_str(text.as_slice().slice_to(rem_idx))
                                .append(text.as_slice().slice_from(idx));
                            *text = new_text;
                            new_idx = rem_idx;
                        }
                    },
                    _ => (),
                }
            }

            // Check for entered text.
            let entered_text = uic.get_entered_text();
            for t in entered_text.iter() {
                let new_text = String::from_str(text.as_slice().slice_to(idx))
                    .append(t.as_slice()).append(text.as_slice().slice_from(idx));
                *text = new_text;
                new_idx = idx + t.len();
                for ch in t.as_slice().chars() {
                    let c = uic.get_character(font_size, ch);
                    new_cursor_x += (c.glyph.advance().x >> 16) as f64;
                }
            }

            State(w_state, Captured(new_idx, new_cursor_x))
        },
    }};

    set_state(uic, ui_id, new_state);

}

/// Check if cursor is over the pad and if so, which
fn over_elem(uic: &mut UIContext,
             pos: Point<f64>,
             mouse_pos: Point<f64>,
             rect_w: f64,
             rect_h: f64,
             pad_pos: Point<f64>,
             pad_w: f64,
             pad_h: f64,
             text_pos: Point<f64>,
             text_w: f64,
             font_size: FontSize,
             text: &str) -> Element {
    match rectangle::is_over(pos, mouse_pos, rect_w, rect_h) {
        false => Nill,
        true => match rectangle::is_over(pad_pos, mouse_pos, pad_w, pad_h) {
            false => Rect,
            true => {
                let (idx, cursor_x) = closest_idx(uic, mouse_pos, pad_h, pad_pos.y, text_pos.x, text_w, font_size, text);
                Text(idx, cursor_x)
            },
        },
    }
}

/// Check which character is closest to the mouse cursor.
fn closest_idx(uic: &mut UIContext,
               mouse_pos: Point<f64>,
               pad_h: f64,
               pad_y: f64,
               text_x: f64,
               text_w: f64,
               font_size: FontSize,
               text: &str) -> (Idx, f64) {
    if mouse_pos.x <= text_x { return (0u, text_x) }
    let mut x = text_x;
    let mut prev_x = x;
    let mut left_x = text_x;
    for (i, ch) in text.chars().enumerate() {
        let character = uic.get_character(font_size, ch);
        let char_w = (character.glyph.advance().x >> 16) as f64;
        x += char_w;
        let right_x = prev_x + char_w / 2.0;
        if mouse_pos.x > left_x && mouse_pos.x < right_x { return (i, prev_x) }
        prev_x = x;
        left_x = right_x;
    }
    (text.len(), text_x + text_w)
}

/// Check and return the current state of the TextBox.
fn get_new_state(over_elem: Element,
                 prev_box_state: State,
                 mouse: MouseState) -> State {
    match prev_box_state {
        State(prev, Uncaptured) => {
            match (over_elem, prev, mouse.left) {
                (_, Normal, Down) => State(Normal, Uncaptured),
                (Nill, Normal, Up) | (Nill, Highlighted(_), Up) => State(Normal, Uncaptured),
                (_, Normal, Up) | (_, Highlighted(_), Up) => State(Highlighted(over_elem), Uncaptured),
                (_, Highlighted(p_elem), Down) | (_, Clicked(p_elem), Down) =>
                    State(Clicked(p_elem), Uncaptured),
                (Text(idx, x), Clicked(Text(_, _)), Up) => State(Highlighted(over_elem), Captured(idx, x)),
                (Nill, _, _) => State(Normal, Uncaptured),
                _ => prev_box_state,
            }
        },
        State(prev, Captured(p_idx, p_x)) => {
            match (over_elem, prev, mouse.left) {
                (Nill, Clicked(Nill), Up) => State(Normal, Uncaptured),
                (Text(idx, x), Clicked(Text(_, _)), Up) => State(Highlighted(over_elem), Captured(idx, x)),
                (_, Normal, Up) | (_, Highlighted(_), Up) | (_, Clicked(_), Up)  =>
                    State(Highlighted(over_elem), Captured(p_idx, p_x)),
                (_, Highlighted(p_elem), Down) | (_, Clicked(p_elem), Down) =>
                    State(Clicked(p_elem), Captured(p_idx, p_x)),
                _ => prev_box_state,
            }
        },
    }
}

<commit_msg>Fixed a small text cursor bug, removed println! from Key matching loop.<commit_after>
use color::Color;
use frame::{
    Framing,
    Frame,
    NoFrame,
};
use graphics::{
    AddColor,
    AddEllipse,
    AddLine,
    AddRoundBorder,
    Context,
    Draw,
};
use label;
use label::{
    NoLabel,
    Label,
    FontSize,
    Labeling,
};
use mouse_state::{
    MouseState,
    Up,
    Down,
};
use opengl_graphics::Gl;
use piston::{
    RenderArgs,
};
use piston::input::keyboard::{
    Key,
    Backspace,
};
use point::Point;
use rectangle;
use rectangle::{
    TopLeft,
    TopRight,
    BottomLeft,
    BottomRight,
};
use ui_context::{
    UIID,
    UIContext,
};
use utils::{
    clamp,
    map_range,
    val_to_string,
};
use widget::TextBox;

pub type Idx = uint;
pub type CursorX = f64;

/// Represents the state of the text_box widget.
#[deriving(Show, PartialEq)]
pub struct State(WidgetState, Capturing);

/// Represents the next tier of state.
#[deriving(Show, PartialEq)]
pub enum WidgetState {
    Normal,
    Highlighted(Element),
    Clicked(Element),
}

/// Whether the textbox is currently captured or not.
#[deriving(Show, PartialEq)]
pub enum Capturing {
    Uncaptured,
    Captured(Idx, CursorX),
}

/// Represents an element of the TextBox widget.
#[deriving(Show, PartialEq)]
pub enum Element {
    Nill,
    Rect,
    Text(Idx, CursorX),
}

impl State {
    /// Return the associated Rectangle state.
    fn as_rectangle_state(&self) -> rectangle::State {
        match self {
            &State(state, capturing) => match capturing {
                Captured(_, _) => rectangle::Normal,
                Uncaptured => match state {
                    Normal => rectangle::Normal,
                    Highlighted(_) => rectangle::Highlighted,
                    Clicked(_) => rectangle::Clicked,
                },
            }
        }
    }
}

widget_fns!(TextBox, State, TextBox(State(Normal, Uncaptured)))

static TEXT_PADDING: f64 = 5f64;

/// Draw the text_box widget.
pub fn draw(args: &RenderArgs,
            gl: &mut Gl,
            uic: &mut UIContext,
            ui_id: UIID,
            pos: Point<f64>,
            width: f64,
            height: f64,
            font_size: FontSize,
            frame: Framing,
            color: Color,
            text: &mut String,
            callback: |&mut String|) {

    let mouse = uic.get_mouse_state();
    let state = *get_state(uic, ui_id);

    // Rect.
    let frame_w = match frame { Frame(w, _) => w, NoFrame => 0.0 };
    let frame_w2 = frame_w * 2.0;
    let pad_pos = pos + Point::new(frame_w, frame_w, 0.0);
    let pad_w = width - frame_w2;
    let pad_h = height - frame_w2;
    let text_pos = Point::new(pad_pos.x + TEXT_PADDING,
                              pad_pos.y + (pad_h - font_size as f64) / 2.0,
                              0.0);
    let text_w = label::width(uic, font_size, text.as_slice());
    let over_elem = over_elem(uic, pos, mouse.pos, width, height,
                              pad_pos, pad_w, pad_h,
                              text_pos, text_w, font_size, text.as_slice());
    let mut new_state = get_new_state(over_elem, state, mouse);

    rectangle::draw(args, gl, new_state.as_rectangle_state(), pos, width, height, frame, color);
    label::draw(args, gl, uic, text_pos, font_size, color.plain_contrast(), text.as_slice());

    let new_state = match new_state { State(w_state, capturing) => match capturing {
        Uncaptured => new_state,
        Captured(idx, cursor_x) => {
            let context = Context::abs(args.width as f64, args.height as f64);
            let (r, g, b, a) = color.plain_contrast().as_tuple();
            context
                .line(cursor_x, pad_pos.y, cursor_x, pad_pos.y + pad_h)
                .round_border_width(1f64)
                .rgba(r, g, b, a)
                .draw(gl);
            let mut new_idx = idx;
            let mut new_cursor_x = cursor_x;

            // Check for control keys.
            let pressed_keys = uic.get_pressed_keys();
            for key in pressed_keys.iter() {
                match *key {
                    Backspace => {
                        if text.len() > 0u
                        && text.len() >= idx
                        && idx > 0u {
                            let rem_idx = idx - 1u;
                            new_cursor_x = {
                                let c = uic.get_character(font_size,
                                                          text.as_slice().char_at(rem_idx));
                                cursor_x - (c.glyph.advance().x >> 16) as f64
                            };
                            let new_text = String::from_str(text.as_slice().slice_to(rem_idx))
                                .append(text.as_slice().slice_from(idx));
                            *text = new_text;
                            new_idx = rem_idx;
                        }
                    },
                    _ => (),
                }
            }

            // Check for entered text.
            let entered_text = uic.get_entered_text();
            for t in entered_text.iter() {
                let new_text = String::from_str(text.as_slice().slice_to(idx))
                    .append(t.as_slice()).append(text.as_slice().slice_from(idx));
                *text = new_text;
                new_idx += t.len();
                for ch in t.as_slice().chars() {
                    let c = uic.get_character(font_size, ch);
                    new_cursor_x += (c.glyph.advance().x >> 16) as f64;
                }
            }

            State(w_state, Captured(new_idx, new_cursor_x))
        },
    }};

    set_state(uic, ui_id, new_state);

}

/// Check if cursor is over the pad and if so, which
fn over_elem(uic: &mut UIContext,
             pos: Point<f64>,
             mouse_pos: Point<f64>,
             rect_w: f64,
             rect_h: f64,
             pad_pos: Point<f64>,
             pad_w: f64,
             pad_h: f64,
             text_pos: Point<f64>,
             text_w: f64,
             font_size: FontSize,
             text: &str) -> Element {
    match rectangle::is_over(pos, mouse_pos, rect_w, rect_h) {
        false => Nill,
        true => match rectangle::is_over(pad_pos, mouse_pos, pad_w, pad_h) {
            false => Rect,
            true => {
                let (idx, cursor_x) = closest_idx(uic, mouse_pos, pad_h, pad_pos.y, text_pos.x, text_w, font_size, text);
                Text(idx, cursor_x)
            },
        },
    }
}

/// Check which character is closest to the mouse cursor.
fn closest_idx(uic: &mut UIContext,
               mouse_pos: Point<f64>,
               pad_h: f64,
               pad_y: f64,
               text_x: f64,
               text_w: f64,
               font_size: FontSize,
               text: &str) -> (Idx, f64) {
    if mouse_pos.x <= text_x { return (0u, text_x) }
    let mut x = text_x;
    let mut prev_x = x;
    let mut left_x = text_x;
    for (i, ch) in text.chars().enumerate() {
        let character = uic.get_character(font_size, ch);
        let char_w = (character.glyph.advance().x >> 16) as f64;
        x += char_w;
        let right_x = prev_x + char_w / 2.0;
        if mouse_pos.x > left_x && mouse_pos.x < right_x { return (i, prev_x) }
        prev_x = x;
        left_x = right_x;
    }
    (text.len(), text_x + text_w)
}

/// Check and return the current state of the TextBox.
fn get_new_state(over_elem: Element,
                 prev_box_state: State,
                 mouse: MouseState) -> State {
    match prev_box_state {
        State(prev, Uncaptured) => {
            match (over_elem, prev, mouse.left) {
                (_, Normal, Down) => State(Normal, Uncaptured),
                (Nill, Normal, Up) | (Nill, Highlighted(_), Up) => State(Normal, Uncaptured),
                (_, Normal, Up) | (_, Highlighted(_), Up) => State(Highlighted(over_elem), Uncaptured),
                (_, Highlighted(p_elem), Down) | (_, Clicked(p_elem), Down) =>
                    State(Clicked(p_elem), Uncaptured),
                (Text(idx, x), Clicked(Text(_, _)), Up) => State(Highlighted(over_elem), Captured(idx, x)),
                (Nill, _, _) => State(Normal, Uncaptured),
                _ => prev_box_state,
            }
        },
        State(prev, Captured(p_idx, p_x)) => {
            match (over_elem, prev, mouse.left) {
                (Nill, Clicked(Nill), Up) => State(Normal, Uncaptured),
                (Text(idx, x), Clicked(Text(_, _)), Up) => State(Highlighted(over_elem), Captured(idx, x)),
                (_, Normal, Up) | (_, Highlighted(_), Up) | (_, Clicked(_), Up)  =>
                    State(Highlighted(over_elem), Captured(p_idx, p_x)),
                (_, Highlighted(p_elem), Down) | (_, Clicked(p_elem), Down) =>
                    State(Clicked(p_elem), Captured(p_idx, p_x)),
                _ => prev_box_state,
            }
        },
    }
}

<|endoftext|>


<commit_before>use crate::utils::{
    in_macro, match_def_path, match_qpath, paths, snippet_with_applicability, span_help_and_lint, span_lint_and_sugg,
};
use if_chain::if_chain;
use rustc::declare_lint_pass;
use rustc::hir::{BorrowKind, Expr, ExprKind, HirVec, Mutability, QPath};
use rustc::lint::{in_external_macro, LateContext, LateLintPass, LintArray, LintPass};
use rustc_errors::Applicability;
use rustc_session::declare_tool_lint;

declare_clippy_lint! {
    /// **What it does:** Checks for `mem::replace()` on an `Option` with
    /// `None`.
    ///
    /// **Why is this bad?** `Option` already has the method `take()` for
    /// taking its current value (Some(..) or None) and replacing it with
    /// `None`.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust
    /// use std::mem;
    ///
    /// let mut an_option = Some(0);
    /// let replaced = mem::replace(&mut an_option, None);
    /// ```
    /// Is better expressed with:
    /// ```rust
    /// let mut an_option = Some(0);
    /// let taken = an_option.take();
    /// ```
    pub MEM_REPLACE_OPTION_WITH_NONE,
    style,
    "replacing an `Option` with `None` instead of `take()`"
}

declare_clippy_lint! {
    /// **What it does:** Checks for `mem::replace(&mut _, mem::uninitialized())`
    /// and `mem::replace(&mut _, mem::zeroed())`.
    ///
    /// **Why is this bad?** This will lead to undefined behavior even if the
    /// value is overwritten later, because the uninitialized value may be
    /// observed in the case of a panic.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    ///
    /// ```
    /// use std::mem;
    ///# fn may_panic(v: Vec<i32>) -> Vec<i32> { v }
    ///
    /// #[allow(deprecated, invalid_value)]
    /// fn myfunc (v: &mut Vec<i32>) {
    ///     let taken_v = unsafe { mem::replace(v, mem::uninitialized()) };
    ///     let new_v = may_panic(taken_v); // undefined behavior on panic
    ///     mem::forget(mem::replace(v, new_v));
    /// }
    /// ```
    ///
    /// The [take_mut](https://docs.rs/take_mut) crate offers a sound solution,
    /// at the cost of either lazily creating a replacement value or aborting
    /// on panic, to ensure that the uninitialized value cannot be observed.
    pub MEM_REPLACE_WITH_UNINIT,
    correctness,
    "`mem::replace(&mut _, mem::uninitialized())` or `mem::replace(&mut _, mem::zeroed())`"
}

declare_clippy_lint! {
    /// **What it does:** Checks for `std::mem::replace` on a value of type
    /// `T` with `T::default()`.
    ///
    /// **Why is this bad?** `std::mem` module already has the method `take` to
    /// take the current value and replace it with the default value of that type.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust
    /// let mut text = String::from("foo");
    /// let replaced = std::mem::replace(&mut text, String::default());
    /// ```
    /// Is better expressed with:
    /// ```rust
    /// let mut text = String::from("foo");
    /// let taken = std::mem::take(&mut text);
    /// ```
    pub MEM_REPLACE_WITH_DEFAULT,
    style,
    "replacing a value of type `T` with `T::default()` instead of using `std::mem::take`"
}

declare_lint_pass!(MemReplace =>
    [MEM_REPLACE_OPTION_WITH_NONE, MEM_REPLACE_WITH_UNINIT, MEM_REPLACE_WITH_DEFAULT]);

fn check_replace_option_with_none(cx: &LateContext<'_, '_>, expr: &'_ Expr, args: &HirVec<Expr>) {
    if let ExprKind::Path(ref replacement_qpath) = args[1].kind {
        // Check that second argument is `Option::None`
        if match_qpath(replacement_qpath, &paths::OPTION_NONE) {
            // Since this is a late pass (already type-checked),
            // and we already know that the second argument is an
            // `Option`, we do not need to check the first
            // argument's type. All that's left is to get
            // replacee's path.
            let replaced_path = match args[0].kind {
                ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, ref replaced) => {
                    if let ExprKind::Path(QPath::Resolved(None, ref replaced_path)) = replaced.kind {
                        replaced_path
                    } else {
                        return;
                    }
                },
                ExprKind::Path(QPath::Resolved(None, ref replaced_path)) => replaced_path,
                _ => return,
            };

            let mut applicability = Applicability::MachineApplicable;
            span_lint_and_sugg(
                cx,
                MEM_REPLACE_OPTION_WITH_NONE,
                expr.span,
                "replacing an `Option` with `None`",
                "consider `Option::take()` instead",
                format!(
                    "{}.take()",
                    snippet_with_applicability(cx, replaced_path.span, "", &mut applicability)
                ),
                applicability,
            );
        }
    }
}

fn check_replace_with_uninit(cx: &LateContext<'_, '_>, expr: &'_ Expr, args: &HirVec<Expr>) {
    if let ExprKind::Call(ref repl_func, ref repl_args) = args[1].kind {
        if_chain! {
            if repl_args.is_empty();
            if let ExprKind::Path(ref repl_func_qpath) = repl_func.kind;
            if let Some(repl_def_id) = cx.tables.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id();
            then {
                if match_def_path(cx, repl_def_id, &paths::MEM_UNINITIALIZED) {
                    span_help_and_lint(
                        cx,
                        MEM_REPLACE_WITH_UNINIT,
                        expr.span,
                        "replacing with `mem::uninitialized()`",
                        "consider using the `take_mut` crate instead",
                    );
                } else if match_def_path(cx, repl_def_id, &paths::MEM_ZEROED) &&
                        !cx.tables.expr_ty(&args[1]).is_primitive() {
                    span_help_and_lint(
                        cx,
                        MEM_REPLACE_WITH_UNINIT,
                        expr.span,
                        "replacing with `mem::zeroed()`",
                        "consider using a default value or the `take_mut` crate instead",
                    );
                }
            }
        }
    }
}

fn check_replace_with_default(cx: &LateContext<'_, '_>, expr: &'_ Expr, args: &HirVec<Expr>) {
    if let ExprKind::Call(ref repl_func, _) = args[1].kind {
        if_chain! {
            if !in_macro(expr.span) && !in_external_macro(cx.tcx.sess, expr.span);
            if let ExprKind::Path(ref repl_func_qpath) = repl_func.kind;
            if let Some(repl_def_id) = cx.tables.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id();
            if match_def_path(cx, repl_def_id, &paths::DEFAULT_TRAIT_METHOD);
            then {
                let mut applicability = Applicability::MachineApplicable;

                span_lint_and_sugg(
                    cx,
                    MEM_REPLACE_WITH_DEFAULT,
                    expr.span,
                    "replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`",
                    "consider using",
                    format!(
                        "std::mem::take({})",
                        snippet_with_applicability(cx, args[0].span, "", &mut applicability)
                    ),
                    applicability,
                );
            }
        }
    }
}

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MemReplace {
    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
        if_chain! {
            // Check that `expr` is a call to `mem::replace()`
            if let ExprKind::Call(ref func, ref func_args) = expr.kind;
            if func_args.len() == 2;
            if let ExprKind::Path(ref func_qpath) = func.kind;
            if let Some(def_id) = cx.tables.qpath_res(func_qpath, func.hir_id).opt_def_id();
            if match_def_path(cx, def_id, &paths::MEM_REPLACE);

            then {
                check_replace_option_with_none(cx, expr, &func_args);
                check_replace_with_uninit(cx, expr, &func_args);
                check_replace_with_default(cx, expr, &func_args);
            }
        }
    }
}
<commit_msg>Destructure `mem:replace` arguments<commit_after>use crate::utils::{
    in_macro, match_def_path, match_qpath, paths, snippet_with_applicability, span_help_and_lint, span_lint_and_sugg,
};
use if_chain::if_chain;
use rustc::declare_lint_pass;
use rustc::hir::{BorrowKind, Expr, ExprKind, Mutability, QPath};
use rustc::lint::{in_external_macro, LateContext, LateLintPass, LintArray, LintPass};
use rustc_errors::Applicability;
use rustc_session::declare_tool_lint;
use syntax::source_map::Span;

declare_clippy_lint! {
    /// **What it does:** Checks for `mem::replace()` on an `Option` with
    /// `None`.
    ///
    /// **Why is this bad?** `Option` already has the method `take()` for
    /// taking its current value (Some(..) or None) and replacing it with
    /// `None`.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust
    /// use std::mem;
    ///
    /// let mut an_option = Some(0);
    /// let replaced = mem::replace(&mut an_option, None);
    /// ```
    /// Is better expressed with:
    /// ```rust
    /// let mut an_option = Some(0);
    /// let taken = an_option.take();
    /// ```
    pub MEM_REPLACE_OPTION_WITH_NONE,
    style,
    "replacing an `Option` with `None` instead of `take()`"
}

declare_clippy_lint! {
    /// **What it does:** Checks for `mem::replace(&mut _, mem::uninitialized())`
    /// and `mem::replace(&mut _, mem::zeroed())`.
    ///
    /// **Why is this bad?** This will lead to undefined behavior even if the
    /// value is overwritten later, because the uninitialized value may be
    /// observed in the case of a panic.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    ///
    /// ```
    /// use std::mem;
    ///# fn may_panic(v: Vec<i32>) -> Vec<i32> { v }
    ///
    /// #[allow(deprecated, invalid_value)]
    /// fn myfunc (v: &mut Vec<i32>) {
    ///     let taken_v = unsafe { mem::replace(v, mem::uninitialized()) };
    ///     let new_v = may_panic(taken_v); // undefined behavior on panic
    ///     mem::forget(mem::replace(v, new_v));
    /// }
    /// ```
    ///
    /// The [take_mut](https://docs.rs/take_mut) crate offers a sound solution,
    /// at the cost of either lazily creating a replacement value or aborting
    /// on panic, to ensure that the uninitialized value cannot be observed.
    pub MEM_REPLACE_WITH_UNINIT,
    correctness,
    "`mem::replace(&mut _, mem::uninitialized())` or `mem::replace(&mut _, mem::zeroed())`"
}

declare_clippy_lint! {
    /// **What it does:** Checks for `std::mem::replace` on a value of type
    /// `T` with `T::default()`.
    ///
    /// **Why is this bad?** `std::mem` module already has the method `take` to
    /// take the current value and replace it with the default value of that type.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust
    /// let mut text = String::from("foo");
    /// let replaced = std::mem::replace(&mut text, String::default());
    /// ```
    /// Is better expressed with:
    /// ```rust
    /// let mut text = String::from("foo");
    /// let taken = std::mem::take(&mut text);
    /// ```
    pub MEM_REPLACE_WITH_DEFAULT,
    style,
    "replacing a value of type `T` with `T::default()` instead of using `std::mem::take`"
}

declare_lint_pass!(MemReplace =>
    [MEM_REPLACE_OPTION_WITH_NONE, MEM_REPLACE_WITH_UNINIT, MEM_REPLACE_WITH_DEFAULT]);

fn check_replace_option_with_none(cx: &LateContext<'_, '_>, src: &Expr, dest: &Expr, expr_span: Span) {
    if let ExprKind::Path(ref replacement_qpath) = src.kind {
        // Check that second argument is `Option::None`
        if match_qpath(replacement_qpath, &paths::OPTION_NONE) {
            // Since this is a late pass (already type-checked),
            // and we already know that the second argument is an
            // `Option`, we do not need to check the first
            // argument's type. All that's left is to get
            // replacee's path.
            let replaced_path = match dest.kind {
                ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, ref replaced) => {
                    if let ExprKind::Path(QPath::Resolved(None, ref replaced_path)) = replaced.kind {
                        replaced_path
                    } else {
                        return;
                    }
                },
                ExprKind::Path(QPath::Resolved(None, ref replaced_path)) => replaced_path,
                _ => return,
            };

            let mut applicability = Applicability::MachineApplicable;
            span_lint_and_sugg(
                cx,
                MEM_REPLACE_OPTION_WITH_NONE,
                expr_span,
                "replacing an `Option` with `None`",
                "consider `Option::take()` instead",
                format!(
                    "{}.take()",
                    snippet_with_applicability(cx, replaced_path.span, "", &mut applicability)
                ),
                applicability,
            );
        }
    }
}

fn check_replace_with_uninit(cx: &LateContext<'_, '_>, src: &Expr, expr_span: Span) {
    if let ExprKind::Call(ref repl_func, ref repl_args) = src.kind {
        if_chain! {
            if repl_args.is_empty();
            if let ExprKind::Path(ref repl_func_qpath) = repl_func.kind;
            if let Some(repl_def_id) = cx.tables.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id();
            then {
                if match_def_path(cx, repl_def_id, &paths::MEM_UNINITIALIZED) {
                    span_help_and_lint(
                        cx,
                        MEM_REPLACE_WITH_UNINIT,
                        expr_span,
                        "replacing with `mem::uninitialized()`",
                        "consider using the `take_mut` crate instead",
                    );
                } else if match_def_path(cx, repl_def_id, &paths::MEM_ZEROED) &&
                        !cx.tables.expr_ty(src).is_primitive() {
                    span_help_and_lint(
                        cx,
                        MEM_REPLACE_WITH_UNINIT,
                        expr_span,
                        "replacing with `mem::zeroed()`",
                        "consider using a default value or the `take_mut` crate instead",
                    );
                }
            }
        }
    }
}

fn check_replace_with_default(cx: &LateContext<'_, '_>, src: &Expr, dest: &Expr, expr_span: Span) {
    if let ExprKind::Call(ref repl_func, _) = src.kind {
        if_chain! {
            if !in_macro(expr_span) && !in_external_macro(cx.tcx.sess, expr_span);
            if let ExprKind::Path(ref repl_func_qpath) = repl_func.kind;
            if let Some(repl_def_id) = cx.tables.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id();
            if match_def_path(cx, repl_def_id, &paths::DEFAULT_TRAIT_METHOD);
            then {
                let mut applicability = Applicability::MachineApplicable;

                span_lint_and_sugg(
                    cx,
                    MEM_REPLACE_WITH_DEFAULT,
                    expr_span,
                    "replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`",
                    "consider using",
                    format!(
                        "std::mem::take({})",
                        snippet_with_applicability(cx, dest.span, "", &mut applicability)
                    ),
                    applicability,
                );
            }
        }
    }
}

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MemReplace {
    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
        if_chain! {
            // Check that `expr` is a call to `mem::replace()`
            if let ExprKind::Call(ref func, ref func_args) = expr.kind;
            if let ExprKind::Path(ref func_qpath) = func.kind;
            if let Some(def_id) = cx.tables.qpath_res(func_qpath, func.hir_id).opt_def_id();
            if match_def_path(cx, def_id, &paths::MEM_REPLACE);
            if let [dest, src] = &**func_args;
            then {
                check_replace_option_with_none(cx, src, dest, expr.span);
                check_replace_with_uninit(cx, src, expr.span);
                check_replace_with_default(cx, src, dest, expr.span);
            }
        }
    }
}
<|endoftext|>


<commit_before>use crate::rustc::hir::{Expr, ExprKind, MutMutable, QPath};
use crate::rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
use crate::rustc::{declare_tool_lint, lint_array};
use crate::utils::{match_def_path, match_qpath, opt_def_id, paths, snippet, span_lint_and_sugg};
use if_chain::if_chain;

/// **What it does:** Checks for `mem::replace()` on an `Option` with
/// `None`.
///
/// **Why is this bad?** `Option` already has the method `take()` for
/// taking its current value (Some(..) or None) and replacing it with
/// `None`.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// let mut an_option = Some(0);
/// let replaced = mem::replace(&mut an_option, None);
/// ```
/// Is better expressed with:
/// ```rust
/// let mut an_option = Some(0);
/// let taken = an_option.take();
/// ```
declare_clippy_lint! {
    pub MEM_REPLACE_OPTION_WITH_NONE,
    style,
    "replacing an `Option` with `None` instead of `take()`"
}

pub struct MemReplace;

impl LintPass for MemReplace {
    fn get_lints(&self) -> LintArray {
        lint_array![MEM_REPLACE_OPTION_WITH_NONE]
    }
}

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MemReplace {
    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
        if_chain! {
            // Check that `expr` is a call to `mem::replace()`
            if let ExprKind::Call(ref func, ref func_args) = expr.node;
            if func_args.len() == 2;
            if let ExprKind::Path(ref func_qpath) = func.node;
            if let Some(def_id) = opt_def_id(cx.tables.qpath_def(func_qpath, func.hir_id));
            if match_def_path(cx.tcx, def_id, &paths::MEM_REPLACE);

            // Check that second argument is `Option::None`
            if let ExprKind::Path(ref replacement_qpath) = func_args[1].node;
            if match_qpath(replacement_qpath, &paths::OPTION_NONE);

            then {
                // Since this is a late pass (already type-checked),
                // and we already know that the second argument is an
                // `Option`, we do not need to check if the first
                // argument's type. All that's left is to get
                // replacee's path.
                let replaced_path = match func_args[0].node {
                    ExprKind::AddrOf(MutMutable, ref replaced) => {
                        if let ExprKind::Path(QPath::Resolved(None, ref replaced_path)) = replaced.node {
                            replaced_path
                        } else {
                            return
                        }
                    },
                    ExprKind::Path(QPath::Resolved(None, ref replaced_path)) => replaced_path,
                    _ => return,
                };

                span_lint_and_sugg(
                    cx,
                    MEM_REPLACE_OPTION_WITH_NONE,
                    expr.span,
                    "replacing an `Option` with `None`",
                    "consider `Option::take()` instead",
                    format!("{}.take()", snippet(cx, replaced_path.span, ""))
                );
            }
        }
    }
}
<commit_msg>mem_replace: fix grammar.<commit_after>use crate::rustc::hir::{Expr, ExprKind, MutMutable, QPath};
use crate::rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
use crate::rustc::{declare_tool_lint, lint_array};
use crate::utils::{match_def_path, match_qpath, opt_def_id, paths, snippet, span_lint_and_sugg};
use if_chain::if_chain;

/// **What it does:** Checks for `mem::replace()` on an `Option` with
/// `None`.
///
/// **Why is this bad?** `Option` already has the method `take()` for
/// taking its current value (Some(..) or None) and replacing it with
/// `None`.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// let mut an_option = Some(0);
/// let replaced = mem::replace(&mut an_option, None);
/// ```
/// Is better expressed with:
/// ```rust
/// let mut an_option = Some(0);
/// let taken = an_option.take();
/// ```
declare_clippy_lint! {
    pub MEM_REPLACE_OPTION_WITH_NONE,
    style,
    "replacing an `Option` with `None` instead of `take()`"
}

pub struct MemReplace;

impl LintPass for MemReplace {
    fn get_lints(&self) -> LintArray {
        lint_array![MEM_REPLACE_OPTION_WITH_NONE]
    }
}

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MemReplace {
    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
        if_chain! {
            // Check that `expr` is a call to `mem::replace()`
            if let ExprKind::Call(ref func, ref func_args) = expr.node;
            if func_args.len() == 2;
            if let ExprKind::Path(ref func_qpath) = func.node;
            if let Some(def_id) = opt_def_id(cx.tables.qpath_def(func_qpath, func.hir_id));
            if match_def_path(cx.tcx, def_id, &paths::MEM_REPLACE);

            // Check that second argument is `Option::None`
            if let ExprKind::Path(ref replacement_qpath) = func_args[1].node;
            if match_qpath(replacement_qpath, &paths::OPTION_NONE);

            then {
                // Since this is a late pass (already type-checked),
                // and we already know that the second argument is an
                // `Option`, we do not need to check the first
                // argument's type. All that's left is to get
                // replacee's path.
                let replaced_path = match func_args[0].node {
                    ExprKind::AddrOf(MutMutable, ref replaced) => {
                        if let ExprKind::Path(QPath::Resolved(None, ref replaced_path)) = replaced.node {
                            replaced_path
                        } else {
                            return
                        }
                    },
                    ExprKind::Path(QPath::Resolved(None, ref replaced_path)) => replaced_path,
                    _ => return,
                };

                span_lint_and_sugg(
                    cx,
                    MEM_REPLACE_OPTION_WITH_NONE,
                    expr.span,
                    "replacing an `Option` with `None`",
                    "consider `Option::take()` instead",
                    format!("{}.take()", snippet(cx, replaced_path.span, ""))
                );
            }
        }
    }
}
<|endoftext|>


<commit_before>//! Unix specific definitions
use std;
use std::io::{self, Read, Write};
use std::sync;
use std::sync::atomic;
use libc;
use nix;
use nix::poll;
use nix::sys::signal;
use nix::sys::termios;

use char_iter;
use consts::{self, KeyPress};
use ::Result;
use ::error;
use super::{RawMode, RawReader, Term};

const STDIN_FILENO: libc::c_int = libc::STDIN_FILENO;
const STDOUT_FILENO: libc::c_int = libc::STDOUT_FILENO;

/// Unsupported Terminals that don't support RAW mode
static UNSUPPORTED_TERM: [&'static str; 3] = ["dumb", "cons25", "emacs"];

#[cfg(any(target_os = "macos", target_os = "freebsd"))]
const TIOCGWINSZ: libc::c_ulong = 0x40087468;

#[cfg(any(all(target_os = "linux", target_env = "gnu"), target_os = "android"))]
const TIOCGWINSZ: libc::c_ulong = 0x5413;

#[cfg(all(target_os = "linux", target_env = "musl"))]
const TIOCGWINSZ: libc::c_int = 0x5413;

fn get_win_size() -> (usize, usize) {
    use std::mem::zeroed;
    use libc::c_ushort;

    unsafe {
        #[repr(C)]
        struct winsize {
            ws_row: c_ushort,
            ws_col: c_ushort,
            ws_xpixel: c_ushort,
            ws_ypixel: c_ushort,
        }

        let mut size: winsize = zeroed();
        match libc::ioctl(STDOUT_FILENO, TIOCGWINSZ, &mut size) {
            0 => (size.ws_col as usize, size.ws_row as usize), // TODO getCursorPosition
            _ => (80, 24),
        }
    }
}

/// Check TERM environment variable to see if current term is in our
/// unsupported list
fn is_unsupported_term() -> bool {
    use std::ascii::AsciiExt;
    match std::env::var("TERM") {
        Ok(term) => {
            for iter in &UNSUPPORTED_TERM {
                if (*iter).eq_ignore_ascii_case(&term) {
                    return true;
                }
            }
            false
        }
        Err(_) => false,
    }
}

/// Return whether or not STDIN, STDOUT or STDERR is a TTY
fn is_a_tty(fd: libc::c_int) -> bool {
    unsafe { libc::isatty(fd) != 0 }
}

pub type Mode = termios::Termios;

impl RawMode for Mode {
    /// Disable RAW mode for the terminal.
    fn disable_raw_mode(&self) -> Result<()> {
        try!(termios::tcsetattr(STDIN_FILENO, termios::TCSADRAIN, self));
        Ok(())
    }
}

// Rust std::io::Stdin is buffered with no way to know if bytes are available.
// So we use low-level stuff instead...
struct StdinRaw {}

impl Read for StdinRaw {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        let res = unsafe {
            libc::read(STDIN_FILENO,
                       buf.as_mut_ptr() as *mut libc::c_void,
                       buf.len() as libc::size_t)
        };
        if res == -1 {
            Err(io::Error::last_os_error())
        } else {
            Ok(res as usize)
        }
    }
}

/// Console input reader
pub struct PosixRawReader {
    chars: char_iter::Chars<StdinRaw>,
}

impl PosixRawReader {
    pub fn new() -> Result<PosixRawReader> {
        let stdin = StdinRaw {};
        Ok(PosixRawReader { chars: char_iter::chars(stdin) })
    }

    fn escape_sequence(&mut self) -> Result<KeyPress> {
        // Read the next two bytes representing the escape sequence.
        let seq1 = try!(self.next_char());
        if seq1 == '[' {
            // ESC [ sequences.
            let seq2 = try!(self.next_char());
            if seq2.is_digit(10) {
                // Extended escape, read additional byte.
                let seq3 = try!(self.next_char());
                if seq3 == '~' {
                    match seq2 {
                        // '1' => Ok(KeyPress::Home),
                        '3' => Ok(KeyPress::Delete),
                        // '4' => Ok(KeyPress::End),
                        '5' => Ok(KeyPress::PageUp),
                        '6' => Ok(KeyPress::PageDown),
                        '7' => Ok(KeyPress::Home),
                        '8' => Ok(KeyPress::End),
                        _ => Ok(KeyPress::UnknownEscSeq),
                    }
                } else {
                    Ok(KeyPress::UnknownEscSeq)
                }
            } else {
                match seq2 {
                    'A' => Ok(KeyPress::Up), // ANSI
                    'B' => Ok(KeyPress::Down),
                    'C' => Ok(KeyPress::Right),
                    'D' => Ok(KeyPress::Left),
                    'F' => Ok(KeyPress::End),
                    'H' => Ok(KeyPress::Home),
                    _ => Ok(KeyPress::UnknownEscSeq),
                }
            }
        } else if seq1 == 'O' {
            // ESC O sequences.
            let seq2 = try!(self.next_char());
            match seq2 {
                'A' => Ok(KeyPress::Up),
                'B' => Ok(KeyPress::Down),
                'C' => Ok(KeyPress::Right),
                'D' => Ok(KeyPress::Left),
                'F' => Ok(KeyPress::End),
                'H' => Ok(KeyPress::Home),
                _ => Ok(KeyPress::UnknownEscSeq),
            }
        } else {
            // TODO ESC-N (n): search history forward not interactively
            // TODO ESC-P (p): search history backward not interactively
            // TODO ESC-R (r): Undo all changes made to this line.
            match seq1 {
                '\x08' => Ok(KeyPress::Meta('\x08')), // Backspace
                '<' => Ok(KeyPress::Meta('<')),
                '>' => Ok(KeyPress::Meta('>')),
                'b' | 'B' => Ok(KeyPress::Meta('B')),
                'c' | 'C' => Ok(KeyPress::Meta('C')),
                'd' | 'D' => Ok(KeyPress::Meta('D')),
                'f' | 'F' => Ok(KeyPress::Meta('F')),
                'l' | 'L' => Ok(KeyPress::Meta('L')),
                't' | 'T' => Ok(KeyPress::Meta('T')),
                'u' | 'U' => Ok(KeyPress::Meta('U')),
                'y' | 'Y' => Ok(KeyPress::Meta('Y')),
                '\x7f' => Ok(KeyPress::Meta('\x7f')), // Delete
                _ => {
                    // writeln!(io::stderr(), "key: {:?}, seq1: {:?}", KeyPress::Esc, seq1).unwrap();
                    Ok(KeyPress::UnknownEscSeq)
                }
            }
        }
    }
}

impl RawReader for PosixRawReader {
    fn next_key(&mut self, timeout_ms: i32) -> Result<KeyPress> {
        let c = try!(self.next_char());

        let mut key = consts::char_to_key_press(c);
        if key == KeyPress::Esc {
            let mut fds =
                [poll::PollFd::new(STDIN_FILENO, poll::POLLIN, poll::EventFlags::empty())];
            match poll::poll(&mut fds, timeout_ms) {
                Ok(n) if n == 0 => {
                    // single escape
                }
                Ok(_) => {
                    // escape sequence
                    key = try!(self.escape_sequence())
                }
                // Err(ref e) if e.kind() == ErrorKind::Interrupted => continue,
                Err(e) => return Err(e.into()),
            }
        }
        Ok(key)
    }

    fn next_char(&mut self) -> Result<char> {
        match self.chars.next() {
            Some(c) => Ok(try!(c)),
            None => Err(error::ReadlineError::Eof),
        }
    }
}

static SIGWINCH_ONCE: sync::Once = sync::ONCE_INIT;
static SIGWINCH: atomic::AtomicBool = atomic::ATOMIC_BOOL_INIT;

fn install_sigwinch_handler() {
    SIGWINCH_ONCE.call_once(|| unsafe {
        let sigwinch = signal::SigAction::new(signal::SigHandler::Handler(sigwinch_handler),
                                              signal::SaFlags::empty(),
                                              signal::SigSet::empty());
        let _ = signal::sigaction(signal::SIGWINCH, &sigwinch);
    });
}

extern "C" fn sigwinch_handler(_: libc::c_int) {
    SIGWINCH.store(true, atomic::Ordering::SeqCst);
}

pub type Terminal = PosixTerminal;

#[derive(Clone,Debug)]
pub struct PosixTerminal {
    unsupported: bool,
    stdin_isatty: bool,
}

impl Term for PosixTerminal {
    type Reader = PosixRawReader;
    type Mode = Mode;

    fn new() -> PosixTerminal {
        let term = PosixTerminal {
            unsupported: is_unsupported_term(),
            stdin_isatty: is_a_tty(STDIN_FILENO),
        };
        if !term.unsupported && term.stdin_isatty && is_a_tty(STDOUT_FILENO) {
            install_sigwinch_handler();
        }
        term
    }

    // Init checks:

    /// Check if current terminal can provide a rich line-editing user interface.
    fn is_unsupported(&self) -> bool {
        self.unsupported
    }

    /// check if stdin is connected to a terminal.
    fn is_stdin_tty(&self) -> bool {
        self.stdin_isatty
    }

    // Interactive loop:

    /// Try to get the number of columns in the current terminal,
    /// or assume 80 if it fails.
    fn get_columns(&self) -> usize {
        let (cols, _) = get_win_size();
        cols
    }

    /// Try to get the number of rows in the current terminal,
    /// or assume 24 if it fails.
    fn get_rows(&self) -> usize {
        let (_, rows) = get_win_size();
        rows
    }

    fn enable_raw_mode(&self) -> Result<Mode> {
        use nix::errno::Errno::ENOTTY;
        use nix::sys::termios::{BRKINT, CS8, ECHO, ICANON, ICRNL, IEXTEN, INPCK, ISIG, ISTRIP,
                                IXON, /* OPOST, */ VMIN, VTIME};
        if !self.stdin_isatty {
            try!(Err(nix::Error::from_errno(ENOTTY)));
        }
        let original_mode = try!(termios::tcgetattr(STDIN_FILENO));
        let mut raw = original_mode;
        // disable BREAK interrupt, CR to NL conversion on input,
        // input parity check, strip high bit (bit 8), output flow control
        raw.c_iflag = raw.c_iflag & !(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
        // we don't want raw output, it turns newlines into straight linefeeds
        // raw.c_oflag = raw.c_oflag & !(OPOST); // disable all output processing
        raw.c_cflag = raw.c_cflag | (CS8); // character-size mark (8 bits)
        // disable echoing, canonical mode, extended input processing and signals
        raw.c_lflag = raw.c_lflag & !(ECHO | ICANON | IEXTEN | ISIG);
        raw.c_cc[VMIN] = 1; // One character-at-a-time input
        raw.c_cc[VTIME] = 0; // with blocking read
        try!(termios::tcsetattr(STDIN_FILENO, termios::TCSADRAIN, &raw));
        Ok(original_mode)
    }

    /// Create a RAW reader
    fn create_reader(&self) -> Result<PosixRawReader> {
        PosixRawReader::new()
    }

    /// Check if a SIGWINCH signal has been received
    fn sigwinch(&self) -> bool {
        SIGWINCH.compare_and_swap(true, false, atomic::Ordering::SeqCst)
    }

    /// Clear the screen. Used to handle ctrl+l
    fn clear_screen(&mut self, w: &mut Write) -> Result<()> {
        try!(w.write_all(b"\x1b[H\x1b[2J"));
        try!(w.flush());
        Ok(())
    }
}

#[cfg(all(unix,test))]
mod test {
    #[test]
    fn test_unsupported_term() {
        ::std::env::set_var("TERM", "xterm");
        assert_eq!(false, super::is_unsupported_term());

        ::std::env::set_var("TERM", "dumb");
        assert_eq!(true, super::is_unsupported_term());
    }
}
<commit_msg>Use libc winsize and TIOCGWINSZ<commit_after>//! Unix specific definitions
use std;
use std::io::{self, Read, Write};
use std::sync;
use std::sync::atomic;
use libc;
use nix;
use nix::poll;
use nix::sys::signal;
use nix::sys::termios;

use char_iter;
use consts::{self, KeyPress};
use ::Result;
use ::error;
use super::{RawMode, RawReader, Term};

const STDIN_FILENO: libc::c_int = libc::STDIN_FILENO;
const STDOUT_FILENO: libc::c_int = libc::STDOUT_FILENO;

/// Unsupported Terminals that don't support RAW mode
static UNSUPPORTED_TERM: [&'static str; 3] = ["dumb", "cons25", "emacs"];

fn get_win_size() -> (usize, usize) {
    use std::mem::zeroed;

    unsafe {
        let mut size: libc::winsize = zeroed();
        match libc::ioctl(STDOUT_FILENO, libc::TIOCGWINSZ, &mut size) {
            0 => (size.ws_col as usize, size.ws_row as usize), // TODO getCursorPosition
            _ => (80, 24),
        }
    }
}

/// Check TERM environment variable to see if current term is in our
/// unsupported list
fn is_unsupported_term() -> bool {
    use std::ascii::AsciiExt;
    match std::env::var("TERM") {
        Ok(term) => {
            for iter in &UNSUPPORTED_TERM {
                if (*iter).eq_ignore_ascii_case(&term) {
                    return true;
                }
            }
            false
        }
        Err(_) => false,
    }
}

/// Return whether or not STDIN, STDOUT or STDERR is a TTY
fn is_a_tty(fd: libc::c_int) -> bool {
    unsafe { libc::isatty(fd) != 0 }
}

pub type Mode = termios::Termios;

impl RawMode for Mode {
    /// Disable RAW mode for the terminal.
    fn disable_raw_mode(&self) -> Result<()> {
        try!(termios::tcsetattr(STDIN_FILENO, termios::TCSADRAIN, self));
        Ok(())
    }
}

// Rust std::io::Stdin is buffered with no way to know if bytes are available.
// So we use low-level stuff instead...
struct StdinRaw {}

impl Read for StdinRaw {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        let res = unsafe {
            libc::read(STDIN_FILENO,
                       buf.as_mut_ptr() as *mut libc::c_void,
                       buf.len() as libc::size_t)
        };
        if res == -1 {
            Err(io::Error::last_os_error())
        } else {
            Ok(res as usize)
        }
    }
}

/// Console input reader
pub struct PosixRawReader {
    chars: char_iter::Chars<StdinRaw>,
}

impl PosixRawReader {
    pub fn new() -> Result<PosixRawReader> {
        let stdin = StdinRaw {};
        Ok(PosixRawReader { chars: char_iter::chars(stdin) })
    }

    fn escape_sequence(&mut self) -> Result<KeyPress> {
        // Read the next two bytes representing the escape sequence.
        let seq1 = try!(self.next_char());
        if seq1 == '[' {
            // ESC [ sequences.
            let seq2 = try!(self.next_char());
            if seq2.is_digit(10) {
                // Extended escape, read additional byte.
                let seq3 = try!(self.next_char());
                if seq3 == '~' {
                    match seq2 {
                        // '1' => Ok(KeyPress::Home),
                        '3' => Ok(KeyPress::Delete),
                        // '4' => Ok(KeyPress::End),
                        '5' => Ok(KeyPress::PageUp),
                        '6' => Ok(KeyPress::PageDown),
                        '7' => Ok(KeyPress::Home),
                        '8' => Ok(KeyPress::End),
                        _ => Ok(KeyPress::UnknownEscSeq),
                    }
                } else {
                    Ok(KeyPress::UnknownEscSeq)
                }
            } else {
                match seq2 {
                    'A' => Ok(KeyPress::Up), // ANSI
                    'B' => Ok(KeyPress::Down),
                    'C' => Ok(KeyPress::Right),
                    'D' => Ok(KeyPress::Left),
                    'F' => Ok(KeyPress::End),
                    'H' => Ok(KeyPress::Home),
                    _ => Ok(KeyPress::UnknownEscSeq),
                }
            }
        } else if seq1 == 'O' {
            // ESC O sequences.
            let seq2 = try!(self.next_char());
            match seq2 {
                'A' => Ok(KeyPress::Up),
                'B' => Ok(KeyPress::Down),
                'C' => Ok(KeyPress::Right),
                'D' => Ok(KeyPress::Left),
                'F' => Ok(KeyPress::End),
                'H' => Ok(KeyPress::Home),
                _ => Ok(KeyPress::UnknownEscSeq),
            }
        } else {
            // TODO ESC-N (n): search history forward not interactively
            // TODO ESC-P (p): search history backward not interactively
            // TODO ESC-R (r): Undo all changes made to this line.
            match seq1 {
                '\x08' => Ok(KeyPress::Meta('\x08')), // Backspace
                '<' => Ok(KeyPress::Meta('<')),
                '>' => Ok(KeyPress::Meta('>')),
                'b' | 'B' => Ok(KeyPress::Meta('B')),
                'c' | 'C' => Ok(KeyPress::Meta('C')),
                'd' | 'D' => Ok(KeyPress::Meta('D')),
                'f' | 'F' => Ok(KeyPress::Meta('F')),
                'l' | 'L' => Ok(KeyPress::Meta('L')),
                't' | 'T' => Ok(KeyPress::Meta('T')),
                'u' | 'U' => Ok(KeyPress::Meta('U')),
                'y' | 'Y' => Ok(KeyPress::Meta('Y')),
                '\x7f' => Ok(KeyPress::Meta('\x7f')), // Delete
                _ => {
                    // writeln!(io::stderr(), "key: {:?}, seq1: {:?}", KeyPress::Esc, seq1).unwrap();
                    Ok(KeyPress::UnknownEscSeq)
                }
            }
        }
    }
}

impl RawReader for PosixRawReader {
    fn next_key(&mut self, timeout_ms: i32) -> Result<KeyPress> {
        let c = try!(self.next_char());

        let mut key = consts::char_to_key_press(c);
        if key == KeyPress::Esc {
            let mut fds =
                [poll::PollFd::new(STDIN_FILENO, poll::POLLIN, poll::EventFlags::empty())];
            match poll::poll(&mut fds, timeout_ms) {
                Ok(n) if n == 0 => {
                    // single escape
                }
                Ok(_) => {
                    // escape sequence
                    key = try!(self.escape_sequence())
                }
                // Err(ref e) if e.kind() == ErrorKind::Interrupted => continue,
                Err(e) => return Err(e.into()),
            }
        }
        Ok(key)
    }

    fn next_char(&mut self) -> Result<char> {
        match self.chars.next() {
            Some(c) => Ok(try!(c)),
            None => Err(error::ReadlineError::Eof),
        }
    }
}

static SIGWINCH_ONCE: sync::Once = sync::ONCE_INIT;
static SIGWINCH: atomic::AtomicBool = atomic::ATOMIC_BOOL_INIT;

fn install_sigwinch_handler() {
    SIGWINCH_ONCE.call_once(|| unsafe {
        let sigwinch = signal::SigAction::new(signal::SigHandler::Handler(sigwinch_handler),
                                              signal::SaFlags::empty(),
                                              signal::SigSet::empty());
        let _ = signal::sigaction(signal::SIGWINCH, &sigwinch);
    });
}

extern "C" fn sigwinch_handler(_: libc::c_int) {
    SIGWINCH.store(true, atomic::Ordering::SeqCst);
}

pub type Terminal = PosixTerminal;

#[derive(Clone,Debug)]
pub struct PosixTerminal {
    unsupported: bool,
    stdin_isatty: bool,
}

impl Term for PosixTerminal {
    type Reader = PosixRawReader;
    type Mode = Mode;

    fn new() -> PosixTerminal {
        let term = PosixTerminal {
            unsupported: is_unsupported_term(),
            stdin_isatty: is_a_tty(STDIN_FILENO),
        };
        if !term.unsupported && term.stdin_isatty && is_a_tty(STDOUT_FILENO) {
            install_sigwinch_handler();
        }
        term
    }

    // Init checks:

    /// Check if current terminal can provide a rich line-editing user interface.
    fn is_unsupported(&self) -> bool {
        self.unsupported
    }

    /// check if stdin is connected to a terminal.
    fn is_stdin_tty(&self) -> bool {
        self.stdin_isatty
    }

    // Interactive loop:

    /// Try to get the number of columns in the current terminal,
    /// or assume 80 if it fails.
    fn get_columns(&self) -> usize {
        let (cols, _) = get_win_size();
        cols
    }

    /// Try to get the number of rows in the current terminal,
    /// or assume 24 if it fails.
    fn get_rows(&self) -> usize {
        let (_, rows) = get_win_size();
        rows
    }

    fn enable_raw_mode(&self) -> Result<Mode> {
        use nix::errno::Errno::ENOTTY;
        use nix::sys::termios::{BRKINT, CS8, ECHO, ICANON, ICRNL, IEXTEN, INPCK, ISIG, ISTRIP,
                                IXON, /* OPOST, */ VMIN, VTIME};
        if !self.stdin_isatty {
            try!(Err(nix::Error::from_errno(ENOTTY)));
        }
        let original_mode = try!(termios::tcgetattr(STDIN_FILENO));
        let mut raw = original_mode;
        // disable BREAK interrupt, CR to NL conversion on input,
        // input parity check, strip high bit (bit 8), output flow control
        raw.c_iflag = raw.c_iflag & !(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
        // we don't want raw output, it turns newlines into straight linefeeds
        // raw.c_oflag = raw.c_oflag & !(OPOST); // disable all output processing
        raw.c_cflag = raw.c_cflag | (CS8); // character-size mark (8 bits)
        // disable echoing, canonical mode, extended input processing and signals
        raw.c_lflag = raw.c_lflag & !(ECHO | ICANON | IEXTEN | ISIG);
        raw.c_cc[VMIN] = 1; // One character-at-a-time input
        raw.c_cc[VTIME] = 0; // with blocking read
        try!(termios::tcsetattr(STDIN_FILENO, termios::TCSADRAIN, &raw));
        Ok(original_mode)
    }

    /// Create a RAW reader
    fn create_reader(&self) -> Result<PosixRawReader> {
        PosixRawReader::new()
    }

    /// Check if a SIGWINCH signal has been received
    fn sigwinch(&self) -> bool {
        SIGWINCH.compare_and_swap(true, false, atomic::Ordering::SeqCst)
    }

    /// Clear the screen. Used to handle ctrl+l
    fn clear_screen(&mut self, w: &mut Write) -> Result<()> {
        try!(w.write_all(b"\x1b[H\x1b[2J"));
        try!(w.flush());
        Ok(())
    }
}

#[cfg(all(unix,test))]
mod test {
    #[test]
    fn test_unsupported_term() {
        ::std::env::set_var("TERM", "xterm");
        assert_eq!(false, super::is_unsupported_term());

        ::std::env::set_var("TERM", "dumb");
        assert_eq!(true, super::is_unsupported_term());
    }
}
<|endoftext|>