<commit_before>use wpilib::wpilib_hal::*;
use wpilib::hal_call::*;
use wpilib::Throttler;

use std::{thread, time, mem, ffi, sync};

use atom::Atom;

const MAX_JOYSTICK_PORTS: usize = 6;
const MAX_JOYSTICK_AXES: usize = 12;
const MAX_JOYSTICK_POVS: usize = 12;

#[derive(Default)]
struct Joysticks {
    axes: [HAL_JoystickAxes; MAX_JOYSTICK_PORTS],
    povs: [HAL_JoystickPOVs; MAX_JOYSTICK_PORTS],
    buttons: [HAL_JoystickButtons; MAX_JOYSTICK_PORTS],
    descriptor: [HAL_JoystickDescriptor; MAX_JOYSTICK_PORTS],
}

#[derive(Debug)]
/// The robot's state
pub enum RobotState {
    Disabled,
    Autonomous,
    Teleop,
    Test,
    EStop,
}

type DSBuffer = Box<(Joysticks, HAL_ControlWord)>;

/// An interface to the driver station, FMS, and joysticks
pub struct DriverStation {
    data: sync::Arc<Atom<DSBuffer>>,
    joysticks: Joysticks,

    /// The state that the robot is currently in.
    pub state: RobotState,

    /// Whether or not the robot is attached to the FMS.
    pub fms_attached: bool,

    /// Whether or not the robot has connection to the driver station.
    pub ds_attached: bool,

    report_throttler: Throttler<f64>,

    waiter: sync::Arc<(sync::Mutex<bool>, sync::Condvar)>,

    join: Option<thread::JoinHandle<()>>,
}

static CREATE_DS: sync::Once = sync::ONCE_INIT;
static mut DRIVER_STATION: *mut DriverStation = 0 as *mut DriverStation;

#[derive(Debug, Copy, Clone)]
/// Some error involving joysticks
pub enum JoystickError {
    JoystickDNE,
    ChannelUnplugged,
    ChannelDNE,
}

#[derive(Debug, Copy, Clone)]
/// An alliance, red or blue
pub enum AllianceId {
    Red,
    Blue,
    Invalid,
}

impl DriverStation {
    fn new() -> DriverStation {
        let data_atom = sync::Arc::new(Atom::empty());
        let waiter = sync::Arc::new((sync::Mutex::new(false), sync::Condvar::new()));

        let mut ds = DriverStation {
            data: data_atom,
            joysticks: Joysticks::default(),
            state: RobotState::Disabled,
            fms_attached: false,
            ds_attached: false,

            // For now use an interval of 0 so we don't actually throttle messages, as the FPGA
            // timer isn't implemented yet.
            report_throttler: Throttler::new(0.0, 0.0),

            waiter: waiter,

            join: None,
        };

        ds.spawn_updater();
        ds
    }

    /// Spawn the updater thread. This should not be called after the constructor is finished.
    fn spawn_updater(&mut self) {
        let data_atom = self.data.clone();
        let waiter = self.waiter.clone();

        self.join = Some(thread::spawn(move || {
            loop {
                // Wait for the HAL to get new data
                unsafe {
                    HAL_WaitForDSData();
                }

                // Update the joysticks and control word using the new data.
                let mut joysticks = Joysticks::default();
                for stick in 0..MAX_JOYSTICK_PORTS {
                    unsafe {
                        HAL_GetJoystickAxes(stick as i32,
                                            &mut joysticks.axes[stick] as *mut HAL_JoystickAxes);
                        HAL_GetJoystickPOVs(stick as i32,
                                            &mut joysticks.povs[stick] as *mut HAL_JoystickPOVs);
                        HAL_GetJoystickButtons(stick as i32,
                                                    &mut joysticks.buttons[stick] as
                                                    *mut HAL_JoystickButtons);
                        HAL_GetJoystickDescriptor(stick as i32,
                                                       &mut joysticks.descriptor[stick] as
                                                       *mut HAL_JoystickDescriptor);
                    }
                }

                let mut control_word: HAL_ControlWord = HAL_ControlWord::default();
                unsafe {
                    HAL_GetControlWord(&mut control_word as *mut HAL_ControlWord);
                }

                // Write that data into the atom for usage by callers
                data_atom.swap(Box::new((joysticks, control_word)));

                // Notify any threads waiting for data
                {
                    let mut guard = waiter.0.lock().unwrap();
                    *guard = true;
                    waiter.1.notify_all();
                }
            }
        }));
    }

    /// Read new joystick and control word data
    fn update_data(&mut self) {
        if let Some(boxed_data) = self.data.take() {
            let new_control_word = boxed_data.1;
            self.joysticks = boxed_data.0;
            self.state = if new_control_word.enabled() {
                if new_control_word.autonomous() {
                    RobotState::Autonomous
                } else {
                    RobotState::Teleop
                }
            } else if new_control_word.eStop() {
                RobotState::EStop
            } else {
                RobotState::Disabled
            };
            self.fms_attached = new_control_word.fmsAttached();
            self.ds_attached = new_control_word.dsAttached();
        }
    }

    /// Report an error to the driver station in its most general form. Don't use this directly,
    /// instead use it in other error reporting methods.
    fn report(&self, is_error: bool, code: i32, error: &str, location: &str, stack: &str) {
        unsafe {
            HAL_SendError(is_error as i32,
                          code,
                          false as i32,
                          ffi::CString::new(error).unwrap().into_raw(),
                          ffi::CString::new(location).unwrap().into_raw(),
                          ffi::CString::new(stack).unwrap().into_raw(),
                          true as i32);
        }
    }

    fn report_error(&mut self, error: &str) {
        self.report(true, 1, error, "", "");
    }

    fn report_warning(&mut self, warning: &str) {
        self.report(false, 1, warning, "", "");
    }

    /// Report a message at a throttled rate
    fn report_throttled(&mut self, is_error: bool, message: &str) {
        // Don't actually throttle it; FPGA timer is unimplemented
        let now = 1f64;
        if self.report_throttler.update(now) {
            self.report(is_error, 1, message, "", "");
        }
    }

    /// Get an instance of the driver station. This will create a new instance if one does not
    /// exist.
    pub fn instance() -> &'static mut DriverStation {
        unsafe {
            CREATE_DS.call_once(|| {
                DRIVER_STATION = mem::transmute(Box::new(DriverStation::new()));
            });
            &mut *DRIVER_STATION
        }
    }

    /// Get an axis on a joystick, in the range of [-1, 1].
    pub fn get_joystick_axis(&mut self, stick: usize, axis: usize) -> Result<f32, JoystickError> {
        self.update_data();

        if stick >= MAX_JOYSTICK_PORTS {
            self.report_throttled(true, "Bad joystick");
            Err(JoystickError::JoystickDNE)
        } else if axis >= MAX_JOYSTICK_AXES {
            self.report_throttled(true, "Bad joystick axis");
            Err(JoystickError::ChannelDNE)
        } else if axis >= self.joysticks.axes[stick].count as usize {
            self.report_throttled(true,
                                  "Joystick axis missing, check if all controllers are plugged in");
            Err(JoystickError::ChannelUnplugged)
        } else {
            Ok(self.joysticks.axes[stick].axes[axis])
        }
    }

    /// Get the position of a POV switch, in degrees.
    pub fn get_joystick_pov(&mut self, stick: usize, pov: usize) -> Result<i16, JoystickError> {
        self.update_data();

        if stick >= MAX_JOYSTICK_POVS {
            self.report_throttled(true, "Bad joystick");
            Err(JoystickError::JoystickDNE)
        } else if pov >= MAX_JOYSTICK_AXES {
            self.report_throttled(true, "Bad joystick pov");
            Err(JoystickError::ChannelDNE)
        } else if pov >= self.joysticks.povs[stick].count as usize {
            self.report_throttled(true,
                                  "Joystick pov missing, check if all controllers are plugged in");
            Err(JoystickError::ChannelUnplugged)
        } else {
            Ok(self.joysticks.povs[stick].povs[pov])
        }
    }

    /// Get the state of a button on a joystick.
    pub fn get_joystick_button(&mut self,
                               stick: usize,
                               button: usize)
                               -> Result<bool, JoystickError> {
        self.update_data();

        if stick >= MAX_JOYSTICK_POVS {
            self.report_throttled(true, "Bad joystick");
            Err(JoystickError::JoystickDNE)
        } else if button == 0 {
            self.report_throttled(true, "Bad joystick button (button IDs start from 1)");
            Err(JoystickError::ChannelDNE)
        } else if button >= self.joysticks.povs[stick].count as usize {
            self.report_throttled(true,
                                  "Joystick button missing, check if all controllers are plugged \
                                   in");
            Err(JoystickError::ChannelUnplugged)
        } else {
            let mask = 1 << (button - 1);
            Ok(self.joysticks.buttons[stick].buttons & mask != 0)
        }
    }

    /// Get the alliance the robot is on.
    pub fn get_alliance(&self) -> HalResult<AllianceId> {
        match hal_call!(HAL_GetAllianceStation())? {
            HAL_AllianceStationID::HAL_AllianceStationID_kRed1 |
            HAL_AllianceStationID::HAL_AllianceStationID_kRed2 |
            HAL_AllianceStationID::HAL_AllianceStationID_kRed3 => Ok(AllianceId::Red),
            HAL_AllianceStationID::HAL_AllianceStationID_kBlue1 |
            HAL_AllianceStationID::HAL_AllianceStationID_kBlue2 |
            HAL_AllianceStationID::HAL_AllianceStationID_kBlue3 => Ok(AllianceId::Blue),
        }
    }

    /// Get the id for the station the driver station is at, as an integer.
    pub fn get_station(&self) -> HalResult<i32> {
        match hal_call!(HAL_GetAllianceStation())? {
            HAL_AllianceStationID::HAL_AllianceStationID_kRed1 |
            HAL_AllianceStationID::HAL_AllianceStationID_kBlue1 => Ok(1),
            HAL_AllianceStationID::HAL_AllianceStationID_kRed2 |
            HAL_AllianceStationID::HAL_AllianceStationID_kBlue2 => Ok(2),
            HAL_AllianceStationID::HAL_AllianceStationID_kRed3 |
            HAL_AllianceStationID::HAL_AllianceStationID_kBlue3 => Ok(3),
        }
    }

    /// Waits for a new driver station packet
    pub fn wait_for_data(&self) {
        let &(ref wait_lock, ref wait_cond) = &*self.waiter;
        let mut has_data = wait_lock.lock().unwrap();
        while !*has_data {
            has_data = wait_cond.wait(has_data).unwrap();
        }
    }

    /// Waits for a new driver station packet and returns true, or returns false if timeout is
    /// exceeded.
    pub fn wait_for_data_or_timeout(&self, timeout: time::Duration) -> bool {
        let &(ref wait_lock, ref wait_cond) = &*self.waiter;
        let mut has_data = wait_lock.lock().unwrap();

        while !*has_data {
            let result = wait_cond.wait_timeout(has_data, timeout).unwrap();
            if result.1.timed_out() {
                return false;
            } else {
                has_data = result.0;
            }
        }
        true
    }
}

impl Drop for DriverStation {
    fn drop(&mut self) {
        if let Some(join) = self.join.take() {
            let _ = join.join();
        }
    }
}
<commit_msg>Fix cached implementations for robot state values<commit_after>use wpilib::wpilib_hal::*;
use wpilib::hal_call::*;
use wpilib::Throttler;

use std::{thread, time, mem, ffi, sync};

use atom::Atom;

const MAX_JOYSTICK_PORTS: usize = 6;
const MAX_JOYSTICK_AXES: usize = 12;
const MAX_JOYSTICK_POVS: usize = 12;

