diff options
Diffstat (limited to 'extension/src/terminal/terminalEmulator.ts')
-rw-r--r-- | extension/src/terminal/terminalEmulator.ts | 301 |
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(); + } +} |