<commit_before>//! Concrete error types for all operations which may be invalid in a certain const context.

use rustc_errors::struct_span_err;
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_session::config::nightly_options;
use rustc_session::parse::feature_err;
use rustc_span::symbol::sym;
use rustc_span::{Span, Symbol};

use super::ConstCx;

/// Emits an error if `op` is not allowed in the given const context.
pub fn non_const<O: NonConstOp>(ccx: &ConstCx<'_, '_>, op: O, span: Span) {
    debug!("illegal_op: op={:?}", op);

    if op.is_allowed_in_item(ccx) {
        return;
    }

    if ccx.tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you {
        ccx.tcx.sess.miri_unleashed_feature(span, O::feature_gate());
        return;
    }

    op.emit_error(ccx, span);
}

/// An operation that is not *always* allowed in a const context.
pub trait NonConstOp: std::fmt::Debug {
    /// Returns the `Symbol` corresponding to the feature gate that would enable this operation,
    /// or `None` if such a feature gate does not exist.
    fn feature_gate() -> Option<Symbol> {
        None
    }

    /// Returns `true` if this operation is allowed in the given item.
    ///
    /// This check should assume that we are not in a non-const `fn`, where all operations are
    /// legal.
    ///
    /// By default, it returns `true` if and only if this operation has a corresponding feature
    /// gate and that gate is enabled.
    fn is_allowed_in_item(&self, ccx: &ConstCx<'_, '_>) -> bool {
        Self::feature_gate().map_or(false, |gate| ccx.tcx.features().enabled(gate))
    }

    fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
        let mut err = struct_span_err!(
            ccx.tcx.sess,
            span,
            E0019,
            "{} contains unimplemented expression type",
            ccx.const_kind()
        );
        if let Some(feat) = Self::feature_gate() {
            err.help(&format!("add `#![feature({})]` to the crate attributes to enable", feat));
        }
        if ccx.tcx.sess.teach(&err.get_code().unwrap()) {
            err.note(
                "A function call isn't allowed in the const's initialization expression \
                      because the expression's value must be known at compile-time.",
            );
            err.note(
                "Remember: you can't use a function call inside a const's initialization \
                      expression! However, you can use it anywhere else.",
            );
        }
        err.emit();
    }
}

/// A function call where the callee is a pointer.
#[derive(Debug)]
pub struct FnCallIndirect;
impl NonConstOp for FnCallIndirect {
    fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
        let mut err =
            ccx.tcx.sess.struct_span_err(span, "function pointers are not allowed in const fn");
        err.emit();
    }
}

/// A function call where the callee is not marked as `const`.
#[derive(Debug)]
pub struct FnCallNonConst(pub DefId);
impl NonConstOp for FnCallNonConst {
    fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
        let mut err = struct_span_err!(
            ccx.tcx.sess,
            span,
            E0015,
            "calls in {}s are limited to constant functions, \
             tuple structs and tuple variants",
            ccx.const_kind(),
        );
        err.emit();
    }
}

/// A call to a `#[unstable]` const fn or `#[rustc_const_unstable]` function.
///
/// Contains the name of the feature that would allow the use of this function.
#[derive(Debug)]
pub struct FnCallUnstable(pub DefId, pub Symbol);
impl NonConstOp for FnCallUnstable {
    fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
        let FnCallUnstable(def_id, feature) = *self;

        let mut err = ccx.tcx.sess.struct_span_err(
            span,
            &format!("`{}` is not yet stable as a const fn", ccx.tcx.def_path_str(def_id)),
        );
        if nightly_options::is_nightly_build() {
            err.help(&format!("add `#![feature({})]` to the crate attributes to enable", feature));
        }
        err.emit();
    }
}

#[derive(Debug)]
pub struct HeapAllocation;
impl NonConstOp for HeapAllocation {
    fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
        let mut err = struct_span_err!(
            ccx.tcx.sess,
            span,
            E0010,
            "allocations are not allowed in {}s",
            ccx.const_kind()
        );
        err.span_label(span, format!("allocation not allowed in {}s", ccx.const_kind()));
        if ccx.tcx.sess.teach(&err.get_code().unwrap()) {
            err.note(
                "The value of statics and constants must be known at compile time, \
                 and they live for the entire lifetime of a program. Creating a boxed \
                 value allocates memory on the heap at runtime, and therefore cannot \
                 be done at compile time.",
            );
        }
        err.emit();
    }
}

#[derive(Debug)]
pub struct InlineAsm;
impl NonConstOp for InlineAsm {}

#[derive(Debug)]
pub struct LiveDrop(pub Option<Span>);
impl NonConstOp for LiveDrop {
    fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
        let mut diagnostic = struct_span_err!(
            ccx.tcx.sess,
            span,
            E0493,
            "destructors cannot be evaluated at compile-time"
        );
        diagnostic.span_label(span, format!("{}s cannot evaluate destructors", ccx.const_kind()));
        if let Some(span) = self.0 {
            diagnostic.span_label(span, "value is dropped here");
        }
        diagnostic.emit();
    }
}

#[derive(Debug)]
pub struct CellBorrow;
impl NonConstOp for CellBorrow {
    fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
        struct_span_err!(
            ccx.tcx.sess,
            span,
            E0492,
            "cannot borrow a constant which may contain \
            interior mutability, create a static instead"
        )
        .emit();
    }
}

#[derive(Debug)]
pub struct MutBorrow;
impl NonConstOp for MutBorrow {
    fn is_allowed_in_item(&self, ccx: &ConstCx<'_, '_>) -> bool {
        // Forbid everywhere except in const fn
        ccx.const_kind() == hir::ConstContext::ConstFn
            && ccx.tcx.features().enabled(Self::feature_gate().unwrap())
    }

    fn feature_gate() -> Option<Symbol> {
        Some(sym::const_mut_refs)
    }

    fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
        let mut err = if ccx.const_kind() == hir::ConstContext::ConstFn {
            feature_err(
                &ccx.tcx.sess.parse_sess,
                sym::const_mut_refs,
                span,
                &format!("mutable references are not allowed in {}s", ccx.const_kind()),
            )
        } else {
            struct_span_err!(
                ccx.tcx.sess,
                span,
                E0764,
                "mutable references are not allowed in {}s",
                ccx.const_kind(),
            )
        };
        err.span_label(span, "`&mut` is only allowed in `const fn`".to_string());
        if ccx.tcx.sess.teach(&err.get_code().unwrap()) {
            err.note(
                "References in statics and constants may only refer \
                      to immutable values.\n\n\
                      Statics are shared everywhere, and if they refer to \
                      mutable data one might violate memory safety since \
                      holding multiple mutable references to shared data \
                      is not allowed.\n\n\
                      If you really want global mutable state, try using \
                      static mut or a global UnsafeCell.",
            );
        }
        err.emit();
    }
}

#[derive(Debug)]
pub struct MutAddressOf;
impl NonConstOp for MutAddressOf {
    fn feature_gate() -> Option<Symbol> {
        Some(sym::const_mut_refs)
    }

    fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
        feature_err(
            &ccx.tcx.sess.parse_sess,
            sym::const_mut_refs,
            span,
            &format!("`&raw mut` is not allowed in {}s", ccx.const_kind()),
        )
        .emit();
    }
}

#[derive(Debug)]
pub struct MutDeref;
impl NonConstOp for MutDeref {
    fn feature_gate() -> Option<Symbol> {
        Some(sym::const_mut_refs)
    }
}

#[derive(Debug)]
pub struct Panic;
impl NonConstOp for Panic {
    fn feature_gate() -> Option<Symbol> {
        Some(sym::const_panic)
    }

    fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
        feature_err(
            &ccx.tcx.sess.parse_sess,
            sym::const_panic,
            span,
            &format!("panicking in {}s is unstable", ccx.const_kind()),
        )
        .emit();
    }
}

#[derive(Debug)]
pub struct RawPtrComparison;
impl NonConstOp for RawPtrComparison {
    fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
        let mut err = ccx
            .tcx
            .sess
            .struct_span_err(span, "pointers cannot be reliably compared during const eval.");
        err.note(
            "see issue #53020 <https://github.com/rust-lang/rust/issues/53020> \
            for more information",
        );
        err.emit();
    }
}

#[derive(Debug)]
pub struct RawPtrDeref;
impl NonConstOp for RawPtrDeref {
    fn feature_gate() -> Option<Symbol> {
        Some(sym::const_raw_ptr_deref)
    }

    fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
        feature_err(
            &ccx.tcx.sess.parse_sess,
            sym::const_raw_ptr_deref,
            span,
            &format!("dereferencing raw pointers in {}s is unstable", ccx.const_kind(),),
        )
        .emit();
    }
}

#[derive(Debug)]
pub struct RawPtrToIntCast;
impl NonConstOp for RawPtrToIntCast {
    fn feature_gate() -> Option<Symbol> {
        Some(sym::const_raw_ptr_to_usize_cast)
    }

    fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
        feature_err(
            &ccx.tcx.sess.parse_sess,
            sym::const_raw_ptr_to_usize_cast,
            span,
            &format!("casting pointers to integers in {}s is unstable", ccx.const_kind(),),
        )
        .emit();
    }
}

/// An access to a (non-thread-local) `static`.
#[derive(Debug)]
pub struct StaticAccess;
impl NonConstOp for StaticAccess {
    fn is_allowed_in_item(&self, ccx: &ConstCx<'_, '_>) -> bool {
        matches!(ccx.const_kind(), hir::ConstContext::Static(_))
    }

    fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
        let mut err = struct_span_err!(
            ccx.tcx.sess,
            span,
            E0013,
            "{}s cannot refer to statics",
            ccx.const_kind()
        );
        err.help(
            "consider extracting the value of the `static` to a `const`, and referring to that",
        );
        if ccx.tcx.sess.teach(&err.get_code().unwrap()) {
            err.note(
                "`static` and `const` variables can refer to other `const` variables. \
                    A `const` variable, however, cannot refer to a `static` variable.",
            );
            err.help("To fix this, the value can be extracted to a `const` and then used.");
        }
        err.emit();
    }
}

/// An access to a thread-local `static`.
#[derive(Debug)]
pub struct ThreadLocalAccess;
impl NonConstOp for ThreadLocalAccess {
    fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
        struct_span_err!(
            ccx.tcx.sess,
            span,
            E0625,
            "thread-local statics cannot be \
            accessed at compile-time"
        )
        .emit();
    }
}

#[derive(Debug)]
pub struct UnionAccess;
impl NonConstOp for UnionAccess {
    fn is_allowed_in_item(&self, ccx: &ConstCx<'_, '_>) -> bool {
        // Union accesses are stable in all contexts except `const fn`.
        ccx.const_kind() != hir::ConstContext::ConstFn
            || ccx.tcx.features().enabled(Self::feature_gate().unwrap())
    }

    fn feature_gate() -> Option<Symbol> {
        Some(sym::const_fn_union)
    }

    fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
        feature_err(
            &ccx.tcx.sess.parse_sess,
            sym::const_fn_union,
            span,
            "unions in const fn are unstable",
        )
        .emit();
    }
}
<commit_msg>Use enum for status of non-const ops<commit_after>//! Concrete error types for all operations which may be invalid in a certain const context.

use rustc_errors::struct_span_err;
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_session::config::nightly_options;
use rustc_session::parse::feature_err;
use rustc_span::symbol::sym;
use rustc_span::{Span, Symbol};