#[derive(Default)]
struct Joysticks {
    axes: [HAL_JoystickAxes; MAX_JOYSTICK_PORTS],
    povs: [HAL_JoystickPOVs; MAX_JOYSTICK_PORTS],
    buttons: [HAL_JoystickButtons; MAX_JOYSTICK_PORTS],
    descriptor: [HAL_JoystickDescriptor; MAX_JOYSTICK_PORTS],
}

#[derive(Debug, Copy, Clone)]
/// The robot's state
pub enum RobotState {
    Disabled,
    Autonomous,
    Teleop,
    Test,
    EStop,
}

type DSBuffer = Box<(Joysticks, HAL_ControlWord)>;

/// An interface to the driver station, FMS, and joysticks
pub struct DriverStation {
    data: sync::Arc<Atom<DSBuffer>>,
    joysticks: Joysticks,

    state: RobotState,
    fms_attached: bool,
    ds_attached: bool,

    report_throttler: Throttler<f64>,

    waiter: sync::Arc<(sync::Mutex<bool>, sync::Condvar)>,

    join: Option<thread::JoinHandle<()>>,
}

static CREATE_DS: sync::Once = sync::ONCE_INIT;
static mut DRIVER_STATION: *mut DriverStation = 0 as *mut DriverStation;

#[derive(Debug, Copy, Clone)]
/// Some error involving joysticks
pub enum JoystickError {
    JoystickDNE,
    ChannelUnplugged,
    ChannelDNE,
}

#[derive(Debug, Copy, Clone)]
/// An alliance, red or blue
pub enum AllianceId {
    Red,
    Blue,
    Invalid,
}

impl DriverStation {
    fn new() -> DriverStation {
        let data_atom = sync::Arc::new(Atom::empty());
        let waiter = sync::Arc::new((sync::Mutex::new(false), sync::Condvar::new()));

        let mut ds = DriverStation {
            data: data_atom,
            joysticks: Joysticks::default(),
            state: RobotState::Disabled,
            fms_attached: false,
            ds_attached: false,

            // For now use an interval of 0 so we don't actually throttle messages, as the FPGA
            // timer isn't implemented yet.
            report_throttler: Throttler::new(0.0, 0.0),

            waiter: waiter,

            join: None,
        };

        ds.spawn_updater();
        ds
    }

    /// Spawn the updater thread. This should not be called after the constructor is finished.
    fn spawn_updater(&mut self) {
        let data_atom = self.data.clone();
        let waiter = self.waiter.clone();

        self.join = Some(thread::spawn(move || {
            loop {
                // Wait for the HAL to get new data
                unsafe {
                    HAL_WaitForDSData();
                }

                // Update the joysticks and control word using the new data.
                let mut joysticks = Joysticks::default();
                for stick in 0..MAX_JOYSTICK_PORTS {
                    unsafe {
                        HAL_GetJoystickAxes(stick as i32,
                                            &mut joysticks.axes[stick] as *mut HAL_JoystickAxes);
                        HAL_GetJoystickPOVs(stick as i32,
                                            &mut joysticks.povs[stick] as *mut HAL_JoystickPOVs);
                        HAL_GetJoystickButtons(stick as i32,
                                                    &mut joysticks.buttons[stick] as
                                                    *mut HAL_JoystickButtons);
                        HAL_GetJoystickDescriptor(stick as i32,
                                                       &mut joysticks.descriptor[stick] as
                                                       *mut HAL_JoystickDescriptor);
                    }
                }

                let mut control_word: HAL_ControlWord = HAL_ControlWord::default();
                unsafe {
                    HAL_GetControlWord(&mut control_word as *mut HAL_ControlWord);
                }

                // Write that data into the atom for usage by callers
                data_atom.swap(Box::new((joysticks, control_word)));

                // Notify any threads waiting for data
                {
                    let mut guard = waiter.0.lock().unwrap();
                    *guard = true;
                    waiter.1.notify_all();
                }
            }
        }));
    }

    /// Read new joystick and control word data
    fn update_data(&mut self) {
        if let Some(boxed_data) = self.data.take() {
            let new_control_word = boxed_data.1;
            self.joysticks = boxed_data.0;
            self.state = if new_control_word.enabled() {
                if new_control_word.autonomous() {
                    RobotState::Autonomous
                } else {
                    RobotState::Teleop
                }
            } else if new_control_word.eStop() {
                RobotState::EStop
            } else {
                RobotState::Disabled
            };
            self.fms_attached = new_control_word.fmsAttached();
            self.ds_attached = new_control_word.dsAttached();
        }
    }

    /// Report an error to the driver station in its most general form. Don't use this directly,
    /// instead use it in other error reporting methods.
    fn report(&self, is_error: bool, code: i32, error: &str, location: &str, stack: &str) {
        unsafe {
            HAL_SendError(is_error as i32,
                          code,
                          false as i32,
                          ffi::CString::new(error).unwrap().into_raw(),
                          ffi::CString::new(location).unwrap().into_raw(),
                          ffi::CString::new(stack).unwrap().into_raw(),
                          true as i32);
        }
    }

    fn report_error(&mut self, error: &str) {
        self.report(true, 1, error, "", "");
    }

    fn report_warning(&mut self, warning: &str) {
        self.report(false, 1, warning, "", "");
    }

    /// Report a message at a throttled rate
    fn report_throttled(&mut self, is_error: bool, message: &str) {
        // Don't actually throttle it; FPGA timer is unimplemented
        let now = 1f64;
        if self.report_throttler.update(now) {
            self.report(is_error, 1, message, "", "");
        }
    }

    /// Get an instance of the driver station. This will create a new instance if one does not
    /// exist.
    pub fn instance() -> &'static mut DriverStation {
        unsafe {
            CREATE_DS.call_once(|| {
                DRIVER_STATION = mem::transmute(Box::new(DriverStation::new()));
            });
            &mut *DRIVER_STATION
        }
    }

    /// Get an axis on a joystick, in the range of [-1, 1].
    pub fn get_joystick_axis(&mut self, stick: usize, axis: usize) -> Result<f32, JoystickError> {
        self.update_data();

        if stick >= MAX_JOYSTICK_PORTS {
            self.report_throttled(true, "Bad joystick");
            Err(JoystickError::JoystickDNE)
        } else if axis >= MAX_JOYSTICK_AXES {
            self.report_throttled(true, "Bad joystick axis");
            Err(JoystickError::ChannelDNE)
        } else if axis >= self.joysticks.axes[stick].count as usize {
            self.report_throttled(true,
                                  "Joystick axis missing, check if all controllers are plugged in");
            Err(JoystickError::ChannelUnplugged)
        } else {
            Ok(self.joysticks.axes[stick].axes[axis])
        }
    }

    /// Get the position of a POV switch, in degrees.
    pub fn get_joystick_pov(&mut self, stick: usize, pov: usize) -> Result<i16, JoystickError> {
        self.update_data();

        if stick >= MAX_JOYSTICK_POVS {
            self.report_throttled(true, "Bad joystick");
            Err(JoystickError::JoystickDNE)
        } else if pov >= MAX_JOYSTICK_AXES {
            self.report_throttled(true, "Bad joystick pov");
            Err(JoystickError::ChannelDNE)
        } else if pov >= self.joysticks.povs[stick].count as usize {
            self.report_throttled(true,
                                  "Joystick pov missing, check if all controllers are plugged in");
            Err(JoystickError::ChannelUnplugged)
        } else {
            Ok(self.joysticks.povs[stick].povs[pov])
        }
    }

    /// Get the state of a button on a joystick.
    pub fn get_joystick_button(&mut self,
                               stick: usize,
                               button: usize)
                               -> Result<bool, JoystickError> {
        self.update_data();

        if stick >= MAX_JOYSTICK_POVS {
            self.report_throttled(true, "Bad joystick");
            Err(JoystickError::JoystickDNE)
        } else if button == 0 {
            self.report_throttled(true, "Bad joystick button (button IDs start from 1)");
            Err(JoystickError::ChannelDNE)
        } else if button >= self.joysticks.povs[stick].count as usize {
            self.report_throttled(true,
                                  "Joystick button missing, check if all controllers are plugged \
                                   in");
            Err(JoystickError::ChannelUnplugged)
        } else {
            let mask = 1 << (button - 1);
            Ok(self.joysticks.buttons[stick].buttons & mask != 0)
        }
    }

    /// Get the alliance the robot is on.
    pub fn get_alliance(&self) -> HalResult<AllianceId> {
        match hal_call!(HAL_GetAllianceStation())? {
            HAL_AllianceStationID::HAL_AllianceStationID_kRed1 |
            HAL_AllianceStationID::HAL_AllianceStationID_kRed2 |
            HAL_AllianceStationID::HAL_AllianceStationID_kRed3 => Ok(AllianceId::Red),
            HAL_AllianceStationID::HAL_AllianceStationID_kBlue1 |
            HAL_AllianceStationID::HAL_AllianceStationID_kBlue2 |
            HAL_AllianceStationID::HAL_AllianceStationID_kBlue3 => Ok(AllianceId::Blue),
        }
    }

    /// Get the id for the station the driver station is at, as an integer.
    pub fn get_station(&self) -> HalResult<i32> {
        match hal_call!(HAL_GetAllianceStation())? {
            HAL_AllianceStationID::HAL_AllianceStationID_kRed1 |
            HAL_AllianceStationID::HAL_AllianceStationID_kBlue1 => Ok(1),
            HAL_AllianceStationID::HAL_AllianceStationID_kRed2 |
            HAL_AllianceStationID::HAL_AllianceStationID_kBlue2 => Ok(2),
            HAL_AllianceStationID::HAL_AllianceStationID_kRed3 |
            HAL_AllianceStationID::HAL_AllianceStationID_kBlue3 => Ok(3),
        }
    }

    /// Wait for a new driver station packet.
    pub fn wait_for_data(&self) {
        let &(ref wait_lock, ref wait_cond) = &*self.waiter;
        let mut has_data = wait_lock.lock().unwrap();
        while !*has_data {
            has_data = wait_cond.wait(has_data).unwrap();
        }
    }

    /// Waits for a new driver station packet and returns true, or returns false if timeout is
    /// exceeded.
    pub fn wait_for_data_or_timeout(&self, timeout: time::Duration) -> bool {
        let &(ref wait_lock, ref wait_cond) = &*self.waiter;
        let mut has_data = wait_lock.lock().unwrap();

        while !*has_data {
            let result = wait_cond.wait_timeout(has_data, timeout).unwrap();
            if result.1.timed_out() {
                return false;
            } else {
                has_data = result.0;
            }
        }
        true
    }

    /// Does the robot have connection to the FMS?
    pub fn is_fms_attached(&mut self) -> bool {
        self.update_data();
        self.fms_attached
    }

    /// Does the robot have connection to the driver station?
    pub fn is_ds_attached(&mut self) -> bool {
        self.update_data();
        self.fms_attached
    }

    /// Get the state of the robot.
    pub fn get_state(&mut self) -> RobotState {
        self.update_data();
        self.state
    }
}

impl Drop for DriverStation {
    fn drop(&mut self) {
        if let Some(join) = self.join.take() {
            let _ = join.join();
        }
    }
}
<|endoftext|>


<commit_before>use std::process;
use std::process::{Command, Stdio};

fn main() {
    #[cfg(target_os = "macos")]
    fn inner() -> &'static str {
        "open"
    }
    #[cfg(target_os = "windows")]
    fn inner() -> &'static str {
        "start"
    }
    #[cfg(target_os = "linux")]
    fn inner() -> &'static str {
        "xdg-open"
    }
    #[cfg(target_os = "unix")]
    fn inner() -> &'static str {
        "xdg-open"
    }

    let command = inner();
    let arg = "http://groonga.org/docs/";
    let err = match Command::new(command)
        .args(&[arg])
        .stdout(Stdio::null())
        .stderr(Stdio::null())
        .spawn() {
        Ok(_) => return (),
        Err(e) => e,
    };
    println!("Failed to execute docopen subcommand. reason: {:?}", err);
    process::exit(1);

}
<commit_msg>grnenv-docopen: Use powershell on Windows<commit_after>use std::process;
use std::process::{Command, Stdio};

