skim 4.0.0

Fuzzy Finder in rust!
Documentation
use crate::tui::{Direction, Size};

/// Layout configuration for the TUI
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "cli", derive(clap::ValueEnum))]
pub enum TuiLayout {
    /// Display from the bottom of the screen
    #[default]
    Default,
    /// Display from the top of the screen
    Reverse,
    /// Display from the top of the screen, prompt at the bottom
    ReverseList,
}

/// Configuration for the preview pane layout
#[derive(Debug, Clone)]
pub struct PreviewLayout {
    /// Direction where preview pane is positioned
    pub direction: Direction,
    /// Size of the preview pane
    pub size: Size,
    /// Whether the preview pane is hidden
    pub hidden: bool,
    /// Optional offset for preview position
    pub offset: Option<String>,
    /// Whether or not to wrap the preview contents
    pub wrap: bool,
    /// Whether or not to run the preview in a PTY
    pub pty: bool,
}

impl Default for PreviewLayout {
    fn default() -> Self {
        Self {
            direction: Direction::Right,
            size: Size::Percent(50),
            hidden: false,
            offset: None,
            wrap: false,
            pty: false,
        }
    }
}

impl From<&str> for PreviewLayout {
    fn from(value: &str) -> Self {
        let mut res: Self = PreviewLayout::default();
        // Parse the remainder which can be: size:offset:hidden, offset:hidden, size:hidden, etc.
        let parts: Vec<&str> = value.split(':').collect();

        for part in parts {
            if part.is_empty() {
                continue;
            }

            if part.starts_with('+') {
                // This is an offset expression
                res.offset = Some(part.to_string());
            } else if part == "hidden" {
                res.hidden = true;
            } else if part == "nohidden" {
                res.hidden = false;
            } else if part == "wrap" {
                res.wrap = true;
            } else if part == "nowrap" {
                res.wrap = false;
            } else if part == "pty" {
                res.pty = true;
            } else if part == "nopty" {
                res.pty = false;
            } else {
                // Try to parse as size
                if let Ok(size) = part.try_into() {
                    res.size = size;
                }
                if let Ok(dir) = part.try_into() {
                    res.direction = dir;
                }
            }
        }
        res
    }
}

// impl PreviewLayout {

// }

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_preview_layout_direction_only() {
        let layout = PreviewLayout::from("left");
        assert_eq!(layout.direction, Direction::Left);
        assert_eq!(layout.size, Size::Percent(50)); // default
        assert!(!layout.hidden);
        assert_eq!(layout.offset, None);

        let layout = PreviewLayout::from("right");
        assert_eq!(layout.direction, Direction::Right);

        let layout = PreviewLayout::from("up");
        assert_eq!(layout.direction, Direction::Up);

        let layout = PreviewLayout::from("down");
        assert_eq!(layout.direction, Direction::Down);
    }

    #[test]
    fn test_preview_layout_with_size() {
        let layout = PreviewLayout::from("left:30%");
        assert_eq!(layout.direction, Direction::Left);
        assert_eq!(layout.size, Size::Percent(30));
        assert!(!layout.hidden);
        assert_eq!(layout.offset, None);

        let layout = PreviewLayout::from("right:40");
        assert_eq!(layout.direction, Direction::Right);
        assert_eq!(layout.size, Size::Fixed(40));
    }

    #[test]
    fn test_preview_layout_with_offset() {
        let layout = PreviewLayout::from("left:+123");
        assert_eq!(layout.direction, Direction::Left);
        assert_eq!(layout.offset, Some("+123".to_string()));

        let layout = PreviewLayout::from("left:+{2}");
        assert_eq!(layout.direction, Direction::Left);
        assert_eq!(layout.offset, Some("+{2}".to_string()));

        let layout = PreviewLayout::from("left:+{2}-2");
        assert_eq!(layout.direction, Direction::Left);
        assert_eq!(layout.offset, Some("+{2}-2".to_string()));
    }

    #[test]
    fn test_preview_layout_with_size_and_offset() {
        let layout = PreviewLayout::from("left:50%:+{2}");
        assert_eq!(layout.direction, Direction::Left);
        assert_eq!(layout.size, Size::Percent(50));
        assert_eq!(layout.offset, Some("+{2}".to_string()));

        let layout = PreviewLayout::from("right:40:+123");
        assert_eq!(layout.direction, Direction::Right);
        assert_eq!(layout.size, Size::Fixed(40));
        assert_eq!(layout.offset, Some("+123".to_string()));
    }

    #[test]
    fn test_preview_layout_with_hidden() {
        let layout = PreviewLayout::from("left:hidden");
        assert_eq!(layout.direction, Direction::Left);
        assert!(layout.hidden);

        let layout = PreviewLayout::from("right:50%:hidden");
        assert_eq!(layout.direction, Direction::Right);
        assert_eq!(layout.size, Size::Percent(50));
        assert!(layout.hidden);
    }

    #[test]
    fn test_preview_layout_complex() {
        let layout = PreviewLayout::from("left:30%:+{2}-5:hidden");
        assert_eq!(layout.direction, Direction::Left);
        assert_eq!(layout.size, Size::Percent(30));
        assert_eq!(layout.offset, Some("+{2}-5".to_string()));
        assert!(layout.hidden);
    }
}