<commit_before>/*
    TODO(ceg): provide the goto-line,goto-offset functions

    We want:

    An Updated collection of metadata that tracks number of newlines in the document sub-blocks

    The root contains the total count of newlines

    NB: This is tied to the document implementation

                 (root)
                   |
               [ 3 + 6 ]
             /          \
        [ 1 + 2 ]     [ 2 + 4 ]
       /        \    /        \
    [1]        [2]  [2]       [4]

    each time a node is indexed
    the document impl must call update hierarchy with the build metadata diff

    the mode subscribe to the document events

    When a node is indexed/added/removed, the document notify us
    Then we build the node metadata
    and ask to update the hierarchy.

    To be fully async we must: have a shadowed tree that matched the document internal representation ?
    and keep a per node doc_revision

    Must we re-index before remove ?
*/

use std::any::Any;

use parking_lot::RwLock;

use std::cell::RefCell;
use std::rc::Rc;
use std::sync::Arc;

use super::Mode;

use crate::core::codepointinfo::CodepointInfo;
use crate::core::editor::InputStageActionMap;
use crate::core::Editor;
use crate::core::EditorEnv;

use crate::core::document;
use crate::core::document::Document;
use crate::core::document::DocumentEvent;
use crate::core::document::DocumentEventCb;

use crate::core::mapped_file::MappedFile;
use crate::core::mapped_file::MappedFileIterator;

use crate::core::view::layout::LayoutEnv;
use crate::core::view::layout::ScreenOverlayFilter;

use crate::core::view;
use crate::core::view::View;
use crate::core::view::ViewEvent;
use crate::core::view::ViewEventDestination;
use crate::core::view::ViewEventSource;

use lazy_static::lazy_static;
use std::collections::HashMap;
// document meta data map
lazy_static! {
    static ref DOC_METADATA_MAP: Arc<RwLock<HashMap<document::Id, RwLock<LineNumberDocumentMetaData>>>> =
        Arc::new(RwLock::new(HashMap::new()));

        // document::Id -> (doc, LineNumberDocumentMetaData)
}

struct LineNumberDocumentMetaData {
    cb_installed: bool,
    root_idx: Option<usize>,
}

impl LineNumberDocumentMetaData {
    pub fn new() -> Self {
        dbg_println!("LineNumberDocumentMetaData");
        LineNumberDocumentMetaData {
            cb_installed: false,
            root_idx: None,
        }
    }
}

struct LineNumberDocumentNodeMetaData {
    nl_count: u64,
    cr_count: u64,
    lf_count: u64,
}

impl LineNumberDocumentNodeMetaData {
    pub fn new() -> Self {
        dbg_println!("LineNumberDocumentNodeMetaData");
        LineNumberDocumentNodeMetaData {
            nl_count: 0,
            cr_count: 0,
            lf_count: 0,
        }
    }
}

pub struct LineNumberMode {
    // add common fields
    doc_subscription: usize,
}

impl LineNumberMode {
    pub fn new() -> Self {
        dbg_println!("LineNumberMode");
        LineNumberMode {
            doc_subscription: 0,
        }
    }
}

pub struct LineNumberModeContext {
    // add per view fields
    target_vid: view::Id,
}

struct LineNumberModeDocEventHandler {
    pub count: usize,
}

impl<'a> Mode for LineNumberMode {
    fn name(&self) -> &'static str {
        &"line-number-mode"
    }

    fn build_action_map(&self) -> InputStageActionMap<'static> {
        InputStageActionMap::new()
    }

    fn alloc_ctx(&self) -> Box<dyn Any> {
        dbg_println!("alloc line-number-mode ctx");
        let ctx = LineNumberModeContext { target_vid: 0 };
        Box::new(ctx)
    }

    fn configure_document(
        &mut self,
        _editor: &mut Editor<'static>,
        _env: &mut EditorEnv<'static>,
        doc: &mut Document<'static>,
    ) {
        // allocate document meta data
        let doc_id = doc.id;

        DOC_METADATA_MAP
            .as_ref()
            .write()
            .entry(doc_id)
            .or_insert(RwLock::new(LineNumberDocumentMetaData::new()));

        let meta = DOC_METADATA_MAP.as_ref().write();
        let meta = meta.get(&doc_id);
        let meta = meta.as_ref().unwrap().write();

        if !meta.cb_installed {
            let cb = Box::new(LineNumberModeDocEventHandler { count: 0 });

            self.doc_subscription = doc.register_subscriber(cb);
        }
    }

    fn configure_view(
        &mut self,
        _editor: &mut Editor<'static>,
        _env: &mut EditorEnv<'static>,
        view: &mut View<'static>,
    ) {
        view.compose_screen_overlay_filters
            .borrow_mut()
            .push(Box::new(LineNumberOverlayFilter::new()));
    }

    fn on_view_event(
        &self,
        editor: &mut Editor<'static>,
        _env: &mut EditorEnv<'static>,
        src: ViewEventSource,
        dst: ViewEventDestination,
        event: &ViewEvent,
    ) {
        let src_view = editor.view_map.get(&src.id).unwrap().write();
        let mut dst_view = editor.view_map.get(&dst.id).unwrap().write();

        match event {
            ViewEvent::Subscribe => {
                dbg_println!(
                    "LINENUM on_view_event src: {:?} dst: {:?}, event {:?}",
                    src,
                    dst,
                    event
                );

                let mut mode_ctx =
                    dst_view.mode_ctx_mut::<LineNumberModeContext>("line-number-mode");
                mode_ctx.target_vid = src.id;
            }
            _ => {}
        }
    }
}

///////////////////////////////////////////////////////////////////////////////////////////////////

impl DocumentEventCb for LineNumberModeDocEventHandler {
    fn cb(&mut self, doc: &Document, event: &DocumentEvent) {
        self.count += 1;

        dbg_println!(
            "LineNumberModeDocEventHandler ev {:?} CB count = {}",
            event,
            self.count
        );

        match event {
            DocumentEvent::NodeIndexed { node_index } => {
                dbg_println!(
                    "TODO index node {} with target codec  {:?}",
                    node_index,
                    event
                );
            }

            DocumentEvent::NodeAdded { node_index } => {}

            DocumentEvent::NodeRemoved { node_index } => {}

            DocumentEvent::NodeChanged { node_index } => {}

            _ => {
                eprintln!("unhandled event {:?}", event);
            }
        }

        doc.show_root_node_bytes_stats();
    }
}

///////////////////////////////////////////////////////////////////////////////////////////////////

pub struct LineNumberOverlayFilter {
    line_offsets: Vec<(u64, u64)>,
    line_number: Vec<(u64, (u64, Option<usize>))>, // (offset, (line_num, node_index))
}

impl LineNumberOverlayFilter {
    pub fn new() -> Self {
        LineNumberOverlayFilter {
            line_offsets: vec![],
            line_number: vec![],
        }
    }
}

// TODO(ceg): move to document.rs
//
// walk through the binary tree and while looking for the node containing "offset"
// and track byte_index count
//                                   SZ(19)   ,       LF(9)
//                   _________[ SZ(7+12),  LF(3+6) ]____________________
//                  /                                                 \
//        __[ 7=SZ(3+4), LF 3=(1+2) ]__                        _____[ 12=(5+7),  LF 6=(2+4) ]__
//       /                             \                      /                                 \
//  [SZ(3), LF(1)]={a,LF,b}    [SZ(4), LF(2)]={a,LF,LF,b }   [5, LF(2)] data{a,LF,b,LF,c} [SZ(7), LF(4)]={a ,LF,LF,b ,Lf,LF,c}
//                  0,1 ,2                     3, 4, 5,6                     7, 8,9,10,11                 12,13,14,15,16,17,18
//
//
fn get_byte_count_at_offset(
    doc: &Document,
    byte_index: usize,
    offset: u64,
) -> (u64, Option<usize>) {
    assert!(byte_index < 256);

    let mut file = doc.buffer.data.as_ref().write();
    let mut cur_index = file.root_index();
    let mut total_count = 0;
    let mut local_offset = offset;
    while cur_index != None {
        let idx = cur_index.unwrap();
        let p_node = &file.pool[idx];

        let is_leaf = p_node.link.left.is_none() && p_node.link.right.is_none();
        if is_leaf {
            let data = document::get_node_data(&mut file, Some(idx));
            for b in data.iter().take(local_offset as usize) {
                if *b as usize == byte_index {
                    total_count += 1;
                }
            }
            return (total_count, cur_index);
        }

        let mut left_node_size = 0;
        let mut left_byte_count = 0;

        if let Some(left_index) = p_node.link.left {
            let left_node = &file.pool[left_index];

            if local_offset < left_node.size {
                cur_index = Some(left_index);
                continue;
            }

            left_byte_count = left_node.byte_count[byte_index];
            left_node_size = left_node.size;
        }

        if let Some(right_index) = p_node.link.right {
            total_count += left_byte_count;
            local_offset -= left_node_size;
            cur_index = Some(right_index);
            continue;
        }
    }

    (0, None)
}

