nushell / nushell

A new type of shell
https://www.nushell.sh/
MIT License
30.5k stars 1.57k forks source link

Conditional source #8214

Open danjenson opened 1 year ago

danjenson commented 1 year ago

Describe the bug

I'd like to source additional nushell files (i.e. local configs) when they exist. However, the following doesn't work

def source-if-exists [path: string] {
  if ($path | path expand | path exists) {
    source $path
  }
}

I get an error saying that $path is not a parse-time constant. On the other hand, if I try the simpler approach

if ("~/some/path" | path expand | path exists) {
  source ~/some/path
}

Then it only sources those definitions inside of the if block. And if I do source-env, nothing happens to either environment (inside or outside the if block).

How to reproduce

  1. Try to conditionally source a nu file in your config.nu

Expected behavior

You could conditionally source files.

Screenshots

No response

Configuration

key value
version 0.76.0
branch
commit_hash
build_os linux-x86_64
build_target x86_64-unknown-linux-gnu
rust_version rustc 1.67.0-nightly (215e3cd21 2022-11-03)
rust_channel nightly-x86_64-unknown-linux-gnu
cargo_version cargo 1.67.0-nightly (7e484fc1a 2022-10-27)
pkg_version 0.76.0
build_time 2023-02-23 20:30:06 -08:00
build_rust_channel release
features database, dataframe, default, trash, which, zip
installed_plugins

Additional context

No response

kubouch commented 1 year ago

You won't be able to source a dynamic path passes as a command argument. However, we were thinking about expanding parse-time evaluation, so you'd be able to do the following (the path would need to be a constant, not passed as a command argument):

const path = "~/some/path" 
const if ($path| path expand | path exists) {
  source $path
}

Would this work for you?

In your example

if ("~/some/path" | path expand | path exists) {
  source ~/some/path
}

if the path exists, it will be parsed anyway, and if not, you'll get a parsee error at source. Currently, the if check is redundant.

Nushell behaves differently from other shell/scripting languages, it works more like a compiled language, see also https://www.nushell.sh/book/thinking_in_nu.html#think-of-nushell-as-a-compiled-language, https://www.nushell.sh/book/how_nushell_code_gets_run.html

danjenson commented 1 year ago

I think the first would work well enough for me. And yea the problem with the second example is that if source fails, it doesn't run the rest of my config, since these lines are at the top (I suppose I could move them to the end).

fj0r commented 1 year ago

test this:

def dynamic_load [] {
    [
        {
            condition: {|before, after| (not ('dynamic-load' in (overlay list))) and ('~/.nu' | path exists) }
            code: "overlay use ~/.nu as dynamic-load"
        }
    ]
}

export-env {
    let-env config = ( $env.config | upsert hooks.env_change.PWD { |config|
        let o = ($config | get -i hooks.env_change.PWD)
        let val = (dynamic_load)
        if $o == $nothing {
            $val
        } else {
            $o | append $val
        }
    })
}
ryan4yin commented 10 months ago
const path = "~/some/path" 
const if ($path| path expand | path exists) {
  source $path
}

This will solve the problem I have now, looking forward to seeing this feature implemented.

marcelarie commented 8 months ago

anyone here knows how it would work on something like this:

ls ~/clones/external/nu_scripts/custom-completions/ | each { |file|
  if $file.type == "dir" {
    use $"($file.name)/(basename $file.name)-completions.nu"
  }
}
kubouch commented 8 months ago

https://github.com/nushell/nushell/pull/10326 will allow a sort-of conditional use or source, e.g., use (if $spam { 'file1.nu' } else { 'file2.nu' }).

xie-zheng commented 6 months ago

anyone here knows how it would work on something like this:

ls ~/clones/external/nu_scripts/custom-completions/ | each { |file|
  if $file.type == "dir" {
    use $"($file.name)/(basename $file.name)-completions.nu"
  }
}

i want to know how to achieve this one too!!!

amtoine commented 6 months ago

@xie-zheng to achieve this, you'd need the argument of use to be a parse-time constant. however, the output of ls can never be a parse-time constant, so that wouldn't work :thinking:

what you could do though is the following

const CUSTOM_COMPLETIONS = [cargo, git, npm]
const CUSTOM_COMPLETIONS_DIR = ("~/clones/external/nu_scripts/custom-completions/" | path expand)

for completion in $CUSTOM_COMPLETIONS {
    use $"($CUSTOM_COMPLETIONS_DIR)/($completion)/($completion)-completions.nu"
}

just not sure about use in a for loop, that might not work :eyes:

xie-zheng commented 6 months ago

@xie-zheng to achieve this, you'd need the argument of use to be a parse-time constant. however, the output of ls can never be a parse-time constant, so that wouldn't work πŸ€”

