<commit_before>// See LICENSE file for copyright and license details.

#![macro_escape]

use std;
use gl;
use gl::types::{GLuint, GLsizeiptr};
use cgmath::matrix::{Matrix, Matrix4, Matrix3, ToMatrix4};
use cgmath::array::Array2;
use cgmath::vector::{Vector3};
use cgmath::angle;
use cgmath::projection;
use core::misc::deg_to_rad;
use core::types::{Size2, MInt};
use visualizer::types::{MFloat, Color3, Color4, ScreenPos};

pub use load_gl_funcs_with = gl::load_with;

pub static GREY_3: Color3 = Color3{r: 0.3, g: 0.3, b: 0.3};
pub static BLACK_3: Color3 = Color3{r: 0.0, g: 0.0, b: 0.0};
pub static WHITE: Color4 = Color4{r: 1.0, g: 1.0, b: 1.0, a: 1.0};
pub static BLUE: Color4 = Color4{r: 0.0, g: 0.0, b: 1.0, a: 1.0};
pub static BLACK: Color4 = Color4{r: 0.0, g: 0.0, b: 0.0, a: 1.0};

macro_rules! verify(
    ($e: expr) => ({
        let result = $e;
        let error_code = gl::GetError();
        if error_code != 0 {
            let description = match error_code {
                gl::INVALID_ENUM =>                  "GL_INVALID_ENUM",
                gl::INVALID_FRAMEBUFFER_OPERATION => "GL_INVALID_FRAMEBUFFER_OPERATION",
                gl::INVALID_OPERATION =>             "GL_INVALID_OPERATION",
                gl::INVALID_VALUE =>                 "GL_INVALID_VALUE",
                gl::NO_ERROR =>                      "GL_NO_ERROR",
                gl::OUT_OF_MEMORY =>                 "GL_OUT_OF_MEMORY",
                _ => fail!("Bad gl error code: {}", error_code),
            };
            fail!("gl error: {}({})", description, error_code);
        }
        result
    })
)

pub enum MeshRenderMode {
    Triangles,
    Lines,
}

impl MeshRenderMode {
    fn to_gl_type(&self) -> GLuint {
        match *self {
            Triangles => gl::TRIANGLES,
            Lines => gl::LINES,
        }
    }
}

pub fn tr(m: Matrix4<MFloat>, v: Vector3<MFloat>) -> Matrix4<MFloat> {
    let mut t = Matrix4::<MFloat>::identity();
    *t.mut_cr(3, 0) = v.x;
    *t.mut_cr(3, 1) = v.y;
    *t.mut_cr(3, 2) = v.z;
    m.mul_m(&t)
}

pub fn scale(m: Matrix4<MFloat>, scale: MFloat) -> Matrix4<MFloat> {
    let mut t = Matrix4::<MFloat>::identity();
    *t.mut_cr(0, 0) = scale;
    *t.mut_cr(1, 1) = scale;
    *t.mut_cr(2, 2) = scale;
    m.mul_m(&t)
}

pub fn rot_x(m: Matrix4<MFloat>, angle: MFloat) -> Matrix4<MFloat> {
    let rad = angle::rad(deg_to_rad(angle));
    let r = Matrix3::from_angle_x(rad).to_matrix4();
    m.mul_m(&r)
}

pub fn rot_z(m: Matrix4<MFloat>, angle: MFloat) -> Matrix4<MFloat> {
    let rad = angle::rad(deg_to_rad(angle));
    let r = Matrix3::from_angle_z(rad).to_matrix4();
    m.mul_m(&r)
}

pub fn init_opengl() {
    verify!(gl::Enable(gl::DEPTH_TEST));
    verify!(gl::Enable(gl::BLEND));
    verify!(gl::BlendFunc(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA));
}

pub fn set_clear_color(color: Color3) {
    verify!(gl::ClearColor(color.r, color.g, color.b, 1.0));
}

pub fn clear_screen() {
    verify!(gl::Clear(gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT));
}

pub fn set_viewport(size: Size2<MInt>) {
    verify!(gl::Viewport(0, 0, size.w, size.h));
}

pub struct Vao {
    id: GLuint,
}

impl Vao {
    pub fn new() -> Vao {
        let mut id = 0;
        unsafe {
            verify!(gl::GenVertexArrays(1, &mut id));
        }
        let vao = Vao{id: id};
        vao.bind();
        vao
    }

    pub fn bind(&self) {
        verify!(gl::BindVertexArray(self.id));
    }

    pub fn unbind(&self) {
        verify!(gl::BindVertexArray(0));
    }

    pub fn draw_array(&self, mesh_mode: MeshRenderMode, faces_count: MInt) {
        let starting_index = 0;
        let vertices_count = faces_count * 3;
        let mode = mesh_mode.to_gl_type();
        verify!(gl::DrawArrays(mode, starting_index, vertices_count));
    }
}

impl Drop for Vao {
    fn drop(&mut self) {
        unsafe {
            verify!(gl::DeleteVertexArrays(1, &self.id));
        }
    }
}

pub struct Vbo {
    id: GLuint,
}

fn get_new_vbo_id() -> GLuint {
    let mut id = 0;
    unsafe {
        verify!(gl::GenBuffers(1, &mut id));
    }
    id
}

impl Vbo {
    pub fn from_data<T>(data: &[T]) -> Vbo {
        let vbo = Vbo{id: get_new_vbo_id()};
        vbo.bind();
        let size = std::mem::size_of::<T>();
        let buf_size = (data.len() * size) as GLsizeiptr;
        if data.len() != 0 {
            unsafe {
                let data_ptr = std::mem::transmute(&data[0]);
                verify!(gl::BufferData(
                    gl::ARRAY_BUFFER,
                    buf_size,
                    data_ptr,
                    gl::STATIC_DRAW,
                ));
            }
        }
        vbo
    }

    pub fn bind(&self) {
        verify!(gl::BindBuffer(gl::ARRAY_BUFFER, self.id));
    }
}

impl Drop for Vbo {
    fn drop(&mut self) {
        unsafe {
            verify!(gl::DeleteBuffers(1, &self.id));
        }
    }
}

pub fn read_pixel_bytes(
    win_size: Size2<MInt>,
    mouse_pos: ScreenPos,
) -> (MInt, MInt, MInt, MInt) {
    let height = win_size.h;
    let reverted_h = height - mouse_pos.v.y;
    let data: [u8, ..4] = [0, 0, 0, 0]; // mut
    unsafe {
        let data_ptr = std::mem::transmute(&data[0]);
        verify!(gl::ReadPixels(
            mouse_pos.v.x, reverted_h, 1, 1,
            gl::RGBA,
            gl::UNSIGNED_BYTE,
            data_ptr
        ));
    }
    (data[0] as MInt, data[1] as MInt, data[2] as MInt, data[3] as MInt)
}

pub fn get_2d_screen_matrix(win_size: Size2<MInt>) -> Matrix4<MFloat> {
    let left = 0.0;
    let right = win_size.w as MFloat;
    let bottom = 0.0;
    let top = win_size.h as MFloat;
    let near = -1.0;
    let far = 1.0;
    projection::ortho(left, right, bottom, top, near, far)
}

// vim: set tabstop=4 shiftwidth=4 softtabstop=4 expandtab:
<commit_msg>Updated for latest cgmath-rs<commit_after>// See LICENSE file for copyright and license details.

#![macro_escape]

use std;
use gl;
use gl::types::{GLuint, GLsizeiptr};
use cgmath::matrix::{Matrix, Matrix4, Matrix3, ToMatrix4};
use cgmath::array::Array2;
use cgmath::vector::{Vector3};
use cgmath::angle;
use cgmath::projection;
use core::misc::deg_to_rad;
use core::types::{Size2, MInt};
use visualizer::types::{MFloat, Color3, Color4, ScreenPos};

pub use load_gl_funcs_with = gl::load_with;

pub static GREY_3: Color3 = Color3{r: 0.3, g: 0.3, b: 0.3};
pub static BLACK_3: Color3 = Color3{r: 0.0, g: 0.0, b: 0.0};
pub static WHITE: Color4 = Color4{r: 1.0, g: 1.0, b: 1.0, a: 1.0};
pub static BLUE: Color4 = Color4{r: 0.0, g: 0.0, b: 1.0, a: 1.0};
pub static BLACK: Color4 = Color4{r: 0.0, g: 0.0, b: 0.0, a: 1.0};

macro_rules! verify(
    ($e: expr) => ({
        let result = $e;
        let error_code = gl::GetError();
        if error_code != 0 {
            let description = match error_code {
                gl::INVALID_ENUM =>                  "GL_INVALID_ENUM",
                gl::INVALID_FRAMEBUFFER_OPERATION => "GL_INVALID_FRAMEBUFFER_OPERATION",
                gl::INVALID_OPERATION =>             "GL_INVALID_OPERATION",
                gl::INVALID_VALUE =>                 "GL_INVALID_VALUE",
                gl::NO_ERROR =>                      "GL_NO_ERROR",
                gl::OUT_OF_MEMORY =>                 "GL_OUT_OF_MEMORY",
                _ => fail!("Bad gl error code: {}", error_code),
            };
            fail!("gl error: {}({})", description, error_code);
        }
        result
    })
)

pub enum MeshRenderMode {
    Triangles,
    Lines,
}

impl MeshRenderMode {
    fn to_gl_type(&self) -> GLuint {
        match *self {
            Triangles => gl::TRIANGLES,
            Lines => gl::LINES,
        }
    }
}

// TODO: replace with something from cgmath-rs
pub fn tr(m: Matrix4<MFloat>, v: Vector3<MFloat>) -> Matrix4<MFloat> {
    let mut t = Matrix4::<MFloat>::identity();
    t[3][0] = v.x;
    t[3][1] = v.y;
    t[3][2] = v.z;
    m.mul_m(&t)
}

pub fn scale(m: Matrix4<MFloat>, scale: MFloat) -> Matrix4<MFloat> {
    let mut t = Matrix4::<MFloat>::identity();
    t[0][0] = scale;
    t[1][1] = scale;
    t[2][2] = scale;
    m.mul_m(&t)
}

pub fn rot_x(m: Matrix4<MFloat>, angle: MFloat) -> Matrix4<MFloat> {
    let rad = angle::rad(deg_to_rad(angle));
    let r = Matrix3::from_angle_x(rad).to_matrix4();
    m.mul_m(&r)
}

pub fn rot_z(m: Matrix4<MFloat>, angle: MFloat) -> Matrix4<MFloat> {
    let rad = angle::rad(deg_to_rad(angle));
    let r = Matrix3::from_angle_z(rad).to_matrix4();
    m.mul_m(&r)
}

pub fn init_opengl() {
    verify!(gl::Enable(gl::DEPTH_TEST));
    verify!(gl::Enable(gl::BLEND));
    verify!(gl::BlendFunc(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA));
}

pub fn set_clear_color(color: Color3) {
    verify!(gl::ClearColor(color.r, color.g, color.b, 1.0));
}

pub fn clear_screen() {
    verify!(gl::Clear(gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT));
}

