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

use serde_json::Value;

use index_set::IndexSet;
use xi_rope::delta::{Delta, DeltaRegion};
use xi_rope::find::{find, CaseMatching};
use xi_rope::rope::{Rope, LinesMetric, RopeInfo};
use xi_rope::tree::Cursor;
use xi_rope::interval::Interval;
use selection::{Selection, SelRegion};
use xi_rope::tree::Metric;

// might be used in the future to support multiple search queries
//pub struct SearchQuery {
//  id: usize,   // necessary?
//  query: String,
//  case_matching: CaseMatching // todo: add regex
//}
//
//pub struct SearchOccurrence {
//  query_id: usize,      // id or reference to SearchQuery?
//  highlight: Selection
//}
//

const BACKWARDS_FIND_CHUNK_SIZE: usize = 32_768;

pub struct Find {
    // todo: support multiple queries
    //  search_queries: Vec<SearchQuery>,

    // todo: occurrences in separate type that references back to the search query (required for coloring, ...)
    //  occurrences: Vec<SearchOccurrence>,

    // todo: the following will be deprecated after adding support for multiple queries
    /// The occurrences, which determine the highlights, have been updated.
    hls_dirty: bool,
    /// The currently active search string
    search_string: Option<String>,
    /// The case matching setting for the currently active search
    case_matching: CaseMatching,
    /// The set of all known find occurrences (highlights)
    occurrences: Option<Selection>,
    /// Set of ranges that have already been searched for the currently active search string
    valid_search: IndexSet,
}

impl Find {
    pub fn new() -> Find {
        Find {
            //      search_queries: Vec::new(),
            //      occurrences: Vec::new(),
            hls_dirty: true,
            search_string: None,
            case_matching: CaseMatching::CaseInsensitive,
            occurrences: None,
            valid_search: IndexSet::new(),
        }
    }

    pub fn occurrences(&self) -> &Option<Selection> {
        &self.occurrences
    }

    pub fn hls_dirty(&self) -> bool {
        self.hls_dirty
    }

    pub fn set_hls_dirty(&mut self, is_dirty: bool) {
        self.hls_dirty = is_dirty
    }

    pub fn update_highlights(&mut self, text: &Rope, last_text: &Rope,
                             delta: &Delta<RopeInfo>) {
        // todo
        // Update search highlights for changed regions
        if self.search_string.is_some() {
            self.valid_search = self.valid_search.apply_delta(delta);
            let mut occurrences = self.occurrences.take().unwrap_or_else(Selection::new);

            // invalidate occurrences around deletion positions
            for DeltaRegion{ old_offset, new_offset, len } in delta.iter_deletions() {
                self.valid_search.delete_range(new_offset, new_offset + len);
                occurrences.delete_range(old_offset, old_offset + len, false);
            }

            occurrences = occurrences.apply_delta(delta, false, false);

            // invalidate occurrences around insert positions
            for DeltaRegion{ new_offset, len, .. } in delta.iter_inserts() {
                self.valid_search.delete_range(new_offset, new_offset + len);
                occurrences.delete_range(new_offset, new_offset + len, false);
            }

            self.occurrences = Some(occurrences);

            // update find for the whole delta (is going to only update invalid regions)
            let (iv, _) = delta.summary();
            self.update_find(text, iv.start(), iv.end(), true, false);
        }
    }

    pub fn do_find(&mut self, text: &Rope, search_string: Option<String>,
                   case_sensitive: bool) -> Value {
        if search_string.is_none() {
            self.unset();
            return Value::Null;
        }

        let search_string = search_string.unwrap();
        if search_string.len() == 0 {
            self.unset();
            return Value::Null;
        }

        self.set_find(text, &search_string, case_sensitive);

        Value::String(search_string.to_string())
    }

    /// Unsets the search and removes all highlights from the view.
    pub fn unset(&mut self) {
        self.search_string = None;
        self.occurrences = None;
        self.hls_dirty = true;
        self.valid_search.clear();
    }

    /// Sets find for the view, highlights occurrences in the current viewport
    /// and selects the first occurrence relative to the last cursor.
    fn set_find(&mut self, text: &Rope, search_string: &str,
                case_sensitive: bool) {
        // todo: this will be removed once multiple queries are supported
        let case_matching = if case_sensitive {
            CaseMatching::Exact
        } else {
            CaseMatching::CaseInsensitive
        };

        if let Some(ref s) = self.search_string {
            if s == search_string && case_matching == self.case_matching {
                // search parameters did not change
                return;
            }
        }

        self.unset();

        self.search_string = Some(search_string.to_string());
        self.case_matching = case_matching;
    }

    pub fn update_find(&mut self, text: &Rope, start: usize, end: usize, include_slop: bool,
                       stop_on_found: bool)
    {
        if self.search_string.is_none() {
            return;
        }

        let text_len = text.len();
        // extend the search by twice the string length (twice, because case matching may increase
        // the length of an occurrence)
        let slop = if include_slop { self.search_string.as_ref().unwrap().len() * 2 } else { 0 };
        let mut occurrences = self.occurrences.take().unwrap_or_else(Selection::new);
        let mut searched_until = end;
        let mut invalidate_from = None;

        for (_start, end) in self.valid_search.minus_one_range(start, end) {
            let search_string = self.search_string.as_ref().unwrap();

            // expand region to be able to find occurrences around the region's edges
            let from = max(0, slop) - slop;
            let to = min(end + slop, text.len());

            // TODO: this interval might cut a unicode codepoint, make sure it is
            // aligned to codepoint boundaries.
            let text = text.subseq(Interval::new_closed_open(0, to));
            let mut cursor = Cursor::new(&text, from);
            while let Some(start) = find(&mut cursor, self.case_matching, &search_string) {
                let end = cursor.pos();

                let region = SelRegion::new(start, end);
                let prev_len = occurrences.len();
                let (_, e) = occurrences.add_range_distinct(region);
                // in case of ambiguous search results (e.g. search "aba" in "ababa"),
                // the search result closer to the beginning of the file wins
                if e != end {
                    // Skip the search result and keep the occurrence that is closer to
                    // the beginning of the file. Re-align the cursor to the kept
                    // occurrence
                    cursor.set(e);
                    continue;
                }

                // add_range_distinct() above removes ambiguous regions after the added
                // region, if something has been deleted, everything thereafter is
                // invalidated
                if occurrences.len() != prev_len + 1 {
                    invalidate_from = Some(end);
                    occurrences.delete_range(end, text_len, false);
                    break;
                }

                if stop_on_found {
                    searched_until = end;
                    break;
                }
            }
        }
        self.occurrences = Some(occurrences);
        if let Some(invalidate_from) = invalidate_from {
            self.valid_search.union_one_range(start, invalidate_from);

            // invalidate all search results from the point of the ambiguous search result until ...
            let is_multi_line = LinesMetric::next(self.search_string.as_ref().unwrap(), 0).is_some();
            if is_multi_line {
                // ... the end of the file
                self.valid_search.delete_range(invalidate_from, text_len);
            } else {
                // ... the end of the line
                let mut cursor = Cursor::new(&text, invalidate_from);
                if let Some(end_of_line) = cursor.next::<LinesMetric>() {
                    self.valid_search.delete_range(invalidate_from, end_of_line);
                }
            }

            // continue with the find for the current region
            self.update_find(text, invalidate_from, end, false, false);
        } else {
            self.valid_search.union_one_range(start, searched_until);
            self.hls_dirty = true;
        }
    }

    pub fn next_occurrence(&mut self, text: &Rope, reverse: bool, wrapped: bool,
                           stop_on_found: bool, allow_same: bool, sel: (usize, usize)) -> Option<SelRegion> {
        let mut next_occurrence;
        let (from, to) = if reverse != wrapped { (0, sel.0) } else { (sel.0, text.len()) };

        loop {
            next_occurrence = self.occurrences.as_ref().and_then(|occurrences| {
                if occurrences.len() == 0 {
                    return None;
                }
                if wrapped { // wrap around file boundaries
                    if reverse {
                        occurrences.last()
                    } else {
                        occurrences.first()
                    }
                } else {
                    let ix = occurrences.search(sel.0);
                    if reverse {
                        ix.checked_sub(1).and_then(|i| occurrences.get(i))
                    } else {
                        occurrences.get(ix).and_then(|oc| {
                            // if possible, the current selection should be extended, instead of
                            // jumping to the next occurrence
                            if oc.end == sel.1 && !allow_same {
                                occurrences.get(ix+1)
                            } else {
                                Some(oc)
                            }
                        })
                    }
                }
            }).cloned();

            let region = {
                let mut unsearched = self.valid_search.minus_one_range(from, to);
                if reverse { unsearched.next_back() } else { unsearched.next() }
            };
            if let Some((b, e)) = region {
                if let Some(ref occurrence) = next_occurrence {
                    if (reverse && occurrence.start >= e) || (!reverse && occurrence.end <= b) {
                        break;
                    }
                }

                if !reverse {
                    self.update_find(text, b, e, false, stop_on_found);
                } else {
                    // when searching backward, the actual search isn't executed backwards, which is
                    // why the search is executed in chunks
                    let start = if e - b > BACKWARDS_FIND_CHUNK_SIZE {
                        e - BACKWARDS_FIND_CHUNK_SIZE
                    } else {
                        b
                    };
                    self.update_find(text, start, e, false, false);
                }
            } else {
                break;
            }
        }

        next_occurrence
    }
}<commit_msg>Fix regex search for cursor matches<commit_after>use std::cmp::{min,max};

use serde_json::Value;

use index_set::IndexSet;
use xi_rope::delta::{Delta, DeltaRegion};
use xi_rope::find::{find, CaseMatching};
use xi_rope::rope::{Rope, LinesMetric, RopeInfo};
use xi_rope::tree::Cursor;
use xi_rope::interval::Interval;
use selection::{Selection, SelRegion};
use xi_rope::tree::Metric;

// might be used in the future to support multiple search queries
//pub struct SearchQuery {
//  id: usize,   // necessary?
//  query: String,
//  case_matching: CaseMatching // todo: add regex
//}
//
//pub struct SearchOccurrence {
//  query_id: usize,      // id or reference to SearchQuery?
//  highlight: Selection
//}
//

const BACKWARDS_FIND_CHUNK_SIZE: usize = 32_768;

pub struct Find {
    // todo: support multiple queries
    //  search_queries: Vec<SearchQuery>,

    // todo: occurrences in separate type that references back to the search query (required for coloring, ...)
    //  occurrences: Vec<SearchOccurrence>,

    // todo: the following will be deprecated after adding support for multiple queries
    /// The occurrences, which determine the highlights, have been updated.
    hls_dirty: bool,
    /// The currently active search string
    search_string: Option<String>,
    /// The case matching setting for the currently active search
    case_matching: CaseMatching,
    /// The set of all known find occurrences (highlights)
    occurrences: Option<Selection>,
    /// Set of ranges that have already been searched for the currently active search string
    valid_search: IndexSet,
}

impl Find {
    pub fn new() -> Find {
        Find {
            //      search_queries: Vec::new(),
            //      occurrences: Vec::new(),
            hls_dirty: true,
            search_string: None,
            case_matching: CaseMatching::CaseInsensitive,
            occurrences: None,
            valid_search: IndexSet::new(),
        }
    }

