diff options
| author | Irene Knapp <ireneista@irenes.space> | 2026-03-27 05:51:13 -0700 |
|---|---|---|
| committer | Irene Knapp <ireneista@irenes.space> | 2026-03-27 05:56:04 -0700 |
| commit | a801a36a5acc8adb43ea308c09e759a2f1c2344c (patch) | |
| tree | 53661577ce2ef238eba6a07a40f32db38460ab21 /src | |
| parent | 08eb793fcecb70c02c83ff5fdc9407c0fadd113c (diff) | |
handling of the neutral column is implemented fully now
this is what POSIX seems to call the "current column", but that's a confusing name... POSIX also seems to think in terms of the cursor's actual position not being a thing that persists, but that feels wrong so here it's a separate concept. Force-Push: yes Change-Id: I0c3a171ebdf9ca74a1a8faaeeb85cd7d2a37b2ff
Diffstat (limited to 'src')
| -rw-r--r-- | src/main.rs | 57 |
1 files changed, 48 insertions, 9 deletions
diff --git a/src/main.rs b/src/main.rs index 7f84646..79abbd5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -35,8 +35,12 @@ struct Window { * aesthetically pleasing, a quiet reminder of how much humans care about * everything. */ - cursor_column: RwLock<usize>, cursor_row: RwLock<usize>, + cursor_column: RwLock<usize>, + /* The neutral column is the one that vertical movement will attempt to + * land in, if it exists in the target row. + */ + cursor_neutral_column: RwLock<usize>, } @@ -177,6 +181,12 @@ impl Ivy { let mut column = window.cursor_column.write().await; if *column > 0 { *column -= 1; + + /* The neutral column only changes if the horizontal part of + * 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(()) @@ -222,6 +232,12 @@ impl Ivy { let mut column = window.cursor_column.write().await; if *column + 1 < width { *column += 1; + + /* The neutral column only changes if the horizontal part of + * 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; } } @@ -236,6 +252,10 @@ 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. + */ async fn handle_movement(&mut self, movement: impl AsyncFn(&mut Ivy) -> Result<()>) -> Result<()> @@ -249,17 +269,35 @@ impl Ivy { movement(self).await?; - let (new_row, new_column) = { - let window = self.window.read().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. + */ - (*window.cursor_row.read().await, - *window.cursor_column.read().await) - }; + let window = self.window.write().await; + let row = window.cursor_row.read().await; + let mut column = window.cursor_column.write().await; + let buffer = self.buffer.read().await; + + if let Some(span) = buffer.line_span(*row).await { + let width = span.end - span.start; + let neutral_column = *window.cursor_neutral_column.read().await; - if old_row != new_row || old_column != new_column { + if neutral_column < width { + *column = neutral_column; + } else if width > 0 { + *column = width - 1; + } else { + *column = 0; + } + } + + if old_row != *row || old_column != *column { let mut terminal = self.terminal.write().await; - terminal.do_cursor_position(new_column, new_row).await?; + terminal.do_cursor_position(*column, *row).await?; terminal.stdout.flush().await?; } @@ -355,8 +393,9 @@ impl Buffer { impl Window { pub async fn new() -> Self { Window { - cursor_column: RwLock::new(0), cursor_row: RwLock::new(0), + cursor_column: RwLock::new(0), + cursor_neutral_column: RwLock::new(0), } } } |