fn main() {
    #[cfg(target_os = "macos")]
    fn inner() -> &'static str {
        "open"
    }
    #[cfg(target_os = "windows")]
    fn inner() -> &'static str {
        "powershell"
    }
    #[cfg(target_os = "linux")]
    fn inner() -> &'static str {
        "xdg-open"
    }
    #[cfg(target_os = "unix")]
    fn inner() -> &'static str {
        "xdg-open"
    }

    #[cfg(not(target_os = "windows"))]
    fn arg() -> [&'static str; 1] {
        ["http://groonga.org/docs/"]
    }
    #[cfg(target_os = "windows")]
    fn arg() -> [&'static str; 3] {
        ["-Command", "Start-Process", "http://groonga.org/docs"]
    }

    let command = inner();
    let arg = arg();
    let err = match Command::new(command)
        .args(&arg)
        .stdout(Stdio::null())
        .stderr(Stdio::null())
        .spawn() {
        Ok(_) => return (),
        Err(e) => e,
    };
    println!("Failed to execute docopen subcommand. reason: {:?}", err);
    process::exit(1);

}
<|endoftext|>


<commit_before>use std::ops::{Add, Div, Mul, Rem, Sub};
use typedef::*;

#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, PartialOrd)]
pub enum Value {
    Boolean(bool),
    Float(Float),
    Integer(Integer),
    Symbol(String),
    String(String),
    Char(char),
    Nil,
}

impl Value {
    pub fn is_nil(&self) -> bool {
        match self {
            &Value::Nil => true,
            _ => false,
        }
    }
}

impl Add for Value {
    type Output = Value;

    fn add(self, rhs: Self) -> Self::Output {
        match self {
            Value::Float(lhs_float) => {
                match rhs {
                    Value::Float(rhs_float) => Value::Float(lhs_float + rhs_float),
                    Value::Integer(rhs_integer) => Value::Float(lhs_float + (rhs_integer as Float)),
                    Value::String(rhs_string) => Value::String(format!("{}{}", lhs_float, rhs_string),),
                    _ => Value::Nil,
                }
            }

            Value::Integer(lhs_integer) => {
                match rhs {
                    Value::Float(rhs_float) => Value::Float((lhs_integer as Float) + rhs_float),
                    Value::Integer(rhs_integer) => Value::Integer(lhs_integer + rhs_integer),
                    Value::String(rhs_string) => Value::String(format!("{}{}", lhs_integer, rhs_string),),
                    _ => Value::Nil,
                }
            }

            Value::String(lhs_string) => {
                match rhs {
                    Value::Float(rhs_float) => Value::String(format!("{}{}", lhs_string, rhs_float),),
                    Value::Integer(rhs_integer) => Value::String(format!("{}{}", lhs_string, rhs_integer),),
                    Value::String(rhs_string) => Value::String(format!("{}{}", lhs_string, rhs_string),),
                    _ => Value::Nil,
                }
            }
            _ => Value::Nil,
        }
    }
}

impl Sub for Value {
    type Output = Value;

    fn sub(self, rhs: Self) -> Self::Output {
        match self {
            Value::Float(lhs_float) => {
                match rhs {
                    Value::Float(rhs_float) => Value::Float(lhs_float - rhs_float),
                    Value::Integer(rhs_integer) => Value::Float(lhs_float - (rhs_integer as Float)),
                    _ => Value::Nil,
                }
            }

            Value::Integer(lhs_integer) => {
                match rhs {
                    Value::Float(rhs_float) => Value::Float((lhs_integer as Float) - rhs_float),
                    Value::Integer(rhs_integer) => Value::Integer(lhs_integer - rhs_integer),
                    _ => Value::Nil,
                }
            }
            _ => Value::Nil,
        }
    }
}

impl Mul for Value {
    type Output = Value;

    fn mul(self, rhs: Self) -> Self::Output {
        match self {
            Value::Float(lhs_float) => {
                match rhs {
                    Value::Float(rhs_float) => Value::Float(lhs_float * rhs_float),
                    Value::Integer(rhs_integer) => Value::Float(lhs_float * (rhs_integer as Float)),
                    _ => Value::Nil,
                }
            }

            Value::Integer(lhs_integer) => {
                match rhs {
                    Value::Float(rhs_float) => Value::Float((lhs_integer as Float) * rhs_float),
                    Value::Integer(rhs_integer) => Value::Integer(lhs_integer * rhs_integer),
                    _ => Value::Nil,
                }
            }
            _ => Value::Nil,
        }
    }
}

impl Div for Value {
    type Output = Value;

    fn div(self, rhs: Self) -> Self::Output {
        match self {
            Value::Float(lhs_float) => {
                match rhs {
                    Value::Float(rhs_float) => Value::Float(lhs_float / rhs_float),
                    Value::Integer(rhs_integer) => Value::Float(lhs_float / (rhs_integer as Float)),
                    _ => Value::Nil,
                }
            }

            Value::Integer(lhs_integer) => {
                match rhs {
                    Value::Float(rhs_float) => Value::Float((lhs_integer as Float) / rhs_float),
                    Value::Integer(rhs_integer) => Value::Integer(lhs_integer / rhs_integer),
                    _ => Value::Nil,
                }
            }
            _ => Value::Nil,
        }
    }
}

impl Rem for Value {
    type Output = Value;

    fn rem(self, rhs: Self) -> Self::Output {
        match self {
            Value::Float(lhs_float) => {
                match rhs {
                    Value::Float(rhs_float) => Value::Float(lhs_float % rhs_float),
                    Value::Integer(rhs_integer) => Value::Float(lhs_float % (rhs_integer as Float)),
                    _ => Value::Nil,
                }
            }

            Value::Integer(lhs_integer) => {
                match rhs {
                    Value::Float(rhs_float) => Value::Float((lhs_integer as Float) % rhs_float),
                    Value::Integer(rhs_integer) => Value::Integer(lhs_integer % rhs_integer),
                    _ => Value::Nil,
                }
            }
            _ => Value::Nil,
        }
    }
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum Target {
    Register(usize),
    Stack(usize),
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum Instruction {
    Add(Target, Target),
    Sub(Target, Target),
    Div(Target, Target),
    Mul(Target, Target),
    Mod(Target, Target),

    Cmp(Target, Target),
    Jmp(Address),
    JmpLt(Address),
    JmpGt(Address),
    JmpEq(Address),

    Push(Target, Value),
    Mov(Target, Target),
    Swp(Target, Target),

    Call(Address),
    Ret,

    Halt,
}

#[derive(Serialize, Deserialize, Debug, Clone, Default)]
pub struct Program {
    pub preamble: String,
    pub version: String,
    pub instructions: Vec<Instruction>,
}

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

    #[test]
    fn comparison() {
        assert!(Value::Float(1.3) < Value::Float(1.4));
        assert!(Value::Float(3.1) > Value::Float(2.4));
        assert!(Value::Float(9.6) == Value::Float(9.6));

        assert!(Value::Integer(124) < Value::Integer(234));
        assert!(Value::Integer(4) > Value::Integer(1));
        assert!(Value::Integer(839) == Value::Integer(839));
    }
}
<commit_msg>Added documentation to values<commit_after>use std::ops::{Add, Div, Mul, Rem, Sub};
use typedef::*;

#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, PartialOrd)]
pub enum Value {
    Boolean(bool), // true | false
    Float(Float), // -1.33 | 0.23114 | 3.141 | ...
    Integer(Integer), // 12 | 42 | 1 | 0 | 24 | ...
    Symbol(String), // :hello | :test | :symbol | ...
    String(String), // "hello world" | "hello!" | "yellow \"blue\" or red" | ...
    Char(char), // 'a' | 'b' | 'c' | 'd' | ...
    Nil, // nil
}

impl Value {
    pub fn is_nil(&self) -> bool {
        match self {
            &Value::Nil => true,
            _ => false,
        }
    }
}

impl Add for Value {
    type Output = Value;

    fn add(self, rhs: Self) -> Self::Output {
        match self {
            Value::Float(lhs_float) => {
                match rhs {
                    Value::Float(rhs_float) => Value::Float(lhs_float + rhs_float),
                    Value::Integer(rhs_integer) => Value::Float(lhs_float + (rhs_integer as Float)),
                    Value::String(rhs_string) => Value::String(format!("{}{}", lhs_float, rhs_string),),
                    _ => Value::Nil,
                }
            }

            Value::Integer(lhs_integer) => {
                match rhs {
                    Value::Float(rhs_float) => Value::Float((lhs_integer as Float) + rhs_float),
                    Value::Integer(rhs_integer) => Value::Integer(lhs_integer + rhs_integer),
                    Value::String(rhs_string) => Value::String(format!("{}{}", lhs_integer, rhs_string),),
                    _ => Value::Nil,
                }
            }

            Value::String(lhs_string) => {
                match rhs {
                    Value::Float(rhs_float) => Value::String(format!("{}{}", lhs_string, rhs_float),),
                    Value::Integer(rhs_integer) => Value::String(format!("{}{}", lhs_string, rhs_integer),),
                    Value::String(rhs_string) => Value::String(format!("{}{}", lhs_string, rhs_string),),
                    _ => Value::Nil,
                }
            }
            _ => Value::Nil,
        }
    }
}

impl Sub for Value {
    type Output = Value;

    fn sub(self, rhs: Self) -> Self::Output {
        match self {
            Value::Float(lhs_float) => {
                match rhs {
                    Value::Float(rhs_float) => Value::Float(lhs_float - rhs_float),
                    Value::Integer(rhs_integer) => Value::Float(lhs_float - (rhs_integer as Float)),
                    _ => Value::Nil,
                }
            }

            Value::Integer(lhs_integer) => {
                match rhs {
                    Value::Float(rhs_float) => Value::Float((lhs_integer as Float) - rhs_float),
                    Value::Integer(rhs_integer) => Value::Integer(lhs_integer - rhs_integer),
                    _ => Value::Nil,
                }
            }
            _ => Value::Nil,
        }
    }
}

impl Mul for Value {
    type Output = Value;

    fn mul(self, rhs: Self) -> Self::Output {
        match self {
            Value::Float(lhs_float) => {
                match rhs {
                    Value::Float(rhs_float) => Value::Float(lhs_float * rhs_float),
                    Value::Integer(rhs_integer) => Value::Float(lhs_float * (rhs_integer as Float)),
                    _ => Value::Nil,
                }
            }

            Value::Integer(lhs_integer) => {
                match rhs {
                    Value::Float(rhs_float) => Value::Float((lhs_integer as Float) * rhs_float),
                    Value::Integer(rhs_integer) => Value::Integer(lhs_integer * rhs_integer),
                    _ => Value::Nil,
                }
            }
            _ => Value::Nil,
        }
    }
}

impl Div for Value {
    type Output = Value;

    fn div(self, rhs: Self) -> Self::Output {
        match self {
            Value::Float(lhs_float) => {
                match rhs {
                    Value::Float(rhs_float) => Value::Float(lhs_float / rhs_float),
                    Value::Integer(rhs_integer) => Value::Float(lhs_float / (rhs_integer as Float)),
                    _ => Value::Nil,
                }
            }

            Value::Integer(lhs_integer) => {
                match rhs {
                    Value::Float(rhs_float) => Value::Float((lhs_integer as Float) / rhs_float),
                    Value::Integer(rhs_integer) => Value::Integer(lhs_integer / rhs_integer),
                    _ => Value::Nil,
                }
            }
            _ => Value::Nil,
        }
    }
}

impl Rem for Value {
    type Output = Value;

