<commit_before>// Copyright 2016 <NAME>, The Alacritty Project Contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//! Exports the `Term` type which is a high-level API for the Grid
use std::ops::Range;

use ansi::{self, Attr};
use grid::{self, Grid, CellFlags, ClearRegion};
use tty;
use ::Rgb;

/// coerce val to be between min and max
fn limit<T: PartialOrd>(val: T, min: T, max: T) -> T {
    if val < min {
        min
    } else if val > max {
        max
    } else {
        val
    }
}

/// tomorrow night bright
///
/// because contrast
pub static COLORS: &'static [Rgb] = &[
    Rgb {r: 0x00, g: 0x00, b: 0x00}, // Black
    Rgb {r: 0xd5, g: 0x4e, b: 0x53}, // Red
    Rgb {r: 0xb9, g: 0xca, b: 0x4a}, // Green
    Rgb {r: 0xe6, g: 0xc5, b: 0x47}, // Yellow
    Rgb {r: 0x7a, g: 0xa6, b: 0xda}, // Blue
    Rgb {r: 0xc3, g: 0x97, b: 0xd8}, // Magenta
    Rgb {r: 0x70, g: 0xc0, b: 0xba}, // Cyan
    Rgb {r: 0x42, g: 0x42, b: 0x42}, // White
    Rgb {r: 0x66, g: 0x66, b: 0x66}, // Bright black
    Rgb {r: 0xff, g: 0x33, b: 0x34}, // Bright red
    Rgb {r: 0x9e, g: 0xc4, b: 0x00}, // Bright green
    Rgb {r: 0xe7, g: 0xc5, b: 0x47}, // Bright yellow
    Rgb {r: 0x7a, g: 0xa6, b: 0xda}, // Bright blue
    Rgb {r: 0xb7, g: 0x7e, b: 0xe0}, // Bright magenta
    Rgb {r: 0x54, g: 0xce, b: 0xd6}, // Bright cyan
    Rgb {r: 0x2a, g: 0x2a, b: 0x2a}, // Bright white
];

pub mod mode {
    bitflags! {
        pub flags TermMode: u8 {
            const SHOW_CURSOR = 0b00000001,
            const APP_CURSOR  = 0b00000010,
            const ANY         = 0b11111111,
            const NONE        = 0b00000000,
        }
    }

    impl Default for TermMode {
        fn default() -> TermMode {
            SHOW_CURSOR
        }
    }
}

pub use self::mode::TermMode;

pub const CURSOR_SHAPE: char = '█';

pub const DEFAULT_FG: Rgb = Rgb { r: 0xea, g: 0xea, b: 0xea};
pub const DEFAULT_BG: Rgb = Rgb { r: 0, g: 0, b: 0};
pub const TAB_SPACES: usize = 8;

/// State for cursor
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct Cursor {
    pub x: u16,
    pub y: u16,
}

impl Default for Cursor {
    fn default() -> Cursor {
        Cursor { x: 0, y: 0 }
    }
}

impl Cursor {
    pub fn goto(&mut self, x: u16, y: u16) {
        self.x = x;
        self.y = y;
    }

    pub fn advance(&mut self, rows: i64, cols: i64) {
        self.x = (self.x as i64 + cols) as u16;
        self.y = (self.y as i64 + rows) as u16;
    }
}

pub struct Term {
    /// The grid
    grid: Grid,

    /// Alternate grid
    alt_grid: Grid,

    /// Alt is active
    alt: bool,

    /// Reference to the underlying tty
    tty: tty::Tty,

    /// The cursor
    cursor: Cursor,

    /// Alt cursor
    alt_cursor: Cursor,

    /// Active foreground color
    fg: Rgb,

    /// Active background color
    bg: Rgb,

    /// Tabstops
    tabs: Vec<bool>,

    /// Cell attributes
    attr: grid::CellFlags,

    /// Mode flags
    mode: TermMode,

    /// Scroll region
    scroll_region: Range<usize>,

    /// Size
    size_info: SizeInfo,
}

