Skip to content

Commit

Permalink
Merge pull request #199 from jmeurin/reuse
Browse files Browse the repository at this point in the history
Reuse
  • Loading branch information
greymd authored Jan 9, 2024
2 parents 5769b47 + f5ea4e9 commit 5972625
Show file tree
Hide file tree
Showing 9 changed files with 372 additions and 224 deletions.
360 changes: 180 additions & 180 deletions .github/workflows/test.yml

Large diffs are not rendered by default.

12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ OPTIONS:
mh main-horizontal
mv main-vertical
-n <number> Set the maximum number of <argument> taken for each pane.
-r Create or reuse the existing panes.
-s Speedy mode: Run command without opening an interactive shell.
-ss Speedy mode AND close a pane automatically at the same time as process exiting.
-S <socket-path> Set a full alternative path to the server socket.
Expand Down Expand Up @@ -326,6 +327,17 @@ When the tmux is already open and `xpanes` is executed on the existing tmux sess
- In addition, it separates the window into multiple panes.
- Finally, the window will be active.

You can use the -x option to create the panes in the current window.

You can use the -r option to create panes in the current window, or reuse the
existing ones. It is useful if you want to rerun the same xpanes command over
and over, e.g.:

xpanes -r -B 'ssh `host`' -c '`program> {}`' server client

will open 2 new panes. When the host reboots, you can rerun the same command
and the existing panes will be reused.

### [Pipe mode] Inside of tmux session & Accepting standard input

