<commit_before>use std::ffi::OsString;
use std::fs;
use std::fs::Permissions;
use std::io;
use std::io::Stderr;
use std::io::Stdin;
use std::io::Stdout;
use std::io::Write;
use std::os::unix::fs::PermissionsExt;
use std::path::PathBuf;
use structopt::StructOpt;
use super::Error;
use super::Result;

enum Context {
    /// The path is absolute (has root).
    Absolute,
    /// The `interactive` option is present.
    Interactive,
}

#[derive(Debug, StructOpt)]
#[structopt(about = "Recursively change the mode of files or directories.")]
struct Options {
    /// Change the mode of files.
    #[structopt(short = "f", long = "file")]
    file: Option<String>,

    /// Change the mode of directories.
    #[structopt(short = "d", long = "dir")]
    dir: Option<String>,

    /// Do not overwrite any files (verbose).
    #[structopt(short = "D", long = "dry-run")]
    dry_run: bool,

    /// Prompt before overwriting each file.
    #[structopt(short = "i", long = "interactive")]
    interactive: bool,

    /// Suppress all interaction.
    #[structopt(short = "s", long = "suppress")]
    suppress: bool,

    /// Explain what's being done.
    #[structopt(short = "V", long = "verbose")]
    verbose: bool,

    /// Show this message.
    #[structopt(short = "h", long = "help")]
    help: bool,

    /// Show the version.
    #[structopt(short = "v", long = "version")]
    version: bool,

    /// The paths to be modified by this tool.
    #[structopt(name = "PATHS", parse(from_str))]
    paths: Vec<PathBuf>,
}

pub struct Chmodrt {
    options: Options,
    stderr: Stderr,
    stdout: Stdout,
    stdin: Stdin,
}

impl Chmodrt {
    /// Constructs this program from an iterable of arguments.
    pub fn from_iter<I>(iter: I) -> Result<Self>
    where
        Self: Sized,
        I: IntoIterator,
        I::Item: Into<OsString> + Clone,
    {
        return Ok(
            Self {
                options: Options::from_iter_safe(iter)?,
                stderr: io::stderr(),
                stdout: io::stdout(),
                stdin: io::stdin(),
            }
        );
    }

    /// Replaces the standard error stream for this program.
    pub fn stderr(&mut self, stderr: Stderr) -> &mut Self {
        self.stderr = stderr;
        return self;
    }

    /// Replaces the standard output stream for this program.
    pub fn stdout(&mut self, stdout: Stdout) -> &mut Self {
        self.stdout = stdout;
        return self;
    }

    /// Replaces the standard input stream for this program.
    pub fn stdin(&mut self, stdin: Stdin) -> &mut Self {
        self.stdin = stdin;
        return self;
    }

    /// Runs this program and writes all errors.
    pub fn run(&mut self) -> Result<()> {
        match self.run_inner() {
            Ok(val) => {
                return Ok(val);
            },
            Err(err) => {
                writeln!(self.stderr, "Error: {}", err)?;
                return Err(err);
            },
        }
    }

    /// Runs this program.
    fn run_inner(&mut self) -> Result<()> {
        // Write the help or version message
        if self.options.help {
            return self.help();
        }
        if self.options.version {
            return self.version();
        }

        // Validate the options
        self.validate()?;

        // Handle the paths
        return self.change();
    }

    /// Validates the options.
    fn validate(&self) -> Result<()> {
        return if {
            self.options.interactive && self.options.suppress ||
            self.has_file() && self.has_dir()
        } {
            Err(Error::Conflict)
        } else if {
            !self.has_file() &&
            !self.has_dir()
        } {
            Err(Error::Missing)
        } else {
            Ok(())
        };
    }

    /// Writes the help message to the standard error stream.
    fn help(&mut self) -> Result<()> {
        Options::clap().write_help(&mut self.stderr)?;
        writeln!(self.stderr, "")?;
        return Ok(());
    }

    /// Writes the version message to the standard error stream.
    fn version(&mut self) -> Result<()> {
        Options::clap().write_version(&mut self.stderr)?;
        writeln!(self.stderr, "")?;
        return Ok(());
    }