    pub fn occurrences(&self) -> &Option<Selection> {
        &self.occurrences
    }

    pub fn hls_dirty(&self) -> bool {
        self.hls_dirty
    }

    pub fn set_hls_dirty(&mut self, is_dirty: bool) {
        self.hls_dirty = is_dirty
    }

    pub fn update_highlights(&mut self, text: &Rope, last_text: &Rope,
                             delta: &Delta<RopeInfo>) {
        // todo
        // Update search highlights for changed regions
        if self.search_string.is_some() {
            self.valid_search = self.valid_search.apply_delta(delta);
            let mut occurrences = self.occurrences.take().unwrap_or_else(Selection::new);

            // invalidate occurrences around deletion positions
            for DeltaRegion{ old_offset, new_offset, len } in delta.iter_deletions() {
                self.valid_search.delete_range(new_offset, new_offset + len);
                occurrences.delete_range(old_offset, old_offset + len, false);
            }

            occurrences = occurrences.apply_delta(delta, false, false);

            // invalidate occurrences around insert positions
            for DeltaRegion{ new_offset, len, .. } in delta.iter_inserts() {
                self.valid_search.delete_range(new_offset, new_offset + len);
                occurrences.delete_range(new_offset, new_offset + len, false);
            }

            self.occurrences = Some(occurrences);

            // update find for the whole delta (is going to only update invalid regions)
            let (iv, _) = delta.summary();
            self.update_find(text, iv.start(), iv.end(), true, false);
        }
    }

    pub fn do_find(&mut self, text: &Rope, search_string: Option<String>,
                   case_sensitive: bool) -> Value {
        if search_string.is_none() {
            self.unset();
            return Value::Null;
        }

        let search_string = search_string.unwrap();
        if search_string.len() == 0 {
            self.unset();
            return Value::Null;
        }

        self.set_find(text, &search_string, case_sensitive);

        Value::String(search_string.to_string())
    }

    /// Unsets the search and removes all highlights from the view.
    pub fn unset(&mut self) {
        self.search_string = None;
        self.occurrences = None;
        self.hls_dirty = true;
        self.valid_search.clear();
    }

    /// Sets find for the view, highlights occurrences in the current viewport
    /// and selects the first occurrence relative to the last cursor.
    fn set_find(&mut self, text: &Rope, search_string: &str,
                case_sensitive: bool) {
        // todo: this will be removed once multiple queries are supported
        let case_matching = if case_sensitive {
            CaseMatching::Exact
        } else {
            CaseMatching::CaseInsensitive
        };

        if let Some(ref s) = self.search_string {
            if s == search_string && case_matching == self.case_matching {
                // search parameters did not change
                return;
            }
        }

        self.unset();

        self.search_string = Some(search_string.to_string());
        self.case_matching = case_matching;
    }

    pub fn update_find(&mut self, text: &Rope, start: usize, end: usize, include_slop: bool,
                       stop_on_found: bool)
    {
        if self.search_string.is_none() {
            return;
        }

        let text_len = text.len();
        // extend the search by twice the string length (twice, because case matching may increase
        // the length of an occurrence)
        let slop = if include_slop { self.search_string.as_ref().unwrap().len() * 2 } else { 0 };
        let mut occurrences = self.occurrences.take().unwrap_or_else(Selection::new);
        let mut searched_until = end;
        let mut invalidate_from = None;

        for (_start, end) in self.valid_search.minus_one_range(start, end) {
            let search_string = self.search_string.as_ref().unwrap();

            // expand region to be able to find occurrences around the region's edges
            let from = max(0, slop) - slop;
            let to = min(end + slop, text.len());

            // TODO: this interval might cut a unicode codepoint, make sure it is
            // aligned to codepoint boundaries.
            let text = text.subseq(Interval::new_closed_open(0, to));
            let mut cursor = Cursor::new(&text, from);
            while let Some(start) = find(&mut cursor, self.case_matching, &search_string) {
                let end = cursor.pos();

                let region = SelRegion::new(start, end);
                let prev_len = occurrences.len();
                let (_, e) = occurrences.add_range_distinct(region);
                // in case of ambiguous search results (e.g. search "aba" in "ababa"),
                // the search result closer to the beginning of the file wins
                if e != end {
                    // Skip the search result and keep the occurrence that is closer to
                    // the beginning of the file. Re-align the cursor to the kept
                    // occurrence
                    cursor.set(e);
                    continue;
                }

                // add_range_distinct() above removes ambiguous regions after the added
                // region, if something has been deleted, everything thereafter is
                // invalidated
                if occurrences.len() != prev_len + 1 {
                    invalidate_from = Some(end);
                    occurrences.delete_range(end, text_len, false);
                    break;
                }

                // in case current cursor matches search result (for example query a* matches)
                // all cursor positions, then cursor needs to be increased so that search
                // continues at next position. Otherwise, search will result in overflow since
                // search will always repeat at current cursor position.
                if start == end {
                    // determine whether end of text is reached and stop search or increase
                    // cursor manually
                    if end + 1 >= text_len {
                        break;
                    } else {
                        cursor.set(end + 1);
                    }
                }

                if stop_on_found {
                    searched_until = end;
                    break;
                }
            }
        }
        self.occurrences = Some(occurrences);
        if let Some(invalidate_from) = invalidate_from {
            self.valid_search.union_one_range(start, invalidate_from);

            // invalidate all search results from the point of the ambiguous search result until ...
            let is_multi_line = LinesMetric::next(self.search_string.as_ref().unwrap(), 0).is_some();
            if is_multi_line {
                // ... the end of the file
                self.valid_search.delete_range(invalidate_from, text_len);
            } else {
                // ... the end of the line
                let mut cursor = Cursor::new(&text, invalidate_from);
                if let Some(end_of_line) = cursor.next::<LinesMetric>() {
                    self.valid_search.delete_range(invalidate_from, end_of_line);
                }
            }

            // continue with the find for the current region
            self.update_find(text, invalidate_from, end, false, false);
        } else {
            self.valid_search.union_one_range(start, searched_until);
            self.hls_dirty = true;
        }
    }

    pub fn next_occurrence(&mut self, text: &Rope, reverse: bool, wrapped: bool,
                           stop_on_found: bool, allow_same: bool, sel: (usize, usize)) -> Option<SelRegion> {
        let mut next_occurrence;
        let (from, to) = if reverse != wrapped { (0, sel.0) } else { (sel.0, text.len()) };

        loop {
            next_occurrence = self.occurrences.as_ref().and_then(|occurrences| {
                if occurrences.len() == 0 {
                    return None;
                }
                if wrapped { // wrap around file boundaries
                    if reverse {
                        occurrences.last()
                    } else {
                        occurrences.first()
                    }
                } else {
                    let ix = occurrences.search(sel.0);
                    if reverse {
                        ix.checked_sub(1).and_then(|i| occurrences.get(i))
                    } else {
                        occurrences.get(ix).and_then(|oc| {
                            // if possible, the current selection should be extended, instead of
                            // jumping to the next occurrence
                            if oc.end == sel.1 && !allow_same {
                                occurrences.get(ix+1)
                            } else {
                                Some(oc)
                            }
                        })
                    }
                }
            }).cloned();

            let region = {
                let mut unsearched = self.valid_search.minus_one_range(from, to);
                if reverse { unsearched.next_back() } else { unsearched.next() }
            };
            if let Some((b, e)) = region {
                if let Some(ref occurrence) = next_occurrence {
                    if (reverse && occurrence.start >= e) || (!reverse && occurrence.end <= b) {
                        break;
                    }
                }

                if !reverse {
                    self.update_find(text, b, e, false, stop_on_found);
                } else {
                    // when searching backward, the actual search isn't executed backwards, which is
                    // why the search is executed in chunks
                    let start = if e - b > BACKWARDS_FIND_CHUNK_SIZE {
                        e - BACKWARDS_FIND_CHUNK_SIZE
                    } else {
                        b
                    };
                    self.update_find(text, start, e, false, false);
                }
            } else {
                break;
            }
        }

        next_occurrence
    }
}<|endoftext|>


<commit_before><commit_msg>Add regex for default cleaner<commit_after><|endoftext|>


<commit_before><commit_msg>SWitch matrix 3x3 inverse and determinant methods to use vector math<commit_after><|endoftext|>


<commit_before><commit_msg>Updates SceneLight to implement from SceneObject.<commit_after><|endoftext|>


<commit_before>// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::ffi::CString;
use std::fmt;
use super::breakpointlocation::SBBreakpointLocation;
use super::stream::SBStream;
use super::stringlist::SBStringList;
use super::lldb_addr_t;
use sys;

/// A logical breakpoint and its associated settings.
///
/// # To Hit or Not
///
/// A breakpoint has multiple ways of controlling whether
/// or not it should be considered active.
///
/// * Enabled. This is controlled via [`is_enabled`] and
///   [`set_enabled`].
/// * One shot. If set, this will be disabled once it has
///   been hit. This is controlled via [`is_oneshot`] and
///   [`set_oneshot`].
/// * Ignore count. If set, this breakpoint will be ignored
///   the first *ignore count* times that it is hit. This is
///   controlled via [`ignore_count`] and [`set_ignore_count`].
///
/// A count of how many times a breakpoint has been it is
/// available via [`hit_count`].
///
/// # Breakpoint Names and Aliases
///
/// Breakpoints can have names associated with them. These are
/// actually more like tags in that the same name can be applied
/// to multiple breakpoints so that a single command invocation
/// can work on multiple breakpoints at once.
///
/// A common use case for this is setting up families of breakpoints,
/// for example on `malloc`, `realloc`, and `free` and giving them
/// all a name of `memory`. Then, you can make it easy for the user
/// enable or disable them all in a single shot.
///
/// Names are managed via [`add_name`], [`remove_name`],
/// [`matches_name`] and [`names`].
///
/// # Breakpoint Locations
///
/// ...
///
/// [`is_enabled`]: #method.is_enabled
/// [`set_enabled`]: #method.set_enabled
/// [`is_oneshot`]: #method.is_oneshot
/// [`set_oneshot`]: #method.set_oneshot
/// [`ignore_count`]: #method.ignore_count
/// [`set_ignore_count`]: #method.set_ignore_count
/// [`hit_count`]: #method.hit_count
/// [`add_name`]: #method.add_name
/// [`remove_name`]: #method.remove_name
/// [`matches_name`]: #method.matches_name
/// [`names`]: #method.names
pub struct SBBreakpoint {
    /// The underlying raw `SBBreakpointRef`.
    pub raw: sys::SBBreakpointRef,
}

impl SBBreakpoint {
    /// Construct a new `SBBreakpoint`.
    pub fn wrap(raw: sys::SBBreakpointRef) -> SBBreakpoint {
        SBBreakpoint { raw: raw }
    }

    /// Construct a new `Some(SBBreakpoint)` or `None`.
    pub fn maybe_wrap(raw: sys::SBBreakpointRef) -> Option<SBBreakpoint> {
        if unsafe { sys::SBBreakpointIsValid(raw) != 0 } {
            Some(SBBreakpoint { raw: raw })
        } else {
            None
        }
    }

