From 5a50ce62f3de839ed95c2203412c42c073232dac Mon Sep 17 00:00:00 2001 From: Gregor Lohaus Date: Fri, 19 Jun 2026 16:41:31 +0200 Subject: [PATCH] post create run commands in panes --- README.md | 3 +++ config.example.toml | 1 + .../java/com/gregor/jprototerm/AppConfig.java | 7 ++++++- .../com/gregor/jprototerm/Compositor.java | 18 ++++++++++++++---- src/main/java/com/gregor/jprototerm/Tab.java | 7 ++++--- .../com/gregor/jprototerm/TerminalWindow.java | 19 +++++++++++++++++-- 6 files changed, 45 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 980a9e8..9729f1b 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/config.example.toml b/config.example.toml index 72c2e90..e620901 100644 --- a/config.example.toml +++ b/config.example.toml @@ -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 = "" diff --git a/src/main/java/com/gregor/jprototerm/AppConfig.java b/src/main/java/com/gregor/jprototerm/AppConfig.java index 93163c2..e8c634d 100644 --- a/src/main/java/com/gregor/jprototerm/AppConfig.java +++ b/src/main/java/com/gregor/jprototerm/AppConfig.java @@ -32,6 +32,7 @@ public record AppConfig( String worktreeRelativePath, String worktreeSplitRegex, String worktreePostCreateAction, + List worktreeCommands, String closeSignal, Map envOverride, Map 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 entry : envOverride.entrySet()) { builder.append(entry.getKey()).append(" = ").append(quoted(entry.getValue())).append('\n'); diff --git a/src/main/java/com/gregor/jprototerm/Compositor.java b/src/main/java/com/gregor/jprototerm/Compositor.java index deaf644..15a4f67 100644 --- a/src/main/java/com/gregor/jprototerm/Compositor.java +++ b/src/main/java/com/gregor/jprototerm/Compositor.java @@ -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; } /** diff --git a/src/main/java/com/gregor/jprototerm/Tab.java b/src/main/java/com/gregor/jprototerm/Tab.java index 639402f..b70546c 100644 --- a/src/main/java/com/gregor/jprototerm/Tab.java +++ b/src/main/java/com/gregor/jprototerm/Tab.java @@ -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() { diff --git a/src/main/java/com/gregor/jprototerm/TerminalWindow.java b/src/main/java/com/gregor/jprototerm/TerminalWindow.java index 9836079..804f490 100644 --- a/src/main/java/com/gregor/jprototerm/TerminalWindow.java +++ b/src/main/java/com/gregor/jprototerm/TerminalWindow.java @@ -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 worktreePaths, boolean floating) { + List 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 readCreatedWorktreePaths(Path createdFile) { try { List paths = new ArrayList<>();