11use crate :: completions:: {
22 Completer , CompletionOptions , SemanticSuggestion , SuggestionKind ,
3- completion_common:: { FileSuggestion , surround_remove} ,
4- completion_options:: NuMatcher ,
5- file_path_completion,
3+ completion_common:: FileSuggestion , completion_options:: NuMatcher ,
64} ;
75use nu_path:: expand_tilde;
86use nu_protocol:: {
97 Span ,
108 engine:: { Stack , StateWorkingSet , VirtualPath } ,
119} ;
1210use reedline:: Suggestion ;
13- use std:: {
14- collections:: HashSet ,
15- path:: { MAIN_SEPARATOR_STR , PathBuf , is_separator} ,
16- } ;
11+ use std:: collections:: HashSet ;
12+
13+ use super :: completion_common:: { complete_item, surround_remove} ;
1714
1815pub struct DotNuCompletion {
1916 /// e.g. use std/a<tab>
@@ -30,30 +27,6 @@ impl Completer for DotNuCompletion {
3027 offset : usize ,
3128 options : & CompletionOptions ,
3229 ) -> Vec < SemanticSuggestion > {
33- let prefix_str = prefix. as_ref ( ) ;
34- let start_with_backquote = prefix_str. starts_with ( '`' ) ;
35- let end_with_backquote = prefix_str. ends_with ( '`' ) ;
36- let prefix_str = prefix_str. replace ( '`' , "" ) ;
37- // e.g. `./`, `..\`, `/`
38- let not_lib_dirs = prefix_str
39- . chars ( )
40- . find ( |c| * c != '.' )
41- . is_some_and ( is_separator) ;
42- let mut search_dirs: Vec < PathBuf > = vec ! [ ] ;
43-
44- let ( base, partial) = if let Some ( ( parent, remain) ) = prefix_str. rsplit_once ( is_separator) {
45- // If prefix_str is only a word we want to search in the current dir.
46- // "/xx" should be split to "/" and "xx".
47- if parent. is_empty ( ) {
48- ( MAIN_SEPARATOR_STR , remain)
49- } else {
50- ( parent, remain)
51- }
52- } else {
53- ( "." , prefix_str. as_str ( ) )
54- } ;
55- let base_dir = base. replace ( is_separator, MAIN_SEPARATOR_STR ) ;
56-
5730 // Fetch the lib dirs
5831 // NOTE: 2 ways to setup `NU_LIB_DIRS`
5932 // 1. `const NU_LIB_DIRS = [paths]`, equal to `nu -I paths`
@@ -62,7 +35,7 @@ impl Completer for DotNuCompletion {
6235 . find_variable ( b"$NU_LIB_DIRS" )
6336 . and_then ( |vid| working_set. get_variable ( vid) . const_val . as_ref ( ) ) ;
6437 let env_lib_dirs = working_set. get_env_var ( "NU_LIB_DIRS" ) ;
65- let lib_dirs : HashSet < PathBuf > = [ const_lib_dirs, env_lib_dirs]
38+ let mut search_dirs = [ const_lib_dirs, env_lib_dirs]
6639 . into_iter ( )
6740 . flatten ( )
6841 . flat_map ( |lib_dirs| {
@@ -72,45 +45,18 @@ impl Completer for DotNuCompletion {
7245 . flat_map ( |it| it. iter ( ) . filter_map ( |x| x. to_path ( ) . ok ( ) ) )
7346 . map ( expand_tilde)
7447 } )
75- . collect ( ) ;
48+ . collect :: < HashSet < _ > > ( ) ;
7649
77- // Check if the base_dir is a folder
78- let cwd = working_set. permanent_state . cwd ( None ) ;
79- if base_dir != "." {
80- let expanded_base_dir = expand_tilde ( & base_dir) ;
81- let is_base_dir_relative = expanded_base_dir. is_relative ( ) ;
82- // Search in base_dir as well as lib_dirs.
83- // After expanded, base_dir can be a relative path or absolute path.
84- // If relative, we join "current working dir" with it to get subdirectory and add to search_dirs.
85- // If absolute, we add it to search_dirs.
86- if let Ok ( mut cwd) = cwd {
87- if is_base_dir_relative {
88- cwd. push ( & base_dir) ;
89- search_dirs. push ( cwd. into_std_path_buf ( ) ) ;
90- } else {
91- search_dirs. push ( expanded_base_dir) ;
92- }
93- }
94- if !not_lib_dirs {
95- search_dirs. extend ( lib_dirs. into_iter ( ) . map ( |mut dir| {
96- dir. push ( & base_dir) ;
97- dir
98- } ) ) ;
99- }
100- } else {
101- if let Ok ( cwd) = cwd {
102- search_dirs. push ( cwd. into_std_path_buf ( ) ) ;
103- }
104- if !not_lib_dirs {
105- search_dirs. extend ( lib_dirs) ;
106- }
50+ if let Ok ( cwd) = working_set. permanent_state . cwd ( None ) {
51+ search_dirs. insert ( cwd. into_std_path_buf ( ) ) ;
10752 }
10853
10954 // Fetch the files filtering the ones that ends with .nu
11055 // and transform them into suggestions
111- let mut completions = file_path_completion (
56+ let mut completions = complete_item (
57+ false ,
11258 span,
113- partial ,
59+ prefix . as_ref ( ) ,
11460 & search_dirs
11561 . iter ( )
11662 . filter_map ( |d| d. to_str ( ) )
@@ -121,13 +67,32 @@ impl Completer for DotNuCompletion {
12167 ) ;
12268
12369 if self . std_virtual_path {
124- let mut matcher = NuMatcher :: new ( partial, options) ;
125- let base_dir = surround_remove ( & base_dir) ;
126- if base_dir == "." {
127- let surround_prefix = partial
128- . chars ( )
129- . take_while ( |c| "`'\" " . contains ( * c) )
130- . collect :: < String > ( ) ;
70+ let surround_prefix = prefix
71+ . as_ref ( )
72+ . chars ( )
73+ . take_while ( |c| "`'\" " . contains ( * c) )
74+ . collect :: < String > ( ) ;
75+ let mut matcher = NuMatcher :: new ( & prefix, options) ;
76+ // Where we have '/' in the prefix, e.g. use std/l
77+ if let Some ( ( base_dir, _) ) = prefix. as_ref ( ) . rsplit_once ( "/" ) {
78+ let base_dir = surround_remove ( base_dir) ;
79+ if let Some ( VirtualPath :: Dir ( sub_paths) ) = working_set. find_virtual_path ( & base_dir)
80+ {
81+ for sub_vp_id in sub_paths {
82+ let ( path, sub_vp) = working_set. get_virtual_path ( * sub_vp_id) ;
83+ let path = format ! ( "{surround_prefix}{path}" ) ;
84+ matcher. add (
85+ path. clone ( ) ,
86+ FileSuggestion {
87+ path,
88+ span,
89+ style : None ,
90+ is_dir : matches ! ( sub_vp, VirtualPath :: Dir ( _) ) ,
91+ } ,
92+ ) ;
93+ }
94+ }
95+ } else {
13196 for path in [ "std" , "std-rfc" ] {
13297 let path = format ! ( "{surround_prefix}{path}" ) ;
13398 matcher. add (
@@ -140,25 +105,6 @@ impl Completer for DotNuCompletion {
140105 } ,
141106 ) ;
142107 }
143- } else if let Some ( VirtualPath :: Dir ( sub_paths) ) =
144- working_set. find_virtual_path ( & base_dir)
145- {
146- for sub_vp_id in sub_paths {
147- let ( path, sub_vp) = working_set. get_virtual_path ( * sub_vp_id) ;
148- let path = path
149- . strip_prefix ( & format ! ( "{base_dir}/" ) )
150- . unwrap_or ( path)
151- . to_string ( ) ;
152- matcher. add (
153- path. clone ( ) ,
154- FileSuggestion {
155- path,
156- span,
157- style : None ,
158- is_dir : matches ! ( sub_vp, VirtualPath :: Dir ( _) ) ,
159- } ,
160- ) ;
161- }
162108 }
163109 completions. extend ( matcher. results ( ) ) ;
164110 }
@@ -171,38 +117,18 @@ impl Completer for DotNuCompletion {
171117 let path = it. path . trim_end_matches ( '`' ) ;
172118 path. ends_with ( ".nu" ) || it. is_dir
173119 } )
174- . map ( |x| {
175- let append_whitespace = !x. is_dir && ( !start_with_backquote || end_with_backquote) ;
176- // Re-calculate the span to replace
177- let mut span_offset = 0 ;
178- let mut value = x. path . to_string ( ) ;
179- // Complete only the last path component
180- if base_dir == MAIN_SEPARATOR_STR {
181- span_offset = base_dir. len ( )
182- } else if base_dir != "." {
183- span_offset = base_dir. len ( ) + 1
184- }
185- // Retain only one '`'
186- if start_with_backquote {
187- value = value. trim_start_matches ( '`' ) . to_string ( ) ;
188- span_offset += 1 ;
189- }
190- // Add the backquote back
191- if end_with_backquote && !value. ends_with ( '`' ) {
192- value. push ( '`' ) ;
193- }
194- let end = x. span . end - offset;
195- let start = std:: cmp:: min ( end, x. span . start - offset + span_offset) ;
196- SemanticSuggestion {
197- suggestion : Suggestion {
198- value,
199- style : x. style ,
200- span : reedline:: Span { start, end } ,
201- append_whitespace,
202- ..Suggestion :: default ( )
120+ . map ( |x| SemanticSuggestion {
121+ suggestion : Suggestion {
122+ value : x. path . to_string ( ) ,
123+ style : x. style ,
124+ span : reedline:: Span {
125+ start : x. span . start - offset,
126+ end : x. span . end - offset,
203127 } ,
204- kind : Some ( SuggestionKind :: Module ) ,
205- }
128+ append_whitespace : !x. is_dir ,
129+ ..Suggestion :: default ( )
130+ } ,
131+ kind : Some ( SuggestionKind :: Module ) ,
206132 } )
207133 . collect :: < Vec < _ > > ( )
208134 }
0 commit comments