<commit_before>use syn::visit_mut::{self, VisitMut};
use syn::{
    Attribute, Block, Expr, ExprArray, ExprAssign, ExprAssignOp, ExprAsync, ExprAwait, ExprBinary,
    ExprBlock, ExprBox, ExprBreak, ExprCall, ExprCast, ExprClosure, ExprContinue, ExprField,
    ExprForLoop, ExprGroup, ExprIf, ExprIndex, ExprLet, ExprLit, ExprLoop, ExprMacro, ExprMatch,
    ExprMethodCall, ExprParen, ExprPath, ExprRange, ExprReference, ExprRepeat, ExprReturn,
    ExprStruct, ExprTry, ExprTryBlock, ExprTuple, ExprType, ExprUnary, ExprUnsafe, ExprWhile,
    ExprYield, File, Item, ItemMod, Stmt,
};

pub fn sanitize(syntax_tree: &mut File) {
    remove_macro_rules_from_vec_item(&mut syntax_tree.items);
    Sanitize.visit_file_mut(syntax_tree);
}

// - Remove all macro_rules
// - Remove doc attributes on statements (dtolnay/cargo-expand#71)
struct Sanitize;

impl VisitMut for Sanitize {
    fn visit_item_mod_mut(&mut self, i: &mut ItemMod) {
        if let Some((_, items)) = &mut i.content {
            remove_macro_rules_from_vec_item(items);
        }
        visit_mut::visit_item_mod_mut(self, i);
    }

    fn visit_block_mut(&mut self, i: &mut Block) {
        i.stmts.retain(|stmt| match stmt {
            Stmt::Item(Item::Macro(_)) => false,
            _ => true,
        });
        visit_mut::visit_block_mut(self, i);
    }

    fn visit_stmt_mut(&mut self, i: &mut Stmt) {
        match i {
            Stmt::Local(local) => remove_doc_attributes(&mut local.attrs),
            Stmt::Expr(e) | Stmt::Semi(e, _) => {
                if let Some(attrs) = attrs_mut(e) {
                    remove_doc_attributes(attrs);
                }
            }
            Stmt::Item(_) => {}
        }
    }
}

fn remove_macro_rules_from_vec_item(items: &mut Vec<Item>) {
    items.retain(|item| match item {
        Item::Macro(_) => false,
        _ => true,
    });
}

fn remove_doc_attributes(attrs: &mut Vec<Attribute>) {
    attrs.retain(|attr| !attr.path.is_ident("doc"));
}

fn attrs_mut(e: &mut Expr) -> Option<&mut Vec<Attribute>> {
    match e {
        Expr::Box(ExprBox { attrs, .. })
        | Expr::Array(ExprArray { attrs, .. })
        | Expr::Call(ExprCall { attrs, .. })
        | Expr::MethodCall(ExprMethodCall { attrs, .. })
        | Expr::Tuple(ExprTuple { attrs, .. })
        | Expr::Binary(ExprBinary { attrs, .. })
        | Expr::Unary(ExprUnary { attrs, .. })
        | Expr::Lit(ExprLit { attrs, .. })
        | Expr::Cast(ExprCast { attrs, .. })
        | Expr::Type(ExprType { attrs, .. })
        | Expr::Let(ExprLet { attrs, .. })
        | Expr::If(ExprIf { attrs, .. })
        | Expr::While(ExprWhile { attrs, .. })
        | Expr::ForLoop(ExprForLoop { attrs, .. })
        | Expr::Loop(ExprLoop { attrs, .. })
        | Expr::Match(ExprMatch { attrs, .. })
        | Expr::Closure(ExprClosure { attrs, .. })
        | Expr::Unsafe(ExprUnsafe { attrs, .. })
        | Expr::Block(ExprBlock { attrs, .. })
        | Expr::Assign(ExprAssign { attrs, .. })
        | Expr::AssignOp(ExprAssignOp { attrs, .. })
        | Expr::Field(ExprField { attrs, .. })
        | Expr::Index(ExprIndex { attrs, .. })
        | Expr::Range(ExprRange { attrs, .. })
        | Expr::Path(ExprPath { attrs, .. })
        | Expr::Reference(ExprReference { attrs, .. })
        | Expr::Break(ExprBreak { attrs, .. })
        | Expr::Continue(ExprContinue { attrs, .. })
        | Expr::Return(ExprReturn { attrs, .. })
        | Expr::Macro(ExprMacro { attrs, .. })
        | Expr::Struct(ExprStruct { attrs, .. })
        | Expr::Repeat(ExprRepeat { attrs, .. })
        | Expr::Paren(ExprParen { attrs, .. })
        | Expr::Group(ExprGroup { attrs, .. })
        | Expr::Try(ExprTry { attrs, .. })
        | Expr::Async(ExprAsync { attrs, .. })
        | Expr::Await(ExprAwait { attrs, .. })
        | Expr::TryBlock(ExprTryBlock { attrs, .. })
        | Expr::Yield(ExprYield { attrs, .. }) => Some(attrs),
        _ => None,
    }
}
<commit_msg>Add missing Sanitize visitor recursion step<commit_after>use syn::visit_mut::{self, VisitMut};
use syn::{
    Attribute, Block, Expr, ExprArray, ExprAssign, ExprAssignOp, ExprAsync, ExprAwait, ExprBinary,
    ExprBlock, ExprBox, ExprBreak, ExprCall, ExprCast, ExprClosure, ExprContinue, ExprField,
    ExprForLoop, ExprGroup, ExprIf, ExprIndex, ExprLet, ExprLit, ExprLoop, ExprMacro, ExprMatch,
    ExprMethodCall, ExprParen, ExprPath, ExprRange, ExprReference, ExprRepeat, ExprReturn,
    ExprStruct, ExprTry, ExprTryBlock, ExprTuple, ExprType, ExprUnary, ExprUnsafe, ExprWhile,
    ExprYield, File, Item, ItemMod, Stmt,
};

pub fn sanitize(syntax_tree: &mut File) {
    remove_macro_rules_from_vec_item(&mut syntax_tree.items);
    Sanitize.visit_file_mut(syntax_tree);
}

// - Remove all macro_rules
// - Remove doc attributes on statements (dtolnay/cargo-expand#71)
struct Sanitize;

impl VisitMut for Sanitize {
    fn visit_item_mod_mut(&mut self, i: &mut ItemMod) {
        if let Some((_, items)) = &mut i.content {
            remove_macro_rules_from_vec_item(items);
        }
        visit_mut::visit_item_mod_mut(self, i);
    }

    fn visit_block_mut(&mut self, i: &mut Block) {
        i.stmts.retain(|stmt| match stmt {
            Stmt::Item(Item::Macro(_)) => false,
            _ => true,
        });
        visit_mut::visit_block_mut(self, i);
    }

    fn visit_stmt_mut(&mut self, i: &mut Stmt) {
        match i {
            Stmt::Local(local) => remove_doc_attributes(&mut local.attrs),
            Stmt::Expr(e) | Stmt::Semi(e, _) => {
                if let Some(attrs) = attrs_mut(e) {
                    remove_doc_attributes(attrs);
                }
            }
            Stmt::Item(_) => {}
        }
        visit_mut::visit_stmt_mut(self, i);
    }
}

fn remove_macro_rules_from_vec_item(items: &mut Vec<Item>) {
    items.retain(|item| match item {
        Item::Macro(_) => false,
        _ => true,
    });
}

fn remove_doc_attributes(attrs: &mut Vec<Attribute>) {
    attrs.retain(|attr| !attr.path.is_ident("doc"));
}

fn attrs_mut(e: &mut Expr) -> Option<&mut Vec<Attribute>> {
    match e {
        Expr::Box(ExprBox { attrs, .. })
        | Expr::Array(ExprArray { attrs, .. })
        | Expr::Call(ExprCall { attrs, .. })
        | Expr::MethodCall(ExprMethodCall { attrs, .. })
        | Expr::Tuple(ExprTuple { attrs, .. })
        | Expr::Binary(ExprBinary { attrs, .. })
        | Expr::Unary(ExprUnary { attrs, .. })
        | Expr::Lit(ExprLit { attrs, .. })
        | Expr::Cast(ExprCast { attrs, .. })
        | Expr::Type(ExprType { attrs, .. })
        | Expr::Let(ExprLet { attrs, .. })
        | Expr::If(ExprIf { attrs, .. })
        | Expr::While(ExprWhile { attrs, .. })
        | Expr::ForLoop(ExprForLoop { attrs, .. })
        | Expr::Loop(ExprLoop { attrs, .. })
        | Expr::Match(ExprMatch { attrs, .. })
        | Expr::Closure(ExprClosure { attrs, .. })
        | Expr::Unsafe(ExprUnsafe { attrs, .. })
        | Expr::Block(ExprBlock { attrs, .. })
        | Expr::Assign(ExprAssign { attrs, .. })
        | Expr::AssignOp(ExprAssignOp { attrs, .. })
        | Expr::Field(ExprField { attrs, .. })
        | Expr::Index(ExprIndex { attrs, .. })
        | Expr::Range(ExprRange { attrs, .. })
        | Expr::Path(ExprPath { attrs, .. })
        | Expr::Reference(ExprReference { attrs, .. })
        | Expr::Break(ExprBreak { attrs, .. })
        | Expr::Continue(ExprContinue { attrs, .. })
        | Expr::Return(ExprReturn { attrs, .. })
        | Expr::Macro(ExprMacro { attrs, .. })
        | Expr::Struct(ExprStruct { attrs, .. })
        | Expr::Repeat(ExprRepeat { attrs, .. })
        | Expr::Paren(ExprParen { attrs, .. })
        | Expr::Group(ExprGroup { attrs, .. })
        | Expr::Try(ExprTry { attrs, .. })
        | Expr::Async(ExprAsync { attrs, .. })
        | Expr::Await(ExprAwait { attrs, .. })
        | Expr::TryBlock(ExprTryBlock { attrs, .. })
        | Expr::Yield(ExprYield { attrs, .. }) => Some(attrs),
        _ => None,
    }
}
<|endoftext|>


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