use super::ConstCx;

/// Emits an error if `op` is not allowed in the given const context.
pub fn non_const<O: NonConstOp>(ccx: &ConstCx<'_, '_>, op: O, span: Span) {
    debug!("illegal_op: op={:?}", op);

    let gate = match op.status_in_item(ccx) {
        Status::Allowed => return,
        Status::Unstable(gate) if ccx.tcx.features().enabled(gate) => return,
        Status::Unstable(gate) => Some(gate),
        Status::Forbidden => None,
    };

    if ccx.tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you {
        ccx.tcx.sess.miri_unleashed_feature(span, gate);
        return;
    }

    op.emit_error(ccx, span);
}

pub enum Status {
    Allowed,
    Unstable(Symbol),
    Forbidden,
}

/// An operation that is not *always* allowed in a const context.
pub trait NonConstOp: std::fmt::Debug {
    /// Returns an enum indicating whether this operation is allowed within the given item.
    fn status_in_item(&self, _ccx: &ConstCx<'_, '_>) -> Status {
        Status::Forbidden
    }

    fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
        let mut err = struct_span_err!(
            ccx.tcx.sess,
            span,
            E0019,
            "{} contains unimplemented expression type",
            ccx.const_kind()
        );

        if let Status::Unstable(gate) = self.status_in_item(ccx) {
            if !ccx.tcx.features().enabled(gate) && nightly_options::is_nightly_build() {
                err.help(&format!("add `#![feature({})]` to the crate attributes to enable", gate));
            }
        }

        if ccx.tcx.sess.teach(&err.get_code().unwrap()) {
            err.note(
                "A function call isn't allowed in the const's initialization expression \
                      because the expression's value must be known at compile-time.",
            );
            err.note(
                "Remember: you can't use a function call inside a const's initialization \
                      expression! However, you can use it anywhere else.",
            );
        }
        err.emit();
    }
}

/// A function call where the callee is a pointer.
#[derive(Debug)]
pub struct FnCallIndirect;
impl NonConstOp for FnCallIndirect {
    fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
        let mut err =
            ccx.tcx.sess.struct_span_err(span, "function pointers are not allowed in const fn");
        err.emit();
    }
}

/// A function call where the callee is not marked as `const`.
#[derive(Debug)]
pub struct FnCallNonConst(pub DefId);
impl NonConstOp for FnCallNonConst {
    fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
        let mut err = struct_span_err!(
            ccx.tcx.sess,
            span,
            E0015,
            "calls in {}s are limited to constant functions, \
             tuple structs and tuple variants",
            ccx.const_kind(),
        );
        err.emit();
    }
}

/// A call to a `#[unstable]` const fn or `#[rustc_const_unstable]` function.
///
/// Contains the name of the feature that would allow the use of this function.
#[derive(Debug)]
pub struct FnCallUnstable(pub DefId, pub Symbol);
impl NonConstOp for FnCallUnstable {
    fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
        let FnCallUnstable(def_id, feature) = *self;

        let mut err = ccx.tcx.sess.struct_span_err(
            span,
            &format!("`{}` is not yet stable as a const fn", ccx.tcx.def_path_str(def_id)),
        );
        if nightly_options::is_nightly_build() {
            err.help(&format!("add `#![feature({})]` to the crate attributes to enable", feature));
        }
        err.emit();
    }
}

#[derive(Debug)]
pub struct HeapAllocation;
impl NonConstOp for HeapAllocation {
    fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
        let mut err = struct_span_err!(
            ccx.tcx.sess,
            span,
            E0010,
            "allocations are not allowed in {}s",
            ccx.const_kind()
        );
        err.span_label(span, format!("allocation not allowed in {}s", ccx.const_kind()));
        if ccx.tcx.sess.teach(&err.get_code().unwrap()) {
            err.note(
                "The value of statics and constants must be known at compile time, \
                 and they live for the entire lifetime of a program. Creating a boxed \
                 value allocates memory on the heap at runtime, and therefore cannot \
                 be done at compile time.",
            );
        }
        err.emit();
    }
}

#[derive(Debug)]
pub struct InlineAsm;
impl NonConstOp for InlineAsm {}

#[derive(Debug)]
pub struct LiveDrop(pub Option<Span>);
impl NonConstOp for LiveDrop {
    fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
        let mut diagnostic = struct_span_err!(
            ccx.tcx.sess,
            span,
            E0493,
            "destructors cannot be evaluated at compile-time"
        );
        diagnostic.span_label(span, format!("{}s cannot evaluate destructors", ccx.const_kind()));
        if let Some(span) = self.0 {
            diagnostic.span_label(span, "value is dropped here");
        }
        diagnostic.emit();
    }
}

#[derive(Debug)]
pub struct CellBorrow;
impl NonConstOp for CellBorrow {
    fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
        struct_span_err!(
            ccx.tcx.sess,
            span,
            E0492,
            "cannot borrow a constant which may contain \
            interior mutability, create a static instead"
        )
        .emit();
    }
}

#[derive(Debug)]
pub struct MutBorrow;
impl NonConstOp for MutBorrow {
    fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
        // Forbid everywhere except in const fn with a feature gate
        if ccx.const_kind() == hir::ConstContext::ConstFn {
            Status::Unstable(sym::const_mut_refs)
        } else {
            Status::Forbidden
        }
    }

    fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
        let mut err = if ccx.const_kind() == hir::ConstContext::ConstFn {
            feature_err(
                &ccx.tcx.sess.parse_sess,
                sym::const_mut_refs,
                span,
                &format!("mutable references are not allowed in {}s", ccx.const_kind()),
            )
        } else {
            let mut err = struct_span_err!(
                ccx.tcx.sess,
                span,
                E0764,
                "mutable references are not allowed in {}s",
                ccx.const_kind(),
            );
            err.span_label(span, format!("`&mut` is only allowed in `const fn`"));
            err
        };
        if ccx.tcx.sess.teach(&err.get_code().unwrap()) {
            err.note(
                "References in statics and constants may only refer \
                      to immutable values.\n\n\
                      Statics are shared everywhere, and if they refer to \
                      mutable data one might violate memory safety since \
                      holding multiple mutable references to shared data \
                      is not allowed.\n\n\
                      If you really want global mutable state, try using \
                      static mut or a global UnsafeCell.",
            );
        }
        err.emit();
    }
}

// FIXME(ecstaticmorse): Unify this with `MutBorrow`. It has basically the same issues.
#[derive(Debug)]
pub struct MutAddressOf;
impl NonConstOp for MutAddressOf {
    fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
        // Forbid everywhere except in const fn with a feature gate
        if ccx.const_kind() == hir::ConstContext::ConstFn {
            Status::Unstable(sym::const_mut_refs)
        } else {
            Status::Forbidden
        }
    }

    fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
        feature_err(
            &ccx.tcx.sess.parse_sess,
            sym::const_mut_refs,
            span,
            &format!("`&raw mut` is not allowed in {}s", ccx.const_kind()),
        )
        .emit();
    }
}

#[derive(Debug)]
pub struct MutDeref;
impl NonConstOp for MutDeref {
    fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status {
        Status::Unstable(sym::const_mut_refs)
    }
}

#[derive(Debug)]
pub struct Panic;
impl NonConstOp for Panic {
    fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status {
        Status::Unstable(sym::const_panic)
    }

    fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
        feature_err(
            &ccx.tcx.sess.parse_sess,
            sym::const_panic,
            span,
            &format!("panicking in {}s is unstable", ccx.const_kind()),
        )
        .emit();
    }
}

#[derive(Debug)]
pub struct RawPtrComparison;
impl NonConstOp for RawPtrComparison {
    fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
        let mut err = ccx
            .tcx
            .sess
            .struct_span_err(span, "pointers cannot be reliably compared during const eval.");
        err.note(
            "see issue #53020 <https://github.com/rust-lang/rust/issues/53020> \
            for more information",
        );
        err.emit();
    }
}

#[derive(Debug)]
pub struct RawPtrDeref;
impl NonConstOp for RawPtrDeref {
    fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status {
        Status::Unstable(sym::const_raw_ptr_deref)
    }

    fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
        feature_err(
            &ccx.tcx.sess.parse_sess,
            sym::const_raw_ptr_deref,
            span,
            &format!("dereferencing raw pointers in {}s is unstable", ccx.const_kind(),),
        )
        .emit();
    }
}

#[derive(Debug)]
pub struct RawPtrToIntCast;
impl NonConstOp for RawPtrToIntCast {
    fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status {
        Status::Unstable(sym::const_raw_ptr_to_usize_cast)
    }

    fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
        feature_err(
            &ccx.tcx.sess.parse_sess,
            sym::const_raw_ptr_to_usize_cast,
            span,
            &format!("casting pointers to integers in {}s is unstable", ccx.const_kind(),),
        )
        .emit();
    }
}

/// An access to a (non-thread-local) `static`.
#[derive(Debug)]
pub struct StaticAccess;
impl NonConstOp for StaticAccess {
    fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
        if let hir::ConstContext::Static(_) = ccx.const_kind() {
            Status::Allowed
        } else {
            Status::Forbidden
        }
    }

    fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
        let mut err = struct_span_err!(
            ccx.tcx.sess,
            span,
            E0013,
            "{}s cannot refer to statics",
            ccx.const_kind()
        );
        err.help(
            "consider extracting the value of the `static` to a `const`, and referring to that",
        );
        if ccx.tcx.sess.teach(&err.get_code().unwrap()) {
            err.note(
                "`static` and `const` variables can refer to other `const` variables. \
                    A `const` variable, however, cannot refer to a `static` variable.",
            );
            err.help("To fix this, the value can be extracted to a `const` and then used.");
        }
        err.emit();
    }
}

/// An access to a thread-local `static`.
#[derive(Debug)]
pub struct ThreadLocalAccess;
impl NonConstOp for ThreadLocalAccess {
    fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
        struct_span_err!(
            ccx.tcx.sess,
            span,
            E0625,
            "thread-local statics cannot be \
            accessed at compile-time"
        )
        .emit();
    }
}

#[derive(Debug)]
pub struct UnionAccess;
impl NonConstOp for UnionAccess {
    fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
        // Union accesses are stable in all contexts except `const fn`.
        if ccx.const_kind() != hir::ConstContext::ConstFn {
            Status::Allowed
        } else {
            Status::Unstable(sym::const_fn_union)
        }
    }

    fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
        feature_err(
            &ccx.tcx.sess.parse_sess,
            sym::const_fn_union,
            span,
            "unions in const fn are unstable",
        )
        .emit();
    }
}
<|endoftext|>


<commit_before>use ide_db::syntax_helpers::node_ext::for_each_break_and_continue_expr;
use syntax::ast::{self, AstNode, HasLoopBody};
use syntax::T;

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

