mirror of
https://github.com/nushell/nushell.git
synced 2025-06-18 16:07:02 +02:00
fix(completion): dotnu_completion for nested folders/scripts (#14978)
# Description Fixes #14213 and some of #14956 <img width="314" alt="image" src="https://github.com/user-attachments/assets/e79d0882-da90-4592-8af5-7ab0c1d2f96a" /> # User-Facing Changes # Tests + Formatting # After Submitting
This commit is contained in:
parent
4540f3829e
commit
63fa6a6df7
@ -31,6 +31,7 @@ pub struct SemanticSuggestion {
|
||||
pub enum SuggestionKind {
|
||||
Command(nu_protocol::engine::CommandType),
|
||||
Type(nu_protocol::Type),
|
||||
Module,
|
||||
}
|
||||
|
||||
impl From<Suggestion> for SemanticSuggestion {
|
||||
|
@ -157,7 +157,6 @@ pub struct FileSuggestion {
|
||||
pub span: nu_protocol::Span,
|
||||
pub path: String,
|
||||
pub style: Option<Style>,
|
||||
pub cwd: PathBuf,
|
||||
}
|
||||
|
||||
/// # Parameters
|
||||
@ -261,7 +260,6 @@ pub fn complete_item(
|
||||
if should_collapse_dots {
|
||||
p = collapse_ndots(p);
|
||||
}
|
||||
let cwd = p.cwd.clone();
|
||||
let path = original_cwd.apply(p, path_separator);
|
||||
let style = ls_colors.as_ref().map(|lsc| {
|
||||
lsc.style_for_path_with_metadata(
|
||||
@ -277,7 +275,6 @@ pub fn complete_item(
|
||||
span,
|
||||
path: escape_path(path, want_directory),
|
||||
style,
|
||||
cwd,
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
|
@ -4,9 +4,9 @@ use nu_protocol::{
|
||||
Span,
|
||||
};
|
||||
use reedline::Suggestion;
|
||||
use std::path::{is_separator, Path, MAIN_SEPARATOR as SEP, MAIN_SEPARATOR_STR};
|
||||
use std::path::{is_separator, PathBuf, MAIN_SEPARATOR as SEP, MAIN_SEPARATOR_STR};
|
||||
|
||||
use super::SemanticSuggestion;
|
||||
use super::{SemanticSuggestion, SuggestionKind};
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct DotNuCompletion {}
|
||||
@ -29,19 +29,16 @@ impl Completer for DotNuCompletion {
|
||||
options: &CompletionOptions,
|
||||
) -> Vec<SemanticSuggestion> {
|
||||
let prefix_str = String::from_utf8_lossy(prefix).replace('`', "");
|
||||
let mut search_dirs: Vec<String> = vec![];
|
||||
let mut search_dirs: Vec<PathBuf> = vec![];
|
||||
|
||||
// If prefix_str is only a word we want to search in the current dir
|
||||
let (base, partial) = prefix_str
|
||||
.rsplit_once(is_separator)
|
||||
.unwrap_or((".", &prefix_str));
|
||||
let base_dir = base.replace(is_separator, MAIN_SEPARATOR_STR);
|
||||
let mut partial = partial.to_string();
|
||||
// On windows, this standardizes paths to use \
|
||||
let mut is_current_folder = false;
|
||||
|
||||
// Fetch the lib dirs
|
||||
let lib_dirs: Vec<String> = working_set
|
||||
let lib_dirs: Vec<PathBuf> = working_set
|
||||
.find_variable(b"$NU_LIB_DIRS")
|
||||
.and_then(|vid| working_set.get_variable(vid).const_val.as_ref())
|
||||
.or(working_set.get_env_var("NU_LIB_DIRS"))
|
||||
@ -55,36 +52,27 @@ impl Completer for DotNuCompletion {
|
||||
.expect("internal error: failed to convert lib path")
|
||||
})
|
||||
})
|
||||
.map(|it| {
|
||||
it.into_os_string()
|
||||
.into_string()
|
||||
.expect("internal error: failed to convert OS path")
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
// Check if the base_dir is a folder
|
||||
// rsplit_once removes the separator
|
||||
let cwd = working_set.permanent_state.cwd(None);
|
||||
if base_dir != "." {
|
||||
// Add the base dir into the directories to be searched
|
||||
search_dirs.push(base_dir.clone());
|
||||
|
||||
// Reset the partial adding the basic dir back
|
||||
// in order to make the span replace work properly
|
||||
let mut base_dir_partial = base_dir;
|
||||
base_dir_partial.push_str(&partial);
|
||||
|
||||
partial = base_dir_partial;
|
||||
// Search in base_dir as well as lib_dirs
|
||||
if let Ok(mut cwd) = cwd {
|
||||
cwd.push(&base_dir);
|
||||
search_dirs.push(cwd.into_std_path_buf());
|
||||
}
|
||||
search_dirs.extend(lib_dirs.into_iter().map(|mut dir| {
|
||||
dir.push(&base_dir);
|
||||
dir
|
||||
}));
|
||||
} else {
|
||||
// Fetch the current folder
|
||||
#[allow(deprecated)]
|
||||
let current_folder = working_set.permanent_state.current_work_dir();
|
||||
is_current_folder = true;
|
||||
|
||||
// Add the current folder and the lib dirs into the
|
||||
// directories to be searched
|
||||
search_dirs.push(current_folder);
|
||||
if let Ok(cwd) = cwd {
|
||||
search_dirs.push(cwd.into_std_path_buf());
|
||||
}
|
||||
search_dirs.extend(lib_dirs);
|
||||
}
|
||||
|
||||
@ -93,40 +81,39 @@ impl Completer for DotNuCompletion {
|
||||
|
||||
let completions = file_path_completion(
|
||||
span,
|
||||
&partial,
|
||||
&search_dirs.iter().map(|d| d.as_str()).collect::<Vec<_>>(),
|
||||
partial,
|
||||
&search_dirs
|
||||
.iter()
|
||||
.map(|d| d.to_str().unwrap_or_default())
|
||||
.collect::<Vec<_>>(),
|
||||
options,
|
||||
working_set.permanent_state,
|
||||
stack,
|
||||
);
|
||||
completions
|
||||
.into_iter()
|
||||
.filter(move |it| {
|
||||
// Different base dir, so we list the .nu files or folders
|
||||
if !is_current_folder {
|
||||
it.path.ends_with(".nu") || it.path.ends_with(SEP)
|
||||
// Different base dir, so we list the .nu files or folders
|
||||
.filter(|it| it.path.ends_with(".nu") || it.path.ends_with(SEP))
|
||||
.map(|x| {
|
||||
let append_whitespace = x.path.ends_with(".nu");
|
||||
// Re-calculate the span to replace
|
||||
let span_offset = if base_dir == "." {
|
||||
0
|
||||
} else {
|
||||
// Lib dirs, so we filter only the .nu files or directory modules
|
||||
if it.path.ends_with(SEP) {
|
||||
Path::new(&it.cwd).join(&it.path).join("mod.nu").exists()
|
||||
} else {
|
||||
it.path.ends_with(".nu")
|
||||
}
|
||||
}
|
||||
})
|
||||
.map(move |x| SemanticSuggestion {
|
||||
suggestion: Suggestion {
|
||||
value: x.path,
|
||||
style: x.style,
|
||||
span: reedline::Span {
|
||||
start: x.span.start - offset,
|
||||
end: x.span.end - offset,
|
||||
base_dir.len() + 1
|
||||
} + prefix.iter().take_while(|c| **c == b'`').count();
|
||||
let end = x.span.end - offset;
|
||||
let start = std::cmp::min(end, x.span.start - offset + span_offset);
|
||||
SemanticSuggestion {
|
||||
suggestion: Suggestion {
|
||||
value: x.path,
|
||||
style: x.style,
|
||||
span: reedline::Span { start, end },
|
||||
append_whitespace,
|
||||
..Suggestion::default()
|
||||
},
|
||||
append_whitespace: true,
|
||||
..Suggestion::default()
|
||||
},
|
||||
// TODO????
|
||||
kind: None,
|
||||
kind: Some(SuggestionKind::Module),
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
|
@ -273,6 +273,15 @@ fn dotnu_completions() {
|
||||
// Instantiate a new completer
|
||||
let mut completer = NuCompleter::new(Arc::new(engine), Arc::new(stack));
|
||||
|
||||
// Test nested nu script
|
||||
#[cfg(windows)]
|
||||
let completion_str = "use .\\dir_module\\sub_module\\".to_string();
|
||||
#[cfg(not(windows))]
|
||||
let completion_str = "use ./dir_module/sub_module/".to_string();
|
||||
let suggestions = completer.complete(&completion_str, completion_str.len());
|
||||
|
||||
match_suggestions(&vec!["sub.nu".into()], &suggestions);
|
||||
|
||||
let expected = vec![
|
||||
"asdf.nu".into(),
|
||||
"bar.nu".into(),
|
||||
@ -283,6 +292,18 @@ fn dotnu_completions() {
|
||||
#[cfg(not(windows))]
|
||||
"dir_module/".into(),
|
||||
"foo.nu".into(),
|
||||
#[cfg(windows)]
|
||||
"lib-dir1\\".into(),
|
||||
#[cfg(not(windows))]
|
||||
"lib-dir1/".into(),
|
||||
#[cfg(windows)]
|
||||
"lib-dir2\\".into(),
|
||||
#[cfg(not(windows))]
|
||||
"lib-dir2/".into(),
|
||||
#[cfg(windows)]
|
||||
"lib-dir3\\".into(),
|
||||
#[cfg(not(windows))]
|
||||
"lib-dir3/".into(),
|
||||
"spam.nu".into(),
|
||||
"xyzzy.nu".into(),
|
||||
];
|
||||
|
@ -668,6 +668,7 @@ impl LanguageServer {
|
||||
.map(|kind| match kind {
|
||||
SuggestionKind::Type(t) => t.to_string(),
|
||||
SuggestionKind::Command(cmd) => cmd.to_string(),
|
||||
SuggestionKind::Module => "".to_string(),
|
||||
})
|
||||
.map(|s| CompletionItemLabelDetails {
|
||||
detail: None,
|
||||
@ -715,6 +716,7 @@ impl LanguageServer {
|
||||
nu_protocol::engine::CommandType::Builtin => Some(CompletionItemKind::FUNCTION),
|
||||
_ => None,
|
||||
},
|
||||
SuggestionKind::Module => Some(CompletionItemKind::MODULE),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
0
tests/fixtures/dotnu_completions/dir_module/sub_module/sub.nu
vendored
Normal file
0
tests/fixtures/dotnu_completions/dir_module/sub_module/sub.nu
vendored
Normal file
Loading…
x
Reference in New Issue
Block a user