    fn rem(self, rhs: Self) -> Self::Output {
        match self {
            Value::Float(lhs_float) => {
                match rhs {
                    Value::Float(rhs_float) => Value::Float(lhs_float % rhs_float),
                    Value::Integer(rhs_integer) => Value::Float(lhs_float % (rhs_integer as Float)),
                    _ => Value::Nil,
                }
            }

            Value::Integer(lhs_integer) => {
                match rhs {
                    Value::Float(rhs_float) => Value::Float((lhs_integer as Float) % rhs_float),
                    Value::Integer(rhs_integer) => Value::Integer(lhs_integer % rhs_integer),
                    _ => Value::Nil,
                }
            }
            _ => Value::Nil,
        }
    }
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum Target {
    Register(usize),
    Stack(usize),
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum Instruction {
    Add(Target, Target),
    Sub(Target, Target),
    Div(Target, Target),
    Mul(Target, Target),
    Mod(Target, Target),

    Cmp(Target, Target),
    Jmp(Address),
    JmpLt(Address),
    JmpGt(Address),
    JmpEq(Address),

    Push(Target, Value),
    Mov(Target, Target),
    Swp(Target, Target),

    Call(Address),
    Ret,

    Halt,
}

#[derive(Serialize, Deserialize, Debug, Clone, Default)]
pub struct Program {
    pub preamble: String,
    pub version: String,
    pub instructions: Vec<Instruction>,
}

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

    #[test]
    fn comparison() {
        assert!(Value::Float(1.3) < Value::Float(1.4));
        assert!(Value::Float(3.1) > Value::Float(2.4));
        assert!(Value::Float(9.6) == Value::Float(9.6));

        assert!(Value::Integer(124) < Value::Integer(234));
        assert!(Value::Integer(4) > Value::Integer(1));
        assert!(Value::Integer(839) == Value::Integer(839));
    }
}
<|endoftext|>


<commit_before>// "Tifflin" Kernel
// - By John Hodge (thePowersGang)
//
// Core/threads/sleep_object.rs
//! Sleep object
use prelude::*;
use core::ops;
use super::thread::{Thread, RunState};
use super::s_runnable_threads;

/// An object on which a thread can sleep, woken by various event sources
///
/// This object should not be moved while references are active
pub struct SleepObject
{
	name: &'static str,
	inner: ::sync::Spinlock< SleepObjectInner >,
}
impl_fmt! {
	Debug(self,f) for SleepObject {{
		let lh = self.inner.lock();
		write!(f, "SleepObject(\"{}\" {} refs, flag={})", self.name, lh.reference_count, lh.flag)
	}}
}
#[derive(Default)]
struct SleepObjectInner
{
	flag: bool,
	reference_count: usize,
	thread: Option<Box<Thread>>,
}

/// Referece to an active sleep object
pub struct SleepObjectRef
{
	obj: *const SleepObject,
}
unsafe impl ::core::marker::Send for SleepObjectRef {}

impl SleepObject
{
	/// Create a new sleep object
	pub fn new(name: &'static str) -> SleepObject
	{
		SleepObject {
			name: name,
			inner: Default::default(),
		}
	}

	/// Wait the current thread on this object
	pub fn wait(&self)
	{
		log_trace!("SleepObject::wait {:p} '{}'", self, self.name);

		let irql = ::sync::hold_interrupts();
		let mut lh = self.inner.lock();
		assert!( lh.thread.is_none(), "A thread is already sleeping on object {:p} '{}'", self, self.name );

		if lh.flag == false
		{
			let mut cur = super::get_cur_thread();
			cur.run_state = RunState::Sleep(self as *const _);
			lh.thread = Some(cur);

			::core::mem::drop(lh);
			::core::mem::drop(irql);

			super::reschedule();

			let cur = super::get_cur_thread();
			assert!( !is!(cur.run_state, RunState::Sleep(_)) );
			assert!( is!(cur.run_state, RunState::Runnable) );
			super::rel_cur_thread(cur);
		}
		else
		{
			lh.flag = false;
		}
	}

	/// Signal this sleep object (waking threads)
	#[tag_safe(irq)]
	#[allow(not_tagged_safe)]	// Holds an IRQ lock
	pub fn signal(&self)
	{
		log_trace!("SleepObject::signal {:p} '{}'", self, self.name);

		let _irq_lock = ::sync::hold_interrupts();
		let mut lh = self.inner.lock();
		// 1. Check for a waiter
		if let Some(mut t) = lh.thread.take()
		{
			t.set_state( RunState::Runnable );
			s_runnable_threads.lock().push(t);
		}
		else
		{
			lh.flag = true;
		}
	}

	/// Obtain a reference to the sleep object
	///
	/// NOTE: After this is called, self must not move
	pub fn get_ref(&self) -> SleepObjectRef {
		self.inner.lock().reference_count += 1;
		SleepObjectRef {
			obj: self as *const _,
		}
	}
}

impl ops::Drop for SleepObject
{
	fn drop(&mut self)
	{
		let lh = self.inner.lock();
		assert!(lh.reference_count == 0, "Sleep object being dropped while references are active");
	}
}

impl SleepObjectRef
{
	/// Checks if this reference points to the passed object
	pub fn is_from(&self, obj: &SleepObject) -> bool {
		self.obj == obj as *const _
	}
}
impl ops::Deref for SleepObjectRef
{
	type Target = SleepObject;

	fn deref(&self) -> &SleepObject {
		// SAFE: Reference counting ensures that this pointer is valid.
		unsafe { &*self.obj }   // > ASSUMPTION: The SleepObject doesn't move after it's borrowed
	}
}

impl ops::Drop for SleepObjectRef
{
	fn drop(&mut self)
	{
		// SAFE: Should still be valid
		let mut lh = unsafe { (*self.obj).inner.lock() };
		assert!(lh.reference_count > 0, "Sleep object's reference count is zero when dropping a reference");
		lh.reference_count -= 1;
	}
}

<commit_msg>Kernel - Silence logging from SleepObject<commit_after>// "Tifflin" Kernel
// - By John Hodge (thePowersGang)
//
// Core/threads/sleep_object.rs
//! Sleep object
use prelude::*;
use core::ops;
use super::thread::{Thread, RunState};
use super::s_runnable_threads;

/// An object on which a thread can sleep, woken by various event sources
///
/// This object should not be moved while references are active
pub struct SleepObject
{
	name: &'static str,
	inner: ::sync::Spinlock< SleepObjectInner >,
}
impl_fmt! {
	Debug(self,f) for SleepObject {{
		let lh = self.inner.lock();
		write!(f, "SleepObject(\"{}\" {} refs, flag={})", self.name, lh.reference_count, lh.flag)
	}}
}
#[derive(Default)]
struct SleepObjectInner
{
	flag: bool,
	reference_count: usize,
	thread: Option<Box<Thread>>,
}

/// Referece to an active sleep object
pub struct SleepObjectRef
{
	obj: *const SleepObject,
}
unsafe impl ::core::marker::Send for SleepObjectRef {}

impl SleepObject
{
	/// Create a new sleep object
	pub fn new(name: &'static str) -> SleepObject
	{
		SleepObject {
			name: name,
			inner: Default::default(),
		}
	}

	/// Wait the current thread on this object
	pub fn wait(&self)
	{
		//log_trace!("SleepObject::wait {:p} '{}'", self, self.name);

		let irql = ::sync::hold_interrupts();
		let mut lh = self.inner.lock();
		assert!( lh.thread.is_none(), "A thread is already sleeping on object {:p} '{}'", self, self.name );

		if lh.flag == false
		{
			let mut cur = super::get_cur_thread();
			cur.run_state = RunState::Sleep(self as *const _);
			lh.thread = Some(cur);

			::core::mem::drop(lh);
			::core::mem::drop(irql);

			super::reschedule();

			let cur = super::get_cur_thread();
			assert!( !is!(cur.run_state, RunState::Sleep(_)) );
			assert!( is!(cur.run_state, RunState::Runnable) );
			super::rel_cur_thread(cur);
		}
		else
		{
			lh.flag = false;
		}
	}

	/// Signal this sleep object (waking threads)
	#[tag_safe(irq)]
	#[allow(not_tagged_safe)]	// Holds an IRQ lock
	pub fn signal(&self)
	{
		//log_trace!("SleepObject::signal {:p} '{}'", self, self.name);

		let _irq_lock = ::sync::hold_interrupts();
		let mut lh = self.inner.lock();
		// 1. Check for a waiter
		if let Some(mut t) = lh.thread.take()
		{
			t.set_state( RunState::Runnable );
			s_runnable_threads.lock().push(t);
		}
		else
		{
			lh.flag = true;
		}
	}

	/// Obtain a reference to the sleep object
	///
	/// NOTE: After this is called, self must not move
	pub fn get_ref(&self) -> SleepObjectRef {
		self.inner.lock().reference_count += 1;
		SleepObjectRef {
			obj: self as *const _,
		}
	}
}

impl ops::Drop for SleepObject
{
	fn drop(&mut self)
	{
		let lh = self.inner.lock();
		assert!(lh.reference_count == 0, "Sleep object being dropped while references are active");
	}
}

impl SleepObjectRef
{
	/// Checks if this reference points to the passed object
	pub fn is_from(&self, obj: &SleepObject) -> bool {
		self.obj == obj as *const _
	}
}
impl ops::Deref for SleepObjectRef
{
	type Target = SleepObject;

