nexedit 0.2.2

A vim-like text editor, with simple shortcuts.
Documentation
use crate::commands::{self, Result};
use crate::errors::*;
use crate::input::Key;
use crate::models::application::{Application, Mode};
use scribe::buffer::Position;

pub fn accept_input(app: &mut Application) -> Result {
    if let Mode::LineJump(ref mode) = app.mode {
        let line_number = mode
            .input
            .parse::<usize>()
            .chain_err(|| "Couldn't parse a line number from the provided input.")?;

        if line_number > 0 {
            let buffer = app
                .workspace
                .current_buffer
                .as_mut()
                .ok_or(BUFFER_MISSING)?;

            let target_line = line_number - 1;

            let mut target_position = Position {
                line: target_line,
                offset: buffer.cursor.offset,
            };

            if !buffer.cursor.move_to(target_position) {
                let line_content = buffer
                    .data()
                    .lines()
                    .nth(target_line)
                    .map(|line| line.to_string())
                    .ok_or("Couldn't find the specified line")?;

                target_position.offset = line_content.len();
                buffer.cursor.move_to(target_position);
            }
        }
    } else {
        bail!("Can't accept line jump input outside of line jump mode.");
    }

    commands::application::switch_to_normal_mode(app)?;
    commands::view::scroll_cursor_to_center(app)?;

    Ok(())
}

pub fn push_search_char(app: &mut Application) -> Result {
    let key = app
        .view
        .last_key()
        .as_ref()
        .ok_or("View hasn't tracked a key press")?;

    if let Key::Char(c) = *key {
        if let Mode::LineJump(ref mut mode) = app.mode {
            mode.input.push(c)
        } else {
            bail!("Can't push search character outside of search insert mode")
        }
    } else {
        bail!("Last key press wasn't a character")
    }

    Ok(())
}

pub fn pop_search_char(app: &mut Application) -> Result {
    if let Mode::LineJump(ref mut mode) = app.mode {
        mode.input.pop()
    } else {
        bail!("Can't pop search character outside of search insert mode")
    };

    Ok(())
}

#[cfg(test)]
mod tests {
    use crate::commands;
    use crate::models::application::{Application, Mode};
    use scribe::buffer::Position;
    use scribe::Buffer;

    #[test]
    fn accept_input_moves_cursor_to_requested_line_and_changes_modes() {
        let mut app = Application::new(&Vec::new()).unwrap();
        let mut buffer = Buffer::new();
        buffer.insert("nexedit");

        app.workspace.add_buffer(buffer);
        commands::application::switch_to_line_jump_mode(&mut app).unwrap();
        match app.mode {
            Mode::LineJump(ref mut mode) => mode.input = "3".to_string(),
            _ => (),
        };
        commands::line_jump::accept_input(&mut app).unwrap();

        assert_eq!(
            *app.workspace.current_buffer.as_ref().unwrap().cursor,
            Position { line: 2, offset: 0 }
        );

        assert!(match app.mode {
            crate::models::application::Mode::Normal => true,
            _ => false,
        });
    }

    #[test]
    fn accept_input_handles_unavailable_offsets() {
        let mut app = Application::new(&Vec::new()).unwrap();
        let mut buffer = Buffer::new();
        buffer.insert("nexedit");
        buffer.cursor.move_to(Position { line: 1, offset: 3 });

        app.workspace.add_buffer(buffer);
        commands::application::switch_to_line_jump_mode(&mut app).unwrap();
        match app.mode {
            Mode::LineJump(ref mut mode) => mode.input = "3".to_string(),
            _ => (),
        };
        commands::line_jump::accept_input(&mut app).unwrap();

        assert_eq!(
            *app.workspace.current_buffer.as_ref().unwrap().cursor,
            Position { line: 2, offset: 3 }
        );

        assert!(match app.mode {
            crate::models::application::Mode::Normal => true,
            _ => false,
        });
    }

    #[test]
    fn accept_input_ignores_zero_input() {
        let mut app = Application::new(&Vec::new()).unwrap();
        let mut buffer = Buffer::new();
        buffer.insert("nexedit");

        app.workspace.add_buffer(buffer);
        commands::application::switch_to_line_jump_mode(&mut app).unwrap();
        match app.mode {
            Mode::LineJump(ref mut mode) => mode.input = "0".to_string(),
            _ => (),
        };
        commands::line_jump::accept_input(&mut app).unwrap();

        assert_eq!(
            *app.workspace.current_buffer.as_ref().unwrap().cursor,
            Position { line: 0, offset: 0 }
        );

        assert!(match app.mode {
            crate::models::application::Mode::Normal => true,
            _ => false,
        });
    }
}