what you could do though is the following

const CUSTOM_COMPLETIONS = [cargo, git, npm]
const CUSTOM_COMPLETIONS_DIR = ("~/clones/external/nu_scripts/custom-completions/" | path expand)

for completion in $CUSTOM_COMPLETIONS {
    use $"($CUSTOM_COMPLETIONS_DIR)/($completion)/($completion)-completions.nu"
}

just not sure about use in a for loop, that might not work πŸ‘€

     ╭─[/Users/xiezheng/Library/Application Support/nushell/config.nu:547:1]
 547 β”‚ const CUSTOM_COMPLETIONS = [cargo, git, glow]
 548 β”‚ const CUSTOM_COMPLETIONS_DIR = ("~/.config/nu_scripts/custom-completions/" | path expand)
     Β·                                ─────────────────────────────┬────────────────────────────
     Β·                                                             ╰── Value is not a parse-time constant
 549 β”‚
     ╰────
  help: Only a subset of expressions are allowed constants during parsing. Try using the 'const' command or typing the value literally.

I thought it would work, but it didn't, sad story...

amtoine commented 6 months ago

@xie-zheng what version of Nushell are you using? you might want to update because that line 548 works on my side with 0.87.1 :yum:

xie-zheng commented 6 months ago
const CUSTOM_COMPLETIONS = [cargo, git, npm]
const CUSTOM_COMPLETIONS_DIR = ("~/clones/external/nu_scripts/custom-completions/" | path expand)

for completion in $CUSTOM_COMPLETIONS {
    use $"($CUSTOM_COMPLETIONS_DIR)/($completion)/($completion)-completions.nu"
}

ok, tried 0.87.1 and both source and use. don't know how can we achieve variable using in source or use

Error:   Γ— Error: nu::shell::not_a_constant
  β”‚
  β”‚   Γ— Not a constant.
  β”‚      ╭─[/Users/xiezheng/Library/Application Support/nushell/config.nu:550:1]
  β”‚  550 β”‚ for completion in $CUSTOM_COMPLETIONS {
  β”‚  551 β”‚     source $"($CUSTOM_COMPLETIONS_DIR)/($completion)/($completion)-completions.nu"
  β”‚      Β·            ───────────────────────────────────┬───────────────────────────────────
  β”‚      Β·                                               ╰── Value is not a parse-time constant
  β”‚  552 β”‚ }
  β”‚      ╰────
  β”‚   help: Only a subset of expressions are allowed constants during parsing. Try using the 'const' command or typing
  β”‚ the
  β”‚         value literally.
  β”‚
     ╭─[/Users/xiezheng/Library/Application Support/nushell/config.nu:550:1]
 550 β”‚ for completion in $CUSTOM_COMPLETIONS {
 551 β”‚     source $"($CUSTOM_COMPLETIONS_DIR)/($completion)/($completion)-completions.nu"
     Β·            ───────────────────────────────────┬───────────────────────────────────
     Β·                                               ╰── Encountered error during parse-time evaluation
 552 β”‚ }
     ╰────
kubouch commented 6 months ago

You shouldn't call use in a for loop because definitions are scoped in Nushell, see https://www.nushell.sh/book/thinking_in_nu.html#think-of-nushell-as-a-compiled-language. It's better to organize custom completions as a module with mod.nu where you re-export all the completions you want (see https://www.nushell.sh/book/modules.html#modules-from-directories).

giggio commented 4 weeks ago

Has anyone found a workaround? I am now thinking of "building" my .nu files, running some process to exclude them from being sourced. Any other options? My problem is that I use the same config files for Windows, Linux (native) and Linux on WSL.

9999years commented 4 weeks ago

@giggio What if you were to (e.g.) unconditionally source nix.nu, and then at the start of nix.nu you check if Nix is available, and if it's not you exit early?

giggio commented 4 weeks ago

@giggio What if you were to (e.g.) unconditionally source nix.nu, and then at the start of nix.nu you check if Nix is available, and if it's not you exit early?

@9999years I tried doing that here: https://github.com/giggio/nuscripts/blob/main/scripts/alias-wsl.nu It does not work. In this case, code is always aliased, even though I'm not on WSL right now.

This used to work some versions back, it does not work anymore.

wthueb commented 2 weeks ago

Are the changes from #10326 supposed to have fixed this? I'm on v0.92.1, and the following doesn't work properly (the file is attempted to be sourced whether or not it exists).

if ("~/.config/nushell/config.custom.nu" | path expand | path exists) {
    source "~/.config/nushell/config.custom.nu"
}
kubouch commented 2 weeks ago

sourced definitions are scoped like everything else in Nushell, so you need to do source (if ... { some_file.nu } else { empty_file.nu }).