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

Config plugin specific settings right away after loading it #84

Closed
zoritle opened this issue Nov 10, 2021 · 10 comments · Fixed by #96
Closed

Config plugin specific settings right away after loading it #84

zoritle opened this issue Nov 10, 2021 · 10 comments · Fixed by #96
Labels
enhancement New feature or request

Comments

@zoritle
Copy link

zoritle commented Nov 10, 2021

Many plugins need specific configurations, bug zgenom can't determine them.
For example:

if ! zgenom saved; then
    echo "Creating a zgenom save"
    zgenom load zsh-users/zsh-history-substring-search
    bindkey '^[[A' history-substring-search-up
    bindkey '^[[B' history-substring-search-down
    zgenom save
fi

the bindkey lines didn't appear in the generated init.zsh file, thus need to be written elsewhere,separate coupled code into different places makes them harder to maintain.

Being able to config plugin settings just after loading it is more intuitive, and easier to add/remove/debug them.
Maybe this can be done by a command to construct and load an "inline plugin", like

zgenom eval "bindkey '^[[A' history-substring-search-up"'

or with heredoc

zgenom eval <<EOP
    bindkey '^[[A' history-substring-search-up
    bindkey '^[[B' history-substring-search-down
EOP

Even better, just write normal zsh statements without any special syntax,and let zgenom automatically add these statements to the generated init.zsh file as is.

@jandamm
Copy link
Owner

jandamm commented Nov 10, 2021

While I like this idea and use it myself in my neovim config I wouldn't see this feature in the scope of zgenom.

However it would be a perfect match for an extension.

If you don't expect the config to appear in the init.zsh right next to the plugin but at the end of the file it would be quite simple to implement as well.

Would you be interested in writing such an extension yourself?

@zoritle
Copy link
Author

zoritle commented Nov 11, 2021

If you don't expect the config to appear in the init.zsh right next to the plugin but at the end of the file it would be quite simple to implement as well.

This would be ok for most simple cases but there are many plugins need some environment variables or other configs to be set before loading them, and also might lead to other unexpected behaviors if the order can't be preserved.

However it's still possible to implement without messing up with zgenom internals by construct an ad-hoc plugin and call zgenom load to loading it, though it's not really 'inlined', simply:

# heredoc only
zgenom-eval() {
    setopt errreturn
    local dir temp name
    dir=$(zgenom api clone_dir inline)
    temp=$(mktemp)
    tee "$temp" >/dev/null
    name="$(sha1sum "$temp" | cut -c 1-7).zsh"
    mv "$temp" "$dir"/"$name"
    zgenom load inline "$name"
}

This is also useful in conjunction with some tools written in other languages, like

zgenom eval <<(starship init zsh --print-full-init)
zgenom eval <<(broot --print-shell-function zsh)
zgenom eval <<(zoxide init zsh)

or some commands generate completion functions by themself.

So perhaps it's also worth to build into zgenom with proper inline and cleanup.

This way is still not as simple and intuitive as writing normal zsh statement,what about writing all these in a separate file like.zgenomrc and get that file sourced by zgenom instead zsh itself, and zgenom replaces all the loading statements with real output to construct the generated init.zsh, do you think this is possible to implement?

@jandamm
Copy link
Owner

jandamm commented Nov 11, 2021

If a deterministic name is created zgenom could be told to not clean up this file while it's used. (Adding the path to ZGENOM_PLUGINS prevents this. see here)

Even within zgenom the order of all commands isn't stored.
While the order of load is stored (actually the order of files) it's not clear what other zgenom calls happened in between.
So the idea create local plugins would be working quite well since those files could be added to the plugin list.

In theory a .zgenomrc could be read line by line, evaluated and then written to the init file.


I see three versions:

  1. zgenom eval [before|after|apply] where all generated lines are stored in memory and applied by pre/appending to the init.zsh
  2. wrap zgenom load somehow to know when what is loaded and modify the sourced file of the plugin. (Config, source plugin, config)
  3. zgenom eval where the output is stored in a file and zgenom load is used to add it as local plugin.

I don't think that 2. can be achieved in a nice way.

The 1. is a bit clunky and in the end the order is only partially restored since it happens before or after all plugins and not in between plugins.

@zoritle
Copy link
Author

zoritle commented Nov 16, 2021

My apologies for the late reply.

If a deterministic name is created zgenom could be told to not clean up this file while it's used.
Isn't zgenom load already added the path to ZGENOM_PLUGINS? The problem seems to be opposite that generated files never get cleaned.

I think processing .zgenomrc and replacing every line begin with zgenom with its output is possible, but is complicated and hard to deal with every corner cases, which will lead to unexpected behavior.

But I have another idea: If we can't use vanilla syntax for custom statements, how about doing it in other way around, keep custom statements as is but use special syntax for loading plugins. We can templating the init.zsh file, for example in zgenomrc:

# write what ever here will be preserved as is in the init.zsh file
export SOMETHING_TO_SET=1
# will expanded to concrete plugin loading statement
$(zgenom load zsh-users/zsh-history-substring-search)

# for just time comsuming tasks without evaluating anything
$(rbenv rehash > /dev/null)

bindkey '^[[A' history-substring-search-up
bindkey '^[[B' history-substring-search-down

And this will become much simpler to implement, since it's just ordinary "shell template". Or more over, to disambiguate with shell syntax, there are other templating engine available in shell, like https://github.com/jirutka/esh.

@jandamm
Copy link
Owner

jandamm commented Nov 16, 2021

