mirror of
https://github.com/nushell/nushell.git
synced 2025-08-09 14:06:40 +02:00
Recursively export constants from modules (#10049)
<!-- 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 #xxxx - fixes #xxxx 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. --> https://github.com/nushell/nushell/pull/9773 introduced constants to modules and allowed to export them, but only within one level. This PR: * allows recursive exporting of constants from all submodules * fixes submodule imports in a list import pattern * makes sure exported constants are actual constants Should unblock https://github.com/nushell/nushell/pull/9678 ### Example: ```nushell module spam { export module eggs { export module bacon { export const viking = 'eats' } } } use spam print $spam.eggs.bacon.viking # prints 'eats' use spam [eggs] print $eggs.bacon.viking # prints 'eats' use spam eggs bacon viking print $viking # prints 'eats' ``` ### Limitation 1: Considering the above `spam` module, attempting to get `eggs bacon` from `spam` module doesn't work directly: ```nushell use spam [ eggs bacon ] # attempts to load `eggs`, then `bacon` use spam [ "eggs bacon" ] # obviously wrong name for a constant, but doesn't work also for commands ``` Workaround (for example): ```nushell use spam eggs use eggs [ bacon ] print $bacon.viking # prints 'eats' ``` I'm thinking I'll just leave it in, as you can easily work around this. It is also a limitation of the import pattern in general, not just constants. ### Limitation 2: `overlay use` successfully imports the constants, but `overlay hide` does not hide them, even though it seems to hide normal variables successfully. This needs more investigation. # User-Facing Changes <!-- List of all changes that impact the user experience here. This helps us keep track of breaking changes. --> Allows recursive constant exports from submodules. # 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 -A clippy::needless_collect -A clippy::result_large_err` to check that you're using the standard code style - `cargo test --workspace` to check that all tests pass - `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 > ``` --> # 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. -->
This commit is contained in:
@ -1409,7 +1409,8 @@ pub fn parse_export_in_module(
|
||||
}
|
||||
b"const" => {
|
||||
let pipeline = parse_const(working_set, &spans[1..]);
|
||||
let export_def_decl_id = if let Some(id) = working_set.find_decl(b"export const") {
|
||||
let export_const_decl_id = if let Some(id) = working_set.find_decl(b"export const")
|
||||
{
|
||||
id
|
||||
} else {
|
||||
working_set.error(ParseError::InternalError(
|
||||
@ -1431,7 +1432,7 @@ pub fn parse_export_in_module(
|
||||
call = def_call.clone();
|
||||
|
||||
call.head = span(&spans[0..=1]);
|
||||
call.decl_id = export_def_decl_id;
|
||||
call.decl_id = export_const_decl_id;
|
||||
} else {
|
||||
working_set.error(ParseError::InternalError(
|
||||
"unexpected output from parsing a definition".into(),
|
||||
@ -1441,15 +1442,19 @@ pub fn parse_export_in_module(
|
||||
|
||||
let mut result = vec![];
|
||||
|
||||
if let Some(decl_name_span) = spans.get(2) {
|
||||
let decl_name = working_set.get_span_contents(*decl_name_span);
|
||||
let decl_name = trim_quotes(decl_name);
|
||||
if let Some(var_name_span) = spans.get(2) {
|
||||
let var_name = working_set.get_span_contents(*var_name_span);
|
||||
let var_name = trim_quotes(var_name);
|
||||
|
||||
if let Some(decl_id) = working_set.find_variable(decl_name) {
|
||||
result.push(Exportable::VarDecl {
|
||||
name: decl_name.to_vec(),
|
||||
id: decl_id,
|
||||
});
|
||||
if let Some(var_id) = working_set.find_variable(var_name) {
|
||||
if let Err(err) = working_set.get_constant(var_id) {
|
||||
working_set.error(err);
|
||||
} else {
|
||||
result.push(Exportable::VarDecl {
|
||||
name: var_name.to_vec(),
|
||||
id: var_id,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
working_set.error(ParseError::InternalError(
|
||||
"failed to find added variable".into(),
|
||||
@ -2285,7 +2290,7 @@ pub fn parse_use(working_set: &mut StateWorkingSet, spans: &[Span]) -> (Pipeline
|
||||
},
|
||||
members: import_pattern.members,
|
||||
hidden: HashSet::new(),
|
||||
module_name_var_id: None,
|
||||
constants: vec![],
|
||||
},
|
||||
module,
|
||||
module_id,
|
||||
@ -2306,7 +2311,7 @@ pub fn parse_use(working_set: &mut StateWorkingSet, spans: &[Span]) -> (Pipeline
|
||||
},
|
||||
members: import_pattern.members,
|
||||
hidden: HashSet::new(),
|
||||
module_name_var_id: None,
|
||||
constants: vec![],
|
||||
},
|
||||
module,
|
||||
module_id,
|
||||
@ -2324,10 +2329,25 @@ pub fn parse_use(working_set: &mut StateWorkingSet, spans: &[Span]) -> (Pipeline
|
||||
);
|
||||
};
|
||||
|
||||
let (definitions, errors) =
|
||||
module.resolve_import_pattern(working_set, module_id, &import_pattern.members, None);
|
||||
let (definitions, errors) = module.resolve_import_pattern(
|
||||
working_set,
|
||||
module_id,
|
||||
&import_pattern.members,
|
||||
None,
|
||||
name_span,
|
||||
);
|
||||
|
||||
working_set.parse_errors.extend(errors);
|
||||
|
||||
let mut constants = vec![];
|
||||
|
||||
for (name, const_val) in definitions.constants {
|
||||
let const_var_id =
|
||||
working_set.add_variable(name.clone(), name_span, const_val.get_type(), false);
|
||||
working_set.set_variable_const_val(const_var_id, const_val);
|
||||
constants.push((name, const_var_id));
|
||||
}
|
||||
|
||||
let exportables = definitions
|
||||
.decls
|
||||
.iter()
|
||||
@ -2345,8 +2365,7 @@ pub fn parse_use(working_set: &mut StateWorkingSet, spans: &[Span]) -> (Pipeline
|
||||
}),
|
||||
)
|
||||
.chain(
|
||||
definitions
|
||||
.variables
|
||||
constants
|
||||
.iter()
|
||||
.map(|(name, variable_id)| Exportable::VarDecl {
|
||||
name: name.clone(),
|
||||
@ -2355,18 +2374,13 @@ pub fn parse_use(working_set: &mut StateWorkingSet, spans: &[Span]) -> (Pipeline
|
||||
)
|
||||
.collect();
|
||||
|
||||
import_pattern.constants = constants.iter().map(|(_, id)| *id).collect();
|
||||
|
||||
// Extend the current scope with the module's exportables
|
||||
working_set.use_decls(definitions.decls);
|
||||
working_set.use_modules(definitions.modules);
|
||||
working_set.use_variables(definitions.variables);
|
||||
working_set.use_variables(constants);
|
||||
|
||||
let module_name_var_id = working_set.add_variable(
|
||||
module.name(),
|
||||
module.span.unwrap_or(Span::unknown()),
|
||||
Type::Any,
|
||||
false,
|
||||
);
|
||||
import_pattern.module_name_var_id = Some(module_name_var_id);
|
||||
// Create a new Use command call to pass the import pattern as parser info
|
||||
let import_pattern_expr = Expression {
|
||||
expr: Expr::ImportPattern(import_pattern),
|
||||
@ -2776,6 +2790,7 @@ pub fn parse_overlay_use(working_set: &mut StateWorkingSet, call: Box<Call>) ->
|
||||
origin_module_id,
|
||||
&[],
|
||||
Some(final_overlay_name.as_bytes()),
|
||||
call.head,
|
||||
)
|
||||
} else {
|
||||
origin_module.resolve_import_pattern(
|
||||
@ -2785,6 +2800,7 @@ pub fn parse_overlay_use(working_set: &mut StateWorkingSet, call: Box<Call>) ->
|
||||
span: overlay_name_span,
|
||||
}],
|
||||
Some(final_overlay_name.as_bytes()),
|
||||
call.head,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
@ -3025,15 +3041,6 @@ pub fn parse_const(working_set: &mut StateWorkingSet, spans: &[Span]) -> Pipelin
|
||||
// const x = 'f', = at least start from index 2
|
||||
if item == b"=" && spans.len() > (span.0 + 1) && span.0 > 1 {
|
||||
let mut idx = span.0;
|
||||
// let rvalue = parse_multispan_value(
|
||||
// working_set,
|
||||
// spans,
|
||||
// &mut idx,
|
||||
// &SyntaxShape::Keyword(
|
||||
// b"=".to_vec(),
|
||||
// Box::new(SyntaxShape::MathExpression),
|
||||
// ),
|
||||
// );
|
||||
|
||||
let rvalue = parse_multispan_value(
|
||||
working_set,
|
||||
@ -3099,7 +3106,6 @@ pub fn parse_const(working_set: &mut StateWorkingSet, spans: &[Span]) -> Pipelin
|
||||
|
||||
// Assign the constant value to the variable
|
||||
working_set.set_variable_const_val(var_id, val);
|
||||
// working_set.add_constant(var_id, val);
|
||||
}
|
||||
Err(err) => working_set.error(err),
|
||||
}
|
||||
|
@ -2977,7 +2977,7 @@ pub fn parse_import_pattern(working_set: &mut StateWorkingSet, spans: &[Span]) -
|
||||
},
|
||||
members: vec![],
|
||||
hidden: HashSet::new(),
|
||||
module_name_var_id: None,
|
||||
constants: vec![],
|
||||
};
|
||||
|
||||
if spans.len() > 1 {
|
||||
|
Reference in New Issue
Block a user