<commit_before>// TODO: Refactor using a matrix for performance

use redox::*;

mod cmd;

#[derive(Copy, Clone, PartialEq, Eq)]
pub enum Mode {
    Insert,
    Normal,
}

pub struct Editor {
    url: String,
    file: Option<File>,
    string: String,
    offset: usize,
    scroll_x: isize,
    scroll_y: isize,
}

impl Editor {
    #[inline(never)]
    pub fn new() -> Self {
        Editor {
            url: String::new(),
            file: Option::None,
            string: String::new(),
            offset: 0,
            scroll_x: 0,
            scroll_y: 0,
        }
    }

    fn backspace(&mut self, window: &mut Window) {
        if self.offset > 0 {
            window.set_title(&format!("{}{}{}","Editor (", &self.url, ") Changed"));
            self.string = self.string[0 .. self.offset - 1].to_string() +
                &self.string[self.offset .. self.string.len()];
            self.offset -= 1;
        }
    }

    fn delete(&mut self, window: &mut Window) {
        if self.offset < self.string.len() {
            window.set_title(&format!("{}{}{}","Editor (", &self.url, ") Changed"));
            self.string = self.string[0 .. self.offset].to_string() +
                &self.string[self.offset + 1 .. self.string.len()];
        }
    }

    // TODO: Add methods for multiple movements
    fn up(&mut self) {
        let mut new_offset = 0;
        for i in 2..self.offset {
            match self.string.as_bytes()[self.offset - i] {
                0 => break,
                10 => {
                    new_offset = self.offset - i + 1;
                    break;
                }
                _ => (),
            }
        }
        self.offset = new_offset;
    }

    fn left(&mut self) {
        if self.offset > 0 {
            self.offset -= 1;
        }
    }

    fn right(&mut self) {
        if self.offset < self.string.len() {
            self.offset += 1;
        }
    }

    fn down(&mut self) {
        let mut new_offset = self.string.len();
        for i in self.offset..self.string.len() {
            match self.string.as_bytes()[i] {
                0 => break,
                10 => {
                    new_offset = i + 1;
                    break;
                }
                _ => (),
            }
        }
        self.offset = new_offset;
    }

    fn cur(&self) -> char {
        self.string.chars().nth(self.offset).unwrap_or('\0')
    }

    fn insert(&mut self, c: char, window: &mut Window) {
        window.set_title(&format!("{}{}{}","self (", &self.url, ") Changed"));
        self.string = self.string[0 .. self.offset].to_string() +
            &c.to_string() +
            &self.string[self.offset .. self.string.len()];
        self.offset += 1;
    }

    fn reload(&mut self, window: &mut Window) {
        window.set_title(&("Editor (".to_string() + &self.url + ")"));
        self.offset = 0;
        self.scroll_x = 0;
        self.scroll_y = 0;

        match self.file {
            Option::Some(ref mut file) => {
                file.seek(Seek::Start(0));
                let mut vec: Vec<u8> = Vec::new();
                file.read_to_end(&mut vec);
                self.string = unsafe { String::from_utf8_unchecked(vec) };
            },
            Option::None => self.string = String::new(),
        }
    }

    fn save(&mut self, window: &mut Window) {
        match self.file {
            Option::Some(ref mut file) => {
                window.set_title(&("Editor (".to_string() + &self.url + ") Saved"));
                file.seek(Seek::Start(0));
                file.write(&self.string.as_bytes());
                file.sync();
            }
            Option::None => {
                //TODO: Ask for file to save to
                window.set_title(&("Editor (".to_string() + &self.url + ") No Open File"));
            }
        }
    }

    fn draw_content(&mut self, window: &mut Window) {
        let mut redraw = false;

        {
            window.set([255, 255, 255, 255]);

            let scroll_x = self.scroll_x;
            let scroll_y = self.scroll_y;

            let mut offset = 0;

            let mut col = -scroll_x;
            let cols = window.width() as isize / 8;

            let mut row = -scroll_y;
            let rows = window.height() as isize / 16;

            for c in self.string.chars() {
                if offset == self.offset {
                    if col >= 0 && col < cols && row >= 0 && row < rows {
                        window.char(8 * col, 16 * row, '_', [128, 128, 128, 255]);
                    } else {
                        if col < 0 { //Too far to the left
                            self.scroll_x += col;
                        } else if col >= cols { //Too far to the right
                            self.scroll_x += cols - col + 1;
                        }
                        if row < 0 { //Too far up
                            self.scroll_y += row;
                        } else if row >= rows { //Too far down
                            self.scroll_y += rows - row + 1;
                        }

                        redraw = true;
                    }
                }

                if c == '\n' {
                    col = -scroll_x;
                    row += 1;
                } else if c == '\t' {
                    col += 8 - col % 8;
                } else {
                    if col >= 0 && col < cols && row >= 0 && row < rows {
                        window.char(8 * col, 16 * row, c, [0, 0, 0, 255]);
                    }
                    col += 1;
                }

                offset += 1;
            }

            if offset == self.offset {
                if col >= 0 && col < cols && row >= 0 && row < rows {
                    window.char(8 * col, 16 * row, '_', [128, 128, 128, 255]);
                } else {
                    if col < 0 { //Too far to the left
                        self.scroll_x += col;
                    } else if col >= cols { //Too far to the right
                        self.scroll_x += cols - col + 1;
                    }
                    if row < 0 { //Too far up
                        self.scroll_y += row;
                    } else if row >= rows { //Too far down
                        self.scroll_y += rows - row + 1;
                    }

                    redraw = true;
                }
            }

            window.sync();
        }

        if redraw {
            self.draw_content(window);
        }
    }

    fn main(&mut self, url: &str) {
        let mut window = Window::new((rand() % 400 + 50) as isize,
        (rand() % 300 + 50) as isize,
        576,
        400,
        &("Editor (".to_string() + url + ")"));

        self.url = url.to_string();
        self.file = Option::Some(File::open(&self.url));

        self.reload(&mut window);
        self.draw_content(&mut window);

        let mut mode = Mode::Normal;

        let mut last_change = String::new();
        let mut multiplier: Option<u32> = None;
        let mut swap = 0;
        let mut period = String::new();
        let mut is_recording = false;

        while let Option::Some(event) = window.poll() {
            match event.to_option() {
                EventOption::Key(key_event) => {
                    if key_event.pressed {
                        cmd::exec(self, &mut mode, &mut multiplier, &mut last_change, key_event, &mut window, &mut swap, &mut period, &mut is_recording);

                        self.draw_content(&mut window);
                    }
                }
                _ => (),
            }
        }
    }
}

pub fn main() {
    match args().get(1) {
        Option::Some(arg) => Editor::new().main(&arg),
        Option::None => Editor::new().main("none://"),
    }
}
<commit_msg>Make movement more logical<commit_after>// TODO: Refactor using a matrix for performance

use redox::*;

mod cmd;

#[derive(Copy, Clone, PartialEq, Eq)]
pub enum Mode {
    Insert,
    Normal,
}

pub struct Editor {
    url: String,
    file: Option<File>,
    string: String,
    offset: usize,
    scroll_x: isize,
    scroll_y: isize,
}

impl Editor {
    #[inline(never)]
    pub fn new() -> Self {
        Editor {
            url: String::new(),
            file: Option::None,
            string: String::new(),
            offset: 0,
            scroll_x: 0,
            scroll_y: 0,
        }
    }

    fn backspace(&mut self, window: &mut Window) {
        if self.offset > 0 {
            window.set_title(&format!("{}{}{}","Editor (", &self.url, ") Changed"));
            self.string = self.string[0 .. self.offset - 1].to_string() +
                &self.string[self.offset .. self.string.len()];
            self.offset -= 1;
        }
    }

    fn delete(&mut self, window: &mut Window) {
        if self.offset < self.string.len() {
            window.set_title(&format!("{}{}{}","Editor (", &self.url, ") Changed"));
            self.string = self.string[0 .. self.offset].to_string() +
                &self.string[self.offset + 1 .. self.string.len()];
        }
    }

    // TODO: Add methods for multiple movements
    fn up(&mut self) {
        let x = self.get_x() - if self.cur() == '\n' { 1 } else { 0 };
        while self.cur() != '\n' {
            self.left();
        }
        self.right();
        let mut new_offset = 0;

        for i in 2..self.offset {
            match self.string.as_bytes()[self.offset - i] {
                0 => break,
                10 => {
                    new_offset = self.offset - i + 1;
                    break;
                }
                _ => (),
            }
        }
        self.offset = new_offset;
        for _ in 1..x {
            if self.cur() != '\n' {
                self.right();
            } else {
                break;
            }
        }
    }

    fn left(&mut self) {
        if self.offset > 0 {
            self.offset -= 1;
        }
    }

    fn right(&mut self) {
        if self.offset < self.string.len() {
            self.offset += 1;
        }
    }

    fn down(&mut self) {
        let x = self.get_x() - if self.cur() == '\n' { 1 } else { 0 };
        let mut new_offset = self.string.len();

        for i in self.offset..self.string.len() {
            match self.string.as_bytes()[i] {
                0 => break,
                10 => {
                    new_offset = i + 1;
                    break;
                }
                _ => (),
            }
        }
        self.offset = new_offset;
        for _ in 1..x {
            if self.cur() != '\n' {
                self.right();
            } else {
                break;
            }
        }
    }

    fn cur(&self) -> char {
        self.string.chars().nth(self.offset).unwrap_or('\0')
    }

    fn insert(&mut self, c: char, window: &mut Window) {
        window.set_title(&format!("{}{}{}","self (", &self.url, ") Changed"));
        self.string = self.string[0 .. self.offset].to_string() +
            &c.to_string() +
            &self.string[self.offset .. self.string.len()];
        self.offset += 1;
    }

    fn reload(&mut self, window: &mut Window) {
        window.set_title(&("Editor (".to_string() + &self.url + ")"));
        self.offset = 0;
        self.scroll_x = 0;
        self.scroll_y = 0;

        match self.file {
            Option::Some(ref mut file) => {
                file.seek(Seek::Start(0));
                let mut vec: Vec<u8> = Vec::new();
                file.read_to_end(&mut vec);
                self.string = unsafe { String::from_utf8_unchecked(vec) };
            },
            Option::None => self.string = String::new(),
        }
    }