// Assist: add_lifetime_to_type
//
// Adds a new lifetime to a struct, enum or union.
//
// ```
// struct Point {
//     x: &u32,
//     y: u32,
// }
// ```
// ->
// ```
// struct Point<'a> {
//     x: &'a u32,
//     y: u32,
// }
// ```
pub(crate) fn add_label_to_loop(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
    let loop_expr = ctx.find_node_at_offset::<ast::LoopExpr>()?;
    let loop_body = loop_expr.loop_body().and_then(|it| it.stmt_list());
    let mut related_exprs = vec![];
    related_exprs.push(ast::Expr::LoopExpr(loop_expr.clone()));
    for_each_break_and_continue_expr(loop_expr.label(), loop_body, &mut |expr| {
        if let ast::Expr::BreakExpr(_) | ast::Expr::ContinueExpr(_) = expr {
            related_exprs.push(expr)
        }
    });
    dbg!(loop_expr.syntax().text_range());

    acc.add(
        AssistId("add_label_to_loop", AssistKind::Generate),
        "Add Label",
        loop_expr.syntax().text_range(),
        |builder| {
            for expr in related_exprs {
                match expr {
                    ast::Expr::BreakExpr(break_expr) => {
                        if let Some(break_token) = break_expr.break_token() {
                            builder.insert(break_token.text_range().end(), " 'loop")
                        }
                    },
                    ast::Expr::ContinueExpr(continue_expr) => {
                        if let Some(continue_token) = continue_expr.continue_token() {
                            builder.insert(continue_token.text_range().end(), " 'loop")
                        }
                    },
                    ast::Expr::LoopExpr(loop_expr) => {
                        if let Some(loop_token) = loop_expr.loop_token() {
                            builder.insert(loop_token.text_range().start(), "'loop: ")
                        }
                    },
                    _ => todo!()
                }
            }
        },
    )
}

#[cfg(test)]
mod tests {
    use crate::tests::check_assist;

    use super::*;

    #[test]
    fn add_label() {
        check_assist(
            add_label_to_loop,
            r#"
fn main() {
    loop$0 {
        break;
        continue;
    }
}"#,
            r#"
fn main() {
    'loop: loop {
        break 'loop;
        continue 'loop;
    }
}"#,
        );
    }

}
<commit_msg>add more tests and some doc<commit_after>use ide_db::syntax_helpers::node_ext::for_each_break_and_continue_expr;
use syntax::ast::{self, AstNode, HasLoopBody};

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

// Assist: add_label_to_loop
//
// Adds a label to a loop.
//
// ```
// loop$0 {
//     break;
//     continue;
// }
// ```
// ->
// ```
// 'loop: loop {
//     break 'loop;
//     continue 'loop;
// }
// ```
pub(crate) fn add_label_to_loop(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
    let loop_expr = ctx.find_node_at_offset::<ast::LoopExpr>()?;
    let loop_body = loop_expr.loop_body().and_then(|it| it.stmt_list());
    let mut related_exprs = vec![];
    related_exprs.push(ast::Expr::LoopExpr(loop_expr.clone()));
    for_each_break_and_continue_expr(loop_expr.label(), loop_body, &mut |expr| {
        if let ast::Expr::BreakExpr(_) | ast::Expr::ContinueExpr(_) = expr {
            related_exprs.push(expr)
        }
    });

    acc.add(
        AssistId("add_label_to_loop", AssistKind::Generate),
        "Add Label",
        loop_expr.syntax().text_range(),
        |builder| {
            for expr in related_exprs {
                match expr {
                    ast::Expr::BreakExpr(break_expr) => {
                        if let Some(break_token) = break_expr.break_token() {
                            builder.insert(break_token.text_range().end(), " 'loop")
                        }
                    }
                    ast::Expr::ContinueExpr(continue_expr) => {
                        if let Some(continue_token) = continue_expr.continue_token() {
                            builder.insert(continue_token.text_range().end(), " 'loop")
                        }
                    }
                    ast::Expr::LoopExpr(loop_expr) => {
                        if let Some(loop_token) = loop_expr.loop_token() {
                            builder.insert(loop_token.text_range().start(), "'loop: ")
                        }
                    }
                    _ => todo!(),
                }
            }
        },
    )
}

#[cfg(test)]
mod tests {
    use crate::tests::check_assist;

    use super::*;

    #[test]
    fn add_label() {
        check_assist(
            add_label_to_loop,
            r#"
fn main() {
    loop$0 {
        break;
        continue;
    }
}"#,
            r#"
fn main() {
    'loop: loop {
        break 'loop;
        continue 'loop;
    }
}"#,
        );
    }

    #[test]
    fn add_label_to_outer_loop() {
        check_assist(
            add_label_to_loop,
            r#"
fn main() {
    loop$0 {
        break;
        continue;
        loop {
            break;
            continue;
        }
    }
}"#,
            r#"
fn main() {
    'loop: loop {
        break 'loop;
        continue 'loop;
        loop {
            break;
            continue;
        }
    }
}"#,
        );
    }

    #[test]
    fn add_label_to_inner_loop() {
        check_assist(
            add_label_to_loop,
            r#"
fn main() {
    loop {
        break;
        continue;
        loop$0 {
            break;
            continue;
        }
    }
}"#,
            r#"
fn main() {
    loop {
        break;
        continue;
        'loop: loop {
            break 'loop;
            continue 'loop;
        }
    }
}"#,
        );
    }
}
<|endoftext|>


<commit_before>use hir::{AssocItem, MacroDef, ModuleDef, Name, PathResolution, ScopeDef, SemanticsScope};
use ra_ide_db::{
    defs::{classify_name_ref, Definition, NameRefClass},
    RootDatabase,
};
use ra_syntax::{algo, ast, match_ast, AstNode, SyntaxNode, SyntaxToken, T};

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

use either::Either;

// Assist: expand_glob_import
//
// Expands glob imports.
//
// ```
// mod foo {
//     pub struct Bar;
//     pub struct Baz;
// }
//
// use foo::*<|>;
//
// fn qux(bar: Bar, baz: Baz) {}
// ```
// ->
// ```
// mod foo {
//     pub struct Bar;
//     pub struct Baz;
// }
//
// use foo::{Baz, Bar};
//
// fn qux(bar: Bar, baz: Baz) {}
// ```
pub(crate) fn expand_glob_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
    let star = ctx.find_token_at_offset(T![*])?;
    let mod_path = find_mod_path(&star)?;

    let source_file = ctx.source_file();
    let scope = ctx.sema.scope_at_offset(source_file.syntax(), ctx.offset());

    let defs_in_mod = find_defs_in_mod(ctx, scope, &mod_path)?;
    let name_refs_in_source_file =
        source_file.syntax().descendants().filter_map(ast::NameRef::cast).collect();
    let used_names = find_used_names(ctx, defs_in_mod, name_refs_in_source_file);

    let parent = star.parent().parent()?;
    acc.add(
        AssistId("expand_glob_import", AssistKind::RefactorRewrite),
        "Expand glob import",
        parent.text_range(),
        |builder| {
            replace_ast(builder, &parent, mod_path, used_names);
        },
    )
}

fn find_mod_path(star: &SyntaxToken) -> Option<ast::Path> {
    star.ancestors().find_map(|n| ast::UseTree::cast(n).and_then(|u| u.path()))
}

#[derive(PartialEq)]
enum Def {
    ModuleDef(ModuleDef),
    MacroDef(MacroDef),
}

impl Def {
    fn name(&self, db: &RootDatabase) -> Option<Name> {
        match self {
            Def::ModuleDef(def) => def.name(db),
            Def::MacroDef(def) => def.name(db),
        }
    }
}

fn find_defs_in_mod(
    ctx: &AssistContext,
    from: SemanticsScope<'_>,
    path: &ast::Path,
) -> Option<Vec<Def>> {
    let hir_path = ctx.sema.lower_path(&path)?;
    let module = if let Some(PathResolution::Def(ModuleDef::Module(module))) =
        from.resolve_hir_path_qualifier(&hir_path)
    {
        module
    } else {
        return None;
    };

    let module_scope = module.scope(ctx.db(), from.module());

    let mut defs = vec![];
    for (_, def) in module_scope {
        match def {
            ScopeDef::ModuleDef(def) => defs.push(Def::ModuleDef(def)),
            ScopeDef::MacroDef(def) => defs.push(Def::MacroDef(def)),
            _ => continue,
        }
    }

    Some(defs)
}

fn find_used_names(
    ctx: &AssistContext,
    defs_in_mod: Vec<Def>,
    name_refs_in_source_file: Vec<ast::NameRef>,
) -> Vec<Name> {
    let defs_in_source_file = name_refs_in_source_file
        .iter()
        .filter_map(|r| classify_name_ref(&ctx.sema, r))
        .filter_map(|rc| match rc {
            NameRefClass::Definition(Definition::ModuleDef(def)) => Some(Def::ModuleDef(def)),
            NameRefClass::Definition(Definition::Macro(def)) => Some(Def::MacroDef(def)),
            _ => None,
        })
        .collect::<Vec<Def>>();

    defs_in_mod
        .iter()
        .filter(|def| {
            if let Def::ModuleDef(ModuleDef::Trait(tr)) = def {
                for item in tr.items(ctx.db()) {
                    if let AssocItem::Function(f) = item {
                        if defs_in_source_file.contains(&Def::ModuleDef(ModuleDef::Function(f))) {
                            return true;
                        }
                    }
                }
            }

            defs_in_source_file.contains(def)
        })
        .filter_map(|d| d.name(ctx.db()))
        .collect()
}