    /// Check whether or not this is a valid `SBBreakpoint` value.
    pub fn is_valid(&self) -> bool {
        unsafe { sys::SBBreakpointIsValid(self.raw) != 0 }
    }

    #[allow(missing_docs)]
    pub fn is_enabled(&self) -> bool {
        unsafe { sys::SBBreakpointIsEnabled(self.raw) != 0 }
    }

    #[allow(missing_docs)]
    pub fn set_enabled(&self, enabled: bool) {
        unsafe { sys::SBBreakpointSetEnabled(self.raw, enabled as u8) }
    }

    #[allow(missing_docs)]
    pub fn is_oneshot(&self) -> bool {
        unsafe { sys::SBBreakpointIsOneShot(self.raw) != 0 }
    }

    #[allow(missing_docs)]
    pub fn set_oneshot(&self, oneshot: bool) {
        unsafe { sys::SBBreakpointSetOneShot(self.raw, oneshot as u8) }
    }

    #[allow(missing_docs)]
    pub fn is_internal(&self) -> bool {
        unsafe { sys::SBBreakpointIsInternal(self.raw) != 0 }
    }

    #[allow(missing_docs)]
    pub fn hit_count(&self) -> u32 {
        unsafe { sys::SBBreakpointGetHitCount(self.raw) }
    }

    #[allow(missing_docs)]
    pub fn ignore_count(&self) -> u32 {
        unsafe { sys::SBBreakpointGetIgnoreCount(self.raw) }
    }

    #[allow(missing_docs)]
    pub fn set_ignore_count(&self, count: u32) {
        unsafe { sys::SBBreakpointSetIgnoreCount(self.raw, count) }
    }

    #[allow(missing_docs)]
    pub fn add_name(&self, name: &str) -> bool {
        let name = CString::new(name).unwrap();
        unsafe { sys::SBBreakpointAddName(self.raw, name.as_ptr()) != 0 }
    }

    #[allow(missing_docs)]
    pub fn remove_name(&self, name: &str) {
        let name = CString::new(name).unwrap();
        unsafe { sys::SBBreakpointRemoveName(self.raw, name.as_ptr()) };
    }

    #[allow(missing_docs)]
    pub fn matches_name(&self, name: &str) -> bool {
        let name = CString::new(name).unwrap();
        unsafe { sys::SBBreakpointMatchesName(self.raw, name.as_ptr()) != 0 }
    }

    #[allow(missing_docs)]
    pub fn names(&self) -> SBStringList {
        let names = SBStringList::new();
        unsafe { sys::SBBreakpointGetNames(self.raw, names.raw) };
        names
    }

    #[allow(missing_docs)]
    pub fn clear_all_breakpoint_sites(&self) {
        unsafe { sys::SBBreakpointClearAllBreakpointSites(self.raw) };
    }

    #[allow(missing_docs)]
    pub fn find_location_by_address(&self, address: lldb_addr_t) -> Option<SBBreakpointLocation> {
        SBBreakpointLocation::maybe_wrap(unsafe {
                                             sys::SBBreakpointFindLocationByAddress(self.raw,
                                                                                address)
                                         })
    }

    #[allow(missing_docs)]
    pub fn find_location_id_by_address(&self, address: lldb_addr_t) -> i32 {
        unsafe { sys::SBBreakpointFindLocationIDByAddress(self.raw, address) }
    }

    #[allow(missing_docs)]
    pub fn find_location_by_id(&self, id: i32) -> Option<SBBreakpointLocation> {
        SBBreakpointLocation::maybe_wrap(unsafe { sys::SBBreakpointFindLocationByID(self.raw, id) })
    }

    #[allow(missing_docs)]
    pub fn locations(&self) -> SBBreakpointLocationIter {
        SBBreakpointLocationIter {
            breakpoint: self,
            idx: 0,
        }
    }
}

impl fmt::Debug for SBBreakpoint {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        let stream = SBStream::new();
        unsafe { sys::SBBreakpointGetDescription(self.raw, stream.raw) };
        write!(fmt, "SBBreakpoint {{ {} }}", stream.data())
    }
}

impl Drop for SBBreakpoint {
    fn drop(&mut self) {
        unsafe { sys::DisposeSBBreakpoint(self.raw) };
    }
}

/// An iterator over the [locations] in an [`SBBreakpoint`].
///
/// [locations]: struct.SBBreakpointLocation.html
/// [`SBBreakpoint`]: struct.SBBreakpoint.html
pub struct SBBreakpointLocationIter<'d> {
    breakpoint: &'d SBBreakpoint,
    idx: usize,
}

impl<'d> Iterator for SBBreakpointLocationIter<'d> {
    type Item = SBBreakpointLocation;

    fn next(&mut self) -> Option<SBBreakpointLocation> {
        if self.idx < unsafe { sys::SBBreakpointGetNumLocations(self.breakpoint.raw) as usize } {
            let r = SBBreakpointLocation::maybe_wrap(unsafe {
                sys::SBBreakpointGetLocationAtIndex(self.breakpoint.raw, self.idx as u32)
            });
            self.idx += 1;
            r
        } else {
            None
        }
    }
}

#[cfg(feature = "graphql")]
graphql_object!(SBBreakpoint: super::debugger::SBDebugger | &self | {
    field is_valid() -> bool {
        self.is_valid()
    }

    field is_enabled() -> bool {
        self.is_enabled()
    }

    field is_oneshot() -> bool {
        self.is_oneshot()
    }

    field is_internal() -> bool {
        self.is_internal()
    }

    field is_valid() -> bool {
        self.is_valid()
    }

    // TODO(bm) This should be u32
    field ignore_count() -> i64 {
        self.ignore_count() as i64
    }

    // TODO(bm) This should be u32
    field hit_count() -> i64 {
        self.hit_count() as i64
    }

    // TODO(bm) Make this work. (Lifetimes.)
    // field names() -> Vec<&str> {
    //     self.names().iter().collect()
    // }

    field locations() -> Vec<SBBreakpointLocation> {
        self.locations().collect()
    }
});
<commit_msg>Add missing SBBreakpoint ID getter.<commit_after>// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::ffi::CString;
use std::fmt;
use super::breakpointlocation::SBBreakpointLocation;
use super::stream::SBStream;
use super::stringlist::SBStringList;
use super::lldb_addr_t;
use sys;

/// A logical breakpoint and its associated settings.
///
/// # To Hit or Not
///
/// A breakpoint has multiple ways of controlling whether
/// or not it should be considered active.
///
/// * Enabled. This is controlled via [`is_enabled`] and
///   [`set_enabled`].
/// * One shot. If set, this will be disabled once it has
///   been hit. This is controlled via [`is_oneshot`] and
///   [`set_oneshot`].
/// * Ignore count. If set, this breakpoint will be ignored
///   the first *ignore count* times that it is hit. This is
///   controlled via [`ignore_count`] and [`set_ignore_count`].
///
/// A count of how many times a breakpoint has been it is
/// available via [`hit_count`].
///
/// # Breakpoint Names and Aliases
///
/// Breakpoints can have names associated with them. These are
/// actually more like tags in that the same name can be applied
/// to multiple breakpoints so that a single command invocation
/// can work on multiple breakpoints at once.
///
/// A common use case for this is setting up families of breakpoints,
/// for example on `malloc`, `realloc`, and `free` and giving them
/// all a name of `memory`. Then, you can make it easy for the user
/// enable or disable them all in a single shot.
///
/// Names are managed via [`add_name`], [`remove_name`],
/// [`matches_name`] and [`names`].
///
/// # Breakpoint Locations
///
/// ...
///
/// [`is_enabled`]: #method.is_enabled
/// [`set_enabled`]: #method.set_enabled
/// [`is_oneshot`]: #method.is_oneshot
/// [`set_oneshot`]: #method.set_oneshot
/// [`ignore_count`]: #method.ignore_count
/// [`set_ignore_count`]: #method.set_ignore_count
/// [`hit_count`]: #method.hit_count
/// [`add_name`]: #method.add_name
/// [`remove_name`]: #method.remove_name
/// [`matches_name`]: #method.matches_name
/// [`names`]: #method.names
pub struct SBBreakpoint {
    /// The underlying raw `SBBreakpointRef`.
    pub raw: sys::SBBreakpointRef,
}

impl SBBreakpoint {
    /// Construct a new `SBBreakpoint`.
    pub fn wrap(raw: sys::SBBreakpointRef) -> SBBreakpoint {
        SBBreakpoint { raw: raw }
    }

    /// Construct a new `Some(SBBreakpoint)` or `None`.
    pub fn maybe_wrap(raw: sys::SBBreakpointRef) -> Option<SBBreakpoint> {
        if unsafe { sys::SBBreakpointIsValid(raw) != 0 } {
            Some(SBBreakpoint { raw: raw })
        } else {
            None
        }
    }

    /// Check whether or not this is a valid `SBBreakpoint` value.
    pub fn is_valid(&self) -> bool {
        unsafe { sys::SBBreakpointIsValid(self.raw) != 0 }
    }

    #[allow(missing_docs)]
    pub fn id(&self) -> i32 {
        unsafe { sys::SBBreakpointGetID(self.raw) }
    }

    #[allow(missing_docs)]
    pub fn is_enabled(&self) -> bool {
        unsafe { sys::SBBreakpointIsEnabled(self.raw) != 0 }
    }

    #[allow(missing_docs)]
    pub fn set_enabled(&self, enabled: bool) {
        unsafe { sys::SBBreakpointSetEnabled(self.raw, enabled as u8) }
    }

    #[allow(missing_docs)]
    pub fn is_oneshot(&self) -> bool {
        unsafe { sys::SBBreakpointIsOneShot(self.raw) != 0 }
    }

    #[allow(missing_docs)]
    pub fn set_oneshot(&self, oneshot: bool) {
        unsafe { sys::SBBreakpointSetOneShot(self.raw, oneshot as u8) }
    }

    #[allow(missing_docs)]
    pub fn is_internal(&self) -> bool {
        unsafe { sys::SBBreakpointIsInternal(self.raw) != 0 }
    }

    #[allow(missing_docs)]
    pub fn hit_count(&self) -> u32 {
        unsafe { sys::SBBreakpointGetHitCount(self.raw) }
    }

    #[allow(missing_docs)]
    pub fn ignore_count(&self) -> u32 {
        unsafe { sys::SBBreakpointGetIgnoreCount(self.raw) }
    }

    #[allow(missing_docs)]
    pub fn set_ignore_count(&self, count: u32) {
        unsafe { sys::SBBreakpointSetIgnoreCount(self.raw, count) }
    }

    #[allow(missing_docs)]
    pub fn add_name(&self, name: &str) -> bool {
        let name = CString::new(name).unwrap();
        unsafe { sys::SBBreakpointAddName(self.raw, name.as_ptr()) != 0 }
    }

    #[allow(missing_docs)]
    pub fn remove_name(&self, name: &str) {
        let name = CString::new(name).unwrap();
        unsafe { sys::SBBreakpointRemoveName(self.raw, name.as_ptr()) };
    }