    fn save(&mut self, window: &mut Window) {
        match self.file {
            Option::Some(ref mut file) => {
                window.set_title(&("Editor (".to_string() + &self.url + ") Saved"));
                file.seek(Seek::Start(0));
                file.write(&self.string.as_bytes());
                file.sync();
            }
            Option::None => {
                //TODO: Ask for file to save to
                window.set_title(&("Editor (".to_string() + &self.url + ") No Open File"));
            }
        }
    }

    fn get_x(&self) -> usize {
        let mut x = 0;
        for (n, c) in self.string.chars().enumerate() {
            if c == '\n' {
                x = 0;
            } else {
                x += 1;
            }
            if n >= self.offset {
                break;
            }
        }
        x
    }

    fn draw_content(&mut self, window: &mut Window) {
        let mut redraw = false;

        {
            window.set([255, 255, 255, 255]);

            let scroll_x = self.scroll_x;
            let scroll_y = self.scroll_y;

            let mut offset = 0;

            let mut col = -scroll_x;
            let cols = window.width() as isize / 8;

            let mut row = -scroll_y;
            let rows = window.height() as isize / 16;

            for c in self.string.chars() {
                if offset == self.offset {
                    if col >= 0 && col < cols && row >= 0 && row < rows {
                        window.char(8 * col, 16 * row, '_', [128, 128, 128, 255]);
                    } else {
                        if col < 0 { //Too far to the left
                            self.scroll_x += col;
                        } else if col >= cols { //Too far to the right
                            self.scroll_x += cols - col + 1;
                        }
                        if row < 0 { //Too far up
                            self.scroll_y += row;
                        } else if row >= rows { //Too far down
                            self.scroll_y += rows - row + 1;
                        }

                        redraw = true;
                    }
                }

                if c == '\n' {
                    col = -scroll_x;
                    row += 1;
                } else if c == '\t' {
                    col += 8 - col % 8;
                } else {
                    if col >= 0 && col < cols && row >= 0 && row < rows {
                        window.char(8 * col, 16 * row, c, [0, 0, 0, 255]);
                    }
                    col += 1;
                }

                offset += 1;
            }

            if offset == self.offset {
                if col >= 0 && col < cols && row >= 0 && row < rows {
                    window.char(8 * col, 16 * row, '_', [128, 128, 128, 255]);
                } else {
                    if col < 0 { //Too far to the left
                        self.scroll_x += col;
                    } else if col >= cols { //Too far to the right
                        self.scroll_x += cols - col + 1;
                    }
                    if row < 0 { //Too far up
                        self.scroll_y += row;
                    } else if row >= rows { //Too far down
                        self.scroll_y += rows - row + 1;
                    }

                    redraw = true;
                }
            }

            window.sync();
        }

        if redraw {
            self.draw_content(window);
        }
    }

    fn main(&mut self, url: &str) {
        let mut window = Window::new((rand() % 400 + 50) as isize,
        (rand() % 300 + 50) as isize,
        576,
        400,
        &("Editor (".to_string() + url + ")"));

        self.url = url.to_string();
        self.file = Option::Some(File::open(&self.url));

        self.reload(&mut window);
        self.draw_content(&mut window);

        let mut mode = Mode::Normal;

        let mut last_change = String::new();
        let mut multiplier: Option<u32> = None;
        let mut swap = 0;
        let mut period = String::new();
        let mut is_recording = false;

        while let Option::Some(event) = window.poll() {
            match event.to_option() {
                EventOption::Key(key_event) => {
                    if key_event.pressed {
                        cmd::exec(self, &mut mode, &mut multiplier, &mut last_change, key_event, &mut window, &mut swap, &mut period, &mut is_recording);

                        self.draw_content(&mut window);
                    }
                }
                _ => (),
            }
        }
    }
}

pub fn main() {
    match args().get(1) {
        Option::Some(arg) => Editor::new().main(&arg),
        Option::None => Editor::new().main("none://"),
    }
}
<|endoftext|>


<commit_before>use redox::*;

#[derive(Copy, Clone)]
pub enum Mode {
    Insert,
    Normal,
}

pub struct Editor {
    url: String,
    file: Option<File>,
    string: String,
    offset: usize,
    scroll_x: isize,
    scroll_y: isize,
}

impl Editor {
    #[inline(never)]
    pub fn new() -> Self {
        Editor {
            url: String::new(),
            file: Option::None,
            string: String::new(),
            offset: 0,
            scroll_x: 0,
            scroll_y: 0,
        }
    }

    fn backspace(&mut self, window: &mut Window) {
         if self.offset > 0 {
             window.set_title(&format!("{}{}{}","Editor (", &self.url, ") Changed"));
             self.string = self.string[0 .. self.offset - 1].to_string() +
                 &self.string[self.offset .. self.string.len()];
             self.offset -= 1;
         }
    }

    fn delete(&mut self, window: &mut Window) {
        if self.offset < self.string.len() {
            window.set_title(&format!("{}{}{}","Editor (", &self.url, ") Changed"));
            self.string = self.string[0 .. self.offset + 1].to_string() +
                &self.string[self.offset + 1 .. self.string.len() - 1];
        }
    }

    fn up(&mut self) {
        let mut new_offset = 0;
        for i in 2..self.offset {
            match self.string.as_bytes()[self.offset - i] {
                0 => break,
                10 => {
                    new_offset = self.offset - i + 1;
                    break;
                }
                _ => (),
            }
        }
        self.offset = new_offset;
    }

    fn left(&mut self) {
        if self.offset > 0 {
            self.offset -= 1;
        }
    }

    fn right(&mut self) {
        if self.offset < self.string.len() {
            self.offset += 1;
        }
    }

    fn down(&mut self) {
        let mut new_offset = self.string.len();
        for i in self.offset..self.string.len() {
            match self.string.as_bytes()[i] {
                0 => break,
                10 => {
                    new_offset = i + 1;
                    break;
                }
                _ => (),
            }
        }
        self.offset = new_offset;
    }

    fn reload(&mut self, window: &mut Window) {
        window.set_title(&("Editor (".to_string() + &self.url + ")"));
        self.offset = 0;
        self.scroll_x = 0;
        self.scroll_y = 0;

        match self.file {
            Option::Some(ref mut file) => {
                file.seek(Seek::Start(0));
                let mut vec: Vec<u8> = Vec::new();
                file.read_to_end(&mut vec);
                self.string = unsafe { String::from_utf8_unchecked(vec) };
            },
            Option::None => self.string = String::new(),
        }
    }

    fn save(&mut self, window: &mut Window) {
        match self.file {
            Option::Some(ref mut file) => {
                window.set_title(&("Editor (".to_string() + &self.url + ") Saved"));
                file.seek(Seek::Start(0));
                file.write(&self.string.as_bytes());
                file.sync();
            }
            Option::None => {
                //TODO: Ask for file to save to
                window.set_title(&("Editor (".to_string() + &self.url + ") No Open File"));
            }
        }
    }

    fn draw_content(&mut self, window: &mut Window) {
        let mut redraw = false;

        {
            window.set([255, 255, 255, 255]);

            let scroll_x = self.scroll_x;
            let scroll_y = self.scroll_y;

            let mut offset = 0;

            let mut col = -scroll_x;
            let cols = window.width() as isize / 8;

            let mut row = -scroll_y;
            let rows = window.height() as isize / 16;

            for c in self.string.chars() {
                if offset == self.offset {
                    if col >= 0 && col < cols && row >= 0 && row < rows {
                        window.char(8 * col, 16 * row, '_', [128, 128, 128, 255]);
                    } else {
                        if col < 0 { //Too far to the left
                            self.scroll_x += col;
                        } else if col >= cols { //Too far to the right
                            self.scroll_x += cols - col + 1;
                        }
                        if row < 0 { //Too far up
                            self.scroll_y += row;
                        } else if row >= rows { //Too far down
                            self.scroll_y += rows - row + 1;
                        }

                        redraw = true;
                    }
                }

                if c == '\n' {
                    col = -scroll_x;
                    row += 1;
                } else if c == '\t' {
                    col += 8 - col % 8;
                } else {
                    if col >= 0 && col < cols && row >= 0 && row < rows {
                        window.char(8 * col, 16 * row, c, [0, 0, 0, 255]);
                    }
                    col += 1;
                }

                offset += 1;
            }

            if offset == self.offset {
                if col >= 0 && col < cols && row >= 0 && row < rows {
                    window.char(8 * col, 16 * row, '_', [128, 128, 128, 255]);
                } else {
                    if col < 0 { //Too far to the left
                        self.scroll_x += col;
                    } else if col >= cols { //Too far to the right
                        self.scroll_x += cols - col + 1;
                    }
                    if row < 0 { //Too far up
                        self.scroll_y += row;
                    } else if row >= rows { //Too far down
                        self.scroll_y += rows - row + 1;
                    }

                    redraw = true;
                }
            }

            window.sync();
        }

        if redraw {
            self.draw_content(window);
        }
    }