	fn deref(&self) -> &SleepObject {
		// SAFE: Reference counting ensures that this pointer is valid.
		unsafe { &*self.obj }   // > ASSUMPTION: The SleepObject doesn't move after it's borrowed
	}
}

impl ops::Drop for SleepObjectRef
{
	fn drop(&mut self)
	{
		// SAFE: Should still be valid
		let mut lh = unsafe { (*self.obj).inner.lock() };
		assert!(lh.reference_count > 0, "Sleep object's reference count is zero when dropping a reference");
		lh.reference_count -= 1;
	}
}

<|endoftext|>


<commit_before>//! FIXME: write short doc here

use rustc_hash::{FxHashMap, FxHashSet};

use hir::{InFile, Name};
use ra_db::SourceDatabase;
use ra_prof::profile;
use ra_syntax::{ast, AstNode, Direction, SyntaxElement, SyntaxKind, SyntaxKind::*, TextRange, T};

use crate::{
    db::RootDatabase,
    references::{
        classify_name, classify_name_ref,
        NameKind::{self, *},
    },
    FileId,
};

#[derive(Debug)]
pub struct HighlightedRange {
    pub range: TextRange,
    pub tag: &'static str,
    pub binding_hash: Option<u64>,
}

fn is_control_keyword(kind: SyntaxKind) -> bool {
    match kind {
        T![for]
        | T![loop]
        | T![while]
        | T![continue]
        | T![break]
        | T![if]
        | T![else]
        | T![match]
        | T![return] => true,
        _ => false,
    }
}

pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec<HighlightedRange> {
    let _p = profile("highlight");
    let parse = db.parse(file_id);
    let root = parse.tree().syntax().clone();

    fn calc_binding_hash(file_id: FileId, name: &Name, shadow_count: u32) -> u64 {
        fn hash<T: std::hash::Hash + std::fmt::Debug>(x: T) -> u64 {
            use std::{collections::hash_map::DefaultHasher, hash::Hasher};

            let mut hasher = DefaultHasher::new();
            x.hash(&mut hasher);
            hasher.finish()
        }

        hash((file_id, name, shadow_count))
    }

    // Visited nodes to handle highlighting priorities
    // FIXME: retain only ranges here
    let mut highlighted: FxHashSet<SyntaxElement> = FxHashSet::default();
    let mut bindings_shadow_count: FxHashMap<Name, u32> = FxHashMap::default();

    let mut res = Vec::new();
    for node in root.descendants_with_tokens() {
        if highlighted.contains(&node) {
            continue;
        }
        let mut binding_hash = None;
        let tag = match node.kind() {
            FN_DEF => {
                bindings_shadow_count.clear();
                continue;
            }
            COMMENT => "comment",
            STRING | RAW_STRING | RAW_BYTE_STRING | BYTE_STRING => "string",
            ATTR => "attribute",
            NAME_REF => {
                if node.ancestors().any(|it| it.kind() == ATTR) {
                    continue;
                }

                let name_ref = node.as_node().cloned().and_then(ast::NameRef::cast).unwrap();
                let name_kind =
                    classify_name_ref(db, InFile::new(file_id.into(), &name_ref)).map(|d| d.kind);

                if let Some(Local(local)) = &name_kind {
                    if let Some(name) = local.name(db) {
                        let shadow_count = bindings_shadow_count.entry(name.clone()).or_default();
                        binding_hash = Some(calc_binding_hash(file_id, &name, *shadow_count))
                    }
                };

                name_kind.map_or("text", |it| highlight_name(db, it))
            }
            NAME => {
                let name = node.as_node().cloned().and_then(ast::Name::cast).unwrap();
                let name_kind =
                    classify_name(db, InFile::new(file_id.into(), &name)).map(|d| d.kind);

                if let Some(Local(local)) = &name_kind {
                    if let Some(name) = local.name(db) {
                        let shadow_count = bindings_shadow_count.entry(name.clone()).or_default();
                        *shadow_count += 1;
                        binding_hash = Some(calc_binding_hash(file_id, &name, *shadow_count))
                    }
                };

                match name_kind {
                    Some(name_kind) => highlight_name(db, name_kind),
                    None => name.syntax().parent().map_or("function", |x| match x.kind() {
                        TYPE_PARAM | STRUCT_DEF | ENUM_DEF | TRAIT_DEF | TYPE_ALIAS_DEF => "type",
                        RECORD_FIELD_DEF => "field",
                        _ => "function",
                    }),
                }
            }
            INT_NUMBER | FLOAT_NUMBER | CHAR | BYTE => "literal",
            LIFETIME => "parameter",
            T![unsafe] => "keyword.unsafe",
            k if is_control_keyword(k) => "keyword.control",
            k if k.is_keyword() => "keyword",
            _ => {
                if let Some(macro_call) = node.as_node().cloned().and_then(ast::MacroCall::cast) {
                    if let Some(path) = macro_call.path() {
                        if let Some(segment) = path.segment() {
                            if let Some(name_ref) = segment.name_ref() {
                                highlighted.insert(name_ref.syntax().clone().into());
                                let range_start = name_ref.syntax().text_range().start();
                                let mut range_end = name_ref.syntax().text_range().end();
                                for sibling in path.syntax().siblings_with_tokens(Direction::Next) {
                                    match sibling.kind() {
                                        T![!] | IDENT => range_end = sibling.text_range().end(),
                                        _ => (),
                                    }
                                }
                                res.push(HighlightedRange {
                                    range: TextRange::from_to(range_start, range_end),
                                    tag: "macro",
                                    binding_hash: None,
                                })
                            }
                        }
                    }
                }
                continue;
            }
        };
        res.push(HighlightedRange { range: node.text_range(), tag, binding_hash })
    }
    res
}

pub(crate) fn highlight_as_html(db: &RootDatabase, file_id: FileId, rainbow: bool) -> String {
    let parse = db.parse(file_id);

    fn rainbowify(seed: u64) -> String {
        use rand::prelude::*;
        let mut rng = SmallRng::seed_from_u64(seed);
        format!(
            "hsl({h},{s}%,{l}%)",
            h = rng.gen_range::<u16, _, _>(0, 361),
            s = rng.gen_range::<u16, _, _>(42, 99),
            l = rng.gen_range::<u16, _, _>(40, 91),
        )
    }

    let mut ranges = highlight(db, file_id);
    ranges.sort_by_key(|it| it.range.start());
    // quick non-optimal heuristic to intersect token ranges and highlighted ranges
    let mut frontier = 0;
    let mut could_intersect: Vec<&HighlightedRange> = Vec::new();

    let mut buf = String::new();
    buf.push_str(&STYLE);
    buf.push_str("
");
    let tokens = parse.tree().syntax().descendants_with_tokens().filter_map(|it| it.into_token());
    for token in tokens {
        could_intersect.retain(|it| token.text_range().start() <= it.range.end());
        while let Some(r) = ranges.get(frontier) {
            if r.range.start() <= token.text_range().end() {
                could_intersect.push(r);
                frontier += 1;
            } else {
                break;
            }
        }
        let text = html_escape(&token.text());
        let ranges = could_intersect
            .iter()
            .filter(|it| token.text_range().is_subrange(&it.range))
            .collect::<Vec<_>>();
        if ranges.is_empty() {
            buf.push_str(&text);
        } else {
            let classes = ranges.iter().map(|x| x.tag).collect::<Vec<_>>().join(" ");
            let binding_hash = ranges.first().and_then(|x| x.binding_hash);
            let color = match (rainbow, binding_hash) {
                (true, Some(hash)) => format!(
                    " data-binding-hash=\"{}\" style=\"color: {};\"",
                    hash,
                    rainbowify(hash)
                ),
                _ => "".into(),
            };
            buf.push_str(&format!("{}", classes, color, text));
        }
    }
    buf.push_str("
");
    buf
}

fn highlight_name(db: &RootDatabase, name_kind: NameKind) -> &'static str {
    match name_kind {
        Macro(_) => "macro",
        Field(_) => "field",
        AssocItem(hir::AssocItem::Function(_)) => "function",
        AssocItem(hir::AssocItem::Const(_)) => "constant",
        AssocItem(hir::AssocItem::TypeAlias(_)) => "type",
        Def(hir::ModuleDef::Module(_)) => "module",
        Def(hir::ModuleDef::Function(_)) => "function",
        Def(hir::ModuleDef::Adt(_)) => "type",
        Def(hir::ModuleDef::EnumVariant(_)) => "constant",
        Def(hir::ModuleDef::Const(_)) => "constant",
        Def(hir::ModuleDef::Static(_)) => "constant",
        Def(hir::ModuleDef::Trait(_)) => "type",
        Def(hir::ModuleDef::TypeAlias(_)) => "type",
        Def(hir::ModuleDef::BuiltinType(_)) => "type",
        SelfType(_) | TypeParam(_) => "type",
        Local(local) => {
            if local.is_mut(db) {
                "variable.mut"
            } else if local.ty(db).is_mutable_reference() {
                "variable.mut"
            } else {
                "variable"
            }
        }
    }
}

//FIXME: like, real html escaping
fn html_escape(text: &str) -> String {
    text.replace("<", "<").replace(">", ">")
}

const STYLE: &str = "

";

#[cfg(test)]
mod tests {
    use crate::mock_analysis::single_file;
    use test_utils::{assert_eq_text, project_dir, read_text};

    #[test]
    fn test_highlighting() {
        let (analysis, file_id) = single_file(
            r#"
#[derive(Clone, Debug)]
struct Foo {
    pub x: i32,
    pub y: i32,
}

fn foo<T>() -> T {
    unimplemented!();
    foo::<i32>();
}

// comment
fn main() {
    println!("Hello, {}!", 92);

    let mut vec = Vec::new();
    if true {
        vec.push(Foo { x: 0, y: 1 });
    }
    unsafe { vec.set_len(0); }

    let mut x = 42;
    let y = &mut x;
    let z = &y;

    y;
}

enum E<X> {
    V(X)
}

impl<X> E<X> {
    fn new<T>() -> E<T> {}
}
"#
            .trim(),
        );
        let dst_file = project_dir().join("crates/ra_ide/src/snapshots/highlighting.html");
        let actual_html = &analysis.highlight_as_html(file_id, false).unwrap();
        let expected_html = &read_text(&dst_file);
        std::fs::write(dst_file, &actual_html).unwrap();
        assert_eq_text!(expected_html, actual_html);
    }

    #[test]
    fn test_rainbow_highlighting() {
        let (analysis, file_id) = single_file(
            r#"
fn main() {
    let hello = "hello";
    let x = hello.to_string();
    let y = hello.to_string();

    let x = "other color please!";
    let y = x.to_string();
}

fn bar() {
    let mut hello = "hello";
}
"#
            .trim(),
        );
        let dst_file = project_dir().join("crates/ra_ide/src/snapshots/rainbow_highlighting.html");
        let actual_html = &analysis.highlight_as_html(file_id, true).unwrap();
        let expected_html = &read_text(&dst_file);
        std::fs::write(dst_file, &actual_html).unwrap();
        assert_eq_text!(expected_html, actual_html);
    }
}
<commit_msg>scopes resolution is more granular<commit_after>//! FIXME: write short doc here

use rustc_hash::{FxHashMap, FxHashSet};

use hir::{InFile, Name};
use ra_db::SourceDatabase;
use ra_prof::profile;
use ra_syntax::{ast, AstNode, Direction, SyntaxElement, SyntaxKind, SyntaxKind::*, TextRange, T};

use crate::{
    db::RootDatabase,
    references::{
        classify_name, classify_name_ref,
        NameKind::{self, *},
    },
    FileId,
};

#[derive(Debug)]
pub struct HighlightedRange {
    pub range: TextRange,
    pub tag: &'static str,
    pub binding_hash: Option<u64>,
}

fn is_control_keyword(kind: SyntaxKind) -> bool {
    match kind {
        T![for]
        | T![loop]
        | T![while]
        | T![continue]
        | T![break]
        | T![if]
        | T![else]
        | T![match]
        | T![return] => true,
        _ => false,
    }
}

pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec<HighlightedRange> {
    let _p = profile("highlight");
    let parse = db.parse(file_id);
    let root = parse.tree().syntax().clone();

    fn calc_binding_hash(file_id: FileId, name: &Name, shadow_count: u32) -> u64 {
        fn hash<T: std::hash::Hash + std::fmt::Debug>(x: T) -> u64 {
            use std::{collections::hash_map::DefaultHasher, hash::Hasher};

            let mut hasher = DefaultHasher::new();
            x.hash(&mut hasher);
            hasher.finish()
        }

        hash((file_id, name, shadow_count))
    }

    // Visited nodes to handle highlighting priorities
    // FIXME: retain only ranges here
    let mut highlighted: FxHashSet<SyntaxElement> = FxHashSet::default();
    let mut bindings_shadow_count: FxHashMap<Name, u32> = FxHashMap::default();

    let mut res = Vec::new();
    for node in root.descendants_with_tokens() {
        if highlighted.contains(&node) {
            continue;
        }
        let mut binding_hash = None;
        let tag = match node.kind() {
            FN_DEF => {
                bindings_shadow_count.clear();
                continue;
            }
            COMMENT => "comment",
            STRING | RAW_STRING | RAW_BYTE_STRING | BYTE_STRING => "string",
            ATTR => "attribute",
            NAME_REF => {
                if node.ancestors().any(|it| it.kind() == ATTR) {
                    continue;
                }

                let name_ref = node.as_node().cloned().and_then(ast::NameRef::cast).unwrap();
                let name_kind =
                    classify_name_ref(db, InFile::new(file_id.into(), &name_ref)).map(|d| d.kind);

                if let Some(Local(local)) = &name_kind {
                    if let Some(name) = local.name(db) {
                        let shadow_count = bindings_shadow_count.entry(name.clone()).or_default();
                        binding_hash = Some(calc_binding_hash(file_id, &name, *shadow_count))
                    }
                };

                name_kind.map_or("text", |it| highlight_name(db, it))
            }
            NAME => {
                let name = node.as_node().cloned().and_then(ast::Name::cast).unwrap();
                let name_kind =
                    classify_name(db, InFile::new(file_id.into(), &name)).map(|d| d.kind);

                if let Some(Local(local)) = &name_kind {
                    if let Some(name) = local.name(db) {
                        let shadow_count = bindings_shadow_count.entry(name.clone()).or_default();
                        *shadow_count += 1;
                        binding_hash = Some(calc_binding_hash(file_id, &name, *shadow_count))
                    }
                };

                match name_kind {
                    Some(name_kind) => highlight_name(db, name_kind),
                    None => name.syntax().parent().map_or("function", |x| match x.kind() {
                        STRUCT_DEF | ENUM_DEF | TRAIT_DEF | TYPE_ALIAS_DEF => "type",
                        TYPE_PARAM => "type.param",
                        RECORD_FIELD_DEF => "field",
                        _ => "function",
                    }),
                }
            }
            INT_NUMBER | FLOAT_NUMBER => "literal.numeric",
            BYTE => "literal.byte",
            CHAR => "literal.char",
            LIFETIME => "lifetime",
            T![unsafe] => "keyword.unsafe",
            k if is_control_keyword(k) => "keyword.control",
            k if k.is_keyword() => "keyword",
            _ => {
                if let Some(macro_call) = node.as_node().cloned().and_then(ast::MacroCall::cast) {
                    if let Some(path) = macro_call.path() {
                        if let Some(segment) = path.segment() {
                            if let Some(name_ref) = segment.name_ref() {
                                highlighted.insert(name_ref.syntax().clone().into());
                                let range_start = name_ref.syntax().text_range().start();
                                let mut range_end = name_ref.syntax().text_range().end();
                                for sibling in path.syntax().siblings_with_tokens(Direction::Next) {
                                    match sibling.kind() {
                                        T![!] | IDENT => range_end = sibling.text_range().end(),
                                        _ => (),
                                    }
                                }
                                res.push(HighlightedRange {
                                    range: TextRange::from_to(range_start, range_end),
                                    tag: "macro",
                                    binding_hash: None,
                                })
                            }
                        }
                    }
                }
                continue;
            }
        };
        res.push(HighlightedRange { range: node.text_range(), tag, binding_hash })
    }
    res
}

pub(crate) fn highlight_as_html(db: &RootDatabase, file_id: FileId, rainbow: bool) -> String {
    let parse = db.parse(file_id);

    fn rainbowify(seed: u64) -> String {
        use rand::prelude::*;
        let mut rng = SmallRng::seed_from_u64(seed);
        format!(
            "hsl({h},{s}%,{l}%)",
            h = rng.gen_range::<u16, _, _>(0, 361),
            s = rng.gen_range::<u16, _, _>(42, 99),
            l = rng.gen_range::<u16, _, _>(40, 91),
        )
    }

    let mut ranges = highlight(db, file_id);
    ranges.sort_by_key(|it| it.range.start());
    // quick non-optimal heuristic to intersect token ranges and highlighted ranges
    let mut frontier = 0;
    let mut could_intersect: Vec<&HighlightedRange> = Vec::new();

    let mut buf = String::new();
    buf.push_str(&STYLE);
    buf.push_str("
");
    let tokens = parse.tree().syntax().descendants_with_tokens().filter_map(|it| it.into_token());
    for token in tokens {
        could_intersect.retain(|it| token.text_range().start() <= it.range.end());
        while let Some(r) = ranges.get(frontier) {
            if r.range.start() <= token.text_range().end() {
                could_intersect.push(r);
                frontier += 1;
            } else {
                break;
            }
        }
        let text = html_escape(&token.text());
        let ranges = could_intersect
            .iter()
            .filter(|it| token.text_range().is_subrange(&it.range))
            .collect::<Vec<_>>();
        if ranges.is_empty() {
            buf.push_str(&text);
        } else {
            let classes = ranges.iter().map(|x| x.tag).collect::<Vec<_>>().join(" ");
            let binding_hash = ranges.first().and_then(|x| x.binding_hash);
            let color = match (rainbow, binding_hash) {
                (true, Some(hash)) => format!(
                    " data-binding-hash=\"{}\" style=\"color: {};\"",
                    hash,
                    rainbowify(hash)
                ),
                _ => "".into(),
            };
            buf.push_str(&format!("{}", classes, color, text));
        }
    }
    buf.push_str("
");
    buf
}

fn highlight_name(db: &RootDatabase, name_kind: NameKind) -> &'static str {
    match name_kind {
        Macro(_) => "macro",
        Field(_) => "field",
        AssocItem(hir::AssocItem::Function(_)) => "function",
        AssocItem(hir::AssocItem::Const(_)) => "constant",
        AssocItem(hir::AssocItem::TypeAlias(_)) => "type.alias",
        Def(hir::ModuleDef::Module(_)) => "module",
        Def(hir::ModuleDef::Function(_)) => "function",
        Def(hir::ModuleDef::Adt(_)) => "type",
        Def(hir::ModuleDef::EnumVariant(_)) => "constant",
        Def(hir::ModuleDef::Const(_)) => "constant",
        Def(hir::ModuleDef::Static(_)) => "constant",
        Def(hir::ModuleDef::Trait(_)) => "type.trait",
        Def(hir::ModuleDef::TypeAlias(_)) => "type.alias",
        Def(hir::ModuleDef::BuiltinType(_)) => "type.builtin",
        SelfType(_) => "type.self",
        TypeParam(_) => "type.param",
        Local(local) => {
            if local.is_mut(db) {
                "variable.mut"
            } else if local.ty(db).is_mutable_reference() {
                "variable.mut"
            } else {
                "variable"
            }
        }
    }
}

//FIXME: like, real html escaping
fn html_escape(text: &str) -> String {
    text.replace("<", "<").replace(">", ">")
}

const STYLE: &str = "

";

#[cfg(test)]
mod tests {
    use crate::mock_analysis::single_file;
    use test_utils::{assert_eq_text, project_dir, read_text};

    #[test]
    fn test_highlighting() {
        let (analysis, file_id) = single_file(
            r#"
#[derive(Clone, Debug)]
struct Foo {
    pub x: i32,
    pub y: i32,
}

fn foo<T>() -> T {
    unimplemented!();
    foo::<i32>();
}

// comment
fn main() {
    println!("Hello, {}!", 92);

    let mut vec = Vec::new();
    if true {
        vec.push(Foo { x: 0, y: 1 });
    }
    unsafe { vec.set_len(0); }

    let mut x = 42;
    let y = &mut x;
    let z = &y;

    y;
}

enum E<X> {
    V(X)
}

impl<X> E<X> {
    fn new<T>() -> E<T> {}
}
"#
            .trim(),
        );
        let dst_file = project_dir().join("crates/ra_ide/src/snapshots/highlighting.html");
        let actual_html = &analysis.highlight_as_html(file_id, false).unwrap();
        let expected_html = &read_text(&dst_file);
        std::fs::write(dst_file, &actual_html).unwrap();
        assert_eq_text!(expected_html, actual_html);
    }

    #[test]
    fn test_rainbow_highlighting() {
        let (analysis, file_id) = single_file(
            r#"
fn main() {
    let hello = "hello";
    let x = hello.to_string();
    let y = hello.to_string();

    let x = "other color please!";
    let y = x.to_string();
}

fn bar() {
    let mut hello = "hello";
}
"#
            .trim(),
        );
        let dst_file = project_dir().join("crates/ra_ide/src/snapshots/rainbow_highlighting.html");
        let actual_html = &analysis.highlight_as_html(file_id, true).unwrap();
        let expected_html = &read_text(&dst_file);
        std::fs::write(dst_file, &actual_html).unwrap();
        assert_eq_text!(expected_html, actual_html);
    }
}
<|endoftext|>


<commit_before>//! The context or environment in which the language server functions. In our
//! server implementation this is know as the `WorldState`.
//!
//! Each tick provides an immutable snapshot of the state as `WorldSnapshot`.

use std::{sync::Arc, time::Instant};

use crossbeam_channel::{unbounded, Receiver, Sender};
use flycheck::FlycheckHandle;
use ide::{Analysis, AnalysisHost, Cancellable, Change, FileId};
use ide_db::base_db::{CrateId, FileLoader, SourceDatabase};
use lsp_types::{SemanticTokens, Url};
use parking_lot::{Mutex, RwLock};
use proc_macro_api::ProcMacroServer;
use project_model::{CargoWorkspace, ProjectWorkspace, Target, WorkspaceBuildScripts};
use rustc_hash::FxHashMap;
use vfs::AnchoredPathBuf;

use crate::{
    config::Config,
    diagnostics::{CheckFixes, DiagnosticCollection},
    from_proto,
    line_index::{LineEndings, LineIndex},
    lsp_ext,
    main_loop::Task,
    mem_docs::MemDocs,
    op_queue::OpQueue,
    reload::{self, SourceRootConfig},
    thread_pool::TaskPool,
    to_proto::url_from_abs_path,
    Result,
};

// Enforces drop order
pub(crate) struct Handle<H, C> {
    pub(crate) handle: H,
    pub(crate) receiver: C,
}

pub(crate) type ReqHandler = fn(&mut GlobalState, lsp_server::Response);
pub(crate) type ReqQueue = lsp_server::ReqQueue<(String, Instant), ReqHandler>;

/// `GlobalState` is the primary mutable state of the language server
///
/// The most interesting components are `vfs`, which stores a consistent
/// snapshot of the file systems, and `analysis_host`, which stores our
/// incremental salsa database.
///
/// Note that this struct has more than one impl in various modules!
pub(crate) struct GlobalState {
    sender: Sender<lsp_server::Message>,
    req_queue: ReqQueue,
    pub(crate) task_pool: Handle<TaskPool<Task>, Receiver<Task>>,
    pub(crate) loader: Handle<Box<dyn vfs::loader::Handle>, Receiver<vfs::loader::Message>>,
    pub(crate) config: Arc<Config>,
    pub(crate) analysis_host: AnalysisHost,
    pub(crate) diagnostics: DiagnosticCollection,
    pub(crate) mem_docs: MemDocs,
    pub(crate) semantic_tokens_cache: Arc<Mutex<FxHashMap<Url, SemanticTokens>>>,
    pub(crate) shutdown_requested: bool,
    pub(crate) proc_macro_changed: bool,
    pub(crate) last_reported_status: Option<lsp_ext::ServerStatusParams>,
    pub(crate) source_root_config: SourceRootConfig,
    pub(crate) proc_macro_client: Option<ProcMacroServer>,

    pub(crate) flycheck: Vec<FlycheckHandle>,
    pub(crate) flycheck_sender: Sender<flycheck::Message>,
    pub(crate) flycheck_receiver: Receiver<flycheck::Message>,

    pub(crate) vfs: Arc<RwLock<(vfs::Vfs, FxHashMap<FileId, LineEndings>)>>,
    pub(crate) vfs_config_version: u32,
    pub(crate) vfs_progress_config_version: u32,
    pub(crate) vfs_progress_n_total: usize,
    pub(crate) vfs_progress_n_done: usize,

    /// `workspaces` field stores the data we actually use, while the `OpQueue`
    /// stores the result of the last fetch.
    ///
    /// If the fetch (partially) fails, we do not update the current value.
    ///
    /// The handling of build data is subtle. We fetch workspace in two phases:
    ///
    /// *First*, we run `cargo metadata`, which gives us fast results for
    /// initial analysis.
    ///
    /// *Second*, we run `cargo check` which runs build scripts and compiles
    /// proc macros.
    ///
    /// We need both for the precise analysis, but we want rust-analyzer to be
    /// at least partially available just after the first phase. That's because
    /// first phase is much faster, and is much less likely to fail.
    ///
    /// This creates a complication -- by the time the second phase completes,
    /// the results of the fist phase could be invalid. That is, while we run
    /// `cargo check`, the user edits `Cargo.toml`, we notice this, and the new
    /// `cargo metadata` completes before `cargo check`.
    ///
    /// An additional complication is that we want to avoid needless work. When
    /// the user just adds comments or whitespace to Cargo.toml, we do not want
    /// to invalidate any salsa caches.
    pub(crate) workspaces: Arc<Vec<ProjectWorkspace>>,
    pub(crate) fetch_workspaces_queue: OpQueue<Vec<anyhow::Result<ProjectWorkspace>>>,
    pub(crate) fetch_build_data_queue:
        OpQueue<(Arc<Vec<ProjectWorkspace>>, Vec<anyhow::Result<WorkspaceBuildScripts>>)>,

    pub(crate) prime_caches_queue: OpQueue<()>,
}

/// An immutable snapshot of the world's state at a point in time.
pub(crate) struct GlobalStateSnapshot {
    pub(crate) config: Arc<Config>,
    pub(crate) analysis: Analysis,
    pub(crate) check_fixes: CheckFixes,
    mem_docs: MemDocs,
    pub(crate) semantic_tokens_cache: Arc<Mutex<FxHashMap<Url, SemanticTokens>>>,
    vfs: Arc<RwLock<(vfs::Vfs, FxHashMap<FileId, LineEndings>)>>,
    pub(crate) workspaces: Arc<Vec<ProjectWorkspace>>,
}

impl std::panic::UnwindSafe for GlobalStateSnapshot {}

impl GlobalState {
    pub(crate) fn new(sender: Sender<lsp_server::Message>, config: Config) -> GlobalState {
        let loader = {
            let (sender, receiver) = unbounded::<vfs::loader::Message>();
            let handle: vfs_notify::NotifyHandle =
                vfs::loader::Handle::spawn(Box::new(move |msg| sender.send(msg).unwrap()));
            let handle = Box::new(handle) as Box<dyn vfs::loader::Handle>;
            Handle { handle, receiver }
        };

        let task_pool = {
            let (sender, receiver) = unbounded();
            let handle = TaskPool::new(sender);
            Handle { handle, receiver }
        };

        let analysis_host = AnalysisHost::new(config.lru_capacity());
        let (flycheck_sender, flycheck_receiver) = unbounded();
        let mut this = GlobalState {
            sender,
            req_queue: ReqQueue::default(),
            task_pool,
            loader,
            config: Arc::new(config.clone()),
            analysis_host,
            diagnostics: Default::default(),
            mem_docs: MemDocs::default(),
            semantic_tokens_cache: Arc::new(Default::default()),
            shutdown_requested: false,
            proc_macro_changed: false,
            last_reported_status: None,
            source_root_config: SourceRootConfig::default(),
            proc_macro_client: None,

            flycheck: Vec::new(),
            flycheck_sender,
            flycheck_receiver,

            vfs: Arc::new(RwLock::new((vfs::Vfs::default(), FxHashMap::default()))),
            vfs_config_version: 0,
            vfs_progress_config_version: 0,
            vfs_progress_n_total: 0,
            vfs_progress_n_done: 0,

            workspaces: Arc::new(Vec::new()),
            fetch_workspaces_queue: OpQueue::default(),
            prime_caches_queue: OpQueue::default(),

            fetch_build_data_queue: OpQueue::default(),
        };
        // Apply any required database inputs from the config.
        this.update_configuration(config);
        this
    }

    pub(crate) fn process_changes(&mut self) -> bool {
        let _p = profile::span("GlobalState::process_changes");
        let mut fs_changes = Vec::new();
        // A file was added or deleted
        let mut has_structure_changes = false;

        let (change, changed_files) = {
            let mut change = Change::new();
            let (vfs, line_endings_map) = &mut *self.vfs.write();
            let changed_files = vfs.take_changes();
            if changed_files.is_empty() {
                return false;
            }

            for file in &changed_files {
                if let Some(path) = vfs.file_path(file.file_id).as_path() {
                    let path = path.to_path_buf();
                    if reload::should_refresh_for_change(&path, file.change_kind) {
                        self.fetch_workspaces_queue.request_op();
                    }
                    fs_changes.push((path, file.change_kind));
                    if file.is_created_or_deleted() {
                        has_structure_changes = true;
                    }
                }

                let text = if file.exists() {
                    let bytes = vfs.file_contents(file.file_id).to_vec();
                    String::from_utf8(bytes).ok().and_then(|text| {
                        let (text, line_endings) = LineEndings::normalize(text);
                        line_endings_map.insert(file.file_id, line_endings);
                        Some(Arc::new(text))
                    })
                } else {
                    None
                };
                change.change_file(file.file_id, text);
            }
            if has_structure_changes {
                let roots = self.source_root_config.partition(vfs);
                change.set_roots(roots);
            }
            (change, changed_files)
        };

        self.analysis_host.apply_change(change);

        let raw_database = &self.analysis_host.raw_database();
        self.proc_macro_changed =
            changed_files.iter().filter(|file| !file.is_created_or_deleted()).any(|file| {
                let crates = raw_database.relevant_crates(file.file_id);
                let crate_graph = raw_database.crate_graph();

                crates.iter().any(|&krate| !crate_graph[krate].is_proc_macro)
            });
        true
    }

    pub(crate) fn snapshot(&self) -> GlobalStateSnapshot {
        GlobalStateSnapshot {
            config: Arc::clone(&self.config),
            workspaces: Arc::clone(&self.workspaces),
            analysis: self.analysis_host.analysis(),
            vfs: Arc::clone(&self.vfs),
            check_fixes: Arc::clone(&self.diagnostics.check_fixes),
            mem_docs: self.mem_docs.clone(),
            semantic_tokens_cache: Arc::clone(&self.semantic_tokens_cache),
        }
    }

    pub(crate) fn send_request<R: lsp_types::request::Request>(
        &mut self,
        params: R::Params,
        handler: ReqHandler,
    ) {
        let request = self.req_queue.outgoing.register(R::METHOD.to_string(), params, handler);
        self.send(request.into());
    }

    pub(crate) fn complete_request(&mut self, response: lsp_server::Response) {
        let handler = self
            .req_queue
            .outgoing
            .complete(response.id.clone())
            .expect("received response for unknown request");
        handler(self, response)
    }

    pub(crate) fn send_notification<N: lsp_types::notification::Notification>(
        &mut self,
        params: N::Params,
    ) {
        let not = lsp_server::Notification::new(N::METHOD.to_string(), params);
        self.send(not.into());
    }

    pub(crate) fn register_request(
        &mut self,
        request: &lsp_server::Request,
        request_received: Instant,
    ) {
        self.req_queue
            .incoming
            .register(request.id.clone(), (request.method.clone(), request_received));
    }

    pub(crate) fn respond(&mut self, response: lsp_server::Response) {
        if let Some((method, start)) = self.req_queue.incoming.complete(response.id.clone()) {
            if let Some(err) = &response.error {
                if err.message.starts_with("server panicked") {
                    self.poke_rust_analyzer_developer(format!("{}, check the log", err.message))
                }
            }

            let duration = start.elapsed();
            tracing::info!("handled {} - ({}) in {:0.2?}", method, response.id, duration);
            self.send(response.into());
        }
    }

    pub(crate) fn cancel(&mut self, request_id: lsp_server::RequestId) {
        if let Some(response) = self.req_queue.incoming.cancel(request_id) {
            self.send(response.into());
        }
    }

    fn send(&mut self, message: lsp_server::Message) {
        self.sender.send(message).unwrap()
    }
}

impl Drop for GlobalState {
    fn drop(&mut self) {
        self.analysis_host.request_cancellation();
    }
}

impl GlobalStateSnapshot {
    pub(crate) fn url_to_file_id(&self, url: &Url) -> Result<FileId> {
        url_to_file_id(&self.vfs.read().0, url)
    }

    pub(crate) fn file_id_to_url(&self, id: FileId) -> Url {
        file_id_to_url(&self.vfs.read().0, id)
    }

    pub(crate) fn file_line_index(&self, file_id: FileId) -> Cancellable<LineIndex> {
        let endings = self.vfs.read().1[&file_id];
        let index = self.analysis.file_line_index(file_id)?;
        let res = LineIndex { index, endings, encoding: self.config.offset_encoding() };
        Ok(res)
    }

    pub(crate) fn url_file_version(&self, url: &Url) -> Option<i32> {
        let path = from_proto::vfs_path(url).ok()?;
        Some(self.mem_docs.get(&path)?.version)
    }

    pub(crate) fn anchored_path(&self, path: &AnchoredPathBuf) -> Url {
        let mut base = self.vfs.read().0.file_path(path.anchor);
        base.pop();
        let path = base.join(&path.path).unwrap();
        let path = path.as_path().unwrap();
        url_from_abs_path(path)
    }

    pub(crate) fn cargo_target_for_crate_root(
        &self,
        crate_id: CrateId,
    ) -> Option<(&CargoWorkspace, Target)> {
        let file_id = self.analysis.crate_root(crate_id).ok()?;
        let path = self.vfs.read().0.file_path(file_id);
        let path = path.as_path()?;
        self.workspaces.iter().find_map(|ws| match ws {
            ProjectWorkspace::Cargo { cargo, .. } => {
                cargo.target_by_root(path).map(|it| (cargo, it))
            }
            ProjectWorkspace::Json { .. } => None,
            ProjectWorkspace::DetachedFiles { .. } => None,
        })
    }
}

pub(crate) fn file_id_to_url(vfs: &vfs::Vfs, id: FileId) -> Url {
    let path = vfs.file_path(id);
    let path = path.as_path().unwrap();
    url_from_abs_path(path)
}

pub(crate) fn url_to_file_id(vfs: &vfs::Vfs, url: &Url) -> Result<FileId> {
    let path = from_proto::vfs_path(url)?;
    let res = vfs.file_id(&path).ok_or_else(|| format!("file not found: {}", path))?;
    Ok(res)
}
<commit_msg>Auto merge of #12008 - Veykril:patch-1, r=Veykril<commit_after>//! The context or environment in which the language server functions. In our
//! server implementation this is know as the `WorldState`.
//!
//! Each tick provides an immutable snapshot of the state as `WorldSnapshot`.

use std::{sync::Arc, time::Instant};

use crossbeam_channel::{unbounded, Receiver, Sender};
use flycheck::FlycheckHandle;
use ide::{Analysis, AnalysisHost, Cancellable, Change, FileId};
use ide_db::base_db::{CrateId, FileLoader, SourceDatabase};
use lsp_types::{SemanticTokens, Url};
use parking_lot::{Mutex, RwLock};
use proc_macro_api::ProcMacroServer;
use project_model::{CargoWorkspace, ProjectWorkspace, Target, WorkspaceBuildScripts};
use rustc_hash::FxHashMap;
use vfs::AnchoredPathBuf;

use crate::{
    config::Config,
    diagnostics::{CheckFixes, DiagnosticCollection},
    from_proto,
    line_index::{LineEndings, LineIndex},
    lsp_ext,
    main_loop::Task,
    mem_docs::MemDocs,
    op_queue::OpQueue,
    reload::{self, SourceRootConfig},
    thread_pool::TaskPool,
    to_proto::url_from_abs_path,
    Result,
};

// Enforces drop order
pub(crate) struct Handle<H, C> {
    pub(crate) handle: H,
    pub(crate) receiver: C,
}

pub(crate) type ReqHandler = fn(&mut GlobalState, lsp_server::Response);
pub(crate) type ReqQueue = lsp_server::ReqQueue<(String, Instant), ReqHandler>;

/// `GlobalState` is the primary mutable state of the language server
///
/// The most interesting components are `vfs`, which stores a consistent
/// snapshot of the file systems, and `analysis_host`, which stores our
/// incremental salsa database.
///
/// Note that this struct has more than one impl in various modules!
pub(crate) struct GlobalState {
    sender: Sender<lsp_server::Message>,
    req_queue: ReqQueue,
    pub(crate) task_pool: Handle<TaskPool<Task>, Receiver<Task>>,
    pub(crate) loader: Handle<Box<dyn vfs::loader::Handle>, Receiver<vfs::loader::Message>>,
    pub(crate) config: Arc<Config>,
    pub(crate) analysis_host: AnalysisHost,
    pub(crate) diagnostics: DiagnosticCollection,
    pub(crate) mem_docs: MemDocs,
    pub(crate) semantic_tokens_cache: Arc<Mutex<FxHashMap<Url, SemanticTokens>>>,
    pub(crate) shutdown_requested: bool,
    pub(crate) proc_macro_changed: bool,
    pub(crate) last_reported_status: Option<lsp_ext::ServerStatusParams>,
    pub(crate) source_root_config: SourceRootConfig,
    pub(crate) proc_macro_client: Option<ProcMacroServer>,

    pub(crate) flycheck: Vec<FlycheckHandle>,
    pub(crate) flycheck_sender: Sender<flycheck::Message>,
    pub(crate) flycheck_receiver: Receiver<flycheck::Message>,

    pub(crate) vfs: Arc<RwLock<(vfs::Vfs, FxHashMap<FileId, LineEndings>)>>,
    pub(crate) vfs_config_version: u32,
    pub(crate) vfs_progress_config_version: u32,
    pub(crate) vfs_progress_n_total: usize,
    pub(crate) vfs_progress_n_done: usize,

    /// `workspaces` field stores the data we actually use, while the `OpQueue`
    /// stores the result of the last fetch.
    ///
    /// If the fetch (partially) fails, we do not update the current value.
    ///
    /// The handling of build data is subtle. We fetch workspace in two phases:
    ///
    /// *First*, we run `cargo metadata`, which gives us fast results for
    /// initial analysis.
    ///
    /// *Second*, we run `cargo check` which runs build scripts and compiles
    /// proc macros.
    ///
    /// We need both for the precise analysis, but we want rust-analyzer to be
    /// at least partially available just after the first phase. That's because
    /// first phase is much faster, and is much less likely to fail.
    ///
    /// This creates a complication -- by the time the second phase completes,
    /// the results of the fist phase could be invalid. That is, while we run
    /// `cargo check`, the user edits `Cargo.toml`, we notice this, and the new
    /// `cargo metadata` completes before `cargo check`.
    ///
    /// An additional complication is that we want to avoid needless work. When
    /// the user just adds comments or whitespace to Cargo.toml, we do not want
    /// to invalidate any salsa caches.
    pub(crate) workspaces: Arc<Vec<ProjectWorkspace>>,
    pub(crate) fetch_workspaces_queue: OpQueue<Vec<anyhow::Result<ProjectWorkspace>>>,
    pub(crate) fetch_build_data_queue:
        OpQueue<(Arc<Vec<ProjectWorkspace>>, Vec<anyhow::Result<WorkspaceBuildScripts>>)>,

    pub(crate) prime_caches_queue: OpQueue<()>,
}

/// An immutable snapshot of the world's state at a point in time.
pub(crate) struct GlobalStateSnapshot {
    pub(crate) config: Arc<Config>,
    pub(crate) analysis: Analysis,
    pub(crate) check_fixes: CheckFixes,
    mem_docs: MemDocs,
    pub(crate) semantic_tokens_cache: Arc<Mutex<FxHashMap<Url, SemanticTokens>>>,
    vfs: Arc<RwLock<(vfs::Vfs, FxHashMap<FileId, LineEndings>)>>,
    pub(crate) workspaces: Arc<Vec<ProjectWorkspace>>,
}

impl std::panic::UnwindSafe for GlobalStateSnapshot {}

impl GlobalState {
    pub(crate) fn new(sender: Sender<lsp_server::Message>, config: Config) -> GlobalState {
        let loader = {
            let (sender, receiver) = unbounded::<vfs::loader::Message>();
            let handle: vfs_notify::NotifyHandle =
                vfs::loader::Handle::spawn(Box::new(move |msg| sender.send(msg).unwrap()));
            let handle = Box::new(handle) as Box<dyn vfs::loader::Handle>;
            Handle { handle, receiver }
        };

        let task_pool = {
            let (sender, receiver) = unbounded();
            let handle = TaskPool::new(sender);
            Handle { handle, receiver }
        };

        let analysis_host = AnalysisHost::new(config.lru_capacity());
        let (flycheck_sender, flycheck_receiver) = unbounded();
        let mut this = GlobalState {
            sender,
            req_queue: ReqQueue::default(),
            task_pool,
            loader,
            config: Arc::new(config.clone()),
            analysis_host,
            diagnostics: Default::default(),
            mem_docs: MemDocs::default(),
            semantic_tokens_cache: Arc::new(Default::default()),
            shutdown_requested: false,
            proc_macro_changed: false,
            last_reported_status: None,
            source_root_config: SourceRootConfig::default(),
            proc_macro_client: None,

            flycheck: Vec::new(),
            flycheck_sender,
            flycheck_receiver,

            vfs: Arc::new(RwLock::new((vfs::Vfs::default(), FxHashMap::default()))),
            vfs_config_version: 0,
            vfs_progress_config_version: 0,
            vfs_progress_n_total: 0,
            vfs_progress_n_done: 0,

            workspaces: Arc::new(Vec::new()),
            fetch_workspaces_queue: OpQueue::default(),
            prime_caches_queue: OpQueue::default(),

            fetch_build_data_queue: OpQueue::default(),
        };
        // Apply any required database inputs from the config.
        this.update_configuration(config);
        this
    }

    pub(crate) fn process_changes(&mut self) -> bool {
        let _p = profile::span("GlobalState::process_changes");
        let mut fs_changes = Vec::new();
        // A file was added or deleted
        let mut has_structure_changes = false;

        let (change, changed_files) = {
            let mut change = Change::new();
            let (vfs, line_endings_map) = &mut *self.vfs.write();
            let changed_files = vfs.take_changes();
            if changed_files.is_empty() {
                return false;
            }

            for file in &changed_files {
                if let Some(path) = vfs.file_path(file.file_id).as_path() {
                    let path = path.to_path_buf();
                    if reload::should_refresh_for_change(&path, file.change_kind) {
                        self.fetch_workspaces_queue.request_op();
                    }
                    fs_changes.push((path, file.change_kind));
                    if file.is_created_or_deleted() {
                        has_structure_changes = true;
                    }
                }

                let text = if file.exists() {
                    let bytes = vfs.file_contents(file.file_id).to_vec();
                    String::from_utf8(bytes).ok().and_then(|text| {
                        let (text, line_endings) = LineEndings::normalize(text);
                        line_endings_map.insert(file.file_id, line_endings);
                        Some(Arc::new(text))
                    })
                } else {
                    None
                };
                change.change_file(file.file_id, text);
            }
            if has_structure_changes {
                let roots = self.source_root_config.partition(vfs);
                change.set_roots(roots);
            }
            (change, changed_files)
        };

        self.analysis_host.apply_change(change);

        let raw_database = &self.analysis_host.raw_database();
        self.proc_macro_changed =
            changed_files.iter().filter(|file| !file.is_created_or_deleted()).any(|file| {
                let crates = raw_database.relevant_crates(file.file_id);
                let crate_graph = raw_database.crate_graph();

                crates.iter().any(|&krate| crate_graph[krate].is_proc_macro)
            });
        true
    }

    pub(crate) fn snapshot(&self) -> GlobalStateSnapshot {
        GlobalStateSnapshot {
            config: Arc::clone(&self.config),
            workspaces: Arc::clone(&self.workspaces),
            analysis: self.analysis_host.analysis(),
            vfs: Arc::clone(&self.vfs),
            check_fixes: Arc::clone(&self.diagnostics.check_fixes),
            mem_docs: self.mem_docs.clone(),
            semantic_tokens_cache: Arc::clone(&self.semantic_tokens_cache),
        }
    }

    pub(crate) fn send_request<R: lsp_types::request::Request>(
        &mut self,
        params: R::Params,
        handler: ReqHandler,
    ) {
        let request = self.req_queue.outgoing.register(R::METHOD.to_string(), params, handler);
        self.send(request.into());
    }

    pub(crate) fn complete_request(&mut self, response: lsp_server::Response) {
        let handler = self
            .req_queue
            .outgoing
            .complete(response.id.clone())
            .expect("received response for unknown request");
        handler(self, response)
    }

    pub(crate) fn send_notification<N: lsp_types::notification::Notification>(
        &mut self,
        params: N::Params,
    ) {
        let not = lsp_server::Notification::new(N::METHOD.to_string(), params);
        self.send(not.into());
    }

    pub(crate) fn register_request(
        &mut self,
        request: &lsp_server::Request,
        request_received: Instant,
    ) {
        self.req_queue
            .incoming
            .register(request.id.clone(), (request.method.clone(), request_received));
    }

    pub(crate) fn respond(&mut self, response: lsp_server::Response) {
        if let Some((method, start)) = self.req_queue.incoming.complete(response.id.clone()) {
            if let Some(err) = &response.error {
                if err.message.starts_with("server panicked") {
                    self.poke_rust_analyzer_developer(format!("{}, check the log", err.message))
                }
            }

            let duration = start.elapsed();
            tracing::info!("handled {} - ({}) in {:0.2?}", method, response.id, duration);
            self.send(response.into());
        }
    }

    pub(crate) fn cancel(&mut self, request_id: lsp_server::RequestId) {
        if let Some(response) = self.req_queue.incoming.cancel(request_id) {
            self.send(response.into());
        }
    }

    fn send(&mut self, message: lsp_server::Message) {
        self.sender.send(message).unwrap()
    }
}

impl Drop for GlobalState {
    fn drop(&mut self) {
        self.analysis_host.request_cancellation();
    }
}

impl GlobalStateSnapshot {
    pub(crate) fn url_to_file_id(&self, url: &Url) -> Result<FileId> {
        url_to_file_id(&self.vfs.read().0, url)
    }

    pub(crate) fn file_id_to_url(&self, id: FileId) -> Url {
        file_id_to_url(&self.vfs.read().0, id)
    }

    pub(crate) fn file_line_index(&self, file_id: FileId) -> Cancellable<LineIndex> {
        let endings = self.vfs.read().1[&file_id];
        let index = self.analysis.file_line_index(file_id)?;
        let res = LineIndex { index, endings, encoding: self.config.offset_encoding() };
        Ok(res)
    }

    pub(crate) fn url_file_version(&self, url: &Url) -> Option<i32> {
        let path = from_proto::vfs_path(url).ok()?;
        Some(self.mem_docs.get(&path)?.version)
    }

    pub(crate) fn anchored_path(&self, path: &AnchoredPathBuf) -> Url {
        let mut base = self.vfs.read().0.file_path(path.anchor);
        base.pop();
        let path = base.join(&path.path).unwrap();
        let path = path.as_path().unwrap();
        url_from_abs_path(path)
    }

    pub(crate) fn cargo_target_for_crate_root(
        &self,
        crate_id: CrateId,
    ) -> Option<(&CargoWorkspace, Target)> {
        let file_id = self.analysis.crate_root(crate_id).ok()?;
        let path = self.vfs.read().0.file_path(file_id);
        let path = path.as_path()?;
        self.workspaces.iter().find_map(|ws| match ws {
            ProjectWorkspace::Cargo { cargo, .. } => {
                cargo.target_by_root(path).map(|it| (cargo, it))
            }
            ProjectWorkspace::Json { .. } => None,
            ProjectWorkspace::DetachedFiles { .. } => None,
        })
    }
}

pub(crate) fn file_id_to_url(vfs: &vfs::Vfs, id: FileId) -> Url {
    let path = vfs.file_path(id);
    let path = path.as_path().unwrap();
    url_from_abs_path(path)
}

pub(crate) fn url_to_file_id(vfs: &vfs::Vfs, url: &Url) -> Result<FileId> {
    let path = from_proto::vfs_path(url)?;
    let res = vfs.file_id(&path).ok_or_else(|| format!("file not found: {}", path))?;
    Ok(res)
}
<|endoftext|>