    #[allow(missing_docs)]
    pub fn matches_name(&self, name: &str) -> bool {
        let name = CString::new(name).unwrap();
        unsafe { sys::SBBreakpointMatchesName(self.raw, name.as_ptr()) != 0 }
    }

    #[allow(missing_docs)]
    pub fn names(&self) -> SBStringList {
        let names = SBStringList::new();
        unsafe { sys::SBBreakpointGetNames(self.raw, names.raw) };
        names
    }

    #[allow(missing_docs)]
    pub fn clear_all_breakpoint_sites(&self) {
        unsafe { sys::SBBreakpointClearAllBreakpointSites(self.raw) };
    }

    #[allow(missing_docs)]
    pub fn find_location_by_address(&self, address: lldb_addr_t) -> Option<SBBreakpointLocation> {
        SBBreakpointLocation::maybe_wrap(unsafe {
                                             sys::SBBreakpointFindLocationByAddress(self.raw,
                                                                                address)
                                         })
    }

    #[allow(missing_docs)]
    pub fn find_location_id_by_address(&self, address: lldb_addr_t) -> i32 {
        unsafe { sys::SBBreakpointFindLocationIDByAddress(self.raw, address) }
    }

    #[allow(missing_docs)]
    pub fn find_location_by_id(&self, id: i32) -> Option<SBBreakpointLocation> {
        SBBreakpointLocation::maybe_wrap(unsafe { sys::SBBreakpointFindLocationByID(self.raw, id) })
    }

    #[allow(missing_docs)]
    pub fn locations(&self) -> SBBreakpointLocationIter {
        SBBreakpointLocationIter {
            breakpoint: self,
            idx: 0,
        }
    }
}

impl fmt::Debug for SBBreakpoint {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        let stream = SBStream::new();
        unsafe { sys::SBBreakpointGetDescription(self.raw, stream.raw) };
        write!(fmt, "SBBreakpoint {{ {} }}", stream.data())
    }
}

impl Drop for SBBreakpoint {
    fn drop(&mut self) {
        unsafe { sys::DisposeSBBreakpoint(self.raw) };
    }
}

/// An iterator over the [locations] in an [`SBBreakpoint`].
///
/// [locations]: struct.SBBreakpointLocation.html
/// [`SBBreakpoint`]: struct.SBBreakpoint.html
pub struct SBBreakpointLocationIter<'d> {
    breakpoint: &'d SBBreakpoint,
    idx: usize,
}

impl<'d> Iterator for SBBreakpointLocationIter<'d> {
    type Item = SBBreakpointLocation;

    fn next(&mut self) -> Option<SBBreakpointLocation> {
        if self.idx < unsafe { sys::SBBreakpointGetNumLocations(self.breakpoint.raw) as usize } {
            let r = SBBreakpointLocation::maybe_wrap(unsafe {
                sys::SBBreakpointGetLocationAtIndex(self.breakpoint.raw, self.idx as u32)
            });
            self.idx += 1;
            r
        } else {
            None
        }
    }
}

#[cfg(feature = "graphql")]
graphql_object!(SBBreakpoint: super::debugger::SBDebugger | &self | {
    field is_valid() -> bool {
        self.is_valid()
    }

    // TODO(bm) This should be i32
    field id() -> i64 {
        self.id() as i64
    }

    field is_enabled() -> bool {
        self.is_enabled()
    }

    field is_oneshot() -> bool {
        self.is_oneshot()
    }

    field is_internal() -> bool {
        self.is_internal()
    }

    field is_valid() -> bool {
        self.is_valid()
    }

    // TODO(bm) This should be u32
    field ignore_count() -> i64 {
        self.ignore_count() as i64
    }

    // TODO(bm) This should be u32
    field hit_count() -> i64 {
        self.hit_count() as i64
    }

    // TODO(bm) Make this work. (Lifetimes.)
    // field names() -> Vec<&str> {
    //     self.names().iter().collect()
    // }

    field locations() -> Vec<SBBreakpointLocation> {
        self.locations().collect()
    }
});
<|endoftext|>


<commit_before>// Copyright 2016 union-find-rs Developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.

extern crate criterion;
extern crate lazy_static;
extern crate union_find;

use criterion::{criterion_group, criterion_main, Criterion};
use lazy_static::lazy_static;
use std::fs::File;
use std::io::{BufRead, BufReader};
use union_find::{
    QuickFindUf, QuickUnionUf, Union, UnionByRank, UnionByRankSize, UnionBySize, UnionBySizeRank,
    UnionFind,
};

lazy_static! {
    static ref TINY: Input = Input::from_file("tiny", "etc/tinyUF.txt");
    static ref MEDIUM: Input = Input::from_file("medium", "etc/mediumUF.txt");
    static ref LARGE: Input = Input::from_file("large", "etc/largeUF.txt");
}

struct Cache<'a, T> {
    input: &'a Input,
    init: Option<T>,
    union1: Option<T>,
    union2: Option<T>,
    find1: Option<T>,
    find2: Option<T>,
}

impl<'a, T> Cache<'a, T> {
    fn new(input: &'a Input) -> Cache<'a, T> {
        Cache {
            input,
            init: None,
            union1: None,
            union2: None,
            find1: None,
            find2: None,
        }
    }

    fn init<V>(&mut self) -> &T
    where
        T: UnionFind<V>,
        V: Union + Default,
    {
        if self.init.is_none() {
            self.init = Some(self.input.init());
        }
        self.init.as_ref().unwrap()
    }

    fn union1<V>(&mut self) -> &T
    where
        T: UnionFind<V> + Clone,
        V: Union + Default,
    {
        if self.union1.is_none() {
            let mut uf = self.init().clone();
            self.input.union(&mut uf);
            self.union1 = Some(uf);
        }
        self.union1.as_ref().unwrap()
    }

    fn union2<V>(&mut self) -> &T
    where
        T: UnionFind<V> + Clone,
        V: Union + Default,
    {
        if self.union2.is_none() {
            let mut uf = self.union1().clone();
            self.input.union(&mut uf);
            self.union2 = Some(uf);
        }
        self.union2.as_ref().unwrap()
    }

    fn find1<V>(&mut self) -> &T
    where
        T: UnionFind<V> + Clone,
        V: Union + Default,
    {
        if self.find1.is_none() {
            let mut uf = self.union1().clone();
            self.input.find_all(&mut uf);
            self.find1 = Some(uf);
        }
        self.find1.as_ref().unwrap()
    }

    fn find2<V>(&mut self) -> &T
    where
        T: UnionFind<V> + Clone,
        V: Union + Default,
    {
        if self.find2.is_none() {
            let mut uf = self.union1().clone();
            self.input.find_all(&mut uf);
            self.find2 = Some(uf);
        }
        self.find2.as_ref().unwrap()
    }
}

#[derive(Clone, Debug)]
struct Input {
    name: &'static str,
    size: usize,
    conn: Vec<(usize, usize)>,
}

impl Input {
    fn from_file(name: &'static str, file_name: &str) -> Input {
        let mut reader = BufReader::new(File::open(file_name).unwrap());
        let mut buf = String::new();

        let _ = reader.read_line(&mut buf).unwrap();
        let size = buf.trim().parse::<usize>().unwrap();
        buf.clear();

        let mut conn = vec![];

        while reader.read_line(&mut buf).unwrap() > 0 {
            {
                let mut sp = buf.split_whitespace();
                let a = sp.next().unwrap().parse::<usize>().unwrap();
                let b = sp.next().unwrap().parse::<usize>().unwrap();
                conn.push((a, b));
            }

            buf.clear();
        }

        Input { name, size, conn }
    }

    fn init<T, V>(&self) -> T
    where
        T: UnionFind<V>,
        V: Union + Default,
    {
        T::new(self.size)
    }

    fn union<T, V>(&self, uf: &mut T)
    where
        T: UnionFind<V>,
        V: Union,
    {
        for &(p, q) in &self.conn {
            uf.union(p, q);
        }
    }
    fn find_all<T, V>(&self, uf: &mut T)
    where
        T: UnionFind<V>,
        V: Union,
    {
        for i in 0..uf.size() {
            let _ = uf.find(i);
        }
    }

    fn bench_clone_from<T, V>(&self, c: &mut Criterion, category: &str, cache: &mut Cache<T>)
    where
        T: UnionFind<V> + Clone,
        V: Union + Default,
    {
        let id = format!("{}::{}::{}", category, self.name, "clone_from");
        let base = cache.init();
        let mut uf = base.clone();
        c.bench_function(&id, |b| {
            b.iter(|| {
                uf.clone_from(base);
            })
        });
    }

    fn bench_union1<T, V>(&self, c: &mut Criterion, category: &str, cache: &mut Cache<T>)
    where
        T: UnionFind<V> + Clone,
        V: Union + Default,
    {
        let id = format!("{}::{}::{}", category, self.name, "union1");
        let base = cache.init();
        let mut uf = base.clone();
        c.bench_function(&id, |b| {
            b.iter(|| {
                uf.clone_from(base);
                self.union(&mut uf);
            })
        });
    }
    fn bench_union2<T, V>(&self, c: &mut Criterion, category: &str, cache: &mut Cache<T>)
    where
        T: UnionFind<V> + Clone,
        V: Union + Default,
    {
        let id = format!("{}::{}::{}", category, self.name, "union2");
        let base = cache.union1();
        let mut uf = base.clone();
        c.bench_function(&id, |b| {
            b.iter(|| {
                uf.clone_from(base);
                self.union(&mut uf);
            })
        });
    }
    fn bench_union3<T, V>(&self, c: &mut Criterion, category: &str, cache: &mut Cache<T>)
    where
        T: UnionFind<V> + Clone,
        V: Union + Default,
    {
        let id = format!("{}::{}::{}", category, self.name, "union3");
        let base = cache.union2();
        let mut uf = base.clone();
        c.bench_function(&id, |b| {
            b.iter(|| {
                uf.clone_from(base);
                self.union(&mut uf);
            })
        });
    }

