mirror of
https://github.com/Eugeny/tabby.git
synced 2024-11-24 06:04:04 +03:00
selectable persistence providers
This commit is contained in:
parent
514fdbfb6a
commit
1c62f3074c
@ -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 },
|
||||
|
@ -1,11 +1,11 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user