Use a floating pane for the buffer mode editor instead of a popup.

This commit is contained in:
nicm
2026-06-19 18:37:10 +00:00
parent c93e2f2332
commit d04b1ffca5
4 changed files with 275 additions and 11 deletions

View File

@@ -492,6 +492,7 @@ server_child_exited(pid_t pid, int status)
wp->flags |= PANE_EXITED; wp->flags |= PANE_EXITED;
window_pane_wait_finish(wp); window_pane_wait_finish(wp);
spawn_editor_finish(wp);
if (window_pane_destroy_ready(wp)) if (window_pane_destroy_ready(wp))
server_destroy_pane(wp, 1); server_destroy_pane(wp, 1);

195
spawn.c
View File

@@ -17,10 +17,12 @@
*/ */
#include <sys/types.h> #include <sys/types.h>
#include <sys/wait.h>
#include <errno.h> #include <errno.h>
#include <paths.h> #include <paths.h>
#include <signal.h> #include <signal.h>
#include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
@@ -56,11 +58,10 @@ spawn_log(const char *from, struct spawn_context *sc)
struct session *s = sc->s; struct session *s = sc->s;
struct winlink *wl = sc->wl; struct winlink *wl = sc->wl;
struct window_pane *wp0 = sc->wp0; struct window_pane *wp0 = sc->wp0;
const char *name = cmdq_get_name(sc->item); const char *name = (sc->name == NULL ? "none" : sc->name);
char tmp[128]; char tmp[128];
log_debug("%s: %s, flags=%#x", from, name, sc->flags); log_debug("%s: name=%s, flags=%#x", from, name, sc->flags);
if (wl != NULL && wp0 != NULL) if (wl != NULL && wp0 != NULL)
xsnprintf(tmp, sizeof tmp, "wl=%d wp0=%%%u", wl->idx, wp0->id); xsnprintf(tmp, sizeof tmp, "wl=%d wp0=%%%u", wl->idx, wp0->id);
else if (wl != NULL) else if (wl != NULL)
@@ -70,7 +71,6 @@ spawn_log(const char *from, struct spawn_context *sc)
else else
xsnprintf(tmp, sizeof tmp, "wl=none wp0=none"); xsnprintf(tmp, sizeof tmp, "wl=none wp0=none");
log_debug("%s: s=$%u %s idx=%d", from, s->id, tmp, sc->idx); log_debug("%s: s=$%u %s idx=%d", from, s->id, tmp, sc->idx);
log_debug("%s: name=%s", from, sc->name == NULL ? "none" : sc->name);
} }
struct winlink * struct winlink *
@@ -203,9 +203,9 @@ struct window_pane *
spawn_pane(struct spawn_context *sc, char **cause) spawn_pane(struct spawn_context *sc, char **cause)
{ {
struct cmdq_item *item = sc->item; struct cmdq_item *item = sc->item;
struct cmd_find_state *target = cmdq_get_target(item); struct client *c;
struct client *c = cmdq_get_client(item);
struct session *s = sc->s; struct session *s = sc->s;
struct session *ts;
struct window *w = sc->wl->window; struct window *w = sc->wl->window;
struct window_pane *new_wp; struct window_pane *new_wp;
struct environ *child; struct environ *child;
@@ -222,6 +222,14 @@ spawn_pane(struct spawn_context *sc, char **cause)
sigset_t set, oldset; sigset_t set, oldset;
key_code key; key_code key;
if (item != NULL) {
ts = cmdq_get_target(item)->s;
c = cmdq_get_client(item);
} else {
ts = s;
c = sc->tc;
}
spawn_log(__func__, sc); spawn_log(__func__, sc);
/* /*
@@ -229,16 +237,19 @@ spawn_pane(struct spawn_context *sc, char **cause)
* the pane's stored one unless specified. * the pane's stored one unless specified.
*/ */
if (sc->cwd != NULL) { if (sc->cwd != NULL) {
cwd = format_single(item, sc->cwd, c, target->s, NULL, NULL); if (item != NULL)
cwd = format_single(item, sc->cwd, c, ts, NULL, NULL);
else
cwd = xstrdup(sc->cwd);
if (*cwd != '/') { if (*cwd != '/') {
xasprintf(&new_cwd, "%s%s%s", xasprintf(&new_cwd, "%s%s%s",
server_client_get_cwd(c, target->s), server_client_get_cwd(c, ts),
*cwd != '\0' ? "/" : "", cwd); *cwd != '\0' ? "/" : "", cwd);
free(cwd); free(cwd);
cwd = new_cwd; cwd = new_cwd;
} }
} else if (~sc->flags & SPAWN_RESPAWN) } else if (~sc->flags & SPAWN_RESPAWN)
cwd = xstrdup(server_client_get_cwd(c, target->s)); cwd = xstrdup(server_client_get_cwd(c, ts));
else else
cwd = NULL; cwd = NULL;
@@ -496,3 +507,169 @@ complete:
notify_window("window-layout-changed", w); notify_window("window-layout-changed", w);
return (new_wp); return (new_wp);
} }
struct spawn_editor_state {
char *path;
pid_t pid;
spawn_finish_edit_cb cb;
void *arg;
};
static void
spawn_editor_free(struct spawn_editor_state *es)
{
unlink(es->path);
free(es->path);
free(es);
}
void
spawn_cancel_editor(struct spawn_editor_state *es)
{
if (es == NULL)
return;
es->cb = NULL;
es->arg = NULL;
}
pid_t
spawn_get_editor_pid(struct spawn_editor_state *es)
{
if (es == NULL)
return (-1);
return (es->pid);
}
void
spawn_editor_finish(struct window_pane *wp)
{
struct spawn_editor_state *es = wp->editor;
FILE *f;
char *buf = NULL;
off_t len = 0;
int status = 128 + SIGHUP;
if (es == NULL)
return;
wp->editor = NULL;
if (wp->flags & PANE_STATUSREADY) {
if (WIFEXITED(wp->status))
status = WEXITSTATUS(wp->status);
else if (WIFSIGNALED(wp->status))
status = WTERMSIG(wp->status) + 128;
}
if (es->cb == NULL) {
spawn_editor_free(es);
return;
}
if (status != 0) {
es->cb(NULL, 0, es->arg);
spawn_editor_free(es);
return;
}
f = fopen(es->path, "r");
if (f != NULL) {
if (fseeko(f, 0, SEEK_END) == 0) {
len = ftello(f);
if (len > 0 && (uintmax_t)len <= (uintmax_t)SIZE_MAX) {
if (fseeko(f, 0, SEEK_SET) == 0) {
buf = malloc(len);
if (buf != NULL &&
fread(buf, len, 1, f) != 1) {
free(buf);
buf = NULL;
len = 0;
}
}
} else
len = 0;
}
fclose(f);
}
es->cb(buf, len, es->arg);
spawn_editor_free(es);
}
struct spawn_editor_state *
spawn_editor(struct client *c, const char *buf, size_t len,
spawn_finish_edit_cb cb, void *arg)
{
struct spawn_editor_state *es;
struct spawn_context sc = { 0 };
struct session *s = c->session;
struct winlink *wl = s->curw;
struct window *w = wl->window;
struct window_pane *wp;
struct layout_cell *lc;
struct environ *env;
FILE *f;
char *cmd, *cause = NULL;
char path[] = _PATH_TMP "tmux.XXXXXXXX";
const char *editor;
int fd;
u_int px, py, sx, sy;
editor = options_get_string(global_options, "editor");
fd = mkstemp(path);
if (fd == -1)
return (NULL);
f = fdopen(fd, "w");
if (f == NULL) {
close(fd);
unlink(path);
return (NULL);
}
if (fwrite(buf, len, 1, f) != 1) {
fclose(f);
unlink(path);
return (NULL);
}
fclose(f);
es = xcalloc(1, sizeof *es);
es->path = xstrdup(path);
es->cb = cb;
es->arg = arg;
sx = w->sx * 9 / 10;
sy = w->sy * 9 / 10;
px = w->sx / 2 - sx / 2;
py = w->sy / 2 - sy / 2;
window_push_zoom(w, 1, 0);
lc = layout_floating_pane(w, sx, sy, px, py);
if (lc == NULL) {
spawn_editor_free(es);
return (NULL);
}
xasprintf(&cmd, "%s %s", editor, path);
env = environ_create();
sc.s = s;
sc.wl = wl;
sc.tc = c;
sc.wp0 = w->active;
sc.lc = lc;
sc.argc = 1;
sc.argv = &cmd;
sc.environ = env;
sc.idx = -1;
sc.cwd = _PATH_TMP;
sc.flags = SPAWN_FLOATING;
wp = spawn_pane(&sc, &cause);
free(cmd);
environ_free(env);
if (wp == NULL) {
free(cause);
spawn_editor_free(es);
return (NULL);
}
options_set_number(wp->options, "remain-on-exit", 0);
es->pid = wp->pid;
wp->editor = es;
return (es);
}

