mirror of
https://github.com/Eugeny/tabby.git
synced 2025-07-07 03:49:56 +00:00
.
This commit is contained in:
parent
90d7ee21ae
commit
2846637815
@ -9,10 +9,11 @@ export interface SessionOptions {
|
|||||||
cwd?: string,
|
cwd?: string,
|
||||||
env?: any,
|
env?: any,
|
||||||
recoveryId?: string
|
recoveryId?: string
|
||||||
|
recoveredTruePID?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export abstract class SessionPersistenceProvider {
|
export abstract class SessionPersistenceProvider {
|
||||||
abstract async recoverSession (recoveryId: any): Promise<SessionOptions>
|
abstract async attachSession (recoveryId: any): Promise<SessionOptions>
|
||||||
abstract async createSession (options: SessionOptions): Promise<SessionOptions>
|
abstract async startSession (options: SessionOptions): Promise<any>
|
||||||
abstract async terminateSession (recoveryId: string): Promise<void>
|
abstract async terminateSession (recoveryId: string): Promise<void>
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,11 @@ export class ButtonProvider extends ToolbarButtonProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async getNewTab (): Promise<TerminalTab> {
|
async getNewTab (): Promise<TerminalTab> {
|
||||||
return new TerminalTab(await this.sessions.createNewSession({ command: 'zsh' }))
|
let cwd = null
|
||||||
|
if (this.app.activeTab instanceof TerminalTab) {
|
||||||
|
cwd = await this.app.activeTab.session.getWorkingDirectory()
|
||||||
|
}
|
||||||
|
return new TerminalTab(await this.sessions.createNewSession({ command: 'zsh', cwd }))
|
||||||
}
|
}
|
||||||
|
|
||||||
provide (): IToolbarButton[] {
|
provide (): IToolbarButton[] {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Subscription } from 'rxjs'
|
import { BehaviorSubject, Subscription } from 'rxjs'
|
||||||
import { Component, NgZone, Output, Inject, EventEmitter, ElementRef } from '@angular/core'
|
import { Component, NgZone, Inject, ElementRef } from '@angular/core'
|
||||||
|
|
||||||
import { ConfigService } from 'services/config'
|
import { ConfigService } from 'services/config'
|
||||||
|
|
||||||
@ -16,9 +16,8 @@ import { hterm, preferenceManager } from '../hterm'
|
|||||||
styles: [require('./terminalTab.scss')],
|
styles: [require('./terminalTab.scss')],
|
||||||
})
|
})
|
||||||
export class TerminalTabComponent extends BaseTabComponent<TerminalTab> {
|
export class TerminalTabComponent extends BaseTabComponent<TerminalTab> {
|
||||||
title: string
|
|
||||||
@Output() titleChange = new EventEmitter()
|
|
||||||
terminal: any
|
terminal: any
|
||||||
|
title$ = new BehaviorSubject('')
|
||||||
configSubscription: Subscription
|
configSubscription: Subscription
|
||||||
focusedSubscription: Subscription
|
focusedSubscription: Subscription
|
||||||
startupTime: number
|
startupTime: number
|
||||||
@ -47,8 +46,7 @@ export class TerminalTabComponent extends BaseTabComponent<TerminalTab> {
|
|||||||
})
|
})
|
||||||
this.terminal.setWindowTitle = (title) => {
|
this.terminal.setWindowTitle = (title) => {
|
||||||
this.zone.run(() => {
|
this.zone.run(() => {
|
||||||
this.title = title
|
this.model.title = title
|
||||||
this.titleChange.emit(title)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
this.terminal.onTerminalReady = () => {
|
this.terminal.onTerminalReady = () => {
|
||||||
|
@ -1,25 +1,12 @@
|
|||||||
import * as fs from 'fs-promise'
|
import * as fs from 'fs-promise'
|
||||||
const exec = require('child-process-promise').exec
|
const exec = require('child-process-promise').exec
|
||||||
|
const spawn = require('child-process-promise').spawn
|
||||||
|
|
||||||
import { SessionOptions, SessionPersistenceProvider } from './api'
|
import { SessionOptions, SessionPersistenceProvider } from './api'
|
||||||
|
|
||||||
|
|
||||||
export class NullPersistenceProvider extends SessionPersistenceProvider {
|
|
||||||
async recoverSession (_recoveryId: any): Promise<SessionOptions> {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
async createSession (_options: SessionOptions): Promise<SessionOptions> {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
async terminateSession (_recoveryId: string): Promise<void> {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export class ScreenPersistenceProvider extends SessionPersistenceProvider {
|
export class ScreenPersistenceProvider extends SessionPersistenceProvider {
|
||||||
|
/*
|
||||||
list(): Promise<any[]> {
|
list(): Promise<any[]> {
|
||||||
return exec('screen -list').then((result) => {
|
return exec('screen -list').then((result) => {
|
||||||
return result.stdout.split('\n')
|
return result.stdout.split('\n')
|
||||||
@ -29,17 +16,30 @@ export class ScreenPersistenceProvider extends SessionPersistenceProvider {
|
|||||||
return []
|
return []
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
async attachSession (recoveryId: any): Promise<SessionOptions> {
|
||||||
|
let lines = (await exec('screen -list')).stdout.split('\n')
|
||||||
|
let screenPID = lines
|
||||||
|
.filter(line => line.indexOf('.' + recoveryId) !== -1)
|
||||||
|
.map(line => parseInt(line.trim().split('.')[0]))[0]
|
||||||
|
|
||||||
|
if (!screenPID) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
lines = (await exec(`ps -o pid --ppid ${screenPID}`)).stdout.split('\n')
|
||||||
|
let recoveredTruePID = parseInt(lines[1].split(/\s/).filter(x => !!x)[0])
|
||||||
|
|
||||||
async recoverSession (recoveryId: any): Promise<SessionOptions> {
|
|
||||||
// TODO check
|
|
||||||
return {
|
return {
|
||||||
recoveryId,
|
recoveryId,
|
||||||
|
recoveredTruePID,
|
||||||
command: 'screen',
|
command: 'screen',
|
||||||
args: ['-r', recoveryId],
|
args: ['-r', recoveryId],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async createSession (options: SessionOptions): Promise<SessionOptions> {
|
async startSession (options: SessionOptions): Promise<any> {
|
||||||
let configPath = '/tmp/.termScreenConfig'
|
let configPath = '/tmp/.termScreenConfig'
|
||||||
await fs.writeFile(configPath, `
|
await fs.writeFile(configPath, `
|
||||||
escape ^^^
|
escape ^^^
|
||||||
@ -52,10 +52,12 @@ export class ScreenPersistenceProvider extends SessionPersistenceProvider {
|
|||||||
hardstatus off
|
hardstatus off
|
||||||
`, 'utf-8')
|
`, 'utf-8')
|
||||||
let recoveryId = `term-tab-${Date.now()}`
|
let recoveryId = `term-tab-${Date.now()}`
|
||||||
options.args = ['-c', configPath, '-U', '-S', recoveryId, '--', options.command].concat(options.args || [])
|
let args = ['-d', '-m', '-c', configPath, '-U', '-S', recoveryId, '--', options.command].concat(options.args || [])
|
||||||
options.command = 'screen'
|
await spawn('screen', args, {
|
||||||
options.recoveryId = recoveryId
|
cwd: options.cwd,
|
||||||
return options
|
env: options.env || process.env,
|
||||||
|
})
|
||||||
|
return recoveryId
|
||||||
}
|
}
|
||||||
|
|
||||||
async terminateSession (recoveryId: string): Promise<void> {
|
async terminateSession (recoveryId: string): Promise<void> {
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import * as nodePTY from 'node-pty'
|
import * as nodePTY from 'node-pty'
|
||||||
|
import * as fs from 'fs-promise'
|
||||||
|
|
||||||
import { Injectable, EventEmitter } from '@angular/core'
|
import { Injectable, EventEmitter } from '@angular/core'
|
||||||
import { Logger, LogService } from 'services/log'
|
import { Logger, LogService } from 'services/log'
|
||||||
@ -12,6 +13,7 @@ export class Session {
|
|||||||
closed = new EventEmitter()
|
closed = new EventEmitter()
|
||||||
destroyed = new EventEmitter()
|
destroyed = new EventEmitter()
|
||||||
recoveryId: string
|
recoveryId: string
|
||||||
|
truePID: number
|
||||||
private pty: any
|
private pty: any
|
||||||
private initialDataBuffer = ''
|
private initialDataBuffer = ''
|
||||||
private initialDataBufferReleased = false
|
private initialDataBufferReleased = false
|
||||||
@ -40,6 +42,8 @@ export class Session {
|
|||||||
env: env,
|
env: env,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
this.truePID = options.recoveredTruePID || (<any>this.pty).pid
|
||||||
|
|
||||||
this.open = true
|
this.open = true
|
||||||
|
|
||||||
this.pty.on('data', (data) => {
|
this.pty.on('data', (data) => {
|
||||||
@ -105,6 +109,10 @@ export class Session {
|
|||||||
this.destroyed.emit()
|
this.destroyed.emit()
|
||||||
this.pty.destroy()
|
this.pty.destroy()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getWorkingDirectory (): Promise<string> {
|
||||||
|
return await fs.readlink(`/proc/${this.truePID}/cwd`)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -122,7 +130,8 @@ export class SessionsService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async createNewSession (options: SessionOptions) : Promise<Session> {
|
async createNewSession (options: SessionOptions) : Promise<Session> {
|
||||||
options = await this.persistence.createSession(options)
|
let recoveryId = await this.persistence.startSession(options)
|
||||||
|
options = await this.persistence.attachSession(recoveryId)
|
||||||
let session = this.addSession(options)
|
let session = this.addSession(options)
|
||||||
return session
|
return session
|
||||||
}
|
}
|
||||||
@ -141,7 +150,7 @@ export class SessionsService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async recover (recoveryId: string) : Promise<Session> {
|
async recover (recoveryId: string) : Promise<Session> {
|
||||||
const options = await this.persistence.recoverSession(recoveryId)
|
const options = await this.persistence.attachSession(recoveryId)
|
||||||
if (!options) {
|
if (!options) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user