// right walk
fn _get_byte_count_at_offset_v2(
    doc: &Document,
    byte_index: usize,
    offset: u64,
) -> (u64, Option<usize>) {
    assert!(byte_index < 256);

    let mut file = doc.buffer.data.as_ref().write();
    let mut cur_index = file.root_index();
    if cur_index.is_none() {
        return (0, None);
    }

    let mut total_count = file.pool[cur_index.unwrap()].byte_count[byte_index];

    let mut local_offset = offset;

    while cur_index != None {
        let idx = cur_index.unwrap();
        let p_node = &file.pool[idx];

        let is_leaf = p_node.link.left.is_none() && p_node.link.right.is_none();
        if is_leaf {
            // TODO(ceg): linear count count lf until local_offset is reached
            // get node data
            let data = document::get_node_data(&mut file, Some(idx));
            for b in data.iter().skip(local_offset as usize) {
                if *b as usize == byte_index {
                    total_count -= 1;
                }
            }
            return (total_count, cur_index);
        }

        let parent_total_count = file.pool[idx].byte_count[byte_index];

        let mut left_byte_count = 0;
        let mut right_byte_count = 0;
        let mut left_node_size = 0;

        #[derive(Debug, PartialEq)]
        enum Direction {
            Left,
            Right,
        }

        let mut dir = Direction::Right;

        if let Some(right_index) = p_node.link.right {
            let right_node = &file.pool[right_index];
            right_byte_count = right_node.byte_count[byte_index];
        }

        if let Some(left_index) = p_node.link.left {
            let left_node = &file.pool[left_index];

            left_byte_count = left_node.byte_count[byte_index];
            left_node_size = left_node.size;

            if local_offset < left_node.size {
                dir = Direction::Left;
            }
        }

        assert_eq!(parent_total_count, left_byte_count + right_byte_count);

        if dir == Direction::Left {
            total_count -= right_byte_count;
            cur_index = p_node.link.left;
        } else {
            cur_index = p_node.link.right;
            local_offset -= left_node_size;
        }
    }

    panic!("");
}

impl ScreenOverlayFilter<'_> for LineNumberOverlayFilter {
    fn name(&self) -> &'static str {
        &"LineNumberOverlay"
    }

    fn setup(&mut self, editor: &Editor, _env: &mut LayoutEnv, view: &Rc<RwLock<View>>) {
        let view = view.read();
        let mode_ctx = view.mode_ctx::<LineNumberModeContext>("line-number-mode");
        let target_vid = mode_ctx.target_vid;
        let src = editor.view_map.get(&target_vid).unwrap().read();
        self.line_offsets = src.screen.read().line_offset.clone();

        self.line_number.clear();

        let doc = src.document();
        let doc = doc.as_ref().unwrap().read();
        if !doc.indexed {
            return;
        }

        // call to get_byte_count_at_offset are SLOW : compute only the first line
        // and read the target screen to compute relative line count
        for offset in self.line_offsets.iter().take(1) {
            let n = get_byte_count_at_offset(&doc, '\n' as usize, offset.0);
            self.line_number.push((offset.0, n));
        }

        let mut line_number = self.line_number[0].1 .0;
        let screen = src.screen.as_ref().read();
        for i in 0..screen.line_index.len() {
            let mut offset = 0;
            if let Some(l) = screen.get_used_line(i) {
                for (idx, cell) in l.iter().enumerate() {
                    if idx == 0 {
                        offset = cell.cpi.offset.unwrap();
                    }
                    if cell.cpi.cp == '\n' {
                        line_number += 1;
                        break;
                    }
                }
            }
            self.line_number.push((offset, (line_number as u64, None)));
        }
    }

    fn run(&mut self, _view: &View, env: &mut LayoutEnv) -> () {
        env.screen.clear();

        if !self.line_number.is_empty() {
            let mut prev_line = 0;
            for (idx, e) in self.line_number.iter().enumerate() {
                let s = if idx > 0 && e.1 .0 == prev_line {
                    format!("           │") // AFTER DEBUG ENABLE THIS
                } else {
                    format!("{: >11}│", e.1 .0 + 1)
                };
                prev_line = e.1 .0;

                for c in s.chars() {
                    let mut cpi = CodepointInfo::new();
                    cpi.displayed_cp = c;
                    env.screen.push(cpi);
                }
                env.screen.select_next_line_index();
            }
            return;
        }

        for e in self.line_offsets.iter() {
            let s = format!("@{}", e.0);
            for c in s.chars() {
                let mut cpi = CodepointInfo::new();
                cpi.displayed_cp = c;
                env.screen.push(cpi);
            }
            env.screen.select_next_line_index();
        }
    }

    fn finish(&mut self, view: &View, env: &mut LayoutEnv) -> () {}
}
<commit_msg>fix line number when word wrap is enabled<commit_after>/*
    TODO(ceg): provide the goto-line,goto-offset functions

    We want:

    An Updated collection of metadata that tracks number of newlines in the document sub-blocks

    The root contains the total count of newlines

    NB: This is tied to the document implementation

                 (root)
                   |
               [ 3 + 6 ]
             /          \
        [ 1 + 2 ]     [ 2 + 4 ]
       /        \    /        \
    [1]        [2]  [2]       [4]

    each time a node is indexed
    the document impl must call update hierarchy with the build metadata diff

    the mode subscribe to the document events

    When a node is indexed/added/removed, the document notify us
    Then we build the node metadata
    and ask to update the hierarchy.

    To be fully async we must: have a shadowed tree that matched the document internal representation ?
    and keep a per node doc_revision

    Must we re-index before remove ?
*/

use std::any::Any;

use parking_lot::RwLock;

use std::cell::RefCell;
use std::rc::Rc;
use std::sync::Arc;

use super::Mode;

use crate::core::codepointinfo::CodepointInfo;
use crate::core::editor::InputStageActionMap;
use crate::core::Editor;
use crate::core::EditorEnv;

use crate::core::document;
use crate::core::document::Document;
use crate::core::document::DocumentEvent;
use crate::core::document::DocumentEventCb;

use crate::core::mapped_file::MappedFile;
use crate::core::mapped_file::MappedFileIterator;

use crate::core::view::layout::LayoutEnv;
use crate::core::view::layout::ScreenOverlayFilter;

use crate::core::view;
use crate::core::view::View;
use crate::core::view::ViewEvent;
use crate::core::view::ViewEventDestination;
use crate::core::view::ViewEventSource;

use lazy_static::lazy_static;
use std::collections::HashMap;
// document meta data map
lazy_static! {
    static ref DOC_METADATA_MAP: Arc<RwLock<HashMap<document::Id, RwLock<LineNumberDocumentMetaData>>>> =
        Arc::new(RwLock::new(HashMap::new()));

        // document::Id -> (doc, LineNumberDocumentMetaData)
}

struct LineNumberDocumentMetaData {
    cb_installed: bool,
    root_idx: Option<usize>,
}

impl LineNumberDocumentMetaData {
    pub fn new() -> Self {
        dbg_println!("LineNumberDocumentMetaData");
        LineNumberDocumentMetaData {
            cb_installed: false,
            root_idx: None,
        }
    }
}

struct LineNumberDocumentNodeMetaData {
    nl_count: u64,
    cr_count: u64,
    lf_count: u64,
}

impl LineNumberDocumentNodeMetaData {
    pub fn new() -> Self {
        dbg_println!("LineNumberDocumentNodeMetaData");
        LineNumberDocumentNodeMetaData {
            nl_count: 0,
            cr_count: 0,
            lf_count: 0,
        }
    }
}

pub struct LineNumberMode {
    // add common fields
    doc_subscription: usize,
}

impl LineNumberMode {
    pub fn new() -> Self {
        dbg_println!("LineNumberMode");
        LineNumberMode {
            doc_subscription: 0,
        }
    }
}

pub struct LineNumberModeContext {
    // add per view fields
    target_vid: view::Id,
}

struct LineNumberModeDocEventHandler {
    pub count: usize,
}

