new panes gain active pwd
This commit is contained in:
@@ -9,6 +9,9 @@ import java.lang.foreign.MemorySegment;
|
||||
import java.lang.foreign.SymbolLookup;
|
||||
import java.lang.foreign.ValueLayout;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -74,6 +77,7 @@ public final class LinuxPty implements AutoCloseable {
|
||||
private static final long SPAWN_ACTIONS_SIZE = 256;
|
||||
private static final long SPAWN_ATTR_SIZE = 512;
|
||||
|
||||
private static final MethodHandle TCGETPGRP = handle("tcgetpgrp", FD_INT_INT);
|
||||
private static final MethodHandle POSIX_OPENPT = handle("posix_openpt", FD_INT_INT);
|
||||
private static final MethodHandle GRANTPT = handle("grantpt", FD_INT_INT);
|
||||
private static final MethodHandle UNLOCKPT = handle("unlockpt", FD_INT_INT);
|
||||
@@ -205,6 +209,25 @@ public final class LinuxPty implements AutoCloseable {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Best-effort current working directory of the terminal's foreground process group, read from
|
||||
* {@code /proc}. This tracks the directory the user is actually in (a {@code cd} in the shell,
|
||||
* or a child program that changed dir), so a newly opened pane can start there. Falls back to
|
||||
* the shell's own pid, and returns {@code null} if it cannot be determined.
|
||||
*/
|
||||
public String currentWorkingDirectory() {
|
||||
if (closed) {
|
||||
return null;
|
||||
}
|
||||
int pgid = callInt(TCGETPGRP, masterFd);
|
||||
int target = pgid > 0 ? pgid : pid;
|
||||
try {
|
||||
return Files.readSymbolicLink(Path.of("/proc", Integer.toString(target), "cwd")).toString();
|
||||
} catch (IOException | RuntimeException ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/** Resizes the terminal window. */
|
||||
public void setWinSize(int columns, int rows) {
|
||||
if (closed) {
|
||||
|
||||
@@ -20,7 +20,8 @@ public final class ShellSession implements AutoCloseable {
|
||||
});
|
||||
}
|
||||
|
||||
public static ShellSession start(String shell, Map<String, String> envOverride, TerminalPane pane, int columns, int rows) {
|
||||
public static ShellSession start(String shell, Map<String, String> envOverride, TerminalPane pane,
|
||||
int columns, int rows, String workingDirectory) {
|
||||
try {
|
||||
Map<String, String> environment = new HashMap<>(System.getenv());
|
||||
environment.put("TERM", "xterm-kitty");
|
||||
@@ -31,7 +32,7 @@ public final class ShellSession implements AutoCloseable {
|
||||
LinuxPty pty = LinuxPty.spawn(
|
||||
new String[] {shell, "-i"},
|
||||
environment,
|
||||
System.getProperty("user.home"));
|
||||
workingDirectory != null ? workingDirectory : System.getProperty("user.home"));
|
||||
ShellSession session = new ShellSession(pty);
|
||||
session.resize(columns, rows);
|
||||
return session;
|
||||
@@ -69,6 +70,11 @@ public final class ShellSession implements AutoCloseable {
|
||||
reader.submit(() -> readOutput(pane));
|
||||
}
|
||||
|
||||
/** Best-effort current working directory of the running shell, or {@code null} if unknown. */
|
||||
public String currentWorkingDirectory() {
|
||||
return closed ? null : pty.currentWorkingDirectory();
|
||||
}
|
||||
|
||||
public void resize(int columns, int rows) {
|
||||
if (closed) {
|
||||
return;
|
||||
|
||||
@@ -308,7 +308,10 @@ final class Tab implements AutoCloseable {
|
||||
widthPx = lastWidth / (tiled.size() + 1);
|
||||
heightPx = availHeight;
|
||||
}
|
||||
return TerminalPane.create(config, metrics, this::markContentChanged, widthPx, heightPx);
|
||||
// Open the new pane in the active pane's working directory, so a split/new pane lands
|
||||
// where the user currently is. null (no active pane yet, or cwd unknown) falls back to home.
|
||||
String workingDirectory = active != null ? active.currentWorkingDirectory() : null;
|
||||
return TerminalPane.create(config, metrics, this::markContentChanged, widthPx, heightPx, workingDirectory);
|
||||
}
|
||||
|
||||
private static boolean directionFilter(Direction direction, TerminalPane current, TerminalPane candidate) {
|
||||
|
||||
@@ -69,9 +69,11 @@ public final class TerminalPane implements AutoCloseable, RenderTarget {
|
||||
* columns and rows fit, and that grid is handed to ghostty and the shell at start-up. A
|
||||
* non-positive size falls back to the configured default grid (used before the first
|
||||
* layout, when no rect is known yet). The pane owns the shell session it starts and runs
|
||||
* {@code onContentChange} on every content change.
|
||||
* {@code onContentChange} on every content change. The shell starts in {@code workingDirectory}
|
||||
* (e.g. the active pane's cwd), or the user's home when {@code null}.
|
||||
*/
|
||||
public static TerminalPane create(AppConfig config, TerminalMetrics metrics, Runnable onContentChange, double widthPx, double heightPx) {
|
||||
public static TerminalPane create(AppConfig config, TerminalMetrics metrics, Runnable onContentChange,
|
||||
double widthPx, double heightPx, String workingDirectory) {
|
||||
int columns = widthPx > 0 ? metrics.columnsFor(widthPx) : config.columns();
|
||||
int rows = heightPx > 0 ? metrics.rowsFor(heightPx) : config.rows();
|
||||
Terminal terminal = Ghostty.open(new TerminalOptions(columns, rows, config.maxScrollback()));
|
||||
@@ -79,7 +81,7 @@ public final class TerminalPane implements AutoCloseable, RenderTarget {
|
||||
TerminalPane pane = new TerminalPane(terminal, metrics, config.kittyGraphics(), onContentChange,
|
||||
new GhosttyTerminalRenderer(metrics), columns, rows);
|
||||
pane.refresh();
|
||||
pane.attach(ShellSession.start(config.shell(), config.envOverride(), pane, columns, rows));
|
||||
pane.attach(ShellSession.start(config.shell(), config.envOverride(), pane, columns, rows, workingDirectory));
|
||||
return pane;
|
||||
}
|
||||
|
||||
@@ -205,6 +207,12 @@ public final class TerminalPane implements AutoCloseable, RenderTarget {
|
||||
}
|
||||
}
|
||||
|
||||
/** Best-effort current working directory of this pane's shell, or {@code null} if unknown. */
|
||||
public String currentWorkingDirectory() {
|
||||
ShellSession current = session;
|
||||
return current != null ? current.currentWorkingDirectory() : null;
|
||||
}
|
||||
|
||||
/** This pane's own content revision, bumped on every change (see {@link #refresh()}). */
|
||||
public long contentVersion() {
|
||||
return contentVersion.get();
|
||||
|
||||
Reference in New Issue
Block a user