    fn bench_find1<T, V>(&self, c: &mut Criterion, category: &str, cache: &mut Cache<T>)
    where
        T: UnionFind<V> + Clone,
        V: Union + Default,
    {
        let id = format!("{}::{}::{}", category, self.name, "find1");
        let base = cache.union1();
        let mut uf = base.clone();
        c.bench_function(&id, |b| {
            b.iter(|| {
                uf.clone_from(base);
                self.find_all(&mut uf);
            })
        });
    }
    fn bench_find2<T, V>(&self, c: &mut Criterion, category: &str, cache: &mut Cache<T>)
    where
        T: UnionFind<V> + Clone,
        V: Union + Default,
    {
        let id = format!("{}::{}::{}", category, self.name, "find2");
        let base = cache.find1();
        let mut uf = base.clone();
        c.bench_function(&id, |b| {
            b.iter(|| {
                uf.clone_from(base);
                self.find_all(&mut uf);
            })
        });
    }
    fn bench_find3<T, V>(&self, c: &mut Criterion, category: &str, cache: &mut Cache<T>)
    where
        T: UnionFind<V> + Clone,
        V: Union + Default,
    {
        let id = format!("{}::{}::{}", category, self.name, "find3");
        let base = cache.find2();
        let mut uf = base.clone();
        c.bench_function(&id, |b| {
            b.iter(|| {
                uf.clone_from(base);
                self.find_all(&mut uf);
            })
        });
    }
}

fn bench_for_type<T, V>(c: &mut Criterion, category: &str, inputs: &[Input])
where
    T: UnionFind<V> + Clone,
    V: Union + Default,
{
    for input in inputs {
        let mut cache = Cache::<T>::new(input);
        input.bench_clone_from(c, category, &mut cache);
        input.bench_union1(c, category, &mut cache);
        input.bench_union2(c, category, &mut cache);
        input.bench_union3(c, category, &mut cache);
        input.bench_find1(c, category, &mut cache);
        input.bench_find2(c, category, &mut cache);
        input.bench_find3(c, category, &mut cache);
    }
}

fn bench(c: &mut Criterion) {
    let inputs = &[
        Input::from_file("tiny", "etc/tinyUF.txt"),
        Input::from_file("medium", "etc/mediumUF.txt"),
        Input::from_file("large", "etc/largeUF.txt"),
    ];

    bench_for_type::<QuickUnionUf<UnionBySize>, _>(c, "quick_union::by_size", inputs);
    bench_for_type::<QuickUnionUf<UnionByRank>, _>(c, "quick_union::by_rank", inputs);
    bench_for_type::<QuickUnionUf<UnionBySizeRank>, _>(c, "quick_union::by_size_rank", inputs);
    bench_for_type::<QuickUnionUf<UnionByRankSize>, _>(c, "quick_union::by_rank_size", inputs);
    bench_for_type::<QuickFindUf<UnionBySize>, _>(c, "quick_find::by_size", inputs);
    bench_for_type::<QuickFindUf<UnionByRank>, _>(c, "quick_find::by_rank", inputs);
    bench_for_type::<QuickFindUf<UnionBySizeRank>, _>(c, "quick_find::by_size_rank", inputs);
    bench_for_type::<QuickFindUf<UnionByRankSize>, _>(c, "quick_find::by_rank_size", inputs);
}

criterion_group!(benches, bench);
criterion_main!(benches);
<commit_msg>reduce redundant benchmark test<commit_after>// Copyright 2016 union-find-rs Developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.

extern crate criterion;
extern crate lazy_static;
extern crate union_find;

use criterion::{criterion_group, criterion_main, Criterion};
use lazy_static::lazy_static;
use std::fs::File;
use std::io::{BufRead, BufReader};
use union_find::{
    QuickFindUf, QuickUnionUf, Union, UnionByRank, UnionByRankSize, UnionBySize, UnionBySizeRank,
    UnionFind,
};

lazy_static! {
    static ref TINY: Input = Input::from_file("tiny", "etc/tinyUF.txt");
    static ref MEDIUM: Input = Input::from_file("medium", "etc/mediumUF.txt");
    static ref LARGE: Input = Input::from_file("large", "etc/largeUF.txt");
}

struct Cache<'a, T> {
    input: &'a Input,
    init: Option<T>,
    union1: Option<T>,
    union2: Option<T>,
    find1: Option<T>,
    find2: Option<T>,
}

impl<'a, T> Cache<'a, T> {
    fn new(input: &'a Input) -> Cache<'a, T> {
        Cache {
            input,
            init: None,
            union1: None,
            union2: None,
            find1: None,
            find2: None,
        }
    }

    fn init<V>(&mut self) -> &T
    where
        T: UnionFind<V>,
        V: Union + Default,
    {
        if self.init.is_none() {
            self.init = Some(self.input.init());
        }
        self.init.as_ref().unwrap()
    }

    fn union1<V>(&mut self) -> &T
    where
        T: UnionFind<V> + Clone,
        V: Union + Default,
    {
        if self.union1.is_none() {
            let mut uf = self.init().clone();
            self.input.union(&mut uf);
            self.union1 = Some(uf);
        }
        self.union1.as_ref().unwrap()
    }

    fn union2<V>(&mut self) -> &T
    where
        T: UnionFind<V> + Clone,
        V: Union + Default,
    {
        if self.union2.is_none() {
            let mut uf = self.union1().clone();
            self.input.union(&mut uf);
            self.union2 = Some(uf);
        }
        self.union2.as_ref().unwrap()
    }

    fn find1<V>(&mut self) -> &T
    where
        T: UnionFind<V> + Clone,
        V: Union + Default,
    {
        if self.find1.is_none() {
            let mut uf = self.union1().clone();
            self.input.find_all(&mut uf);
            self.find1 = Some(uf);
        }
        self.find1.as_ref().unwrap()
    }

    fn find2<V>(&mut self) -> &T
    where
        T: UnionFind<V> + Clone,
        V: Union + Default,
    {
        if self.find2.is_none() {
            let mut uf = self.union1().clone();
            self.input.find_all(&mut uf);
            self.find2 = Some(uf);
        }
        self.find2.as_ref().unwrap()
    }
}

#[derive(Clone, Debug)]
struct Input {
    name: &'static str,
    size: usize,
    conn: Vec<(usize, usize)>,
}

impl Input {
    fn from_file(name: &'static str, file_name: &str) -> Input {
        let mut reader = BufReader::new(File::open(file_name).unwrap());
        let mut buf = String::new();

        let _ = reader.read_line(&mut buf).unwrap();
        let size = buf.trim().parse::<usize>().unwrap();
        buf.clear();

        let mut conn = vec![];

        while reader.read_line(&mut buf).unwrap() > 0 {
            {
                let mut sp = buf.split_whitespace();
                let a = sp.next().unwrap().parse::<usize>().unwrap();
                let b = sp.next().unwrap().parse::<usize>().unwrap();
                conn.push((a, b));
            }

            buf.clear();
        }

        Input { name, size, conn }
    }

    fn init<T, V>(&self) -> T
    where
        T: UnionFind<V>,
        V: Union + Default,
    {
        T::new(self.size)
    }

    fn union<T, V>(&self, uf: &mut T)
    where
        T: UnionFind<V>,
        V: Union,
    {
        for &(p, q) in &self.conn {
            uf.union(p, q);
        }
    }
    fn find_all<T, V>(&self, uf: &mut T)
    where
        T: UnionFind<V>,
        V: Union,
    {
        for i in 0..uf.size() {
            let _ = uf.find(i);
        }
    }

    fn bench_clone_from<T, V>(&self, c: &mut Criterion, category: &str, cache: &mut Cache<T>)
    where
        T: UnionFind<V> + Clone,
        V: Union + Default,
    {
        let id = format!("{}::{}::{}", category, self.name, "clone_from");
        let base = cache.init();
        let mut uf = base.clone();
        c.bench_function(&id, |b| {
            b.iter(|| {
                uf.clone_from(base);
            })
        });
    }

    fn bench_union1<T, V>(&self, c: &mut Criterion, category: &str, cache: &mut Cache<T>)
    where
        T: UnionFind<V> + Clone,
        V: Union + Default,
    {
        let id = format!("{}::{}::{}", category, self.name, "union1");
        let base = cache.init();
        let mut uf = base.clone();
        c.bench_function(&id, |b| {
            b.iter(|| {
                uf.clone_from(base);
                self.union(&mut uf);
            })
        });
    }
    fn bench_union2<T, V>(&self, c: &mut Criterion, category: &str, cache: &mut Cache<T>)
    where
        T: UnionFind<V> + Clone,
        V: Union + Default,
    {
        let id = format!("{}::{}::{}", category, self.name, "union2");
        let base = cache.union1();
        let mut uf = base.clone();
        c.bench_function(&id, |b| {
            b.iter(|| {
                uf.clone_from(base);
                self.union(&mut uf);
            })
        });
    }
    fn bench_union3<T, V>(&self, c: &mut Criterion, category: &str, cache: &mut Cache<T>)
    where
        T: UnionFind<V> + Clone,
        V: Union + Default,
    {
        let id = format!("{}::{}::{}", category, self.name, "union3");
        let base = cache.union2();
        let mut uf = base.clone();
        c.bench_function(&id, |b| {
            b.iter(|| {
                uf.clone_from(base);
                self.union(&mut uf);
            })
        });
    }

    fn bench_find1<T, V>(&self, c: &mut Criterion, category: &str, cache: &mut Cache<T>)
    where
        T: UnionFind<V> + Clone,
        V: Union + Default,
    {
        let id = format!("{}::{}::{}", category, self.name, "find1");
        let base = cache.union1();
        let mut uf = base.clone();
        c.bench_function(&id, |b| {
            b.iter(|| {
                uf.clone_from(base);
                self.find_all(&mut uf);
            })
        });
    }
    fn bench_find2<T, V>(&self, c: &mut Criterion, category: &str, cache: &mut Cache<T>)
    where
        T: UnionFind<V> + Clone,
        V: Union + Default,
    {
        let id = format!("{}::{}::{}", category, self.name, "find2");
        let base = cache.find1();
        let mut uf = base.clone();
        c.bench_function(&id, |b| {
            b.iter(|| {
                uf.clone_from(base);
                self.find_all(&mut uf);
            })
        });
    }
    fn bench_find3<T, V>(&self, c: &mut Criterion, category: &str, cache: &mut Cache<T>)
    where
        T: UnionFind<V> + Clone,
        V: Union + Default,
    {
        let id = format!("{}::{}::{}", category, self.name, "find3");
        let base = cache.find2();
        let mut uf = base.clone();
        c.bench_function(&id, |b| {
            b.iter(|| {
                uf.clone_from(base);
                self.find_all(&mut uf);
            })
        });
    }

    fn bench_union<T, V>(&self, c: &mut Criterion, category: &str)
    where
        T: UnionFind<V> + Clone,
        V: Union + Default,
    {
        let mut cache = Cache::<T>::new(self);
        self.bench_union1(c, category, &mut cache);
    }