pub fn set_viewport(size: Size2<MInt>) {
    verify!(gl::Viewport(0, 0, size.w, size.h));
}

pub struct Vao {
    id: GLuint,
}

impl Vao {
    pub fn new() -> Vao {
        let mut id = 0;
        unsafe {
            verify!(gl::GenVertexArrays(1, &mut id));
        }
        let vao = Vao{id: id};
        vao.bind();
        vao
    }

    pub fn bind(&self) {
        verify!(gl::BindVertexArray(self.id));
    }

    pub fn unbind(&self) {
        verify!(gl::BindVertexArray(0));
    }

    pub fn draw_array(&self, mesh_mode: MeshRenderMode, faces_count: MInt) {
        let starting_index = 0;
        let vertices_count = faces_count * 3;
        let mode = mesh_mode.to_gl_type();
        verify!(gl::DrawArrays(mode, starting_index, vertices_count));
    }
}

impl Drop for Vao {
    fn drop(&mut self) {
        unsafe {
            verify!(gl::DeleteVertexArrays(1, &self.id));
        }
    }
}

pub struct Vbo {
    id: GLuint,
}

fn get_new_vbo_id() -> GLuint {
    let mut id = 0;
    unsafe {
        verify!(gl::GenBuffers(1, &mut id));
    }
    id
}

impl Vbo {
    pub fn from_data<T>(data: &[T]) -> Vbo {
        let vbo = Vbo{id: get_new_vbo_id()};
        vbo.bind();
        let size = std::mem::size_of::<T>();
        let buf_size = (data.len() * size) as GLsizeiptr;
        if data.len() != 0 {
            unsafe {
                let data_ptr = std::mem::transmute(&data[0]);
                verify!(gl::BufferData(
                    gl::ARRAY_BUFFER,
                    buf_size,
                    data_ptr,
                    gl::STATIC_DRAW,
                ));
            }
        }
        vbo
    }

    pub fn bind(&self) {
        verify!(gl::BindBuffer(gl::ARRAY_BUFFER, self.id));
    }
}

impl Drop for Vbo {
    fn drop(&mut self) {
        unsafe {
            verify!(gl::DeleteBuffers(1, &self.id));
        }
    }
}

pub fn read_pixel_bytes(
    win_size: Size2<MInt>,
    mouse_pos: ScreenPos,
) -> (MInt, MInt, MInt, MInt) {
    let height = win_size.h;
    let reverted_h = height - mouse_pos.v.y;
    let data: [u8, ..4] = [0, 0, 0, 0]; // mut
    unsafe {
        let data_ptr = std::mem::transmute(&data[0]);
        verify!(gl::ReadPixels(
            mouse_pos.v.x, reverted_h, 1, 1,
            gl::RGBA,
            gl::UNSIGNED_BYTE,
            data_ptr
        ));
    }
    (data[0] as MInt, data[1] as MInt, data[2] as MInt, data[3] as MInt)
}

pub fn get_2d_screen_matrix(win_size: Size2<MInt>) -> Matrix4<MFloat> {
    let left = 0.0;
    let right = win_size.w as MFloat;
    let bottom = 0.0;
    let top = win_size.h as MFloat;
    let near = -1.0;
    let far = 1.0;
    projection::ortho(left, right, bottom, top, near, far)
}

// vim: set tabstop=4 shiftwidth=4 softtabstop=4 expandtab:
<|endoftext|>


<commit_before>use std::rc::Rc;
use std::cell::RefCell;
use std::sync::mpsc::Sender;

use Event;
use super::event;

use user32;
use winapi;

/// Stores the current window and its events dispatcher.
///
/// We only have one window per thread. We still store the HWND in case where we
///  receive an event for another window.
thread_local!(pub static WINDOW: Rc<RefCell<Option<(winapi::HWND, Sender<Event>)>>> = Rc::new(RefCell::new(None)));

/// Checks that the window is the good one, and if so send the event to it.
fn send_event(input_window: winapi::HWND, event: Event) {
    WINDOW.with(|window| {
        let window = window.borrow();
        let stored = match *window {
            None => return,
            Some(ref v) => v
        };

        let &(ref win, ref sender) = stored;

        if win != &input_window {
            return;
        }

        sender.send(event).ok();  // ignoring if closed
    });
}

/// This is the callback that is called by `DispatchMessage` in the events loop.
///
/// Returning 0 tells the Win32 API that the message has been processed.
pub unsafe extern "system" fn callback(window: winapi::HWND, msg: winapi::UINT,
                                       wparam: winapi::WPARAM, lparam: winapi::LPARAM)
                                       -> winapi::LRESULT
{
    match msg {
        winapi::WM_DESTROY => {
            use events::Event::Closed;

            WINDOW.with(|w| {
                let w = w.borrow();
                let &(ref win, _) = match *w {
                    None => return,
                    Some(ref v) => v
                };

                if win == &window {
                    user32::PostQuitMessage(0);
                }
            });

            send_event(window, Closed);
            0
        },

        winapi::WM_ERASEBKGND => {
            1
        },

        winapi::WM_SIZE => {
            use events::Event::Resized;
            let w = winapi::LOWORD(lparam as winapi::DWORD) as u32;
            let h = winapi::HIWORD(lparam as winapi::DWORD) as u32;
            send_event(window, Resized(w, h));
            0
        },

        winapi::WM_MOVE => {
            use events::Event::Moved;
            let x = winapi::LOWORD(lparam as winapi::DWORD) as i32;
            let y = winapi::HIWORD(lparam as winapi::DWORD) as i32;
            send_event(window, Moved(x, y));
            0
        },

        winapi::WM_CHAR => {
            use std::mem;
            use events::Event::ReceivedCharacter;
            let chr: char = mem::transmute(wparam as u32);
            send_event(window, ReceivedCharacter(chr));
            0
        },

        winapi::WM_MOUSEMOVE => {
            use events::Event::MouseMoved;

            let x = winapi::GET_X_LPARAM(lparam) as i32;
            let y = winapi::GET_Y_LPARAM(lparam) as i32;

            send_event(window, MouseMoved((x, y)));

            0
        },

        winapi::WM_MOUSEWHEEL => {
            use events::Event::MouseWheel;

            let value = (wparam >> 16) as i16;
            let value = value as i32;

            send_event(window, MouseWheel(value));

            0
        },

        winapi::WM_KEYDOWN => {
            use events::Event::KeyboardInput;
            use events::ElementState::Pressed;
            let scancode = ((lparam >> 16) & 0xff) as u8;
            let vkey = event::vkeycode_to_element(wparam);
            send_event(window, KeyboardInput(Pressed, scancode, vkey));
            0
        },

        winapi::WM_KEYUP => {
            use events::Event::KeyboardInput;
            use events::ElementState::Released;
            let scancode = ((lparam >> 16) & 0xff) as u8;
            let vkey = event::vkeycode_to_element(wparam);
            send_event(window, KeyboardInput(Released, scancode, vkey));
            0
        },

        winapi::WM_LBUTTONDOWN => {
            use events::Event::MouseInput;
            use events::MouseButton::Left;
            use events::ElementState::Pressed;
            send_event(window, MouseInput(Pressed, Left));
            0
        },

        winapi::WM_LBUTTONUP => {
            use events::Event::MouseInput;
            use events::MouseButton::Left;
            use events::ElementState::Released;
            send_event(window, MouseInput(Released, Left));
            0
        },

        winapi::WM_RBUTTONDOWN => {
            use events::Event::MouseInput;
            use events::MouseButton::Right;
            use events::ElementState::Pressed;
            send_event(window, MouseInput(Pressed, Right));
            0
        },

        winapi::WM_RBUTTONUP => {
            use events::Event::MouseInput;
            use events::MouseButton::Right;
            use events::ElementState::Released;
            send_event(window, MouseInput(Released, Right));
            0
        },

        winapi::WM_MBUTTONDOWN => {
            use events::Event::MouseInput;
            use events::MouseButton::Middle;
            use events::ElementState::Pressed;
            send_event(window, MouseInput(Pressed, Middle));
            0
        },

        winapi::WM_MBUTTONUP => {
            use events::Event::MouseInput;
            use events::MouseButton::Middle;
            use events::ElementState::Released;
            send_event(window, MouseInput(Released, Middle));
            0
        },

        winapi::WM_SETFOCUS => {
            use events::Event::Focused;
            send_event(window, Focused(true));
            0
        },

        winapi::WM_KILLFOCUS => {
            use events::Event::Focused;
            send_event(window, Focused(false));
            0
        },

        _ => {
            user32::DefWindowProcW(window, msg, wparam, lparam)
        }
    }
}
<commit_msg>Add draft for raw input<commit_after>use std::mem;
use std::rc::Rc;
use std::cell::RefCell;
use std::sync::mpsc::Sender;

use Event;
use super::event;

use user32;
use winapi;

/// Stores the current window and its events dispatcher.
///
/// We only have one window per thread. We still store the HWND in case where we
///  receive an event for another window.
thread_local!(pub static WINDOW: Rc<RefCell<Option<(winapi::HWND, Sender<Event>)>>> = Rc::new(RefCell::new(None)));

/// Checks that the window is the good one, and if so send the event to it.
fn send_event(input_window: winapi::HWND, event: Event) {
    WINDOW.with(|window| {
        let window = window.borrow();
        let stored = match *window {
            None => return,
            Some(ref v) => v
        };

        let &(ref win, ref sender) = stored;

        if win != &input_window {
            return;
        }

        sender.send(event).ok();  // ignoring if closed
    });
}