    /// Authorizes directory and file access by prompting the user and reading
    /// from the standard input stream.
    fn auth(&mut self, path: &PathBuf, context: Context) -> Result<bool> {
        // Determine the appropriate prompt
        let prompt = match context {
            Context::Absolute => "is absolute",
            Context::Interactive => "mode will be changed",
        };

        let mut input = String::new();
        loop {
            // Prompt the user and normalize the input
            write!(self.stderr, r#""{}" {} - continue? [y/n] "#, path.display(), prompt)?;
            self.stdin.read_line(&mut input)?;

            // The response must be `y` or `n`
            match input.trim().to_lowercase().as_str() {
                "n" => {
                    if self.options.verbose {
                        writeln!(self.stderr, "Skipped.")?;
                    }
                    return Ok(false);
                },
                "y" => {
                    return Ok(true);
                },
                _ => {
                    input.clear();
                },
            }
        }
    }

    /// Changes all paths provided by the user. Authorization may be requested
    /// if the `suppress` option is not present.
    fn change(&mut self) -> Result<()> {
        let mode = u32::from_str_radix(
            if self.has_file() {
                self.options.file.as_ref().unwrap()
            } else if self.has_dir() {
                self.options.dir.as_ref().unwrap()
            } else {
                ""
            },
            8,
        )?;
        for path in self.options.paths.to_owned() {
            if !self.options.suppress && path.has_root() {
                // Authorize absolute paths (optional)
                if let Ok(false) = self.auth(&path, Context::Absolute) {
                    continue;
                }
            }

            if path.is_file() {
                // The path is a file
                if self.has_file() {
                    self.change_one(&path, mode)?;
                }
            } else {
                // Try the path as a directory
                self.change_many(&path, mode)?;
            }
        }
        return Ok(());
    }

    /// Changes all entries under the given directory and writes all errors.
    fn change_many(&mut self, path: &PathBuf, mode: u32) -> Result<()> {
        return if let Err(err) = self.change_many_inner(path, mode) {
            self.write_error("Cannot access", path, &err)
        } else {
            Ok(())
        };
    }

    /// Changes all entries under the given directory.
    fn change_many_inner(&mut self, path: &PathBuf, mode: u32) -> Result<()> {
        if self.has_dir() {
            self.change_one(path, mode)?;
        }
        for entry in path.read_dir()? {
            let path = entry?.path();

            // Recurse if the entry is a directory
            if path.is_file() {
                if self.has_file() {
                    self.change_one(&path, mode)?;
                }
            } else {
                self.change_many(&path, mode)?;
            }
        }
        return Ok(());
    }

    /// Changes the mode of the given path and writes all errors.
    fn change_one(&mut self, path: &PathBuf, mode: u32) -> Result<()> {
        return if let Err(err) = self.change_one_inner(path, mode) {
            self.write_error("Cannot change permissions", path, &err)
        } else {
            Ok(())
        };
    }

    /// Changes the mode of the given path. Authorization may be requested and
    /// additional information may be written if the `interactive` and
    /// `verbose` options are present. The path will not be changed during a
    /// `dry-run`.
    fn change_one_inner(&mut self, path: &PathBuf, mode: u32) -> Result<()> {
        if self.options.interactive && !self.options.suppress {
            // Authorize every path (optional)
            if let Ok(false) = self.auth(path, Context::Interactive) {
                return Ok(());
            }
        }

        if !self.options.dry_run {
            // Change the mode of the path
            fs::set_permissions(path, Permissions::from_mode(mode))?;

            if self.options.verbose {
                // Write the results (optional)
                self.write_result("changed", path)?;
            }
        } else {
            // Perform a dry run (optional)
            self.write_result("will be changed", path)?;
        }
        return Ok(());
    }

    /// Determines if the file option is present.
    fn has_file(&self) -> bool {
        return self.options.file.is_some();
    }

    /// Determines if the directory option is present.
    fn has_dir(&self) -> bool {
        return self.options.dir.is_some();
    }

    /// Writes a path related error to the standard error stream.
    fn write_error(&mut self, msg: &str, path: &PathBuf, err: &Error) -> Result<()> {
        writeln!(self.stderr, r#"Error: {} "{}": {}"#, msg, path.display(), err)?;
        return Ok(());
    }

    /// Writes the result of an operation to the standard output stream.
    fn write_result(&mut self, msg: &str, path: &PathBuf) -> Result<()> {
        writeln!(self.stdout, r#""{}": mode {}."#, path.display(), msg)?;
        return Ok(());
    }
}
<commit_msg>Changed help message<commit_after>use std::ffi::OsString;
use std::fs;
use std::fs::Permissions;
use std::io;
use std::io::Stderr;
use std::io::Stdin;
use std::io::Stdout;
use std::io::Write;
use std::os::unix::fs::PermissionsExt;
use std::path::PathBuf;
use structopt::StructOpt;
use super::Error;
use super::Result;

enum Context {
    /// The path is absolute (has root).
    Absolute,
    /// The `interactive` option is present.
    Interactive,
}

#[derive(Debug, StructOpt)]
#[structopt(about = "Recursively change the mode of files or directories.")]
struct Options {
    /// The mode to use for files.
    #[structopt(short = "f", long = "file")]
    file: Option<String>,

    /// The mode to use for directories.
    #[structopt(short = "d", long = "dir")]
    dir: Option<String>,

    /// Do not overwrite any files (verbose).
    #[structopt(short = "D", long = "dry-run")]
    dry_run: bool,

    /// Prompt before overwriting each file.
    #[structopt(short = "i", long = "interactive")]
    interactive: bool,

    /// Suppress all interaction.
    #[structopt(short = "s", long = "suppress")]
    suppress: bool,

    /// Explain what's being done.
    #[structopt(short = "V", long = "verbose")]
    verbose: bool,

    /// Show this message.
    #[structopt(short = "h", long = "help")]
    help: bool,

    /// Show the version.
    #[structopt(short = "v", long = "version")]
    version: bool,

    /// The paths to be modified by this tool.
    #[structopt(name = "PATHS", parse(from_str))]
    paths: Vec<PathBuf>,
}

pub struct Chmodrt {
    options: Options,
    stderr: Stderr,
    stdout: Stdout,
    stdin: Stdin,
}

impl Chmodrt {
    /// Constructs this program from an iterable of arguments.
    pub fn from_iter<I>(iter: I) -> Result<Self>
    where
        Self: Sized,
        I: IntoIterator,
        I::Item: Into<OsString> + Clone,
    {
        return Ok(
            Self {
                options: Options::from_iter_safe(iter)?,
                stderr: io::stderr(),
                stdout: io::stdout(),
                stdin: io::stdin(),
            }
        );
    }

    /// Replaces the standard error stream for this program.
    pub fn stderr(&mut self, stderr: Stderr) -> &mut Self {
        self.stderr = stderr;
        return self;
    }

    /// Replaces the standard output stream for this program.
    pub fn stdout(&mut self, stdout: Stdout) -> &mut Self {
        self.stdout = stdout;
        return self;
    }

    /// Replaces the standard input stream for this program.
    pub fn stdin(&mut self, stdin: Stdin) -> &mut Self {
        self.stdin = stdin;
        return self;
    }

    /// Runs this program and writes all errors.
    pub fn run(&mut self) -> Result<()> {
        match self.run_inner() {
            Ok(val) => {
                return Ok(val);
            },
            Err(err) => {
                writeln!(self.stderr, "Error: {}", err)?;
                return Err(err);
            },
        }
    }

    /// Runs this program.
    fn run_inner(&mut self) -> Result<()> {
        // Write the help or version message
        if self.options.help {
            return self.help();
        }
        if self.options.version {
            return self.version();
        }

        // Validate the options
        self.validate()?;

        // Handle the paths
        return self.change();
    }

    /// Validates the options.
    fn validate(&self) -> Result<()> {
        return if {
            self.options.interactive && self.options.suppress ||
            self.has_file() && self.has_dir()
        } {
            Err(Error::Conflict)
        } else if {
            !self.has_file() &&
            !self.has_dir()
        } {
            Err(Error::Missing)
        } else {
            Ok(())
        };
    }

    /// Writes the help message to the standard error stream.
    fn help(&mut self) -> Result<()> {
        Options::clap().write_help(&mut self.stderr)?;
        writeln!(self.stderr, "")?;
        return Ok(());
    }

    /// Writes the version message to the standard error stream.
    fn version(&mut self) -> Result<()> {
        Options::clap().write_version(&mut self.stderr)?;
        writeln!(self.stderr, "")?;
        return Ok(());
    }

    /// Authorizes directory and file access by prompting the user and reading
    /// from the standard input stream.
    fn auth(&mut self, path: &PathBuf, context: Context) -> Result<bool> {
        // Determine the appropriate prompt
        let prompt = match context {
            Context::Absolute => "is absolute",
            Context::Interactive => "mode will be changed",
        };

        let mut input = String::new();
        loop {
            // Prompt the user and normalize the input
            write!(self.stderr, r#""{}" {} - continue? [y/n] "#, path.display(), prompt)?;
            self.stdin.read_line(&mut input)?;

            // The response must be `y` or `n`
            match input.trim().to_lowercase().as_str() {
                "n" => {
                    if self.options.verbose {
                        writeln!(self.stderr, "Skipped.")?;
                    }
                    return Ok(false);
                },
                "y" => {
                    return Ok(true);
                },
                _ => {
                    input.clear();
                },
            }
        }
    }

    /// Changes all paths provided by the user. Authorization may be requested
    /// if the `suppress` option is not present.
    fn change(&mut self) -> Result<()> {
        let mode = u32::from_str_radix(
            if self.has_file() {
                self.options.file.as_ref().unwrap()
            } else if self.has_dir() {
                self.options.dir.as_ref().unwrap()
            } else {
                ""
            },
            8,
        )?;
        for path in self.options.paths.to_owned() {
            if !self.options.suppress && path.has_root() {
                // Authorize absolute paths (optional)
                if let Ok(false) = self.auth(&path, Context::Absolute) {
                    continue;
                }
            }

            if path.is_file() {
                // The path is a file
                if self.has_file() {
                    self.change_one(&path, mode)?;
                }
            } else {
                // Try the path as a directory
                self.change_many(&path, mode)?;
            }
        }
        return Ok(());
    }

    /// Changes all entries under the given directory and writes all errors.
    fn change_many(&mut self, path: &PathBuf, mode: u32) -> Result<()> {
        return if let Err(err) = self.change_many_inner(path, mode) {
            self.write_error("Cannot access", path, &err)
        } else {
            Ok(())
        };
    }

    /// Changes all entries under the given directory.
    fn change_many_inner(&mut self, path: &PathBuf, mode: u32) -> Result<()> {
        if self.has_dir() {
            self.change_one(path, mode)?;
        }
        for entry in path.read_dir()? {
            let path = entry?.path();

            // Recurse if the entry is a directory
            if path.is_file() {
                if self.has_file() {
                    self.change_one(&path, mode)?;
                }
            } else {
                self.change_many(&path, mode)?;
            }
        }
        return Ok(());
    }

    /// Changes the mode of the given path and writes all errors.
    fn change_one(&mut self, path: &PathBuf, mode: u32) -> Result<()> {
        return if let Err(err) = self.change_one_inner(path, mode) {
            self.write_error("Cannot change permissions", path, &err)
        } else {
            Ok(())
        };
    }

    /// Changes the mode of the given path. Authorization may be requested and
    /// additional information may be written if the `interactive` and
    /// `verbose` options are present. The path will not be changed during a
    /// `dry-run`.
    fn change_one_inner(&mut self, path: &PathBuf, mode: u32) -> Result<()> {
        if self.options.interactive && !self.options.suppress {
            // Authorize every path (optional)
            if let Ok(false) = self.auth(path, Context::Interactive) {
                return Ok(());
            }
        }

        if !self.options.dry_run {
            // Change the mode of the path
            fs::set_permissions(path, Permissions::from_mode(mode))?;

            if self.options.verbose {
                // Write the results (optional)
                self.write_result("changed", path)?;
            }
        } else {
            // Perform a dry run (optional)
            self.write_result("will be changed", path)?;
        }
        return Ok(());
    }

    /// Determines if the file option is present.
    fn has_file(&self) -> bool {
        return self.options.file.is_some();
    }

    /// Determines if the directory option is present.
    fn has_dir(&self) -> bool {
        return self.options.dir.is_some();
    }

    /// Writes a path related error to the standard error stream.
    fn write_error(&mut self, msg: &str, path: &PathBuf, err: &Error) -> Result<()> {
        writeln!(self.stderr, r#"Error: {} "{}": {}"#, msg, path.display(), err)?;
        return Ok(());
    }

    /// Writes the result of an operation to the standard output stream.
    fn write_result(&mut self, msg: &str, path: &PathBuf) -> Result<()> {
        writeln!(self.stdout, r#""{}": mode {}."#, path.display(), msg)?;
        return Ok(());
    }
}
<|endoftext|>


<commit_before>use std::num::Float;

use super::{Item, ItemSchedData};
use time::now_utc;

static INITIAL_FF: f64 = 2.5;

pub fn init_item() -> ItemSchedData {
    ItemSchedData {
        last_reviewed: now_utc().to_timespec(),
        ff: INITIAL_FF,
        int_step: 0,
        iri: 0.0,
    }
}

pub fn assess_item(data: &ItemSchedData, fam: u8) -> ItemSchedData {
    let int_step = if fam < 2 {
        1
    } else {
        data.int_step + 1
    };

    let iri = match int_step {
        1 => 1.0,
        2 => 3.0,
        _ => data.iri + data.ff
    };

    // SM-2 algorithm says only to adjust if familiar was above a certain
    // threshold. haven't thought about what effect this has yet.
    // TODO: revisit

    let ff = data.ff + match fam {
        0 => 0.,
        1 => 0.,
        2 => -0.32,
        3 => -0.14,
        4 => 0.,
        5 => 0.1,
        _ => unreachable!("Internal error: managed to reach unreachable code. I'm impressed.")
    };

    ItemSchedData {
        last_reviewed: now_utc().to_timespec(),
        ff: ff,
        int_step: int_step,
        iri: iri,
    }
}

pub fn list_display_item(item: Item) -> String {
    format!("{:3} : {}", item.id, item.desc)
}

pub fn full_display_item(item: Item) -> String {
    let dur = now_utc().to_timespec() - item.data.last_reviewed;
    format!("{:3} : {}\nLast reviewed: {} hours ago\nFF: {}\nint_step: {}\nIRI: {}",
            item.id, item.desc, dur.num_hours(), item.data.ff,
            item.data.int_step, item.data.iri)
}

pub fn filter_review_items(items: Vec<Item>) -> Vec<Item> {
    let curr_time = now_utc().to_timespec();

    fn days_to_seconds(days: f64) -> i64 {
        (days * 86400.0).round() as i64
    }

    items.into_iter()
         .filter(|i| (curr_time - i.data.last_reviewed).num_seconds() > days_to_seconds(i.data.iri))
         .collect()
}

pub fn review_display_item(review_num: usize, item: &Item) -> String {
    format!("\n{:3} - {} (id: {})\n\n0-5 or 's' to skip item or 'q' to quit > ",
            review_num, item.desc, item.id)
}
<commit_msg>Add the logic to ensure ff doesn't go below 1.3. Somehow this got lost<commit_after>use std::num::Float;

use super::{Item, ItemSchedData};
use time::now_utc;

static INITIAL_FF: f64 = 2.5;

pub fn init_item() -> ItemSchedData {
    ItemSchedData {
        last_reviewed: now_utc().to_timespec(),
        ff: INITIAL_FF,
        int_step: 0,
        iri: 0.0,
    }
}

pub fn assess_item(data: &ItemSchedData, fam: u8) -> ItemSchedData {
    let int_step = if fam < 2 {
        1
    } else {
        data.int_step + 1
    };

    let iri = match int_step {
        1 => 1.0,
        2 => 3.0,
        _ => data.iri + data.ff
    };

    // SM-2 algorithm says only to adjust if familiar was above a certain
    // threshold. haven't thought about what effect this has yet.
    // TODO: revisit

    let mut ff = data.ff + match fam {
        0 => 0.,
        1 => 0.,
        2 => -0.32,
        3 => -0.14,
        4 => 0.,
        5 => 0.1,
        _ => unreachable!("Internal error: managed to reach unreachable code. I'm impressed.")
    };

    if ff < 1.3 {
        ff = 1.3;
    }

    ItemSchedData {
        last_reviewed: now_utc().to_timespec(),
        ff: ff,
        int_step: int_step,
        iri: iri,
    }
}

pub fn list_display_item(item: Item) -> String {
    format!("{:3} : {}", item.id, item.desc)
}

pub fn full_display_item(item: Item) -> String {
    let dur = now_utc().to_timespec() - item.data.last_reviewed;
    format!("{:3} : {}\nLast reviewed: {} hours ago\nFF: {}\nint_step: {}\nIRI: {}",
            item.id, item.desc, dur.num_hours(), item.data.ff,
            item.data.int_step, item.data.iri)
}

pub fn filter_review_items(items: Vec<Item>) -> Vec<Item> {
    let curr_time = now_utc().to_timespec();

    fn days_to_seconds(days: f64) -> i64 {
        (days * 86400.0).round() as i64
    }

    items.into_iter()
         .filter(|i| (curr_time - i.data.last_reviewed).num_seconds() > days_to_seconds(i.data.iri))
         .collect()
}

pub fn review_display_item(review_num: usize, item: &Item) -> String {
    format!("\n{:3} - {} (id: {})\n\n0-5 or 's' to skip item or 'q' to quit > ",
            review_num, item.desc, item.id)
}
<|endoftext|>


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

use check::{FnCtxt, structurally_resolved_type};
use middle::subst::{FnSpace};
use middle::traits;
use middle::traits::{Obligation, ObligationCause};
use middle::traits::report_fulfillment_errors;
use middle::ty::{mod, Ty, AsPredicate, ToPolyTraitRef};
use middle::infer;
use std::rc::Rc;
use syntax::ast;
use syntax::codemap::Span;
use util::ppaux::{Repr, ty_to_string};

pub fn check_object_cast<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
                                   cast_expr: &ast::Expr,
                                   source_expr: &ast::Expr,
                                   target_object_ty: Ty<'tcx>)
{
    debug!("check_object_cast(cast_expr={}, target_object_ty={})",
           cast_expr.repr(fcx.tcx()),
           target_object_ty.repr(fcx.tcx()));

    // Look up vtables for the type we're casting to,
    // passing in the source and target type.  The source
    // must be a pointer type suitable to the object sigil,
    // e.g.: `&x as &Trait` or `box x as Box<Trait>`
    let source_ty = fcx.expr_ty(source_expr);
    let source_ty = structurally_resolved_type(fcx, source_expr.span, source_ty);
    debug!("source_ty={}", source_ty.repr(fcx.tcx()));
    match (&source_ty.sty, &target_object_ty.sty) {
        (&ty::ty_uniq(referent_ty), &ty::ty_uniq(object_trait_ty)) => {
            let object_trait = object_trait(&object_trait_ty);

            // Ensure that if ~T is cast to ~Trait, then T : Trait
            push_cast_obligation(fcx, cast_expr, object_trait, referent_ty);
            check_object_safety(fcx.tcx(), object_trait, source_expr.span);
        }

        (&ty::ty_rptr(referent_region, ty::mt { ty: referent_ty,
                                                mutbl: referent_mutbl }),
         &ty::ty_rptr(target_region, ty::mt { ty: object_trait_ty,
                                              mutbl: target_mutbl })) =>
        {
            let object_trait = object_trait(&object_trait_ty);
            if !mutability_allowed(referent_mutbl, target_mutbl) {
                fcx.tcx().sess.span_err(source_expr.span,
                                        "types differ in mutability");
            } else {
                // Ensure that if &'a T is cast to &'b Trait, then T : Trait
                push_cast_obligation(fcx, cast_expr,
                                     object_trait,
                                     referent_ty);

                // Ensure that if &'a T is cast to &'b Trait, then 'b <= 'a
                infer::mk_subr(fcx.infcx(),
                               infer::RelateObjectBound(source_expr.span),
                               *target_region,
                               *referent_region);

                check_object_safety(fcx.tcx(), object_trait, source_expr.span);
            }
        }

        (_, &ty::ty_uniq(..)) => {
            fcx.ccx.tcx.sess.span_err(
                source_expr.span,
                format!("can only cast an boxed pointer \
                         to a boxed object, not a {}",
                        ty::ty_sort_string(fcx.tcx(), source_ty))[]);
        }

        (_, &ty::ty_rptr(..)) => {
            fcx.ccx.tcx.sess.span_err(
                source_expr.span,
                format!("can only cast a &-pointer \
                         to an &-object, not a {}",
                        ty::ty_sort_string(fcx.tcx(), source_ty))[]);
        }

        _ => {
            fcx.tcx().sess.span_bug(
                source_expr.span,
                "expected object type");
        }
    }

    fn object_trait<'a, 'tcx>(t: &'a Ty<'tcx>) -> &'a ty::TyTrait<'tcx> {
        match t.sty {
            ty::ty_trait(ref ty_trait) => &**ty_trait,
            _ => panic!("expected ty_trait")
        }
    }

    fn mutability_allowed(a_mutbl: ast::Mutability,
                          b_mutbl: ast::Mutability)
                          -> bool {
        a_mutbl == b_mutbl ||
            (a_mutbl == ast::MutMutable && b_mutbl == ast::MutImmutable)
    }

    fn push_cast_obligation<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
                                      cast_expr: &ast::Expr,
                                      object_trait: &ty::TyTrait<'tcx>,
                                      referent_ty: Ty<'tcx>) {
        let object_trait_ref =
            register_object_cast_obligations(fcx,
                                             cast_expr.span,
                                             object_trait,
                                             referent_ty);

        // Finally record the object_trait_ref for use during trans
        // (it would prob be better not to do this, but it's just kind
        // of a pain to have to reconstruct it).
        fcx.write_object_cast(cast_expr.id, object_trait_ref);
    }
}

// Check that a trait is 'object-safe'. This should be checked whenever a trait object
// is created (by casting or coercion, etc.). A trait is object-safe if all its
// methods are object-safe. A trait method is object-safe if it does not take
// self by value, has no type parameters and does not use the `Self` type, except
// in self position.
pub fn check_object_safety<'tcx>(tcx: &ty::ctxt<'tcx>,
                                 object_trait: &ty::TyTrait<'tcx>,
                                 span: Span)
{
    let object_trait_ref =
        object_trait.principal_trait_ref_with_self_ty(tcx, tcx.types.err);
    for tr in traits::supertraits(tcx, object_trait_ref) {
        check_object_safety_inner(tcx, &tr, span);
    }
}

fn check_object_safety_inner<'tcx>(tcx: &ty::ctxt<'tcx>,
                                 object_trait: &ty::PolyTraitRef<'tcx>,
                                 span: Span) {
    let trait_items = ty::trait_items(tcx, object_trait.def_id());

    let mut errors = Vec::new();
    for item in trait_items.iter() {
        match *item {
            ty::MethodTraitItem(ref m) => {
                errors.push(check_object_safety_of_method(tcx, &**m))
            }
            ty::TypeTraitItem(_) => {}
        }
    }

    let mut errors = errors.iter().flat_map(|x| x.iter()).peekable();
    if errors.peek().is_some() {
        let trait_name = ty::item_path_str(tcx, object_trait.def_id());
        span_err!(tcx.sess, span, E0038,
            "cannot convert to a trait object because trait `{}` is not object-safe",
            trait_name);

        for msg in errors {
            tcx.sess.note(msg[]);
        }
    }

    /// Returns a vec of error messages. If hte vec is empty - no errors!
    ///
    /// There are some limitations to calling functions through an object, because (a) the self
    /// type is not known (that's the whole point of a trait instance, after all, to obscure the
    /// self type) and (b) the call must go through a vtable and hence cannot be monomorphized.
    fn check_object_safety_of_method<'tcx>(tcx: &ty::ctxt<'tcx>,
                                           method: &ty::Method<'tcx>)
                                           -> Vec<String> {
        let mut msgs = Vec::new();

        let method_name = method.name.repr(tcx);

        match method.explicit_self {
            ty::ByValueExplicitSelfCategory => { // reason (a) above
                msgs.push(format!("cannot call a method (`{}`) with a by-value \
                                   receiver through a trait object", method_name))
            }

            ty::StaticExplicitSelfCategory => {
                // Static methods are always object-safe since they
                // can't be called through a trait object
                return msgs
            }
            ty::ByReferenceExplicitSelfCategory(..) |
            ty::ByBoxExplicitSelfCategory => {}
        }

        // reason (a) above
        let check_for_self_ty = |ty| {
            if ty::type_has_self(ty) {
                Some(format!(
                    "cannot call a method (`{}`) whose type contains \
                     a self-type (`{}`) through a trait object",
                    method_name, ty_to_string(tcx, ty)))
            } else {
                None
            }
        };
        let ref sig = method.fty.sig;
        for &input_ty in sig.0.inputs[1..].iter() {
            if let Some(msg) = check_for_self_ty(input_ty) {
                msgs.push(msg);
            }
        }
        if let ty::FnConverging(result_type) = sig.0.output {
            if let Some(msg) = check_for_self_ty(result_type) {
                msgs.push(msg);
            }
        }

        if method.generics.has_type_params(FnSpace) {
            // reason (b) above
            msgs.push(format!("cannot call a generic method (`{}`) through a trait object",
                              method_name));
        }

        msgs
    }
}

pub fn register_object_cast_obligations<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
                                                  span: Span,
                                                  object_trait: &ty::TyTrait<'tcx>,
                                                  referent_ty: Ty<'tcx>)
                                                  -> ty::PolyTraitRef<'tcx>
{
    // We can only make objects from sized types.
    fcx.register_builtin_bound(
        referent_ty,
        ty::BoundSized,
        traits::ObligationCause::new(span, fcx.body_id, traits::ObjectSized));

    // This is just for better error reporting. Kinda goofy. The object type stuff
    // needs some refactoring so there is a more convenient type to pass around.
    let object_trait_ty =
        ty::mk_trait(fcx.tcx(),
                     object_trait.principal.clone(),
                     object_trait.bounds.clone());

    debug!("register_object_cast_obligations: referent_ty={} object_trait_ty={}",
           referent_ty.repr(fcx.tcx()),
           object_trait_ty.repr(fcx.tcx()));

    // Create the obligation for casting from T to Trait.
    let object_trait_ref =
        object_trait.principal_trait_ref_with_self_ty(fcx.tcx(), referent_ty);
    let object_obligation =
        Obligation::new(
            ObligationCause::new(span,
                                 fcx.body_id,
                                 traits::ObjectCastObligation(object_trait_ty)),
            object_trait_ref.as_predicate());
    fcx.register_predicate(object_obligation);

    // Create additional obligations for all the various builtin
    // bounds attached to the object cast. (In other words, if the
    // object type is Foo+Send, this would create an obligation
    // for the Send check.)
    for builtin_bound in object_trait.bounds.builtin_bounds.iter() {
        fcx.register_builtin_bound(
            referent_ty,
            builtin_bound,
            ObligationCause::new(span, fcx.body_id, traits::ObjectCastObligation(object_trait_ty)));
    }

    object_trait_ref
}

pub fn select_all_fcx_obligations_or_error(fcx: &FnCtxt) {
    debug!("select_all_fcx_obligations_or_error");

    let mut fulfillment_cx = fcx.inh.fulfillment_cx.borrow_mut();
    let r = fulfillment_cx.select_all_or_error(fcx.infcx(),
                                               &fcx.inh.param_env,
                                               fcx);
    match r {
        Ok(()) => { }
        Err(errors) => { report_fulfillment_errors(fcx.infcx(), &errors); }
    }
}

/// Select as many obligations as we can at present.
pub fn select_fcx_obligations_where_possible(fcx: &FnCtxt)
{
    match
        fcx.inh.fulfillment_cx
        .borrow_mut()
        .select_where_possible(fcx.infcx(), &fcx.inh.param_env, fcx)
    {
        Ok(()) => { }
        Err(errors) => { report_fulfillment_errors(fcx.infcx(), &errors); }
    }
}

/// Try to select any fcx obligation that we haven't tried yet, in an effort to improve inference.
/// You could just call `select_fcx_obligations_where_possible` except that it leads to repeated
/// work.
pub fn select_new_fcx_obligations(fcx: &FnCtxt) {
    match
        fcx.inh.fulfillment_cx
        .borrow_mut()
        .select_new_obligations(fcx.infcx(), &fcx.inh.param_env, fcx)
    {
        Ok(()) => { }
        Err(errors) => { report_fulfillment_errors(fcx.infcx(), &errors); }
    }
}

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

use check::{FnCtxt, structurally_resolved_type};
use middle::subst::{FnSpace, SelfSpace};
use middle::traits;
use middle::traits::{Obligation, ObligationCause};
use middle::traits::report_fulfillment_errors;
use middle::ty::{mod, Ty, AsPredicate, ToPolyTraitRef};
use middle::infer;
use std::rc::Rc;
use syntax::ast;
use syntax::codemap::Span;
use util::ppaux::{Repr, ty_to_string};

pub fn check_object_cast<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
                                   cast_expr: &ast::Expr,
                                   source_expr: &ast::Expr,
                                   target_object_ty: Ty<'tcx>)
{
    debug!("check_object_cast(cast_expr={}, target_object_ty={})",
           cast_expr.repr(fcx.tcx()),
           target_object_ty.repr(fcx.tcx()));

    // Look up vtables for the type we're casting to,
    // passing in the source and target type.  The source
    // must be a pointer type suitable to the object sigil,
    // e.g.: `&x as &Trait` or `box x as Box<Trait>`
    let source_ty = fcx.expr_ty(source_expr);
    let source_ty = structurally_resolved_type(fcx, source_expr.span, source_ty);
    debug!("source_ty={}", source_ty.repr(fcx.tcx()));
    match (&source_ty.sty, &target_object_ty.sty) {
        (&ty::ty_uniq(referent_ty), &ty::ty_uniq(object_trait_ty)) => {
            let object_trait = object_trait(&object_trait_ty);

            // Ensure that if ~T is cast to ~Trait, then T : Trait
            push_cast_obligation(fcx, cast_expr, object_trait, referent_ty);
            check_object_safety(fcx.tcx(), object_trait, source_expr.span);
        }

        (&ty::ty_rptr(referent_region, ty::mt { ty: referent_ty,
                                                mutbl: referent_mutbl }),
         &ty::ty_rptr(target_region, ty::mt { ty: object_trait_ty,
                                              mutbl: target_mutbl })) =>
        {
            let object_trait = object_trait(&object_trait_ty);
            if !mutability_allowed(referent_mutbl, target_mutbl) {
                fcx.tcx().sess.span_err(source_expr.span,
                                        "types differ in mutability");
            } else {
                // Ensure that if &'a T is cast to &'b Trait, then T : Trait
                push_cast_obligation(fcx, cast_expr,
                                     object_trait,
                                     referent_ty);

                // Ensure that if &'a T is cast to &'b Trait, then 'b <= 'a
                infer::mk_subr(fcx.infcx(),
                               infer::RelateObjectBound(source_expr.span),
                               *target_region,
                               *referent_region);

                check_object_safety(fcx.tcx(), object_trait, source_expr.span);
            }
        }

        (_, &ty::ty_uniq(..)) => {
            fcx.ccx.tcx.sess.span_err(
                source_expr.span,
                format!("can only cast an boxed pointer \
                         to a boxed object, not a {}",
                        ty::ty_sort_string(fcx.tcx(), source_ty))[]);
        }

        (_, &ty::ty_rptr(..)) => {
            fcx.ccx.tcx.sess.span_err(
                source_expr.span,
                format!("can only cast a &-pointer \
                         to an &-object, not a {}",
                        ty::ty_sort_string(fcx.tcx(), source_ty))[]);
        }

        _ => {
            fcx.tcx().sess.span_bug(
                source_expr.span,
                "expected object type");
        }
    }

    fn object_trait<'a, 'tcx>(t: &'a Ty<'tcx>) -> &'a ty::TyTrait<'tcx> {
        match t.sty {
            ty::ty_trait(ref ty_trait) => &**ty_trait,
            _ => panic!("expected ty_trait")
        }
    }

    fn mutability_allowed(a_mutbl: ast::Mutability,
                          b_mutbl: ast::Mutability)
                          -> bool {
        a_mutbl == b_mutbl ||
            (a_mutbl == ast::MutMutable && b_mutbl == ast::MutImmutable)
    }

    fn push_cast_obligation<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
                                      cast_expr: &ast::Expr,
                                      object_trait: &ty::TyTrait<'tcx>,
                                      referent_ty: Ty<'tcx>) {
        let object_trait_ref =
            register_object_cast_obligations(fcx,
                                             cast_expr.span,
                                             object_trait,
                                             referent_ty);

        // Finally record the object_trait_ref for use during trans
        // (it would prob be better not to do this, but it's just kind
        // of a pain to have to reconstruct it).
        fcx.write_object_cast(cast_expr.id, object_trait_ref);
    }
}

// Check that a trait is 'object-safe'. This should be checked whenever a trait object
// is created (by casting or coercion, etc.). A trait is object-safe if all its
// methods are object-safe. A trait method is object-safe if it does not take
// self by value, has no type parameters and does not use the `Self` type, except
// in self position.
pub fn check_object_safety<'tcx>(tcx: &ty::ctxt<'tcx>,
                                 object_trait: &ty::TyTrait<'tcx>,
                                 span: Span)
{
    let object_trait_ref =
        object_trait.principal_trait_ref_with_self_ty(tcx, tcx.types.err);
    for tr in traits::supertraits(tcx, object_trait_ref) {
        check_object_safety_inner(tcx, &tr, span);
    }
}

fn check_object_safety_inner<'tcx>(tcx: &ty::ctxt<'tcx>,
                                   object_trait: &ty::PolyTraitRef<'tcx>,
                                   span: Span) {
    let trait_items = ty::trait_items(tcx, object_trait.def_id());

    let mut errors = Vec::new();
    for item in trait_items.iter() {
        match *item {
            ty::MethodTraitItem(ref m) => {
                errors.push(check_object_safety_of_method(tcx, object_trait, &**m))
            }
            ty::TypeTraitItem(_) => {}
        }
    }

    let mut errors = errors.iter().flat_map(|x| x.iter()).peekable();
    if errors.peek().is_some() {
        let trait_name = ty::item_path_str(tcx, object_trait.def_id());
        span_err!(tcx.sess, span, E0038,
            "cannot convert to a trait object because trait `{}` is not object-safe",
            trait_name);

        for msg in errors {
            tcx.sess.note(msg[]);
        }
    }

    /// Returns a vec of error messages. If hte vec is empty - no errors!
    ///
    /// There are some limitations to calling functions through an object, because (a) the self
    /// type is not known (that's the whole point of a trait instance, after all, to obscure the
    /// self type) and (b) the call must go through a vtable and hence cannot be monomorphized.
    fn check_object_safety_of_method<'tcx>(tcx: &ty::ctxt<'tcx>,
                                           object_trait: &ty::PolyTraitRef<'tcx>,
                                           method: &ty::Method<'tcx>)
                                           -> Vec<String> {
        let mut msgs = Vec::new();

        let method_name = method.name.repr(tcx);

        match method.explicit_self {
            ty::ByValueExplicitSelfCategory => { // reason (a) above
                msgs.push(format!("cannot call a method (`{}`) with a by-value \
                                   receiver through a trait object", method_name))
            }

            ty::StaticExplicitSelfCategory => {
                // Static methods are always object-safe since they
                // can't be called through a trait object
                return msgs
            }
            ty::ByReferenceExplicitSelfCategory(..) |
            ty::ByBoxExplicitSelfCategory => {}
        }

        // reason (a) above
        let check_for_self_ty = |ty| {
            if contains_illegal_self_type_reference(tcx, object_trait.def_id(), ty) {
                Some(format!(
                    "cannot call a method (`{}`) whose type contains \
                     a self-type (`{}`) through a trait object",
                    method_name, ty_to_string(tcx, ty)))
            } else {
                None
            }
        };
        let ref sig = method.fty.sig;
        for &input_ty in sig.0.inputs[1..].iter() {
            if let Some(msg) = check_for_self_ty(input_ty) {
                msgs.push(msg);
            }
        }
        if let ty::FnConverging(result_type) = sig.0.output {
            if let Some(msg) = check_for_self_ty(result_type) {
                msgs.push(msg);
            }
        }

        if method.generics.has_type_params(FnSpace) {
            // reason (b) above
            msgs.push(format!("cannot call a generic method (`{}`) through a trait object",
                              method_name));
        }

        msgs
    }

    fn contains_illegal_self_type_reference<'tcx>(tcx: &ty::ctxt<'tcx>,
                                                  trait_def_id: ast::DefId,
                                                  ty: Ty<'tcx>)
                                                  -> bool
    {
        // This is somewhat subtle. In general, we want to forbid
        // references to `Self` in the argument and return types,
        // since the value of `Self` is erased. However, there is one
        // exception: it is ok to reference `Self` in order to access
        // an associated type of the current trait, since we retain
        // the value of those associated types in the object type
        // itself.
        //
        // ```rust
        // trait SuperTrait {
        //     type X;
        // }
        //
        // trait Trait : SuperTrait {
        //     type Y;
        //     fn foo(&self, x: Self) // bad
        //     fn foo(&self) -> Self // bad
        //     fn foo(&self) -> Option<Self> // bad
        //     fn foo(&self) -> Self::Y // OK, desugars to next example
        //     fn foo(&self) -> <Self as Trait>::Y // OK
        //     fn foo(&self) -> Self::X // OK, desugars to next example
        //     fn foo(&self) -> <Self as SuperTrait>::X // OK
        // }
        // ```
        //
        // However, it is not as simple as allowing `Self` in a projected
        // type, because there are illegal ways to use `Self` as well:
        //
        // ```rust
        // trait Trait : SuperTrait {
        //     ...
        //     fn foo(&self) -> <Self as SomeOtherTrait>::X;
        // }
        // ```
        //
        // Here we will not have the type of `X` recorded in the
        // object type, and we cannot resolve `Self as SomeOtherTrait`
        // without knowing what `Self` is.

        let mut supertraits: Option<Vec<ty::PolyTraitRef<'tcx>>> = None;
        let mut error = false;
        ty::maybe_walk_ty(ty, |ty| {
            match ty.sty {
                ty::ty_param(ref param_ty) => {
                    if param_ty.space == SelfSpace {
                        error = true;
                    }

                    false // no contained types to walk
                }

                ty::ty_projection(ref data) => {
                    // This is a projected type `<Foo as SomeTrait>::X`.

                    // Compute supertraits of current trait lazilly.
                    if supertraits.is_none() {
                        let trait_def = ty::lookup_trait_def(tcx, trait_def_id);
                        let trait_ref = ty::Binder(trait_def.trait_ref.clone());
                        supertraits = Some(traits::supertraits(tcx, trait_ref).collect());
                    }

                    // Determine whether the trait reference `Foo as
                    // SomeTrait` is in fact a supertrait of the
                    // current trait. In that case, this type is
                    // legal, because the type `X` will be specified
                    // in the object type.  Note that we can just use
                    // direct equality here because all of these types
                    // are part of the formal parameter listing, and
                    // hence there should be no inference variables.
                    let projection_trait_ref = ty::Binder(data.trait_ref.clone());
                    let is_supertrait_of_current_trait =
                        supertraits.as_ref().unwrap().contains(&projection_trait_ref);

                    if is_supertrait_of_current_trait {
                        false // do not walk contained types, do not report error, do collect $200
                    } else {
                        true // DO walk contained types, POSSIBLY reporting an error
                    }
                }

                _ => true, // walk contained types, if any
            }
        });

        error
    }
}