impl<'a> Mode for LineNumberMode {
    fn name(&self) -> &'static str {
        &"line-number-mode"
    }

    fn build_action_map(&self) -> InputStageActionMap<'static> {
        InputStageActionMap::new()
    }

    fn alloc_ctx(&self) -> Box<dyn Any> {
        dbg_println!("alloc line-number-mode ctx");
        let ctx = LineNumberModeContext { target_vid: 0 };
        Box::new(ctx)
    }

    fn configure_document(
        &mut self,
        _editor: &mut Editor<'static>,
        _env: &mut EditorEnv<'static>,
        doc: &mut Document<'static>,
    ) {
        // allocate document meta data
        let doc_id = doc.id;

        DOC_METADATA_MAP
            .as_ref()
            .write()
            .entry(doc_id)
            .or_insert(RwLock::new(LineNumberDocumentMetaData::new()));

        let meta = DOC_METADATA_MAP.as_ref().write();
        let meta = meta.get(&doc_id);
        let meta = meta.as_ref().unwrap().write();

        if !meta.cb_installed {
            let cb = Box::new(LineNumberModeDocEventHandler { count: 0 });

            self.doc_subscription = doc.register_subscriber(cb);
        }
    }

    fn configure_view(
        &mut self,
        _editor: &mut Editor<'static>,
        _env: &mut EditorEnv<'static>,
        view: &mut View<'static>,
    ) {
        view.compose_screen_overlay_filters
            .borrow_mut()
            .push(Box::new(LineNumberOverlayFilter::new()));
    }

    fn on_view_event(
        &self,
        editor: &mut Editor<'static>,
        _env: &mut EditorEnv<'static>,
        src: ViewEventSource,
        dst: ViewEventDestination,
        event: &ViewEvent,
    ) {
        let src_view = editor.view_map.get(&src.id).unwrap().write();
        let mut dst_view = editor.view_map.get(&dst.id).unwrap().write();

        match event {
            ViewEvent::Subscribe => {
                dbg_println!(
                    "LINENUM on_view_event src: {:?} dst: {:?}, event {:?}",
                    src,
                    dst,
                    event
                );

                let mut mode_ctx =
                    dst_view.mode_ctx_mut::<LineNumberModeContext>("line-number-mode");
                mode_ctx.target_vid = src.id;
            }
            _ => {}
        }
    }
}

///////////////////////////////////////////////////////////////////////////////////////////////////

impl DocumentEventCb for LineNumberModeDocEventHandler {
    fn cb(&mut self, doc: &Document, event: &DocumentEvent) {
        self.count += 1;

        dbg_println!(
            "LineNumberModeDocEventHandler ev {:?} CB count = {}",
            event,
            self.count
        );

        match event {
            DocumentEvent::NodeIndexed { node_index } => {
                dbg_println!(
                    "TODO index node {} with target codec  {:?}",
                    node_index,
                    event
                );
            }

            DocumentEvent::NodeAdded { node_index } => {}

            DocumentEvent::NodeRemoved { node_index } => {}

            DocumentEvent::NodeChanged { node_index } => {}

            _ => {
                eprintln!("unhandled event {:?}", event);
            }
        }

        doc.show_root_node_bytes_stats();
    }
}

///////////////////////////////////////////////////////////////////////////////////////////////////

pub struct LineNumberOverlayFilter {
    line_offsets: Vec<(u64, u64)>,
    line_number: Vec<(u64, (u64, Option<usize>))>, // (offset, (line_num, node_index))
}

impl LineNumberOverlayFilter {
    pub fn new() -> Self {
        LineNumberOverlayFilter {
            line_offsets: vec![],
            line_number: vec![],
        }
    }
}

// TODO(ceg): move to document.rs
//
// walk through the binary tree and while looking for the node containing "offset"
// and track byte_index count
//                                   SZ(19)   ,       LF(9)
//                   _________[ SZ(7+12),  LF(3+6) ]____________________
//                  /                                                 \
//        __[ 7=SZ(3+4), LF 3=(1+2) ]__                        _____[ 12=(5+7),  LF 6=(2+4) ]__
//       /                             \                      /                                 \
//  [SZ(3), LF(1)]={a,LF,b}    [SZ(4), LF(2)]={a,LF,LF,b }   [5, LF(2)] data{a,LF,b,LF,c} [SZ(7), LF(4)]={a ,LF,LF,b ,Lf,LF,c}
//                  0,1 ,2                     3, 4, 5,6                     7, 8,9,10,11                 12,13,14,15,16,17,18
//
//
fn get_byte_count_at_offset(
    doc: &Document,
    byte_index: usize,
    offset: u64,
) -> (u64, Option<usize>) {
    assert!(byte_index < 256);

    let mut file = doc.buffer.data.as_ref().write();
    let mut cur_index = file.root_index();
    let mut total_count = 0;
    let mut local_offset = offset;
    while cur_index != None {
        let idx = cur_index.unwrap();
        let p_node = &file.pool[idx];

        let is_leaf = p_node.link.left.is_none() && p_node.link.right.is_none();
        if is_leaf {
            let data = document::get_node_data(&mut file, Some(idx));
            for b in data.iter().take(local_offset as usize) {
                if *b as usize == byte_index {
                    total_count += 1;
                }
            }
            return (total_count, cur_index);
        }

        let mut left_node_size = 0;
        let mut left_byte_count = 0;

        if let Some(left_index) = p_node.link.left {
            let left_node = &file.pool[left_index];

            if local_offset < left_node.size {
                cur_index = Some(left_index);
                continue;
            }

            left_byte_count = left_node.byte_count[byte_index];
            left_node_size = left_node.size;
        }

        if let Some(right_index) = p_node.link.right {
            total_count += left_byte_count;
            local_offset -= left_node_size;
            cur_index = Some(right_index);
            continue;
        }
    }

    (0, None)
}

// right walk
fn _get_byte_count_at_offset_v2(
    doc: &Document,
    byte_index: usize,
    offset: u64,
) -> (u64, Option<usize>) {
    assert!(byte_index < 256);

    let mut file = doc.buffer.data.as_ref().write();
    let mut cur_index = file.root_index();
    if cur_index.is_none() {
        return (0, None);
    }

    let mut total_count = file.pool[cur_index.unwrap()].byte_count[byte_index];

    let mut local_offset = offset;

    while cur_index != None {
        let idx = cur_index.unwrap();
        let p_node = &file.pool[idx];

        let is_leaf = p_node.link.left.is_none() && p_node.link.right.is_none();
        if is_leaf {
            // TODO(ceg): linear count count lf until local_offset is reached
            // get node data
            let data = document::get_node_data(&mut file, Some(idx));
            for b in data.iter().skip(local_offset as usize) {
                if *b as usize == byte_index {
                    total_count -= 1;
                }
            }
            return (total_count, cur_index);
        }

        let parent_total_count = file.pool[idx].byte_count[byte_index];

        let mut left_byte_count = 0;
        let mut right_byte_count = 0;
        let mut left_node_size = 0;

        #[derive(Debug, PartialEq)]
        enum Direction {
            Left,
            Right,
        }

        let mut dir = Direction::Right;

        if let Some(right_index) = p_node.link.right {
            let right_node = &file.pool[right_index];
            right_byte_count = right_node.byte_count[byte_index];
        }

        if let Some(left_index) = p_node.link.left {
            let left_node = &file.pool[left_index];

            left_byte_count = left_node.byte_count[byte_index];
            left_node_size = left_node.size;

            if local_offset < left_node.size {
                dir = Direction::Left;
            }
        }

        assert_eq!(parent_total_count, left_byte_count + right_byte_count);

        if dir == Direction::Left {
            total_count -= right_byte_count;
            cur_index = p_node.link.left;
        } else {
            cur_index = p_node.link.right;
            local_offset -= left_node_size;
        }
    }

    panic!("");
}

impl ScreenOverlayFilter<'_> for LineNumberOverlayFilter {
    fn name(&self) -> &'static str {
        &"LineNumberOverlay"
    }

    fn setup(&mut self, editor: &Editor, _env: &mut LayoutEnv, view: &Rc<RwLock<View>>) {
        let view = view.read();
        let mode_ctx = view.mode_ctx::<LineNumberModeContext>("line-number-mode");
        let target_vid = mode_ctx.target_vid;
        let src = editor.view_map.get(&target_vid).unwrap().read();
        self.line_offsets = src.screen.read().line_offset.clone();

        self.line_number.clear();

        let doc = src.document();
        let doc = doc.as_ref().unwrap().read();
        if !doc.indexed {
            return;
        }

        // call to get_byte_count_at_offset are SLOW : compute only the first line
        // and read the target screen to compute relative line count
        for offset in self.line_offsets.iter().take(1) {
            let n = get_byte_count_at_offset(&doc, '\n' as usize, offset.0);
            self.line_number.push((offset.0, n));
        }

        let mut line_number = self.line_number[0].1 .0;
        let screen = src.screen.as_ref().read();
        for i in 0..screen.line_index.len() {
            let mut offset = 0;
            if let Some(l) = screen.get_used_line(i) {
                for (idx, cell) in l.iter().enumerate() {
                    if idx == 0 {
                        offset = cell.cpi.offset.unwrap();
                    }
                    if cell.cpi.metadata == false && cell.cpi.cp == '\n' {
                        line_number += 1;
                        break;
                    }
                }
            }
            self.line_number.push((offset, (line_number as u64, None)));
        }
    }

    fn run(&mut self, _view: &View, env: &mut LayoutEnv) -> () {
        env.screen.clear();

        if !self.line_number.is_empty() {
            let mut prev_line = 0;
            for (idx, e) in self.line_number.iter().enumerate() {
                let s = if idx > 0 && e.1 .0 == prev_line {
                    format!("           │") // AFTER DEBUG ENABLE THIS
                } else {
                    format!("{: >11}│", e.1 .0 + 1)
                };
                prev_line = e.1 .0;

                for c in s.chars() {
                    let mut cpi = CodepointInfo::new();
                    cpi.displayed_cp = c;
                    env.screen.push(cpi);
                }
                env.screen.select_next_line_index();
            }
            return;
        }

        for e in self.line_offsets.iter() {
            let s = format!("@{}", e.0);
            for c in s.chars() {
                let mut cpi = CodepointInfo::new();
                cpi.displayed_cp = c;
                env.screen.push(cpi);
            }
            env.screen.select_next_line_index();
        }
    }

    fn finish(&mut self, view: &View, env: &mut LayoutEnv) -> () {}
}
<|endoftext|>


