mirror of
https://github.com/nushell/nushell.git
synced 2025-06-19 08:26:57 +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 {
|
pub enum SuggestionKind {
|
||||||
Command(nu_protocol::engine::CommandType),
|
Command(nu_protocol::engine::CommandType),
|
||||||
Type(nu_protocol::Type),
|
Type(nu_protocol::Type),
|
||||||
|
Module,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Suggestion> for SemanticSuggestion {
|
impl From<Suggestion> for SemanticSuggestion {
|
||||||
|
@ -157,7 +157,6 @@ pub struct FileSuggestion {
|
|||||||
pub span: nu_protocol::Span,
|
pub span: nu_protocol::Span,
|
||||||
pub path: String,
|
pub path: String,
|
||||||
pub style: Option<Style>,
|
pub style: Option<Style>,
|
||||||
pub cwd: PathBuf,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// # Parameters
|
/// # Parameters
|
||||||
@ -261,7 +260,6 @@ pub fn complete_item(
|
|||||||
if should_collapse_dots {
|
if should_collapse_dots {
|
||||||
p = collapse_ndots(p);
|
p = collapse_ndots(p);
|
||||||
}
|
}
|
||||||
let cwd = p.cwd.clone();
|
|
||||||
let path = original_cwd.apply(p, path_separator);
|
let path = original_cwd.apply(p, path_separator);
|
||||||
let style = ls_colors.as_ref().map(|lsc| {
|
let style = ls_colors.as_ref().map(|lsc| {
|
||||||
lsc.style_for_path_with_metadata(
|
lsc.style_for_path_with_metadata(
|
||||||
@ -277,7 +275,6 @@ pub fn complete_item(
|
|||||||
span,
|
span,
|
||||||
path: escape_path(path, want_directory),
|
path: escape_path(path, want_directory),
|
||||||
style,
|
style,
|
||||||
cwd,
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
|
@ -4,9 +4,9 @@ use nu_protocol::{
|
|||||||
Span,
|
Span,
|
||||||
};
|
};
|
||||||
use reedline::Suggestion;
|
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)]
|
#[derive(Clone, Default)]
|
||||||
pub struct DotNuCompletion {}
|
pub struct DotNuCompletion {}
|
||||||
@ -29,19 +29,16 @@ impl Completer for DotNuCompletion {
|
|||||||
options: &CompletionOptions,
|
options: &CompletionOptions,
|
||||||
) -> Vec<SemanticSuggestion> {
|
) -> Vec<SemanticSuggestion> {
|
||||||
let prefix_str = String::from_utf8_lossy(prefix).replace('`', "");
|
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
|
// If prefix_str is only a word we want to search in the current dir
|
||||||
let (base, partial) = prefix_str
|
let (base, partial) = prefix_str
|
||||||
.rsplit_once(is_separator)
|
.rsplit_once(is_separator)
|
||||||
.unwrap_or((".", &prefix_str));
|
.unwrap_or((".", &prefix_str));
|
||||||
let base_dir = base.replace(is_separator, MAIN_SEPARATOR_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
|
// Fetch the lib dirs
|
||||||
let lib_dirs: Vec<String> = working_set
|
let lib_dirs: Vec<PathBuf> = working_set
|
||||||
.find_variable(b"$NU_LIB_DIRS")
|
.find_variable(b"$NU_LIB_DIRS")
|
||||||
.and_then(|vid| working_set.get_variable(vid).const_val.as_ref())
|
.and_then(|vid| working_set.get_variable(vid).const_val.as_ref())
|
||||||
.or(working_set.get_env_var("NU_LIB_DIRS"))
|
.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")
|
.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()
|
.collect()
|
||||||
})
|
})
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
||||||
// Check if the base_dir is a folder
|
// Check if the base_dir is a folder
|
||||||
// rsplit_once removes the separator
|
// rsplit_once removes the separator
|
||||||
|
let cwd = working_set.permanent_state.cwd(None);
|
||||||
if base_dir != "." {
|
if base_dir != "." {
|
||||||
// Add the base dir into the directories to be searched
|
// Search in base_dir as well as lib_dirs
|
||||||
search_dirs.push(base_dir.clone());
|
if let Ok(mut cwd) = cwd {
|
||||||
|
cwd.push(&base_dir);
|
||||||
// Reset the partial adding the basic dir back
|
search_dirs.push(cwd.into_std_path_buf());
|
||||||
// in order to make the span replace work properly
|
}
|
||||||
let mut base_dir_partial = base_dir;
|
search_dirs.extend(lib_dirs.into_iter().map(|mut dir| {
|
||||||
base_dir_partial.push_str(&partial);
|
dir.push(&base_dir);
|
||||||
|
dir
|
||||||
partial = base_dir_partial;
|
}));
|
||||||
} else {
|
} else {
|
||||||
// Fetch the current folder
|
if let Ok(cwd) = cwd {
|
||||||
#[allow(deprecated)]
|
search_dirs.push(cwd.into_std_path_buf());
|
||||||
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);
|
|
||||||
search_dirs.extend(lib_dirs);
|
search_dirs.extend(lib_dirs);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,40 +81,39 @@ impl Completer for DotNuCompletion {
|
|||||||
|
|
||||||
let completions = file_path_completion(
|
let completions = file_path_completion(
|
||||||
span,
|
span,
|
||||||
&partial,
|
partial,
|
||||||
&search_dirs.iter().map(|d| d.as_str()).collect::<Vec<_>>(),
|
&search_dirs
|
||||||
|
.iter()
|
||||||
|
.map(|d| d.to_str().unwrap_or_default())
|
||||||
|
.collect::<Vec<_>>(),
|
||||||
options,
|
options,
|
||||||
working_set.permanent_state,
|
working_set.permanent_state,
|
||||||
stack,
|
stack,
|
||||||
);
|
);
|
||||||
completions
|
completions
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.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
|
.filter(|it| it.path.ends_with(".nu") || it.path.ends_with(SEP))
|
||||||
if !is_current_folder {
|
.map(|x| {
|
||||||
it.path.ends_with(".nu") || it.path.ends_with(SEP)
|
let append_whitespace = x.path.ends_with(".nu");
|
||||||
|
// Re-calculate the span to replace
|
||||||
|
let span_offset = if base_dir == "." {
|
||||||
|
0
|
||||||
} else {
|
} else {
|
||||||
// Lib dirs, so we filter only the .nu files or directory modules
|
base_dir.len() + 1
|
||||||
if it.path.ends_with(SEP) {
|
} + prefix.iter().take_while(|c| **c == b'`').count();
|
||||||
Path::new(&it.cwd).join(&it.path).join("mod.nu").exists()
|
let end = x.span.end - offset;
|
||||||
} else {
|
let start = std::cmp::min(end, x.span.start - offset + span_offset);
|
||||||
it.path.ends_with(".nu")
|
SemanticSuggestion {
|
||||||
}
|
suggestion: Suggestion {
|
||||||
}
|
value: x.path,
|
||||||
})
|
style: x.style,
|
||||||
.map(move |x| SemanticSuggestion {
|
span: reedline::Span { start, end },
|
||||||
suggestion: Suggestion {
|
append_whitespace,
|
||||||
value: x.path,
|
..Suggestion::default()
|
||||||
style: x.style,
|
|
||||||
span: reedline::Span {
|
|
||||||
start: x.span.start - offset,
|
|
||||||
end: x.span.end - offset,
|
|
||||||
},
|
},
|
||||||
append_whitespace: true,
|
kind: Some(SuggestionKind::Module),
|
||||||
..Suggestion::default()
|
}
|
||||||
},
|
|
||||||
// TODO????
|
|
||||||
kind: None,
|
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
}
|
}
|
||||||
|
@ -273,6 +273,15 @@ fn dotnu_completions() {
|
|||||||
// Instantiate a new completer
|
// Instantiate a new completer
|
||||||
let mut completer = NuCompleter::new(Arc::new(engine), Arc::new(stack));
|
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![
|
let expected = vec![
|
||||||
"asdf.nu".into(),
|
"asdf.nu".into(),
|
||||||
"bar.nu".into(),
|
"bar.nu".into(),
|
||||||
@ -283,6 +292,18 @@ fn dotnu_completions() {
|
|||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
"dir_module/".into(),
|
"dir_module/".into(),
|
||||||
"foo.nu".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(),
|
"spam.nu".into(),
|
||||||
"xyzzy.nu".into(),
|
"xyzzy.nu".into(),
|
||||||
];
|
];
|
||||||
|
@ -668,6 +668,7 @@ impl LanguageServer {
|
|||||||
.map(|kind| match kind {
|
.map(|kind| match kind {
|
||||||
SuggestionKind::Type(t) => t.to_string(),
|
SuggestionKind::Type(t) => t.to_string(),
|
||||||
SuggestionKind::Command(cmd) => cmd.to_string(),
|
SuggestionKind::Command(cmd) => cmd.to_string(),
|
||||||
|
SuggestionKind::Module => "".to_string(),
|
||||||
})
|
})
|
||||||
.map(|s| CompletionItemLabelDetails {
|
.map(|s| CompletionItemLabelDetails {
|
||||||
detail: None,
|
detail: None,
|
||||||
@ -715,6 +716,7 @@ impl LanguageServer {
|
|||||||
nu_protocol::engine::CommandType::Builtin => Some(CompletionItemKind::FUNCTION),
|
nu_protocol::engine::CommandType::Builtin => Some(CompletionItemKind::FUNCTION),
|
||||||
_ => None,
|
_ => 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