@@ -49,6 +49,156 @@ fn extract_rubies_dir_from_line(words: &[&str]) -> Option<PathBuf> {
4949 None
5050}
5151
52+ /// Suggest directories for completion
53+ fn suggest_directories ( current : & str ) {
54+ let current_path = std:: path:: Path :: new ( current) ;
55+
56+ let ( search_dir, prefix) = if current. is_empty ( ) {
57+ ( std:: path:: PathBuf :: from ( "." ) , "" )
58+ } else if current. ends_with ( '/' ) || current. ends_with ( std:: path:: MAIN_SEPARATOR ) {
59+ ( current_path. to_path_buf ( ) , "" )
60+ } else {
61+ match current_path. parent ( ) {
62+ Some ( parent) if !parent. as_os_str ( ) . is_empty ( ) => {
63+ let prefix = current_path
64+ . file_name ( )
65+ . and_then ( |n| n. to_str ( ) )
66+ . unwrap_or ( "" ) ;
67+ ( parent. to_path_buf ( ) , prefix)
68+ }
69+ _ => ( std:: path:: PathBuf :: from ( "." ) , current) ,
70+ }
71+ } ;
72+
73+ let Ok ( entries) = std:: fs:: read_dir ( & search_dir) else {
74+ return ;
75+ } ;
76+
77+ let mut candidates = Vec :: new ( ) ;
78+
79+ for entry in entries. flatten ( ) {
80+ let Ok ( file_type) = entry. file_type ( ) else {
81+ continue ;
82+ } ;
83+
84+ if !file_type. is_dir ( ) {
85+ continue ;
86+ }
87+
88+ let file_name = entry. file_name ( ) ;
89+ let Some ( name) = file_name. to_str ( ) else {
90+ continue ;
91+ } ;
92+
93+ if name. starts_with ( '.' ) && !prefix. starts_with ( '.' ) {
94+ continue ;
95+ }
96+
97+ if !name. starts_with ( prefix) {
98+ continue ;
99+ }
100+
101+ let candidate_path =
102+ if current. ends_with ( '/' ) || current. ends_with ( std:: path:: MAIN_SEPARATOR ) {
103+ format ! ( "{}{}/" , current, name)
104+ } else if let Some ( parent) = current_path. parent ( ) {
105+ if parent. as_os_str ( ) . is_empty ( ) || parent == std:: path:: Path :: new ( "." ) {
106+ format ! ( "{}/" , name)
107+ } else {
108+ format ! ( "{}/{}/" , parent. display( ) , name)
109+ }
110+ } else {
111+ format ! ( "{}/" , name)
112+ } ;
113+
114+ candidates. push ( candidate_path) ;
115+ }
116+
117+ candidates. sort ( ) ;
118+ for candidate in candidates {
119+ println ! ( "{}" , candidate) ;
120+ }
121+ }
122+
123+ /// Suggest files and directories for completion
124+ fn suggest_files ( current : & str ) {
125+ let current_path = std:: path:: Path :: new ( current) ;
126+
127+ let ( search_dir, prefix) = if current. is_empty ( ) {
128+ ( std:: path:: PathBuf :: from ( "." ) , "" )
129+ } else if current. ends_with ( '/' ) || current. ends_with ( std:: path:: MAIN_SEPARATOR ) {
130+ ( current_path. to_path_buf ( ) , "" )
131+ } else {
132+ match current_path. parent ( ) {
133+ Some ( parent) if !parent. as_os_str ( ) . is_empty ( ) => {
134+ let prefix = current_path
135+ . file_name ( )
136+ . and_then ( |n| n. to_str ( ) )
137+ . unwrap_or ( "" ) ;
138+ ( parent. to_path_buf ( ) , prefix)
139+ }
140+ _ => ( std:: path:: PathBuf :: from ( "." ) , current) ,
141+ }
142+ } ;
143+
144+ let Ok ( entries) = std:: fs:: read_dir ( & search_dir) else {
145+ return ;
146+ } ;
147+
148+ let mut candidates = Vec :: new ( ) ;
149+
150+ for entry in entries. flatten ( ) {
151+ let Ok ( file_type) = entry. file_type ( ) else {
152+ continue ;
153+ } ;
154+
155+ let file_name = entry. file_name ( ) ;
156+ let Some ( name) = file_name. to_str ( ) else {
157+ continue ;
158+ } ;
159+
160+ if name. starts_with ( '.' ) && !prefix. starts_with ( '.' ) {
161+ continue ;
162+ }
163+
164+ if !name. starts_with ( prefix) {
165+ continue ;
166+ }
167+
168+ let candidate_path =
169+ if current. ends_with ( '/' ) || current. ends_with ( std:: path:: MAIN_SEPARATOR ) {
170+ if file_type. is_dir ( ) {
171+ format ! ( "{}{}/" , current, name)
172+ } else {
173+ format ! ( "{}{}" , current, name)
174+ }
175+ } else if let Some ( parent) = current_path. parent ( ) {
176+ if parent. as_os_str ( ) . is_empty ( ) || parent == std:: path:: Path :: new ( "." ) {
177+ if file_type. is_dir ( ) {
178+ format ! ( "{}/" , name)
179+ } else {
180+ name. to_string ( )
181+ }
182+ } else if file_type. is_dir ( ) {
183+ format ! ( "{}/{}/" , parent. display( ) , name)
184+ } else {
185+ format ! ( "{}/{}" , parent. display( ) , name)
186+ }
187+ } else if file_type. is_dir ( ) {
188+ format ! ( "{}/" , name)
189+ } else {
190+ name. to_string ( )
191+ } ;
192+
193+ candidates. push ( candidate_path) ;
194+ }
195+
196+ candidates. sort ( ) ;
197+ for candidate in candidates {
198+ println ! ( "{}" , candidate) ;
199+ }
200+ }
201+
52202/// Generate dynamic completions based on current line and cursor position
53203pub fn generate_completions (
54204 line : & str ,
@@ -83,6 +233,26 @@ pub fn generate_completions(
83233 suggest_ruby_versions ( rubies_dir, current_word) ;
84234 return ;
85235 }
236+ if prev == "-R" || prev == "--rubies-dir" {
237+ suggest_directories ( current_word) ;
238+ return ;
239+ }
240+ if prev == "-C" || prev == "--work-dir" {
241+ suggest_directories ( current_word) ;
242+ return ;
243+ }
244+ if prev == "-G" || prev == "--gem-home" {
245+ suggest_directories ( current_word) ;
246+ return ;
247+ }
248+ if prev == "-c" || prev == "--config" {
249+ suggest_files ( current_word) ;
250+ return ;
251+ }
252+ if prev == "-P" || prev == "--project" {
253+ suggest_files ( current_word) ;
254+ return ;
255+ }
86256 if prev == "shell-integration" {
87257 if "bash" . starts_with ( current_word) {
88258 println ! ( "bash" ) ;
0 commit comments