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 = "," split_regex = ","
# One of: "none", "cd", "create_panes", "create_panes_floating". # One of: "none", "cd", "create_panes", "create_panes_floating".
post_create_action = "none" post_create_action = "none"
commands = []
[env.override] [env.override]
ZELLIJ_SESSION_NAME = "" 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 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 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`. `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 - `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 - `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 - 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 = "," split_regex = ","
# One of: "none", "cd", "create_panes", "create_panes_floating". # One of: "none", "cd", "create_panes", "create_panes_floating".
post_create_action = "none" post_create_action = "none"
commands = []
[env.override] [env.override]
ZELLIJ_SESSION_NAME = "" ZELLIJ_SESSION_NAME = ""

View File

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

View File

@@ -196,12 +196,22 @@ public final class Compositor {
mutateCurrentTab(() -> currentTab().createPane()); mutateCurrentTab(() -> currentTab().createPane());
} }
public void createTiledPane(String workingDirectory) { public TerminalPane createTiledPane(String workingDirectory) {
mutateCurrentTab(() -> currentTab().createTiledPane(workingDirectory)); if (isEmpty()) {
return null;
}
TerminalPane pane = currentTab().createTiledPane(workingDirectory);
layoutVersion++;
return pane;
} }
public void createFloatingPaneInDirectory(String workingDirectory) { public TerminalPane createFloatingPaneInDirectory(String workingDirectory) {
mutateCurrentTab(() -> currentTab().createFloatingPaneInDirectory(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); TerminalPane pane = openPane(false, workingDirectory);
tiled.add(pane); tiled.add(pane);
setActive(pane); setActive(pane);
return pane;
} }
void createFloatingPaneInDirectory(String workingDirectory) { TerminalPane createFloatingPaneInDirectory(String workingDirectory) {
addFloating(openPane(true, workingDirectory)); return addFloating(openPane(true, workingDirectory));
} }
void nextFloatingPane() { void nextFloatingPane() {

View File

@@ -377,12 +377,27 @@ final class TerminalWindow {
switch (action.trim().toLowerCase(Locale.ROOT)) { switch (action.trim().toLowerCase(Locale.ROOT)) {
case "none" -> { } case "none" -> { }
case "cd" -> lastActivePane.send("cd " + shellQuote(worktreePaths.get(worktreePaths.size() - 1)) + "\r"); case "cd" -> lastActivePane.send("cd " + shellQuote(worktreePaths.get(worktreePaths.size() - 1)) + "\r");
case "create_panes" -> worktreePaths.forEach(compositor::createTiledPane); case "create_panes" -> createWorktreePanes(worktreePaths, false);
case "create_panes_floating" -> worktreePaths.forEach(compositor::createFloatingPaneInDirectory); case "create_panes_floating" -> createWorktreePanes(worktreePaths, true);
default -> System.err.println("Unknown worktree.post_create_action '" + action + "'"); 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) { private List<String> readCreatedWorktreePaths(Path createdFile) {
try { try {
List<String> paths = new ArrayList<>(); List<String> paths = new ArrayList<>();