/// This is the callback that is called by `DispatchMessage` in the events loop.
///
/// Returning 0 tells the Win32 API that the message has been processed.
pub unsafe extern "system" fn callback(window: winapi::HWND, msg: winapi::UINT,
                                       wparam: winapi::WPARAM, lparam: winapi::LPARAM)
                                       -> winapi::LRESULT
{
    match msg {
        winapi::WM_DESTROY => {
            use events::Event::Closed;

            WINDOW.with(|w| {
                let w = w.borrow();
                let &(ref win, _) = match *w {
                    None => return,
                    Some(ref v) => v
                };

                if win == &window {
                    user32::PostQuitMessage(0);
                }
            });

            send_event(window, Closed);
            0
        },

        winapi::WM_ERASEBKGND => {
            1
        },

        winapi::WM_SIZE => {
            use events::Event::Resized;
            let w = winapi::LOWORD(lparam as winapi::DWORD) as u32;
            let h = winapi::HIWORD(lparam as winapi::DWORD) as u32;
            send_event(window, Resized(w, h));
            0
        },

        winapi::WM_MOVE => {
            use events::Event::Moved;
            let x = winapi::LOWORD(lparam as winapi::DWORD) as i32;
            let y = winapi::HIWORD(lparam as winapi::DWORD) as i32;
            send_event(window, Moved(x, y));
            0
        },

        winapi::WM_CHAR => {
            use std::mem;
            use events::Event::ReceivedCharacter;
            let chr: char = mem::transmute(wparam as u32);
            send_event(window, ReceivedCharacter(chr));
            0
        },

        winapi::WM_MOUSEMOVE => {
            use events::Event::MouseMoved;

            let x = winapi::GET_X_LPARAM(lparam) as i32;
            let y = winapi::GET_Y_LPARAM(lparam) as i32;

            send_event(window, MouseMoved((x, y)));

            0
        },

        winapi::WM_MOUSEWHEEL => {
            use events::Event::MouseWheel;

            let value = (wparam >> 16) as i16;
            let value = value as i32;

            send_event(window, MouseWheel(value));

            0
        },

        winapi::WM_KEYDOWN => {
            use events::Event::KeyboardInput;
            use events::ElementState::Pressed;
            let scancode = ((lparam >> 16) & 0xff) as u8;
            let vkey = event::vkeycode_to_element(wparam);
            send_event(window, KeyboardInput(Pressed, scancode, vkey));
            0
        },

        winapi::WM_KEYUP => {
            use events::Event::KeyboardInput;
            use events::ElementState::Released;
            let scancode = ((lparam >> 16) & 0xff) as u8;
            let vkey = event::vkeycode_to_element(wparam);
            send_event(window, KeyboardInput(Released, scancode, vkey));
            0
        },

        winapi::WM_LBUTTONDOWN => {
            use events::Event::MouseInput;
            use events::MouseButton::Left;
            use events::ElementState::Pressed;
            send_event(window, MouseInput(Pressed, Left));
            0
        },

        winapi::WM_LBUTTONUP => {
            use events::Event::MouseInput;
            use events::MouseButton::Left;
            use events::ElementState::Released;
            send_event(window, MouseInput(Released, Left));
            0
        },

        winapi::WM_RBUTTONDOWN => {
            use events::Event::MouseInput;
            use events::MouseButton::Right;
            use events::ElementState::Pressed;
            send_event(window, MouseInput(Pressed, Right));
            0
        },

        winapi::WM_RBUTTONUP => {
            use events::Event::MouseInput;
            use events::MouseButton::Right;
            use events::ElementState::Released;
            send_event(window, MouseInput(Released, Right));
            0
        },

        winapi::WM_MBUTTONDOWN => {
            use events::Event::MouseInput;
            use events::MouseButton::Middle;
            use events::ElementState::Pressed;
            send_event(window, MouseInput(Pressed, Middle));
            0
        },

        winapi::WM_MBUTTONUP => {
            use events::Event::MouseInput;
            use events::MouseButton::Middle;
            use events::ElementState::Released;
            send_event(window, MouseInput(Released, Middle));
            0
        },

        winapi::WM_INPUT => {
            let mut data: winapi::RAWINPUT = mem::uninitialized();
            let mut data_size = mem::size_of::<winapi::RAWINPUT>() as winapi::UINT;
            user32::GetRawInputData(mem::transmute(lparam), winapi::RID_INPUT,
                                    mem::transmute(&mut data), &mut data_size,
                                    mem::size_of::<winapi::RAWINPUTHEADER>() as winapi::UINT);

            if data.header.dwType == winapi::RIM_TYPEMOUSE {
                let _x = data.mouse.lLastX;  // FIXME: this is not always the relative movement
                let _y = data.mouse.lLastY;
                // TODO:
                //send_event(window, Event::MouseRawMovement { x: x, y: y });

                0

            } else {
                user32::DefWindowProcW(window, msg, wparam, lparam)
            }
        },

        winapi::WM_SETFOCUS => {
            use events::Event::Focused;
            send_event(window, Focused(true));
            0
        },

        winapi::WM_KILLFOCUS => {
            use events::Event::Focused;
            send_event(window, Focused(false));
            0
        },

        _ => {
            user32::DefWindowProcW(window, msg, wparam, lparam)
        }
    }
}
<|endoftext|>


<commit_before>extern crate r68k_tools;

use r68k_tools::assembler::Assembler;
use std::io;
use std::io::BufReader;
use r68k_tools::srecords::write_s68;

fn main() {
    let r68k = Assembler::new();

    let asm = r#"
* let's start off with a comment, and then set PC to $1000
ORG $1000

ADD.B   #$3,D0
ADD.B   D0,D1
"#;

    println!("{}", asm);
    let mut reader = BufReader::new(asm.as_bytes());
    let (end, mem) = r68k.assemble(&mut reader).unwrap();
    println!("ends before address {:06x}", end);
    let mut stdout = io::stdout();
    write_s68(&mut stdout, vec![&mem], 0).unwrap();
}
<commit_msg>Adapt asm example to new parser<commit_after>extern crate r68k_tools;

use r68k_tools::assembler::Assembler;
use std::io;
use std::io::BufReader;
use r68k_tools::srecords::write_s68;
use r68k_tools::memory::Memory;

fn main() {
    let r68k = Assembler::new();

    let asm = r#"
    ; let's start off with a comment, and then set PC to $1000
    ORG $1000

    ADD.B   #$3,D0
    ADD.B   D0,D1
"#;

    println!("{}", asm);
    let mut reader = BufReader::new(asm.as_bytes());
    let (end, mem) = r68k.assemble(&mut reader).unwrap();
    let offset = mem.offset();
    println!("assembled {:06x} - {:06x}", offset, end);
    let mut stdout = io::stdout();
    write_s68(&mut stdout, vec![&mem], 0).unwrap();
}
<|endoftext|>


<commit_before>// Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0.

use std::convert::TryFrom;

use cop_codegen::rpn_fn;
use cop_datatype::{EvalType, FieldTypeAccessor, FieldTypeFlag};
use tipb::expression::FieldType;

use crate::coprocessor::codec::data_type::*;
use crate::coprocessor::dag::expr::EvalContext;
use crate::coprocessor::dag::rpn_expr::{RpnExpressionNode, RpnFnCallExtra};
use crate::coprocessor::Result;

/// Gets the cast function between specified data types.
///
/// TODO: This function supports some internal casts performed by TiKV. However it would be better
/// to be done in TiDB.
pub fn get_cast_fn_rpn_node(
    from_field_type: &FieldType,
    to_field_type: FieldType,
) -> Result<RpnExpressionNode> {
    let from = box_try!(EvalType::try_from(from_field_type.tp()));
    let to = box_try!(EvalType::try_from(to_field_type.tp()));
    let func_meta = match (from, to) {
        (EvalType::Int, EvalType::Decimal) => {
            if !from_field_type
                .as_accessor()
                .flag()
                .contains(FieldTypeFlag::UNSIGNED)
                && !to_field_type
                    .as_accessor()
                    .flag()
                    .contains(FieldTypeFlag::UNSIGNED)
            {
                cast_int_as_decimal_fn_meta()
            } else {
                cast_uint_as_decimal_fn_meta()
            }
        }
        (EvalType::Bytes, EvalType::Real) => cast_string_as_real_fn_meta(),
        (EvalType::DateTime, EvalType::Real) => cast_time_as_real_fn_meta(),
        (EvalType::Duration, EvalType::Real) => cast_duration_as_real_fn_meta(),
        (EvalType::Json, EvalType::Real) => cast_json_as_real_fn_meta(),
        _ => return Err(box_err!("Unsupported cast from {} to {}", from, to)),
    };
    // This cast function is inserted by `Coprocessor` automatically,
    // the `inUnion` flag always false in this situation. Ideally,
    // the cast function should be inserted by TiDB and pushed down
    // with all implicit arguments.
    Ok(RpnExpressionNode::FnCall {
        func_meta,
        args_len: 1,
        field_type: to_field_type,
        implicit_args: Vec::new(),
    })
}

fn produce_dec_with_specified_tp(
    ctx: &mut EvalContext,
    dec: Decimal,
    ft: &FieldType,
) -> Result<Decimal> {
    // FIXME: The implementation is not exactly the same as TiDB's `ProduceDecWithSpecifiedTp`.
    let (flen, decimal) = (ft.flen(), ft.decimal());
    if flen == cop_datatype::UNSPECIFIED_LENGTH || decimal == cop_datatype::UNSPECIFIED_LENGTH {
        return Ok(dec);
    }
    Ok(dec.convert_to(ctx, flen as u8, decimal as u8)?)
}

// TODO: remove it after CAST function use `in_union` function
#[allow(dead_code)]

/// Indicates whether the current expression is evaluated in union statement
///
/// Note: The TiDB will push down the `inUnion` flag by implicit constant arguments,
/// but some CAST expressions inserted by TiKV coprocessor use an empty vector to represent
/// the `inUnion` flag is false.
/// See: https://github.com/pingcap/tidb/blob/1e403873d905b2d0ad3be06bd8cd261203d84638/expression/builtin.go#L260
fn in_union(implicit_args: &[ScalarValue]) -> bool {
    implicit_args.get(0) == Some(&ScalarValue::Int(Some(1)))
}

/// The unsigned int implementation for push down signature `CastIntAsDecimal`.
#[rpn_fn(capture = [ctx, extra])]
#[inline]
pub fn cast_uint_as_decimal(
    ctx: &mut EvalContext,
    extra: &RpnFnCallExtra<'_>,
    val: &Option<i64>,
) -> Result<Option<Decimal>> {
    match val {
        None => Ok(None),
        Some(val) => {
            let dec = Decimal::from(*val as u64);
            Ok(Some(produce_dec_with_specified_tp(
                ctx,
                dec,
                extra.ret_field_type,
            )?))
        }
    }
}

/// The signed int implementation for push down signature `CastIntAsDecimal`.
#[rpn_fn(capture = [ctx, extra])]
#[inline]
pub fn cast_int_as_decimal(
    ctx: &mut EvalContext,
    extra: &RpnFnCallExtra<'_>,
    val: &Option<i64>,
) -> Result<Option<Decimal>> {
    match val {
        None => Ok(None),
        Some(val) => {
            let dec = Decimal::from(*val);
            Ok(Some(produce_dec_with_specified_tp(
                ctx,
                dec,
                extra.ret_field_type,
            )?))
        }
    }
}

/// The implementation for push down signature `CastStringAsReal`.
#[rpn_fn(capture = [ctx])]
#[inline]
pub fn cast_string_as_real(ctx: &mut EvalContext, val: &Option<Bytes>) -> Result<Option<Real>> {
    use crate::coprocessor::codec::convert::bytes_to_f64;

    match val {
        None => Ok(None),
        Some(val) => {
            let val = bytes_to_f64(ctx, val.as_slice())?;
            // FIXME: There is an additional step `ProduceFloatWithSpecifiedTp` in TiDB.
            Ok(Real::new(val).ok())
        }
    }
}

/// The implementation for push down signature `CastTimeAsReal`.
#[rpn_fn]
#[inline]
pub fn cast_time_as_real(val: &Option<DateTime>) -> Result<Option<Real>> {
    match val {
        None => Ok(None),
        Some(val) => {
            let val = val.to_decimal()?.as_f64()?;
            Ok(Real::new(val).ok())
        }
    }
}

/// The implementation for push down signature `CastDurationAsReal`.
#[rpn_fn]
#[inline]
fn cast_duration_as_real(val: &Option<Duration>) -> Result<Option<Real>> {
    match val {
        None => Ok(None),
        Some(val) => {
            let val = Decimal::try_from(*val)?.as_f64()?;
            Ok(Real::new(val).ok())
        }
    }
}

