From a1db493c06eb185eb9ead55c4d9787cfc52b5e08 Mon Sep 17 00:00:00 2001 From: root Date: Tue, 20 Jan 2026 22:31:41 +0400 Subject: [PATCH] fix: open file in editor on Enter in TUI This change implements the ability to open selected files in the default editor when pressing Enter in the TUI results view. It respects the $EDITOR environment variable and attempts to jump to the matching line number for common editors (vi, nano, emacs). --- src/ui/search_tui.rs | 55 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 50 insertions(+), 5 deletions(-) diff --git a/src/ui/search_tui.rs b/src/ui/search_tui.rs index f1c4a80..2da9bc9 100644 --- a/src/ui/search_tui.rs +++ b/src/ui/search_tui.rs @@ -1,4 +1,4 @@ -use anyhow::Result; +use anyhow::{Context, Result}; use crossterm::{ event::{self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode, KeyEventKind}, execute, @@ -13,6 +13,7 @@ use ratatui::{ Frame, Terminal, }; use std::io; +use std::process::Command; use crate::core::{SearchEngine, SearchResult}; @@ -65,6 +66,42 @@ impl SearchTui { result } + fn open_file(&mut self, path: &std::path::Path, line: i32) -> Result<()> { + // Suspend TUI + disable_raw_mode()?; + execute!(io::stdout(), LeaveAlternateScreen, DisableMouseCapture)?; + + let editor = std::env::var("EDITOR").unwrap_or_else(|_| "nano".to_string()); + + let mut command = Command::new(&editor); + + // Handle common editors that support +LINE syntax + if editor.contains("vi") || editor.contains("nano") || editor.contains("emacs") { + command.arg(format!("+{}", line)); + } + + let status = command + .arg(path) + .status() + .context("Failed to open editor")?; + + if !status.success() { + self.status_message = Some(format!("Editor exited with error: {}", status)); + } + + // Restore TUI + enable_raw_mode()?; + execute!(io::stdout(), EnterAlternateScreen, EnableMouseCapture)?; + + // Redraw immediately + // We can't access terminal here directly to redraw, but the main loop will catch up on next iteration. + // However, to avoid a flash/blank screen until next event, we might want to trigger a redraw if possible, + // or just let the loop handle it. + // Since we are returning control to run_loop which calls terminal.draw(), it should be fine. + + Ok(()) + } + fn run_loop(&mut self, terminal: &mut Terminal>) -> Result<()> { loop { terminal.draw(|f| self.ui(f))?; @@ -111,10 +148,18 @@ impl SearchTui { } KeyCode::Enter => { if let Some(selected) = self.list_state.selected() { - if let Some(result) = self.results.get(selected) { - // Open file in default editor (or just print path) - self.status_message = - Some(format!("Selected: {}", result.path.display())); + // Clone the necessary data to avoid borrowing self.results while calling open_file + let result_data = self.results.get(selected).map(|r| (r.path.clone(), r.start_line)); + + if let Some((path, start_line)) = result_data { + // Open file in default editor + if let Err(e) = self.open_file(&path, start_line + 1) { + self.status_message = Some(format!("Error opening file: {}", e)); + } else { + self.status_message = Some(format!("Opened: {}", path.display())); + // Force a clear/redraw is implicit as we loop back + terminal.clear()?; + } } } }