nushell/crates/nu-cli/tests/completions/support/completions_helpers.rs
Lee Wonjoon e3f78b8793
Keep forward slash when autocomplete on Windows (#13321)
Related #7044 

# 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.
-->

When autocomplete path with `/` on Windows, paths keep with slash
instead of backslash(`\`).

If mixed both, path completion uses a last path seperator.


![image](https://github.com/nushell/nushell/assets/4014016/b09633fd-0e4a-4cd9-9c14-2bca30625804)


![image](https://github.com/nushell/nushell/assets/4014016/e1f228f8-4cce-43eb-a34a-dfa54efd2ebb)


![image](https://github.com/nushell/nushell/assets/4014016/0694443a-3017-4828-be60-5f39ffd96440)

# User-Facing Changes
<!-- List of all changes that impact the user experience here. This
helps us keep track of breaking changes. -->


![completion](https://github.com/nushell/nushell/assets/4014016/03626544-6a14-4d8b-a607-21a4472f8037)

# 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 toolkit.nu; toolkit test stdlib"` 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
> ```
-->

# 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.
-->
2024-07-30 08:28:41 -05:00

251 lines
7.0 KiB
Rust

use nu_engine::eval_block;
use nu_parser::parse;
use nu_path::{AbsolutePathBuf, PathBuf};
use nu_protocol::{
debugger::WithoutDebug,
engine::{EngineState, Stack, StateWorkingSet},
PipelineData, ShellError, Span, Value,
};
use nu_test_support::fs;
use reedline::Suggestion;
use 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() -> (AbsolutePathBuf, String, EngineState, Stack) {
// Target folder inside assets
let dir = fs::fixtures().join("completions");
let dir_str = dir
.clone()
.into_os_string()
.into_string()
.unwrap_or_default();
// Create a new engine with default context
let mut engine_state = create_default_context();
// Add $nu
engine_state.generate_nu_constant();
// 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)
}
// creates a new engine with the current path into the completions fixtures folder
pub fn new_dotnu_engine() -> (AbsolutePathBuf, String, EngineState, Stack) {
// Target folder inside assets
let dir = fs::fixtures().join("dotnu_completions");
let dir_str = dir
.clone()
.into_os_string()
.into_string()
.unwrap_or_default();
let dir_span = nu_protocol::Span::new(0, dir_str.len());
// Create a new engine with default context
let mut engine_state = create_default_context();
// Add $nu
engine_state.generate_nu_constant();
// New stack
let mut stack = Stack::new();
// Add pwd as env var
stack.add_env_var("PWD".to_string(), Value::string(dir_str.clone(), dir_span));
stack.add_env_var(
"TEST".to_string(),
Value::string("NUSHELL".to_string(), dir_span),
);
stack.add_env_var(
"NU_LIB_DIRS".to_string(),
Value::List {
vals: vec![
Value::string(file(dir.join("lib-dir1")), dir_span),
Value::string(file(dir.join("lib-dir2")), dir_span),
Value::string(file(dir.join("lib-dir3")), dir_span),
],
internal_span: dir_span,
},
);
// 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() -> (AbsolutePathBuf, String, EngineState, Stack) {
// Target folder inside assets
let dir = fs::fixtures().join("quoted_completions");
let dir_str = dir
.clone()
.into_os_string()
.into_string()
.unwrap_or_default();
// 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() -> (AbsolutePathBuf, String, EngineState, Stack) {
// Target folder inside assets
let dir = fs::fixtures().join("partial_completions");
let dir_str = dir
.clone()
.into_os_string()
.into_string()
.unwrap_or_default();
// 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"
)
}
let suggestoins_str = suggestions
.iter()
.map(|it| it.value.clone())
.collect::<Vec<_>>();
assert_eq!(expected, &suggestoins_str);
}
// append the separator to the converted path
pub fn folder(path: impl Into<PathBuf>) -> String {
let mut converted_path = file(path);
converted_path.push(MAIN_SEPARATOR);
converted_path
}
// convert a given path to string
pub fn file(path: impl Into<PathBuf>) -> String {
path.into().into_os_string().into_string().unwrap()
}
// 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: AbsolutePathBuf,
) -> 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::<WithoutDebug>(
engine_state,
stack,
&block,
PipelineData::Value(Value::nothing(Span::unknown()), None),
)
.is_ok());
// Merge environment into the permanent state
engine_state.merge_env(stack, &dir)
}