/// The implementation for push down signature `CastJsonAsReal`.
#[rpn_fn(capture = [ctx])]
#[inline]
fn cast_json_as_real(ctx: &mut EvalContext, val: &Option<Json>) -> Result<Option<Real>> {
    match val {
        None => Ok(None),
        Some(val) => {
            let val = val.cast_to_real(ctx)?;
            Ok(Real::new(val).ok())
        }
    }
}

#[cfg(test)]
mod tests {
    #[test]
    fn test_in_union() {
        use super::*;

        assert_eq!(in_union(&[]), false);
        assert_eq!(in_union(&[ScalarValue::Int(None)]), false);
        assert_eq!(in_union(&[ScalarValue::Int(Some(0))]), false);
        assert_eq!(
            in_union(&[ScalarValue::Int(Some(0)), ScalarValue::Int(Some(1))]),
            false
        );
        assert_eq!(in_union(&[ScalarValue::Int(Some(1))]), true);
        assert_eq!(
            in_union(&[ScalarValue::Int(Some(1)), ScalarValue::Int(Some(0))]),
            true
        );
    }
}
<commit_msg>coprocessor: extract a utility function 'is_unsigned' (#5093)<commit_after>// Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0.

use std::convert::TryFrom;

use cop_codegen::rpn_fn;
use cop_datatype::{EvalType, FieldTypeAccessor, FieldTypeFlag};
use tipb::expression::FieldType;

use crate::coprocessor::codec::data_type::*;
use crate::coprocessor::dag::expr::EvalContext;
use crate::coprocessor::dag::rpn_expr::{RpnExpressionNode, RpnFnCallExtra};
use crate::coprocessor::Result;

/// Gets the cast function between specified data types.
///
/// TODO: This function supports some internal casts performed by TiKV. However it would be better
/// to be done in TiDB.
pub fn get_cast_fn_rpn_node(
    from_field_type: &FieldType,
    to_field_type: FieldType,
) -> Result<RpnExpressionNode> {
    let from = box_try!(EvalType::try_from(from_field_type.tp()));
    let to = box_try!(EvalType::try_from(to_field_type.tp()));
    let func_meta = match (from, to) {
        (EvalType::Int, EvalType::Decimal) => {
            if !is_unsigned(from_field_type) && !is_unsigned(&to_field_type) {
                cast_int_as_decimal_fn_meta()
            } else {
                cast_uint_as_decimal_fn_meta()
            }
        }
        (EvalType::Bytes, EvalType::Real) => cast_string_as_real_fn_meta(),
        (EvalType::DateTime, EvalType::Real) => cast_time_as_real_fn_meta(),
        (EvalType::Duration, EvalType::Real) => cast_duration_as_real_fn_meta(),
        (EvalType::Json, EvalType::Real) => cast_json_as_real_fn_meta(),
        _ => return Err(box_err!("Unsupported cast from {} to {}", from, to)),
    };
    // This cast function is inserted by `Coprocessor` automatically,
    // the `inUnion` flag always false in this situation. Ideally,
    // the cast function should be inserted by TiDB and pushed down
    // with all implicit arguments.
    Ok(RpnExpressionNode::FnCall {
        func_meta,
        args_len: 1,
        field_type: to_field_type,
        implicit_args: Vec::new(),
    })
}

#[inline]
fn is_unsigned(ft: &FieldType) -> bool {
    ft.as_accessor().flag().contains(FieldTypeFlag::UNSIGNED)
}

fn produce_dec_with_specified_tp(
    ctx: &mut EvalContext,
    dec: Decimal,
    ft: &FieldType,
) -> Result<Decimal> {
    // FIXME: The implementation is not exactly the same as TiDB's `ProduceDecWithSpecifiedTp`.
    let (flen, decimal) = (ft.flen(), ft.decimal());
    if flen == cop_datatype::UNSPECIFIED_LENGTH || decimal == cop_datatype::UNSPECIFIED_LENGTH {
        return Ok(dec);
    }
    Ok(dec.convert_to(ctx, flen as u8, decimal as u8)?)
}

// TODO: remove it after CAST function use `in_union` function
#[allow(dead_code)]

/// Indicates whether the current expression is evaluated in union statement
///
/// Note: The TiDB will push down the `inUnion` flag by implicit constant arguments,
/// but some CAST expressions inserted by TiKV coprocessor use an empty vector to represent
/// the `inUnion` flag is false.
/// See: https://github.com/pingcap/tidb/blob/1e403873d905b2d0ad3be06bd8cd261203d84638/expression/builtin.go#L260
fn in_union(implicit_args: &[ScalarValue]) -> bool {
    implicit_args.get(0) == Some(&ScalarValue::Int(Some(1)))
}

/// The unsigned int implementation for push down signature `CastIntAsDecimal`.
#[rpn_fn(capture = [ctx, extra])]
#[inline]
pub fn cast_uint_as_decimal(
    ctx: &mut EvalContext,
    extra: &RpnFnCallExtra<'_>,
    val: &Option<i64>,
) -> Result<Option<Decimal>> {
    match val {
        None => Ok(None),
        Some(val) => {
            let dec = Decimal::from(*val as u64);
            Ok(Some(produce_dec_with_specified_tp(
                ctx,
                dec,
                extra.ret_field_type,
            )?))
        }
    }
}

/// The signed int implementation for push down signature `CastIntAsDecimal`.
#[rpn_fn(capture = [ctx, extra])]
#[inline]
pub fn cast_int_as_decimal(
    ctx: &mut EvalContext,
    extra: &RpnFnCallExtra<'_>,
    val: &Option<i64>,
) -> Result<Option<Decimal>> {
    match val {
        None => Ok(None),
        Some(val) => {
            let dec = Decimal::from(*val);
            Ok(Some(produce_dec_with_specified_tp(
                ctx,
                dec,
                extra.ret_field_type,
            )?))
        }
    }
}

/// The implementation for push down signature `CastStringAsReal`.
#[rpn_fn(capture = [ctx])]
#[inline]
pub fn cast_string_as_real(ctx: &mut EvalContext, val: &Option<Bytes>) -> Result<Option<Real>> {
    use crate::coprocessor::codec::convert::bytes_to_f64;

    match val {
        None => Ok(None),
        Some(val) => {
            let val = bytes_to_f64(ctx, val.as_slice())?;
            // FIXME: There is an additional step `ProduceFloatWithSpecifiedTp` in TiDB.
            Ok(Real::new(val).ok())
        }
    }
}

/// The implementation for push down signature `CastTimeAsReal`.
#[rpn_fn]
#[inline]
pub fn cast_time_as_real(val: &Option<DateTime>) -> Result<Option<Real>> {
    match val {
        None => Ok(None),
        Some(val) => {
            let val = val.to_decimal()?.as_f64()?;
            Ok(Real::new(val).ok())
        }
    }
}

/// The implementation for push down signature `CastDurationAsReal`.
#[rpn_fn]
#[inline]
fn cast_duration_as_real(val: &Option<Duration>) -> Result<Option<Real>> {
    match val {
        None => Ok(None),
        Some(val) => {
            let val = Decimal::try_from(*val)?.as_f64()?;
            Ok(Real::new(val).ok())
        }
    }
}

/// The implementation for push down signature `CastJsonAsReal`.
#[rpn_fn(capture = [ctx])]
#[inline]
fn cast_json_as_real(ctx: &mut EvalContext, val: &Option<Json>) -> Result<Option<Real>> {
    match val {
        None => Ok(None),
        Some(val) => {
            let val = val.cast_to_real(ctx)?;
            Ok(Real::new(val).ok())
        }
    }
}

#[cfg(test)]
mod tests {
    #[test]
    fn test_in_union() {
        use super::*;

        assert_eq!(in_union(&[]), false);
        assert_eq!(in_union(&[ScalarValue::Int(None)]), false);
        assert_eq!(in_union(&[ScalarValue::Int(Some(0))]), false);
        assert_eq!(
            in_union(&[ScalarValue::Int(Some(0)), ScalarValue::Int(Some(1))]),
            false
        );
        assert_eq!(in_union(&[ScalarValue::Int(Some(1))]), true);
        assert_eq!(
            in_union(&[ScalarValue::Int(Some(1)), ScalarValue::Int(Some(0))]),
            true
        );
    }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! Rust helpers for Gecko's `nsStyleCoord`.

use gecko_bindings::bindings::{Gecko_ResetStyleCoord, Gecko_SetStyleCoordCalcValue, Gecko_AddRefCalcArbitraryThread};
use gecko_bindings::structs::{nsStyleCoord_Calc, nsStyleUnit, nsStyleUnion, nsStyleCoord, nsStyleSides, nsStyleCorners};
use gecko_bindings::structs::{nsStyleCoord_CalcValue, nscoord};
use std::mem;

impl nsStyleCoord {
    #[inline]
    /// Get a `null` nsStyleCoord.
    pub fn null() -> Self {
        // Can't construct directly because it has private fields
        let mut coord: Self = unsafe { mem::zeroed() };
        coord.leaky_set_null();
        coord
    }
}

impl CoordData for nsStyleCoord {
    #[inline]
    fn unit(&self) -> nsStyleUnit {
        unsafe {
            *self.get_mUnit()
        }
    }
    #[inline]
    fn union(&self) -> nsStyleUnion {
        unsafe {
            *self.get_mValue()
        }
    }
}

impl CoordDataMut for nsStyleCoord {
    unsafe fn values_mut(&mut self) -> (&mut nsStyleUnit, &mut nsStyleUnion) {
        let unit = self.get_mUnit_mut() as *mut _;
        let value = self.get_mValue_mut() as *mut _;
        (&mut *unit, &mut *value)
    }
}

impl nsStyleCoord_CalcValue {
    /// Create an "empty" CalcValue (whose value is `0`).
    pub fn new() -> Self {
        nsStyleCoord_CalcValue {
            mLength: 0,
            mPercent: 0.0,
            mHasPercent: false,
        }
    }
}

impl PartialEq for nsStyleCoord_CalcValue {
    fn eq(&self, other: &Self) -> bool {
        self.mLength == other.mLength &&
            self.mPercent == other.mPercent &&
            self.mHasPercent == other.mHasPercent
    }
}

impl nsStyleSides {
    /// Immutably get the `nsStyleCoord`-like object representing the side at
    /// index `index`.
    #[inline]
    pub fn data_at(&self, index: usize) -> SidesData {
        SidesData {
            sides: self,
            index: index,
        }
    }

    /// Mutably get the `nsStyleCoord`-like object representing the side at
    /// index `index`.
    #[inline]
    pub fn data_at_mut(&mut self, index: usize) -> SidesDataMut {
        SidesDataMut {
            sides: self,
            index: index,
        }
    }
}

/// A `nsStyleCoord`-like object on top of an immutable reference to
/// `nsStyleSides`.
pub struct SidesData<'a> {
    sides: &'a nsStyleSides,
    index: usize,
}

/// A `nsStyleCoord`-like object on top of an mutable reference to
/// `nsStyleSides`.
pub struct SidesDataMut<'a> {
    sides: &'a mut nsStyleSides,
    index: usize,
}

