1- use anyhow:: Result ;
1+ use anyhow:: { anyhow , Result } ;
22use ratatui:: widgets:: ListState ;
33use signal_hook:: { consts:: SIGINT , consts:: SIGTERM , iterator:: Signals } ;
44use std:: sync:: mpsc;
55use std:: thread;
66
7- use crate :: filesystem:: { list_files, FileNode } ;
7+ use crate :: filesystem:: { list_files, normalize_path , trim_end_slash , FileNode } ;
88use crate :: numbers:: ClampNumExt ;
99use crate :: tree:: { render_tree_nodes, TreeNode } ;
1010use crate :: tui:: Tui ;
1111
1212#[ derive( Debug , Default ) ]
1313pub struct App {
1414 pub should_quit : bool ,
15+ pub starting_dir : String ,
1516 pub parent_nodes : Vec < FileNode > , // nodes leading to the current directory
1617 pub child_nodes : Vec < FileNode > , // nodes in the current directory
1718 pub child_tree_nodes : Vec < TreeNode > , // nodes of whole filesystem tree
@@ -28,7 +29,7 @@ impl App {
2829 }
2930
3031 pub fn run ( & mut self ) -> Result < ( ) > {
31- self . pre_init ( ) ;
32+ self . pre_init ( ) ? ;
3233 let signal_rx = self . handle_signals ( ) ;
3334 self . init ( ) ;
3435 let mut tui = Tui :: new ( ) ;
@@ -62,12 +63,27 @@ impl App {
6263
6364 pub fn tick ( & mut self ) { }
6465
65- pub fn pre_init ( & mut self ) {
66+ pub fn pre_init ( & mut self ) -> Result < ( ) > {
6667 let args: Vec < String > = std:: env:: args ( ) . collect ( ) ;
67- if args. last ( ) . map ( |s| s == "--version" ) . unwrap_or ( false ) {
68- println ! ( "{}" , env!( "CARGO_PKG_VERSION" ) ) ;
69- std:: process:: exit ( 0 ) ;
68+ if args. len ( ) == 2 {
69+ let last: & String = args. last ( ) . unwrap ( ) ;
70+ if last == "--version" {
71+ println ! ( "{}" , env!( "CARGO_PKG_VERSION" ) ) ;
72+ std:: process:: exit ( 0 ) ;
73+ } else if last == "--help" {
74+ println ! ( "fpick - interactive file picker. Usage:" ) ;
75+ println ! ( "fpick - to select a file in a current directory and return its path" ) ;
76+ println ! ( "fpick <path> - to select a file starting from a specified directory" ) ;
77+ println ! ( "fpick --version - to print version" ) ;
78+ println ! ( "fpick --help - to print usage" ) ;
79+ std:: process:: exit ( 0 ) ;
80+ } else {
81+ self . starting_dir = trim_end_slash ( last. to_string ( ) ) ;
82+ }
83+ } else if args. len ( ) > 2 {
84+ return Err ( anyhow ! ( "unrecognized arguments. Use --help for usage" ) ) ;
7085 }
86+ Ok ( ( ) )
7187 }
7288
7389 pub fn init ( & mut self ) {
@@ -98,6 +114,10 @@ impl App {
98114 }
99115 }
100116
117+ pub fn reset_cursor_offset ( & mut self ) {
118+ self . file_tree_state = self . file_tree_state . clone ( ) . with_offset ( 0 ) ;
119+ }
120+
101121 pub fn move_cursor ( & mut self , delta : i32 ) {
102122 let new_cursor = ( self . dir_cursor as i32 + delta)
103123 . clamp_max ( self . child_tree_nodes . len ( ) as i32 - 1 )
@@ -106,14 +126,18 @@ impl App {
106126 }
107127
108128 pub fn get_current_string_path ( & self ) -> String {
109- if self . parent_nodes . is_empty ( ) {
110- return "." . to_string ( ) ;
111- }
112- self . parent_nodes
129+ let mut all_names = self
130+ . parent_nodes
113131 . iter ( )
114132 . map ( |node| node. name . to_string ( ) )
115- . collect :: < Vec < String > > ( )
116- . join ( "/" )
133+ . collect :: < Vec < String > > ( ) ;
134+ if !self . starting_dir . is_empty ( ) {
135+ all_names. insert ( 0 , self . starting_dir . clone ( ) ) ;
136+ }
137+ if all_names. is_empty ( ) {
138+ return "." . to_string ( ) ;
139+ }
140+ return normalize_path ( all_names. join ( "/" ) ) ;
117141 }
118142
119143 pub fn populate_current_child_nodes ( & mut self ) {
@@ -157,6 +181,7 @@ impl App {
157181 self . dir_cursor = 0 ;
158182 }
159183 }
184+ self . reset_cursor_offset ( ) ;
160185 self . set_dir_cursor ( self . dir_cursor ) ;
161186 }
162187
@@ -182,6 +207,7 @@ impl App {
182207 self . parent_nodes . push ( selected_node) ;
183208 self . filter_text . clear ( ) ;
184209 self . populate_current_child_nodes ( ) ;
210+ self . reset_cursor_offset ( ) ;
185211 self . set_dir_cursor ( 0 ) ;
186212 }
187213
@@ -195,20 +221,24 @@ impl App {
195221 }
196222 let selected_node: FileNode = selected_node_o. unwrap ( ) ;
197223
198- let mut all_nodes = self . parent_nodes . clone ( ) ;
199- all_nodes. push ( selected_node) ;
200- let selected_path = all_nodes
224+ let mut all_names = self
225+ . parent_nodes
201226 . iter ( )
202227 . map ( |node| node. name . to_string ( ) )
203- . collect :: < Vec < String > > ( )
204- . join ( "/" ) ;
228+ . collect :: < Vec < String > > ( ) ;
229+ if !self . starting_dir . is_empty ( ) {
230+ all_names. insert ( 0 , self . starting_dir . clone ( ) ) ;
231+ }
232+ all_names. push ( selected_node. name . to_string ( ) ) ;
233+ let selected_path = normalize_path ( all_names. join ( "/" ) ) ;
205234
206235 self . picked_path = Some ( selected_path) ;
207236 self . quit ( ) ;
208237 }
209238
210239 pub fn render_tree_nodes ( & mut self ) {
211240 self . child_tree_nodes = render_tree_nodes ( & self . child_nodes , & self . filter_text ) ;
241+ self . reset_cursor_offset ( ) ;
212242 self . move_cursor ( 0 ) ; // validate cursor position
213243 }
214244}
0 commit comments