View File

@@ -87,6 +87,8 @@ struct window_buffer_modedata {
struct cmd_find_state fs; struct cmd_find_state fs;
struct mode_tree_data *data; struct mode_tree_data *data;
struct spawn_editor_state *editor;
struct window_buffer_editdata *edit;
char *command; char *command;
char *format; char *format;
char *key_format; char *key_format;
@@ -99,8 +101,12 @@ struct window_buffer_editdata {
u_int wp_id; u_int wp_id;
char *name; char *name;
struct paste_buffer *pb; struct paste_buffer *pb;
struct spawn_editor_state *editor;
}; };
static void window_buffer_finish_edit(struct window_buffer_editdata *);
static void window_buffer_draw_waiting(struct window_buffer_modedata *);
static enum sort_order window_buffer_order_seq[] = { static enum sort_order window_buffer_order_seq[] = {
SORT_CREATION, SORT_CREATION,
SORT_NAME, SORT_NAME,
@@ -394,6 +400,11 @@ window_buffer_free(struct window_mode_entry *wme)
if (data == NULL) if (data == NULL)
return; return;
if (data->editor != NULL) {
spawn_cancel_editor(data->editor);
window_buffer_finish_edit(data->edit);
}
mode_tree_free(data->data); mode_tree_free(data->data);
for (i = 0; i < data->item_size; i++) for (i = 0; i < data->item_size; i++)
@@ -422,6 +433,7 @@ window_buffer_update(struct window_mode_entry *wme)
mode_tree_build(data->data); mode_tree_build(data->data);
mode_tree_draw(data->data); mode_tree_draw(data->data);
window_buffer_draw_waiting(data);
data->wp->flags |= PANE_REDRAW; data->wp->flags |= PANE_REDRAW;
} }
@@ -466,6 +478,49 @@ window_buffer_finish_edit(struct window_buffer_editdata *ed)
free(ed); free(ed);
} }
static void
window_buffer_draw_waiting(struct window_buffer_modedata *data)
{
struct screen_write_ctx ctx;
struct screen *s = data->wp->screen;
struct grid_cell gc;
char text[128];
u_int sx, sy, box_w, box_h, x, y, text_x;
size_t textlen;
pid_t pid;
if (data->editor == NULL)
return;
sx = screen_size_x(s);
sy = screen_size_y(s);
if (sx == 0 || sy == 0)
return;
pid = spawn_get_editor_pid(data->editor);
if (pid == -1)
xsnprintf(text, sizeof text, "WAITING FOR EDITOR");
else
xsnprintf(text, sizeof text, "WAITING FOR EDITOR (PID %ld)",
(long)pid);
textlen = strlen(text);
box_w = textlen + 4;
box_h = 3;
if (sx < box_w || sy < box_h)
return;
x = (sx - box_w) / 2;
y = (sy - box_h) / 2;
text_x = x + (box_w - textlen) / 2;
memcpy(&gc, &grid_default_cell, sizeof gc);
screen_write_start(&ctx, s);
screen_write_cursormove(&ctx, x, y, 0);
screen_write_box(&ctx, box_w, box_h, BOX_LINES_DEFAULT, &gc, NULL);
screen_write_cursormove(&ctx, text_x, y + 1, 0);
screen_write_nputs(&ctx, box_w - 2, &gc, "%s", text);
screen_write_stop(&ctx);
}
static void static void
window_buffer_edit_close_cb(char *buf, size_t len, void *arg) window_buffer_edit_close_cb(char *buf, size_t len, void *arg)
{ {
@@ -477,6 +532,18 @@ window_buffer_edit_close_cb(char *buf, size_t len, void *arg)
struct window_buffer_modedata *data; struct window_buffer_modedata *data;
struct window_mode_entry *wme; struct window_mode_entry *wme;
wp = window_pane_find_by_id(ed->wp_id);
if (wp != NULL) {
wme = TAILQ_FIRST(&wp->modes);
if (wme != NULL && wme->mode == &window_buffer_mode) {
data = wme->data;
if (data->editor == ed->editor) {
data->editor = NULL;
data->edit = NULL;
}
}
}
if (buf == NULL || len == 0) { if (buf == NULL || len == 0) {
window_buffer_finish_edit(ed); window_buffer_finish_edit(ed);
return; return;
@@ -499,10 +566,11 @@ window_buffer_edit_close_cb(char *buf, size_t len, void *arg)
wp = window_pane_find_by_id(ed->wp_id); wp = window_pane_find_by_id(ed->wp_id);
if (wp != NULL) { if (wp != NULL) {
wme = TAILQ_FIRST(&wp->modes); wme = TAILQ_FIRST(&wp->modes);
if (wme->mode == &window_buffer_mode) { if (wme != NULL && wme->mode == &window_buffer_mode) {
data = wme->data; data = wme->data;
mode_tree_build(data->data); mode_tree_build(data->data);
mode_tree_draw(data->data); mode_tree_draw(data->data);
window_buffer_draw_waiting(data);
} }
wp->flags |= PANE_REDRAW; wp->flags |= PANE_REDRAW;
} }
@@ -518,6 +586,8 @@ window_buffer_start_edit(struct window_buffer_modedata *data,
size_t len; size_t len;
struct window_buffer_editdata *ed; struct window_buffer_editdata *ed;
if (data->editor != NULL)
return;
if ((pb = paste_get_name(item->name)) == NULL) if ((pb = paste_get_name(item->name)) == NULL)
return; return;
buf = paste_buffer_data(pb, &len); buf = paste_buffer_data(pb, &len);
@@ -527,8 +597,13 @@ window_buffer_start_edit(struct window_buffer_modedata *data,
ed->name = xstrdup(paste_buffer_name(pb)); ed->name = xstrdup(paste_buffer_name(pb));
ed->pb = pb; ed->pb = pb;
if (popup_editor(c, buf, len, window_buffer_edit_close_cb, ed) != 0) ed->editor = spawn_editor(c, buf, len, window_buffer_edit_close_cb, ed);
if (ed->editor == NULL)
window_buffer_finish_edit(ed); window_buffer_finish_edit(ed);
else {
data->editor = ed->editor;
data->edit = ed;
}
} }
static void static void
@@ -546,6 +621,13 @@ window_buffer_key(struct window_mode_entry *wme, struct client *c,
finished = 1; finished = 1;
goto out; goto out;
} }
if (data->editor != NULL) {
if (key == 'q' || key == '\033' || key == '\003')
finished = 1;
else
finished = 0;
goto out;
}
finished = mode_tree_key(mtd, c, &key, m, NULL, NULL); finished = mode_tree_key(mtd, c, &key, m, NULL, NULL);
switch (key) { switch (key) {
@@ -579,6 +661,7 @@ out:
window_pane_reset_mode(wp); window_pane_reset_mode(wp);
else { else {
mode_tree_draw(mtd); mode_tree_draw(mtd);
window_buffer_draw_waiting(data);
wp->flags |= PANE_REDRAW; wp->flags |= PANE_REDRAW;
} }
} }

View File

@@ -390,6 +390,8 @@ window_pane_destroy_ready(struct window_pane *wp)
*/ */
if (wp->wait_item != NULL && (~wp->flags & PANE_STATUSREADY)) if (wp->wait_item != NULL && (~wp->flags & PANE_STATUSREADY))
return (0); return (0);
if (wp->editor != NULL && (~wp->flags & PANE_STATUSREADY))
return (0);
return (1); return (1);
} }
@@ -1124,6 +1126,7 @@ static void
window_pane_destroy(struct window_pane *wp) window_pane_destroy(struct window_pane *wp)
{ {
window_pane_wait_finish(wp); window_pane_wait_finish(wp);
spawn_editor_finish(wp);
window_pane_reset_mode_all(wp); window_pane_reset_mode_all(wp);
free(wp->searchstr); free(wp->searchstr);