diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/main.rs | 143 |
1 files changed, 117 insertions, 26 deletions
diff --git a/src/main.rs b/src/main.rs index 9b2a2e7..4b5dc5a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -44,6 +44,12 @@ struct Window { scroll_top: RwLock<usize>, } +enum MovementColumnBehavior { + CurrentFromNeutral, + NeutralFromCurrent, + FirstNonBlank, +} + fn main() -> ExitCode { smol::block_on(async { @@ -214,10 +220,10 @@ impl Ivy { * the movement actually moves. Tentatively, it doesn't look * like POSIX has an opinion on this, but it matches Vim. */ - *window.cursor_neutral_column.write().await = *column; + Ok(MovementColumnBehavior::NeutralFromCurrent) + } else { + Ok(MovementColumnBehavior::CurrentFromNeutral) } - - Ok(()) }).await?; }, @@ -231,7 +237,7 @@ impl Ivy { *row += 1; }; - Ok(()) + Ok(MovementColumnBehavior::CurrentFromNeutral) }).await?; }, @@ -244,7 +250,7 @@ impl Ivy { *row -= 1; } - Ok(()) + Ok(MovementColumnBehavior::CurrentFromNeutral) }).await?; }, @@ -265,11 +271,13 @@ impl Ivy { * the movement actually moves. Tentatively, it doesn't look * like POSIX has an opinion on this, but it matches Vim. */ - *window.cursor_neutral_column.write().await = *column; + Ok(MovementColumnBehavior::NeutralFromCurrent) + } else { + Ok(MovementColumnBehavior::CurrentFromNeutral) } + } else { + Ok(MovementColumnBehavior::CurrentFromNeutral) } - - Ok(()) }).await?; }, @@ -283,9 +291,7 @@ impl Ivy { /* For this one, the neutral column changes regardless of * whether the column does. */ - *window.cursor_neutral_column.write().await = *column; - - Ok(()) + Ok(MovementColumnBehavior::NeutralFromCurrent) }).await?; }, @@ -304,14 +310,43 @@ impl Ivy { } else { *column = 0; } + } - /* For this one, the neutral column changes regardless of - * whether the column does. - */ - *window.cursor_neutral_column.write().await = *column; + /* For this one, the neutral column changes regardless of + * whether the column does. + */ + Ok(MovementColumnBehavior::NeutralFromCurrent) + }).await?; + }, + + 'H' => { + self.handle_movement(async |ivy: &mut Ivy| { + let window = ivy.window.write().await; + + let mut row = window.cursor_row.write().await; + *row = *window.scroll_top.read().await; + + Ok(MovementColumnBehavior::FirstNonBlank) + }).await?; + }, + + 'L' => { + self.handle_movement(async |ivy: &mut Ivy| { + let window = ivy.window.write().await; + let terminal = ivy.terminal.read().await; + let buffer = ivy.buffer.read().await; + + let scroll_top = *window.scroll_top.read().await; + let height = *terminal.height.read().await; + let total_lines = buffer.lines.read().await.len(); + + let mut row = window.cursor_row.write().await; + *row = scroll_top + height - 2; + if *row > total_lines - 1 { + *row = total_lines - 1; } - Ok(()) + Ok(MovementColumnBehavior::FirstNonBlank) }).await?; }, @@ -325,9 +360,13 @@ impl Ivy { /* A movement is an arbitrarily complicated action that will not change the * contents of the buffer, but may change other things, including the cursor * position. + * + * The return value of the action indicates whether to update the neutral + * column. */ async fn handle_movement(&mut self, - movement: impl AsyncFn(&mut Ivy) -> Result<()>) + movement: impl AsyncFn(&mut Ivy) + -> Result<MovementColumnBehavior>) -> Result<()> { let (old_row, old_column) = { @@ -337,15 +376,28 @@ impl Ivy { *window.cursor_column.read().await) }; - movement(self).await?; - - /* We clamp the column to the line width unconditionally, regardless of - * what kind of movement we did. This is always correct, because no - * intended behavior ever places the cursor beyond the end of the line. - * It does require taking a couple of locks that we might always need, but - * there's no strong reason to avoid that. - */ - self.clamp_cursor_column().await?; + let column_behavior = movement(self).await?; + + match column_behavior { + MovementColumnBehavior::NeutralFromCurrent => { + /* We clamp the destination column to the line width and use that as + * the neutral column. + */ + self.update_neutral_column().await?; + }, + MovementColumnBehavior::CurrentFromNeutral => { + /* We clamp the neutral column to the line width and use that as the + * destination column. + */ + self.clamp_cursor_column().await?; + }, + MovementColumnBehavior::FirstNonBlank => { + /* We compute the first non-blank column on the line and use that as + * both the destination and neutral columns. + */ + self.first_non_blank_column().await?; + }, + } self.scroll_to_cursor().await?; @@ -366,6 +418,28 @@ impl Ivy { Ok(()) } + async fn update_neutral_column(&mut self) -> Result<()> { + let buffer = self.buffer.write().await; + let window = self.window.write().await; + let mut column = window.cursor_column.write().await; + let mut neutral_column = window.cursor_neutral_column.write().await; + let row = window.cursor_row.read().await; + + if let Some(span) = buffer.line_span(*row).await { + let width = span.end - span.start; + + if width == 0 { + *column = 0; + } else if *column > width - 1 { + *column = width - 1; + } + } + + *neutral_column = *column; + + Ok(()) + } + async fn clamp_cursor_column(&mut self) -> Result<()> { let window = self.window.write().await; let row = window.cursor_row.read().await; @@ -388,6 +462,23 @@ impl Ivy { Ok(()) } + async fn first_non_blank_column(&mut self) -> Result<()> { + let row = *self.window.read().await.cursor_row.read().await; + + if let Some(span) = self.buffer.read().await.line_span(row).await { + /* TODO */ + let window = self.window.write().await; + *window.cursor_column.write().await = 0; + *window.cursor_neutral_column.write().await = 0; + } else { + let window = self.window.write().await; + *window.cursor_column.write().await = 0; + *window.cursor_neutral_column.write().await = 0; + } + + Ok(()) + } + async fn scroll_to_cursor(&mut self) -> Result<()> { let old_scroll_top = *self.window.read().await.scroll_top.read().await; |