    fn main(&mut self, url: &str) {
        let mut window = Window::new((rand() % 400 + 50) as isize,
                                     (rand() % 300 + 50) as isize,
                                     576,
                                     400,
                                      &("Editor (".to_string() + url + ")"));

        self.url = url.to_string();
        self.file = Option::Some(File::open(&self.url));

        self.reload(&mut window);
        self.draw_content(&mut window);

        let mut mode = Mode::Normal;

        let mut last_change = String::new();

        while let Option::Some(event) = window.poll() {
            match event.to_option() {
                EventOption::Key(key_event) => {
                    if key_event.pressed {
                        use self::Mode::*;
                        match (mode, key_event.scancode) {
                            (Insert, K_ESC) => {
                                mode = Normal;
                            },
                            (Insert, K_BKSP) => self.backspace(&mut window),
                            (Insert, K_DEL) => self.delete(&mut window),
                            (_, K_F5) => self.reload(&mut window),
                            (_, K_F6) => self.save(&mut window),
                            (_, K_HOME) => self.offset = 0,
                            (_, K_UP) => self.up(),
                            (_, K_LEFT) => self.left(),
                            (_, K_RIGHT) => self.right(),
                            (_, K_END) => self.offset = self.string.len(),
                            (_, K_DOWN) => self.down(),
                            (m, _) => match (m, key_event.character) {
                                (Normal, 'i') => {
                                    mode = Insert;
                                    last_change = self.string.clone();
                                },
                                (Normal, 'h') => self.left(),
                                (Normal, 'l') => self.right(),
                                (Normal, 'k') => self.up(),
                                (Normal, 'j') => self.down(),
                                (Normal, 'G') => self.offset = self.string.len(),
                                (Normal, 'a') => {
                                    self.right();
                                    mode = Insert;
                                    last_change = self.string.clone();
                                },
                                (Normal, 'x') => self.delete(&mut window),
                                (Normal, 'X') => self.backspace(&mut window),
                                (Normal, 'u') => {
                                    self.offset = 0;
                                    ::core::mem::swap(&mut last_change, &mut self.string);
                                },
                                (Insert, '\0') => (),
                                (Insert, _) => {
                                    window.set_title(&format!("{}{}{}","Editor (", &self.url, ") Changed"));
                                    self.string = self.string[0 .. self.offset].to_string() +
                                                  &key_event.character.to_string() +
                                                  &self.string[self.offset .. self.string.len()];
                                    self.offset += 1;
                                },
                                _ => {},
                            }
                        }

                        self.draw_content(&mut window);
                    }
                }
                _ => (),
            }
        }
    }
}

pub fn main() {
    match args().get(1) {
        Option::Some(arg) => Editor::new().main(&arg),
        Option::None => Editor::new().main("none://"),
    }
}
<commit_msg>Add numerals (multiple commands at once), s.a. `2j`.<commit_after>use redox::*;

#[derive(Copy, Clone)]
pub enum Mode {
    Insert,
    Normal,
}

pub struct Editor {
    url: String,
    file: Option<File>,
    string: String,
    offset: usize,
    scroll_x: isize,
    scroll_y: isize,
}

impl Editor {
    #[inline(never)]
    pub fn new() -> Self {
        Editor {
            url: String::new(),
            file: Option::None,
            string: String::new(),
            offset: 0,
            scroll_x: 0,
            scroll_y: 0,
        }
    }

    fn backspace(&mut self, window: &mut Window) {
         if self.offset > 0 {
             window.set_title(&format!("{}{}{}","Editor (", &self.url, ") Changed"));
             self.string = self.string[0 .. self.offset - 1].to_string() +
                 &self.string[self.offset .. self.string.len()];
             self.offset -= 1;
         }
    }

    fn delete(&mut self, window: &mut Window) {
        if self.offset < self.string.len() {
            window.set_title(&format!("{}{}{}","Editor (", &self.url, ") Changed"));
            self.string = self.string[0 .. self.offset + 1].to_string() +
                &self.string[self.offset + 1 .. self.string.len() - 1];
        }
    }

    fn up(&mut self) {
        let mut new_offset = 0;
        for i in 2..self.offset {
            match self.string.as_bytes()[self.offset - i] {
                0 => break,
                10 => {
                    new_offset = self.offset - i + 1;
                    break;
                }
                _ => (),
            }
        }
        self.offset = new_offset;
    }

    fn left(&mut self) {
        if self.offset > 0 {
            self.offset -= 1;
        }
    }

    fn right(&mut self) {
        if self.offset < self.string.len() {
            self.offset += 1;
        }
    }

    fn down(&mut self) {
        let mut new_offset = self.string.len();
        for i in self.offset..self.string.len() {
            match self.string.as_bytes()[i] {
                0 => break,
                10 => {
                    new_offset = i + 1;
                    break;
                }
                _ => (),
            }
        }
        self.offset = new_offset;
    }

    fn reload(&mut self, window: &mut Window) {
        window.set_title(&("Editor (".to_string() + &self.url + ")"));
        self.offset = 0;
        self.scroll_x = 0;
        self.scroll_y = 0;

        match self.file {
            Option::Some(ref mut file) => {
                file.seek(Seek::Start(0));
                let mut vec: Vec<u8> = Vec::new();
                file.read_to_end(&mut vec);
                self.string = unsafe { String::from_utf8_unchecked(vec) };
            },
            Option::None => self.string = String::new(),
        }
    }

    fn save(&mut self, window: &mut Window) {
        match self.file {
            Option::Some(ref mut file) => {
                window.set_title(&("Editor (".to_string() + &self.url + ") Saved"));
                file.seek(Seek::Start(0));
                file.write(&self.string.as_bytes());
                file.sync();
            }
            Option::None => {
                //TODO: Ask for file to save to
                window.set_title(&("Editor (".to_string() + &self.url + ") No Open File"));
            }
        }
    }

    fn draw_content(&mut self, window: &mut Window) {
        let mut redraw = false;

        {
            window.set([255, 255, 255, 255]);

            let scroll_x = self.scroll_x;
            let scroll_y = self.scroll_y;

            let mut offset = 0;

            let mut col = -scroll_x;
            let cols = window.width() as isize / 8;

            let mut row = -scroll_y;
            let rows = window.height() as isize / 16;

            for c in self.string.chars() {
                if offset == self.offset {
                    if col >= 0 && col < cols && row >= 0 && row < rows {
                        window.char(8 * col, 16 * row, '_', [128, 128, 128, 255]);
                    } else {
                        if col < 0 { //Too far to the left
                            self.scroll_x += col;
                        } else if col >= cols { //Too far to the right
                            self.scroll_x += cols - col + 1;
                        }
                        if row < 0 { //Too far up
                            self.scroll_y += row;
                        } else if row >= rows { //Too far down
                            self.scroll_y += rows - row + 1;
                        }

                        redraw = true;
                    }
                }

                if c == '\n' {
                    col = -scroll_x;
                    row += 1;
                } else if c == '\t' {
                    col += 8 - col % 8;
                } else {
                    if col >= 0 && col < cols && row >= 0 && row < rows {
                        window.char(8 * col, 16 * row, c, [0, 0, 0, 255]);
                    }
                    col += 1;
                }

                offset += 1;
            }

            if offset == self.offset {
                if col >= 0 && col < cols && row >= 0 && row < rows {
                    window.char(8 * col, 16 * row, '_', [128, 128, 128, 255]);
                } else {
                    if col < 0 { //Too far to the left
                        self.scroll_x += col;
                    } else if col >= cols { //Too far to the right
                        self.scroll_x += cols - col + 1;
                    }
                    if row < 0 { //Too far up
                        self.scroll_y += row;
                    } else if row >= rows { //Too far down
                        self.scroll_y += rows - row + 1;
                    }

                    redraw = true;
                }
            }

            window.sync();
        }

        if redraw {
            self.draw_content(window);
        }
    }

    fn main(&mut self, url: &str) {
        let mut window = Window::new((rand() % 400 + 50) as isize,
                                     (rand() % 300 + 50) as isize,
                                     576,
                                     400,
                                      &("Editor (".to_string() + url + ")"));

        self.url = url.to_string();
        self.file = Option::Some(File::open(&self.url));

        self.reload(&mut window);
        self.draw_content(&mut window);

        let mut mode = Mode::Normal;

        let mut last_change = String::new();
        let mut multiplier: Option<u32> = None;

        while let Option::Some(event) = window.poll() {
            match event.to_option() {
                EventOption::Key(key_event) => {
                    if key_event.pressed {
                        use self::Mode::*;
                        match (mode, key_event.scancode) {
                            (Insert, K_ESC) => {
                                mode = Normal;
                            },
                            (Insert, K_BKSP) => self.backspace(&mut window),
                            (Insert, K_DEL) => self.delete(&mut window),
                            (_, K_F5) => self.reload(&mut window),
                            (_, K_F6) => self.save(&mut window),
                            (_, K_HOME) => self.offset = 0,
                            (_, K_UP) => self.up(),
                            (_, K_LEFT) => self.left(),
                            (_, K_RIGHT) => self.right(),
                            (_, K_END) => self.offset = self.string.len(),
                            (_, K_DOWN) => self.down(),
                            (m, _) => {
                                let (no_mult, mut times) = match multiplier {
                                    Some(n) => (false, n),
                                    None => (true, 1),
                                };
                                let mut is_none = false;

                                match key_event.character {
                                    '0' if no_mult => times = 0,
                                    '0' => times *= 10,

                                    '1' if no_mult => times = 1,
                                    '1' => times = times * 10 + 1,

                                    '2' if no_mult => times = 2,
                                    '2' => times = times * 10 + 2,

                                    '3' if no_mult => times = 3,
                                    '3' => times = times * 10 + 3,

                                    '4' if no_mult => times = 4,
                                    '4' => times = times * 10 + 4,

                                    '5' if no_mult => times = 5,
                                    '5' => times = times * 10 + 5,

                                    '6' if no_mult => times = 6,
                                    '6' => times = times * 10 + 6,

                                    '7' if no_mult => times = 7,
                                    '7' => times = times * 10 + 7,

                                    '8' if no_mult => times = 8,
                                    '8' => times = times * 10 + 8,

                                    '9' if no_mult => times = 9,
                                    '9' => times = times * 10 + 9,
                                    _ => {
                                        for _ in 0 .. times {
                                            match (m, key_event.character) {
                                                (Normal, 'i') => {
                                                    mode = Insert;
                                                    last_change = self.string.clone();
                                                },
                                                (Normal, 'h') => self.left(),
                                                (Normal, 'l') => self.right(),
                                                (Normal, 'k') => self.up(),
                                                (Normal, 'j') => self.down(),
                                                (Normal, 'G') => self.offset = self.string.len(),
                                                (Normal, 'a') => {
                                                    self.right();
                                                    mode = Insert;
                                                    last_change = self.string.clone();
                                                },
                                                (Normal, 'x') => self.delete(&mut window),
                                                (Normal, 'X') => self.backspace(&mut window),
                                                (Normal, 'u') => {
                                                    self.offset = 0;
                                                    ::core::mem::swap(&mut last_change, &mut self.string);
                                                },
                                                (Insert, '\0') => (),
                                                (Insert, _) => {
                                                    window.set_title(&format!("{}{}{}","Editor (", &self.url, ") Changed"));
                                                    self.string = self.string[0 .. self.offset].to_string() +
                                                        &key_event.character.to_string() +
                                                        &self.string[self.offset .. self.string.len()];
                                                    self.offset += 1;
                                                },
                                                _ => {},
                                            }
                                        }
                                        is_none = true;
                                    }
                                }

                                if !is_none {
                                    multiplier = Some(times);
                                } else {
                                    multiplier = None;
                                }

                            }
                        }

                        self.draw_content(&mut window);
                    }
                }
                _ => (),
            }
        }
    }
}

