improve completions of use and overlay use (#11330)

# Description
this PR is two-fold
- make `use` and `overlay use` use the same completion algorithm in
48f29b633
- list directory modules in completions of both with 402acde5c

# User-Facing Changes
i currently have the following in my `NU_LIB_DIRS`
<details>
<summary>click to see the script</summary>

```nushell
for dir in $env.NU_LIB_DIRS {
    print $dir
    print (ls $dir --short-names | select name type)
}
```
</details>

```
/home/amtoine/.local/share/nupm/modules
#┬────────name────────┬type
0│nu-git-manager      │dir
1│nu-git-manager-sugar│dir
2│nu-hooks            │dir
3│nu-scripts          │dir
4│nu-themes           │dir
5│nupm                │dir
─┴────────────────────┴────

/home/amtoine/.config/nushell/overlays
#┬──name──┬type
0│ocaml.nu│file
─┴────────┴────
```

> **Note**
> all the samples below are run from the Nushell repo, i.e. a directory
with a `toolkit.nu` module

## before the changes
- `use` would give me `["ocaml.nu", "toolkit.nu"]` 
- `overlay use` would give me `[]` 

## after the changes
both commands give me
```nushell
[
    "nupm/",
    "ocaml.nu",
    "toolkit.nu",
    "nu-scripts/",
    "nu-git-manager/",
    "nu-git-manager-sugar/",
]
```

# Tests + Formatting
- adds a new `directory_completion/mod.nu` to the completion fixtures
- make sure `source-env`, `use` and `overlay-use` are all tested in the
_dotnu_ test
- fix all the other tests that use completions in the fixtures directory
for completions

# After Submitting
This commit is contained in:
Antoine Stevan 2023-12-19 10:14:34 +01:00 committed by GitHub
parent 729373aba0
commit c6043eb500
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 56 additions and 10 deletions

View File

@ -250,7 +250,9 @@ impl NuCompleter {
working_set.get_span_contents(previous_expr.0).to_vec(); working_set.get_span_contents(previous_expr.0).to_vec();
// Completion for .nu files // Completion for .nu files
if prev_expr_str == b"use" || prev_expr_str == b"source-env" if prev_expr_str == b"use"
|| prev_expr_str == b"overlay use"
|| prev_expr_str == b"source-env"
{ {
let mut completer = let mut completer =
DotNuCompletion::new(self.engine_state.clone()); DotNuCompletion::new(self.engine_state.clone());

View File

@ -5,7 +5,7 @@ use nu_protocol::{
}; };
use reedline::Suggestion; use reedline::Suggestion;
use std::{ use std::{
path::{is_separator, MAIN_SEPARATOR as SEP, MAIN_SEPARATOR_STR}, path::{is_separator, Path, MAIN_SEPARATOR as SEP, MAIN_SEPARATOR_STR},
sync::Arc, sync::Arc,
}; };
@ -91,17 +91,22 @@ impl Completer for DotNuCompletion {
// and transform them into suggestions // and transform them into suggestions
let output: Vec<Suggestion> = search_dirs let output: Vec<Suggestion> = search_dirs
.into_iter() .into_iter()
.flat_map(|it| { .flat_map(|search_dir| {
file_path_completion(span, &partial, &it, options) let completions = file_path_completion(span, &partial, &search_dir, options);
completions
.into_iter() .into_iter()
.filter(|it| { .filter(move |it| {
// Different base dir, so we list the .nu files or folders // Different base dir, so we list the .nu files or folders
if !is_current_folder { if !is_current_folder {
it.1.ends_with(".nu") || it.1.ends_with(SEP) it.1.ends_with(".nu") || it.1.ends_with(SEP)
} else { } else {
// Lib dirs, so we filter only the .nu files // Lib dirs, so we filter only the .nu files or directory modules
if it.1.ends_with(SEP) {
Path::new(&search_dir).join(&it.1).join("mod.nu").exists()
} else {
it.1.ends_with(".nu") it.1.ends_with(".nu")
} }
}
}) })
.map(move |x| Suggestion { .map(move |x| Suggestion {
value: x.1, value: x.1,

View File

@ -91,7 +91,7 @@ fn variables_dollar_sign_with_varialblecompletion() {
let target_dir = "$ "; let target_dir = "$ ";
let suggestions = completer.complete(target_dir, target_dir.len()); let suggestions = completer.complete(target_dir, target_dir.len());
assert_eq!(7, suggestions.len()); assert_eq!(8, suggestions.len());
} }
#[rstest] #[rstest]
@ -144,15 +144,34 @@ fn dotnu_completions() {
let completion_str = "source-env ".to_string(); let completion_str = "source-env ".to_string();
let suggestions = completer.complete(&completion_str, completion_str.len()); let suggestions = completer.complete(&completion_str, completion_str.len());
assert_eq!(1, suggestions.len()); assert_eq!(2, suggestions.len());
assert_eq!("custom_completion.nu", suggestions.first().unwrap().value); assert_eq!("custom_completion.nu", suggestions.first().unwrap().value);
#[cfg(windows)]
assert_eq!("directory_completion\\", suggestions.get(1).unwrap().value);
#[cfg(not(windows))]
assert_eq!("directory_completion/", suggestions.get(1).unwrap().value);
// Test use completion // Test use completion
let completion_str = "use ".to_string(); let completion_str = "use ".to_string();
let suggestions = completer.complete(&completion_str, completion_str.len()); let suggestions = completer.complete(&completion_str, completion_str.len());
assert_eq!(1, suggestions.len()); assert_eq!(2, suggestions.len());
assert_eq!("custom_completion.nu", suggestions.first().unwrap().value); assert_eq!("custom_completion.nu", suggestions.first().unwrap().value);
#[cfg(windows)]
assert_eq!("directory_completion\\", suggestions.get(1).unwrap().value);
#[cfg(not(windows))]
assert_eq!("directory_completion/", suggestions.get(1).unwrap().value);
// Test overlay use completion
let completion_str = "overlay use ".to_string();
let suggestions = completer.complete(&completion_str, completion_str.len());
assert_eq!(2, suggestions.len());
assert_eq!("custom_completion.nu", suggestions.first().unwrap().value);
#[cfg(windows)]
assert_eq!("directory_completion\\", suggestions.get(1).unwrap().value);
#[cfg(not(windows))]
assert_eq!("directory_completion/", suggestions.get(1).unwrap().value);
} }
#[test] #[test]
@ -208,6 +227,7 @@ fn file_completions() {
let expected_paths: Vec<String> = vec![ let expected_paths: Vec<String> = vec![
folder(dir.join("another")), folder(dir.join("another")),
file(dir.join("custom_completion.nu")), file(dir.join("custom_completion.nu")),
folder(dir.join("directory_completion")),
file(dir.join("nushell")), file(dir.join("nushell")),
folder(dir.join("test_a")), folder(dir.join("test_a")),
folder(dir.join("test_b")), folder(dir.join("test_b")),
@ -323,6 +343,7 @@ fn command_ls_with_filecompletion() {
let expected_paths: Vec<String> = vec![ let expected_paths: Vec<String> = vec![
"another\\".to_string(), "another\\".to_string(),
"custom_completion.nu".to_string(), "custom_completion.nu".to_string(),
"directory_completion\\".to_string(),
"nushell".to_string(), "nushell".to_string(),
"test_a\\".to_string(), "test_a\\".to_string(),
"test_b\\".to_string(), "test_b\\".to_string(),
@ -333,6 +354,7 @@ fn command_ls_with_filecompletion() {
let expected_paths: Vec<String> = vec![ let expected_paths: Vec<String> = vec![
"another/".to_string(), "another/".to_string(),
"custom_completion.nu".to_string(), "custom_completion.nu".to_string(),
"directory_completion/".to_string(),
"nushell".to_string(), "nushell".to_string(),
"test_a/".to_string(), "test_a/".to_string(),
"test_b/".to_string(), "test_b/".to_string(),
@ -355,6 +377,7 @@ fn command_open_with_filecompletion() {
let expected_paths: Vec<String> = vec![ let expected_paths: Vec<String> = vec![
"another\\".to_string(), "another\\".to_string(),
"custom_completion.nu".to_string(), "custom_completion.nu".to_string(),
"directory_completion\\".to_string(),
"nushell".to_string(), "nushell".to_string(),
"test_a\\".to_string(), "test_a\\".to_string(),
"test_b\\".to_string(), "test_b\\".to_string(),
@ -365,6 +388,7 @@ fn command_open_with_filecompletion() {
let expected_paths: Vec<String> = vec![ let expected_paths: Vec<String> = vec![
"another/".to_string(), "another/".to_string(),
"custom_completion.nu".to_string(), "custom_completion.nu".to_string(),
"directory_completion/".to_string(),
"nushell".to_string(), "nushell".to_string(),
"test_a/".to_string(), "test_a/".to_string(),
"test_b/".to_string(), "test_b/".to_string(),
@ -388,6 +412,7 @@ fn command_rm_with_globcompletion() {
let expected_paths: Vec<String> = vec![ let expected_paths: Vec<String> = vec![
"another\\".to_string(), "another\\".to_string(),
"custom_completion.nu".to_string(), "custom_completion.nu".to_string(),
"directory_completion\\".to_string(),
"nushell".to_string(), "nushell".to_string(),
"test_a\\".to_string(), "test_a\\".to_string(),
"test_b\\".to_string(), "test_b\\".to_string(),
@ -398,6 +423,7 @@ fn command_rm_with_globcompletion() {
let expected_paths: Vec<String> = vec![ let expected_paths: Vec<String> = vec![
"another/".to_string(), "another/".to_string(),
"custom_completion.nu".to_string(), "custom_completion.nu".to_string(),
"directory_completion/".to_string(),
"nushell".to_string(), "nushell".to_string(),
"test_a/".to_string(), "test_a/".to_string(),
"test_b/".to_string(), "test_b/".to_string(),
@ -421,6 +447,7 @@ fn command_cp_with_globcompletion() {
let expected_paths: Vec<String> = vec![ let expected_paths: Vec<String> = vec![
"another\\".to_string(), "another\\".to_string(),
"custom_completion.nu".to_string(), "custom_completion.nu".to_string(),
"directory_completion\\".to_string(),
"nushell".to_string(), "nushell".to_string(),
"test_a\\".to_string(), "test_a\\".to_string(),
"test_b\\".to_string(), "test_b\\".to_string(),
@ -431,6 +458,7 @@ fn command_cp_with_globcompletion() {
let expected_paths: Vec<String> = vec![ let expected_paths: Vec<String> = vec![
"another/".to_string(), "another/".to_string(),
"custom_completion.nu".to_string(), "custom_completion.nu".to_string(),
"directory_completion/".to_string(),
"nushell".to_string(), "nushell".to_string(),
"test_a/".to_string(), "test_a/".to_string(),
"test_b/".to_string(), "test_b/".to_string(),
@ -454,6 +482,7 @@ fn command_save_with_filecompletion() {
let expected_paths: Vec<String> = vec![ let expected_paths: Vec<String> = vec![
"another\\".to_string(), "another\\".to_string(),
"custom_completion.nu".to_string(), "custom_completion.nu".to_string(),
"directory_completion\\".to_string(),
"nushell".to_string(), "nushell".to_string(),
"test_a\\".to_string(), "test_a\\".to_string(),
"test_b\\".to_string(), "test_b\\".to_string(),
@ -464,6 +493,7 @@ fn command_save_with_filecompletion() {
let expected_paths: Vec<String> = vec![ let expected_paths: Vec<String> = vec![
"another/".to_string(), "another/".to_string(),
"custom_completion.nu".to_string(), "custom_completion.nu".to_string(),
"directory_completion/".to_string(),
"nushell".to_string(), "nushell".to_string(),
"test_a/".to_string(), "test_a/".to_string(),
"test_b/".to_string(), "test_b/".to_string(),
@ -487,6 +517,7 @@ fn command_touch_with_filecompletion() {
let expected_paths: Vec<String> = vec![ let expected_paths: Vec<String> = vec![
"another\\".to_string(), "another\\".to_string(),
"custom_completion.nu".to_string(), "custom_completion.nu".to_string(),
"directory_completion\\".to_string(),
"nushell".to_string(), "nushell".to_string(),
"test_a\\".to_string(), "test_a\\".to_string(),
"test_b\\".to_string(), "test_b\\".to_string(),
@ -497,6 +528,7 @@ fn command_touch_with_filecompletion() {
let expected_paths: Vec<String> = vec![ let expected_paths: Vec<String> = vec![
"another/".to_string(), "another/".to_string(),
"custom_completion.nu".to_string(), "custom_completion.nu".to_string(),
"directory_completion/".to_string(),
"nushell".to_string(), "nushell".to_string(),
"test_a/".to_string(), "test_a/".to_string(),
"test_b/".to_string(), "test_b/".to_string(),
@ -520,6 +552,7 @@ fn command_watch_with_filecompletion() {
let expected_paths: Vec<String> = vec![ let expected_paths: Vec<String> = vec![
"another\\".to_string(), "another\\".to_string(),
"custom_completion.nu".to_string(), "custom_completion.nu".to_string(),
"directory_completion\\".to_string(),
"nushell".to_string(), "nushell".to_string(),
"test_a\\".to_string(), "test_a\\".to_string(),
"test_b\\".to_string(), "test_b\\".to_string(),
@ -530,6 +563,7 @@ fn command_watch_with_filecompletion() {
let expected_paths: Vec<String> = vec![ let expected_paths: Vec<String> = vec![
"another/".to_string(), "another/".to_string(),
"custom_completion.nu".to_string(), "custom_completion.nu".to_string(),
"directory_completion/".to_string(),
"nushell".to_string(), "nushell".to_string(),
"test_a/".to_string(), "test_a/".to_string(),
"test_b/".to_string(), "test_b/".to_string(),
@ -625,6 +659,7 @@ fn folder_with_directorycompletions() {
// Create the expected values // Create the expected values
let expected_paths: Vec<String> = vec![ let expected_paths: Vec<String> = vec![
folder(dir.join("another")), folder(dir.join("another")),
folder(dir.join("directory_completion")),
folder(dir.join("test_a")), folder(dir.join("test_a")),
folder(dir.join("test_b")), folder(dir.join("test_b")),
folder(dir.join(".hidden_folder")), folder(dir.join(".hidden_folder")),
@ -839,6 +874,7 @@ fn unknown_command_completion() {
let expected_paths: Vec<String> = vec![ let expected_paths: Vec<String> = vec![
"another\\".to_string(), "another\\".to_string(),
"custom_completion.nu".to_string(), "custom_completion.nu".to_string(),
"directory_completion\\".to_string(),
"nushell".to_string(), "nushell".to_string(),
"test_a\\".to_string(), "test_a\\".to_string(),
"test_b\\".to_string(), "test_b\\".to_string(),
@ -849,6 +885,7 @@ fn unknown_command_completion() {
let expected_paths: Vec<String> = vec![ let expected_paths: Vec<String> = vec![
"another/".to_string(), "another/".to_string(),
"custom_completion.nu".to_string(), "custom_completion.nu".to_string(),
"directory_completion/".to_string(),
"nushell".to_string(), "nushell".to_string(),
"test_a/".to_string(), "test_a/".to_string(),
"test_b/".to_string(), "test_b/".to_string(),
@ -899,6 +936,7 @@ fn filecompletions_triggers_after_cursor() {
let expected_paths: Vec<String> = vec![ let expected_paths: Vec<String> = vec![
"another\\".to_string(), "another\\".to_string(),
"custom_completion.nu".to_string(), "custom_completion.nu".to_string(),
"directory_completion\\".to_string(),
"nushell".to_string(), "nushell".to_string(),
"test_a\\".to_string(), "test_a\\".to_string(),
"test_b\\".to_string(), "test_b\\".to_string(),
@ -909,6 +947,7 @@ fn filecompletions_triggers_after_cursor() {
let expected_paths: Vec<String> = vec![ let expected_paths: Vec<String> = vec![
"another/".to_string(), "another/".to_string(),
"custom_completion.nu".to_string(), "custom_completion.nu".to_string(),
"directory_completion/".to_string(),
"nushell".to_string(), "nushell".to_string(),
"test_a/".to_string(), "test_a/".to_string(),
"test_b/".to_string(), "test_b/".to_string(),

View File