pub fn register_object_cast_obligations<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
                                                  span: Span,
                                                  object_trait: &ty::TyTrait<'tcx>,
                                                  referent_ty: Ty<'tcx>)
                                                  -> ty::PolyTraitRef<'tcx>
{
    // We can only make objects from sized types.
    fcx.register_builtin_bound(
        referent_ty,
        ty::BoundSized,
        traits::ObligationCause::new(span, fcx.body_id, traits::ObjectSized));

    // This is just for better error reporting. Kinda goofy. The object type stuff
    // needs some refactoring so there is a more convenient type to pass around.
    let object_trait_ty =
        ty::mk_trait(fcx.tcx(),
                     object_trait.principal.clone(),
                     object_trait.bounds.clone());

    debug!("register_object_cast_obligations: referent_ty={} object_trait_ty={}",
           referent_ty.repr(fcx.tcx()),
           object_trait_ty.repr(fcx.tcx()));

    // Create the obligation for casting from T to Trait.
    let object_trait_ref =
        object_trait.principal_trait_ref_with_self_ty(fcx.tcx(), referent_ty);
    let object_obligation =
        Obligation::new(
            ObligationCause::new(span,
                                 fcx.body_id,
                                 traits::ObjectCastObligation(object_trait_ty)),
            object_trait_ref.as_predicate());
    fcx.register_predicate(object_obligation);

    // Create additional obligations for all the various builtin
    // bounds attached to the object cast. (In other words, if the
    // object type is Foo+Send, this would create an obligation
    // for the Send check.)
    for builtin_bound in object_trait.bounds.builtin_bounds.iter() {
        fcx.register_builtin_bound(
            referent_ty,
            builtin_bound,
            ObligationCause::new(span, fcx.body_id, traits::ObjectCastObligation(object_trait_ty)));
    }

    object_trait_ref
}