//! Fundamental algebraic structures.
//!
//! For most applications requiring an abstraction over the reals, `Real`
//! should be sufficient.
//!
//! ## Algebraic properties
//!
//! The goal of algebraic structures is to allow elements of sets to be combined together using one
//! or several operators. The number and properties of those operators characterize the algebraic
//! structure. Abstract operators are usually noted `∘`, `+`, or `×`. The last two are preferred
//! when their behavior conform with the usual meaning of addition and multiplication of reals.
//! Let `Self` be a set. Here is a list of the most common properties those operator may fulfill:
//!
//! ~~~notrust
//! (Closure)       a, b ∈ Self ⇒ a ∘ b ∈ Self,
//! (Divisibility)  ∀ a, b ∈ Self, ∃! r, l ∈ Self such that l ∘ a = b and a ∘ r = b
//! (Invertibility) ∃ e ∈ Self, ∀ a ∈ Self, ∃ r, l ∈ Self such that l ∘ a = a ∘ r = e
//!                 If the right and left inverse are equal they are usually noted r = l = a⁻¹.
//! (Associativity) ∀ a, b, c ∈ Self, (a ∘ b) ∘ c = a ∘ (b ∘ c)
//! (Neutral Elt.)  ∃ e ∈ Self, ∀ a ∈ Self, e ∘ a = a ∘ e = a
//! (Commutativity) ∀ a, b ∈ Self, a ∘ b = b ∘ a
//! ~~~
//!
//! ## Identity elements
//!
//! Two traits are provided that allow the definition of the additive and
//! multiplicative identity elements:
//!
//! - `IdentityAdditive`
//! - `IdentityMultiplicative`
//!
//! ## AbstractGroup-like structures
//!
//! These structures are provided for both the addition and multiplication.
//!
//! ~~~notrust
//!            AbstractMagma
//!                 |
//!         _______/ \______
//!        /                \
//!  divisibility       associativity
//!       |                  |
//!       V                  V
//! AbstractQuasigroup AbstractSemigroup
//!       |                  |
//!   identity            identity
//!       |                  |
//!       V                  V
//!  AbstractLoop       AbstractMonoid
//!       |                  |
//!  associativity     invertibility
//!        \______   _______/
//!               \ /
//!                |
//!                V
//!          AbstractGroup
//!                |
//!          commutativity
//!                |
//!                V
//!      AbstractGroupAbelian
//! ~~~
//!
//! The following traits are provided:
//!
//! - (`Abstract`|`Additive`|`Multiplicative`)`Magma`
//! - (`Abstract`|`Additive`|`Multiplicative`)`Quasigroup`
//! - (`Abstract`|`Additive`|`Multiplicative`)`Loop`
//! - (`Abstract`|`Additive`|`Multiplicative`)`Semigroup`
//! - (`Abstract`|`Additive`|`Multiplicative`)`Monoid`
//! - (`Abstract`|`Additive`|`Multiplicative`)`Group`
//! - (`Abstract`|`Additive`|`Multiplicative`)`GroupAbelian`
//!
//! ## Ring-like structures
//!
//! ~~~notrust
//!      GroupAbelian           Monoid
//!           \________   ________/
//!                    \ /
//!                     |
//!                     V
//!                    Ring
//!                     |
//!            commutativity_of_mul
//!                     |
//!                     V
//!              RingCommutative           GroupAbelian
//!                      \_______   ___________/
//!                              \ /
//!                               |
//!                               V
//!                             Field
//! ~~~
//!
//! The following traits are provided:
//!
//! -            `Ring`
//! - `RingCommutative`
//! -           `Field`
//!
//! ## Module-like structures
//!
//! ~~~notrust
//!     GroupAbelian         RingCommutative
//!           \______         _____/
//!                  \       /
//!                   |     |
//!                   V     V
//!                Module<Scalar>          Field
//!                    \______         _____/
//!                           \       /
//!                            |     |
//!                            V     V
//!                      VectorSpace<Scalar>
//! ~~~
//!
//! The following traits are provided:
//!
//! - `Module`
//! - `VectorSpace`
//!
//! # Quickcheck properties
//!
//! Functions are provided to test that algebraic properties like
//! assciociativity and commutativity hold for a given set of arguments.
//!
//! For example:
//!
//! ~~~.ignore
//! use algebra::general::SemigroupMultiplicative;
//!
//! quickcheck! (
//!     fn prop_mul_is_associative(args: (i32, i32, i32)) -> bool {
//!         SemigroupMultiplicative::prop_mul_is_associative(args)
//!     }
//! );
//! ~~~

pub use self::operator::{Inverse, Operator, Multiplicative, Additive,
                         ClosedAdd, ClosedMul, ClosedSub, ClosedNeg, ClosedDiv};
pub use self::identity::{Identity, Id};
pub use self::subset::{SubsetOf, SupersetOf};

pub use self::one_operator::{AbstractMagma, AbstractQuasigroup, AbstractLoop, AbstractSemigroup,
                             AbstractMonoid, AbstractGroup, AbstractGroupAbelian};
pub use self::two_operators::{AbstractRing, AbstractRingCommutative, AbstractField};
pub use self::module::AbstractModule;
pub use self::lattice::{MeetSemilattice, JoinSemilattice, Lattice};
pub use self::specialized::{
    AdditiveMagma, AdditiveQuasigroup, AdditiveLoop, AdditiveSemigroup,
    AdditiveMonoid, AdditiveGroup, AdditiveGroupAbelian,
    MultiplicativeMagma, MultiplicativeQuasigroup, MultiplicativeLoop, MultiplicativeSemigroup,
    MultiplicativeMonoid, MultiplicativeGroup, MultiplicativeGroupAbelian,
    Ring, RingCommutative, Field, Module};
pub use self::real::Real;

#[macro_use]
mod one_operator;
mod two_operators;
mod module;
mod identity;
mod operator;
mod real;
mod lattice;
mod subset;
mod specialized;
#[doc(hidden)]
pub mod wrapper;
<commit_msg>Updated general/mod.rs since documentation is not compiling with nightly<commit_after>// Copyright 2013-2014 The Algebra Developers.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Fundamental algebraic structures.
//!
//! For most applications requiring an abstraction over the reals, `Real`
//! should be sufficient.
//!
//! ## Algebraic properties
//!
//! The goal of algebraic structures is to allow elements of sets to be combined together using one
//! or several operators. The number and properties of those operators characterize the algebraic
//! structure. Abstract operators are usually noted `∘`, `+`, or `×`. The last two are preferred
//! when their behavior conform with the usual meaning of addition and multiplication of reals.
//! Let `Self` be a set. Here is a list of the most common properties those operator may fulfill:
//!
//! ~~~notrust
//! (Closure)       a, b ∈ Self ⇒ a ∘ b ∈ Self,
//! (Divisibility)  ∀ a, b ∈ Self, ∃! r, l ∈ Self such that l ∘ a = b and a ∘ r = b
//! (Invertibility) ∃ e ∈ Self, ∀ a ∈ Self, ∃ r, l ∈ Self such that l ∘ a = a ∘ r = e
//!                 If the right and left inverse are equal they are usually noted r = l = a⁻¹.
//! (Associativity) ∀ a, b, c ∈ Self, (a ∘ b) ∘ c = a ∘ (b ∘ c)
//! (Neutral Elt.)  ∃ e ∈ Self, ∀ a ∈ Self, e ∘ a = a ∘ e = a
//! (Commutativity) ∀ a, b ∈ Self, a ∘ b = b ∘ a
//! ~~~
//!
//! ## Identity elements
//!
//! Two traits are provided that allow the definition of the additive and
//! multiplicative identity elements:
//!
//! - `IdentityAdditive`
//! - `IdentityMultiplicative`
//!
//! ## AbstractGroup-like structures
//!
//! These structures are provided for both the addition and multiplication.
//!
//! ~~~notrust
//!            AbstractMagma
//!                 |
//!         _______/ \______
//!        /                \
//!  divisibility       associativity
//!       |                  |
//!       V                  V
//! AbstractQuasigroup AbstractSemigroup
//!       |                  |
//!   identity            identity
//!       |                  |
//!       V                  V
//!  AbstractLoop       AbstractMonoid
//!       |                  |
//!  associativity     invertibility
//!        \______   _______/
//!               \ /
//!                |
//!                V
//!          AbstractGroup
//!                |
//!          commutativity
//!                |
//!                V
//!      AbstractGroupAbelian
//! ~~~
//!
//! The following traits are provided:
//!
//! - (`Abstract`|`Additive`|`Multiplicative`)`Magma`
//! - (`Abstract`|`Additive`|`Multiplicative`)`Quasigroup`
//! - (`Abstract`|`Additive`|`Multiplicative`)`Loop`
//! - (`Abstract`|`Additive`|`Multiplicative`)`Semigroup`
//! - (`Abstract`|`Additive`|`Multiplicative`)`Monoid`
//! - (`Abstract`|`Additive`|`Multiplicative`)`Group`
//! - (`Abstract`|`Additive`|`Multiplicative`)`GroupAbelian`
//!
//! ## Ring-like structures
//!
//! ~~~notrust
//!      GroupAbelian           Monoid
//!           \________   ________/
//!                    \ /
//!                     |
//!                     V
//!                    Ring
//!                     |
//!            commutativity_of_mul
//!                     |
//!                     V
//!              RingCommutative           GroupAbelian
//!                      \_______   ___________/
//!                              \ /
//!                               |
//!                               V
//!                             Field
//! ~~~
//!
//! The following traits are provided:
//!
//! - `Ring`
//! - `RingCommutative`
//! - `Field`
//!
//! ## Module-like structures
//!
//! ~~~notrust
//!     GroupAbelian         RingCommutative
//!           \______         _____/
//!                  \       /
//!                   |     |
//!                   V     V
//!                Module<Scalar>          Field
//!                    \______         _____/
//!                           \       /
//!                            |     |
//!                            V     V
//!                      VectorSpace<Scalar>
//! ~~~
//!
//! The following traits are provided:
//!
//! - `Module`
//! - `VectorSpace`
//!
//! # Quickcheck properties
//!
//! Functions are provided to test that algebraic properties like
//! assciociativity and commutativity hold for a given set of arguments.
//!
//! For example:
//!
//! ~~~.ignore
//! use algebra::general::SemigroupMultiplicative;
//!
//! quickcheck! (
//!     fn prop_mul_is_associative(args: (i32, i32, i32)) -> bool {
//!         SemigroupMultiplicative::prop_mul_is_associative(args)
//!     }
//! );
//! ~~~

pub use self::operator::{Inverse, Operator, Multiplicative, Additive,
                         ClosedAdd, ClosedMul, ClosedSub, ClosedNeg, ClosedDiv};
pub use self::identity::{Identity, Id};
pub use self::subset::{SubsetOf, SupersetOf};

pub use self::one_operator::{AbstractMagma, AbstractQuasigroup, AbstractLoop, AbstractSemigroup,
                             AbstractMonoid, AbstractGroup, AbstractGroupAbelian};
pub use self::two_operators::{AbstractRing, AbstractRingCommutative, AbstractField};
pub use self::module::AbstractModule;
pub use self::lattice::{MeetSemilattice, JoinSemilattice, Lattice};
pub use self::specialized::{
    AdditiveMagma, AdditiveQuasigroup, AdditiveLoop, AdditiveSemigroup,
    AdditiveMonoid, AdditiveGroup, AdditiveGroupAbelian,
    MultiplicativeMagma, MultiplicativeQuasigroup, MultiplicativeLoop, MultiplicativeSemigroup,
    MultiplicativeMonoid, MultiplicativeGroup, MultiplicativeGroupAbelian,
    Ring, RingCommutative, Field, Module};
pub use self::real::Real;

#[macro_use]
mod one_operator;
mod two_operators;
mod module;
mod identity;
mod operator;
mod real;
mod lattice;
mod subset;
mod specialized;
#[doc(hidden)]
pub mod wrapper;
<|endoftext|>


<commit_before>use std::cmp::{max, min};

use unicode_width::UnicodeWidthStr;

use crate::buffer::Buffer;
use crate::layout::Rect;
use crate::style::Style;
use crate::symbols::bar;
use crate::widgets::{Block, Widget};

/// Display multiple bars in a single widgets
///
/// # Examples
///
/// ```
/// # use tui::widgets::{Block, Borders, BarChart};
/// # use tui::style::{Style, Color, Modifier};
/// # fn main() {
/// BarChart::default()
///     .block(Block::default().title("BarChart").borders(Borders::ALL))
///     .bar_width(3)
///     .bar_gap(1)
///     .style(Style::default().fg(Color::Yellow).bg(Color::Red))
///     .value_style(Style::default().fg(Color::Red).modifier(Modifier::BOLD))
///     .label_style(Style::default().fg(Color::White))
///     .data(&[("B0", 0), ("B1", 2), ("B2", 4), ("B3", 3)])
///     .max(4);
/// # }
/// ```
pub struct BarChart<'a> {
    /// Block to wrap the widget in
    block: Option<Block<'a>>,
    /// The width of each bar
    bar_width: u16,
    /// The gap between each bar
    bar_gap: u16,
    /// Style of the values printed at the bottom of each bar
    value_style: Style,
    /// Style of the labels printed under each bar
    label_style: Style,
    /// Style for the widget
    style: Style,
    /// Slice of (label, value) pair to plot on the chart
    data: &'a [(&'a str, u64)],
    /// Value necessary for a bar to reach the maximum height (if no value is specified,
    /// the maximum value in the data is taken as reference)
    max: Option<u64>,
    /// Values to display on the bar (computed when the data is passed to the widget)
    values: Vec<String>,
}