<commit_before>use drm::control::{connector, crtc, Mode};
use nix::libc::c_void;
use std::cell::RefCell;
use std::rc::Rc;

use super::error::*;
use backend::drm::{Device, Surface};
use backend::egl::native::{Backend, NativeDisplay, NativeSurface};
use backend::egl::{EGLContext, EGLSurface};
use backend::graphics::gl::{GLGraphicsBackend, PixelFormat};
use backend::graphics::{CursorBackend, SwapBuffersError};

pub struct EglSurface<
    B: Backend<Surface = <D as Device>::Surface> + 'static,
    D: Device + NativeDisplay<B> + 'static,
> where
    <D as Device>::Surface: NativeSurface,
{
    pub(super) dev: Rc<RefCell<EGLContext<B, D>>>,
    pub(super) surface: EGLSurface<B::Surface>,
}

impl<B: Backend<Surface = <D as Device>::Surface> + 'static, D: Device + NativeDisplay<B> + 'static> Surface
    for EglSurface<B, D>
where
    <D as Device>::Surface: NativeSurface,
{
    type Error = Error;
    type Connectors = <<D as Device>::Surface as Surface>::Connectors;

    fn crtc(&self) -> crtc::Handle {
        (*self.surface).crtc()
    }

    fn current_connectors(&self) -> Self::Connectors {
        self.surface.current_connectors()
    }

    fn pending_connectors(&self) -> Self::Connectors {
        self.surface.pending_connectors()
    }

    fn add_connector(&self, connector: connector::Handle) -> Result<()> {
        self.surface
            .add_connector(connector)
            .chain_err(|| ErrorKind::UnderlyingBackendError)
    }

    fn remove_connector(&self, connector: connector::Handle) -> Result<()> {
        self.surface
            .remove_connector(connector)
            .chain_err(|| ErrorKind::UnderlyingBackendError)
    }

    fn current_mode(&self) -> Mode {
        self.surface.current_mode()
    }

    fn pending_mode(&self) -> Mode {
        self.surface.pending_mode()
    }

    fn use_mode(&self, mode: Mode) -> Result<()> {
        self.surface
            .use_mode(mode)
            .chain_err(|| ErrorKind::UnderlyingBackendError)
    }
}

impl<'a, B: Backend<Surface = <D as Device>::Surface> + 'static, D: Device + NativeDisplay<B> + 'static>
    CursorBackend<'a> for EglSurface<B, D>
where
    D: CursorBackend<'a>,
    <D as Device>::Surface: NativeSurface,
{
    type CursorFormat = <D as CursorBackend<'a>>::CursorFormat;
    type Error = <D as CursorBackend<'a>>::Error;

    fn set_cursor_position(&self, x: u32, y: u32) -> ::std::result::Result<(), Self::Error> {
        self.dev.borrow().set_cursor_position(x, y)
    }

    fn set_cursor_representation<'b>(
        &'b self,
        buffer: Self::CursorFormat,
        hotspot: (u32, u32),
    ) -> ::std::result::Result<(), Self::Error>
    where
        'a: 'b,
    {
        let dev = self.dev.borrow();
        dev.set_cursor_representation(buffer, hotspot)
    }
}

impl<B: Backend<Surface = <D as Device>::Surface> + 'static, D: Device + NativeDisplay<B> + 'static>
    GLGraphicsBackend for EglSurface<B, D>
where
    <D as Device>::Surface: NativeSurface,
{
    fn swap_buffers(&self) -> ::std::result::Result<(), SwapBuffersError> {
        self.surface.swap_buffers()
    }

    unsafe fn get_proc_address(&self, symbol: &str) -> *const c_void {
        self.dev.borrow().get_proc_address(symbol)
    }

    fn get_framebuffer_dimensions(&self) -> (u32, u32) {
        let (w, h) = self.pending_mode().size();
        (w as u32, h as u32)
    }

    fn is_current(&self) -> bool {
        self.dev.borrow().is_current() && self.surface.is_current()
    }

    unsafe fn make_current(&self) -> ::std::result::Result<(), SwapBuffersError> {
        self.surface.make_current()
    }

    fn get_pixel_format(&self) -> PixelFormat {
        self.dev.borrow().get_pixel_format()
    }
}
<commit_msg>egl_surface: fix CursorBackend implementation<commit_after>use drm::control::{connector, crtc, Mode};
use nix::libc::c_void;
use std::cell::RefCell;
use std::rc::Rc;

use super::error::*;
use backend::drm::{Device, Surface};
use backend::egl::native::{Backend, NativeDisplay, NativeSurface};
use backend::egl::{EGLContext, EGLSurface};
use backend::graphics::gl::{GLGraphicsBackend, PixelFormat};
use backend::graphics::{CursorBackend, SwapBuffersError};

pub struct EglSurface<
    B: Backend<Surface = <D as Device>::Surface> + 'static,
    D: Device + NativeDisplay<B> + 'static,
> where
    <D as Device>::Surface: NativeSurface,
{
    pub(super) dev: Rc<RefCell<EGLContext<B, D>>>,
    pub(super) surface: EGLSurface<B::Surface>,
}

impl<B: Backend<Surface = <D as Device>::Surface> + 'static, D: Device + NativeDisplay<B> + 'static> Surface
    for EglSurface<B, D>
where
    <D as Device>::Surface: NativeSurface,
{
    type Error = Error;
    type Connectors = <<D as Device>::Surface as Surface>::Connectors;

    fn crtc(&self) -> crtc::Handle {
        (*self.surface).crtc()
    }

    fn current_connectors(&self) -> Self::Connectors {
        self.surface.current_connectors()
    }

    fn pending_connectors(&self) -> Self::Connectors {
        self.surface.pending_connectors()
    }

    fn add_connector(&self, connector: connector::Handle) -> Result<()> {
        self.surface
            .add_connector(connector)
            .chain_err(|| ErrorKind::UnderlyingBackendError)
    }

    fn remove_connector(&self, connector: connector::Handle) -> Result<()> {
        self.surface
            .remove_connector(connector)
            .chain_err(|| ErrorKind::UnderlyingBackendError)
    }

    fn current_mode(&self) -> Mode {
        self.surface.current_mode()
    }

    fn pending_mode(&self) -> Mode {
        self.surface.pending_mode()
    }

    fn use_mode(&self, mode: Mode) -> Result<()> {
        self.surface
            .use_mode(mode)
            .chain_err(|| ErrorKind::UnderlyingBackendError)
    }
}

impl<'a, B: Backend<Surface = <D as Device>::Surface> + 'static, D: Device + NativeDisplay<B> + 'static>
    CursorBackend<'a> for EglSurface<B, D>
where
    <D as Device>::Surface: NativeSurface + CursorBackend<'a>,
{
    type CursorFormat = <D::Surface as CursorBackend<'a>>::CursorFormat;
    type Error = <D::Surface as CursorBackend<'a>>::Error;

    fn set_cursor_position(&self, x: u32, y: u32) -> ::std::result::Result<(), Self::Error> {
        self.surface.set_cursor_position(x, y)
    }

    fn set_cursor_representation<'b>(
        &'b self,
        buffer: Self::CursorFormat,
        hotspot: (u32, u32),
    ) -> ::std::result::Result<(), Self::Error>
    where
        'a: 'b,
    {
        self.surface.set_cursor_representation(buffer, hotspot)
    }
}

impl<B: Backend<Surface = <D as Device>::Surface> + 'static, D: Device + NativeDisplay<B> + 'static>
    GLGraphicsBackend for EglSurface<B, D>
where
    <D as Device>::Surface: NativeSurface,
{
    fn swap_buffers(&self) -> ::std::result::Result<(), SwapBuffersError> {
        self.surface.swap_buffers()
    }

    unsafe fn get_proc_address(&self, symbol: &str) -> *const c_void {
        self.dev.borrow().get_proc_address(symbol)
    }

    fn get_framebuffer_dimensions(&self) -> (u32, u32) {
        let (w, h) = self.pending_mode().size();
        (w as u32, h as u32)
    }

    fn is_current(&self) -> bool {
        self.dev.borrow().is_current() && self.surface.is_current()
    }

    unsafe fn make_current(&self) -> ::std::result::Result<(), SwapBuffersError> {
        self.surface.make_current()
    }

    fn get_pixel_format(&self) -> PixelFormat {
        self.dev.borrow().get_pixel_format()
    }
}
<|endoftext|>


<commit_before>//xfail-stage0
use std;