impl<'a> CoordData for SidesData<'a> {
    #[inline]
    fn unit(&self) -> nsStyleUnit {
        unsafe {
            self.sides.get_mUnits()[self.index]
        }
    }
    #[inline]
    fn union(&self) -> nsStyleUnion {
        unsafe {
            self.sides.get_mValues()[self.index]
        }
    }
}
impl<'a> CoordData for SidesDataMut<'a> {
    #[inline]
    fn unit(&self) -> nsStyleUnit {
        unsafe {
            self.sides.get_mUnits()[self.index]
        }
    }
    #[inline]
    fn union(&self) -> nsStyleUnion {
        unsafe {
            self.sides.get_mValues()[self.index]
        }
    }
}
impl<'a> CoordDataMut for SidesDataMut<'a> {
    unsafe fn values_mut(&mut self) -> (&mut nsStyleUnit, &mut nsStyleUnion) {
        let unit = &mut self.sides.get_mUnits_mut()[self.index] as *mut _;
        let value = &mut self.sides.get_mValues_mut()[self.index] as *mut _;
        (&mut *unit, &mut *value)
    }
}

impl nsStyleCorners {
    /// Get a `nsStyleCoord` like object representing the given index's value
    /// and unit.
    #[inline]
    pub fn data_at(&self, index: usize) -> CornersData {
        CornersData {
            corners: self,
            index: index,
        }
    }

    /// Get a `nsStyleCoord` like object representing the mutable given index's
    /// value and unit.
    #[inline]
    pub fn data_at_mut(&mut self, index: usize) -> CornersDataMut {
        CornersDataMut {
            corners: self,
            index: index,
        }
    }
}

/// A `nsStyleCoord`-like struct on top of `nsStyleCorners`.
pub struct CornersData<'a> {
    corners: &'a nsStyleCorners,
    index: usize,
}

/// A `nsStyleCoord`-like struct on top of a mutable `nsStyleCorners` reference.
pub struct CornersDataMut<'a> {
    corners: &'a mut nsStyleCorners,
    index: usize,
}

impl<'a> CoordData for CornersData<'a> {
    fn unit(&self) -> nsStyleUnit {
        unsafe {
            self.corners.get_mUnits()[self.index]
        }
    }
    fn union(&self) -> nsStyleUnion {
        unsafe {
            self.corners.get_mValues()[self.index]
        }
    }
}
impl<'a> CoordData for CornersDataMut<'a> {
    fn unit(&self) -> nsStyleUnit {
        unsafe {
            self.corners.get_mUnits()[self.index]
        }
    }
    fn union(&self) -> nsStyleUnion {
        unsafe {
            self.corners.get_mValues()[self.index]
        }
    }
}
impl<'a> CoordDataMut for CornersDataMut<'a> {
    unsafe fn values_mut(&mut self) -> (&mut nsStyleUnit, &mut nsStyleUnion) {
        let unit = &mut self.corners.get_mUnits_mut()[self.index] as *mut _;
        let value = &mut self.corners.get_mValues_mut()[self.index] as *mut _;
        (&mut *unit, &mut *value)
    }
}

/// Enum representing the tagged union that is CoordData.
///
/// In release mode this should never actually exist in the code, and will be
/// optimized out by threading matches and inlining.
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum CoordDataValue {
    /// eStyleUnit_Null
    Null,
    /// eStyleUnit_Normal
    Normal,
    /// eStyleUnit_Auto
    Auto,
    /// eStyleUnit_None
    None,
    /// eStyleUnit_Percent
    Percent(f32),
    /// eStyleUnit_Factor
    Factor(f32),
    /// eStyleUnit_Degree
    Degree(f32),
    /// eStyleUnit_Grad
    Grad(f32),
    /// eStyleUnit_Radian
    Radian(f32),
    /// eStyleUnit_Turn
    Turn(f32),
    /// eStyleUnit_FlexFraction
    FlexFraction(f32),
    /// eStyleUnit_Coord
    Coord(nscoord),
    /// eStyleUnit_Integer
    Integer(i32),
    /// eStyleUnit_Enumerated
    Enumerated(u32),
    /// eStyleUnit_Calc
    Calc(nsStyleCoord_CalcValue),
}

/// A trait to abstract on top of a mutable `nsStyleCoord`-like object.
pub trait CoordDataMut : CoordData {
    /// Get mutably the unit and the union.
    ///
    /// This is unsafe since it's possible to modify the unit without changing
    /// the union.
    ///
    /// NB: This can't be two methods since we can't mutably borrow twice
    unsafe fn values_mut(&mut self) -> (&mut nsStyleUnit, &mut nsStyleUnion);

    /// Clean up any resources used by the union.
    ///
    /// Currently, this only happens if the nsStyleUnit is a Calc.
    #[inline]
    fn reset(&mut self) {
        unsafe {
            if self.unit() == nsStyleUnit::eStyleUnit_Calc {
                let (unit, union) = self.values_mut();
                Gecko_ResetStyleCoord(unit, union);
            }
        }
    }

    #[inline]
    /// Copies the unit and value from another `CoordData` type.
    fn copy_from<T: CoordData>(&mut self, other: &T) {
        unsafe {
            self.reset();
            self.copy_from_unchecked(other);
            self.addref_if_calc();
        }
    }

    #[inline]
    /// Moves the unit and value from another `CoordData` type.
    fn move_from<T: CoordData>(&mut self, other: T) {
        unsafe {
            self.reset();
            self.copy_from_unchecked(&other);
        }
    }

    #[inline]
    /// Copies the unit and value from another `CoordData` type without checking
    /// the type of the value (so refcounted values like calc may leak).
    unsafe fn copy_from_unchecked<T: CoordData>(&mut self, other: &T) {
        let (unit, union) = self.values_mut();
        *unit = other.unit();
        *union = other.union();
    }

    /// Useful for initializing uninits, given that `set_value` may segfault on
    /// uninits.
    fn leaky_set_null(&mut self) {
        use gecko_bindings::structs::nsStyleUnit::*;
        unsafe {
            let (unit, union) = self.values_mut();
            *unit = eStyleUnit_Null;
            *union.mInt.as_mut() = 0;
        }
    }

    #[inline(always)]
    /// Sets the inner value.
    fn set_value(&mut self, value: CoordDataValue) {
        use gecko_bindings::structs::nsStyleUnit::*;
        use self::CoordDataValue::*;
        self.reset();
        unsafe {
            let (unit, union) = self.values_mut();
            match value {
                Null => {
                    *unit = eStyleUnit_Null;
                    *union.mInt.as_mut() = 0;
                }
                Normal => {
                    *unit = eStyleUnit_Normal;
                    *union.mInt.as_mut() = 0;
                }
                Auto => {
                    *unit = eStyleUnit_Auto;
                    *union.mInt.as_mut() = 0;
                }
                None => {
                    *unit = eStyleUnit_None;
                    *union.mInt.as_mut() = 0;
                }
                Percent(f) => {
                    *unit = eStyleUnit_Percent;
                    *union.mFloat.as_mut() = f;
                }
                Factor(f) => {
                    *unit = eStyleUnit_Factor;
                    *union.mFloat.as_mut() = f;
                }
                Degree(f) => {
                    *unit = eStyleUnit_Degree;
                    *union.mFloat.as_mut() = f;
                }
                Grad(f) => {
                    *unit = eStyleUnit_Grad;
                    *union.mFloat.as_mut() = f;
                }
                Radian(f) => {
                    *unit = eStyleUnit_Radian;
                    *union.mFloat.as_mut() = f;
                }
                Turn(f) => {
                    *unit = eStyleUnit_Turn;
                    *union.mFloat.as_mut() = f;
                }
                FlexFraction(f) => {
                    *unit = eStyleUnit_FlexFraction;
                    *union.mFloat.as_mut() = f;
                }
                Coord(coord) => {
                    *unit = eStyleUnit_Coord;
                    *union.mInt.as_mut() = coord;
                }
                Integer(i) => {
                    *unit = eStyleUnit_Integer;
                    *union.mInt.as_mut() = i;
                }
                Enumerated(i) => {
                    *unit = eStyleUnit_Enumerated;
                    *union.mInt.as_mut() = i as i32;
                }
                Calc(calc) => {
                    // Gecko_SetStyleCoordCalcValue changes the unit internally
                    Gecko_SetStyleCoordCalcValue(unit, union, calc);
                }
            }
        }
    }

    #[inline]
    /// Gets the `Calc` value mutably, asserts in debug builds if the unit is
    /// not `Calc`.
    unsafe fn as_calc_mut(&mut self) -> &mut nsStyleCoord_Calc {
        debug_assert!(self.unit() == nsStyleUnit::eStyleUnit_Calc);
        &mut *(*self.union().mPointer.as_mut() as *mut nsStyleCoord_Calc)
    }

    #[inline]
    /// Does what it promises, if the unit is `calc`, it bumps the reference
    /// count _of the calc expression_.
    fn addref_if_calc(&mut self) {
        unsafe {
            if self.unit() == nsStyleUnit::eStyleUnit_Calc {
                Gecko_AddRefCalcArbitraryThread(self.as_calc_mut());
            }
        }
    }
}
/// A trait to abstract on top of a `nsStyleCoord`-like object.
pub trait CoordData {
    /// Get the unit of this object.
    fn unit(&self) -> nsStyleUnit;
    /// Get the `nsStyleUnion` for this object.
    fn union(&self) -> nsStyleUnion;

    #[inline(always)]
    /// Get the appropriate value for this object.
    fn as_value(&self) -> CoordDataValue {
        use gecko_bindings::structs::nsStyleUnit::*;
        use self::CoordDataValue::*;
        unsafe {
            match self.unit() {
                eStyleUnit_Null => Null,
                eStyleUnit_Normal => Normal,
                eStyleUnit_Auto => Auto,
                eStyleUnit_None => None,
                eStyleUnit_Percent => Percent(self.get_float()),
                eStyleUnit_Factor => Factor(self.get_float()),
                eStyleUnit_Degree => Degree(self.get_float()),
                eStyleUnit_Grad => Grad(self.get_float()),
                eStyleUnit_Radian => Radian(self.get_float()),
                eStyleUnit_Turn => Turn(self.get_float()),
                eStyleUnit_FlexFraction => FlexFraction(self.get_float()),
                eStyleUnit_Coord => Coord(self.get_integer()),
                eStyleUnit_Integer => Integer(self.get_integer()),
                eStyleUnit_Enumerated => Enumerated(self.get_integer() as u32),
                eStyleUnit_Calc => Calc(self.get_calc_value()),
            }
        }
    }

    #[inline]
    /// Pretend inner value is a float; obtain it.
    unsafe fn get_float(&self) -> f32 {
        use gecko_bindings::structs::nsStyleUnit::*;
        debug_assert!(self.unit() == eStyleUnit_Percent || self.unit() == eStyleUnit_Factor
                      || self.unit() == eStyleUnit_Degree || self.unit() == eStyleUnit_Grad
                      || self.unit() == eStyleUnit_Radian || self.unit() == eStyleUnit_Turn
                      || self.unit() == eStyleUnit_FlexFraction);
        *self.union().mFloat.as_ref()
    }