impl<'a> Default for BarChart<'a> {
    fn default() -> BarChart<'a> {
        BarChart {
            block: None,
            max: None,
            data: &[],
            values: Vec::new(),
            bar_width: 1,
            bar_gap: 1,
            value_style: Default::default(),
            label_style: Default::default(),
            style: Default::default(),
        }
    }
}

impl<'a> BarChart<'a> {
    pub fn data(mut self, data: &'a [(&'a str, u64)]) -> BarChart<'a> {
        self.data = data;
        self.values = Vec::with_capacity(self.data.len());
        for &(_, v) in self.data {
            self.values.push(format!("{}", v));
        }
        self
    }

    pub fn block(mut self, block: Block<'a>) -> BarChart<'a> {
        self.block = Some(block);
        self
    }
    pub fn max(mut self, max: u64) -> BarChart<'a> {
        self.max = Some(max);
        self
    }

    pub fn bar_width(mut self, width: u16) -> BarChart<'a> {
        self.bar_width = width;
        self
    }
    pub fn bar_gap(mut self, gap: u16) -> BarChart<'a> {
        self.bar_gap = gap;
        self
    }
    pub fn value_style(mut self, style: Style) -> BarChart<'a> {
        self.value_style = style;
        self
    }
    pub fn label_style(mut self, style: Style) -> BarChart<'a> {
        self.label_style = style;
        self
    }
    pub fn style(mut self, style: Style) -> BarChart<'a> {
        self.style = style;
        self
    }
}

impl<'a> Widget for BarChart<'a> {
    fn draw(&mut self, area: Rect, buf: &mut Buffer) {
        let chart_area = match self.block {
            Some(ref mut b) => {
                b.draw(area, buf);
                b.inner(area)
            }
            None => area,
        };

        if chart_area.height < 2 {
            return;
        }

        self.background(chart_area, buf, self.style.bg);

        let max = self
            .max
            .unwrap_or_else(|| self.data.iter().fold(0, |acc, &(_, v)| max(v, acc)));
        let max_index = min(
            (chart_area.width / (self.bar_width + self.bar_gap)) as usize,
            self.data.len(),
        );
        let mut data = self
            .data
            .iter()
            .take(max_index)
            .map(|&(l, v)| {
                (
                    l,
                    v * u64::from(chart_area.height) * 8 / std::cmp::max(max, 1),
                )
            })
            .collect::<Vec<(&str, u64)>>();
        for j in (0..chart_area.height - 1).rev() {
            for (i, d) in data.iter_mut().enumerate() {
                let symbol = match d.1 {
                    0 => " ",
                    1 => bar::ONE_EIGHTH,
                    2 => bar::ONE_QUATER,
                    3 => bar::THREE_EIGHTHS,
                    4 => bar::HALF,
                    5 => bar::FIVE_EIGHTHS,
                    6 => bar::THREE_QUATERS,
                    7 => bar::SEVEN_EIGHTHS,
                    _ => bar::FULL,
                };

                for x in 0..self.bar_width {
                    buf.get_mut(
                        chart_area.left() + i as u16 * (self.bar_width + self.bar_gap) + x,
                        chart_area.top() + j,
                    )
                    .set_symbol(symbol)
                    .set_style(self.style);
                }

                if d.1 > 8 {
                    d.1 -= 8;
                } else {
                    d.1 = 0;
                }
            }
        }

        for (i, &(label, value)) in self.data.iter().take(max_index).enumerate() {
            if value != 0 {
                let value_label = &self.values[i];
                let width = value_label.width() as u16;
                if width < self.bar_width {
                    buf.set_string(
                        chart_area.left()
                            + i as u16 * (self.bar_width + self.bar_gap)
                            + (self.bar_width - width) / 2,
                        chart_area.bottom() - 2,
                        value_label,
                        self.value_style,
                    );
                }
            }
            buf.set_stringn(
                chart_area.left() + i as u16 * (self.bar_width + self.bar_gap),
                chart_area.bottom() - 1,
                label,
                self.bar_width as usize,
                self.label_style,
            );
        }
    }
}
<commit_msg>typo in barcharg.rs<commit_after>use std::cmp::{max, min};

use unicode_width::UnicodeWidthStr;

use crate::buffer::Buffer;
use crate::layout::Rect;
use crate::style::Style;
use crate::symbols::bar;
use crate::widgets::{Block, Widget};

/// Display multiple bars in a single widgets
///
/// # Examples
///
/// ```
/// # use tui::widgets::{Block, Borders, BarChart};
/// # use tui::style::{Style, Color, Modifier};
/// # fn main() {
/// BarChart::default()
///     .block(Block::default().title("BarChart").borders(Borders::ALL))
///     .bar_width(3)
///     .bar_gap(1)
///     .style(Style::default().fg(Color::Yellow).bg(Color::Red))
///     .value_style(Style::default().fg(Color::Red).modifier(Modifier::BOLD))
///     .label_style(Style::default().fg(Color::White))
///     .data(&[("B0", 0), ("B1", 2), ("B2", 4), ("B3", 3)])
///     .max(4);
/// # }
/// ```
pub struct BarChart<'a> {
    /// Block to wrap the widget in
    block: Option<Block<'a>>,
    /// The width of each bar
    bar_width: u16,
    /// The gap between each bar
    bar_gap: u16,
    /// Style of the values printed at the bottom of each bar
    value_style: Style,
    /// Style of the labels printed under each bar
    label_style: Style,
    /// Style for the widget
    style: Style,
    /// Slice of (label, value) pair to plot on the chart
    data: &'a [(&'a str, u64)],
    /// Value necessary for a bar to reach the maximum height (if no value is specified,
    /// the maximum value in the data is taken as reference)
    max: Option<u64>,
    /// Values to display on the bar (computed when the data is passed to the widget)
    values: Vec<String>,
}

impl<'a> Default for BarChart<'a> {
    fn default() -> BarChart<'a> {
        BarChart {
            block: None,
            max: None,
            data: &[],
            values: Vec::new(),
            bar_width: 1,
            bar_gap: 1,
            value_style: Default::default(),
            label_style: Default::default(),
            style: Default::default(),
        }
    }
}

impl<'a> BarChart<'a> {
    pub fn data(mut self, data: &'a [(&'a str, u64)]) -> BarChart<'a> {
        self.data = data;
        self.values = Vec::with_capacity(self.data.len());
        for &(_, v) in self.data {
            self.values.push(format!("{}", v));
        }
        self
    }

    pub fn block(mut self, block: Block<'a>) -> BarChart<'a> {
        self.block = Some(block);
        self
    }
    pub fn max(mut self, max: u64) -> BarChart<'a> {
        self.max = Some(max);
        self
    }

    pub fn bar_width(mut self, width: u16) -> BarChart<'a> {
        self.bar_width = width;
        self
    }
    pub fn bar_gap(mut self, gap: u16) -> BarChart<'a> {
        self.bar_gap = gap;
        self
    }
    pub fn value_style(mut self, style: Style) -> BarChart<'a> {
        self.value_style = style;
        self
    }
    pub fn label_style(mut self, style: Style) -> BarChart<'a> {
        self.label_style = style;
        self
    }
    pub fn style(mut self, style: Style) -> BarChart<'a> {
        self.style = style;
        self
    }
}

impl<'a> Widget for BarChart<'a> {
    fn draw(&mut self, area: Rect, buf: &mut Buffer) {
        let chart_area = match self.block {
            Some(ref mut b) => {
                b.draw(area, buf);
                b.inner(area)
            }
            None => area,
        };

        if chart_area.height < 2 {
            return;
        }

        self.background(chart_area, buf, self.style.bg);

        let max = self
            .max
            .unwrap_or_else(|| self.data.iter().fold(0, |acc, &(_, v)| max(v, acc)));
        let max_index = min(
            (chart_area.width / (self.bar_width + self.bar_gap)) as usize,
            self.data.len(),
        );
        let mut data = self
            .data
            .iter()
            .take(max_index)
            .map(|&(l, v)| {
                (
                    l,
                    v * u64::from(chart_area.height) * 8 / std::cmp::max(max, 1),
                )
            })
            .collect::<Vec<(&str, u64)>>();
        for j in (0..chart_area.height - 1).rev() {
            for (i, d) in data.iter_mut().enumerate() {
                let symbol = match d.1 {
                    0 => " ",
                    1 => bar::ONE_EIGHTH,
                    2 => bar::ONE_QUARTER,
                    3 => bar::THREE_EIGHTHS,
                    4 => bar::HALF,
                    5 => bar::FIVE_EIGHTHS,
                    6 => bar::THREE_QUARTERS,
                    7 => bar::SEVEN_EIGHTHS,
                    _ => bar::FULL,
                };

                for x in 0..self.bar_width {
                    buf.get_mut(
                        chart_area.left() + i as u16 * (self.bar_width + self.bar_gap) + x,
                        chart_area.top() + j,
                    )
                    .set_symbol(symbol)
                    .set_style(self.style);
                }

                if d.1 > 8 {
                    d.1 -= 8;
                } else {
                    d.1 = 0;
                }
            }
        }

        for (i, &(label, value)) in self.data.iter().take(max_index).enumerate() {
            if value != 0 {
                let value_label = &self.values[i];
                let width = value_label.width() as u16;
                if width < self.bar_width {
                    buf.set_string(
                        chart_area.left()
                            + i as u16 * (self.bar_width + self.bar_gap)
                            + (self.bar_width - width) / 2,
                        chart_area.bottom() - 2,
                        value_label,
                        self.value_style,
                    );
                }
            }
            buf.set_stringn(
                chart_area.left() + i as u16 * (self.bar_width + self.bar_gap),
                chart_area.bottom() - 1,
                label,
                self.bar_width as usize,
                self.label_style,
            );
        }
    }
}
<|endoftext|>


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

//! A more sophisticated cache that manages user state.

use std::path::PathBuf;
use serde_json::Value;

use plugin_base;
use plugin_base::PluginRequest;

pub use plugin_base::{Error, ScopeSpan};

const CHUNK_SIZE: usize = 1024 * 1024;

/// A handler that the plugin needs to instantiate.
pub trait Handler {
    type State: Default + Clone;

    fn initialize(&mut self, ctx: PluginCtx<Self::State>, buf_size: usize);
    fn update(&mut self, ctx: PluginCtx<Self::State>);
    fn did_save(&mut self, ctx: PluginCtx<Self::State>);
    #[allow(unused_variables)]
    fn idle(&mut self, ctx: PluginCtx<Self::State>, token: usize) {}
}

struct CacheEntry<S> {
    line_num: usize,
    offset: usize,
    user_state: Option<S>,
}

/// The caching state
#[derive(Default)]
struct MyState<S> {
    /// The length of the document in bytes.
    buf_size: usize,
    view_id: String,
    rev: u64,
    cache: Option<String>,
    cache_offset: usize,

    // line iteration state
    line_num: usize,
    offset_of_line: usize,

    /// A chunk of the document.
    chunk: String,
    /// Starting offset of the chunk.
    chunk_offset: usize,

    // cache of per-line state
    // Note: this doesn't store the 0 state, that's assumed
    state_cache: Vec<CacheEntry<S>>,