fn replace_ast(
    builder: &mut AssistBuilder,
    node: &SyntaxNode,
    path: ast::Path,
    used_names: Vec<Name>,
) {
    let replacement: Either<ast::UseTree, ast::UseTreeList> = if used_names.len() == 1 {
        Either::Left(ast::make::use_tree(
            ast::make::path_from_text(&format!("{}::{}", path, used_names.first().unwrap())),
            None,
            None,
            false,
        ))
    } else {
        Either::Right(ast::make::use_tree_list(used_names.iter().map(|n| {
            ast::make::use_tree(ast::make::path_from_text(&n.to_string()), None, None, false)
        })))
    };

    let mut replace_node = |replacement: Either<ast::UseTree, ast::UseTreeList>| {
        algo::diff(node, &replacement.either(|u| u.syntax().clone(), |ut| ut.syntax().clone()))
            .into_text_edit(builder.text_edit_builder());
    };

    match_ast! {
        match node {
            ast::UseTree(use_tree) => {
                replace_node(replacement);
            },
            ast::UseTreeList(use_tree_list) => {
                replace_node(replacement);
            },
            ast::Use(use_item) => {
                builder.replace_ast(use_item, ast::make::use_item(replacement.left_or_else(|ut| ast::make::use_tree(path, Some(ut), None, false))));
            },
            _ => {},
        }
    }
}

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

    use super::*;

    #[test]
    fn expanding_glob_import() {
        check_assist(
            expand_glob_import,
            r"
mod foo {
    pub struct Bar;
    pub struct Baz;
    pub struct Qux;

    pub fn f() {}
}

use foo::*<|>;

fn qux(bar: Bar, baz: Baz) {
    f();
}
",
            r"
mod foo {
    pub struct Bar;
    pub struct Baz;
    pub struct Qux;

    pub fn f() {}
}

use foo::{Baz, Bar, f};

fn qux(bar: Bar, baz: Baz) {
    f();
}
",
        )
    }

    #[test]
    fn expanding_glob_import_with_existing_explicit_names() {
        check_assist(
            expand_glob_import,
            r"
mod foo {
    pub struct Bar;
    pub struct Baz;
    pub struct Qux;

    pub fn f() {}
}

use foo::{*<|>, f};

fn qux(bar: Bar, baz: Baz) {
    f();
}
",
            r"
mod foo {
    pub struct Bar;
    pub struct Baz;
    pub struct Qux;

    pub fn f() {}
}

use foo::{Baz, Bar, f};

fn qux(bar: Bar, baz: Baz) {
    f();
}
",
        )
    }

    #[test]
    fn expanding_nested_glob_import() {
        check_assist(
            expand_glob_import,
            r"
mod foo {
    mod bar {
        pub struct Bar;
        pub struct Baz;
        pub struct Qux;

        pub fn f() {}
    }

    mod baz {
        pub fn g() {}
    }
}

use foo::{bar::{*<|>, f}, baz::*};

fn qux(bar: Bar, baz: Baz) {
    f();
    g();
}
",
            r"
mod foo {
    mod bar {
        pub struct Bar;
        pub struct Baz;
        pub struct Qux;

        pub fn f() {}
    }

    mod baz {
        pub fn g() {}
    }
}

use foo::{bar::{Baz, Bar, f}, baz::*};

fn qux(bar: Bar, baz: Baz) {
    f();
    g();
}
",
        )
    }

    #[test]
    fn expanding_glob_import_with_macro_defs() {
        check_assist(
            expand_glob_import,
            r"
//- /lib.rs crate:foo
#[macro_export]
macro_rules! bar {
    () => ()
}

pub fn baz() {}

//- /main.rs crate:main deps:foo
use foo::*<|>;

fn main() {
    bar!();
    baz();
}
",
            r"
use foo::{bar, baz};

fn main() {
    bar!();
    baz();
}
",
        )
    }

    #[test]
    fn expanding_glob_import_with_trait_method_uses() {
        check_assist(
            expand_glob_import,
            r"
//- /lib.rs crate:foo
pub trait Tr {
    fn method(&self) {}
}
impl Tr for () {}

//- /main.rs crate:main deps:foo
use foo::*<|>;

fn main() {
    ().method();
}
",
            r"
use foo::Tr;

fn main() {
    ().method();
}
",
        )
    }

    #[test]
    fn expanding_is_not_applicable_if_cursor_is_not_in_star_token() {
        check_assist_not_applicable(
            expand_glob_import,
            r"
    mod foo {
        pub struct Bar;
        pub struct Baz;
        pub struct Qux;
    }

    use foo::Bar<|>;

    fn qux(bar: Bar, baz: Baz) {}
    ",
        )
    }
}
<commit_msg>Pattern match on slice elements instead of using `.first().unwrap()`<commit_after>use hir::{AssocItem, MacroDef, ModuleDef, Name, PathResolution, ScopeDef, SemanticsScope};
use ra_ide_db::{
    defs::{classify_name_ref, Definition, NameRefClass},
    RootDatabase,
};
use ra_syntax::{algo, ast, match_ast, AstNode, SyntaxNode, SyntaxToken, T};

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

use either::Either;

// Assist: expand_glob_import
//
// Expands glob imports.
//
// ```
// mod foo {
//     pub struct Bar;
//     pub struct Baz;
// }
//
// use foo::*<|>;
//
// fn qux(bar: Bar, baz: Baz) {}
// ```
// ->
// ```
// mod foo {
//     pub struct Bar;
//     pub struct Baz;
// }
//
// use foo::{Baz, Bar};
//
// fn qux(bar: Bar, baz: Baz) {}
// ```
pub(crate) fn expand_glob_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
    let star = ctx.find_token_at_offset(T![*])?;
    let mod_path = find_mod_path(&star)?;

    let source_file = ctx.source_file();
    let scope = ctx.sema.scope_at_offset(source_file.syntax(), ctx.offset());

    let defs_in_mod = find_defs_in_mod(ctx, scope, &mod_path)?;
    let name_refs_in_source_file =
        source_file.syntax().descendants().filter_map(ast::NameRef::cast).collect();
    let used_names = find_used_names(ctx, defs_in_mod, name_refs_in_source_file);

    let parent = star.parent().parent()?;
    acc.add(
        AssistId("expand_glob_import", AssistKind::RefactorRewrite),
        "Expand glob import",
        parent.text_range(),
        |builder| {
            replace_ast(builder, &parent, mod_path, used_names);
        },
    )
}

fn find_mod_path(star: &SyntaxToken) -> Option<ast::Path> {
    star.ancestors().find_map(|n| ast::UseTree::cast(n).and_then(|u| u.path()))
}

#[derive(PartialEq)]
enum Def {
    ModuleDef(ModuleDef),
    MacroDef(MacroDef),
}

impl Def {
    fn name(&self, db: &RootDatabase) -> Option<Name> {
        match self {
            Def::ModuleDef(def) => def.name(db),
            Def::MacroDef(def) => def.name(db),
        }
    }
}

fn find_defs_in_mod(
    ctx: &AssistContext,
    from: SemanticsScope<'_>,
    path: &ast::Path,
) -> Option<Vec<Def>> {
    let hir_path = ctx.sema.lower_path(&path)?;
    let module = if let Some(PathResolution::Def(ModuleDef::Module(module))) =
        from.resolve_hir_path_qualifier(&hir_path)
    {
        module
    } else {
        return None;
    };

    let module_scope = module.scope(ctx.db(), from.module());

    let mut defs = vec![];
    for (_, def) in module_scope {
        match def {
            ScopeDef::ModuleDef(def) => defs.push(Def::ModuleDef(def)),
            ScopeDef::MacroDef(def) => defs.push(Def::MacroDef(def)),
            _ => continue,
        }
    }

    Some(defs)
}

fn find_used_names(
    ctx: &AssistContext,
    defs_in_mod: Vec<Def>,
    name_refs_in_source_file: Vec<ast::NameRef>,
) -> Vec<Name> {
    let defs_in_source_file = name_refs_in_source_file
        .iter()
        .filter_map(|r| classify_name_ref(&ctx.sema, r))
        .filter_map(|rc| match rc {
            NameRefClass::Definition(Definition::ModuleDef(def)) => Some(Def::ModuleDef(def)),
            NameRefClass::Definition(Definition::Macro(def)) => Some(Def::MacroDef(def)),
            _ => None,
        })
        .collect::<Vec<Def>>();

    defs_in_mod
        .iter()
        .filter(|def| {
            if let Def::ModuleDef(ModuleDef::Trait(tr)) = def {
                for item in tr.items(ctx.db()) {
                    if let AssocItem::Function(f) = item {
                        if defs_in_source_file.contains(&Def::ModuleDef(ModuleDef::Function(f))) {
                            return true;
                        }
                    }
                }
            }

            defs_in_source_file.contains(def)
        })
        .filter_map(|d| d.name(ctx.db()))
        .collect()
}

fn replace_ast(
    builder: &mut AssistBuilder,
    node: &SyntaxNode,
    path: ast::Path,
    used_names: Vec<Name>,
) {
    let replacement: Either<ast::UseTree, ast::UseTreeList> = match used_names.as_slice() {
        [name] => Either::Left(ast::make::use_tree(
            ast::make::path_from_text(&format!("{}::{}", path, name)),
            None,
            None,
            false,
        )),
        names => Either::Right(ast::make::use_tree_list(names.iter().map(|n| {
            ast::make::use_tree(ast::make::path_from_text(&n.to_string()), None, None, false)
        }))),
    };

    let mut replace_node = |replacement: Either<ast::UseTree, ast::UseTreeList>| {
        algo::diff(node, &replacement.either(|u| u.syntax().clone(), |ut| ut.syntax().clone()))
            .into_text_edit(builder.text_edit_builder());
    };

    match_ast! {
        match node {
            ast::UseTree(use_tree) => {
                replace_node(replacement);
            },
            ast::UseTreeList(use_tree_list) => {
                replace_node(replacement);
            },
            ast::Use(use_item) => {
                builder.replace_ast(use_item, ast::make::use_item(replacement.left_or_else(|ut| ast::make::use_tree(path, Some(ut), None, false))));
            },
            _ => {},
        }
    }
}

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

    use super::*;

    #[test]
    fn expanding_glob_import() {
        check_assist(
            expand_glob_import,
            r"
mod foo {
    pub struct Bar;
    pub struct Baz;
    pub struct Qux;

    pub fn f() {}
}

use foo::*<|>;

fn qux(bar: Bar, baz: Baz) {
    f();
}
",
            r"
mod foo {
    pub struct Bar;
    pub struct Baz;
    pub struct Qux;

    pub fn f() {}
}

use foo::{Baz, Bar, f};

fn qux(bar: Bar, baz: Baz) {
    f();
}
",
        )
    }

    #[test]
    fn expanding_glob_import_with_existing_explicit_names() {
        check_assist(
            expand_glob_import,
            r"
mod foo {
    pub struct Bar;
    pub struct Baz;
    pub struct Qux;

    pub fn f() {}
}

use foo::{*<|>, f};

fn qux(bar: Bar, baz: Baz) {
    f();
}
",
            r"
mod foo {
    pub struct Bar;
    pub struct Baz;
    pub struct Qux;

    pub fn f() {}
}

use foo::{Baz, Bar, f};

fn qux(bar: Bar, baz: Baz) {
    f();
}
",
        )
    }

    #[test]
    fn expanding_nested_glob_import() {
        check_assist(
            expand_glob_import,
            r"
mod foo {
    mod bar {
        pub struct Bar;
        pub struct Baz;
        pub struct Qux;

        pub fn f() {}
    }

    mod baz {
        pub fn g() {}
    }
}

use foo::{bar::{*<|>, f}, baz::*};

fn qux(bar: Bar, baz: Baz) {
    f();
    g();
}
",
            r"
mod foo {
    mod bar {
        pub struct Bar;
        pub struct Baz;
        pub struct Qux;

        pub fn f() {}
    }

    mod baz {
        pub fn g() {}
    }
}

use foo::{bar::{Baz, Bar, f}, baz::*};

fn qux(bar: Bar, baz: Baz) {
    f();
    g();
}
",
        )
    }

    #[test]
    fn expanding_glob_import_with_macro_defs() {
        check_assist(
            expand_glob_import,
            r"
//- /lib.rs crate:foo
#[macro_export]
macro_rules! bar {
    () => ()
}

pub fn baz() {}

//- /main.rs crate:main deps:foo
use foo::*<|>;

fn main() {
    bar!();
    baz();
}
",
            r"
use foo::{bar, baz};

fn main() {
    bar!();
    baz();
}
",
        )
    }

    #[test]
    fn expanding_glob_import_with_trait_method_uses() {
        check_assist(
            expand_glob_import,
            r"
//- /lib.rs crate:foo
pub trait Tr {
    fn method(&self) {}
}
impl Tr for () {}

//- /main.rs crate:main deps:foo
use foo::*<|>;

fn main() {
    ().method();
}
",
            r"
use foo::Tr;

fn main() {
    ().method();
}
",
        )
    }

    #[test]
    fn expanding_is_not_applicable_if_cursor_is_not_in_star_token() {
        check_assist_not_applicable(
            expand_glob_import,
            r"
    mod foo {
        pub struct Bar;
        pub struct Baz;
        pub struct Qux;
    }

    use foo::Bar<|>;

    fn qux(bar: Bar, baz: Baz) {}
    ",
        )
    }
}
<|endoftext|>