    #[inline]
    /// Pretend inner value is an int; obtain it.
    unsafe fn get_integer(&self) -> i32 {
        use gecko_bindings::structs::nsStyleUnit::*;
        debug_assert!(self.unit() == eStyleUnit_Coord || self.unit() == eStyleUnit_Integer
                      || self.unit() == eStyleUnit_Enumerated);
        *self.union().mInt.as_ref()
    }

    #[inline]
    /// Pretend inner value is a calc; obtain it.
    /// Ensure that the unit is Calc before calling this.
    unsafe fn get_calc_value(&self) -> nsStyleCoord_CalcValue {
        debug_assert!(self.unit() == nsStyleUnit::eStyleUnit_Calc);
        (*self.as_calc())._base
    }

    #[inline]
    /// Pretend the inner value is a calc expression, and obtain it.
    unsafe fn as_calc(&self) -> &nsStyleCoord_Calc {
        debug_assert!(self.unit() == nsStyleUnit::eStyleUnit_Calc);
        &*(*self.union().mPointer.as_ref() as *const nsStyleCoord_Calc)
    }
}
<commit_msg>Auto merge of #18275 - Manishearth:coorddata-unsafe, r=nox<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! Rust helpers for Gecko's `nsStyleCoord`.

use gecko_bindings::bindings::{Gecko_ResetStyleCoord, Gecko_SetStyleCoordCalcValue, Gecko_AddRefCalcArbitraryThread};
use gecko_bindings::structs::{nsStyleCoord_Calc, nsStyleUnit, nsStyleUnion, nsStyleCoord, nsStyleSides, nsStyleCorners};
use gecko_bindings::structs::{nsStyleCoord_CalcValue, nscoord};
use std::mem;

impl nsStyleCoord {
    #[inline]
    /// Get a `null` nsStyleCoord.
    pub fn null() -> Self {
        // Can't construct directly because it has private fields
        let mut coord: Self = unsafe { mem::zeroed() };
        coord.leaky_set_null();
        coord
    }
}

unsafe impl CoordData for nsStyleCoord {
    #[inline]
    fn unit(&self) -> nsStyleUnit {
        unsafe {
            *self.get_mUnit()
        }
    }
    #[inline]
    fn union(&self) -> nsStyleUnion {
        unsafe {
            *self.get_mValue()
        }
    }
}

unsafe impl CoordDataMut for nsStyleCoord {
    unsafe fn values_mut(&mut self) -> (&mut nsStyleUnit, &mut nsStyleUnion) {
        let unit = self.get_mUnit_mut() as *mut _;
        let value = self.get_mValue_mut() as *mut _;
        (&mut *unit, &mut *value)
    }
}

impl nsStyleCoord_CalcValue {
    /// Create an "empty" CalcValue (whose value is `0`).
    pub fn new() -> Self {
        nsStyleCoord_CalcValue {
            mLength: 0,
            mPercent: 0.0,
            mHasPercent: false,
        }
    }
}

impl PartialEq for nsStyleCoord_CalcValue {
    fn eq(&self, other: &Self) -> bool {
        self.mLength == other.mLength &&
            self.mPercent == other.mPercent &&
            self.mHasPercent == other.mHasPercent
    }
}

impl nsStyleSides {
    /// Immutably get the `nsStyleCoord`-like object representing the side at
    /// index `index`.
    #[inline]
    pub fn data_at(&self, index: usize) -> SidesData {
        SidesData {
            sides: self,
            index: index,
        }
    }

    /// Mutably get the `nsStyleCoord`-like object representing the side at
    /// index `index`.
    #[inline]
    pub fn data_at_mut(&mut self, index: usize) -> SidesDataMut {
        SidesDataMut {
            sides: self,
            index: index,
        }
    }
}

/// A `nsStyleCoord`-like object on top of an immutable reference to
/// `nsStyleSides`.
pub struct SidesData<'a> {
    sides: &'a nsStyleSides,
    index: usize,
}

/// A `nsStyleCoord`-like object on top of an mutable reference to
/// `nsStyleSides`.
pub struct SidesDataMut<'a> {
    sides: &'a mut nsStyleSides,
    index: usize,
}

unsafe impl<'a> CoordData for SidesData<'a> {
    #[inline]
    fn unit(&self) -> nsStyleUnit {
        unsafe {
            self.sides.get_mUnits()[self.index]
        }
    }
    #[inline]
    fn union(&self) -> nsStyleUnion {
        unsafe {
            self.sides.get_mValues()[self.index]
        }
    }
}
unsafe impl<'a> CoordData for SidesDataMut<'a> {
    #[inline]
    fn unit(&self) -> nsStyleUnit {
        unsafe {
            self.sides.get_mUnits()[self.index]
        }
    }
    #[inline]
    fn union(&self) -> nsStyleUnion {
        unsafe {
            self.sides.get_mValues()[self.index]
        }
    }
}
unsafe impl<'a> CoordDataMut for SidesDataMut<'a> {
    unsafe fn values_mut(&mut self) -> (&mut nsStyleUnit, &mut nsStyleUnion) {
        let unit = &mut self.sides.get_mUnits_mut()[self.index] as *mut _;
        let value = &mut self.sides.get_mValues_mut()[self.index] as *mut _;
        (&mut *unit, &mut *value)
    }
}

impl nsStyleCorners {
    /// Get a `nsStyleCoord` like object representing the given index's value
    /// and unit.
    #[inline]
    pub fn data_at(&self, index: usize) -> CornersData {
        CornersData {
            corners: self,
            index: index,
        }
    }

    /// Get a `nsStyleCoord` like object representing the mutable given index's
    /// value and unit.
    #[inline]
    pub fn data_at_mut(&mut self, index: usize) -> CornersDataMut {
        CornersDataMut {
            corners: self,
            index: index,
        }
    }
}

/// A `nsStyleCoord`-like struct on top of `nsStyleCorners`.
pub struct CornersData<'a> {
    corners: &'a nsStyleCorners,
    index: usize,
}

/// A `nsStyleCoord`-like struct on top of a mutable `nsStyleCorners` reference.
pub struct CornersDataMut<'a> {
    corners: &'a mut nsStyleCorners,
    index: usize,
}

unsafe impl<'a> CoordData for CornersData<'a> {
    fn unit(&self) -> nsStyleUnit {
        unsafe {
            self.corners.get_mUnits()[self.index]
        }
    }
    fn union(&self) -> nsStyleUnion {
        unsafe {
            self.corners.get_mValues()[self.index]
        }
    }
}
unsafe impl<'a> CoordData for CornersDataMut<'a> {
    fn unit(&self) -> nsStyleUnit {
        unsafe {
            self.corners.get_mUnits()[self.index]
        }
    }
    fn union(&self) -> nsStyleUnion {
        unsafe {
            self.corners.get_mValues()[self.index]
        }
    }
}
unsafe impl<'a> CoordDataMut for CornersDataMut<'a> {
    unsafe fn values_mut(&mut self) -> (&mut nsStyleUnit, &mut nsStyleUnion) {
        let unit = &mut self.corners.get_mUnits_mut()[self.index] as *mut _;
        let value = &mut self.corners.get_mValues_mut()[self.index] as *mut _;
        (&mut *unit, &mut *value)
    }
}

/// Enum representing the tagged union that is CoordData.
///
/// In release mode this should never actually exist in the code, and will be
/// optimized out by threading matches and inlining.
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum CoordDataValue {
    /// eStyleUnit_Null
    Null,
    /// eStyleUnit_Normal
    Normal,
    /// eStyleUnit_Auto
    Auto,
    /// eStyleUnit_None
    None,
    /// eStyleUnit_Percent
    Percent(f32),
    /// eStyleUnit_Factor
    Factor(f32),
    /// eStyleUnit_Degree
    Degree(f32),
    /// eStyleUnit_Grad
    Grad(f32),
    /// eStyleUnit_Radian
    Radian(f32),
    /// eStyleUnit_Turn
    Turn(f32),
    /// eStyleUnit_FlexFraction
    FlexFraction(f32),
    /// eStyleUnit_Coord
    Coord(nscoord),
    /// eStyleUnit_Integer
    Integer(i32),
    /// eStyleUnit_Enumerated
    Enumerated(u32),
    /// eStyleUnit_Calc
    Calc(nsStyleCoord_CalcValue),
}

/// A trait to abstract on top of a mutable `nsStyleCoord`-like object.
pub unsafe trait CoordDataMut : CoordData {
    /// Get mutably the unit and the union.
    ///
    /// This is unsafe since it's possible to modify the unit without changing
    /// the union.
    ///
    /// NB: This can't be two methods since we can't mutably borrow twice
    unsafe fn values_mut(&mut self) -> (&mut nsStyleUnit, &mut nsStyleUnion);

    /// Clean up any resources used by the union.
    ///
    /// Currently, this only happens if the nsStyleUnit is a Calc.
    #[inline]
    fn reset(&mut self) {
        unsafe {
            if self.unit() == nsStyleUnit::eStyleUnit_Calc {
                let (unit, union) = self.values_mut();
                Gecko_ResetStyleCoord(unit, union);
            }
        }
    }

    #[inline]
    /// Copies the unit and value from another `CoordData` type.
    fn copy_from<T: CoordData>(&mut self, other: &T) {
        unsafe {
            self.reset();
            self.copy_from_unchecked(other);
            self.addref_if_calc();
        }
    }

    #[inline]
    /// Moves the unit and value from another `CoordData` type.
    fn move_from<T: CoordData>(&mut self, other: T) {
        unsafe {
            self.reset();
            self.copy_from_unchecked(&other);
        }
    }

    #[inline]
    /// Copies the unit and value from another `CoordData` type without checking
    /// the type of the value (so refcounted values like calc may leak).
    unsafe fn copy_from_unchecked<T: CoordData>(&mut self, other: &T) {
        let (unit, union) = self.values_mut();
        *unit = other.unit();
        *union = other.union();
    }

    /// Useful for initializing uninits, given that `set_value` may segfault on
    /// uninits.
    fn leaky_set_null(&mut self) {
        use gecko_bindings::structs::nsStyleUnit::*;
        unsafe {
            let (unit, union) = self.values_mut();
            *unit = eStyleUnit_Null;
            *union.mInt.as_mut() = 0;
        }
    }