    syntax: String,
    path: Option<PathBuf>,
}

pub struct PluginCtx<'a, S: 'a> {
    state: &'a mut MyState<S>,
    peer: plugin_base::PluginCtx<'a>,
}

struct MyHandler<'a, H: Handler + 'a> {
    handler: &'a mut H,
    state: MyState<H::State>,
}

impl<'a, H: Handler> plugin_base::Handler for MyHandler<'a, H> {
    fn call(&mut self, req: &PluginRequest, peer: plugin_base::PluginCtx) -> Option<Value> {
        let ctx = PluginCtx {
            state: &mut self.state,
            peer: peer,
        };
        match *req {
            PluginRequest::Ping => {
                print_err!("got ping");
                None
            }
            PluginRequest::Initialize(ref init_info) => {
                ctx.state.buf_size = init_info.buf_size;
                assert_eq!(init_info.views.len(), 1);
                ctx.state.view_id = init_info.views[0].clone();
                ctx.state.rev = init_info.rev;
                ctx.state.syntax = init_info.syntax.clone();
                ctx.state.path = init_info.path.clone().map(|p| PathBuf::from(p));
                self.handler.initialize(ctx, init_info.buf_size);
                None
            }
            PluginRequest::Update { start, end, new_len, rev, text, .. } => {
                //print_err!("got update notification {:?}", edit_type);
                ctx.state.buf_size = ctx.state.buf_size - (end - start) + new_len;
                ctx.state.rev = rev;
                if let (Some(text), Some(mut cache)) = (text, ctx.state.cache.take()) {
                    let off = ctx.state.cache_offset;
                    if start >= off && start <= off + cache.len() {
                        let tail = if end < off + cache.len() {
                            Some(cache[end - off ..].to_string())
                        } else {
                            None
                        };
                        cache.truncate(start - off);
                        cache.push_str(text);
                        if let Some(tail) = tail {
                            cache.push_str(&tail);
                        }
                        ctx.state.cache = Some(cache);
                    }
                }
                ctx.state.line_num = 0;
                ctx.state.offset_of_line = 0;
                self.handler.update(ctx);
                Some(Value::from(0i32))
            }
            PluginRequest::DidSave { ref path } => {
                let new_path = Some(path.to_owned());
                if ctx.state.path != new_path {
                    ctx.state.line_num = 0;
                    ctx.state.offset_of_line = 0;
                }
                ctx.state.path = Some(path.to_owned());
                self.handler.did_save(ctx);
                None
            }
        }
    }

    fn idle(&mut self, peer: plugin_base::PluginCtx, token: usize) {
        let ctx = PluginCtx {
            state: &mut self.state,
            peer: peer,
        };
        self.handler.idle(ctx, token);
    }
}

pub fn mainloop<H: Handler>(handler: &mut H) {
    let mut my_handler = MyHandler {
        handler: handler,
        state: MyState::default(),
    };
    plugin_base::mainloop(&mut my_handler);
}

impl<'a, S: Default + Clone> PluginCtx<'a, S> {

    pub fn get_path(&self) -> Option<&PathBuf> {
        match self.state.path {
            Some(ref p) => Some(p),
            None => None,
        }
    }

    pub fn add_scopes(&self, scopes: &Vec<Vec<String>>) {
        self.peer.add_scopes(&self.state.view_id, scopes)
    }

    pub fn update_spans(&self, start: usize, len: usize, spans: &[ScopeSpan]) {
        self.peer.update_spans(&self.state.view_id, start, len, self.state.rev, spans)
    }

    /// Determines whether an incoming request (or notification) is pending. This
    /// is intended to reduce latency for bulk operations done in the background.
    pub fn request_is_pending(&self) -> bool {
        self.peer.request_is_pending()
    }

    /// Schedule the idle handler to be run when there are no requests pending.
    pub fn schedule_idle(&mut self, token: usize) {
        self.peer.schedule_idle(token);
    }

    /// Find an entry in the cache by line num. On return `Ok(i)` means entry
    /// at index `i` is an exact match, while `Err(i)` means the entry would be
    /// inserted at `i`.
    fn find_line(&self, line_num: usize) -> Result<usize, usize> {
        self.state.state_cache.binary_search_by(|probe| probe.line_num.cmp(&line_num))
    }

    /// Get state at or before given line number.
    pub fn get_prev(&self, line_num: usize) -> (usize, usize, S) {
        if line_num > 0 {
            let mut ix = match self.find_line(line_num) {
                Ok(ix) => ix,
                Err(0) => return (0, 0, S::default()),
                Err(ix) => ix - 1,
            };
            loop {
                let item = &self.state.state_cache[ix];
                if let Some(ref s) = item.user_state {
                    return (item.line_num, item.offset, s.clone());
                }
                if ix == 0 { break; }
                ix -= 1;
            }
        }
        (0, 0, S::default())
    }

    /// Set the state at the given line number.
    pub fn set(&mut self, line_num: usize, s: S) {
        self.get_entry(line_num).user_state = Some(s);
    }

    /// Get the cache entry at the given line number, creating it if necessary.
    fn get_entry(&mut self, line_num: usize) -> &mut CacheEntry<S> {
        match self.find_line(line_num) {
            Ok(ix) => &mut self.state.state_cache[ix],
            Err(ix) => unimplemented!(),
        }
    }

    /// Insert a new entry into the cache, returning its index.
    fn insert_entry(&mut self, line_num: usize, offset: usize, user_state: Option<S>) -> usize {
        match self.find_line(line_num) {
            // TODO: evict if full
            Ok(ix) => panic!("entry already exists"),
            Err(ix) => {
                self.state.state_cache.insert(ix, CacheEntry {
                    line_num, offset, user_state
                });
                ix
            }
        }
    }

    fn fetch_chunk(&mut self, start: usize) -> Result<String, Error> {
        self.peer.get_data(&self.state.view_id, start, CHUNK_SIZE, self.state.rev)
    }

    /// Get a slice of the document, containing at least the given interval
    /// plus at least one more codepoint (unless at EOF).
    fn get_chunk(&mut self, start: usize, end: usize) -> Result<&str, Error> {
        loop {
            let chunk_start = self.state.chunk_offset;
            let chunk_end = chunk_start + self.state.chunk.len();
            if start >= chunk_start && start < chunk_end {
                // At least the first codepoint at start is in the chunk
                if end < chunk_end || end == self.state.buf_size {
                    return Ok(&self.state.chunk[start - chunk_start ..]);
                }
                let new_chunk = self.fetch_chunk(chunk_end)?;
                if start == chunk_start {
                    self.state.chunk.push_str(&new_chunk);
                } else {
                    self.state.chunk_offset = start;
                    self.state.chunk = [&self.state.chunk[start - chunk_start ..],
                        &new_chunk].concat();
                }
            } else {
                // TODO: if chunk_start < start + CHUNK_SIZE, could fetch smaller
                // chunk and concat; probably not a major savings in practice.
                self.state.chunk = self.fetch_chunk(start)?;
                self.state.chunk_offset = start;
            }
        }
    }

    fn get_offset_ix_of_line(&mut self, line_num: usize) -> Result<(usize, usize), Error> {
        if line_num == 0 {
            return Ok((0, 0));
        }
        match self.find_line(line_num) {
            Ok(ix) => Ok((self.state.state_cache[ix].offset, ix)),
            Err(ix) => {
                let (mut l, mut offset) = if ix == 0 { (0, 0) } else {
                    let item = &self.state.state_cache[ix - 1];
                    (item.line_num, item.offset)
                };
                let mut end = offset;
                loop {
                    let chunk = self.get_chunk(offset, end)?;
                    if let Some(pos) = memchr(b'\n', chunk.as_bytes()) {
                        offset += pos + 1;
                        l += 1;
                        if l == line_num {
                            return Ok((offset, ix));
                        }
                    } else {
                        end = offset + chunk.len();
                    }
                }
                unimplemented!()
            }
        }
    }

    fn get_line_len(&mut self, start: usize) -> Result<usize, Error> {
        let mut end = start;
        loop {
            let buf_size = self.state.buf_size;
            let chunk = self.get_chunk(start, end)?;
            match memchr(b'\n', chunk.as_bytes()) {
                Some(pos) => return Ok(pos + 1),
                None => {
                    end = start + chunk.len();
                    if end == buf_size {
                        return Ok(chunk.len());
                    }
                }
            }
        }
    }

    pub fn get_line(&mut self, line_num: usize) -> Result<&str, Error> {
        let (start, _ix) = self.get_offset_ix_of_line(line_num)?;
        // TODO: if cache entry at ix + 1 has line_num + 1, then we know line len
        let len = self.get_line_len(start)?;
        // TODO: this will pull in the first codepoint of the next line, which
        // is not necessary.
        let chunk = self.get_chunk(start, start + len)?;
        Ok(&chunk[start .. start + len])
    }

}

// TODO: use burntsushi memchr, or import this from xi_rope
fn memchr(needle: u8, haystack: &[u8]) -> Option<usize> {
    haystack.iter().position(|&b| b == needle)
}
<commit_msg>Continue developing state caching for plugin<commit_after>// Copyright 2016 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! A more sophisticated cache that manages user state.

use std::path::PathBuf;
use serde_json::Value;

use plugin_base;
use plugin_base::PluginRequest;

pub use plugin_base::{Error, ScopeSpan};

const CHUNK_SIZE: usize = 1024 * 1024;

/// A handler that the plugin needs to instantiate.
pub trait Handler {
    type State: Default + Clone;

    fn initialize(&mut self, ctx: PluginCtx<Self::State>, buf_size: usize);
    fn update(&mut self, ctx: PluginCtx<Self::State>);
    fn did_save(&mut self, ctx: PluginCtx<Self::State>);
    #[allow(unused_variables)]
    fn idle(&mut self, ctx: PluginCtx<Self::State>, token: usize) {}
}

struct CacheEntry<S> {
    line_num: usize,
    offset: usize,
    user_state: Option<S>,
}

/// The caching state
#[derive(Default)]
struct MyState<S> {
    /// The length of the document in bytes.
    buf_size: usize,
    view_id: String,
    rev: u64,

    /// A chunk of the document.
    chunk: String,
    /// Starting offset of the chunk.
    chunk_offset: usize,

    // cache of per-line state
    // Note: this doesn't store the 0 state, that's assumed
    state_cache: Vec<CacheEntry<S>>,

    /// The frontier, represented as a sorted list of line numbers.
    frontier: Vec<usize>,

    syntax: String,
    path: Option<PathBuf>,
}

pub struct PluginCtx<'a, S: 'a> {
    state: &'a mut MyState<S>,
    peer: plugin_base::PluginCtx<'a>,
}

struct MyHandler<'a, H: Handler + 'a> {
    handler: &'a mut H,
    state: MyState<H::State>,
}