<commit_before>//  notty is a new kind of terminal emulator.
//  Copyright (C) 2015 without boats
//
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU Affero General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU Affero General Public License for more details.
//
//  You should have received a copy of the GNU Affero General Public License
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
//! The datatypes module defines the abstract datatypes used by other components of notty.
//!
//! The types in this module are intended to be passed between modules. As a design restriction,
//! any methods on any type in this submodule are required to take the receiver immutably.
use std::cmp;

use mime::Mime;

use cfg;

mod iter;
mod key;

pub use self::iter::CoordsIter;
pub use self::key::Key;

pub use notty_encoding::args::*;

pub mod args {
    pub use super::{
        Area,
        BufferSettings,
        Coords,
        Color,
        Direction,
        EchoSettings,
        InputSettings,
        MediaAlignment,
        MediaPosition,
        Movement,
        Region,
        Style,
    };
    pub use super::Area::*;
    pub use super::Direction::*;
    pub use super::InputSettings::*;
    pub use super::MediaAlignment::*;
    pub use super::MediaPosition::*;
    pub use super::Movement::*;
    pub use super::Style::*;
    pub use notty_encoding::args::Argument;
}

/// Data that could be placed in a character cell.
#[derive(Clone)]
pub enum CellData {
    /// A single unicode code point.
    Char(char),
    /// An extension code point such as U+301. Normally, writing this to the screen does not
    /// overwrite a cell, but instead applies it to the char in the cell.
    ExtensionChar(char),
    /// Non-character media data, with a mime type, positioning and size info.
    Image {
        pos: MediaPosition,
        width: u32,
        height: u32,
        data: Vec<u8>,
        mime: Mime,
    }
}

/// A kind of escape code format (used for structuring response strings).
#[derive(Copy, Clone, Eq, PartialEq)]
pub enum Code {
    ANSI,
    Notty,
}

/// Calculate the movement from one coordinate to another within a region.
pub fn move_within(Coords {x, y}: Coords, movement: Movement, region: Region) -> Coords {
    use self::Movement::*;
    use self::Direction::*;
    match movement {
        Position(coords)    => region.xy_within(coords),
        Column(n)           => Coords {x: region.x_within(n), y: y},
        Row(n)              => Coords {x: x, y: region.y_within(n)},
        ToEdge(Up)          => Coords {x: x, y: region.top},
        ToEdge(Down)        => Coords {x: x, y: region.bottom - 1},
        ToEdge(Left)        => Coords {x: region.left, y: y},
        ToEdge(Right)       => Coords {x: region.right - 1, y: y},
        ToBeginning         => Coords {x: region.left, y: region.top},
        ToEnd               => Coords {x: region.right - 1, y: region.bottom - 1},
        To(Up, n, true) if region.top + n > y       => {
            let x = x.saturating_sub((region.top + n - y) / (region.bottom - region.top) + 1);
            let y = region.bottom - (region.top + n - y) % (region.bottom - region.top);
            if x < region.left {
                Coords { x: region.left, y: region.top }
            } else {
                Coords { x: x, y: y }
            }
        }
        To(Down, n, true) if y + n >= region.bottom  => {
            let x = x + (y + n - region.bottom) / (region.bottom - region.top) + 1;
            let y = region.top + (y + n - region.bottom) % (region.bottom - region.top);
            if x >= region.right {
                Coords { x: region.right - 1, y: region.bottom - 1 }
            } else {
                Coords { x: x, y: y }
            }
        }
        To(Left, n, true) if region.left + n > x    => {
            let y = y.saturating_sub((region.left + n - x) / (region.right - region.left) + 1);
            let x = region.right - (region.left + n - x) % (region.right - region.left);
            if y < region.top {
                Coords { x: region.left, y: region.top }
            } else {
                Coords { x: x, y: y }
            }
        }
        To(Right, n, true) if x + n >= region.right  => {
            let y = y + (x + n - region.right) / (region.right - region.left) + 1;
            let x = region.left + (x + n - region.right) % (region.right - region.left);
            if y >= region.bottom {
                Coords { x: region.right - 1, y: region.bottom - 1 }
            } else {
                Coords { x: x, y: y }
            }
        }
        To(Up, n, _) | IndexTo(Up, n)         => {
            Coords {x: x, y: cmp::max(region.top, y.saturating_sub(n))}
        }
        To(Down, n, _) | IndexTo(Down, n)     => {
            Coords {x: x, y: cmp::min(y.saturating_add(n), region.bottom - 1)}
        }
        To(Left, n, _) | IndexTo(Left, n)     => {
            Coords {x: cmp::max(region.left, x.saturating_sub(n)), y: y}
        }
        To(Right, n, _) | IndexTo(Right, n)   => {
            Coords {x: cmp::min(x.saturating_add(n), region.right - 1), y: y}
        }
        Tab(Left, n, true) if region.left + n > x => {
            unimplemented!()
        }
        Tab(Right, n, true) if x + n >= region.right => {
            unimplemented!()
        }
        Tab(Left, n, false)                 => {
            let tab = ((x / cfg::TAB_STOP).saturating_sub(n)) * cfg::TAB_STOP;
            Coords {x: cmp::max(tab, region.left), y: y}
        }
        Tab(Right, n, false)                => {
            let tab = ((x / cfg::TAB_STOP) + n) * cfg::TAB_STOP;
            Coords {x: cmp::min(tab, region.right - 1), y: y}
        }
        Tab(..)                             => unimplemented!(),
        PreviousLine(n)                     => {
            Coords {x: 0, y: cmp::max(y.saturating_sub(n), region.top)}
        }
        NextLine(n)                         => {
            Coords {x: 0, y: cmp::min(y.saturating_add(n), region.bottom - 1)}
        }
    }
}
<commit_msg>Fix tab right case.<commit_after>//  notty is a new kind of terminal emulator.
//  Copyright (C) 2015 without boats
//
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU Affero General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU Affero General Public License for more details.
//
//  You should have received a copy of the GNU Affero General Public License
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
//! The datatypes module defines the abstract datatypes used by other components of notty.
//!
//! The types in this module are intended to be passed between modules. As a design restriction,
//! any methods on any type in this submodule are required to take the receiver immutably.
use std::cmp;

use mime::Mime;

use cfg;

mod iter;
mod key;

pub use self::iter::CoordsIter;
pub use self::key::Key;

pub use notty_encoding::args::*;

pub mod args {
    pub use super::{
        Area,
        BufferSettings,
        Coords,
        Color,
        Direction,
        EchoSettings,
        InputSettings,
        MediaAlignment,
        MediaPosition,
        Movement,
        Region,
        Style,
    };
    pub use super::Area::*;
    pub use super::Direction::*;
    pub use super::InputSettings::*;
    pub use super::MediaAlignment::*;
    pub use super::MediaPosition::*;
    pub use super::Movement::*;
    pub use super::Style::*;
    pub use notty_encoding::args::Argument;
}

/// Data that could be placed in a character cell.
#[derive(Clone)]
pub enum CellData {
    /// A single unicode code point.
    Char(char),
    /// An extension code point such as U+301. Normally, writing this to the screen does not
    /// overwrite a cell, but instead applies it to the char in the cell.
    ExtensionChar(char),
    /// Non-character media data, with a mime type, positioning and size info.
    Image {
        pos: MediaPosition,
        width: u32,
        height: u32,
        data: Vec<u8>,
        mime: Mime,
    }
}

/// A kind of escape code format (used for structuring response strings).
#[derive(Copy, Clone, Eq, PartialEq)]
pub enum Code {
    ANSI,
    Notty,
}

/// Calculate the movement from one coordinate to another within a region.
pub fn move_within(Coords {x, y}: Coords, movement: Movement, region: Region) -> Coords {
    use self::Movement::*;
    use self::Direction::*;
    match movement {
        Position(coords)    => region.xy_within(coords),
        Column(n)           => Coords {x: region.x_within(n), y: y},
        Row(n)              => Coords {x: x, y: region.y_within(n)},
        ToEdge(Up)          => Coords {x: x, y: region.top},
        ToEdge(Down)        => Coords {x: x, y: region.bottom - 1},
        ToEdge(Left)        => Coords {x: region.left, y: y},
        ToEdge(Right)       => Coords {x: region.right - 1, y: y},
        ToBeginning         => Coords {x: region.left, y: region.top},
        ToEnd               => Coords {x: region.right - 1, y: region.bottom - 1},
        To(Up, n, true) if region.top + n > y       => {
            let x = x.saturating_sub((region.top + n - y) / (region.bottom - region.top) + 1);
            let y = region.bottom - (region.top + n - y) % (region.bottom - region.top);
            if x < region.left {
                Coords { x: region.left, y: region.top }
            } else {
                Coords { x: x, y: y }
            }
        }
        To(Down, n, true) if y + n >= region.bottom  => {
            let x = x + (y + n - region.bottom) / (region.bottom - region.top) + 1;
            let y = region.top + (y + n - region.bottom) % (region.bottom - region.top);
            if x >= region.right {
                Coords { x: region.right - 1, y: region.bottom - 1 }
            } else {
                Coords { x: x, y: y }
            }
        }
        To(Left, n, true) if region.left + n > x    => {
            let y = y.saturating_sub((region.left + n - x) / (region.right - region.left) + 1);
            let x = region.right - (region.left + n - x) % (region.right - region.left);
            if y < region.top {
                Coords { x: region.left, y: region.top }
            } else {
                Coords { x: x, y: y }
            }
        }
        To(Right, n, true) if x + n >= region.right  => {
            let y = y + (x + n - region.right) / (region.right - region.left) + 1;
            let x = region.left + (x + n - region.right) % (region.right - region.left);
            if y >= region.bottom {
                Coords { x: region.right - 1, y: region.bottom - 1 }
            } else {
                Coords { x: x, y: y }
            }
        }
        To(Up, n, _) | IndexTo(Up, n)         => {
            Coords {x: x, y: cmp::max(region.top, y.saturating_sub(n))}
        }
        To(Down, n, _) | IndexTo(Down, n)     => {
            Coords {x: x, y: cmp::min(y.saturating_add(n), region.bottom - 1)}
        }
        To(Left, n, _) | IndexTo(Left, n)     => {
            Coords {x: cmp::max(region.left, x.saturating_sub(n)), y: y}
        }
        To(Right, n, _) | IndexTo(Right, n)   => {
            Coords {x: cmp::min(x.saturating_add(n), region.right - 1), y: y}
        }
        Tab(Left, n, true) if region.left + n > x => {
            unimplemented!()
        }
        Tab(Right, n, true) if x + n >= region.right => {
            unimplemented!()
        }
        Tab(Left, n, false)                 => {
            let tab = ((x / cfg::TAB_STOP).saturating_sub(n)) * cfg::TAB_STOP;
            Coords {x: cmp::max(tab, region.left), y: y}
        }
        Tab(Right, n, _)                => {
            let tab = ((x / cfg::TAB_STOP) + n) * cfg::TAB_STOP;
            Coords {x: cmp::min(tab, region.right - 1), y: y}
        }
        Tab(..)                             => unimplemented!(),
        PreviousLine(n)                     => {
            Coords {x: 0, y: cmp::max(y.saturating_sub(n), region.top)}
        }
        NextLine(n)                         => {
            Coords {x: 0, y: cmp::min(y.saturating_add(n), region.bottom - 1)}
        }
    }
}
<|endoftext|>


<commit_before>use time;
use std::io::timer::sleep;

