Open ww7 opened 6 years ago
There is plenty of room to config prezto in the files already provided for startup, it’s just a matter to place your overrides in the appropriate place. most of them should probably go in ${ZDOTDIR:-$HOME}/.zshrc
before invoking zprezto init, to choose the modules that should get loaded, using the appropriate zstyle
calls (that admittedly, are sparsely documented). so here is my attempt at making the documentation a bit better. (I'm also submitting a PR with this explanation, hopefully the team will find it useful)
(For our new users, the notation ${ZDOTDIR:$HOME}
means that f the variable $ZDOTDIR
is not set, it should default to the value of $HOME
. its like a short-circuit for an IF statement testing if a variable is set. thus, source ${ZDOTDIR:-$HOME}/.zshenv
is equivalent to:
if [[ -n $ZDOTDIR ]]; then
DOTFILES_PATH=$ZDOTDIR
else
DOTFILES_PATH=$HOME
fi
source $DOTFILES_PATH/.zshenv
#pro tip! set $ZDOTDIR on /etc/zshenv to ~/.zconf to have all runcoms live there instead of cluttering ~ =)
Also, any tweaks to prezto’s behavior should go after invoking zprezto init in zshrc or, in ${ZDOTDIR:-$HOME}/.zlogin
. Note however that the zlogin
(sysem or user) files will be sourced only for login shells. That means, when logging in to a remote system via ssh for example or when calling say, XTerm
or your favorite emulator by specifying you want a login shell (by calling zsh -ls
instead of plain zsh
). Otherwise, the zlogin
files are ignored.
Also, note that another difference between interactive login shells and plain interactive shells, is that, besides sourcing zlogin
files, interactive login shells also allocate a pseudo-tty on the kernel and are session leaders. (programs you run from that session will be subprocesses of the session leader. also, if for some reason you kill the session leader, its children die with it, or become zombies ) so its a trade-off specially on systems where several users login to the same machine that may have limited pseudo-TTYs available. this kind of workload used to be more common. (nowdays most Linux systems are multi-user but single-operator)
#Pro tip: configure your window manager to call zsh -ls for terminals.
#Example using i3wm on ubuntu:
#i3wm.conf:
...
set $XTERM_CMD='xterm -e zsh -ls`
bind $mod+x $XTERM_CMD
...
Why is that? because login shells are assumed to be the main point of interaction with a possibly human user (the truth is out there) while non login shells would be spawned as sub-shells of a login shell , when executing scripts that call zsh
as a command interpreter, for example, when the shebang is #!/usr/bin/env zsh
or equivalent.
When launching a new terminal within an X11 session, it is safe to assume that you are already logged in so any terminal emulator will launch an interactive-non-login shell. Which is, per specification the correct behavior, but not the behavior most users expect.
There is a reason for this specified behaviour: zlogin
files should be used for interaction oriented settings such as custom completion, keybindings, tmux
session setup, and most alias
es.
The advantage of grouping all interaction oriented settings in .zlogin
is that non-interactive shells (executing scripts via zsh -c [script]
, the shebang, subshells or make
invoked shells, for example) will not be burdened by additional config settings making them faster and more responsive, as keybindings and aliases are not even loaded at startup, after all, an executable non-interactive script, should never call on keybindings or aliases (assuming they were properly written to be portable)
Finally, it is a common source of frustration amongst graphical environment users, to spend some time adding their customizations to .zlogin
only to see them ignored when launching a new XTerm
or tmux
pane.
This can be solved by calling (or aliasing) your terminal emulator with the required option to invoke a login shell, for XTerm
, for example you would call xterm -e zsh -ls
on tmux you may add one of the following lines to your .tmux.conf
:
set -g default-command 'exec /usr/bin/zsh -ls' #this will make zsh a login shell AND a session leader
set -g default-shell '/usr/bin/zsh -ls'
if you want to replace your non-login shell on your emulator with a brand-new shiny login shell you can issue exec zsh -ls
at your command prompt. Doing this, however will not write that terminal’s history to file unless INC_APPEND_HISTORY
was set when you strted the shell
INC_APPEND_HISTORY
makes zsh
append history entries as soon as the command is finished. zprezto helpfuly sets INC_APPEND_HISTORY
, INC_APPEND_HISTORY_TIME
, and SHARE_HISTORY
which makes your history available immediately from different terminals on command completion, and records execution time on the history file. Pretty neat, huh?)
The opposite mistake is to tack-on all interactive customizations on zshrc
which leads to bloated shells on non-interactive environments. Perhaps desktop workstation users will not notice any difference, but in resource-constrained environments it may be significant in terms of execution time and energy consumption (v.gr when running a Raspberry PI on batteries or on a cell phone environment such as termux
)
here is a summary of the startup file and their intended purpose, as well as when are they executed:
always runs | when GLOBAL_RCS is set |
when RCS is set |
Purpose | stuff that usually goes there, and notes |
---|---|---|---|---|
/etc/zshenv |
system provided minimal environment | Always runs! should be minimal set ZDOTDIR, minimal environment. system paths | ||
${ZDOTDIR:-$HOME}/.zshenv |
user environment overrides non-interactive, non-login shells (when invoked via shebang in a script or via zsh -c [script] or when invoked by make |
personal environment overrides, such as MANPATH , TERM , fpath non interactive scripts should have their environment completly set up here ) |
||
/etc/zshprofile |
system profile for login shells (zsh -ls zsh - or via _ssh forced command_ ) |
minimal system paths for remote interaction, site fpath , site $LOCALE , lang. |
||
${ZDOTDIR:-$HOME}/.zshprofile |
personal preferences for interactive login shells | your preferred $LOCALE , $LANG , readlne config, cdpath , session managers, additional fpath ) login shells (but non necessarily ineractive as when invoked via ssh remote cmd ) will read config up to this point |
||
/etc/zshrc |
system provided startup script, for interactive shells, (local shells that live in an XTerm , URxvt , gnome-terminal ) or subshells (like running xterm from the command prompt of a login xterm, |
site login accounting, security monitors, site command logging policy | ||
${ZDOTDIR:-$HOME}/.zshrc |
user customization of their interactive environment for terminals | sourcing custom completions (gcloud.comp.inc ), custom user frameworks (zprezto is invoked here), additional path required by custom software installed in /opt/* |
||
/etc/zshlogin |
additional customization for interactive login shells, such as those accessed via ssh | tmux attach to existing session | ||
${ZDOTDIR:-$HOME}/.zshlogin |
user script for login shell startup (this shell is a session leader, and allocates a ptty |
aliases, keybindings, personal startup programs, time tracking apps, session managers, quote of the day, fortunes | ||
.. | ... your shell session happens here ... | |||
${ZDOTDIR:-$HOME}/.zshlogout |
personal cleanup tasks | setting personal crontabs or at tasks, logging checout to your time tracking software, fortune to say goodbye. |
||
/etc/zshlogout |
site cleanup tasks | site command logging policy stop, login accounting records, security context spindown, etc |
you may be thinking... why is zpreztorc
not invoked on .zlogin
instead of .zshrc
? I believe it was a trade-off to eliminate complexity by having the whole of prezto configured and launched from a single point, as some of the modules need to be sourced early in the startup process, editor
and gnu-utls
come to mind. if invoked later on the startup, such as would be the case in .zlogin
would makee it easier to conflict with stuff users may add on zshrc
. Also, it avoids *not being loaded and causing user frustration if the user launches non-login shells from graphical environments, as discussed above.
use your .zshrc after zprezto has been invoked, or before to remove or activate packages via zstyle
calls
or
use .zshlogin to override or tweak zprezto behavior. there is also .zpreztorc
but you may want to leave that one alone.
In any case, this question is often brought up by users that use the distributed runcoms as is which is a terrible idea, since doing git pull
to update your distribution will clobber your customizations. it’s a better idea to copy the distributed runcoms to your $ZDOTDIR
and merge any changes an update may bring, if any (they are usually confined to .zpreztorc
)
I forgot to mention that by default, on stock zsh
both RCS
and GLOBAL_RCS
are both set out of the box, so the sequence above is what you'd usually expect on a fresh install.
Personally, i find that debian-derived Linux distributions set up a bunch of un-needed stuff and indirections (hey, i'm a plain BSD guy since puberty). Some of it is nice, like the additional fpath
to the site functions directories that add completions and menu-completions to almost every command you may come up with.
So short-circuit the nonsense by copying the good stuff from /etc/zsh/zshrc
into my ~/.zshrc
and then
unsetting GLOBAL_RCS
at the end of my ~/.zshenv
which has the effect of skipping the system /etc/zsh/{zprofile,zshrc,zlogin}
and only sourcing ${ZDOTDIR:-$HOME}/{.zprofile,.zshrc,.zlogin}
in that order.
i don't reccomend this unles you really know what you're doing, you may end up with an unusable configuration.
In any case, this question is often brought up by users that use the distributed runcoms as is which is a terrible idea, since doing
git pull
to update your distribution will clobber your customizations. it’s a better idea to copy the distributed runcoms to your$ZDOTDIR
and merge any changes an update may bring, if any (they are usually confined to.zpreztorc
)
If I understand the upgrade process correctly, I am supposed to fork this project then deal with merge conflicts every time I update??? That's insane!
Once I set my configuration I expect updates to just work like they do in all other programs.
Why can't runcoms do their thing and then source local files like $ZDOTDIR/.zshrc_local
?
These local files should be able to override everything. Perhaps prezto should only mark modules for inclusion until last config file is loaded and ended and then include everything. That way I can have in default zprezto.rc
something like this:
zstyle ':prezto:load' pmodule \
'environment' \
'terminal' \
'editor' \
'history' \
'directory' \
'spectrum' \
'utility' \
'completion' \
'prompt' \
'git' \
'ruby' \
'rails' \
'syntax-highlighting'
And in my .zshrc_local
I could have
zstyle ':prezto:unload' pmodule \
'spectrum'
Because zstyle ':prezto:load'
is just putting a module name in a list of modules marked to be loaded,
there wouldn't be any real performance penalty even if every module gets marked to be loaded/ unloaded 10 times.
Point is every configuration should be overridable from standard zsh files in locations which must be showed in README.md and these locations should not change over time. Example of such location could be $ZDOTDIR/.zshrc_local
That way I can pull this repo on every commit and never have any merge conflicts because my settings override prezto's without any performance penalty.
I personally agree with you @marko-avlijas - this is why I don't use the standard method of loading prezto.
There's an example here: https://github.com/belak/dotfiles/blob/5febcfcdb6364db2216145e06c13184ffacb8894/zshrc
I view the provided runcoms as a starting point. The "official" method of managing them is simply a suggestion.
It may be worth looking into an official way for simple customization of runcoms, but for now I'd just recommend managing them yourself outside the prezto repo - they're not very complicated.
Additionally, most prezto settings can be set in a .zpreztorc file - this is enough for most people.
git pull
is great, but maybe add workflow for more easy prezto customization, like add in .zshrc: