提交 1c62f307 编写于 作者: E Eugene Pankov

selectable persistence providers

上级 514fdbfb
......@@ -28,6 +28,10 @@ export interface SessionOptions {
}
export abstract class SessionPersistenceProvider {
abstract id: string
abstract displayName: string
abstract isAvailable (): boolean
abstract async attachSession (recoveryId: any): Promise<SessionOptions>
abstract async startSession (options: SessionOptions): Promise<any>
abstract async terminateSession (recoveryId: string): Promise<void>
......
......@@ -259,3 +259,15 @@
[value]='"audible"'
)
| Audible
.form-group
label Session persistence
select.form-control(
'[(ngModel)]'='config.store.terminal.persistence',
(ngModelChange)='config.save()',
)
option([ngValue]='null') Off
option(
*ngFor='let provider of persistenceProviders',
[ngValue]='provider.id'
) {{provider.displayName}}
......@@ -5,7 +5,7 @@ const fontManager = require('font-manager')
import { Component, Inject } from '@angular/core'
import { ConfigService, HostAppService, Platform } from 'terminus-core'
import { TerminalColorSchemeProvider, ITerminalColorScheme, IShell, ShellProvider } from '../api'
import { TerminalColorSchemeProvider, ITerminalColorScheme, IShell, ShellProvider, SessionPersistenceProvider } from '../api'
@Component({
template: require('./terminalSettingsTab.component.pug'),
......@@ -14,6 +14,7 @@ import { TerminalColorSchemeProvider, ITerminalColorScheme, IShell, ShellProvide
export class TerminalSettingsTabComponent {
fonts: string[] = []
shells: IShell[] = []
persistenceProviders: SessionPersistenceProvider[]
colorSchemes: ITerminalColorScheme[] = []
equalComparator = equal
editingColorScheme: ITerminalColorScheme
......@@ -24,7 +25,10 @@ export class TerminalSettingsTabComponent {
private hostApp: HostAppService,
@Inject(ShellProvider) private shellProviders: ShellProvider[],
@Inject(TerminalColorSchemeProvider) private colorSchemeProviders: TerminalColorSchemeProvider[],
) { }
@Inject(SessionPersistenceProvider) persistenceProviders: SessionPersistenceProvider[],
) {
this.persistenceProviders = persistenceProviders.filter(x => x.isAvailable())
}
async ngOnInit () {
if (this.hostApp.platform === Platform.Windows || this.hostApp.platform === Platform.macOS) {
......
......@@ -43,6 +43,7 @@ export class TerminalConfigProvider extends ConfigProvider {
terminal: {
font: 'Menlo',
shell: '~default-shell~',
persistence: 'screen',
},
hotkeys: {
'copy': [
......@@ -74,6 +75,7 @@ export class TerminalConfigProvider extends ConfigProvider {
terminal: {
font: 'Consolas',
shell: '~clink~',
persistence: null,
},
hotkeys: {
'copy': [
......@@ -104,6 +106,7 @@ export class TerminalConfigProvider extends ConfigProvider {
terminal: {
font: 'Liberation Mono',
shell: '~default-shell~',
persistence: 'screen',
},
hotkeys: {
'copy': [
......
......@@ -3,7 +3,7 @@ import { BrowserModule } from '@angular/platform-browser'
import { FormsModule } from '@angular/forms'
import { NgbModule } from '@ng-bootstrap/ng-bootstrap'
import { HostAppService, Platform, ToolbarButtonProvider, TabRecoveryProvider, ConfigProvider, HotkeysService, HotkeyProvider } from 'terminus-core'
import { ToolbarButtonProvider, TabRecoveryProvider, ConfigProvider, HotkeysService, HotkeyProvider } from 'terminus-core'
import { SettingsTabProvider } from 'terminus-settings'
import { TerminalTabComponent } from './components/terminalTab.component'
......@@ -12,8 +12,8 @@ import { ColorPickerComponent } from './components/colorPicker.component'
import { SessionsService } from './services/sessions.service'
import { ScreenPersistenceProvider } from './persistenceProviders'
import { TMuxPersistenceProvider } from './tmux'
import { ScreenPersistenceProvider } from './persistence/screen'
import { TMuxPersistenceProvider } from './persistence/tmux'
import { ButtonProvider } from './buttonProvider'
import { RecoveryProvider } from './recoveryProvider'
import { SessionPersistenceProvider, TerminalColorSchemeProvider, TerminalDecorator, ShellProvider } from './api'
......@@ -42,36 +42,17 @@ import { hterm } from './hterm'
],
providers: [
SessionsService,
ScreenPersistenceProvider,
TMuxPersistenceProvider,
{ provide: ToolbarButtonProvider, useClass: ButtonProvider, multi: true },
{ provide: TabRecoveryProvider, useClass: RecoveryProvider, multi: true },
{
provide: SessionPersistenceProvider,
useFactory: (
hostApp: HostAppService,
screen: ScreenPersistenceProvider,
tmux: TMuxPersistenceProvider,
) => {
if (hostApp.platform === Platform.Windows) {
return null
} else {
if (tmux.isAvailable()) {
tmux.init()
return tmux
} else {
return screen
}
}
},
deps: [HostAppService, ScreenPersistenceProvider, TMuxPersistenceProvider],
},
{ provide: SettingsTabProvider, useClass: TerminalSettingsTabProvider, multi: true },
{ provide: ConfigProvider, useClass: TerminalConfigProvider, multi: true },
{ provide: HotkeyProvider, useClass: TerminalHotkeyProvider, multi: true },
{ provide: TerminalColorSchemeProvider, useClass: HyperColorSchemes, multi: true },
{ provide: TerminalDecorator, useClass: PathDropDecorator, multi: true },
{ provide: SessionPersistenceProvider, useClass: ScreenPersistenceProvider, multi: true },
{ provide: SessionPersistenceProvider, useClass: TMuxPersistenceProvider, multi: true },
{ provide: ShellProvider, useClass: WindowsStockShellsProvider, multi: true },
{ provide: ShellProvider, useClass: MacOSDefaultShellProvider, multi: true },
{ provide: ShellProvider, useClass: LinuxDefaultShellProvider, multi: true },
......
import * as fs from 'mz/fs'
import { exec, spawn } from 'mz/child_process'
import { exec as execCallback } from 'child_process'
import { exec as execAsync, execFileSync } from 'child_process'
import { AsyncSubject } from 'rxjs'
import { Injectable } from '@angular/core'
import { Logger, LogService } from 'terminus-core'
import { SessionOptions, SessionPersistenceProvider } from './api'
import { SessionOptions, SessionPersistenceProvider } from '../api'
declare function delay (ms: number): Promise<void>
......@@ -29,6 +29,8 @@ async function listProcesses (): Promise<IChildProcess[]> {
@Injectable()
export class ScreenPersistenceProvider extends SessionPersistenceProvider {
id = 'screen'
displayName = 'GNU Screen'
private logger: Logger
constructor (
......@@ -38,9 +40,18 @@ export class ScreenPersistenceProvider extends SessionPersistenceProvider {
this.logger = log.create('main')
}
isAvailable () {
try {
execFileSync('sh', ['-c', 'which screen'])
return true
} catch (_) {
return false
}
}
async attachSession (recoveryId: any): Promise<SessionOptions> {
let lines = await new Promise<string[]>(resolve => {
execCallback('screen -list', (_err, stdout) => {
execAsync('screen -list', (_err, stdout) => {
// returns an error code on macOS
resolve(stdout.split('\n'))
})
......
......@@ -3,7 +3,7 @@ import { execFileSync } from 'child_process'
import * as AsyncLock from 'async-lock'
import { ConnectableObservable, AsyncSubject, Subject } from 'rxjs'
import * as childProcess from 'child_process'
import { SessionOptions, SessionPersistenceProvider } from './api'
import { SessionOptions, SessionPersistenceProvider } from '../api'
const TMUX_CONFIG = `
set -g status off
......@@ -174,10 +174,15 @@ export class TMux {
@Injectable()
export class TMuxPersistenceProvider extends SessionPersistenceProvider {
id = 'tmux'
displayName = 'Tmux'
private tmux: TMux
constructor () {
super()
if (this.isAvailable()) {
this.tmux = new TMux()
}
}
isAvailable (): boolean {
......@@ -189,10 +194,6 @@ export class TMuxPersistenceProvider extends SessionPersistenceProvider {
}
}
init () {
this.tmux = new TMux()
}
async attachSession (recoveryId: any): Promise<SessionOptions> {
let sessions = await this.tmux.list()
if (!sessions.includes(recoveryId)) {
......
......@@ -3,8 +3,8 @@ const psNode = require('ps-node')
let nodePTY
import * as fs from 'mz/fs'
import { Subject } from 'rxjs'
import { Injectable } from '@angular/core'
import { Logger, LogService, ElectronService } from 'terminus-core'
import { Injectable, Inject } from '@angular/core'
import { Logger, LogService, ElectronService, ConfigService } from 'terminus-core'
import { exec } from 'mz/child_process'
import { SessionOptions, SessionPersistenceProvider } from '../api'
......@@ -178,7 +178,8 @@ export class SessionsService {
private lastID = 0
constructor (
private persistence: SessionPersistenceProvider,
@Inject(SessionPersistenceProvider) private persistenceProviders: SessionPersistenceProvider[],
private config: ConfigService,
electron: ElectronService,
log: LogService,
) {
......@@ -187,9 +188,10 @@ export class SessionsService {
}
async prepareNewSession (options: SessionOptions): Promise<SessionOptions> {
if (this.persistence) {
let recoveryId = await this.persistence.startSession(options)
options = await this.persistence.attachSession(recoveryId)
let persistence = this.getPersistence()
if (persistence) {
let recoveryId = await persistence.startSession(options)
options = await persistence.attachSession(recoveryId)
}
return options
}
......@@ -198,10 +200,11 @@ export class SessionsService {
this.lastID++
options.name = `session-${this.lastID}`
let session = new Session(options)
let persistence = this.getPersistence()
session.destroyed$.first().subscribe(() => {
delete this.sessions[session.name]
if (this.persistence) {
this.persistence.terminateSession(session.recoveryId)
if (persistence) {
persistence.terminateSession(session.recoveryId)
}
})
this.sessions[session.name] = session
......@@ -209,9 +212,14 @@ export class SessionsService {
}
async recover (recoveryId: string): Promise<SessionOptions> {
if (!this.persistence) {
return null
let persistence = this.getPersistence()
if (persistence) {
return await persistence.attachSession(recoveryId)
}
return await this.persistence.attachSession(recoveryId)
return null
}
private getPersistence (): SessionPersistenceProvider {
return this.persistenceProviders.find(x => x.id === this.config.store.terminal.persistence) || null
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册