/// Terminal size info
#[derive(Debug)]
pub struct SizeInfo {
    /// Terminal window width
    pub width: f32,

    /// Terminal window height
    pub height: f32,

    /// Width of individual cell
    pub cell_width: f32,

    /// Height of individual cell
    pub cell_height: f32,
}

impl SizeInfo {
    #[inline]
    pub fn rows(&self) -> usize {
        (self.height / self.cell_height) as usize
    }

    #[inline]
    pub fn cols(&self) -> usize {
        (self.width / self.cell_width) as usize
    }
}

impl Term {
    pub fn new(width: f32, height: f32, cell_width: f32, cell_height: f32) -> Term {
        let size = SizeInfo {
            width: width as f32,
            height: height as f32,
            cell_width: cell_width as f32,
            cell_height: cell_height as f32,
        };

        let num_cols = size.cols();
        let num_rows = size.rows();

        println!("num_cols, num_rows = {}, {}", num_cols, num_rows);

        let grid = Grid::new(num_rows, num_cols);

        let tty = tty::new(num_rows as u8, num_cols as u8);
        tty.resize(num_rows, num_cols, size.width as usize, size.height as usize);

        let mut tabs = (0..grid.num_cols()).map(|i| i % TAB_SPACES == 0)
                                       .collect::<Vec<bool>>();
        tabs[0] = false;

        let alt = grid.clone();
        let scroll_region = 0..grid.num_rows();

        Term {
            grid: grid,
            alt_grid: alt,
            alt: false,
            cursor: Cursor::default(),
            alt_cursor: Cursor::default(),
            fg: DEFAULT_FG,
            bg: DEFAULT_BG,
            tty: tty,
            tabs: tabs,
            attr: CellFlags::empty(),
            mode: Default::default(),
            scroll_region: scroll_region,
            size_info: size
        }
    }

    /// Resize terminal to new dimensions
    pub fn resize(&mut self, width: f32, height: f32) {
        let size = SizeInfo {
            width: width,
            height: height,
            cell_width: self.size_info.cell_width,
            cell_height: self.size_info.cell_height,
        };

        let old_cols = self.size_info.cols();
        let old_rows = self.size_info.rows();
        let num_cols = size.cols();
        let num_rows = size.rows();

        self.size_info = size;

        if old_cols == num_cols && old_rows == num_rows {
            return;
        }

        // Scroll up to keep cursor and as much context as possible in grid. This only runs when the
        // rows decreases.
        self.scroll_region = 0..self.grid.num_rows();
        // XXX why is +1 required?
        let row_diff = (self.cursor_y() as isize - num_rows as isize) + 1;
        if row_diff > 0 {
            self.scroll(row_diff);
            self.cursor.advance(-row_diff as i64, 0);
        }

        println!("num_cols, num_rows = {}, {}", num_cols, num_rows);

        // Resize grids to new size
        self.grid.resize(num_rows, num_cols);
        self.alt_grid.resize(num_rows, num_cols);

        // Ensure cursor is in-bounds
        self.cursor.y = limit(self.cursor.y, 0, num_rows as u16);
        self.cursor.x = limit(self.cursor.x, 0, num_cols as u16);

        // Recreate tabs list
        self.tabs = (0..self.grid.num_cols()).map(|i| i % TAB_SPACES == 0)
                                             .collect::<Vec<bool>>();

        // Make sure bottom of terminal is clear
        if row_diff > 0 {
            self.grid.clear_region((self.cursor.y as usize)..);
            self.alt_grid.clear_region((self.cursor.y as usize)..);
        }

        // Reset scrolling region to new size
        self.scroll_region = 0..self.grid.num_rows();

        // Inform tty of new dimensions
        self.tty.resize(num_rows,
                        num_cols,
                        self.size_info.width as usize,
                        self.size_info.height as usize);

    }

    #[inline]
    pub fn tty(&self) -> &tty::Tty {
        &self.tty
    }

    #[inline]
    pub fn size_info(&self) -> &SizeInfo {
        &self.size_info
    }