    #[inline(always)]
    /// Sets the inner value.
    fn set_value(&mut self, value: CoordDataValue) {
        use gecko_bindings::structs::nsStyleUnit::*;
        use self::CoordDataValue::*;
        self.reset();
        unsafe {
            let (unit, union) = self.values_mut();
            match value {
                Null => {
                    *unit = eStyleUnit_Null;
                    *union.mInt.as_mut() = 0;
                }
                Normal => {
                    *unit = eStyleUnit_Normal;
                    *union.mInt.as_mut() = 0;
                }
                Auto => {
                    *unit = eStyleUnit_Auto;
                    *union.mInt.as_mut() = 0;
                }
                None => {
                    *unit = eStyleUnit_None;
                    *union.mInt.as_mut() = 0;
                }
                Percent(f) => {
                    *unit = eStyleUnit_Percent;
                    *union.mFloat.as_mut() = f;
                }
                Factor(f) => {
                    *unit = eStyleUnit_Factor;
                    *union.mFloat.as_mut() = f;
                }
                Degree(f) => {
                    *unit = eStyleUnit_Degree;
                    *union.mFloat.as_mut() = f;
                }
                Grad(f) => {
                    *unit = eStyleUnit_Grad;
                    *union.mFloat.as_mut() = f;
                }
                Radian(f) => {
                    *unit = eStyleUnit_Radian;
                    *union.mFloat.as_mut() = f;
                }
                Turn(f) => {
                    *unit = eStyleUnit_Turn;
                    *union.mFloat.as_mut() = f;
                }
                FlexFraction(f) => {
                    *unit = eStyleUnit_FlexFraction;
                    *union.mFloat.as_mut() = f;
                }
                Coord(coord) => {
                    *unit = eStyleUnit_Coord;
                    *union.mInt.as_mut() = coord;
                }
                Integer(i) => {
                    *unit = eStyleUnit_Integer;
                    *union.mInt.as_mut() = i;
                }
                Enumerated(i) => {
                    *unit = eStyleUnit_Enumerated;
                    *union.mInt.as_mut() = i as i32;
                }
                Calc(calc) => {
                    // Gecko_SetStyleCoordCalcValue changes the unit internally
                    Gecko_SetStyleCoordCalcValue(unit, union, calc);
                }
            }
        }
    }

    #[inline]
    /// Gets the `Calc` value mutably, asserts in debug builds if the unit is
    /// not `Calc`.
    unsafe fn as_calc_mut(&mut self) -> &mut nsStyleCoord_Calc {
        debug_assert!(self.unit() == nsStyleUnit::eStyleUnit_Calc);
        &mut *(*self.union().mPointer.as_mut() as *mut nsStyleCoord_Calc)
    }

    #[inline]
    /// Does what it promises, if the unit is `calc`, it bumps the reference
    /// count _of the calc expression_.
    fn addref_if_calc(&mut self) {
        unsafe {
            if self.unit() == nsStyleUnit::eStyleUnit_Calc {
                Gecko_AddRefCalcArbitraryThread(self.as_calc_mut());
            }
        }
    }
}
/// A trait to abstract on top of a `nsStyleCoord`-like object.
pub unsafe trait CoordData {
    /// Get the unit of this object.
    fn unit(&self) -> nsStyleUnit;
    /// Get the `nsStyleUnion` for this object.
    fn union(&self) -> nsStyleUnion;

    #[inline(always)]
    /// Get the appropriate value for this object.
    fn as_value(&self) -> CoordDataValue {
        use gecko_bindings::structs::nsStyleUnit::*;
        use self::CoordDataValue::*;
        unsafe {
            match self.unit() {
                eStyleUnit_Null => Null,
                eStyleUnit_Normal => Normal,
                eStyleUnit_Auto => Auto,
                eStyleUnit_None => None,
                eStyleUnit_Percent => Percent(self.get_float()),
                eStyleUnit_Factor => Factor(self.get_float()),
                eStyleUnit_Degree => Degree(self.get_float()),
                eStyleUnit_Grad => Grad(self.get_float()),
                eStyleUnit_Radian => Radian(self.get_float()),
                eStyleUnit_Turn => Turn(self.get_float()),
                eStyleUnit_FlexFraction => FlexFraction(self.get_float()),
                eStyleUnit_Coord => Coord(self.get_integer()),
                eStyleUnit_Integer => Integer(self.get_integer()),
                eStyleUnit_Enumerated => Enumerated(self.get_integer() as u32),
                eStyleUnit_Calc => Calc(self.get_calc_value()),
            }
        }
    }

    #[inline]
    /// Pretend inner value is a float; obtain it.
    unsafe fn get_float(&self) -> f32 {
        use gecko_bindings::structs::nsStyleUnit::*;
        debug_assert!(self.unit() == eStyleUnit_Percent || self.unit() == eStyleUnit_Factor
                      || self.unit() == eStyleUnit_Degree || self.unit() == eStyleUnit_Grad
                      || self.unit() == eStyleUnit_Radian || self.unit() == eStyleUnit_Turn
                      || self.unit() == eStyleUnit_FlexFraction);
        *self.union().mFloat.as_ref()
    }

    #[inline]
    /// Pretend inner value is an int; obtain it.
    unsafe fn get_integer(&self) -> i32 {
        use gecko_bindings::structs::nsStyleUnit::*;
        debug_assert!(self.unit() == eStyleUnit_Coord || self.unit() == eStyleUnit_Integer
                      || self.unit() == eStyleUnit_Enumerated);
        *self.union().mInt.as_ref()
    }

    #[inline]
    /// Pretend inner value is a calc; obtain it.
    /// Ensure that the unit is Calc before calling this.
    unsafe fn get_calc_value(&self) -> nsStyleCoord_CalcValue {
        debug_assert!(self.unit() == nsStyleUnit::eStyleUnit_Calc);
        (*self.as_calc())._base
    }

    #[inline]
    /// Pretend the inner value is a calc expression, and obtain it.
    unsafe fn as_calc(&self) -> &nsStyleCoord_Calc {
        debug_assert!(self.unit() == nsStyleUnit::eStyleUnit_Calc);
        &*(*self.union().mPointer.as_ref() as *const nsStyleCoord_Calc)
    }
}
<|endoftext|>


<commit_before>//! Unlike the usual language server features, hints are not generated on-demand
//! with the usual request-response model. Instead, a hints server runs in the
//! background all the time. That way, the hints can progressively get better.
//! For example, when opening a long file, the hints may appear from top to
//! bottom as more code is evaluated. Then, the individual closures could get
//! fuzzed with ever-more-complex inputs, resulting in some error cases to be
//! displayed over time.
//! While doing all that, we can pause regularly between executing instructions
//! so that we don't occupy a single CPU at 100%.

use super::utils::LspPositionConversion;
use crate::{
    compiler::{
        ast::{AstKind, FindAst},
        ast_to_hir::AstToHir,
        cst_to_ast::CstToAst,
        hir,
    },
    database::Database,
    input::{Input, InputDb},
    language_server::utils::TupleToPosition,
    vm::{tracer::TraceEntry, use_provider::DbUseProvider, value::Value, Status, Vm},
    CloneWithExtension,
};
use itertools::Itertools;
use lsp_types::{notification::Notification, Position};
use rand::{prelude::SliceRandom, thread_rng};
use serde::{Deserialize, Serialize};
use std::{collections::HashMap, fs, sync::Arc, time::Duration};
use tokio::{
    sync::{
        mpsc::{error::TryRecvError, Receiver, Sender},
        Mutex,
    },
    time::sleep,
};

pub enum Event {
    UpdateModule(Input),
    CloseModule(Input),
    Shutdown,
}

#[derive(PartialEq, Eq, Debug, Clone, Serialize, Deserialize)]
pub struct Hint {
    kind: HintKind,
    text: String,
    position: Position,
}
#[derive(PartialEq, Eq, Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum HintKind {
    Value,
    Panic,
}

#[derive(Serialize, Deserialize)]
pub struct HintsNotification {
    pub uri: String,
    pub hints: Vec<Hint>,
}
impl Notification for HintsNotification {
    const METHOD: &'static str = "candy/textDocument/publishHints";

    type Params = Self;
}

pub async fn run_server(
    db: Arc<Mutex<Database>>,
    mut incoming_events: Receiver<Event>,
    outgoing_hints: Sender<(Input, Vec<Hint>)>,
) {
    let mut vms = HashMap::new();

    'server_loop: loop {
        loop {
            match incoming_events.try_recv() {
                Ok(event) => handle_event(db.clone(), &mut incoming_events, &mut vms, event).await,
                Err(TryRecvError::Empty) => break,
                Err(TryRecvError::Disconnected) => break 'server_loop,
            }
        }

        let input_of_chosen_vm = vms
            .iter()
            .filter(|(_, vm)| match vm.status() {
                Status::Running => true,
                Status::Done => false,
                Status::Panicked(_) => false,
            })
            .collect_vec()
            .choose(&mut thread_rng())
            .map(|(input, _)| (*input).clone());

        if let Some(input) = input_of_chosen_vm {
            let hints = {
                let vm = vms.get_mut(&input).unwrap();
                let db = db.lock().await;
                let use_provider = DbUseProvider { db: &db };
                vm.run(&use_provider, 5);
                collect_hints(&db, &input, vm)
            };
            outgoing_hints.send((input, hints)).await.unwrap();
        }

        sleep(Duration::from_millis(100)).await;
    }
}

async fn handle_event(
    db: Arc<Mutex<Database>>,
    incoming_events: &mut Receiver<Event>,
    vms: &mut HashMap<Input, Vm>,
    event: Event,
) {
    match event {
        Event::UpdateModule(input) => match vm_for_input(db, input.clone()).await {
            Some(vm) => {
                vms.insert(input, vm);
            }
            None => {}
        },
        Event::CloseModule(input) => {
            vms.remove(&input);
        }
        Event::Shutdown => {
            incoming_events.close();
        }
    }
}

async fn vm_for_input(db: Arc<Mutex<Database>>, input: Input) -> Option<Vm> {
    let db = db.lock().await;
    let module_closure = Value::module_closure_of_input(&db, input.clone())?;
    let mut vm = Vm::new();
    let use_provider = DbUseProvider { db: &db };
    vm.set_up_module_closure_execution(&use_provider, module_closure);
    Some(vm)
}

fn collect_hints(db: &Database, input: &Input, vm: &mut Vm) -> Vec<Hint> {
    log::debug!("Calculating hints for {input}");
    let mut hints = vec![];

    match vm.status() {
        Status::Running => {
            log::info!("VM is still running.");
        }
        Status::Done => {
            let return_value = vm.tear_down_module_closure_execution();
            log::info!("VM is done. Export map: {return_value}");
        }
        Status::Panicked(value) => {
            log::error!("VM panicked with value {value}.");
            match panic_hint(&db, input.clone(), &vm, value) {
                Some(hint) => {
                    hints.push(hint);
                }
                None => log::error!("Module panicked, but we are not displaying an error."),
            }
        }
    };
    if let Some(path) = input.to_path() {
        let trace = vm.tracer.dump_call_tree();
        let trace_file = path.clone_with_extension("candy.trace");
        fs::write(trace_file.clone(), trace).unwrap();
    }

    for entry in vm.tracer.log() {
        let (id, value) = match entry {
            TraceEntry::ValueEvaluated { id, value } => {
                if &id.input != input {
                    continue;
                }
                let ast_id = match db.hir_to_ast_id(id.clone()) {
                    Some(ast_id) => ast_id,
                    None => continue,
                };
                let ast = match db.ast(input.clone()) {
                    Some((ast, _)) => (*ast).clone(),
                    None => continue,
                };
                match ast.find(&ast_id) {
                    None => continue,
                    Some(ast) => match ast.kind {
                        AstKind::Assignment { .. } => {}
                        _ => continue,
                    },
                }
                (id.clone(), value.clone())
            }
            _ => continue,
        };

        hints.push(Hint {
            kind: HintKind::Value,
            text: format!(" # {value}"),
            position: id_to_end_of_line(&db, input.clone(), id).unwrap(),
        });
    }

    // If multiple hints are on the same line, only show the last one.
    // TODO: Give panic hints a higher priority.
    let hints = hints
        .into_iter()
        .group_by(|hint| hint.position.line)
        .into_iter()
        .map(|(_, hints)| hints.into_iter().last().unwrap())
        .collect_vec();

    hints
}