use GameWindow;
use keyboard;
use mouse;
use event;

/// Render argument.
#[deriving(Clone)]
pub struct RenderArgs {
    /// Extrapolated time in seconds, used to do smooth animation.
    pub ext_dt: f64,
    /// The width of rendered area.
    pub width: u32,
    /// The height of rendered area.
    pub height: u32,
}

/// Update argument.
#[deriving(Clone)]
pub struct UpdateArgs {
    /// Delta time in seconds.
    pub dt: f64,
}

/// Key press arguments.
#[deriving(Clone)]
pub struct KeyPressArgs {
    /// Keyboard key.
    pub key: keyboard::Key,
}

/// Key release arguments.
#[deriving(Clone)]
pub struct KeyReleaseArgs {
    /// Keyboard key.
    pub key: keyboard::Key,
}

/// Mouse press arguments.
#[deriving(Clone)]
pub struct MousePressArgs {
    /// Mouse button.
    pub button: mouse::Button,
}

/// Mouse release arguments.
#[deriving(Clone)]
pub struct MouseReleaseArgs {
    /// Mouse button.
    pub button: mouse::Button,
}

/// Mouse move arguments.
#[deriving(Clone)]
pub struct MouseMoveArgs {
    /// x.
    pub x: f64,
    /// y.
    pub y: f64,
}

/// Mouse relative move arguments.
#[deriving(Clone)]
pub struct MouseRelativeMoveArgs {
    /// Delta x.
    pub dx: f64,
    /// Delta y.
    pub dy: f64,
}

/// Mouse scroll arguments.
#[deriving(Clone)]
pub struct MouseScrollArgs {
    /// x.
    pub x: f64,
    /// y.
    pub y: f64,
}

/// Contains the different game events.
#[deriving(Clone)]
pub enum GameEvent {
    /// Render graphics.
    Render(RenderArgs),
    /// Update physical state of the game.
    Update(UpdateArgs),
    /// Pressed a keyboard key.
    KeyPress(KeyPressArgs),
    /// Released a keyboard key.
    KeyRelease(KeyReleaseArgs),
    /// Pressed a mouse button.
    MousePress(MousePressArgs),
    /// Released a mouse button.
    MouseRelease(MouseReleaseArgs),
    /// Moved mouse cursor.
    MouseMove(MouseMoveArgs),
    /// Moved mouse relative, not bounded by cursor.
    MouseRelativeMove(MouseRelativeMoveArgs),
    /// Scrolled mouse.
    MouseScroll(MouseScrollArgs)
}

enum GameIteratorState {
    RenderState,
    SwapBuffersState,
    PrepareUpdateLoopState,
    UpdateLoopState,
    HandleEventsState,
    MouseRelativeMoveState(f64, f64),
    UpdateState,
}

/// Settings for the game loop behavior.
#[deriving(Clone)]
pub struct GameIteratorSettings {
    /// The number of updates per second (UPS).
    pub updates_per_second: u64,
    /// The maximum number of frames per second (FPS target).
    pub max_frames_per_second: u64,
}

/// A game loop iterator.
///
/// Example:
///
/// ```Rust
/// let game_iter_settings = GameIteratorSettings {
///     updates_per_second: 120,
///     max_frames_per_second: 60,
/// };
/// let mut game_iter = GameIterator::new(&mut window, &game_iter_settings);
/// loop {
///     match game_iter.next() {
///         None => break,
///         Some(mut e) => match e {
///             Render(ref mut args) => {
///                 // Create graphics context with absolute coordinates.
///                 let c = Context::abs(args.width as f64, args.height as f64);
///                 // Do rendering here.
///             },
///             _ => {},
///         },
///     }
/// }
/// ```
pub struct GameIterator<'a, W> {
    /// The game window used by iterator.
    pub game_window: &'a mut W,
    state: GameIteratorState,
    last_update: u64,
    update_time_in_ns: u64,
    dt: f64,
    min_updates_per_frame: u64,
    min_ns_per_frame: u64,
    start_render: u64,
    next_render: u64,
    updated: u64,
}

static billion: u64 = 1_000_000_000;

impl<'a, W: GameWindow> GameIterator<'a, W> {
    /// Creates a new game iterator.
    pub fn new(
        game_window: &'a mut W,
        settings: &GameIteratorSettings
    ) -> GameIterator<'a, W> {
        let updates_per_second: u64 = settings.updates_per_second;
        let max_frames_per_second: u64 = settings.max_frames_per_second;

        let start = time::precise_time_ns();
        GameIterator {
            game_window: game_window,
            state: RenderState,
            last_update: start,
            update_time_in_ns: billion / updates_per_second,
            dt: 1.0 / updates_per_second as f64,
            // You can make this lower if needed.
            min_updates_per_frame: updates_per_second / max_frames_per_second,
            min_ns_per_frame: billion / max_frames_per_second,
            start_render: start,
            next_render: start,
            updated: 0,
        }
    }
}

impl<'a, W: GameWindow>
Iterator<GameEvent>
for GameIterator<'a, W> {
    /// Returns the next game event.
    fn next(&mut self) -> Option<GameEvent> {
        match self.state {
            RenderState => {
                if self.game_window.should_close() { return None; }

                self.start_render = time::precise_time_ns();
                // Rendering code
                let (w, h) = self.game_window.get_size();
                if w != 0 && h != 0 {
                    // Swap buffers next time.
                    self.state = SwapBuffersState;
                    return Some(Render(RenderArgs {
                            // Extrapolate time forward to allow smooth motion.
                            // 'start_render' is always bigger than 'last_update'.
                            ext_dt: (self.start_render - self.last_update) as f64 / billion as f64,
                            width: w,
                            height: h,
                        }
                    ));
                }

                self.state = PrepareUpdateLoopState;
                return self.next();
            },
            SwapBuffersState => {
                self.game_window.swap_buffers();
                self.state = PrepareUpdateLoopState;
                return self.next();
            },
            PrepareUpdateLoopState => {
                self.updated = 0;
                self.next_render = self.start_render + self.min_ns_per_frame;
                self.state = UpdateLoopState;
                return self.next();
            },
            UpdateLoopState => {
                let got_min_updates = self.updated < self.min_updates_per_frame;
                let got_time_to_update = time::precise_time_ns() < self.next_render;
                let before_next_frame = self.last_update + self.update_time_in_ns < self.next_render;

                if ( got_time_to_update || got_min_updates ) && before_next_frame {
                    self.state = HandleEventsState;
                    return self.next();
                }

                // Wait if possible.
                // Convert to ms because that is what the sleep function takes.
                let t = (self.next_render - time::precise_time_ns() ) / 1_000_000;
                if t > 1 && t < 1000000 { // The second half just checks if it overflowed,
                                          // which tells us that t should have been negative
                                          // and we are running slow and shouldn't sleep.
                    sleep( t );
                }
                self.state = RenderState;
                return self.next();
            },
            HandleEventsState => {
                // Handle all events before updating.
                return match self.game_window.poll_event() {
                    event::KeyPressed(key) => {
                        Some(KeyPress(KeyPressArgs {
                            key: key,
                        }))
                    },
                    event::KeyReleased(key) => {
                        Some(KeyRelease(KeyReleaseArgs {
                            key: key,
                        }))
                    },
                    event::MouseButtonPressed(mouse_button) => {
                        Some(MousePress(MousePressArgs {
                            button: mouse_button,
                        }))
                    },
                    event::MouseButtonReleased(mouse_button) => {
                        Some(MouseRelease(MouseReleaseArgs {
                            button: mouse_button,
                        }))
                    },
                    event::MouseMoved(x, y, relative_move) => {
                        match relative_move {
                            Some((dx, dy)) =>
                                self.state = MouseRelativeMoveState(dx, dy),
                            None => {},
                        };
                        Some(MouseMove(MouseMoveArgs {
                            x: x,
                            y: y,
                        }))
                    },
                    event::MouseScrolled(x, y) => {
                        Some(MouseScroll(MouseScrollArgs {
                            x: x,
                            y: y
                        }))
                    },
                    event::NoEvent => {
                        self.state = UpdateState;
                        self.next()
                    },
                }
            },
            MouseRelativeMoveState(dx, dy) => {
                self.state = HandleEventsState;
                return Some(MouseRelativeMove(MouseRelativeMoveArgs {
                    dx: dx,
                    dy: dy,
                }));
            },
            UpdateState => {
                self.updated += 1;
                self.state = UpdateLoopState;
                self.last_update += self.update_time_in_ns;
                return Some(Update(UpdateArgs{
                    dt: self.dt,
                }));
            },
        };

        /*
        // copied.

        while !self.should_close(game_window) {

            let start_render = time::precise_time_ns();

            // Rendering code
            let (w, h) = game_window.get_size();
            if w != 0 && h != 0 {
                self.viewport(game_window);
                let mut gl = Gl::new(&mut gl_data, asset_store);
                bg.clear(&mut gl);
                // Extrapolate time forward to allow smooth motion.
                // 'now' is always bigger than 'last_update'.
                let ext_dt = (start_render - last_update) as f64 / billion as f64;
                self.render(
                    ext_dt,
                    &context
                        .trans(-1.0, 1.0)
                        .scale(2.0 / w as f64, -2.0 / h as f64)
                        .store_view(),
                    &mut gl
                        );
                self.swap_buffers(game_window);
            }

            let next_render = start_render + min_ns_per_frame;

            // Update gamestate
            let mut updated = 0;

            while // If we haven't reached the required number of updates yet
                  ( updated < min_updates_per_frame ||
                    // Or we have the time to update further
                    time::precise_time_ns() < next_render ) &&
                  //And we haven't already progressed time to far
                  last_update + update_time_in_ns < next_render {

                self.handle_events(game_window, asset_store);
                self.update(dt, asset_store);

                updated += 1;
                last_update += update_time_in_ns;
            }

            // Wait if possible

            let t = (next_render - time::precise_time_ns() ) / 1_000_000;
            if t > 1 && t < 1000000 { // The second half just checks if it overflowed,
                                      // which tells us that t should have been negative
                                      // and we are running slow and shouldn't sleep.
                sleep( t );
            }

        }
        */
    }
}
<commit_msg>intersperse updates and frames more precisely<commit_after>use time;
use std::io::timer::sleep;

use GameWindow;
use keyboard;
use mouse;
use event;

/// Render argument.
#[deriving(Clone)]
pub struct RenderArgs {
    /// Extrapolated time in seconds, used to do smooth animation.
    pub ext_dt: f64,
    /// The width of rendered area.
    pub width: u32,
    /// The height of rendered area.
    pub height: u32,
}

/// Update argument.
#[deriving(Clone)]
pub struct UpdateArgs {
    /// Delta time in seconds.
    pub dt: f64,
}

/// Key press arguments.
#[deriving(Clone)]
pub struct KeyPressArgs {
    /// Keyboard key.
    pub key: keyboard::Key,
}

/// Key release arguments.
#[deriving(Clone)]
pub struct KeyReleaseArgs {
    /// Keyboard key.
    pub key: keyboard::Key,
}

/// Mouse press arguments.
#[deriving(Clone)]
pub struct MousePressArgs {
    /// Mouse button.
    pub button: mouse::Button,
}

/// Mouse release arguments.
#[deriving(Clone)]
pub struct MouseReleaseArgs {
    /// Mouse button.
    pub button: mouse::Button,
}

