|
|
|
@ -13,8 +13,6 @@ import { findExecutable } from '../utils/findExecutable' |
|
|
|
|
import { spawnSync } from 'child_process' |
|
|
|
|
import { stripAnsi } from '../lib' |
|
|
|
|
import { DataBatcher } from '../lib/databatcher' |
|
|
|
|
import { Worker } from 'worker_threads' |
|
|
|
|
import { utilityProcess } from 'electron' |
|
|
|
|
|
|
|
|
|
export const detectDefaultShell = () => { |
|
|
|
|
const { env } = process |
|
|
|
@ -66,12 +64,6 @@ const parseEnv = (env: any) => { |
|
|
|
|
return returnValue |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export default defaultShell |
|
|
|
|
|
|
|
|
|
const profile: Profile = { |
|
|
|
@ -111,7 +103,7 @@ const clientProfile: Profile = { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
class XtermPluginClient extends ElectronBasePluginClient { |
|
|
|
|
terminals: Electron.UtilityProcess[] = [] |
|
|
|
|
terminals: pty.IPty[] = [] |
|
|
|
|
dataBatchers: DataBatcher[] = [] |
|
|
|
|
workingDir: string = '' |
|
|
|
|
parsedEnv: any = null |
|
|
|
@ -125,18 +117,17 @@ class XtermPluginClient extends ElectronBasePluginClient { |
|
|
|
|
this.workingDir = await this.call('fs' as any, 'getWorkingDir') |
|
|
|
|
console.log('workingDir', this.workingDir) |
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
if (!(process.platform === 'win32')) { |
|
|
|
|
const { stdout } = spawnSync(defaultShell, getShellEnvArgs, { |
|
|
|
|
encoding: 'utf8', |
|
|
|
|
}) |
|
|
|
|
this.parsedEnv = parseEnv(stdout) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
async keystroke(key: string, pid: number): Promise<void> { |
|
|
|
|
//this.terminals[pid].write(key)
|
|
|
|
|
this.terminals[pid].postMessage({ type: 'write', data: key }) |
|
|
|
|
this.terminals[pid].write(key) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
async getShells(): Promise<string[]> { |
|
|
|
@ -157,57 +148,43 @@ class XtermPluginClient extends ElectronBasePluginClient { |
|
|
|
|
|
|
|
|
|
async createTerminal(path?: string, shell?: string): Promise<number> { |
|
|
|
|
const start_time = Date.now() |
|
|
|
|
return new Promise((resolve, reject) => { |
|
|
|
|
let mypy: Electron.UtilityProcess = utilityProcess.fork(__dirname + '/xtermWorker.js') |
|
|
|
|
const end_time_fork = Date.now() |
|
|
|
|
console.log(`fork took ${end_time_fork - start_time} ms`) |
|
|
|
|
console.log('createTerminal', path, shell || defaultShell) |
|
|
|
|
|
|
|
|
|
const env = this.parsedEnv || process.env |
|
|
|
|
|
|
|
|
|
mypy.on('message', (message: any) => { |
|
|
|
|
//console.log('message', message)
|
|
|
|
|
if (message.type === 'spawned') { |
|
|
|
|
const end_time_spawn = Date.now() |
|
|
|
|
console.log(`spawn message took ${end_time_spawn - end_time_fork} ms`) |
|
|
|
|
const pid = message.pid |
|
|
|
|
const dataBatcher = new DataBatcher(pid) |
|
|
|
|
this.dataBatchers[pid] = dataBatcher |
|
|
|
|
dataBatcher.on('flush', (data: string, uid: number) => { |
|
|
|
|
this.sendData(data, uid) |
|
|
|
|
}) |
|
|
|
|
this.terminals[pid] = mypy |
|
|
|
|
const end_time = Date.now() |
|
|
|
|
console.log('spawned', pid, end_time - start_time) |
|
|
|
|
resolve(pid) |
|
|
|
|
} |
|
|
|
|
if (message.type === 'data') { |
|
|
|
|
this.dataBatchers[message.pid].write(Buffer.from(message.data)) |
|
|
|
|
} |
|
|
|
|
if (message.type === 'exit') { |
|
|
|
|
this.closeTerminal(message.pid) |
|
|
|
|
} |
|
|
|
|
}) |
|
|
|
|
const env = this.parsedEnv || process.env |
|
|
|
|
|
|
|
|
|
mypy.postMessage({ |
|
|
|
|
type: 'spawn', shell, args: [], options: |
|
|
|
|
{ |
|
|
|
|
const ptyProcess = pty.spawn(shell || defaultShell, [], { |
|
|
|
|
name: 'xterm-color', |
|
|
|
|
cols: 40, |
|
|
|
|
rows: 10, |
|
|
|
|
cols: 80, |
|
|
|
|
rows: 20, |
|
|
|
|
cwd: path || process.cwd(), |
|
|
|
|
env: env, |
|
|
|
|
encoding: 'utf8', |
|
|
|
|
} |
|
|
|
|
}); |
|
|
|
|
const dataBatcher = new DataBatcher(ptyProcess.pid) |
|
|
|
|
this.dataBatchers[ptyProcess.pid] = dataBatcher |
|
|
|
|
ptyProcess.onData((data: string) => { |
|
|
|
|
//console.log('data', data)
|
|
|
|
|
dataBatcher.write(Buffer.from(data)) |
|
|
|
|
}) |
|
|
|
|
ptyProcess.onExit(() => { |
|
|
|
|
const pid = ptyProcess.pid |
|
|
|
|
this.closeTerminal(pid) |
|
|
|
|
}) |
|
|
|
|
dataBatcher.on('flush', (data: string, uid: number) => { |
|
|
|
|
this.sendData(data, uid) |
|
|
|
|
}) |
|
|
|
|
this.terminals[ptyProcess.pid] = ptyProcess |
|
|
|
|
const end_time = Date.now() |
|
|
|
|
console.log('createTerminal', end_time - start_time) |
|
|
|
|
return ptyProcess.pid |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
async closeTerminal(pid: number): Promise<void> { |
|
|
|
|
if (this.terminals) { |
|
|
|
|
if (this.terminals[pid]) { |
|
|
|
|
try { |
|
|
|
|
this.terminals[pid].postMessage({ type: 'close' }) |
|
|
|
|
//this.terminals[pid].kill()
|
|
|
|
|
this.terminals[pid].kill() |
|
|
|
|
} catch (err) { |
|
|
|
|
// ignore
|
|
|
|
|
} |
|
|
|
@ -222,7 +199,7 @@ class XtermPluginClient extends ElectronBasePluginClient { |
|
|
|
|
async resize({ cols, rows }: { cols: number; rows: number }, pid: number) { |
|
|
|
|
if (this.terminals[pid]) { |
|
|
|
|
try { |
|
|
|
|
//this.terminals[pid].postMessage({ type: 'resize', cols, rows })
|
|
|
|
|
this.terminals[pid].resize(cols, rows) |
|
|
|
|
} catch (_err) { |
|
|
|
|
const err = _err as { stack: any } |
|
|
|
|
console.error(err.stack) |
|
|
|
@ -234,7 +211,7 @@ class XtermPluginClient extends ElectronBasePluginClient { |
|
|
|
|
|
|
|
|
|
async closeAll(): Promise<void> { |
|
|
|
|
for (const pid in this.terminals) { |
|
|
|
|
this.terminals[pid].postMessage({ type: 'close' }) |
|
|
|
|
this.terminals[pid].kill() |
|
|
|
|
delete this.terminals[pid] |
|
|
|
|
this.emit('close', pid) |
|
|
|
|
} |
|
|
|
|