impl<'a, H: Handler> plugin_base::Handler for MyHandler<'a, H> {
    fn call(&mut self, req: &PluginRequest, peer: plugin_base::PluginCtx) -> Option<Value> {
        let mut ctx = PluginCtx {
            state: &mut self.state,
            peer: peer,
        };
        match *req {
            PluginRequest::Ping => {
                print_err!("got ping");
                None
            }
            PluginRequest::Initialize(ref init_info) => {
                ctx.state.buf_size = init_info.buf_size;
                assert_eq!(init_info.views.len(), 1);
                ctx.state.view_id = init_info.views[0].clone();
                ctx.state.rev = init_info.rev;
                ctx.state.syntax = init_info.syntax.clone();
                ctx.state.path = init_info.path.clone().map(|p| PathBuf::from(p));
                ctx.truncate_frontier(0);
                self.handler.initialize(ctx, init_info.buf_size);
                None
            }
            PluginRequest::Update { start, end, new_len, rev, text, .. } => {
                //print_err!("got update notification {:?}", edit_type);
                ctx.state.buf_size = ctx.state.buf_size - (end - start) + new_len;
                ctx.state.rev = rev;
                if let Some(text) = text {
                    let off = ctx.state.chunk_offset;
                    if start >= off && start <= off + ctx.state.chunk.len() {
                        let tail = if end < off + ctx.state.chunk.len() {
                            Some(ctx.state.chunk[end - off ..].to_string())
                        } else {
                            None
                        };
                        ctx.state.chunk.truncate(start - off);
                        ctx.state.chunk.push_str(text);
                        if let Some(tail) = tail {
                            ctx.state.chunk.push_str(&tail);
                        }
                        // TODO: this can be a lot more precise; instead of
                        // truncating, fix up line numbers (if needed) and delete
                        // user state in interior of changed region.
                        ctx.truncate_cache(start);
                    } else {
                        ctx.state.chunk.clear();
                        ctx.state.chunk_offset = 0;
                        ctx.truncate_cache(start);
                    }
                } else {
                    ctx.state.chunk.clear();
                    ctx.state.chunk_offset = 0;
                    ctx.truncate_cache(start);
                }
                self.handler.update(ctx);
                Some(Value::from(0i32))
            }
            PluginRequest::DidSave { ref path } => {
                ctx.state.path = Some(path.to_owned());
                self.handler.did_save(ctx);
                None
            }
        }
    }

    fn idle(&mut self, peer: plugin_base::PluginCtx, token: usize) {
        let ctx = PluginCtx {
            state: &mut self.state,
            peer: peer,
        };
        self.handler.idle(ctx, token);
    }
}

pub fn mainloop<H: Handler>(handler: &mut H) {
    let mut my_handler = MyHandler {
        handler: handler,
        state: MyState::default(),
    };
    plugin_base::mainloop(&mut my_handler);
}

impl<'a, S: Default + Clone> PluginCtx<'a, S> {

    pub fn get_path(&self) -> Option<&PathBuf> {
        match self.state.path {
            Some(ref p) => Some(p),
            None => None,
        }
    }

    pub fn add_scopes(&self, scopes: &Vec<Vec<String>>) {
        self.peer.add_scopes(&self.state.view_id, scopes)
    }

    pub fn update_spans(&self, start: usize, len: usize, spans: &[ScopeSpan]) {
        self.peer.update_spans(&self.state.view_id, start, len, self.state.rev, spans)
    }

    /// Determines whether an incoming request (or notification) is pending. This
    /// is intended to reduce latency for bulk operations done in the background.
    pub fn request_is_pending(&self) -> bool {
        self.peer.request_is_pending()
    }

    /// Schedule the idle handler to be run when there are no requests pending.
    pub fn schedule_idle(&mut self, token: usize) {
        self.peer.schedule_idle(token);
    }

    /// Find an entry in the cache by line num. On return `Ok(i)` means entry
    /// at index `i` is an exact match, while `Err(i)` means the entry would be
    /// inserted at `i`.
    fn find_line(&self, line_num: usize) -> Result<usize, usize> {
        self.state.state_cache.binary_search_by(|probe| probe.line_num.cmp(&line_num))
    }

    /// Find an entry in the cache by offset. Similar to `find_line`.
    fn find_offset(&self, offset: usize) -> Result<usize, usize> {
        self.state.state_cache.binary_search_by(|probe| probe.offset.cmp(&offset))
    }

    /// Get state at or before given line number. Returns line number, offset,
    /// and user state.
    pub fn get_prev(&self, line_num: usize) -> (usize, usize, S) {
        if line_num > 0 {
            let mut ix = match self.find_line(line_num) {
                Ok(ix) => ix,
                Err(0) => return (0, 0, S::default()),
                Err(ix) => ix - 1,
            };
            loop {
                let item = &self.state.state_cache[ix];
                if let Some(ref s) = item.user_state {
                    return (item.line_num, item.offset, s.clone());
                }
                if ix == 0 { break; }
                ix -= 1;
            }
        }
        (0, 0, S::default())
    }

    /// Set the state at the given line number.
    pub fn set(&mut self, line_num: usize, s: S) {
        self.get_entry(line_num).user_state = Some(s);
    }

    /// Get the cache entry at the given line number, creating it if necessary.
    fn get_entry(&mut self, line_num: usize) -> &mut CacheEntry<S> {
        match self.find_line(line_num) {
            Ok(ix) => &mut self.state.state_cache[ix],
            Err(_ix) => {
                // TODO: could get rid of redundant binary search
                let (offset, _ix) = self.get_offset_ix_of_line(line_num).expect("TODO return result");
                let new_ix = self.insert_entry(line_num, offset, None);
                &mut self.state.state_cache[new_ix]
            }
        }
    }

    /// Insert a new entry into the cache, returning its index.
    fn insert_entry(&mut self, line_num: usize, offset: usize, user_state: Option<S>) -> usize {
        match self.find_line(line_num) {
            // TODO: evict if full
            Ok(_ix) => panic!("entry already exists"),
            Err(ix) => {
                self.state.state_cache.insert(ix, CacheEntry {
                    line_num, offset, user_state
                });
                ix
            }
        }
    }

    fn fetch_chunk(&mut self, start: usize) -> Result<String, Error> {
        self.peer.get_data(&self.state.view_id, start, CHUNK_SIZE, self.state.rev)
    }

    /// Get a slice of the document, containing at least the given interval
    /// plus at least one more codepoint (unless at EOF).
    fn get_chunk(&mut self, start: usize, end: usize) -> Result<&str, Error> {
        loop {
            let chunk_start = self.state.chunk_offset;
            let chunk_end = chunk_start + self.state.chunk.len();
            if start >= chunk_start && start < chunk_end {
                // At least the first codepoint at start is in the chunk.
                if end < chunk_end || chunk_end == self.state.buf_size {
                    return Ok(&self.state.chunk[start - chunk_start ..]);
                }
                let new_chunk = self.fetch_chunk(chunk_end)?;
                if start == chunk_start {
                    self.state.chunk.push_str(&new_chunk);
                } else {
                    self.state.chunk_offset = start;
                    self.state.chunk = [&self.state.chunk[start - chunk_start ..],
                        &new_chunk].concat();
                }
            } else {
                // TODO: if chunk_start < start + CHUNK_SIZE, could fetch smaller
                // chunk and concat; probably not a major savings in practice.
                self.state.chunk = self.fetch_chunk(start)?;
                self.state.chunk_offset = start;
            }
        }
    }

    fn get_offset_ix_of_line(&mut self, line_num: usize) -> Result<(usize, usize), Error> {
        if line_num == 0 {
            return Ok((0, 0));
        }
        match self.find_line(line_num) {
            Ok(ix) => Ok((self.state.state_cache[ix].offset, ix)),
            Err(ix) => {
                let (mut l, mut offset) = if ix == 0 { (0, 0) } else {
                    let item = &self.state.state_cache[ix - 1];
                    (item.line_num, item.offset)
                };
                let mut end = offset;
                loop {
                    let chunk = self.get_chunk(offset, end)?;
                    if let Some(pos) = memchr(b'\n', chunk.as_bytes()) {
                        offset += pos + 1;
                        l += 1;
                        if l == line_num {
                            return Ok((offset, ix));
                        }
                    } else {
                        end = offset + chunk.len();
                    }
                }
            }
        }
    }

    fn get_line_len(&mut self, start: usize) -> Result<usize, Error> {
        let mut end = start;
        loop {
            let buf_size = self.state.buf_size;
            let chunk = self.get_chunk(start, end)?;
            match memchr(b'\n', chunk.as_bytes()) {
                Some(pos) => return Ok(pos + 1),
                None => {
                    end = start + chunk.len();
                    if end == buf_size {
                        return Ok(chunk.len());
                    }
                }
            }
        }
    }

    pub fn get_line(&mut self, line_num: usize) -> Result<&str, Error> {
        let (start, _ix) = self.get_offset_ix_of_line(line_num)?;
        // TODO: if cache entry at ix + 1 has line_num + 1, then we know line len
        let len = self.get_line_len(start)?;
        // TODO: this will pull in the first codepoint of the next line, which
        // is not necessary.
        let chunk = self.get_chunk(start, start + len)?;
        Ok(&chunk[start .. start + len])
    }

    /// Release all state _after_ the given offset.
    fn truncate_cache(&mut self, offset: usize) {
        let (line_num, ix) = match self.find_offset(offset) {
            Ok(ix) => (self.state.state_cache[ix].line_num, ix + 1),
            Err(ix) => (
                if ix == 0 { 0 } else {
                    self.state.state_cache[ix - 1].line_num
                },
                ix
            ),
        };
        self.truncate_frontier(line_num);
        self.state.state_cache.truncate(ix);
    }

    fn truncate_frontier(&mut self, line_num: usize) {
        match self.state.frontier.binary_search(&line_num) {
            Ok(ix) => self.state.frontier.truncate(ix + 1),
            Err(ix) => {
                self.state.frontier.truncate(ix);
                self.state.frontier.push(line_num);
            }
        }
    }

    /// The frontier keeps track of work needing to be done. A typical
    /// user will call `get_frontier` to get a line number, do the work
    /// on that line, insert state for the next line, and then call either
    /// `update_frontier` or `close_frontier` depending on whether there
    /// is more work to be done at that location.
    pub fn get_frontier(&self) -> Option<usize> {
        self.state.frontier.first().cloned()
    }

    /// Updates the frontier. This can go backward, but most typically
    /// goes forward by 1 line (compared to the `get_frontier` result).
    pub fn update_frontier(&mut self, new_frontier: usize) {
        if self.state.frontier.get(1) == Some(&new_frontier) {
            self.state.frontier.remove(0);
        } else {
            self.state.frontier[0] = new_frontier;
        }
    }

    /// Closes the current frontier. This is the correct choice to handle
    /// EOF.
    pub fn close_frontier(&mut self) {
        self.state.frontier.remove(0);
    }
}

// TODO: use burntsushi memchr, or import this from xi_rope
fn memchr(needle: u8, haystack: &[u8]) -> Option<usize> {
    haystack.iter().position(|&b| b == needle)
}
<|endoftext|>


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

use anyhow::{anyhow, bail, Context, Result};
use ra_ap_base_db::{FileId, SourceRoot};
use ra_ap_hir as ra_hir;
use ra_ap_ide as ra_ide;
use ra_ap_paths::AbsPathBuf;
use ra_ap_project_model::{CargoConfig, ProcMacroClient, ProjectManifest, ProjectWorkspace};
use ra_ap_syntax::ast::{self, AstNode};
use ra_ap_vfs as ra_vfs;
use ra_ap_vfs_notify as vfs_notify;
use std::sync::mpsc;
use std::{collections::HashMap, convert::TryFrom, path::Path, sync::Arc};

pub(crate) struct RustAnalyzer {
    with_sysroot: bool,
    root_directory: AbsPathBuf,
    analysis_host: ra_ide::AnalysisHost,
    vfs: ra_vfs::Vfs,
    loader: vfs_notify::NotifyHandle,
    message_receiver: mpsc::Receiver<ra_vfs::loader::Message>,
    last_cargo_toml: Option<Vec<u8>>,
    source_file: AbsPathBuf,
    source_file_id: FileId,
    current_source: Arc<String>,
}

pub(crate) struct VariableInfo {
    /// The variable's type as Rust code.
    pub(crate) type_name: String,
    /// Whether the variable is declared as mutable.
    pub(crate) is_mutable: bool,
}

