Back out select-layout multiple windows.

This commit is contained in:
Michael Grant
2026-07-06 22:45:41 +01:00
parent c3e7738cc5
commit 6413409ab3
4 changed files with 16 additions and 177 deletions

View File

@@ -18,7 +18,6 @@
#include <sys/types.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
@@ -30,14 +29,6 @@
static enum cmd_retval cmd_select_layout_exec(struct cmd *,
struct cmdq_item *);
static enum cmd_retval cmd_select_layout_exec_multiple(struct cmdq_item *,
const char *);
struct cmd_select_layout_record {
struct window *w;
char *layout;
struct layout_prepared *prepared;
};
const struct cmd_entry cmd_select_layout_entry = {
.name = "select-layout",
@@ -78,121 +69,6 @@ const struct cmd_entry cmd_previous_layout_entry = {
.exec = cmd_select_layout_exec
};
static void
cmd_select_layout_free_records(struct cmd_select_layout_record *records,
u_int nrecords)
{
u_int i;
for (i = 0; i < nrecords; i++) {
free(records[i].layout);
layout_free_prepared(records[i].prepared);
}
free(records);
}
/* Apply a list of @window-id:layout records. */
static enum cmd_retval
cmd_select_layout_exec_multiple(struct cmdq_item *item, const char *input)
{
struct cmd_select_layout_record *records = NULL;
struct window *w;
const char *ptr = input, *idstart, *idend;
const char *layoutstart, *layoutend;
char *id, *cause, *oldlayout;
u_int i, nrecords = 0;
for (;;) {
while (*ptr != '\0' && isspace((u_char)*ptr))
ptr++;
if (*ptr == '\0')
break;
if (*ptr != '@')
goto invalid;
idstart = ptr++;
if (!isdigit((u_char)*ptr))
goto invalid;
while (isdigit((u_char)*ptr))
ptr++;
idend = ptr;
while (*ptr != '\0' && isspace((u_char)*ptr))
ptr++;
if (*ptr++ != ':')
goto invalid;
id = xstrndup(idstart, idend - idstart);
w = window_find_by_id_str(id);
if (w == NULL) {
cmdq_error(item, "unknown window: %s", id);
free(id);
goto fail;
}
free(id);
for (i = 0; i < nrecords; i++) {
if (records[i].w == w) {
cmdq_error(item, "duplicate window: @%u", w->id);
goto fail;
}
}
layoutstart = ptr;
while (*ptr != '\0' && *ptr != '@')
ptr++;
layoutend = ptr;
while (layoutend != layoutstart &&
isspace((u_char)layoutend[-1]))
layoutend--;
if (layoutend == layoutstart)
goto invalid;
records = xreallocarray(records, nrecords + 1,
sizeof *records);
records[nrecords].w = w;
records[nrecords].layout = xstrndup(layoutstart,
layoutend - layoutstart);
records[nrecords].prepared = NULL;
nrecords++;
}
if (nrecords == 0)
goto invalid;
/* Validate every record before changing any window. */
for (i = 0; i < nrecords; i++) {
records[i].prepared = layout_prepare(records[i].w,
records[i].layout, &cause);
if (records[i].prepared == NULL) {
cmdq_error(item, "@%u: %s", records[i].w->id, cause);
free(cause);
goto fail;
}
}
for (i = 0; i < nrecords; i++) {
w = records[i].w;
server_unzoom_window(w);
oldlayout = w->old_layout;
w->old_layout = layout_dump(w, w->layout_root);
layout_apply_prepared(w, records[i].prepared);
records[i].prepared = NULL;
free(oldlayout);
}
recalculate_sizes();
for (i = 0; i < nrecords; i++) {
server_redraw_window(records[i].w);
notify_window("window-layout-changed", records[i].w);
}
cmd_select_layout_free_records(records, nrecords);
return (CMD_RETURN_NORMAL);
invalid:
cmdq_error(item, "invalid multiple-window layout");
fail:
cmd_select_layout_free_records(records, nrecords);
return (CMD_RETURN_ERROR);
}
static enum cmd_retval
cmd_select_layout_exec(struct cmd *self, struct cmdq_item *item)
{
@@ -205,21 +81,16 @@ cmd_select_layout_exec(struct cmd *self, struct cmdq_item *item)
const char *layoutname;
char *oldlayout, *cause;
int next, previous, layout;
const char *ptr;
if (cmd_get_entry(self) == &cmd_select_layout_entry &&
args_count(args) != 0 && !args_has(args, 'E') &&
!args_has(args, 'n') && !args_has(args, 'o') &&
!args_has(args, 'p')) {
ptr = args_string(args, 0);
while (*ptr != '\0' && isspace((u_char)*ptr))
ptr++;
if (*ptr == '@')
return (cmd_select_layout_exec_multiple(item, ptr));
if (layout_set_lookup(ptr) == -1) {
prepared = layout_prepare(w, ptr, &cause);
layoutname = args_string(args, 0);
if (layout_set_lookup(layoutname) == -1) {
prepared = layout_prepare(w, layoutname, &cause);
if (prepared == NULL) {
cmdq_error(item, "%s: %s", cause, ptr);
cmdq_error(item, "%s: %s", cause, layoutname);
free(cause);
return (CMD_RETURN_ERROR);
}

View File

@@ -76,10 +76,6 @@
* used when all match the target window, tree order is used when none match,
* and a partial match is rejected. Surplus cells are removed from the end when
* the target has fewer panes; a target with more panes is rejected.
*
* cmd-select-layout.c also accepts multiple window records in this form:
*
* @window-id:layout [@window-id:layout ...]
*/
#define LAYOUT_STRING_MAX 8192

View File

@@ -338,26 +338,17 @@ sleep 0.5
$TMUX new-session -d -x 80 -y 24 || exit 1
$TMUX split-window -h || fail "split-window failed"
# Multiple @window-id:layout records may be applied in one command. The
# newline produced by list-windows is insignificant whitespace.
# select-layout changes only the window selected by -t. A window ID is not
# accepted as part of the layout argument.
$TMUX new-window -d -n second || fail "second window failed"
$TMUX split-window -d -h -t @1 || fail "second window split failed"
layouts=$($TMUX list-windows -F '#{window_id}:#{window_layout}')
$TMUX select-layout "$layouts" || fail "multiple-window layout was rejected"
must_equal "$($TMUX list-windows -F '#{window_id}:#{window_layout}')" \
"$layouts"
compact=$(printf %s "$layouts" | tr -d '\n')
$TMUX select-layout "$compact" || fail "compact multiple-window layout failed"
# All records are validated before any window is changed.
before=$($TMUX display-message -p -t @0 '#{window_width}x#{window_height}')
must_fail $TMUX select-layout "@0:$reported@1:0000,"
must_equal "$($TMUX display-message -p -t @0 '#{window_width}x#{window_height}')" \
"$before"
must_fail $TMUX select-layout '@0'
must_fail $TMUX select-layout '@0:'
must_fail $TMUX select-layout "@0:$legacy@0:$legacy"
must_fail $TMUX select-layout "@999999:$legacy"
$TMUX split-window -v -t @1 || fail "second window split failed"
first=$($TMUX display-message -p -t @0 '#{window_layout}')
second=$($TMUX display-message -p -t @1 '#{window_layout}')
$TMUX select-layout -t @1 even-horizontal || fail "target layout failed"
must_equal "$($TMUX display-message -p -t @0 '#{window_layout}')" "$first"
[ "$($TMUX display-message -p -t @1 '#{window_layout}')" != "$second" ] || \
fail "target window layout did not change"
must_fail $TMUX select-layout "@0:$first"
# Add floating panes and verify exact z-order, semicolon output and zoom state.
$TMUX kill-server 2>/dev/null

23
tmux.1
View File

@@ -3447,16 +3447,6 @@ The format is described under
In control mode, the old form is used unless the client has the
.Ql new\-window\-layouts
flag.
.Pp
Layouts for multiple windows may be produced in a form suitable for
.Ic select\-layout
using
.Fl F .
For example, they may be captured and reapplied directly:
.Bd -literal -offset indent
$ layouts=\[dq]$(tmux list\-windows \-F \[aq]#{window_id}:#{window_layout}\[aq])\[dq]
$ tmux select\-layout \[dq]$layouts\[dq]
.Ed
.Tg movep
.It Xo Ic move\-pane
.Op Fl bdfhMv
@@ -4008,17 +3998,8 @@ uses
for the layout in its default output.
.Pp
A layout description is a snapshot, not a complete saved session.
It rearranges existing panes and windows but does not create panes or windows.
.Pp
Layouts for multiple windows may be prefixed by their window IDs and joined:
.Bd -literal -offset indent
window-layouts = @window-id:layout [@window-id:layout ...]
.Ed
.Pp
Whitespace, including newlines, may appear between the window records.
Each window layout retains its own checksum and version prefix.
All records are checked before any window is changed.
Unknown or duplicate window IDs are rejected.
It rearranges existing panes in the target window but does not create panes or
windows.
.Tg selectp
.It Xo Ic select\-pane
.Op Fl DdeLlMmRUZ