fn main() {

    obj a() {
        fn foo() -> int {
            ret 2;
        }
        fn bar() -> int {
            ret self.foo();
        }
    }

    auto my_a = a();

    // Degenerate anonymous object: one that doesn't add any new
    // methods or fields.

    auto my_d = obj() { with my_a };

    assert (my_d.foo() == 2);
    assert (my_d.bar() == 2);

}
<commit_msg>Un-xfail a test.<commit_after>use std;

fn main() {

    obj a() {
        fn foo() -> int {
            ret 2;
        }
        fn bar() -> int {
            ret self.foo();
        }
    }

    auto my_a = a();

    // Degenerate anonymous object: one that doesn't add any new
    // methods or fields.

    auto my_d = obj() { with my_a };

    assert (my_d.foo() == 2);
    assert (my_d.bar() == 2);

}
<|endoftext|>


<commit_before>use time::precise_time_ns;

use std::sync::mpsc::{channel, Sender};
use std::thread::Thread;

use search;

use super::types::{Cmd, Response};
use super::state::{State, Mode};
use self::time_start::time_start;

mod go_param;
mod time_start;
mod pos;

pub fn process(state: &mut State, cmd: Cmd, output: &Sender<Response>) {
    match cmd {
        Cmd::Debug(val) => {
            state.is_debug = val;
            state.search_tx.as_ref().map(|tx| { let _ = tx.send(search::Cmd::Stop); } );
        },
        Cmd::IsReady => {
            // TODO implement IsReady
            unimplemented!();
        },
        Cmd::Register(ref param) => {
            // TODO register
            unimplemented!();
        },
        cmd => {
            match state.mode {
                Mode::Init => {
                    if cmd == Cmd::Uci {
                        // TODO print id/option/uciok
                        state.mode = Mode::Wait;
                        unimplemented!();
                    }
                },
                Mode::Wait => {
                    match cmd {
                        Cmd::SetOption(ref name, ref val_opt) => {
                            // TODO maybe initialize
                            // TODO set options
                            unimplemented!();
                        },
                        Cmd::UciNewGame => {
                            // TODO note that ucinewgame supported
                            // TODO reset game status
                            state.mode = Mode::NewGame;
                            unimplemented!();
                        },
                        Cmd::SetupPosition(pos, from_to_vec) => {
                            if state.ucinewgame_support {
                                pos::setup_same(&mut state.search_state, pos, from_to_vec);
                                state.mode = Mode::Ready;
                            } else {
                                process(state, Cmd::UciNewGame, output);
                                state.ucinewgame_support = false;
                                process(state, Cmd::SetupPosition(pos, from_to_vec), output);
                            }
                        },
                        _ => {},
                    }
                },
                Mode::NewGame => {
                    if let Cmd::SetupPosition(pos, from_to_vec) = cmd {
                        pos::setup_new(&mut state.search_state, pos, from_to_vec);
                        state.mode = Mode::Ready;
                    }
                },
                Mode::Ready => {
                    assert!(state.search_state.is_some());
                    if let Cmd::Go(param) = cmd {
                        go_param::setup(state, param);
                        let (search_tx, search_rx) = channel::<search::Cmd>();
                        let search_state = state.search_state.as_ref().unwrap().clone();
                        let output = output.clone();
                        let temp = Thread::scoped(move ||
                                                  search::start(search_state, search_rx, output));

                        state.search_tx = Some(search_tx);
                        state.search_guard = Some(temp);

                        if !state.search_state.as_ref().unwrap().param.ponder {
                            time_start(state);
                        }

                        state.mode = Mode::Search;
                    }
                },
                Mode::Search => {
                    assert!(state.search_state.is_some());
                    assert!(state.search_guard.is_some());
                    assert!(state.search_tx.is_some());
                    match cmd {
                        Cmd::PonderHit => {
                            if !state.search_state.as_ref().unwrap().param.ponder {
                                return;
                            }
                            let _ = state.search_tx.as_ref().unwrap().send(search::Cmd::PonderHit);
                            state.search_state.as_mut().unwrap().param.ponder = false;
                            state.start_move_time = Some(precise_time_ns());
                            time_start(state);
                        },
                        Cmd::Stop => {
                            let _ = state.search_tx.as_ref().unwrap().send(search::Cmd::Stop);
                            let _ = state.search_guard.take().unwrap().join();
                            state.search_tx = None;
                            state.start_search_time = None;
                            state.start_move_time = None;
                            state.time_data = None;
                            if let Some(time_kill_tx) = state.time_kill_tx.take() {
                                let _ = time_kill_tx.send(());
                            }
                            state.time_rx = None;
                            state.mode = Mode::Wait;
                            // TODO what to do about search_state
                            unimplemented!();
                        },
                        _ => {},
                    }
                },
            }
        },
    }
}
<commit_msg>Do not modify search state when terminating search<commit_after>use time::precise_time_ns;

use std::sync::mpsc::{channel, Sender};
use std::thread::Thread;

use search;

use super::types::{Cmd, Response};
use super::state::{State, Mode};
use self::time_start::time_start;

mod go_param;
mod time_start;
mod pos;

pub fn process(state: &mut State, cmd: Cmd, output: &Sender<Response>) {
    match cmd {
        Cmd::Debug(val) => {
            state.is_debug = val;
            state.search_tx.as_ref().map(|tx| { let _ = tx.send(search::Cmd::Stop); } );
        },
        Cmd::IsReady => {
            // TODO implement IsReady
            unimplemented!();
        },
        Cmd::Register(ref param) => {
            // TODO register
            unimplemented!();
        },
        cmd => {
            match state.mode {
                Mode::Init => {
                    if cmd == Cmd::Uci {
                        // TODO print id/option/uciok
                        state.mode = Mode::Wait;
                        unimplemented!();
                    }
                },
                Mode::Wait => {
                    match cmd {
                        Cmd::SetOption(ref name, ref val_opt) => {
                            // TODO maybe initialize
                            // TODO set options
                            unimplemented!();
                        },
                        Cmd::UciNewGame => {
                            // TODO note that ucinewgame supported
                            // TODO reset game status
                            state.mode = Mode::NewGame;
                            unimplemented!();
                        },
                        Cmd::SetupPosition(pos, from_to_vec) => {
                            if state.ucinewgame_support {
                                pos::setup_same(&mut state.search_state, pos, from_to_vec);
                                state.mode = Mode::Ready;
                            } else {
                                process(state, Cmd::UciNewGame, output);
                                state.ucinewgame_support = false;
                                process(state, Cmd::SetupPosition(pos, from_to_vec), output);
                            }
                        },
                        _ => {},
                    }
                },
                Mode::NewGame => {
                    if let Cmd::SetupPosition(pos, from_to_vec) = cmd {
                        pos::setup_new(&mut state.search_state, pos, from_to_vec);
                        state.mode = Mode::Ready;
                    }
                },
                Mode::Ready => {
                    assert!(state.search_state.is_some());
                    if let Cmd::Go(param) = cmd {
                        go_param::setup(state, param);
                        let (search_tx, search_rx) = channel::<search::Cmd>();
                        let search_state = state.search_state.as_ref().unwrap().clone();
                        let output = output.clone();
                        let temp = Thread::scoped(move ||
                                                  search::start(search_state, search_rx, output));

                        state.search_tx = Some(search_tx);
                        state.search_guard = Some(temp);

                        if !state.search_state.as_ref().unwrap().param.ponder {
                            time_start(state);
                        }

                        state.mode = Mode::Search;
                    }
                },
                Mode::Search => {
                    assert!(state.search_state.is_some());
                    assert!(state.search_guard.is_some());
                    assert!(state.search_tx.is_some());
                    match cmd {
                        Cmd::PonderHit => {
                            if !state.search_state.as_ref().unwrap().param.ponder {
                                return;
                            }
                            let _ = state.search_tx.as_ref().unwrap().send(search::Cmd::PonderHit);
                            state.search_state.as_mut().unwrap().param.ponder = false;
                            state.start_move_time = Some(precise_time_ns());
                            time_start(state);
                        },
                        Cmd::Stop => {
                            let _ = state.search_tx.as_ref().unwrap().send(search::Cmd::Stop);
                            let _ = state.search_guard.take().unwrap().join();
                            state.search_tx = None;
                            state.start_search_time = None;
                            state.start_move_time = None;
                            state.time_data = None;
                            if let Some(time_kill_tx) = state.time_kill_tx.take() {
                                let _ = time_kill_tx.send(());
                            }
                            state.time_rx = None;
                            state.mode = Mode::Wait;
                        },
                        _ => {},
                    }
                },
            }
        },
    }
}
<|endoftext|>


<commit_before>use std::fmt::{mod, Show, Formatter};
use std::io::{UserDir};
use std::io::fs::{mkdir_recursive,rmdir_recursive};
use serialize::{Encodable,Encoder};
use url::Url;
use git2;

use util::{CargoResult, ChainError, human, ToUrl, internal, Require};

#[deriving(PartialEq,Clone,Encodable)]
pub enum GitReference {
    Master,
    Other(String)
}

#[deriving(PartialEq,Clone,Encodable)]
pub struct GitRevision(String);