/// Mouse move arguments.
#[deriving(Clone)]
pub struct MouseMoveArgs {
    /// x.
    pub x: f64,
    /// y.
    pub y: f64,
}

/// Mouse relative move arguments.
#[deriving(Clone)]
pub struct MouseRelativeMoveArgs {
    /// Delta x.
    pub dx: f64,
    /// Delta y.
    pub dy: f64,
}

/// Mouse scroll arguments.
#[deriving(Clone)]
pub struct MouseScrollArgs {
    /// x.
    pub x: f64,
    /// y.
    pub y: f64,
}

/// Contains the different game events.
#[deriving(Clone)]
pub enum GameEvent {
    /// Render graphics.
    Render(RenderArgs),
    /// Update physical state of the game.
    Update(UpdateArgs),
    /// Pressed a keyboard key.
    KeyPress(KeyPressArgs),
    /// Released a keyboard key.
    KeyRelease(KeyReleaseArgs),
    /// Pressed a mouse button.
    MousePress(MousePressArgs),
    /// Released a mouse button.
    MouseRelease(MouseReleaseArgs),
    /// Moved mouse cursor.
    MouseMove(MouseMoveArgs),
    /// Moved mouse relative, not bounded by cursor.
    MouseRelativeMove(MouseRelativeMoveArgs),
    /// Scrolled mouse.
    MouseScroll(MouseScrollArgs)
}

enum GameIteratorState {
    RenderState,
    SwapBuffersState,
    PrepareUpdateLoopState,
    UpdateLoopState,
    HandleEventsState,
    MouseRelativeMoveState(f64, f64),
    UpdateState,
}

/// Settings for the game loop behavior.
#[deriving(Clone)]
pub struct GameIteratorSettings {
    /// The number of updates per second (UPS).
    pub updates_per_second: u64,
    /// The maximum number of frames per second (FPS target).
    pub max_frames_per_second: u64,
}

/// A game loop iterator.
///
/// Example:
///
/// ```Rust
/// let game_iter_settings = GameIteratorSettings {
///     updates_per_second: 120,
///     max_frames_per_second: 60,
/// };
/// let mut game_iter = GameIterator::new(&mut window, &game_iter_settings);
/// loop {
///     match game_iter.next() {
///         None => break,
///         Some(mut e) => match e {
///             Render(ref mut args) => {
///                 // Create graphics context with absolute coordinates.
///                 let c = Context::abs(args.width as f64, args.height as f64);
///                 // Do rendering here.
///             },
///             _ => {},
///         },
///     }
/// }
/// ```
pub struct GameIterator<'a, W> {
    /// The game window used by iterator.
    pub game_window: &'a mut W,
    state: GameIteratorState,
    last_update: u64,
    last_frame: u64,
    dt_update_in_ns: u64,
    dt_frame_in_ns: u64,
    dt: f64,
}

static billion: u64 = 1_000_000_000;

impl<'a, W: GameWindow> GameIterator<'a, W> {
    /// Creates a new game iterator.
    pub fn new(
        game_window: &'a mut W,
        settings: &GameIteratorSettings
    ) -> GameIterator<'a, W> {
        let updates_per_second: u64 = settings.updates_per_second;
        let max_frames_per_second: u64 = settings.max_frames_per_second;

        let start = time::precise_time_ns();
        GameIterator {
            game_window: game_window,
            state: RenderState,
            last_update: start,
            last_frame: start,
            dt_update_in_ns: billion / updates_per_second,
            dt_frame_in_ns: billion / max_frames_per_second,
            dt: 1.0 / updates_per_second as f64,
        }
    }
}

impl<'a, W: GameWindow>
Iterator<GameEvent>
for GameIterator<'a, W> {
    /// Returns the next game event.
    fn next(&mut self) -> Option<GameEvent> {
        match self.state {
            RenderState => {
                if self.game_window.should_close() { return None; }

                let start_render = time::precise_time_ns();
                self.last_frame = start_render;
                // Rendering code
                let (w, h) = self.game_window.get_size();
                if w != 0 && h != 0 {
                    // Swap buffers next time.
                    self.state = SwapBuffersState;
                    return Some(Render(RenderArgs {
                            // Extrapolate time forward to allow smooth motion.
                            ext_dt: (start_render - self.last_frame) as f64 / billion as f64,
                            width: w,
                            height: h,
                        }
                    ));
                }

                self.state = PrepareUpdateLoopState;
                return self.next();
            },
            SwapBuffersState => {
                self.game_window.swap_buffers();
                self.state = PrepareUpdateLoopState;
                return self.next();
            },
            PrepareUpdateLoopState => {
                self.state = UpdateLoopState;
                return self.next();
            },
            UpdateLoopState => {
                let current_time = time::precise_time_ns();

                let updates_wanted =
                    (current_time as f64 - self.last_update as f64) /
                    self.dt_update_in_ns as f64;
                let frames_wanted =
                    (current_time as f64 - self.last_frame as f64) /
                    self.dt_frame_in_ns as f64;

                if updates_wanted >= 1.0 && updates_wanted > frames_wanted {
                    self.state = HandleEventsState;
                    return self.next();
                }

                if frames_wanted >= 1.0 {
                    self.state = RenderState;
                    return self.next();
                }

                let next_frame = self.last_frame + self.dt_frame_in_ns;
                assert!(next_frame > current_time);
                // Convert to ms because that is what the sleep function takes.
                // Divide by 2 so we don't overshoot the next frame.
                sleep( (next_frame - current_time) / 1_000_000 / 2 );
                return self.next();
            },
            HandleEventsState => {
                // Handle all events before updating.
                return match self.game_window.poll_event() {
                    event::KeyPressed(key) => {
                        Some(KeyPress(KeyPressArgs {
                            key: key,
                        }))
                    },
                    event::KeyReleased(key) => {
                        Some(KeyRelease(KeyReleaseArgs {
                            key: key,
                        }))
                    },
                    event::MouseButtonPressed(mouse_button) => {
                        Some(MousePress(MousePressArgs {
                            button: mouse_button,
                        }))
                    },
                    event::MouseButtonReleased(mouse_button) => {
                        Some(MouseRelease(MouseReleaseArgs {
                            button: mouse_button,
                        }))
                    },
                    event::MouseMoved(x, y, relative_move) => {
                        match relative_move {
                            Some((dx, dy)) =>
                                self.state = MouseRelativeMoveState(dx, dy),
                            None => {},
                        };
                        Some(MouseMove(MouseMoveArgs {
                            x: x,
                            y: y,
                        }))
                    },
                    event::MouseScrolled(x, y) => {
                        Some(MouseScroll(MouseScrollArgs {
                            x: x,
                            y: y
                        }))
                    },
                    event::NoEvent => {
                        self.state = UpdateState;
                        self.next()
                    },
                }
            },
            MouseRelativeMoveState(dx, dy) => {
                self.state = HandleEventsState;
                return Some(MouseRelativeMove(MouseRelativeMoveArgs {
                    dx: dx,
                    dy: dy,
                }));
            },
            UpdateState => {
                self.state = UpdateLoopState;
                self.last_update = time::precise_time_ns();
                return Some(Update(UpdateArgs{
                    dt: self.dt,
                }));
            },
        };

        /*
        // copied.

        while !self.should_close(game_window) {

            let start_render = time::precise_time_ns();

            // Rendering code
            let (w, h) = game_window.get_size();
            if w != 0 && h != 0 {
                self.viewport(game_window);
                let mut gl = Gl::new(&mut gl_data, asset_store);
                bg.clear(&mut gl);
                // Extrapolate time forward to allow smooth motion.
                // 'now' is always bigger than 'last_update'.
                let ext_dt = (start_render - last_update) as f64 / billion as f64;
                self.render(
                    ext_dt,
                    &context
                        .trans(-1.0, 1.0)
                        .scale(2.0 / w as f64, -2.0 / h as f64)
                        .store_view(),
                    &mut gl
                        );
                self.swap_buffers(game_window);
            }

            let next_render = start_render + min_ns_per_frame;

            // Update gamestate
            let mut updated = 0;

            while // If we haven't reached the required number of updates yet
                  ( updated < min_updates_per_frame ||
                    // Or we have the time to update further
                    time::precise_time_ns() < next_render ) &&
                  //And we haven't already progressed time to far
                  last_update + update_time_in_ns < next_render {

                self.handle_events(game_window, asset_store);
                self.update(dt, asset_store);

                updated += 1;
                last_update += update_time_in_ns;
            }

            // Wait if possible

            let t = (next_render - time::precise_time_ns() ) / 1_000_000;
            if t > 1 && t < 1000000 { // The second half just checks if it overflowed,
                                      // which tells us that t should have been negative
                                      // and we are running slow and shouldn't sleep.
                sleep( t );
            }

        }
        */
    }
}
<|endoftext|>


<commit_before>use super::GpuMode;

pub const CONTROL: usize = 0xFF40; // LCD Control
pub const STAT: usize = 0xFF41; // LCD Controller status
pub const SCY: usize = 0xFF42;
pub const SCX: usize = 0xFF43;
pub const LY: usize = 0xFF44; // read-only
pub const LYC: usize = 0xFF45;
pub const BGP: usize = 0xFF47; // ignored in CGB mode
pub const OBP_0: usize = 0xFF48; // ignored in CGB mode
pub const OBP_1: usize = 0xFF49; // ignored in CGB mode
pub const WY: usize = 0xFF4A;
pub const WX: usize = 0xFF4B;

pub enum LcdControl {
    BgDisplayEnable = 1 << 0,
    ObjDisplayEnable = 1 << 1,
    ObjSize = 1 << 2,
    BgTileMapDisplaySelect = 1 << 3,
    BgWindowTileDataSelect = 1 << 4,
    WindowDisplayEnable = 1 << 5,
    WindowTileMapDisplaySelect = 1 << 6,
    LcdDisplayEnable = 1 << 7,
}
impl LcdControl {
    pub fn is_set(self, register: u8) -> bool {
        let v = self as u8;
        ((register >> v) & 0x01) == 0x01
    }
}

pub enum LcdControllerStatus {
    HBlankInterrupt = 1 << 3,
    VBlankInterrupt = 1 << 4,
    OamInterrupt = 1 << 5,
    LyCoincidenceInterrupt = 1 << 6,
}
impl LcdControllerStatus {
    pub fn is_set(self, register: u8) -> bool {
        let v = self as u8;
        ((register >> v) & 0x01) == 0x01
    }

    pub fn with_mode(register: u8, mode: GpuMode) -> u8 {
        (register & 0xFC) | (mode as u8)
    }

    /// Set the register's bit 2 to true if LYC=LY, false otherwise.
    pub fn with_coincidence_flag(register: u8, coincidence: bool) -> u8 {
        (register & 0xFB) | if coincidence { 0x01 } else { 0x00 }
    }
}
<commit_msg>GPU registers: add unit tests and fix bugs<commit_after>use super::GpuMode;

pub const CONTROL: usize = 0xFF40; // LCD Control
pub const STAT: usize = 0xFF41; // LCD Controller status
pub const SCY: usize = 0xFF42;
pub const SCX: usize = 0xFF43;
pub const LY: usize = 0xFF44; // read-only
pub const LYC: usize = 0xFF45;
pub const BGP: usize = 0xFF47; // ignored in CGB mode
pub const OBP_0: usize = 0xFF48; // ignored in CGB mode
pub const OBP_1: usize = 0xFF49; // ignored in CGB mode
pub const WY: usize = 0xFF4A;
pub const WX: usize = 0xFF4B;