Isn't zgenom load already added the path to ZGENOM_PLUGINS? The problem seems to be opposite that generated files never get cleaned.

It is. The zgenom eval would have to create $ZGEN_DIR/evals/name/init.zsh since the deletion happens on a per folder basis. Putting all files in $ZGEN_DIR/evals prevents evals to be deleted until there is no eval statement anymore.

As for your idea with the "vanilla syntax", while this might solve some of the issues it sounds like a lot of added complexity for something that would probably be a niche feature.

# will expanded to concrete plugin loading statement
$(zgenom load zsh-users/zsh-history-substring-search)

This would load the shell into the current session but wouldn't add anything to the init file. So it would either be slow or the whole zgenom save would have to be rewritten.

@jandamm
Copy link
Owner

jandamm commented Nov 16, 2021

Please try out this version:

zgenom-eval() {
    setopt errreturn
    local dir temp name
    temp=$(mktemp)
    tee "$temp" >/dev/null
    name="$(sha1sum "$temp" | cut -c 1-7)"
    repo="inline/$name"
    dir=$(zgenom api clone_dir $repo)
    [[ ! -e $dir ]] && mkdir -p $dir
    mv "$temp" "$dir"/init.zsh
    zgenom load $repo
}

Now everything should be cleaned up correctly.

@zoritle
Copy link
Author

zoritle commented Nov 16, 2021

I mean using zgenomrc as a template for the generated init.zsh.

This may need some refactor of zgenom-load and zgenom-save to rewrite loading logic:

# in zgenom-load
ZERO="${source_file}" source "${source_file}"
# in zgenom-save
__zgenom_write "ZERO=${(qqq)file} source ${(qqq)file}"

to something roughly:

__source_plugin(){
    # figure out how to source a plugin
    local output='ZERO="${source_file}" source "${source_file}"'
    if [[ -n "${ZGENOM_RUNNING_IN_SAVE}" ]]; then
        echo "$output"
    else
        eval "$output"
    fi
}

# in zgenom-load
__source_plugin $plugin
# in zgenom-save
ZGENOM_RUNNING_IN_SAVE=1
...
__source_plugin $plugin

same applied for fpath and prezto stuff.

And then replace only the loading part in zgenom-save with user provided template:

__zgenom_write "$(expand-template .zgenomrc)"

naively expand-template implementation, maybe better just use https://github.com/jirutka/esh

expand-template(){
    local name=$1 temp
    temp=$(mktemp)
    echo "cat <<EOF" >> $temp
    cat $name >> $temp
    echo "" >> $temp
    echo "EOF" >> $temp
    source $temp
}

zgenomrc:

$(zgenom load zsh-users/zsh-history-substring-search)
bindkey '^[[A' history-substring-search-up
bindkey '^[[B' history-substring-search-down
#$(rbenv rehash && echo "rehashed")

expanded to init.zsh:

# head added by zgenom
...
zsh_loaded_plugins+=( "zsh-users/zsh-history-substring-search" )
ZERO="zsh-users/zsh-history-substring-search/___/zsh-history-substring-search.plugin.zsh" source "zsh-history-substring-search/___/zsh-history-substring-search.plugin.zsh"
bindkey '^[[A' history-substring-search-up
bindkey '^[[B' history-substring-search-down
#rehashed
...
# foot added by zgenom

Not very complicated, but it's different from current approch of zgenom.

@zoritle
Copy link
Author

zoritle commented Nov 16, 2021

Please try out this version:

zgenom-eval() {
    setopt errreturn
    local dir temp name
    temp=$(mktemp)
    tee "$temp" >/dev/null
    name="$(sha1sum "$temp" | cut -c 1-7)"
    repo="inline/$name"
    dir=$(zgenom api clone_dir $repo)
    [[ ! -e $dir ]] && mkdir -p $dir
    mv "$temp" "$dir"/init.zsh
    zgenom load $repo
}

Now everything should be cleaned up correctly.

Yes this worked very well. In my first comment I'm feeling a little overwhelm to create a whole repo for just a snippet so I used single file, but that's indeed OK, a folder is not really weight.

@jandamm
Copy link
Owner

jandamm commented Nov 16, 2021

With complexity I didn't necessarily hard to implement but it's a completely different api from the existing one.
Adding two different methods to initialize also adds additional complexity.
This would definitely something I'd have a look on when creating a new plugin manager.

For the existing api of zgenom I find zgenom eval a far better match. It's one additional function which can live in an extension and none of the existing zgenom code needs to change.

@jandamm jandamm added the enhancement New feature or request label Nov 23, 2021
@jandamm
Copy link
Owner

jandamm commented Nov 24, 2021

Please check out https://github.com/jandamm/zgenom-ext-eval.
I'll update the readme of zgenom later and I'll add a link then as well.

jandamm added a commit that referenced this issue Dec 1, 2021
This allows

```zsh
zgenom load zsh-users/zsh-history-substring-search
zgenom eval <<EOF
bindkey '^[[A' history-substring-search-up
bindkey '^[[B' history-substring-search-down
EOF

zgenom eval --name zoxide <<(zoxide init zsh)
```

Closes #84
jandamm added a commit that referenced this issue Dec 1, 2021
This allows

```zsh
zgenom load zsh-users/zsh-history-substring-search
zgenom eval <<EOF
bindkey '^[[A' history-substring-search-up
bindkey '^[[B' history-substring-search-down
EOF

zgenom eval --name zoxide <<(zoxide init zsh)
```

Closes #84
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants