I tend to have multiple windows in a tmux session, and run long processes (such as database migrations and complex queries, etc) in one window while I keep focus on another.
I'd like to set something up to flag the window when the process is finished. I plan to do this with zsh functions, but I'm having trouble finding the command to set a bell on a given tmux window. I looked at set-window-option and I found window_flag but I don't know how to set window_flag
How can I set and clear an indicator for a given tmux window via a shell command?
In order to send a bell to any terminal, including tmux, you just have to print \a to the terminal. You can use echo '\a' or print '\a' for that. (On shells other than ZSH you might need to use echo -e '\a' or printf '\a' instead.)
If a bell occurred in a window tmux adds a ! to the window name in the status bar. Activating a window automatically removes the bell flag, that also means that a the flag will not be set if the bell occurs in the currently active window.
You can also set a separate style for these windows in the status line with window-status-bell-style option (the default seems to be reverse, that is switching fore- and background colors).
Additionally you can get tmux to show a short message if a bell occurred by setting the bell-action option to any.
I have the following in my ~/.zshrc to ring the bell, if a process takes at least 60 seconds to finish:
autoload -Uz add-zsh-hook
typeset -i LONGRUNTIME=60
save_starttime () {
starttime=$SECONDS
}
set_longrunning_alert () {
if ((LONGRUNTIME > 0 && SECONDS - starttime >= LONGRUNTIME)); then
print "\a"
fi
}
add-zsh-hook preexec save_starttime
add-zsh-hook precmd set_longrunning_alert
Related
I often use ^Z to make sleep a process, possibly open a new one, make this one sleep too, and so on, also moving between different Tmux windows.
So what I would like, is that the Tmux status bar update relevantly to indicate me how many processes are sleeping in the currently focused window.
Is that possible?
This is a common question - how to pass information from a shell inside tmux to tmux. The easiest way to do this is to have your shell do it as part of PS1 or some other variable that is evaluated when the prompt is printed.
There are two things you can do:
1) Set a user option with tmux set -w #myoption xyz, then you can use it in the status line with #{#myoption}. This has the disadvantage that it cannot work over ssh.
2) Set the pane title using the escape sequence, for example: printf "\033]2;xyz\033\\". It is then available in #{pane_title}. This works over ssh but had the disadvantage that there is no way to prevent applications also changing the title if they want.
In either case you will only want to run this when TMUX is set, so something like:
[ -n "$TMUX" ] && tmux set -w #myoption $(jobs|wc -l)
TLDR;
I need a way for .zshrc to automatically be sourced each time a command is executed. PROMPT needs to be updated each time a command is executed in order to show relevant information in the prompt.
Reason
I use Watson cli for tracking time. On my previous bash setup, I prepended my prompt ($PS1) with a symbol that indicates whether the timer is running or not (red/green). I have mimicked this functionality with Oh My Zsh, as follows (in the theme file):
WATSON_DIR="$HOME/Library/Application Support/watson"
watson_status() {
local txtred="${fg_bold[red]}"
local txtgrn="${fg_bold[green]}"
local txtrst="${reset_color}"
# Started
local status_color="$txtgrn"
# Stopped
if [[ $(cat "$WATSON_DIR/state") == '{}' ]]; then
status_color="$txtred"
fi
echo -e "$status_color""◉""$txtrst"
}
PROMPT="╭── %{$(watson_status) $fg_bold[green]%}%~%{$reset_color%}$(git_prompt_info) ⌚ %{$FG[130]%}%*%{$reset_color%}
╰─➤ $ "
Current issue
The icon will indicate the color of the state at the time that .zshrc was executed. For example, if the timer is running and the icon is properly indicating green, stopping the timer will not cause the icon to turn red. In order to see the icon change color, I have to source .zshrc.
This indicates that the function watson_status() needs to be run each time a command is executed, to give the latest status at the time of the command
I recently ported some prompt code from bash to zsh - on OSX Big Sur - and these were the two big "things to know" for me:
add setopt PROMPT_SUBST to your .zshrc. This "allows for functions in the prompt"
use single quotes when defining your PS1 / PROMPT. If you use double quotes, then the whole string will be evaluated once when the terminal starts, and then that evaluated value is "re-executed" every time the command changes. But you want the functions to be re-evaluated, not the output of the function at the time when the terminal is created.
Easiest example I used to confirm I had it working:
# print_epoch() { date '+%s' }
# this is what you want
# export PS1='$(print_epoch) > '
# this is not what you want
# export PS1="$(print_epoch) > "
Also worth noting PS1 and PROMPT are interchangeable
Extra:
While we are sharing here is my fairly minimal echo my user, directory, and git branch PROMPT with some colors and a pretty leaf:
parse_git_branch() {
git branch 2> /dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/\1/'
}
setopt PROMPT_SUBST
autoload -U colors && colors
export PROMPT='%n %~ %F{blue}🌿$(parse_git_branch)%f > '
noting that:
%n prints name
%~ prints directory relative to home
%F{blue} changes text to blue
%f resets color
Documentation: http://zsh.sourceforge.net/Doc/Release/Prompt-Expansion.html#Visual-effects
I discovered this little navigation trick the other day, which allows me to trigger menu completions by number when I enter 'cd -'
~ cd -
0 -- ~/home
1 -- ~/home/stuff
2 -- ~/downloads
3 -- ~/wallpaper
Shell scripting syntax still reads like a foreign language to me, but to get this functionality my directory stack history is piped into the function below.
DIRSTACKSIZE=9
DIRSTACKFILE=~/.zdirs
if [[ -f $DIRSTACKFILE ]] && [[ $#dirstack -eq 0 ]];
then dirstack=( ${(f)"$(< $DIRSTACKFILE)"} )
[[ -d $dirstack[1] ]] && cd $dirstack[1] && cd $OLDPWD
fi
chpwd() {
print -l $PWD ${(u)dirstack} >$DIRSTACKFILE
}
The magical part is being able to choose from the list by number, but I have come to learn that this is probably
because the functionality for navigation by number is baked in to the 'cd -' command. Still, I'd like to use this everywhere.
Any tips writing a wrapper function (or something like that, I guess) for the completion menu that pipes in completions from the menu when it is triggered
and displays them in a numbered list where those numbers select the corresponding element?
I've gotten started reading the manual and what not, but everything remains rather opaque. Thanks!
First off, the code snippet you show has nothing to do with completion. Instead, what it does is to record the directory stack to a file in order to preserve it between zsh sessions. (Personally, I'm not even sure this is a good idea.)
A good place to start investigating zsh completions is the _complete_help ZLE widget. This is bound by default to ^Xh in zsh's viins (vi insert) keyboard map, but is unbound by default in the emacs keymap. If you want to use it in the emacs keymap (the default for many people), you have to bind it:
bindkey -M emacs "^Xh" _complete_help
Now you can type cd - (or cd +) and follow it by CTRL-Xh instead of TAB. You should see the following output:
tags in context :completion::complete:cd::
directory-stack (_directory_stack _cd)
(At this point I'll admit we're getting close to the limits of my knowledge of the zsh completion system.)
Now you can see the completer functions for the directory-stack tag in this particular context. The one you're probably interested in is _directory_stack, and you can see the content of that function with:
functions _directory_stack
…which is where those leading numbers are actually generated.
Arguably it's possible to write similar completion functions for other completion contexts, and apply the using zstyle. However, this is non-trivial completion magic, and beyond anything I have attempted
I want tmux pane title to refresh immediately after I start htop, so I added alias in my config.fish:
alias h "htop;tmux refresh-client -S"
But it does nothing. I also tried with delay:
alias h "htop;sleep 0.1;tmux refresh-client -S"
That also did nothing - tmux still refreshes only after default interval, which is too long for me, and you can only decrease it to 1 second and not less.
What did I do wrong and is it even possible what I want to do?
Maybe this is a bit easier to see when we remove the alias from the equation:
echo banana; sleep 5s; echo sausage
will echo "banana", wait for 5 seconds and only then print "sausage", so
htop; tmux refresh-client -S
will run htop, wait until it is finished and then run tmux refresh-client -S, at which point fish will be the foreground process again.
What would have to be done instead is to get the shell to integrate with tmux. Now, apparently tmux has an escape sequence for Names and titles, so
printf '\ekhtop\e\\' # \e is \033 - the escape character
changes the window title to "htop".
Fish has events that functions can be bound to, so something like
function tmux_name --on-event fish_preexec
printf '\ek%s\e\\' "$argv" # the argument for preexec is the commandline about to be executed
end
will set the tmux window name always to the command line. This won't reset it when the command has finished, so we need a second function
function tmux_reset_name --on-event fish_postexec
# $argv for postexec is also the commandline
# so we can't use it. Just hardcode "fish".
printf '\ek%s\e\\' fish
end
Not that this is perfect or anything - it'll still set the title even for very short-running commands, it'll use the full commandline even for long commands (maybe using just $argv[1] would be better).
Note that these functions will have to be defined in config.fish or a file explicitly sourced by it (or ~/.config/fish/conf.d/), because function files are autoloaded, so fish won't know about the event.
I know that I can exec a date command in my zsh prompt.
However, it shows the old time; to see the current time, I have to hit <return> and get a new prompt with the current time.
Is there a way to configure the zsh prompt to constantly update itself every second?
Note: I wrote this answer for a similar question, but seeing how this question has more views I think reposting my answer here would be useful.
This is in fact possible without resorting to strange hacks. I've got this in my .zshrc
RPROMPT='[%D{%L:%M:%S %p}]'
TMOUT=1
TRAPALRM() {
zle reset-prompt
}
The TRAPALRM function gets called every TMOUT seconds (in this case 1), and here it performs a prompt refresh, and does so until a command starts execution (and it doesn't interfere with anything you type on the prompt before hitting enter).
Source: http://www.zsh.org/mla/users/2007/msg00944.html (It's from 2007!)
Sounds like a pleasant request to me. If anything it makes more sense than showing the time when the prompt was displayed.
Fortunately Peter Stephenson posted a technique. Try something like this in .zshrc:
PROMPT="[%T] %n#%M %~ %# "
schedprompt() {
emulate -L zsh
zmodload -i zsh/sched
# Remove existing event, so that multiple calls to
# "schedprompt" work OK. (You could put one in precmd to push
# the timer 30 seconds into the future, for example.)
integer i=${"${(#)zsh_scheduled_events#*:*:}"[(I)schedprompt]}
(( i )) && sched -$i
# Test that zle is running before calling the widget (recommended
# to avoid error messages).
# Otherwise it updates on entry to zle, so there's no loss.
zle && zle reset-prompt
# This ensures we're not too far off the start of the minute
sched +30 schedprompt
}
schedprompt
This would be .... unpleasant in a standard zsh prompt (or bash, or other shells).
I suggest you'd be better off using Gnu Screen.
Screen can have a status line which can show the time.
Here's an example screenrc scroll down to "Red Hat Magazine A guide to GNU Screen" to see the sample (i'll reproduce that here) which will, when screen is run, show the current time in the lower right corner of the terminal:
~/.screenrc
hardstatus alwayslastline
hardstatus string '%{= kG}[ %{G}%H %{g}][%= %{=kw}%?%-Lw%?%{r}(%{W}%n*%f%t%?(%u)%?%{r})%{w}%?%+Lw%?%?%= %{g}][%{B}%Y-%m-%d %{W}%c %{g}]'
# Default screens
screen -t shell1 0
screen -t shell2 1
http://www.gnu.org/software/screen/