    pub fn grid(&self) -> &Grid {
        &self.grid
    }

    #[inline]
    pub fn mode(&self) -> &TermMode {
        &self.mode
    }

    pub fn swap_alt(&mut self) {
        self.alt = !self.alt;
        ::std::mem::swap(&mut self.grid, &mut self.alt_grid);
        ::std::mem::swap(&mut self.cursor, &mut self.alt_cursor);

        if self.alt {
            self.grid.clear();
        }
    }

    #[inline]
    pub fn cursor_x(&self) -> u16 {
        self.cursor.x
    }

    #[inline]
    pub fn cursor_y(&self) -> u16 {
        self.cursor.y
    }

    #[inline]
    pub fn cursor(&self) -> Cursor {
        self.cursor
    }

    /// Set character in current cursor position
    fn set_char(&mut self, c: char) {
        if self.cursor.x == self.grid.num_cols() as u16 {
            println!("wrapping");
            self.cursor.y += 1;
            self.cursor.x = 0;
        }

        if self.cursor.y == self.grid.num_rows() as u16 {
            panic!("cursor fell off grid");
        }

        let cell = &mut self.grid[self.cursor];
        cell.c = c;
        cell.fg = self.fg;
        cell.bg = self.bg;
        cell.flags = self.attr;
    }

    /// Convenience function for scrolling
    fn scroll(&mut self, count: isize) {
        println!("[TERM] scrolling {} lines", count);
        self.grid.scroll(self.scroll_region.clone(), count);
        if count > 0 {
            // Scrolled down, so need to clear from bottom
            let start = self.scroll_region.end - (count as usize);
            self.grid.clear_region(start..self.scroll_region.end);
        } else {
            // Scrolled up, clear from top
            let end = self.scroll_region.start + ((-count) as usize);
            self.grid.clear_region(self.scroll_region.start..end);
        }
    }
}

impl ansi::TermInfo for Term {
    #[inline]
    fn rows(&self) -> usize {
        self.grid.num_rows()
    }

    #[inline]
    fn cols(&self) -> usize {
        self.grid.num_cols()
    }
}

impl ansi::Handler for Term {
    /// A character to be displayed
    #[inline]
    fn input(&mut self, c: char) {
        self.set_char(c);
        self.cursor.x += 1;
    }

    fn goto(&mut self, x: i64, y: i64) {
        println!("goto: x={}, y={}", x, y);
        self.cursor.goto(x as u16, y as u16);
    }
    fn goto_row(&mut self, y: i64) {
        println!("goto_row: {}", y);
        let x = self.cursor_x();
        self.cursor.goto(x, y as u16);
    }
    fn goto_col(&mut self, x: i64) {
        println!("goto_col: {}", x);
        let y = self.cursor_y();
        self.cursor.goto(x as u16, y);
    }

    fn insert_blank(&mut self, num: i64) { println!("insert_blank: {}", num); }

    fn move_up(&mut self, rows: i64) {
        println!("move_up: {}", rows);
        self.cursor.advance(-rows, 0);
    }

    fn move_down(&mut self, rows: i64) {
        println!("move_down: {}", rows);
        self.cursor.advance(rows, 0);
    }

    fn move_forward(&mut self, cols: i64) {
        println!("move_forward: {}", cols);
        self.cursor.advance(0, cols);
    }

    fn move_backward(&mut self, spaces: i64) {
        println!("move_backward: {}", spaces);
        self.cursor.advance(0, -spaces);
    }

    fn identify_terminal(&mut self) { println!("identify_terminal"); }
    fn move_down_and_cr(&mut self, rows: i64) { println!("move_down_and_cr: {}", rows); }
    fn move_up_and_cr(&mut self, rows: i64) { println!("move_up_and_cr: {}", rows); }
    fn put_tab(&mut self, mut count: i64) {
        println!("put_tab: {}", count);

        let mut x = self.cursor_x();
        while x < self.grid.num_cols() as u16 && count != 0 {
            count -= 1;
            loop {
                if x == self.grid.num_cols() as u16 || self.tabs[x as usize] {
                    break;
                }
                x += 1;
            }
        }

        self.cursor.x = x;
    }