impl GitReference {
    pub fn for_str<S: Str>(string: S) -> GitReference {
        if string.as_slice() == "master" {
            Master
        } else {
            Other(string.as_slice().to_string())
        }
    }
}

impl Str for GitReference {
    fn as_slice(&self) -> &str {
        match *self {
            Master => "master",
            Other(ref string) => string.as_slice()
        }
    }
}

impl Show for GitReference {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        self.as_slice().fmt(f)
    }
}

impl Str for GitRevision {
    fn as_slice(&self) -> &str {
        let GitRevision(ref me) = *self;
        me.as_slice()
    }
}

impl Show for GitRevision {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        self.as_slice().fmt(f)
    }
}

/// GitRemote represents a remote repository. It gets cloned into a local
/// GitDatabase.
#[deriving(PartialEq,Clone,Show)]
pub struct GitRemote {
    url: Url,
}

#[deriving(PartialEq,Clone,Encodable)]
struct EncodableGitRemote {
    url: String,
}

impl<E, S: Encoder<E>> Encodable<S, E> for GitRemote {
    fn encode(&self, s: &mut S) -> Result<(), E> {
        EncodableGitRemote {
            url: self.url.to_string()
        }.encode(s)
    }
}

/// GitDatabase is a local clone of a remote repository's database. Multiple
/// GitCheckouts can be cloned from this GitDatabase.
pub struct GitDatabase {
    remote: GitRemote,
    path: Path,
    repo: git2::Repository,
}

#[deriving(Encodable)]
pub struct EncodableGitDatabase {
    remote: GitRemote,
    path: String,
}

impl<E, S: Encoder<E>> Encodable<S, E> for GitDatabase {
    fn encode(&self, s: &mut S) -> Result<(), E> {
        EncodableGitDatabase {
            remote: self.remote.clone(),
            path: self.path.display().to_string()
        }.encode(s)
    }
}

/// GitCheckout is a local checkout of a particular revision. Calling
/// `clone_into` with a reference will resolve the reference into a revision,
/// and return a CargoError if no revision for that reference was found.
pub struct GitCheckout<'a> {
    database: &'a GitDatabase,
    location: Path,
    revision: GitRevision,
    repo: git2::Repository,
}

#[deriving(Encodable)]
pub struct EncodableGitCheckout {
    database: EncodableGitDatabase,
    location: String,
    revision: String,
}

impl<'a, E, S: Encoder<E>> Encodable<S, E> for GitCheckout<'a> {
    fn encode(&self, s: &mut S) -> Result<(), E> {
        EncodableGitCheckout {
            location: self.location.display().to_string(),
            revision: self.revision.to_string(),
            database: EncodableGitDatabase {
                remote: self.database.remote.clone(),
                path: self.database.path.display().to_string(),
            },
        }.encode(s)
    }
}

// Implementations

impl GitRemote {
    pub fn new(url: &Url) -> GitRemote {
        GitRemote { url: url.clone() }
    }

    pub fn get_url(&self) -> &Url {
        &self.url
    }

    pub fn rev_for<S: Str>(&self, path: &Path, reference: S)
                           -> CargoResult<GitRevision> {
        let db = try!(self.db_at(path));
        db.rev_for(reference)
    }

    pub fn checkout(&self, into: &Path) -> CargoResult<GitDatabase> {
        let repo = if into.exists() {
            let r = try!(git2::Repository::open(into));
            try!(self.fetch_into(&r).chain_error(|| {
                internal(format!("failed to fetch into {}", into.display()))
            }));
            r
        } else {
            try!(self.clone_into(into).chain_error(|| {
                internal(format!("failed to clone into: {}", into.display()))
            }))
        };

        Ok(GitDatabase { remote: self.clone(), path: into.clone(), repo: repo })
    }

    pub fn db_at(&self, db_path: &Path) -> CargoResult<GitDatabase> {
        let repo = try!(git2::Repository::open(db_path));
        Ok(GitDatabase {
            remote: self.clone(),
            path: db_path.clone(),
            repo: repo,
        })
    }

    fn fetch_into(&self, dst: &git2::Repository) -> CargoResult<()> {
        let url = self.url.to_string();
        let refspec = "refs/heads/*:refs/heads/*";
        let mut remote = try!(dst.remote_create_anonymous(url.as_slice(),
                                                          refspec));
        try!(remote.add_fetch("refs/tags/*:refs/tags/*"));
        try!(remote.fetch(None, None));
        Ok(())
    }

    fn clone_into(&self, dst: &Path) -> CargoResult<git2::Repository> {
        let url = self.url.to_string();
        try!(mkdir_recursive(dst, UserDir));
        let repo = try!(git2::build::RepoBuilder::new().bare(true)
                                                       .hardlinks(false)
                                                       .clone(url.as_slice(), dst));
        Ok(repo)
    }
}

impl GitDatabase {
    fn get_path<'a>(&'a self) -> &'a Path {
        &self.path
    }

    pub fn copy_to(&self, rev: GitRevision, dest: &Path)
                   -> CargoResult<GitCheckout> {
        match git2::Repository::open(dest) {
            Ok(repo) => {
                let is_fresh = match repo.revparse_single("HEAD") {
                    Ok(head) => head.id().to_string() == rev.to_string(),
                    _ => false,
                };
                if is_fresh {
                    return Ok(GitCheckout::new(dest, self, rev, repo))
                }
            }
            _ => {}
        }

        GitCheckout::clone_into(dest, self, rev)
    }

    pub fn rev_for<S: Str>(&self, reference: S) -> CargoResult<GitRevision> {
        let rev = try!(self.repo.revparse_single(reference.as_slice()));
        Ok(GitRevision(rev.id().to_string()))
    }

    pub fn has_ref<S: Str>(&self, reference: S) -> CargoResult<()> {
        try!(self.repo.revparse_single(reference.as_slice()));
        Ok(())
    }
}

impl<'a> GitCheckout<'a> {
    fn new<'a>(path: &Path, database: &'a GitDatabase, revision: GitRevision,
               repo: git2::Repository)
               -> GitCheckout<'a>
    {
        GitCheckout {
            location: path.clone(),
            database: database,
            revision: revision,
            repo: repo,
        }
    }

    fn clone_into<'a>(into: &Path, database: &'a GitDatabase,
                      revision: GitRevision)
                      -> CargoResult<GitCheckout<'a>>
    {
        let repo = try!(GitCheckout::clone_repo(database.get_path(), into));
        let checkout = GitCheckout::new(into, database, revision, repo);

        try!(checkout.reset().chain_error(|| {
            internal("failed to reset to the right revision")
        }));
        try!(checkout.update_submodules().chain_error(|| {
            internal("failed to update submodules")
        }));

        Ok(checkout)
    }

    pub fn get_rev(&self) -> &str {
        self.revision.as_slice()
    }

    fn clone_repo(source: &Path, into: &Path) -> CargoResult<git2::Repository> {
        let dirname = into.dir_path();

        try!(mkdir_recursive(&dirname, UserDir).chain_error(|| {
            human(format!("Couldn't mkdir {}", dirname.display()))
        }));

        if into.exists() {
            try!(rmdir_recursive(into).chain_error(|| {
                human(format!("Couldn't rmdir {}", into.display()))
            }));
        }

        let url = try!(source.to_url().map_err(human));
        let url = url.to_string();
        let repo = try!(git2::Repository::clone(url.as_slice(),
                                                into).chain_error(|| {
            internal(format!("failed to clone {} into {}", source.display(),
                             into.display()))
        }));
        Ok(repo)
    }

    fn reset(&self) -> CargoResult<()> {
        info!("reset {} to {}", self.repo.path().display(),
              self.revision.as_slice());
        let oid = try!(git2::Oid::from_str(self.revision.as_slice()));
        let object = try!(git2::Object::lookup(&self.repo, oid, None));
        try!(self.repo.reset(&object, git2::Hard, None, None));
        Ok(())
    }

    fn update_submodules(&self) -> CargoResult<()> {
        return update_submodules(&self.repo);

        fn update_submodules(repo: &git2::Repository) -> CargoResult<()> {
            info!("update submodules for: {}", repo.path().display());

            for mut child in try!(repo.submodules()).move_iter() {
                try!(child.init(false));
                let url = try!(child.url().require(|| {
                    internal("non-utf8 url for submodule")
                }));

                // A submodule which is listed in .gitmodules but not actually
                // checked out will not have a head id, so we should ignore it.
                let head = match child.head_id() {
                    Some(head) => head,
                    None => continue,
                };

                // If the submodule hasn't been checked out yet, we need to
                // clone it. If it has been checked out and the head is the same
                // as the submodule's head, then we can bail out and go to the
                // next submodule.
                let repo = match child.open() {
                    Ok(repo) => {
                        if child.head_id() == try!(repo.head()).target() {
                            continue
                        }
                        repo
                    }
                    Err(..) => {
                        let path = repo.path().dir_path().join(child.path());
                        try!(git2::Repository::clone(url, &path))
                    }
                };

                // Fetch data from origin and reset to the head commit
                let refspec = "refs/heads/*:refs/heads/*";
                let mut remote = try!(repo.remote_create_anonymous(url, refspec));
                try!(remote.fetch(None, None).chain_error(|| {
                    internal(format!("failed to fetch submodule `{}` from {}",
                                     child.name().unwrap_or(""), url))
                }));

                let obj = try!(git2::Object::lookup(&repo, head, None));
                try!(repo.reset(&obj, git2::Hard, None, None));
                try!(update_submodules(&repo));
            }
            Ok(())
        }
    }
}
<commit_msg>auto merge of #501 : alexcrichton/cargo/issue-480, r=brson<commit_after>use std::fmt::{mod, Show, Formatter};
use std::io::{UserDir};
use std::io::fs::{mkdir_recursive,rmdir_recursive};
use serialize::{Encodable,Encoder};
use url::Url;
use git2;