pub fn select_all_fcx_obligations_or_error(fcx: &FnCtxt) {
    debug!("select_all_fcx_obligations_or_error");

    let mut fulfillment_cx = fcx.inh.fulfillment_cx.borrow_mut();
    let r = fulfillment_cx.select_all_or_error(fcx.infcx(),
                                               &fcx.inh.param_env,
                                               fcx);
    match r {
        Ok(()) => { }
        Err(errors) => { report_fulfillment_errors(fcx.infcx(), &errors); }
    }
}

/// Select as many obligations as we can at present.
pub fn select_fcx_obligations_where_possible(fcx: &FnCtxt)
{
    match
        fcx.inh.fulfillment_cx
        .borrow_mut()
        .select_where_possible(fcx.infcx(), &fcx.inh.param_env, fcx)
    {
        Ok(()) => { }
        Err(errors) => { report_fulfillment_errors(fcx.infcx(), &errors); }
    }
}

/// Try to select any fcx obligation that we haven't tried yet, in an effort to improve inference.
/// You could just call `select_fcx_obligations_where_possible` except that it leads to repeated
/// work.
pub fn select_new_fcx_obligations(fcx: &FnCtxt) {
    match
        fcx.inh.fulfillment_cx
        .borrow_mut()
        .select_new_obligations(fcx.infcx(), &fcx.inh.param_env, fcx)
    {
        Ok(()) => { }
        Err(errors) => { report_fulfillment_errors(fcx.infcx(), &errors); }
    }
}

