lineeditor 0.4.1

A cross platform rich line editor
Documentation
use std::io::stdout;
use std::io::Result;
use std::io::Write;

use crossterm::cursor::position;
use crossterm::cursor::MoveTo;
use crossterm::cursor::MoveToColumn;
use crossterm::cursor::MoveToNextLine;
use crossterm::cursor::MoveToPreviousLine;
use crossterm::terminal;
use crossterm::terminal::Clear;
use crossterm::terminal::ClearType;
use crossterm::terminal::ScrollUp;
use crossterm::QueueableCommand;

use crate::completion::Suggestion;
use crate::style::Style;
use crate::ListView;

#[derive(Default)]
pub struct DropDownListView {
    elements: Vec<Suggestion>,
    focus_style: Style,
    focus_position: i64,
    is_visible: bool,
}

impl ListView<Suggestion> for DropDownListView {
    fn render(&mut self) -> Result<()> {
        let mut stdout = std::io::BufWriter::new(std::io::stderr());

        let (_, rows) = terminal::size()?;
        let (start_column, start_row) = position()?;

        let mut number_of_scrolls = 0;
        if (start_row + 1 + self.elements.len() as u16) > rows {
            number_of_scrolls = (start_row + 1 + self.elements.len() as u16) - rows + 1;
            stdout.queue(ScrollUp(number_of_scrolls))?;
            stdout.queue(MoveToPreviousLine(number_of_scrolls))?;
        }

        for (index, suggestion) in self.elements.iter_mut().enumerate() {
            let content = &mut suggestion.content;
            stdout.queue(MoveToNextLine(1))?;
            stdout.queue(MoveToColumn(start_column))?;

            if index as i64 == self.focus_position {
                let mut current_styles = content.styles().clone();
                content.style_all(self.focus_style.clone());
                super::base::render_styled_buffer(&mut stdout, content)?;
                content.set_styles(&mut current_styles);
            } else {
                super::base::render_styled_buffer(&mut stdout, content)?;
            }
        }

        stdout.queue(MoveTo(start_column, start_row - number_of_scrolls))?;
        stdout.flush()?;
        Ok(())
    }

    fn clear(&self) -> Result<()> {
        let mut stdout = stdout();
        stdout.queue(Clear(ClearType::FromCursorDown))?;
        stdout.flush()?;
        Ok(())
    }

    fn set_visibility(&mut self, visible: bool) {
        self.is_visible = visible;
    }

    fn is_visible(&self) -> bool {
        self.is_visible
    }

    fn set_focus_position(&mut self, position: i64) {
        self.focus_position = position;
    }

    fn set_focus_style(&mut self, style: Style) {
        self.focus_style = style;
    }

    fn focus_next(&mut self) {
        if self.focus_position < self.elements.len() as i64 - 1 {
            self.focus_position += 1;
        }
    }

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

    fn clear_focus(&mut self) {
        self.focus_position = 0;
    }

    fn reset(&mut self) {
        self.clear_elements();
        self.clear_focus();
    }

    fn set_elements(&mut self, elements: &mut Vec<Suggestion>) {
        self.elements.append(elements);
    }

    fn clear_elements(&mut self) {
        self.elements.clear();
    }

    fn selected_element(&self) -> Option<&Suggestion> {
        self.elements.get(self.focus_position as usize)
    }

    fn is_empty(&self) -> bool {
        self.elements.is_empty()
    }

    fn len(&self) -> usize {
        self.elements.len()
    }
}