fn panic_hint(db: &Database, input: Input, vm: &Vm, panic_message: Value) -> Option<Hint> {
    // We want to show the hint at the last call site still inside the current
    // module. If there is no call site in this module, then the panic results
    // from a compiler error in a previous stage which is already reported.
    let last_call_in_this_module = vm
        .tracer
        .stack()
        .iter()
        .rev()
        .filter(|entry| {
            let id = match entry {
                TraceEntry::CallStarted { id, .. } => id,
                TraceEntry::NeedsStarted { id, .. } => id,
                _ => return false,
            };
            // Make sure the entry comes from the same file and is not generated code.
            id.input == input && db.hir_to_cst_id(id.clone()).is_some()
        })
        .next()?;

    let (id, call_info) = match last_call_in_this_module {
        TraceEntry::CallStarted { id, closure, args } => (
            id,
            format!(
                "{closure} {}",
                args.iter().map(|arg| format!("{arg}")).join(" ")
            ),
        ),
        TraceEntry::NeedsStarted {
            id,
            condition,
            message,
        } => (id, format!("needs {condition} {message}")),
        _ => unreachable!(),
    };

    Some(Hint {
        kind: HintKind::Panic,
        text: format!(
            " # Calling {call_info} panicked because {}.",
            if let Value::Text(message) = panic_message {
                message
            } else {
                format!("{panic_message}")
            }
        ),
        position: id_to_end_of_line(db, input, id.clone())?,
    })
}

fn id_to_end_of_line(db: &Database, input: Input, id: hir::Id) -> Option<Position> {
    let span = db.hir_id_to_display_span(id.clone())?;

    let line = db
        .offset_to_lsp(input.clone(), span.start)
        .to_position()
        .line;
    let line_start_offsets = db.line_start_utf8_byte_offsets(input.clone());
    let last_characer_of_line = if line as usize == line_start_offsets.len() - 1 {
        db.get_input(input.clone()).unwrap().len()
    } else {
        line_start_offsets[(line + 1) as usize] - 1
    };
    let position = db
        .offset_to_lsp(input.clone(), last_characer_of_line)
        .to_position();
    Some(position)
}
<commit_msg>Improve panic hint text<commit_after>//! Unlike the usual language server features, hints are not generated on-demand
//! with the usual request-response model. Instead, a hints server runs in the
//! background all the time. That way, the hints can progressively get better.
//! For example, when opening a long file, the hints may appear from top to
//! bottom as more code is evaluated. Then, the individual closures could get
//! fuzzed with ever-more-complex inputs, resulting in some error cases to be
//! displayed over time.
//! While doing all that, we can pause regularly between executing instructions
//! so that we don't occupy a single CPU at 100%.

use super::utils::LspPositionConversion;
use crate::{
    compiler::{
        ast::{AstKind, FindAst},
        ast_to_hir::AstToHir,
        cst_to_ast::CstToAst,
        hir,
    },
    database::Database,
    input::{Input, InputDb},
    language_server::utils::TupleToPosition,
    vm::{tracer::TraceEntry, use_provider::DbUseProvider, value::Value, Status, Vm},
    CloneWithExtension,
};
use itertools::Itertools;
use lsp_types::{notification::Notification, Position};
use rand::{prelude::SliceRandom, thread_rng};
use serde::{Deserialize, Serialize};
use std::{collections::HashMap, fs, sync::Arc, time::Duration};
use tokio::{
    sync::{
        mpsc::{error::TryRecvError, Receiver, Sender},
        Mutex,
    },
    time::sleep,
};

pub enum Event {
    UpdateModule(Input),
    CloseModule(Input),
    Shutdown,
}

#[derive(PartialEq, Eq, Debug, Clone, Serialize, Deserialize)]
pub struct Hint {
    kind: HintKind,
    text: String,
    position: Position,
}
#[derive(PartialEq, Eq, Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum HintKind {
    Value,
    Panic,
}

#[derive(Serialize, Deserialize)]
pub struct HintsNotification {
    pub uri: String,
    pub hints: Vec<Hint>,
}
impl Notification for HintsNotification {
    const METHOD: &'static str = "candy/textDocument/publishHints";

    type Params = Self;
}

pub async fn run_server(
    db: Arc<Mutex<Database>>,
    mut incoming_events: Receiver<Event>,
    outgoing_hints: Sender<(Input, Vec<Hint>)>,
) {
    let mut vms = HashMap::new();

    'server_loop: loop {
        loop {
            match incoming_events.try_recv() {
                Ok(event) => handle_event(db.clone(), &mut incoming_events, &mut vms, event).await,
                Err(TryRecvError::Empty) => break,
                Err(TryRecvError::Disconnected) => break 'server_loop,
            }
        }

        let input_of_chosen_vm = vms
            .iter()
            .filter(|(_, vm)| match vm.status() {
                Status::Running => true,
                Status::Done => false,
                Status::Panicked(_) => false,
            })
            .collect_vec()
            .choose(&mut thread_rng())
            .map(|(input, _)| (*input).clone());

        if let Some(input) = input_of_chosen_vm {
            let hints = {
                let vm = vms.get_mut(&input).unwrap();
                let db = db.lock().await;
                let use_provider = DbUseProvider { db: &db };
                vm.run(&use_provider, 5);
                collect_hints(&db, &input, vm)
            };
            outgoing_hints.send((input, hints)).await.unwrap();
        }

        sleep(Duration::from_millis(100)).await;
    }
}

async fn handle_event(
    db: Arc<Mutex<Database>>,
    incoming_events: &mut Receiver<Event>,
    vms: &mut HashMap<Input, Vm>,
    event: Event,
) {
    match event {
        Event::UpdateModule(input) => match vm_for_input(db, input.clone()).await {
            Some(vm) => {
                vms.insert(input, vm);
            }
            None => {}
        },
        Event::CloseModule(input) => {
            vms.remove(&input);
        }
        Event::Shutdown => {
            incoming_events.close();
        }
    }
}

async fn vm_for_input(db: Arc<Mutex<Database>>, input: Input) -> Option<Vm> {
    let db = db.lock().await;
    let module_closure = Value::module_closure_of_input(&db, input.clone())?;
    let mut vm = Vm::new();
    let use_provider = DbUseProvider { db: &db };
    vm.set_up_module_closure_execution(&use_provider, module_closure);
    Some(vm)
}

fn collect_hints(db: &Database, input: &Input, vm: &mut Vm) -> Vec<Hint> {
    log::debug!("Calculating hints for {input}");
    let mut hints = vec![];

    match vm.status() {
        Status::Running => {
            log::info!("VM is still running.");
        }
        Status::Done => {
            let return_value = vm.tear_down_module_closure_execution();
            log::info!("VM is done. Export map: {return_value}");
        }
        Status::Panicked(value) => {
            log::error!("VM panicked with value {value}.");
            match panic_hint(&db, input.clone(), &vm, value) {
                Some(hint) => {
                    hints.push(hint);
                }
                None => log::error!("Module panicked, but we are not displaying an error."),
            }
        }
    };
    if let Some(path) = input.to_path() {
        let trace = vm.tracer.dump_call_tree();
        let trace_file = path.clone_with_extension("candy.trace");
        fs::write(trace_file.clone(), trace).unwrap();
    }

    for entry in vm.tracer.log() {
        let (id, value) = match entry {
            TraceEntry::ValueEvaluated { id, value } => {
                if &id.input != input {
                    continue;
                }
                let ast_id = match db.hir_to_ast_id(id.clone()) {
                    Some(ast_id) => ast_id,
                    None => continue,
                };
                let ast = match db.ast(input.clone()) {
                    Some((ast, _)) => (*ast).clone(),
                    None => continue,
                };
                match ast.find(&ast_id) {
                    None => continue,
                    Some(ast) => match ast.kind {
                        AstKind::Assignment { .. } => {}
                        _ => continue,
                    },
                }
                (id.clone(), value.clone())
            }
            _ => continue,
        };

        hints.push(Hint {
            kind: HintKind::Value,
            text: format!(" # {value}"),
            position: id_to_end_of_line(&db, input.clone(), id).unwrap(),
        });
    }

    // If multiple hints are on the same line, only show the last one.
    // TODO: Give panic hints a higher priority.
    let hints = hints
        .into_iter()
        .group_by(|hint| hint.position.line)
        .into_iter()
        .map(|(_, hints)| hints.into_iter().last().unwrap())
        .collect_vec();

    hints
}

fn panic_hint(db: &Database, input: Input, vm: &Vm, panic_message: Value) -> Option<Hint> {
    // We want to show the hint at the last call site still inside the current
    // module. If there is no call site in this module, then the panic results
    // from a compiler error in a previous stage which is already reported.
    let last_call_in_this_module = vm
        .tracer
        .stack()
        .iter()
        .rev()
        .filter(|entry| {
            let id = match entry {
                TraceEntry::CallStarted { id, .. } => id,
                TraceEntry::NeedsStarted { id, .. } => id,
                _ => return false,
            };
            // Make sure the entry comes from the same file and is not generated code.
            id.input == input && db.hir_to_cst_id(id.clone()).is_some()
        })
        .next()?;

    let (id, call_info) = match last_call_in_this_module {
        TraceEntry::CallStarted { id, closure, args } => (
            id,
            format!(
                "{closure} {}",
                args.iter().map(|arg| format!("{arg}")).join(" ")
            ),
        ),
        TraceEntry::NeedsStarted {
            id,
            condition,
            message,
        } => (id, format!("needs {condition} {message}")),
        _ => unreachable!(),
    };

    let message = if let Value::Text(message) = panic_message {
        message
    } else {
        format!("{panic_message}")
    };
    Some(Hint {
        kind: HintKind::Panic,
        text: format!(
            " # Calling `{call_info}` panicked because {}{}",
            message,
            if message.ends_with('.') { "" } else { "." }
        ),
        position: id_to_end_of_line(db, input, id.clone())?,
    })
}

fn id_to_end_of_line(db: &Database, input: Input, id: hir::Id) -> Option<Position> {
    let span = db.hir_id_to_display_span(id.clone())?;

    let line = db
        .offset_to_lsp(input.clone(), span.start)
        .to_position()
        .line;
    let line_start_offsets = db.line_start_utf8_byte_offsets(input.clone());
    let last_characer_of_line = if line as usize == line_start_offsets.len() - 1 {
        db.get_input(input.clone()).unwrap().len()
    } else {
        line_start_offsets[(line + 1) as usize] - 1
    };
    let position = db
        .offset_to_lsp(input.clone(), last_characer_of_line)
        .to_position();
    Some(position)
}
<|endoftext|>