@@ -8,7 +8,9 @@ use crate::git::{self, StatusCache};
8
8
use crate :: icons;
9
9
use crate :: utils;
10
10
use crossterm:: {
11
- event:: { self , DisableMouseCapture , EnableMouseCapture , Event , KeyCode } ,
11
+ event:: {
12
+ self , DisableMouseCapture , EnableMouseCapture , Event , KeyCode , KeyEvent , KeyModifiers ,
13
+ } ,
12
14
execute,
13
15
terminal:: { disable_raw_mode, enable_raw_mode, EnterAlternateScreen , LeaveAlternateScreen } ,
14
16
} ;
@@ -22,21 +24,18 @@ use ratatui::{
22
24
} ;
23
25
use std:: env;
24
26
use std:: fs;
25
- use std:: io:: { self , Stdout , Write } ;
27
+ use std:: io:: { stderr , stdout , IsTerminal , Write } ;
26
28
use std:: path:: { Path , PathBuf } ;
27
29
use std:: process:: Command ;
28
30
29
31
// Platform-specific import for unix permissions
30
32
#[ cfg( unix) ]
31
33
use std:: os:: unix:: fs:: PermissionsExt ;
32
34
33
- // ... (rest of TUI file is unchanged, no new bugs were present here)
34
- // The existing TUI code should work correctly with the updated git.rs
35
-
36
- // ... (pasting the rest of the file for completeness)
37
35
enum PostExitAction {
38
36
None ,
39
37
OpenFile ( PathBuf ) ,
38
+ PrintPath ( PathBuf ) ,
40
39
}
41
40
42
41
#[ derive( Debug , Clone ) ]
@@ -152,30 +151,33 @@ impl AppState {
152
151
}
153
152
154
153
pub fn run ( args : & InteractiveArgs ) -> anyhow:: Result < ( ) > {
155
- let root_path = match fs:: canonicalize ( & args. path ) {
156
- Ok ( path) => path,
157
- Err ( e) => anyhow:: bail!( "Invalid path '{}': {}" , args. path. display( ) , e) ,
158
- } ;
159
-
160
- if !root_path. is_dir ( ) {
154
+ if !args. path . is_dir ( ) {
161
155
anyhow:: bail!( "'{}' is not a directory." , args. path. display( ) ) ;
162
156
}
157
+ let root_path = fs:: canonicalize ( & args. path ) ?;
163
158
164
159
let mut app_state = AppState :: new ( args, & root_path) ?;
165
160
let mut terminal = setup_terminal ( ) ?;
166
161
let post_exit_action = run_app ( & mut terminal, & mut app_state, args) ?;
167
162
restore_terminal ( & mut terminal) ?;
168
163
169
- if let PostExitAction :: OpenFile ( path) = post_exit_action {
170
- let editor = env:: var ( "EDITOR" ) . unwrap_or_else ( |_| {
171
- if cfg ! ( windows) {
172
- "notepad" . to_string ( )
173
- } else {
174
- "vim" . to_string ( )
175
- }
176
- } ) ;
177
- Command :: new ( editor) . arg ( path) . status ( ) ?;
164
+ match post_exit_action {
165
+ PostExitAction :: OpenFile ( path) => {
166
+ let editor = env:: var ( "EDITOR" ) . unwrap_or_else ( |_| {
167
+ if cfg ! ( windows) {
168
+ "notepad" . to_string ( )
169
+ } else {
170
+ "vim" . to_string ( )
171
+ }
172
+ } ) ;
173
+ Command :: new ( editor) . arg ( path) . status ( ) ?;
174
+ }
175
+ PostExitAction :: PrintPath ( path) => {
176
+ println ! ( "{}" , path. display( ) ) ;
177
+ }
178
+ PostExitAction :: None => { }
178
179
}
180
+
179
181
Ok ( ( ) )
180
182
}
181
183
@@ -186,12 +188,23 @@ fn run_app<B: Backend + Write>(
186
188
) -> anyhow:: Result < PostExitAction > {
187
189
loop {
188
190
terminal. draw ( |f| ui ( f, app_state, args) ) ?;
191
+
189
192
if let Event :: Key ( key) = event:: read ( ) ? {
190
- match key. code {
191
- KeyCode :: Char ( 'q' ) | KeyCode :: Esc => break Ok ( PostExitAction :: None ) ,
192
- KeyCode :: Down | KeyCode :: Char ( 'j' ) => app_state. next ( ) ,
193
- KeyCode :: Up | KeyCode :: Char ( 'k' ) => app_state. previous ( ) ,
194
- KeyCode :: Enter => {
193
+ match key {
194
+ KeyEvent { code : KeyCode :: Char ( 's' ) , modifiers : KeyModifiers :: CONTROL , .. } => {
195
+ if let Some ( entry) = app_state. get_selected_entry ( ) {
196
+ break Ok ( PostExitAction :: PrintPath ( entry. path . clone ( ) ) ) ;
197
+ }
198
+ }
199
+ KeyEvent { code : KeyCode :: Char ( 'q' ) , .. } | KeyEvent { code : KeyCode :: Esc , .. } => {
200
+ break Ok ( PostExitAction :: None ) ;
201
+ }
202
+ KeyEvent { code : KeyCode :: Down , .. }
203
+ | KeyEvent { code : KeyCode :: Char ( 'j' ) , .. } => app_state. next ( ) ,
204
+ KeyEvent { code : KeyCode :: Up , .. } | KeyEvent { code : KeyCode :: Char ( 'k' ) , .. } => {
205
+ app_state. previous ( )
206
+ }
207
+ KeyEvent { code : KeyCode :: Enter , .. } => {
195
208
if let Some ( entry) = app_state. get_selected_entry ( ) {
196
209
if entry. is_dir {
197
210
app_state. toggle_selected_directory ( ) ;
@@ -291,7 +304,6 @@ fn scan_directory(
291
304
let mut entries = Vec :: new ( ) ;
292
305
let mut builder = WalkBuilder :: new ( path) ;
293
306
builder. hidden ( !args. all ) . git_ignore ( args. gitignore ) ;
294
-
295
307
for result in builder. build ( ) . flatten ( ) {
296
308
if result. path ( ) == path {
297
309
continue ;
@@ -355,11 +367,17 @@ fn map_color(c: colored::Color) -> Color {
355
367
}
356
368
}
357
369
358
- fn setup_terminal ( ) -> anyhow:: Result < Terminal < CrosstermBackend < Stdout > > > {
359
- let mut stdout = io:: stdout ( ) ;
370
+ // --- Terminal setup and restore functions ---
371
+ type TerminalWriter = CrosstermBackend < Box < dyn Write + Send > > ;
372
+
373
+ fn setup_terminal ( ) -> anyhow:: Result < Terminal < TerminalWriter > > {
374
+ // If stdout is a pipe, draw the TUI to stderr, otherwise use stdout.
375
+ let writer: Box < dyn Write + Send > =
376
+ if stdout ( ) . is_terminal ( ) { Box :: new ( stdout ( ) ) } else { Box :: new ( stderr ( ) ) } ;
360
377
enable_raw_mode ( ) ?;
361
- execute ! ( stdout, EnterAlternateScreen , EnableMouseCapture ) ?;
362
- let backend = CrosstermBackend :: new ( stdout) ;
378
+ let mut writer_mut = writer;
379
+ execute ! ( writer_mut, EnterAlternateScreen , EnableMouseCapture ) ?;
380
+ let backend = CrosstermBackend :: new ( writer_mut) ;
363
381
Terminal :: new ( backend) . map_err ( anyhow:: Error :: from)
364
382
}
365
383
@@ -370,6 +388,7 @@ fn restore_terminal<B: Backend + Write>(terminal: &mut Terminal<B>) -> anyhow::R
370
388
Ok ( ( ) )
371
389
}
372
390
391
+ // ... (Unit tests remain the same)
373
392
#[ cfg( test) ]
374
393
mod tests {
375
394
use super :: * ;
0 commit comments