<commit_before>#[crate_id = "github.com/csherratt/snowmew#snowmew-render:0.1"];
#[license = "ASL2"];
#[crate_type = "lib"];
#[comment = "A game engine in rust"];
#[allow(dead_code)];

extern crate std;
extern crate glfw = "glfw-rs";
extern crate cgmath;
extern crate snowmew;
extern crate cow;
extern crate gl;
extern crate OpenCL;
extern crate ovr = "ovr-rs";
extern crate collections;
extern crate extra;
extern crate native;
extern crate time;

pub use config::Config;

use std::ptr;
use std::mem;
use std::comm::{Chan, Port, Empty, Disconnected, Data};

use cgmath::vector::Vec3;
use cgmath::matrix::{Matrix, ToMat4};
use cgmath::rotation::Rotation3;
use cgmath::angle::{ToRad, deg};

use snowmew::core::{object_key};
use snowmew::camera::Camera;
use snowmew::display::Display;
use snowmew::input::InputHandle;

use db::Graphics;
use pipeline::{DrawTarget, Pipeline};
use drawlist::{Drawlist, DrawlistBindless, DrawlistStandard};
use OpenCL::hl::{CommandQueue};
use query::Query;
use compute_accelerator::PositionGlAccelerator;

mod db;
mod shader;
mod vertex_buffer;
mod drawlist;
mod hmd;
mod pipeline;
mod query;
mod compute_accelerator;
mod config;

enum RenderCommand {
    Update(snowmew::core::Database, object_key, object_key),
    Waiting(Chan<~Drawlist>),
    Complete(~Drawlist),
    Setup(Chan<Option<CommandQueue>>),
    Finish
}

fn swap_buffers(disp: &mut Display)
{
    disp.swap_buffers();
    unsafe {
        gl::DrawElements(gl::TRIANGLES, 6i32, gl::UNSIGNED_INT, ptr::null());
        let sync = gl::FenceSync(gl::SYNC_GPU_COMMANDS_COMPLETE, 0);
        gl::ClientWaitSync(sync, gl::SYNC_FLUSH_COMMANDS_BIT, 1_000_000_000u64);
        gl::DeleteSync(sync);
    }
}

fn render_task(chan: Chan<RenderCommand>)
{
    let (p, c) = Chan::new();
    chan.send(Setup(c.clone()));
    let _ = p.recv();

    let (p, c) = Chan::new();
    chan.send(Waiting(c.clone()));
    loop {
        let mut dl = p.recv();
        dl.setup_scene_async();
        chan.send(Waiting(c.clone()));
        chan.send(Complete(dl));
    }
}

fn render_server(port: Port<RenderCommand>, db: snowmew::core::Database, display: Display, ih: InputHandle)
{
    let (_, _, queue) = OpenCL::util::create_compute_context_prefer(OpenCL::util::GPU_PREFERED).unwrap();

    let mut queue = Some(queue);

    let mut db = db::Graphics::new(db);
    let mut scene = 0;
    let mut camera = 0;
    let mut display = display;
    let cfg = Config::new(display.get_context_version());

    display.make_current();
    gl::load_with(glfw::get_proc_address);

    let mut pipeline = if display.is_hmd() {
        ~pipeline::Hmd::new(pipeline::Forward::new(), 1.7, &display.hmd()) as ~pipeline::Pipeline
    } else {
        let (width, height) = display.size();
        ~pipeline::Defered::new(pipeline::Forward::new(), width, height) as ~pipeline::Pipeline
    };

    // todo move!
    gl::Enable(gl::SCISSOR_TEST);
    gl::Enable(gl::DEPTH_TEST);
    gl::Enable(gl::CULL_FACE);
    gl::Enable(gl::LINE_SMOOTH);
    gl::Enable(gl::BLEND);
    gl::CullFace(gl::BACK);
    glfw::set_swap_interval(1);

    db.load(&cfg);

    //let accl = PositionGlAccelerator::new();

    let mut drawlists = if cfg.use_bindless() {
        ~[]
    } else {
        ~[DrawlistStandard::from_config(&cfg),
          DrawlistStandard::from_config(&cfg)]
    };

    let mut waiting = ~[];

    let mut render_calc = Query::new();
    let mut render_scene = Query::new();

    loop {
        let cmd = if drawlists.len() == 0 || waiting.len() == 0 || scene == 0{
            Some(port.recv())
        } else {
            match port.try_recv() {
                Empty => None,
                Disconnected => return,
                Data(dat) => Some(dat)
            }
        };

        match cmd {
            Some(Setup(ch)) => {
                let mut out = None;
                mem::swap(&mut queue, &mut out);
                ch.send(out)
            },
            Some(Update(new, s, c)) => {
                db.update(new);
                db.load(&cfg);
                scene = s;
                camera = c;
            },
            Some(Waiting(ch)) => {
                if scene != 0 && drawlists.len() != 0 {
                    let mut dl = drawlists.pop().unwrap();
                    dl.bind_scene(db.clone(), scene);
                    ch.send(dl);
                } else {
                    waiting.push(ch);
                }
            },
            Some(Complete(mut dl)) => {
                let time = render_calc.start_time();
                dl.setup_scene();
                time.end();

                let render = render_scene.start_time();
                let rot = db.current.location(camera).unwrap().get().rot;
                let camera_trans = db.current.position(camera);

                let input = ih.get();
                let rift = input.predicted;
                let rift = rift.mul_q(&Rotation3::from_axis_angle(&Vec3::new(0f32, 1f32, 0f32), deg(180 as f32).to_rad()));

                let camera = Camera::new(rot.mul_q(&rift), camera_trans.mul_m(&rift.to_mat4()));

                let dt = DrawTarget::new(0, (0, 0), display.size());

                pipeline.render(dl, &db, &camera.get_matrices(display.size()), &dt);
                render.end();

                swap_buffers(&mut display);
                drawlists.push(dl);

                println!("{} {}", time.time_sync(), render.time_sync());

                render_calc = time.to_query();
                render_scene = render.to_query();
            },
            Some(Finish) => {
                return
            },
            None => {
                if drawlists.len() > 0 && waiting.len() > 0 {
                    println!("sending");
                    let ch = waiting.pop().unwrap();
                    let mut dl = drawlists.pop().unwrap();
                    dl.bind_scene(db.clone(), scene);
                    ch.send(dl);
                }
            }
        }
    }
}

pub struct RenderManager
{
    priv ch: Chan<RenderCommand>
}

impl RenderManager
{
    pub fn new(db: snowmew::core::Database, display: Display, ih: InputHandle) -> RenderManager
    {
        let (port, chan) = Chan::new();

        let mut taskopts = std::task::TaskOpts::new();
        taskopts.name = Some("render-main".into_maybe_owned());

        native::task::spawn_opts(taskopts, proc() {
            let db = db;
            let display = display;
            let ih = ih;

            render_server(port, db, display, ih);
        });

        let mut taskopts = std::task::TaskOpts::new();
        taskopts.name = Some("render worker #0".into_maybe_owned());

        let task_c = chan.clone();
        native::task::spawn_opts(taskopts, proc() {
            render_task(task_c);
        });

        RenderManager {
            ch: chan
        }
    }

    pub fn update(&mut self, db: snowmew::core::Database, scene: object_key, camera: object_key)
    {
        self.ch.send(Update(db, scene, camera));
    }
}

impl Drop for RenderManager
{
    fn drop(&mut self) {
        self.ch.send(Finish)
    }
}<commit_msg>added cleanup correctly<commit_after>#[crate_id = "github.com/csherratt/snowmew#snowmew-render:0.1"];
#[license = "ASL2"];
#[crate_type = "lib"];
#[comment = "A game engine in rust"];
#[allow(dead_code)];

extern crate std;
extern crate glfw = "glfw-rs";
extern crate cgmath;
extern crate snowmew;
extern crate cow;
extern crate gl;
extern crate OpenCL;
extern crate ovr = "ovr-rs";
extern crate collections;
extern crate extra;
extern crate native;
extern crate time;

pub use config::Config;

use std::ptr;
use std::mem;
use std::comm::{Chan, Port, Empty, Disconnected, Data};

use cgmath::vector::Vec3;
use cgmath::matrix::{Matrix, ToMat4};
use cgmath::rotation::Rotation3;
use cgmath::angle::{ToRad, deg};

use snowmew::core::{object_key};
use snowmew::camera::Camera;
use snowmew::display::Display;
use snowmew::input::InputHandle;

use db::Graphics;
use pipeline::{DrawTarget, Pipeline};
use drawlist::{Drawlist, DrawlistBindless, DrawlistStandard};
use OpenCL::hl::{CommandQueue};
use query::Query;
use compute_accelerator::PositionGlAccelerator;

mod db;
mod shader;
mod vertex_buffer;
mod drawlist;
mod hmd;
mod pipeline;
mod query;
mod compute_accelerator;
mod config;

enum RenderCommand {
    Update(snowmew::core::Database, object_key, object_key),
    Waiting(Chan<Option<~Drawlist>>),
    Complete(~Drawlist),
    Setup(Chan<Option<CommandQueue>>),
    Finish(Chan<()>)
}

fn swap_buffers(disp: &mut Display)
{
    disp.swap_buffers();
    unsafe {
        gl::DrawElements(gl::TRIANGLES, 6i32, gl::UNSIGNED_INT, ptr::null());
        let sync = gl::FenceSync(gl::SYNC_GPU_COMMANDS_COMPLETE, 0);
        gl::ClientWaitSync(sync, gl::SYNC_FLUSH_COMMANDS_BIT, 1_000_000_000u64);
        gl::DeleteSync(sync);
    }
}

fn render_task(chan: Chan<RenderCommand>)
{
    let (p, c) = Chan::new();
    chan.send(Setup(c.clone()));
    let _ = p.recv();

    let (p, c) = Chan::new();
    chan.send(Waiting(c.clone()));
    loop {
        for dl in p.iter() {
            match dl {
                Some(mut dl) => {
                    dl.setup_scene_async();
                    chan.send(Waiting(c.clone()));
                    chan.send(Complete(dl));
                },
                None => {
                    println!("render task: exiting");
                    return
                }
            }

        }
    }
}