When `xpanes` accepts standard input (i.e, `xpanes` follows another command and pipe `|`) under **Normal mode2** , `xpanes`'s behavior is going to be the special one called "Pipe mode".
Expand Down
126 changes: 84 additions & 42 deletions bin/xpanes
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ XP_MAX_PANE_ARGS=""
XP_OPT_SET_TITLE=0
XP_OPT_CHANGE_BORDER=0
XP_OPT_EXTRA=0
XP_OPT_REUSE=0
XP_OPT_SPEEDY=0
XP_OPT_SPEEDY_AWAIT=0
XP_OPT_USE_PRESET_LAYOUT=0
Expand Down Expand Up @@ -199,6 +200,7 @@ OPTIONS:
mh main-horizontal
mv main-vertical
-n <number> Set the maximum number of <argument> taken for each pane.
-r Reuse the existing panes, or create then in the current active window.
-s Speedy mode: Run command without opening an interactive shell.
-ss Speedy mode AND close a pane automatically at the same time as process exiting.
-S <socket-path> Set a full alternative path to the server socket.
Expand Down Expand Up @@ -1438,7 +1440,11 @@ xpns_pre_execution() {
local _args4args=""

if [[ ${XP_OPT_EXTRA} -eq 1 ]]; then
xpns_log "error" "'-x' must be used within the running tmux session."
xpns_log "error" "'-x' must be used on the running tmux session."
exit ${XP_EINVAL}
fi
if [[ ${XP_OPT_REUSE} -eq 1 ]]; then
xpns_log "error" "'-r' must be used on the running tmux session."
exit ${XP_EINVAL}
fi

Expand Down Expand Up @@ -1571,14 +1577,29 @@ xpns_execution() {
xpns_suppress_allow_rename "${_def_allow_rename-}"
XP_CMD_UTILITY="$(xpns_get_joined_begin_commands "${XP_CMD_UTILITY}")"

if [[ ${XP_OPT_REUSE} -eq 1 ]]; then
_window_name="$( ${TMUX_XPANES_EXEC} display -p -F "#{window_id}")"
_pane_count="$( ${TMUX_XPANES_EXEC} list-panes | grep -c .)"
_pane_base_index=$(($(${TMUX_XPANES_EXEC} display -p -F "#{pane_index}") + 1))
_pane_active_pane_id=$(${TMUX_XPANES_EXEC} display -p -F "#{pane_id}")

# Reusing the panes: if there's only 1 pane, behave like -x otherwise
# make sure there are enough panes of higher index than the current one.
# This allows to run -r to create the panes and then repeatedly to reuse
# those panes.
if [[ $((_pane_count - _pane_base_index)) -lt ${#XP_ARGS[@]} ]]; then
xpns_log "error" "Not enough panes ($((_pane_count - 1))) after this one to run the ${#XP_ARGS[@]} commands."
exit ${XP_EINVAL}
fi
fi
if [[ ${XP_OPT_EXTRA} -eq 1 ]]; then
# Reuse existing window name
# tmux 1.6 does not support -F option
_window_name="$( ${TMUX_XPANES_EXEC} display -p -F "#{window_id}")"
_pane_count="$( ${TMUX_XPANES_EXEC} list-panes | grep -c .)"
_pane_base_index=$((_pane_base_index + _pane_count - 1))
_pane_active_pane_id=$(${TMUX_XPANES_EXEC} display -p -F "#{pane_id}")
else
elif [[ ${XP_OPT_REUSE} -eq 0 ]]; then
_window_name=$(
xpns_generate_window_name \
"${XP_EMPTY_STR}" \
Expand All @@ -1590,43 +1611,45 @@ xpns_execution() {
## --------------------
# Prepare window and panes
## --------------------
if [[ ${XP_OPT_EXTRA} -eq 1 ]]; then
xpns_prepare_extra_panes \
"${_window_name}" \
"${_pane_base_index}" \
"${XP_OPT_LOG_STORE}" \
"${XP_OPT_SET_TITLE}" \
"${XP_OPT_SPEEDY}" \
"${XP_OPT_SPEEDY_AWAIT}" \
"${XP_OPT_INTERVAL}" \
"${XP_REPSTR}" \
"${XP_CMD_UTILITY}" \
"${XP_ARGS[@]}"
elif [[ ${XP_OPT_USE_PRESET_LAYOUT} -eq 1 ]]; then
xpns_prepare_preset_layout_window \
"${_window_name}" \
"${_pane_base_index}" \
"${XP_OPT_LOG_STORE}" \
"${XP_OPT_SET_TITLE}" \
"${XP_OPT_ATTACH}" \
"${XP_OPT_SPEEDY}" \
"${XP_OPT_SPEEDY_AWAIT}" \
"${XP_OPT_INTERVAL}" \
"${XP_REPSTR}" \
"${XP_CMD_UTILITY}" \
"${XP_ARGS[@]}"
elif [[ ${XP_OPT_USE_PRESET_LAYOUT} -eq 0 ]]; then
xpns_prepare_window \
"${_window_name}" \
"${XP_OPT_LOG_STORE}" \
"${XP_OPT_SET_TITLE}" \
"${XP_OPT_ATTACH}" \
"${XP_OPT_SPEEDY}" \
"${XP_OPT_SPEEDY_AWAIT}" \
"${XP_OPT_INTERVAL}" \
"${XP_REPSTR}" \
"${XP_CMD_UTILITY}" \
"${XP_ARGS[@]}"
if [[ ${XP_OPT_REUSE} -eq 0 ]]; then
if [[ ${XP_OPT_EXTRA} -eq 1 ]]; then
xpns_prepare_extra_panes \
"${_window_name}" \
"${_pane_base_index}" \
"${XP_OPT_LOG_STORE}" \
"${XP_OPT_SET_TITLE}" \
"${XP_OPT_SPEEDY}" \
"${XP_OPT_SPEEDY_AWAIT}" \
"${XP_OPT_INTERVAL}" \
"${XP_REPSTR}" \
"${XP_CMD_UTILITY}" \
"${XP_ARGS[@]}"
elif [[ ${XP_OPT_USE_PRESET_LAYOUT} -eq 1 ]]; then
xpns_prepare_preset_layout_window \
"${_window_name}" \
"${_pane_base_index}" \
"${XP_OPT_LOG_STORE}" \
"${XP_OPT_SET_TITLE}" \
"${XP_OPT_ATTACH}" \
"${XP_OPT_SPEEDY}" \
"${XP_OPT_SPEEDY_AWAIT}" \
"${XP_OPT_INTERVAL}" \
"${XP_REPSTR}" \
"${XP_CMD_UTILITY}" \
"${XP_ARGS[@]}"
elif [[ ${XP_OPT_USE_PRESET_LAYOUT} -eq 0 ]]; then
xpns_prepare_window \
"${_window_name}" \
"${XP_OPT_LOG_STORE}" \
"${XP_OPT_SET_TITLE}" \
"${XP_OPT_ATTACH}" \
"${XP_OPT_SPEEDY}" \
"${XP_OPT_SPEEDY_AWAIT}" \
"${XP_OPT_INTERVAL}" \
"${XP_REPSTR}" \
"${XP_CMD_UTILITY}" \
"${XP_ARGS[@]}"
fi
fi

## With -ss option, it is possible to close all the panes as of here.
Expand Down Expand Up @@ -1698,7 +1721,7 @@ xpns_execution() {

## With -l <layout>, panes are organized.
## As well as -x, they are re-organized.
if [[ $XP_OPT_USE_PRESET_LAYOUT -eq 1 ]] || [[ ${XP_OPT_EXTRA} -eq 1 ]]; then
if [[ (${XP_OPT_USE_PRESET_LAYOUT} -eq 1 || ${XP_OPT_EXTRA} -eq 1) && ${XP_OPT_REUSE} -eq 0 ]]; then
xpns_organize_panes \
"${_window_name}" \
"${_last_args_idx}"
Expand All @@ -1723,8 +1746,8 @@ xpns_execution() {
pane-border-status "${TMUX_XPANES_PANE_BORDER_STATUS}"
fi

# In case of -x, this statement is skipped to keep the original window name
if [[ ${XP_OPT_EXTRA} -eq 0 ]]; then
# In case of -x or -r, this statement is skipped to keep the original window name
if [[ ${XP_OPT_EXTRA} -eq 0 ]] && [[ ${XP_OPT_REUSE} -eq 0 ]]; then
# Restore original window name.
${TMUX_XPANES_EXEC} \
rename-window -t "${_window_name}" \
Expand Down Expand Up @@ -1987,6 +2010,7 @@ xpns_parse_options() {
exit 0
;;
-x)
# If you modify those, make similar changes to -r
XP_OPTIONS+=("$opt")
XP_OPT_EXTRA=1
XP_OPT_USE_PRESET_LAYOUT=1 ## Layout presets must be used with -x
Expand All @@ -2013,6 +2037,24 @@ xpns_parse_options() {
sleep 1
fi
;;
-r)
XP_OPTIONS+=("$opt")
# Reusing the panes: if there's only 1 pane, behave like -x
# This allows to run -r to create the panes and then repeatedly to reuse
# those panes.
_pane_count="$( ${TMUX_XPANES_EXEC} list-panes | grep -c .)"
if [[ ${_pane_count} -eq 1 ]]; then
XP_OPT_EXTRA=1
XP_OPT_USE_PRESET_LAYOUT=1 ## Layout presets must be used with -x
if ! xpns_warning_before_extra; then
exit ${XP_EINTENT}
fi
else
XP_OPT_REUSE=1
XP_OPT_EXTRA=0
XP_OPT_IS_SYNC=0
fi
;;
-s)
XP_OPTIONS+=("$opt")
XP_OPT_SPEEDY=1
Expand Down
1 change: 1 addition & 0 deletions completion/zsh/_xpanes
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ _xpanes () {
"($opts_omit -s -ss --ssh)-s[Speedy mode: Run command without opening an interactive shell.]" \
"($opts_omit -ss -s)-ss[Speedy mode AND close a pane automatically at the same time as process exiting.]" \
"($opts_omit -n)-n[Set the maximum number of <argument> taken for each pane.]:num:" \
"($opts_omit -r)-r[Reuse the existing panes, or create then in the current active window.]" \
"($opts_omit -S)-S[Specify a full alternative path to the server socket.]:filename:_files" \
"($opts_omit -t)-t[Display each argument on the each pane's border as their title.]" \
"($opts_omit -x --stay)-x[Create extra panes in the current active window.]" \
Expand Down
3 changes: 3 additions & 0 deletions man/man.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@ OPTIONS
`-n` <*number*>
Set the maximum number of <*argument*> taken for each pane.

`-r`
Reuse the existing panes, or create then in the current active window.

`-S` <*socket-path*>
Set a full alternative path to the server socket.

Expand Down
3 changes: 3 additions & 0 deletions man/xpanes.1
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,9 @@ Speedy mode AND close a pane automatically at the same time as process exiting.
\fB\fC\-n\fR <\fInumber\fP>
Set the maximum number of <\fIargument\fP> taken for each pane.
.TP
\fB\fC\-r\fR
Reuse the existing panes, or create then in the current active window.
.TP
\fB\fC\-S\fR <\fIsocket\-path\fP>
Set a full alternative path to the server socket.
.TP
Expand Down
9 changes: 9 additions & 0 deletions test/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,12 @@ $ git submodule update

4. Run `bash ./update_yaml.sh`

## How to run specific test

Execute `test_generator.sh <number>` and execute the generated result as a script.
For the test case 85, run like below.

```bash
$ bash test_generator.sh 85 > test_85.sh
$ bash ./test_85.sh
```
80 changes: 79 additions & 1 deletion test/cases_all.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2813,7 +2813,7 @@ test_x_option_abort() {

_cmd="${EXEC} -S $_socket_file -x AAAA AAAA BBBB CCCC"
eval "${_cmd}"
# Run -a option with Normal mode1
# Run -x option with Normal mode1
assertEquals 4 $?

: "In TMUX session" && {
Expand Down Expand Up @@ -4621,6 +4621,84 @@ test_b_x_log_option () {
return 0
}

# @case: 85
# @skip:
test_r_option_error () {
local _socket_file="${SHUNIT_TMPDIR}/.xpanes-shunit"
local _cmd="${EXEC} -d -S $_socket_file -r AAA BBB CCC"
echo $'\n'" $ $_cmd"$'\n'
eval "$_cmd"
assertEquals 4 $?
close_tmux_session "$_socket_file"
return 0
}

# @case: 86
# @skip:
test_r_option_same_as_x() {
local _socket_file="${SHUNIT_TMPDIR}/.xpanes-shunit"
local _cmd
: "In TMUX session" && {
_cmd="${EXEC} -S $_socket_file -r -c 'echo {}' AAA BBB CCC"
create_tmux_session "$_socket_file"
exec_tmux_session "$_socket_file" "$_cmd"
# If there is only one pane, -r acts like -x
# There must be 4 pane though.
wait_panes_separation "$_socket_file" ".*" "4"
assert_tiled_four_panes "$_socket_file" ".*"
close_tmux_session "$_socket_file"
}
return 0
}

# @case: 87
# @skip:
test_x_r_option() {
local _socket_file="${SHUNIT_TMPDIR}/.xpanes-shunit"
local _tmpdir="${SHUNIT_TMPDIR}"
mkdir -p "${_tmpdir}/fin"
local _cmd
: "In TMUX session" && {
_cmd="${EXEC} -S $_socket_file -x -c 'echo {}' AAA BBB CCC"
create_tmux_session "$_socket_file"
exec_tmux_session "$_socket_file" "$_cmd"
# If there is only one pane, -r acts like -x
# There must be 4 pane though.
wait_panes_separation "$_socket_file" ".*" "4"
_cmd="${EXEC} -S $_socket_file -r -c 'echo {} > ${_tmpdir}/fin/{}' DDD EEE FFF"
exec_tmux_session "$_socket_file" "$_cmd"
wait_existing_file_number "${_tmpdir}/fin" "3" # DDD EEE FFF files should exist
close_tmux_session "$_socket_file"
rm -f "${_tmpdir}"/fin/*
rmdir "${_tmpdir}"/fin
}
return 0
}

# @case: 88
# @skip:
test_x_r_option_invalid_pane_count() {
local _socket_file="${SHUNIT_TMPDIR}/.xpanes-shunit"
local _tmpdir="${SHUNIT_TMPDIR}"
mkdir -p "${_tmpdir}/fin"
local _cmd
: "In TMUX session" && {
_cmd="${EXEC} -S $_socket_file -x -c 'echo {}' AAA BBB CCC"
create_tmux_session "$_socket_file"
exec_tmux_session "$_socket_file" "$_cmd"
# If there is only one pane, -r acts like -x
# There must be 4 pane though.
wait_panes_separation "$_socket_file" ".*" "4"
_cmd="${EXEC} -S $_socket_file -r -c 'echo {}' DDD EEE FFF GGG; echo \$? > ${_tmpdir}/fin/status"
exec_tmux_session "$_socket_file" "$_cmd"
assertEquals 4 "$(<"${_tmpdir}"/fin/status)"
close_tmux_session "$_socket_file"
rm -f "${_tmpdir}"/fin/*
rmdir "${_tmpdir}"/fin
}
return 0
}

###:-:-:END_TESTING:-:-:###

###:-:-:INSERT_TESTING:-:-:###
Expand Down
2 changes: 1 addition & 1 deletion test/config.pict
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ tmux:1.8,1.9,1.9a,2.0,2.1,2.2,2.3,2.4,2.5,2.6,2.7,2.8,2.9,2.9a,3.0a,3.1,3.1b,3.1

## Create test cases
# cat cases_all.sh | grep '@case:' | awk '{print $NF}' | xargs | tr ' ' ',' | awk '{print "case:"$0}'
case:1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84
case:1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88

## Create exceptional conditions
# cat cases_all.sh | grep -E '@skip: ?.' -B1 | awk '{print $2,$3}' | awk 'NF{print $2}' | xargs -n 2 | tr , ' ' | awk '{for (i=2;i<=NF;i++){print $1,$i}}' | awk '{print "IF[case] = "$1" THEN [tmux] <> \""$2"\";"}'
Expand Down

0 comments on commit 5972625

Please sign in to comment.