Enable conditional source and use patterns by allowing null as a no-op module (#14773)

Related:
- #14329
- #13872
- #8214

# Description & User-Facing Changes

This PR allows enables the following uses, which are all no-op.
```nushell
source null
source-env null
use null
overlay use null
```

The motivation for this change is conditional sourcing of files. For
example, with this change `login.nu` may be deprecated and replaced with
the following code in `config.nu`
```nushell
const login_module = if $nu.is-login { "login.nu" } else { null }
source $login_module
```

# Tests + Formatting
I'm hoping for CI to pass 😄

# After Submitting
Add a part about the conditional sourcing pattern to the website.
This commit is contained in:
Bahex
2025-01-09 15:37:27 +03:00
committed by GitHub
parent 5cf6dea997
commit 79f19f2fc7
7 changed files with 160 additions and 25 deletions

View File

@ -2367,18 +2367,37 @@ pub fn parse_use(
let import_pattern_expr = parse_import_pattern(working_set, args_spans);
let import_pattern = if let Expression {
expr: Expr::ImportPattern(import_pattern),
..
} = &import_pattern_expr
{
import_pattern.clone()
} else {
working_set.error(ParseError::UnknownState(
"internal error: Import pattern positional is not import pattern".into(),
import_pattern_expr.span,
));
return (garbage_pipeline(working_set, spans), vec![]);
let import_pattern = match &import_pattern_expr {
Expression {
expr: Expr::Nothing,
..
} => {
let mut call = call;
call.set_parser_info(
"noop".to_string(),
Expression::new_unknown(Expr::Nothing, Span::unknown(), Type::Nothing),
);
return (
Pipeline::from_vec(vec![Expression::new(
working_set,
Expr::Call(call),
Span::concat(spans),
Type::Any,
)]),
vec![],
);
}
Expression {
expr: Expr::ImportPattern(import_pattern),
..
} => import_pattern.clone(),
_ => {
working_set.error(ParseError::UnknownState(
"internal error: Import pattern positional is not import pattern".into(),
import_pattern_expr.span,
));
return (garbage_pipeline(working_set, spans), vec![]);
}
};
let (mut import_pattern, module, module_id) = if let Some(module_id) = import_pattern.head.id {
@ -2755,6 +2774,19 @@ pub fn parse_overlay_use(working_set: &mut StateWorkingSet, call: Box<Call>) ->
let (overlay_name, overlay_name_span) = if let Some(expr) = call.positional_nth(0) {
match eval_constant(working_set, expr) {
Ok(Value::Nothing { .. }) => {
let mut call = call;
call.set_parser_info(
"noop".to_string(),
Expression::new_unknown(Expr::Bool(true), Span::unknown(), Type::Bool),
);
return Pipeline::from_vec(vec![Expression::new(
working_set,
Expr::Call(call),
call_span,
Type::Any,
)]);
}
Ok(val) => match val.coerce_into_string() {
Ok(s) => (s, expr.span),
Err(err) => {
@ -3494,6 +3526,20 @@ pub fn parse_source(working_set: &mut StateWorkingSet, lite_command: &LiteComman
}
};
if val.is_nothing() {
let mut call = call;
call.set_parser_info(
"noop".to_string(),
Expression::new_unknown(Expr::Nothing, Span::unknown(), Type::Nothing),
);
return Pipeline::from_vec(vec![Expression::new(
working_set,
Expr::Call(call),
Span::concat(spans),
Type::Any,
)]);
}
let filename = match val.coerce_into_string() {
Ok(s) => s,
Err(err) => {

View File

@ -15,7 +15,7 @@ use nu_engine::DIR_VAR_PARSER_INFO;
use nu_protocol::{
ast::*, engine::StateWorkingSet, eval_const::eval_constant, BlockId, DeclId, DidYouMean,
FilesizeUnit, Flag, ParseError, PositionalArg, Signature, Span, Spanned, SyntaxShape, Type,
VarId, ENV_VARIABLE_ID, IN_VARIABLE_ID,
Value, VarId, ENV_VARIABLE_ID, IN_VARIABLE_ID,
};
use std::{
collections::{HashMap, HashSet},
@ -3054,6 +3054,14 @@ pub fn parse_import_pattern(working_set: &mut StateWorkingSet, spans: &[Span]) -
let head_expr = parse_value(working_set, *head_span, &SyntaxShape::Any);
let (maybe_module_id, head_name) = match eval_constant(working_set, &head_expr) {
Ok(Value::Nothing { .. }) => {
return Expression::new(
working_set,
Expr::Nothing,
Span::concat(spans),
Type::Nothing,
);
}
Ok(val) => match val.coerce_into_string() {
Ok(s) => (working_set.find_module(s.as_bytes()), s.into_bytes()),
Err(err) => {