This commit is contained in:
Eugene Pankov 2017-04-02 17:33:55 +02:00
parent 90d7ee21ae
commit 2846637815
5 changed files with 47 additions and 33 deletions

View File

@ -9,10 +9,11 @@ export interface SessionOptions {
cwd?: string,
env?: any,
recoveryId?: string
recoveredTruePID?: number
}
export abstract class SessionPersistenceProvider {
abstract async recoverSession (recoveryId: any): Promise<SessionOptions>
abstract async createSession (options: SessionOptions): Promise<SessionOptions>
abstract async attachSession (recoveryId: any): Promise<SessionOptions>
abstract async startSession (options: SessionOptions): Promise<any>
abstract async terminateSession (recoveryId: string): Promise<void>
}

View File

@ -20,7 +20,11 @@ export class ButtonProvider extends ToolbarButtonProvider {
}
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[] {

View File

@ -1,5 +1,5 @@
import { Subscription } from 'rxjs'
import { Component, NgZone, Output, Inject, EventEmitter, ElementRef } from '@angular/core'
import { BehaviorSubject, Subscription } from 'rxjs'
import { Component, NgZone, Inject, ElementRef } from '@angular/core'
import { ConfigService } from 'services/config'
@ -16,9 +16,8 @@ import { hterm, preferenceManager } from '../hterm'
styles: [require('./terminalTab.scss')],
})
export class TerminalTabComponent extends BaseTabComponent<TerminalTab> {
title: string
@Output() titleChange = new EventEmitter()
terminal: any
title$ = new BehaviorSubject('')
configSubscription: Subscription
focusedSubscription: Subscription
startupTime: number
@ -47,8 +46,7 @@ export class TerminalTabComponent extends BaseTabComponent<TerminalTab> {
})
this.terminal.setWindowTitle = (title) => {
this.zone.run(() => {
this.title = title
this.titleChange.emit(title)
this.model.title = title
})
}
this.terminal.onTerminalReady = () => {

View File

@ -1,25 +1,12 @@
import * as fs from 'fs-promise'
const exec = require('child-process-promise').exec
const spawn = require('child-process-promise').spawn
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 {
/*
list(): Promise<any[]> {
return exec('screen -list').then((result) => {
return result.stdout.split('\n')
@ -29,17 +16,30 @@ export class ScreenPersistenceProvider extends SessionPersistenceProvider {
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 {
recoveryId,
recoveredTruePID,
command: 'screen',
args: ['-r', recoveryId],
}
}
async createSession (options: SessionOptions): Promise<SessionOptions> {
async startSession (options: SessionOptions): Promise<any> {
let configPath = '/tmp/.termScreenConfig'
await fs.writeFile(configPath, `
escape ^^^
@ -52,10 +52,12 @@ export class ScreenPersistenceProvider extends SessionPersistenceProvider {
hardstatus off
`, 'utf-8')
let recoveryId = `term-tab-${Date.now()}`
options.args = ['-c', configPath, '-U', '-S', recoveryId, '--', options.command].concat(options.args || [])
options.command = 'screen'
options.recoveryId = recoveryId
return options
let args = ['-d', '-m', '-c', configPath, '-U', '-S', recoveryId, '--', options.command].concat(options.args || [])
await spawn('screen', args, {
cwd: options.cwd,
env: options.env || process.env,
})
return recoveryId
}
async terminateSession (recoveryId: string): Promise<void> {

View File

@ -1,4 +1,5 @@
import * as nodePTY from 'node-pty'
import * as fs from 'fs-promise'
import { Injectable, EventEmitter } from '@angular/core'
import { Logger, LogService } from 'services/log'
@ -12,6 +13,7 @@ export class Session {
closed = new EventEmitter()
destroyed = new EventEmitter()
recoveryId: string
truePID: number
private pty: any
private initialDataBuffer = ''
private initialDataBufferReleased = false
@ -40,6 +42,8 @@ export class Session {
env: env,
})
this.truePID = options.recoveredTruePID || (<any>this.pty).pid
this.open = true
this.pty.on('data', (data) => {
@ -105,6 +109,10 @@ export class Session {
this.destroyed.emit()
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> {
options = await this.persistence.createSession(options)
let recoveryId = await this.persistence.startSession(options)
options = await this.persistence.attachSession(recoveryId)
let session = this.addSession(options)
return session
}
@ -141,7 +150,7 @@ export class SessionsService {
}
async recover (recoveryId: string) : Promise<Session> {
const options = await this.persistence.recoverSession(recoveryId)
const options = await this.persistence.attachSession(recoveryId)
if (!options) {
return null
}