diff --git a/src/edit.rs b/src/edit.rs index fa02a4249..8e96c0962 100644 --- a/src/edit.rs +++ b/src/edit.rs @@ -588,7 +588,7 @@ impl<'out, 'prompt, H: Helper> State<'out, 'prompt, H> { } } - /// Moves the cursor to the same column in the line above + /// Moves the cursor to the same column in the line below pub fn edit_move_line_down(&mut self, n: RepeatCount) -> Result { if self.line.move_to_line_down(n) { self.move_cursor()?; diff --git a/src/layout.rs b/src/layout.rs index 5aa8834fa..4ebdbd92e 100644 --- a/src/layout.rs +++ b/src/layout.rs @@ -1,5 +1,7 @@ use std::cmp::Ordering; +use crate::line_buffer::{ChangeListener, DeleteListener, Direction}; + #[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] pub struct Position { pub col: usize, // The leftmost column is number 0. @@ -21,13 +23,81 @@ impl Ord for Position { } } +/// (byte offset in input buffer <=> Screen cell) +pub type Cell = (usize, Position); + +/// All positions are relative to start of the prompt == origin (col: 0, row: 0) #[derive(Debug, Default)] pub struct Layout { - /// Prompt Unicode/visible width and height + /// Prompt Unicode/visible width and last row (relative) pub prompt_size: Position, pub default_prompt: bool, /// Cursor position (relative to the start of the prompt) + /// - cursor.row >= prompt_size.row + /// - if cursor.row >= prompt_size.row then cursor.col >= + /// prompt_size.col/width + // FIXME pub cursor: Position, - /// Number of rows used so far (from start of prompt to end of input) + /// Number of rows used so far (from start of prompt to end + /// of input or hint) + /// - cursor <= end + // FIXME pub end: Position, + /// Input breaked into sorted cells + // TODO ignore zero-width grapheme (even '\n') ? + pub cells: Vec, +} + +impl Layout { + /// Find the nearest byte / grapheme offset in input buffer + /// matching `pos` + fn find_byte_by(&self, pos: Position) -> &Cell { + match self.cells.binary_search_by_key(&pos, |cell| cell.1) { + Ok(i) => &self.cells[i], + Err(i) => { + if i < self.cells.len() { + &self.cells[i] + } else { + todo!() + } + } + } + } + + /// Find the nearest cell in screen matching `offset` + fn find_position_by(&self, offset: usize) -> &Cell { + match self.cells.binary_search_by_key(&offset, |cell| cell.0) { + Ok(i) => &self.cells[i], + Err(i) => { + if i < self.cells.len() { + &self.cells[i] + } else { + todo!() + } + } + } + } +} + +// TODO Update (cursor, end, cells) accordingly +// But we need to remember (old cursor/end row) +impl DeleteListener for Layout { + fn delete(&mut self, idx: usize, string: &str, dir: Direction) { + todo!() + } +} +// TODO Update (cursor, end, cells) accordingly +// But we need to remember (old cursor/end row) +impl ChangeListener for Layout { + fn insert_char(&mut self, idx: usize, c: char) { + todo!() + } + + fn insert_str(&mut self, idx: usize, string: &str) { + todo!() + } + + fn replace(&mut self, idx: usize, old: &str, new: &str) { + todo!() + } } diff --git a/src/line_buffer.rs b/src/line_buffer.rs index ac9c68821..31ca6f6b2 100644 --- a/src/line_buffer.rs +++ b/src/line_buffer.rs @@ -582,6 +582,7 @@ impl LineBuffer { } /// Moves the cursor to the same column in the line above + // FIXME pub fn move_to_line_up(&mut self, n: RepeatCount) -> bool { match self.buf[..self.pos].rfind('\n') { Some(off) => { @@ -610,6 +611,7 @@ impl LineBuffer { /// N lines up starting from the current one /// /// Fails if the cursor is on the first line + // FIXME fn n_lines_up(&self, n: RepeatCount) -> Option<(usize, usize)> { let mut start = if let Some(off) = self.buf[..self.pos].rfind('\n') { off + 1 @@ -633,6 +635,7 @@ impl LineBuffer { /// N lines down starting from the current one /// /// Fails if the cursor is on the last line + // FIXME fn n_lines_down(&self, n: RepeatCount) -> Option<(usize, usize)> { let mut end = if let Some(off) = self.buf[self.pos..].find('\n') { self.pos + off + 1 @@ -651,7 +654,8 @@ impl LineBuffer { Some((start, end)) } - /// Moves the cursor to the same column in the line above + /// Moves the cursor to the same column in the line below + // FIXME pub fn move_to_line_down(&mut self, n: RepeatCount) -> bool { match self.buf[self.pos..].find('\n') { Some(off) => { diff --git a/src/tty/mod.rs b/src/tty/mod.rs index f2abf0df5..9317647d4 100644 --- a/src/tty/mod.rs +++ b/src/tty/mod.rs @@ -60,6 +60,7 @@ pub trait Renderer { /// Compute layout for rendering prompt + line + some info (either hint, /// validation msg, ...). on the screen. Depending on screen width, line /// wrapping may be applied. + // FIXME fn compute_layout( &self, prompt_size: Position, @@ -67,6 +68,7 @@ pub trait Renderer { line: &LineBuffer, info: Option<&str>, ) -> Layout { + let mut cells = Vec::with_capacity(line.len()); // calculate the desired position of the cursor let pos = line.pos(); let cursor = self.calculate_position(&line[..pos], prompt_size); @@ -85,6 +87,7 @@ pub trait Renderer { default_prompt, cursor, end, + cells, }; debug_assert!(new_layout.prompt_size <= new_layout.cursor); debug_assert!(new_layout.cursor <= new_layout.end); @@ -93,6 +96,7 @@ pub trait Renderer { /// Calculate the number of columns and rows used to display `s` on a /// `cols` width terminal starting at `orig`. + // FIXME fn calculate_position(&self, s: &str, orig: Position) -> Position; fn write_and_flush(&mut self, buf: &str) -> Result<()>; diff --git a/src/tty/unix.rs b/src/tty/unix.rs index edaea5470..daec80199 100644 --- a/src/tty/unix.rs +++ b/src/tty/unix.rs @@ -67,6 +67,7 @@ fn get_win_size(fd: RawFd) -> (usize, usize) { } else { size.ws_row as usize }; + debug!(target: "rustyline", "winsize: cols: {}, rows: {}", cols, rows); (cols, rows) } _ => (80, 24),