fn render_server(port: Port<RenderCommand>, db: snowmew::core::Database, display: Display, ih: InputHandle)
{
    let (_, _, queue) = OpenCL::util::create_compute_context_prefer(OpenCL::util::GPU_PREFERED).unwrap();

    let mut queue = Some(queue);

    let mut db = db::Graphics::new(db);
    let mut scene = 0;
    let mut camera = 0;
    let mut display = display;
    let cfg = Config::new(display.get_context_version());

    display.make_current();
    gl::load_with(glfw::get_proc_address);

    let mut pipeline = if display.is_hmd() {
        ~pipeline::Hmd::new(pipeline::Forward::new(), 1.7, &display.hmd()) as ~pipeline::Pipeline
    } else {
        let (width, height) = display.size();
        ~pipeline::Defered::new(pipeline::Forward::new(), width, height) as ~pipeline::Pipeline
    };

    // todo move!
    gl::Enable(gl::SCISSOR_TEST);
    gl::Enable(gl::DEPTH_TEST);
    gl::Enable(gl::CULL_FACE);
    gl::Enable(gl::LINE_SMOOTH);
    gl::Enable(gl::BLEND);
    gl::CullFace(gl::BACK);
    glfw::set_swap_interval(1);

    db.load(&cfg);

    //let accl = PositionGlAccelerator::new();

    let mut drawlists = if cfg.use_bindless() {
        ~[]
    } else {
        ~[DrawlistStandard::from_config(&cfg),
          DrawlistStandard::from_config(&cfg)]
    };

    let mut num_workers = 1;
    let mut waiting = ~[];

    loop {
        let cmd = if drawlists.len() == 0 || waiting.len() == 0 || scene == 0 {
            Some(port.recv())
        } else {
            match port.try_recv() {
                Empty => None,
                Disconnected => return,
                Data(dat) => Some(dat)
            }
        };

        match cmd {
            Some(Setup(ch)) => {
                let mut out = None;
                mem::swap(&mut queue, &mut out);
                ch.send(out)
            },
            Some(Update(new, s, c)) => {
                db.update(new);
                db.load(&cfg);
                scene = s;
                camera = c;
            },
            Some(Waiting(ch)) => {
                if scene != 0 && drawlists.len() != 0 {
                    let mut dl = drawlists.pop().unwrap();
                    dl.bind_scene(db.clone(), scene);
                    ch.send(Some(dl));
                } else {
                    waiting.push(ch);
                }
            },
            Some(Complete(mut dl)) => {
                dl.setup_scene();
                let rot = db.current.location(camera).unwrap().get().rot;
                let camera_trans = db.current.position(camera);

                let input = ih.get();
                let rift = input.predicted;
                let rift = rift.mul_q(&Rotation3::from_axis_angle(&Vec3::new(0f32, 1f32, 0f32), deg(180 as f32).to_rad()));

                let camera = Camera::new(rot.mul_q(&rift), camera_trans.mul_m(&rift.to_mat4()));

                let dt = DrawTarget::new(0, (0, 0), display.size());

                pipeline.render(dl, &db, &camera.get_matrices(display.size()), &dt);

                swap_buffers(&mut display);
                drawlists.push(dl);
            },
            Some(Finish(ack)) => {
                // flush the port, this should release any
                // async drawlist workers
                println!("render: dropping waiting");
                while waiting.len() > 0 {
                    let c = waiting.pop().unwrap();
                    c.send(None);
                    num_workers -= 1;
                }
                println!("render: waiting for open connections to close");
                while num_workers > 0 {
                    match port.recv() {
                        Waiting(ch) => {
                            num_workers -= 1;
                            ch.send(None)
                        },
                        _ => ()
                    }
                }
                ack.send(());
                println!("render: exiting");
                return;
            },
            None => {
                if drawlists.len() > 0 && waiting.len() > 0 {
                    println!("sending");
                    let ch = waiting.pop().unwrap();
                    let mut dl = drawlists.pop().unwrap();
                    dl.bind_scene(db.clone(), scene);
                    ch.send(Some(dl));
                }
            }
        }
    }

}

pub struct RenderManager
{
    priv ch: Chan<RenderCommand>
}

impl RenderManager
{
    pub fn new(db: snowmew::core::Database, display: Display, ih: InputHandle) -> RenderManager
    {
        let (port, chan) = Chan::new();

        let mut taskopts = std::task::TaskOpts::new();
        taskopts.name = Some("render-main".into_maybe_owned());

        native::task::spawn_opts(taskopts, proc() {
            let db = db;
            let display = display;
            let ih = ih;

            render_server(port, db, display, ih);
        });

        let mut taskopts = std::task::TaskOpts::new();
        taskopts.name = Some("render worker #0".into_maybe_owned());

        let task_c = chan.clone();
        native::task::spawn_opts(taskopts, proc() {
            render_task(task_c);
        });

        RenderManager {
            ch: chan
        }
    }

    pub fn update(&mut self, db: snowmew::core::Database, scene: object_key, camera: object_key)
    {
        self.ch.send(Update(db, scene, camera));
    }
}

impl Drop for RenderManager
{
    fn drop(&mut self) {
        let (p, c) = Chan::new();
        self.ch.send(Finish(c));
        let _ = self.ch;
        p.recv();
    }
}<|endoftext|>


<commit_before>use std::io;
#[cfg(unix)]
use std::os::unix::io::{AsRawFd, RawFd};
use std::time::Duration;

pub type Event = usize;

pub type Events = Vec<Event>;

#[derive(Debug)]
pub struct Selector {}

impl Selector {
    pub fn try_clone(&self) -> io::Result<Selector> {
        os_required!();
    }

    pub fn select(&self, _: &mut Events, _: Option<Duration>) -> io::Result<()> {
        os_required!();
    }
}

#[cfg(unix)]
cfg_any_os_util! {
    use crate::{Interest, Token};

    impl Selector {
        pub fn register(&self, _: RawFd, _: Token, _: Interest) -> io::Result<()> {
            os_required!();
        }

        pub fn reregister(&self, _: RawFd, _: Token, _: Interest) -> io::Result<()> {
            os_required!();
        }

        pub fn deregister(&self, _: RawFd) -> io::Result<()> {
            os_required!();
        }
    }
}

cfg_net! {
    #[cfg(debug_assertions)]
    impl Selector {
        pub fn id(&self) -> usize {
            os_required!();
        }
    }
}

#[cfg(unix)]
impl AsRawFd for Selector {
    fn as_raw_fd(&self) -> RawFd {
        os_required!()
    }
}

pub mod event {
    use crate::sys::Event;
    use crate::Token;
    use std::fmt;

    pub fn token(_: &Event) -> Token {
        os_required!();
    }

    pub fn is_readable(_: &Event) -> bool {
        os_required!();
    }

    pub fn is_writable(_: &Event) -> bool {
        os_required!();
    }

    pub fn is_error(_: &Event) -> bool {
        os_required!();
    }

    pub fn is_read_closed(_: &Event) -> bool {
        os_required!();
    }

    pub fn is_write_closed(_: &Event) -> bool {
        os_required!();
    }

    pub fn is_priority(_: &Event) -> bool {
        os_required!();
    }

    pub fn is_aio(_: &Event) -> bool {
        os_required!();
    }

    pub fn is_lio(_: &Event) -> bool {
        os_required!();
    }

    pub fn debug_details(_: &mut fmt::Formatter<'_>, _: &Event) -> fmt::Result {
        os_required!();
    }
}
<commit_msg>Silence some Clippy warnings<commit_after>use std::io;
#[cfg(unix)]
use std::os::unix::io::{AsRawFd, RawFd};
use std::time::Duration;

pub type Event = usize;

pub type Events = Vec<Event>;

#[derive(Debug)]
pub struct Selector {}

impl Selector {
    pub fn try_clone(&self) -> io::Result<Selector> {
        os_required!();
    }

    pub fn select(&self, _: &mut Events, _: Option<Duration>) -> io::Result<()> {
        os_required!();
    }
}

#[cfg(unix)]
cfg_any_os_util! {
    use crate::{Interest, Token};

    impl Selector {
        pub fn register(&self, _: RawFd, _: Token, _: Interest) -> io::Result<()> {
            os_required!();
        }

        pub fn reregister(&self, _: RawFd, _: Token, _: Interest) -> io::Result<()> {
            os_required!();
        }

        pub fn deregister(&self, _: RawFd) -> io::Result<()> {
            os_required!();
        }
    }
}

cfg_net! {
    #[cfg(debug_assertions)]
    impl Selector {
        pub fn id(&self) -> usize {
            os_required!();
        }
    }
}

#[cfg(unix)]
impl AsRawFd for Selector {
    fn as_raw_fd(&self) -> RawFd {
        os_required!()
    }
}

#[allow(clippy::trivially_copy_pass_by_ref)]
pub mod event {
    use crate::sys::Event;
    use crate::Token;
    use std::fmt;

    pub fn token(_: &Event) -> Token {
        os_required!();
    }

    pub fn is_readable(_: &Event) -> bool {
        os_required!();
    }

    pub fn is_writable(_: &Event) -> bool {
        os_required!();
    }

    pub fn is_error(_: &Event) -> bool {
        os_required!();
    }

    pub fn is_read_closed(_: &Event) -> bool {
        os_required!();
    }

    pub fn is_write_closed(_: &Event) -> bool {
        os_required!();
    }

    pub fn is_priority(_: &Event) -> bool {
        os_required!();
    }

    pub fn is_aio(_: &Event) -> bool {
        os_required!();
    }

    pub fn is_lio(_: &Event) -> bool {
        os_required!();
    }

    pub fn debug_details(_: &mut fmt::Formatter<'_>, _: &Event) -> fmt::Result {
        os_required!();
    }
}
<|endoftext|>


<commit_before>extern crate rand;

use std::io;
use std::cmp::Ordering;
use rand::Rng;

fn main() {
    println!("Guess the number!");

    let secret_number = rand::thread_rng().gen_range(1, 101);

    loop {
        println!("Please input your guess.");

        let mut guess = String::new();

        io::stdin()
            .read_line(&mut guess)
            .ok()
            .expect("Failed to read line");

        let guess: u32 = match guess.trim().parse() {
            Ok(num) => num,
            Err(_) => continue,
        };

        println!("You guessed: {}", guess);

        match guess.cmp(&secret_number) {
            Ordering::Less => println!("Too small!"),
            Ordering::Greater => println!("Too big!"),
            Ordering::Equal => {
                println!("You win!");
                break;
            }
        }
    }
}
<commit_msg>guessing_game: delete ok()<commit_after>extern crate rand;

use std::io;
use std::cmp::Ordering;
use rand::Rng;

fn main() {
    println!("Guess the number!");

    let secret_number = rand::thread_rng().gen_range(1, 101);

    loop {
        println!("Please input your guess.");

        let mut guess = String::new();

        io::stdin()
            .read_line(&mut guess)
            .expect("Failed to read line");

        let guess: u32 = match guess.trim().parse() {
            Ok(num) => num,
            Err(_) => continue,
        };

        println!("You guessed: {}", guess);

        match guess.cmp(&secret_number) {
            Ordering::Less => println!("Too small!"),
            Ordering::Greater => println!("Too big!"),
            Ordering::Equal => {
                println!("You win!");
                break;
            }
        }
    }
}
<|endoftext|>


<commit_before>extern crate nom;

use std::str;
use nom::{
    is_hex_digit,
    digit,
    alpha,
    alphanumeric,
};

fn is_ws(c: char) -> bool {
    let u = c as u32;
    u == 0x20 || u == 0x9 || u == 0xD || u == 0xA
}

fn is_anon(c: char) -> bool {
    c == '[' || c == ']' || is_ws(c)
}

#[derive(PartialEq, Debug)]
pub struct Langtag<'a> {
    language: &'a str,
    subtags:  Vec<&'a str>,
}
/* [144s] LANGTAG */
named!(pub langtag<&str, Langtag>, do_parse!(
    tag!("@") >>
    language: alpha >>
    tag!("-") >>
    subtags:  separated_list_complete!(
        tag!("-"),
        alphanumeric
    ) >>
    (Langtag{
        language: language,
        subtags:  subtags,
    })
));

#[derive(PartialEq, Debug)]
pub struct Integer<'a> {
    sign:          Option<&'a str>,
    integer_part:  &'a str,
}
/* [19] INTEGER */
named!(pub integer<&str, Integer>, do_parse!(
    sign:         opt!(alt!(tag!("+") | tag!("-"))) >>
    integer_part: digit                             >>
    (Integer{
        sign:          sign,
        integer_part:  integer_part,
    })
));

#[derive(PartialEq, Debug)]
pub struct Decimal<'a> {
    sign:          Option<&'a str>,
    integer_part:  Option<&'a str>,
    decimal_point: &'a str,
    decimal_part:  &'a str,
}
/* [20] DECIMAL */
named!(pub decimal<&str, Decimal>, do_parse!(
    sign:          opt!(alt!(tag!("+") | tag!("-"))) >>
    integer_part:  opt!(digit)                       >>
    decimal_point: tag!(".")                         >>
    decimal_part:  digit                             >>
    (Decimal{
        sign:          sign,
        integer_part:  integer_part,
        decimal_point: decimal_point,
        decimal_part:  decimal_part,
    })
));