<|endoftext|>


<commit_before><commit_msg>chore: resolve clippy lints<commit_after><|endoftext|>


<commit_before><commit_msg>Made syntax a variable lifetime, removed a clone<commit_after><|endoftext|>


<commit_before><commit_msg>address parsing seems to work right, split off into (still too big and fiddly) fn<commit_after><|endoftext|>


<commit_before><commit_msg>Allow telling it to join another channel<commit_after><|endoftext|>


<commit_before><commit_msg>Return proper exit code from all the possible paths<commit_after><|endoftext|>


<commit_before><commit_msg>Adds -v param for logging verbosity<commit_after><|endoftext|>


<commit_before>#![feature(globs)]

extern crate libc;
extern crate neovim;
extern crate rgtk;
extern crate "rustc-serialize" as rustc_serialize;

use rgtk::*;
use std::collections::HashSet;
use std::io::fs;
use std::io::fs::PathExtensions;

mod projects;
mod ui;
mod utils;

mod ffi {
    pub use libc::{c_int, c_uchar, c_void};
    pub use libc::funcs::posix88::unistd::{close, pipe, read, write};
    pub use libc::types::os::arch::c95::size_t;

    extern "C" {
        pub fn fork () -> c_int;
        pub fn kill (pid: c_int, sig: c_int);
    }
}

fn gui_main(
    pty: &mut gtk::VtePty,
    read_fd: ffi::c_int,
    write_fd: ffi::c_int,
    pid: ffi::c_int)
{
    gtk::init();

    // constants

    let width = 1242;
    let height = 768;
    let editor_height = ((height as f32) * 0.75) as i32;

    // create the window

    let title = format!("SolidOak {}.{}.{}",
                        option_env!("CARGO_PKG_VERSION_MAJOR").unwrap(),
                        option_env!("CARGO_PKG_VERSION_MINOR").unwrap(),
                        option_env!("CARGO_PKG_VERSION_PATCH").unwrap());
    let mut window = gtk::Window::new(gtk::WindowType::TopLevel).unwrap();
    window.set_title(title.as_slice());
    window.set_window_position(gtk::WindowPosition::Center);
    window.set_default_size(width, height);

    window.connect(gtk::signals::DeleteEvent::new(|_| {
        unsafe {
            ffi::close(read_fd);
            ffi::close(write_fd);
            ffi::kill(pid, 15);
        }
        gtk::main_quit();
        true
    }));

    // create the panes

    let new_button = gtk::Button::new_with_label("New Project").unwrap();
    let import_button = gtk::Button::new_with_label("Import").unwrap();
    let rename_button = gtk::Button::new_with_label("Rename").unwrap();
    let remove_button = gtk::Button::new_with_label("Remove").unwrap();

    let mut project_buttons = gtk::Box::new(gtk::Orientation::Horizontal, 0).unwrap();
    project_buttons.set_size_request(-1, -1);
    project_buttons.add(&new_button);
    project_buttons.add(&import_button);
    project_buttons.add(&rename_button);
    project_buttons.add(&remove_button);

    let mut project_tree = gtk::TreeView::new().unwrap();
    let selection = project_tree.get_selection().unwrap();
    let column_types = [glib::ffi::g_type_string, glib::ffi::g_type_string];
    let store = gtk::TreeStore::new(&column_types).unwrap();
    let model = store.get_model().unwrap();
    project_tree.set_model(&model);
    project_tree.set_headers_visible(false);

    let mut scroll_pane = gtk::ScrolledWindow::new(None, None).unwrap();
    scroll_pane.add(&project_tree);

    let column = gtk::TreeViewColumn::new().unwrap();
    let cell = gtk::CellRendererText::new().unwrap();
    column.pack_start(&cell, true);
    column.add_attribute(&cell, "text", 0);
    project_tree.append_column(&column);

    let mut project_pane = gtk::Box::new(gtk::Orientation::Vertical, 0).unwrap();
    project_pane.set_size_request(-1, -1);
    project_pane.pack_start(&project_buttons, false, true, 0);
    project_pane.pack_start(&scroll_pane, true, true, 0);

    let mut editor_pane = gtk::VteTerminal::new().unwrap();
    editor_pane.set_size_request(-1, editor_height);
    editor_pane.set_pty(pty);
    editor_pane.watch_child(pid);

    let run_button = gtk::Button::new_with_label("Run").unwrap();
    let build_button = gtk::Button::new_with_label("Build").unwrap();
    let test_button = gtk::Button::new_with_label("Test").unwrap();
    let clean_button = gtk::Button::new_with_label("Clean").unwrap();
    let stop_button = gtk::Button::new_with_label("Stop").unwrap();

    let mut build_buttons = gtk::Box::new(gtk::Orientation::Horizontal, 0).unwrap();
    build_buttons.set_size_request(-1, -1);
    build_buttons.add(&run_button);
    build_buttons.add(&build_button);
    build_buttons.add(&test_button);
    build_buttons.add(&clean_button);
    build_buttons.add(&stop_button);

    let build_term = gtk::VteTerminal::new().unwrap();

    let mut build_pane = gtk::Box::new(gtk::Orientation::Vertical, 0).unwrap();
    build_pane.pack_start(&build_buttons, false, true, 0);
    build_pane.pack_start(&build_term, true, true, 0);

    let mut content = gtk::Box::new(gtk::Orientation::Vertical, 0).unwrap();
    content.pack_start(&editor_pane, false, true, 0);
    content.pack_start(&build_pane, true, true, 0);

    let mut hbox = gtk::Box::new(gtk::Orientation::Horizontal, 0).unwrap();
    hbox.pack_start(&project_pane, false, true, 0);
    hbox.pack_start(&content, true, true, 0);
    window.add(&hbox);

    // populate the project tree

    let mut state = ::utils::State{
        projects: HashSet::new(),
        expansions: HashSet::new(),
        selection: None,
        tree_model: &model,
        tree_store: &store,
        tree_selection: &selection,
        rename_button: &rename_button,
        remove_button: &remove_button,
    };

    ::utils::read_prefs(&mut state);
    ::ui::update_project_tree(&mut state, &mut project_tree);

    // connect to the signals

    new_button.connect(gtk::signals::Clicked::new(|| {
        ::projects::new_project(&mut state, &mut project_tree);
    }));
    import_button.connect(gtk::signals::Clicked::new(|| {
        ::projects::import_project(&mut state, &mut project_tree);
    }));
    rename_button.connect(gtk::signals::Clicked::new(|| {
        ::projects::rename_file(&mut state);
    }));
    remove_button.connect(gtk::signals::Clicked::new(|| {
        ::projects::remove_item(&mut state);
    }));
    selection.connect(gtk::signals::Changed::new(|| {
        ::projects::save_selection(&mut state);
    }));
    project_tree.connect(gtk::signals::RowCollapsed::new(|iter_raw, _| {
        let iter = gtk::TreeIter::wrap_pointer(iter_raw);
        ::projects::remove_expansion(&mut state, &iter);
    }));
    project_tree.connect(gtk::signals::RowExpanded::new(|iter_raw, _| {
        let iter = gtk::TreeIter::wrap_pointer(iter_raw);
        ::projects::add_expansion(&mut state, &iter);
    }));

    // show the window

    window.show_all();
    gtk::main();
}