    /// Backspace `count` characters
    #[inline]
    fn backspace(&mut self) {
        println!("backspace");
        self.cursor.x -= 1;
    }

    /// Carriage return
    #[inline]
    fn carriage_return(&mut self) {
        println!("carriage_return");
        self.cursor.x = 0;
    }

    /// Linefeed
    #[inline]
    fn linefeed(&mut self) {
        println!("linefeed");
        // TODO handle scroll? not clear what parts of this the pty handle
        if self.cursor_y() + 1 >= self.scroll_region.end as u16 {
            self.scroll(1);
            self.clear_line(ansi::LineClearMode::Right);
        } else {
            self.cursor.y += 1;
        }
    }

    /// Set current position as a tabstop
    fn bell(&mut self) { println!("bell"); }
    fn substitute(&mut self) { println!("substitute"); }
    fn newline(&mut self) { println!("newline"); }
    fn set_horizontal_tabstop(&mut self) { println!("set_horizontal_tabstop"); }
    fn scroll_up(&mut self, rows: i64) {
        println!("scroll_up: {}", rows);
        self.scroll(-rows as isize);
    }
    fn scroll_down(&mut self, rows: i64) {
        println!("scroll_down: {}", rows);
        self.scroll(rows as isize);
    }
    fn insert_blank_lines(&mut self, count: i64) {
        println!("insert_blank_lines: {}", count);
        if self.scroll_region.contains(self.cursor_y() as usize) {
            self.scroll(-count as isize);
        }
    }
    fn delete_lines(&mut self, count: i64) {
        if self.scroll_region.contains(self.cursor_y() as usize) {<commit_msg>Fix sign error with scroll directions<commit_after>// Copyright 2016 <NAME>, The Alacritty Project Contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//! Exports the `Term` type which is a high-level API for the Grid
use std::ops::Range;

use ansi::{self, Attr};
use grid::{self, Grid, CellFlags, ClearRegion};
use tty;
use ::Rgb;

/// coerce val to be between min and max
fn limit<T: PartialOrd>(val: T, min: T, max: T) -> T {
    if val < min {
        min
    } else if val > max {
        max
    } else {
        val
    }
}

/// tomorrow night bright
///
/// because contrast
pub static COLORS: &'static [Rgb] = &[
    Rgb {r: 0x00, g: 0x00, b: 0x00}, // Black
    Rgb {r: 0xd5, g: 0x4e, b: 0x53}, // Red
    Rgb {r: 0xb9, g: 0xca, b: 0x4a}, // Green
    Rgb {r: 0xe6, g: 0xc5, b: 0x47}, // Yellow
    Rgb {r: 0x7a, g: 0xa6, b: 0xda}, // Blue
    Rgb {r: 0xc3, g: 0x97, b: 0xd8}, // Magenta
    Rgb {r: 0x70, g: 0xc0, b: 0xba}, // Cyan
    Rgb {r: 0x42, g: 0x42, b: 0x42}, // White
    Rgb {r: 0x66, g: 0x66, b: 0x66}, // Bright black
    Rgb {r: 0xff, g: 0x33, b: 0x34}, // Bright red
    Rgb {r: 0x9e, g: 0xc4, b: 0x00}, // Bright green
    Rgb {r: 0xe7, g: 0xc5, b: 0x47}, // Bright yellow
    Rgb {r: 0x7a, g: 0xa6, b: 0xda}, // Bright blue
    Rgb {r: 0xb7, g: 0x7e, b: 0xe0}, // Bright magenta
    Rgb {r: 0x54, g: 0xce, b: 0xd6}, // Bright cyan
    Rgb {r: 0x2a, g: 0x2a, b: 0x2a}, // Bright white
];

pub mod mode {
    bitflags! {
        pub flags TermMode: u8 {
            const SHOW_CURSOR = 0b00000001,
            const APP_CURSOR  = 0b00000010,
            const ANY         = 0b11111111,
            const NONE        = 0b00000000,
        }
    }

    impl Default for TermMode {
        fn default() -> TermMode {
            SHOW_CURSOR
        }
    }
}

pub use self::mode::TermMode;

pub const CURSOR_SHAPE: char = '█';

pub const DEFAULT_FG: Rgb = Rgb { r: 0xea, g: 0xea, b: 0xea};
pub const DEFAULT_BG: Rgb = Rgb { r: 0, g: 0, b: 0};
pub const TAB_SPACES: usize = 8;

/// State for cursor
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct Cursor {
    pub x: u16,
    pub y: u16,
}

impl Default for Cursor {
    fn default() -> Cursor {
        Cursor { x: 0, y: 0 }
    }
}

impl Cursor {
    pub fn goto(&mut self, x: u16, y: u16) {
        self.x = x;
        self.y = y;
    }

    pub fn advance(&mut self, rows: i64, cols: i64) {
        self.x = (self.x as i64 + cols) as u16;
        self.y = (self.y as i64 + rows) as u16;
    }
}

pub struct Term {
    /// The grid
    grid: Grid,

    /// Alternate grid
    alt_grid: Grid,

    /// Alt is active
    alt: bool,

    /// Reference to the underlying tty
    tty: tty::Tty,

    /// The cursor
    cursor: Cursor,

    /// Alt cursor
    alt_cursor: Cursor,

    /// Active foreground color
    fg: Rgb,

    /// Active background color
    bg: Rgb,

    /// Tabstops
    tabs: Vec<bool>,

    /// Cell attributes
    attr: grid::CellFlags,

    /// Mode flags
    mode: TermMode,

    /// Scroll region
    scroll_region: Range<usize>,

    /// Size
    size_info: SizeInfo,
}

/// Terminal size info
#[derive(Debug)]
pub struct SizeInfo {
    /// Terminal window width
    pub width: f32,

    /// Terminal window height
    pub height: f32,

    /// Width of individual cell
    pub cell_width: f32,

    /// Height of individual cell
    pub cell_height: f32,
}

impl SizeInfo {
    #[inline]
    pub fn rows(&self) -> usize {
        (self.height / self.cell_height) as usize
    }

    #[inline]
    pub fn cols(&self) -> usize {
        (self.width / self.cell_width) as usize
    }
}

impl Term {
    pub fn new(width: f32, height: f32, cell_width: f32, cell_height: f32) -> Term {
        let size = SizeInfo {
            width: width as f32,
            height: height as f32,
            cell_width: cell_width as f32,
            cell_height: cell_height as f32,
        };

        let num_cols = size.cols();
        let num_rows = size.rows();

        println!("num_cols, num_rows = {}, {}", num_cols, num_rows);

        let grid = Grid::new(num_rows, num_cols);

        let tty = tty::new(num_rows as u8, num_cols as u8);
        tty.resize(num_rows, num_cols, size.width as usize, size.height as usize);

        let mut tabs = (0..grid.num_cols()).map(|i| i % TAB_SPACES == 0)
                                       .collect::<Vec<bool>>();
        tabs[0] = false;

        let alt = grid.clone();
        let scroll_region = 0..grid.num_rows();

        Term {
            grid: grid,
            alt_grid: alt,
            alt: false,
            cursor: Cursor::default(),
            alt_cursor: Cursor::default(),
            fg: DEFAULT_FG,
            bg: DEFAULT_BG,
            tty: tty,
            tabs: tabs,
            attr: CellFlags::empty(),
            mode: Default::default(),
            scroll_region: scroll_region,
            size_info: size
        }
    }

    /// Resize terminal to new dimensions
    pub fn resize(&mut self, width: f32, height: f32) {
        let size = SizeInfo {
            width: width,
            height: height,
            cell_width: self.size_info.cell_width,
            cell_height: self.size_info.cell_height,
        };

        let old_cols = self.size_info.cols();
        let old_rows = self.size_info.rows();
        let num_cols = size.cols();
        let num_rows = size.rows();

        self.size_info = size;

        if old_cols == num_cols && old_rows == num_rows {
            return;
        }

        // Scroll up to keep cursor and as much context as possible in grid. This only runs when the
        // rows decreases.
        self.scroll_region = 0..self.grid.num_rows();
        // XXX why is +1 required?
        let row_diff = (self.cursor_y() as isize - num_rows as isize) + 1;
        if row_diff > 0 {
            self.scroll(row_diff);
            self.cursor.advance(-row_diff as i64, 0);
        }

        println!("num_cols, num_rows = {}, {}", num_cols, num_rows);

        // Resize grids to new size
        self.grid.resize(num_rows, num_cols);
        self.alt_grid.resize(num_rows, num_cols);

        // Ensure cursor is in-bounds
        self.cursor.y = limit(self.cursor.y, 0, num_rows as u16);
        self.cursor.x = limit(self.cursor.x, 0, num_cols as u16);

        // Recreate tabs list
        self.tabs = (0..self.grid.num_cols()).map(|i| i % TAB_SPACES == 0)
                                             .collect::<Vec<bool>>();

        // Make sure bottom of terminal is clear
        if row_diff > 0 {
            self.grid.clear_region((self.cursor.y as usize)..);
            self.alt_grid.clear_region((self.cursor.y as usize)..);
        }

        // Reset scrolling region to new size
        self.scroll_region = 0..self.grid.num_rows();

        // Inform tty of new dimensions
        self.tty.resize(num_rows,
                        num_cols,
                        self.size_info.width as usize,
                        self.size_info.height as usize);

    }

    #[inline]
    pub fn tty(&self) -> &tty::Tty {
        &self.tty
    }

    #[inline]
    pub fn size_info(&self) -> &SizeInfo {
        &self.size_info
    }

    pub fn grid(&self) -> &Grid {
        &self.grid
    }

    #[inline]
    pub fn mode(&self) -> &TermMode {
        &self.mode
    }

    pub fn swap_alt(&mut self) {
        self.alt = !self.alt;
        ::std::mem::swap(&mut self.grid, &mut self.alt_grid);
        ::std::mem::swap(&mut self.cursor, &mut self.alt_cursor);

        if self.alt {
            self.grid.clear();
        }
    }

    #[inline]
    pub fn cursor_x(&self) -> u16 {
        self.cursor.x
    }

    #[inline]
    pub fn cursor_y(&self) -> u16 {
        self.cursor.y
    }

    #[inline]
    pub fn cursor(&self) -> Cursor {
        self.cursor
    }

    /// Set character in current cursor position
    fn set_char(&mut self, c: char) {
        if self.cursor.x == self.grid.num_cols() as u16 {
            println!("wrapping");
            self.cursor.y += 1;
            self.cursor.x = 0;
        }

        if self.cursor.y == self.grid.num_rows() as u16 {
            panic!("cursor fell off grid");
        }

        let cell = &mut self.grid[self.cursor];
        cell.c = c;
        cell.fg = self.fg;
        cell.bg = self.bg;
        cell.flags = self.attr;
    }

    /// Convenience function for scrolling
    fn scroll(&mut self, count: isize) {
        println!("[TERM] scrolling {} lines", count);
        self.grid.scroll(self.scroll_region.clone(), count);
        if count > 0 {
            // Scrolled down, so need to clear from bottom
            let start = self.scroll_region.end - (count as usize);
            self.grid.clear_region(start..self.scroll_region.end);
        } else {
            // Scrolled up, clear from top
            let end = self.scroll_region.start + ((-count) as usize);
            self.grid.clear_region(self.scroll_region.start..end);
        }
    }
}

impl ansi::TermInfo for Term {
    #[inline]
    fn rows(&self) -> usize {
        self.grid.num_rows()
    }

    #[inline]
    fn cols(&self) -> usize {
        self.grid.num_cols()
    }
}

impl ansi::Handler for Term {
    /// A character to be displayed
    #[inline]
    fn input(&mut self, c: char) {
        self.set_char(c);
        self.cursor.x += 1;
    }

    fn goto(&mut self, x: i64, y: i64) {
        println!("goto: x={}, y={}", x, y);
        self.cursor.goto(x as u16, y as u16);
    }
    fn goto_row(&mut self, y: i64) {
        println!("goto_row: {}", y);
        let x = self.cursor_x();
        self.cursor.goto(x, y as u16);
    }
    fn goto_col(&mut self, x: i64) {
        println!("goto_col: {}", x);
        let y = self.cursor_y();
        self.cursor.goto(x as u16, y);
    }

    fn insert_blank(&mut self, num: i64) { println!("insert_blank: {}", num); }

    fn move_up(&mut self, rows: i64) {
        println!("move_up: {}", rows);
        self.cursor.advance(-rows, 0);
    }

    fn move_down(&mut self, rows: i64) {
        println!("move_down: {}", rows);
        self.cursor.advance(rows, 0);
    }

    fn move_forward(&mut self, cols: i64) {
        println!("move_forward: {}", cols);
        self.cursor.advance(0, cols);
    }

    fn move_backward(&mut self, spaces: i64) {
        println!("move_backward: {}", spaces);
        self.cursor.advance(0, -spaces);
    }

    fn identify_terminal(&mut self) { println!("identify_terminal"); }
    fn move_down_and_cr(&mut self, rows: i64) { println!("move_down_and_cr: {}", rows); }
    fn move_up_and_cr(&mut self, rows: i64) { println!("move_up_and_cr: {}", rows); }
    fn put_tab(&mut self, mut count: i64) {
        println!("put_tab: {}", count);

        let mut x = self.cursor_x();
        while x < self.grid.num_cols() as u16 && count != 0 {
            count -= 1;
            loop {
                if x == self.grid.num_cols() as u16 || self.tabs[x as usize] {
                    break;
                }
                x += 1;
            }
        }

        self.cursor.x = x;
    }

    /// Backspace `count` characters
    #[inline]
    fn backspace(&mut self) {
        println!("backspace");
        self.cursor.x -= 1;
    }

    /// Carriage return
    #[inline]
    fn carriage_return(&mut self) {
        println!("carriage_return");
        self.cursor.x = 0;
    }

    /// Linefeed
    #[inline]
    fn linefeed(&mut self) {
        println!("linefeed");
        // TODO handle scroll? not clear what parts of this the pty handle
        if self.cursor_y() + 1 >= self.scroll_region.end as u16 {
            self.scroll(1);
            self.clear_line(ansi::LineClearMode::Right);
        } else {
            self.cursor.y += 1;
        }
    }

    /// Set current position as a tabstop
    fn bell(&mut self) { println!("bell"); }
    fn substitute(&mut self) { println!("substitute"); }
    fn newline(&mut self) { println!("newline"); }
    fn set_horizontal_tabstop(&mut self) { println!("set_horizontal_tabstop"); }
    fn scroll_up(&mut self, rows: i64) {
        println!("scroll_up: {}", rows);
        self.scroll(rows as isize);
    }
    fn scroll_down(&mut self, rows: i64) {
        println!("scroll_down: {}", rows);
        self.scroll(-rows as isize);
    }
    fn insert_blank_lines(&mut self, count: i64) {
        println!("insert_blank_lines: {}", count);
        if self.scroll_region.contains(self.cursor_y() as usize) {
            self.scroll(-count as isize);
        }
    }
    fn delete_lines(&mut self, count: i64) {
        if self.scroll_region.contains(self.cursor_y() as usize) {
{'fraction_non_alphanumeric': 0.13917486463814535, 'fraction_numerical': 0.023106840539922215, 'mean_word_length': 1.8682195975503062, 'pattern_counts': {'":': 0, '<': 23, '<?xml version=': 0, '>': 59, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 6, 'word_list_counts': {'cursed_substrings.json': 6, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6145181', 'n_tokens_mistral': 9244, 'n_tokens_neox': 8765, 'n_words': 2459}