forked from extern/nushell
5c15a4dd6e
<!-- if this PR closes one or more issues, you can automatically link the PR with them by using one of the [*linking keywords*](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword), e.g. - this PR should close #5683 you can also mention related issues, PRs or discussions! --> # Description <!-- Thank you for improving Nushell. Please, check our [contributing guide](../CONTRIBUTING.md) and talk to the core team before making major changes. Description of your pull request goes here. **Provide examples and/or screenshots** if your changes affect the user experience. --> This PR allows tab completion for nested directories while only specifying a part of the directory names. To illustrate this, if I type `tar/de/inc` and hit tab, it autocompletes to `./target/debug/incremental`. # User-Facing Changes <!-- List of all changes that impact the user experience here. This helps us keep track of breaking changes. --> Nested paths can be tab completed by typing lesser characters. # Tests + Formatting <!-- Don't forget to add tests that cover your changes. Make sure you've run and fixed any issues with these commands: - `cargo fmt --all -- --check` to check standard code formatting (`cargo fmt --all` applies these changes) - `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used` to check that you're using the standard code style - `cargo test --workspace` to check that all tests pass (on Windows make sure to [enable developer mode](https://learn.microsoft.com/en-us/windows/apps/get-started/developer-mode-features-and-debugging)) - `cargo run -- -c "use std testing; testing run-tests --path crates/nu-std"` to run the tests for the standard library > **Note** > from `nushell` you can also use the `toolkit` as follows > ```bash > use toolkit.nu # or use an `env_change` hook to activate it automatically > toolkit check pr > ``` --> Tests cases are added. # After Submitting <!-- If your PR had any user-facing changes, update [the documentation](https://github.com/nushell/nushell.github.io) after the PR is merged, if necessary. This will help us keep the docs up to date. -->
210 lines
5.8 KiB
Rust
210 lines
5.8 KiB
Rust
use std::path::PathBuf;
|
|
|
|
use nu_engine::eval_block;
|
|
use nu_parser::parse;
|
|
use nu_protocol::{
|
|
engine::{EngineState, Stack, StateWorkingSet},
|
|
eval_const::create_nu_constant,
|
|
PipelineData, ShellError, Span, Value, NU_VARIABLE_ID,
|
|
};
|
|
use nu_test_support::fs;
|
|
use reedline::Suggestion;
|
|
const SEP: char = std::path::MAIN_SEPARATOR;
|
|
|
|
fn create_default_context() -> EngineState {
|
|
nu_command::add_shell_command_context(nu_cmd_lang::create_default_context())
|
|
}
|
|
|
|
// creates a new engine with the current path into the completions fixtures folder
|
|
pub fn new_engine() -> (PathBuf, String, EngineState, Stack) {
|
|
// Target folder inside assets
|
|
let dir = fs::fixtures().join("completions");
|
|
let mut dir_str = dir
|
|
.clone()
|
|
.into_os_string()
|
|
.into_string()
|
|
.unwrap_or_default();
|
|
dir_str.push(SEP);
|
|
|
|
// Create a new engine with default context
|
|
let mut engine_state = create_default_context();
|
|
|
|
// Add $nu
|
|
let nu_const =
|
|
create_nu_constant(&engine_state, Span::test_data()).expect("Failed creating $nu");
|
|
engine_state.set_variable_const_val(NU_VARIABLE_ID, nu_const);
|
|
|
|
// New stack
|
|
let mut stack = Stack::new();
|
|
|
|
// Add pwd as env var
|
|
stack.add_env_var(
|
|
"PWD".to_string(),
|
|
Value::string(dir_str.clone(), nu_protocol::Span::new(0, dir_str.len())),
|
|
);
|
|
stack.add_env_var(
|
|
"TEST".to_string(),
|
|
Value::string(
|
|
"NUSHELL".to_string(),
|
|
nu_protocol::Span::new(0, dir_str.len()),
|
|
),
|
|
);
|
|
#[cfg(windows)]
|
|
stack.add_env_var(
|
|
"Path".to_string(),
|
|
Value::string(
|
|
"c:\\some\\path;c:\\some\\other\\path".to_string(),
|
|
nu_protocol::Span::new(0, dir_str.len()),
|
|
),
|
|
);
|
|
#[cfg(not(windows))]
|
|
stack.add_env_var(
|
|
"PATH".to_string(),
|
|
Value::string(
|
|
"/some/path:/some/other/path".to_string(),
|
|
nu_protocol::Span::new(0, dir_str.len()),
|
|
),
|
|
);
|
|
|
|
// Merge environment into the permanent state
|
|
let merge_result = engine_state.merge_env(&mut stack, &dir);
|
|
assert!(merge_result.is_ok());
|
|
|
|
(dir, dir_str, engine_state, stack)
|
|
}
|
|
|
|
pub fn new_quote_engine() -> (PathBuf, String, EngineState, Stack) {
|
|
// Target folder inside assets
|
|
let dir = fs::fixtures().join("quoted_completions");
|
|
let mut dir_str = dir
|
|
.clone()
|
|
.into_os_string()
|
|
.into_string()
|
|
.unwrap_or_default();
|
|
dir_str.push(SEP);
|
|
|
|
// Create a new engine with default context
|
|
let mut engine_state = create_default_context();
|
|
|
|
// New stack
|
|
let mut stack = Stack::new();
|
|
|
|
// Add pwd as env var
|
|
stack.add_env_var(
|
|
"PWD".to_string(),
|
|
Value::string(dir_str.clone(), nu_protocol::Span::new(0, dir_str.len())),
|
|
);
|
|
stack.add_env_var(
|
|
"TEST".to_string(),
|
|
Value::string(
|
|
"NUSHELL".to_string(),
|
|
nu_protocol::Span::new(0, dir_str.len()),
|
|
),
|
|
);
|
|
|
|
// Merge environment into the permanent state
|
|
let merge_result = engine_state.merge_env(&mut stack, &dir);
|
|
assert!(merge_result.is_ok());
|
|
|
|
(dir, dir_str, engine_state, stack)
|
|
}
|
|
|
|
pub fn new_partial_engine() -> (PathBuf, String, EngineState, Stack) {
|
|
// Target folder inside assets
|
|
let dir = fs::fixtures().join("partial_completions");
|
|
let mut dir_str = dir
|
|
.clone()
|
|
.into_os_string()
|
|
.into_string()
|
|
.unwrap_or_default();
|
|
dir_str.push(SEP);
|
|
|
|
// Create a new engine with default context
|
|
let mut engine_state = create_default_context();
|
|
|
|
// New stack
|
|
let mut stack = Stack::new();
|
|
|
|
// Add pwd as env var
|
|
stack.add_env_var(
|
|
"PWD".to_string(),
|
|
Value::string(dir_str.clone(), nu_protocol::Span::new(0, dir_str.len())),
|
|
);
|
|
stack.add_env_var(
|
|
"TEST".to_string(),
|
|
Value::string(
|
|
"NUSHELL".to_string(),
|
|
nu_protocol::Span::new(0, dir_str.len()),
|
|
),
|
|
);
|
|
|
|
// Merge environment into the permanent state
|
|
let merge_result = engine_state.merge_env(&mut stack, &dir);
|
|
assert!(merge_result.is_ok());
|
|
|
|
(dir, dir_str, engine_state, stack)
|
|
}
|
|
|
|
// match a list of suggestions with the expected values
|
|
pub fn match_suggestions(expected: Vec<String>, suggestions: Vec<Suggestion>) {
|
|
let expected_len = expected.len();
|
|
let suggestions_len = suggestions.len();
|
|
if expected_len != suggestions_len {
|
|
panic!(
|
|
"\nexpected {expected_len} suggestions but got {suggestions_len}: \n\
|
|
Suggestions: {suggestions:#?} \n\
|
|
Expected: {expected:#?}\n"
|
|
)
|
|
}
|
|
expected.iter().zip(suggestions).for_each(|it| {
|
|
assert_eq!(it.0, &it.1.value);
|
|
});
|
|
}
|
|
|
|
// append the separator to the converted path
|
|
pub fn folder(path: PathBuf) -> String {
|
|
let mut converted_path = file(path);
|
|
converted_path.push(SEP);
|
|
|
|
converted_path
|
|
}
|
|
|
|
// convert a given path to string
|
|
pub fn file(path: PathBuf) -> String {
|
|
path.into_os_string().into_string().unwrap_or_default()
|
|
}
|
|
|
|
// merge_input executes the given input into the engine
|
|
// and merges the state
|
|
pub fn merge_input(
|
|
input: &[u8],
|
|
engine_state: &mut EngineState,
|
|
stack: &mut Stack,
|
|
dir: PathBuf,
|
|
) -> Result<(), ShellError> {
|
|
let (block, delta) = {
|
|
let mut working_set = StateWorkingSet::new(engine_state);
|
|
|
|
let block = parse(&mut working_set, None, input, false);
|
|
|
|
assert!(working_set.parse_errors.is_empty());
|
|
|
|
(block, working_set.render())
|
|
};
|
|
|
|
engine_state.merge_delta(delta)?;
|
|
|
|
assert!(eval_block(
|
|
engine_state,
|
|
stack,
|
|
&block,
|
|
PipelineData::Value(Value::nothing(Span::unknown(),), None),
|
|
false,
|
|
false
|
|
)
|
|
.is_ok());
|
|
|
|
// Merge environment into the permanent state
|
|
engine_state.merge_env(stack, &dir)
|
|
}
|