pub enum LcdControl {
    BgDisplayEnable = 0,
    ObjDisplayEnable = 1,
    ObjSize = 2,
    BgTileMapDisplaySelect = 3,
    BgWindowTileDataSelect = 4,
    WindowDisplayEnable = 5,
    WindowTileMapDisplaySelect = 6,
    LcdDisplayEnable = 7,
}
impl LcdControl {
    pub fn is_set(self, register: u8) -> bool {
        let v = self as usize;
        ((register >> v) & 0x01) == 0x01
    }
}

pub enum LcdControllerStatus {
    HBlankInterrupt = 3,
    VBlankInterrupt = 4,
    OamInterrupt = 5,
    LyCoincidenceInterrupt = 6,
}
impl LcdControllerStatus {
    pub fn is_set(self, register: u8) -> bool {
        let v = self as usize;
        ((register >> v) & 0x01) == 0x01
    }

    pub fn with_mode(register: u8, mode: GpuMode) -> u8 {
        (register & 0xFC) | (mode as u8)
    }

    /// Set the register's bit 2 to true if LYC=LY, false otherwise.
    pub fn with_coincidence_flag(register: u8, coincidence: bool) -> u8 {
        (register & 0xFB) | if coincidence { 0x04 } else { 0x00 }
    }
}

#[cfg(test)]
mod test {
    use super::LcdControl::*;
    use super::LcdControllerStatus;
    use super::LcdControllerStatus::*;

    #[test]
    fn test_lcd_control_is_set() {
        assert_eq!(BgDisplayEnable.is_set(1 << 0), true);
        assert_eq!(ObjDisplayEnable.is_set(1 << 1), true);
        assert_eq!(ObjSize.is_set(1 << 2), true);
        assert_eq!(BgTileMapDisplaySelect.is_set(1 << 3), true);
        assert_eq!(BgWindowTileDataSelect.is_set(1 << 4), true);
        assert_eq!(WindowDisplayEnable.is_set(1 << 5), true);
        assert_eq!(WindowTileMapDisplaySelect.is_set(1 << 6), true);
        assert_eq!(LcdDisplayEnable.is_set(1 << 7), true);
    }

    #[test]
    fn test_lcdc_status_is_set() {
        assert_eq!(HBlankInterrupt.is_set(1 << 3), true);
        assert_eq!(VBlankInterrupt.is_set(1 << 4), true);
        assert_eq!(OamInterrupt.is_set(1 << 5), true);
        assert_eq!(LyCoincidenceInterrupt.is_set(1 << 6), true);
    }

    #[test]
    fn test_lcdc_status_with_mode() {
        use gpu::GpuMode::*;
        let lcdc_status = 0b_0110_1011;
        assert_eq!(LcdControllerStatus::with_mode(lcdc_status, H_Blank), 0b_0110_1000);
        assert_eq!(LcdControllerStatus::with_mode(lcdc_status, V_Blank), 0b_0110_1001);
        assert_eq!(LcdControllerStatus::with_mode(lcdc_status, OAM_Read), 0b_0110_1010);
        assert_eq!(LcdControllerStatus::with_mode(lcdc_status, VRAM_Read), 0b_0110_1011);
    }

    #[test]
    fn test_lcdc_status_with_coincidence_flag() {
        assert_eq!(LcdControllerStatus::with_coincidence_flag(0b_1011_0011, true), 0b_1011_0111);
        assert_eq!(LcdControllerStatus::with_coincidence_flag(0b_1011_0111, false), 0b_1011_0011);
    }
}
<|endoftext|>


<commit_before>//! Tests for various types of file (video, image, compressed, etc).
//!
//! Currently this is dependent on the file’s name and extension, because
//! those are the only metadata that we have access to without reading the
//! file’s contents.

use fs::File;

impl<'a> File<'a> {

    /// An “immediate” file is something that can be run or activated somehow
    /// in order to kick off the build of a project. It’s usually only present
    /// in directories full of source code.
    pub fn is_immediate(&self) -> bool {
        self.name.starts_with("README") || self.name_is_one_of( &[
            "Makefile", "Cargo.toml", "SConstruct", "CMakeLists.txt",
            "build.gradle", "Rakefile", "Gruntfile.js",
            "Gruntfile.coffee",
        ])
    }

    pub fn is_image(&self) -> bool {
        self.extension_is_one_of( &[
            "png", "jpeg", "jpg", "gif", "bmp", "tiff", "tif",
            "ppm", "pgm", "pbm", "pnm", "webp", "raw", "arw",
            "svg", "stl", "eps", "dvi", "ps", "cbr",
            "cbz", "xpm", "ico",
        ])
    }

    pub fn is_video(&self) -> bool {
        self.extension_is_one_of( &[
            "avi", "flv", "m2v", "mkv", "mov", "mp4", "mpeg",
            "mpg", "ogm", "ogv", "vob", "wmv",
        ])
    }

    pub fn is_music(&self) -> bool {
        self.extension_is_one_of( &[
            "aac", "m4a", "mp3", "ogg", "wma",
        ])
    }

    // Lossless music, rather than any other kind of data...
    pub fn is_lossless(&self) -> bool {
        self.extension_is_one_of( &[
            "alac", "ape", "flac", "wav",
        ])
    }

    pub fn is_crypto(&self) -> bool {
        self.extension_is_one_of( &[
            "asc", "enc", "gpg", "pgp", "sig", "signature", "pfx", "p12",
        ])
    }

    pub fn is_document(&self) -> bool {
        self.extension_is_one_of( &[
            "djvu", "doc", "docx", "dvi", "eml", "eps", "fotd",
            "odp", "odt", "pdf", "ppt", "pptx", "rtf",
            "xls", "xlsx",
        ])
    }

    pub fn is_compressed(&self) -> bool {
        self.extension_is_one_of( &[
            "zip", "tar", "Z", "gz", "bz2", "a", "ar", "7z",
            "iso", "dmg", "tc", "rar", "par", "tgz",
        ])
    }

    pub fn is_temp(&self) -> bool {
        self.name.ends_with('~')
            || (self.name.starts_with('#') && self.name.ends_with('#'))
            || self.extension_is_one_of( &[ "tmp", "swp", "swo", "swn", "bak" ])
    }

    pub fn is_compiled(&self) -> bool {
        if self.extension_is_one_of( &[ "class", "elc", "hi", "o", "pyc" ]) {
            true
        }
        else if let Some(dir) = self.dir {
            self.get_source_files().iter().any(|path| dir.contains(path))
        }
        else {
            false
        }
    }
}

#[cfg(broken_test)]
mod test {
    use file::test::{dummy_stat, new_file};

    #[test]
    fn lowercase() {
        let file = new_file(dummy_stat(), "/barracks.wav");
        assert_eq!(FileType::Lossless, file.get_type())
    }

    #[test]
    fn uppercase() {
        let file = new_file(dummy_stat(), "/BARRACKS.WAV");
        assert_eq!(FileType::Lossless, file.get_type())
    }

    #[test]
    fn cargo() {
        let file = new_file(dummy_stat(), "/Cargo.toml");
        assert_eq!(FileType::Immediate, file.get_type())
    }

    #[test]
    fn not_cargo() {
        let file = new_file(dummy_stat(), "/cargo.toml");
        assert_eq!(FileType::Normal, file.get_type())
    }
}
<commit_msg>Add cr2 as image<commit_after>//! Tests for various types of file (video, image, compressed, etc).
//!
//! Currently this is dependent on the file’s name and extension, because
//! those are the only metadata that we have access to without reading the
//! file’s contents.

use fs::File;

impl<'a> File<'a> {

    /// An “immediate” file is something that can be run or activated somehow
    /// in order to kick off the build of a project. It’s usually only present
    /// in directories full of source code.
    pub fn is_immediate(&self) -> bool {
        self.name.starts_with("README") || self.name_is_one_of( &[
            "Makefile", "Cargo.toml", "SConstruct", "CMakeLists.txt",
            "build.gradle", "Rakefile", "Gruntfile.js",
            "Gruntfile.coffee",
        ])
    }

    pub fn is_image(&self) -> bool {
        self.extension_is_one_of( &[
            "png", "jpeg", "jpg", "gif", "bmp", "tiff", "tif",
            "ppm", "pgm", "pbm", "pnm", "webp", "raw", "arw",
            "svg", "stl", "eps", "dvi", "ps", "cbr",
            "cbz", "xpm", "ico", "cr2",
        ])
    }

    pub fn is_video(&self) -> bool {
        self.extension_is_one_of( &[
            "avi", "flv", "m2v", "mkv", "mov", "mp4", "mpeg",
            "mpg", "ogm", "ogv", "vob", "wmv",
        ])
    }

    pub fn is_music(&self) -> bool {
        self.extension_is_one_of( &[
            "aac", "m4a", "mp3", "ogg", "wma",
        ])
    }

    // Lossless music, rather than any other kind of data...
    pub fn is_lossless(&self) -> bool {
        self.extension_is_one_of( &[
            "alac", "ape", "flac", "wav",
        ])
    }

    pub fn is_crypto(&self) -> bool {
        self.extension_is_one_of( &[
            "asc", "enc", "gpg", "pgp", "sig", "signature", "pfx", "p12",
        ])
    }

    pub fn is_document(&self) -> bool {
        self.extension_is_one_of( &[
            "djvu", "doc", "docx", "dvi", "eml", "eps", "fotd",
            "odp", "odt", "pdf", "ppt", "pptx", "rtf",
            "xls", "xlsx",
        ])
    }

    pub fn is_compressed(&self) -> bool {
        self.extension_is_one_of( &[
            "zip", "tar", "Z", "gz", "bz2", "a", "ar", "7z",
            "iso", "dmg", "tc", "rar", "par", "tgz",
        ])
    }

    pub fn is_temp(&self) -> bool {
        self.name.ends_with('~')
            || (self.name.starts_with('#') && self.name.ends_with('#'))
            || self.extension_is_one_of( &[ "tmp", "swp", "swo", "swn", "bak" ])
    }

    pub fn is_compiled(&self) -> bool {
        if self.extension_is_one_of( &[ "class", "elc", "hi", "o", "pyc" ]) {
            true
        }
        else if let Some(dir) = self.dir {
            self.get_source_files().iter().any(|path| dir.contains(path))
        }
        else {
            false
        }
    }
}

#[cfg(broken_test)]
mod test {
    use file::test::{dummy_stat, new_file};

    #[test]
    fn lowercase() {
        let file = new_file(dummy_stat(), "/barracks.wav");
        assert_eq!(FileType::Lossless, file.get_type())
    }

    #[test]
    fn uppercase() {
        let file = new_file(dummy_stat(), "/BARRACKS.WAV");
        assert_eq!(FileType::Lossless, file.get_type())
    }

    #[test]
    fn cargo() {
        let file = new_file(dummy_stat(), "/Cargo.toml");
        assert_eq!(FileType::Immediate, file.get_type())
    }

    #[test]
    fn not_cargo() {
        let file = new_file(dummy_stat(), "/cargo.toml");
        assert_eq!(FileType::Normal, file.get_type())
    }
}
<|endoftext|>