use util::{CargoResult, ChainError, human, ToUrl, internal, Require};

#[deriving(PartialEq,Clone,Encodable)]
pub enum GitReference {
    Master,
    Other(String)
}

#[deriving(PartialEq,Clone,Encodable)]
pub struct GitRevision(String);

impl GitReference {
    pub fn for_str<S: Str>(string: S) -> GitReference {
        if string.as_slice() == "master" {
            Master
        } else {
            Other(string.as_slice().to_string())
        }
    }
}

impl Str for GitReference {
    fn as_slice(&self) -> &str {
        match *self {
            Master => "master",
            Other(ref string) => string.as_slice()
        }
    }
}

impl Show for GitReference {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        self.as_slice().fmt(f)
    }
}

impl Str for GitRevision {
    fn as_slice(&self) -> &str {
        let GitRevision(ref me) = *self;
        me.as_slice()
    }
}

impl Show for GitRevision {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        self.as_slice().fmt(f)
    }
}

/// GitRemote represents a remote repository. It gets cloned into a local
/// GitDatabase.
#[deriving(PartialEq,Clone,Show)]
pub struct GitRemote {
    url: Url,
}

#[deriving(PartialEq,Clone,Encodable)]
struct EncodableGitRemote {
    url: String,
}

impl<E, S: Encoder<E>> Encodable<S, E> for GitRemote {
    fn encode(&self, s: &mut S) -> Result<(), E> {
        EncodableGitRemote {
            url: self.url.to_string()
        }.encode(s)
    }
}

/// GitDatabase is a local clone of a remote repository's database. Multiple
/// GitCheckouts can be cloned from this GitDatabase.
pub struct GitDatabase {
    remote: GitRemote,
    path: Path,
    repo: git2::Repository,
}

#[deriving(Encodable)]
pub struct EncodableGitDatabase {
    remote: GitRemote,
    path: String,
}

impl<E, S: Encoder<E>> Encodable<S, E> for GitDatabase {
    fn encode(&self, s: &mut S) -> Result<(), E> {
        EncodableGitDatabase {
            remote: self.remote.clone(),
            path: self.path.display().to_string()
        }.encode(s)
    }
}

/// GitCheckout is a local checkout of a particular revision. Calling
/// `clone_into` with a reference will resolve the reference into a revision,
/// and return a CargoError if no revision for that reference was found.
pub struct GitCheckout<'a> {
    database: &'a GitDatabase,
    location: Path,
    revision: GitRevision,
    repo: git2::Repository,
}

#[deriving(Encodable)]
pub struct EncodableGitCheckout {
    database: EncodableGitDatabase,
    location: String,
    revision: String,
}

impl<'a, E, S: Encoder<E>> Encodable<S, E> for GitCheckout<'a> {
    fn encode(&self, s: &mut S) -> Result<(), E> {
        EncodableGitCheckout {
            location: self.location.display().to_string(),
            revision: self.revision.to_string(),
            database: EncodableGitDatabase {
                remote: self.database.remote.clone(),
                path: self.database.path.display().to_string(),
            },
        }.encode(s)
    }
}

// Implementations

impl GitRemote {
    pub fn new(url: &Url) -> GitRemote {
        GitRemote { url: url.clone() }
    }

    pub fn get_url(&self) -> &Url {
        &self.url
    }

    pub fn rev_for<S: Str>(&self, path: &Path, reference: S)
                           -> CargoResult<GitRevision> {
        let db = try!(self.db_at(path));
        db.rev_for(reference)
    }

    pub fn checkout(&self, into: &Path) -> CargoResult<GitDatabase> {
        let repo = match git2::Repository::open(into) {
            Ok(repo) => {
                try!(self.fetch_into(&repo).chain_error(|| {
                    internal(format!("failed to fetch into {}", into.display()))
                }));
                repo
            }
            Err(..) => {
                try!(self.clone_into(into).chain_error(|| {
                    internal(format!("failed to clone into: {}", into.display()))
                }))
            }
        };

        Ok(GitDatabase { remote: self.clone(), path: into.clone(), repo: repo })
    }

    pub fn db_at(&self, db_path: &Path) -> CargoResult<GitDatabase> {
        let repo = try!(git2::Repository::open(db_path));
        Ok(GitDatabase {
            remote: self.clone(),
            path: db_path.clone(),
            repo: repo,
        })
    }

    fn fetch_into(&self, dst: &git2::Repository) -> CargoResult<()> {
        let url = self.url.to_string();
        let refspec = "refs/heads/*:refs/heads/*";
        let mut remote = try!(dst.remote_create_anonymous(url.as_slice(),
                                                          refspec));
        try!(remote.add_fetch("refs/tags/*:refs/tags/*"));
        try!(remote.fetch(None, None));
        Ok(())
    }

    fn clone_into(&self, dst: &Path) -> CargoResult<git2::Repository> {
        let url = self.url.to_string();
        if dst.exists() {
            try!(rmdir_recursive(dst));
        }
        try!(mkdir_recursive(dst, UserDir));
        let repo = try!(git2::build::RepoBuilder::new().bare(true)
                                                       .hardlinks(false)
                                                       .clone(url.as_slice(), dst));
        Ok(repo)
    }
}

impl GitDatabase {
    fn get_path<'a>(&'a self) -> &'a Path {
        &self.path
    }

    pub fn copy_to(&self, rev: GitRevision, dest: &Path)
                   -> CargoResult<GitCheckout> {
        match git2::Repository::open(dest) {
            Ok(repo) => {
                let is_fresh = match repo.revparse_single("HEAD") {
                    Ok(head) => head.id().to_string() == rev.to_string(),
                    _ => false,
                };
                if is_fresh {
                    return Ok(GitCheckout::new(dest, self, rev, repo))
                }
            }
            _ => {}
        }

        GitCheckout::clone_into(dest, self, rev)
    }

    pub fn rev_for<S: Str>(&self, reference: S) -> CargoResult<GitRevision> {
        let rev = try!(self.repo.revparse_single(reference.as_slice()));
        Ok(GitRevision(rev.id().to_string()))
    }

    pub fn has_ref<S: Str>(&self, reference: S) -> CargoResult<()> {
        try!(self.repo.revparse_single(reference.as_slice()));
        Ok(())
    }
}

impl<'a> GitCheckout<'a> {
    fn new<'a>(path: &Path, database: &'a GitDatabase, revision: GitRevision,
               repo: git2::Repository)
               -> GitCheckout<'a>
    {
        GitCheckout {
            location: path.clone(),
            database: database,
            revision: revision,
            repo: repo,
        }
    }

    fn clone_into<'a>(into: &Path, database: &'a GitDatabase,
                      revision: GitRevision)
                      -> CargoResult<GitCheckout<'a>>
    {
        let repo = try!(GitCheckout::clone_repo(database.get_path(), into));
        let checkout = GitCheckout::new(into, database, revision, repo);

        try!(checkout.reset().chain_error(|| {
            internal("failed to reset to the right revision")
        }));
        try!(checkout.update_submodules().chain_error(|| {
            internal("failed to update submodules")
        }));

        Ok(checkout)
    }

    pub fn get_rev(&self) -> &str {
        self.revision.as_slice()
    }

    fn clone_repo(source: &Path, into: &Path) -> CargoResult<git2::Repository> {
        let dirname = into.dir_path();

        try!(mkdir_recursive(&dirname, UserDir).chain_error(|| {
            human(format!("Couldn't mkdir {}", dirname.display()))
        }));

        if into.exists() {
            try!(rmdir_recursive(into).chain_error(|| {
                human(format!("Couldn't rmdir {}", into.display()))
            }));
        }

        let url = try!(source.to_url().map_err(human));
        let url = url.to_string();
        let repo = try!(git2::Repository::clone(url.as_slice(),
                                                into).chain_error(|| {
            internal(format!("failed to clone {} into {}", source.display(),
                             into.display()))
        }));
        Ok(repo)
    }

    fn reset(&self) -> CargoResult<()> {
        info!("reset {} to {}", self.repo.path().display(),
              self.revision.as_slice());
        let oid = try!(git2::Oid::from_str(self.revision.as_slice()));
        let object = try!(git2::Object::lookup(&self.repo, oid, None));
        try!(self.repo.reset(&object, git2::Hard, None, None));
        Ok(())
    }

    fn update_submodules(&self) -> CargoResult<()> {
        return update_submodules(&self.repo);

        fn update_submodules(repo: &git2::Repository) -> CargoResult<()> {
            info!("update submodules for: {}", repo.path().display());

            for mut child in try!(repo.submodules()).move_iter() {
                try!(child.init(false));
                let url = try!(child.url().require(|| {
                    internal("non-utf8 url for submodule")
                }));

                // A submodule which is listed in .gitmodules but not actually
                // checked out will not have a head id, so we should ignore it.
                let head = match child.head_id() {
                    Some(head) => head,
                    None => continue,
                };

                // If the submodule hasn't been checked out yet, we need to
                // clone it. If it has been checked out and the head is the same
                // as the submodule's head, then we can bail out and go to the
                // next submodule.
                let repo = match child.open() {
                    Ok(repo) => {
                        if child.head_id() == try!(repo.head()).target() {
                            continue
                        }
                        repo
                    }
                    Err(..) => {
                        let path = repo.path().dir_path().join(child.path());
                        try!(git2::Repository::clone(url, &path))
                    }
                };

                // Fetch data from origin and reset to the head commit
                let refspec = "refs/heads/*:refs/heads/*";
                let mut remote = try!(repo.remote_create_anonymous(url, refspec));
                try!(remote.fetch(None, None).chain_error(|| {
                    internal(format!("failed to fetch submodule `{}` from {}",
                                     child.name().unwrap_or(""), url))
                }));

                let obj = try!(git2::Object::lookup(&repo, head, None));
                try!(repo.reset(&obj, git2::Hard, None, None));
                try!(update_submodules(&repo));
            }
            Ok(())
        }
    }
}
<|endoftext|>


