post create run commands in panes

This commit is contained in:
2026-06-19 16:41:31 +02:00
parent 27b7ba6904
commit 5a50ce62f3
6 changed files with 45 additions and 10 deletions

View File

@@ -154,6 +154,7 @@ relative_worktree_path = "./.worktrees"
split_regex = ","
# One of: "none", "cd", "create_panes", "create_panes_floating".
post_create_action = "none"
commands = []
[env.override]
ZELLIJ_SESSION_NAME = ""
@@ -196,6 +197,8 @@ paste = "CTRL+SHIFT+V"
pane's working directory. `worktree.post_create_action` can then do nothing with `none`, `cd` the
previously active pane to the last created worktree, create one tiled pane per worktree with
`create_panes`, or create one floating pane per worktree with `create_panes_floating`.
`worktree.commands`, for example `["npm install", "git status"]`, can run commands in created
panes; commands are assigned in order and repeat when fewer commands than panes are created.
- `Alt+y`: enter pane-sync selection mode, commit the selection, or stop an active pane sync
- `Space`: toggle the focused pane in the sync set while pane-sync selection mode is active
- Once committed, input typed or pasted into any synced pane is mirrored to the other synced panes

View File

@@ -24,6 +24,7 @@ relative_worktree_path = "./.worktrees"
split_regex = ","
# One of: "none", "cd", "create_panes", "create_panes_floating".
post_create_action = "none"
commands = []
[env.override]
ZELLIJ_SESSION_NAME = ""

View File

@@ -32,6 +32,7 @@ public record AppConfig(
String worktreeRelativePath,
String worktreeSplitRegex,
String worktreePostCreateAction,
List<String> worktreeCommands,
String closeSignal,
Map<String, String> envOverride,
Map<String, KeyBinding> keybindings
@@ -81,6 +82,7 @@ public record AppConfig(
stringValue(document, "worktree.relative_worktree_path", defaults.worktreeRelativePath),
stringValue(document, "worktree.split_regex", defaults.worktreeSplitRegex),
stringValue(document, "worktree.post_create_action", defaults.worktreePostCreateAction),
stringListValue(document, "worktree.commands", defaults.worktreeCommands),
closeSignalValue(document, defaults.closeSignal),
envOverride(document, defaults.envOverride),
keybindings(document, defaults)
@@ -106,6 +108,7 @@ public record AppConfig(
"./.worktrees",
",",
"none",
List.of(),
"SIGTERM",
Map.of(),
Map.ofEntries(
@@ -146,6 +149,7 @@ public record AppConfig(
worktreeRelativePath,
worktreeSplitRegex,
worktreePostCreateAction,
worktreeCommands,
closeSignal,
envOverride,
keybindings
@@ -251,7 +255,8 @@ public record AppConfig(
builder.append("[worktree]\n");
builder.append("relative_worktree_path = ").append(quoted(worktreeRelativePath)).append('\n');
builder.append("split_regex = ").append(quoted(worktreeSplitRegex)).append('\n');
builder.append("post_create_action = ").append(quoted(worktreePostCreateAction)).append("\n\n");
builder.append("post_create_action = ").append(quoted(worktreePostCreateAction)).append('\n');
builder.append("commands = ").append(quotedList(worktreeCommands)).append("\n\n");
builder.append("[env.override]\n");
for (Map.Entry<String, String> entry : envOverride.entrySet()) {
builder.append(entry.getKey()).append(" = ").append(quoted(entry.getValue())).append('\n');

View File

@@ -196,12 +196,22 @@ public final class Compositor {
mutateCurrentTab(() -> currentTab().createPane());
}
public void createTiledPane(String workingDirectory) {
mutateCurrentTab(() -> currentTab().createTiledPane(workingDirectory));
public TerminalPane createTiledPane(String workingDirectory) {
if (isEmpty()) {
return null;
}
TerminalPane pane = currentTab().createTiledPane(workingDirectory);
layoutVersion++;
return pane;
}
public void createFloatingPaneInDirectory(String workingDirectory) {
mutateCurrentTab(() -> currentTab().createFloatingPaneInDirectory(workingDirectory));
public TerminalPane createFloatingPaneInDirectory(String workingDirectory) {
if (isEmpty()) {
return null;
}
TerminalPane pane = currentTab().createFloatingPaneInDirectory(workingDirectory);
layoutVersion++;
return pane;
}
/**

View File

@@ -241,14 +241,15 @@ final class Tab implements AutoCloseable {
}
}
void createTiledPane(String workingDirectory) {
TerminalPane createTiledPane(String workingDirectory) {
TerminalPane pane = openPane(false, workingDirectory);
tiled.add(pane);
setActive(pane);
return pane;
}
void createFloatingPaneInDirectory(String workingDirectory) {
addFloating(openPane(true, workingDirectory));
TerminalPane createFloatingPaneInDirectory(String workingDirectory) {
return addFloating(openPane(true, workingDirectory));
}
void nextFloatingPane() {

View File

@@ -377,12 +377,27 @@ final class TerminalWindow {
switch (action.trim().toLowerCase(Locale.ROOT)) {
case "none" -> { }
case "cd" -> lastActivePane.send("cd " + shellQuote(worktreePaths.get(worktreePaths.size() - 1)) + "\r");
case "create_panes" -> worktreePaths.forEach(compositor::createTiledPane);
case "create_panes_floating" -> worktreePaths.forEach(compositor::createFloatingPaneInDirectory);
case "create_panes" -> createWorktreePanes(worktreePaths, false);
case "create_panes_floating" -> createWorktreePanes(worktreePaths, true);
default -> System.err.println("Unknown worktree.post_create_action '" + action + "'");
}
}
private void createWorktreePanes(List<String> worktreePaths, boolean floating) {
List<String> commands = config.worktreeCommands();
for (int i = 0; i < worktreePaths.size(); i++) {
TerminalPane pane = floating
? compositor.createFloatingPaneInDirectory(worktreePaths.get(i))
: compositor.createTiledPane(worktreePaths.get(i));
if (pane != null && !commands.isEmpty()) {
String command = commands.get(i % commands.size());
if (command != null && !command.isBlank()) {
pane.send(command + "\r");
}
}
}
}
private List<String> readCreatedWorktreePaths(Path createdFile) {
try {
List<String> paths = new ArrayList<>();