#[derive(PartialEq, Debug)]
pub enum Mantissa<'a> {
    IntegerDecimal {
        integer_part:  &'a str,
        decimal_point: &'a str,
        decimal_part:  Option<&'a str>,
    },
    Decimal {
        decimal_point: &'a str,
        decimal_part:  &'a str,
    },
    Integer {
        integer_part: &'a str,
    },
}
#[derive(PartialEq, Debug)]
pub struct Double<'a> {
    sign:     Option<&'a str>,
    mantissa: Mantissa<'a>,
    exponent: Exponent<'a>,
}
/* [21] DOUBLE */
named!(pub double<&str, Double>, do_parse!(
    sign:     opt!(alt!(tag!("+") | tag!("-"))) >>
    mantissa: alt!(
        do_parse!(
            integer_part:  digit       >>
            decimal_point: tag!(".")   >>
            decimal_part:  opt!(digit) >>
            (Mantissa::IntegerDecimal{
                integer_part:  integer_part,
                decimal_point: decimal_point,
                decimal_part:  decimal_part,
            })
        ) |
        do_parse!(
            decimal_point: tag!(".") >>
            decimal_part:  digit     >>
            (Mantissa::Decimal{
                decimal_point: decimal_point,
                decimal_part:  decimal_part,
            })
        ) |
        do_parse!(
            integer_part: digit >>
            (Mantissa::Integer{
                integer_part: integer_part,
            })
        )
    ) >>
    exponent: exponent >>
    (Double{
        sign:     sign,
        mantissa: mantissa,
        exponent: exponent,
    })
));

#[derive(PartialEq, Debug)]
pub struct Exponent<'a> {
    exponent_symbol: &'a str,
    sign:            Option<&'a str>,
    exponent_number: &'a str,
}

/* [154s] EXPONENT */
named!(pub exponent<&str, Exponent>, do_parse!(
    exponent_symbol: alt!(tag!("e") | tag!("E"))       >>
    sign:            opt!(alt!(tag!("+") | tag!("-"))) >>
    exponent_number: digit                             >>
    (Exponent{
        exponent_symbol: exponent_symbol,
        sign:            sign,
        exponent_number: exponent_number,
    })
));

/* [161s] WS */
named!(pub ws<&str, &str>, take_while_s!(is_ws));

/* [162s] ANON */
named!(pub anon<&str, &str>, verify!(
    take_while_s!(is_anon),
    |val:&str| {
        let len = val.char_indices().count();
        val.char_indices().all(|(idx, c)| {
            if idx == 0 {
                c == '['
            } else if idx == len - 1 {
                c == ']'
            } else {
                is_ws(c)
            }
        })
    }
));

/* [170s] PERCENT */
named!(pub percent<&str, &str>, verify!(
    take_s!(3),
    |val:&str| {
        let bytes = val.as_bytes();
        bytes[0] == 0x25 && is_hex_digit(bytes[1]) && is_hex_digit(bytes[2])
    }
));

#[cfg(test)]
mod test {
    use super::*;
    use nom::IResult;
    use nom::ErrorKind;

    #[test]
    fn langtag_normal_test() {
        let input    = "@en-a-bbb-a-ccc,rest";
        let expected = IResult::Done(",rest", Langtag{
            language: "en",
            subtags:  vec!["a", "bbb", "a", "ccc"]
        });
        assert_eq!(langtag(input), expected);
    }

    #[test]
    fn langtag_abnormal_test() {
        let input    = "@en01-a-bbb-a-ccc,rest";
        let expected = IResult::Error(ErrorKind::Tag);
        assert_eq!(langtag(input), expected);
    }

    #[test]
    fn integer_normal_test() {
        let input    = "-1234rest";
        let expected = IResult::Done("rest", Integer{
            sign:          Some("-"),
            integer_part:  "1234",
        });
        assert_eq!(integer(input), expected);
    }

    #[test]
    fn decimal_normal_test() {
        let input    = "-1234.5678rest";
        let expected = IResult::Done("rest", Decimal{
            sign:          Some("-"),
            integer_part:  Some("1234"),
            decimal_point: ".",
            decimal_part:  "5678",
        });
        assert_eq!(decimal(input), expected);
    }

    #[test]
    fn double_normal_test() {
        let input    = "-1234.5678e-0123456789rest";
        let expected = IResult::Done("rest", Double{
            sign:     Some("-"),
            mantissa: Mantissa::IntegerDecimal {
                integer_part:  "1234",
                decimal_point: ".",
                decimal_part:  Some("5678"),
            },
            exponent: Exponent {
                exponent_symbol: "e",
                sign:            Some("-"),
                exponent_number: "0123456789",
            },
        });
        assert_eq!(double(input), expected);

        let input    = "-.5678e-0123456789rest";
        let expected = IResult::Done("rest", Double{
            sign:     Some("-"),
            mantissa: Mantissa::Decimal {
                decimal_point: ".",
                decimal_part:  "5678",
            },
            exponent: Exponent {
                exponent_symbol: "e",
                sign:            Some("-"),
                exponent_number: "0123456789",
            },
        });
        assert_eq!(double(input), expected);

        let input    = "+5678e-0123456789rest";
        let expected = IResult::Done("rest", Double{
            sign:     Some("+"),
            mantissa: Mantissa::Integer {
                integer_part: "5678",
            },
            exponent: Exponent {
                exponent_symbol: "e",
                sign:            Some("-"),
                exponent_number: "0123456789",
            },
        });
        assert_eq!(double(input), expected);
    }

    #[test]
    fn exponent_normal_test() {
        let input    = "e-0123456789rest";
        let expected = IResult::Done("rest", Exponent{
            exponent_symbol: "e",
            sign:            Some("-"),
            exponent_number: "0123456789",
        });
        assert_eq!(exponent(input), expected);

        let input    = "E+0123456789rest";
        let expected = IResult::Done("rest", Exponent{
            exponent_symbol: "E",
            sign:            Some("+"),
            exponent_number: "0123456789",
        });
        assert_eq!(exponent(input), expected);

        let input    = "e0123456789rest";
        let expected = IResult::Done("rest", Exponent{
            exponent_symbol: "e",
            sign:            None,
            exponent_number: "0123456789",
        });
        assert_eq!(exponent(input), expected);
    }

    #[test]
    fn ws_test() {
        assert_eq!(ws("   a"), IResult::Done("a", "   "));
    }

    #[test]
    fn anon_test() {
        assert_eq!(anon("[ ]a"), IResult::Done("a", "[ ]"));
    }

    #[test]
    fn percent_test() {
        assert_eq!(percent("%2Abc"), IResult::Done("bc", "%2A"));
    }
}
<commit_msg>Use exponent parser instead of Exponent struct for 'double' test<commit_after>extern crate nom;

use std::str;
use nom::{
    is_hex_digit,
    digit,
    alpha,
    alphanumeric,
};

fn is_ws(c: char) -> bool {
    let u = c as u32;
    u == 0x20 || u == 0x9 || u == 0xD || u == 0xA
}

fn is_anon(c: char) -> bool {
    c == '[' || c == ']' || is_ws(c)
}

#[derive(PartialEq, Debug)]
pub struct Langtag<'a> {
    language: &'a str,
    subtags:  Vec<&'a str>,
}
/* [144s] LANGTAG */
named!(pub langtag<&str, Langtag>, do_parse!(
    tag!("@") >>
    language: alpha >>
    tag!("-") >>
    subtags:  separated_list_complete!(
        tag!("-"),
        alphanumeric
    ) >>
    (Langtag{
        language: language,
        subtags:  subtags,
    })
));

#[derive(PartialEq, Debug)]
pub struct Integer<'a> {
    sign:          Option<&'a str>,
    integer_part:  &'a str,
}
/* [19] INTEGER */
named!(pub integer<&str, Integer>, do_parse!(
    sign:         opt!(alt!(tag!("+") | tag!("-"))) >>
    integer_part: digit                             >>
    (Integer{
        sign:          sign,
        integer_part:  integer_part,
    })
));

#[derive(PartialEq, Debug)]
pub struct Decimal<'a> {
    sign:          Option<&'a str>,
    integer_part:  Option<&'a str>,
    decimal_point: &'a str,
    decimal_part:  &'a str,
}
/* [20] DECIMAL */
named!(pub decimal<&str, Decimal>, do_parse!(
    sign:          opt!(alt!(tag!("+") | tag!("-"))) >>
    integer_part:  opt!(digit)                       >>
    decimal_point: tag!(".")                         >>
    decimal_part:  digit                             >>
    (Decimal{
        sign:          sign,
        integer_part:  integer_part,
        decimal_point: decimal_point,
        decimal_part:  decimal_part,
    })
));

#[derive(PartialEq, Debug)]
pub enum Mantissa<'a> {
    IntegerDecimal {
        integer_part:  &'a str,
        decimal_point: &'a str,
        decimal_part:  Option<&'a str>,
    },
    Decimal {
        decimal_point: &'a str,
        decimal_part:  &'a str,
    },
    Integer {
        integer_part: &'a str,
    },
}
#[derive(PartialEq, Debug)]
pub struct Double<'a> {
    sign:     Option<&'a str>,
    mantissa: Mantissa<'a>,
    exponent: Exponent<'a>,
}
/* [21] DOUBLE */
named!(pub double<&str, Double>, do_parse!(
    sign:     opt!(alt!(tag!("+") | tag!("-"))) >>
    mantissa: alt!(
        do_parse!(
            integer_part:  digit       >>
            decimal_point: tag!(".")   >>
            decimal_part:  opt!(digit) >>
            (Mantissa::IntegerDecimal{
                integer_part:  integer_part,
                decimal_point: decimal_point,
                decimal_part:  decimal_part,
            })
        ) |
        do_parse!(
            decimal_point: tag!(".") >>
            decimal_part:  digit     >>
            (Mantissa::Decimal{
                decimal_point: decimal_point,
                decimal_part:  decimal_part,
            })
        ) |
        do_parse!(
            integer_part: digit >>
            (Mantissa::Integer{
                integer_part: integer_part,
            })
        )
    ) >>
    exponent: exponent >>
    (Double{
        sign:     sign,
        mantissa: mantissa,
        exponent: exponent,
    })
));

#[derive(PartialEq, Debug)]
pub struct Exponent<'a> {
    exponent_symbol: &'a str,
    sign:            Option<&'a str>,
    exponent_number: &'a str,
}

/* [154s] EXPONENT */
named!(pub exponent<&str, Exponent>, do_parse!(
    exponent_symbol: alt!(tag!("e") | tag!("E"))       >>
    sign:            opt!(alt!(tag!("+") | tag!("-"))) >>
    exponent_number: digit                             >>
    (Exponent{
        exponent_symbol: exponent_symbol,
        sign:            sign,
        exponent_number: exponent_number,
    })
));

/* [161s] WS */
named!(pub ws<&str, &str>, take_while_s!(is_ws));

/* [162s] ANON */
named!(pub anon<&str, &str>, verify!(
    take_while_s!(is_anon),
    |val:&str| {
        let len = val.char_indices().count();
        val.char_indices().all(|(idx, c)| {
            if idx == 0 {
                c == '['
            } else if idx == len - 1 {
                c == ']'
            } else {
                is_ws(c)
            }
        })
    }
));