<commit_before>// STD Dependencies -----------------------------------------------------------
use std::fmt;

// External Dependencies ------------------------------------------------------
use chrono;
use rayon::iter::{ParallelIterator, IntoParallelIterator};

// Discord Dependencies -------------------------------------------------------
use discord::model::{ChannelId, ServerId};

// Internal Dependencies ------------------------------------------------------
use ::bot::{Bot, BotConfig};
use ::core::EventQueue;
use ::action::{ActionHandler, ActionGroup, MessageActions};
use ::db::models::Streamer;

// Stream Online Check Implementation -----------------------------------------
pub struct Action {
    server_id: ServerId
}

impl Action {
    pub fn new(server_id: ServerId) -> Box<Action> {
        Box::new(Action {
            server_id: server_id
        })
    }
}

impl ActionHandler for Action {

    fn run(&mut self, bot: &mut Bot, config: &BotConfig, _: &mut EventQueue) -> ActionGroup {
        if let Some(server) = bot.get_server(&self.server_id) {

            // Run HTTP requests in parallel
            let states: Vec<(Streamer, Option<bool>, ActionGroup)> = server.list_streamers().into_par_iter().map(|streamer| {
                check_stream(config, streamer)

            }).collect();

            // Then check results in the bot thread
            states.into_iter().flat_map(|(streamer, state, actions)| {

                // Update last known streaming state in database
                if let Some(state) = state {
                    server.update_streamer_online_state(
                        &streamer.twitch_nick,
                        state
                    );
                }

                actions

            }).collect()

        } else {
            vec![]
        }
    }

}

impl fmt::Display for Action {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "[OnlineCheckAction]")
    }
}

fn check_stream(config: &BotConfig, streamer: Streamer) -> (Streamer, Option<bool>, ActionGroup) {
    match super::stream::get_stream(config, &streamer.twitch_nick) {
        Ok(stream) => {

            let is_online = stream.stream_type == "live";
            if is_online != streamer.is_online {

                let channel_id: u64 = streamer.channel_id.parse().expect("Invalid channel id!");
                let channel_id = ChannelId(channel_id);

                if is_online {
                    info!(
                        "[Twitch] \"{}\" is now online, playing \"{}\" in {}p for {} viewers.",
                        streamer.twitch_nick,
                        stream.game,
                        stream.resolution,
                        stream.viewers
                    );

                    // Wait at least 60 seconds between every online announcement
                    let now = chrono::UTC::now().timestamp() as i32;
                    let actions: ActionGroup = if now > streamer.last_online + 60 {
                        vec![
                            MessageActions::Send::single_public_channel(&channel_id, format!(
                                "@here Twitch streamer **{}** is now online, playing **{}** in {}p for {} viewers!",
                                streamer.twitch_nick,
                                stream.game,
                                stream.resolution,
                                stream.viewers
                            )),
                            MessageActions::Send::single_public_channel(&channel_id, format!(
                                "https://twitch.tv/{}",
                                streamer.twitch_nick,
                            ))
                        ]

                    } else {
                        vec![]
                    };
                    (streamer, Some(true), actions)

                } else {
                    info!( "[Twitch] \"{}\" is now offline.", streamer.twitch_nick);
                    (streamer, Some(false), vec![])
                }

            } else {
                info!("[Twitch] No state change for \"{}\"", streamer.twitch_nick);
                (streamer, None, vec![])
            }

        },
        Err(_) => {
            warn!(
                "[Twitch] Failed to query twitch API for \"{}\".",
                streamer.twitch_nick
            );
            (streamer, None, vec![])
        }
    }
}

<commit_msg>Update streamer online notifications to not post the large stream preview image.<commit_after>// STD Dependencies -----------------------------------------------------------
use std::fmt;

// External Dependencies ------------------------------------------------------
use chrono;
use rayon::iter::{ParallelIterator, IntoParallelIterator};

// Discord Dependencies -------------------------------------------------------
use discord::model::{ChannelId, ServerId};

// Internal Dependencies ------------------------------------------------------
use ::bot::{Bot, BotConfig};
use ::core::EventQueue;
use ::action::{ActionHandler, ActionGroup, MessageActions};
use ::db::models::Streamer;

// Stream Online Check Implementation -----------------------------------------
pub struct Action {
    server_id: ServerId
}

impl Action {
    pub fn new(server_id: ServerId) -> Box<Action> {
        Box::new(Action {
            server_id: server_id
        })
    }
}

impl ActionHandler for Action {

    fn run(&mut self, bot: &mut Bot, config: &BotConfig, _: &mut EventQueue) -> ActionGroup {
        if let Some(server) = bot.get_server(&self.server_id) {

            // Run HTTP requests in parallel
            let states: Vec<(Streamer, Option<bool>, ActionGroup)> = server.list_streamers().into_par_iter().map(|streamer| {
                check_stream(config, streamer)

            }).collect();

            // Then check results in the bot thread
            states.into_iter().flat_map(|(streamer, state, actions)| {

                // Update last known streaming state in database
                if let Some(state) = state {
                    server.update_streamer_online_state(
                        &streamer.twitch_nick,
                        state
                    );
                }

                actions

            }).collect()

        } else {
            vec![]
        }
    }

}

impl fmt::Display for Action {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "[OnlineCheckAction]")
    }
}

fn check_stream(config: &BotConfig, streamer: Streamer) -> (Streamer, Option<bool>, ActionGroup) {
    match super::stream::get_stream(config, &streamer.twitch_nick) {
        Ok(stream) => {

            let is_online = stream.stream_type == "live";
            if is_online != streamer.is_online {

                let channel_id: u64 = streamer.channel_id.parse().expect("Invalid channel id!");
                let channel_id = ChannelId(channel_id);

                if is_online {
                    info!(
                        "[Twitch] \"{}\" is now online, playing \"{}\" in {}p for {} viewers.",
                        streamer.twitch_nick,
                        stream.game,
                        stream.resolution,
                        stream.viewers
                    );

                    // Wait at least 60 seconds between every online announcement
                    let now = chrono::UTC::now().timestamp() as i32;
                    let actions: ActionGroup = if now > streamer.last_online + 60 {
                        vec![
                            MessageActions::Send::single_public_channel(&channel_id, format!(
                                "@here Twitch streamer **{}** is now online, playing **{}** in {}p for {} viewers!",
                                streamer.twitch_nick,
                                stream.game,
                                stream.resolution,
                                stream.viewers
                            )),
                            MessageActions::Send::single_public_channel(&channel_id, format!(
                                "__https://twitch.tv/{}__",
                                streamer.twitch_nick,
                            ))
                        ]

                    } else {
                        vec![]
                    };
                    (streamer, Some(true), actions)

                } else {
                    info!( "[Twitch] \"{}\" is now offline.", streamer.twitch_nick);
                    (streamer, Some(false), vec![])
                }

            } else {
                info!("[Twitch] No state change for \"{}\"", streamer.twitch_nick);
                (streamer, None, vec![])
            }

        },
        Err(_) => {
            warn!(
                "[Twitch] Failed to query twitch API for \"{}\".",
                streamer.twitch_nick
            );
            (streamer, None, vec![])
        }
    }
}

<|endoftext|>