Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Changing environment from inside a block #4771

Closed
ajeetdsouza opened this issue Mar 7, 2022 · 13 comments
Closed

Changing environment from inside a block #4771

ajeetdsouza opened this issue Mar 7, 2022 · 13 comments
Labels
enhancement New feature or request Stale used for marking issues and prs as stale

Comments

@ajeetdsouza
Copy link

Related problem

I need a way to conditionally cd into a directory. I need this to implement the z function in zoxide. I know that using def-env allows us to set environment variables from within a function, but how can I do this inside an if-else block?

Describe the solution you'd like

Perhaps an if-env keyword?

Describe alternatives you've considered

No response

Additional context and details

No response

@fdncred fdncred added the enhancement New feature or request label Mar 7, 2022
@fdncred
Copy link
Collaborator

fdncred commented Mar 7, 2022

I don't believe one can do this yet. that's what this PR is all about #4656 - @jntrnr is still considering if we should allow this or not.

@jmoore34
Copy link
Contributor

jmoore34 commented Mar 7, 2022

Here is a workaround I used for something similar:

def-env goto [] {
    let input = $in
    cd (
        if ($input | path type) == file {
            ($input | path dirname)
        } else {
            $input
        }
    )
}

@fdncred
Copy link
Collaborator

fdncred commented Mar 7, 2022

good point @jmoore34, as long as the if expression is inside of () part of the cd statement it should work. btw - i love this command jon. i use $nu.config-path | goto frequently.

@ajeetdsouza
Copy link
Author

I'm using that, but it only works when you are doing a cd in both cases. Otherwise, it would be something like:

cd (if $condition { "/tmp" } else { "$nu.cwd" })

This works great, but throws an extra error if the current directory does not exist (i.e. has been deleted).

@fdncred
Copy link
Collaborator

fdncred commented Mar 7, 2022

@ajeetdsouza i'm not sure how applicable is but do -i works a lot better now. this eats all those errors do -i { cd (if true { "c:\\tempz" } else { "$nu.cwd" }) }

@ajeetdsouza ajeetdsouza changed the title cd-ing from inside an if-else block Changing environment from inside a block Mar 7, 2022
@ajeetdsouza
Copy link
Author

I've updated the title to reflect my more general use case. For example, I'd like to be able to change an environment variable from within PROMPT_COMMAND. Currently, this appears to be impossible to do, but if support for this were added, zoxide would be able to use PWD hooks with Nushell.

@kubouch
Copy link
Contributor

kubouch commented Mar 8, 2022

@ajeetdsouza Can you give an example how you would use changing environment variables from inside PROMPT_COMMAND?

In general, I think it again comes to the design limitations of the language where we don't allow modifying a global scope. For example, scoped environment variables are necessary to be able to safely implement par-each.

However, we were thinking about implementing hooks when you enter a directory (and possibly other cases) so maybe that would help your use case? But that will be after 0.60.

@ajeetdsouza
Copy link
Author

However, we were thinking about implementing hooks when you enter a directory

This is exactly what I wanted to do. If I have hooks, I probably wouldn't need this feature. However, it seems inconsistent to me that the language allows something like def-env, but has no mechanism for defining environment variables in any other block (even when the block is within a def-env).

@kubouch
Copy link
Contributor

kubouch commented Mar 8, 2022

We're generally not keen on things modifying the outer scope, but you're right, it's not consistent. Theoretically, we could generalize it and have something like "env-block" so that def-env foo [] { ... } would become def foo [] {-env ... } (lol syntax). Such block could be reusable with if (is xyz {-env ... }) and other places where it's safe. Commands where it would be unsafe (par-each) or weird (each, for) would just disallow the "env blocks". Whether it's a good idea is a question but theoretically it seems possible.

@71
Copy link

71 commented Mar 24, 2022

I was bitten by the same issue (def-env not propagating to inner blocks). No matter what the solution is, I believe it would be good to document this limitation and/or show warnings when let-env (or friend) is used in an inner block of a def-env function.

I found a simple workaround (though not ideal, as it can significantly alter how a function looks, especially since variables are immutable): build a record of environment variable entries in the function, and then call load-env $record at the top level of the function. Something like

let new-env = if $foo { {FOO: 1} } else { {} }

load-env $new-env

@kubouch
Copy link
Contributor

kubouch commented Mar 24, 2022

@71 Currently, we follow a simple procedure: all blocks are scoped, except def-env which preserves the environment. That's a relatively simple rule but we've noticed people bumping into this and we're looking into ways to improve it.

I'd personally lean towards removing def-env and making it possible to mark any block as env-preserving, so you could make your own def-env alternative, but also have to option to make if/else (and other) blocks preserve the environment with some distinct syntax. If we started special-casing if to preserve the environment or making it dependent on whether it is inside def-env or not, it would create a lot of implicit rules and make the mental model more complicated.

@71
Copy link

71 commented Mar 24, 2022

@kubouch I understand this, but since this requires some design discussion I commented to ask for a temporary improvement (warning about why def-env does not propagate to inner blocks) and to suggest a workaround (using load-env).

@github-actions github-actions bot added the Stale used for marking issues and prs as stale label Jan 17, 2023
@sophiajt
Copy link
Contributor

This should be quite a bit easier now.

Blocks can update the environment (closures can not). Commands like if and for use blocks, so you can do:

if true {
  $env.FOO = "BAR"
}

And the environment inside the function will update. Using that in combination with def-env will give you a command that can conditionally update the environment.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request Stale used for marking issues and prs as stale
Projects
None yet
Development

No branches or pull requests

6 participants