    fn bench_full<T, V>(&self, c: &mut Criterion, category: &str)
    where
        T: UnionFind<V> + Clone,
        V: Union + Default,
    {
        let mut cache = Cache::<T>::new(self);
        self.bench_clone_from(c, category, &mut cache);
        self.bench_union1(c, category, &mut cache);
        self.bench_union2(c, category, &mut cache);
        self.bench_union3(c, category, &mut cache);
        self.bench_find1(c, category, &mut cache);
        self.bench_find2(c, category, &mut cache);
        self.bench_find3(c, category, &mut cache);
    }
}

fn bench(c: &mut Criterion) {
    let tiny = Input::from_file("tiny", "etc/tinyUF.txt");
    let medium = Input::from_file("medium", "etc/mediumUF.txt");
    let large = Input::from_file("large", "etc/largeUF.txt");

    for input in &[&tiny, &medium, &large] {
        input.bench_full::<QuickUnionUf<UnionBySize>, _>(c, "quick_union");
        input.bench_full::<QuickFindUf<UnionBySize>, _>(c, "quick_find");
    }

    {
        let input = &tiny;
        input.bench_union::<QuickUnionUf<UnionBySize>, _>(c, "by_size");
        input.bench_union::<QuickUnionUf<UnionByRank>, _>(c, "by_rank");
        input.bench_union::<QuickUnionUf<UnionBySizeRank>, _>(c, "by_size_rank");
        input.bench_union::<QuickUnionUf<UnionByRankSize>, _>(c, "by_rank_size");
    }
}

criterion_group!(benches, bench);
criterion_main!(benches);
<|endoftext|>


<commit_before>#![feature(test)]
#![feature(step_by)]
extern crate raw_nbits_vec;
extern crate test;
extern crate num;

#[cfg(test)]
mod tests {
    macro_rules! bench_test {
        ($(($m: ident, $nbits: ident, $storage: ident)),*) => {
            mod get {
                $(
                    mod $m {
                        use raw_nbits_vec::*;
                        use test::{self, Bencher};
                        #[bench]
                        fn bench(b: &mut Bencher) {
                            let n = test::black_box(1000);
                            let mut vec: NbitsVec<$nbits, $storage> = NbitsVec::with_capacity(n);
                            unsafe { vec.set_len(n) }
                            b.iter(|| {
                                (0..n).fold(0, |_a, i| vec.get(i));
                            })
                        }
                    }
                )*
            }
            mod resize {
                $(
                    mod $m {
                        use raw_nbits_vec::*;
                        use test::{self, Bencher};
                        use num::Zero;
                        #[bench]
                        fn bench(b: &mut Bencher) {
                            b.iter(|| {
                                let n = test::black_box(1000);
                                let mut vec: NbitsVec<$nbits, $storage> = NbitsVec::new();
                                for i in (0..n).step_by(10) {
                                    vec.resize(i, $storage::zero());
                                }
                            })
                        }
                    }
                )*
            }
            mod set {
                $(
                    mod $m {
                        use raw_nbits_vec::*;
                        use test::{self, Bencher};
                        #[bench]
                        fn set(b: &mut Bencher) {
                            b.iter(|| {
                                let n = test::black_box(100);
                                let mut vec: NbitsVec<As1bits> = NbitsVec::with_capacity(n);
                                unsafe { vec.set_len(n) };
                                for i in 0..n {
                                    vec.set(i, i);
                                }
                            });
                        }
                    }
                )*
            }
            mod push {
                $(
                    mod $m {
                        use raw_nbits_vec::*;
                        use test::{self, Bencher};
                        #[bench]
                        fn set(b: &mut Bencher) {
                            b.iter(|| {
                                let n = test::black_box(100);
                                let mut vec: NbitsVec<As1bits> = NbitsVec::with_capacity(n);
                                for i in 0..n {
                                    vec.push(i)
                                }
                            });
                        }
                    }
                 )*
            }
            /*
            mod fill {
                $(
                    mod $m {
                        use raw_nbits_vec::*;
                        use test::{self, Bencher};
                        #[bench]
                        fn bench(b: &mut Bencher) {
                            b.iter(|| {
                                let n = test::black_box(100);
                                let mut vec: NbitsVec<As1bits> = NbitsVec::with_capacity(n);
                                unsafe { vec.set_len(n) };
                                vec.fill_bits(0, n, false);
                            });
                        }
                    }
                )*
            }
            */
        }
    }
    bench_test! {
        (test_4bits_usize, As4bits, usize),
        (test_4bits_u64, As4bits, u64),
        (test_4bits_u32, As4bits, u32),
        (test_4bits_u16, As4bits, u16),
        (test_4bits_u8, As4bits, u8),
        (test_3bits_usize, As3bits, usize),
        (test_3bits_u64, As3bits, u64),
        (test_3bits_u32, As3bits, u32),
        (test_3bits_u16, As3bits, u16),
        (test_3bits_u8, As3bits, u8),
        (test_2bits_usize, As2bits, usize),
        (test_2bits_u64, As2bits, u64),
        (test_2bits_u32, As2bits, u32),
        (test_2bits_u16, As2bits, u16),
        (test_2bits_u8, As2bits, u8),
        (test_1bits_usize, As1bits, usize),
        (test_1bits_u64, As1bits, u64),
        (test_1bits_u32, As1bits, u32),
        (test_1bits_u16, As1bits, u16),
        (test_1bits_u8, As1bits, u8)
    }
}
<commit_msg>Changed in benches code<commit_after>#![feature(test)]
#![feature(step_by)]
extern crate raw_nbits_vec;
extern crate test;
extern crate num;

mod tests {
    macro_rules! bench_test {
        ($(($m: ident, $nbits: ident, $storage: ident)),*) => {
            mod get {
                $(
                    mod $m {
                        use raw_nbits_vec::*;
                        use test::{self, Bencher};
                        #[bench]
                        fn bench(b: &mut Bencher) {
                            let n = test::black_box(100);
                            let mut vec: NbitsVec<$nbits, $storage> = NbitsVec::with_capacity(n);
                            unsafe { vec.set_len(n) }
                            b.iter(|| {
                                (0..n).fold(0, |_a, i| vec.get(i));
                            })
                        }
                    }
                )*
            }
            mod resize {
                $(
                    mod $m {
                        use raw_nbits_vec::*;
                        use test::{self, Bencher};
                        use num::Zero;
                        #[bench]
                        fn bench(b: &mut Bencher) {
                            let n = test::black_box(100);
                            let mut vec: NbitsVec<$nbits, $storage> = NbitsVec::new();
                            b.iter(|| {
                                for i in (0..n).step_by(10) {
                                    vec.resize(i, $storage::zero());
                                }
                            })
                        }
                    }
                )*
            }
            mod set {
                $(
                    mod $m {
                        use raw_nbits_vec::*;
                        use test::{self, Bencher};
                        #[bench]
                        fn bench(b: &mut Bencher) {
                            b.iter(|| {
                                let n = test::black_box(100);
                                let mut vec: NbitsVec<As1bits> = NbitsVec::with_capacity(n);
                                unsafe { vec.set_len(n) };
                                for i in 0..n {
                                    vec.set(i, i);
                                }
                            });
                        }
                    }
                )*
            }
            mod new {
                $(
                    mod $m {
                        use raw_nbits_vec::*;
                        use test::{self, Bencher};
                        #[bench]
                        fn bench(b: &mut Bencher) {
                            b.iter(|| {
                                let n = test::black_box(100);
                                let vec: NbitsVec<As1bits> = NbitsVec::with_capacity(n);
                                vec
                            });
                        }
                    }
                 )*
            }
            mod push {
                $(
                    mod $m {
                        use raw_nbits_vec::*;
                        use test::{self, Bencher};
                        #[bench]
                        fn set(b: &mut Bencher) {
                            b.iter(|| {
                                let n = test::black_box(100);
                                let mut vec: NbitsVec<As1bits> = NbitsVec::with_capacity(n);
                                for i in 0..n {
                                    vec.push(i)
                                }
                            });
                        }
                    }
                 )*
            }
        }
    }
    bench_test! {
        (test_4bits_usize, As4bits, usize),
        (test_4bits_u64, As4bits, u64),
        (test_4bits_u32, As4bits, u32),
        (test_4bits_u16, As4bits, u16),
        (test_4bits_u8, As4bits, u8),
        (test_3bits_usize, As3bits, usize),
        (test_3bits_u64, As3bits, u64),
        (test_3bits_u32, As3bits, u32),
        (test_3bits_u16, As3bits, u16),
        (test_3bits_u8, As3bits, u8),
        (test_2bits_usize, As2bits, usize),
        (test_2bits_u64, As2bits, u64),
        (test_2bits_u32, As2bits, u32),
        (test_2bits_u16, As2bits, u16),
        (test_2bits_u8, As2bits, u8),
        (test_1bits_usize, As1bits, usize),
        (test_1bits_u64, As1bits, u64),
        (test_1bits_u32, As1bits, u32),
        (test_1bits_u16, As1bits, u16),
        (test_1bits_u8, As1bits, u8)
    }
}
<|endoftext|>


<commit_before>use hir::db::HirDatabase;
use ra_syntax::{
    ast::{self, AstNode},
    TextUnit,
    SyntaxKind::{
        L_PAREN, R_PAREN, L_CURLY, R_CURLY, L_BRACK, R_BRACK, EXCL
    },
};
use crate::{AssistCtx, Assist};

pub(crate) fn remove_dbg(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
    let macro_call = ctx.node_at_offset::<ast::MacroCall>()?;

    if !is_valid_macrocall(macro_call, "dbg")? {
        return None;
    }

    let macro_range = macro_call.syntax().range();

    // If the cursor is inside the macrocall, we'll try to maintain
    // the cursor position by subtracting the length of dbg!( from the start
    // of the filerange, otherwise we'll default to using the start of the macrocall
    let cursor_pos = {
        let file_range = ctx.frange.range;

        let offset_start = file_range
            .start()
            .checked_sub(macro_range.start())
            .unwrap_or_else(|| TextUnit::from(0));

        let dbg_size = dbg!(TextUnit::of_str("dbg!("));

        if offset_start > dbg_size {
            file_range.start() - dbg_size
        } else {
            macro_range.start()
        }
    };

    let macro_content = {
        let macro_args = macro_call.token_tree()?.syntax();
        let _ = dbg!(macro_args.text());
        let range = dbg!(macro_args.range());
        let start = range.start() + TextUnit::of_char('(');
        let end = range.end() - TextUnit::of_char(')');

        macro_args.text().slice(start..end).to_string()
    };

    ctx.build("remove dbg!()", |edit| {
        edit.replace(macro_range, macro_content);
        edit.set_cursor(cursor_pos);
    })
}

/// Verifies that the given macro_call actually matches the given name
/// and contains proper ending tokens
fn is_valid_macrocall(macro_call: &ast::MacroCall, macro_name: &str) -> Option<bool> {
    let path = macro_call.path()?;
    let name_ref = path.segment()?.name_ref()?;

    // Make sure it is actually a dbg-macrocall, dbg followed by !
    let excl = path.syntax().next_sibling()?;

    if name_ref.text() != macro_name || excl.kind() != EXCL {
        return None;
    }

    let node = macro_call.token_tree()?.syntax();
    let first_child = node.first_child()?;
    let last_child = node.last_child()?;

    match (first_child.kind(), last_child.kind()) {
        (L_PAREN, R_PAREN) | (L_BRACK, R_BRACK) | (L_CURLY, R_CURLY) => Some(true),
        _ => Some(false),
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::helpers::{check_assist, check_assist_not_applicable};

    #[test]
    fn test_remove_dbg() {
        check_assist(remove_dbg, "<|>dbg!(1 + 1)", "<|>1 + 1");

        check_assist(remove_dbg, "dbg!<|>((1 + 1))", "<|>(1 + 1)");

        check_assist(remove_dbg, "dbg!(1 <|>+ 1)", "1 <|>+ 1");

        check_assist(remove_dbg, "let _ = <|>dbg!(1 + 1)", "let _ = <|>1 + 1");

        check_assist(
            remove_dbg,
            "
fn foo(n: usize) {
    if let Some(_) = dbg!(n.<|>checked_sub(4)) {
        // ...
    }
}
",
            "
fn foo(n: usize) {
    if let Some(_) = n.<|>checked_sub(4) {
        // ...
    }
}
",
        );
    }
    #[test]
    fn test_remove_dbg_with_brackets_and_braces() {
        check_assist(remove_dbg, "dbg![<|>1 + 1]", "<|>1 + 1");
        check_assist(remove_dbg, "dbg!{<|>1 + 1}", "<|>1 + 1");
    }

    #[test]
    fn test_remove_dbg_not_applicable() {
        check_assist_not_applicable(remove_dbg, "<|>vec![1, 2, 3]");
        check_assist_not_applicable(remove_dbg, "<|>dbg(5, 6, 7)");
        check_assist_not_applicable(remove_dbg, "<|>dbg!(5, 6, 7");
    }
}
<commit_msg>Remove unnecessary dbg! calls<commit_after>use hir::db::HirDatabase;
use ra_syntax::{
    ast::{self, AstNode},
    TextUnit,
    SyntaxKind::{
        L_PAREN, R_PAREN, L_CURLY, R_CURLY, L_BRACK, R_BRACK, EXCL
    },
};
use crate::{AssistCtx, Assist};

pub(crate) fn remove_dbg(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
    let macro_call = ctx.node_at_offset::<ast::MacroCall>()?;

    if !is_valid_macrocall(macro_call, "dbg")? {
        return None;
    }

    let macro_range = macro_call.syntax().range();

    // If the cursor is inside the macrocall, we'll try to maintain
    // the cursor position by subtracting the length of dbg!( from the start
    // of the filerange, otherwise we'll default to using the start of the macrocall
    let cursor_pos = {
        let file_range = ctx.frange.range;

        let offset_start = file_range
            .start()
            .checked_sub(macro_range.start())
            .unwrap_or_else(|| TextUnit::from(0));

        let dbg_size = TextUnit::of_str("dbg!(");

        if offset_start > dbg_size {
            file_range.start() - dbg_size
        } else {
            macro_range.start()
        }
    };

    let macro_content = {
        let macro_args = macro_call.token_tree()?.syntax();
        let range = macro_args.range();
        let start = range.start() + TextUnit::of_char('(');
        let end = range.end() - TextUnit::of_char(')');

        macro_args.text().slice(start..end).to_string()
    };

    ctx.build("remove dbg!()", |edit| {
        edit.replace(macro_range, macro_content);
        edit.set_cursor(cursor_pos);
    })
}

/// Verifies that the given macro_call actually matches the given name
/// and contains proper ending tokens
fn is_valid_macrocall(macro_call: &ast::MacroCall, macro_name: &str) -> Option<bool> {
    let path = macro_call.path()?;
    let name_ref = path.segment()?.name_ref()?;

    // Make sure it is actually a dbg-macrocall, dbg followed by !
    let excl = path.syntax().next_sibling()?;

    if name_ref.text() != macro_name || excl.kind() != EXCL {
        return None;
    }

    let node = macro_call.token_tree()?.syntax();
    let first_child = node.first_child()?;
    let last_child = node.last_child()?;

    match (first_child.kind(), last_child.kind()) {
        (L_PAREN, R_PAREN) | (L_BRACK, R_BRACK) | (L_CURLY, R_CURLY) => Some(true),
        _ => Some(false),
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::helpers::{check_assist, check_assist_not_applicable};

    #[test]
    fn test_remove_dbg() {
        check_assist(remove_dbg, "<|>dbg!(1 + 1)", "<|>1 + 1");

        check_assist(remove_dbg, "dbg!<|>((1 + 1))", "<|>(1 + 1)");

        check_assist(remove_dbg, "dbg!(1 <|>+ 1)", "1 <|>+ 1");

        check_assist(remove_dbg, "let _ = <|>dbg!(1 + 1)", "let _ = <|>1 + 1");

        check_assist(
            remove_dbg,
            "
fn foo(n: usize) {
    if let Some(_) = dbg!(n.<|>checked_sub(4)) {
        // ...
    }
}
",
            "
fn foo(n: usize) {
    if let Some(_) = n.<|>checked_sub(4) {
        // ...
    }
}
",
        );
    }
    #[test]
    fn test_remove_dbg_with_brackets_and_braces() {
        check_assist(remove_dbg, "dbg![<|>1 + 1]", "<|>1 + 1");
        check_assist(remove_dbg, "dbg!{<|>1 + 1}", "<|>1 + 1");
    }

    #[test]
    fn test_remove_dbg_not_applicable() {
        check_assist_not_applicable(remove_dbg, "<|>vec![1, 2, 3]");
        check_assist_not_applicable(remove_dbg, "<|>dbg(5, 6, 7)");
        check_assist_not_applicable(remove_dbg, "<|>dbg!(5, 6, 7");
    }
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

//! Utilities for reporting errors to an LSP client
use crate::lsp::{
    publish_diagnostic, Diagnostic, DiagnosticSeverity, Position, PublishDiagnosticsParams, Range,
    Url,
};
use common::{Diagnostic as CompilerDiagnostic, Location};
use crossbeam::crossbeam_channel::Sender;
use dashmap::mapref::entry::Entry;
use dashmap::DashMap;
use lsp_server::Message;
use relay_compiler::{
    errors::{BuildProjectError, Error},
    source_for_location, FsSourceReader, SourceReader,
};
use std::path::PathBuf;

/// Converts a Location to a Url pointing to the canonical path based on the root_dir provided.
/// Returns None if we are unable to do the conversion
fn url_from_location(location: Location, root_dir: &PathBuf) -> Option<Url> {
    let file_path = location.source_location().path();
    let canonical_path = std::fs::canonicalize(root_dir.join(file_path)).ok()?;
    Url::from_file_path(canonical_path).ok()
}

#[derive(Default)]
struct DiagnosticSet {
    /// Stores the diagnostics from IDE source, which will be updated on any text change
    quick_diagnostics: Vec<Diagnostic>,
    /// Stores the diagnostics from watchman source, which will be updated on file save
    regular_diagnostics: Vec<Diagnostic>,
}

pub struct DiagnosticReporter {
    active_diagnostics: DashMap<Url, DiagnosticSet>,
    sender: Sender<Message>,
    root_dir: PathBuf,
    source_reader: Box<dyn SourceReader + Send + Sync>,
}

impl DiagnosticReporter {
    pub fn new(root_dir: PathBuf, sender: Sender<Message>) -> Self {
        Self {
            active_diagnostics: Default::default(),
            sender,
            root_dir,
            source_reader: Box::new(FsSourceReader),
        }
    }

    pub fn clear_regular_diagnostics(&self) {
        for mut r in self.active_diagnostics.iter_mut() {
            let (url, diagnostics) = r.pair_mut();
            diagnostics.regular_diagnostics.clear();
            self.publish_diangostics_set(url, diagnostics);
        }
        self.active_diagnostics.retain(|_, diagnostics| {
            !diagnostics.regular_diagnostics.is_empty() || !diagnostics.quick_diagnostics.is_empty()
        })
    }

    pub fn report_error(&self, error: &Error) {
        match error {
            Error::DiagnosticsError { errors } => {
                for diagnostic in errors {
                    self.report_diagnostic(diagnostic);
                }
            }
            Error::BuildProjectsErrors { errors } => {
                for error in errors {
                    self.print_project_error(error);
                }
            }
            Error::Cancelled => {
                // Ignore the cancellation
            }
            error => {
                self.print_generic_error(format!("{}", error));
            }
        }
    }

    pub fn commit_diagnostics(&self) {
        for r in self.active_diagnostics.iter() {
            let (url, diagnostics) = r.pair();
            self.publish_diangostics_set(url, diagnostics)
        }
    }

    fn add_diagnostic(&self, url: Url, diagnostic: Diagnostic) {
        self.active_diagnostics
            .entry(url)
            .or_default()
            .regular_diagnostics
            .push(diagnostic);
    }

    pub fn update_quick_diagnostics_for_url(&self, url: Url, diagnostics: Vec<Diagnostic>) {
        match self.active_diagnostics.entry(url.clone()) {
            Entry::Occupied(mut e) => {
                let data = e.get_mut();
                if data.quick_diagnostics != diagnostics {
                    data.quick_diagnostics = diagnostics;
                    self.publish_diangostics_set(&url, data);
                }
            }
            Entry::Vacant(e) => {
                if !diagnostics.is_empty() {
                    let data = DiagnosticSet {
                        regular_diagnostics: vec![],
                        quick_diagnostics: diagnostics,
                    };
                    self.publish_diangostics_set(&url, &data);
                    e.insert(data);
                }
            }
        }
    }

    pub fn clear_quick_diagnostics_for_url(&self, url: &Url) {
        if let Some(mut diagnostics) = self.active_diagnostics.get_mut(url) {
            if !diagnostics.quick_diagnostics.is_empty() {
                diagnostics.quick_diagnostics.clear();
                self.publish_diangostics_set(url, &diagnostics)
            }
        }
    }

    fn publish_diangostics_set(&self, url: &Url, diagnostics: &DiagnosticSet) {
        let mut next_diagnostics = diagnostics.quick_diagnostics.clone();
        for diagnostic in &diagnostics.regular_diagnostics {
            if !next_diagnostics
                .iter()
                .any(|prev_diag| prev_diag.eq(&diagnostic))
            {
                next_diagnostics.push(diagnostic.clone());
            }
        }
        let params = PublishDiagnosticsParams {
            diagnostics: next_diagnostics,
            uri: url.clone(),
            version: None,
        };
        publish_diagnostic(params, &self.sender).ok();
    }

    #[cfg(test)]
    fn set_source_reader(&mut self, source_reader: Box<dyn SourceReader + Send + Sync>) {
        self.source_reader = source_reader;
    }

    fn report_diagnostic(&self, diagnostic: &CompilerDiagnostic) {
        let message = diagnostic.message().to_string();

        let location = diagnostic.location();

        let url = match url_from_location(location, &self.root_dir) {
            Some(url) => url,
            None => {
                // TODO(brandondail) we should always be able to parse as a Url, so log here when we don't
                if let Ok(root) = Url::from_directory_path(&self.root_dir) {
                    root
                } else {
                    return;
                }
            }
        };

        let source = if let Some(source) = source_for_location(
            &self.root_dir,
            location.source_location(),
            self.source_reader.as_ref(),
        ) {
            source
        } else {
            return;
        };

        let range = location
            .span()
            .to_range(&source.text, source.line_index, source.column_index);

        let diagnostic = Diagnostic {
            code: None,
            message,
            range,
            related_information: None,
            severity: Some(DiagnosticSeverity::Error),
            source: None,
            tags: None,
        };
        self.add_diagnostic(url, diagnostic);
    }

    fn print_project_error(&self, error: &BuildProjectError) {
        match error {
            BuildProjectError::ValidationErrors { errors } => {
                for diagnostic in errors {
                    self.report_diagnostic(diagnostic);
                }
            }
            BuildProjectError::PersistErrors { errors } => {
                for error in errors {
                    self.print_generic_error(format!("{}", error));
                }
            }
            _ => {
                self.print_generic_error(format!("{}", error));
            }
        }
    }

    fn print_generic_error(&self, message: String) {
        let diagnostic = Diagnostic {
            code: None,
            message,
            range: Range::new(Position::new(0, 0), Position::new(0, 0)),
            related_information: None,
            severity: Some(DiagnosticSeverity::Error),
            source: None,
            tags: None,
        };
        let url = Url::from_directory_path(&self.root_dir)
            .expect("print_generic_error: Could not convert self.root_dir to Url");
        self.add_diagnostic(url, diagnostic);
    }
}

#[cfg(test)]
mod tests {
    use super::DiagnosticReporter;
    use common::{Diagnostic, Location, SourceLocationKey, Span};
    use crossbeam::crossbeam_channel;
    use interner::Intern;
    use relay_compiler::SourceReader;
    use std::path::PathBuf;

    struct MockSourceReader(String);

    impl SourceReader for MockSourceReader {
        fn read_to_string(&self, _path: &PathBuf) -> std::io::Result<String> {
            Ok(self.0.to_string())
        }
    }

    #[test]
    fn report_diagnostic_test() {
        let root_dir = PathBuf::from("/tmp");
        let (sender, _) = crossbeam_channel::unbounded();
        let mut reporter = DiagnosticReporter::new(root_dir, sender);
        reporter.set_source_reader(Box::new(MockSourceReader("Content".to_string())));
        let source_location = SourceLocationKey::Standalone {
            path: "foo.txt".intern(),
        };
        assert_eq!(reporter.active_diagnostics.len(), 0);
        reporter.report_diagnostic(&Diagnostic::error(
            "test message",
            Location::new(source_location, Span { start: 0, end: 1 }),
        ));
        assert_eq!(reporter.active_diagnostics.len(), 1);
    }

    /// This test will assert that the message without URL (with generated source) won't be reported by LSPStatusReporter
    /// I'm not sure if this is the right behavior, but lets capture it here.
    #[test]
    fn do_not_report_diagnostic_without_url_test() {
        let root_dir = PathBuf::from("/tmp");
        let (sender, _) = crossbeam_channel::unbounded();

        let mut reporter = DiagnosticReporter::new(root_dir, sender);
        reporter.set_source_reader(Box::new(MockSourceReader("".to_string())));

        reporter.report_diagnostic(&Diagnostic::error("-", Location::generated()));
        assert_eq!(reporter.active_diagnostics.len(), 0);
    }
}
<commit_msg>Fix typo<commit_after>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

//! Utilities for reporting errors to an LSP client
use crate::lsp::{
    publish_diagnostic, Diagnostic, DiagnosticSeverity, Position, PublishDiagnosticsParams, Range,
    Url,
};
use common::{Diagnostic as CompilerDiagnostic, Location};
use crossbeam::crossbeam_channel::Sender;
use dashmap::mapref::entry::Entry;
use dashmap::DashMap;
use lsp_server::Message;
use relay_compiler::{
    errors::{BuildProjectError, Error},
    source_for_location, FsSourceReader, SourceReader,
};
use std::path::PathBuf;

/// Converts a Location to a Url pointing to the canonical path based on the root_dir provided.
/// Returns None if we are unable to do the conversion
fn url_from_location(location: Location, root_dir: &PathBuf) -> Option<Url> {
    let file_path = location.source_location().path();
    let canonical_path = std::fs::canonicalize(root_dir.join(file_path)).ok()?;
    Url::from_file_path(canonical_path).ok()
}

#[derive(Default)]
struct DiagnosticSet {
    /// Stores the diagnostics from IDE source, which will be updated on any text change
    quick_diagnostics: Vec<Diagnostic>,
    /// Stores the diagnostics from watchman source, which will be updated on file save
    regular_diagnostics: Vec<Diagnostic>,
}

pub struct DiagnosticReporter {
    active_diagnostics: DashMap<Url, DiagnosticSet>,
    sender: Sender<Message>,
    root_dir: PathBuf,
    source_reader: Box<dyn SourceReader + Send + Sync>,
}

impl DiagnosticReporter {
    pub fn new(root_dir: PathBuf, sender: Sender<Message>) -> Self {
        Self {
            active_diagnostics: Default::default(),
            sender,
            root_dir,
            source_reader: Box::new(FsSourceReader),
        }
    }

    pub fn clear_regular_diagnostics(&self) {
        for mut r in self.active_diagnostics.iter_mut() {
            let (url, diagnostics) = r.pair_mut();
            diagnostics.regular_diagnostics.clear();
            self.publish_diagnostics_set(url, diagnostics);
        }
        self.active_diagnostics.retain(|_, diagnostics| {
            !diagnostics.regular_diagnostics.is_empty() || !diagnostics.quick_diagnostics.is_empty()
        })
    }

    pub fn report_error(&self, error: &Error) {
        match error {
            Error::DiagnosticsError { errors } => {
                for diagnostic in errors {
                    self.report_diagnostic(diagnostic);
                }
            }
            Error::BuildProjectsErrors { errors } => {
                for error in errors {
                    self.print_project_error(error);
                }
            }
            Error::Cancelled => {
                // Ignore the cancellation
            }
            error => {
                self.print_generic_error(format!("{}", error));
            }
        }
    }

    pub fn commit_diagnostics(&self) {
        for r in self.active_diagnostics.iter() {
            let (url, diagnostics) = r.pair();
            self.publish_diagnostics_set(url, diagnostics)
        }
    }

    fn add_diagnostic(&self, url: Url, diagnostic: Diagnostic) {
        self.active_diagnostics
            .entry(url)
            .or_default()
            .regular_diagnostics
            .push(diagnostic);
    }

    pub fn update_quick_diagnostics_for_url(&self, url: Url, diagnostics: Vec<Diagnostic>) {
        match self.active_diagnostics.entry(url.clone()) {
            Entry::Occupied(mut e) => {
                let data = e.get_mut();
                if data.quick_diagnostics != diagnostics {
                    data.quick_diagnostics = diagnostics;
                    self.publish_diagnostics_set(&url, data);
                }
            }
            Entry::Vacant(e) => {
                if !diagnostics.is_empty() {
                    let data = DiagnosticSet {
                        regular_diagnostics: vec![],
                        quick_diagnostics: diagnostics,
                    };
                    self.publish_diagnostics_set(&url, &data);
                    e.insert(data);
                }
            }
        }
    }

    pub fn clear_quick_diagnostics_for_url(&self, url: &Url) {
        if let Some(mut diagnostics) = self.active_diagnostics.get_mut(url) {
            if !diagnostics.quick_diagnostics.is_empty() {
                diagnostics.quick_diagnostics.clear();
                self.publish_diagnostics_set(url, &diagnostics)
            }
        }
    }

    fn publish_diagnostics_set(&self, url: &Url, diagnostics: &DiagnosticSet) {
        let mut next_diagnostics = diagnostics.quick_diagnostics.clone();
        for diagnostic in &diagnostics.regular_diagnostics {
            if !next_diagnostics
                .iter()
                .any(|prev_diag| prev_diag.eq(&diagnostic))
            {
                next_diagnostics.push(diagnostic.clone());
            }
        }
        let params = PublishDiagnosticsParams {
            diagnostics: next_diagnostics,
            uri: url.clone(),
            version: None,
        };
        publish_diagnostic(params, &self.sender).ok();
    }

    #[cfg(test)]
    fn set_source_reader(&mut self, source_reader: Box<dyn SourceReader + Send + Sync>) {
        self.source_reader = source_reader;
    }

    fn report_diagnostic(&self, diagnostic: &CompilerDiagnostic) {
        let message = diagnostic.message().to_string();

        let location = diagnostic.location();

        let url = match url_from_location(location, &self.root_dir) {
            Some(url) => url,
            None => {
                // TODO(brandondail) we should always be able to parse as a Url, so log here when we don't
                if let Ok(root) = Url::from_directory_path(&self.root_dir) {
                    root
                } else {
                    return;
                }
            }
        };

        let source = if let Some(source) = source_for_location(
            &self.root_dir,
            location.source_location(),
            self.source_reader.as_ref(),
        ) {
            source
        } else {
            return;
        };

        let range = location
            .span()
            .to_range(&source.text, source.line_index, source.column_index);

        let diagnostic = Diagnostic {
            code: None,
            message,
            range,
            related_information: None,
            severity: Some(DiagnosticSeverity::Error),
            source: None,
            tags: None,
        };
        self.add_diagnostic(url, diagnostic);
    }

    fn print_project_error(&self, error: &BuildProjectError) {
        match error {
            BuildProjectError::ValidationErrors { errors } => {
                for diagnostic in errors {
                    self.report_diagnostic(diagnostic);
                }
            }
            BuildProjectError::PersistErrors { errors } => {
                for error in errors {
                    self.print_generic_error(format!("{}", error));
                }
            }
            _ => {
                self.print_generic_error(format!("{}", error));
            }
        }
    }

    fn print_generic_error(&self, message: String) {
        let diagnostic = Diagnostic {
            code: None,
            message,
            range: Range::new(Position::new(0, 0), Position::new(0, 0)),
            related_information: None,
            severity: Some(DiagnosticSeverity::Error),
            source: None,
            tags: None,
        };
        let url = Url::from_directory_path(&self.root_dir)
            .expect("print_generic_error: Could not convert self.root_dir to Url");
        self.add_diagnostic(url, diagnostic);
    }
}

#[cfg(test)]
mod tests {
    use super::DiagnosticReporter;
    use common::{Diagnostic, Location, SourceLocationKey, Span};
    use crossbeam::crossbeam_channel;
    use interner::Intern;
    use relay_compiler::SourceReader;
    use std::path::PathBuf;

    struct MockSourceReader(String);

    impl SourceReader for MockSourceReader {
        fn read_to_string(&self, _path: &PathBuf) -> std::io::Result<String> {
            Ok(self.0.to_string())
        }
    }

    #[test]
    fn report_diagnostic_test() {
        let root_dir = PathBuf::from("/tmp");
        let (sender, _) = crossbeam_channel::unbounded();
        let mut reporter = DiagnosticReporter::new(root_dir, sender);
        reporter.set_source_reader(Box::new(MockSourceReader("Content".to_string())));
        let source_location = SourceLocationKey::Standalone {
            path: "foo.txt".intern(),
        };
        assert_eq!(reporter.active_diagnostics.len(), 0);
        reporter.report_diagnostic(&Diagnostic::error(
            "test message",
            Location::new(source_location, Span { start: 0, end: 1 }),
        ));
        assert_eq!(reporter.active_diagnostics.len(), 1);
    }

    /// This test will assert that the message without URL (with generated source) won't be reported by LSPStatusReporter
    /// I'm not sure if this is the right behavior, but lets capture it here.
    #[test]
    fn do_not_report_diagnostic_without_url_test() {
        let root_dir = PathBuf::from("/tmp");
        let (sender, _) = crossbeam_channel::unbounded();

        let mut reporter = DiagnosticReporter::new(root_dir, sender);
        reporter.set_source_reader(Box::new(MockSourceReader("".to_string())));

        reporter.report_diagnostic(&Diagnostic::error("-", Location::generated()));
        assert_eq!(reporter.active_diagnostics.len(), 0);
    }
}
<|endoftext|>