summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorIrene Knapp <ireneista@irenes.space>2026-03-27 05:51:13 -0700
committerIrene Knapp <ireneista@irenes.space>2026-03-27 05:56:04 -0700
commita801a36a5acc8adb43ea308c09e759a2f1c2344c (patch)
tree53661577ce2ef238eba6a07a40f32db38460ab21 /src
parent08eb793fcecb70c02c83ff5fdc9407c0fadd113c (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.rs57
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),
     }
   }
 }