moved more electron stuff out of tabby-local

This commit is contained in:
Eugene Pankov
2023-03-19 13:20:30 +01:00
parent 35ca7015c8
commit 8cba805522
12 changed files with 207 additions and 179 deletions

View File

@@ -59,3 +59,22 @@ export abstract class UACService {
abstract patchSessionOptionsForUAC (sessionOptions: SessionOptions): SessionOptions
}
export abstract class PTYProxy {
abstract getID (): string
abstract getPID (): Promise<number>
abstract resize (columns: number, rows: number): Promise<void>
abstract write (data: Buffer): Promise<void>
abstract kill (signal?: string): Promise<void>
abstract ackData (length: number): void
abstract subscribe (event: string, handler: (..._: any[]) => void): void
abstract unsubscribeAll (): void
abstract getChildProcesses (): Promise<ChildProcess[]>
abstract getTruePID (): Promise<number>
abstract getWorkingDirectory (): Promise<string|null>
}
export abstract class PTYInterface {
abstract spawn (...options: any[]): Promise<PTYProxy>
abstract restore (id: string): Promise<PTYProxy|null>
}

View File

@@ -51,7 +51,7 @@ export class TerminalTabComponent extends BaseTerminalTabComponent<LocalProfile>
protected onFrontendReady (): void {
this.initializeSession(this.size.columns, this.size.rows)
this.savedStateIsLive = this.profile.options.restoreFromPTYID === this.session?.getPTYID()
this.savedStateIsLive = this.profile.options.restoreFromPTYID === this.session?.getID()
super.onFrontendReady()
}
@@ -82,7 +82,7 @@ export class TerminalTabComponent extends BaseTerminalTabComponent<LocalProfile>
options: {
...this.profile.options,
cwd: cwd ?? this.profile.options.cwd,
restoreFromPTYID: options?.includeState && this.session?.getPTYID(),
restoreFromPTYID: options?.includeState && this.session?.getID(),
},
},
savedState: options?.includeState && this.frontend?.saveState(),

View File

