Search code examples
fish

Passing 'out' variables to function


I'm trying to make my fish_prompt fancier by having the colors of my username and host change depending on what they are. However, if this attempt fails, I'd rather leave the colors as defaults of $fish_color_[host|user] So far, this works:

function fish_prompt
    # …
    set -l color_host $fish_color_host
    if command -qs shasum
        set color_host (colorize (prompt_hostname))
    end
    # …
    echo -n -s \
        (set_color --bold $color_user)      "$USER" \
        (set_color normal)                  @ \
        (set_color $color_host)             (prompt_hostname) \
        ' ' \
        (set_color --bold $color_cwd)       (prompt_pwd) \
        (set_color normal)                  " $suffix "
end

function colorize
    echo -n (printf "$argv" | shasum -ta 256 - | cut -c 59-64 | tr -d '\n')
end

At this point, I thought that this would look a lot cleaner if the shasum check were in colorize instead of being repeated in fish_prompt. I then tried this, figuring I might be able to pass the variable name if I single quoted it:

function fish_prompt
    # …
    set -l color_user $fish_color_user
    colorize_more 'color_user' "$USER"
    # …
end

function colorize_more -a what -a whence
    if command -qs shasum
        set "$what" (printf "$whence" | shasum -ta 256 - | cut -c 59-64 | tr -d '\n')
    end
end

However, this doesn't appear to change $color_user in fish_prompt. I suspect that's because the set in colorize_more can't modify a variable that's local to the one in fish_prompt, as the color that should come out of colorize_more is a yellow instead of the green that I get from $fish_color_user.

How can I restructure my fish_prompt and accessory function(s) so that I have a colorize function that sets a variable to some value only if it is able to?


Solution

  • There are multiple possibilities here.

    One is the "--no-scope-shadowing" flag to function

    function colorize_more -a what -a whence --no-scope-shadowing
    

    that will keep the outer local scope visible. This won't work with e.g. $argv, and if you need any variables they could potentially overwrite externally defined ones, so this is to be used with care.

    Another is to define the variables as global instead - this has the disadvantage that they'll then stay defined and will conflict with any global variable of the same name.

    Another is to output the value instead of assigning it.

    E.g.

    function colorize_more -a whence
        if command -qs shasum
            echo (printf "$whence" | shasum -ta 256 - | cut -c 59-64 | tr -d '\n')
        end
    end
    
    set -l color_user (colorize_more $USER)
    set -q color_user[1]; or set color_user $fish_color_user
    # or you could let colorize_more output $fish_color_user if it wouldn't output anything else