cli: Use latest specified flag value when repeated (#15919)

# Description

This PR makes the last specified CLI arguments take precedence over the
earlier ones.

Existing command line tools that align with the new behaviour include:
- `neovim`: `nvim -u a.lua -u b.lua` will use `b.lua`
- `ripgrep`: you can have `--smart-case` in your user config but
override it later with `--case-sensitive` or `--ignore-case` (not
exactly the same flag override as the one I'm talking about but I think
it's still a valid example of latter flags taking precedence over the
first ones)

I think a flag defined last can be considered an override. This allows
having a `nu` alias that includes some default config (`alias nu="nu
--config something.nu"`) but being able to override that default config
as if using `nu` normally.
 
## Example

```sh
nu --config config1.nu --config config2.nu -c '$nu.config-path'
```
The current behavior would print `config1.nu`, and the new one would
print `config2.nu`

## Implementation

Just `.rev()` the iterator to search for arguments starting from the end
of the list. To support that I had to modify the return type of
`named_iter` (I couldn't find a more generic way than
`DoubleEndedIterator`).

# User-Facing Changes

- Users passing repeated flags and relying in nushell using the first
value will experience breakage. Given that right now there's no point in
passing a flag multiple times I guess not many users will be affected

# Tests + Formatting

I added a test that checks the new behavior with `--config` and
`--env-config`. I'm happy to add more cases if needed

# After Submitting
This commit is contained in:
Joaquín Triñanes
2025-06-13 01:23:38 +02:00
committed by GitHub
parent bd3930d00d
commit 12465193a4
2 changed files with 27 additions and 3 deletions

View File

@ -128,7 +128,8 @@ impl Call {
pub fn named_iter(
&self,
) -> impl Iterator<Item = &(Spanned<String>, Option<Spanned<String>>, Option<Expression>)> {
) -> impl DoubleEndedIterator<Item = &(Spanned<String>, Option<Spanned<String>>, Option<Expression>)>
{
self.arguments.iter().filter_map(|arg| match arg {
Argument::Named(named) => Some(named),
Argument::Positional(_) => None,
@ -222,7 +223,7 @@ impl Call {
}
pub fn get_flag_expr(&self, flag_name: &str) -> Option<&Expression> {
for name in self.named_iter() {
for name in self.named_iter().rev() {
if flag_name == name.0.item {
return name.2.as_ref();
}
@ -232,7 +233,7 @@ impl Call {
}
pub fn get_named_arg(&self, flag_name: &str) -> Option<Spanned<String>> {
for name in self.named_iter() {
for name in self.named_iter().rev() {
if flag_name == name.0.item {
return Some(name.0.clone());
}