mirror of
https://github.com/nushell/nushell.git
synced 2025-03-15 16:08:34 +01:00
feat(completion): stdlib virtual path completion & exportable completion
This commit is contained in:
parent
81e496673e
commit
63d9867b60
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -3541,6 +3541,7 @@ dependencies = [
|
|||||||
"nu-path",
|
"nu-path",
|
||||||
"nu-plugin-engine",
|
"nu-plugin-engine",
|
||||||
"nu-protocol",
|
"nu-protocol",
|
||||||
|
"nu-std",
|
||||||
"nu-test-support",
|
"nu-test-support",
|
||||||
"nu-utils",
|
"nu-utils",
|
||||||
"nucleo-matcher",
|
"nucleo-matcher",
|
||||||
|
@ -13,6 +13,7 @@ bench = false
|
|||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.102.1" }
|
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.102.1" }
|
||||||
nu-command = { path = "../nu-command", version = "0.102.1" }
|
nu-command = { path = "../nu-command", version = "0.102.1" }
|
||||||
|
nu-std = { path = "../nu-std", version = "0.102.1" }
|
||||||
nu-test-support = { path = "../nu-test-support", version = "0.102.1" }
|
nu-test-support = { path = "../nu-test-support", version = "0.102.1" }
|
||||||
rstest = { workspace = true, default-features = false }
|
rstest = { workspace = true, default-features = false }
|
||||||
tempfile = { workspace = true }
|
tempfile = { workspace = true }
|
||||||
|
@ -1,21 +1,20 @@
|
|||||||
use crate::completions::{
|
use crate::completions::{
|
||||||
|
base::{SemanticSuggestion, SuggestionKind},
|
||||||
AttributableCompletion, AttributeCompletion, CellPathCompletion, CommandCompletion, Completer,
|
AttributableCompletion, AttributeCompletion, CellPathCompletion, CommandCompletion, Completer,
|
||||||
CompletionOptions, CustomCompletion, DirectoryCompletion, DotNuCompletion, FileCompletion,
|
CompletionOptions, CustomCompletion, DirectoryCompletion, DotNuCompletion,
|
||||||
FlagCompletion, OperatorCompletion, VariableCompletion,
|
ExportableCompletion, FileCompletion, FlagCompletion, OperatorCompletion, VariableCompletion,
|
||||||
};
|
};
|
||||||
use nu_color_config::{color_record_to_nustyle, lookup_ansi_color_style};
|
use nu_color_config::{color_record_to_nustyle, lookup_ansi_color_style};
|
||||||
use nu_engine::eval_block;
|
use nu_engine::eval_block;
|
||||||
use nu_parser::{flatten_expression, parse};
|
use nu_parser::{flatten_expression, parse, parse_module_file_or_dir};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::{Argument, Block, Expr, Expression, FindMapResult, Traverse},
|
ast::{Argument, Block, Expr, Expression, FindMapResult, ListItem, Traverse},
|
||||||
debugger::WithoutDebug,
|
debugger::WithoutDebug,
|
||||||
engine::{Closure, EngineState, Stack, StateWorkingSet},
|
engine::{Closure, EngineState, Stack, StateWorkingSet},
|
||||||
PipelineData, Span, Type, Value,
|
PipelineData, Span, Type, Value,
|
||||||
};
|
};
|
||||||
use reedline::{Completer as ReedlineCompleter, Suggestion};
|
use reedline::{Completer as ReedlineCompleter, Suggestion};
|
||||||
use std::{str, sync::Arc};
|
use std::sync::Arc;
|
||||||
|
|
||||||
use super::base::{SemanticSuggestion, SuggestionKind};
|
|
||||||
|
|
||||||
/// Used as the function `f` in find_map Traverse
|
/// Used as the function `f` in find_map Traverse
|
||||||
///
|
///
|
||||||
@ -57,8 +56,13 @@ fn find_pipeline_element_by_position<'a>(
|
|||||||
Expr::FullCellPath(fcp) => fcp
|
Expr::FullCellPath(fcp) => fcp
|
||||||
.head
|
.head
|
||||||
.find_map(working_set, &closure)
|
.find_map(working_set, &closure)
|
||||||
.or(Some(expr))
|
|
||||||
.map(FindMapResult::Found)
|
.map(FindMapResult::Found)
|
||||||
|
// e.g. use std/util [<tab>
|
||||||
|
.or_else(|| {
|
||||||
|
(fcp.head.span.contains(pos) && matches!(fcp.head.expr, Expr::List(_)))
|
||||||
|
.then_some(FindMapResult::Continue)
|
||||||
|
})
|
||||||
|
.or(Some(FindMapResult::Found(expr)))
|
||||||
.unwrap_or_default(),
|
.unwrap_or_default(),
|
||||||
Expr::Var(_) => FindMapResult::Found(expr),
|
Expr::Var(_) => FindMapResult::Found(expr),
|
||||||
Expr::AttributeBlock(ab) => ab
|
Expr::AttributeBlock(ab) => ab
|
||||||
@ -127,6 +131,18 @@ struct Context<'a> {
|
|||||||
offset: usize,
|
offset: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// For argument completion
|
||||||
|
struct PositionalArguments<'a> {
|
||||||
|
/// command name
|
||||||
|
command_head: &'a [u8],
|
||||||
|
/// indices of positional arguments
|
||||||
|
positional_arg_indices: Vec<usize>,
|
||||||
|
/// argument list
|
||||||
|
arguments: &'a [Argument],
|
||||||
|
/// expression of current argument
|
||||||
|
expr: &'a Expression,
|
||||||
|
}
|
||||||
|
|
||||||
impl Context<'_> {
|
impl Context<'_> {
|
||||||
fn new<'a>(
|
fn new<'a>(
|
||||||
working_set: &'a StateWorkingSet,
|
working_set: &'a StateWorkingSet,
|
||||||
@ -328,7 +344,8 @@ impl NuCompleter {
|
|||||||
// NOTE: the argument to complete is not necessarily the last one
|
// NOTE: the argument to complete is not necessarily the last one
|
||||||
// for lsp completion, we don't trim the text,
|
// for lsp completion, we don't trim the text,
|
||||||
// so that `def`s after pos can be completed
|
// so that `def`s after pos can be completed
|
||||||
for arg in call.arguments.iter() {
|
let mut positional_arg_indices = Vec::new();
|
||||||
|
for (arg_idx, arg) in call.arguments.iter().enumerate() {
|
||||||
let span = arg.span();
|
let span = arg.span();
|
||||||
if span.contains(pos) {
|
if span.contains(pos) {
|
||||||
// if customized completion specified, it has highest priority
|
// if customized completion specified, it has highest priority
|
||||||
@ -379,9 +396,15 @@ impl NuCompleter {
|
|||||||
// complete according to expression type and command head
|
// complete according to expression type and command head
|
||||||
Argument::Positional(expr) => {
|
Argument::Positional(expr) => {
|
||||||
let command_head = working_set.get_span_contents(call.head);
|
let command_head = working_set.get_span_contents(call.head);
|
||||||
|
positional_arg_indices.push(arg_idx);
|
||||||
self.argument_completion_helper(
|
self.argument_completion_helper(
|
||||||
command_head,
|
PositionalArguments {
|
||||||
expr,
|
command_head,
|
||||||
|
positional_arg_indices,
|
||||||
|
arguments: &call.arguments,
|
||||||
|
expr,
|
||||||
|
},
|
||||||
|
pos,
|
||||||
&ctx,
|
&ctx,
|
||||||
suggestions.is_empty(),
|
suggestions.is_empty(),
|
||||||
)
|
)
|
||||||
@ -389,6 +412,8 @@ impl NuCompleter {
|
|||||||
_ => vec![],
|
_ => vec![],
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
} else if !matches!(arg, Argument::Named(_)) {
|
||||||
|
positional_arg_indices.push(arg_idx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -497,18 +522,97 @@ impl NuCompleter {
|
|||||||
|
|
||||||
fn argument_completion_helper(
|
fn argument_completion_helper(
|
||||||
&self,
|
&self,
|
||||||
command_head: &[u8],
|
argument_info: PositionalArguments,
|
||||||
expr: &Expression,
|
pos: usize,
|
||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
need_fallback: bool,
|
need_fallback: bool,
|
||||||
) -> Vec<SemanticSuggestion> {
|
) -> Vec<SemanticSuggestion> {
|
||||||
|
let PositionalArguments {
|
||||||
|
command_head,
|
||||||
|
positional_arg_indices,
|
||||||
|
arguments,
|
||||||
|
expr,
|
||||||
|
} = argument_info;
|
||||||
// special commands
|
// special commands
|
||||||
match command_head {
|
match command_head {
|
||||||
// complete module file/directory
|
// complete module file/directory
|
||||||
// TODO: if module file already specified,
|
b"use" | b"export use" | b"overlay use" | b"source-env"
|
||||||
|
if positional_arg_indices.len() == 1 =>
|
||||||
|
{
|
||||||
|
return self.process_completion(
|
||||||
|
&mut DotNuCompletion {
|
||||||
|
std_virtual_path: command_head != b"source-env",
|
||||||
|
},
|
||||||
|
ctx,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// NOTE: if module file already specified,
|
||||||
// should parse it to get modules/commands/consts to complete
|
// should parse it to get modules/commands/consts to complete
|
||||||
b"use" | b"export use" | b"overlay use" | b"source-env" => {
|
b"use" | b"export use" => {
|
||||||
return self.process_completion(&mut DotNuCompletion, ctx);
|
let Some(Argument::Positional(Expression {
|
||||||
|
expr: Expr::String(module_name),
|
||||||
|
span,
|
||||||
|
..
|
||||||
|
})) = positional_arg_indices
|
||||||
|
.first()
|
||||||
|
.and_then(|i| arguments.get(*i))
|
||||||
|
else {
|
||||||
|
return vec![];
|
||||||
|
};
|
||||||
|
let module_name = module_name.as_bytes();
|
||||||
|
let (module_id, temp_working_set) = match ctx.working_set.find_module(module_name) {
|
||||||
|
Some(module_id) => (module_id, None),
|
||||||
|
None => {
|
||||||
|
let mut temp_working_set =
|
||||||
|
StateWorkingSet::new(ctx.working_set.permanent_state);
|
||||||
|
let Some(module_id) = parse_module_file_or_dir(
|
||||||
|
&mut temp_working_set,
|
||||||
|
module_name,
|
||||||
|
*span,
|
||||||
|
None,
|
||||||
|
) else {
|
||||||
|
return vec![];
|
||||||
|
};
|
||||||
|
(module_id, Some(temp_working_set))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let mut exportable_completion = ExportableCompletion {
|
||||||
|
module_id,
|
||||||
|
temp_working_set,
|
||||||
|
in_list: false,
|
||||||
|
};
|
||||||
|
let mut complete_on_list_items = |items: &[ListItem]| -> Vec<SemanticSuggestion> {
|
||||||
|
exportable_completion.in_list = true;
|
||||||
|
for item in items {
|
||||||
|
let span = item.expr().span;
|
||||||
|
if span.contains(pos) {
|
||||||
|
let offset = span.start.saturating_sub(ctx.span.start);
|
||||||
|
let end_offset =
|
||||||
|
ctx.prefix.len().min(pos.min(span.end) - ctx.span.start + 1);
|
||||||
|
let new_ctx = Context::new(
|
||||||
|
ctx.working_set,
|
||||||
|
Span::new(span.start, ctx.span.end.min(span.end).min(pos + 1)),
|
||||||
|
ctx.prefix.get(offset..end_offset).unwrap_or_default(),
|
||||||
|
ctx.offset,
|
||||||
|
);
|
||||||
|
return self.process_completion(&mut exportable_completion, &new_ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
vec![]
|
||||||
|
};
|
||||||
|
|
||||||
|
match &expr.expr {
|
||||||
|
Expr::String(_) => {
|
||||||
|
return self.process_completion(&mut exportable_completion, ctx);
|
||||||
|
}
|
||||||
|
Expr::FullCellPath(fcp) => match &fcp.head.expr {
|
||||||
|
Expr::List(items) => {
|
||||||
|
return complete_on_list_items(items);
|
||||||
|
}
|
||||||
|
_ => return vec![],
|
||||||
|
},
|
||||||
|
_ => return vec![],
|
||||||
|
}
|
||||||
}
|
}
|
||||||
b"which" => {
|
b"which" => {
|
||||||
let mut completer = CommandCompletion {
|
let mut completer = CommandCompletion {
|
||||||
|
@ -139,7 +139,7 @@ impl OriginalCwd {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn surround_remove(partial: &str) -> String {
|
pub fn surround_remove(partial: &str) -> String {
|
||||||
for c in ['`', '"', '\''] {
|
for c in ['`', '"', '\''] {
|
||||||
if partial.starts_with(c) {
|
if partial.starts_with(c) {
|
||||||
let ret = partial.strip_prefix(c).unwrap_or(partial);
|
let ret = partial.strip_prefix(c).unwrap_or(partial);
|
||||||
|
@ -1,18 +1,23 @@
|
|||||||
use crate::completions::{file_path_completion, Completer, CompletionOptions};
|
use crate::completions::{
|
||||||
|
completion_common::{surround_remove, FileSuggestion},
|
||||||
|
completion_options::NuMatcher,
|
||||||
|
file_path_completion, Completer, CompletionOptions, SemanticSuggestion, SuggestionKind,
|
||||||
|
};
|
||||||
use nu_path::expand_tilde;
|
use nu_path::expand_tilde;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
engine::{Stack, StateWorkingSet},
|
engine::{Stack, StateWorkingSet, VirtualPath},
|
||||||
Span,
|
Span,
|
||||||
};
|
};
|
||||||
use reedline::Suggestion;
|
use reedline::Suggestion;
|
||||||
use std::{
|
use std::{
|
||||||
collections::HashSet,
|
collections::HashSet,
|
||||||
path::{is_separator, PathBuf, MAIN_SEPARATOR as SEP, MAIN_SEPARATOR_STR},
|
path::{is_separator, PathBuf, MAIN_SEPARATOR_STR},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{SemanticSuggestion, SuggestionKind};
|
pub struct DotNuCompletion {
|
||||||
|
/// e.g. use std/a<tab>
|
||||||
pub struct DotNuCompletion;
|
pub std_virtual_path: bool,
|
||||||
|
}
|
||||||
|
|
||||||
impl Completer for DotNuCompletion {
|
impl Completer for DotNuCompletion {
|
||||||
fn fetch(
|
fn fetch(
|
||||||
@ -102,7 +107,7 @@ impl Completer for DotNuCompletion {
|
|||||||
|
|
||||||
// Fetch the files filtering the ones that ends with .nu
|
// Fetch the files filtering the ones that ends with .nu
|
||||||
// and transform them into suggestions
|
// and transform them into suggestions
|
||||||
let completions = file_path_completion(
|
let mut completions = file_path_completion(
|
||||||
span,
|
span,
|
||||||
partial,
|
partial,
|
||||||
&search_dirs
|
&search_dirs
|
||||||
@ -113,17 +118,57 @@ impl Completer for DotNuCompletion {
|
|||||||
working_set.permanent_state,
|
working_set.permanent_state,
|
||||||
stack,
|
stack,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if self.std_virtual_path {
|
||||||
|
let mut matcher = NuMatcher::<()>::new(partial, options);
|
||||||
|
let base_dir = surround_remove(&base_dir);
|
||||||
|
if base_dir == "." {
|
||||||
|
let surround_prefix = partial
|
||||||
|
.chars()
|
||||||
|
.take_while(|c| "`'\"".contains(*c))
|
||||||
|
.collect::<String>();
|
||||||
|
for path in ["std", "std-rfc"] {
|
||||||
|
let path = format!("{}{}", surround_prefix, path);
|
||||||
|
if matcher.matches(&path) {
|
||||||
|
completions.push(FileSuggestion {
|
||||||
|
path,
|
||||||
|
span,
|
||||||
|
style: None,
|
||||||
|
is_dir: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if let Some(VirtualPath::Dir(sub_paths)) =
|
||||||
|
working_set.find_virtual_path(&base_dir)
|
||||||
|
{
|
||||||
|
for sub_vp_id in sub_paths {
|
||||||
|
let (path, sub_vp) = working_set.get_virtual_path(*sub_vp_id);
|
||||||
|
let path = path
|
||||||
|
.strip_prefix(&format!("{}/", base_dir))
|
||||||
|
.unwrap_or(path)
|
||||||
|
.to_string();
|
||||||
|
if matcher.matches(&path) {
|
||||||
|
completions.push(FileSuggestion {
|
||||||
|
path,
|
||||||
|
span,
|
||||||
|
style: None,
|
||||||
|
is_dir: matches!(sub_vp, VirtualPath::Dir(_)),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
completions
|
completions
|
||||||
.into_iter()
|
.into_iter()
|
||||||
// Different base dir, so we list the .nu files or folders
|
// Different base dir, so we list the .nu files or folders
|
||||||
.filter(|it| {
|
.filter(|it| {
|
||||||
// for paths with spaces in them
|
// for paths with spaces in them
|
||||||
let path = it.path.trim_end_matches('`');
|
let path = it.path.trim_end_matches('`');
|
||||||
path.ends_with(".nu") || path.ends_with(SEP)
|
path.ends_with(".nu") || it.is_dir
|
||||||
})
|
})
|
||||||
.map(|x| {
|
.map(|x| {
|
||||||
let append_whitespace =
|
let append_whitespace = !x.is_dir && (!start_with_backquote || end_with_backquote);
|
||||||
x.path.ends_with(".nu") && (!start_with_backquote || end_with_backquote);
|
|
||||||
// Re-calculate the span to replace
|
// Re-calculate the span to replace
|
||||||
let mut span_offset = 0;
|
let mut span_offset = 0;
|
||||||
let mut value = x.path.to_string();
|
let mut value = x.path.to_string();
|
||||||
|
107
crates/nu-cli/src/completions/exportable_completions.rs
Normal file
107
crates/nu-cli/src/completions/exportable_completions.rs
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
use crate::completions::{
|
||||||
|
completion_common::surround_remove, completion_options::NuMatcher, Completer,
|
||||||
|
CompletionOptions, SemanticSuggestion, SuggestionKind,
|
||||||
|
};
|
||||||
|
use nu_protocol::{
|
||||||
|
engine::{Stack, StateWorkingSet},
|
||||||
|
ModuleId, Span,
|
||||||
|
};
|
||||||
|
use reedline::Suggestion;
|
||||||
|
|
||||||
|
pub struct ExportableCompletion<'a> {
|
||||||
|
pub module_id: ModuleId,
|
||||||
|
pub temp_working_set: Option<StateWorkingSet<'a>>,
|
||||||
|
pub in_list: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// If name contains space, wrap it in quotes
|
||||||
|
fn wrapped_name(name: String) -> String {
|
||||||
|
if !name.contains(' ') {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
if name.contains('\'') {
|
||||||
|
format!("\"{}\"", name.replace('"', r#"\""#))
|
||||||
|
} else {
|
||||||
|
format!("'{name}'")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Completer for ExportableCompletion<'_> {
|
||||||
|
fn fetch(
|
||||||
|
&mut self,
|
||||||
|
working_set: &StateWorkingSet,
|
||||||
|
_stack: &Stack,
|
||||||
|
prefix: impl AsRef<str>,
|
||||||
|
span: Span,
|
||||||
|
offset: usize,
|
||||||
|
options: &CompletionOptions,
|
||||||
|
) -> Vec<SemanticSuggestion> {
|
||||||
|
let mut matcher = NuMatcher::<()>::new(surround_remove(prefix.as_ref()), options);
|
||||||
|
let mut results = Vec::new();
|
||||||
|
let span = reedline::Span {
|
||||||
|
start: span.start - offset,
|
||||||
|
end: span.end - offset,
|
||||||
|
};
|
||||||
|
let mut add_suggestion = |value: String,
|
||||||
|
description: Option<String>,
|
||||||
|
extra: Option<Vec<String>>,
|
||||||
|
kind: SuggestionKind| {
|
||||||
|
results.push(SemanticSuggestion {
|
||||||
|
suggestion: Suggestion {
|
||||||
|
value,
|
||||||
|
span,
|
||||||
|
description,
|
||||||
|
extra,
|
||||||
|
append_whitespace: !self.in_list,
|
||||||
|
..Suggestion::default()
|
||||||
|
},
|
||||||
|
kind: Some(kind),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
let working_set = self.temp_working_set.as_ref().unwrap_or(working_set);
|
||||||
|
let module = working_set.get_module(self.module_id);
|
||||||
|
|
||||||
|
for (name, decl_id) in &module.decls {
|
||||||
|
let name = String::from_utf8_lossy(name).to_string();
|
||||||
|
if matcher.matches(&name) {
|
||||||
|
let cmd = working_set.get_decl(*decl_id);
|
||||||
|
add_suggestion(
|
||||||
|
wrapped_name(name),
|
||||||
|
Some(cmd.description().to_string()),
|
||||||
|
None,
|
||||||
|
SuggestionKind::Command(cmd.command_type()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (name, module_id) in &module.submodules {
|
||||||
|
let name = String::from_utf8_lossy(name).to_string();
|
||||||
|
if matcher.matches(&name) {
|
||||||
|
let comments = working_set.get_module_comments(*module_id).map(|spans| {
|
||||||
|
spans
|
||||||
|
.iter()
|
||||||
|
.map(|sp| {
|
||||||
|
String::from_utf8_lossy(working_set.get_span_contents(*sp)).into()
|
||||||
|
})
|
||||||
|
.collect::<Vec<String>>()
|
||||||
|
});
|
||||||
|
add_suggestion(wrapped_name(name), None, comments, SuggestionKind::Module);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (name, var_id) in &module.constants {
|
||||||
|
let name = String::from_utf8_lossy(name).to_string();
|
||||||
|
if matcher.matches(&name) {
|
||||||
|
let var = working_set.get_variable(*var_id);
|
||||||
|
add_suggestion(
|
||||||
|
wrapped_name(name),
|
||||||
|
var.const_val
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|v| v.clone().coerce_into_string().ok()),
|
||||||
|
None,
|
||||||
|
SuggestionKind::Variable,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
results
|
||||||
|
}
|
||||||
|
}
|
@ -1,12 +1,12 @@
|
|||||||
use crate::completions::{completion_options::NuMatcher, Completer, CompletionOptions};
|
use crate::completions::{
|
||||||
|
completion_options::NuMatcher, Completer, CompletionOptions, SemanticSuggestion, SuggestionKind,
|
||||||
|
};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
engine::{Stack, StateWorkingSet},
|
engine::{Stack, StateWorkingSet},
|
||||||
DeclId, Span,
|
DeclId, Span,
|
||||||
};
|
};
|
||||||
use reedline::Suggestion;
|
use reedline::Suggestion;
|
||||||
|
|
||||||
use super::{SemanticSuggestion, SuggestionKind};
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct FlagCompletion {
|
pub struct FlagCompletion {
|
||||||
pub decl_id: DeclId,
|
pub decl_id: DeclId,
|
||||||
|
@ -8,6 +8,7 @@ mod completion_options;
|
|||||||
mod custom_completions;
|
mod custom_completions;
|
||||||
mod directory_completions;
|
mod directory_completions;
|
||||||
mod dotnu_completions;
|
mod dotnu_completions;
|
||||||
|
mod exportable_completions;
|
||||||
mod file_completions;
|
mod file_completions;
|
||||||
mod flag_completions;
|
mod flag_completions;
|
||||||
mod operator_completions;
|
mod operator_completions;
|
||||||
@ -22,6 +23,7 @@ pub use completion_options::{CompletionOptions, MatchAlgorithm};
|
|||||||
pub use custom_completions::CustomCompletion;
|
pub use custom_completions::CustomCompletion;
|
||||||
pub use directory_completions::DirectoryCompletion;
|
pub use directory_completions::DirectoryCompletion;
|
||||||
pub use dotnu_completions::DotNuCompletion;
|
pub use dotnu_completions::DotNuCompletion;
|
||||||
|
pub use exportable_completions::ExportableCompletion;
|
||||||
pub use file_completions::{file_path_completion, FileCompletion};
|
pub use file_completions::{file_path_completion, FileCompletion};
|
||||||
pub use flag_completions::FlagCompletion;
|
pub use flag_completions::FlagCompletion;
|
||||||
pub use operator_completions::OperatorCompletion;
|
pub use operator_completions::OperatorCompletion;
|
||||||
|
@ -11,6 +11,7 @@ use nu_engine::eval_block;
|
|||||||
use nu_parser::parse;
|
use nu_parser::parse;
|
||||||
use nu_path::expand_tilde;
|
use nu_path::expand_tilde;
|
||||||
use nu_protocol::{debugger::WithoutDebug, engine::StateWorkingSet, PipelineData};
|
use nu_protocol::{debugger::WithoutDebug, engine::StateWorkingSet, PipelineData};
|
||||||
|
use nu_std::load_standard_library;
|
||||||
use reedline::{Completer, Suggestion};
|
use reedline::{Completer, Suggestion};
|
||||||
use rstest::{fixture, rstest};
|
use rstest::{fixture, rstest};
|
||||||
use support::{
|
use support::{
|
||||||
@ -468,7 +469,7 @@ fn dotnu_completions() {
|
|||||||
|
|
||||||
match_suggestions(&vec!["sub.nu`"], &suggestions);
|
match_suggestions(&vec!["sub.nu`"], &suggestions);
|
||||||
|
|
||||||
let expected = vec![
|
let mut expected = vec![
|
||||||
"asdf.nu",
|
"asdf.nu",
|
||||||
"bar.nu",
|
"bar.nu",
|
||||||
"bat.nu",
|
"bat.nu",
|
||||||
@ -501,6 +502,8 @@ fn dotnu_completions() {
|
|||||||
match_suggestions(&expected, &suggestions);
|
match_suggestions(&expected, &suggestions);
|
||||||
|
|
||||||
// Test use completion
|
// Test use completion
|
||||||
|
expected.push("std");
|
||||||
|
expected.push("std-rfc");
|
||||||
let completion_str = "use ";
|
let completion_str = "use ";
|
||||||
let suggestions = completer.complete(completion_str, completion_str.len());
|
let suggestions = completer.complete(completion_str, completion_str.len());
|
||||||
|
|
||||||
@ -532,6 +535,29 @@ fn dotnu_completions() {
|
|||||||
match_dir_content_for_dotnu(dir_content, &suggestions);
|
match_dir_content_for_dotnu(dir_content, &suggestions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn dotnu_stdlib_completions() {
|
||||||
|
let (_, _, mut engine, stack) = new_dotnu_engine();
|
||||||
|
assert!(load_standard_library(&mut engine).is_ok());
|
||||||
|
let mut completer = NuCompleter::new(Arc::new(engine), Arc::new(stack));
|
||||||
|
|
||||||
|
let completion_str = "export use std/ass";
|
||||||
|
let suggestions = completer.complete(completion_str, completion_str.len());
|
||||||
|
match_suggestions(&vec!["assert"], &suggestions);
|
||||||
|
|
||||||
|
let completion_str = "use `std-rfc/cli";
|
||||||
|
let suggestions = completer.complete(completion_str, completion_str.len());
|
||||||
|
match_suggestions(&vec!["clip"], &suggestions);
|
||||||
|
|
||||||
|
let completion_str = "use \"std";
|
||||||
|
let suggestions = completer.complete(completion_str, completion_str.len());
|
||||||
|
match_suggestions(&vec!["\"std", "\"std-rfc"], &suggestions);
|
||||||
|
|
||||||
|
let completion_str = "overlay use \'std-rfc/cli";
|
||||||
|
let suggestions = completer.complete(completion_str, completion_str.len());
|
||||||
|
match_suggestions(&vec!["clip"], &suggestions);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn dotnu_completions_const_nu_lib_dirs() {
|
fn dotnu_completions_const_nu_lib_dirs() {
|
||||||
let (_, _, engine, stack) = new_dotnu_engine();
|
let (_, _, engine, stack) = new_dotnu_engine();
|
||||||
|
Loading…
Reference in New Issue
Block a user