дзеркало
https://github.com/l0ng-ai/openidea.git
синхронізовано 2026-11-09 19:25:54 +02:00
fix(terminal): write process input off the UI thread (#2183)
A child that stopped reading its input could freeze the whole window, because terminal input was written synchronously on the UI thread. Terminal, Run and debug stdin now share a per-session ordered writer queue bounded to 4 MiB and 1,024 writes. An overflow rejects the whole write and shows an error; closing or killing a session discards pending input without waiting for the child, and an explicit stdin EOF drains accepted writes first. Refs #2160
Цей коміт міститься в:
1 джерело
004da36bed
коміт
43bcd66b2b
13 files changed
+512
-36
No files matched your search
@@ -1257,7 +1257,7 @@ export async function sendStdin(sessionId: string, text: string): Promise<void>
|
||||
} catch (e) {
|
||||
enqueue(sessionId, {
|
||||
kind: "system",
|
||||
text: tx`Cannot write to stdin: ${e instanceof Error ? e.message : String(e)}\n`,
|
||||
text: tx`Cannot write to stdin: ${e instanceof Error ? tx(e.message) : String(e)}\n`,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1266,7 +1266,12 @@ export async function sendStdin(sessionId: string, text: string): Promise<void>
|
||||
export function writeTerminal(sessionId: string, data: string) {
|
||||
const session = getSession(sessionId);
|
||||
if (!session || session.runId === null || session.status !== "running") return;
|
||||
void call(commands.runStdin(session.runId, data)).catch(() => undefined);
|
||||
void call(commands.runStdin(session.runId, data)).catch((e) => {
|
||||
enqueue(sessionId, {
|
||||
kind: "system",
|
||||
text: tx`Cannot write to stdin: ${e instanceof Error ? tx(e.message) : String(e)}\n`,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/** The terminal console's size in cells; the process's terminal follows it. */
|
||||
|
||||
@@ -16,6 +16,7 @@ const ipc = vi.hoisted(() => ({
|
||||
termKill: vi.fn(),
|
||||
termResize: vi.fn(),
|
||||
termIsBusy: vi.fn(),
|
||||
termWrite: vi.fn(),
|
||||
},
|
||||
}));
|
||||
const workbench = vi.hoisted(() => ({
|
||||
@@ -122,6 +123,27 @@ describe("resolveStartCwd", () => {
|
||||
describe("terminal start directory fallback", () => {
|
||||
beforeEach(() => vi.clearAllMocks());
|
||||
|
||||
it("shows rejected terminal input instead of silently discarding it", async () => {
|
||||
ipc.commands.termSpawn.mockResolvedValue({ id: 9, title: "zsh" });
|
||||
ipc.commands.termWrite.mockRejectedValueOnce(new Error("input queue full"));
|
||||
const session = new TerminalSession({
|
||||
key: "s-input-error",
|
||||
cwd: "/repo",
|
||||
onChange: () => undefined,
|
||||
onFocus: () => undefined,
|
||||
});
|
||||
await session.start();
|
||||
session.input("paste");
|
||||
await vi.waitFor(() =>
|
||||
expect(workbench.notify).toHaveBeenCalledWith({
|
||||
level: "error",
|
||||
title: "Cannot write to terminal",
|
||||
body: "input queue full",
|
||||
}),
|
||||
);
|
||||
session.dispose();
|
||||
});
|
||||
|
||||
it("spawns a restored session in the project root when its saved directory is gone", async () => {
|
||||
ipc.commands.fsEntryKind.mockRejectedValue(new Error("gone"));
|
||||
ipc.commands.termSpawn.mockResolvedValue({ id: 7, title: "zsh" });
|
||||
|
||||
@@ -9,6 +9,7 @@ import { SearchAddon, type ISearchOptions } from "@xterm/addon-search";
|
||||
import "@xterm/xterm/css/xterm.css";
|
||||
import { Channel } from "@tauri-apps/api/core";
|
||||
import { call, commands, isTauri, type TerminalId } from "@/platform/ipc";
|
||||
import { tx } from "@/platform/i18n";
|
||||
import { editorService, notifications, registerFontZoomSurface } from "@/workbench/contributions";
|
||||
import { fitScheduler, settledResize } from "./fit";
|
||||
import { installTerminalDrop } from "./drop";
|
||||
@@ -658,14 +659,20 @@ export class TerminalSession {
|
||||
this.pendingInput += data;
|
||||
return;
|
||||
}
|
||||
void commands.termWrite(this.ptyId, data);
|
||||
void call(commands.termWrite(this.ptyId, data)).catch((error) => {
|
||||
notifications().notify({
|
||||
level: "error",
|
||||
title: tx("Cannot write to terminal"),
|
||||
body: error instanceof Error ? tx(error.message) : String(error),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/** Sends keystrokes buffered while the PTY was starting. */
|
||||
private flushPendingInput() {
|
||||
const queued = this.pendingInput;
|
||||
this.pendingInput = "";
|
||||
if (queued && this.ptyId !== null) void commands.termWrite(this.ptyId, queued);
|
||||
if (queued && this.ptyId !== null) this.input(queued);
|
||||
}
|
||||
|
||||
/** Writes input immediately (shortcuts translated outside xterm). */
|
||||
|
||||
Reference in new issue
Заблокувати користувача