summaryrefslogtreecommitdiff
path: root/extension/src/terminal/terminalEmulator.ts
diff options
context:
space:
mode:
authorNate Sesti <sestinj@gmail.com>2023-06-06 17:50:50 -0400
committerNate Sesti <sestinj@gmail.com>2023-06-06 17:50:50 -0400
commit7af8208d0adb9586e60bd67fd6f4f321a35262d8 (patch)
treee3b5e19f9a71d1267d8cd7e1271c47a02b9641cf /extension/src/terminal/terminalEmulator.ts
parent42d9dadcaac714b7d6e789fbdeafb2dad04dbed7 (diff)
downloadsncontinue-7af8208d0adb9586e60bd67fd6f4f321a35262d8.tar.gz
sncontinue-7af8208d0adb9586e60bd67fd6f4f321a35262d8.tar.bz2
sncontinue-7af8208d0adb9586e60bd67fd6f4f321a35262d8.zip
trying to reliably capture terminal output in vsc
Diffstat (limited to 'extension/src/terminal/terminalEmulator.ts')
-rw-r--r--extension/src/terminal/terminalEmulator.ts301
1 files changed, 161 insertions, 140 deletions
diff --git a/extension/src/terminal/terminalEmulator.ts b/extension/src/terminal/terminalEmulator.ts
index 6cf65970..8974b7e3 100644
--- a/extension/src/terminal/terminalEmulator.ts
+++ b/extension/src/terminal/terminalEmulator.ts
@@ -1,140 +1,161 @@
-// /* Terminal emulator - commented because node-pty is causing problems. */
-
-// import * as vscode from "vscode";
-// import pty = require("node-pty");
-// import os = require("os");
-// import { extensionContext } from "../activation/activate";
-// import { debugPanelWebview } from "../debugPanel"; // Need to consider having multiple panels, where to store this state.
-// import {
-// CommandCaptureSnooper,
-// PythonTracebackSnooper,
-// TerminalSnooper,
-// } from "./snoopers";
-
-// export function tracebackToWebviewAction(traceback: string) {
-// if (debugPanelWebview) {
-// debugPanelWebview.postMessage({
-// type: "traceback",
-// value: traceback,
-// });
-// } else {
-// vscode.commands
-// .executeCommand("continue.openContinueGUI", extensionContext)
-// .then(() => {
-// // TODO: Waiting for the webview to load, but should add a hook to the onLoad message event. Same thing in autodebugTest command in commands.ts
-// setTimeout(() => {
-// debugPanelWebview?.postMessage({
-// type: "traceback",
-// value: traceback,
-// });
-// }, 500);
-// });
-// }
-// }
-
-// const DEFAULT_SNOOPERS = [
-// new PythonTracebackSnooper(tracebackToWebviewAction),
-// new CommandCaptureSnooper((data: string) => {
-// if (data.trim().startsWith("pytest ")) {
-// let fileAndFunctionSpecifier = data.split(" ")[1];
-// vscode.commands.executeCommand(
-// "continue.debugTest",
-// fileAndFunctionSpecifier
-// );
-// }
-// }),
-// ];
-
-// // Whenever a user opens a terminal, replace it with ours
-// vscode.window.onDidOpenTerminal((terminal) => {
-// if (terminal.name != "Continue") {
-// terminal.dispose();
-// openCapturedTerminal();
-// }
-// });
-
-// function getDefaultShell(): string {
-// if (process.platform !== "win32") {
-// return os.userInfo().shell;
-// }
-// switch (process.platform) {
-// case "win32":
-// return process.env.COMSPEC || "cmd.exe";
-// // case "darwin":
-// // return process.env.SHELL || "/bin/zsh";
-// // default:
-// // return process.env.SHELL || "/bin/sh";
-// }
-// }
-
-// function getRootDir(): string | undefined {
-// var isWindows = os.platform() === "win32";
-// let cwd = isWindows ? process.env.USERPROFILE : process.env.HOME;
-// if (
-// vscode.workspace.workspaceFolders &&
-// vscode.workspace.workspaceFolders.length > 0
-// ) {
-// cwd = vscode.workspace.workspaceFolders[0].uri.fsPath;
-// }
-// return cwd;
-// }
-
-// export function openCapturedTerminal(
-// snoopers: TerminalSnooper<string>[] = DEFAULT_SNOOPERS
-// ) {
-// // If there is another existing, non-Continue terminal, delete it
-// let terminals = vscode.window.terminals;
-// for (let i = 0; i < terminals.length; i++) {
-// if (terminals[i].name != "Continue") {
-// terminals[i].dispose();
-// }
-// }
-
-// let env = { ...(process.env as any) };
-// if (os.platform() !== "win32") {
-// env["PATH"] += ":" + ["/opt/homebrew/bin", "/opt/homebrew/sbin"].join(":");
-// }
-
-// var ptyProcess = pty.spawn(getDefaultShell(), [], {
-// name: "xterm-256color",
-// cols: 160, // TODO: Get size of vscode terminal, and change with resize
-// rows: 26,
-// cwd: getRootDir(),
-// env,
-// useConpty: true,
-// });
-
-// const writeEmitter = new vscode.EventEmitter<string>();
-
-// ptyProcess.onData((data: any) => {
-// // Let each of the snoopers see the new data
-// for (let snooper of snoopers) {
-// snooper.onData(data);
-// }
-
-// // Pass data through to terminal
-// writeEmitter.fire(data);
-// });
-// process.on("exit", () => ptyProcess.kill());
-
-// const newPty: vscode.Pseudoterminal = {
-// onDidWrite: writeEmitter.event,
-// open: () => {},
-// close: () => {},
-// handleInput: (data) => {
-// for (let snooper of snoopers) {
-// snooper.onWrite(data);
-// }
-// ptyProcess.write(data);
-// },
-// };
-// const terminal = vscode.window.createTerminal({
-// name: "Continue",
-// pty: newPty,
-// });
-// terminal.show();
-
-// setTimeout(() => {
-// ptyProcess.write("clear\r");
-// }, 500);
-// }
+/* Terminal emulator - commented because node-pty is causing problems. */
+
+import * as vscode from "vscode";
+import os = require("os");
+import stripAnsi from "strip-ansi";
+
+function loadNativeModule<T>(id: string): T | null {
+ try {
+ return require(`${vscode.env.appRoot}/node_modules.asar/${id}`);
+ } catch (err) {
+ // ignore
+ }
+
+ try {
+ return require(`${vscode.env.appRoot}/node_modules/${id}`);
+ } catch (err) {
+ // ignore
+ }
+
+ return null;
+}
+
+const pty = loadNativeModule<any>("node-pty");
+
+function getDefaultShell(): string {
+ if (process.platform !== "win32") {
+ return os.userInfo().shell;
+ }
+ switch (process.platform) {
+ case "win32":
+ return process.env.COMSPEC || "cmd.exe";
+ // case "darwin":
+ // return process.env.SHELL || "/bin/zsh";
+ // default:
+ // return process.env.SHELL || "/bin/sh";
+ }
+}
+
+function getRootDir(): string | undefined {
+ const isWindows = os.platform() === "win32";
+ let cwd = isWindows ? process.env.USERPROFILE : process.env.HOME;
+ if (
+ vscode.workspace.workspaceFolders &&
+ vscode.workspace.workspaceFolders.length > 0
+ ) {
+ cwd = vscode.workspace.workspaceFolders[0].uri.fsPath;
+ }
+ return cwd;
+}
+
+export class CapturedTerminal {
+ private readonly terminal: vscode.Terminal;
+ private readonly shellCmd: string;
+ private readonly ptyProcess: any;
+
+ private shellPrompt: string | undefined = undefined;
+ private dataBuffer: string = "";
+
+ private onDataListeners: ((data: string) => void)[] = [];
+
+ show() {
+ this.terminal.show();
+ }
+
+ private commandQueue: [string, (output: string) => void][] = [];
+ private hasRunCommand: boolean = false;
+
+ private async waitForCommandToFinish() {
+ return new Promise<string>((resolve, reject) => {
+ this.onDataListeners.push((data: any) => {
+ const strippedData = stripAnsi(data);
+ this.dataBuffer += strippedData;
+ const lines = this.dataBuffer.split("\n");
+ if (
+ lines.length > 0 &&
+ lines[lines.length - 1].includes("bash-") &&
+ lines[lines.length - 1].trim().endsWith("$")
+ ) {
+ resolve(this.dataBuffer);
+ this.dataBuffer = "";
+ this.onDataListeners = [];
+ }
+ });
+ });
+ }
+
+ async runCommand(command: string): Promise<string> {
+ if (!this.hasRunCommand) {
+ this.hasRunCommand = true;
+ // Let the first bash- prompt appear and let python env be opened
+ await this.waitForCommandToFinish();
+ }
+
+ if (this.commandQueue.length === 0) {
+ return new Promise(async (resolve, reject) => {
+ this.commandQueue.push([command, resolve]);
+
+ while (this.commandQueue.length > 0) {
+ const [command, resolve] = this.commandQueue.shift()!;
+
+ this.terminal.sendText(command);
+ resolve(await this.waitForCommandToFinish());
+ }
+ });
+ } else {
+ return new Promise((resolve, reject) => {
+ this.commandQueue.push([command, resolve]);
+ });
+ }
+ }
+
+ private readonly writeEmitter: vscode.EventEmitter<string>;
+
+ constructor(terminalName: string) {
+ this.shellCmd = "bash"; // getDefaultShell();
+
+ const env = { ...(process.env as any) };
+ if (os.platform() !== "win32") {
+ env.PATH += `:${["/opt/homebrew/bin", "/opt/homebrew/sbin"].join(":")}`;
+ }
+
+ // Create the pseudo terminal
+ this.ptyProcess = pty.spawn(this.shellCmd, [], {
+ name: "xterm-256color",
+ cols: 160, // TODO: Get size of vscode terminal, and change with resize
+ rows: 26,
+ cwd: getRootDir(),
+ env,
+ useConpty: true,
+ });
+
+ this.writeEmitter = new vscode.EventEmitter<string>();
+
+ this.ptyProcess.onData((data: any) => {
+ // Pass data through to terminal
+ this.writeEmitter.fire(data);
+
+ for (let listener of this.onDataListeners) {
+ listener(data);
+ }
+ });
+
+ process.on("exit", () => this.ptyProcess.kill());
+
+ const newPty: vscode.Pseudoterminal = {
+ onDidWrite: this.writeEmitter.event,
+ open: () => {},
+ close: () => {},
+ handleInput: (data) => {
+ this.ptyProcess.write(data);
+ },
+ };
+
+ // Create and clear the terminal
+ this.terminal = vscode.window.createTerminal({
+ name: terminalName,
+ pty: newPty,
+ });
+ this.terminal.show();
+ }
+}