fn main() {
    // create data dir
    let home_dir = ::utils::get_home_dir();
    let data_dir = home_dir.join(::utils::DATA_DIR);
    if !data_dir.exists() {
        match fs::mkdir(&data_dir, ::std::io::USER_DIR) {
            Ok(_) => {
                for res in ::utils::DATA_CONTENT.iter() {
                    let res_path = data_dir.join_many(res.path);
                    ::std::io::fs::mkdir_recursive(&res_path.dir_path(), ::std::io::USER_DIR).ok();
                    ::std::io::File::create(&res_path).write(res.data.as_bytes()).ok();
                }
                println!("Created data dir at {}", data_dir.as_str().unwrap());
            },
            Err(e) => { println!("Error creating data dir: {}", e) }
        }
    }

    // set $VIM to the data dir if it isn't already set
    if ::std::os::getenv("VIM").is_none() {
        ::std::os::setenv("VIM", data_dir.as_str().unwrap());
    }

    // create config file
    let config_file = home_dir.join(::utils::CONFIG_FILE);
    if !config_file.exists() {
        match ::std::io::File::create(&config_file).write(::utils::CONFIG_CONTENT.as_bytes()) {
            Ok(_) => { println!("Created config file at {}", config_file.as_str().unwrap()) },
            Err(e) => { println!("Error creating config file: {}", e) }
        }
    }

    // takes care of piping stdin/stdout between the gui and nvim
    let mut pty = gtk::VtePty::new().unwrap();

    // two pairs of anonymous pipes for msgpack-rpc between the gui and nvim
    let mut nvim_from_gui : [ffi::c_int, ..2] = [0, ..2];
    let mut gui_from_nvim : [ffi::c_int, ..2] = [0, ..2];
    unsafe {
        ffi::pipe(nvim_from_gui.as_mut_ptr());
        ffi::pipe(gui_from_nvim.as_mut_ptr());
    };

    // split into two processes
    let pid = unsafe { ffi::fork() };

    if pid > 0 { // the gui process
        ::std::thread::Thread::spawn(move || {
            // send attach_ui message
            {
                let mut arr = neovim::Array::new();
                arr.add_integer(80);
                arr.add_integer(24);
                let msg = neovim::serialize_message(1, "attach_ui", &arr);
                let msg_ptr = msg.as_slice().as_ptr() as *const ffi::c_void;
                unsafe { ffi::write(nvim_from_gui[1], msg_ptr, msg.len() as ffi::size_t) };
            }

            // listen for messages
            let mut buf : [ffi::c_uchar, ..1024] = [0, ..1024];
            unsafe {
                loop {
                    let n = ffi::read(gui_from_nvim[0], buf.as_mut_ptr() as *mut ffi::c_void, 1024);
                    if n < 0 {
                        break;
                    } else if n > 0 {
                        let msg = ::std::string::String::from_raw_buf_len(buf.as_ptr(), n as uint);
                        let arr = neovim::deserialize_message(&msg);
                        print!("Received:");
                        for i in range(0, arr.len()) {
                            let o = arr.get(i);
                            if o.is_some() { print!(" {}", o.unwrap()) } else { print!(" Nil") };
                        }
                        println!("");
                    }
                }
            }
        }).detach();

        // start the gui
        gui_main(&mut pty, gui_from_nvim[0], gui_from_nvim[1], pid);
    } else { // the nvim process
        // prepare this process to be piped into the gui
        pty.child_setup();

        // start nvim
        let mut args = ::std::os::args().clone();
        args.push_all(&["-u".to_string(), config_file.as_str().unwrap().to_string()]);
        neovim::run_with_fds(args, nvim_from_gui[0], gui_from_nvim[1]);
    }
}
<commit_msg>Simplify printing of array<commit_after>#![feature(globs)]

extern crate libc;
extern crate neovim;
extern crate rgtk;
extern crate "rustc-serialize" as rustc_serialize;

use rgtk::*;
use std::collections::HashSet;
use std::io::fs;
use std::io::fs::PathExtensions;

mod projects;
mod ui;
mod utils;

mod ffi {
    pub use libc::{c_int, c_uchar, c_void};
    pub use libc::funcs::posix88::unistd::{close, pipe, read, write};
    pub use libc::types::os::arch::c95::size_t;

    extern "C" {
        pub fn fork () -> c_int;
        pub fn kill (pid: c_int, sig: c_int);
    }
}

fn gui_main(
    pty: &mut gtk::VtePty,
    read_fd: ffi::c_int,
    write_fd: ffi::c_int,
    pid: ffi::c_int)
{
    gtk::init();

    // constants

    let width = 1242;
    let height = 768;
    let editor_height = ((height as f32) * 0.75) as i32;

    // create the window

    let title = format!("SolidOak {}.{}.{}",
                        option_env!("CARGO_PKG_VERSION_MAJOR").unwrap(),
                        option_env!("CARGO_PKG_VERSION_MINOR").unwrap(),
                        option_env!("CARGO_PKG_VERSION_PATCH").unwrap());
    let mut window = gtk::Window::new(gtk::WindowType::TopLevel).unwrap();
    window.set_title(title.as_slice());
    window.set_window_position(gtk::WindowPosition::Center);
    window.set_default_size(width, height);

    window.connect(gtk::signals::DeleteEvent::new(|_| {
        unsafe {
            ffi::close(read_fd);
            ffi::close(write_fd);
            ffi::kill(pid, 15);
        }
        gtk::main_quit();
        true
    }));

    // create the panes

    let new_button = gtk::Button::new_with_label("New Project").unwrap();
    let import_button = gtk::Button::new_with_label("Import").unwrap();
    let rename_button = gtk::Button::new_with_label("Rename").unwrap();
    let remove_button = gtk::Button::new_with_label("Remove").unwrap();

    let mut project_buttons = gtk::Box::new(gtk::Orientation::Horizontal, 0).unwrap();
    project_buttons.set_size_request(-1, -1);
    project_buttons.add(&new_button);
    project_buttons.add(&import_button);
    project_buttons.add(&rename_button);
    project_buttons.add(&remove_button);

    let mut project_tree = gtk::TreeView::new().unwrap();
    let selection = project_tree.get_selection().unwrap();
    let column_types = [glib::ffi::g_type_string, glib::ffi::g_type_string];
    let store = gtk::TreeStore::new(&column_types).unwrap();
    let model = store.get_model().unwrap();
    project_tree.set_model(&model);
    project_tree.set_headers_visible(false);

    let mut scroll_pane = gtk::ScrolledWindow::new(None, None).unwrap();
    scroll_pane.add(&project_tree);

    let column = gtk::TreeViewColumn::new().unwrap();
    let cell = gtk::CellRendererText::new().unwrap();
    column.pack_start(&cell, true);
    column.add_attribute(&cell, "text", 0);
    project_tree.append_column(&column);

    let mut project_pane = gtk::Box::new(gtk::Orientation::Vertical, 0).unwrap();
    project_pane.set_size_request(-1, -1);
    project_pane.pack_start(&project_buttons, false, true, 0);
    project_pane.pack_start(&scroll_pane, true, true, 0);

    let mut editor_pane = gtk::VteTerminal::new().unwrap();
    editor_pane.set_size_request(-1, editor_height);
    editor_pane.set_pty(pty);
    editor_pane.watch_child(pid);

    let run_button = gtk::Button::new_with_label("Run").unwrap();
    let build_button = gtk::Button::new_with_label("Build").unwrap();
    let test_button = gtk::Button::new_with_label("Test").unwrap();
    let clean_button = gtk::Button::new_with_label("Clean").unwrap();
    let stop_button = gtk::Button::new_with_label("Stop").unwrap();

    let mut build_buttons = gtk::Box::new(gtk::Orientation::Horizontal, 0).unwrap();
    build_buttons.set_size_request(-1, -1);
    build_buttons.add(&run_button);
    build_buttons.add(&build_button);
    build_buttons.add(&test_button);
    build_buttons.add(&clean_button);
    build_buttons.add(&stop_button);

    let build_term = gtk::VteTerminal::new().unwrap();

    let mut build_pane = gtk::Box::new(gtk::Orientation::Vertical, 0).unwrap();
    build_pane.pack_start(&build_buttons, false, true, 0);
    build_pane.pack_start(&build_term, true, true, 0);

    let mut content = gtk::Box::new(gtk::Orientation::Vertical, 0).unwrap();
    content.pack_start(&editor_pane, false, true, 0);
    content.pack_start(&build_pane, true, true, 0);

    let mut hbox = gtk::Box::new(gtk::Orientation::Horizontal, 0).unwrap();
    hbox.pack_start(&project_pane, false, true, 0);
    hbox.pack_start(&content, true, true, 0);
    window.add(&hbox);

    // populate the project tree

    let mut state = ::utils::State{
        projects: HashSet::new(),
        expansions: HashSet::new(),
        selection: None,
        tree_model: &model,
        tree_store: &store,
        tree_selection: &selection,
        rename_button: &rename_button,
        remove_button: &remove_button,
    };

    ::utils::read_prefs(&mut state);
    ::ui::update_project_tree(&mut state, &mut project_tree);

    // connect to the signals

    new_button.connect(gtk::signals::Clicked::new(|| {
        ::projects::new_project(&mut state, &mut project_tree);
    }));
    import_button.connect(gtk::signals::Clicked::new(|| {
        ::projects::import_project(&mut state, &mut project_tree);
    }));
    rename_button.connect(gtk::signals::Clicked::new(|| {
        ::projects::rename_file(&mut state);
    }));
    remove_button.connect(gtk::signals::Clicked::new(|| {
        ::projects::remove_item(&mut state);
    }));
    selection.connect(gtk::signals::Changed::new(|| {
        ::projects::save_selection(&mut state);
    }));
    project_tree.connect(gtk::signals::RowCollapsed::new(|iter_raw, _| {
        let iter = gtk::TreeIter::wrap_pointer(iter_raw);
        ::projects::remove_expansion(&mut state, &iter);
    }));
    project_tree.connect(gtk::signals::RowExpanded::new(|iter_raw, _| {
        let iter = gtk::TreeIter::wrap_pointer(iter_raw);
        ::projects::add_expansion(&mut state, &iter);
    }));

    // show the window

    window.show_all();
    gtk::main();
}