impl RustAnalyzer {
    pub(crate) fn new(root_directory: &Path) -> Result<RustAnalyzer> {
        use ra_vfs::loader::Handle;
        let (message_sender, message_receiver) = std::sync::mpsc::channel();
        let mut vfs = ra_vfs::Vfs::default();
        let root_directory = AbsPathBuf::try_from(root_directory.to_owned())
            .map_err(|path| anyhow!("Evcxr tmpdir is not absolute: '{:?}'", path))?;
        let source_file = root_directory.join("src/lib.rs");
        // We need to write the file to the filesystem even though we subsequently set the file
        // contents via the vfs and change.change_file. This is because the loader checks for the
        // files existence when determining the crate structure.
        let src_dir = root_directory.join("src");
        std::fs::create_dir_all(&src_dir)
            .with_context(|| format!("Failed to create directory `{:?}`", src_dir))?;
        // Pre-allocate an ID for our main source file.
        let vfs_source_file: ra_vfs::VfsPath = source_file.clone().into();
        vfs.set_file_contents(vfs_source_file.clone(), Some(vec![]));
        let source_file_id = vfs.file_id(&vfs_source_file.clone()).unwrap();
        Ok(RustAnalyzer {
            with_sysroot: true,
            root_directory,
            analysis_host: Default::default(),
            vfs,
            loader: vfs_notify::NotifyHandle::spawn(Box::new(move |message| {
                let _ = message_sender.send(message);
            })),
            message_receiver,
            last_cargo_toml: None,
            source_file,
            source_file_id,
            current_source: Arc::new(String::new()),
        })
    }

    pub(crate) fn set_source(&mut self, source: String) -> Result<()> {
        self.current_source = Arc::new(source.to_owned());
        let mut change = ra_ide::Change::new();

        std::fs::write(self.source_file.as_path(), &source)
            .with_context(|| format!("Failed to write {:?}", self.source_file))?;
        self.vfs.set_file_contents(
            self.source_file.clone().into(),
            Some(source.bytes().collect()),
        );
        change.change_file(self.source_file_id, Some(Arc::clone(&self.current_source)));

        // Check to see if we haven't yet loaded Cargo.toml, or if it's changed since we read it.
        let cargo_toml = Some(std::fs::read(self.cargo_toml_filename()).with_context(|| {
            format!(
                "Failed to read Cargo.toml from `{:?}`",
                self.cargo_toml_filename()
            )
        })?);
        if cargo_toml != self.last_cargo_toml {
            self.load_cargo_toml(&mut change)?;
            self.last_cargo_toml = cargo_toml;
        }

        self.analysis_host.apply_change(change);
        Ok(())
    }

