diff --git a/cmd-split-window.c b/cmd-split-window.c index bbec0cc8..ed3f47fc 100644 --- a/cmd-split-window.c +++ b/cmd-split-window.c @@ -28,22 +28,23 @@ #include "tmux.h" /* - * Split a window (add a new pane). + * Create a new pane. */ #define SPLIT_WINDOW_TEMPLATE "#{session_name}:#{window_index}.#{pane_index}" -static enum cmd_retval cmd_split_window_exec(struct cmd *, - struct cmdq_item *); +static enum cmd_retval cmd_split_window_exec(struct cmd *, struct cmdq_item *); -const struct cmd_entry cmd_split_window_entry = { - .name = "split-window", - .alias = "splitw", +const struct cmd_entry cmd_new_pane_entry = { + .name = "new-pane", + .alias = "newp", - .args = { "bc:de:fF:hIl:p:Pt:vZ", 0, -1, NULL }, - .usage = "[-bdefhIPvZ] [-c start-directory] [-e environment] " - "[-F format] [-l size] " CMD_TARGET_PANE_USAGE - " [shell-command [argument ...]]", + .args = { "bc:de:fF:hIkl:m:p:PR:s:S:t:vZ", 0, -1, NULL }, + .usage = "[-bdefhIklPvZ] [-c start-directory] [-e environment] " + "[-F format] [-l size] [-m message] [-p percentage] " + "[-s style] [-S active-border-style] " + "[-R inactive-border-style] " CMD_TARGET_PANE_USAGE " " + "[shell-command [argument ...]]", .target = { 't', CMD_FIND_PANE, 0 }, @@ -51,6 +52,46 @@ const struct cmd_entry cmd_split_window_entry = { .exec = cmd_split_window_exec }; +const struct cmd_entry cmd_split_window_entry = { + .name = "split-window", + .alias = "splitw", + + .args = { "bc:de:fF:hIkl:m:p:PR:s:S:t:vZ", 0, -1, NULL }, + .usage = "[-bdefhIklPvZ] [-c start-directory] [-e environment] " + "[-F format] [-l size] [-m message] [-p percentage] " + "[-s style] [-S active-border-style] " + "[-R inactive-border-style] " CMD_TARGET_PANE_USAGE " " + "[shell-command [argument ...]]", + + .target = { 't', CMD_FIND_PANE, 0 }, + + .flags = 0, + .exec = cmd_split_window_exec +}; + +static struct layout_cell * +cmd_split_window_get_tiled_layout_cell(struct cmdq_item *item, + struct args *args, struct window *w, struct window_pane *wp, int flags) +{ + enum layout_type type; + struct layout_cell *lc = NULL; + char *cause = NULL; + int size; + + if (window_pane_tile_geometry(w, wp, &size, &flags, &type, item, args, + &cause) != 0) { + cmdq_error(item, "invalid tiled geometry %s", cause); + free(cause); + return (NULL); + } + + window_push_zoom(wp->window, 1, args_has(args, 'Z')); + lc = layout_split_pane(wp, type, size, flags); + if (lc == NULL) + cmdq_error(item, "no space for new pane"); + return (lc); +} + static enum cmd_retval cmd_split_window_exec(struct cmd *self, struct cmdq_item *item) { @@ -63,51 +104,14 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item) struct winlink *wl = target->wl; struct window *w = wl->window; struct window_pane *wp = target->wp, *new_wp; - enum layout_type type; - struct layout_cell *lc; + struct layout_cell *lc = NULL; struct cmd_find_state fs; - int size, flags, input; - const char *template; + int flags, input; + const char *template, *style; char *cause = NULL, *cp; struct args_value *av; - u_int count = args_count(args), curval = 0; + u_int count = args_count(args); - type = LAYOUT_TOPBOTTOM; - if (args_has(args, 'h')) - type = LAYOUT_LEFTRIGHT; - - /* If the 'p' flag is dropped then this bit can be moved into 'l'. */ - if (args_has(args, 'l') || args_has(args, 'p')) { - if (args_has(args, 'f')) { - if (type == LAYOUT_TOPBOTTOM) - curval = w->sy; - else - curval = w->sx; - } else { - if (type == LAYOUT_TOPBOTTOM) - curval = wp->sy; - else - curval = wp->sx; - } - } - - size = -1; - if (args_has(args, 'l')) { - size = args_percentage_and_expand(args, 'l', 0, INT_MAX, curval, - item, &cause); - } else if (args_has(args, 'p')) { - size = args_strtonum_and_expand(args, 'p', 0, 100, item, - &cause); - if (cause == NULL) - size = curval * size / 100; - } - if (cause != NULL) { - cmdq_error(item, "size %s", cause); - free(cause); - return (CMD_RETURN_ERROR); - } - - window_push_zoom(wp->window, 1, args_has(args, 'Z')); input = (args_has(args, 'I') && count == 0); flags = 0; @@ -118,11 +122,9 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item) if (input || (count == 1 && *args_string(args, 0) == '\0')) flags |= SPAWN_EMPTY; - lc = layout_split_pane(wp, type, size, flags); - if (lc == NULL) { - cmdq_error(item, "no space for new pane"); + lc = cmd_split_window_get_tiled_layout_cell(item, args, w, wp, flags); + if (lc == NULL) return (CMD_RETURN_ERROR); - } sc.item = item; sc.s = s; @@ -157,6 +159,44 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item) environ_free(sc.environ); return (CMD_RETURN_ERROR); } + + style = args_get(args, 's'); + if (style != NULL) { + if (options_set_string(new_wp->options, "window-style", 0, + "%s", style) == NULL) { + cmdq_error(item, "bad style: %s", style); + return (CMD_RETURN_ERROR); + } + options_set_string(new_wp->options, "window-active-style", 0, + "%s", style); + new_wp->flags |= (PANE_REDRAW|PANE_STYLECHANGED + |PANE_THEMECHANGED); + } + style = args_get(args, 'S'); + if (style != NULL) { + if (options_set_string(new_wp->options, + "pane-active-border-style", 0, "%s", style) == NULL) { + cmdq_error(item, "bad active border style: %s", style); + return (CMD_RETURN_ERROR); + } + } + style = args_get(args, 'R'); + if (style != NULL) { + if (options_set_string(new_wp->options, "pane-border-style", 0, + "%s", style) == NULL) { + cmdq_error(item, "bad inactive border style: %s", + style); + return (CMD_RETURN_ERROR); + } + } + if (args_has(args, 'k') || args_has(args, 'm')) { + options_set_number(new_wp->options, "remain-on-exit", 3); + if (args_has(args, 'm')) + options_set_string(new_wp->options, + "remain-on-exit-format", + 0, "%s", args_get(args, 'm')); + } + if (input) { switch (window_pane_start_input(new_wp, item, &cause)) { case -1: diff --git a/cmd.c b/cmd.c index cd64a3ea..d20b8736 100644 --- a/cmd.c +++ b/cmd.c @@ -72,6 +72,7 @@ extern const struct cmd_entry cmd_lock_server_entry; extern const struct cmd_entry cmd_lock_session_entry; extern const struct cmd_entry cmd_move_pane_entry; extern const struct cmd_entry cmd_move_window_entry; +extern const struct cmd_entry cmd_new_pane_entry; extern const struct cmd_entry cmd_new_session_entry; extern const struct cmd_entry cmd_new_window_entry; extern const struct cmd_entry cmd_next_layout_entry; @@ -164,6 +165,7 @@ const struct cmd_entry *cmd_table[] = { &cmd_lock_session_entry, &cmd_move_pane_entry, &cmd_move_window_entry, + &cmd_new_pane_entry, &cmd_new_session_entry, &cmd_new_window_entry, &cmd_next_layout_entry, diff --git a/tmux.1 b/tmux.1 index a0138a64..9bc8d8a4 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3268,6 +3268,98 @@ By default, it uses the format .Ql #{session_name}:#{window_index} but a different format may be specified with .Fl F . +.Tg newp +.It Xo Ic new\-pane +.Op Fl bdefhIkPvZ +.Op Fl c Ar start\-directory +.Op Fl e Ar environment +.Op Fl F Ar format +.Op Fl l Ar size +.Op Fl m Ar message +.Op Fl p Ar percentage +.Op Fl R Ar inactive\-border\-style +.Op Fl s Ar style +.Op Fl S Ar active\-border\-style +.Op Fl t Ar target\-pane +.Op Ar shell\-command Op Ar argument ... +.Xc +.D1 Pq alias: Ic newp +Create a new pane. +The new pane is created by splitting +.Ar target\-pane . +If +.Fl d +is given, the session does not make the new pane the current pane. +.Fl Z +zooms if the window is not zoomed, or keeps it zoomed if already zoomed. +.Fl s +sets the style for the pane content. +.Fl S +sets the border style when the pane is active and +.Fl R +sets the border style when the pane is inactive (see +.Sx STYLES ) . +.Pp +.Fl h +does a horizontal split and +.Fl v +a vertical split; if neither is specified, +.Fl v +is assumed. +The +.Fl l +option specifies the size of the new pane in lines (for vertical split) or in +columns (for horizontal split); +.Ar size +may be followed by +.Ql % +to specify a percentage of the available space. +.Fl p +is a shorthand option for this. +The +.Fl b +option causes the new pane to be created to the left of or above +.Ar target\-pane . +The +.Fl f +option creates a new pane spanning the full window height (with +.Fl h ) +or full window width (with +.Fl v ) , +instead of splitting the active pane. +.Pp +.Fl k +keeps the pane open after the optional +.Ar shell\-command +exits and waits for a key to be pressed before closing it. +The message shown is controlled by the +.Ic remain\-on\-exit\-format +option. +.Fl m Ar message +is equivalent to +.Fl k +but also sets the +.Ic remain\-on\-exit\-format +option for this pane to +.Ar message . +.Pp +An empty +.Ar shell\-command +(\[aq]\[aq]) will create a pane with no command running in it. +The +.Fl I +flag (if +.Ar shell\-command +is not specified or empty) +will create an empty pane and forward any output from stdin to it. +For example: +.Bd -literal -offset indent +$ make 2>&1|tmux splitw \-dI & +.Ed +.Pp +All other options have the same meaning as for the +.Ic new\-window +command. .Tg nextl .It Ic next\-layout Op Fl t Ar target\-window .D1 Pq alias: Ic nextl @@ -3592,65 +3684,28 @@ the command behaves like .Ic last\-window . .Tg splitw .It Xo Ic split\-window -.Op Fl bdfhIvPZ +.Op Fl bdefhIkPvZ .Op Fl c Ar start\-directory .Op Fl e Ar environment .Op Fl F Ar format .Op Fl l Ar size +.Op Fl m Ar message +.Op Fl p Ar percentage +.Op Fl R Ar inactive\-border\-style +.Op Fl s Ar style +.Op Fl S Ar active\-border\-style .Op Fl t Ar target\-pane .Op Ar shell\-command Op Ar argument ... .Xc .D1 Pq alias: Ic splitw -Create a new pane by splitting -.Ar target\-pane : -.Fl h -does a horizontal split and -.Fl v -a vertical split; if neither is specified, -.Fl v -is assumed. -The -.Fl l -option specifies the size of the new pane in lines (for vertical split) or in -columns (for horizontal split); -.Ar size -may be followed by -.Ql % -to specify a percentage of the available space. -The -.Fl b -option causes the new pane to be created to the left of or above +Creates a new pane by splitting .Ar target\-pane . -The -.Fl f -option creates a new pane spanning the full window height (with -.Fl h ) -or full window width (with -.Fl v ) , -instead of splitting the active pane. -.Fl Z -zooms if the window is not zoomed, or keeps it zoomed if already zoomed. +Shares behavior with +.Ic new\-pane . .Pp -An empty -.Ar shell\-command -(\[aq]\[aq]) will create a pane with no command running in it. -Output can be sent to such a pane with the -.Ic display\-message -command. -The -.Fl I -flag (if -.Ar shell\-command -is not specified or empty) -will create an empty pane and forward any output from stdin to it. -For example: -.Bd -literal -offset indent -$ make 2>&1|tmux splitw \-dI & -.Ed -.Pp -All other options have the same meaning as for the -.Ic new\-window -command. +See +.Ic new\-pane +for more details. .Tg swapp .It Xo Ic swap\-pane .Op Fl dDUZ diff --git a/tmux.h b/tmux.h index fec919e2..c94c6e07 100644 --- a/tmux.h +++ b/tmux.h @@ -3412,6 +3412,9 @@ enum client_theme window_pane_get_theme(struct window_pane *); void window_pane_send_theme_update(struct window_pane *); struct style_range *window_pane_border_status_get_range(struct window_pane *, u_int, u_int); +int window_pane_tile_geometry(struct window *, + struct window_pane *, int *, int *, enum layout_type *, + struct cmdq_item *, struct args *, char **); /* layout.c */ u_int layout_count_cells(struct layout_cell *); diff --git a/window.c b/window.c index 57f3c972..f554ee67 100644 --- a/window.c +++ b/window.c @@ -2021,3 +2021,53 @@ window_pane_border_status_get_range(struct window_pane *wp, u_int x, u_int y) */ return (style_ranges_get_range(srs, x - wp->xoff - 2)); } + +int +window_pane_tile_geometry(struct window *w, struct window_pane *wp, + int *out_size, int *out_flags, enum layout_type *out_type, + struct cmdq_item *item, struct args *args, char **cause) +{ + int size = -1, flags = *out_flags; + enum layout_type type; + u_int curval = 0; + + type = LAYOUT_TOPBOTTOM; + if (args_has(args, 'h')) + type = LAYOUT_LEFTRIGHT; + + if (args_has(args, 'l') || args_has(args, 'p')) { + if (args_has(args, 'f')) { + if (type == LAYOUT_TOPBOTTOM) + curval = w->sy; + else + curval = w->sx; + } else { + if (type == LAYOUT_TOPBOTTOM) + curval = wp->sy; + else + curval = wp->sx; + } + } + + if (args_has(args, 'l')) { + size = args_percentage_and_expand(args, 'l', 0, INT_MAX, curval, + item, cause); + } else if (args_has(args, 'p')) { + size = args_strtonum_and_expand(args, 'p', 0, 100, item, + cause); + if (cause == NULL) + size = curval * size / 100; + } + if (*cause != NULL) + return (-1); + + if (args_has(args, 'b')) + flags |= SPAWN_BEFORE; + if (args_has(args, 'f')) + flags |= SPAWN_FULLSIZE; + + *out_size = size; + *out_flags = flags; + *out_type = type; + return (0); +}