@@ -1,87 +1,12 @@
import * as psNode from 'ps-node'
import * as fs from 'mz/fs'
import * as fsSync from 'fs'
import { Injector } from '@angular/core'
import { HostAppService, ConfigService, WIN_BUILD_CONPTY_SUPPORTED, isWindowsBuild, Platform, BootstrapData, BOOTSTRAP_DATA, LogService } from 'tabby-core'
import { BaseSession } from 'tabby-terminal'
import { ipcRenderer } from 'electron'
import { getWorkingDirectoryFromPID } from 'native-process-working-directory'
import { SessionOptions, ChildProcess } from './api'
/* eslint-disable block-scoped-var */
try {
var macOSNativeProcessList = require('macos-native-processlist') // eslint-disable-line @typescript-eslint/no-var-requires, no-var
} catch { }
try {
var windowsProcessTree = require('windows-process-tree') // eslint-disable-line @typescript-eslint/no-var-requires, no-var
} catch { }
import { SessionOptions, ChildProcess, PTYInterface, PTYProxy } from './api'
const windowsDirectoryRegex = /([a-zA-Z]:[^\:\[\]\?\"\<\>\|]+)/mi
// eslint-disable-next-line @typescript-eslint/no-extraneous-class
export class PTYProxy {
private id: string
private subscriptions: Map<string, any> = new Map()
static spawn (...options: any[]): PTYProxy {
return new PTYProxy(null, ...options)
}
static restore (id: string): PTYProxy|null {
if (ipcRenderer.sendSync('pty:exists', id)) {
return new PTYProxy(id)
}
return null
}
private constructor (id: string|null, ...options: any[]) {
if (id) {
this.id = id
} else {
this.id = ipcRenderer.sendSync('pty:spawn', ...options)
}
}
getPTYID (): string {
return this.id
}
getPID (): number {
return ipcRenderer.sendSync('pty:get-pid', this.id)
}
subscribe (event: string, handler: (..._: any[]) => void): void {
const key = `pty:${this.id}:${event}`
const newHandler = (_event, ...args) => handler(...args)
this.subscriptions.set(key, newHandler)
ipcRenderer.on(key, newHandler)
}
ackData (length: number): void {
ipcRenderer.send('pty:ack-data', this.id, length)
}
unsubscribeAll (): void {
for (const k of this.subscriptions.keys()) {
ipcRenderer.off(k, this.subscriptions.get(k))
}
}
resize (columns: number, rows: number): void {
ipcRenderer.send('pty:resize', this.id, columns, rows)
}
write (data: Buffer): void {
ipcRenderer.send('pty:write', this.id, data)
}
kill (signal?: string): void {
ipcRenderer.send('pty:kill', this.id, signal)
}
}
function mergeEnv (...envs) {
const result = {}
const keyMap = {}
@@ -121,19 +46,23 @@ export class Session extends BaseSession {
private config: ConfigService
private hostApp: HostAppService
private bootstrapData: BootstrapData
private ptyInterface: PTYInterface
constructor (injector: Injector) {
constructor (
injector: Injector,
) {
super(injector.get(LogService).create('local'))
this.config = injector.get(ConfigService)
this.hostApp = injector.get(HostAppService)
this.ptyInterface = injector.get(PTYInterface)
this.bootstrapData = injector.get(BOOTSTRAP_DATA)
}
start (options: SessionOptions): void {
async start (options: SessionOptions): Promise<void> {
let pty: PTYProxy|null = null
if (options.restoreFromPTYID) {
pty = PTYProxy.restore(options.restoreFromPTYID)
pty = await this.ptyInterface.restore(options.restoreFromPTYID)
options.restoreFromPTYID = undefined
}
@@ -175,7 +104,7 @@ export class Session extends BaseSession {
cwd = undefined
}
pty = PTYProxy.spawn(options.command, options.args ?? [], {
pty = await this.ptyInterface.spawn(options.command, options.args ?? [], {
name: 'xterm-256color',
cols: options.width ?? 80,
rows: options.height ?? 30,
@@ -191,17 +120,9 @@ export class Session extends BaseSession {
this.pty = pty
this.truePID = this.pty.getPID()
setTimeout(async () => {
// Retrieve any possible single children now that shell has fully started
let processes = await this.getChildProcesses()
while (processes.length === 1) {
this.truePID = processes[0].pid
processes = await this.getChildProcesses()
}
pty.getTruePID().then(async () => {
this.initialCWD = await this.getWorkingDirectory()
}, 2000)
})
this.open = true
@@ -236,8 +157,8 @@ export class Session extends BaseSession {
this.destroyed$.subscribe(() => this.pty!.unsubscribeAll())
}
getPTYID (): string|null {
return this.pty?.getPTYID() ?? null
getID (): string|null {
return this.pty?.getID() ?? null
}
resize (columns: number, rows: number): void {
@@ -258,37 +179,7 @@ export class Session extends BaseSession {
}
async getChildProcesses (): Promise<ChildProcess[]> {
if (!this.truePID) {
return []
}
if (this.hostApp.platform === Platform.macOS) {
const processes = await macOSNativeProcessList.getProcessList()
return processes.filter(x => x.ppid === this.truePID).map(p => ({
pid: p.pid,
ppid: p.ppid,
command: p.name,
}))
}
if (this.hostApp.platform === Platform.Windows) {
return new Promise<ChildProcess[]>(resolve => {
windowsProcessTree.getProcessTree(this.truePID, tree => {
resolve(tree ? tree.children.map(child => ({
pid: child.pid,
ppid: tree.pid,
command: child.name,
})) : [])
})
})
}
return new Promise<ChildProcess[]>((resolve, reject) => {
psNode.lookup({ ppid: this.truePID }, (err, processes) => {
if (err) {
reject(err)
return
}
resolve(processes as ChildProcess[])
})
})
return this.pty?.getChildProcesses() ?? []
}
async gracefullyKillProcess (): Promise<void> {
@@ -297,9 +188,9 @@ export class Session extends BaseSession {
} else {
await new Promise<void>((resolve) => {
this.kill('SIGTERM')
setTimeout(() => {
setTimeout(async () => {
try {
process.kill(this.pty!.getPID(), 0)
process.kill(await this.pty!.getPID(), 0)
// still alive
this.kill('SIGKILL')
resolve()
@@ -312,19 +203,16 @@ export class Session extends BaseSession {
}
supportsWorkingDirectory (): boolean {
return !!(this.truePID ?? this.reportedCWD ?? this.guessedCWD)
return !!(this.initialCWD ?? this.reportedCWD ?? this.guessedCWD)
}
async getWorkingDirectory (): Promise<string|null> {
if (this.reportedCWD) {
return this.reportedCWD
}
if (!this.truePID) {
return null
}
let cwd: string|null = null
try {
cwd = getWorkingDirectoryFromPID(this.truePID)
cwd = await this.pty?.getWorkingDirectory() ?? null
} catch (exc) {
console.info('Could not read working directory:', exc)
}