Closed maximbaz closed 2 years ago
Hey @romkatv! I'm giving z4h/v3 a spin, and would like to share some ideas and ask some questions at the same time
Thanks!
- Tab completion: traversing hidden folders
You mentioned it is possible to add an option to traverse hidden folders as well when
glob_dots
is set, could you please add it or give me a hint how to do this locally?
This should do it:
diff --git a/fn/-z4h-comp-files b/fn/-z4h-comp-files
index 9c63668..02ea709 100644
--- a/fn/-z4h-comp-files
+++ b/fn/-z4h-comp-files
@@ -14,13 +14,13 @@
if (( only_dirs )); then
if (( dot_glob )); then
- local dirs=($path_prefix${^${(Q)words:#.*}}/*(D-/Y1N:h:t))
+ local dirs=($path_prefix${^${(Q)words}}/*(D-/Y1N:h:t))
else
local dirs=($path_prefix${^${(Q)words:#.*}}/*(-/Y1N:h:t))
fi
else
if (( dot_glob )); then
- local dirs=($path_prefix${^${(Q)words:#.*}}/*(DY1N:h:t))
+ local dirs=($path_prefix${^${(Q)words}}/*(DY1N:h:t))
else
local dirs=($path_prefix${^${(Q)words:#.*}}/*(Y1N:h:t))
fi
@@ -36,7 +36,6 @@
cmd+=(-path ./${(b)dir}/'*' -o)
done
cmd[-1]=(')' -prune)
- cmd+=(-o -name '.*' -prune)
(( dot_glob )) && cmd+=(-print)
cmd+=(-o -print)
fi
diff --git a/fn/z4h-cd-down b/fn/z4h-cd-down
index ba47b08..ee71d35 100644
--- a/fn/z4h-cd-down
+++ b/fn/z4h-cd-down
@@ -9,7 +9,7 @@
if (( dot_glob )); then
local dirs=(./*(-/DN))
- local non_empty=(${^${dirs:#./.*}}/*(D-/Y1N:h:t))
+ local non_empty=(${^dirs}/*(D-/Y1N:h:t))
else
local dirs=(./*(-/N))
local non_empty=(${^dirs}/*(-/Y1N:h:t))
@@ -23,7 +23,6 @@
cmd+=(-path ./${(b)dir}/'*' -o)
done
cmd[-1]=(')' -prune)
- cmd+=(-o -name '.*' -prune)
(( dot_glob )) && cmd+=(-print)
cmd+=(-o -print)
fi
The standard completion system respects
ignored-patterns
.This was a great hint, just as one idea it allows to ignore
.zwc
files, for example withzstyle ':completion:*:*:kak:*:*' ignored-patterns '*.zwc'
opening.p10k.zsh
in editor is simpler, askak .p<Tab>
now has only one match instead of two, sofzf
doesn't show up at all and I go straight to.p10k.zsh
.
I like this! I added this zstyle
to zsh4humans.
Currently recursive directory listing doesn't respect it but it's on my TODO list to fix this.
Would be awesome! Could you please suggest what would be a proper
zstyle
syntax that you want to support for ignoring folders forcd
? Say for example I wanted to ignore~/.cache/
and~/.docker/
and everything underneath them.
I don't know yet.
- Tab completion: do not require space to search different dirs
Suppose I just cloned
zsh4humans
, I press<Alt-Down>
and I want to navigate tozsh4humans/fn
. Typingz fn
will find it, but typingzfn
will not. I am used to just typing infzf
without having to think where I should put spaces... Could we at least have an option, if the current behavior is something you prefer?
You are using an unreleased version of zsh4humans, which is essentially my personal dotfiles. I'm experimenting with --exact
fzf flag right now, which turns off fuzzy matching by default. You can still get fuzzy matching for a term with 'zfn
. You can also search for --exact
in zsh4humans and remove it.
FWIW, after a few days of adjusting to --exact
I very much prefer it. Fuzzy matching in fzf is its main weakness. All other fuzzy finders I've tried have much better fuzzy matching logic.
By the way, while
<Alt-Down>
showszsh4humans zsh4humans/fn
But
cd <Tab>
showszsh4humans ./zsh4humans/fn
That's a bug. Fixed.
- Tab completion:
fzf-tab
continuous-trigger
Am I right that it will become unnecessary when everything will be traversed?
It's still necessary for two reasons.
.git
is not traversed but sometimes I want to type something like cat .git/HEAD
or ls .git/refs/remotes/*/*
.x/blah
, you can quickly find x/
and press Tab instead of waiting for everything before x/
to be traversed first.
- Tab completion: multiple selection doesn't seem to work
Works for me.
docker run -e TERM -e COLORTERM -w /root -it --rm alpine sh -uec '
apk add zsh curl
echo POWERLEVEL9K_DISABLE_CONFIGURATION_WIZARD=true >~/.p10k.zsh
sh -c "$(curl -fsSL https://raw.githubusercontent.com/romkatv/zsh4humans/v3/install)"'
And then, after pressing Enter:
(Superfluous spaces between arguments are the result of a bug in fzf-tab. I don't know yet if it's fixable.)
Have you considered adding groups and using different colors to fzf completion?
Maybe. In general I don't like group colors because it can look awful without sorting (and I don't want to enable sorting of completion candidates) and interferes with fzf's highlighting of matched terms. If fzf supported highlting via reverse video, that would help.
- "Additional Zsh startup files" in README
I would suggest to rephrase this a little
I haven't updated docs in v3 yet. You may have noticed that v3 has two rc files but the docs say there is just one.
- In
.zprofile
I have[[ -z $DISPLAY && "$(tty)" == "/dev/tty1" ]] && exec sway
This is awful. Does you OS come without init system? Using zsh rc files to start services isn't something I would recommend or consider a valid use case.
- In
.zshenv
I have a list of environment variables that I want to be exported both in terminal and also for GUI
This is equally awful. Does you system come without /etc/environment
, ~/.pam_environment
or equivalent? .zshenv
is for setting up environment specific to zsh. It's evaluated in every zsh process (e.g., zsh -c 'echo hi'
). What you are looking for is environment that gets set up when a user logs in.
- Avoid exporting
XDG_CACHE_HOME
I would suggest to drop this line:
https://github.com/romkatv/zsh4humans/blob/6af0bfc8727b5e5ed72643ce5c457e9cbc513afb/.zshenv#L11
Removed. This line was originally in .zshrc
so that users who do want to override XDG_*
knew where to put them. In v3 I've moved the top of .zshrc
to .zshenv
because that code is very tricky and because 99.9% of users shouldn't change it. I also envision that some users will want to have ZDOTDIR=~/.zsh
or similar, and this setup has explicit support for it.
- Add
TIMEFMT='user=%U system=%S cpu=%P total=%*E'
toz4h
Done.
- Terminal title to contain timestamp
I plan to support it via zstyle
where you can specify preexec
and precmd
terminal title formats similarly to prompt (with %
-escapes and stuff).
Because transient prompt (often) removes the timestamp of when a command was started, you once recommended me to add the time to the terminal title instead, to be able to see how long the command is already running. I still think it was a great idea, might be worth adding directly to
z4h
? Time is only needed for when a command is running (not when title shows current dir), and maybe only when transient prompt is enabled (but I'd probably just show the time always when command is running). What do you think?
Maybe. I prefer to show less by default and let users add extra stuff if they want. I think it's better than showing stuff than users don't want (even if they have an option to turn it off).
- Support moooooore
LS_COLORS
Have you seen this project? https://github.com/trapd00r/LS_COLORS
Yes. I used it in the past.
Currently I can source their file and it will affect my
ls
, but I don't think tab completion respects this variable, maybe because colors are set immediately after a built-inLS_COLORS
is defined?
That's a bug. Fixed.
My position here is the same as w.r.t. to terminal title. zsh4humans by default should be usable and should have all vital features. Extra features should be easy to turn on. Every time users want to turn something off, it's worse than when users want to turn something on. Both of these imply that the defaults aren't optimal for the user but turning off is worse than on.
trapd00r/LS_COLORS is easy to enable for those who want it. If it was on by default, I would turn it off in my own dotfiles. The more colors you add to regular files, the more difficult it becomes to distinguish between regular and special files. That distinction is important to me.
I think some regular file colors would help me but I cannot think of a default that would work for everyone.
- I still use your trick to have a new line added in transient prompt, just curious if noone else requested this to become a simple config variable? :)
I don't think anyone has asked for it. If this were to become popular, I could add an option to the configuration wizard.
- Bind this to
Ctrl+/
and it will toggle comment for the currently typed command, just like in VS Code and other editors :)
This looks like what I used to have in my .zshrc
. Nowadays I use Alt+O instead. It requires fewer keystrokes to recover the stashed command. I also want to try push-input
and see how that goes.
- Bind this to
Ctrl+V,V
to edit the current command in EDITOR, very useful for multi-line commands
I used it for a while but I no longer do. Editing in $EDITOR
means no completions and no syntax highlighting. I now edit multi-line commands directly in zle. It takes a little while to get used to pressing Alt+Enter instad of Enter but after that it's quite comfortable.
- Make
Ctrl+Z
toggle pressingCtrl+Z
andfg
, very useful for quickly checking terminal behind an editor and going back to the editormy-ctrl-z() { if [[ $#BUFFER -eq 0 ]]; then BUFFER="fg" zle accept-line -w else zle push-input -w zle clear-screen -w fi } zle -N my-ctrl-z
The idea looks interesting although the specifics look rather unintuitive to me. How about this?
my-ctrl-z() {
(( ${(%):-%j} > 0 )) && fg
}
zle -N my-ctrl-z
bindkey '^Z' my-ctrl-z
That's enough for now
UPDATE: OK, one more Have you considered to add these options to the list of default options in
z4h
?
HIST_IGNORE_ALL_DUPS
Currently if you press Ctrl+R, you'll see all commands you've typed in the current zsh session (except for consecutive dups). I find it valuable. HIST_IGNORE_ALL_DUPS
will lose that.
to keep the history file smaller?
z4h sets HIST_SAVE_NO_DUPS
, so dups are not saved to the history file.
HIST_SAVE_NO_DUPS
This is set.
RC_QUOTES
to allow'Henry''s Garage'
instead of'Henry'\''s Garage'
?
This is too dangerous to enable by default as it changes the meaning of POSIX-compliant commands. Can be very surprising. Users who want this option can put it in their own .zshrc
.
I recalled there is one more case where I use continuous tab completion.
mkdir -p /tmp/a/b{1,2}/c{1,2}
cd /tmp/a/b/c<TAB>
This will first offer you to choose between b1
and b2
. If you confirm your selection with Tab, it'll immediately present you with the choice between c1
and c2
.
By the way, have you tried z4h ssh
? Do you use ssh
often? I can give you a short intro about setting it up to take full advantage of file transfers to the remote and then back to local host. For me z4h ssh
(which I invoke simply as ssh
) was one of the main reasons I've switched to z4h.
Tab completion: traversing hidden folders
This should do it...
Yup, works, thanks 🙂
I'm experimenting with
--exact
fzf flag right now, which turns off fuzzy matching by default. You can still get fuzzy matching for a term with'zfn
. You can also search for--exact
in zsh4humans and remove it. FWIW, after a few days of adjusting to--exact
I very much prefer it. Fuzzy matching in fzf is its main weakness. All other fuzzy finders I've tried have much better fuzzy matching logic.
Thanks for the hint! I do agree that the matching algorithm in fzf is worse than in competitors, but I still prefer the fuzzy flow 🙂 I removed --exact
for now.
I guess long-term it would make sense to leave it up to users to put --exact
to their $FZF_DEFAULT_OPTS
, I imagine flags like --exact
are very subjective to people's preferences and with these flags consistency is very important with how you are using fzf for everything else outside of z4h (in other words, not to confuse your muscle memory, if you use --exact
outside of z4h you would want to have --exact
also in z4h, and the opposite). What do you think?
Am I right that it will become unnecessary when everything will be traversed?
It's still necessary for two reasons... I recalled there is one more case where I use continuous tab completion.
Riiiight, makes total sense.
Tab completion: multiple selection doesn't seem to work
Works for me....
Uh, my bad, turns out Ctrl+Space
was bound by another app. It's working now 🙂
What would be nice if Ctrl+Space
would not only toggle the current element, but also move to the next one. Because when you multi-select, you usually select several items, usually consequent, so there is little reason to stay on the current item.
This is awful. Does you OS come without init system? This is equally awful. Does you system come without /etc/environment, ~/.pam_environment or equivalent?
Hehe 🙂 I'm still in search for a good way to manage this, but PAM environment is also not ideal as its syntax is limited in what you can do, and then people tend to have their variables declared all over the place... In any case, your point is accepted 👍
Every time users want to turn something off, it's worse than when users want to turn something on. Both of these imply that the defaults aren't optimal for the user but turning off is worse than on.
I like this logic!
Make Ctrl+Z toggle pressing Ctrl+Z and fg
The idea looks interesting although the specifics look rather unintuitive to me. How about this?
This one has two downsides:
Ctrl+Z
6 times, you will see that part of the prompt will not become transient, it will have all the iconsTo be frank, the snippet I posted is also not ideal, if you press Ctrl+Z
6 times your prompt will also contain useless garbage. The true ideal behavior would be like Alt+<arrow>
bindings behave in z4h, where the prompt is being redrawn instead of being "submitted", i.e. the cursor stays on the same line in the terminal. I don't know if it's possible to achieve though...
By the way, have you tried
z4h ssh
? Do you usessh
often?
I use it often, I have tried z4h ssh
and find it amazing! By the way, I confirm that with the latest zsh-bin
everything works perfectly with kitty.
I can give you a short intro about setting it up to take full advantage of file transfers to the remote and then back to local host.
Would really appreciate this!
And thanks for all the fixes you have already made after this thread 🙂
I guess long-term it would make sense to leave it up to users to put
--exact
to their$FZF_DEFAULT_OPTS
This is what worries me the most when you try a version of my code that isn't yet ready for others to use. On one hand it's great to have early feedback. On the other hand you find the product and the code behind it of worse quality than what you came to expect, and your feedback is also higher on noise than normal (but still very valuable) due to the observations of "I know" kind.
It's not my plan to have --exact
hard-coded. It's just something I can do to my own dotfiles to see how it feels. When v3 is released, it will definitely have a way to control fzf flags, and it'll almost certainly be done with zstyle
. Since z4h doesn't use any of the builtin fzf widgets, it doesn't make sense to respect FZF_*
parameters. I've already yanked them out in preparation.
If v3 will have a default for fuzzy/not-fuzzy search, it'll be fuzzy. It'll be easy to change. Non-fuzzy isn't so much better to warrant going against the status quo established by fzf.
I'm not sure if I mentioned it before. If everything goes well, v4 will likely be the last version of zsh4humans. Or to put it the other way, v3 will be the last version that I don't want to become popular because I want to break backward compatibility one more time. Before I release v3, I'll address some major issues and clean things up. I'll try to keep the release low key just like the previous versions. I want feedback but only from intelligent users. v4 will have a setup wizard similar to the one in p10k where you'll be able to choose this vs that for the common options that I anticipate. Once v4 is out, I'll maintain backward compatibility for as long as I muster.
What would be nice if
Ctrl+Space
would not only toggle the current element, but also move to the next one.
Good idea. I don't control fzf but maybe it's something I can add upstream.
This one has two downsides:
- It "breaks" transient prompt, as in if you open an editor and then press
Ctrl+Z
6 times, you will see that part of the prompt will not become transient, it will have all the icons
Ah, you want something like Alt+Tab for full-screen apps in the terminal? That sounds cool! I'll see what I can do.
By the way, I confirm that with the latest
zsh-bin
everything works perfectly with kitty.
Thanks!
I can give you a short intro about setting it up to take full advantage of file transfers to the remote and then back to local host.
Would really appreciate this!
I've put it on my TODO list. Will ping you later.
And thanks for all the fixes you have already made after this thread 🙂
Thanks for the suggestions and bug reports! Invaluable as always.
This is what worries me the most when you try a version of my code that isn't yet ready for others to use. On one hand it's great to have early feedback. On the other hand you find the product and the code behind it of worse quality than what you came to expect, and your feedback is also higher on noise than normal (but still very valuable) due to the observations of "I know" kind.
Makes sense 🙂 Don't get me wrong, I knew what I subscribed for when I decided to try v3 despite your warning about it not being ready - feel free to just reply with "I know" on such comments 👍
Ah, you want something like
Alt+Tab
for full-screen apps in the terminal? That sounds cool! I'll see what I can do.
Precisely!
Found a small issue when testing history retrieval over ssh, on closing connection I get this error:
(anon):zf_mv:1: /tmp/zshxurItS: invalid cross-device link
It is coming from this line:
https://github.com/romkatv/zsh4humans/blob/93fc6e46ff5df17c4514b444eaba065858efedef/fn/-z4h-ssh#L360
My /tmp
is on RAM (tmpfs), and it seems zf_mv
cannot move files between different devices; changing zf_mv
to mv
on that line fixes the issue but I'm not sure it's the right approach.
Found a small issue when testing history retrieval over ssh, on closing connection I get this error:
Thanks!
it seems
zf_mv
cannot move files between different devices
Yeah, this annoying limitation is even documented. It's really annoying when you are trying to write fast crash-safe code.
changing
zf_mv
tomv
on that line fixes the issue but I'm not sure it's the right approach.
This isn't safe. Pick up the right fix here: https://github.com/romkatv/zsh4humans/commit/c200c733880a8ddcb6c9d58dd0f80073f6fdca79.
I found a... weirdness 🤔
Say I have a file aliases.zsh
containing the following:
alias sayhello='echo hello'
greet() { sayhello world }
If I source aliases.zsh
in .zshrc
, open a new shell and run greet world
, it will work as expected. But if I instead z4h source aliases.zsh
in .zshrc
, then greet world
will not expand the alias and crash with command not found: sayhello
Seems like if you source
a script from within a function, it does it... in a different context or something 🤔
It's easy to workaround (e.g. by replacing the alias with sayhello() { echo "$@" }
), but maybe you'll have any ideas how to make the behavior of z4h source
match source
...
I found a... weirdness
Aww, that sucks. I've asked on zsh-workers: https://www.zsh.org/mla/workers//2020/msg01019.html. I'm not holding my breath though.
Another small observation: bash completions seem to be loaded too late in the game, for example in order to enable completions for azure-cli we need to z4h source
this file in .zshrc
, and it would fail unless we also autoload and call bashcompinit
manually. I'm not sure if this is just a matter of extracting bashcompinit
out of -z4h-post-init()
, because there is some other stuff related to the completions in that function... 🤔
Another small observation: bash completions seem to be loaded too late in the game
Thanks for letting me know. To be honest, I haven't tested this feature and I don't use bash completions myself.
I've moved the call to bashcompinit
to z4h init
, so az
completions should work. There are still cases where it won't work though. E.g., complete -p
and complete -r
won't work until zsh is fully initialized. I don't know if bash completion functions ever call these during initialization.
Aww, that sucks. I've asked on zsh-workers: https://www.zsh.org/mla/workers//2020/msg01019.html. I'm not holding my breath though.
I received the reply I expected from reading the code of zcompile.
zcompile foo && source foo
is not equivalent to source foo
in one aspect: the former won't expand aliases defined in foo
while sourcing foo
. This is by design.While I agree with (2), I cannot enforce it and I don't want to have this gotcha in z4h source
. So I've changed z4h source
to not compile by default. You can pass -c
to enable compilation.
# Do not zcompile.
z4h source foo.zsh
# Do zcompile.
z4h source -c bar.zsh
Note that -c
doesn't always speed things up. Compared to plain z4h source
, it takes additional N - M * file_size
seconds. If the file is very small, z4h source -c
is slower than plain z4h source
. The threshold is fairly low though.
Do you have any plans for merging <Alt-down>
and cd <Tab>
to have a common implementation underneath both features? Just thinking that it takes time to maintain both, and from a user perspective it is kinda annoying when you stumble upon minor inconsistencies between them (because over time you stop noticing which one of the two you use). I found myself using fzf-tab
continuous-trigger
quite often actually (for one of the cases you mentioned, to stop traversing everything and focus on a certain dir), and this feature is only available via cd <Tab>
.
In general I've been extensively using v3 this entire week, testing different features, and I must say it is working really great, very solid work!
Do you have any plans for merging
<Alt-down>
andcd <Tab>
to have a common implementation underneath both features?
I think you want to ask a different question.
from a user perspective it is kinda annoying when you stumble upon minor inconsistencies between them
That's a good question. Or rather a good bug report / feature request.
I found myself using
fzf-tab
continuous-trigger
quite often actually (for one of the cases you mentioned, to stop traversing everything and focus on a certain dir), and this feature is only available viacd <Tab>
.
Fixed. Now Tab works in Alt+Down.
In general I've been extensively using v3 this entire week, testing different features, and I must say it is working really great, very solid work!
Thanks! I'm glad.
By the way, what do you think of word-based widgets? Do you use them? Have you noticed that they are different from the stock widgets? Do they behave the way you would expect?
Perhaps there are more widgets that you haven't tried? Here's the list of bindings that I recommend trying.
Bindings | Description |
---|---|
Ctrl+Left | Previous word |
Ctrl+Right | Next word |
Ctrl+Home | Beginning |
Ctrl+End | End |
Bindings | Description |
---|---|
Ctrl+Backspace | Previous word |
Ctrl+Delete | Next word |
Alt+K | Everything left of cursor |
Ctrl+K | Everything right of cursor |
Ctrl+U | Current line |
Alt+J | Everything |
Bindings | Description |
---|---|
Up | Previous local history event |
Down | Next local history event |
Ctrl+Up | Previous shared history event |
Ctrl+Down | Next shared history event |
Bindings | Description |
---|---|
Alt+M | Accept autosuggestion without moving cursor |
Alt+O | Push command to history without executing |
Ctrl+/ | Undo |
Alt+/ | Redo |
Alt+H | Show help for command |
Undo is super useful. I undo Tab completions often. For example, I might type something like rmdir -- ./**/*~*/.git/*(/^F)
, hit Tab to expand the glob, visually validate that it does what I expect (this one is supposed to match empty directories recursively except in .git
), and then hit Ctrl+/ to undo the expansion before executing the command. This way I have the command with an unexpanded glob in my history in case I want to run it again.
Another common use case for Undo is after pasting form clipboard. I often find out that clipboard has something other than what I expect.
Local history (Up) vs global history (Ctrl+Up) is great if you use several zsh sessions at once.
The default behavior of prefix-based history widgets (like Up in z4h) is to move cursor to the end. This has some unintuitive consequences as it effectively introduces two cursors. One cursor is visible, and it's positioned at the end. The other cursor is invisible and it's positioned at the same place where it was before you pressed Up. The invisible cursor demarcates the command prefix used by the widget in case you press Up again. Here's an example of how it usually works.
cd
and press Up. You'll see the last command from your history that starts with cd
. Let's say it's cd ~/foo
.cd
.cd
. Perhaps cd /bar
.The last point is tricky because it requires you to remember where the invisible cursor is. Many actions lead to the invisible cursor change its position to align with the visible cursor, so this can be tricky. E.g., if after the first Up you were to manually move cursor to the left and then back, the next Up wouldn't give you cd /bar
. Instead, it would give you a command that starts with cd ~/foo
.
In z4h there is always one cursor position. Or, to put it another way, the invisible cursor is always at the same position as the visible. This is achieved by not changing the cursor position when you press Up. This makes it much easier to reason about Up. This key always gives you a command that has the same prefix as the current command line up to the cursor.
I also noticed that when I press Up I'm more likely to change the command at the front rather than at the back (add sudo
, or some flag), so it also saves keystrokes.
Alt+H is nice. Try typing typeset
and hitting this key combo. Also try it on git clone
.
I use Alt+M often. If you want to accept an autosuggestions and edit something close to the current cursor position, Alt+M can save many keystrokes.
Ctrl+K, Alt+K and Alt+J make command line editing much more efficient.
Let me know if you discover any issues with these or have improvement suggestions.
By the way, what do you think of word-based widgets? Do you use them? Have you noticed that they are different from the stock widgets? Do they behave the way you would expect?
There maybe are some subtle differences comparing to what I was used to (like $WORDCHARS
, will get to that in a second), but in general they behave the way I expect, I think defaults are chosen well.
Perhaps there are more widgets that you haven't tried? Here's the list of bindings that I recommend trying.
Oh thanks for sharing so much details!
Movement & Deletions shortcuts
The keys are intuitive once you read through them :+1:
I noticed you set WORDCHARS
to empty string, it makes sense for precision jumps (e.g. when you want to update something in the middle of a file path), but on the other hand sometimes you just want to get to the beginning of a long file path and it is annoying to press Ctrl+Left
many times.
What do you think about implementing two additional shortcuts that would jump to a next/previous whitespace? This idea is inspired by vim
and other modal editors, where you have shortcuts that operate on word
and WORD
. Ctrl+Alt+Left/Right
seems to be available?
I think it could also be a good idea to set WORDCHARS=''
in z4h-init
only if WORDCHARS
var is not defined by that point yet, in case user does want to customize it before loading z4h.
History shortcuts
Good idea, but I might be doing something wrong, they all seem to operate on a local history.
Is my expectation correct in this example?
echo 1
echo 2
Ctrl+Up
I should get echo 2
, but I get echo 1
As for prefix-based history widgets:
The default behavior of prefix-based history widgets (like
Up
in z4h) is to move cursor to the end. This has some unintuitive consequences as it effectively introduces two cursors...
Riiiiight, that's why when I was using this plugin separately (before z4h), it would change background color of the text between the beginning of the prompt and the invisible cursor, to highlight the common prefix part that is being searched...
I definitely think you found a more intuitive approach than that.
Alt+O
is amazing! This is what I was using "toggle comment" for, but now it's 100 times better.
Ctrl+/
and Alt+/
are amazing! Your examples are awesome demo, I will learn to use them more.
The only feedback I have is that having a default binding use special symbols will most likely not work well for international people, for example @cyrinux uses Azerty and he has to press Shift+:
in order to get /
char, and so when you press Ctrl+Shift+<anything>
, this binding is not being passed to zsh, terminal catches it.
I don't have a good alternative in mind, and of course it's easy to change, but if you can think of a different default, it might be worth considering.
Alt+H
is a good one too, I also haven't used it before!
Try typing
typeset
and hitting this key combo. Also try it ongit clone
.
Wow! I have a couple of suggestions, but I think they all might be suggestions for run-help
which you don't control...
alias gcl="git clone --recursive"
, $ gcl<Alt-h>
shows help for git
, not git clone
(even without --recursive
)Alt+M
is again something I haven't used before, but your example makes a very good case!
There maybe are some subtle differences comparing to what I was used to (like
$WORDCHARS
, will get to that in a second), but in general they behave the way I expect, I think defaults are chosen well.
Let me show how z4h word-based widgets are different from the defaults. Suppose you have this command line:
ls / foo/bar
The default forward-word
with the default WORDCHARS
(which includes /
) will go over the following cursor positions:
ls / foo/bar
^ ^ ^
This is OK-ish but too coarse most of the time. Ideally, there should be another position where the cursor stops -- somewhere between foo
and bar
.
Without /
in WORDCHARS
:
ls / foo/bar
^ ^ ^
Note how the cursor jumps over the first argument of ls
. This problem becomes more apparent if you set WORDCHARS
to empty (like most zsh users seem to do).
ls . / $_ &> foo/bar
^ ^ ^
That jump from ls
straight to foo
really sucks.
For comparison, in z4h it looks like this:
ls . / $_ &> foo/bar
^ ^ ^ ^ ^ ^ ^
sometimes you just want to get to the beginning of a long file path and it is annoying to press Ctrl+Left many times.
Indeed.
What do you think about implementing two additional shortcuts that would jump to a next/previous whitespace?
I've added 4 new widgets:
# Move cursor one zsh word forward.
bindkey '^[[1;6C' z4h-forward-zword # ctrl+shift+right
# Move cursor one zsh word backward.
bindkey '^[[1;6D' z4h-backward-zword # ctrl+shift+left
# Delete next zsh word.
bindkey '^[[3;6~' z4h-kill-zword # ctrl+shift+del
# Delete previous zsh word.
bindkey '^[^H' z4h-backward-kill-zword # ctrl+alt+bs
Here's how they tokenize commands:
foo ' bar / baz ' $(qux quux)
^ ^ ^
I'm running out of decent keys to bind things to, so you'll likely have to rebind these to whatever works for you.
Let me know what you think about these widgets.
I think it could also be a good idea to set
WORDCHARS=''
inz4h-init
only ifWORDCHARS
var is not defined by that point yet, in case user does want to customize it before loading z4h.
You can set WORDCHARS
in ~/.zshrc
after z4h init
.
History shortcuts Good idea, but I might be doing something wrong, they all seem to operate on a local history. Is my expectation correct in this example?
- Open two terminals side by side
- In terminal 1 run
echo 1
- In terminal 2 run
echo 2
- Back in terminal 1, if I press
Ctrl+Up
I should getecho 2
, but I getecho 1
Zsh reads shared history before precmd
, so you need to press Enter in terminal 1 to pick up the latest command from terminal 2.
@cyrinux uses Azerty and he has to press
Shift+:
in order to get/
char, and so when you pressCtrl+Shift+<anything>
, this binding is not being passed to zsh, terminal catches it.
Noted. Eventually z4h will have an interactive wizard for setting up bindings but for now I'm afraid the only solution is to rebind things in zshrc.
suggestions for
run-help
[...]
- It prints to stdout how it expands commands and aliases, I don't really need to see that info
- When I have
alias gcl="git clone --recursive"
,$ gcl<Alt-h>
shows help forgit
, notgit clone
(even without--recursive
)
(1) doesn't seem terrible but (2) is something I'll try to fix. I also noticed what appears to be inconsistent behavior:
% alias ls='ls --color=auto'
% run-help ls
ls is an alias for ls --color=auto
Press any key for more help or q to quit
If I press any key other than q
, it'll open man page on ls
. Seems fine. However, with alias gcl="git clone --recursive"
man gets opened automatically.
Let me show how z4h word-based widgets are different from the defaults
Uh, yeah definitely z4h approach is a lot better! And with the 4 new widgets the workflow is very nice, easy to choose if you want to make a short and precise jump vs long to the end of the zword
jump.
I've added 4 new widgets: I'm running out of decent keys to bind things to, so you'll likely have to rebind these to whatever works for you.
Using Ctrl+Shift
makes sense (also is consistent with text editors where you additionally press Shift
to jump by WORD
), I cheated and made kitty terminal send ^[^H
when I press Ctrl+Shift+Backspace
, so that all 4 new key bindings use Ctrl+Shift
consistently. In kitty you also must "open up" Ctrl+Shift
keys before they are available to zsh, so if you are interested, the full config is this:
map ctrl+shift+left noop
map ctrl+shift+right noop
map ctrl+shift+delete noop
map ctrl+shift+backspace send_text all \x1b\x08
Other than that, these widgets behave exactly as I expect them to do, and I think they are a very nice addition to the toolkit.
Zsh reads shared history before
precmd
, so you need to press Enter in terminal 1 to pick up the latest command from terminal 2.
Ooh, I see! Yes now I can reproduce the difference between local and global history.
Would it make sense to make zsh read shared history on Ctrl+Up
keypress as well? Maybe together with some smart way of detecting if it has to vs if history file hasn't changed since the last time it was read. Just to make this feature a bit more obvious for users who don't know how it is implemented internally.
UPDATE 1: I know you added bindkey -M emacs '^H' backward-kill-word
to .zshrc
for demo purposes, but would make sense to also have that binding in the core z4h (and be mapped to z4h-backward-kill-word
instead)
UPDATE 2: There's also a small issue with z4h-backward-kill-word
, it doesn't move the cursor upon removal. Type cat one.txt | grep something
, put cursor right before the dot and press Ctrl+W
to remove one
(maybe with an intention to write two
instead), but the cursor will no longer stay on the dot.
Using
Ctrl+Shift
makes sense (also is consistent with text editors where you additionally pressShift
to jump byWORD
)
I think Ctrl is the standard key modifier on Windows and Linux to move/delete words. Holding shift usually enables selection, so Ctrl+Shift+Right selects the next word.
By the way, I've been contemplating implementing the same Shift behavior in Zsh. I think it can be reproduced exactly with just one difference. After selecting text with Shift+Arrows you'll have to use a shortcut other than the standard you've configured in the terminal to copy to clipboard. E.g., I normally copy text from the terminal to clipboard with Ctrl+Shift+C or Ctrl+Insert but here I'll need to press Ctrl+S or something like that. (Currently my Ctrl+S binding copies the whole command line to clipboard.)
What do you think of this?
I cheated and made kitty terminal send
^[^H
when I pressCtrl+Shift+Backspace
, so that all 4 new key bindings useCtrl+Shift
consistently. In kitty you also must "open up"Ctrl+Shift
keys before they are available to zsh, so if you are interested, the full config is this:map ctrl+shift+left noop map ctrl+shift+right noop map ctrl+shift+delete noop map ctrl+shift+backspace send_text all \x1b\x08
Nice!
Would it make sense to make zsh read shared history on
Ctrl+Up
keypress as well?
Yes, that would definitely be better but it's very difficult to achieve. z4h simply exposes the builtin zsh history via bindings. In order to read global history on Ctrl+Up I would need to write a ton of code.
In Zsh, anything that uses history can request either local or global history (there is also internal history but it's less interesting). I chose for Ctrl+R and for autosuggestions to use global history, but I could also choose to use local, or to provide Ctrl+Shift+R for local history. I didn't do this because it doesn't seem very useful.
On the other hand, I made Up and Down use local history. I think this is what everyone expects and what I prefer myself.
When I just added Ctrl+Up and Ctrl+Down I was using them a lot. Nowadays Ctrl+R without a query shows your whole global history, so most of the time I use Ctrl+R instead of Ctrl+Up. I'm thinking of removing Ctrl+Up and Ctrl+Down bindings. This will free up these keys for something more useful and I won't have to explain this local/global history distinction.
UPDATE 1: I know you added
bindkey -M emacs '^H' backward-kill-word
to.zshrc
for demo purposes, but would make sense to also have that binding in the core z4h (and be mapped toz4h-backward-kill-word
instead)
I don't want to overwrite the default bindings in z4h. ^H
in emacs
keymap is bound to backward-delete-char
and some users expect it to work that way. If you have to define an extra binding, that's one thing. You are just adding more stuff on top z4h. But if z4h overrides the default binding that you like, that's quite different. In this case z4h is being overly aggressive and opinionated.
So, instead of following your suggestions I've moved the binding for z4h-backward-kill-zword
to .zshrc
(^[^H
is bound to backward-kill-word
by default). You'll need to add it to your own .zshrc
.
UPDATE 2: There's also a small issue with
z4h-backward-kill-word
, it doesn't move the cursor upon removal. Typecat one.txt | grep something
, put cursor right before the dot and pressCtrl+W
to removeone
(maybe with an intention to writetwo
instead), but the cursor will no longer stay on the dot.
Thanks! Fixed.
I'm thinking of removing
Ctrl+Up
andCtrl+Down
bindings. This will free up these keys for something more useful and I won't have to explain this local/global history distinction.
I think this reasoning here, and your choice for Ctrl+R
and autosuggestions to use global history, all make sense.
Just one question to confirm, I noticed that contrary to Ctrl+Up
, Ctrl+R
and autosuggestions both require not just pressing Enter
, but actually running some other command to refresh the global history - is there a reason for why history is not refreshed for them in precmd
?
Example:
Ctrl+R
or echo<Up>
, both will not see echo 2
until you run something else first in terminal 1.By the way, I've been contemplating implementing the same
Shift
behavior in Zsh. I think it can be reproduced exactly with just one difference. After selecting text withShift+Arrows
you'll have to use a shortcut other than the standard you've configured in the terminal to copy to clipboard.
Interesting idea, before I comment too much I'd like to clarify my understanding first, my first impression is that it will only be useful for "copy region to clipboard" operation, because "select to delete" or "select to replace" would require same or more number of keypresses comparing to a simple deletion using Ctrl+{,Shift+}{Delete,Backspace}
keys - is that right or am I missing something here?
I noticed that contrary to
Ctrl+Up
,Ctrl+R
and autosuggestions both require not just pressingEnter
, but actually running some other command to refresh the global history - is there a reason for why history is not refreshed for them inprecmd
?Example:
- Open two terminals side by side
- In terminal 1 run echo 1
- In terminal 2 run echo 2
- Back in terminal 1, press Enter, then try
Ctrl+R
orecho<Up>
, both will not seeecho 2
until you run something else first in terminal 1.
I confirm this. Looks like a zsh bug at the first glance. I'll dig into it when I get some free time.
Interesting idea, before I comment too much I'd like to clarify my understanding first, my first impression is that it will only be useful for "copy region to clipboard" operation, because "select to delete" or "select to replace" would require same or more number of keypresses comparing to a simple deletion using
Ctrl+{,Shift+}{Delete,Backspace}
keys - is that right or am I missing something here?
People use shift-select + delete in text editors even though the same logic applies there. If I were to implement this feature in zsh, I'd make delete and backspace work on selection so that it works the way people expect.
I should've mentioned that this is all very speculative. If I could create true terminal selection with Shift-Arrows (the same kind of selection you can produce with a mouse), I would definitely do it but zsh-only selection with custom keys to copy/cut/paste doesn't sound very exciting to me. But maybe am I'm wrong. Maybe I would like it if I tried it.
Sorry for hijacking this issue, but it's called 'Feedback' which is a very general topic, and that's what this post is about.
I was wondering if this function would be acceptable to include in z4h. It's quite subtle until you realize you've used it without knowing it. It does happen from time to time (at least for me) you find
or grep
for files, copying one of the results and pasting it to your prompt right after you've written cd
.
Another thing; I just noticed that e93734c sets the $ZSH variable (?), the same variable-name I have been using for my own zsh-snippets located in ~/.zsh/
. And (without actually test it yet by updating z4h) I guess there might be a collision.
ZSH=${ZSH:-${ZDOTDIR:-$HOME}/.zsh}
() {
for config_file ($ZSH/rc/*.zsh) z4h source $config_file
}
What would be the recommended way to include your own zsh-folder similar to the above?
Thank you for your awesome work and inspiring code. :)
Sorry for hijacking this issue, but it's called 'Feedback' which is a very general topic, and that's what this post is about.
Sure, feedback is welcome.
I was wondering if this function would be acceptable to include in z4h. It's quite subtle until you realize you've used it without knowing it. It does happen from time to time (at least for me) you
find
orgrep
for files, copying one of the results and pasting it to your prompt right after you've writtencd
.
Maybe, although the motivation is rather low. I don't recall ever having an error from cd
because I'd passed an existing file that's not a directory as an argument.
If some feature cannot be simply enabled by the user through their own config, or if enabling it correctly is difficult, tricky or error prone, then it's a strong signal that it must be provided by z4h. This cd
function is not of this kind. You can define it in your zshrc or whatever.
Another thing; I just noticed that e93734c sets the $ZSH variable (?)
If this is a question, the answer is yes, it does set ZSH
parameter unless it was already set.
the same variable-name I have been using for my own zsh-snippets located in
~/.zsh/
. And (without actually test it yet by updating z4h) I guess there might be a collision.
I guess you'd better test it.
What would be the recommended way to include your own zsh-folder similar to the above?
Not sure I understand what you are asking. If you want to create ~/.zsh
and point ZSH
to it, go ahead. If understand you currently, you've already done that, so 👍
(FWIW, I personally don't find ZSH
pointing to ~/.zsh
useful. It hardly saves typing and it increases complexity due to indirection and opens up an avenue for clashes. Note that ZSH
is used by Oh My Zsh.)
Want to share a couple of more observations regarding how file completion works, would be nice if it these can be improved:
I noticed that tab completion doesn't cross mountpoint boundaries, for example my /run/user/1000
is mounted on tmpfs
, so $ vim /run/<Tab>
will not list anything inside /run/user/1000/
. I use btrfs with multiple subvolumes, so I stumble upon this quite often. Not critical, but slightly annoying 🙂
In a git repo, try git log -p -- <Tab>
, you will only see autocompletions for the files and folders in current dir, but not recursive like you would get with $ vim <Tab>
.
- I noticed that tab completion doesn't cross mountpoint boundaries
This is intentional. Weird and bad things can happen if you traverse all directories. To stay on the safe side I made tab completion in z4h stay within a single filesystem.
I've added a note to make it customizable although I don't know yet how exactly it should look like. Perhaps a blacklist of target filesystems into which it's not allowed to recurse if the search has started from another filesystem? The current behavior would be described as blacklist='*'
(do not recurse into any filesystems that are different from the starting point). Setting blacklist to ''
(empty) would allow recursing into everything. When you notice that recursing into devtmpfs
is not something you want, you can add it to the blacklist.
Would that work for you?
- In a git repo, try
git log -p -- <Tab>
, you will only see autocompletions for the files and folders in current dir, but not recursive like you would get with$ vim <Tab>
.
Yeah, this really sucks. Not only git
completion is dead slow, it also drops most of the information it computes on the floor forcing you to repeatedly perform (slow!) completion.
To fix this one would have to change/patch/hack git
completion. No changes in zsh4humans required. It's not possible to do it without changing git
completion.
I've added it to my TODO list but it's a good task for anyone else to pick up. It's well isolated and independent from zsh4humans.
What would be really cool is to implement git
completion on top of gitstatus. It has all the info. Then it would be super fast. This is just musings though. I suspect it's a ton of work.
Weird and bad things can happen if you traverse all directories. Setting blacklist to
''
(empty) would allow recursing into everything. When you notice that recursing intodevtmpfs
is not something you want, you can add it to the blacklist.
I see. I think the use-case that I have (which would apply to btrfs, zfs, and maybe others) can be solved without any configuration, if z4h would by default traverse not only the filesystem of the starting point, but also filesystem of where the /
is mounted (e.g. $ mount | awk '/on \/ / {print $5}'
). I think this should be safe? And I doubt anyone would really ask for more (but let's see).
UPDATE: re-reading your message, I think what you meant by "blacklist='*'
(do not recurse into any filesystems that are different from the starting point)" is not what I initially understood, if you are suggesting that if starting point filesystem if btrfs, and a folder is discovered during traversal is mounted also on btrfs, then that folder will be traversed - then this approach will work perfectly well as well, and again maybe configuration is not needed altogether.
If not, then I would suggest providing whitelist
instead of blacklist
, so that users could just add whitelist='btrfs'
and still benefit from ignoring everything else in mount
just like vanilla z4h does, without spending too much time on configuring blacklist
very precisely.
To fix this one would have to change/patch/hack
git
completion. No changes in zsh4humans required. It's not possible to do it without changinggit
completion.
Makes sense!
I see. I think the use-case that I have (which would apply to btrfs, zfs, and maybe others) can be solved without any configuration, if z4h would by default traverse not only the filesystem of the starting point, but also filesystem of where the
/
is mounted
That's an interesting idea. I implemented this if find
is GNU. Let me know whether this works well for you.
Sadly find
doesn't seem to know what btrfs is:
$ command find / . -maxdepth 0 -printf '%F\n'
unknown
unknown
While there are alternative ways to retrieving the filesystems, like:
$ command df --output=fstype . / | command sed -e 1d
btrfs
btrfs
We won't be able to plug them into find
command to actually run the search, find . -fstype btrfs
finds nothing 😞
I found some old bug mentioning this issue, but I don't think it is very trustworthy, e.g. contrary to what they say, df
definitely returns correct info.
Sadly
find
doesn't seem to know what btrfs is:
That's unfortunate. I've changed the code so that if /
or .
use "unknown" filesystem, the code reverts to the old behavior of using -xdev
.
If you are feeling generous, mind submitting a bug against GNU find? Would be nice if it was fixed. Or maybe they'll tell you how to fix your system so that you get btrfs
instead of unknown
.
We won't be able to plug them into
find
command to actually run the search
Yeah, I know about this gotcha. That's why I retrieve file system type with find
.
If you are feeling generous, mind submitting a bug against GNU find?
The bug you've found is still open. Maybe post a comment there? Since now df
displays btrfs
filesystem type for subdirectories of btrfs mounts (this is true, right?), it's not entirely unreasonable to check how df
does that and to do the same in find
.
Yep, I'll take care of it and report back when there is an update 👍
- In a git repo, try
git log -p -- <Tab>
, you will only see autocompletions for the files and folders in current dir, but not recursive like you would get with$ vim <Tab>
.Yeah, this really sucks. [...] I've added it to my TODO list [...]
I took a stab at it. If you update zsh4humans and type git add <TAB>
, you'll see the list of all unstaged and untracked files. Let me know how it works for you.
There is one downside as far as I can see. If you create a directory with a thousand files in it and type git add <TAB>
, you'll get a long list of files. If your goal is to add all of them, with normal git completions you would add the directory but it's not offered as a completion in zsh4humans. One way to improve the UI is to inject a bunch of directories into the list of completion candidates. There is a question of which directories to inject and I think it would be sensible to inject the smallest set of directories that partitions the set of files in the maximum number of ways. For example, given these files:
a/b/c/1
a/b/c/2
a/b/1
a/b/d/1
These would be the injected directories:
a/b
a/b/c
a/b
allows you to select all files. a/b/c
selects the first two files. There is no need to inject a
because it selects the same files as a/b
. There is no need to inject a/b/d
because it selects the same files as a/b/d/1
.
WDYT?
It is really really good! I tested so far git add
, git checkout
, git log
, all behave perfectly.
Relative paths work too, i.e. if you are in a subfolder, <Tab>
will only suggest children, while ../<Tab>
will include parent and its nested files.
I completely agree with the proposed logic for injecting directories 👍
FYI: You can now customize terminal title with zstyle. Here are the defaults:
if (( P9K_SSH )); then
zstyle ':z4h' term-title-preexec '%n@%m: ${1//\%/%%}'
zstyle ':z4h' term-title-precmd '%n@%m: %~'
else
zstyle ':z4h' term-title-preexec '${1//\%/%%}'
zstyle ':z4h' term-title-precmd '%~'
fi
Values get prompt-expanded. In preexec
$1
is the original command and $2
is the same command after alias expansion.
If you explicitly set value to empty, title won't be set.
@maximbaz Heads up: I've changed the API for specifying terminal title. Here are the new defaults (the values are the same as before but the context is different):
zstyle :z4h:term-title:ssh precmd '%n@%m: %~'
zstyle :z4h:term-title:ssh preexec '%n@%m: ${1//\%/%%}'
zstyle :z4h:term-title:local precmd '%~'
zstyle :z4h:term-title:local preexec '${1//\%/%%}'
I did this after seeing how you've used the previous API and realizing that it causes terminal title flickering when you start zsh. The new API no longer requires you to check P9K_SSH
and thus you can place zstyle
definitions at the top of ~/.zshrc
above z4h init
. This will remove flickering.
Thanks, this is awesome 👍
Would it be possible to support <Alt-Up>
shortcut inside <Alt-Down>
? It happened to me a few times that I would press <Alt-Down>
and accidentally press Tab
on a wrong entry (so I enter a wrong subdir), and it's then annoying that I need to press Esc
, then go up with <Alt-Up>
, and then restart <Alt-Down>
from there. If I could just correct my mistake while I'm in <Alt-Down>
menu without all that hassle... 😄
That's a good idea. I like it.
Alt+Left should also work and it's a more natural choice to revert an accidental Tab on a wrong fzf
entry. E.g., if you intend to go into foo/
but accidentally press Tab on bar/baz/
, Alt+Left will take you back while Alt+Up will take you to bar/
.
For consistency, Alt+Right should also work: Alt+Left followed by Alt+Right should be a no-op. It's possible to make Alt+Down behave identically to Tab but this makes sense only within fzf
from Alt+Down and not within Tab completion or Ctrl+R (other Alt+Arrow bindings can be made to work consistency in all instances of fzf
).
I've added this to my TODO list. Hopefully will get to it once I'm done with the Real Life backlog.
Heads up: I've pointed stable
channel of zsh-syntax-highlighting
to my fork with a bunch of optimizations that make it about 2x faster. Once you run z4h update
, you'll be using this code. If you notice anything weird or broken w.r.t. syntax highlighting, let me know.
I've sent a PR upstream but there is no ETA yet.
P.S.
You can define the channel for a package like this:
zstyle ':z4h:zsh-syntax-highlighting' channel stable
Available channels:
none
: do not install the package.dev
: use master
branch from the upstream repo. This is generally not a good idea because z4h patching packages dynamically and these patches may not work when upstream is updated.testing
: use z4h-testing
branch from https://github.com/zsh4humans/zsh-syntax-highlighting. This normally points to some revision of the upstream repo and has no extra changes. This is the channel I use myself. After a while, if I don't discover any issues, I point z4h-stable
to the same commit (promote testing to stable).stable
: use z4h-stable
branch from https://github.com/zsh4humans/zsh-syntax-highlighting. This branch follows z4h-testing
with some delay. This is the recommended channel.There is also a special channel called command
that allows you to override the command used to install the package. For example, here's how you can instruct z4h
to use zsh-syntax-highlighting from a local repo that you've manually cloned to ~/zsh-syntax-highlighting
(I do this when testing local changes):
zstyle ':z4h:zsh-syntax-highlighting' channel command 'zf_ln -s -- ~/zsh-syntax-highlighting $Z4H_PACKAGE_DIR'
Channel changes come into effect only when you run z4h update
.
Some early feedback on ssh and history retrieval, based on your own dotfiles (I realize it is very early feedback):
To store history files locally I'd put them in some folder, not to clutter $HOME
(a good candidate could be $XDG_DATA_HOME
), so instead of:
zstyle -e ':z4h:ssh:*' retrieve-history 'reply=($ZDOTDIR/.zsh_history.${(%):-%m}:$z4h_ssh_host)'
I went for:
zstyle -e ':z4h:ssh:*' retrieve-history 'reply=($XDG_DATA_HOME/zsh-history/${z4h_ssh_host##*:})'
(this also cuts off my local hostname from the name of the file to make it a bit more clean)
When ssh'ing to a remote, I assume the point of this exercise is to upload the local copy of the history file back to the ~/.zsh_history
on remote, where remote $HISTFILE
will point. If so, in z4h-ssh-configure()
function instead of:
z4h_ssh_send_files[$file]='"$ZDOTDIR"/'${file:t}
I suppose it should be something like this:
z4h_ssh_send_files[$file]='"$ZDOTDIR"/.zsh_history'
This way the history is synchronized bi-directionally, I can edit the history file locally and on next ssh it will be uploaded and used.
This is an cool feature! One other interesting scenario it allows is to share a single history file with a set of remotes, e.g. all servers in one datacenter, which is pretty easy to implement in a custom z4h-ssh-configure()
.
To store history files locally I'd put them in some folder, not to clutter
$HOME
(a good candidate could be$XDG_DATA_HOME
), so instead of:zstyle -e ':z4h:ssh:*' retrieve-history 'reply=($ZDOTDIR/.zsh_history.${(%):-%m}:$z4h_ssh_host)'
I went for:
zstyle -e ':z4h:ssh:*' retrieve-history 'reply=($XDG_DATA_HOME/zsh-history/${z4h_ssh_host##*:})'
LGTM. You can replace ${z4h_ssh_host##*:}
with $z4h_ssh_host
. Hostnames cannot contain colon.
(I personally don't care about cluttering $HOME
.)
(this also cuts off my local hostname from the name of the file to make it a bit more clean)
I use several physical local machines for work and I store zsh history in a private Git repo. To avoid merge conflicts, local history on each machine is stored in ~/.zsh_history.${(%):-%m}
. On startup, all ~/.zsh_history*
files are read with fc -RI
. This means that commands I type on machine A are available in the command history when I'm working on machine B.
Remote history (commands that I run over ssh) is also stored in the private GIt repo. Once again, to avoid merge conflicts, the file names contain local host name: ~/.zsh_history.${(%):-%m}:$z4h_ssh_host
.
When ssh'ing to a remote, I assume the point of this exercise is to upload the local copy of the history file back to the
~/.zsh_history
on remote, where remote$HISTFILE
will point.
This will loose history when you open multiple simultaneous ssh connections, so it's now how I do it. Instead, I send over .zsh_history.*:$z4h_ssh_host
and read these files with fc -RI
on the remote host.
One other interesting scenario it allows is to share a single history file with a set of remotes, e.g. all servers in one datacenter
This will cause history loss and merge issues. What I do instead, is send multiple history files.
Here's how my configs work.
HISTFILE
is set in ~/.zshenv
: https://github.com/romkatv/dotfiles-public/blob/d4bd5ac92ae89eb3beb1c5e2bdb2df861cbab720/.zshenv#L9. This override kicks in only on the local host. On the remote host HISTFILE
is set by z4h to its default value of ~/.zsh_history
. Note that setting HISTFILE
is necessary only if you are using multiple local machines and want to synchronize history between them through a private Git repo.HISTFILE
is read on startup automatically, while extra history files are read manually in ~/.zshrc
: https://github.com/romkatv/dotfiles-public/blob/8334d8932eabddaf4569de4c3e617b2e911851b4/.zshrc#L68-L73.${(%):-%m}
in the file name is necessary only in the case of multiple local hosts with synchronized history.
function my-ssh-configure() {
emulate -L zsh
(( $+functions[z4h-ssh-configure] )) && z4h-ssh-configure
local -a servers=(foo bar)
local -a routers=(baz qux)
local -a hosts
case $z4h_ssh_host in
${~${(j:|:)routers}}) hosts=($routers);;
${~${(j:|:)servers}}) hosts=($servers);;
esac
local file
for file in $ZDOTDIR/.zsh_history.*:${^hosts}(N); do
(( $+z4h_ssh_send_files[$file] )) && continue
z4h_ssh_send_files[$file]='"$ZDOTDIR"/'${file:t}
done
}
zstyle ':z4h:ssh:*' configure my-ssh-configure
This is very flexible. If you like, you can send local history to some (or all) machines. You can assign more than one group to a machine. You can also define groups dynamically based on their host names (e.g., anything that matches `*.prod.microsoft.com` could be one group).
Basically, whenever you work on machine X (be it local or remote), you can make history from any set of machines available on it. It's up to you to decide what that set should be.
- All history for host X is sent to host X with this code: https://github.com/romkatv/dotfiles-public/blob/8334d8932eabddaf4569de4c3e617b2e911851b4/.zshrc#L84-L90.
You might wonder what the point of this might be. Doesn't host X already have all its history in ~/.zsh_history
?
The answer is that host X may have been reimaged. The only storage I consider persistent is Git, so history is ultimately stored in a Git repo. You can reimage a local or a remote machine and still have all configs, history, etc.
Heads up: User rc files no longer get compiled by default. If you want them compiled to speed up Zsh startup slightly, add this to .zshrc
:
z4h compile -- $ZDOTDIR/{.zshenv,.zprofile,.zshrc,.zlogin,.zlogout}
See z4h help compile
for caveats.
Heads up: Widgets that change the current working directory are now bound to Shift+Arrows and cd-key
zstyle no longer exists. If you want to keep using Alt+Arrows, add this to your .zshrc
:
z4h bindkey z4h-cd-back Alt+Left
z4h bindkey z4h-cd-forward Alt+Right
z4h bindkey z4h-cd-up Alt+Up
z4h bindkey z4h-cd-down Alt+Down
You can also use plain bindkey
. z4h bindkey
is syntax sugar.
Thank you very much for the detailed replies, very interesting and educational! I didn't know about fc -RI
, merging history files makes a lot of sense, your workflow is indeed very flexible.
Just out of curiosity, how do you keep zsh history in git repo, do you have a periodic timer that executes git commit
, git pull
, git push
on all your physical local machines?
Heads up: I've pointed stable channel of zsh-syntax-highlighting to my fork with a bunch of optimizations that make it about 2x faster.
This works well, the only tiny regression I noticed is that newly installed binaries are not colored properly even after a rehash
call. For example, add this to your .zshrc
:
install-foobar() {
echo '#!/bin/sh\necho foobar' > ~/bin/foobar
chmod +x ~/bin/foobar
rehash
}
(Assuming ~/bin
exists and is in your $PATH)
Then in your terminal type:
$ install-foobar
$ foobar
Notice that foobar
is colored in red, while on the upstream zsh-syntax-highlighting
the color is correct after rehash
call.
Heads up: Widgets that change the current working directory are now bound to Shift+Arrows
Out of curiosity, why? Are you planning some other functionality to Alt+Arrows?
You can also use plain bindkey. z4h bindkey is syntax sugar.
This is a neat syntax sugar! Helpful because otherwise you always end up writing a comment with human-readable key combination.
Not very important and might be out of scope, the only binding I couldn't convert to z4h bindkey
is the one that consists of two consequent keypresses, namely ^V^V
, I wonder if this should be supported as z4h bindkey edit-command-line Ctrl+V Ctrl+V
?
Just out of curiosity, how do you keep zsh history in git repo, do you have a periodic timer that executes
git commit
,git pull
,git push
on all your physical local machines?
I have two Git repos where I store my stuff: dotfiles-public and dotfiles-private. Both are overlaid over $HOME
(that is, their worktree is $HOME
), so I can version any file without moving or symlinking it. I sync with this: https://github.com/romkatv/dotfiles-public/blob/master/dotfiles/functions/sync-dotfiles, which I run manually. It synchronizes both repos.
The point of using a separate file for history on every machine is twofold. The first reason is that it gives local history precedence (when I hit Ctrl+R, commands that I ran on machine A are displayed before the commands from machine B). The second reason is that it avoids merge conflicts (every history files is modified only on one machine, so no conflicts when pulling).
My private Git repo doesn't contain just history. There are also various configs and scripts that I don't want to share publicly.
There are a few more important bits to my dotfiles management:
my_git_repo
prompt segment: https://github.com/romkatv/dotfiles-public/blob/8784b2702621002172ecbe91abe27d5c62d95efb/.p10k.zsh#L45-L52toggle-dotfiles
zle widget: https://github.com/romkatv/dotfiles-public/blob/master/dotfiles/functions/toggle-dotfilestoggle-dotfiles
: https://github.com/romkatv/dotfiles-public/blob/8334d8932eabddaf4569de4c3e617b2e911851b4/.zshrc#L115-L118When I press Ctrl+P once, I get public
showing up in prompt and Git status in prompt corresponds to dotfiles-public repo. All git
commands also target this repo. So if I'm in ~/foo/bar
and want to add ./baz
to dotfiles-public, I hit Ctrl+P and type git add baz
, git commit
, etc. If I hit Ctrl+P another time, it activates dotfiles-private. Another Ctrl+P gets me to normal state.
It's very convenient. It's much better than anything else I've tried.
Heads up: I've pointed stable channel of zsh-syntax-highlighting to my fork with a bunch of optimizations that make it about 2x faster.
This works well, the only tiny regression I noticed is that newly installed binaries are not colored properly even after a
rehash
call. For example, add this to your.zshrc
:install-foobar() { echo '#!/bin/sh\necho foobar' > ~/bin/foobar chmod +x ~/bin/foobar rehash }
(Assuming
~/bin
exists and is in your $PATH)Then in your terminal type:
$ install-foobar $ foobar
Notice that
foobar
is colored in red, while on the upstreamzsh-syntax-highlighting
the color is correct afterrehash
call.
Cannot reproduce. I get green foobar
.
Can you confirm that you get red foobar
if you follow your own instructions? What if you run z4h update
first?
Heads up: Widgets that change the current working directory are now bound to Shift+Arrows
Out of curiosity, why? Are you planning some other functionality to Alt+Arrows?
I want to release v3 next week, so I'm cleaning things up and making sure the basics work with the default .zshrc
. Alt+Arrows is the most natural key binding for z4h-cd-*
widgets for PC users because they mimic back/forward button bindings in web browsers and a few other apps. Unfortunately, Mac users are accustomed to having Alt+Arrows (or rather Option+Arrows) moving the cursor by words. This functionality is more important than z4h-cd-*
widgets, so if I have to choose which one works by default I'll go with the cursor movement.
I think I've already mentioned that v4 will have a wizard that will take care of configuring key bindings. This is still my plan.
Not very important and might be out of scope, the only binding I couldn't convert to
z4h bindkey
is the one that consists of two consequent keypresses, namely^V^V
, I wonder if this should be supported asz4h bindkey edit-command-line Ctrl+V Ctrl+V
?
The current version of z4h bindkey
supports a small subset of key combinations supported by builtin bindkey
. I'll add new ones later. That said, I just added two more, including the one you are asking for.
z4h bindkey edit-command-line 'Ctrl+V Ctrl+V'
Note the quotes. z4h bindkey
allows you to bind more than one key sequence at once, so without quotes it's also a valid call.
# Bind Ctrl+E and End to end-of-line.
z4h bindkey end-of-line Ctrl+E End
I've also added support for single-key bindings.
z4h bindkey z4h-cd-up 'Ctrl+X U'
Note that z4h bindkey
automatically defines lower and upper case bindings, so Ctrl+X U
is translated to ^XU
and ^Xu
.
If you are binding Ctrl+V Ctrl+V, you might want to delete the standard Ctrl+V binding with bindkey -r '^V'
. Otherwise pressing Ctrl+V will be ambiguous, so zsh will wait for 10ms * KEYTIMEOUT
(this is 200ms by default in z4h) for you to press Ctrl+V a second time. If you don't do it fast enough, you'll get quoted-insert
. Vi keymap users have to live with this (Esc is a prefix of pretty much every key binding) but emacs keymap users can quite easily avoid defining bindings with a shared prefix. I find Ctrl+V useful, so if I wanted to to have a binding for edit-command-line
, I'd choose something other than Ctrl+V Ctrl+V (I'd most likely choose Alt+Something because it's easy to press and there are many free keys not bound to anything with Alt).
@maximbaz Try updating z4h and removing this line from your config: https://github.com/maximbaz/dotfiles/blob/2a20b995bc9495a1866a1fd1faf98d71fa73dbff/.zshrc#L6
zstyle ':z4h:ssh:router' passthrough yes
Does it still work as before?
I'd like to have function ssh() { z4h ssh "$@" }
in the default .zshrc
, so I've implemented automatic passthrough logic so that ssh
just works without requiring manual zstyle ... passthrough yes
overrides.
Cannot reproduce. I get green foobar.
My apologies, I made a mistake while preparing the test example. Turns out the key here is that rehash
is called in a subshell, i.e. the example should have used regular braces like so:
install-foobar() (
echo '#!/bin/sh\necho foobar' > ~/bin/foobar
chmod +x ~/bin/foobar
rehash
)
To be honest it calls for refactoring on my side (there is really no reason for me to use subshell), but this still might be worth solving.
if I wanted to to have a binding for edit-command-line, I'd choose something other than Ctrl+V Ctrl+V (I'd most likely choose Alt+Something because it's easy to press and there are many free keys not bound to anything with Alt).
Good point... :D
Try updating z4h and removing this line from your config: Does it still work as before?
Not nicely, I can connect but I get a few lines of syntax error (line 1 column 5)
when I ssh to my router if I remove this line.
My apologies, I made a mistake while preparing the test example. Turns out the key here is that
rehash
is called in a subshell
rehash
in a subshell has no effect.
So the regression in my zsh-syntax-highlighting fork is the following:
% touch ~/bin/foobar
% chmod +x ~/bin/foobar
% foobar
The original zsh-syntax-highlighting highlights foobar
in green on the last line. My fork highlights it in red. I'll find a way to fix this.
Not nicely, I can connect but I get a few lines of
syntax error (line 1 column 5)
when I ssh to my router if I remove this line.
I need to fix this but this error message isn't enough to figure out what the problem is.
Could you add set -x
at the top of $Z4H/zsh4humans/fn/-z4h-cmd-ssh
and $Z4H/zsh4humans/sc/ssh-bootstrap
(under the shebang), restart zsh and run ssh router
?
There you go: https://paste.maximbaz.com/f5d5f7f501ef
UPDATE: wait, did't add set -x
to the second file, stand by...
UPDATE 2: adding set -x
didn't make any difference but I updated the link with the new output anyway, you can look at it now
+-z4h-cmd-ssh:266> ssh -T router '/bin/cat >/tmp/z4h-ssh.maximbaz.1999541.1598085653.13144' syntax error (line 1 column 5)
Interesting. Does the following command also produce this error?
command ssh router '/bin/cat >/dev/null' </dev/null
Do you know why? What is the login shell on router
?
+-z4h-cmd-ssh:266> ssh -T router '/bin/cat >/tmp/z4h-ssh.maximbaz.1999541.1598085653.13144' syntax error (line 1 column 5)
Interesting. Does the following command also produce this error?
command ssh router '/bin/cat >/dev/null' </dev/null
Do you know why? What is the login shell on
router
?
Hi @romkatv , we got the same router with @maximbaz . Yes the above command produce the same error.
I don't know exactly what shell it is, but it is a mikrotik router with the last RouterOS (6.47).
$ command ssh router '/bin/cat >/dev/null' </dev/null
syntax error (line 1 column 5)
Hey @romkatv! I'm giving z4h/v3 a spin, and would like to share some ideas and ask some questions at the same time 🙂
You mentioned it is possible to add an option to traverse hidden folders as well when
glob_dots
is set, could you please add it or give me a hint how to do this locally?Turns out I edit files in hidden folders too often, it would be helpful if fzf included everything.
This was a great hint, just as one idea it allows to ignore
.zwc
files, for example withzstyle ':completion:*:*:kak:*:*' ignored-patterns '*.zwc'
opening.p10k.zsh
in editor is simpler, askak .p<Tab>
now has only one match instead of two, sofzf
doesn't show up at all and I go straight to.p10k.zsh
.Would be awesome! Could you please suggest what would be a proper
zstyle
syntax that you want to support for ignoring folders forcd
? Say for example I wanted to ignore~/.cache/
and~/.docker/
and everything underneath them.Suppose I just cloned
zsh4humans
, I press<Alt-Down>
and I want to navigate tozsh4humans/fn
. Typingz fn
will find it, but typingzfn
will not. I am used to just typing infzf
without having to think where I should put spaces... Could we at least have an option, if the current behavior is something you prefer?By the way, while
<Alt-Down>
showsBut
cd <Tab>
showsBoth work, but the latter is less aesthetic :)
fzf-tab
continuous-trigger
Am I right that it will become unnecessary when everything will be traversed? If so, this could be cleaned up I presume:
https://github.com/romkatv/zsh4humans/blob/6af0bfc8727b5e5ed72643ce5c457e9cbc513afb/.zshrc#L56-L58
Or I'm doing something wrong :) What I expected to work is to type
$ cp .zsh<Tab>
and pressCtrl+Space
to select more than one entryHave you considered adding groups and using different colors to fzf completion? I have some half-broken leftovers from prezto, but they will suffice to show a difference:
Current style:
With grouping and coloring:
I don't care about group names too much, we could just hide them, but I do think coloring different types of autocomplete is useful and nice.
I would suggest to rephrase this a little, let me give you an example when editing those files makes sense: it is a nice and easy way to start GUI. Here's how I do it:
.zprofile
I have[[ -z $DISPLAY && "$(tty)" == "/dev/tty1" ]] && exec sway
.zshenv
I have a list of environment variables that I want to be exported both in terminal and also for GUI.zshenv
is loaded before.zprofile
, so whatever I export in.zshenv
is visible bysway
and all GUI apps.zshrc
I have only setup that is relevant for terminal, but not GUILet me know if this makes sense. Right now I simply put all my variables on top of
.zshenv
generated byz4h
, but I'm wondering if the README needs to be adjusted to relax the bold phrase a bit, maybe just say that everything thatz4h
generates must be preserved without modifications?XDG_CACHE_HOME
I would suggest to drop this line:
https://github.com/romkatv/zsh4humans/blob/6af0bfc8727b5e5ed72643ce5c457e9cbc513afb/.zshenv#L11
I believe every software knows how to fallback to
$HOME/.cache
in the absence of that variable, and it should be a very conscious user decision to export all thoseXDG_*
variables, let's not do it for them.TIMEFMT='user=%U system=%S cpu=%P total=%*E'
toz4h
First saw it in your dotfiles, and instantly fell in love. I believe it's exactly one of those things that makes
zsh
for humans and should be done for everyone 🙂Because transient prompt (often) removes the timestamp of when a command was started, you once recommended me to add the time to the terminal title instead, to be able to see how long the command is already running. I still think it was a great idea, might be worth adding directly to
z4h
? Time is only needed for when a command is running (not when title shows current dir), and maybe only when transient prompt is enabled (but I'd probably just show the time always when command is running). What do you think?LS_COLORS
Have you seen this project? https://github.com/trapd00r/LS_COLORS
I think it would be very cool to tap into their work and have an integration with it, just like you do with
zsh-syntax-highlight
and others. Currently I can source their file and it will affect myls
, but I don't think tab completion respects this variable, maybe because colors are set immediately after a built-inLS_COLORS
is defined?https://github.com/romkatv/zsh4humans/blob/6af0bfc8727b5e5ed72643ce5c457e9cbc513afb/fn/-z4h-init#L392
I have had a few small snippets taken from here and there that I use quite frequently, want to show them and see if you would want to take anything directly in
z4h
:Ctrl+/
and it will toggle comment for the currently typed command, just like in VS Code and other editors :)Ctrl+V,V
to edit the current command in EDITOR, very useful for multi-line commandsCtrl+Z
toggle pressingCtrl+Z
andfg
, very useful for quickly checking terminal behind an editor and going back to the editorThat's enough for now 😄
UPDATE: OK, one more 😁 Have you considered to add these options to the list of default options in
z4h
?HIST_IGNORE_ALL_DUPS
andHIST_SAVE_NO_DUPS
to keep the history file smaller?RC_QUOTES
to allow'Henry''s Garage'
instead of'Henry'\''s Garage'
?