pub fn main() {
    match args().get(1) {
        Option::Some(arg) => Editor::new().main(&arg),
        Option::None => Editor::new().main("none://"),
    }
}
<|endoftext|>


<commit_before>use lapin::{
    executor::Executor,
    Result,
};

#[derive(Debug)]
pub struct AsyncStdExecutor;

impl Executor for AsyncStdExecutor {
    fn execute(&self, f: Box<dyn FnOnce() + Send>) -> Result<()> {
        async_std::task::spawn(async {
            f();
        });
        Ok(())
    }
}
<commit_msg>implement async-amqp<commit_after>use lapin::{executor::Executor, ConnectionProperties, Result};
use std::sync::Arc;

pub trait LapinAsyncStdExt {
    fn with_async_std(self) -> Self
    where
        Self: Sized;
}

impl LapinAsyncStdExt for ConnectionProperties {
    fn with_async_std(mut self) -> Self {
        self.executor = Some(Arc::new(AsyncStdExecutor));
        self
    }
}

#[derive(Debug)]
struct AsyncStdExecutor;

impl Executor for AsyncStdExecutor {
    fn execute(&self, f: Box<dyn FnOnce() + Send>) -> Result<()> {
        async_std::task::spawn(async {
            f();
        });
        Ok(())
    }
}
<|endoftext|>


<commit_before>#[cfg(target_os = "android")]
#[macro_use]
extern crate android_glue;

extern crate glutin;

use glutin::{Event, ElementState};

mod support;

#[cfg(target_os = "android")]
android_start!(main);

fn main() {
    let window = glutin::WindowBuilder::new().build().unwrap();
    window.set_title("glutin - Cursor grabbing test");
    let _ = unsafe { window.make_current() };

    let context = support::load(&window);
    let mut grabbed = false;

    for event in window.poll_events() {
        match event {
            Event::KeyboardInput(ElementState::Pressed, _, _) => {
                if grabbed {
                    grabbed = false;
                    window.set_cursor_state(glutin::CursorState::Normal)
                          .ok().expect("could not ungrab mouse cursor");
                } else {
                    grabbed = true;
                    window.set_cursor_state(glutin::CursorState::Grab)
                          .ok().expect("could not grab mouse cursor");
                }
            },

            a @ Event::MouseMoved(_) => {
                println!("{:?}", a);
            },

            _ => (),
        }

        context.draw_frame((0.0, 1.0, 0.0, 1.0));
        let _ = window.swap_buffers();
    }
}

<commit_msg>Fixed grabbing example.<commit_after>#[cfg(target_os = "android")]
#[macro_use]
extern crate android_glue;

extern crate glutin;

use glutin::{Event, ElementState};

mod support;

#[cfg(target_os = "android")]
android_start!(main);

fn main() {
    let window = glutin::WindowBuilder::new().build().unwrap();
    window.set_title("glutin - Cursor grabbing test");
    let _ = unsafe { window.make_current() };

    let context = support::load(&window);
    let mut grabbed = false;

    for event in window.wait_events() {
        match event {
            Event::KeyboardInput(ElementState::Pressed, _, _) => {
                if grabbed {
                    grabbed = false;
                    window.set_cursor_state(glutin::CursorState::Normal)
                          .ok().expect("could not ungrab mouse cursor");
                } else {
                    grabbed = true;
                    window.set_cursor_state(glutin::CursorState::Grab)
                          .ok().expect("could not grab mouse cursor");
                }
            },

            Event::Closed => break,

            a @ Event::MouseMoved(_) => {
                println!("{:?}", a);
            },

            _ => (),
        }

        context.draw_frame((0.0, 1.0, 0.0, 1.0));
        let _ = window.swap_buffers();
    }
}
<|endoftext|>


<commit_before>extern crate cabot;

use async_std;
use cabot::{Client, RequestBuilder};

#[async_std::main]
async fn main() {
    let request = RequestBuilder::new("https://www.rust-lang.org/")
        .build()
        .unwrap();
    let client = Client::new();
    let response = client.execute(&request).await.unwrap();
    print!("{}", response.body_as_string().unwrap());
}
<commit_msg>Update example<commit_after>extern crate cabot;

use async_std;
use cabot::{Client, RequestBuilder};

#[async_std::main]
async fn main() {
    let request = RequestBuilder::new("https://www.rust-lang.org")
        .build()
        .unwrap();
    let client = Client::new();
    let response = client.execute(&request).await.unwrap();
    print!("{}", response.body_as_string().unwrap());
}
<|endoftext|>


<commit_before>/* Copyright 2016 Jordan Miner
 *
 * Licensed under the MIT license <LICENSE or
 * http://opensource.org/licenses/MIT>. This file may not be copied,
 * modified, or distributed except according to those terms.
 */

#[macro_use]
extern crate clear_coat;
extern crate iup_sys;
extern crate smallvec;

use std::rc::Rc;
use iup_sys::*;
use smallvec::SmallVec;

use clear_coat::*;
use clear_coat::common_attrs_cbs::*;

struct CursorsCanvas {
    canvas: Canvas,
}

impl CursorsCanvas {
    pub fn new() -> Self {
        CursorsCanvas {
            canvas: Canvas::new(),
        }
    }
}

impl CursorAttribute for CursorsCanvas {}

unsafe impl Control for CursorsCanvas {
    fn handle(&self) -> *mut Ihandle {
        self.canvas.handle()
    }
}

fn create_cursors_page() -> Box<Control> {
    let cursors_canvas = Rc::new(CursorsCanvas::new());

    let radios_info = [
        ("None", Cursor::None),
        ("Arrow", Cursor::Arrow),
        ("Busy", Cursor::Busy),
        ("Cross", Cursor::Cross),
        ("Hand", Cursor::Hand),
        ("Help", Cursor::Help),
        ("Move", Cursor::Move),
        ("ResizeN", Cursor::ResizeN),
        ("ResizeS", Cursor::ResizeS),
        ("ResizeNS", Cursor::ResizeNS),
        ("ResizeW", Cursor::ResizeW),
        ("ResizeE", Cursor::ResizeE),
        ("ResizeWE", Cursor::ResizeWE),
        ("ResizeNE", Cursor::ResizeNE),
        ("ResizeSW", Cursor::ResizeSW),
        ("ResizeNW", Cursor::ResizeNW),
        ("ResizeSE", Cursor::ResizeSE),
        ("Text", Cursor::Text),
    ];

    let mut radios = SmallVec::<[Toggle; 32]>::new();
    for &(text, cur) in radios_info.iter() {
        let toggle = Toggle::new();
        toggle.set_title(text);
        let cursors_canvas2 = cursors_canvas.clone();
        toggle.action_event().add(move |checked| {
            if checked { cursors_canvas2.set_cursor(cur); }
        });
        radios.push(toggle);
    }

    let grid = grid_box!(
        &radios[0],
        &radios[1],
        &radios[2],
        &radios[3],
        &radios[4],
        &radios[5],
        &radios[6],
        &radios[7],
        &radios[8],
        &radios[9],
        &radios[10],
        &radios[11],
        &radios[12],
        &radios[13],
        &radios[14],
        &radios[15],
        &radios[16],
        &radios[17],
    );
    grid.set_num_div(NumDiv::Fixed(2));
    let page = vbox!(
        &cursors_canvas,
        hbox!(
            fill!(),
            &grid,
            fill!(),
        ),
    );
    Box::new(page)
}

fn main() {

    let dialog = Dialog::new();

    let tabs = Tabs::new();

    tabs.append_tabs(&[
        TabInfo::new(&*create_cursors_page()).title("Cursors"),
        TabInfo::new(&Fill::new()).title("File Dialog"),
    ]);

    dialog.append(&tabs).expect("failed to build the window");
    dialog.set_title("Showcase");

    dialog.show_xy(ScreenPosition::Center, ScreenPosition::Center)
          .expect("failed to show the window");
    main_loop();
}
<commit_msg>Fix mouse cursor Toggles overlapping<commit_after>/* Copyright 2016 Jordan Miner
 *
 * Licensed under the MIT license <LICENSE or
 * http://opensource.org/licenses/MIT>. This file may not be copied,
 * modified, or distributed except according to those terms.
 */

#[macro_use]
extern crate clear_coat;
extern crate iup_sys;
extern crate smallvec;

use std::rc::Rc;
use iup_sys::*;
use smallvec::SmallVec;

use clear_coat::*;
use clear_coat::common_attrs_cbs::*;

struct CursorsCanvas {
    canvas: Canvas,
}

impl CursorsCanvas {
    pub fn new() -> Self {
        CursorsCanvas {
            canvas: Canvas::new(),
        }
    }
}

impl CursorAttribute for CursorsCanvas {}

unsafe impl Control for CursorsCanvas {
    fn handle(&self) -> *mut Ihandle {
        self.canvas.handle()
    }
}

fn create_cursors_page() -> Box<Control> {
    let cursors_canvas = Rc::new(CursorsCanvas::new());

    let radios_info = [
        ("None", Cursor::None),
        ("Arrow", Cursor::Arrow),
        ("Busy", Cursor::Busy),
        ("Cross", Cursor::Cross),
        ("Hand", Cursor::Hand),
        ("Help", Cursor::Help),
        ("Move", Cursor::Move),
        ("ResizeN", Cursor::ResizeN),
        ("ResizeS", Cursor::ResizeS),
        ("ResizeNS", Cursor::ResizeNS),
        ("ResizeW", Cursor::ResizeW),
        ("ResizeE", Cursor::ResizeE),
        ("ResizeWE", Cursor::ResizeWE),
        ("ResizeNE", Cursor::ResizeNE),
        ("ResizeSW", Cursor::ResizeSW),
        ("ResizeNW", Cursor::ResizeNW),
        ("ResizeSE", Cursor::ResizeSE),
        ("Text", Cursor::Text),
    ];

    let mut radios = SmallVec::<[Toggle; 32]>::new();
    for &(text, cur) in radios_info.iter() {
        let toggle = Toggle::new();
        toggle.set_title(text);
        let cursors_canvas2 = cursors_canvas.clone();
        toggle.action_event().add(move |checked| {
            if checked { cursors_canvas2.set_cursor(cur); }
        });
        radios.push(toggle);
    }

    let grid = grid_box!(
        &radios[0],
        &radios[1],
        &radios[2],
        &radios[3],
        &radios[4],
        &radios[5],
        &radios[6],
        &radios[7],
        &radios[8],
        &radios[9],
        &radios[10],
        &radios[11],
        &radios[12],
        &radios[13],
        &radios[14],
        &radios[15],
        &radios[16],
        &radios[17],
    );
    grid.set_num_div(NumDiv::Fixed(2));
    grid.fit_all_to_children();
    let page = vbox!(
        &cursors_canvas,
        hbox!(
            fill!(),
            &grid,
            fill!(),
        ),
    );
    Box::new(page)
}

fn main() {

    let dialog = Dialog::new();

    let tabs = Tabs::new();

    tabs.append_tabs(&[
        TabInfo::new(&*create_cursors_page()).title("Cursors"),
        TabInfo::new(&Fill::new()).title("File Dialog"),
    ]);

    dialog.append(&tabs).expect("failed to build the window");
    dialog.set_title("Showcase");

    dialog.show_xy(ScreenPosition::Center, ScreenPosition::Center)
          .expect("failed to show the window");
    main_loop();
}
<|endoftext|>


<commit_before>#![deny(warnings)]
#![feature(const_fn)]

extern crate orbclient;
extern crate orbimage;
extern crate orbfont;

use std::env;
use std::fs::{self, File};
use std::process::Command;
use std::thread;

use orbclient::{Color, EventOption, Window};
use orbimage::Image;
use orbfont::Font;

use package::Package;

pub mod package;

const BAR_COLOR: Color = Color::rgb(40, 45, 57);
const BAR_HIGHLIGHT_COLOR: Color = Color::rgb(80, 86, 102);
const TEXT_COLOR: Color = Color::rgb(204, 210, 224);
const TEXT_HIGHLIGHT_COLOR: Color = Color::rgb(235, 241, 255);

//TODO: Implement display size in orbclient
#[cfg(target_os = "redox")]
fn get_display_size() -> (i32, i32) {
    match File::open("display:") {
        Ok(display) => {
            let path = display.path().map(|path| path.into_os_string().into_string().unwrap_or(String::new())).unwrap_or(String::new());
            let res = path.split(":").nth(1).unwrap_or("");
            let width = res.split("/").nth(0).unwrap_or("").parse::<i32>().unwrap_or(0);
            let height = res.split("/").nth(1).unwrap_or("").parse::<i32>().unwrap_or(0);
            (width, height)
        },
        Err(err) => panic!("launcher: failed to get display size: {}", err)
    }
}

#[cfg(not(target_os = "redox"))]
fn get_display_size() -> (i32, i32) {
    panic!("launcher: failed to get display size")
}

fn get_packages() -> Vec<Package> {
    let mut packages: Vec<Package> = Vec::new();

    for entry_result in fs::read_dir("/apps/").unwrap() {
        let entry = entry_result.unwrap();
        if entry.file_type().unwrap().is_dir() {
            packages.push(Package::from_path(&("/apps/".to_string() + entry.file_name().to_str().unwrap())));
        }
    }

    packages.sort_by(|a, b| a.name.cmp(&b.name));

    packages
}

fn draw(window: &mut Window, packages: &Vec<Package>, shutdown: &Image, selected: i32){
    let w = window.width();
    let h = window.height();
    window.set(BAR_COLOR);

    let mut x = 0;
    let mut i = 0;
    for package in packages.iter() {
        let y = h as isize - package.icon.height() as isize;

        if i == selected {
            window.rect(x as i32, y as i32,
                              package.icon.width() as u32, package.icon.height() as u32,
                              BAR_HIGHLIGHT_COLOR);
        }

        package.icon.draw(window, x as i32, y as i32);

        x = x + package.icon.width() as i32;
        i += 1;
    }

    x = w as i32 - shutdown.width() as i32;
    let y = h as isize - shutdown.height() as isize;

    if i == selected {
        window.rect(x as i32, y as i32,
                          shutdown.width() as u32, shutdown.height() as u32,
                          BAR_HIGHLIGHT_COLOR);
    }

    shutdown.draw(window, x as i32, y as i32);

    window.sync();
}

fn draw_chooser(window: &mut Window, font: &Font, packages: &Vec<Package>, _mouse_x: i32, mouse_y: i32){
    let w = window.width();

    window.set(BAR_COLOR);

    let mut y = 0;
    for package in packages.iter() {
        let highlight = mouse_y >= y as i32 && mouse_y < y + 32;

        if highlight {
            window.rect(0, y, w, 32, BAR_HIGHLIGHT_COLOR);
        }

        package.icon.draw(window, 0, y);

        let mut c_x = 40;
        for c in package.name.chars() {
            font.render(&c.to_string(), 16.0).draw(window, c_x as i32, y + 8, if highlight { TEXT_HIGHLIGHT_COLOR } else { TEXT_COLOR });
            c_x += 8;
        }

        y += 32;
    }

    window.sync();
}

fn main() {
    let paths = env::args().skip(1);
    if paths.len() > 0 {
        for ref path in paths {
            let mut packages = get_packages();

            packages.retain(|package| -> bool {
                for accept in package.accepts.iter() {
                    if (accept.starts_with('*') && path.ends_with(&accept[1 ..])) ||
                       (accept.ends_with('*') && path.starts_with(&accept[.. accept.len() - 1])) {
                        return true;
                    }
                }
                false
            });

            if packages.len() > 1 {
                let mut window = Window::new(-1, -1, 400, packages.len() as u32 * 32, path).unwrap();
                let font = Font::find(None, None, None).unwrap();

                draw_chooser(&mut window, &font, &packages, -1, -1);
                'choosing: loop {
                    for event in window.events() {
                        match event.to_option() {
                            EventOption::Mouse(mouse_event) => {
                                draw_chooser(&mut window, &font, &packages, mouse_event.x, mouse_event.y);

                                if mouse_event.left_button {
                                    let mut y = 0;
                                    for package in packages.iter() {
                                        if mouse_event.y >= y && mouse_event.y < y + 32 {
                                            if let Err(err) = Command::new(&package.binary).arg(path).spawn() {
                                                println!("{}: Failed to launch: {}", package.binary, err);
                                            }
                                            break 'choosing;
                                        }
                                        y += 32;
                                    }
                                }
                            },
                            EventOption::Quit(_) => break 'choosing,
                            _ => ()
                        }
                    }

                    thread::yield_now();
                }
            } else if let Some(package) = packages.get(0) {
                if let Err(err) = Command::new(&package.binary).arg(&path).spawn() {
                    println!("launcher: failed to launch '{}': {}", package.binary, err);
                }
            } else {
                println!("launcher: no application found for '{}'", path);
            }
        }
    } else {
        let packages = get_packages();

        let shutdown = Image::from_path("/ui/actions/system-shutdown.bmp").unwrap_or(Image::default());

        let (width, height) = get_display_size();
        let mut window = Window::new(0, height - 32, width as u32, 32, "").unwrap();

        let mut selected = -1;

        draw(&mut window, &packages, &shutdown, selected);
        'running: loop {
            for event in window.events() {
                match event.to_option() {
                    EventOption::Mouse(mouse_event) => {
                        let mut now_selected = -1;

                        {
                            let mut x = 0;
                            let mut i = 0;
                            for package in packages.iter() {
                                let y = window.height() as i32 - package.icon.height() as i32;
                                if mouse_event.y >= y && mouse_event.x >= x &&
                                   mouse_event.x < x + package.icon.width() as i32 {
                                    now_selected = i;
                                }
                                x = x + package.icon.width() as i32;
                                i += 1;
                            }

                            x = window.width() as i32 - shutdown.width() as i32;
                            let y = window.height() as i32 - shutdown.height() as i32;
                            if mouse_event.y >= y && mouse_event.x >= x &&
                               mouse_event.x < x + shutdown.width() as i32 {
                                   now_selected = i;
                            }
                        }

                        if now_selected != selected {
                            selected = now_selected;
                            draw(&mut window, &packages, &shutdown, selected);
                        }

                        if mouse_event.left_button {
                            let mut i = 0;
                            for package in packages.iter() {
                                if i == selected {
                                    if let Err(err) = Command::new(&package.binary).spawn() {
                                        println!("{}: Failed to launch: {}", package.binary, err);
                                    }
                                }
                                i += 1;
                            }

                            if i == selected {
                                   File::create("acpi:off").unwrap();
                            }
                        }
                    },
                    EventOption::Quit(_) => break 'running,
                    _ => ()
                }
            }

            thread::yield_now();
        }
    }
}
<commit_msg>hack to get all entries to show up with ralloc<commit_after>#![deny(warnings)]
#![feature(const_fn)]

extern crate orbclient;
extern crate orbimage;
extern crate orbfont;

use std::env;
use std::fs::File;
use std::path::Path;
use std::process::Command;
use std::thread;

use orbclient::{Color, EventOption, Window};
use orbimage::Image;
use orbfont::Font;

use package::Package;

pub mod package;

const BAR_COLOR: Color = Color::rgb(40, 45, 57);
const BAR_HIGHLIGHT_COLOR: Color = Color::rgb(80, 86, 102);
const TEXT_COLOR: Color = Color::rgb(204, 210, 224);
const TEXT_HIGHLIGHT_COLOR: Color = Color::rgb(235, 241, 255);

//TODO: Implement display size in orbclient
#[cfg(target_os = "redox")]
fn get_display_size() -> (i32, i32) {
    match File::open("display:") {
        Ok(display) => {
            let path = display.path().map(|path| path.into_os_string().into_string().unwrap_or(String::new())).unwrap_or(String::new());
            let res = path.split(":").nth(1).unwrap_or("");
            let width = res.split("/").nth(0).unwrap_or("").parse::<i32>().unwrap_or(0);
            let height = res.split("/").nth(1).unwrap_or("").parse::<i32>().unwrap_or(0);
            (width, height)
        },
        Err(err) => panic!("launcher: failed to get display size: {}", err)
    }
}

#[cfg(not(target_os = "redox"))]
fn get_display_size() -> (i32, i32) {
    panic!("launcher: failed to get display size")
}

fn get_packages() -> Vec<Package> {
    let read_dir = Path::new("/apps/").read_dir().expect("failed to read_dir on /apps/");

    let mut entries = vec![];
    for dir in read_dir {
        let dir = match dir {
            Ok(x) => x,
            Err(_) => continue,
        };
        let file_name = dir.file_name().to_string_lossy().into_owned();
        if dir.file_type().expect("failed to get file_type").is_dir() {
            entries.push(file_name);
        }
    }

    entries.sort();

    let mut packages: Vec<Package> = Vec::new();
    for entry in entries.iter() {
        packages.push(Package::from_path(&format!("/apps/{}", entry)));
    }

    packages
}

fn draw(window: &mut Window, packages: &Vec<Package>, shutdown: &Image, selected: i32){
    let w = window.width();
    let h = window.height();
    window.set(BAR_COLOR);

    let mut x = 0;
    let mut i = 0;
    for package in packages.iter() {
        let y = h as isize - package.icon.height() as isize;

        if i == selected {
            window.rect(x as i32, y as i32,
                              package.icon.width() as u32, package.icon.height() as u32,
                              BAR_HIGHLIGHT_COLOR);
        }

        package.icon.draw(window, x as i32, y as i32);

        x = x + package.icon.width() as i32;
        i += 1;
    }

    x = w as i32 - shutdown.width() as i32;
    let y = h as isize - shutdown.height() as isize;

    if i == selected {
        window.rect(x as i32, y as i32,
                          shutdown.width() as u32, shutdown.height() as u32,
                          BAR_HIGHLIGHT_COLOR);
    }

    shutdown.draw(window, x as i32, y as i32);

    window.sync();
}

fn draw_chooser(window: &mut Window, font: &Font, packages: &Vec<Package>, _mouse_x: i32, mouse_y: i32){
    let w = window.width();

    window.set(BAR_COLOR);

    let mut y = 0;
    for package in packages.iter() {
        let highlight = mouse_y >= y as i32 && mouse_y < y + 32;

        if highlight {
            window.rect(0, y, w, 32, BAR_HIGHLIGHT_COLOR);
        }

        package.icon.draw(window, 0, y);

        let mut c_x = 40;
        for c in package.name.chars() {
            font.render(&c.to_string(), 16.0).draw(window, c_x as i32, y + 8, if highlight { TEXT_HIGHLIGHT_COLOR } else { TEXT_COLOR });
            c_x += 8;
        }

        y += 32;
    }

    window.sync();
}

fn main() {
    let paths = env::args().skip(1);
    if paths.len() > 0 {
        for ref path in paths {
            let mut packages = get_packages();

            packages.retain(|package| -> bool {
                for accept in package.accepts.iter() {
                    if (accept.starts_with('*') && path.ends_with(&accept[1 ..])) ||
                       (accept.ends_with('*') && path.starts_with(&accept[.. accept.len() - 1])) {
                        return true;
                    }
                }
                false
            });

            if packages.len() > 1 {
                let mut window = Window::new(-1, -1, 400, packages.len() as u32 * 32, path).unwrap();
                let font = Font::find(None, None, None).unwrap();

                draw_chooser(&mut window, &font, &packages, -1, -1);
                'choosing: loop {
                    for event in window.events() {
                        match event.to_option() {
                            EventOption::Mouse(mouse_event) => {
                                draw_chooser(&mut window, &font, &packages, mouse_event.x, mouse_event.y);

                                if mouse_event.left_button {
                                    let mut y = 0;
                                    for package in packages.iter() {
                                        if mouse_event.y >= y && mouse_event.y < y + 32 {
                                            if let Err(err) = Command::new(&package.binary).arg(path).spawn() {
                                                println!("{}: Failed to launch: {}", package.binary, err);
                                            }
                                            break 'choosing;
                                        }
                                        y += 32;
                                    }
                                }
                            },
                            EventOption::Quit(_) => break 'choosing,
                            _ => ()
                        }
                    }

                    thread::yield_now();
                }
            } else if let Some(package) = packages.get(0) {
                if let Err(err) = Command::new(&package.binary).arg(&path).spawn() {
                    println!("launcher: failed to launch '{}': {}", package.binary, err);
                }
            } else {
                println!("launcher: no application found for '{}'", path);
            }
        }
    } else {
        let packages = get_packages();

        let shutdown = Image::from_path("/ui/actions/system-shutdown.bmp").unwrap_or(Image::default());

        let (width, height) = get_display_size();
        let mut window = Window::new(0, height - 32, width as u32, 32, "").unwrap();

        let mut selected = -1;

        draw(&mut window, &packages, &shutdown, selected);
        'running: loop {
            for event in window.events() {
                match event.to_option() {
                    EventOption::Mouse(mouse_event) => {
                        let mut now_selected = -1;

                        {
                            let mut x = 0;
                            let mut i = 0;
                            for package in packages.iter() {
                                let y = window.height() as i32 - package.icon.height() as i32;
                                if mouse_event.y >= y && mouse_event.x >= x &&
                                   mouse_event.x < x + package.icon.width() as i32 {
                                    now_selected = i;
                                }
                                x = x + package.icon.width() as i32;
                                i += 1;
                            }

                            x = window.width() as i32 - shutdown.width() as i32;
                            let y = window.height() as i32 - shutdown.height() as i32;
                            if mouse_event.y >= y && mouse_event.x >= x &&
                               mouse_event.x < x + shutdown.width() as i32 {
                                   now_selected = i;
                            }
                        }

                        if now_selected != selected {
                            selected = now_selected;
                            draw(&mut window, &packages, &shutdown, selected);
                        }

                        if mouse_event.left_button {
                            let mut i = 0;
                            for package in packages.iter() {
                                if i == selected {
                                    if let Err(err) = Command::new(&package.binary).spawn() {
                                        println!("{}: Failed to launch: {}", package.binary, err);
                                    }
                                }
                                i += 1;
                            }

                            if i == selected {
                                   File::create("acpi:off").unwrap();
                            }
                        }
                    },
                    EventOption::Quit(_) => break 'running,
                    _ => ()
                }
            }

            thread::yield_now();
        }
    }
}
<|endoftext|>


<commit_before>// The MIT License (MIT)
//
// Copyright (c) 2015 Johan Johansson
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

use std::fmt;

/// A position or interval in a string of source code
#[derive(Clone, Copy, Hash, PartialEq, Eq)]
pub struct SrcPos<'a> {
	pub src: &'a str,
	pub start: usize,
	pub end: Option<usize>,
}
impl<'a> SrcPos<'a> {
	/// Construct a new `SrcPos` representing a position in `src`
	fn new_pos(src: &'a str, pos: usize) -> Self {
		SrcPos{ src: src, start: pos, end: None }
	}
	/// Construct a new `SrcPos` representing an interval in `src`
	fn new_interval(src: &'a str, start: usize, end: usize) -> Self {
		SrcPos{ src: src, start: start, end: Some(end) }
	}
}
impl<'a> fmt::Debug for SrcPos<'a> {
	fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
		match self.end {
			Some(end) => write!(fmt, "SrcPos {{ start: {}, end: {} }}", self.start, end),
			None => write!(fmt, "SrcPos {{ start: {} }}", self.start),
		}
	}
}

/// Print an error together with information regarding position in source and panic.
macro_rules! src_error_panic {
	($src:expr, $pos:expr, $msg:expr) => {
		src_error_panic!(SrcPos::new_pos($src, $pos), $msg)
	};
	($pos:expr, $msg:expr) => {{
		use term;

		let (start, mut line_start) = ($pos.start, 0);
		for (line_n, line) in $pos.src.lines().enumerate().map(|(n, line)| (n+1, line)) {
			let line_len = line.len() + 1; // Include length of newline char

			if line_start <= start && start < line_start + line_len {
				let mut t = term::stdout().expect("Could not acquire access to stdout");

				let col = start - line_start;

				print!("{}:{}: ", line_n, col);
				t.fg(term::color::BRIGHT_RED).unwrap();
				print!("Error: ");
				t.reset().unwrap();
				println!("{}", $msg);
				println!("{}: {}", line_n, line);
				t.fg(term::color::BRIGHT_RED).unwrap();
				println!("{}^{}",
					::std::iter::repeat(' ').take(col + (line_n as f32).log10() as usize + 3)
						.collect::<String>(),
					::std::iter::repeat('~')
						.take(::std::cmp::min(
							$pos.end.map(|e| e - start - 1).unwrap_or(0),
							line_len - col))
						.collect::<String>()
				);
				t.reset().unwrap();

				println!("Error occured. Terminating compilation.");
				::std::process::exit(0);
			}
			line_start += line_len;
		}
		panic!("Internal compiler error: src_error_panic: Index {} not reached. src.len(): {}",
			$pos.start,
			$pos.src.len())
	}};
}

/// A string literal.
///
/// Can be either a `Plain` string literal, where *escapes* such as newline, '\n',
/// can be produced using backslash, `\`, or a `Raw` string literal,
/// where escape sequences are not processed.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum StrLit<'a> {
	Plain(&'a str),
	Raw(&'a str),
}

/// A token
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Token<'a> {
	LParen,
	RParen,
	Ident(&'a str),
	Num(&'a str),
	Str(StrLit<'a>),
	Quote,
}

/// Tokenize the string literal in `src` at `start`.
/// Return the `Token` and it's length, including delimiting characters, in the source.
///
/// # Panics
/// Panics if the string literal is unterminated
fn tokenize_str_lit(src: &str, start: usize) -> (Token, usize) {
	let str_body_src = &src[start + 1 ..];
	let mut char_it = str_body_src.char_indices();

	while let Some((i, c)) = char_it.next() {
		match c {
			// TODO: Unescape escape sequences
			// '\\' => { char_it.next(); },
			'"' => return (Token::Str(StrLit::Plain(&str_body_src[..i])), i + 2),
			_ => (),
		}
	}
	src_error_panic!(src, start, "Unterminated string literal")
}

/// Tokenize the raw string literal in `src` at `start`.
/// Return the `Token` and it's length, including delimiting characters, in the source.
///
/// # Panics
/// Panics if the raw string literal is unterminated
fn tokenize_raw_str_lit(src: &str, start: usize) -> (Token, usize) {
	let str_src = &src[start + 1 ..];
	let n_delim_octothorpes = str_src.chars().take_while(|&c| c == '#').count();

	if ! str_src[n_delim_octothorpes..].starts_with('"') {
		src_error_panic!(src, start, "Invalid raw string delim");
	}

	let delim_octothorpes = &str_src[.. 1 + n_delim_octothorpes];

	let str_body_src = &str_src[n_delim_octothorpes + 1 ..];
	for (i, c) in str_body_src.char_indices() {
		if c == '"' && str_body_src[i + 1 ..].starts_with(delim_octothorpes) {
			return (Token::Str(StrLit::Raw(&str_body_src[..i])), n_delim_octothorpes + 2 + i)
		}
	}
	src_error_panic!(src, start, "Unterminated raw string literal")
}

/// Tokenize the numeric literal in `src` at `start`.
/// Return the `Token` and it's length in the source.
///
/// # Panics
/// Panics if the literal is not a valid numeric literal
fn tokenize_num_lit(src: &str, start: usize) -> (Token, usize) {
	let src_num = &src[start..];
	let mut has_decimal_pt = false;
	let mut has_e = false;
	let mut prev_was_e = false;

	for (i, c) in src_num.char_indices() {
		match c {
			'_' => (),
			'E' if !has_e => {
				has_e = true;
				prev_was_e = true
			},
			'-' if prev_was_e => (),
			_ if c.is_numeric() => (),
			'.' if !has_decimal_pt => has_decimal_pt = true,
			_ if is_delim_char(c) => return (Token::Num(&src_num[..i]), i),
			_ => break
		}
		if c != 'E' {
			prev_was_e = false;
		}
	}
	src_error_panic!(src, start, "Invalid numeric literal")
}

/// Returns whether `c` delimits tokens
fn is_delim_char(c: char) -> bool {
	match c {
		'(' | ')' | '[' | ']' | '{' | '}' | ';' => true,
		_ if c.is_whitespace() => true,
		_ => false,
	}
}

/// Returns whether `c` is a valid character of an ident
fn is_ident_char(c: char) -> bool {
	match c {
		'\'' | ':' | '"' => false,
		_ if is_delim_char(c) => false,
		_ => true,
	}
}

/// Tokenize the numeric literal in `src` at `start`.
/// Return the `Token` and it's length in the source.
///
/// # Panics
/// Panics if the literal is not a valid numeric literal
fn tokenize_ident(src: &str, start: usize) -> (Token, usize) {
	let src_ident = &src[start..];
	for (i, c) in src_ident.char_indices() {
		if is_delim_char(c) {
			return (Token::Ident(&src_ident[..i]), i)
		} else if ! is_ident_char(c) {
			break
		}
	}
	src_error_panic!(src, start, "Invalid ident")
}

/// An iterator of `Token`s and their position in some source code
#[derive(Debug)]
struct Tokens<'a> {
	src: &'a str,
	pos: usize,
}
impl<'a> From<&'a str> for Tokens<'a> {
	fn from(src: &'a str) -> Tokens { Tokens{ src: src, pos: 0 } }
}
impl<'a> Iterator for Tokens<'a> {
	type Item = (Token<'a>, SrcPos<'a>);

	fn next(&mut self) -> Option<(Token<'a>, SrcPos<'a>)> {
		let pos = self.pos;
		let mut char_it = self.src[pos..]
			.char_indices()
			.map(|(n, c)| (pos + n, c));

		while let Some((i, c)) = char_it.next() {
			let (token, len) = match c {
				_ if c.is_whitespace() => continue,
				';' => {
					while let Some((_, c)) = char_it.next() {
						if c == '\n' { break; }
					}
					continue
				},
				'\'' => (Token::Quote, 1),
				':' => (Token::Ident(":"), 1),
				'(' => (Token::LParen, 1),
				')' => (Token::RParen, 1),
				'"' => tokenize_str_lit(self.src, i),
				'r' if self.src[i + 1 ..].starts_with(|c: char| c == '"' || c == '#') =>
					tokenize_raw_str_lit(self.src, i),
				_ if c.is_numeric() => tokenize_num_lit(self.src, i),
				_ if is_ident_char(c) => tokenize_ident(self.src, i),
				_ => src_error_panic!(self.src, i, "Unexpected char"),
			};

			self.pos = i + len;
			return Some((token, SrcPos{ src: self.src, start: i, end: Some(self.pos) }));
		}
		None
	}
}

/// A tree of lists, identifiers, and literals
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum TokenTree<'a> {
	List(Vec<TokenTreeMeta<'a>>),
	Ident(&'a str),
	Num(&'a str),
	Str(StrLit<'a>),
}

/// A `TokenTree` with meta-data
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TokenTreeMeta<'a> {
	pub tt: TokenTree<'a>,
	pub pos: SrcPos<'a>,
}
impl<'a> TokenTreeMeta<'a> {
	/// Construct a new `TokenTreeMeta` from a `TokenTree` and a source position
	pub fn new(tt: TokenTree<'a>, pos: SrcPos<'a>) -> Self { TokenTreeMeta{ tt: tt, pos: pos } }

	pub fn new_list(tts: Vec<TokenTreeMeta<'a>>, pos: SrcPos<'a>) -> Self {
		TokenTreeMeta{ tt: TokenTree::List(tts), pos: pos }
	}

	/// Construct a new `TokenTreeMeta` from a token with a position, and the tokens following
	fn from_token((token, mut pos): (Token<'a>, SrcPos<'a>), nexts: &mut Tokens<'a>) -> Self {
		let tt = match token {
			Token::LParen => {
				let (list, end) = tokens_to_trees_until(nexts, Some((pos, &Token::RParen)));
				pos.end = end;
				TokenTree::List(list)
			},
			Token::Ident(ident) => TokenTree::Ident(ident),
			Token::Num(num) => TokenTree::Num(num),
			Token::Str(s) => TokenTree::Str(s),
			Token::Quote => TokenTree::List(vec![
				TokenTreeMeta::new(TokenTree::Ident("quote"), pos),
				TokenTreeMeta::from_token(
					nexts.next().unwrap_or(src_error_panic!(pos, "Free quote")),
					nexts)
			]),
			_ => src_error_panic!(pos, "Unexpected token"),
		};
		TokenTreeMeta::new(tt, pos)
	}

	pub fn relocate(self, pos: SrcPos<'a>) -> TokenTreeMeta<'a> {
		match self.tt {
			TokenTree::List(list) => TokenTreeMeta::new_list(
				list.map_in_place(|li| li.relocate(pos)),
				pos),
			tt => TokenTreeMeta::new(tt, pos),
		}
	}
}

/// Construct trees from `tokens` until a lone `delim` is encountered.
///
/// Returns trees and index of closing delimiter if one was supplied.
fn tokens_to_trees_until<'a>(tokens: &mut Tokens<'a>, start_and_delim: Option<(SrcPos, &Token)>)
	-> (Vec<TokenTreeMeta<'a>>, Option<usize>)
{
	let (start, delim) = start_and_delim.map(|(s, t)| (Some(s), Some(t)))
		.unwrap_or((None, None));

	let mut tts = Vec::new();

	while let Some((token, t_pos)) = tokens.next() {
		if Some(&token) == delim {
			return (tts, t_pos.end)
		} else {
			tts.push(TokenTreeMeta::from_token((token, t_pos), tokens))
		}
	}
	match start {
		None => (tts, None),
		Some(pos) => src_error_panic!(pos, "Undelimited item"),
	}
}

/// Represent some source code as `TokenTreeMeta`s
pub fn token_trees_from_src(src: &str) -> Vec<TokenTreeMeta> {
	tokens_to_trees_until(&mut Tokens::from(src), None).0
}

#[cfg(test)]
mod test {
	use super::*;
	use super::Token::*;
}
<commit_msg>Fix bug with lexing raw string literals<commit_after>// The MIT License (MIT)
//
// Copyright (c) 2015 Johan Johansson
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

use std::fmt;

/// A position or interval in a string of source code
#[derive(Clone, Copy, Hash, PartialEq, Eq)]
pub struct SrcPos<'a> {
	pub src: &'a str,
	pub start: usize,
	pub end: Option<usize>,
}
impl<'a> SrcPos<'a> {
	/// Construct a new `SrcPos` representing a position in `src`
	fn new_pos(src: &'a str, pos: usize) -> Self {
		SrcPos{ src: src, start: pos, end: None }
	}
	/// Construct a new `SrcPos` representing an interval in `src`
	fn new_interval(src: &'a str, start: usize, end: usize) -> Self {
		SrcPos{ src: src, start: start, end: Some(end) }
	}
}
impl<'a> fmt::Debug for SrcPos<'a> {
	fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
		match self.end {
			Some(end) => write!(fmt, "SrcPos {{ start: {}, end: {} }}", self.start, end),
			None => write!(fmt, "SrcPos {{ start: {} }}", self.start),
		}
	}
}

/// Print an error together with information regarding position in source and panic.
macro_rules! src_error_panic {
	($src:expr, $pos:expr, $msg:expr) => {
		src_error_panic!(SrcPos::new_pos($src, $pos), $msg)
	};
	($pos:expr, $msg:expr) => {{
		use term;

		let (start, mut line_start) = ($pos.start, 0);
		for (line_n, line) in $pos.src.lines().enumerate().map(|(n, line)| (n+1, line)) {
			let line_len = line.len() + 1; // Include length of newline char

			if line_start <= start && start < line_start + line_len {
				let mut t = term::stdout().expect("Could not acquire access to stdout");

				let col = start - line_start;

				print!("{}:{}: ", line_n, col);
				t.fg(term::color::BRIGHT_RED).unwrap();
				print!("Error: ");
				t.reset().unwrap();
				println!("{}", $msg);
				println!("{}: {}", line_n, line);
				t.fg(term::color::BRIGHT_RED).unwrap();
				println!("{}^{}",
					::std::iter::repeat(' ').take(col + (line_n as f32).log10() as usize + 3)
						.collect::<String>(),
					::std::iter::repeat('~')
						.take(::std::cmp::min(
							$pos.end.map(|e| e - start - 1).unwrap_or(0),
							line_len - col))
						.collect::<String>()
				);
				t.reset().unwrap();

				println!("Error occured. Terminating compilation.");
				::std::process::exit(0);
			}
			line_start += line_len;
		}
		panic!("Internal compiler error: src_error_panic: Index {} not reached. src.len(): {}",
			$pos.start,
			$pos.src.len())
	}};
}

/// A string literal.
///
/// Can be either a `Plain` string literal, where *escapes* such as newline, '\n',
/// can be produced using backslash, `\`, or a `Raw` string literal,
/// where escape sequences are not processed.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum StrLit<'a> {
	Plain(&'a str),
	Raw(&'a str),
}

/// A token
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Token<'a> {
	LParen,
	RParen,
	Ident(&'a str),
	Num(&'a str),
	Str(StrLit<'a>),
	Quote,
}

/// Tokenize the string literal in `src` at `start`.
/// Return the `Token` and it's length, including delimiting characters, in the source.
///
/// # Panics
/// Panics if the string literal is unterminated
fn tokenize_str_lit(src: &str, start: usize) -> (Token, usize) {
	let str_body_src = &src[start + 1 ..];
	let mut char_it = str_body_src.char_indices();

	while let Some((i, c)) = char_it.next() {
		match c {
			// TODO: Unescape escape sequences
			// '\\' => { char_it.next(); },
			'"' => return (Token::Str(StrLit::Plain(&str_body_src[..i])), i + 2),
			_ => (),
		}
	}
	src_error_panic!(src, start, "Unterminated string literal")
}

/// Tokenize the raw string literal in `src` at `start`.
/// Return the `Token` and it's length, including delimiting characters, in the source.
///
/// # Panics
/// Panics if the raw string literal is unterminated
fn tokenize_raw_str_lit(src: &str, start: usize) -> (Token, usize) {
	let str_src = &src[start + 1 ..];
	let n_delim_octothorpes = str_src.chars().take_while(|&c| c == '#').count();

	if ! str_src[n_delim_octothorpes..].starts_with('"') {
		src_error_panic!(src, start, "Invalid raw string delimiter");
	}

	let delim_octothorpes = &str_src[..n_delim_octothorpes];

	let str_body_src = &str_src[n_delim_octothorpes + 1 ..];
	for (i, c) in str_body_src.char_indices() {
		if c == '"' && str_body_src[i + 1 ..].starts_with(delim_octothorpes) {
			// octothorpes before and after + 'r' + open and end quotes + str len
			let literal_len = n_delim_octothorpes * 2 + 3 + i;
			return (Token::Str(StrLit::Raw(&str_body_src[..i])), literal_len)
		}
	}
	src_error_panic!(src, start, "Unterminated raw string literal")
}

/// Tokenize the numeric literal in `src` at `start`.
/// Return the `Token` and it's length in the source.
///
/// # Panics
/// Panics if the literal is not a valid numeric literal
fn tokenize_num_lit(src: &str, start: usize) -> (Token, usize) {
	let src_num = &src[start..];
	let mut has_decimal_pt = false;
	let mut has_e = false;
	let mut prev_was_e = false;

	for (i, c) in src_num.char_indices() {
		match c {
			'_' => (),
			'E' if !has_e => {
				has_e = true;
				prev_was_e = true
			},
			'-' if prev_was_e => (),
			_ if c.is_numeric() => (),
			'.' if !has_decimal_pt => has_decimal_pt = true,
			_ if is_delim_char(c) => return (Token::Num(&src_num[..i]), i),
			_ => break
		}
		if c != 'E' {
			prev_was_e = false;
		}
	}
	src_error_panic!(src, start, "Invalid numeric literal")
}

/// Returns whether `c` delimits tokens
fn is_delim_char(c: char) -> bool {
	match c {
		'(' | ')' | '[' | ']' | '{' | '}' | ';' => true,
		_ if c.is_whitespace() => true,
		_ => false,
	}
}

/// Returns whether `c` is a valid character of an ident
fn is_ident_char(c: char) -> bool {
	match c {
		'\'' | ':' | '"' => false,
		_ if is_delim_char(c) => false,
		_ => true,
	}
}

/// Tokenize the numeric literal in `src` at `start`.
/// Return the `Token` and it's length in the source.
///
/// # Panics
/// Panics if the literal is not a valid numeric literal
fn tokenize_ident(src: &str, start: usize) -> (Token, usize) {
	let src_ident = &src[start..];
	for (i, c) in src_ident.char_indices() {
		if is_delim_char(c) {
			return (Token::Ident(&src_ident[..i]), i)
		} else if ! is_ident_char(c) {
			break
		}
	}
	src_error_panic!(src, start, "Invalid ident")
}

/// An iterator of `Token`s and their position in some source code
#[derive(Debug)]
struct Tokens<'a> {
	src: &'a str,
	pos: usize,
}
impl<'a> From<&'a str> for Tokens<'a> {
	fn from(src: &'a str) -> Tokens { Tokens{ src: src, pos: 0 } }
}
impl<'a> Iterator for Tokens<'a> {
	type Item = (Token<'a>, SrcPos<'a>);

	fn next(&mut self) -> Option<(Token<'a>, SrcPos<'a>)> {
		let pos = self.pos;
		let mut char_it = self.src[pos..]
			.char_indices()
			.map(|(n, c)| (pos + n, c));

		while let Some((i, c)) = char_it.next() {
			let (token, len) = match c {
				_ if c.is_whitespace() => continue,
				';' => {
					while let Some((_, c)) = char_it.next() {
						if c == '\n' { break; }
					}
					continue
				},
				'\'' => (Token::Quote, 1),
				':' => (Token::Ident(":"), 1),
				'(' => (Token::LParen, 1),
				')' => (Token::RParen, 1),
				'"' => tokenize_str_lit(self.src, i),
				'r' if self.src[i + 1 ..].starts_with(|c: char| c == '"' || c == '#') =>
					tokenize_raw_str_lit(self.src, i),
				_ if c.is_numeric() => tokenize_num_lit(self.src, i),
				_ if is_ident_char(c) => tokenize_ident(self.src, i),
				_ => src_error_panic!(self.src, i, "Unexpected char"),
			};

			self.pos = i + len;
			return Some((token, SrcPos{ src: self.src, start: i, end: Some(self.pos) }));
		}
		None
	}
}

/// A tree of lists, identifiers, and literals
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum TokenTree<'a> {
	List(Vec<TokenTreeMeta<'a>>),
	Ident(&'a str),
	Num(&'a str),
	Str(StrLit<'a>),
}

/// A `TokenTree` with meta-data
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TokenTreeMeta<'a> {
	pub tt: TokenTree<'a>,
	pub pos: SrcPos<'a>,
}
impl<'a> TokenTreeMeta<'a> {
	/// Construct a new `TokenTreeMeta` from a `TokenTree` and a source position
	pub fn new(tt: TokenTree<'a>, pos: SrcPos<'a>) -> Self { TokenTreeMeta{ tt: tt, pos: pos } }

	pub fn new_list(tts: Vec<TokenTreeMeta<'a>>, pos: SrcPos<'a>) -> Self {
		TokenTreeMeta{ tt: TokenTree::List(tts), pos: pos }
	}

	/// Construct a new `TokenTreeMeta` from a token with a position, and the tokens following
	fn from_token((token, mut pos): (Token<'a>, SrcPos<'a>), nexts: &mut Tokens<'a>) -> Self {
		let tt = match token {
			Token::LParen => {
				let (list, end) = tokens_to_trees_until(nexts, Some((pos, &Token::RParen)));
				pos.end = end;
				TokenTree::List(list)
			},
			Token::Ident(ident) => TokenTree::Ident(ident),
			Token::Num(num) => TokenTree::Num(num),
			Token::Str(s) => TokenTree::Str(s),
			Token::Quote => TokenTree::List(vec![
				TokenTreeMeta::new(TokenTree::Ident("quote"), pos),
				TokenTreeMeta::from_token(
					nexts.next().unwrap_or(src_error_panic!(pos, "Free quote")),
					nexts)
			]),
			_ => src_error_panic!(pos, "Unexpected token"),
		};
		TokenTreeMeta::new(tt, pos)
	}

	pub fn relocate(self, pos: SrcPos<'a>) -> TokenTreeMeta<'a> {
		match self.tt {
			TokenTree::List(list) => TokenTreeMeta::new_list(
				list.map_in_place(|li| li.relocate(pos)),
				pos),
			tt => TokenTreeMeta::new(tt, pos),
		}
	}
}

/// Construct trees from `tokens` until a lone `delim` is encountered.
///
/// Returns trees and index of closing delimiter if one was supplied.
fn tokens_to_trees_until<'a>(tokens: &mut Tokens<'a>, start_and_delim: Option<(SrcPos, &Token)>)
	-> (Vec<TokenTreeMeta<'a>>, Option<usize>)
{
	let (start, delim) = start_and_delim.map(|(s, t)| (Some(s), Some(t)))
		.unwrap_or((None, None));

	let mut tts = Vec::new();

	while let Some((token, t_pos)) = tokens.next() {
		if Some(&token) == delim {
			return (tts, t_pos.end)
		} else {
			tts.push(TokenTreeMeta::from_token((token, t_pos), tokens))
		}
	}
	match start {
		None => (tts, None),
		Some(pos) => src_error_panic!(pos, "Undelimited item"),
	}
}

/// Represent some source code as `TokenTreeMeta`s
pub fn token_trees_from_src(src: &str) -> Vec<TokenTreeMeta> {
	tokens_to_trees_until(&mut Tokens::from(src), None).0
}

#[cfg(test)]
mod test {
	use super::*;
	use super::Token::*;
}
<|endoftext|>