/* [170s] PERCENT */
named!(pub percent<&str, &str>, verify!(
    take_s!(3),
    |val:&str| {
        let bytes = val.as_bytes();
        bytes[0] == 0x25 && is_hex_digit(bytes[1]) && is_hex_digit(bytes[2])
    }
));

#[cfg(test)]
mod test {
    use super::*;
    use nom::IResult;
    use nom::ErrorKind;

    #[test]
    fn langtag_normal_test() {
        let input    = "@en-a-bbb-a-ccc,rest";
        let expected = IResult::Done(",rest", Langtag{
            language: "en",
            subtags:  vec!["a", "bbb", "a", "ccc"]
        });
        assert_eq!(langtag(input), expected);
    }

    #[test]
    fn langtag_abnormal_test() {
        let input    = "@en01-a-bbb-a-ccc,rest";
        let expected = IResult::Error(ErrorKind::Tag);
        assert_eq!(langtag(input), expected);
    }

    #[test]
    fn integer_normal_test() {
        let input    = "-1234rest";
        let expected = IResult::Done("rest", Integer{
            sign:          Some("-"),
            integer_part:  "1234",
        });
        assert_eq!(integer(input), expected);
    }

    #[test]
    fn decimal_normal_test() {
        let input    = "-1234.5678rest";
        let expected = IResult::Done("rest", Decimal{
            sign:          Some("-"),
            integer_part:  Some("1234"),
            decimal_point: ".",
            decimal_part:  "5678",
        });
        assert_eq!(decimal(input), expected);
    }

    #[test]
    fn double_normal_test() {
        let input    = "-1234.5678e-0123456789rest";
        let expected = IResult::Done("rest", Double{
            sign:     Some("-"),
            mantissa: Mantissa::IntegerDecimal {
                integer_part:  "1234",
                decimal_point: ".",
                decimal_part:  Some("5678"),
            },
            exponent: exponent("e-0123456789").unwrap().1,
        });
        assert_eq!(double(input), expected);

        let input    = "-.5678e-0123456789rest";
        let expected = IResult::Done("rest", Double{
            sign:     Some("-"),
            mantissa: Mantissa::Decimal {
                decimal_point: ".",
                decimal_part:  "5678",
            },
            exponent: exponent("e-0123456789").unwrap().1,
        });
        assert_eq!(double(input), expected);

        let input    = "+5678e-0123456789rest";
        let expected = IResult::Done("rest", Double{
            sign:     Some("+"),
            mantissa: Mantissa::Integer {
                integer_part: "5678",
            },
            exponent: exponent("e-0123456789").unwrap().1,
        });
        assert_eq!(double(input), expected);
    }

    #[test]
    fn exponent_normal_test() {
        let input    = "e-0123456789rest";
        let expected = IResult::Done("rest", Exponent{
            exponent_symbol: "e",
            sign:            Some("-"),
            exponent_number: "0123456789",
        });
        assert_eq!(exponent(input), expected);

        let input    = "E+0123456789rest";
        let expected = IResult::Done("rest", Exponent{
            exponent_symbol: "E",
            sign:            Some("+"),
            exponent_number: "0123456789",
        });
        assert_eq!(exponent(input), expected);

        let input    = "e0123456789rest";
        let expected = IResult::Done("rest", Exponent{
            exponent_symbol: "e",
            sign:            None,
            exponent_number: "0123456789",
        });
        assert_eq!(exponent(input), expected);
    }

    #[test]
    fn ws_test() {
        assert_eq!(ws("   a"), IResult::Done("a", "   "));
    }

    #[test]
    fn anon_test() {
        assert_eq!(anon("[ ]a"), IResult::Done("a", "[ ]"));
    }

    #[test]
    fn percent_test() {
        assert_eq!(percent("%2Abc"), IResult::Done("bc", "%2A"));
    }
}
<|endoftext|>


<commit_before>use algo;
use grammar;
use lexer::{tokenize, Token};
use text_unit::{TextRange, TextUnit};
use yellow::{self, SyntaxNodeRef, GreenNode, SyntaxError};
use parser_impl;
use parser_api::Parser;
use {
    SyntaxKind::*,
};
use text_utils::replace_range;

#[derive(Debug, Clone)]
pub struct AtomEdit {
    pub delete: TextRange,
    pub insert: String,
}

impl AtomEdit {
    pub fn replace(range: TextRange, replace_with: String) -> AtomEdit {
        AtomEdit { delete: range, insert: replace_with }
    }

    pub fn delete(range: TextRange) -> AtomEdit {
        AtomEdit::replace(range, String::new())
    }

    pub fn insert(offset: TextUnit, text: String) -> AtomEdit {
        AtomEdit::replace(TextRange::offset_len(offset, 0.into()), text)
    }
}

pub(crate) fn incremental_reparse(
    node: SyntaxNodeRef,
    edit: &AtomEdit,
    errors: Vec<SyntaxError>,
) -> Option<(GreenNode, Vec<SyntaxError>)> {
    let (node, green, new_errors) =
        reparse_leaf(node, &edit).or_else(|| reparse_block(node, &edit))?;
    let green_root = node.replace_with(green);
    let errors = merge_errors(errors, new_errors, node, edit);
    Some((green_root, errors))
}

fn reparse_leaf<'node>(
    node: SyntaxNodeRef<'node>,
    edit: &AtomEdit,
) -> Option<(SyntaxNodeRef<'node>, GreenNode, Vec<SyntaxError>)> {
    let node = algo::find_covering_node(node, edit.delete);
    match node.kind() {
        | WHITESPACE
        | COMMENT
        | DOC_COMMENT
        | IDENT
        | STRING
        | RAW_STRING => {
            let text = get_text_after_edit(node, &edit);
            let tokens = tokenize(&text);
            let token = match tokens[..] {
                [token] if token.kind == node.kind() => token,
                _ => return None,
            };

            if token.kind == IDENT && is_contextual_kw(&text) {
                return None;
            }

            let green = GreenNode::new_leaf(node.kind(), &text);
            let new_errors = vec![];
            Some((node, green, new_errors))
        }
        _ => None,
    }
}

fn reparse_block<'node>(
    node: SyntaxNodeRef<'node>,
    edit: &AtomEdit,
) -> Option<(SyntaxNodeRef<'node>, GreenNode, Vec<SyntaxError>)> {
    let (node, reparser) = find_reparsable_node(node, edit.delete)?;
    let text = get_text_after_edit(node, &edit);
    let tokens = tokenize(&text);
    if !is_balanced(&tokens) {
        return None;
    }
    let (green, new_errors) =
        parser_impl::parse_with::<yellow::GreenBuilder>(
            &text, &tokens, reparser,
        );
    Some((node, green, new_errors))
}

fn get_text_after_edit(node: SyntaxNodeRef, edit: &AtomEdit) -> String {
    replace_range(
        node.text().to_string(),
        edit.delete - node.range().start(),
        &edit.insert,
    )
}

fn is_contextual_kw(text: &str) -> bool {
    match text {
        | "auto"
        | "default"
        | "union" => true,
        _ => false,
    }
}

fn find_reparsable_node<'node>(
    node: SyntaxNodeRef<'node>,
    range: TextRange,
) -> Option<(SyntaxNodeRef<'node>, fn(&mut Parser))> {
    let node = algo::find_covering_node(node, range);
    return algo::ancestors(node)
        .filter_map(|node| reparser(node).map(|r| (node, r)))
        .next();

    fn reparser(node: SyntaxNodeRef) -> Option<fn(&mut Parser)> {
        let res = match node.kind() {
            BLOCK => grammar::block,
            NAMED_FIELD_DEF_LIST => grammar::named_field_def_list,
            NAMED_FIELD_LIST => grammar::named_field_list,
            ENUM_VARIANT_LIST => grammar::enum_variant_list,
            MATCH_ARM_LIST => grammar::match_arm_list,
            USE_TREE_LIST => grammar::use_tree_list,
            EXTERN_ITEM_LIST => grammar::extern_item_list,
            TOKEN_TREE if node.first_child().unwrap().kind() == L_CURLY => grammar::token_tree,
            ITEM_LIST => {
                let parent = node.parent().unwrap();
                match parent.kind() {
                    IMPL_ITEM => grammar::impl_item_list,
                    TRAIT_DEF => grammar::trait_item_list,
                    MODULE => grammar::mod_item_list,
                    _ => return None,
                }
            }
            _ => return None,
        };
        Some(res)
    }
}

fn is_balanced(tokens: &[Token]) -> bool {
    if tokens.len() == 0
        || tokens.first().unwrap().kind != L_CURLY
        || tokens.last().unwrap().kind != R_CURLY {
        return false;
    }
    let mut balance = 0usize;
    for t in tokens.iter() {
        match t.kind {
            L_CURLY => balance += 1,
            R_CURLY => balance = match balance.checked_sub(1) {
                Some(b) => b,
                None => return false,
            },
            _ => (),
        }
    }
    balance == 0
}

fn merge_errors(
    old_errors: Vec<SyntaxError>,
    new_errors: Vec<SyntaxError>,
    old_node: SyntaxNodeRef,
    edit: &AtomEdit,
) -> Vec<SyntaxError> {
    let mut res = Vec::new();
    for e in old_errors {
        if e.offset <= old_node.range().start() {
            res.push(e)
        } else if e.offset >= old_node.range().end() {
            res.push(SyntaxError {
                msg: e.msg,
                offset: e.offset + TextUnit::of_str(&edit.insert) - edit.delete.len(),
            })
        }
    }
    for e in new_errors {
        res.push(SyntaxError {
            msg: e.msg,
            offset: e.offset + old_node.range().start(),
        })
    }
    res
}

#[cfg(test)]
use super::{File, text_utils, test_utils, utils};

#[cfg(test)]
mod tests {
    use super::{*, utils::dump_tree};