fn main() {
    // create data dir
    let home_dir = ::utils::get_home_dir();
    let data_dir = home_dir.join(::utils::DATA_DIR);
    if !data_dir.exists() {
        match fs::mkdir(&data_dir, ::std::io::USER_DIR) {
            Ok(_) => {
                for res in ::utils::DATA_CONTENT.iter() {
                    let res_path = data_dir.join_many(res.path);
                    ::std::io::fs::mkdir_recursive(&res_path.dir_path(), ::std::io::USER_DIR).ok();
                    ::std::io::File::create(&res_path).write(res.data.as_bytes()).ok();
                }
                println!("Created data dir at {}", data_dir.as_str().unwrap());
            },
            Err(e) => { println!("Error creating data dir: {}", e) }
        }
    }

    // set $VIM to the data dir if it isn't already set
    if ::std::os::getenv("VIM").is_none() {
        ::std::os::setenv("VIM", data_dir.as_str().unwrap());
    }

    // create config file
    let config_file = home_dir.join(::utils::CONFIG_FILE);
    if !config_file.exists() {
        match ::std::io::File::create(&config_file).write(::utils::CONFIG_CONTENT.as_bytes()) {
            Ok(_) => { println!("Created config file at {}", config_file.as_str().unwrap()) },
            Err(e) => { println!("Error creating config file: {}", e) }
        }
    }

    // takes care of piping stdin/stdout between the gui and nvim
    let mut pty = gtk::VtePty::new().unwrap();

    // two pairs of anonymous pipes for msgpack-rpc between the gui and nvim
    let mut nvim_from_gui : [ffi::c_int, ..2] = [0, ..2];
    let mut gui_from_nvim : [ffi::c_int, ..2] = [0, ..2];
    unsafe {
        ffi::pipe(nvim_from_gui.as_mut_ptr());
        ffi::pipe(gui_from_nvim.as_mut_ptr());
    };

    // split into two processes
    let pid = unsafe { ffi::fork() };

    if pid > 0 { // the gui process
        ::std::thread::Thread::spawn(move || {
            // send attach_ui message
            {
                let mut arr = neovim::Array::new();
                arr.add_integer(80);
                arr.add_integer(24);
                let msg = neovim::serialize_message(1, "attach_ui", &arr);
                let msg_ptr = msg.as_slice().as_ptr() as *const ffi::c_void;
                unsafe { ffi::write(nvim_from_gui[1], msg_ptr, msg.len() as ffi::size_t) };
            }

            // listen for messages
            let mut buf : [ffi::c_uchar, ..1024] = [0, ..1024];
            unsafe {
                loop {
                    let n = ffi::read(gui_from_nvim[0], buf.as_mut_ptr() as *mut ffi::c_void, 1024);
                    if n < 0 {
                        break;
                    } else if n > 0 {
                        let msg = ::std::string::String::from_raw_buf_len(buf.as_ptr(), n as uint);
                        let arr = neovim::deserialize_message(&msg);
                        println!("Received: {}", arr);
                    }
                }
            }
        }).detach();

        // start the gui
        gui_main(&mut pty, gui_from_nvim[0], gui_from_nvim[1], pid);
    } else { // the nvim process
        // prepare this process to be piped into the gui
        pty.child_setup();

        // start nvim
        let mut args = ::std::os::args().clone();
        args.push_all(&["-u".to_string(), config_file.as_str().unwrap().to_string()]);
        neovim::run_with_fds(args, nvim_from_gui[0], gui_from_nvim[1]);
    }
}
<|endoftext|>


<commit_before><commit_msg>Wait for the competitor in concurrent tests<commit_after><|endoftext|>


<commit_before><commit_msg>count deferred tasks as inactive<commit_after><|endoftext|>


<commit_before>use ra_syntax::{
    ast::{self, AstNode},
    SyntaxKind::{WHITESPACE, SEMI},
    SyntaxNode, TextUnit,
};

use crate::assists::{AssistCtx, Assist};

pub fn introduce_variable<'a>(ctx: AssistCtx) -> Option<Assist> {
    let node = ctx.covering_node();
    let expr = node.ancestors().filter_map(ast::Expr::cast).next()?;

    let anchor_stmt = anchor_stmt(expr)?;
    let indent = anchor_stmt.prev_sibling()?;
    if indent.kind() != WHITESPACE {
        return None;
    }
    ctx.build("introduce variable", move |edit| {
        let mut buf = String::new();

        buf.push_str("let var_name = ");
        expr.syntax().text().push_to(&mut buf);
        let full_stmt = ast::ExprStmt::cast(anchor_stmt);
        let is_full_stmt = if let Some(expr_stmt) = full_stmt {
            Some(expr.syntax()) == expr_stmt.expr().map(|e| e.syntax())
        } else {
            false
        };
        if is_full_stmt {
            if !full_stmt.unwrap().has_semi() {
                buf.push_str(";");
            }
            edit.replace(expr.syntax().range(), buf);
        } else {
            buf.push_str(";");
            indent.text().push_to(&mut buf);
            edit.replace(expr.syntax().range(), "var_name".to_string());
            edit.insert(anchor_stmt.range().start(), buf);
        }
        edit.set_cursor(anchor_stmt.range().start() + TextUnit::of_str("let "));
    })
}

/// Statement or last in the block expression, which will follow
/// the freshly introduced var.
fn anchor_stmt(expr: &ast::Expr) -> Option<&SyntaxNode> {
    expr.syntax().ancestors().find(|&node| {
        if ast::Stmt::cast(node).is_some() {
            return true;
        }
        if let Some(expr) = node
            .parent()
            .and_then(ast::Block::cast)
            .and_then(|it| it.expr())
        {
            if expr.syntax() == node {
                return true;
            }
        }
        false
    })
}

fn is_semi_right_after(node: &SyntaxNode) -> bool {
    let mut node = node;
    loop {
        if let Some(next) = node.next_sibling() {
            if next.kind() == WHITESPACE {
                node = next;
                continue;
            }
            return next.kind() == SEMI;
        }
        return false;
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::assists::check_assist_range;

    #[test]
    fn test_introduce_var_simple() {
        check_assist_range(
            introduce_variable,
            "
fn foo() {
    foo(<|>1 + 1<|>);
}",
            "
fn foo() {
    let <|>var_name = 1 + 1;
    foo(var_name);
}",
        );
    }

    #[test]
    fn test_introduce_var_expr_stmt() {
        check_assist_range(
            introduce_variable,
            "
fn foo() {
    <|>1 + 1<|>;
}",
            "
fn foo() {
    let <|>var_name = 1 + 1;
}",
        );
    }

    #[test]
    fn test_introduce_var_part_of_expr_stmt() {
        check_assist_range(
            introduce_variable,
            "
fn foo() {
    <|>1<|> + 1;
}",
            "
fn foo() {
    let <|>var_name = 1;
    var_name + 1;
}",
        );
    }

    #[test]
    fn test_introduce_var_last_expr() {
        check_assist_range(
            introduce_variable,
            "
fn foo() {
    bar(<|>1 + 1<|>)
}",
            "
fn foo() {
    let <|>var_name = 1 + 1;
    bar(var_name)
}",
        );
    }

    #[test]
    fn test_introduce_var_last_full_expr() {
        check_assist_range(
            introduce_variable,
            "
fn foo() {
    <|>bar(1 + 1)<|>
}",
            "
fn foo() {
    let <|>var_name = bar(1 + 1);
    var_name
}",
        );
    }

    #[test]
    fn test_introduce_var_block_expr_second_to_last() {
        check_assist_range(
            introduce_variable,
            "
fn foo() {
    <|>{ let x = 0; x }<|>
    something_else();
}",
            "
fn foo() {
    let <|>var_name = { let x = 0; x };
    something_else();
}",
        );
    }
}
<commit_msg>delete unused fn<commit_after>use ra_syntax::{
    ast::{self, AstNode},
    SyntaxKind::{WHITESPACE, SEMI},
    SyntaxNode, TextUnit,
};

use crate::assists::{AssistCtx, Assist};

pub fn introduce_variable<'a>(ctx: AssistCtx) -> Option<Assist> {
    let node = ctx.covering_node();
    let expr = node.ancestors().filter_map(ast::Expr::cast).next()?;

    let anchor_stmt = anchor_stmt(expr)?;
    let indent = anchor_stmt.prev_sibling()?;
    if indent.kind() != WHITESPACE {
        return None;
    }
    ctx.build("introduce variable", move |edit| {
        let mut buf = String::new();

        buf.push_str("let var_name = ");
        expr.syntax().text().push_to(&mut buf);
        let full_stmt = ast::ExprStmt::cast(anchor_stmt);
        let is_full_stmt = if let Some(expr_stmt) = full_stmt {
            Some(expr.syntax()) == expr_stmt.expr().map(|e| e.syntax())
        } else {
            false
        };
        if is_full_stmt {
            if !full_stmt.unwrap().has_semi() {
                buf.push_str(";");
            }
            edit.replace(expr.syntax().range(), buf);
        } else {
            buf.push_str(";");
            indent.text().push_to(&mut buf);
            edit.replace(expr.syntax().range(), "var_name".to_string());
            edit.insert(anchor_stmt.range().start(), buf);
        }
        edit.set_cursor(anchor_stmt.range().start() + TextUnit::of_str("let "));
    })
}

