tenseleyflow/fackr / 56d113d

Browse files

feat: Ctrl+G/F5 goto line with line:col syntax

Authored by espadonne
SHA
56d113dd9f864eaf4b806af68c0af82bc03794e6
Parents
ac0080d
Tree
743c203

1 changed file

StatusFile+-
M src/editor/state.rs 72 0
src/editor/state.rsmodified
@@ -94,6 +94,8 @@ enum TextInputAction {
9494
     GitCommit,
9595
     /// Create a git tag
9696
     GitTag,
97
+    /// Go to line (and optionally column)
98
+    GotoLine,
9799
 }
98100
 
99101
 /// LSP UI state
@@ -1534,6 +1536,9 @@ impl Editor {
15341536
             // === File operations ===
15351537
             // Open file browser (Fortress mode): Ctrl+O
15361538
             (Key::Char('o'), Modifiers { ctrl: true, .. }) => self.open_fortress(),
1539
+            // Go to line: Ctrl+G or F5
1540
+            (Key::Char('g'), Modifiers { ctrl: true, .. }) |
1541
+            (Key::F(5), _) => self.open_goto_line(),
15371542
 
15381543
             // === Editing ===
15391544
             (Key::Char(c), Modifiers { ctrl: false, alt: false, .. }) => {
@@ -3970,7 +3975,74 @@ impl Editor {
39703975
                 let (_, msg) = self.workspace.fuss.git_tag(buffer);
39713976
                 self.message = Some(msg);
39723977
             }
3978
+            TextInputAction::GotoLine => {
3979
+                self.goto_line_col(buffer);
3980
+            }
3981
+        }
3982
+    }
3983
+
3984
+    /// Open the goto line prompt
3985
+    fn open_goto_line(&mut self) {
3986
+        self.prompt = PromptState::TextInput {
3987
+            label: "Go to line: ".to_string(),
3988
+            buffer: String::new(),
3989
+            action: TextInputAction::GotoLine,
3990
+        };
3991
+        self.message = Some("Go to line: ".to_string());
3992
+    }
3993
+
3994
+    /// Parse line:col input and jump to position
3995
+    fn goto_line_col(&mut self, input: &str) {
3996
+        let input = input.trim();
3997
+        if input.is_empty() {
3998
+            return;
39733999
         }
4000
+
4001
+        // Parse formats: "line", "line:", "line:col"
4002
+        let (line_str, col_str) = if let Some(colon_pos) = input.find(':') {
4003
+            (&input[..colon_pos], &input[colon_pos + 1..])
4004
+        } else {
4005
+            (input, "")
4006
+        };
4007
+
4008
+        let line: usize = match line_str.parse::<usize>() {
4009
+            Ok(n) if n > 0 => n - 1, // Convert to 0-indexed
4010
+            Ok(_) => {
4011
+                self.message = Some("Invalid line number".to_string());
4012
+                return;
4013
+            }
4014
+            Err(_) => {
4015
+                self.message = Some("Invalid line number".to_string());
4016
+                return;
4017
+            }
4018
+        };
4019
+
4020
+        let col: usize = if col_str.is_empty() {
4021
+            0
4022
+        } else {
4023
+            match col_str.parse::<usize>() {
4024
+                Ok(n) if n > 0 => n - 1, // Convert to 0-indexed
4025
+                Ok(_) => 0,
4026
+                Err(_) => 0,
4027
+            }
4028
+        };
4029
+
4030
+        // Clamp to buffer bounds
4031
+        let line_count = self.buffer().line_count();
4032
+        let line = line.min(line_count.saturating_sub(1));
4033
+        let line_len = self.buffer().line_len(line);
4034
+        let col = col.min(line_len);
4035
+
4036
+        // Move cursor
4037
+        self.cursor_mut().line = line;
4038
+        self.cursor_mut().col = col;
4039
+        self.cursor_mut().desired_col = col;
4040
+        self.cursor_mut().clear_selection();
4041
+
4042
+        // Center the view on the target line
4043
+        self.scroll_to_cursor();
4044
+
4045
+        self.message = Some(format!("Line {}, Column {}", line + 1, col + 1));
39744046
     }
39754047
 
39764048
     fn restore_backups(&mut self) -> Result<()> {