    #[test]
    fn reparse_test() {
        fn do_check(before: &str, replace_with: &str) {
            let (range, before) = test_utils::extract_range(before);
            let after = text_utils::replace_range(before.clone(), range, replace_with);

            let fully_reparsed = File::parse(&after);
            let incrementally_reparsed = {
                let f = File::parse(&before);
                let edit = AtomEdit { delete: range, insert: replace_with.to_string() };
                f.incremental_reparse(&edit).expect("cannot incrementally reparse")
            };
            assert_eq_text!(
                &dump_tree(fully_reparsed.syntax()),
                &dump_tree(incrementally_reparsed.syntax()),
            )
        }

        do_check(r"
fn foo() {
    let x = foo + <|>bar<|>
}
", "baz");
        do_check(r"
fn foo() {
    let x = foo<|> + bar<|>
}
", "baz");
        do_check(r"
struct Foo {
    f: foo<|><|>
}
", ",\n    g: (),");
        do_check(r"
fn foo {
    let;
    1 + 1;
    <|>92<|>;
}
", "62");
        do_check(r"
mod foo {
    fn <|><|>
}
", "bar");
        do_check(r"
trait Foo {
    type <|>Foo<|>;
}
", "Output");
        do_check(r"
trait Foo {
    type<|> Foo<|>;
}
", "Output");
        do_check(r"
impl IntoIterator<Item=i32> for Foo {
    f<|><|>
}
", "n next(");
        do_check(r"
use a::b::{foo,<|>,bar<|>};
    ", "baz");
        do_check(r"
pub enum A {
    Foo<|><|>
}
", "\nBar;\n");
        do_check(r"
foo!{a, b<|><|> d}
", ", c[3]");
        do_check(r"
fn foo() {
    vec![<|><|>]
}
", "123");
        do_check(r"
extern {
    fn<|>;<|>
}
", " exit(code: c_int)");
        do_check(r"<|><|>
fn foo() -> i32 {
    1
}
", "\n\n\n   \n");
        do_check(r"
fn foo() -> <|><|> {}
", "  \n");
        do_check(r"
fn <|>foo<|>() -> i32 {
    1
}
", "bar");
        do_check(r"
fn aa<|><|>bb() {

}
", "foofoo");
        do_check(r"
fn aabb /* <|><|> */ () {

}
", "some comment");
        do_check(r"
fn aabb <|><|> () {

}
", "    \t\t\n\n");
        do_check(r"
trait foo {
// comment <|><|>
}
", "\n");
        do_check(r"
/// good <|><|>omment
mod {
}
", "c");
        do_check(r#"
fn -> &str { "Hello<|><|>" }
"#, ", world");
        do_check(r#"
fn -> &str { // "Hello<|><|>"
"#, ", world");
    }
}<commit_msg>independent tests for incremental reparsing of blocks and leaves<commit_after>use algo;
use grammar;
use lexer::{tokenize, Token};
use text_unit::{TextRange, TextUnit};
use yellow::{self, SyntaxNodeRef, GreenNode, SyntaxError};
use parser_impl;
use parser_api::Parser;
use {
    SyntaxKind::*,
};
use text_utils::replace_range;

#[derive(Debug, Clone)]
pub struct AtomEdit {
    pub delete: TextRange,
    pub insert: String,
}

impl AtomEdit {
    pub fn replace(range: TextRange, replace_with: String) -> AtomEdit {
        AtomEdit { delete: range, insert: replace_with }
    }

    pub fn delete(range: TextRange) -> AtomEdit {
        AtomEdit::replace(range, String::new())
    }

    pub fn insert(offset: TextUnit, text: String) -> AtomEdit {
        AtomEdit::replace(TextRange::offset_len(offset, 0.into()), text)
    }
}

pub(crate) fn incremental_reparse(
    node: SyntaxNodeRef,
    edit: &AtomEdit,
    errors: Vec<SyntaxError>,
) -> Option<(GreenNode, Vec<SyntaxError>)> {
    let (node, green, new_errors) =
        reparse_leaf(node, &edit).or_else(|| reparse_block(node, &edit))?;
    let green_root = node.replace_with(green);
    let errors = merge_errors(errors, new_errors, node, edit);
    Some((green_root, errors))
}

fn reparse_leaf<'node>(
    node: SyntaxNodeRef<'node>,
    edit: &AtomEdit,
) -> Option<(SyntaxNodeRef<'node>, GreenNode, Vec<SyntaxError>)> {
    let node = algo::find_covering_node(node, edit.delete);
    match node.kind() {
        | WHITESPACE
        | COMMENT
        | DOC_COMMENT
        | IDENT
        | STRING
        | RAW_STRING => {
            let text = get_text_after_edit(node, &edit);
            let tokens = tokenize(&text);
            let token = match tokens[..] {
                [token] if token.kind == node.kind() => token,
                _ => return None,
            };

            if token.kind == IDENT && is_contextual_kw(&text) {
                return None;
            }

            let green = GreenNode::new_leaf(node.kind(), &text);
            let new_errors = vec![];
            Some((node, green, new_errors))
        }
        _ => None,
    }
}

fn reparse_block<'node>(
    node: SyntaxNodeRef<'node>,
    edit: &AtomEdit,
) -> Option<(SyntaxNodeRef<'node>, GreenNode, Vec<SyntaxError>)> {
    let (node, reparser) = find_reparsable_node(node, edit.delete)?;
    let text = get_text_after_edit(node, &edit);
    let tokens = tokenize(&text);
    if !is_balanced(&tokens) {
        return None;
    }
    let (green, new_errors) =
        parser_impl::parse_with::<yellow::GreenBuilder>(
            &text, &tokens, reparser,
        );
    Some((node, green, new_errors))
}

fn get_text_after_edit(node: SyntaxNodeRef, edit: &AtomEdit) -> String {
    replace_range(
        node.text().to_string(),
        edit.delete - node.range().start(),
        &edit.insert,
    )
}

fn is_contextual_kw(text: &str) -> bool {
    match text {
        | "auto"
        | "default"
        | "union" => true,
        _ => false,
    }
}

fn find_reparsable_node<'node>(
    node: SyntaxNodeRef<'node>,
    range: TextRange,
) -> Option<(SyntaxNodeRef<'node>, fn(&mut Parser))> {
    let node = algo::find_covering_node(node, range);
    return algo::ancestors(node)
        .filter_map(|node| reparser(node).map(|r| (node, r)))
        .next();

    fn reparser(node: SyntaxNodeRef) -> Option<fn(&mut Parser)> {
        let res = match node.kind() {
            BLOCK => grammar::block,
            NAMED_FIELD_DEF_LIST => grammar::named_field_def_list,
            NAMED_FIELD_LIST => grammar::named_field_list,
            ENUM_VARIANT_LIST => grammar::enum_variant_list,
            MATCH_ARM_LIST => grammar::match_arm_list,
            USE_TREE_LIST => grammar::use_tree_list,
            EXTERN_ITEM_LIST => grammar::extern_item_list,
            TOKEN_TREE if node.first_child().unwrap().kind() == L_CURLY => grammar::token_tree,
            ITEM_LIST => {
                let parent = node.parent().unwrap();
                match parent.kind() {
                    IMPL_ITEM => grammar::impl_item_list,
                    TRAIT_DEF => grammar::trait_item_list,
                    MODULE => grammar::mod_item_list,
                    _ => return None,
                }
            }
            _ => return None,
        };
        Some(res)
    }
}

fn is_balanced(tokens: &[Token]) -> bool {
    if tokens.len() == 0
        || tokens.first().unwrap().kind != L_CURLY
        || tokens.last().unwrap().kind != R_CURLY {
        return false;
    }
    let mut balance = 0usize;
    for t in tokens.iter() {
        match t.kind {
            L_CURLY => balance += 1,
            R_CURLY => balance = match balance.checked_sub(1) {
                Some(b) => b,
                None => return false,
            },
            _ => (),
        }
    }
    balance == 0
}

fn merge_errors(
    old_errors: Vec<SyntaxError>,
    new_errors: Vec<SyntaxError>,
    old_node: SyntaxNodeRef,
    edit: &AtomEdit,
) -> Vec<SyntaxError> {
    let mut res = Vec::new();
    for e in old_errors {
        if e.offset <= old_node.range().start() {
            res.push(e)
        } else if e.offset >= old_node.range().end() {
            res.push(SyntaxError {
                msg: e.msg,
                offset: e.offset + TextUnit::of_str(&edit.insert) - edit.delete.len(),
            })
        }
    }
    for e in new_errors {
        res.push(SyntaxError {
            msg: e.msg,
            offset: e.offset + old_node.range().start(),
        })
    }
    res
}

#[cfg(test)]
mod tests {
    use super::{
        super::{
            File,
            test_utils::extract_range,
            text_utils::replace_range,
            utils::dump_tree,
        },
        reparse_leaf, reparse_block, AtomEdit, GreenNode, SyntaxError, SyntaxNodeRef,
    };

    fn do_check<F>(
        before: &str,
        replace_with: &str,
        reparser: F,
    ) where
        for<'a> F: Fn(
            SyntaxNodeRef<'a>,
            &AtomEdit,
        ) -> Option<(SyntaxNodeRef<'a>, GreenNode, Vec<SyntaxError>)>
    {
        let (range, before) = extract_range(before);
        let after = replace_range(before.clone(), range, replace_with);

        let fully_reparsed = File::parse(&after);
        let incrementally_reparsed = {
            let f = File::parse(&before);
            let edit = AtomEdit { delete: range, insert: replace_with.to_string() };
            let (node, green, new_errors) =
                reparser(f.syntax(), &edit).expect("cannot incrementally reparse");
            let green_root = node.replace_with(green);
            let errors = super::merge_errors(f.errors(), new_errors, node, &edit);
            File::new(green_root, errors)
        };

        assert_eq_text!(
            &dump_tree(fully_reparsed.syntax()),
            &dump_tree(incrementally_reparsed.syntax()),
        )
    }

    #[test]
    fn reparse_block_tests() {
        let do_check = |before, replace_to|
            do_check(before, replace_to, reparse_block);

        do_check(r"
fn foo() {
    let x = foo + <|>bar<|>
}
", "baz");
        do_check(r"
fn foo() {
    let x = foo<|> + bar<|>
}
", "baz");
        do_check(r"
struct Foo {
    f: foo<|><|>
}
", ",\n    g: (),");
        do_check(r"
fn foo {
    let;
    1 + 1;
    <|>92<|>;
}
", "62");
        do_check(r"
mod foo {
    fn <|><|>
}
", "bar");
        do_check(r"
trait Foo {
    type <|>Foo<|>;
}
", "Output");
        do_check(r"
impl IntoIterator<Item=i32> for Foo {
    f<|><|>
}
", "n next(");
        do_check(r"
use a::b::{foo,<|>,bar<|>};
    ", "baz");
        do_check(r"
pub enum A {
    Foo<|><|>
}
", "\nBar;\n");
        do_check(r"
foo!{a, b<|><|> d}
", ", c[3]");
        do_check(r"
fn foo() {
    vec![<|><|>]
}
", "123");
        do_check(r"
extern {
    fn<|>;<|>
}
", " exit(code: c_int)");
    }

    #[test]
    fn reparse_leaf_tests() {
        let do_check = |before, replace_to|
            do_check(before, replace_to, reparse_leaf);

        do_check(r"<|><|>
fn foo() -> i32 { 1 }
", "\n\n\n   \n");
        do_check(r"
fn foo() -> <|><|> {}
", "  \n");
        do_check(r"
fn <|>foo<|>() -> i32 { 1 }
", "bar");
        do_check(r"
fn foo<|><|>foo() {  }
", "bar");
        do_check(r"
fn foo /* <|><|> */ () {}
", "some comment");
        do_check(r"
fn baz <|><|> () {}
", "    \t\t\n\n");
        do_check(r"
fn baz <|><|> () {}
", "    \t\t\n\n");
        do_check(r"
/// foo <|><|>omment
mod { }
", "c");
        do_check(r#"
fn -> &str { "Hello<|><|>" }
"#, ", world");
        do_check(r#"
fn -> &str { // "Hello<|><|>"
"#, ", world");
        do_check(r##"
fn -> &str { r#"Hello<|><|>"#
"##, ", world");
        do_check(r"
#[derive(<|>Copy<|>)]
enum Foo {

}
", "Clone");
    }
}<|endoftext|>


<commit_before>//! Project loading & configuration updates
use std::{mem, sync::Arc};

use crossbeam_channel::unbounded;
use flycheck::FlycheckHandle;
use ra_db::{CrateGraph, SourceRoot, VfsPath};
use ra_ide::AnalysisChange;
use ra_project_model::{PackageRoot, ProcMacroClient, ProjectWorkspace};
use vfs::{file_set::FileSetConfig, AbsPath, AbsPathBuf, ChangeKind};

use crate::{
    config::{Config, FilesWatcher, LinkedProject},
    global_state::{GlobalState, Handle, Status},
    lsp_ext,
    main_loop::Task,
};
use ra_prof::profile;

impl GlobalState {
    pub(crate) fn update_configuration(&mut self, config: Config) {
        let _p = profile("GlobalState::update_configuration");
        let old_config = mem::replace(&mut self.config, config);
        if self.config.lru_capacity != old_config.lru_capacity {
            self.analysis_host.update_lru_capacity(old_config.lru_capacity);
        }
        if self.config.linked_projects != old_config.linked_projects {
            self.fetch_workspaces()
        } else if self.config.flycheck != old_config.flycheck {
            self.reload_flycheck();
        }
    }
    pub(crate) fn maybe_refresh(&mut self, changes: &[(AbsPathBuf, ChangeKind)]) {
        if !changes.iter().any(|(path, kind)| is_interesting(path, *kind)) {
            return;
        }
        match self.status {
            Status::Loading | Status::NeedsReload => return,
            Status::Ready | Status::Invalid => (),
        }
        if self.config.cargo_autoreload {
            self.fetch_workspaces();
        } else {
            self.transition(Status::NeedsReload);
        }

        fn is_interesting(path: &AbsPath, change_kind: ChangeKind) -> bool {
            const IMPLICIT_TARGET_FILES: &[&str] = &["build.rs", "src/main.rs", "src/lib.rs"];
            const IMPLICIT_TARGET_DIRS: &[&str] = &["src/bin", "examples", "tests", "benches"];

            if path.ends_with("Cargo.toml") || path.ends_with("Cargo.lock") {
                return true;
            }
            if change_kind == ChangeKind::Modify {
                return false;
            }
            if path.extension().unwrap_or_default() != "rs" {
                return false;
            }
            if IMPLICIT_TARGET_FILES.iter().any(|it| path.ends_with(it)) {
                return true;
            }
            let parent = match path.parent() {
                Some(it) => it,
                None => return false,
            };
            if IMPLICIT_TARGET_DIRS.iter().any(|it| parent.ends_with(it)) {
                return true;
            }
            if path.ends_with("main.rs") {
                let grand_parent = match parent.parent() {
                    Some(it) => it,
                    None => return false,
                };
                if IMPLICIT_TARGET_DIRS.iter().any(|it| grand_parent.ends_with(it)) {
                    return true;
                }
            }
            false
        }
    }
    pub(crate) fn transition(&mut self, new_status: Status) {
        self.status = new_status;
        if self.config.client_caps.status_notification {
            let lsp_status = match new_status {
                Status::Loading => lsp_ext::Status::Loading,
                Status::Ready => lsp_ext::Status::Ready,
                Status::Invalid => lsp_ext::Status::Invalid,
                Status::NeedsReload => lsp_ext::Status::NeedsReload,
            };
            self.send_notification::<lsp_ext::StatusNotification>(lsp_status);
        }
    }
    pub(crate) fn fetch_workspaces(&mut self) {
        self.task_pool.handle.spawn({
            let linked_projects = self.config.linked_projects.clone();
            let cargo_config = self.config.cargo.clone();
            let with_sysroot = self.config.with_sysroot.clone();
            move || {
                let workspaces = linked_projects
                    .iter()
                    .map(|project| match project {
                        LinkedProject::ProjectManifest(manifest) => {
                            ra_project_model::ProjectWorkspace::load(
                                manifest.clone(),
                                &cargo_config,
                                with_sysroot,
                            )
                        }
                        LinkedProject::InlineJsonProject(it) => {
                            Ok(ra_project_model::ProjectWorkspace::Json { project: it.clone() })
                        }
                    })
                    .collect::<Vec<_>>();
                Task::Workspaces(workspaces)
            }
        });
    }
    pub(crate) fn switch_workspaces(&mut self, workspaces: Vec<anyhow::Result<ProjectWorkspace>>) {
        let _p = profile("GlobalState::switch_workspaces");
        log::info!("reloading projects: {:?}", self.config.linked_projects);

        let mut has_errors = false;
        let workspaces = workspaces
            .into_iter()
            .filter_map(|res| {
                res.map_err(|err| {
                    has_errors = true;
                    log::error!("failed to load workspace: {:#}", err);
                    self.show_message(
                        lsp_types::MessageType::Error,
                        format!("rust-analyzer failed to load workspace: {:#}", err),
                    );
                })
                .ok()
            })
            .collect::<Vec<_>>();

        if &*self.workspaces == &workspaces {
            return;
        }

        if !self.workspaces.is_empty() && has_errors {
            return;
        }

        if let FilesWatcher::Client = self.config.files.watcher {
            let registration_options = lsp_types::DidChangeWatchedFilesRegistrationOptions {
                watchers: workspaces
                    .iter()
                    .flat_map(ProjectWorkspace::to_roots)
                    .filter(PackageRoot::is_member)
                    .map(|root| format!("{}/**/*.rs", root.path().display()))
                    .map(|glob_pattern| lsp_types::FileSystemWatcher { glob_pattern, kind: None })
                    .collect(),
            };
            let registration = lsp_types::Registration {
                id: "workspace/didChangeWatchedFiles".to_string(),
                method: "workspace/didChangeWatchedFiles".to_string(),
                register_options: Some(serde_json::to_value(registration_options).unwrap()),
            };
            self.send_request::<lsp_types::request::RegisterCapability>(
                lsp_types::RegistrationParams { registrations: vec![registration] },
                |_, _| (),
            );
        }

        let mut change = AnalysisChange::new();

        let project_folders = ProjectFolders::new(&workspaces);

        self.proc_macro_client = match &self.config.proc_macro_srv {
            None => ProcMacroClient::dummy(),
            Some((path, args)) => match ProcMacroClient::extern_process(path.into(), args) {
                Ok(it) => it,
                Err(err) => {
                    log::error!(
                        "Failed to run ra_proc_macro_srv from path {}, error: {:?}",
                        path.display(),
                        err
                    );
                    ProcMacroClient::dummy()
                }
            },
        };

        let watch = match self.config.files.watcher {
            FilesWatcher::Client => vec![],
            FilesWatcher::Notify => project_folders.watch,
        };
        self.loader.handle.set_config(vfs::loader::Config { load: project_folders.load, watch });

        // Create crate graph from all the workspaces
        let crate_graph = {
            let mut crate_graph = CrateGraph::default();
            let vfs = &mut self.vfs.write().0;
            let loader = &mut self.loader;
            let mut load = |path: &AbsPath| {
                let contents = loader.handle.load_sync(path);
                let path = vfs::VfsPath::from(path.to_path_buf());
                vfs.set_file_contents(path.clone(), contents);
                vfs.file_id(&path)
            };
            for ws in workspaces.iter() {
                crate_graph.extend(ws.to_crate_graph(
                    self.config.cargo.target.as_deref(),
                    &self.proc_macro_client,
                    &mut load,
                ));
            }

            crate_graph
        };
        change.set_crate_graph(crate_graph);

        self.source_root_config = project_folders.source_root_config;
        self.workspaces = Arc::new(workspaces);

        self.analysis_host.apply_change(change);
        self.process_changes();
        self.reload_flycheck();
    }

    fn reload_flycheck(&mut self) {
        let config = match self.config.flycheck.clone() {
            Some(it) => it,
            None => {
                self.flycheck = None;
                return;
            }
        };

        // FIXME: Figure out the multi-workspace situation
        self.flycheck = self.workspaces.iter().find_map(move |w| match w {
            ProjectWorkspace::Cargo { cargo, .. } => {
                let (sender, receiver) = unbounded();
                let sender = Box::new(move |msg| sender.send(msg).unwrap());
                let cargo_project_root = cargo.workspace_root().to_path_buf();
                let handle =
                    FlycheckHandle::spawn(sender, config.clone(), cargo_project_root.into());
                Some(Handle { handle, receiver })
            }
            ProjectWorkspace::Json { .. } => {
                log::warn!("Cargo check watching only supported for cargo workspaces, disabling");
                None
            }
        })
    }
}

#[derive(Default)]
pub(crate) struct ProjectFolders {
    pub(crate) load: Vec<vfs::loader::Entry>,
    pub(crate) watch: Vec<usize>,
    pub(crate) source_root_config: SourceRootConfig,
}

impl ProjectFolders {
    pub(crate) fn new(workspaces: &[ProjectWorkspace]) -> ProjectFolders {
        let mut res = ProjectFolders::default();
        let mut fsc = FileSetConfig::builder();
        let mut local_filesets = vec![];

        for root in workspaces.iter().flat_map(|it| it.to_roots()) {
            let path = root.path().to_owned();

            let mut file_set_roots: Vec<VfsPath> = vec![];

            let entry = if root.is_member() {
                vfs::loader::Entry::local_cargo_package(path.to_path_buf())
            } else {
                vfs::loader::Entry::cargo_package_dependency(path.to_path_buf())
            };
            res.load.push(entry);
            if root.is_member() {
                res.watch.push(res.load.len() - 1);
            }

            if let Some(out_dir) = root.out_dir() {
                let out_dir = out_dir.to_path_buf();
                res.load.push(vfs::loader::Entry::rs_files_recursively(out_dir.clone()));
                if root.is_member() {
                    res.watch.push(res.load.len() - 1);
                }
                file_set_roots.push(out_dir.into());
            }
            file_set_roots.push(path.to_path_buf().into());

            if root.is_member() {
                local_filesets.push(fsc.len());
            }
            fsc.add_file_set(file_set_roots)
        }

        let fsc = fsc.build();
        res.source_root_config = SourceRootConfig { fsc, local_filesets };

        res
    }
}

#[derive(Default, Debug)]
pub(crate) struct SourceRootConfig {
    pub(crate) fsc: FileSetConfig,
    pub(crate) local_filesets: Vec<usize>,
}

impl SourceRootConfig {
    pub(crate) fn partition(&self, vfs: &vfs::Vfs) -> Vec<SourceRoot> {
        let _p = profile("SourceRootConfig::partition");
        self.fsc
            .partition(vfs)
            .into_iter()
            .enumerate()
            .map(|(idx, file_set)| {
                let is_local = self.local_filesets.contains(&idx);
                if is_local {
                    SourceRoot::new_local(file_set)
                } else {
                    SourceRoot::new_library(file_set)
                }
            })
            .collect()
    }
}
<commit_msg>Don't show error when speculatively reloading workspace<commit_after>//! Project loading & configuration updates
use std::{mem, sync::Arc};

use crossbeam_channel::unbounded;
use flycheck::FlycheckHandle;
use ra_db::{CrateGraph, SourceRoot, VfsPath};
use ra_ide::AnalysisChange;
use ra_project_model::{PackageRoot, ProcMacroClient, ProjectWorkspace};
use vfs::{file_set::FileSetConfig, AbsPath, AbsPathBuf, ChangeKind};

use crate::{
    config::{Config, FilesWatcher, LinkedProject},
    global_state::{GlobalState, Handle, Status},
    lsp_ext,
    main_loop::Task,
};
use ra_prof::profile;

impl GlobalState {
    pub(crate) fn update_configuration(&mut self, config: Config) {
        let _p = profile("GlobalState::update_configuration");
        let old_config = mem::replace(&mut self.config, config);
        if self.config.lru_capacity != old_config.lru_capacity {
            self.analysis_host.update_lru_capacity(old_config.lru_capacity);
        }
        if self.config.linked_projects != old_config.linked_projects {
            self.fetch_workspaces()
        } else if self.config.flycheck != old_config.flycheck {
            self.reload_flycheck();
        }
    }
    pub(crate) fn maybe_refresh(&mut self, changes: &[(AbsPathBuf, ChangeKind)]) {
        if !changes.iter().any(|(path, kind)| is_interesting(path, *kind)) {
            return;
        }
        match self.status {
            Status::Loading | Status::NeedsReload => return,
            Status::Ready | Status::Invalid => (),
        }
        if self.config.cargo_autoreload {
            self.fetch_workspaces();
        } else {
            self.transition(Status::NeedsReload);
        }

        fn is_interesting(path: &AbsPath, change_kind: ChangeKind) -> bool {
            const IMPLICIT_TARGET_FILES: &[&str] = &["build.rs", "src/main.rs", "src/lib.rs"];
            const IMPLICIT_TARGET_DIRS: &[&str] = &["src/bin", "examples", "tests", "benches"];

            if path.ends_with("Cargo.toml") || path.ends_with("Cargo.lock") {
                return true;
            }
            if change_kind == ChangeKind::Modify {
                return false;
            }
            if path.extension().unwrap_or_default() != "rs" {
                return false;
            }
            if IMPLICIT_TARGET_FILES.iter().any(|it| path.ends_with(it)) {
                return true;
            }
            let parent = match path.parent() {
                Some(it) => it,
                None => return false,
            };
            if IMPLICIT_TARGET_DIRS.iter().any(|it| parent.ends_with(it)) {
                return true;
            }
            if path.ends_with("main.rs") {
                let grand_parent = match parent.parent() {
                    Some(it) => it,
                    None => return false,
                };
                if IMPLICIT_TARGET_DIRS.iter().any(|it| grand_parent.ends_with(it)) {
                    return true;
                }
            }
            false
        }
    }
    pub(crate) fn transition(&mut self, new_status: Status) {
        self.status = new_status;
        if self.config.client_caps.status_notification {
            let lsp_status = match new_status {
                Status::Loading => lsp_ext::Status::Loading,
                Status::Ready => lsp_ext::Status::Ready,
                Status::Invalid => lsp_ext::Status::Invalid,
                Status::NeedsReload => lsp_ext::Status::NeedsReload,
            };
            self.send_notification::<lsp_ext::StatusNotification>(lsp_status);
        }
    }
    pub(crate) fn fetch_workspaces(&mut self) {
        self.task_pool.handle.spawn({
            let linked_projects = self.config.linked_projects.clone();
            let cargo_config = self.config.cargo.clone();
            let with_sysroot = self.config.with_sysroot.clone();
            move || {
                let workspaces = linked_projects
                    .iter()
                    .map(|project| match project {
                        LinkedProject::ProjectManifest(manifest) => {
                            ra_project_model::ProjectWorkspace::load(
                                manifest.clone(),
                                &cargo_config,
                                with_sysroot,
                            )
                        }
                        LinkedProject::InlineJsonProject(it) => {
                            Ok(ra_project_model::ProjectWorkspace::Json { project: it.clone() })
                        }
                    })
                    .collect::<Vec<_>>();
                Task::Workspaces(workspaces)
            }
        });
    }
    pub(crate) fn switch_workspaces(&mut self, workspaces: Vec<anyhow::Result<ProjectWorkspace>>) {
        let _p = profile("GlobalState::switch_workspaces");
        log::info!("reloading projects: {:?}", self.config.linked_projects);

        let mut has_errors = false;
        let workspaces = workspaces
            .into_iter()
            .filter_map(|res| {
                res.map_err(|err| {
                    has_errors = true;
                    log::error!("failed to load workspace: {:#}", err);
                    if self.workspaces.is_empty() {
                        self.show_message(
                            lsp_types::MessageType::Error,
                            format!("rust-analyzer failed to load workspace: {:#}", err),
                        );
                    }
                })
                .ok()
            })
            .collect::<Vec<_>>();

        if &*self.workspaces == &workspaces {
            return;
        }

        if !self.workspaces.is_empty() && has_errors {
            return;
        }

        if let FilesWatcher::Client = self.config.files.watcher {
            let registration_options = lsp_types::DidChangeWatchedFilesRegistrationOptions {
                watchers: workspaces
                    .iter()
                    .flat_map(ProjectWorkspace::to_roots)
                    .filter(PackageRoot::is_member)
                    .map(|root| format!("{}/**/*.rs", root.path().display()))
                    .map(|glob_pattern| lsp_types::FileSystemWatcher { glob_pattern, kind: None })
                    .collect(),
            };
            let registration = lsp_types::Registration {
                id: "workspace/didChangeWatchedFiles".to_string(),
                method: "workspace/didChangeWatchedFiles".to_string(),
                register_options: Some(serde_json::to_value(registration_options).unwrap()),
            };
            self.send_request::<lsp_types::request::RegisterCapability>(
                lsp_types::RegistrationParams { registrations: vec![registration] },
                |_, _| (),
            );
        }

        let mut change = AnalysisChange::new();

        let project_folders = ProjectFolders::new(&workspaces);

        self.proc_macro_client = match &self.config.proc_macro_srv {
            None => ProcMacroClient::dummy(),
            Some((path, args)) => match ProcMacroClient::extern_process(path.into(), args) {
                Ok(it) => it,
                Err(err) => {
                    log::error!(
                        "Failed to run ra_proc_macro_srv from path {}, error: {:?}",
                        path.display(),
                        err
                    );
                    ProcMacroClient::dummy()
                }
            },
        };

        let watch = match self.config.files.watcher {
            FilesWatcher::Client => vec![],
            FilesWatcher::Notify => project_folders.watch,
        };
        self.loader.handle.set_config(vfs::loader::Config { load: project_folders.load, watch });

        // Create crate graph from all the workspaces
        let crate_graph = {
            let mut crate_graph = CrateGraph::default();
            let vfs = &mut self.vfs.write().0;
            let loader = &mut self.loader;
            let mut load = |path: &AbsPath| {
                let contents = loader.handle.load_sync(path);
                let path = vfs::VfsPath::from(path.to_path_buf());
                vfs.set_file_contents(path.clone(), contents);
                vfs.file_id(&path)
            };
            for ws in workspaces.iter() {
                crate_graph.extend(ws.to_crate_graph(
                    self.config.cargo.target.as_deref(),
                    &self.proc_macro_client,
                    &mut load,
                ));
            }

            crate_graph
        };
        change.set_crate_graph(crate_graph);

        self.source_root_config = project_folders.source_root_config;
        self.workspaces = Arc::new(workspaces);

        self.analysis_host.apply_change(change);
        self.process_changes();
        self.reload_flycheck();
    }

    fn reload_flycheck(&mut self) {
        let config = match self.config.flycheck.clone() {
            Some(it) => it,
            None => {
                self.flycheck = None;
                return;
            }
        };

        // FIXME: Figure out the multi-workspace situation
        self.flycheck = self.workspaces.iter().find_map(move |w| match w {
            ProjectWorkspace::Cargo { cargo, .. } => {
                let (sender, receiver) = unbounded();
                let sender = Box::new(move |msg| sender.send(msg).unwrap());
                let cargo_project_root = cargo.workspace_root().to_path_buf();
                let handle =
                    FlycheckHandle::spawn(sender, config.clone(), cargo_project_root.into());
                Some(Handle { handle, receiver })
            }
            ProjectWorkspace::Json { .. } => {
                log::warn!("Cargo check watching only supported for cargo workspaces, disabling");
                None
            }
        })
    }
}

#[derive(Default)]
pub(crate) struct ProjectFolders {
    pub(crate) load: Vec<vfs::loader::Entry>,
    pub(crate) watch: Vec<usize>,
    pub(crate) source_root_config: SourceRootConfig,
}

impl ProjectFolders {
    pub(crate) fn new(workspaces: &[ProjectWorkspace]) -> ProjectFolders {
        let mut res = ProjectFolders::default();
        let mut fsc = FileSetConfig::builder();
        let mut local_filesets = vec![];

        for root in workspaces.iter().flat_map(|it| it.to_roots()) {
            let path = root.path().to_owned();

            let mut file_set_roots: Vec<VfsPath> = vec![];

            let entry = if root.is_member() {
                vfs::loader::Entry::local_cargo_package(path.to_path_buf())
            } else {
                vfs::loader::Entry::cargo_package_dependency(path.to_path_buf())
            };
            res.load.push(entry);
            if root.is_member() {
                res.watch.push(res.load.len() - 1);
            }

            if let Some(out_dir) = root.out_dir() {
                let out_dir = out_dir.to_path_buf();
                res.load.push(vfs::loader::Entry::rs_files_recursively(out_dir.clone()));
                if root.is_member() {
                    res.watch.push(res.load.len() - 1);
                }
                file_set_roots.push(out_dir.into());
            }
            file_set_roots.push(path.to_path_buf().into());

            if root.is_member() {
                local_filesets.push(fsc.len());
            }
            fsc.add_file_set(file_set_roots)
        }

        let fsc = fsc.build();
        res.source_root_config = SourceRootConfig { fsc, local_filesets };

        res
    }
}

#[derive(Default, Debug)]
pub(crate) struct SourceRootConfig {
    pub(crate) fsc: FileSetConfig,
    pub(crate) local_filesets: Vec<usize>,
}

impl SourceRootConfig {
    pub(crate) fn partition(&self, vfs: &vfs::Vfs) -> Vec<SourceRoot> {
        let _p = profile("SourceRootConfig::partition");
        self.fsc
            .partition(vfs)
            .into_iter()
            .enumerate()
            .map(|(idx, file_set)| {
                let is_local = self.local_filesets.contains(&idx);
                if is_local {
                    SourceRoot::new_local(file_set)
                } else {
                    SourceRoot::new_library(file_set)
                }
            })
            .collect()
    }
}
<|endoftext|>


<commit_before>use anyhow::Result;
use bytes::Bytes;
use std::collections::HashMap;
use std::process;
use std::sync::atomic::AtomicBool;
use std::sync::Mutex;
use std::time::{Duration, Instant};
use tide::Request;

use async_std::sync::{channel, Arc, Receiver, Sender};
use futures::sink::SinkExt;
use futures::stream::StreamExt;
use tungstenite::Message;

use async_ctrlc::CtrlC;
use async_std::task;

#[derive(Clone, Copy)]
#[repr(u8)]
enum SystemCommand {
    WakeUp = 1,
    Disconnect = 2,
}

#[derive(Copy, Clone, Debug)]
enum ReceiverType {
    System,
    Hypercore,
    Client,
}

#[derive(Clone)]
struct State {
    system_commands: Receiver<Bytes>,
    writers: HashMap<Bytes, Sender<Bytes>>,
    readers: HashMap<Bytes, Receiver<Bytes>>,
}

#[derive(Clone)]
struct WrappedData {
    data: Bytes,
    receiver_type: ReceiverType,
    id: Option<Bytes>,
}

type WrappedBytesClosure = Box<dyn Fn(Bytes) -> WrappedData + Send + Sync>;

async fn async_main(initial_state: State) -> Result<()> {
    let mut app = tide::with_state(initial_state);

    app.at("/ws").get(|req: Request<State>| async move {
        tide::websocket::upgrade(req, |_req, handle| async move {
            // https://docs.rs/async-tungstenite/0.10.0/async_tungstenite/struct.WebSocketStream.html
            let ws = handle.into_inner();
            let (mut ws_writer, mut ws_reader) = ws.split();
            let init_msg: Bytes = Bytes::from(ws_reader.next().await.unwrap().unwrap().into_data());
            dbg!(&init_msg);
            let hypercore_writer = _req.state().writers[&*init_msg].clone();
            ws_writer
                .send(Message::Text("reply-to-init".to_string()))
                .await
                .unwrap();
            let (client_sender, client_receiver): (Sender<Bytes>, Receiver<Bytes>) = channel(1000);
            let init_msg: Arc<Bytes> = Arc::new(init_msg.clone());
            task::spawn(async move {
                let receivers = collect_receivers(_req.state(), client_receiver.clone(), init_msg);
                let mut merged_receivers = futures::stream::select_all(receivers);
                let mut now = Instant::now();
                while let Some(wrapped_value) = merged_receivers.next().await {
                    match wrapped_value.receiver_type {
                        ReceiverType::System => {
                            if wrapped_value.data.as_ref().get(0)
                                == Some(&(SystemCommand::Disconnect as u8))
                            {
                                dbg!("got disconnect");
                                ws_writer.close().await.unwrap();
                                break;
                            }
                            // Send ping message if enough time has elapsed
                            if now.elapsed().as_secs() > 30 {
                                dbg!("sending ping");
                                ws_writer
                                    .send(tungstenite::Message::Ping(Vec::new()))
                                    .await
                                    .unwrap();
                                now = Instant::now();
                            }
                        }
                        ReceiverType::Client => {
                            dbg!("got client request");
                            ws_writer
                                .send(tungstenite::Message::Binary(
                                    wrapped_value.data.as_ref().to_vec(),
                                ))
                                .await
                                .unwrap();
                        }
                        _ => break,
                    }
                }
            });
            loop {
                let incoming_msg = ws_reader.next().await;
                let msg = incoming_msg.unwrap().unwrap();
                client_sender.send(Bytes::from(msg.into_data())).await;
            }
        })
    });

    app.listen("0.0.0.0:8080").await?;
    Ok(())
}

fn collect_receivers(
    state: &State,
    client_receiver: Receiver<Bytes>,
    init_msg: Arc<Bytes>,
) -> Vec<futures::stream::Map<Receiver<Bytes>, WrappedBytesClosure>> {
    let reader = state.readers[init_msg.as_ref()].clone();
    vec![
        state
            .system_commands
            .clone()
            .map(Box::new(|data| WrappedData {
                data,
                receiver_type: ReceiverType::System,
                id: None,
            }) as WrappedBytesClosure),
        reader.map(Box::new(move |data| WrappedData {
            data,
            receiver_type: ReceiverType::Hypercore,
            id: Some((*init_msg).clone()),
        }) as WrappedBytesClosure),
        client_receiver.map(Box::new(move |data| WrappedData {
            data,
            receiver_type: ReceiverType::Client,
            id: None,
        }) as WrappedBytesClosure),
    ]
}

fn main() -> Result<()> {
    fern::Dispatch::new()
        .format(|out, message, record| {
            out.finish(format_args!(
                "{}[{}][{}] {}",
                chrono::Local::now().format("[%Y-%m-%d %H:%M:%S]"),
                record.target(),
                record.level(),
                message
            ))
        })
        .level(log::LevelFilter::Debug)
        .chain(std::io::stdout())
        .apply()?;
    let (ping_sender, system_command_receiver) = channel(1000);
    let (demo_sender, demo_receiver) = channel(1000);
    let initial_state = State {
        system_commands: system_command_receiver,
        writers: [(Bytes::from_static("demo".as_bytes()), demo_sender)]
            .iter()
            .cloned()
            .collect(),
        readers: [(Bytes::from_static("demo".as_bytes()), demo_receiver)]
            .iter()
            .cloned()
            .collect(),
    };

    // Listen to ctrlc in a separate task
    let ctrlc = CtrlC::new().expect("cannot create Ctrl+C handler?");
    let disconnect_sender = ping_sender.clone();
    let abort: Arc<Mutex<AtomicBool>> = Arc::new(Mutex::new(AtomicBool::new(false)));
    let abort_writer = abort.clone();
    task::spawn(async move {
        ctrlc.await;
        disconnect_sender
            .send(Bytes::from_static(&[SystemCommand::Disconnect as u8]))
            .await;
        *abort_writer.as_ref().lock().unwrap() = AtomicBool::new(true);
        // Wait 2s before killing, to allow time for file saving and closing sockets
        task::sleep(Duration::from_millis(2000)).await;
        process::exit(0);
    });

    // Need to start a system executor to send the WakeUp command, we send it once per second so
    // that the listener will send a WS Ping when it wants to in a 1s delay.
    task::spawn(async move {
        let mut interval = async_std::stream::interval(Duration::from_secs(1));
        while interval.next().await.is_some() && !*abort.as_ref().lock().unwrap().get_mut() {
            task::sleep(Duration::from_millis(1000)).await;
            dbg!("Sending WakeUp");
            ping_sender
                .send(Bytes::from_static(&[SystemCommand::WakeUp as u8]))
                .await;
        }
    });

    // Block server with initial state
    futures::executor::block_on(async_main(initial_state))?;

    Ok(())
}
<commit_msg>Add hypercore task to the mix<commit_after>use anyhow::Result;
use bytes::Bytes;
use std::collections::HashMap;
use std::process;
use std::sync::atomic::AtomicBool;
use std::sync::Mutex;
use std::time::{Duration, Instant};
use tide::Request;

use async_std::sync::{channel, Arc, Receiver, Sender};
use futures::sink::SinkExt;
use futures::stream::StreamExt;
use tungstenite::Message;

use async_ctrlc::CtrlC;
use async_std::task;

#[derive(Clone, Copy)]
#[repr(u8)]
enum SystemCommand {
    WakeUp = 1,
    Disconnect = 2,
}

#[derive(Copy, Clone, Debug)]
enum ReceiverType {
    System,
    Hypercore,
    Client,
}

#[derive(Clone)]
struct State {
    system_commands: Receiver<Bytes>,
    writers: HashMap<Bytes, Sender<Bytes>>,
    readers: HashMap<Bytes, Receiver<Bytes>>,
}

#[derive(Clone)]
struct WrappedData {
    data: Bytes,
    receiver_type: ReceiverType,
    id: Option<Bytes>,
}

type WrappedBytesClosure = Box<dyn Fn(Bytes) -> WrappedData + Send + Sync>;

async fn async_main(initial_state: State) -> Result<()> {
    let mut app = tide::with_state(initial_state);

    app.at("/ws").get(|req: Request<State>| async move {
        tide::websocket::upgrade(req, |_req, handle| async move {
            // https://docs.rs/async-tungstenite/0.10.0/async_tungstenite/struct.WebSocketStream.html
            let ws = handle.into_inner();
            let (mut ws_writer, mut ws_reader) = ws.split();
            let init_msg: Bytes = Bytes::from(ws_reader.next().await.unwrap().unwrap().into_data());
            dbg!(&init_msg);
            let hypercore_writer = _req.state().writers[&*init_msg].clone();
            ws_writer
                .send(Message::Text("reply-to-init".to_string()))
                .await
                .unwrap();
            let (client_sender, client_receiver): (Sender<Bytes>, Receiver<Bytes>) = channel(1000);
            let init_msg: Arc<Bytes> = Arc::new(init_msg.clone());
            task::spawn(async move {
                let receivers = collect_receivers(_req.state(), client_receiver.clone(), init_msg);
                let mut merged_receivers = futures::stream::select_all(receivers);
                let mut now = Instant::now();
                while let Some(wrapped_value) = merged_receivers.next().await {
                    match wrapped_value.receiver_type {
                        ReceiverType::System => {
                            if wrapped_value.data.as_ref().get(0)
                                == Some(&(SystemCommand::Disconnect as u8))
                            {
                                dbg!("got disconnect");
                                ws_writer.close().await.unwrap();
                                break;
                            }
                            // Send ping message if enough time has elapsed
                            if now.elapsed().as_secs() > 30 {
                                dbg!("sending ping");
                                ws_writer
                                    .send(tungstenite::Message::Ping(Vec::new()))
                                    .await
                                    .unwrap();
                                now = Instant::now();
                            }
                        }
                        ReceiverType::Client => {
                            dbg!("got client request, forwarding to hypercore");
                            hypercore_writer.send(wrapped_value.data).await;
                        }
                        ReceiverType::Hypercore => {
                            dbg!("got hypercore message, sending to client");
                            ws_writer
                                .send(tungstenite::Message::Binary(
                                    wrapped_value.data.as_ref().to_vec(),
                                ))
                                .await
                                .unwrap();
                        }
                    }
                }
            });
            loop {
                let incoming_msg = ws_reader.next().await;
                let msg = incoming_msg.unwrap().unwrap();
                client_sender.send(Bytes::from(msg.into_data())).await;
            }
        })
    });

    app.listen("0.0.0.0:8080").await?;
    Ok(())
}

fn collect_receivers(
    state: &State,
    client_receiver: Receiver<Bytes>,
    init_msg: Arc<Bytes>,
) -> Vec<futures::stream::Map<Receiver<Bytes>, WrappedBytesClosure>> {
    let reader = state.readers[init_msg.as_ref()].clone();
    vec![
        state
            .system_commands
            .clone()
            .map(Box::new(|data| WrappedData {
                data,
                receiver_type: ReceiverType::System,
                id: None,
            }) as WrappedBytesClosure),
        reader.map(Box::new(move |data| WrappedData {
            data,
            receiver_type: ReceiverType::Hypercore,
            id: Some((*init_msg).clone()),
        }) as WrappedBytesClosure),
        client_receiver.map(Box::new(move |data| WrappedData {
            data,
            receiver_type: ReceiverType::Client,
            id: None,
        }) as WrappedBytesClosure),
    ]
}

fn main() -> Result<()> {
    fern::Dispatch::new()
        .format(|out, message, record| {
            out.finish(format_args!(
                "{}[{}][{}] {}",
                chrono::Local::now().format("[%Y-%m-%d %H:%M:%S]"),
                record.target(),
                record.level(),
                message
            ))
        })
        .level(log::LevelFilter::Debug)
        .chain(std::io::stdout())
        .apply()?;

    let (ping_sender, system_command_receiver) = channel(1000);
    let (demo_sender, demo_receiver) = channel(1000);
    let state_demo_sender = demo_sender.clone();
    let state_demo_receiver = demo_receiver.clone();
    let initial_state = State {
        system_commands: system_command_receiver,
        writers: [(Bytes::from_static("demo".as_bytes()), state_demo_sender)]
            .iter()
            .cloned()
            .collect(),
        readers: [(Bytes::from_static("demo".as_bytes()), state_demo_receiver)]
            .iter()
            .cloned()
            .collect(),
    };

    // Listen to ctrlc in a separate task
    let ctrlc = CtrlC::new().expect("cannot create Ctrl+C handler?");
    let disconnect_sender = ping_sender.clone();
    let abort: Arc<Mutex<AtomicBool>> = Arc::new(Mutex::new(AtomicBool::new(false)));
    let abort_writer = abort.clone();
    task::spawn(async move {
        ctrlc.await;
        disconnect_sender
            .send(Bytes::from_static(&[SystemCommand::Disconnect as u8]))
            .await;
        *abort_writer.as_ref().lock().unwrap() = AtomicBool::new(true);
        // Wait 2s before killing, to allow time for file saving and closing sockets
        task::sleep(Duration::from_millis(2000)).await;
        process::exit(0);
    });

    // Need to start a system executor to send the WakeUp command, we send it once per second so
    // that the listener will send a WS Ping when it wants to in a 1s delay.
    task::spawn(async move {
        let mut interval = async_std::stream::interval(Duration::from_secs(1));
        while interval.next().await.is_some() && !*abort.as_ref().lock().unwrap().get_mut() {
            task::sleep(Duration::from_millis(1000)).await;
            dbg!("Sending WakeUp");
            ping_sender
                .send(Bytes::from_static(&[SystemCommand::WakeUp as u8]))
                .await;
        }
    });

    // Launch a task for the demo owner
    task::spawn(async move {
        while let Some(data) = demo_receiver.clone().next().await {
            dbg!("Got hypercore data, pinging it right back");
            demo_sender.send(data).await;
        }
    });

    // Block server with initial state
    futures::executor::block_on(async_main(initial_state))?;

    Ok(())
}
<|endoftext|>