/// Statement or last in the block expression, which will follow
/// the freshly introduced var.
fn anchor_stmt(expr: &ast::Expr) -> Option<&SyntaxNode> {
    expr.syntax().ancestors().find(|&node| {
        if ast::Stmt::cast(node).is_some() {
            return true;
        }
        if let Some(expr) = node
            .parent()
            .and_then(ast::Block::cast)
            .and_then(|it| it.expr())
        {
            if expr.syntax() == node {
                return true;
            }
        }
        false
    })
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::assists::check_assist_range;

    #[test]
    fn test_introduce_var_simple() {
        check_assist_range(
            introduce_variable,
            "
fn foo() {
    foo(<|>1 + 1<|>);
}",
            "
fn foo() {
    let <|>var_name = 1 + 1;
    foo(var_name);
}",
        );
    }

    #[test]
    fn test_introduce_var_expr_stmt() {
        check_assist_range(
            introduce_variable,
            "
fn foo() {
    <|>1 + 1<|>;
}",
            "
fn foo() {
    let <|>var_name = 1 + 1;
}",
        );
    }

    #[test]
    fn test_introduce_var_part_of_expr_stmt() {
        check_assist_range(
            introduce_variable,
            "
fn foo() {
    <|>1<|> + 1;
}",
            "
fn foo() {
    let <|>var_name = 1;
    var_name + 1;
}",
        );
    }

    #[test]
    fn test_introduce_var_last_expr() {
        check_assist_range(
            introduce_variable,
            "
fn foo() {
    bar(<|>1 + 1<|>)
}",
            "
fn foo() {
    let <|>var_name = 1 + 1;
    bar(var_name)
}",
        );
    }

    #[test]
    fn test_introduce_var_last_full_expr() {
        check_assist_range(
            introduce_variable,
            "
fn foo() {
    <|>bar(1 + 1)<|>
}",
            "
fn foo() {
    let <|>var_name = bar(1 + 1);
    var_name
}",
        );
    }

    #[test]
    fn test_introduce_var_block_expr_second_to_last() {
        check_assist_range(
            introduce_variable,
            "
fn foo() {
    <|>{ let x = 0; x }<|>
    something_else();
}",
            "
fn foo() {
    let <|>var_name = { let x = 0; x };
    something_else();
}",
        );
    }
}
<|endoftext|>


<commit_before>use ra_syntax::{
    ast::{self, AstNode},
    SyntaxKind::{WHITESPACE, SEMI},
    SyntaxNode, TextUnit,
};

use crate::assists::{AssistCtx, Assist};

pub fn introduce_variable<'a>(ctx: AssistCtx) -> Option<Assist> {
    let node = ctx.covering_node();
    let expr = node.ancestors().filter_map(ast::Expr::cast).next()?;

    let anchor_stmt = anchor_stmt(expr)?;
    let indent = anchor_stmt.prev_sibling()?;
    if indent.kind() != WHITESPACE {
        return None;
    }
    ctx.build("introduce variable", move |edit| {
        let mut buf = String::new();

        buf.push_str("let var_name = ");
        expr.syntax().text().push_to(&mut buf);
        let full_stmt = ast::ExprStmt::cast(anchor_stmt);
        let is_full_stmt = if let Some(expr_stmt) = full_stmt {
            Some(expr.syntax()) == expr_stmt.expr().map(|e| e.syntax())
        } else {
            false
        };
        if is_full_stmt {
            if !full_stmt.unwrap().has_semi() {
                buf.push_str(";");
            }
            edit.replace(expr.syntax().range(), buf);
        } else {
            buf.push_str(";");
            indent.text().push_to(&mut buf);
            edit.replace(expr.syntax().range(), "var_name".to_string());
            edit.insert(anchor_stmt.range().start(), buf);
        }
        edit.set_cursor(anchor_stmt.range().start() + TextUnit::of_str("let "));
    })
}

/// Statement or last in the block expression, which will follow
/// the freshly introduced var.
fn anchor_stmt(expr: &ast::Expr) -> Option<&SyntaxNode> {
    expr.syntax().ancestors().find(|&node| {
        if ast::Stmt::cast(node).is_some() {
            return true;
        }
        if let Some(expr) = node
            .parent()
            .and_then(ast::Block::cast)
            .and_then(|it| it.expr())
        {
            if expr.syntax() == node {
                return true;
            }
        }
        false
    })
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::assists::check_assist_range;

    #[test]
    fn test_introduce_var_simple() {
        check_assist_range(
            introduce_variable,
            "
fn foo() {
    foo(<|>1 + 1<|>);
}",
            "
fn foo() {
    let <|>var_name = 1 + 1;
    foo(var_name);
}",
        );
    }

    #[test]
    fn test_introduce_var_expr_stmt() {
        check_assist_range(
            introduce_variable,
            "
fn foo() {
    <|>1 + 1<|>;
}",
            "
fn foo() {
    let <|>var_name = 1 + 1;
}",
        );
    }

    #[test]
    fn test_introduce_var_part_of_expr_stmt() {
        check_assist_range(
            introduce_variable,
            "
fn foo() {
    <|>1<|> + 1;
}",
            "
fn foo() {
    let <|>var_name = 1;
    var_name + 1;
}",
        );
    }

    #[test]
    fn test_introduce_var_last_expr() {
        check_assist_range(
            introduce_variable,
            "
fn foo() {
    bar(<|>1 + 1<|>)
}",
            "
fn foo() {
    let <|>var_name = 1 + 1;
    bar(var_name)
}",
        );
    }

    #[test]
    fn test_introduce_var_last_full_expr() {
        check_assist_range(
            introduce_variable,
            "
fn foo() {
    <|>bar(1 + 1)<|>
}",
            "
fn foo() {
    let <|>var_name = bar(1 + 1);
    var_name
}",
        );
    }

    #[test]
    fn test_introduce_var_block_expr_second_to_last() {
        check_assist_range(
            introduce_variable,
            "
fn foo() {
    <|>{ let x = 0; x }<|>
    something_else();
}",
            "
fn foo() {
    let <|>var_name = { let x = 0; x };
    something_else();
}",
        );
    }
}
<commit_msg>don't need SEMI any more<commit_after>use ra_syntax::{
    ast::{self, AstNode},
    SyntaxKind::WHITESPACE,
    SyntaxNode, TextUnit,
};

use crate::assists::{AssistCtx, Assist};

pub fn introduce_variable<'a>(ctx: AssistCtx) -> Option<Assist> {
    let node = ctx.covering_node();
    let expr = node.ancestors().filter_map(ast::Expr::cast).next()?;

    let anchor_stmt = anchor_stmt(expr)?;
    let indent = anchor_stmt.prev_sibling()?;
    if indent.kind() != WHITESPACE {
        return None;
    }
    ctx.build("introduce variable", move |edit| {
        let mut buf = String::new();

        buf.push_str("let var_name = ");
        expr.syntax().text().push_to(&mut buf);
        let full_stmt = ast::ExprStmt::cast(anchor_stmt);
        let is_full_stmt = if let Some(expr_stmt) = full_stmt {
            Some(expr.syntax()) == expr_stmt.expr().map(|e| e.syntax())
        } else {
            false
        };
        if is_full_stmt {
            if !full_stmt.unwrap().has_semi() {
                buf.push_str(";");
            }
            edit.replace(expr.syntax().range(), buf);
        } else {
            buf.push_str(";");
            indent.text().push_to(&mut buf);
            edit.replace(expr.syntax().range(), "var_name".to_string());
            edit.insert(anchor_stmt.range().start(), buf);
        }
        edit.set_cursor(anchor_stmt.range().start() + TextUnit::of_str("let "));
    })
}

/// Statement or last in the block expression, which will follow
/// the freshly introduced var.
fn anchor_stmt(expr: &ast::Expr) -> Option<&SyntaxNode> {
    expr.syntax().ancestors().find(|&node| {
        if ast::Stmt::cast(node).is_some() {
            return true;
        }
        if let Some(expr) = node
            .parent()
            .and_then(ast::Block::cast)
            .and_then(|it| it.expr())
        {
            if expr.syntax() == node {
                return true;
            }
        }
        false
    })
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::assists::check_assist_range;

    #[test]
    fn test_introduce_var_simple() {
        check_assist_range(
            introduce_variable,
            "
fn foo() {
    foo(<|>1 + 1<|>);
}",
            "
fn foo() {
    let <|>var_name = 1 + 1;
    foo(var_name);
}",
        );
    }

    #[test]
    fn test_introduce_var_expr_stmt() {
        check_assist_range(
            introduce_variable,
            "
fn foo() {
    <|>1 + 1<|>;
}",
            "
fn foo() {
    let <|>var_name = 1 + 1;
}",
        );
    }

    #[test]
    fn test_introduce_var_part_of_expr_stmt() {
        check_assist_range(
            introduce_variable,
            "
fn foo() {
    <|>1<|> + 1;
}",
            "
fn foo() {
    let <|>var_name = 1;
    var_name + 1;
}",
        );
    }

    #[test]
    fn test_introduce_var_last_expr() {
        check_assist_range(
            introduce_variable,
            "
fn foo() {
    bar(<|>1 + 1<|>)
}",
            "
fn foo() {
    let <|>var_name = 1 + 1;
    bar(var_name)
}",
        );
    }

    #[test]
    fn test_introduce_var_last_full_expr() {
        check_assist_range(
            introduce_variable,
            "
fn foo() {
    <|>bar(1 + 1)<|>
}",
            "
fn foo() {
    let <|>var_name = bar(1 + 1);
    var_name
}",
        );
    }

    #[test]
    fn test_introduce_var_block_expr_second_to_last() {
        check_assist_range(
            introduce_variable,
            "
fn foo() {
    <|>{ let x = 0; x }<|>
    something_else();
}",
            "
fn foo() {
    let <|>var_name = { let x = 0; x };
    something_else();
}",
        );
    }
}
<|endoftext|>