    /// Returns top-level variable names and their types in the specified function.
    pub(crate) fn top_level_variables(&self, function_name: &str) -> HashMap<String, VariableInfo> {
        use ra_ap_syntax::ast::{ModuleItemOwner, NameOwner};
        let mut result = HashMap::new();
        let sema = ra_ide::Semantics::new(self.analysis_host.raw_database());
        let source_file = sema.parse(self.source_file_id);
        for item in source_file.items() {
            if let ast::Item::Fn(function) = item {
                if function
                    .name()
                    .map(|n| n.text() == function_name)
                    .unwrap_or(false)
                {
                    let body = if let Some(b) = function.body() {
                        b
                    } else {
                        continue;
                    };
                    let module = sema.scope(&function.syntax()).module().unwrap();
                    for statement in body.statements() {
                        if let ast::Stmt::LetStmt(let_stmt) = statement {
                            if let Some(pat) = let_stmt.pat() {
                                if let ast::Pat::IdentPat(ident_pat) = pat.clone() {
                                    if let Some(name) = ident_pat.name() {
                                        use ra_hir::HirDisplay;
                                        if let Some(ty) = sema.type_of_pat(&pat) {
                                            if let Ok(type_name) =
                                                ty.display_source_code(sema.db, module.into())
                                            {
                                                result.insert(
                                                    name.text().to_string(),
                                                    VariableInfo {
                                                        type_name,
                                                        is_mutable: ident_pat.mut_token().is_some(),
                                                    },
                                                );
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        result
    }

    fn load_cargo_toml(&mut self, change: &mut ra_ide::Change) -> Result<()> {
        let manifest = ProjectManifest::from_manifest_file(self.cargo_toml_filename())?;
        let workspace =
            ProjectWorkspace::load(manifest, &CargoConfig::default(), self.with_sysroot).unwrap();
        let load = workspace
            .to_roots()
            .iter()
            .map(|root| {
                ra_vfs::loader::Entry::Directories(ra_vfs::loader::Directories {
                    extensions: vec!["rs".to_owned()],
                    include: root.include.clone(),
                    exclude: root.exclude.clone(),
                })
            })
            .collect();
        // Note, set_config is what triggers loading and calling the callback that we registered when we created self.loader.
        use ra_vfs::loader::Handle;
        self.loader.set_config(ra_vfs::loader::Config {
            load,
            watch: vec![],
        });

        for message in &self.message_receiver {
            match message {
                ra_vfs::loader::Message::Progress { n_total, n_done } => {
                    if n_total == n_done {
                        break;
                    }
                }
                ra_vfs::loader::Message::Loaded { files } => {
                    for (path, contents) in files {
                        let vfs_path: ra_vfs::VfsPath = path.to_path_buf().into();
                        self.vfs
                            .set_file_contents(vfs_path.clone(), contents.clone());
                    }
                }
            }
        }

        for changed_file in self.vfs.take_changes() {
            let new_contents = if changed_file.exists() {
                String::from_utf8(self.vfs.file_contents(changed_file.file_id).to_owned())
                    .ok()
                    .map(|contents| Arc::new(contents))
            } else {
                None
            };
            change.change_file(changed_file.file_id, new_contents);
        }
        change.set_roots(
            ra_vfs::file_set::FileSetConfig::default()
                .partition(&self.vfs)
                .into_iter()
                .map(|file_set| SourceRoot::new_local(file_set))
                .collect(),
        );
        change.set_crate_graph(workspace.to_crate_graph(
            None,
            &ProcMacroClient::dummy(),
            &mut |path| self.vfs.file_id(&path.to_path_buf().into()),
        ));
        Ok(())
    }

    fn cargo_toml_filename(&self) -> AbsPathBuf {
        self.root_directory.join("Cargo.toml")
    }

    pub(crate) fn completions(&self, position: usize) -> Result<Completions> {
        let mut completions = Vec::new();
        let mut range = None;
        let mut config = ra_ide::CompletionConfig {
            enable_postfix_completions: true,
            add_call_parenthesis: true,
            add_call_argument_snippets: true,
            snippet_cap: None,
        };
        config.allow_snippets(true);
        if let Ok(Some(completion_items)) = self.analysis_host.analysis().completions(
            &config,
            ra_ide::FilePosition {
                file_id: self.source_file_id,
                offset: (position as u32).into(),
            },
        ) {
            for item in completion_items {
                use regex::Regex;
                lazy_static! {
                    static ref ARG_PLACEHOLDER: Regex =
                        Regex::new("\\$\\{[0-9]+:([^}]+)\\}").unwrap();
                }
                let mut indels = item.text_edit().iter();
                if let Some(indel) = indels.next() {
                    let text_to_delete = &self.current_source[indel.delete];
                    // Rust analyzer returns all available methods/fields etc. It's up to us to
                    // decide how what we filter and what we keep.
                    if !item.lookup().starts_with(text_to_delete) {
                        continue;
                    }
                    completions.push(Completion {
                        code: ARG_PLACEHOLDER
                            .replace_all(&indel.insert, "$1")
                            .replace("$0", ""),
                    });
                    if let Some(previous_range) = range.as_ref() {
                        if *previous_range != indel.delete {
                            bail!("Different completions wanted to replace different parts of the text");
                        }
                    } else {
                        range = Some(indel.delete)
                    }
                }
                if indels.next().is_some() {
                    bail!("Completion unexpectedly provided more than one insertion/deletion");
                }
            }
        }
        Ok(Completions {
            completions,
            start_offset: range.map(|range| range.start().into()).unwrap_or(position),
            end_offset: range.map(|range| range.end().into()).unwrap_or(position),
        })
    }
}

/// Completions found in a particular context.
#[derive(Default)]
pub struct Completions {
    pub completions: Vec<Completion>,
    pub start_offset: usize,
    pub end_offset: usize,
}

/// A code completion. We use our own type rather than exposing rust-analyzer's CompletionItem,
/// since rust-analyzer is an internal implementation detail, so we don't want to expose it in a
/// public API.
#[derive(Debug, Eq, PartialEq)]
pub struct Completion {
    pub code: String,
}

/// Returns whether this appears to be a valid type. Rust analyzer, when asked to emit code for some
/// types, produces invalid code. In particular, fixed sized arrays come out without a size. e.g.
/// instead of `[i32, 5]`, we get `[i32, _]`.
pub(crate) fn is_type_valid(type_name: &str) -> bool {
    if let Ok(ty) = ast::Type::parse(&type_name) {
        for node in ty.syntax().descendants() {
            if node.kind() == ra_ap_syntax::SyntaxKind::ERROR {
                return false;
            }
        }
        return true;
    }
    false
}

#[cfg(test)]
mod test {
    use super::{is_type_valid, RustAnalyzer};
    use anyhow::Result;
    use tempfile;

    #[test]
    fn get_variable_types() -> Result<()> {
        let tmpdir = tempfile::tempdir()?;
        let mut ra = RustAnalyzer::new(tmpdir.path())?;
        ra.with_sysroot = false;
        std::fs::write(
            ra.cargo_toml_filename().to_path_buf(),
            r#"
            [package]
            name = "foo"
            version = "0.1.0"

            [lib]
            "#,
        )?;

        ra.set_source(
            r#"
            fn foo() {
                let v1 = true;
                let mut v1 = 42i32;
                let v2 = &[false];
                {
                    let v2 = false;
                    let v100 = true;
                }
            }
            fn foo2() {
                let v9 = true;
            }"#
            .to_owned(),
        )?;
        let var_types = ra.top_level_variables("foo");
        assert_eq!(var_types["v1"].type_name, "i32");
        assert!(var_types["v1"].is_mutable);
        assert_eq!(var_types["v2"].type_name, "&[bool; _]");
        assert!(!var_types["v2"].is_mutable);
        assert!(var_types.get("v100").is_none());

        ra.set_source(
            r#"
            fn foo() {
                let v1 = 1u16;
            }"#
            .to_owned(),
        )?;
        let var_types = ra.top_level_variables("foo");
        assert_eq!(var_types["v1"].type_name, "u16");
        assert!(var_types.get("v2").is_none());

        Ok(())
    }

    #[test]
    fn test_is_type_valid() {
        assert!(is_type_valid("Vec<String>"));
        assert!(is_type_valid("&[i32]"));
        assert!(!is_type_valid("[i32, _]"));
    }
}
<commit_msg>Fix for tab completion with unnamed args<commit_after>// Copyright 2020 The Evcxr Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use anyhow::{anyhow, bail, Context, Result};
use ra_ap_base_db::{FileId, SourceRoot};
use ra_ap_hir as ra_hir;
use ra_ap_ide as ra_ide;
use ra_ap_paths::AbsPathBuf;
use ra_ap_project_model::{CargoConfig, ProcMacroClient, ProjectManifest, ProjectWorkspace};
use ra_ap_syntax::ast::{self, AstNode};
use ra_ap_vfs as ra_vfs;
use ra_ap_vfs_notify as vfs_notify;
use std::sync::mpsc;
use std::{collections::HashMap, convert::TryFrom, path::Path, sync::Arc};

pub(crate) struct RustAnalyzer {
    with_sysroot: bool,
    root_directory: AbsPathBuf,
    analysis_host: ra_ide::AnalysisHost,
    vfs: ra_vfs::Vfs,
    loader: vfs_notify::NotifyHandle,
    message_receiver: mpsc::Receiver<ra_vfs::loader::Message>,
    last_cargo_toml: Option<Vec<u8>>,
    source_file: AbsPathBuf,
    source_file_id: FileId,
    current_source: Arc<String>,
}

pub(crate) struct VariableInfo {
    /// The variable's type as Rust code.
    pub(crate) type_name: String,
    /// Whether the variable is declared as mutable.
    pub(crate) is_mutable: bool,
}

impl RustAnalyzer {
    pub(crate) fn new(root_directory: &Path) -> Result<RustAnalyzer> {
        use ra_vfs::loader::Handle;
        let (message_sender, message_receiver) = std::sync::mpsc::channel();
        let mut vfs = ra_vfs::Vfs::default();
        let root_directory = AbsPathBuf::try_from(root_directory.to_owned())
            .map_err(|path| anyhow!("Evcxr tmpdir is not absolute: '{:?}'", path))?;
        let source_file = root_directory.join("src/lib.rs");
        // We need to write the file to the filesystem even though we subsequently set the file
        // contents via the vfs and change.change_file. This is because the loader checks for the
        // files existence when determining the crate structure.
        let src_dir = root_directory.join("src");
        std::fs::create_dir_all(&src_dir)
            .with_context(|| format!("Failed to create directory `{:?}`", src_dir))?;
        // Pre-allocate an ID for our main source file.
        let vfs_source_file: ra_vfs::VfsPath = source_file.clone().into();
        vfs.set_file_contents(vfs_source_file.clone(), Some(vec![]));
        let source_file_id = vfs.file_id(&vfs_source_file.clone()).unwrap();
        Ok(RustAnalyzer {
            with_sysroot: true,
            root_directory,
            analysis_host: Default::default(),
            vfs,
            loader: vfs_notify::NotifyHandle::spawn(Box::new(move |message| {
                let _ = message_sender.send(message);
            })),
            message_receiver,
            last_cargo_toml: None,
            source_file,
            source_file_id,
            current_source: Arc::new(String::new()),
        })
    }

    pub(crate) fn set_source(&mut self, source: String) -> Result<()> {
        self.current_source = Arc::new(source.to_owned());
        let mut change = ra_ide::Change::new();

        std::fs::write(self.source_file.as_path(), &source)
            .with_context(|| format!("Failed to write {:?}", self.source_file))?;
        self.vfs.set_file_contents(
            self.source_file.clone().into(),
            Some(source.bytes().collect()),
        );
        change.change_file(self.source_file_id, Some(Arc::clone(&self.current_source)));

        // Check to see if we haven't yet loaded Cargo.toml, or if it's changed since we read it.
        let cargo_toml = Some(std::fs::read(self.cargo_toml_filename()).with_context(|| {
            format!(
                "Failed to read Cargo.toml from `{:?}`",
                self.cargo_toml_filename()
            )
        })?);
        if cargo_toml != self.last_cargo_toml {
            self.load_cargo_toml(&mut change)?;
            self.last_cargo_toml = cargo_toml;
        }

        self.analysis_host.apply_change(change);
        Ok(())
    }

    /// Returns top-level variable names and their types in the specified function.
    pub(crate) fn top_level_variables(&self, function_name: &str) -> HashMap<String, VariableInfo> {
        use ra_ap_syntax::ast::{ModuleItemOwner, NameOwner};
        let mut result = HashMap::new();
        let sema = ra_ide::Semantics::new(self.analysis_host.raw_database());
        let source_file = sema.parse(self.source_file_id);
        for item in source_file.items() {
            if let ast::Item::Fn(function) = item {
                if function
                    .name()
                    .map(|n| n.text() == function_name)
                    .unwrap_or(false)
                {
                    let body = if let Some(b) = function.body() {
                        b
                    } else {
                        continue;
                    };
                    let module = sema.scope(&function.syntax()).module().unwrap();
                    for statement in body.statements() {
                        if let ast::Stmt::LetStmt(let_stmt) = statement {
                            if let Some(pat) = let_stmt.pat() {
                                if let ast::Pat::IdentPat(ident_pat) = pat.clone() {
                                    if let Some(name) = ident_pat.name() {
                                        use ra_hir::HirDisplay;
                                        if let Some(ty) = sema.type_of_pat(&pat) {
                                            if let Ok(type_name) =
                                                ty.display_source_code(sema.db, module.into())
                                            {
                                                result.insert(
                                                    name.text().to_string(),
                                                    VariableInfo {
                                                        type_name,
                                                        is_mutable: ident_pat.mut_token().is_some(),
                                                    },
                                                );
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        result
    }

    fn load_cargo_toml(&mut self, change: &mut ra_ide::Change) -> Result<()> {
        let manifest = ProjectManifest::from_manifest_file(self.cargo_toml_filename())?;
        let workspace =
            ProjectWorkspace::load(manifest, &CargoConfig::default(), self.with_sysroot).unwrap();
        let load = workspace
            .to_roots()
            .iter()
            .map(|root| {
                ra_vfs::loader::Entry::Directories(ra_vfs::loader::Directories {
                    extensions: vec!["rs".to_owned()],
                    include: root.include.clone(),
                    exclude: root.exclude.clone(),
                })
            })
            .collect();
        // Note, set_config is what triggers loading and calling the callback that we registered when we created self.loader.
        use ra_vfs::loader::Handle;
        self.loader.set_config(ra_vfs::loader::Config {
            load,
            watch: vec![],
        });

        for message in &self.message_receiver {
            match message {
                ra_vfs::loader::Message::Progress { n_total, n_done } => {
                    if n_total == n_done {
                        break;
                    }
                }
                ra_vfs::loader::Message::Loaded { files } => {
                    for (path, contents) in files {
                        let vfs_path: ra_vfs::VfsPath = path.to_path_buf().into();
                        self.vfs
                            .set_file_contents(vfs_path.clone(), contents.clone());
                    }
                }
            }
        }

        for changed_file in self.vfs.take_changes() {
            let new_contents = if changed_file.exists() {
                String::from_utf8(self.vfs.file_contents(changed_file.file_id).to_owned())
                    .ok()
                    .map(|contents| Arc::new(contents))
            } else {
                None
            };
            change.change_file(changed_file.file_id, new_contents);
        }
        change.set_roots(
            ra_vfs::file_set::FileSetConfig::default()
                .partition(&self.vfs)
                .into_iter()
                .map(|file_set| SourceRoot::new_local(file_set))
                .collect(),
        );
        change.set_crate_graph(workspace.to_crate_graph(
            None,
            &ProcMacroClient::dummy(),
            &mut |path| self.vfs.file_id(&path.to_path_buf().into()),
        ));
        Ok(())
    }

    fn cargo_toml_filename(&self) -> AbsPathBuf {
        self.root_directory.join("Cargo.toml")
    }

    pub(crate) fn completions(&self, position: usize) -> Result<Completions> {
        let mut completions = Vec::new();
        let mut range = None;
        let mut config = ra_ide::CompletionConfig {
            enable_postfix_completions: true,
            add_call_parenthesis: true,
            add_call_argument_snippets: true,
            snippet_cap: None,
        };
        config.allow_snippets(true);
        if let Ok(Some(completion_items)) = self.analysis_host.analysis().completions(
            &config,
            ra_ide::FilePosition {
                file_id: self.source_file_id,
                offset: (position as u32).into(),
            },
        ) {
            for item in completion_items {
                use regex::Regex;
                lazy_static! {
                    static ref ARG_PLACEHOLDER: Regex =
                        Regex::new("\\$\\{[0-9]+:([^}]*)\\}").unwrap();
                }
                let mut indels = item.text_edit().iter();
                if let Some(indel) = indels.next() {
                    let text_to_delete = &self.current_source[indel.delete];
                    // Rust analyzer returns all available methods/fields etc. It's up to us to
                    // decide how what we filter and what we keep.
                    if !item.lookup().starts_with(text_to_delete) {
                        continue;
                    }
                    completions.push(Completion {
                        code: ARG_PLACEHOLDER
                            .replace_all(&indel.insert, "$1")
                            .replace("$0", ""),
                    });
                    if let Some(previous_range) = range.as_ref() {
                        if *previous_range != indel.delete {
                            bail!("Different completions wanted to replace different parts of the text");
                        }
                    } else {
                        range = Some(indel.delete)
                    }
                }
                if indels.next().is_some() {
                    bail!("Completion unexpectedly provided more than one insertion/deletion");
                }
            }
        }
        Ok(Completions {
            completions,
            start_offset: range.map(|range| range.start().into()).unwrap_or(position),
            end_offset: range.map(|range| range.end().into()).unwrap_or(position),
        })
    }
}

/// Completions found in a particular context.
#[derive(Default)]
pub struct Completions {
    pub completions: Vec<Completion>,
    pub start_offset: usize,
    pub end_offset: usize,
}

/// A code completion. We use our own type rather than exposing rust-analyzer's CompletionItem,
/// since rust-analyzer is an internal implementation detail, so we don't want to expose it in a
/// public API.
#[derive(Debug, Eq, PartialEq)]
pub struct Completion {
    pub code: String,
}

/// Returns whether this appears to be a valid type. Rust analyzer, when asked to emit code for some
/// types, produces invalid code. In particular, fixed sized arrays come out without a size. e.g.
/// instead of `[i32, 5]`, we get `[i32, _]`.
pub(crate) fn is_type_valid(type_name: &str) -> bool {
    if let Ok(ty) = ast::Type::parse(&type_name) {
        for node in ty.syntax().descendants() {
            if node.kind() == ra_ap_syntax::SyntaxKind::ERROR {
                return false;
            }
        }
        return true;
    }
    false
}

#[cfg(test)]
mod test {
    use super::{is_type_valid, RustAnalyzer};
    use anyhow::Result;
    use tempfile;

    #[test]
    fn get_variable_types() -> Result<()> {
        let tmpdir = tempfile::tempdir()?;
        let mut ra = RustAnalyzer::new(tmpdir.path())?;
        ra.with_sysroot = false;
        std::fs::write(
            ra.cargo_toml_filename().to_path_buf(),
            r#"
            [package]
            name = "foo"
            version = "0.1.0"

            [lib]
            "#,
        )?;

        ra.set_source(
            r#"
            fn foo() {
                let v1 = true;
                let mut v1 = 42i32;
                let v2 = &[false];
                {
                    let v2 = false;
                    let v100 = true;
                }
            }
            fn foo2() {
                let v9 = true;
            }"#
            .to_owned(),
        )?;
        let var_types = ra.top_level_variables("foo");
        assert_eq!(var_types["v1"].type_name, "i32");
        assert!(var_types["v1"].is_mutable);
        assert_eq!(var_types["v2"].type_name, "&[bool; _]");
        assert!(!var_types["v2"].is_mutable);
        assert!(var_types.get("v100").is_none());

        ra.set_source(
            r#"
            fn foo() {
                let v1 = 1u16;
            }"#
            .to_owned(),
        )?;
        let var_types = ra.top_level_variables("foo");
        assert_eq!(var_types["v1"].type_name, "u16");
        assert!(var_types.get("v2").is_none());

        Ok(())
    }

    #[test]
    fn test_is_type_valid() {
        assert!(is_type_valid("Vec<String>"));
        assert!(is_type_valid("&[i32]"));
        assert!(!is_type_valid("[i32, _]"));
    }
}
<|endoftext|>


<commit_before>
use std::num::Float;
use sdl::video::{Surface};
use sdl::event::{Key};

use world::{World, RayCastResult};
use math::{LineSeg, Vec2, Mat3};
use input::{InputState};

const SPEED: f32 = 0.4;
const TURN: f32 = 0.03;

const FOV_DIV: f32 = 600.0;
const VISPLANE_DIST: f32 = 600.0;
const WALL_HEIGHT_: f32 = 10.0;
const PERSON_HEIGHT_: f32 = 5.0;

pub struct Game<'a> {
    pub pos: Vec2,
    pub face_angle: f32,
    pub world: &'a World,
    pub show_map: bool,
    pub t: f32
}

impl<'a> Game<'a> {
    pub fn step(&mut self, input: &InputState) {
        if input.has_key(Key::Left)  { self.face_angle -= TURN; }
        if input.has_key(Key::Right) { self.face_angle += TURN; }

        if input.has_key(Key::W) { self.do_move( 1.0,  0.0); }
        if input.has_key(Key::S) { self.do_move(-1.0,  0.0); }
        if input.has_key(Key::A) { self.do_move( 0.0, -1.0); }
        if input.has_key(Key::D) { self.do_move( 0.0,  1.0); }

        self.show_map = input.has_key(Key::Tab);
        self.t += 0.02;
    }

    fn do_move(&mut self, para: f32, perp: f32) {
        let sin = SPEED*Float::sin(self.face_angle);
        let cos = SPEED*Float::cos(self.face_angle);
        self.pos.x +=  sin*para + cos*perp;
        self.pos.y += -cos*para + sin*perp;
    }

    pub fn render(&self, surf: &Surface) {
        if self.show_map {
            self.render_map(&surf);
        } else {
            self.render_game(&surf);
        }
    }

    fn render_map(&self, surf: &Surface) {
        let trans = Mat3::rotation(-self.face_angle)
                  * Mat3::translation(-self.pos)
                  * Mat3::scale(Vec2::new(2.0, 2.0));

        surf.clear();
        for wall in self.world.get_walls().iter() {
            draw_seg(&surf, wall.transform(trans), 0xFF, 0x00, 0x00);
        }

        let w = surf.get_width() as usize;
        for x in 0..w {
            let offset = ((x as f32) - (w as f32) / 2.0) / FOV_DIV;

            self.world.cast_ray(self.pos, self.face_angle + offset).map(|RayCastResult {pos, ..}| {
                let draw_sg = LineSeg::new(self.pos.x, self.pos.y, pos.x, pos.y);
                draw_seg(&surf, draw_sg.transform(trans), 0x00, 0xFF, 0x00);
            });
        }
    }

    fn render_game(&self, surf: &Surface) {
        let w = surf.get_width() as usize;
        let h = surf.get_height() as usize;

        let WALL_HEIGHT = WALL_HEIGHT_ + self.t;
        let PERSON_HEIGHT = PERSON_HEIGHT_ + Float::abs(Float::sin(self.t * 3.0)) * 5.0;

        surf.with_lock(|pixels| {
            for x in 0..w {
                let offset = ((x as f32) - (w as f32) / 2.0) / FOV_DIV;

                let cast = self.world.cast_ray(self.pos, self.face_angle + offset);
                if !cast.is_some() { continue; }
                let RayCastResult {dist, pos: hit_pos, along} = cast.unwrap();

                let cast_dist = dist * Float::cos(offset);

                let pxheight = (VISPLANE_DIST * WALL_HEIGHT / cast_dist) as isize;

                let bottompx = h as isize/2 + (VISPLANE_DIST * PERSON_HEIGHT / cast_dist) as isize;
                let toppx = bottompx - pxheight;

                let top = if toppx < h as isize && toppx >= 0 { toppx as usize } else { 0 };
                let bottom = if bottompx < h as isize && bottompx >= 0 { bottompx as usize } else { h };

                let brightness = ((0xFF - top) as f32) / 255.0;
                for y in top..bottom {
                    //let yy = (y as f32 - (h as f32 - px_height as f32) / 2.0) / px_height as f32;
                    let tex_lookup = (along * 25.0) as u8 ^ 42; // (255.0*yy) as u8;
                    let color = ((tex_lookup as f32) * brightness) as u8;
                    put_px(pixels, w, x, y, color / 2, color / 2, color);
                }

                for y in bottom..h {
                    let dist_floor = VISPLANE_DIST * PERSON_HEIGHT / ((y as f32) - (h as f32)/2.0);
                    let brightness = ((0xFF - (h-y)) as f32) / 255.0;
                    let floor_pos = self.pos + (hit_pos - self.pos) * dist_floor / cast_dist;
                    let tex_lookup = ((floor_pos.x * 10.0) as u8) ^ ((floor_pos.y * 10.0) as u8);
                    let color = ((tex_lookup as f32) * brightness) as u8;
                    put_px(pixels, w, x, y, 0x00, color, 0x00);
                }
            }
            true
        });
    }
}

fn put_px(pixels: &mut [u8], w: usize, x: usize, y: usize, r: u8, g: u8, b: u8) {
    pixels[3*(w*y+x) + 0] = b;
    pixels[3*(w*y+x) + 1] = g;
    pixels[3*(w*y+x) + 2] = r;
}

fn draw_seg(surf: &Surface, seg: LineSeg, r: u8, g: u8, b: u8) {
    let w = surf.get_width() as usize;
    let h = surf.get_height() as usize;
    let len = seg.get_length();

    surf.with_lock(|pixels| {
        for t in 0..(len as usize) {
            let pt = seg.at(t as f32 / len);
            let ux = (pt.x + (w/2) as f32) as usize;
            let uy = (pt.y + (h/2) as f32) as usize;

            if ux < w && uy < h {
                put_px(pixels, w, ux, uy, r, g, b);
            }
        };
        true
    });
}
<commit_msg>Fixed texture look up and brightness on variable height walls<commit_after>
use std::num::Float;
use sdl::video::{Surface};
use sdl::event::{Key};

use world::{World, RayCastResult};
use math::{LineSeg, Vec2, Mat3};
use input::{InputState};

const SPEED: f32 = 0.4;
const TURN: f32 = 0.03;

const FOV_DIV: f32 = 600.0;
const VISPLANE_DIST: f32 = 600.0;
const BASE_WALL_HEIGHT: f32 = 10.0;
const PERSON_HEIGHT: f32 = 5.0;

pub struct Game<'a> {
    pub pos: Vec2,
    pub face_angle: f32,
    pub world: &'a World,
    pub show_map: bool,
    pub t: f32
}

impl<'a> Game<'a> {
    pub fn step(&mut self, input: &InputState) {
        if input.has_key(Key::Left)  { self.face_angle -= TURN; }
        if input.has_key(Key::Right) { self.face_angle += TURN; }

        if input.has_key(Key::W) { self.do_move( 1.0,  0.0); }
        if input.has_key(Key::S) { self.do_move(-1.0,  0.0); }
        if input.has_key(Key::A) { self.do_move( 0.0, -1.0); }
        if input.has_key(Key::D) { self.do_move( 0.0,  1.0); }

        self.show_map = input.has_key(Key::Tab);
        self.t += 0.02;
    }

    fn do_move(&mut self, para: f32, perp: f32) {
        let sin = SPEED*Float::sin(self.face_angle);
        let cos = SPEED*Float::cos(self.face_angle);
        self.pos.x +=  sin*para + cos*perp;
        self.pos.y += -cos*para + sin*perp;
    }

    pub fn render(&self, surf: &Surface) {
        if self.show_map {
            self.render_map(&surf);
        } else {
            self.render_game(&surf);
        }
    }

    fn render_map(&self, surf: &Surface) {
        let trans = Mat3::rotation(-self.face_angle)
                  * Mat3::translation(-self.pos)
                  * Mat3::scale(Vec2::new(2.0, 2.0));

        surf.clear();
        for wall in self.world.get_walls().iter() {
            draw_seg(&surf, wall.transform(trans), 0xFF, 0x00, 0x00);
        }

        let w = surf.get_width() as usize;
        for x in 0..w {
            let offset = ((x as f32) - (w as f32) / 2.0) / FOV_DIV;

            self.world.cast_ray(self.pos, self.face_angle + offset).map(|RayCastResult {pos, ..}| {
                let draw_sg = LineSeg::new(self.pos.x, self.pos.y, pos.x, pos.y);
                draw_seg(&surf, draw_sg.transform(trans), 0x00, 0xFF, 0x00);
            });
        }
    }

    fn render_game(&self, surf: &Surface) {
        let w = surf.get_width() as usize;
        let h = surf.get_height() as usize;

        let wall_height = BASE_WALL_HEIGHT + self.t;
        let person_height = PERSON_HEIGHT + Float::abs(Float::sin(self.t * 3.0)) * 5.0;

        surf.with_lock(|pixels| {
            for x in 0..w {
                let offset = ((x as f32) - (w as f32) / 2.0) / FOV_DIV;

                let cast = self.world.cast_ray(self.pos, self.face_angle + offset);
                if !cast.is_some() { continue; }
                let RayCastResult {dist, pos: hit_pos, along} = cast.unwrap();

                let cast_dist = dist * Float::cos(offset);

                let pxheight = (VISPLANE_DIST * wall_height / cast_dist) as isize;
                let bottompx = h as isize/2 + (VISPLANE_DIST * person_height / cast_dist) as isize;
                let toppx = bottompx - pxheight;
                let top = if toppx < h as isize && toppx >= 0 { toppx as usize } else { 0 };
                let bottom = if bottompx < h as isize && bottompx >= 0 { bottompx as usize } else { h };

                let brightness = (20.0 / cast_dist).min(1.0).max(0.0);
                for y in top..bottom {
                    let yy = (bottompx as usize - y) as f32 / pxheight as f32 * wall_height / 15.0;
                    let tex_lookup = (along * 25.0) as u8 ^ (512.0*yy) as u8;
                    let color = ((tex_lookup as f32) * brightness) as u8;
                    put_px(pixels, w, x, y, color / 2, color / 2, color);
                }

                for y in bottom..h {
                    let dist_floor = VISPLANE_DIST * person_height / ((y as f32) - (h as f32)/2.0);
                    let brightness = (20.0 / dist_floor).min(1.0).max(0.0);
                    let floor_pos = self.pos + (hit_pos - self.pos) * dist_floor / cast_dist;
                    let tex_lookup = ((floor_pos.x * 10.0) as u8) ^ ((floor_pos.y * 10.0) as u8);
                    let color = ((tex_lookup as f32) * brightness) as u8;
                    put_px(pixels, w, x, y, 0x00, color, 0x00);
                }
            }
            true
        });
    }
}

fn put_px(pixels: &mut [u8], w: usize, x: usize, y: usize, r: u8, g: u8, b: u8) {
    pixels[3*(w*y+x) + 0] = b;
    pixels[3*(w*y+x) + 1] = g;
    pixels[3*(w*y+x) + 2] = r;
}

fn draw_seg(surf: &Surface, seg: LineSeg, r: u8, g: u8, b: u8) {
    let w = surf.get_width() as usize;
    let h = surf.get_height() as usize;
    let len = seg.get_length();

    surf.with_lock(|pixels| {
        for t in 0..(len as usize) {
            let pt = seg.at(t as f32 / len);
            let ux = (pt.x + (w/2) as f32) as usize;
            let uy = (pt.y + (h/2) as f32) as usize;

            if ux < w && uy < h {
                put_px(pixels, w, ux, uy, r, g, b);
            }
        };
        true
    });
}
<|endoftext|>